Patch Detail
Show a patch.
GET /api/patches/2319/?format=api
{ "id": 2319, "url": "https://patchwork.libcamera.org/api/patches/2319/?format=api", "web_url": "https://patchwork.libcamera.org/patch/2319/", "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": "<20191108205409.18845-24-laurent.pinchart@ideasonboard.com>", "date": "2019-11-08T20:54:08", "name": "[libcamera-devel,v2,23/24] test: ipa: Add IPA wrappers test", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "321fe6a8aac0c63da3201456e2fefd872d6c4e02", "submitter": { "id": 2, "url": "https://patchwork.libcamera.org/api/people/2/?format=api", "name": "Laurent Pinchart", "email": "laurent.pinchart@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/2319/mbox/", "series": [ { "id": 568, "url": "https://patchwork.libcamera.org/api/series/568/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=568", "date": "2019-11-08T20:53:45", "name": "Control serialization and IPA C API", "version": 2, "mbox": "https://patchwork.libcamera.org/series/568/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/2319/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/2319/checks/", "tags": {}, "headers": { "Return-Path": "<laurent.pinchart@ideasonboard.com>", "Received": [ "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8A5F26154E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 8 Nov 2019 21:54:30 +0100 (CET)", "from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2CAD92D1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 8 Nov 2019 21:54:30 +0100 (CET)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1573246470;\n\tbh=ujs8C1zgbvG3p71JM9xJG+kgcNXVKvfDHbRY/bbudAw=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=IjScbwtWZFfLK63HdfKoue23I3xTxENi0QzrwwzD3jv+HMOyoDWXN0TnIW5U8jB4L\n\ty0DPNAJrX4AqFkhWiuOjn7PDlg8X3w3E59wSkTmO8E4czNkixUeInteVwAdVzOBhs3\n\tGs+K3luY4kciWPUr5+p6ihZl2oS1BaVSPlUOwkS8=", "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 8 Nov 2019 22:54:08 +0200", "Message-Id": "<20191108205409.18845-24-laurent.pinchart@ideasonboard.com>", "X-Mailer": "git-send-email 2.23.0", "In-Reply-To": "<20191108205409.18845-1-laurent.pinchart@ideasonboard.com>", "References": "<20191108205409.18845-1-laurent.pinchart@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v2 23/24] test: ipa: Add IPA wrappers test", "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>", "X-List-Received-Date": "Fri, 08 Nov 2019 20:54:30 -0000" }, "content": "Wrap an IPAInterface in an IPAInterfaceWrapper in an IPAContextWrapper,\nand verify that the translation between C and C++ APIs work correctly.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n test/ipa/ipa_wrappers_test.cpp | 390 +++++++++++++++++++++++++++++++++\n test/ipa/meson.build | 5 +-\n 2 files changed, 393 insertions(+), 2 deletions(-)\n create mode 100644 test/ipa/ipa_wrappers_test.cpp", "diff": "diff --git a/test/ipa/ipa_wrappers_test.cpp b/test/ipa/ipa_wrappers_test.cpp\nnew file mode 100644\nindex 000000000000..c0717f0e301b\n--- /dev/null\n+++ b/test/ipa/ipa_wrappers_test.cpp\n@@ -0,0 +1,390 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipa_wrappers_test.cpp - Test the IPA interface and context wrappers\n+ */\n+\n+#include <fcntl.h>\n+#include <iostream>\n+#include <memory>\n+#include <linux/videodev2.h>\n+#include <sys/stat.h>\n+#include <unistd.h>\n+\n+#include <libcamera/controls.h>\n+#include <libipa/ipa_interface_wrapper.h>\n+\n+#include \"device_enumerator.h\"\n+#include \"ipa_context_wrapper.h\"\n+#include \"media_device.h\"\n+#include \"utils.h\"\n+#include \"v4l2_subdevice.h\"\n+\n+#include \"test.h\"\n+\n+using namespace libcamera;\n+using namespace std;\n+\n+enum Operation {\n+\tOp_init,\n+\tOp_configure,\n+\tOp_mapBuffers,\n+\tOp_unmapBuffers,\n+\tOp_processEvent,\n+};\n+\n+class TestIPAInterface : public IPAInterface\n+{\n+public:\n+\tTestIPAInterface()\n+\t\t: sequence_(0)\n+\t{\n+\t}\n+\n+\tint init() override\n+\t{\n+\t\treport(Op_init, TestPass);\n+\t\treturn 0;\n+\t}\n+\n+\tvoid configure(const std::map<unsigned int, IPAStream> &streamConfig,\n+\t\t const std::map<unsigned int, const ControlInfoMap &> &entityControls) override\n+\t{\n+\t\t/* Verify streamConfig. */\n+\t\tif (streamConfig.size() != 2) {\n+\t\t\tcerr << \"configure(): Invalid number of streams \"\n+\t\t\t << streamConfig.size() << endl;\n+\t\t\treturn report(Op_configure, TestFail);\n+\t\t}\n+\n+\t\tauto iter = streamConfig.find(1);\n+\t\tif (iter == streamConfig.end()) {\n+\t\t\tcerr << \"configure(): No configuration for stream 1\" << endl;\n+\t\t\treturn report(Op_configure, TestFail);\n+\t\t}\n+\t\tconst IPAStream *stream = &iter->second;\n+\t\tif (stream->pixelFormat != V4L2_PIX_FMT_YUYV ||\n+\t\t stream->size != Size{ 1024, 768 }) {\n+\t\t\tcerr << \"configure(): Invalid configuration for stream 1\" << endl;\n+\t\t\treturn report(Op_configure, TestFail);\n+\t\t}\n+\n+\t\titer = streamConfig.find(2);\n+\t\tif (iter == streamConfig.end()) {\n+\t\t\tcerr << \"configure(): No configuration for stream 2\" << endl;\n+\t\t\treturn report(Op_configure, TestFail);\n+\t\t}\n+\t\tstream = &iter->second;\n+\t\tif (stream->pixelFormat != V4L2_PIX_FMT_NV12 ||\n+\t\t stream->size != Size{ 800, 600 }) {\n+\t\t\tcerr << \"configure(): Invalid configuration for stream 2\" << endl;\n+\t\t\treturn report(Op_configure, TestFail);\n+\t\t}\n+\n+\t\t/* Verify entityControls. */\n+\t\tauto ctrlIter = entityControls.find(42);\n+\t\tif (ctrlIter == entityControls.end()) {\n+\t\t\tcerr << \"configure(): Controls not found\" << endl;\n+\t\t\treturn report(Op_configure, TestFail);\n+\t\t}\n+\n+\t\tconst ControlInfoMap &infoMap = ctrlIter->second;\n+\n+\t\tif (infoMap.count(V4L2_CID_BRIGHTNESS) != 1 ||\n+\t\t infoMap.count(V4L2_CID_CONTRAST) != 1 ||\n+\t\t infoMap.count(V4L2_CID_SATURATION) != 1) {\n+\t\t\tcerr << \"configure(): Invalid control IDs\" << endl;\n+\t\t\treturn report(Op_configure, TestFail);\n+\t\t}\n+\n+\t\treport(Op_configure, TestPass);\n+\t}\n+\n+\tvoid mapBuffers(const std::vector<IPABuffer> &buffers) override\n+\t{\n+\t\tif (buffers.size() != 2) {\n+\t\t\tcerr << \"mapBuffers(): Invalid number of buffers \"\n+\t\t\t << buffers.size() << endl;\n+\t\t\treturn report(Op_mapBuffers, TestFail);\n+\t\t}\n+\n+\t\tif (buffers[0].id != 10 ||\n+\t\t buffers[1].id != 11) {\n+\t\t\tcerr << \"mapBuffers(): Invalid buffer IDs\" << endl;\n+\t\t\treturn report(Op_mapBuffers, TestFail);\n+\t\t}\n+\n+\t\tif (buffers[0].memory.planes().size() != 3 ||\n+\t\t buffers[1].memory.planes().size() != 3) {\n+\t\t\tcerr << \"mapBuffers(): Invalid number of planes\" << endl;\n+\t\t\treturn report(Op_mapBuffers, TestFail);\n+\t\t}\n+\n+\t\tif (buffers[0].memory.planes()[0].length() != 4096 ||\n+\t\t buffers[0].memory.planes()[1].length() != 0 ||\n+\t\t buffers[0].memory.planes()[2].length() != 0 ||\n+\t\t buffers[0].memory.planes()[0].length() != 4096 ||\n+\t\t buffers[1].memory.planes()[1].length() != 4096 ||\n+\t\t buffers[1].memory.planes()[2].length() != 0) {\n+\t\t\tcerr << \"mapBuffers(): Invalid length\" << endl;\n+\t\t\treturn report(Op_mapBuffers, TestFail);\n+\t\t}\n+\n+\t\tif (buffers[0].memory.planes()[0].dmabuf() == -1 ||\n+\t\t buffers[0].memory.planes()[1].dmabuf() != -1 ||\n+\t\t buffers[0].memory.planes()[2].dmabuf() != -1 ||\n+\t\t buffers[0].memory.planes()[0].dmabuf() == -1 ||\n+\t\t buffers[1].memory.planes()[1].dmabuf() == -1 ||\n+\t\t buffers[1].memory.planes()[2].dmabuf() != -1) {\n+\t\t\tcerr << \"mapBuffers(): Invalid dmabuf\" << endl;\n+\t\t\treturn report(Op_mapBuffers, TestFail);\n+\t\t}\n+\n+\t\treport(Op_mapBuffers, TestPass);\n+\t}\n+\n+\tvoid unmapBuffers(const std::vector<unsigned int> &ids) override\n+\t{\n+\t\tif (ids.size() != 2) {\n+\t\t\tcerr << \"unmapBuffers(): Invalid number of ids \"\n+\t\t\t << ids.size() << endl;\n+\t\t\treturn report(Op_unmapBuffers, TestFail);\n+\t\t}\n+\n+\t\tif (ids[0] != 10 || ids[1] != 11) {\n+\t\t\tcerr << \"unmapBuffers(): Invalid buffer IDs\" << endl;\n+\t\t\treturn report(Op_unmapBuffers, TestFail);\n+\t\t}\n+\n+\t\treport(Op_unmapBuffers, TestPass);\n+\t}\n+\n+\tvoid processEvent(const IPAOperationData &data) override\n+\t{\n+\t\t/* Verify operation and data. */\n+\t\tif (data.operation != Op_processEvent) {\n+\t\t\tcerr << \"processEvent(): Invalid operation \"\n+\t\t\t << data.operation << endl;\n+\t\t\treturn report(Op_processEvent, TestFail);\n+\t\t}\n+\n+\t\tif (data.data != std::vector<unsigned int>{ 1, 2, 3, 4 }) {\n+\t\t\tcerr << \"processEvent(): Invalid data\" << endl;\n+\t\t\treturn report(Op_processEvent, TestFail);\n+\t\t}\n+\n+\t\t/* Verify controls. */\n+\t\tif (data.controls.size() != 1) {\n+\t\t\tcerr << \"processEvent(): Controls not found\" << endl;\n+\t\t\treturn report(Op_processEvent, TestFail);\n+\t\t}\n+\n+\t\tconst ControlList &controls = data.controls[0];\n+\t\tif (controls.get(V4L2_CID_BRIGHTNESS).get<int32_t>() != 10 ||\n+\t\t controls.get(V4L2_CID_CONTRAST).get<int32_t>() != 20 ||\n+\t\t controls.get(V4L2_CID_SATURATION).get<int32_t>() != 30) {\n+\t\t\tcerr << \"processEvent(): Invalid controls\" << endl;\n+\t\t\treturn report(Op_processEvent, TestFail);\n+\t\t}\n+\n+\t\treport(Op_processEvent, TestPass);\n+\t}\n+\n+private:\n+\tvoid report(Operation op, int status)\n+\t{\n+\t\tIPAOperationData data;\n+\t\tdata.operation = op;\n+\t\tdata.data.resize(1);\n+\t\tdata.data[0] = status;\n+\t\tqueueFrameAction.emit(sequence_++, data);\n+\t}\n+\n+\tunsigned int sequence_;\n+};\n+\n+#define INVOKE(method, ...) \\\n+\tinvoke(&IPAInterface::method, Op_##method, #method, ##__VA_ARGS__)\n+\n+class IPAWrappersTest : public Test\n+{\n+public:\n+\tIPAWrappersTest()\n+\t\t: subdev_(nullptr), wrapper_(nullptr), sequence_(0), fd_(-1)\n+\t{\n+\t}\n+\n+protected:\n+\tint init() override\n+\t{\n+\t\t/* Locate the VIMC Sensor B subdevice. */\n+\t\tenumerator_ = unique_ptr<DeviceEnumerator>(DeviceEnumerator::create());\n+\t\tif (!enumerator_) {\n+\t\t\tcerr << \"Failed to create device enumerator\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tif (enumerator_->enumerate()) {\n+\t\t\tcerr << \"Failed to enumerate media devices\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tDeviceMatch dm(\"vimc\");\n+\t\tmedia_ = enumerator_->search(dm);\n+\t\tif (!media_) {\n+\t\t\tcerr << \"No VIMC media device found: skip test\" << endl;\n+\t\t\treturn TestSkip;\n+\t\t}\n+\n+\t\tMediaEntity *entity = media_->getEntityByName(\"Sensor A\");\n+\t\tif (!entity) {\n+\t\t\tcerr << \"Unable to find media entity 'Sensor A'\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tsubdev_ = new V4L2Subdevice(entity);\n+\t\tif (subdev_->open() < 0) {\n+\t\t\tcerr << \"Unable to open 'Sensor A' subdevice\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\t/* Force usage of the C API as that's what we want to test. */\n+\t\tint ret = setenv(\"LIBCAMERA_IPA_FORCE_C_API\", \"\", 1);\n+\t\tif (ret)\n+\t\t\treturn TestFail;\n+\n+\t\tstd::unique_ptr<IPAInterface> intf = utils::make_unique<TestIPAInterface>();\n+\t\twrapper_ = new IPAContextWrapper(new IPAInterfaceWrapper(std::move(intf)));\n+\t\twrapper_->queueFrameAction.connect(this, &IPAWrappersTest::queueFrameAction);\n+\n+\t\t/* Create a file descriptor for the buffer-related operations. */\n+\t\tfd_ = open(\"/tmp\", O_TMPFILE | O_RDWR, 0600);\n+\t\tif (fd_ == -1)\n+\t\t\treturn TestFail;\n+\n+\t\tftruncate(fd_, 4096);\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tint run() override\n+\t{\n+\t\tint ret;\n+\n+\t\t/* Test configure(). */\n+\t\tstd::map<unsigned int, IPAStream> config{\n+\t\t\t{ 1, { V4L2_PIX_FMT_YUYV, { 1024, 768 } } },\n+\t\t\t{ 2, { V4L2_PIX_FMT_NV12, { 800, 600 } } },\n+\t\t};\n+\t\tstd::map<unsigned int, const ControlInfoMap &> controlInfo;\n+\t\tcontrolInfo.emplace(42, subdev_->controls());\n+\t\tret = INVOKE(configure, config, controlInfo);\n+\t\tif (ret == TestFail)\n+\t\t\treturn TestFail;\n+\n+\t\t/* Test mapBuffers(). */\n+\t\tstd::vector<IPABuffer> buffers(2);\n+\t\tbuffers[0].memory.planes().resize(3);\n+\t\tbuffers[0].id = 10;\n+\t\tbuffers[0].memory.planes()[0].setDmabuf(fd_, 4096);\n+\t\tbuffers[1].id = 11;\n+\t\tbuffers[1].memory.planes().resize(3);\n+\t\tbuffers[1].memory.planes()[0].setDmabuf(fd_, 4096);\n+\t\tbuffers[1].memory.planes()[1].setDmabuf(fd_, 4096);\n+\n+\t\tret = INVOKE(mapBuffers, buffers);\n+\t\tif (ret == TestFail)\n+\t\t\treturn TestFail;\n+\n+\t\t/* Test unmapBuffers(). */\n+\t\tstd::vector<unsigned int> bufferIds = { 10, 11 };\n+\t\tret = INVOKE(unmapBuffers, bufferIds);\n+\t\tif (ret == TestFail)\n+\t\t\treturn TestFail;\n+\n+\t\t/* Test processEvent(). */\n+\t\tIPAOperationData data;\n+\t\tdata.operation = Op_processEvent;\n+\t\tdata.data = { 1, 2, 3, 4 };\n+\t\tdata.controls.emplace_back(subdev_->controls());\n+\n+\t\tControlList &controls = data.controls.back();\n+\t\tcontrols.set(V4L2_CID_BRIGHTNESS, static_cast<int32_t>(10));\n+\t\tcontrols.set(V4L2_CID_CONTRAST, static_cast<int32_t>(20));\n+\t\tcontrols.set(V4L2_CID_SATURATION, static_cast<int32_t>(30));\n+\n+\t\tret = INVOKE(processEvent, data);\n+\t\tif (ret == TestFail)\n+\t\t\treturn TestFail;\n+\n+\t\t/*\n+\t\t * Test init() last to ensure nothing in the wrappers or\n+\t\t * serializer depends on init() being called first.\n+\t\t */\n+\t\tret = INVOKE(init);\n+\t\tif (ret == TestFail)\n+\t\t\treturn TestFail;\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tvoid cleanup() override\n+\t{\n+\t\tdelete wrapper_;\n+\t\tdelete subdev_;\n+\n+\t\tif (fd_ != -1)\n+\t\t\tclose(fd_);\n+\t}\n+\n+private:\n+\ttemplate<typename T, typename... Args1, typename... Args2>\n+\tint invoke(T (IPAInterface::*func)(Args1...), Operation op,\n+\t\t const char *name, Args2... args)\n+\t{\n+\t\tdata_ = IPAOperationData();\n+\t\t(wrapper_->*func)(args...);\n+\n+\t\tif (frame_ != sequence_) {\n+\t\t\tcerr << \"IPAInterface::\" << name\n+\t\t\t << \"(): invalid frame number \" << frame_\n+\t\t\t << \", expected \" << sequence_;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tsequence_++;\n+\n+\t\tif (data_.operation != op) {\n+\t\t\tcerr << \"IPAInterface::\" << name\n+\t\t\t << \"(): failed to propagate\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tif (data_.data[0] != TestPass) {\n+\t\t\tcerr << \"IPAInterface::\" << name\n+\t\t\t << \"(): reported an error\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tvoid queueFrameAction(unsigned int frame, const IPAOperationData &data)\n+\t{\n+\t\tframe_ = frame;\n+\t\tdata_ = data;\n+\t}\n+\n+\tstd::shared_ptr<MediaDevice> media_;\n+\tstd::unique_ptr<DeviceEnumerator> enumerator_;\n+\tV4L2Subdevice *subdev_;\n+\n+\tIPAContextWrapper *wrapper_;\n+\tIPAOperationData data_;\n+\tunsigned int sequence_;\n+\tunsigned int frame_;\n+\tint fd_;\n+};\n+\n+TEST_REGISTER(IPAWrappersTest)\ndiff --git a/test/ipa/meson.build b/test/ipa/meson.build\nindex c501fcf99aed..f925c50a085e 100644\n--- a/test/ipa/meson.build\n+++ b/test/ipa/meson.build\n@@ -1,13 +1,14 @@\n ipa_test = [\n ['ipa_module_test', 'ipa_module_test.cpp'],\n ['ipa_interface_test', 'ipa_interface_test.cpp'],\n+ ['ipa_wrappers_test', 'ipa_wrappers_test.cpp'],\n ]\n \n foreach t : ipa_test\n exe = executable(t[0], t[1],\n dependencies : libcamera_dep,\n- link_with : test_libraries,\n- include_directories : test_includes_internal)\n+ link_with : [libipa, test_libraries],\n+ include_directories : [libipa_includes, test_includes_internal])\n \n test(t[0], exe, suite : 'ipa')\n endforeach\n", "prefixes": [ "libcamera-devel", "v2", "23/24" ] }