blob: bf0f2011f2f788f122a23cac28931c1f6063d2c9 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <semaphore.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <list>
#include <unordered_map>
#include <unordered_set>
#include <base/at_exit.h>
#include <base/bind.h>
#include <base/command_line.h>
#include <base/logging.h>
#include <gtest/gtest.h>
#include "common/libcab_test_internal.h"
#include "cros-camera/camera_algorithm_bridge.h"
#include "cros-camera/common.h"
namespace libcab_test {
// This test should be run against the fake libcam_algo.so created with
// fake_libcam_algo.cc.
class CameraAlgorithmBridgeFixture : public testing::Test,
public camera_algorithm_callback_ops_t {
public:
const size_t kShmBufferSize = 2048;
CameraAlgorithmBridgeFixture() {
CameraAlgorithmBridgeFixture::return_callback =
CameraAlgorithmBridgeFixture::ReturnCallbackForwarder;
bridge_ = cros::CameraAlgorithmBridge::CreateInstance();
if (!bridge_ || bridge_->Initialize(this) != 0) {
ADD_FAILURE() << "Failed to initialize camera algorithm bridge";
return;
}
sem_init(&return_sem_, 0, 0);
}
~CameraAlgorithmBridgeFixture() { sem_destroy(&return_sem_); }
protected:
static void ReturnCallbackForwarder(
const camera_algorithm_callback_ops_t* callback_ops,
uint32_t status,
int32_t buffer_handle) {
if (callback_ops) {
auto s = const_cast<CameraAlgorithmBridgeFixture*>(
static_cast<const CameraAlgorithmBridgeFixture*>(callback_ops));
s->ReturnCallback(status, buffer_handle);
}
}
virtual void ReturnCallback(uint32_t status, int32_t buffer_handle) {
base::AutoLock l(request_set_lock_);
if (request_set_.find(buffer_handle) == request_set_.end()) {
ADD_FAILURE() << "Invalid handle received from the return callback";
return;
}
status_list_.push_back(status);
request_set_.erase(buffer_handle);
sem_post(&return_sem_);
}
std::unique_ptr<cros::CameraAlgorithmBridge> bridge_;
base::Lock request_set_lock_;
std::unordered_set<int32_t> request_set_;
std::list<int32_t> status_list_;
sem_t return_sem_;
private:
DISALLOW_COPY_AND_ASSIGN(CameraAlgorithmBridgeFixture);
};
TEST_F(CameraAlgorithmBridgeFixture, BasicOperation) {
int fd = shm_open("/myshm", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ASSERT_LE(0, fd) << "Failed to create shared memory";
ASSERT_LE(0, fcntl(fd, F_GETFD));
ASSERT_EQ(0, ftruncate(fd, kShmBufferSize));
int32_t handle = bridge_->RegisterBuffer(fd);
ASSERT_LE(0, handle) << "Handle should be of positive value";
ASSERT_LE(0, fcntl(fd, F_GETFD));
std::vector<uint8_t> req_header(1, REQUEST_TEST_COMMAND_NORMAL);
{
base::AutoLock l(request_set_lock_);
request_set_.insert(handle);
}
bridge_->Request(req_header, handle);
struct timespec timeout = {};
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 1;
ASSERT_EQ(0, sem_timedwait(&return_sem_, &timeout));
ASSERT_EQ(0, status_list_.front());
std::vector<int32_t> handles({handle});
bridge_->DeregisterBuffers(handles);
close(fd);
shm_unlink("/myshm");
}
TEST_F(CameraAlgorithmBridgeFixture, InvalidFdOrHandle) {
int32_t handle = bridge_->RegisterBuffer(-1);
ASSERT_GT(0, handle) << "Registering invalid fd should have failed";
int fd = shm_open("/myshm", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ASSERT_LE(0, fd) << "Failed to create shared memory";
ASSERT_EQ(0, ftruncate(fd, kShmBufferSize));
handle = bridge_->RegisterBuffer(fd);
ASSERT_LE(0, handle) << "Handle should be of positive value";
std::vector<uint8_t> req_header(1, REQUEST_TEST_COMMAND_NORMAL);
{
base::AutoLock l(request_set_lock_);
request_set_.insert(handle - 1);
request_set_.insert(handle + 1);
}
bridge_->Request(req_header, handle - 1);
bridge_->Request(req_header, handle + 1);
struct timespec timeout = {};
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 1;
for (uint32_t i = 0; i < 2; i++) {
ASSERT_EQ(0, sem_timedwait(&return_sem_, &timeout));
}
for (const auto& it : status_list_) {
ASSERT_EQ(-EBADF, it);
}
std::vector<int32_t> handles({handle});
bridge_->DeregisterBuffers(handles);
close(fd);
shm_unlink("/myshm");
ASSERT_GT(0, bridge_->RegisterBuffer(fd))
<< "Registering invalid fd should have failed";
}
TEST_F(CameraAlgorithmBridgeFixture, MultiRequests) {
const uint32_t kNumberOfFds = 256;
auto GetShmName = [](uint32_t num) {
std::stringstream ss;
ss << num;
return std::string("/myshm") + ss.str();
};
std::vector<int> fds, handles;
for (uint32_t i = 1; i <= kNumberOfFds; i++) {
fds.push_back(
shm_open(GetShmName(i).c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR));
ASSERT_LE(0, fds.back()) << "Failed to create shared memory";
ASSERT_EQ(0, ftruncate(fds.back(), kShmBufferSize));
handles.push_back(bridge_->RegisterBuffer(fds.back()));
ASSERT_LE(0, handles.back()) << "Handle should be of positive value";
}
for (const auto handle : handles) {
std::vector<uint8_t> req_header(1, REQUEST_TEST_COMMAND_NORMAL);
{
base::AutoLock l(request_set_lock_);
request_set_.insert(handle);
}
bridge_->Request(req_header, handle);
}
struct timespec timeout = {};
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 1;
for (size_t i = 0; i < handles.size(); ++i) {
ASSERT_EQ(0, sem_timedwait(&return_sem_, &timeout));
}
for (const auto& it : status_list_) {
ASSERT_EQ(0, it);
}
bridge_->DeregisterBuffers(handles);
for (const auto fd : fds) {
close(fd);
}
for (uint32_t i = 1; i <= kNumberOfFds; i++) {
shm_unlink(GetShmName(i).c_str());
}
}
// This test should be run against the fake libcam_algo.so created with
// fake_libcam_algo.cc.
class CameraAlgorithmBridgeStatusFixture : public CameraAlgorithmBridgeFixture {
public:
CameraAlgorithmBridgeStatusFixture() {}
protected:
void ReturnCallback(uint32_t status, int32_t buffer_handle) override {
base::AutoLock l(hash_codes_lock_);
if (buffer_handle < 0 ||
static_cast<size_t>(buffer_handle) >= hash_codes_.size() ||
hash_codes_[buffer_handle] != status) {
ADD_FAILURE() << "Invalid status received from the return callback";
return;
}
sem_post(&return_sem_);
}
base::Lock hash_codes_lock_;
// Stores hashcode generated from |req_header| of the Request calls.
std::vector<uint32_t> hash_codes_;
private:
DISALLOW_COPY_AND_ASSIGN(CameraAlgorithmBridgeStatusFixture);
};
static int GenerateRandomHeader(uint32_t max_header_len,
std::vector<uint8_t>* header) {
if (max_header_len == 0 || !header) {
return -EINVAL;
}
static unsigned int seed = time(NULL) + getpid();
header->resize((rand_r(&seed) % max_header_len) + 1);
for (auto& it : *header) {
it = rand_r(&seed);
}
return 0;
}
TEST_F(CameraAlgorithmBridgeStatusFixture, VerifyReturnStatus) {
const uint32_t kNumberOfTests = 256;
const uint32_t kMaxReqHeaderSize = 64;
for (uint32_t i = 0; i <= kNumberOfTests; i++) {
std::vector<uint8_t> req_header;
GenerateRandomHeader(kMaxReqHeaderSize, &req_header);
req_header[0] = REQUEST_TEST_COMMAND_VERIFY_STATUS;
{
base::AutoLock l(hash_codes_lock_);
hash_codes_.push_back(SimpleHash(req_header.data(), req_header.size()));
}
bridge_->Request(req_header, i);
}
struct timespec timeout = {};
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 1;
for (uint32_t i = 0; i <= kNumberOfTests; i++) {
ASSERT_EQ(0, sem_timedwait(&return_sem_, &timeout));
}
}
} // namespace libcab_test
int main(int argc, char** argv) {
static base::AtExitManager exit_manager;
// Set up logging so we can enable VLOGs with -v / --vmodule.
base::CommandLine::Init(argc, argv);
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
LOG_ASSERT(logging::InitLogging(settings));
// Initialize and run all tests
::testing::InitGoogleTest(&argc, argv);
int result = RUN_ALL_TESTS();
return result;
}