[libcamera-devel,38/38] tests: Add test for IPAIPCUnixSocket

Message ID 20200922133537.258098-39-paul.elder@ideasonboard.com
State Superseded
Headers show
Series
  • IPA isolation implementation
Related show

Commit Message

Paul Elder Sept. 22, 2020, 1:35 p.m. UTC
Test the IPC functions of IPAIPCUnixSocket.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
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

Patch

diff --git a/test/ipc/meson.build b/test/ipc/meson.build
index 650df1d6..ecb6d022 100644
--- a/test/ipc/meson.build
+++ b/test/ipc/meson.build
@@ -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
diff --git a/test/ipc/unixsocket_ipc.cpp b/test/ipc/unixsocket_ipc.cpp
new file mode 100644
index 00000000..1f2d2815
--- /dev/null
+++ b/test/ipc/unixsocket_ipc.cpp
@@ -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) = readHeader(message);
+		eraseHeader(message);
+
+		switch (cmd) {
+		case CMD_EXIT: {
+		       exit_ = true;
+		       break;
+		}
+
+		case CMD_GET_SYNC: {
+			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();
+}