/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2019, Google Inc. * * SharedFD test */ #include <fcntl.h> #include <iostream> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <libcamera/base/shared_fd.h> #include <libcamera/base/utils.h> #include "test.h" using namespace libcamera; using namespace std; class SharedFDTest : public Test { protected: int init() { desc1_ = nullptr; desc2_ = nullptr; fd_ = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); if (fd_ < 0) return TestFail; /* Cache inode number of temp file. */ struct stat s; if (fstat(fd_, &s)) return TestFail; inodeNr_ = s.st_ino; return 0; } int run() { /* Test creating empty SharedFD. */ desc1_ = new SharedFD(); if (desc1_->get() != -1) { std::cout << "Failed fd numerical check (default constructor)" << std::endl; return TestFail; } delete desc1_; desc1_ = nullptr; /* * Test creating SharedFD by copying numerical file * descriptor. */ desc1_ = new SharedFD(fd_); if (desc1_->get() == fd_) { std::cout << "Failed fd numerical check (lvalue ref constructor)" << std::endl; return TestFail; } if (!isValidFd(fd_) || !isValidFd(desc1_->get())) { std::cout << "Failed fd validity after construction (lvalue ref constructor)" << std::endl; return TestFail; } int fd = desc1_->get(); delete desc1_; desc1_ = nullptr; if (!isValidFd(fd_) || isValidFd(fd)) { std::cout << "Failed fd validity after destruction (lvalue ref constructor)" << std::endl; return TestFail; } /* * Test creating SharedFD by taking ownership of * numerical file descriptor. */ int dupFd = dup(fd_); int dupFdCopy = dupFd; desc1_ = new SharedFD(std::move(dupFd)); if (desc1_->get() != dupFdCopy) { std::cout << "Failed fd numerical check (rvalue ref constructor)" << std::endl; return TestFail; } if (dupFd != -1 || !isValidFd(fd_) || !isValidFd(desc1_->get())) { std::cout << "Failed fd validity after construction (rvalue ref constructor)" << std::endl; return TestFail; } fd = desc1_->get(); delete desc1_; desc1_ = nullptr; if (!isValidFd(fd_) || isValidFd(fd)) { std::cout << "Failed fd validity after destruction (rvalue ref constructor)" << std::endl; return TestFail; } /* Test creating SharedFD from other SharedFD. */ desc1_ = new SharedFD(fd_); desc2_ = new SharedFD(*desc1_); if (desc1_->get() == fd_ || desc2_->get() == fd_ || desc1_->get() != desc2_->get()) { std::cout << "Failed fd numerical check (copy constructor)" << std::endl; return TestFail; } if (!isValidFd(desc1_->get()) || !isValidFd(desc2_->get())) { std::cout << "Failed fd validity after construction (copy constructor)" << std::endl; return TestFail; } delete desc1_; desc1_ = nullptr; if (!isValidFd(desc2_->get())) { std::cout << "Failed fd validity after destruction (copy constructor)" << std::endl; return TestFail; } delete desc2_; desc2_ = nullptr; /* Test creating SharedFD by taking over other SharedFD. */ desc1_ = new SharedFD(fd_); fd = desc1_->get(); desc2_ = new SharedFD(std::move(*desc1_)); if (desc1_->get() != -1 || desc2_->get() != fd) { std::cout << "Failed fd numerical check (move constructor)" << std::endl; return TestFail; } if (!isValidFd(desc2_->get())) { std::cout << "Failed fd validity after construction (move constructor)" << std::endl; return TestFail; } delete desc1_; desc1_ = nullptr; delete desc2_; desc2_ = nullptr; /* Test creating SharedFD by copy assignment. */ desc1_ = new SharedFD(); desc2_ = new SharedFD(fd_); fd = desc2_->get(); *desc1_ = *desc2_; if (desc1_->get() != fd || desc2_->get() != fd) { std::cout << "Failed fd numerical check (copy assignment)" << std::endl; return TestFail; } if (!isValidFd(desc1_->get()) || !isValidFd(desc2_->get())) { std::cout << "Failed fd validity after construction (copy assignment)" << std::endl; return TestFail; } delete desc1_; desc1_ = nullptr; delete desc2_; desc2_ = nullptr; /* Test creating SharedFD by move assignment. */ desc1_ = new SharedFD(); desc2_ = new SharedFD(fd_); fd = desc2_->get(); *desc1_ = std::move(*desc2_); if (desc1_->get() != fd || desc2_->get() != -1) { std::cout << "Failed fd numerical check (move assignment)" << std::endl; return TestFail; } if (!isValidFd(desc1_->get())) { std::cout << "Failed fd validity after construction (move assignment)" << std::endl; return TestFail; } delete desc1_; desc1_ = nullptr; delete desc2_; desc2_ = nullptr; return TestPass; } void cleanup() { delete desc2_; delete desc1_; if (fd_ > 0) close(fd_); } private: bool isValidFd(int fd) { struct stat s; if (fstat(fd, &s)) return false; /* Check that inode number matches cached temp file. */ return s.st_ino == inodeNr_; } int fd_; ino_t inodeNr_; SharedFD *desc1_, *desc2_; }; TEST_REGISTER(SharedFDTest)