Patch Detail
Show a patch.
GET /api/1.1/patches/1532/?format=api
{ "id": 1532, "url": "https://patchwork.libcamera.org/api/1.1/patches/1532/?format=api", "web_url": "https://patchwork.libcamera.org/patch/1532/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20190630162514.20522-4-niklas.soderlund@ragnatech.se>", "date": "2019-06-30T16:25:14", "name": "[libcamera-devel,v2,3/3] test: ipc: unix: Add test for IPCUnixSocket", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "1026691d3eb856f1034f170cbb5a36465551cb4d", "submitter": { "id": 5, "url": "https://patchwork.libcamera.org/api/1.1/people/5/?format=api", "name": "Niklas Söderlund", "email": "niklas.soderlund@ragnatech.se" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/1532/mbox/", "series": [ { "id": 382, "url": "https://patchwork.libcamera.org/api/1.1/series/382/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=382", "date": "2019-06-30T16:25:12", "name": "libcamera: ipc: unix: Add a IPC mechanism based on Unix sockets", "version": 2, "mbox": "https://patchwork.libcamera.org/series/382/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/1532/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/1532/checks/", "tags": {}, "headers": { "Return-Path": "<niklas.soderlund@ragnatech.se>", "Received": [ "from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net\n\t[195.74.38.228])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5B1A761EB4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 30 Jun 2019 18:25:33 +0200 (CEST)", "from bismarck.berto.se (unknown [145.14.112.32])\n\tby bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA\n\tid a25856ea-9b53-11e9-8ab4-005056917a89;\n\tSun, 30 Jun 2019 18:25:12 +0200 (CEST)" ], "X-Halon-ID": "a25856ea-9b53-11e9-8ab4-005056917a89", "Authorized-sender": "niklas@soderlund.pp.se", "From": "=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Sun, 30 Jun 2019 18:25:14 +0200", "Message-Id": "<20190630162514.20522-4-niklas.soderlund@ragnatech.se>", "X-Mailer": "git-send-email 2.21.0", "In-Reply-To": "<20190630162514.20522-1-niklas.soderlund@ragnatech.se>", "References": "<20190630162514.20522-1-niklas.soderlund@ragnatech.se>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v2 3/3] test: ipc: unix: Add test for\n\tIPCUnixSocket", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.23", "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": "Sun, 30 Jun 2019 16:25:33 -0000" }, "content": "Test that the IPC supports sending data and file descriptors over the\nIPC medium. To be able to execute the test two parts are needed, one\nto drive the test and act as the libcamera (master) and a one to act as\nthe IPA (slave).\n\nThe master drives the testing posting requests to the slave to process\nand sometimes respond to. A few different tests are performed.\n\n- Master sends an array to the slave which responds with a reversed copy\n of the array. The master verifies that a reversed array is returned.\n\n- Master sends a list of file descriptors and ask the slave to calculate\n and respond with the sum of the size of the files. The master verifies\n that the calculated size is correct.\n\n- Master sends a pre-computed size and a list of file descriptors and\n ask the slave to verify that the pre-computed size matches the sum of\n the size of the file descriptors.\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n---\n test/ipc/meson.build | 12 ++\n test/ipc/unixsocket.cpp | 390 ++++++++++++++++++++++++++++++++++++++++\n test/meson.build | 1 +\n 3 files changed, 403 insertions(+)\n create mode 100644 test/ipc/meson.build\n create mode 100644 test/ipc/unixsocket.cpp", "diff": "diff --git a/test/ipc/meson.build b/test/ipc/meson.build\nnew file mode 100644\nindex 0000000000000000..ca8375f35df91731\n--- /dev/null\n+++ b/test/ipc/meson.build\n@@ -0,0 +1,12 @@\n+ipc_tests = [\n+ [ 'unixsocket', 'unixsocket.cpp' ],\n+]\n+\n+foreach t : ipc_tests\n+ exe = executable(t[0], t[1],\n+ dependencies : libcamera_dep,\n+ link_with : test_libraries,\n+ include_directories : test_includes_internal)\n+\n+ test(t[0], exe, suite : 'ipc', is_parallel : false)\n+endforeach\ndiff --git a/test/ipc/unixsocket.cpp b/test/ipc/unixsocket.cpp\nnew file mode 100644\nindex 0000000000000000..f433102af1b6d2a3\n--- /dev/null\n+++ b/test/ipc/unixsocket.cpp\n@@ -0,0 +1,390 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * unixsocket.cpp - Unix socket IPC test\n+ */\n+\n+#include <algorithm>\n+#include <atomic>\n+#include <fcntl.h>\n+#include <iostream>\n+#include <string.h>\n+#include <sys/wait.h>\n+#include <unistd.h>\n+\n+#include <libcamera/camera_manager.h>\n+#include <libcamera/event_dispatcher.h>\n+#include <libcamera/timer.h>\n+\n+#include \"ipc_unixsocket.h\"\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+\n+using namespace std;\n+using namespace libcamera;\n+\n+int calculateLength(int fd)\n+{\n+\tlseek(fd, 0, 0);\n+\tint size = lseek(fd, 0, SEEK_END);\n+\tlseek(fd, 0, 0);\n+\n+\treturn size;\n+}\n+\n+class UnixSocketTestSlave\n+{\n+public:\n+\tUnixSocketTestSlave()\n+\t\t: exitCode_(EXIT_FAILURE), exit_(false)\n+\t{\n+\t\tdispatcher_ = CameraManager::instance()->eventDispatcher();\n+\t\tipc_.readyRead.connect(this, &UnixSocketTestSlave::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\tif (ipc->receive(&message)) {\n+\t\t\tcerr << \"Receive message failed\" << endl;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tconst uint8_t cmd = message.data[0];\n+\t\tcout << \"Slave received command \" << static_cast<unsigned int>(cmd) << endl;\n+\n+\t\tswitch (cmd) {\n+\t\tcase CMD_CLOSE:\n+\t\t\tstop(0);\n+\t\t\tbreak;\n+\n+\t\tcase CMD_REVERSE: {\n+\t\t\tresponse.data = message.data;\n+\t\t\tstd::reverse(response.data.begin() + 1, response.data.end());\n+\n+\t\t\tret = ipc_.send(response);\n+\t\t\tif (ret < 0) {\n+\t\t\t\tcerr << \"Reverse fail\" << endl;\n+\t\t\t\tstop(ret);\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcase CMD_LEN_CALC: {\n+\t\t\tint size = 0;\n+\t\t\tfor (int fd : message.fds)\n+\t\t\t\tsize += calculateLength(fd);\n+\n+\t\t\tresponse.data.resize(1 + sizeof(size));\n+\t\t\tresponse.data[0] = cmd;\n+\t\t\tmemcpy(response.data.data() + 1, &size, sizeof(size));\n+\n+\t\t\tret = ipc_.send(response);\n+\t\t\tif (ret < 0) {\n+\t\t\t\tcerr << \"Calc fail\" << endl;\n+\t\t\t\tstop(ret);\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcase CMD_LEN_CMP: {\n+\t\t\tint size = 0;\n+\t\t\tfor (int fd : message.fds)\n+\t\t\t\tsize += calculateLength(fd);\n+\n+\t\t\tint cmp;\n+\t\t\tmemcpy(&cmp, message.data.data() + 1, sizeof(cmp));\n+\n+\t\t\tif (cmp != size) {\n+\t\t\t\tcerr << \"Compare fail\" << endl;\n+\t\t\t\tstop(-ERANGE);\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tdefault:\n+\t\t\tcerr << \"Unknown command \" << cmd << endl;\n+\t\t\tstop(-EINVAL);\n+\t\t\tbreak;\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 UnixSocketTest : 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 testReverse()\n+\t{\n+\t\tIPCUnixSocket::Payload message, response;\n+\t\tint ret;\n+\n+\t\tmessage.data = { CMD_REVERSE, 1, 2, 3, 4, 5 };\n+\n+\t\tret = call(message, &response);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tstd::reverse(response.data.begin() + 1, response.data.end());\n+\t\tif (message.data != response.data)\n+\t\t\treturn TestFail;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tint testCalc()\n+\t{\n+\t\tIPCUnixSocket::Payload message, response;\n+\t\tint sizeOut, sizeIn, ret;\n+\n+\t\tsizeOut = prepareFDs(&message, 2);\n+\t\tif (sizeOut < 0)\n+\t\t\treturn sizeOut;\n+\n+\t\tmessage.data.push_back(CMD_LEN_CALC);\n+\n+\t\tret = call(message, &response);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tmemcpy(&sizeIn, response.data.data() + 1, sizeof(sizeIn));\n+\t\tif (sizeOut != sizeIn)\n+\t\t\treturn TestFail;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tint testCmp()\n+\t{\n+\t\tIPCUnixSocket::Payload message;\n+\t\tint size;\n+\n+\t\tsize = prepareFDs(&message, 7);\n+\t\tif (size < 0)\n+\t\t\treturn size;\n+\n+\t\tmessage.data.resize(1 + sizeof(size));\n+\t\tmessage.data[0] = CMD_LEN_CMP;\n+\t\tmemcpy(message.data.data() + 1, &size, sizeof(size));\n+\n+\t\tif (ipc_.send(message))\n+\t\t\treturn TestFail;\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, &UnixSocketTest::readyRead);\n+\n+\t\t/* Test reversing a string, this test sending only data. */\n+\t\tif (testReverse()) {\n+\t\t\tcerr << \"Reveres array test failed\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\t/* Test offloading a calculation, this test sending only FDs. */\n+\t\tif (testCalc()) {\n+\t\t\tcerr << \"Calc test failed\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\t/* Test fire and forget, this tests sending data and FDs. */\n+\t\tif (testCmp()) {\n+\t\t\tcerr << \"Cmp test failed\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\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\tif (callResponse_)\n+\t\t\treturn -EBUSY;\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\tCameraManager::instance()->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\tIPCUnixSocket::Payload message;\n+\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\tconst uint8_t cmd = callResponse_->data[0];\n+\t\tcout << \"Master received command \" << static_cast<unsigned int>(cmd) << endl;\n+\n+\t\tcallDone_ = true;\n+\t}\n+\n+\tint prepareFDs(IPCUnixSocket::Payload *message, unsigned int num)\n+\t{\n+\t\tint fd = open(\"/proc/self/exe\", O_RDONLY);\n+\t\tif (fd < 0)\n+\t\t\treturn fd;\n+\n+\t\tint size = 0;\n+\t\tfor (unsigned int i = 0; i < num; i++) {\n+\t\t\tint clone = dup(fd);\n+\t\t\tif (clone < 0)\n+\t\t\t\treturn clone;\n+\n+\t\t\tsize += calculateLength(clone);\n+\t\t\tmessage->fds.push_back(clone);\n+\t\t}\n+\n+\t\tclose(fd);\n+\n+\t\treturn size;\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\tUnixSocketTestSlave slave;\n+\t\treturn slave.run(ipcfd);\n+\t}\n+\n+\treturn UnixSocketTest().execute();\n+}\ndiff --git a/test/meson.build b/test/meson.build\nindex c36ac24796367501..3666f6b2385bd4ca 100644\n--- a/test/meson.build\n+++ b/test/meson.build\n@@ -2,6 +2,7 @@ subdir('libtest')\n \n subdir('camera')\n subdir('ipa')\n+subdir('ipc')\n subdir('media_device')\n subdir('pipeline')\n subdir('stream')\n", "prefixes": [ "libcamera-devel", "v2", "3/3" ] }