Show a patch.

GET /api/patches/1579/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1579,
    "url": "https://patchwork.libcamera.org/api/patches/1579/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/1579/",
    "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": "<20190701232339.5191-3-laurent.pinchart@ideasonboard.com>",
    "date": "2019-07-01T23:23:38",
    "name": "[libcamera-devel,v4,2/3] test: ipc: unix: Add test for IPCUnixSocket",
    "commit_ref": "a00fdabacdd093c3eccb3d44155e151f59d783bf",
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "eaaebe578d033c8c0aedccd4fd3640e40c153d99",
    "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/1579/mbox/",
    "series": [
        {
            "id": 389,
            "url": "https://patchwork.libcamera.org/api/series/389/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=389",
            "date": "2019-07-01T23:23:36",
            "name": "libcamera: ipc: unix: Add a IPC mechanism based on Unix sockets",
            "version": 4,
            "mbox": "https://patchwork.libcamera.org/series/389/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/1579/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/1579/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 3A1236157C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Jul 2019 01:24:04 +0200 (CEST)",
            "from pendragon.bb.dnainternet.fi\n\t(dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi\n\t[IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id DB992524\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Jul 2019 01:24:03 +0200 (CEST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1562023444;\n\tbh=AiaJ4x8KtlvqQe9YHb/2vNKiUFGnRIiCtRcD/1BVXFc=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=PVu/LBxRUT+T/xs2wApja8M2FbW6q5qK9Xe2litOIeYRIA5E7qkDuq8tn/spw/s/9\n\tw81iQ/90C94vyrlH5ckWhDnqASesGzZj8HG1X3QFeHSDRxq/a8TEDCcmMwBwNknQxG\n\tDu9PTb7TBTUlPAgsYp7vCCe3kPTc9NKw4w2EWWuU=",
        "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Tue,  2 Jul 2019 02:23:38 +0300",
        "Message-Id": "<20190701232339.5191-3-laurent.pinchart@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.21.0",
        "In-Reply-To": "<20190701232339.5191-1-laurent.pinchart@ideasonboard.com>",
        "References": "<20190701232339.5191-1-laurent.pinchart@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v4 2/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": "Mon, 01 Jul 2019 23:24:04 -0000"
    },
    "content": "From: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n\nTest 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 ties to sends an empty message making sure that the send call\n  fails.\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  asks the slave to verify that the pre-computed size matches the sum of\n  the size of the file descriptors.\n\n- Master sends two file descriptors and asks the salve to join the file\n  contents in a new file and respond with its file descriptor. The\n  master then verifies that the content of the returned file descriptor\n  matches the order of the original two files.\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\nChanges since v3:\n\n- Use O_TMPFILE instead of shm_open()\n- Miscellaneous typo and small fixes\n---\n test/ipc/meson.build    |  12 +\n test/ipc/unixsocket.cpp | 502 ++++++++++++++++++++++++++++++++++++++++\n test/meson.build        |   1 +\n 3 files changed, 515 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 000000000000..ca8375f35df9\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 000000000000..eeef64842a75\n--- /dev/null\n+++ b/test/ipc/unixsocket.cpp\n@@ -0,0 +1,502 @@\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 <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/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+#include \"utils.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+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\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\tconst uint8_t cmd = message.data[0];\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 failed\" << 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 failed\" << 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 failed\" << endl;\n+\t\t\t\tstop(-ERANGE);\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcase CMD_JOIN: {\n+\t\t\tint outfd = open(\"/tmp\", O_TMPFILE | O_RDWR,\n+\t\t\t\t\t S_IRUSR | S_IWUSR);\n+\t\t\tif (outfd < 0) {\n+\t\t\t\tcerr << \"Create out file failed\" << endl;\n+\t\t\t\tstop(outfd);\n+\t\t\t\treturn;\n+\t\t\t}\n+\n+\t\t\tfor (int fd : message.fds) {\n+\t\t\t\twhile (true) {\n+\t\t\t\t\tchar buf[32];\n+\t\t\t\t\tssize_t num = read(fd, &buf, sizeof(buf));\n+\n+\t\t\t\t\tif (num < 0) {\n+\t\t\t\t\t\tcerr << \"Read failed\" << endl;\n+\t\t\t\t\t\tstop(-EIO);\n+\t\t\t\t\t\treturn;\n+\t\t\t\t\t} else if (!num)\n+\t\t\t\t\t\tbreak;\n+\n+\t\t\t\t\tif (write(outfd, buf, num) < 0) {\n+\t\t\t\t\t\tcerr << \"Write failed\" << endl;\n+\t\t\t\t\t\tstop(-EIO);\n+\t\t\t\t\t\treturn;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tclose(fd);\n+\t\t\t}\n+\n+\t\t\tlseek(outfd, 0, 0);\n+\t\t\tresponse.data.push_back(CMD_JOIN);\n+\t\t\tresponse.fds.push_back(outfd);\n+\n+\t\t\tret = ipc_.send(response);\n+\t\t\tif (ret < 0) {\n+\t\t\t\tcerr << \"Join failed\" << endl;\n+\t\t\t\tstop(ret);\n+\t\t\t}\n+\n+\t\t\tclose(outfd);\n+\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 testEmptyFail()\n+\t{\n+\t\tIPCUnixSocket::Payload message;\n+\n+\t\treturn ipc_.send(message) != -EINVAL;\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 testFdOrder()\n+\t{\n+\t\tIPCUnixSocket::Payload message, response;\n+\t\tint ret;\n+\n+\t\tstatic const char *strings[2] = {\n+\t\t\t\"Foo\",\n+\t\t\t\"Bar\",\n+\t\t};\n+\t\tint fds[2];\n+\n+\t\tfor (unsigned int i = 0; i < ARRAY_SIZE(strings); i++) {\n+\t\t\tunsigned int len = strlen(strings[i]);\n+\n+\t\t\tfds[i] = open(\"/tmp\", O_TMPFILE | O_RDWR,\n+\t\t\t\t      S_IRUSR | S_IWUSR);\n+\t\t\tif (fds[i] < 0)\n+\t\t\t\treturn TestFail;\n+\n+\t\t\tret = write(fds[i], strings[i], len);\n+\t\t\tif (ret < 0)\n+\t\t\t\treturn TestFail;\n+\n+\t\t\tlseek(fds[i], 0, 0);\n+\t\t\tmessage.fds.push_back(fds[i]);\n+\t\t}\n+\n+\t\tmessage.data.push_back(CMD_JOIN);\n+\n+\t\tret = call(message, &response);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tfor (unsigned int i = 0; i < ARRAY_SIZE(strings); i++) {\n+\t\t\tunsigned int len = strlen(strings[i]);\n+\t\t\tchar buf[len];\n+\n+\t\t\tclose(fds[i]);\n+\n+\t\t\tif (read(response.fds[0], &buf, len) <= 0)\n+\t\t\t\treturn TestFail;\n+\n+\t\t\tif (memcmp(buf, strings[i], len))\n+\t\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tclose(response.fds[0]);\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 that an empty message fails. */\n+\t\tif (testEmptyFail()) {\n+\t\t\tcerr << \"Empty message 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/* Test order of file descriptors. */\n+\t\tif (testFdOrder()) {\n+\t\t\tcerr << \"fd order 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\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\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+\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 c36ac2479636..3666f6b2385b 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",
        "v4",
        "2/3"
    ]
}