Patch Detail
Show a patch.
GET /api/1.1/patches/12191/?format=api
{ "id": 12191, "url": "https://patchwork.libcamera.org/api/1.1/patches/12191/?format=api", "web_url": "https://patchwork.libcamera.org/patch/12191/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/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": "<20210503193307.108607-4-nfraprado@collabora.com>", "date": "2021-05-03T19:33:07", "name": "[libcamera-devel,RFC,v2,3/3] lc-compliance: Refactor using Googletest", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "099461775471e61a1170232dd1902f7cb2b5de9a", "submitter": { "id": 84, "url": "https://patchwork.libcamera.org/api/1.1/people/84/?format=api", "name": "Nícolas F. R. A. Prado", "email": "nfraprado@collabora.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/12191/mbox/", "series": [ { "id": 1996, "url": "https://patchwork.libcamera.org/api/1.1/series/1996/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1996", "date": "2021-05-03T19:33:04", "name": "lc-compliance: Refactor using Googletest", "version": 2, "mbox": "https://patchwork.libcamera.org/series/1996/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/12191/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/12191/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 EF132BDE78\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 3 May 2021 19:34:04 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C192E6891B;\n\tMon, 3 May 2021 21:34:04 +0200 (CEST)", "from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id EE527688AE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 3 May 2021 21:34:03 +0200 (CEST)", "from localhost.localdomain (unknown\n\t[IPv6:2804:14c:1a9:2978:995d:672b:100f:2fd9])\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 C810F1F41E9A;\n\tMon, 3 May 2021 20:34:01 +0100 (BST)" ], "From": "=?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= <nfraprado@collabora.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Mon, 3 May 2021 16:33:07 -0300", "Message-Id": "<20210503193307.108607-4-nfraprado@collabora.com>", "X-Mailer": "git-send-email 2.31.1", "In-Reply-To": "<20210503193307.108607-1-nfraprado@collabora.com>", "References": "<20210503193307.108607-1-nfraprado@collabora.com>", "MIME-Version": "1.0", "Subject": "[libcamera-devel] [RFC PATCH v2 3/3] 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>", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "base64", "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 v2:\n- Changed from static to dynamic test registration\n- Removed -c flag\n\nThere's still an issue with the refactoring that the shared_ptr of the tests\napparently aren't being deleted on time, which causes:\n\n\t[20:26:08.744507935] [103243] ERROR DeviceEnumerator device_enumerator.cpp:165 Removing media device /dev/media0 while still in use\n\t[20:26:08.744548175] [103243] ERROR DeviceEnumerator device_enumerator.cpp:165 Removing media device /dev/media1 while still in use\n\tSegmentation fault (core dumped)\n\nI tried explicitly resetting the shared_ptr on the destructor of the tests but\nthat didn't work. In fact, when just listing the tests, the constructor for the\ntests isn't even called. So I think the issue has to do with the passing of the\ncamera shared pointer to the REGISTER_TESTS() macro through that functor (?):\n\n\t[=]() -> testsuite* { return new testcase(c, r, rq); }); \\\n\nNot sure how to solve this. Any tip would be welcome.\n\n src/lc-compliance/main.cpp | 99 +++++++++---------\n src/lc-compliance/meson.build | 3 +\n src/lc-compliance/simple_capture.cpp | 74 +++++--------\n src/lc-compliance/simple_capture.h | 8 +-\n src/lc-compliance/single_stream.cpp | 151 ++++++++++++++-------------\n src/lc-compliance/tests.h | 15 ++-\n 6 files changed, 175 insertions(+), 175 deletions(-)", "diff": "diff --git a/src/lc-compliance/main.cpp b/src/lc-compliance/main.cpp\nindex 54cee54aa978..add0d7729aec 100644\n--- a/src/lc-compliance/main.cpp\n+++ b/src/lc-compliance/main.cpp\n@@ -9,6 +9,8 @@\n #include <iostream>\n #include <string.h>\n \n+#include <gtest/gtest.h>\n+\n #include <libcamera/libcamera.h>\n \n #include \"../cam/options.h\"\n@@ -17,7 +19,6 @@\n using namespace libcamera;\n \n enum {\n-\tOptCamera = 'c',\n \tOptHelp = 'h',\n };\n \n@@ -28,14 +29,15 @@ public:\n \t~Harness();\n \n \tint exec();\n+\tint init();\n+\tvoid registerTests();\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+\tstd::vector<std::shared_ptr<Camera>> cameras_;\n };\n \n Harness::Harness(const OptionsParser::Options &options)\n@@ -46,33 +48,14 @@ Harness::Harness(const OptionsParser::Options &options)\n \n Harness::~Harness()\n {\n-\tif (camera_) {\n-\t\tcamera_->release();\n-\t\tcamera_.reset();\n+\tfor (auto &c : cameras_) {\n+\t\tc->release();\n+\t\tc.reset();\n \t}\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 \tint ret = cm_->start();\n@@ -82,42 +65,26 @@ int Harness::init()\n \t\treturn ret;\n \t}\n \n-\tif (!options_.isSet(OptCamera)) {\n-\t\tstd::cout << \"No camera specified, available cameras:\" << std::endl;\n-\t\tlistCameras();\n-\t\treturn -ENODEV;\n-\t}\n-\n-\tconst std::string &cameraId = options_[OptCamera];\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+\tfor (auto cam : cm_->cameras()) {\n+\t\tif (cam->acquire()) {\n+\t\t\tstd::cout << \"Failed to acquire camera\" << std::endl;\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tcameras_.push_back(cam);\n \t}\n \n-\tif (camera_->acquire()) {\n-\t\tstd::cout << \"Failed to acquire camera\" << std::endl;\n-\t\treturn -EINVAL;\n-\t}\n-\n-\tstd::cout << \"Using camera \" << cameraId << std::endl;\n-\n \treturn 0;\n }\n \n void Harness::listCameras()\n {\n-\tfor (const std::shared_ptr<Camera> &cam : cm_->cameras())\n-\t\tstd::cout << \"- \" << cam.get()->id() << std::endl;\n+\tfor (const std::shared_ptr<Camera> &c : cm_->cameras())\n+\t\tstd::cout << \"- \" << c.get()->id() << std::endl;\n }\n \n static int parseOptions(int argc, char **argv, OptionsParser::Options *options)\n {\n \tOptionsParser parser;\n-\tparser.addOption(OptCamera, OptionString,\n-\t\t\t \"Specify which camera to operate on, by id\", \"camera\",\n-\t\t\t ArgumentRequired, \"camera\");\n \tparser.addOption(OptHelp, OptionNone, \"Display this help message\",\n \t\t\t \"help\");\n \n@@ -133,6 +100,31 @@ static int parseOptions(int argc, char **argv, OptionsParser::Options *options)\n \treturn 0;\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+\n+void Harness::registerTests() {\n+\tstd::map<StreamRole, std::string> roles = {{Raw, \"Raw\"},\n+\t\t\t\t\t\t {StillCapture, \"Still\"},\n+\t\t\t\t\t\t {VideoRecording, \"Video\"},\n+\t\t\t\t\t\t {Viewfinder, \"Viewfinder\"}};\n+\tstd::vector<int> requests = {1, 2, 3, 5, 8, 13, 21, 34, 55, 89};\n+\n+\tregisterSingleStreamTests(cameras_, roles, requests);\n+}\n+\n int main(int argc, char **argv)\n {\n \tOptionsParser::Options options;\n@@ -143,6 +135,13 @@ 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+\tharness.registerTests();\n \n-\treturn harness.exec() ? EXIT_FAILURE : EXIT_SUCCESS;\n+\t::testing::InitGoogleTest(&argc, argv);\n+\ttesting::UnitTest::GetInstance()->listeners().Append(new ThrowListener);\n+\treturn RUN_ALL_TESTS();\n }\ndiff --git a/src/lc-compliance/meson.build b/src/lc-compliance/meson.build\nindex a2bfcceb1259..704bc18af3e1 100644\n--- a/src/lc-compliance/meson.build\n+++ b/src/lc-compliance/meson.build\n@@ -18,10 +18,13 @@ lc_compliance_sources = files([\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/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..0f8465083456 100644\n--- a/src/lc-compliance/simple_capture.h\n+++ b/src/lc-compliance/simple_capture.h\n@@ -17,13 +17,13 @@\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 +40,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 +56,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..eb6a6f305826 100644\n--- a/src/lc-compliance/single_stream.cpp\n+++ b/src/lc-compliance/single_stream.cpp\n@@ -7,91 +7,94 @@\n \n #include <iostream>\n \n+#include <gtest/gtest.h>\n+\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-{\n-\tSimpleCaptureBalanced capture(camera);\n+class SingleStream : public testing::Test {\n+public:\n+\texplicit SingleStream(std::shared_ptr<Camera> camera, StreamRole role, unsigned int numRequests)\n+\t\t: camera_(camera), role_(role), numRequests_(numRequests) {}\n \n-\tResults::Result ret = capture.configure(role);\n-\tif (ret.first != Results::Pass)\n-\t\treturn ret;\n+protected:\n+\tstd::shared_ptr<Camera> camera_;\n+\tStreamRole role_;\n+\tunsigned int numRequests_;\n+};\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+/*\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+class BalancedSingleCycle : public SingleStream {\n+public:\n+\texplicit BalancedSingleCycle(std::shared_ptr<Camera> camera, StreamRole role, unsigned int numRequests)\n+\t\t: SingleStream(camera, role, numRequests) {}\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+\tvoid TestBody() override {\n+\t\tSimpleCaptureBalanced capture(camera_);\n \n-Results::Result testRequestUnbalance(std::shared_ptr<Camera> camera,\n-\t\t\t\t StreamRole role, unsigned int numRequests)\n-{\n-\tSimpleCaptureUnbalanced capture(camera);\n+\t\tcapture.configure(role_);\n \n-\tResults::Result ret = capture.configure(role);\n-\tif (ret.first != Results::Pass)\n-\t\treturn ret;\n+\t\tcapture.capture(numRequests_);\n+\t};\n+};\n \n-\treturn capture.capture(numRequests);\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+class BalancedMultiCycle : public SingleStream {\n+public:\n+\texplicit BalancedMultiCycle(std::shared_ptr<Camera> camera, StreamRole role, unsigned int numRequests)\n+\t\t: SingleStream(camera, role, numRequests) {}\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+\tvoid TestBody() override {\n+\t\tunsigned int numRepeats = 3;\n+\n+\t\tSimpleCaptureBalanced capture(camera_);\n+\n+\t\tcapture.configure(role_);\n+\n+\t\tfor (unsigned int starts = 0; starts < numRepeats; starts++)\n+\t\t\tcapture.capture(numRequests_);\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+/*\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+class Unbalanced : public SingleStream {\n+public:\n+\texplicit Unbalanced(std::shared_ptr<Camera> camera, StreamRole role, unsigned int numRequests)\n+\t\t: SingleStream(camera, role, numRequests) {}\n+\n+\tvoid TestBody() override {\n+\t\tSimpleCaptureUnbalanced capture(camera_);\n+\n+\t\tcapture.configure(role_);\n+\n+\t\tcapture.capture(numRequests_);\n+\t};\n+};\n+\n+void registerSingleStreamTests(std::vector<std::shared_ptr<Camera>> cameras,\n+\t\tstd::map<StreamRole, std::string> roles, std::vector<int> requests)\n+{\n+\tREGISTER_TESTS(SingleStream, BalancedSingleCycle, cameras, roles, requests);\n+\tREGISTER_TESTS(SingleStream, BalancedMultiCycle, cameras, roles, requests);\n+\tREGISTER_TESTS(SingleStream, Unbalanced, cameras, roles, requests);\n }\ndiff --git a/src/lc-compliance/tests.h b/src/lc-compliance/tests.h\nindex 396605214e4b..6d5a8b88c287 100644\n--- a/src/lc-compliance/tests.h\n+++ b/src/lc-compliance/tests.h\n@@ -11,6 +11,19 @@\n \n #include \"results.h\"\n \n-Results testSingleStream(std::shared_ptr<libcamera::Camera> camera);\n+void registerSingleStreamTests(std::vector<std::shared_ptr<libcamera::Camera>> cameras,\n+\t\tstd::map<libcamera::StreamRole, std::string> roles, std::vector<int> requests);\n+\n+#define REGISTER_TESTS(testsuite, testcase, cameras, roles, requests) \\\n+for (auto c : cameras) { \\\n+\tfor (auto [r, rn] : roles) { \\\n+\t\tfor (auto rq : requests) { \\\n+\t\t\ttesting::RegisterTest( \\\n+\t\t\t\t#testsuite, (std::string(#testcase) + \"/\" + c->id() + \"/\" + rn + \"/\" + std::to_string(rq)).c_str(), \\\n+\t\t\t\tnullptr, nullptr, __FILE__, __LINE__, \\\n+\t\t\t\t[=]() -> testsuite* { return new testcase(c, r, rq); }); \\\n+\t\t} \\\n+\t} \\\n+}\n \n #endif /* __LC_COMPLIANCE_TESTS_H__ */\n", "prefixes": [ "libcamera-devel", "RFC", "v2", "3/3" ] }