From patchwork Fri Jun 25 19:39:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12710 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id AB2D0C321A for ; Fri, 25 Jun 2021 19:39:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7BFC4684CF; Fri, 25 Jun 2021 21:39:52 +0200 (CEST) Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e3e3]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6F783684C9 for ; Fri, 25 Jun 2021 21:39:50 +0200 (CEST) Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:51f6:da24:ae79:702d]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id F1F081F4486D; Fri, 25 Jun 2021 20:39:47 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: libcamera-devel@lists.libcamera.org Date: Fri, 25 Jun 2021 16:39:21 -0300 Message-Id: <20210625193925.517406-2-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210625193925.517406-1-nfraprado@collabora.com> References: <20210625193925.517406-1-nfraprado@collabora.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v9 1/5] libcamera: camera: Make stop() idempotent X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kernel@collabora.com, =?utf-8?q?Andr=C3=A9_Almeida?= Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Make Camera::stop() idempotent so that it can be called in any state and consecutive times. When called in any state other than CameraRunning, it is a no-op. This simplifies the cleanup path for applications. Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- No changes in v9 No changes in v8 No changes in v7 No changes in v6 Changes in v5: - Thanks to Laurent: - Simplified isRunning() Changes in v4: - Thanks to Jacopo: - Added \todo in Camera::stop() No changes in v3 Changes in v2: - Suggested by Kieran: - Add new isRunning() function instead of relying on isAccessAllowed() - Suggested by Laurent: - Make stop()'s description clearer src/libcamera/camera.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 1340c266cc5f..a13a15dd1fc9 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -347,6 +347,7 @@ public: const std::set &streams); ~Private(); + bool isRunning() const; int isAccessAllowed(State state, bool allowDisconnected = false, const char *from = __builtin_FUNCTION()) const; int isAccessAllowed(State low, State high, @@ -388,6 +389,11 @@ static const char *const camera_state_names[] = { "Running", }; +bool Camera::Private::isRunning() const +{ + return state_.load(std::memory_order_acquire) == CameraRunning; +} + int Camera::Private::isAccessAllowed(State state, bool allowDisconnected, const char *from) const { @@ -1061,9 +1067,10 @@ int Camera::start(const ControlList *controls) * This method stops capturing and processing requests immediately. All pending * requests are cancelled and complete synchronously in an error state. * - * \context This function may only be called when the camera is in the Running - * state as defined in \ref camera_operation, and shall be synchronized by the - * caller with other functions that affect the camera state. + * \context This function may be called in any camera state as defined in \ref + * camera_operation, and shall be synchronized by the caller with other + * functions that affect the camera state. If called when the camera isn't + * running, it is a no-op. * * \return 0 on success or a negative error code otherwise * \retval -ENODEV The camera has been disconnected from the system @@ -1073,6 +1080,13 @@ int Camera::stop() { Private *const d = LIBCAMERA_D_PTR(); + /* + * \todo Make calling stop() when not in 'Running' part of the state + * machine rather than take this shortcut + */ + if (!d->isRunning()) + return 0; + int ret = d->isAccessAllowed(Private::CameraRunning); if (ret < 0) return ret; From patchwork Fri Jun 25 19:39:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12711 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id E2FFCC321A for ; Fri, 25 Jun 2021 19:39:53 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A62A9684D2; Fri, 25 Jun 2021 21:39:53 +0200 (CEST) Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6318E684C9 for ; Fri, 25 Jun 2021 21:39:53 +0200 (CEST) Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:51f6:da24:ae79:702d]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id ED7581F44870; Fri, 25 Jun 2021 20:39:50 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: libcamera-devel@lists.libcamera.org Date: Fri, 25 Jun 2021 16:39:22 -0300 Message-Id: <20210625193925.517406-3-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210625193925.517406-1-nfraprado@collabora.com> References: <20210625193925.517406-1-nfraprado@collabora.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v9 2/5] lc-compliance: Make SimpleCapture::stop() idempotent X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kernel@collabora.com, =?utf-8?q?Andr=C3=A9_Almeida?= Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Make SimpleCapture::stop() be able to be called multiple times and at any point so that it can be called from the destructor and an assert failure can return immediately. Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- No changes in v9 No changes in v8 Changes in v7: - Thanks to Jacopo: - Moved the check for buffers allocated to the beginning of SimpleCapture::stop() No changes in v6 No changes in v5 No changes in v4 No changes in v3 Changes in v2: - Suggested by Laurent: - Fixed code style src/lc-compliance/simple_capture.cpp | 30 ++++++++++------------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/lc-compliance/simple_capture.cpp b/src/lc-compliance/simple_capture.cpp index 64e862a08e3a..bfc91b26444e 100644 --- a/src/lc-compliance/simple_capture.cpp +++ b/src/lc-compliance/simple_capture.cpp @@ -17,6 +17,7 @@ SimpleCapture::SimpleCapture(std::shared_ptr camera) SimpleCapture::~SimpleCapture() { + stop(); } Results::Result SimpleCapture::configure(StreamRole role) @@ -55,12 +56,14 @@ Results::Result SimpleCapture::start() void SimpleCapture::stop() { - Stream *stream = config_->at(0).stream(); + if (!config_ || !allocator_->allocated()) + return; camera_->stop(); camera_->requestCompleted.disconnect(this, &SimpleCapture::requestComplete); + Stream *stream = config_->at(0).stream(); allocator_->free(stream); } @@ -84,7 +87,6 @@ Results::Result SimpleCaptureBalanced::capture(unsigned int numRequests) if (buffers.size() > numRequests) { /* Cache buffers.size() before we destroy it in stop() */ int buffers_size = buffers.size(); - stop(); return { Results::Skip, "Camera needs " + std::to_string(buffers_size) + " requests, can't test only " + std::to_string(numRequests) }; @@ -98,20 +100,14 @@ Results::Result SimpleCaptureBalanced::capture(unsigned int numRequests) std::vector> requests; for (const std::unique_ptr &buffer : buffers) { std::unique_ptr request = camera_->createRequest(); - if (!request) { - stop(); + if (!request) return { Results::Fail, "Can't create request" }; - } - if (request->addBuffer(stream, buffer.get())) { - stop(); + if (request->addBuffer(stream, buffer.get())) return { Results::Fail, "Can't set buffer for request" }; - } - if (queueRequest(request.get()) < 0) { - stop(); + if (queueRequest(request.get()) < 0) return { Results::Fail, "Failed to queue request" }; - } requests.push_back(std::move(request)); } @@ -175,20 +171,14 @@ Results::Result SimpleCaptureUnbalanced::capture(unsigned int numRequests) std::vector> requests; for (const std::unique_ptr &buffer : buffers) { std::unique_ptr request = camera_->createRequest(); - if (!request) { - stop(); + if (!request) return { Results::Fail, "Can't create request" }; - } - if (request->addBuffer(stream, buffer.get())) { - stop(); + if (request->addBuffer(stream, buffer.get())) return { Results::Fail, "Can't set buffer for request" }; - } - if (camera_->queueRequest(request.get()) < 0) { - stop(); + if (camera_->queueRequest(request.get()) < 0) return { Results::Fail, "Failed to queue request" }; - } requests.push_back(std::move(request)); } From patchwork Fri Jun 25 19:39:23 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12712 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 0FADCC321A for ; Fri, 25 Jun 2021 19:39:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CD857684D5; Fri, 25 Jun 2021 21:39:57 +0200 (CEST) Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e3e3]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CD2AA684CA for ; Fri, 25 Jun 2021 21:39:56 +0200 (CEST) Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:51f6:da24:ae79:702d]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id 6C0D11F4487C; Fri, 25 Jun 2021 20:39:53 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: libcamera-devel@lists.libcamera.org Date: Fri, 25 Jun 2021 16:39:23 -0300 Message-Id: <20210625193925.517406-4-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210625193925.517406-1-nfraprado@collabora.com> References: <20210625193925.517406-1-nfraprado@collabora.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v9 3/5] lc-compliance: Add Environment singleton X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kernel@collabora.com, =?utf-8?q?Andr=C3=A9_Almeida?= Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a singleton Environment class in order to make the camera available inside all tests. This is needed for the Googletest refactor, otherwise the tests, which are statically declared, won't be able to access the camera. Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- No changes in v9 Changes in v8: - Thanks to Laurent: - Fixed compiling error in Clang - Thanks to Kieran: - Fixed coding style issues Changes in v7: - Thanks to Jacopo: - Fixed style issue - Made CameraManager a unique_ptr and passed to Environment as raw pointer Changes in v6: - Thanks to Niklas: - Made cameraId() return const& Changes in v5: - Thanks to Laurent: - Stored CameraManager and cameraId instead of Camera in Environment - Thanks to Laurent & Niklas: - Improved Environment singleton class instantiation and destruction Added in v4 src/lc-compliance/environment.cpp | 20 ++++++++++++++++++++ src/lc-compliance/environment.h | 31 +++++++++++++++++++++++++++++++ src/lc-compliance/meson.build | 1 + 3 files changed, 52 insertions(+) create mode 100644 src/lc-compliance/environment.cpp create mode 100644 src/lc-compliance/environment.h diff --git a/src/lc-compliance/environment.cpp b/src/lc-compliance/environment.cpp new file mode 100644 index 000000000000..9e24b5e34e4a --- /dev/null +++ b/src/lc-compliance/environment.cpp @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021, Collabora Ltd. + * + * environment.cpp - Common environment for tests + */ + +#include "environment.h" + +Environment *Environment::get() +{ + static Environment instance; + return &instance; +} + +void Environment::setup(CameraManager *cm, std::string cameraId) +{ + cm_ = cm; + cameraId_ = cameraId; +} diff --git a/src/lc-compliance/environment.h b/src/lc-compliance/environment.h new file mode 100644 index 000000000000..1c7d9a558dc2 --- /dev/null +++ b/src/lc-compliance/environment.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021, Collabora Ltd. + * + * environment.h - Common environment for tests + */ +#ifndef __LC_COMPLIANCE_ENVIRONMENT_H__ +#define __LC_COMPLIANCE_ENVIRONMENT_H__ + +#include + +using namespace libcamera; + +class Environment +{ +public: + static Environment *get(); + + void setup(CameraManager *cm, std::string cameraId); + + const std::string &cameraId() const { return cameraId_; } + CameraManager *cm() const { return cm_; } + +private: + Environment() = default; + + std::string cameraId_; + CameraManager *cm_; +}; + +#endif /* __LC_COMPLIANCE_ENVIRONMENT_H__ */ diff --git a/src/lc-compliance/meson.build b/src/lc-compliance/meson.build index a2bfcceb1259..6dd49085569f 100644 --- a/src/lc-compliance/meson.build +++ b/src/lc-compliance/meson.build @@ -12,6 +12,7 @@ lc_compliance_enabled = true lc_compliance_sources = files([ '../cam/event_loop.cpp', '../cam/options.cpp', + 'environment.cpp', 'main.cpp', 'results.cpp', 'simple_capture.cpp', From patchwork Fri Jun 25 19:39:24 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12713 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 9A4A5C321A for ; Fri, 25 Jun 2021 19:40:01 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5E76E684D4; Fri, 25 Jun 2021 21:40:01 +0200 (CEST) Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3315D684CC for ; Fri, 25 Jun 2021 21:40:00 +0200 (CEST) Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:51f6:da24:ae79:702d]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id 4B9E11F44881; Fri, 25 Jun 2021 20:39:57 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: libcamera-devel@lists.libcamera.org Date: Fri, 25 Jun 2021 16:39:24 -0300 Message-Id: <20210625193925.517406-5-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210625193925.517406-1-nfraprado@collabora.com> References: <20210625193925.517406-1-nfraprado@collabora.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v9 4/5] lc-compliance: Refactor using Googletest X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kernel@collabora.com, =?utf-8?q?Andr=C3=A9_Almeida?= Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Refactor lc-compliance using Googletest as the test framework. Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: Niklas Söderlund Reviewed-by: Jacopo Mondi --- Changes in v9: - Thanks to Jacopo: - Removed the Harness class, substituting methods with static helper functions - Added EXPECT() for the number of buffers allocated in SimpleCapture::start() - Changed ASSERT_FALSE() to ASSERT_EQ(..., 0) in SetUp() - Changed tests naming: - TestSuite instance: RolesAndRequests -> CaptureTests - TestSuite: FixedRequestsTest -> SingleStream - TestCase: BalancedSingleCycle -> Capture - TestCase: BalancedMultiCycle -> CaptureStartStop - TestCase: Unbalanced -> UnbalancedStop - Renamed single_stream.cpp to capture_test.cpp Changes in v8: - Thanks to Laurent: - Fixed lc-compliance's meson.build to disable when gtest is not installed - Fixed compiling error in Clang Changes in v7: - Thanks to Jacopo: - Made CameraManager a unique_ptr and passed to Environment as raw pointer No changes in v6 Changes in v5: - Thanks to Laurent: - Fixed style issues - Added SetUp and TearDown functions to acquire and release the camera for each test Changes in v4: - Removed old lc-compliance results classes - Thanks to Niklas: - Improved naming of tests Changes in v3: - Thanks to Niklas: - Went back to static test registration, and created a Singleton Environment class to store the camera global variable - Added a nameParameters() function to give meaningful names to the test parameters, removing the need to register each test suite for every role src/lc-compliance/capture_test.cpp | 127 +++++++++++++++++++++++++++ src/lc-compliance/main.cpp | 106 +++++++++------------- src/lc-compliance/meson.build | 7 +- src/lc-compliance/results.cpp | 75 ---------------- src/lc-compliance/results.h | 47 ---------- src/lc-compliance/simple_capture.cpp | 77 +++++++--------- src/lc-compliance/simple_capture.h | 9 +- src/lc-compliance/single_stream.cpp | 97 -------------------- src/lc-compliance/tests.h | 16 ---- 9 files changed, 206 insertions(+), 355 deletions(-) create mode 100644 src/lc-compliance/capture_test.cpp delete mode 100644 src/lc-compliance/results.cpp delete mode 100644 src/lc-compliance/results.h delete mode 100644 src/lc-compliance/single_stream.cpp delete mode 100644 src/lc-compliance/tests.h diff --git a/src/lc-compliance/capture_test.cpp b/src/lc-compliance/capture_test.cpp new file mode 100644 index 000000000000..116ae496fe04 --- /dev/null +++ b/src/lc-compliance/capture_test.cpp @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * single_stream.cpp - Test a single camera stream + */ + +#include + +#include + +#include "environment.h" +#include "simple_capture.h" + +using namespace libcamera; + +const std::vector NUMREQUESTS = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }; +const std::vector ROLES = { Raw, StillCapture, VideoRecording, Viewfinder }; + +class SingleStream : public testing::TestWithParam> +{ +public: + static std::string nameParameters(const testing::TestParamInfo &info); + +protected: + void SetUp() override; + void TearDown() override; + + std::shared_ptr 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 &info) +{ + std::map rolesMap = { { Raw, "Raw" }, + { StillCapture, "StillCapture" }, + { VideoRecording, "VideoRecording" }, + { 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 needs N+M requests queued to complete N requests to + * the application. + */ +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/lc-compliance/main.cpp b/src/lc-compliance/main.cpp index 54cee54aa978..b01768b5fc0b 100644 --- a/src/lc-compliance/main.cpp +++ b/src/lc-compliance/main.cpp @@ -9,10 +9,12 @@ #include #include +#include + #include +#include "environment.h" #include "../cam/options.h" -#include "tests.h" using namespace libcamera; @@ -21,97 +23,59 @@ enum { OptHelp = 'h', }; -class Harness +/* + * Make asserts act like exceptions, otherwise they only fail (or skip) the + * current function. From gtest documentation: + * https://google.github.io/googletest/advanced.html#asserting-on-subroutines-with-an-exception + */ +class ThrowListener : public testing::EmptyTestEventListener { -public: - Harness(const OptionsParser::Options &options); - ~Harness(); - - int exec(); - -private: - int init(); - void listCameras(); - - OptionsParser::Options options_; - std::unique_ptr cm_; - std::shared_ptr camera_; + void OnTestPartResult(const testing::TestPartResult &result) override + { + if (result.type() == testing::TestPartResult::kFatalFailure || + result.type() == testing::TestPartResult::kSkip) + throw testing::AssertionException(result); + } }; -Harness::Harness(const OptionsParser::Options &options) - : options_(options) -{ - cm_ = std::make_unique(); -} - -Harness::~Harness() +static void listCameras(CameraManager *cm) { - if (camera_) { - camera_->release(); - camera_.reset(); - } - - cm_->stop(); + for (const std::shared_ptr &cam : cm->cameras()) + std::cout << "- " << cam.get()->id() << std::endl; } -int Harness::exec() +static int initCamera(CameraManager *cm, OptionsParser::Options options) { - int ret = init(); - if (ret) - return ret; - - std::vector results; + std::shared_ptr camera; - results.push_back(testSingleStream(camera_)); - - for (const Results &result : results) { - ret = result.summary(); - if (ret) - return ret; - } - - return 0; -} - -int Harness::init() -{ - int ret = cm_->start(); + int ret = cm->start(); if (ret) { std::cout << "Failed to start camera manager: " << strerror(-ret) << std::endl; return ret; } - if (!options_.isSet(OptCamera)) { + if (!options.isSet(OptCamera)) { std::cout << "No camera specified, available cameras:" << std::endl; - listCameras(); + listCameras(cm); return -ENODEV; } - const std::string &cameraId = options_[OptCamera]; - camera_ = cm_->get(cameraId); - if (!camera_) { + const std::string &cameraId = options[OptCamera]; + camera = cm->get(cameraId); + if (!camera) { std::cout << "Camera " << cameraId << " not found, available cameras:" << std::endl; - listCameras(); + listCameras(cm); return -ENODEV; } - if (camera_->acquire()) { - std::cout << "Failed to acquire camera" << std::endl; - return -EINVAL; - } + Environment::get()->setup(cm, cameraId); std::cout << "Using camera " << cameraId << std::endl; return 0; } -void Harness::listCameras() -{ - for (const std::shared_ptr &cam : cm_->cameras()) - std::cout << "- " << cam.get()->id() << std::endl; -} - static int parseOptions(int argc, char **argv, OptionsParser::Options *options) { OptionsParser parser; @@ -142,7 +106,17 @@ int main(int argc, char **argv) if (ret < 0) return EXIT_FAILURE; - Harness harness(options); + std::unique_ptr cm = std::make_unique(); + + ret = initCamera(cm.get(), options); + if (ret) + return ret; + + testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); + + ret = RUN_ALL_TESTS(); + + cm->stop(); - return harness.exec() ? EXIT_FAILURE : EXIT_SUCCESS; + return ret; } diff --git a/src/lc-compliance/meson.build b/src/lc-compliance/meson.build index 6dd49085569f..f3a79ae438a9 100644 --- a/src/lc-compliance/meson.build +++ b/src/lc-compliance/meson.build @@ -1,8 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 libevent = dependency('libevent_pthreads', required : get_option('lc-compliance')) +libgtest = dependency('gtest', required : get_option('lc-compliance')) -if not libevent.found() +if not (libevent.found() and libgtest.found()) lc_compliance_enabled = false subdir_done() endif @@ -14,9 +15,8 @@ lc_compliance_sources = files([ '../cam/options.cpp', 'environment.cpp', 'main.cpp', - 'results.cpp', 'simple_capture.cpp', - 'single_stream.cpp', + 'capture_test.cpp', ]) lc_compliance = executable('lc-compliance', lc_compliance_sources, @@ -24,5 +24,6 @@ lc_compliance = executable('lc-compliance', lc_compliance_sources, libatomic, libcamera_dep, libevent, + libgtest, ], install : true) diff --git a/src/lc-compliance/results.cpp b/src/lc-compliance/results.cpp deleted file mode 100644 index f149f7859e28..000000000000 --- a/src/lc-compliance/results.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020, Google Inc. - * - * results.cpp - Test result aggregator - */ - -#include "results.h" - -#include - -void Results::add(const Result &result) -{ - if (result.first == Pass) - passed_++; - else if (result.first == Fail) - failed_++; - else if (result.first == Skip) - skipped_++; - - printResult(result); -} - -void Results::add(Status status, const std::string &message) -{ - add({ status, message }); -} - -void Results::fail(const std::string &message) -{ - add(Fail, message); -} - -void Results::pass(const std::string &message) -{ - add(Pass, message); -} - -void Results::skip(const std::string &message) -{ - add(Skip, message); -} - -int Results::summary() const -{ - if (failed_ + passed_ + skipped_ != planned_) { - std::cout << "Planned and executed number of tests differ " - << failed_ + passed_ + skipped_ << " executed " - << planned_ << " planned" << std::endl; - - return -EINVAL; - } - - std::cout << planned_ << " tests executed, " - << passed_ << " tests passed, " - << skipped_ << " tests skipped and " - << failed_ << " tests failed " << std::endl; - - return 0; -} - -void Results::printResult(const Result &result) -{ - std::string prefix; - - /* \todo Make parsable as TAP. */ - if (result.first == Pass) - prefix = "PASS"; - else if (result.first == Fail) - prefix = "FAIL"; - else if (result.first == Skip) - prefix = "SKIP"; - - std::cout << "- " << prefix << ": " << result.second << std::endl; -} diff --git a/src/lc-compliance/results.h b/src/lc-compliance/results.h deleted file mode 100644 index 2a3722b841a6..000000000000 --- a/src/lc-compliance/results.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020, Google Inc. - * - * results.h - Test result aggregator - */ -#ifndef __LC_COMPLIANCE_RESULTS_H__ -#define __LC_COMPLIANCE_RESULTS_H__ - -#include -#include - -/* \todo Check if result aggregator can be shared with self tests in test/ */ -class Results -{ -public: - enum Status { - Fail, - Pass, - Skip, - }; - - using Result = std::pair; - - Results(unsigned int planned) - : planned_(planned), passed_(0), failed_(0), skipped_(0) - { - } - - void add(const Result &result); - void add(Status status, const std::string &message); - void fail(const std::string &message); - void pass(const std::string &message); - void skip(const std::string &message); - - int summary() const; - -private: - void printResult(const Result &result); - - unsigned int planned_; - unsigned int passed_; - unsigned int failed_; - unsigned int skipped_; -}; - -#endif /* __LC_COMPLIANCE_RESULTS_H__ */ diff --git a/src/lc-compliance/simple_capture.cpp b/src/lc-compliance/simple_capture.cpp index bfc91b26444e..ce0883672709 100644 --- a/src/lc-compliance/simple_capture.cpp +++ b/src/lc-compliance/simple_capture.cpp @@ -5,6 +5,8 @@ * simple_capture.cpp - Simple capture helper */ +#include + #include "simple_capture.h" using namespace libcamera; @@ -20,38 +22,37 @@ SimpleCapture::~SimpleCapture() stop(); } -Results::Result SimpleCapture::configure(StreamRole role) +void SimpleCapture::configure(StreamRole role) { config_ = camera_->generateConfiguration({ role }); - if (!config_) - return { Results::Skip, "Role not supported by camera" }; + if (!config_) { + std::cout << "Role not supported by camera" << std::endl; + GTEST_SKIP(); + } if (config_->validate() != CameraConfiguration::Valid) { config_.reset(); - return { Results::Fail, "Configuration not valid" }; + FAIL() << "Configuration not valid"; } if (camera_->configure(config_.get())) { config_.reset(); - return { Results::Fail, "Failed to configure camera" }; + FAIL() << "Failed to configure camera"; } - - return { Results::Pass, "Configure camera" }; } -Results::Result SimpleCapture::start() +void SimpleCapture::start() { Stream *stream = config_->at(0).stream(); - if (allocator_->allocate(stream) < 0) - return { Results::Fail, "Failed to allocate buffers" }; + int count = allocator_->allocate(stream); - if (camera_->start()) - return { Results::Fail, "Failed to start camera" }; + 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_TRUE(!camera_->start()) << "Failed to start camera"; - return { Results::Pass, "Started camera" }; + camera_->requestCompleted.connect(this, &SimpleCapture::requestComplete); } void SimpleCapture::stop() @@ -74,22 +75,19 @@ SimpleCaptureBalanced::SimpleCaptureBalanced(std::shared_ptr camera) { } -Results::Result SimpleCaptureBalanced::capture(unsigned int numRequests) +void SimpleCaptureBalanced::capture(unsigned int numRequests) { - Results::Result ret = start(); - if (ret.first != Results::Pass) - return ret; + start(); Stream *stream = config_->at(0).stream(); const std::vector> &buffers = allocator_->buffers(stream); /* No point in testing less requests then the camera depth. */ if (buffers.size() > numRequests) { - /* Cache buffers.size() before we destroy it in stop() */ - int buffers_size = buffers.size(); - - return { Results::Skip, "Camera needs " + std::to_string(buffers_size) - + " requests, can't test only " + std::to_string(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; @@ -100,14 +98,11 @@ Results::Result SimpleCaptureBalanced::capture(unsigned int numRequests) std::vector> requests; for (const std::unique_ptr &buffer : buffers) { std::unique_ptr request = camera_->createRequest(); - if (!request) - return { Results::Fail, "Can't create request" }; + ASSERT_TRUE(request) << "Can't create request"; - if (request->addBuffer(stream, buffer.get())) - return { Results::Fail, "Can't set buffer for request" }; + ASSERT_FALSE(request->addBuffer(stream, buffer.get())) << "Can't set buffer for request"; - if (queueRequest(request.get()) < 0) - return { Results::Fail, "Failed to queue request" }; + ASSERT_GE(queueRequest(request.get()), 0) << "Failed to queue request"; requests.push_back(std::move(request)); } @@ -118,12 +113,7 @@ Results::Result SimpleCaptureBalanced::capture(unsigned int numRequests) stop(); delete loop_; - if (captureCount_ != captureLimit_) - return { Results::Fail, "Got " + std::to_string(captureCount_) + - " request, wanted " + std::to_string(captureLimit_) }; - - return { Results::Pass, "Balanced capture of " + - std::to_string(numRequests) + " requests" }; + ASSERT_EQ(captureCount_, captureLimit_); } int SimpleCaptureBalanced::queueRequest(Request *request) @@ -155,11 +145,9 @@ SimpleCaptureUnbalanced::SimpleCaptureUnbalanced(std::shared_ptr camera) { } -Results::Result SimpleCaptureUnbalanced::capture(unsigned int numRequests) +void SimpleCaptureUnbalanced::capture(unsigned int numRequests) { - Results::Result ret = start(); - if (ret.first != Results::Pass) - return ret; + start(); Stream *stream = config_->at(0).stream(); const std::vector> &buffers = allocator_->buffers(stream); @@ -171,14 +159,11 @@ Results::Result SimpleCaptureUnbalanced::capture(unsigned int numRequests) std::vector> requests; for (const std::unique_ptr &buffer : buffers) { std::unique_ptr request = camera_->createRequest(); - if (!request) - return { Results::Fail, "Can't create request" }; + ASSERT_TRUE(request) << "Can't create request"; - if (request->addBuffer(stream, buffer.get())) - return { Results::Fail, "Can't set buffer for request" }; + ASSERT_FALSE(request->addBuffer(stream, buffer.get())) << "Can't set buffer for request"; - if (camera_->queueRequest(request.get()) < 0) - return { Results::Fail, "Failed to queue request" }; + ASSERT_GE(camera_->queueRequest(request.get()), 0) << "Failed to queue request"; requests.push_back(std::move(request)); } @@ -189,7 +174,7 @@ Results::Result SimpleCaptureUnbalanced::capture(unsigned int numRequests) stop(); delete loop_; - return { status ? Results::Fail : Results::Pass, "Unbalanced capture of " + std::to_string(numRequests) + " requests" }; + ASSERT_FALSE(status); } void SimpleCaptureUnbalanced::requestComplete(Request *request) diff --git a/src/lc-compliance/simple_capture.h b/src/lc-compliance/simple_capture.h index d9de53fb63a3..62984243a1fa 100644 --- a/src/lc-compliance/simple_capture.h +++ b/src/lc-compliance/simple_capture.h @@ -12,18 +12,17 @@ #include #include "../cam/event_loop.h" -#include "results.h" class SimpleCapture { public: - Results::Result configure(libcamera::StreamRole role); + void configure(libcamera::StreamRole role); protected: SimpleCapture(std::shared_ptr camera); virtual ~SimpleCapture(); - Results::Result start(); + void start(); void stop(); virtual void requestComplete(libcamera::Request *request) = 0; @@ -40,7 +39,7 @@ class SimpleCaptureBalanced : public SimpleCapture public: SimpleCaptureBalanced(std::shared_ptr camera); - Results::Result capture(unsigned int numRequests); + void capture(unsigned int numRequests); private: int queueRequest(libcamera::Request *request); @@ -56,7 +55,7 @@ class SimpleCaptureUnbalanced : public SimpleCapture public: SimpleCaptureUnbalanced(std::shared_ptr camera); - Results::Result capture(unsigned int numRequests); + void capture(unsigned int numRequests); private: void requestComplete(libcamera::Request *request) override; diff --git a/src/lc-compliance/single_stream.cpp b/src/lc-compliance/single_stream.cpp deleted file mode 100644 index 8318b42f42d6..000000000000 --- a/src/lc-compliance/single_stream.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020, Google Inc. - * - * single_stream.cpp - Test a single camera stream - */ - -#include - -#include "simple_capture.h" -#include "tests.h" - -using namespace libcamera; - -Results::Result testRequestBalance(std::shared_ptr camera, - StreamRole role, unsigned int startCycles, - unsigned int numRequests) -{ - SimpleCaptureBalanced capture(camera); - - Results::Result ret = capture.configure(role); - if (ret.first != Results::Pass) - return ret; - - for (unsigned int starts = 0; starts < startCycles; starts++) { - ret = capture.capture(numRequests); - if (ret.first != Results::Pass) - return ret; - } - - return { Results::Pass, "Balanced capture of " + - std::to_string(numRequests) + " requests with " + - std::to_string(startCycles) + " start cycles" }; -} - -Results::Result testRequestUnbalance(std::shared_ptr camera, - StreamRole role, unsigned int numRequests) -{ - SimpleCaptureUnbalanced capture(camera); - - Results::Result ret = capture.configure(role); - if (ret.first != Results::Pass) - return ret; - - return capture.capture(numRequests); -} - -Results testSingleStream(std::shared_ptr camera) -{ - static const std::vector> roles = { - { "raw", Raw }, - { "still", StillCapture }, - { "video", VideoRecording }, - { "viewfinder", Viewfinder }, - }; - static const std::vector numRequests = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }; - - Results results(numRequests.size() * roles.size() * 3); - - for (const auto &role : roles) { - std::cout << "= Test role " << role.first << std::endl; - /* - * Test single capture cycles - * - * Makes sure the camera completes the exact number of requests queued. - * Example failure is a camera that needs N+M requests queued to - * complete N requests to the application. - */ - std::cout << "* Test single capture cycles" << std::endl; - for (unsigned int num : numRequests) - results.add(testRequestBalance(camera, role.second, 1, num)); - - /* - * 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. - */ - std::cout << "* Test multiple start/stop cycles" << std::endl; - for (unsigned int num : numRequests) - results.add(testRequestBalance(camera, role.second, 3, num)); - - /* - * 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. - */ - std::cout << "* Test unbalanced stop" << std::endl; - for (unsigned int num : numRequests) - results.add(testRequestUnbalance(camera, role.second, num)); - } - - return results; -} diff --git a/src/lc-compliance/tests.h b/src/lc-compliance/tests.h deleted file mode 100644 index 396605214e4b..000000000000 --- a/src/lc-compliance/tests.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020, Google Inc. - * - * tests.h - Test modules - */ -#ifndef __LC_COMPLIANCE_TESTS_H__ -#define __LC_COMPLIANCE_TESTS_H__ - -#include - -#include "results.h" - -Results testSingleStream(std::shared_ptr camera); - -#endif /* __LC_COMPLIANCE_TESTS_H__ */ From patchwork Fri Jun 25 19:39:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= X-Patchwork-Id: 12714 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 18FAFC321A for ; Fri, 25 Jun 2021 19:40:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D58F6684D9; Fri, 25 Jun 2021 21:40:03 +0200 (CEST) Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e3e3]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E789F684CA for ; Fri, 25 Jun 2021 21:40:02 +0200 (CEST) Received: from localhost.localdomain (unknown [IPv6:2804:14c:1a9:2434:51f6:da24:ae79:702d]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: nfraprado) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id A928F1F4486D; Fri, 25 Jun 2021 20:40:00 +0100 (BST) From: =?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= To: libcamera-devel@lists.libcamera.org Date: Fri, 25 Jun 2021 16:39:25 -0300 Message-Id: <20210625193925.517406-6-nfraprado@collabora.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210625193925.517406-1-nfraprado@collabora.com> References: <20210625193925.517406-1-nfraprado@collabora.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v9 5/5] lc-compliance: Add list and filter parameters X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kernel@collabora.com, =?utf-8?q?Andr=C3=A9_Almeida?= Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a --list parameter that lists all current tests (by mapping to googletest's --gtest_list_tests). Add a --filter 'filterString' parameter that filters the tests to run (by mapping to googletest's --gtest_filter). While at it, add to the help message that further googletest options can be passed through the environment. Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: Niklas Söderlund Reviewed-by: Jacopo Mondi --- Changes in v9: - Thanks to Jacopo: - Fixed some style issues - Added to --help that further Googletest options can be passed as environment variables - Thanks to Paul: - Fixed compiling error on clang Changes in v8: - Thanks to Kieran: - Switched from malloc/free to new/delete in gtest parameter allocation Changes in v7: - Thanks to Niklas: - Removed intermediary filter string variable in Harness::initGtestParameters() Changes in v6: - Thanks to Niklas: - Changed buildGtestParameters() into initGtestParameters() and removed the need for Harness::gtestArgc_ and Harness::gtestArgv_ Changes in v5: - Thanks to Niklas: - Moved buildGtestParameters() inside run() No changes in v4 src/lc-compliance/main.cpp | 76 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/src/lc-compliance/main.cpp b/src/lc-compliance/main.cpp index b01768b5fc0b..45ec70351f2c 100644 --- a/src/lc-compliance/main.cpp +++ b/src/lc-compliance/main.cpp @@ -20,6 +20,8 @@ using namespace libcamera; enum { OptCamera = 'c', + OptList = 'l', + OptFilter = 'f', OptHelp = 'h', }; @@ -76,12 +78,73 @@ static int initCamera(CameraManager *cm, OptionsParser::Options options) return 0; } +static int initGtestParameters(char *arg0, OptionsParser::Options options) +{ + const std::map gtestFlags = { { "list", "--gtest_list_tests" }, + { "filter", "--gtest_filter" } }; + + int argc = 0; + 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++; + + if (options.isSet(OptList)) { + argv[argc] = const_cast(gtestFlags.at("list").c_str()); + argc++; + } + + 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(options[OptFilter]); + + argv[argc] = const_cast(filterParam.c_str()); + argc++; + } + + argv[argc] = nullptr; + + ::testing::InitGoogleTest(&argc, argv); + + delete[] argv; + + return 0; +} + +static int initGtest(char *arg0, OptionsParser::Options options) +{ + int ret = initGtestParameters(arg0, options); + if (ret) + return ret; + + testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); + + return 0; +} + static int parseOptions(int argc, char **argv, OptionsParser::Options *options) { OptionsParser parser; parser.addOption(OptCamera, OptionString, "Specify which camera to operate on, by id", "camera", ArgumentRequired, "camera"); + parser.addOption(OptList, OptionNone, "List all tests and exit", "list"); + parser.addOption(OptFilter, OptionString, + "Specify which tests to run", "filter", + ArgumentRequired, "filter"); parser.addOption(OptHelp, OptionNone, "Display this help message", "help"); @@ -91,6 +154,8 @@ static int parseOptions(int argc, char **argv, OptionsParser::Options *options) if (options->isSet(OptHelp)) { parser.usage(); + std::cerr << "Further options from Googletest can be passed as environment variables" + << std::endl; return -EINTR; } @@ -108,12 +173,17 @@ int main(int argc, char **argv) std::unique_ptr cm = std::make_unique(); - ret = initCamera(cm.get(), options); + /* No need to initialize the camera if we'll just list tests */ + if (!options.isSet(OptList)) { + ret = initCamera(cm.get(), options); + if (ret) + return ret; + } + + ret = initGtest(argv[0], options); if (ret) return ret; - testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); - ret = RUN_ALL_TESTS(); cm->stop();