Show a patch.

GET /api/patches/12349/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 12349,
    "url": "https://patchwork.libcamera.org/api/patches/12349/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/12349/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20210521133054.274502-5-nfraprado@collabora.com>",
    "date": "2021-05-21T13:30:53",
    "name": "[libcamera-devel,v4,4/5] lc-compliance: Refactor using Googletest",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "e028fde109e3df9b645d5ecb206bd7b876b12672",
    "submitter": {
        "id": 84,
        "url": "https://patchwork.libcamera.org/api/people/84/?format=api",
        "name": "Nícolas F. R. A. Prado",
        "email": "nfraprado@collabora.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/12349/mbox/",
    "series": [
        {
            "id": 2044,
            "url": "https://patchwork.libcamera.org/api/series/2044/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2044",
            "date": "2021-05-21T13:30:49",
            "name": "lc-compliance: Refactor using Googletest",
            "version": 4,
            "mbox": "https://patchwork.libcamera.org/series/2044/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/12349/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/12349/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 8EDD9C31FF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 21 May 2021 13:32:01 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 591876891E;\n\tFri, 21 May 2021 15:32:01 +0200 (CEST)",
            "from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 15D5368911\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 21 May 2021 15:32:00 +0200 (CEST)",
            "from localhost.localdomain (unknown\n\t[IPv6:2804:14c:1a9:2978:fc03:8702:eda1:a1f8])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128\n\tbits))\n\t(No client certificate requested) (Authenticated sender: nfraprado)\n\tby bhuna.collabora.co.uk (Postfix) with ESMTPSA id E3F6A1F44082;\n\tFri, 21 May 2021 14:31:57 +0100 (BST)"
        ],
        "From": "=?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= <nfraprado@collabora.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Fri, 21 May 2021 10:30:53 -0300",
        "Message-Id": "<20210521133054.274502-5-nfraprado@collabora.com>",
        "X-Mailer": "git-send-email 2.31.1",
        "In-Reply-To": "<20210521133054.274502-1-nfraprado@collabora.com>",
        "References": "<20210521133054.274502-1-nfraprado@collabora.com>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v4 4/5] lc-compliance: Refactor using\n\tGoogletest",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "Cc": "kernel@collabora.com, =?utf-8?q?Andr=C3=A9_Almeida?=\n\t<andrealmeid@collabora.com>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Refactor lc-compliance using Googletest as the test framework.\n\nSigned-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>\n---\nChanges in v4:\n- Removed old lc-compliance results classes\n- Thanks to Niklas:\n  - Improved naming of tests\n\nChanges in v3:\n- Thanks to Niklas:\n  - Went back to static test registration, and created a Singleton Environment\n    class to store the camera global variable\n- Added a nameParameters() function to give meaningful names to the test\n  parameters, removing the need to register each test suite for every role\n\n src/lc-compliance/main.cpp           |  71 +++++++------\n src/lc-compliance/meson.build        |   4 +-\n src/lc-compliance/results.cpp        |  75 --------------\n src/lc-compliance/results.h          |  47 ---------\n src/lc-compliance/simple_capture.cpp |  74 +++++--------\n src/lc-compliance/simple_capture.h   |   9 +-\n src/lc-compliance/single_stream.cpp  | 150 ++++++++++++++-------------\n src/lc-compliance/tests.h            |  16 ---\n 8 files changed, 152 insertions(+), 294 deletions(-)\n delete mode 100644 src/lc-compliance/results.cpp\n delete mode 100644 src/lc-compliance/results.h\n delete mode 100644 src/lc-compliance/tests.h",
    "diff": "diff --git a/src/lc-compliance/main.cpp b/src/lc-compliance/main.cpp\nindex 54cee54aa978..37884ff70a69 100644\n--- a/src/lc-compliance/main.cpp\n+++ b/src/lc-compliance/main.cpp\n@@ -9,10 +9,12 @@\n #include <iostream>\n #include <string.h>\n \n+#include <gtest/gtest.h>\n+\n #include <libcamera/libcamera.h>\n \n+#include \"environment.h\"\n #include \"../cam/options.h\"\n-#include \"tests.h\"\n \n using namespace libcamera;\n \n@@ -21,21 +23,34 @@ enum {\n \tOptHelp = 'h',\n };\n \n+/*\n+ * Make asserts act like exceptions, otherwise they only fail (or skip) the\n+ * current function. From gtest documentation:\n+ * https://google.github.io/googletest/advanced.html#asserting-on-subroutines-with-an-exception\n+ */\n+class ThrowListener : public testing::EmptyTestEventListener {\n+\tvoid OnTestPartResult(const testing::TestPartResult& result) override {\n+\t\tif (result.type() == testing::TestPartResult::kFatalFailure\n+\t\t    || result.type() == testing::TestPartResult::kSkip) {\n+\t\t\tthrow testing::AssertionException(result);\n+\t\t}\n+\t}\n+};\n+\n class Harness\n {\n public:\n \tHarness(const OptionsParser::Options &options);\n \t~Harness();\n \n-\tint exec();\n+\tint init();\n+\tint run(int argc, char **argv);\n \n private:\n-\tint init();\n \tvoid listCameras();\n \n \tOptionsParser::Options options_;\n \tstd::unique_ptr<CameraManager> cm_;\n-\tstd::shared_ptr<Camera> camera_;\n };\n \n Harness::Harness(const OptionsParser::Options &options)\n@@ -46,35 +61,15 @@ Harness::Harness(const OptionsParser::Options &options)\n \n Harness::~Harness()\n {\n-\tif (camera_) {\n-\t\tcamera_->release();\n-\t\tcamera_.reset();\n-\t}\n+\tEnvironment::instance()->destroy();\n \n \tcm_->stop();\n }\n \n-int Harness::exec()\n-{\n-\tint ret = init();\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tstd::vector<Results> results;\n-\n-\tresults.push_back(testSingleStream(camera_));\n-\n-\tfor (const Results &result : results) {\n-\t\tret = result.summary();\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\t}\n-\n-\treturn 0;\n-}\n-\n int Harness::init()\n {\n+\tstd::shared_ptr<Camera> camera;\n+\n \tint ret = cm_->start();\n \tif (ret) {\n \t\tstd::cout << \"Failed to start camera manager: \"\n@@ -89,23 +84,34 @@ int Harness::init()\n \t}\n \n \tconst std::string &cameraId = options_[OptCamera];\n-\tcamera_ = cm_->get(cameraId);\n-\tif (!camera_) {\n+\tcamera = cm_->get(cameraId);\n+\tif (!camera) {\n \t\tstd::cout << \"Camera \" << cameraId << \" not found, available cameras:\" << std::endl;\n \t\tlistCameras();\n \t\treturn -ENODEV;\n \t}\n \n-\tif (camera_->acquire()) {\n+\tif (camera->acquire()) {\n \t\tstd::cout << \"Failed to acquire camera\" << std::endl;\n \t\treturn -EINVAL;\n \t}\n \n+\tEnvironment::create(camera);\n+\n \tstd::cout << \"Using camera \" << cameraId << std::endl;\n \n \treturn 0;\n }\n \n+int Harness::run(int argc, char **argv)\n+{\n+\t::testing::InitGoogleTest(&argc, argv);\n+\n+\ttesting::UnitTest::GetInstance()->listeners().Append(new ThrowListener);\n+\n+\treturn RUN_ALL_TESTS();\n+}\n+\n void Harness::listCameras()\n {\n \tfor (const std::shared_ptr<Camera> &cam : cm_->cameras())\n@@ -143,6 +149,9 @@ int main(int argc, char **argv)\n \t\treturn EXIT_FAILURE;\n \n \tHarness harness(options);\n+\tret = harness.init();\n+\tif (ret)\n+\t\treturn ret;\n \n-\treturn harness.exec() ? EXIT_FAILURE : EXIT_SUCCESS;\n+\treturn harness.run(argc, argv);\n }\ndiff --git a/src/lc-compliance/meson.build b/src/lc-compliance/meson.build\nindex c287017575bd..ae68844ac9bd 100644\n--- a/src/lc-compliance/meson.build\n+++ b/src/lc-compliance/meson.build\n@@ -13,16 +13,18 @@ lc_compliance_sources = files([\n     '../cam/event_loop.cpp',\n     '../cam/options.cpp',\n     'main.cpp',\n-    'results.cpp',\n     'environment.cpp',\n     'simple_capture.cpp',\n     'single_stream.cpp',\n ])\n \n+gtest_dep = dependency('gtest')\n+\n lc_compliance  = executable('lc-compliance', lc_compliance_sources,\n                             dependencies : [\n                                 libatomic,\n                                 libcamera_dep,\n                                 libevent,\n+                                gtest_dep,\n                             ],\n                             install : true)\ndiff --git a/src/lc-compliance/results.cpp b/src/lc-compliance/results.cpp\ndeleted file mode 100644\nindex f149f7859e28..000000000000\n--- a/src/lc-compliance/results.cpp\n+++ /dev/null\n@@ -1,75 +0,0 @@\n-/* SPDX-License-Identifier: GPL-2.0-or-later */\n-/*\n- * Copyright (C) 2020, Google Inc.\n- *\n- * results.cpp - Test result aggregator\n- */\n-\n-#include \"results.h\"\n-\n-#include <iostream>\n-\n-void Results::add(const Result &result)\n-{\n-\tif (result.first == Pass)\n-\t\tpassed_++;\n-\telse if (result.first == Fail)\n-\t\tfailed_++;\n-\telse if (result.first == Skip)\n-\t\tskipped_++;\n-\n-\tprintResult(result);\n-}\n-\n-void Results::add(Status status, const std::string &message)\n-{\n-\tadd({ status, message });\n-}\n-\n-void Results::fail(const std::string &message)\n-{\n-\tadd(Fail, message);\n-}\n-\n-void Results::pass(const std::string &message)\n-{\n-\tadd(Pass, message);\n-}\n-\n-void Results::skip(const std::string &message)\n-{\n-\tadd(Skip, message);\n-}\n-\n-int Results::summary() const\n-{\n-\tif (failed_ + passed_ + skipped_ != planned_) {\n-\t\tstd::cout << \"Planned and executed number of tests differ \"\n-\t\t\t  << failed_ + passed_ + skipped_ << \" executed \"\n-\t\t\t  << planned_ << \" planned\" << std::endl;\n-\n-\t\treturn -EINVAL;\n-\t}\n-\n-\tstd::cout << planned_ << \" tests executed, \"\n-\t\t  << passed_ << \" tests passed, \"\n-\t\t  << skipped_ << \" tests skipped and \"\n-\t\t  << failed_ << \" tests failed \" << std::endl;\n-\n-\treturn 0;\n-}\n-\n-void Results::printResult(const Result &result)\n-{\n-\tstd::string prefix;\n-\n-\t/* \\todo Make parsable as TAP. */\n-\tif (result.first == Pass)\n-\t\tprefix = \"PASS\";\n-\telse if (result.first == Fail)\n-\t\tprefix = \"FAIL\";\n-\telse if (result.first == Skip)\n-\t\tprefix = \"SKIP\";\n-\n-\tstd::cout << \"- \" << prefix << \": \" << result.second << std::endl;\n-}\ndiff --git a/src/lc-compliance/results.h b/src/lc-compliance/results.h\ndeleted file mode 100644\nindex 2a3722b841a6..000000000000\n--- a/src/lc-compliance/results.h\n+++ /dev/null\n@@ -1,47 +0,0 @@\n-/* SPDX-License-Identifier: GPL-2.0-or-later */\n-/*\n- * Copyright (C) 2020, Google Inc.\n- *\n- * results.h - Test result aggregator\n- */\n-#ifndef __LC_COMPLIANCE_RESULTS_H__\n-#define __LC_COMPLIANCE_RESULTS_H__\n-\n-#include <string>\n-#include <utility>\n-\n-/* \\todo Check if result aggregator can be shared with self tests in test/ */\n-class Results\n-{\n-public:\n-\tenum Status {\n-\t\tFail,\n-\t\tPass,\n-\t\tSkip,\n-\t};\n-\n-\tusing Result = std::pair<Status, std::string>;\n-\n-\tResults(unsigned int planned)\n-\t\t: planned_(planned), passed_(0), failed_(0), skipped_(0)\n-\t{\n-\t}\n-\n-\tvoid add(const Result &result);\n-\tvoid add(Status status, const std::string &message);\n-\tvoid fail(const std::string &message);\n-\tvoid pass(const std::string &message);\n-\tvoid skip(const std::string &message);\n-\n-\tint summary() const;\n-\n-private:\n-\tvoid printResult(const Result &result);\n-\n-\tunsigned int planned_;\n-\tunsigned int passed_;\n-\tunsigned int failed_;\n-\tunsigned int skipped_;\n-};\n-\n-#endif /* __LC_COMPLIANCE_RESULTS_H__ */\ndiff --git a/src/lc-compliance/simple_capture.cpp b/src/lc-compliance/simple_capture.cpp\nindex f90fe6d0f9aa..7731eb16f8c2 100644\n--- a/src/lc-compliance/simple_capture.cpp\n+++ b/src/lc-compliance/simple_capture.cpp\n@@ -5,6 +5,8 @@\n  * simple_capture.cpp - Simple capture helper\n  */\n \n+#include <gtest/gtest.h>\n+\n #include \"simple_capture.h\"\n \n using namespace libcamera;\n@@ -20,38 +22,34 @@ SimpleCapture::~SimpleCapture()\n \tstop();\n }\n \n-Results::Result SimpleCapture::configure(StreamRole role)\n+void SimpleCapture::configure(StreamRole role)\n {\n \tconfig_ = camera_->generateConfiguration({ role });\n \n-\tif (!config_)\n-\t\treturn { Results::Skip, \"Role not supported by camera\" };\n+\tif (!config_) {\n+\t\tstd::cout << \"Role not supported by camera\" << std::endl;\n+\t\tGTEST_SKIP();\n+\t}\n \n \tif (config_->validate() != CameraConfiguration::Valid) {\n \t\tconfig_.reset();\n-\t\treturn { Results::Fail, \"Configuration not valid\" };\n+\t\tFAIL() << \"Configuration not valid\";\n \t}\n \n \tif (camera_->configure(config_.get())) {\n \t\tconfig_.reset();\n-\t\treturn { Results::Fail, \"Failed to configure camera\" };\n+\t\tFAIL() << \"Failed to configure camera\";\n \t}\n-\n-\treturn { Results::Pass, \"Configure camera\" };\n }\n \n-Results::Result SimpleCapture::start()\n+void SimpleCapture::start()\n {\n \tStream *stream = config_->at(0).stream();\n-\tif (allocator_->allocate(stream) < 0)\n-\t\treturn { Results::Fail, \"Failed to allocate buffers\" };\n+\tASSERT_GE(allocator_->allocate(stream), 0) << \"Failed to allocate buffers\";\n \n-\tif (camera_->start())\n-\t\treturn { Results::Fail, \"Failed to start camera\" };\n+\tASSERT_TRUE(!camera_->start()) << \"Failed to start camera\";\n \n \tcamera_->requestCompleted.connect(this, &SimpleCapture::requestComplete);\n-\n-\treturn { Results::Pass, \"Started camera\" };\n }\n \n void SimpleCapture::stop()\n@@ -77,22 +75,19 @@ SimpleCaptureBalanced::SimpleCaptureBalanced(std::shared_ptr<Camera> camera)\n {\n }\n \n-Results::Result SimpleCaptureBalanced::capture(unsigned int numRequests)\n+void SimpleCaptureBalanced::capture(unsigned int numRequests)\n {\n-\tResults::Result ret = start();\n-\tif (ret.first != Results::Pass)\n-\t\treturn ret;\n+\tstart();\n \n \tStream *stream = config_->at(0).stream();\n \tconst std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);\n \n \t/* No point in testing less requests then the camera depth. */\n \tif (buffers.size() > numRequests) {\n-\t\t/* Cache buffers.size() before we destroy it in stop() */\n-\t\tint buffers_size = buffers.size();\n-\n-\t\treturn { Results::Skip, \"Camera needs \" + std::to_string(buffers_size)\n-\t\t\t+ \" requests, can't test only \" + std::to_string(numRequests) };\n+\t\tstd::cout << \"Camera needs \" + std::to_string(buffers.size())\n+\t\t\t+ \" requests, can't test only \"\n+\t\t\t+ std::to_string(numRequests) << std::endl;\n+\t\tGTEST_SKIP();\n \t}\n \n \tqueueCount_ = 0;\n@@ -103,14 +98,11 @@ Results::Result SimpleCaptureBalanced::capture(unsigned int numRequests)\n \tstd::vector<std::unique_ptr<libcamera::Request>> requests;\n \tfor (const std::unique_ptr<FrameBuffer> &buffer : buffers) {\n \t\tstd::unique_ptr<Request> request = camera_->createRequest();\n-\t\tif (!request)\n-\t\t\treturn { Results::Fail, \"Can't create request\" };\n+\t\tASSERT_TRUE(request) << \"Can't create request\";\n \n-\t\tif (request->addBuffer(stream, buffer.get()))\n-\t\t\treturn { Results::Fail, \"Can't set buffer for request\" };\n+\t\tASSERT_FALSE(request->addBuffer(stream, buffer.get())) << \"Can't set buffer for request\";\n \n-\t\tif (queueRequest(request.get()) < 0)\n-\t\t\treturn { Results::Fail, \"Failed to queue request\" };\n+\t\tASSERT_GE(queueRequest(request.get()), 0) << \"Failed to queue request\";\n \n \t\trequests.push_back(std::move(request));\n \t}\n@@ -121,12 +113,7 @@ Results::Result SimpleCaptureBalanced::capture(unsigned int numRequests)\n \tstop();\n \tdelete loop_;\n \n-\tif (captureCount_ != captureLimit_)\n-\t\treturn { Results::Fail, \"Got \" + std::to_string(captureCount_) +\n-\t\t\t\" request, wanted \" + std::to_string(captureLimit_) };\n-\n-\treturn { Results::Pass, \"Balanced capture of \" +\n-\t\tstd::to_string(numRequests) + \" requests\" };\n+\tASSERT_EQ(captureCount_, captureLimit_);\n }\n \n int SimpleCaptureBalanced::queueRequest(Request *request)\n@@ -158,11 +145,9 @@ SimpleCaptureUnbalanced::SimpleCaptureUnbalanced(std::shared_ptr<Camera> camera)\n {\n }\n \n-Results::Result SimpleCaptureUnbalanced::capture(unsigned int numRequests)\n+void SimpleCaptureUnbalanced::capture(unsigned int numRequests)\n {\n-\tResults::Result ret = start();\n-\tif (ret.first != Results::Pass)\n-\t\treturn ret;\n+\tstart();\n \n \tStream *stream = config_->at(0).stream();\n \tconst std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);\n@@ -174,14 +159,11 @@ Results::Result SimpleCaptureUnbalanced::capture(unsigned int numRequests)\n \tstd::vector<std::unique_ptr<libcamera::Request>> requests;\n \tfor (const std::unique_ptr<FrameBuffer> &buffer : buffers) {\n \t\tstd::unique_ptr<Request> request = camera_->createRequest();\n-\t\tif (!request)\n-\t\t\treturn { Results::Fail, \"Can't create request\" };\n+\t\tASSERT_TRUE(request) << \"Can't create request\";\n \n-\t\tif (request->addBuffer(stream, buffer.get()))\n-\t\t\treturn { Results::Fail, \"Can't set buffer for request\" };\n+\t\tASSERT_FALSE(request->addBuffer(stream, buffer.get())) << \"Can't set buffer for request\";\n \n-\t\tif (camera_->queueRequest(request.get()) < 0)\n-\t\t\treturn { Results::Fail, \"Failed to queue request\" };\n+\t\tASSERT_GE(camera_->queueRequest(request.get()), 0) << \"Failed to queue request\";\n \n \t\trequests.push_back(std::move(request));\n \t}\n@@ -192,7 +174,7 @@ Results::Result SimpleCaptureUnbalanced::capture(unsigned int numRequests)\n \tstop();\n \tdelete loop_;\n \n-\treturn { status ? Results::Fail : Results::Pass, \"Unbalanced capture of \" + std::to_string(numRequests) + \" requests\" };\n+\tASSERT_FALSE(status);\n }\n \n void SimpleCaptureUnbalanced::requestComplete(Request *request)\ndiff --git a/src/lc-compliance/simple_capture.h b/src/lc-compliance/simple_capture.h\nindex d9de53fb63a3..62984243a1fa 100644\n--- a/src/lc-compliance/simple_capture.h\n+++ b/src/lc-compliance/simple_capture.h\n@@ -12,18 +12,17 @@\n #include <libcamera/libcamera.h>\n \n #include \"../cam/event_loop.h\"\n-#include \"results.h\"\n \n class SimpleCapture\n {\n public:\n-\tResults::Result configure(libcamera::StreamRole role);\n+\tvoid configure(libcamera::StreamRole role);\n \n protected:\n \tSimpleCapture(std::shared_ptr<libcamera::Camera> camera);\n \tvirtual ~SimpleCapture();\n \n-\tResults::Result start();\n+\tvoid start();\n \tvoid stop();\n \n \tvirtual void requestComplete(libcamera::Request *request) = 0;\n@@ -40,7 +39,7 @@ class SimpleCaptureBalanced : public SimpleCapture\n public:\n \tSimpleCaptureBalanced(std::shared_ptr<libcamera::Camera> camera);\n \n-\tResults::Result capture(unsigned int numRequests);\n+\tvoid capture(unsigned int numRequests);\n \n private:\n \tint queueRequest(libcamera::Request *request);\n@@ -56,7 +55,7 @@ class SimpleCaptureUnbalanced : public SimpleCapture\n public:\n \tSimpleCaptureUnbalanced(std::shared_ptr<libcamera::Camera> camera);\n \n-\tResults::Result capture(unsigned int numRequests);\n+\tvoid capture(unsigned int numRequests);\n \n private:\n \tvoid requestComplete(libcamera::Request *request) override;\ndiff --git a/src/lc-compliance/single_stream.cpp b/src/lc-compliance/single_stream.cpp\nindex 8318b42f42d6..a8ae2f0e355b 100644\n--- a/src/lc-compliance/single_stream.cpp\n+++ b/src/lc-compliance/single_stream.cpp\n@@ -7,91 +7,95 @@\n \n #include <iostream>\n \n+#include <gtest/gtest.h>\n+\n+#include \"environment.h\"\n #include \"simple_capture.h\"\n-#include \"tests.h\"\n \n using namespace libcamera;\n \n-Results::Result testRequestBalance(std::shared_ptr<Camera> camera,\n-\t\t\t\t   StreamRole role, unsigned int startCycles,\n-\t\t\t\t   unsigned int numRequests)\n+const std::vector<int> NUMREQUESTS = {1, 2, 3, 5, 8, 13, 21, 34, 55, 89};\n+const std::vector<StreamRole> ROLES = {Raw, StillCapture, VideoRecording, Viewfinder};\n+\n+class FixedRequestsTest :\n+\tpublic testing::TestWithParam<std::tuple<StreamRole, int>> {\n+};\n+\n+std::string nameParameters(const testing::TestParamInfo<FixedRequestsTest::ParamType>& info)\n {\n+\tstd::map<StreamRole, std::string> rolesMap = {{Raw, \"Raw\"},\n+\t\t\t\t\t\t      {StillCapture, \"StillCapture\"},\n+\t\t\t\t\t\t      {VideoRecording, \"VideoRecording\"},\n+\t\t\t\t\t\t      {Viewfinder, \"Viewfinder\"}};\n+\n+\tstd::string roleName = rolesMap[std::get<0>(info.param)];\n+\tstd::string numRequestsName = std::to_string(std::get<1>(info.param));\n+\n+\treturn roleName + \"_\" + numRequestsName;\n+}\n+\n+/*\n+ * Test single capture cycles\n+ *\n+ * Makes sure the camera completes the exact number of requests queued. Example\n+ * failure is a camera that needs N+M requests queued to complete N requests to\n+ * the application.\n+ */\n+TEST_P(FixedRequestsTest, BalancedSingleCycle)\n+{\n+\tauto [role, numRequests] = GetParam();\n+\tstd::shared_ptr<Camera> camera = Environment::instance()->camera();\n+\n \tSimpleCaptureBalanced capture(camera);\n \n-\tResults::Result ret = capture.configure(role);\n-\tif (ret.first != Results::Pass)\n-\t\treturn ret;\n+\tcapture.configure(role);\n \n-\tfor (unsigned int starts = 0; starts < startCycles; starts++) {\n-\t\tret = capture.capture(numRequests);\n-\t\tif (ret.first != Results::Pass)\n-\t\t\treturn ret;\n-\t}\n+\tcapture.capture(numRequests);\n+};\n \n-\treturn { Results::Pass, \"Balanced capture of \" +\n-\t\tstd::to_string(numRequests) + \" requests with \" +\n-\t\tstd::to_string(startCycles) + \" start cycles\" };\n-}\n+/*\n+ * Test multiple start/stop cycles\n+ *\n+ * Makes sure the camera supports multiple start/stop cycles. Example failure is\n+ * a camera that does not clean up correctly in its error path but is only\n+ * tested by single-capture applications.\n+ */\n+TEST_P(FixedRequestsTest, BalancedMultiCycle)\n+{\n+\tauto [role, numRequests] = GetParam();\n+\tstd::shared_ptr<Camera> camera = Environment::instance()->camera();\n+\tunsigned int numRepeats = 3;\n+\n+\tSimpleCaptureBalanced capture(camera);\n \n-Results::Result testRequestUnbalance(std::shared_ptr<Camera> camera,\n-\t\t\t\t     StreamRole role, unsigned int numRequests)\n+\tcapture.configure(role);\n+\n+\tfor (unsigned int starts = 0; starts < numRepeats; starts++)\n+\t\tcapture.capture(numRequests);\n+};\n+\n+/*\n+ * Test unbalanced stop\n+ *\n+ * Makes sure the camera supports a stop with requests queued. Example failure\n+ * is a camera that does not handle cancelation of buffers coming back from the\n+ * video device while stopping.\n+ */\n+TEST_P(FixedRequestsTest, Unbalanced)\n {\n+\tauto [role, numRequests] = GetParam();\n+\tstd::shared_ptr<Camera> camera = Environment::instance()->camera();\n+\n \tSimpleCaptureUnbalanced capture(camera);\n \n-\tResults::Result ret = capture.configure(role);\n-\tif (ret.first != Results::Pass)\n-\t\treturn ret;\n+\tcapture.configure(role);\n \n-\treturn capture.capture(numRequests);\n-}\n+\tcapture.capture(numRequests);\n+};\n \n-Results testSingleStream(std::shared_ptr<Camera> camera)\n-{\n-\tstatic const std::vector<std::pair<std::string, StreamRole>> roles = {\n-\t\t{ \"raw\", Raw },\n-\t\t{ \"still\", StillCapture },\n-\t\t{ \"video\", VideoRecording },\n-\t\t{ \"viewfinder\", Viewfinder },\n-\t};\n-\tstatic const std::vector<unsigned int> numRequests = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };\n-\n-\tResults results(numRequests.size() * roles.size() * 3);\n-\n-\tfor (const auto &role : roles) {\n-\t\tstd::cout << \"= Test role \" << role.first << std::endl;\n-\t\t/*\n-\t\t * Test single capture cycles\n-\t\t *\n-\t\t * Makes sure the camera completes the exact number of requests queued.\n-\t\t * Example failure is a camera that needs N+M requests queued to\n-\t\t * complete N requests to the application.\n-\t\t */\n-\t\tstd::cout << \"* Test single capture cycles\" << std::endl;\n-\t\tfor (unsigned int num : numRequests)\n-\t\t\tresults.add(testRequestBalance(camera, role.second, 1, num));\n-\n-\t\t/*\n-\t\t * Test multiple start/stop cycles\n-\t\t *\n-\t\t * Makes sure the camera supports multiple start/stop cycles.\n-\t\t * Example failure is a camera that does not clean up correctly in its\n-\t\t * error path but is only tested by single-capture applications.\n-\t\t */\n-\t\tstd::cout << \"* Test multiple start/stop cycles\" << std::endl;\n-\t\tfor (unsigned int num : numRequests)\n-\t\t\tresults.add(testRequestBalance(camera, role.second, 3, num));\n-\n-\t\t/*\n-\t\t * Test unbalanced stop\n-\t\t *\n-\t\t * Makes sure the camera supports a stop with requests queued.\n-\t\t * Example failure is a camera that does not handle cancelation\n-\t\t * of buffers coming back from the video device while stopping.\n-\t\t */\n-\t\tstd::cout << \"* Test unbalanced stop\" << std::endl;\n-\t\tfor (unsigned int num : numRequests)\n-\t\t\tresults.add(testRequestUnbalance(camera, role.second, num));\n-\t}\n-\n-\treturn results;\n-}\n+\n+INSTANTIATE_TEST_SUITE_P(RolesAndRequests,\n+\t\t\t FixedRequestsTest,\n+\t\t\t testing::Combine(testing::ValuesIn(ROLES),\n+\t\t\t\t\t  testing::ValuesIn(NUMREQUESTS)),\n+\t\t\t nameParameters);\ndiff --git a/src/lc-compliance/tests.h b/src/lc-compliance/tests.h\ndeleted file mode 100644\nindex 396605214e4b..000000000000\n--- a/src/lc-compliance/tests.h\n+++ /dev/null\n@@ -1,16 +0,0 @@\n-/* SPDX-License-Identifier: GPL-2.0-or-later */\n-/*\n- * Copyright (C) 2020, Google Inc.\n- *\n- * tests.h - Test modules\n- */\n-#ifndef __LC_COMPLIANCE_TESTS_H__\n-#define __LC_COMPLIANCE_TESTS_H__\n-\n-#include <libcamera/libcamera.h>\n-\n-#include \"results.h\"\n-\n-Results testSingleStream(std::shared_ptr<libcamera::Camera> camera);\n-\n-#endif /* __LC_COMPLIANCE_TESTS_H__ */\n",
    "prefixes": [
        "libcamera-devel",
        "v4",
        "4/5"
    ]
}