diff options
Diffstat (limited to 'src/apps/lc-compliance')
-rw-r--r-- | src/apps/lc-compliance/capture_test.cpp | 135 | ||||
-rw-r--r-- | src/apps/lc-compliance/environment.cpp | 2 | ||||
-rw-r--r-- | src/apps/lc-compliance/environment.h | 4 | ||||
-rw-r--r-- | src/apps/lc-compliance/helpers/capture.cpp | 176 | ||||
-rw-r--r-- | src/apps/lc-compliance/helpers/capture.h | 45 | ||||
-rw-r--r-- | src/apps/lc-compliance/main.cpp | 44 | ||||
-rw-r--r-- | src/apps/lc-compliance/meson.build | 17 | ||||
-rw-r--r-- | src/apps/lc-compliance/simple_capture.cpp | 190 | ||||
-rw-r--r-- | src/apps/lc-compliance/simple_capture.h | 66 | ||||
-rw-r--r-- | src/apps/lc-compliance/test_base.cpp | 28 | ||||
-rw-r--r-- | src/apps/lc-compliance/test_base.h | 24 | ||||
-rw-r--r-- | src/apps/lc-compliance/tests/capture_test.cpp | 145 |
12 files changed, 446 insertions, 430 deletions
diff --git a/src/apps/lc-compliance/capture_test.cpp b/src/apps/lc-compliance/capture_test.cpp deleted file mode 100644 index 1dcfcf92..00000000 --- a/src/apps/lc-compliance/capture_test.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020, Google Inc. - * Copyright (C) 2021, Collabora Ltd. - * - * capture_test.cpp - Test camera capture - */ - -#include <iostream> - -#include <gtest/gtest.h> - -#include "environment.h" -#include "simple_capture.h" - -using namespace libcamera; - -const std::vector<int> NUMREQUESTS = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }; -const std::vector<StreamRole> ROLES = { - StreamRole::Raw, - StreamRole::StillCapture, - StreamRole::VideoRecording, - StreamRole::Viewfinder -}; - -class SingleStream : public testing::TestWithParam<std::tuple<StreamRole, int>> -{ -public: - static std::string nameParameters(const testing::TestParamInfo<SingleStream::ParamType> &info); - -protected: - void SetUp() override; - void TearDown() override; - - std::shared_ptr<Camera> camera_; -}; - -/* - * We use gtest's SetUp() and TearDown() instead of constructor and destructor - * in order to be able to assert on them. - */ -void SingleStream::SetUp() -{ - Environment *env = Environment::get(); - - camera_ = env->cm()->get(env->cameraId()); - - ASSERT_EQ(camera_->acquire(), 0); -} - -void SingleStream::TearDown() -{ - if (!camera_) - return; - - camera_->release(); - camera_.reset(); -} - -std::string SingleStream::nameParameters(const testing::TestParamInfo<SingleStream::ParamType> &info) -{ - std::map<StreamRole, std::string> rolesMap = { - { StreamRole::Raw, "Raw" }, - { StreamRole::StillCapture, "StillCapture" }, - { StreamRole::VideoRecording, "VideoRecording" }, - { StreamRole::Viewfinder, "Viewfinder" } - }; - - std::string roleName = rolesMap[std::get<0>(info.param)]; - std::string numRequestsName = std::to_string(std::get<1>(info.param)); - - return roleName + "_" + numRequestsName; -} - -/* - * Test single capture cycles - * - * Makes sure the camera completes the exact number of requests queued. Example - * failure is a camera that completes less requests than the number of requests - * queued. - */ -TEST_P(SingleStream, Capture) -{ - auto [role, numRequests] = GetParam(); - - SimpleCaptureBalanced capture(camera_); - - capture.configure(role); - - capture.capture(numRequests); -} - -/* - * Test multiple start/stop cycles - * - * Makes sure the camera supports multiple start/stop cycles. Example failure is - * a camera that does not clean up correctly in its error path but is only - * tested by single-capture applications. - */ -TEST_P(SingleStream, CaptureStartStop) -{ - auto [role, numRequests] = GetParam(); - unsigned int numRepeats = 3; - - SimpleCaptureBalanced capture(camera_); - - capture.configure(role); - - for (unsigned int starts = 0; starts < numRepeats; starts++) - capture.capture(numRequests); -} - -/* - * Test unbalanced stop - * - * Makes sure the camera supports a stop with requests queued. Example failure - * is a camera that does not handle cancelation of buffers coming back from the - * video device while stopping. - */ -TEST_P(SingleStream, UnbalancedStop) -{ - auto [role, numRequests] = GetParam(); - - SimpleCaptureUnbalanced capture(camera_); - - capture.configure(role); - - capture.capture(numRequests); -} - -INSTANTIATE_TEST_SUITE_P(CaptureTests, - SingleStream, - testing::Combine(testing::ValuesIn(ROLES), - testing::ValuesIn(NUMREQUESTS)), - SingleStream::nameParameters); diff --git a/src/apps/lc-compliance/environment.cpp b/src/apps/lc-compliance/environment.cpp index 5eb3775f..987264f1 100644 --- a/src/apps/lc-compliance/environment.cpp +++ b/src/apps/lc-compliance/environment.cpp @@ -2,7 +2,7 @@ /* * Copyright (C) 2021, Collabora Ltd. * - * environment.cpp - Common environment for tests + * Common environment for tests */ #include "environment.h" diff --git a/src/apps/lc-compliance/environment.h b/src/apps/lc-compliance/environment.h index 0debbcce..834c722e 100644 --- a/src/apps/lc-compliance/environment.h +++ b/src/apps/lc-compliance/environment.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2021, Collabora Ltd. * - * environment.h - Common environment for tests + * Common environment for tests */ #pragma once @@ -23,5 +23,5 @@ private: Environment() = default; std::string cameraId_; - libcamera::CameraManager *cm_; + libcamera::CameraManager *cm_ = nullptr; }; diff --git a/src/apps/lc-compliance/helpers/capture.cpp b/src/apps/lc-compliance/helpers/capture.cpp new file mode 100644 index 00000000..2a3fa3b6 --- /dev/null +++ b/src/apps/lc-compliance/helpers/capture.cpp @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020-2021, Google Inc. + * + * Simple capture helper + */ + +#include "capture.h" + +#include <assert.h> + +#include <gtest/gtest.h> + +using namespace libcamera; + +Capture::Capture(std::shared_ptr<Camera> camera) + : camera_(std::move(camera)), allocator_(camera_) +{ +} + +Capture::~Capture() +{ + stop(); +} + +void Capture::configure(libcamera::Span<const libcamera::StreamRole> roles) +{ + assert(!roles.empty()); + + config_ = camera_->generateConfiguration(roles); + if (!config_) + GTEST_SKIP() << "Roles not supported by camera"; + + ASSERT_EQ(config_->size(), roles.size()) << "Unexpected number of streams in configuration"; + + /* + * Set the buffers count to the largest value across all streams. + * \todo: Should all streams from a Camera have the same buffer count ? + */ + auto largest = + std::max_element(config_->begin(), config_->end(), + [](const StreamConfiguration &l, const StreamConfiguration &r) + { return l.bufferCount < r.bufferCount; }); + + assert(largest != config_->end()); + + for (auto &cfg : *config_) + cfg.bufferCount = largest->bufferCount; + + if (config_->validate() != CameraConfiguration::Valid) { + config_.reset(); + FAIL() << "Configuration not valid"; + } + + if (camera_->configure(config_.get())) { + config_.reset(); + FAIL() << "Failed to configure camera"; + } +} + +void Capture::run(unsigned int captureLimit, std::optional<unsigned int> queueLimit) +{ + assert(!queueLimit || captureLimit <= *queueLimit); + + captureLimit_ = captureLimit; + queueLimit_ = queueLimit; + + captureCount_ = queueCount_ = 0; + + EventLoop loop; + loop_ = &loop; + + start(); + + for (const auto &request : requests_) + queueRequest(request.get()); + + EXPECT_EQ(loop_->exec(), 0); + + stop(); + + EXPECT_LE(captureLimit_, captureCount_); + EXPECT_LE(captureCount_, queueCount_); + EXPECT_TRUE(!queueLimit_ || queueCount_ <= *queueLimit_); +} + +int Capture::queueRequest(libcamera::Request *request) +{ + if (queueLimit_ && queueCount_ >= *queueLimit_) + return 0; + + int ret = camera_->queueRequest(request); + if (ret < 0) + return ret; + + queueCount_ += 1; + return 0; +} + +void Capture::requestComplete(Request *request) +{ + captureCount_++; + if (captureCount_ >= captureLimit_) { + loop_->exit(0); + return; + } + + EXPECT_EQ(request->status(), Request::Status::RequestComplete) + << "Request didn't complete successfully"; + + request->reuse(Request::ReuseBuffers); + if (queueRequest(request)) + loop_->exit(-EINVAL); +} + +void Capture::start() +{ + assert(config_); + assert(!config_->empty()); + assert(!allocator_.allocated()); + assert(requests_.empty()); + + const auto bufferCount = config_->at(0).bufferCount; + + /* No point in testing less requests then the camera depth. */ + if (queueLimit_ && *queueLimit_ < bufferCount) { + GTEST_SKIP() << "Camera needs " << bufferCount + << " requests, can't test only " << *queueLimit_; + } + + for (std::size_t i = 0; i < bufferCount; i++) { + std::unique_ptr<Request> request = camera_->createRequest(); + ASSERT_TRUE(request) << "Can't create request"; + requests_.push_back(std::move(request)); + } + + for (const auto &cfg : *config_) { + Stream *stream = cfg.stream(); + + int count = allocator_.allocate(stream); + ASSERT_GE(count, 0) << "Failed to allocate buffers"; + + const auto &buffers = allocator_.buffers(stream); + ASSERT_EQ(buffers.size(), bufferCount) << "Mismatching buffer count"; + + for (std::size_t i = 0; i < bufferCount; i++) { + ASSERT_EQ(requests_[i]->addBuffer(stream, buffers[i].get()), 0) + << "Failed to add buffer to request"; + } + } + + ASSERT_TRUE(allocator_.allocated()); + + camera_->requestCompleted.connect(this, &Capture::requestComplete); + + ASSERT_EQ(camera_->start(), 0) << "Failed to start camera"; +} + +void Capture::stop() +{ + if (!config_ || !allocator_.allocated()) + return; + + camera_->stop(); + + camera_->requestCompleted.disconnect(this); + + requests_.clear(); + + for (const auto &cfg : *config_) { + EXPECT_EQ(allocator_.free(cfg.stream()), 0) + << "Failed to free buffers associated with stream"; + } + + EXPECT_FALSE(allocator_.allocated()); +} diff --git a/src/apps/lc-compliance/helpers/capture.h b/src/apps/lc-compliance/helpers/capture.h new file mode 100644 index 00000000..ea01c11c --- /dev/null +++ b/src/apps/lc-compliance/helpers/capture.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020-2021, Google Inc. + * + * Simple capture helper + */ + +#pragma once + +#include <memory> +#include <optional> + +#include <libcamera/libcamera.h> + +#include "../common/event_loop.h" + +class Capture +{ +public: + Capture(std::shared_ptr<libcamera::Camera> camera); + ~Capture(); + + void configure(libcamera::Span<const libcamera::StreamRole> roles); + void run(unsigned int captureLimit, std::optional<unsigned int> queueLimit = {}); + +private: + LIBCAMERA_DISABLE_COPY_AND_MOVE(Capture) + + void start(); + void stop(); + + int queueRequest(libcamera::Request *request); + void requestComplete(libcamera::Request *request); + + std::shared_ptr<libcamera::Camera> camera_; + libcamera::FrameBufferAllocator allocator_; + std::unique_ptr<libcamera::CameraConfiguration> config_; + std::vector<std::unique_ptr<libcamera::Request>> requests_; + + EventLoop *loop_ = nullptr; + unsigned int captureLimit_ = 0; + std::optional<unsigned int> queueLimit_; + unsigned int captureCount_ = 0; + unsigned int queueCount_ = 0; +}; diff --git a/src/apps/lc-compliance/main.cpp b/src/apps/lc-compliance/main.cpp index 74e0d4df..e9f0ffbb 100644 --- a/src/apps/lc-compliance/main.cpp +++ b/src/apps/lc-compliance/main.cpp @@ -3,7 +3,7 @@ * Copyright (C) 2020, Google Inc. * Copyright (C) 2021, Collabora Ltd. * - * main.cpp - lc-compliance - The libcamera compliance tool + * lc-compliance - The libcamera compliance tool */ #include <iomanip> @@ -45,13 +45,11 @@ class ThrowListener : public testing::EmptyTestEventListener static void listCameras(CameraManager *cm) { for (const std::shared_ptr<Camera> &cam : cm->cameras()) - std::cout << "- " << cam.get()->id() << std::endl; + std::cout << "- " << cam->id() << std::endl; } static int initCamera(CameraManager *cm, OptionsParser::Options options) { - std::shared_ptr<Camera> camera; - int ret = cm->start(); if (ret) { std::cout << "Failed to start camera manager: " @@ -66,7 +64,7 @@ static int initCamera(CameraManager *cm, OptionsParser::Options options) } const std::string &cameraId = options[OptCamera]; - camera = cm->get(cameraId); + std::shared_ptr<Camera> camera = cm->get(cameraId); if (!camera) { std::cout << "Camera " << cameraId << " not found, available cameras:" << std::endl; listCameras(cm); @@ -82,45 +80,27 @@ static int initCamera(CameraManager *cm, OptionsParser::Options options) static int initGtestParameters(char *arg0, OptionsParser::Options options) { - const std::map<std::string, std::string> gtestFlags = { { "list", "--gtest_list_tests" }, - { "filter", "--gtest_filter" } }; - - int argc = 0; + std::vector<const char *> argv; std::string filterParam; - /* - * +2 to have space for both the 0th argument that is needed but not - * used and the null at the end. - */ - char **argv = new char *[(gtestFlags.size() + 2)]; - if (!argv) - return -ENOMEM; - - argv[0] = arg0; - argc++; + argv.push_back(arg0); - if (options.isSet(OptList)) { - argv[argc] = const_cast<char *>(gtestFlags.at("list").c_str()); - argc++; - } + if (options.isSet(OptList)) + argv.push_back("--gtest_list_tests"); if (options.isSet(OptFilter)) { /* * The filter flag needs to be passed as a single parameter, in * the format --gtest_filter=filterStr */ - filterParam = gtestFlags.at("filter") + "=" + - static_cast<const std::string &>(options[OptFilter]); - - argv[argc] = const_cast<char *>(filterParam.c_str()); - argc++; + filterParam = "--gtest_filter=" + options[OptFilter].toString(); + argv.push_back(filterParam.c_str()); } - argv[argc] = nullptr; - - ::testing::InitGoogleTest(&argc, argv); + argv.push_back(nullptr); - delete[] argv; + int argc = argv.size(); + ::testing::InitGoogleTest(&argc, const_cast<char **>(argv.data())); return 0; } diff --git a/src/apps/lc-compliance/meson.build b/src/apps/lc-compliance/meson.build index 51d9075a..80b9a160 100644 --- a/src/apps/lc-compliance/meson.build +++ b/src/apps/lc-compliance/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 -libgtest = dependency('gtest', required : get_option('lc-compliance'), +libgtest = dependency('gtest', version : '>=1.10.0', + required : get_option('lc-compliance'), fallback : ['gtest', 'gtest_dep']) if opt_lc_compliance.disabled() or not libevent.found() or not libgtest.found() @@ -12,9 +13,15 @@ lc_compliance_enabled = true lc_compliance_sources = files([ 'environment.cpp', + 'helpers/capture.cpp', 'main.cpp', - 'simple_capture.cpp', - 'capture_test.cpp', + 'test_base.cpp', + 'tests/capture_test.cpp', +]) + +lc_compliance_includes = ([ + include_directories('.'), + include_directories('helpers/') ]) lc_compliance = executable('lc-compliance', lc_compliance_sources, @@ -26,4 +33,6 @@ lc_compliance = executable('lc-compliance', lc_compliance_sources, libevent, libgtest, ], - install : true) + include_directories : lc_compliance_includes, + install : true, + install_tag : 'bin-devel') diff --git a/src/apps/lc-compliance/simple_capture.cpp b/src/apps/lc-compliance/simple_capture.cpp deleted file mode 100644 index cf4d7cf3..00000000 --- a/src/apps/lc-compliance/simple_capture.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020-2021, Google Inc. - * - * simple_capture.cpp - Simple capture helper - */ - -#include <gtest/gtest.h> - -#include "simple_capture.h" - -using namespace libcamera; - -SimpleCapture::SimpleCapture(std::shared_ptr<Camera> camera) - : loop_(nullptr), camera_(camera), - allocator_(std::make_unique<FrameBufferAllocator>(camera)) -{ -} - -SimpleCapture::~SimpleCapture() -{ - stop(); -} - -void SimpleCapture::configure(StreamRole role) -{ - config_ = camera_->generateConfiguration({ role }); - - if (!config_) { - std::cout << "Role not supported by camera" << std::endl; - GTEST_SKIP(); - } - - if (config_->validate() != CameraConfiguration::Valid) { - config_.reset(); - FAIL() << "Configuration not valid"; - } - - if (camera_->configure(config_.get())) { - config_.reset(); - FAIL() << "Failed to configure camera"; - } -} - -void SimpleCapture::start() -{ - Stream *stream = config_->at(0).stream(); - int count = allocator_->allocate(stream); - - ASSERT_GE(count, 0) << "Failed to allocate buffers"; - EXPECT_EQ(count, config_->at(0).bufferCount) << "Allocated less buffers than expected"; - - camera_->requestCompleted.connect(this, &SimpleCapture::requestComplete); - - ASSERT_EQ(camera_->start(), 0) << "Failed to start camera"; -} - -void SimpleCapture::stop() -{ - if (!config_ || !allocator_->allocated()) - return; - - camera_->stop(); - - camera_->requestCompleted.disconnect(this); - - Stream *stream = config_->at(0).stream(); - requests_.clear(); - allocator_->free(stream); -} - -/* SimpleCaptureBalanced */ - -SimpleCaptureBalanced::SimpleCaptureBalanced(std::shared_ptr<Camera> camera) - : SimpleCapture(camera) -{ -} - -void SimpleCaptureBalanced::capture(unsigned int numRequests) -{ - start(); - - Stream *stream = config_->at(0).stream(); - const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream); - - /* No point in testing less requests then the camera depth. */ - if (buffers.size() > numRequests) { - std::cout << "Camera needs " + std::to_string(buffers.size()) - + " requests, can't test only " - + std::to_string(numRequests) << std::endl; - GTEST_SKIP(); - } - - queueCount_ = 0; - captureCount_ = 0; - captureLimit_ = numRequests; - - /* Queue the recommended number of reqeuests. */ - for (const std::unique_ptr<FrameBuffer> &buffer : buffers) { - std::unique_ptr<Request> request = camera_->createRequest(); - ASSERT_TRUE(request) << "Can't create request"; - - ASSERT_EQ(request->addBuffer(stream, buffer.get()), 0) << "Can't set buffer for request"; - - ASSERT_EQ(queueRequest(request.get()), 0) << "Failed to queue request"; - - requests_.push_back(std::move(request)); - } - - /* Run capture session. */ - loop_ = new EventLoop(); - loop_->exec(); - stop(); - delete loop_; - - ASSERT_EQ(captureCount_, captureLimit_); -} - -int SimpleCaptureBalanced::queueRequest(Request *request) -{ - queueCount_++; - if (queueCount_ > captureLimit_) - return 0; - - return camera_->queueRequest(request); -} - -void SimpleCaptureBalanced::requestComplete(Request *request) -{ - captureCount_++; - if (captureCount_ >= captureLimit_) { - loop_->exit(0); - return; - } - - request->reuse(Request::ReuseBuffers); - if (queueRequest(request)) - loop_->exit(-EINVAL); -} - -/* SimpleCaptureUnbalanced */ - -SimpleCaptureUnbalanced::SimpleCaptureUnbalanced(std::shared_ptr<Camera> camera) - : SimpleCapture(camera) -{ -} - -void SimpleCaptureUnbalanced::capture(unsigned int numRequests) -{ - start(); - - Stream *stream = config_->at(0).stream(); - const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream); - - captureCount_ = 0; - captureLimit_ = numRequests; - - /* Queue the recommended number of reqeuests. */ - for (const std::unique_ptr<FrameBuffer> &buffer : buffers) { - std::unique_ptr<Request> request = camera_->createRequest(); - ASSERT_TRUE(request) << "Can't create request"; - - ASSERT_EQ(request->addBuffer(stream, buffer.get()), 0) << "Can't set buffer for request"; - - ASSERT_EQ(camera_->queueRequest(request.get()), 0) << "Failed to queue request"; - - requests_.push_back(std::move(request)); - } - - /* Run capture session. */ - loop_ = new EventLoop(); - int status = loop_->exec(); - stop(); - delete loop_; - - ASSERT_EQ(status, 0); -} - -void SimpleCaptureUnbalanced::requestComplete(Request *request) -{ - captureCount_++; - if (captureCount_ >= captureLimit_) { - loop_->exit(0); - return; - } - - request->reuse(Request::ReuseBuffers); - if (camera_->queueRequest(request)) - loop_->exit(-EINVAL); -} diff --git a/src/apps/lc-compliance/simple_capture.h b/src/apps/lc-compliance/simple_capture.h deleted file mode 100644 index 2911d601..00000000 --- a/src/apps/lc-compliance/simple_capture.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020-2021, Google Inc. - * - * simple_capture.h - Simple capture helper - */ - -#pragma once - -#include <memory> - -#include <libcamera/libcamera.h> - -#include "../common/event_loop.h" - -class SimpleCapture -{ -public: - void configure(libcamera::StreamRole role); - -protected: - SimpleCapture(std::shared_ptr<libcamera::Camera> camera); - virtual ~SimpleCapture(); - - void start(); - void stop(); - - virtual void requestComplete(libcamera::Request *request) = 0; - - EventLoop *loop_; - - std::shared_ptr<libcamera::Camera> camera_; - std::unique_ptr<libcamera::FrameBufferAllocator> allocator_; - std::unique_ptr<libcamera::CameraConfiguration> config_; - std::vector<std::unique_ptr<libcamera::Request>> requests_; -}; - -class SimpleCaptureBalanced : public SimpleCapture -{ -public: - SimpleCaptureBalanced(std::shared_ptr<libcamera::Camera> camera); - - void capture(unsigned int numRequests); - -private: - int queueRequest(libcamera::Request *request); - void requestComplete(libcamera::Request *request) override; - - unsigned int queueCount_; - unsigned int captureCount_; - unsigned int captureLimit_; -}; - -class SimpleCaptureUnbalanced : public SimpleCapture -{ -public: - SimpleCaptureUnbalanced(std::shared_ptr<libcamera::Camera> camera); - - void capture(unsigned int numRequests); - -private: - void requestComplete(libcamera::Request *request) override; - - unsigned int captureCount_; - unsigned int captureLimit_; -}; diff --git a/src/apps/lc-compliance/test_base.cpp b/src/apps/lc-compliance/test_base.cpp new file mode 100644 index 00000000..c9957b9e --- /dev/null +++ b/src/apps/lc-compliance/test_base.cpp @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021, Collabora Ltd. + * + * test_base.cpp - Base definitions for tests + */ + +#include "test_base.h" + +#include "environment.h" + +void CameraHolder::acquireCamera() +{ + Environment *env = Environment::get(); + + camera_ = env->cm()->get(env->cameraId()); + + ASSERT_EQ(camera_->acquire(), 0); +} + +void CameraHolder::releaseCamera() +{ + if (!camera_) + return; + + camera_->release(); + camera_.reset(); +} diff --git a/src/apps/lc-compliance/test_base.h b/src/apps/lc-compliance/test_base.h new file mode 100644 index 00000000..52347749 --- /dev/null +++ b/src/apps/lc-compliance/test_base.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021, Collabora Ltd. + * + * test_base.h - Base definitions for tests + */ + +#ifndef __LC_COMPLIANCE_TEST_BASE_H__ +#define __LC_COMPLIANCE_TEST_BASE_H__ + +#include <libcamera/libcamera.h> + +#include <gtest/gtest.h> + +class CameraHolder +{ +protected: + void acquireCamera(); + void releaseCamera(); + + std::shared_ptr<libcamera::Camera> camera_; +}; + +#endif /* __LC_COMPLIANCE_TEST_BASE_H__ */ diff --git a/src/apps/lc-compliance/tests/capture_test.cpp b/src/apps/lc-compliance/tests/capture_test.cpp new file mode 100644 index 00000000..29d8b7f8 --- /dev/null +++ b/src/apps/lc-compliance/tests/capture_test.cpp @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * Copyright (C) 2021, Collabora Ltd. + * + * Test camera capture + */ + +#include "capture.h" + +#include <sstream> +#include <string> +#include <tuple> +#include <vector> + +#include <gtest/gtest.h> + +#include "test_base.h" + +namespace { + +using namespace libcamera; + +class SimpleCapture : public testing::TestWithParam<std::tuple<std::vector<StreamRole>, int>>, public CameraHolder +{ +public: + static std::string nameParameters(const testing::TestParamInfo<SimpleCapture::ParamType> &info); + +protected: + void SetUp() override; + void TearDown() override; +}; + +/* + * We use gtest's SetUp() and TearDown() instead of constructor and destructor + * in order to be able to assert on them. + */ +void SimpleCapture::SetUp() +{ + acquireCamera(); +} + +void SimpleCapture::TearDown() +{ + releaseCamera(); +} + +std::string SimpleCapture::nameParameters(const testing::TestParamInfo<SimpleCapture::ParamType> &info) +{ + const auto &[roles, numRequests] = info.param; + std::ostringstream ss; + + for (StreamRole r : roles) + ss << r << '_'; + + ss << '_' << numRequests; + + return ss.str(); +} + +/* + * Test single capture cycles + * + * Makes sure the camera completes the exact number of requests queued. Example + * failure is a camera that completes less requests than the number of requests + * queued. + */ +TEST_P(SimpleCapture, Capture) +{ + const auto &[roles, numRequests] = GetParam(); + + Capture capture(camera_); + + capture.configure(roles); + + capture.run(numRequests, numRequests); +} + +/* + * Test multiple start/stop cycles + * + * Makes sure the camera supports multiple start/stop cycles. Example failure is + * a camera that does not clean up correctly in its error path but is only + * tested by single-capture applications. + */ +TEST_P(SimpleCapture, CaptureStartStop) +{ + const auto &[roles, numRequests] = GetParam(); + unsigned int numRepeats = 3; + + Capture capture(camera_); + + capture.configure(roles); + + for (unsigned int starts = 0; starts < numRepeats; starts++) + capture.run(numRequests, numRequests); +} + +/* + * Test unbalanced stop + * + * Makes sure the camera supports a stop with requests queued. Example failure + * is a camera that does not handle cancelation of buffers coming back from the + * video device while stopping. + */ +TEST_P(SimpleCapture, UnbalancedStop) +{ + const auto &[roles, numRequests] = GetParam(); + + Capture capture(camera_); + + capture.configure(roles); + + capture.run(numRequests); +} + +const int NUMREQUESTS[] = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }; + +const std::vector<StreamRole> SINGLEROLES[] = { + { StreamRole::Raw, }, + { StreamRole::StillCapture, }, + { StreamRole::VideoRecording, }, + { StreamRole::Viewfinder, }, +}; + +const std::vector<StreamRole> MULTIROLES[] = { + { StreamRole::Raw, StreamRole::StillCapture }, + { StreamRole::Raw, StreamRole::VideoRecording }, + { StreamRole::StillCapture, StreamRole::VideoRecording }, + { StreamRole::VideoRecording, StreamRole::VideoRecording }, +}; + +INSTANTIATE_TEST_SUITE_P(SingleStream, + SimpleCapture, + testing::Combine(testing::ValuesIn(SINGLEROLES), + testing::ValuesIn(NUMREQUESTS)), + SimpleCapture::nameParameters); + +INSTANTIATE_TEST_SUITE_P(MultiStream, + SimpleCapture, + testing::Combine(testing::ValuesIn(MULTIROLES), + testing::ValuesIn(NUMREQUESTS)), + SimpleCapture::nameParameters); + +} /* namespace */ |