[{"id":17549,"web_url":"https://patchwork.libcamera.org/comment/17549/","msgid":"<YMgeaSJk/zncOAkI@pendragon.ideasonboard.com>","date":"2021-06-15T03:28:41","subject":"Re: [libcamera-devel] [PATCH v7 4/5] lc-compliance: Refactor using\n\tGoogletest","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Nícolas,\n\nThank you for the patch.\n\nOn Thu, Jun 10, 2021 at 03:37:31PM -0300, Nícolas F. R. A. Prado wrote:\n> Refactor lc-compliance using Googletest as the test framework.\n> \n> Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>\n> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> ---\n> Changes in v7:\n> - Thanks to Jacopo:\n>   - Made CameraManager a unique_ptr and passed to Environment as raw pointer\n> \n> No changes in v6\n> \n> Changes in v5:\n> - Thanks to Laurent:\n>   - Fixed style issues\n>   - Added SetUp and TearDown functions to acquire and release the camera for\n>     each test\n> \n> Changes in v4:\n> - Removed old lc-compliance results classes\n> - Thanks to Niklas:\n>   - Improved naming of tests\n> \n> Changes 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           |  73 +++++------\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  | 174 ++++++++++++++++-----------\n>  src/lc-compliance/tests.h            |  16 ---\n>  8 files changed, 175 insertions(+), 297 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\n> \n> diff --git a/src/lc-compliance/main.cpp b/src/lc-compliance/main.cpp\n> index 54cee54aa978..8c18845141de 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,35 @@ 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> +{\n> +\tvoid OnTestPartResult(const testing::TestPartResult &result) override\n> +\t{\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}\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 +62,13 @@ 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> -\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 +83,29 @@ 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> -\t\tstd::cout << \"Failed to acquire camera\" << std::endl;\n> -\t\treturn -EINVAL;\n> -\t}\n> +\tEnvironment::get()->setup(cm_.get(), cameraId);\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 +143,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>  }\n> diff --git a/src/lc-compliance/meson.build b/src/lc-compliance/meson.build\n> index 6dd49085569f..156b938ee9ad 100644\n> --- a/src/lc-compliance/meson.build\n> +++ b/src/lc-compliance/meson.build\n> @@ -14,15 +14,17 @@ lc_compliance_sources = files([\n>      '../cam/options.cpp',\n>      'environment.cpp',\n>      'main.cpp',\n> -    'results.cpp',\n>      'simple_capture.cpp',\n>      'single_stream.cpp',\n>  ])\n>  \n> +libgtest = dependency('gtest')\n\nThis will cause configuration to fail if gtest isn't found. As\nlc-compliance is a feature option, the auto case needs to be handled\ncorrectly. You can see at the top of this file how the dependency on\nlibevent is handled, and do the same for gtest.\n\n> +\n>  lc_compliance  = executable('lc-compliance', lc_compliance_sources,\n>                              dependencies : [\n>                                  libatomic,\n>                                  libcamera_dep,\n>                                  libevent,\n> +                                libgtest,\n>                              ],\n>                              install : true)\n> diff --git a/src/lc-compliance/results.cpp b/src/lc-compliance/results.cpp\n> deleted file mode 100644\n> index 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> -}\n> diff --git a/src/lc-compliance/results.h b/src/lc-compliance/results.h\n> deleted file mode 100644\n> index 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__ */\n> diff --git a/src/lc-compliance/simple_capture.cpp b/src/lc-compliance/simple_capture.cpp\n> index bfc91b26444e..d5a9d9e8cebe 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> @@ -74,22 +72,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> @@ -100,14 +95,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> @@ -118,12 +110,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> @@ -155,11 +142,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> @@ -171,14 +156,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> @@ -189,7 +171,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)\n> diff --git a/src/lc-compliance/simple_capture.h b/src/lc-compliance/simple_capture.h\n> index 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;\n> diff --git a/src/lc-compliance/single_stream.cpp b/src/lc-compliance/single_stream.cpp\n> index 8318b42f42d6..837e17a1c189 100644\n> --- a/src/lc-compliance/single_stream.cpp\n> +++ b/src/lc-compliance/single_stream.cpp\n> @@ -7,91 +7,121 @@\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 : public testing::TestWithParam<std::tuple<StreamRole, int>>\n>  {\n> -\tSimpleCaptureBalanced capture(camera);\n> +public:\n> +\tstatic std::string nameParameters(const testing::TestParamInfo<FixedRequestsTest::ParamType> &info);\n>  \n> -\tResults::Result ret = capture.configure(role);\n> -\tif (ret.first != Results::Pass)\n> -\t\treturn ret;\n> +protected:\n> +\tvoid SetUp() override;\n> +\tvoid TearDown() override;\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> +\tstd::shared_ptr<Camera> camera_;\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> + * We use gtest's SetUp() and TearDown() instead of constructor and destructor\n> + * in order to be able to assert on them.\n> + */\n> +void FixedRequestsTest::SetUp()\n> +{\n> +\tEnvironment *env = Environment::get();\n> +\n> +\tcamera_ = env->cm()->get(env->cameraId());\n> +\n> +\tASSERT_FALSE(camera_->acquire());\n>  }\n>  \n> -Results::Result testRequestUnbalance(std::shared_ptr<Camera> camera,\n> -\t\t\t\t     StreamRole role, unsigned int numRequests)\n> +void FixedRequestsTest::TearDown()\n>  {\n> -\tSimpleCaptureUnbalanced capture(camera);\n> +\tif (!camera_)\n> +\t\treturn;\n>  \n> -\tResults::Result ret = capture.configure(role);\n> -\tif (ret.first != Results::Pass)\n> -\t\treturn ret;\n> -\n> -\treturn capture.capture(numRequests);\n> +\tcamera_->release();\n> +\tcamera_.reset();\n>  }\n>  \n> -Results testSingleStream(std::shared_ptr<Camera> camera)\n> +std::string FixedRequestsTest::nameParameters(const testing::TestParamInfo<FixedRequestsTest::ParamType> &info)\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> +\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> +\n> +\tSimpleCaptureBalanced capture(camera_);\n> +\n> +\tcapture.configure(role);\n> +\n> +\tcapture.capture(numRequests);\n> +};\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> +\tunsigned int numRepeats = 3;\n> +\n> +\tSimpleCaptureBalanced capture(camera_);\n> +\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> +\n> +\tSimpleCaptureUnbalanced capture(camera_);\n> +\n> +\tcapture.configure(role);\n> +\n> +\tcapture.capture(numRequests);\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 FixedRequestsTest::nameParameters);\n> diff --git a/src/lc-compliance/tests.h b/src/lc-compliance/tests.h\n> deleted file mode 100644\n> index 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__ */","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 CFA90BD78E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 15 Jun 2021 03:29:04 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 45CD868946;\n\tTue, 15 Jun 2021 05:29:04 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A07A86050D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 15 Jun 2021 05:29:02 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 04B983E5;\n\tTue, 15 Jun 2021 05:29:01 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"u0QgEzA1\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1623727742;\n\tbh=zGgVsTBD7kZslhDxxZS9cdHCUoxmf5HFGoeu9SakfaU=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=u0QgEzA1C5FVV8EzqRMSqT3TE/L5hcDja5eHlJ0pmPIAsvME7lmK9/5aLOhc+1p2P\n\t+x9AhrXiJLxJ73ODojmXtpFRwNYuov5CCg/Zi0JrWz5UPqVqbOv4FsmKWjkA9WjStZ\n\t4lSAHwSaL/yIzXx3ccfbsmR2f1F+CB9dTVpFQM/E=","Date":"Tue, 15 Jun 2021 06:28:41 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4=?= Prado <nfraprado@collabora.com>","Message-ID":"<YMgeaSJk/zncOAkI@pendragon.ideasonboard.com>","References":"<20210610183732.174697-1-nfraprado@collabora.com>\n\t<20210610183732.174697-5-nfraprado@collabora.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20210610183732.174697-5-nfraprado@collabora.com>","Subject":"Re: [libcamera-devel] [PATCH v7 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":"libcamera-devel@lists.libcamera.org, kernel@collabora.com, =?utf-8?q?A?=\n\t=?utf-8?b?bmRyw6k=?= Almeida <andrealmeid@collabora.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":17551,"web_url":"https://patchwork.libcamera.org/comment/17551/","msgid":"<YMghQlHJEBxC3NpY@pendragon.ideasonboard.com>","date":"2021-06-15T03:40:50","subject":"Re: [libcamera-devel] [PATCH v7 4/5] lc-compliance: Refactor using\n\tGoogletest","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"A few other comments:\n\nOn Tue, Jun 15, 2021 at 06:28:43AM +0300, Laurent Pinchart wrote:\n> Hi Nícolas,\n> \n> Thank you for the patch.\n> \n> On Thu, Jun 10, 2021 at 03:37:31PM -0300, Nícolas F. R. A. Prado wrote:\n> > Refactor lc-compliance using Googletest as the test framework.\n> > \n> > Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>\n> > Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> > ---\n> > Changes in v7:\n> > - Thanks to Jacopo:\n> >   - Made CameraManager a unique_ptr and passed to Environment as raw pointer\n> > \n> > No changes in v6\n> > \n> > Changes in v5:\n> > - Thanks to Laurent:\n> >   - Fixed style issues\n> >   - Added SetUp and TearDown functions to acquire and release the camera for\n> >     each test\n> > \n> > Changes in v4:\n> > - Removed old lc-compliance results classes\n> > - Thanks to Niklas:\n> >   - Improved naming of tests\n> > \n> > Changes 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           |  73 +++++------\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  | 174 ++++++++++++++++-----------\n> >  src/lc-compliance/tests.h            |  16 ---\n> >  8 files changed, 175 insertions(+), 297 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\n> > \n> > diff --git a/src/lc-compliance/main.cpp b/src/lc-compliance/main.cpp\n> > index 54cee54aa978..8c18845141de 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,35 @@ 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> > +{\n> > +\tvoid OnTestPartResult(const testing::TestPartResult &result) override\n> > +\t{\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}\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 +62,13 @@ 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> > -\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 +83,29 @@ 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> > -\t\tstd::cout << \"Failed to acquire camera\" << std::endl;\n> > -\t\treturn -EINVAL;\n> > -\t}\n> > +\tEnvironment::get()->setup(cm_.get(), cameraId);\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 +143,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> >  }\n> > diff --git a/src/lc-compliance/meson.build b/src/lc-compliance/meson.build\n> > index 6dd49085569f..156b938ee9ad 100644\n> > --- a/src/lc-compliance/meson.build\n> > +++ b/src/lc-compliance/meson.build\n> > @@ -14,15 +14,17 @@ lc_compliance_sources = files([\n> >      '../cam/options.cpp',\n> >      'environment.cpp',\n> >      'main.cpp',\n> > -    'results.cpp',\n> >      'simple_capture.cpp',\n> >      'single_stream.cpp',\n> >  ])\n> >  \n> > +libgtest = dependency('gtest')\n> \n> This will cause configuration to fail if gtest isn't found. As\n> lc-compliance is a feature option, the auto case needs to be handled\n> correctly. You can see at the top of this file how the dependency on\n> libevent is handled, and do the same for gtest.\n> \n> > +\n> >  lc_compliance  = executable('lc-compliance', lc_compliance_sources,\n> >                              dependencies : [\n> >                                  libatomic,\n> >                                  libcamera_dep,\n> >                                  libevent,\n> > +                                libgtest,\n> >                              ],\n> >                              install : true)\n> > diff --git a/src/lc-compliance/results.cpp b/src/lc-compliance/results.cpp\n> > deleted file mode 100644\n> > index 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> > -}\n> > diff --git a/src/lc-compliance/results.h b/src/lc-compliance/results.h\n> > deleted file mode 100644\n> > index 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__ */\n> > diff --git a/src/lc-compliance/simple_capture.cpp b/src/lc-compliance/simple_capture.cpp\n> > index bfc91b26444e..d5a9d9e8cebe 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> > @@ -74,22 +72,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> > @@ -100,14 +95,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> > @@ -118,12 +110,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> > @@ -155,11 +142,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> > @@ -171,14 +156,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> > @@ -189,7 +171,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)\n> > diff --git a/src/lc-compliance/simple_capture.h b/src/lc-compliance/simple_capture.h\n> > index 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;\n> > diff --git a/src/lc-compliance/single_stream.cpp b/src/lc-compliance/single_stream.cpp\n> > index 8318b42f42d6..837e17a1c189 100644\n> > --- a/src/lc-compliance/single_stream.cpp\n> > +++ b/src/lc-compliance/single_stream.cpp\n> > @@ -7,91 +7,121 @@\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 : public testing::TestWithParam<std::tuple<StreamRole, int>>\n> >  {\n> > -\tSimpleCaptureBalanced capture(camera);\n> > +public:\n> > +\tstatic std::string nameParameters(const testing::TestParamInfo<FixedRequestsTest::ParamType> &info);\n> >  \n> > -\tResults::Result ret = capture.configure(role);\n> > -\tif (ret.first != Results::Pass)\n> > -\t\treturn ret;\n> > +protected:\n> > +\tvoid SetUp() override;\n> > +\tvoid TearDown() override;\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> > +\tstd::shared_ptr<Camera> camera_;\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> > + * We use gtest's SetUp() and TearDown() instead of constructor and destructor\n> > + * in order to be able to assert on them.\n> > + */\n> > +void FixedRequestsTest::SetUp()\n> > +{\n> > +\tEnvironment *env = Environment::get();\n> > +\n> > +\tcamera_ = env->cm()->get(env->cameraId());\n> > +\n> > +\tASSERT_FALSE(camera_->acquire());\n> >  }\n> >  \n> > -Results::Result testRequestUnbalance(std::shared_ptr<Camera> camera,\n> > -\t\t\t\t     StreamRole role, unsigned int numRequests)\n> > +void FixedRequestsTest::TearDown()\n> >  {\n> > -\tSimpleCaptureUnbalanced capture(camera);\n> > +\tif (!camera_)\n> > +\t\treturn;\n> >  \n> > -\tResults::Result ret = capture.configure(role);\n> > -\tif (ret.first != Results::Pass)\n> > -\t\treturn ret;\n> > -\n> > -\treturn capture.capture(numRequests);\n> > +\tcamera_->release();\n> > +\tcamera_.reset();\n> >  }\n> >  \n> > -Results testSingleStream(std::shared_ptr<Camera> camera)\n> > +std::string FixedRequestsTest::nameParameters(const testing::TestParamInfo<FixedRequestsTest::ParamType> &info)\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> > +\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> > +\n> > +\tSimpleCaptureBalanced capture(camera_);\n> > +\n> > +\tcapture.configure(role);\n> > +\n> > +\tcapture.capture(numRequests);\n> > +};\n\nWhen compiling with clang,\n\n../../src/lc-compliance/single_stream.cpp:83:2: error: extra ';' outside of a function is incompatible with C++98 [-Werror,-Wc++98-compat-extra-semi]\n};\n ^\n../../src/lc-compliance/single_stream.cpp:103:2: error: extra ';' outside of a function is incompatible with C++98 [-Werror,-Wc++98-compat-extra-semi]\n};\n ^\n../../src/lc-compliance/single_stream.cpp:121:2: error: extra ';' outside of a function is incompatible with C++98 [-Werror,-Wc++98-compat-extra-semi]\n};\n ^\n3 errors generated.\n\nThere's another more annoying issue:\n\n[15/79] Linking target src/lc-compliance/lc-compliance\nFAILED: src/lc-compliance/lc-compliance\nclang++-10  -o src/lc-compliance/lc-compliance src/lc-compliance/lc-compliance.p/.._cam_event_loop.cpp.o src/lc-compliance/lc-compliance.p/.._cam_options.cpp.o src/lc-compliance/lc-compliance.p/environment.cpp.o src/lc-compliance/lc-compliance.p/main.cpp.o src/lc-compliance/lc-compliance.p/simple_capture.cpp.o src/lc-compliance/lc-compliance.p/single_stream.cpp.o -Wl,--as-needed -Wl,--no-undefined -stdlib=libc++ -Wextra-semi -Wshadow -include config.h -Wno-c99-designator '-Wl,-rpath,$ORIGIN/../libcamera' -Wl,-rpath-link,src/libcamera -Wl,--start-group src/libcamera/libcamera.so -latomic /usr/lib64/libevent_pthreads.so /usr/lib64/libevent.so /usr/lib64/libgtest.so -lpthread -Wl,--end-group\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: src/lc-compliance/lc-compliance.p/single_stream.cpp.o: in function `FixedRequestsTest::SetUp()':\n../../src/lc-compliance/single_stream.cpp:42: undefined reference to `testing::internal::GetBoolAssertionFailureMessage(testing::AssertionResult const&, char const*, char const*, char const*)'\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: src/lc-compliance/lc-compliance.p/single_stream.cpp.o: in function `testing::internal::ParameterizedTestSuiteInfo<FixedRequestsTest>::RegisterTests()':\n/usr/include/gtest/internal/gtest-param-util.h:591: undefined reference to `testing::Message::GetString() const'\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: /usr/include/gtest/internal/gtest-param-util.h:604: undefined reference to `testing::internal::InsertSyntheticTestCase(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, testing::internal::CodeLocation, bool)'\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: src/lc-compliance/lc-compliance.p/simple_capture.cpp.o: in function `SimpleCapture::start()':\n../../src/lc-compliance/simple_capture.cpp:50: undefined reference to `testing::internal::GetBoolAssertionFailureMessage(testing::AssertionResult const&, char const*, char const*, char const*)'\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: src/lc-compliance/lc-compliance.p/simple_capture.cpp.o: in function `SimpleCaptureBalanced::capture(unsigned int)':\n../../src/lc-compliance/simple_capture.cpp:98: undefined reference to `testing::internal::GetBoolAssertionFailureMessage(testing::AssertionResult const&, char const*, char const*, char const*)'\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: ../../src/lc-compliance/simple_capture.cpp:100: undefined reference to `testing::internal::GetBoolAssertionFailureMessage(testing::AssertionResult const&, char const*, char const*, char const*)'\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: src/lc-compliance/lc-compliance.p/simple_capture.cpp.o: in function `SimpleCaptureUnbalanced::capture(unsigned int)':\n../../src/lc-compliance/simple_capture.cpp:159: undefined reference to `testing::internal::GetBoolAssertionFailureMessage(testing::AssertionResult const&, char const*, char const*, char const*)'\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: ../../src/lc-compliance/simple_capture.cpp:161: undefined reference to `testing::internal::GetBoolAssertionFailureMessage(testing::AssertionResult const&, char const*, char const*, char const*)'\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: src/lc-compliance/lc-compliance.p/simple_capture.cpp.o:../../src/lc-compliance/simple_capture.cpp:174: more undefined references to `testing::internal::GetBoolAssertionFailureMessage(testing::AssertionResult const&, char const*, char const*, char const*)' follow\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: src/lc-compliance/lc-compliance.p/simple_capture.cpp.o: in function `testing::AssertionResult::AppendMessage(testing::Message const&)':\n/usr/include/gtest/gtest.h:357: undefined reference to `testing::Message::GetString() const'\n/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../x86_64-pc-linux-gnu/bin/ld: src/lc-compliance/lc-compliance.p/simple_capture.cpp.o: in function `testing::AssertionResult testing::internal::CmpHelperEQFailure<unsigned int, unsigned int>(char const*, char const*, unsigned int const&, unsigned int const&)':\n/usr/include/gtest/gtest.h:1524: undefined reference to `testing::internal::EqFailure(char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)'\nclang-10: error: linker command failed with exit code 1 (use -v to see invocation)\n\nI expect this to be caused by libcamera linking to libc++ while gtest is\ncompiled (on my system) against libstdc++. I don't think we can do much\nabout it, so let's ignore it for now. The semicolon issue should still\nbe fixed.\n\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> > +\tunsigned int numRepeats = 3;\n> > +\n> > +\tSimpleCaptureBalanced capture(camera_);\n> > +\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> > +\n> > +\tSimpleCaptureUnbalanced capture(camera_);\n> > +\n> > +\tcapture.configure(role);\n> > +\n> > +\tcapture.capture(numRequests);\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 FixedRequestsTest::nameParameters);\n> > diff --git a/src/lc-compliance/tests.h b/src/lc-compliance/tests.h\n> > deleted file mode 100644\n> > index 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__ */","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 C82A2BD78E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 15 Jun 2021 03:41:12 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2C67468946;\n\tTue, 15 Jun 2021 05:41:12 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B94DD6050D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 15 Jun 2021 05:41:10 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 22D16436;\n\tTue, 15 Jun 2021 05:41:10 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Lj6sQhpy\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1623728470;\n\tbh=Iqby/HTXEaZPjlUrhtK2Dps7zHh2zwRwGYl6u+jrZPs=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Lj6sQhpy1cQJVsdpUDNmYWsqhOLvbgs1P+YIhg6ZVK5+T4YGVSfWZ8AfN6ESg5dM5\n\tQ53l7hvCtgmwxt1xbb1sFmYowbok8eC+zRaIKts8q10swjCObfDCgIVHRyV161biyo\n\tcLxTDh4Fu2ke60dJAvLK3jnMI8jdHkhDWus+PuuA=","Date":"Tue, 15 Jun 2021 06:40:50 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4=?= Prado <nfraprado@collabora.com>","Message-ID":"<YMghQlHJEBxC3NpY@pendragon.ideasonboard.com>","References":"<20210610183732.174697-1-nfraprado@collabora.com>\n\t<20210610183732.174697-5-nfraprado@collabora.com>\n\t<YMgeaSJk/zncOAkI@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<YMgeaSJk/zncOAkI@pendragon.ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v7 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":"libcamera-devel@lists.libcamera.org, kernel@collabora.com, =?utf-8?q?A?=\n\t=?utf-8?b?bmRyw6k=?= Almeida <andrealmeid@collabora.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]