Patch Detail
Show a patch.
GET /api/patches/11067/?format=api
{ "id": 11067, "url": "https://patchwork.libcamera.org/api/patches/11067/?format=api", "web_url": "https://patchwork.libcamera.org/patch/11067/", "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": "<20210129165336.256739-1-niklas.soderlund@ragnatech.se>", "date": "2021-01-29T16:53:36", "name": "[libcamera-devel] lc-compliance: Add a libcamera compliance tool", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "207760b95f6d8e67a919d7555c40377fd5f95ace", "submitter": { "id": 5, "url": "https://patchwork.libcamera.org/api/people/5/?format=api", "name": "Niklas Söderlund", "email": "niklas.soderlund@ragnatech.se" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/11067/mbox/", "series": [ { "id": 1628, "url": "https://patchwork.libcamera.org/api/series/1628/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1628", "date": "2021-01-29T16:53:36", "name": "[libcamera-devel] lc-compliance: Add a libcamera compliance tool", "version": 1, "mbox": "https://patchwork.libcamera.org/series/1628/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/11067/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/11067/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 F24E7BD808\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 29 Jan 2021 16:53:44 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5040C683B9;\n\tFri, 29 Jan 2021 17:53:44 +0100 (CET)", "from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net\n\t[195.74.38.229])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D0FE6683AB\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 29 Jan 2021 17:53:42 +0100 (CET)", "from bismarck.berto.se (p4fca2458.dip0.t-ipconnect.de\n\t[79.202.36.88])\n\tby bin-vsp-out-03.atm.binero.net (Halon) with ESMTPA\n\tid 88428af7-6252-11eb-b73f-0050569116f7;\n\tFri, 29 Jan 2021 17:53:40 +0100 (CET)" ], "X-Halon-ID": "88428af7-6252-11eb-b73f-0050569116f7", "Authorized-sender": "niklas.soderlund@fsdn.se", "From": "=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 29 Jan 2021 17:53:36 +0100", "Message-Id": "<20210129165336.256739-1-niklas.soderlund@ragnatech.se>", "X-Mailer": "git-send-email 2.30.0", "MIME-Version": "1.0", "Subject": "[libcamera-devel] [PATCH] lc-compliance: Add a libcamera compliance\n\ttool", "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>", "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": "Add a compliance tool to ease testing of cameras on target. In contrast\nto the unit-tests under test/ that aims to test the internal components\nof libcamera the compliance aims to test application use-cases and to\nsome extend the public API.\n\nThis change adds the boilerplate code for a simple framework to write\ntests in as well as two simple test. The tests aims both to demonstrate\nthe tool and to catch real problems. The tests added are:\n\n - Test that if one queues exactly N requests to a camera exactly N\n requests are eventually completed.\n\n - Test that a configured camera can be started and stopped multiple\n times in an attempt to exercise cleanup code paths otherwise not\n often tested with 'cam' for example.\n\nExample pass run on Raspberry Pi:\n\n $ LIBCAMERA_LOG_LEVELS=\"*:ERROR\" lc-compliance -c \"/base/soc/i2c0mux/i2c@1/imx219@10\"\n [2:36:17.672447527] [11896] ERROR V4L2 v4l2_device.cpp:191 'imx219 10-0010': Control 0x009a0922 not found\n [2:36:17.673095289] [11896] ERROR V4L2 v4l2_subdevice.cpp:285 'imx219 10-0010': Unable to get rectangle 2 on pad 0: Invalid argument\n Using camera /base/soc/i2c0mux/i2c@1/imx219@10\n Test single capture cycles\n - SKIP - Camera needs 4 requests, can't test only 1\n - SKIP - Camera needs 4 requests, can't test only 2\n - SKIP - Camera needs 4 requests, can't test only 3\n - PASS - Balanced capture of 5 requests with 1 start cycles\n - PASS - Balanced capture of 8 requests with 1 start cycles\n - PASS - Balanced capture of 13 requests with 1 start cycles\n - PASS - Balanced capture of 21 requests with 1 start cycles\n - PASS - Balanced capture of 34 requests with 1 start cycles\n - PASS - Balanced capture of 55 requests with 1 start cycles\n - PASS - Balanced capture of 89 requests with 1 start cycles\n Test multiple start/stop cycles\n - SKIP - Camera needs 4 requests, can't test only 1\n - SKIP - Camera needs 4 requests, can't test only 2\n - SKIP - Camera needs 4 requests, can't test only 3\n - PASS - Balanced capture of 5 requests with 3 start cycles\n - PASS - Balanced capture of 8 requests with 3 start cycles\n - PASS - Balanced capture of 13 requests with 3 start cycles\n - PASS - Balanced capture of 21 requests with 3 start cycles\n - PASS - Balanced capture of 34 requests with 3 start cycles\n - PASS - Balanced capture of 55 requests with 3 start cycles\n - PASS - Balanced capture of 89 requests with 3 start cycles\n 20 tests executed, 14 tests passed, 6 tests skipped and 0 tests failed\n\nExample fail run on IPU3:\n\n - LIBCAMERA_LOG_LEVELS=\"*:ERROR\" lc-compliance -c \"\\_SB_.PCI0.I2C2.CAM0\"\n - [0:31:36.992763102] [6420] ERROR V4L2 v4l2_device.cpp:191 'ov13858 8-0010': Control 0x009a0922 not found\n - [0:31:36.992815436] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov13858 8-0010': Unable to get rectangle 2 on pad 0: Inappropriate ioctl for device\n - [0:31:36.992847049] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov13858 8-0010': Unable to get rectangle 1 on pad 0: Inappropriate ioctl for device\n - [0:31:36.992869029] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov13858 8-0010': Unable to get rectangle 0 on pad 0: Inappropriate ioctl for device\n - [0:31:36.993152932] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov13858 8-0010': Unable to get rectangle 0 on pad 0: Inappropriate ioctl for device\n - [0:31:36.993172833] [6420] ERROR CameraSensor camera_sensor.cpp:678 'ov13858 8-0010': The analogue crop rectangle has been defaulted to the active area size\n - [0:31:36.993208149] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov13858 8-0010': Unable to get rectangle 0 on pad 0: Inappropriate ioctl for device\n - [0:31:36.993224502] [6420] ERROR CameraSensor camera_sensor.cpp:678 'ov13858 8-0010': The analogue crop rectangle has been defaulted to the active area size\n - [0:31:36.993354770] [6420] ERROR V4L2 v4l2_device.cpp:191 'ov5670 10-0036': Control 0x009a0922 not found\n - [0:31:36.993377622] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov5670 10-0036': Unable to get rectangle 2 on pad 0: Inappropriate ioctl for device\n - [0:31:36.993398093] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov5670 10-0036': Unable to get rectangle 1 on pad 0: Inappropriate ioctl for device\n - [0:31:36.993415611] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov5670 10-0036': Unable to get rectangle 0 on pad 0: Inappropriate ioctl for device\n - [0:31:36.993660192] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov5670 10-0036': Unable to get rectangle 0 on pad 0: Inappropriate ioctl for device\n - [0:31:36.993678492] [6420] ERROR CameraSensor camera_sensor.cpp:678 'ov5670 10-0036': The analogue crop rectangle has been defaulted to the active area size\n - [0:31:36.993724648] [6420] ERROR V4L2 v4l2_subdevice.cpp:285 'ov5670 10-0036': Unable to get rectangle 0 on pad 0: Inappropriate ioctl for device\n - [0:31:36.993741032] [6420] ERROR CameraSensor camera_sensor.cpp:678 'ov5670 10-0036': The analogue crop rectangle has been defaulted to the active area size\n - Using camera \\_SB_.PCI0.I2C2.CAM0\n - Test single capture cycles\n - - SKIP - Camera needs 4 requests, can't test only 1\n - - SKIP - Camera needs 4 requests, can't test only 2\n - - SKIP - Camera needs 4 requests, can't test only 3\n - - PASS - Balanced capture of 5 requests with 1 start cycles\n - - PASS - Balanced capture of 8 requests with 1 start cycles\n - - PASS - Balanced capture of 13 requests with 1 start cycles\n - - PASS - Balanced capture of 21 requests with 1 start cycles\n - - PASS - Balanced capture of 34 requests with 1 start cycles\n - [0:31:41.969783243] [6420] ERROR IPU3 cio2.cpp:264 CIO2 buffer underrun\n <Application lockup here as the pipeline error is not propagated to the application>\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n---\nHi,\n\nI have tested this on RkISP1, RPi and IPU3 on latest master [1]. I plan\nto post separate patches which starts to address some of the problems\nfound while working. A fix for 'cam --captur=N' is coming shortly for\nexample.\n\nI still have to find a good fix for a races found in the\nIPU3 pipeline between the CIO2 and IMGU (as showed above). What I don't\n(yet) have a plan for is how to fix that request queueing errors are not\npropagated in our application facing API leading the test application to\nwait forever to a request the library knows have failed, ideas welcome.\n\n1. 958c80a4f1c28301 (\"android: camera_device: Set AE precapture trigger according to request\")\n---\n src/lc-compliance/main.cpp | 139 ++++++++++++++++++\n src/lc-compliance/meson.build | 24 ++++\n src/lc-compliance/results.cpp | 75 ++++++++++\n src/lc-compliance/results.h | 45 ++++++\n src/lc-compliance/single_stream.cpp | 210 ++++++++++++++++++++++++++++\n src/lc-compliance/tests.h | 16 +++\n src/meson.build | 2 +\n 7 files changed, 511 insertions(+)\n create mode 100644 src/lc-compliance/main.cpp\n create mode 100644 src/lc-compliance/meson.build\n create mode 100644 src/lc-compliance/results.cpp\n create mode 100644 src/lc-compliance/results.h\n create mode 100644 src/lc-compliance/single_stream.cpp\n create mode 100644 src/lc-compliance/tests.h", "diff": "diff --git a/src/lc-compliance/main.cpp b/src/lc-compliance/main.cpp\nnew file mode 100644\nindex 0000000000000000..e1cbce7eac3df2bc\n--- /dev/null\n+++ b/src/lc-compliance/main.cpp\n@@ -0,0 +1,139 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * main.cpp - lc-compliance - The libcamera compliance tool\n+ */\n+\n+#include <iomanip>\n+#include <iostream>\n+#include <string.h>\n+\n+#include <libcamera/libcamera.h>\n+\n+#include \"../cam/options.h\"\n+#include \"tests.h\"\n+\n+using namespace libcamera;\n+\n+class Harness\n+{\n+public:\n+\tHarness();\n+\t~Harness();\n+\n+\tint exec(int argc, char **argv);\n+\n+private:\n+\tenum {\n+\t\tOptCamera = 'c',\n+\t\tOptHelp = 'h',\n+\t};\n+\n+\tint parseOptions(int argc, char **argv);\n+\tint init(int argc, char **argv);\n+\n+\tOptionsParser::Options options_;\n+\tstd::unique_ptr<CameraManager> cm_;\n+\tstd::shared_ptr<Camera> camera_;\n+};\n+\n+Harness::Harness()\n+{\n+\tcm_ = std::make_unique<CameraManager>();\n+}\n+\n+Harness::~Harness()\n+{\n+\tif (camera_) {\n+\t\tcamera_->release();\n+\t\tcamera_.reset();\n+\t}\n+\n+\tcm_->stop();\n+}\n+\n+int Harness::exec(int argc, char **argv)\n+{\n+\tint ret = init(argc, argv);\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(int argc, char **argv)\n+{\n+\tint ret = parseOptions(argc, argv);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = cm_->start();\n+\tif (ret) {\n+\t\tstd::cout << \"Failed to start camera manager: \"\n+\t\t\t << strerror(-ret) << std::endl;\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\tfor (const std::shared_ptr<Camera> &cam : cm_->cameras())\n+\t\t\tstd::cout << \"- \" << cam.get()->id() << std::endl;\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\tfor (const std::shared_ptr<Camera> &cam : cm_->cameras())\n+\t\t\tstd::cout << \"- \" << cam.get()->id() << std::endl;\n+\t\treturn -ENODEV;\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+int Harness::parseOptions(int argc, char **argv)\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+\toptions_ = parser.parse(argc, argv);\n+\tif (!options_.valid())\n+\t\treturn -EINVAL;\n+\n+\tif (options_.empty() || options_.isSet(OptHelp)) {\n+\t\tparser.usage();\n+\t\treturn options_.empty() ? -EINVAL : -EINTR;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int main(int argc, char **argv)\n+{\n+\tHarness harness;\n+\treturn harness.exec(argc, argv) ? EXIT_FAILURE : 0;\n+}\ndiff --git a/src/lc-compliance/meson.build b/src/lc-compliance/meson.build\nnew file mode 100644\nindex 0000000000000000..d700a307c71405b4\n--- /dev/null\n+++ b/src/lc-compliance/meson.build\n@@ -0,0 +1,24 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+libevent = dependency('libevent_pthreads', required : false)\n+\n+if not libevent.found()\n+ warning('libevent_pthreads not found, \\'lc-compliance\\' application will not be compiled')\n+ subdir_done()\n+endif\n+\n+lc_compliance_sources = files([\n+ '../cam/event_loop.cpp',\n+ '../cam/options.cpp',\n+ 'main.cpp',\n+ 'results.cpp',\n+ 'single_stream.cpp',\n+])\n+\n+lc_compliance = executable('lc-compliance', lc_compliance_sources,\n+ dependencies : [\n+ libatomic,\n+ libcamera_dep,\n+ libevent,\n+ ],\n+ install : true)\ndiff --git a/src/lc-compliance/results.cpp b/src/lc-compliance/results.cpp\nnew file mode 100644\nindex 0000000000000000..fb4242bf49a3268b\n--- /dev/null\n+++ b/src/lc-compliance/results.cpp\n@@ -0,0 +1,75 @@\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 numer 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\nnew file mode 100644\nindex 0000000000000000..a02fd5ab46edd62c\n--- /dev/null\n+++ b/src/lc-compliance/results.h\n@@ -0,0 +1,45 @@\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+\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/single_stream.cpp b/src/lc-compliance/single_stream.cpp\nnew file mode 100644\nindex 0000000000000000..d31b6f8d06487a85\n--- /dev/null\n+++ b/src/lc-compliance/single_stream.cpp\n@@ -0,0 +1,210 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * single_stream.cpp - Test a single camera stream\n+ */\n+\n+#include <iostream>\n+\n+#include \"../cam/event_loop.h\"\n+#include \"tests.h\"\n+\n+using namespace libcamera;\n+\n+class SimpleCapture\n+{\n+public:\n+\tSimpleCapture(std::shared_ptr<Camera> camera)\n+\t\t: camera_(camera), allocator_(std::make_unique<FrameBufferAllocator>(camera))\n+\t{\n+\t}\n+\n+\tResults::Result configure(StreamRole role);\n+\tResults::Result start();\n+\tResults::Result capture(unsigned int numRequests);\n+\tResults::Result stop();\n+\n+private:\n+\tint queueRequest(Request *request);\n+\tvoid requestComplete(Request *request);\n+\n+\tstd::shared_ptr<libcamera::Camera> camera_;\n+\tstd::unique_ptr<FrameBufferAllocator> allocator_;\n+\tstd::unique_ptr<libcamera::CameraConfiguration> config_;\n+\n+\tEventLoop *loop_;\n+\tunsigned int queueCount_;\n+\tunsigned int captureCount_;\n+\tunsigned int captureLimit_;\n+};\n+\n+Results::Result SimpleCapture::configure(StreamRole role)\n+{\n+\tconfig_ = camera_->generateConfiguration({ role });\n+\n+\tif (config_->validate() != CameraConfiguration::Valid) {\n+\t\tconfig_.reset();\n+\t\treturn { Results::Fail, \"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}\n+\n+\treturn { Results::Pass, \"Configure camera\" };\n+}\n+\n+Results::Result 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 camera\" };\n+\n+\tif (camera_->start())\n+\t\treturn { Results::Fail, \"Failed to start camera\" };\n+\n+\tcamera_->requestCompleted.connect(this, &SimpleCapture::requestComplete);\n+\n+\treturn { Results::Pass, \"Started camera\" };\n+}\n+\n+Results::Result SimpleCapture::capture(unsigned int numRequests)\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\treturn { Results::Skip, \"Camera needs \" + std::to_string(buffers.size()) + \" requests, can't test only \" + std::to_string(numRequests) };\n+\n+\tqueueCount_ = 0;\n+\tcaptureCount_ = 0;\n+\tcaptureLimit_ = numRequests;\n+\n+\t/* Queue the camera recommended number of reqeuests. */\n+\tstd::vector<std::unique_ptr<libcamera::Request>> requests;\n+\tfor (const std::unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {\n+\t\tstd::unique_ptr<Request> request = camera_->createRequest();\n+\t\tif (!request)\n+\t\t\treturn { Results::Fail, \"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+\n+\t\tif (queueRequest(request.get()) < 0)\n+\t\t\treturn { Results::Fail, \"Failed to queue request\" };\n+\n+\t\trequests.push_back(std::move(request));\n+\t}\n+\n+\t/* Run capture session. */\n+\tloop_ = new EventLoop();\n+\tloop_->exec();\n+\tdelete loop_;\n+\n+\tif (captureCount_ != captureLimit_)\n+\t\treturn { Results::Fail, \"Got \" + std::to_string(captureCount_) + \" request, wanted \" + std::to_string(captureLimit_) };\n+\n+\treturn { Results::Pass, \"Balanced capture of \" + std::to_string(numRequests) + \" requests\" };\n+}\n+\n+Results::Result SimpleCapture::stop()\n+{\n+\tStream *stream = config_->at(0).stream();\n+\n+\tcamera_->stop();\n+\n+\tcamera_->requestCompleted.disconnect(this, &SimpleCapture::requestComplete);\n+\n+\tallocator_->free(stream);\n+\n+\treturn { Results::Pass, \"Stopped camera\" };\n+}\n+\n+int SimpleCapture::queueRequest(Request *request)\n+{\n+\tqueueCount_++;\n+\tif (queueCount_ > captureLimit_)\n+\t\treturn 0;\n+\n+\treturn camera_->queueRequest(request);\n+}\n+\n+void SimpleCapture::requestComplete(Request *request)\n+{\n+\tcaptureCount_++;\n+\tif (captureCount_ >= captureLimit_) {\n+\t\tloop_->exit(0);\n+\t\treturn;\n+\t}\n+\n+\trequest->reuse(Request::ReuseBuffers);\n+\tif (queueRequest(request))\n+\t\tloop_->exit(-EINVAL);\n+}\n+\n+Results::Result testRequestBalance(std::shared_ptr<Camera> camera,\n+\t\t\t\t unsigned int startCycles,\n+\t\t\t\t unsigned int numRequests)\n+{\n+\tSimpleCapture capture(camera);\n+\tResults::Result ret;\n+\n+\tret = capture.configure(Viewfinder);\n+\tif (ret.first != Results::Pass)\n+\t\treturn ret;\n+\n+\tfor (unsigned int starts = 0; starts < startCycles; starts++) {\n+\t\tret = capture.start();\n+\t\tif (ret.first != Results::Pass)\n+\t\t\treturn ret;\n+\n+\t\tret = capture.capture(numRequests);\n+\t\tif (ret.first != Results::Pass) {\n+\t\t\tcapture.stop();\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = capture.stop();\n+\t\tif (ret.first != Results::Pass)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn { Results::Pass, \"Balanced capture of \" + std::to_string(numRequests) + \" requests with \" + std::to_string(startCycles) + \" start cycles\" };\n+}\n+\n+Results testSingleStream(std::shared_ptr<Camera> camera)\n+{\n+\tconst std::vector<unsigned int> numRequests = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };\n+\n+\tResults results(numRequests.size() * 2);\n+\n+\tif (!camera)\n+\t\treturn results;\n+\n+\t/*\n+\t * Test single capture cycles\n+\t *\n+\t * Makes sure the camera completes the exact number of requests queued.\n+\t * Example failure is a camera that needs N+M requests queued to\n+\t * complete N requests to the application.\n+\t */\n+\tstd::cout << \"Test single capture cycles\" << std::endl;\n+\tfor (unsigned int num : numRequests)\n+\t\tresults.add(testRequestBalance(camera, 1, num));\n+\n+\t/*\n+\t * Test multiple start/stop cycles\n+\t *\n+\t * Makes sure the camera supports multiple start/stop cycles.\n+\t * Example failure is a camera that does not clean up correctly in its\n+\t * error path but is only tested by single-capture applications.\n+\t */\n+\tstd::cout << \"Test multiple start/stop cycles\" << std::endl;\n+\tfor (unsigned int num : numRequests)\n+\t\tresults.add(testRequestBalance(camera, 3, num));\n+\n+\treturn results;\n+}\ndiff --git a/src/lc-compliance/tests.h b/src/lc-compliance/tests.h\nnew file mode 100644\nindex 0000000000000000..396605214e4b8980\n--- /dev/null\n+++ b/src/lc-compliance/tests.h\n@@ -0,0 +1,16 @@\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__ */\ndiff --git a/src/meson.build b/src/meson.build\nindex 4b75f05878bcb702..a8e1af7adf2ca9c8 100644\n--- a/src/meson.build\n+++ b/src/meson.build\n@@ -18,6 +18,8 @@ subdir('android')\n subdir('libcamera')\n subdir('ipa')\n \n+subdir('lc-compliance')\n+\n subdir('cam')\n subdir('qcam')\n \n", "prefixes": [ "libcamera-devel" ] }