@@ -1,7 +1,8 @@
# SPDX-License-Identifier: CC0-1.0
ipc_tests = [
- [ 'unixsocket', 'unixsocket.cpp' ],
+ [ 'unixsocket_ipc', 'unixsocket_ipc.cpp' ],
+ [ 'unixsocket', 'unixsocket.cpp' ],
]
foreach t : ipc_tests
new file mode 100644
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * unixsocket_ipc.cpp - Unix socket IPC test
+ */
+
+#include <algorithm>
+#include <fcntl.h>
+#include <iostream>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <libcamera/event_dispatcher.h>
+#include <libcamera/timer.h>
+
+#include "libcamera/internal/ipa_data_serializer.h"
+#include "libcamera/internal/ipa_ipc_unixsocket.h"
+#include "libcamera/internal/process.h"
+#include "libcamera/internal/thread.h"
+#include "libcamera/internal/utils.h"
+
+#include "test.h"
+
+#define CMD_EXIT 0
+#define CMD_GET_SYNC 1
+#define CMD_SET_ASYNC 2
+
+using namespace std;
+using namespace libcamera;
+
+class UnixSocketTestIPCSlave
+{
+public:
+ UnixSocketTestIPCSlave()
+ : value_(1337), exitCode_(EXIT_FAILURE), exit_(false)
+ {
+ dispatcher_ = Thread::current()->eventDispatcher();
+ ipc_.readyRead.connect(this, &UnixSocketTestIPCSlave::readyRead);
+ }
+
+ int run(int fd)
+ {
+ if (ipc_.bind(fd)) {
+ cerr << "Failed to connect to IPC channel" << endl;
+ return EXIT_FAILURE;
+ }
+
+ while (!exit_)
+ dispatcher_->processEvents();
+
+ ipc_.close();
+
+ return exitCode_;
+ }
+
+private:
+ void readyRead(IPCUnixSocket *ipc)
+ {
+ IPCUnixSocket::Payload message, response;
+ int ret;
+
+ ret = ipc->receive(&message);
+ if (ret) {
+ cerr << "Receive message failed: " << ret << endl;
+ return;
+ }
+
+ uint32_t cmd, seq;
+ std::tie(cmd, seq) = IPAIPCUnixSocket::readHeader(message);
+ IPAIPCUnixSocket::eraseHeader(message);
+
+ switch (cmd) {
+ case CMD_EXIT: {
+ exit_ = true;
+ break;
+ }
+
+ case CMD_GET_SYNC: {
+ IPAIPCUnixSocket::writeHeader(response, cmd, seq);
+
+ vector<uint8_t> buf;
+ tie(buf, ignore) = IPADataSerializer<int32_t>::serialize(value_);
+ response.data.insert(response.data.end(), buf.begin(), buf.end());
+
+ ret = ipc_.send(response);
+ if (ret < 0) {
+ cerr << "Reply failed" << endl;
+ stop(ret);
+ }
+ break;
+ }
+
+ case CMD_SET_ASYNC: {
+ value_ = IPADataSerializer<int32_t>::deserialize(message.data);
+ break;
+ }
+ }
+ }
+
+ void stop(int code)
+ {
+ exitCode_ = code;
+ exit_ = true;
+ }
+
+ int32_t value_;
+
+ IPCUnixSocket ipc_;
+ EventDispatcher *dispatcher_;
+ int exitCode_;
+ bool exit_;
+};
+
+class UnixSocketTestIPC : public Test
+{
+protected:
+ int init()
+ {
+ return 0;
+ }
+
+ int setVal(int32_t val)
+ {
+ vector<uint8_t> buf;
+ tie(buf, ignore) = IPADataSerializer<int32_t>::serialize(val);
+
+ int ret = ipc_->sendAsync(CMD_SET_ASYNC, buf, {});
+ if (ret < 0) {
+ cerr << "Failed to call set value" << endl;
+ return ret;
+ }
+
+ return 0;
+ }
+
+ int getVal()
+ {
+ vector<uint8_t> buf;
+
+ int ret = ipc_->sendSync(CMD_GET_SYNC, {}, {}, &buf);
+ if (ret < 0) {
+ cerr << "Failed to call get value" << endl;
+ return ret;
+ }
+
+ return IPADataSerializer<int32_t>::deserialize(buf);
+ }
+
+ int exit()
+ {
+ int ret = ipc_->sendAsync(CMD_EXIT, {}, {});
+ if (ret < 0) {
+ cerr << "Failed to call exit" << endl;
+ return ret;
+ }
+
+ return 0;
+ }
+
+ int run()
+ {
+ char selfpath[100];
+ memset(selfpath, 0, sizeof(selfpath));
+ int ret = readlink("/proc/self/exe", selfpath, sizeof(selfpath));
+ if (ret < 0) {
+ int err = errno;
+ cerr << "Failed to get path: " << strerror(err) << endl;
+ return TestFail;
+ }
+
+ ipc_ = std::make_unique<IPAIPCUnixSocket>("", selfpath);
+ if (!ipc_->isValid()) {
+ cerr << "Failed to create IPAIPC" << endl;
+ return TestFail;
+ }
+
+
+ ret = getVal();
+ if (ret < 0) {
+ cerr << "Failed to get initial value: " << strerror(-ret) << endl;
+ return TestFail;
+ }
+ if (ret != 1337) {
+ cerr << "Wrong inital value, expected 1337, got " << ret << endl;
+ return TestFail;
+ }
+
+ ret = setVal(9001);
+ if (ret < 0) {
+ cerr << "Failed to set value: " << strerror(-ret) << endl;
+ return TestFail;
+ }
+
+ ret = getVal();
+ if (ret < 0) {
+ cerr << "Failed to get value: " << strerror(-ret) << endl;
+ return TestFail;
+ }
+ if (ret != 9001) {
+ cerr << "Wrong set value, expected 9001, got " << ret << endl;
+ return TestFail;
+ }
+
+ ret = exit();
+ if (ret < 0) {
+ cerr << "Failed to exit: " << strerror(-ret) << endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+
+private:
+ ProcessManager processManager_;
+
+ std::unique_ptr<IPAIPCUnixSocket> ipc_;
+};
+
+/*
+ * Can't use TEST_REGISTER() as single binary needs to act as both proxy
+ * master and slave.
+ */
+int main(int argc, char **argv)
+{
+ /* IPAIPCUnixSocket passes IPA module path in argv[1] */
+ if (argc == 3) {
+ int ipcfd = std::stoi(argv[2]);
+ UnixSocketTestIPCSlave slave;
+ return slave.run(ipcfd);
+ }
+
+ return UnixSocketTestIPC().execute();
+}
Test the IPC functions of IPAIPCUnixSocket. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> --- Changes in v3: - use readHeader, writeHeader, and eraseHeader as static class functions of IPAIPCUnixSocket New in v2 --- test/ipc/meson.build | 3 +- test/ipc/unixsocket_ipc.cpp | 238 ++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 test/ipc/unixsocket_ipc.cpp