{"id":9392,"url":"https://patchwork.libcamera.org/api/patches/9392/?format=json","web_url":"https://patchwork.libcamera.org/patch/9392/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200826110926.67192-9-paul.elder@ideasonboard.com>","date":"2020-08-26T11:09:17","name":"[libcamera-devel,RFC,08/17] tests: IPC: Add various tests for the IPC framework","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"1c3acb75962cf0b31189f9893d07ca74bb741b73","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/?format=json","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/9392/mbox/","series":[{"id":1243,"url":"https://patchwork.libcamera.org/api/series/1243/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=1243","date":"2020-08-26T11:09:09","name":"[libcamera-devel,RFC,01/17] IPA: IPC: raspberrypi: Add data definition and generated header","version":1,"mbox":"https://patchwork.libcamera.org/series/1243/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/9392/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/9392/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 C222DBD87E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 26 Aug 2020 11:10:09 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8E6496290A;\n\tWed, 26 Aug 2020 13:10:09 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9DE3060387\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 26 Aug 2020 13:10:08 +0200 (CEST)","from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8C92353C;\n\tWed, 26 Aug 2020 13:10:06 +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=\"bPfcS5Kb\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1598440208;\n\tbh=udlIiFlJQhkwZsEE1j6AMeT/KpZdrmswIKiPql6/6hc=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=bPfcS5KbqcmhULLyKx95J6H1s+8h1Yh7NwLO6kMDjF4bNrbnDrPHhJe/72Jiq6GzZ\n\tw5NwdI9GUif07hvNdSehpCqdOXxwzNTsz5Cvph0tW7ejFonlFVBLgS+ZweeDrClya+\n\twBz3crjNQIIBTr6CwjYGovH0VZfLRvXHWnFHzlpE=","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Wed, 26 Aug 2020 20:09:17 +0900","Message-Id":"<20200826110926.67192-9-paul.elder@ideasonboard.com>","X-Mailer":"git-send-email 2.27.0","In-Reply-To":"<20200826110926.67192-1-paul.elder@ideasonboard.com>","References":"<20200826110926.67192-1-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Subject":"[libcamera-devel] [RFC PATCH 08/17] tests: IPC: Add various tests\n\tfor the IPC framework","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=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"This patch includes tests that I used while debugging. I just kind of\nplopped them here. If there's any value in these, I'll brush them up and\nkeep them, otherwise I'll discard them.\n\n- unixsocket_fds confirms that passing fds over the process boundary\n  increases the fd count\n- control_serialization_ipa tests de/serializing ControlList and\n  ControlInfoMap using IPADataSerializer\n- rpi_action_serialization tests de/serialization of RPiActionParams\n- rpi_config_serialization tests de/serialization of RPiConfigureParams\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n test/ipa/meson.build                          |   4 +-\n test/ipc/meson.build                          |   3 +-\n test/ipc/unixsocket_fds.cpp                   | 284 ++++++++++++++++++\n .../control_serialization_ipa.cpp             |  85 ++++++\n test/serialization/meson.build                |   5 +-\n .../rpi_action_serialization.cpp              | 141 +++++++++\n .../rpi_config_serialization.cpp              | 162 ++++++++++\n 7 files changed, 680 insertions(+), 4 deletions(-)\n create mode 100644 test/ipc/unixsocket_fds.cpp\n create mode 100644 test/serialization/control_serialization_ipa.cpp\n create mode 100644 test/serialization/rpi_action_serialization.cpp\n create mode 100644 test/serialization/rpi_config_serialization.cpp","diff":"diff --git a/test/ipa/meson.build b/test/ipa/meson.build\nindex ba672f3f..b89c6b66 100644\n--- a/test/ipa/meson.build\n+++ b/test/ipa/meson.build\n@@ -2,8 +2,8 @@\n \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+    #['ipa_interface_test',  'ipa_interface_test.cpp'],\n+    #['ipa_wrappers_test',   'ipa_wrappers_test.cpp'],\n ]\n \n foreach t : ipa_test\ndiff --git a/test/ipc/meson.build b/test/ipc/meson.build\nindex 650df1d6..e960aa82 100644\n--- a/test/ipc/meson.build\n+++ b/test/ipc/meson.build\n@@ -1,7 +1,8 @@\n # SPDX-License-Identifier: CC0-1.0\n \n ipc_tests = [\n-    [ 'unixsocket',  'unixsocket.cpp' ],\n+    [ 'unixsocket_fds', 'unixsocket_fds.cpp' ],\n+    [ 'unixsocket',     'unixsocket.cpp' ],\n ]\n \n foreach t : ipc_tests\ndiff --git a/test/ipc/unixsocket_fds.cpp b/test/ipc/unixsocket_fds.cpp\nnew file mode 100644\nindex 00000000..8e9c6864\n--- /dev/null\n+++ b/test/ipc/unixsocket_fds.cpp\n@@ -0,0 +1,284 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * unixsocket_fds.cpp - Unix socket IPC test with file descriptors\n+ */\n+\n+#include <algorithm>\n+#include <fcntl.h>\n+#include <iostream>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+#include <sys/wait.h>\n+#include <unistd.h>\n+\n+#include <libcamera/event_dispatcher.h>\n+#include <libcamera/timer.h>\n+\n+#include \"libcamera/internal/ipc_unixsocket.h\"\n+#include \"libcamera/internal/thread.h\"\n+#include \"libcamera/internal/utils.h\"\n+\n+#include \"test.h\"\n+\n+#define CMD_CLOSE\t0\n+#define CMD_REVERSE\t1\n+#define CMD_LEN_CALC\t2\n+#define CMD_LEN_CMP\t3\n+#define CMD_JOIN\t4\n+\n+using namespace std;\n+using namespace libcamera;\n+\n+class UnixSocketTestFdSlave\n+{\n+public:\n+\tUnixSocketTestFdSlave()\n+\t\t: exitCode_(EXIT_FAILURE), exit_(false)\n+\t{\n+\t\tdispatcher_ = Thread::current()->eventDispatcher();\n+\t\tipc_.readyRead.connect(this, &UnixSocketTestFdSlave::readyRead);\n+\t}\n+\n+\tint run(int fd)\n+\t{\n+\t\tif (ipc_.bind(fd)) {\n+\t\t\tcerr << \"Failed to connect to IPC channel\" << endl;\n+\t\t\treturn EXIT_FAILURE;\n+\t\t}\n+\n+\t\twhile (!exit_)\n+\t\t\tdispatcher_->processEvents();\n+\n+\t\tipc_.close();\n+\n+\t\treturn exitCode_;\n+\t}\n+\n+private:\n+\tvoid readyRead(IPCUnixSocket *ipc)\n+\t{\n+\t\tIPCUnixSocket::Payload message, response;\n+\t\tint ret;\n+\n+\t\tret = ipc->receive(&message);\n+\t\tif (ret) {\n+\t\t\tcerr << \"Receive message failed: \" << ret << endl;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tint fd = message.fds[0];\n+\n+\t\tcerr << \"slave: received \" << fd << endl;\n+\n+\t\tresponse.fds = { fd };\n+\n+\t\tret = ipc_.send(response);\n+\t\tif (ret < 0) {\n+\t\t\tcerr << \"Reply failed\" << endl;\n+\t\t\tstop(ret);\n+\t\t}\n+\t}\n+\n+\tvoid stop(int code)\n+\t{\n+\t\texitCode_ = code;\n+\t\texit_ = true;\n+\t}\n+\n+\tIPCUnixSocket ipc_;\n+\tEventDispatcher *dispatcher_;\n+\tint exitCode_;\n+\tbool exit_;\n+};\n+\n+class UnixSocketTestFd : public Test\n+{\n+protected:\n+\tint slaveStart(int fd)\n+\t{\n+\t\tpid_ = fork();\n+\n+\t\tif (pid_ == -1)\n+\t\t\treturn TestFail;\n+\n+\t\tif (!pid_) {\n+\t\t\tstd::string arg = std::to_string(fd);\n+\t\t\texecl(\"/proc/self/exe\", \"/proc/self/exe\",\n+\t\t\t      arg.c_str(), nullptr);\n+\n+\t\t\t/* Only get here if exec fails. */\n+\t\t\texit(TestFail);\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tint slaveStop()\n+\t{\n+\t\tint status;\n+\n+\t\tif (pid_ < 0)\n+\t\t\treturn TestFail;\n+\n+\t\tif (waitpid(pid_, &status, 0) < 0)\n+\t\t\treturn TestFail;\n+\n+\t\tif (!WIFEXITED(status) || WEXITSTATUS(status))\n+\t\t\treturn TestFail;\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tint testSendNewFd()\n+\t{\n+\t\tIPCUnixSocket::Payload message, response;\n+\t\tint ret;\n+\n+\t\tint fd = open(\"/dev/null\", O_RDONLY);\n+\t\tif (fd < 0) {\n+\t\t\tret = -errno;\n+\t\t\tcerr << \"Failed to open /dev/null: \" << strerror(-ret) << endl;\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tcerr << \"master: opened and sending fd \" << fd << endl;\n+\n+\t\tmessage.fds = { fd };\n+\n+\t\tret = call(message, &response);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tcerr << \"master: received fd \" << response.fds[0] << endl;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tint testSendOldFd(int fd)\n+\t{\n+\t\tIPCUnixSocket::Payload message, response;\n+\t\tint ret;\n+\n+\t\tcerr << \"master: sending fd \" << fd << endl;\n+\n+\t\tmessage.fds = { fd };\n+\n+\t\tret = call(message, &response);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tcerr << \"master: received fd \" << response.fds[0] << endl;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tint init()\n+\t{\n+\t\tcallResponse_ = nullptr;\n+\t\treturn 0;\n+\t}\n+\n+\tint run()\n+\t{\n+\t\tint slavefd = ipc_.create();\n+\t\tif (slavefd < 0)\n+\t\t\treturn TestFail;\n+\n+\t\tif (slaveStart(slavefd)) {\n+\t\t\tcerr << \"Failed to start slave\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tipc_.readyRead.connect(this, &UnixSocketTestFd::readyRead);\n+\n+\t\tfor (int i = 0; i < 10; i++)\n+\t\t\ttestSendNewFd();\n+\n+\t\tfor (int i = 0; i < 10; i++)\n+\t\t\ttestSendOldFd(20);\n+\n+\t\t/* Close slave connection. */\n+\t\tIPCUnixSocket::Payload close;\n+\t\tclose.data.push_back(CMD_CLOSE);\n+\t\tif (ipc_.send(close)) {\n+\t\t\tcerr << \"Closing IPC channel failed\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tipc_.close();\n+\t\tif (slaveStop()) {\n+\t\t\tcerr << \"Failed to stop slave\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+private:\n+\tint call(const IPCUnixSocket::Payload &message, IPCUnixSocket::Payload *response)\n+\t{\n+\t\tTimer timeout;\n+\t\tint ret;\n+\n+\t\tcallDone_ = false;\n+\t\tcallResponse_ = response;\n+\n+\t\tret = ipc_.send(message);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\ttimeout.start(200);\n+\t\twhile (!callDone_) {\n+\t\t\tif (!timeout.isRunning()) {\n+\t\t\t\tcerr << \"Call timeout!\" << endl;\n+\t\t\t\tcallResponse_ = nullptr;\n+\t\t\t\treturn -ETIMEDOUT;\n+\t\t\t}\n+\n+\t\t\tThread::current()->eventDispatcher()->processEvents();\n+\t\t}\n+\n+\t\tcallResponse_ = nullptr;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tvoid readyRead(IPCUnixSocket *ipc)\n+\t{\n+\t\tif (!callResponse_) {\n+\t\t\tcerr << \"Read ready without expecting data, fail.\" << endl;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (ipc->receive(callResponse_)) {\n+\t\t\tcerr << \"Receive message failed\" << endl;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tcallDone_ = true;\n+\t}\n+\n+\tpid_t pid_;\n+\tIPCUnixSocket ipc_;\n+\tbool callDone_;\n+\tIPCUnixSocket::Payload *callResponse_;\n+};\n+\n+/*\n+ * Can't use TEST_REGISTER() as single binary needs to act as both proxy\n+ * master and slave.\n+ */\n+int main(int argc, char **argv)\n+{\n+\tif (argc == 2) {\n+\t\tint ipcfd = std::stoi(argv[1]);\n+\t\tUnixSocketTestFdSlave slave;\n+\t\treturn slave.run(ipcfd);\n+\t}\n+\n+\treturn UnixSocketTestFd().execute();\n+}\ndiff --git a/test/serialization/control_serialization_ipa.cpp b/test/serialization/control_serialization_ipa.cpp\nnew file mode 100644\nindex 00000000..88489326\n--- /dev/null\n+++ b/test/serialization/control_serialization_ipa.cpp\n@@ -0,0 +1,85 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * control_serialization_ipa.cpp - Serialize and deserialize controls with IPADataSerializer\n+ */\n+\n+#include <fcntl.h>\n+#include <iostream>\n+#include <string.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+#include <tuple>\n+#include <unistd.h>\n+#include <vector>\n+\n+#include <libcamera/ipa/raspberrypi.h>\n+#include <libcamera/timer.h>\n+\n+#include \"libcamera/internal/device_enumerator.h\"\n+#include \"libcamera/internal/ipa_data_serializer.h\"\n+#include \"libcamera/internal/ipa_manager.h\"\n+#include \"libcamera/internal/ipa_module.h\"\n+#include \"libcamera/internal/pipeline_handler.h\"\n+#include \"libcamera/internal/thread.h\"\n+\n+#include \"serialization_test.h\"\n+#include \"test.h\"\n+\n+using namespace std;\n+using namespace libcamera;\n+\n+class ControlSerializationIPATest : public CameraTest, public Test\n+{\n+public:\n+\tControlSerializationIPATest()\n+\t\t: CameraTest(\"imx219\")\n+\t{\n+\t}\n+\n+protected:\n+\tint init() override\n+\t{\n+\t\treturn status_;\n+\t}\n+\n+\tint run() override\n+\t{\n+\t\tControlSerializer cs;\n+\n+\t\t/* Create a control list with three controls. */\n+\t\tconst ControlInfoMap &infoMap = camera_->controls();\n+\t\tControlList list(infoMap);\n+\n+\t\tlist.set(controls::Brightness, 0.5f);\n+\t\tlist.set(controls::Contrast, 1.2f);\n+\t\tlist.set(controls::Saturation, 0.2f);\n+\n+\t\tvector<uint8_t> infoMapBuf;\n+\t\ttie(infoMapBuf, ignore) = IPADataSerializer<const ControlInfoMap>::serialize(infoMap, &cs);\n+\n+\t\tvector<uint8_t> listBuf;\n+\t\ttie(listBuf, ignore) = IPADataSerializer<ControlList>::serialize(list, *list.infoMap(), &cs);\n+\n+\n+\t\tconst ControlInfoMap infoMapOut = IPADataSerializer<const ControlInfoMap>::deserialize(infoMapBuf, &cs);\n+\n+\t\tControlList listOut = IPADataSerializer<ControlList>::deserialize(listBuf, &cs);\n+\n+\n+\t\tif (!SerializationTest::equals(infoMap, infoMapOut)) {\n+\t\t\tcerr << \"Deserialized map doesn't match original\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tif (!SerializationTest::equals(list, listOut)) {\n+\t\t\tcerr << \"Deserialized list doesn't match original\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+};\n+\n+TEST_REGISTER(ControlSerializationIPATest)\ndiff --git a/test/serialization/meson.build b/test/serialization/meson.build\nindex a9d9cbcb..ecd07adf 100644\n--- a/test/serialization/meson.build\n+++ b/test/serialization/meson.build\n@@ -1,7 +1,10 @@\n # SPDX-License-Identifier: CC0-1.0\n \n serialization_tests = [\n-    [ 'control_serialization',    'control_serialization.cpp' ],\n+    [ 'control_serialization_ipa', 'control_serialization_ipa.cpp' ],\n+    [ 'control_serialization',     'control_serialization.cpp' ],\n+    [ 'rpi_config_serialization',  'rpi_config_serialization.cpp' ],\n+    [ 'rpi_action_serialization',  'rpi_action_serialization.cpp' ],\n ]\n \n foreach t : serialization_tests\ndiff --git a/test/serialization/rpi_action_serialization.cpp b/test/serialization/rpi_action_serialization.cpp\nnew file mode 100644\nindex 00000000..de9db03f\n--- /dev/null\n+++ b/test/serialization/rpi_action_serialization.cpp\n@@ -0,0 +1,141 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * rpi_action_serialization.cpp - Serialize and deserialize raspberrypi IPA action data\n+ */\n+\n+#include <fcntl.h>\n+#include <iostream>\n+#include <string.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+#include <tuple>\n+#include <unistd.h>\n+#include <vector>\n+\n+#include <libcamera/ipa/raspberrypi.h>\n+#include <libcamera/ipa/raspberrypi_serializer.h>\n+#include <libcamera/ipa/raspberrypi_wrapper.h>\n+#include <libcamera/timer.h>\n+\n+#include \"libcamera/internal/device_enumerator.h\"\n+#include \"libcamera/internal/ipa_data_serializer.h\"\n+#include \"libcamera/internal/ipa_manager.h\"\n+#include \"libcamera/internal/ipa_module.h\"\n+#include \"libcamera/internal/pipeline_handler.h\"\n+#include \"libcamera/internal/thread.h\"\n+\n+#include \"serialization_test.h\"\n+#include \"test.h\"\n+\n+using namespace std;\n+using namespace libcamera;\n+\n+class RPiActionSerializationTest : public CameraTest, public Test\n+{\n+public:\n+\tRPiActionSerializationTest()\n+\t\t: CameraTest(\"imx219\")\n+\t{\n+\t}\n+\n+protected:\n+\tint init() override\n+\t{\n+\t\treturn status_;\n+\t}\n+\n+\tint run() override\n+\t{\n+\t\tControlSerializer cs;\n+\n+\t\tRPiConfigureParams ipaConfig;\n+\n+\t\tconst ControlInfoMap &infoMap1 = camera_->controls();\n+\t\tControlList list1(infoMap1);\n+\t\tlist1.set(controls::Brightness, 0.5f);\n+\t\tlist1.set(controls::Contrast, 1.2f);\n+\t\tlist1.set(controls::Saturation, 0.2f);\n+\n+\t\tconst ControlInfoMap &infoMap2 = camera_->controls();\n+\t\tControlList list2(infoMap2);\n+\t\tlist2.set(controls::Brightness, -0.8f);\n+\t\tlist2.set(controls::Contrast, 2.1f);\n+\t\tlist2.set(controls::Saturation, 1.1f);\n+\n+\t\tRPiStatsCompletePayload s;\n+\t\ts.bufferId_ = 111;\n+\t\ts.controls_ = list2;\n+\n+\t\tRPiActionParams action;\n+\t\taction.op_ = RPI_IPA_ACTION_RUN_ISP;\n+\t\taction.bufferId_ = 222;\n+\t\taction.statsComplete_ = s;\n+\t\taction.controls_ = list1;\n+\n+\n+\t\tvector<uint8_t> buf;\n+\t\ttie(buf, std::ignore) = IPADataSerializer<RPiActionParams>::serialize(action, &cs);\n+\n+\t\tRPiActionParams actionOut = IPADataSerializer<RPiActionParams>::deserialize(buf, &cs);\n+\n+\n+\t\tif (!equals(action, actionOut)) {\n+\t\t\tcerr << \"Deserialized action doesn't match original\" << endl;\n+\t\t\treturn TestFail;\n+\t\t} else {\n+\t\t\tcerr << \"Pass!\" << endl;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+private:\n+\tbool equals(const RPiStatsCompletePayload &lhs, const RPiStatsCompletePayload &rhs)\n+\t{\n+\t\tbool matches = true;\n+\t\tif (lhs.bufferId_ != rhs.bufferId_)\n+\t\t\tmatches = false;\n+\n+\t\tif (!SerializationTest::equals(lhs.controls_, rhs.controls_)) {\n+\t\t\tmatches = false;\n+\t\t\tcerr << \"controls in stats complete not matching\" << endl;\n+\t\t}\n+\n+\t\treturn matches;\n+\t}\n+\n+\tbool equals(const RPiActionParams &lhs, const RPiActionParams &rhs)\n+\t{\n+\t\tbool matches = true;\n+\n+\t\tif (lhs.op_ != rhs.op_) {\n+\t\t\tmatches = false;\n+\t\t\tcerr << \"op_ expected \" << lhs.op_ << \" got \" << rhs.op_;\n+\t\t}\n+\n+\t\tif (lhs.bufferId_ != rhs.bufferId_) {\n+\t\t\tmatches = false;\n+\t\t\tcerr << \"bufferId_ expected \" << lhs.bufferId_ << \" got \" << rhs.bufferId_;\n+\t\t}\n+\n+\t\tif (!equals(lhs.statsComplete_, rhs.statsComplete_)) {\n+\t\t\tmatches = false;\n+\t\t\tcerr << \"statsComplete_ expected {\"\n+\t\t\t\t<< lhs.statsComplete_.bufferId_ << \", and some controls}\"\n+\t\t\t     << \" got {\"\n+\t\t\t\t<< rhs.statsComplete_.bufferId_ << \", and some controls}\" << endl;\n+\t\t}\n+\n+\t\tif (!SerializationTest::equals(lhs.controls_, rhs.controls_)) {\n+\t\t\tmatches = false;\n+\t\t\tcerr << \"controls not matching\" << endl;\n+\t\t}\n+\n+\t\treturn matches;\n+\t}\n+\n+};\n+\n+TEST_REGISTER(RPiActionSerializationTest)\ndiff --git a/test/serialization/rpi_config_serialization.cpp b/test/serialization/rpi_config_serialization.cpp\nnew file mode 100644\nindex 00000000..390579f1\n--- /dev/null\n+++ b/test/serialization/rpi_config_serialization.cpp\n@@ -0,0 +1,162 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * rpi_config_serialization.cpp - Serialize and deserialize raspberrypi IPA configuration data\n+ */\n+\n+#include <fcntl.h>\n+#include <iostream>\n+#include <string.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+#include <tuple>\n+#include <unistd.h>\n+#include <vector>\n+\n+#include <libcamera/ipa/raspberrypi.h>\n+#include <libcamera/ipa/raspberrypi_serializer.h>\n+#include <libcamera/ipa/raspberrypi_wrapper.h>\n+#include <libcamera/timer.h>\n+\n+#include \"libcamera/internal/device_enumerator.h\"\n+#include \"libcamera/internal/ipa_data_serializer.h\"\n+#include \"libcamera/internal/ipa_manager.h\"\n+#include \"libcamera/internal/ipa_module.h\"\n+#include \"libcamera/internal/pipeline_handler.h\"\n+#include \"libcamera/internal/thread.h\"\n+\n+#include \"serialization_test.h\"\n+#include \"test.h\"\n+\n+using namespace std;\n+using namespace libcamera;\n+\n+class RPiConfigSerializationTest : public CameraTest, public Test\n+{\n+public:\n+\tRPiConfigSerializationTest()\n+\t\t: CameraTest(\"imx219\")\n+\t{\n+\t}\n+\n+protected:\n+\tint init() override\n+\t{\n+\t\treturn status_;\n+\t}\n+\n+\tint run() override\n+\t{\n+\t\tControlSerializer cs;\n+\n+\t\tRPiConfigureParams ipaConfig;\n+\n+\t\tconst ControlInfoMap &infoMap = camera_->controls();\n+\t\tControlList list(infoMap);\n+\t\tlist.set(controls::Brightness, 0.5f);\n+\t\tlist.set(controls::Contrast, 1.2f);\n+\t\tlist.set(controls::Saturation, 0.2f);\n+\n+\n+\t\tRPiConfigurePayload p1;\n+\t\tp1.op_ = RPI_IPA_CONFIG_LS_TABLE;\n+\t\tp1.lsTableHandle_ = FileDescriptor(222);\n+\n+\t\tRPiConfigurePayload p2;\n+\t\tp2.op_ = RPI_IPA_CONFIG_STAGGERED_WRITE;\n+\t\tp2.staggeredWriteResult_ = RPiStaggeredWritePayload(6, 6, 6);\n+\n+\t\tRPiConfigurePayload p3;\n+\t\tp3.op_ = RPI_IPA_CONFIG_SENSOR;\n+\t\tp3.controls_ = list;\n+\n+\t\tipaConfig.payload_.push_back(p1);\n+\t\tipaConfig.payload_.push_back(p2);\n+\t\tipaConfig.payload_.push_back(p3);\n+\n+\t\tvector<uint8_t> buf;\n+\t\tvector<int32_t> fds;\n+\t\ttie(buf, fds) = IPADataSerializer<RPiConfigureParams>::serialize(ipaConfig, &cs);\n+\n+\t\tRPiConfigureParams ipaConfigOut = IPADataSerializer<RPiConfigureParams>::deserialize(buf, fds, &cs);\n+\n+\n+\t\tif (!equals(ipaConfig, ipaConfigOut)) {\n+\t\t\tcerr << \"Deserialized config doesn't match original\" << endl;\n+\t\t\treturn TestFail;\n+\t\t} else {\n+\t\t\tcerr << \"Pass!\" << endl;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+private:\n+\tbool equals(const RPiStaggeredWritePayload &lhs, const RPiStaggeredWritePayload &rhs)\n+\t{\n+\t\treturn lhs.gainDelay_ == rhs.gainDelay_ &&\n+\t\t       lhs.exposureDelay_ == rhs.exposureDelay_ && \n+\t\t       lhs.sensorMetadata_ == rhs.sensorMetadata_;\n+\t}\n+\n+\tbool equals(const RPiConfigurePayload &lhs, const RPiConfigurePayload &rhs)\n+\t{\n+\t\tbool matches = true;\n+\n+\t\tif (lhs.op_ != rhs.op_) {\n+\t\t\tmatches = false;\n+\t\t\tcerr << \"expected \" << lhs.op_ << \" got \" << rhs.op_;\n+\t\t}\n+\n+\t\tif (lhs.lsTableHandle_.fd() != rhs.lsTableHandle_.fd()) {\n+\t\t\tmatches = false;\n+\t\t\tcerr << \"expected \" << lhs.lsTableHandle_.fd() << \" got \" << rhs.lsTableHandle_.fd();\n+\t\t}\n+\n+\t\tif (lhs.bufferFd_ != rhs.bufferFd_) {\n+\t\t\tmatches = false;\n+\t\t\tcerr << \"expected \" << lhs.bufferFd_ << \" got \" << rhs.bufferFd_;\n+\t\t}\n+\n+\t\tif (!equals(lhs.staggeredWriteResult_, rhs.staggeredWriteResult_)) {\n+\t\t\tmatches = false;\n+\t\t\tcerr << \"expected {\"\n+\t\t\t\t<< lhs.staggeredWriteResult_.gainDelay_ << \", \"\n+\t\t\t\t<< lhs.staggeredWriteResult_.exposureDelay_ << \", \"\n+\t\t\t\t<< lhs.staggeredWriteResult_.sensorMetadata_ << \"} \"\n+\t\t\t     << \" got {\"\n+\t\t\t\t<< rhs.staggeredWriteResult_.gainDelay_ << \", \"\n+\t\t\t\t<< rhs.staggeredWriteResult_.exposureDelay_ << \", \"\n+\t\t\t\t<< rhs.staggeredWriteResult_.sensorMetadata_ << \"} \";\n+\t\t}\n+\n+\t\tif (!SerializationTest::equals(lhs.controls_, rhs.controls_)) {\n+\t\t\tmatches = false;\n+\t\t}\n+\n+\t\treturn matches;\n+\t}\n+\n+\tbool equals(const RPiConfigureParams &lhs, const RPiConfigureParams &rhs)\n+\t{\n+\t\tbool matches = true;\n+\n+\t\tif (lhs.payload_.size() != rhs.payload_.size()) {\n+\t\t\tcerr << \"non-matching size\" << endl;\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\tsize_t len = lhs.payload_.size();\n+\t\tfor (unsigned int i = 0; i < len; i++) {\n+\t\t\tcerr << \"[\" << i << \"]: \" << endl;\n+\t\t\tif (!equals(lhs.payload_[i], rhs.payload_[i]))\n+\t\t\t\tmatches = false;\n+\t\t}\n+\n+\t\treturn matches;\n+\t}\n+\n+};\n+\n+TEST_REGISTER(RPiConfigSerializationTest)\n","prefixes":["libcamera-devel","RFC","08/17"]}