[libcamera-devel] [RFC PATCH 08/17] tests: IPC: Add various tests for the IPC framework
Paul Elder
paul.elder at ideasonboard.com
Wed Aug 26 13:09:17 CEST 2020
This patch includes tests that I used while debugging. I just kind of
plopped them here. If there's any value in these, I'll brush them up and
keep them, otherwise I'll discard them.
- unixsocket_fds confirms that passing fds over the process boundary
increases the fd count
- control_serialization_ipa tests de/serializing ControlList and
ControlInfoMap using IPADataSerializer
- rpi_action_serialization tests de/serialization of RPiActionParams
- rpi_config_serialization tests de/serialization of RPiConfigureParams
Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
---
test/ipa/meson.build | 4 +-
test/ipc/meson.build | 3 +-
test/ipc/unixsocket_fds.cpp | 284 ++++++++++++++++++
.../control_serialization_ipa.cpp | 85 ++++++
test/serialization/meson.build | 5 +-
.../rpi_action_serialization.cpp | 141 +++++++++
.../rpi_config_serialization.cpp | 162 ++++++++++
7 files changed, 680 insertions(+), 4 deletions(-)
create mode 100644 test/ipc/unixsocket_fds.cpp
create mode 100644 test/serialization/control_serialization_ipa.cpp
create mode 100644 test/serialization/rpi_action_serialization.cpp
create mode 100644 test/serialization/rpi_config_serialization.cpp
diff --git a/test/ipa/meson.build b/test/ipa/meson.build
index ba672f3f..b89c6b66 100644
--- a/test/ipa/meson.build
+++ b/test/ipa/meson.build
@@ -2,8 +2,8 @@
ipa_test = [
['ipa_module_test', 'ipa_module_test.cpp'],
- ['ipa_interface_test', 'ipa_interface_test.cpp'],
- ['ipa_wrappers_test', 'ipa_wrappers_test.cpp'],
+ #['ipa_interface_test', 'ipa_interface_test.cpp'],
+ #['ipa_wrappers_test', 'ipa_wrappers_test.cpp'],
]
foreach t : ipa_test
diff --git a/test/ipc/meson.build b/test/ipc/meson.build
index 650df1d6..e960aa82 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_fds', 'unixsocket_fds.cpp' ],
+ [ 'unixsocket', 'unixsocket.cpp' ],
]
foreach t : ipc_tests
diff --git a/test/ipc/unixsocket_fds.cpp b/test/ipc/unixsocket_fds.cpp
new file mode 100644
index 00000000..8e9c6864
--- /dev/null
+++ b/test/ipc/unixsocket_fds.cpp
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * unixsocket_fds.cpp - Unix socket IPC test with file descriptors
+ */
+
+#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/ipc_unixsocket.h"
+#include "libcamera/internal/thread.h"
+#include "libcamera/internal/utils.h"
+
+#include "test.h"
+
+#define CMD_CLOSE 0
+#define CMD_REVERSE 1
+#define CMD_LEN_CALC 2
+#define CMD_LEN_CMP 3
+#define CMD_JOIN 4
+
+using namespace std;
+using namespace libcamera;
+
+class UnixSocketTestFdSlave
+{
+public:
+ UnixSocketTestFdSlave()
+ : exitCode_(EXIT_FAILURE), exit_(false)
+ {
+ dispatcher_ = Thread::current()->eventDispatcher();
+ ipc_.readyRead.connect(this, &UnixSocketTestFdSlave::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;
+ }
+
+ int fd = message.fds[0];
+
+ cerr << "slave: received " << fd << endl;
+
+ response.fds = { fd };
+
+ ret = ipc_.send(response);
+ if (ret < 0) {
+ cerr << "Reply failed" << endl;
+ stop(ret);
+ }
+ }
+
+ void stop(int code)
+ {
+ exitCode_ = code;
+ exit_ = true;
+ }
+
+ IPCUnixSocket ipc_;
+ EventDispatcher *dispatcher_;
+ int exitCode_;
+ bool exit_;
+};
+
+class UnixSocketTestFd : public Test
+{
+protected:
+ int slaveStart(int fd)
+ {
+ pid_ = fork();
+
+ if (pid_ == -1)
+ return TestFail;
+
+ if (!pid_) {
+ std::string arg = std::to_string(fd);
+ execl("/proc/self/exe", "/proc/self/exe",
+ arg.c_str(), nullptr);
+
+ /* Only get here if exec fails. */
+ exit(TestFail);
+ }
+
+ return TestPass;
+ }
+
+ int slaveStop()
+ {
+ int status;
+
+ if (pid_ < 0)
+ return TestFail;
+
+ if (waitpid(pid_, &status, 0) < 0)
+ return TestFail;
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ return TestFail;
+
+ return TestPass;
+ }
+
+ int testSendNewFd()
+ {
+ IPCUnixSocket::Payload message, response;
+ int ret;
+
+ int fd = open("/dev/null", O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ cerr << "Failed to open /dev/null: " << strerror(-ret) << endl;
+ return ret;
+ }
+
+ cerr << "master: opened and sending fd " << fd << endl;
+
+ message.fds = { fd };
+
+ ret = call(message, &response);
+ if (ret)
+ return ret;
+
+ cerr << "master: received fd " << response.fds[0] << endl;
+
+ return 0;
+ }
+
+ int testSendOldFd(int fd)
+ {
+ IPCUnixSocket::Payload message, response;
+ int ret;
+
+ cerr << "master: sending fd " << fd << endl;
+
+ message.fds = { fd };
+
+ ret = call(message, &response);
+ if (ret)
+ return ret;
+
+ cerr << "master: received fd " << response.fds[0] << endl;
+
+ return 0;
+ }
+
+ int init()
+ {
+ callResponse_ = nullptr;
+ return 0;
+ }
+
+ int run()
+ {
+ int slavefd = ipc_.create();
+ if (slavefd < 0)
+ return TestFail;
+
+ if (slaveStart(slavefd)) {
+ cerr << "Failed to start slave" << endl;
+ return TestFail;
+ }
+
+ ipc_.readyRead.connect(this, &UnixSocketTestFd::readyRead);
+
+ for (int i = 0; i < 10; i++)
+ testSendNewFd();
+
+ for (int i = 0; i < 10; i++)
+ testSendOldFd(20);
+
+ /* Close slave connection. */
+ IPCUnixSocket::Payload close;
+ close.data.push_back(CMD_CLOSE);
+ if (ipc_.send(close)) {
+ cerr << "Closing IPC channel failed" << endl;
+ return TestFail;
+ }
+
+ ipc_.close();
+ if (slaveStop()) {
+ cerr << "Failed to stop slave" << endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+
+private:
+ int call(const IPCUnixSocket::Payload &message, IPCUnixSocket::Payload *response)
+ {
+ Timer timeout;
+ int ret;
+
+ callDone_ = false;
+ callResponse_ = response;
+
+ ret = ipc_.send(message);
+ if (ret)
+ return ret;
+
+ timeout.start(200);
+ while (!callDone_) {
+ if (!timeout.isRunning()) {
+ cerr << "Call timeout!" << endl;
+ callResponse_ = nullptr;
+ return -ETIMEDOUT;
+ }
+
+ Thread::current()->eventDispatcher()->processEvents();
+ }
+
+ callResponse_ = nullptr;
+
+ return 0;
+ }
+
+ void readyRead(IPCUnixSocket *ipc)
+ {
+ if (!callResponse_) {
+ cerr << "Read ready without expecting data, fail." << endl;
+ return;
+ }
+
+ if (ipc->receive(callResponse_)) {
+ cerr << "Receive message failed" << endl;
+ return;
+ }
+
+ callDone_ = true;
+ }
+
+ pid_t pid_;
+ IPCUnixSocket ipc_;
+ bool callDone_;
+ IPCUnixSocket::Payload *callResponse_;
+};
+
+/*
+ * Can't use TEST_REGISTER() as single binary needs to act as both proxy
+ * master and slave.
+ */
+int main(int argc, char **argv)
+{
+ if (argc == 2) {
+ int ipcfd = std::stoi(argv[1]);
+ UnixSocketTestFdSlave slave;
+ return slave.run(ipcfd);
+ }
+
+ return UnixSocketTestFd().execute();
+}
diff --git a/test/serialization/control_serialization_ipa.cpp b/test/serialization/control_serialization_ipa.cpp
new file mode 100644
index 00000000..88489326
--- /dev/null
+++ b/test/serialization/control_serialization_ipa.cpp
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * control_serialization_ipa.cpp - Serialize and deserialize controls with IPADataSerializer
+ */
+
+#include <fcntl.h>
+#include <iostream>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <tuple>
+#include <unistd.h>
+#include <vector>
+
+#include <libcamera/ipa/raspberrypi.h>
+#include <libcamera/timer.h>
+
+#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+#include "libcamera/internal/ipa_manager.h"
+#include "libcamera/internal/ipa_module.h"
+#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/thread.h"
+
+#include "serialization_test.h"
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+
+class ControlSerializationIPATest : public CameraTest, public Test
+{
+public:
+ ControlSerializationIPATest()
+ : CameraTest("imx219")
+ {
+ }
+
+protected:
+ int init() override
+ {
+ return status_;
+ }
+
+ int run() override
+ {
+ ControlSerializer cs;
+
+ /* Create a control list with three controls. */
+ const ControlInfoMap &infoMap = camera_->controls();
+ ControlList list(infoMap);
+
+ list.set(controls::Brightness, 0.5f);
+ list.set(controls::Contrast, 1.2f);
+ list.set(controls::Saturation, 0.2f);
+
+ vector<uint8_t> infoMapBuf;
+ tie(infoMapBuf, ignore) = IPADataSerializer<const ControlInfoMap>::serialize(infoMap, &cs);
+
+ vector<uint8_t> listBuf;
+ tie(listBuf, ignore) = IPADataSerializer<ControlList>::serialize(list, *list.infoMap(), &cs);
+
+
+ const ControlInfoMap infoMapOut = IPADataSerializer<const ControlInfoMap>::deserialize(infoMapBuf, &cs);
+
+ ControlList listOut = IPADataSerializer<ControlList>::deserialize(listBuf, &cs);
+
+
+ if (!SerializationTest::equals(infoMap, infoMapOut)) {
+ cerr << "Deserialized map doesn't match original" << endl;
+ return TestFail;
+ }
+
+ if (!SerializationTest::equals(list, listOut)) {
+ cerr << "Deserialized list doesn't match original" << endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(ControlSerializationIPATest)
diff --git a/test/serialization/meson.build b/test/serialization/meson.build
index a9d9cbcb..ecd07adf 100644
--- a/test/serialization/meson.build
+++ b/test/serialization/meson.build
@@ -1,7 +1,10 @@
# SPDX-License-Identifier: CC0-1.0
serialization_tests = [
- [ 'control_serialization', 'control_serialization.cpp' ],
+ [ 'control_serialization_ipa', 'control_serialization_ipa.cpp' ],
+ [ 'control_serialization', 'control_serialization.cpp' ],
+ [ 'rpi_config_serialization', 'rpi_config_serialization.cpp' ],
+ [ 'rpi_action_serialization', 'rpi_action_serialization.cpp' ],
]
foreach t : serialization_tests
diff --git a/test/serialization/rpi_action_serialization.cpp b/test/serialization/rpi_action_serialization.cpp
new file mode 100644
index 00000000..de9db03f
--- /dev/null
+++ b/test/serialization/rpi_action_serialization.cpp
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * rpi_action_serialization.cpp - Serialize and deserialize raspberrypi IPA action data
+ */
+
+#include <fcntl.h>
+#include <iostream>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <tuple>
+#include <unistd.h>
+#include <vector>
+
+#include <libcamera/ipa/raspberrypi.h>
+#include <libcamera/ipa/raspberrypi_serializer.h>
+#include <libcamera/ipa/raspberrypi_wrapper.h>
+#include <libcamera/timer.h>
+
+#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+#include "libcamera/internal/ipa_manager.h"
+#include "libcamera/internal/ipa_module.h"
+#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/thread.h"
+
+#include "serialization_test.h"
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+
+class RPiActionSerializationTest : public CameraTest, public Test
+{
+public:
+ RPiActionSerializationTest()
+ : CameraTest("imx219")
+ {
+ }
+
+protected:
+ int init() override
+ {
+ return status_;
+ }
+
+ int run() override
+ {
+ ControlSerializer cs;
+
+ RPiConfigureParams ipaConfig;
+
+ const ControlInfoMap &infoMap1 = camera_->controls();
+ ControlList list1(infoMap1);
+ list1.set(controls::Brightness, 0.5f);
+ list1.set(controls::Contrast, 1.2f);
+ list1.set(controls::Saturation, 0.2f);
+
+ const ControlInfoMap &infoMap2 = camera_->controls();
+ ControlList list2(infoMap2);
+ list2.set(controls::Brightness, -0.8f);
+ list2.set(controls::Contrast, 2.1f);
+ list2.set(controls::Saturation, 1.1f);
+
+ RPiStatsCompletePayload s;
+ s.bufferId_ = 111;
+ s.controls_ = list2;
+
+ RPiActionParams action;
+ action.op_ = RPI_IPA_ACTION_RUN_ISP;
+ action.bufferId_ = 222;
+ action.statsComplete_ = s;
+ action.controls_ = list1;
+
+
+ vector<uint8_t> buf;
+ tie(buf, std::ignore) = IPADataSerializer<RPiActionParams>::serialize(action, &cs);
+
+ RPiActionParams actionOut = IPADataSerializer<RPiActionParams>::deserialize(buf, &cs);
+
+
+ if (!equals(action, actionOut)) {
+ cerr << "Deserialized action doesn't match original" << endl;
+ return TestFail;
+ } else {
+ cerr << "Pass!" << endl;
+ }
+
+ return TestPass;
+ }
+
+private:
+ bool equals(const RPiStatsCompletePayload &lhs, const RPiStatsCompletePayload &rhs)
+ {
+ bool matches = true;
+ if (lhs.bufferId_ != rhs.bufferId_)
+ matches = false;
+
+ if (!SerializationTest::equals(lhs.controls_, rhs.controls_)) {
+ matches = false;
+ cerr << "controls in stats complete not matching" << endl;
+ }
+
+ return matches;
+ }
+
+ bool equals(const RPiActionParams &lhs, const RPiActionParams &rhs)
+ {
+ bool matches = true;
+
+ if (lhs.op_ != rhs.op_) {
+ matches = false;
+ cerr << "op_ expected " << lhs.op_ << " got " << rhs.op_;
+ }
+
+ if (lhs.bufferId_ != rhs.bufferId_) {
+ matches = false;
+ cerr << "bufferId_ expected " << lhs.bufferId_ << " got " << rhs.bufferId_;
+ }
+
+ if (!equals(lhs.statsComplete_, rhs.statsComplete_)) {
+ matches = false;
+ cerr << "statsComplete_ expected {"
+ << lhs.statsComplete_.bufferId_ << ", and some controls}"
+ << " got {"
+ << rhs.statsComplete_.bufferId_ << ", and some controls}" << endl;
+ }
+
+ if (!SerializationTest::equals(lhs.controls_, rhs.controls_)) {
+ matches = false;
+ cerr << "controls not matching" << endl;
+ }
+
+ return matches;
+ }
+
+};
+
+TEST_REGISTER(RPiActionSerializationTest)
diff --git a/test/serialization/rpi_config_serialization.cpp b/test/serialization/rpi_config_serialization.cpp
new file mode 100644
index 00000000..390579f1
--- /dev/null
+++ b/test/serialization/rpi_config_serialization.cpp
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * rpi_config_serialization.cpp - Serialize and deserialize raspberrypi IPA configuration data
+ */
+
+#include <fcntl.h>
+#include <iostream>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <tuple>
+#include <unistd.h>
+#include <vector>
+
+#include <libcamera/ipa/raspberrypi.h>
+#include <libcamera/ipa/raspberrypi_serializer.h>
+#include <libcamera/ipa/raspberrypi_wrapper.h>
+#include <libcamera/timer.h>
+
+#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+#include "libcamera/internal/ipa_manager.h"
+#include "libcamera/internal/ipa_module.h"
+#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/thread.h"
+
+#include "serialization_test.h"
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+
+class RPiConfigSerializationTest : public CameraTest, public Test
+{
+public:
+ RPiConfigSerializationTest()
+ : CameraTest("imx219")
+ {
+ }
+
+protected:
+ int init() override
+ {
+ return status_;
+ }
+
+ int run() override
+ {
+ ControlSerializer cs;
+
+ RPiConfigureParams ipaConfig;
+
+ const ControlInfoMap &infoMap = camera_->controls();
+ ControlList list(infoMap);
+ list.set(controls::Brightness, 0.5f);
+ list.set(controls::Contrast, 1.2f);
+ list.set(controls::Saturation, 0.2f);
+
+
+ RPiConfigurePayload p1;
+ p1.op_ = RPI_IPA_CONFIG_LS_TABLE;
+ p1.lsTableHandle_ = FileDescriptor(222);
+
+ RPiConfigurePayload p2;
+ p2.op_ = RPI_IPA_CONFIG_STAGGERED_WRITE;
+ p2.staggeredWriteResult_ = RPiStaggeredWritePayload(6, 6, 6);
+
+ RPiConfigurePayload p3;
+ p3.op_ = RPI_IPA_CONFIG_SENSOR;
+ p3.controls_ = list;
+
+ ipaConfig.payload_.push_back(p1);
+ ipaConfig.payload_.push_back(p2);
+ ipaConfig.payload_.push_back(p3);
+
+ vector<uint8_t> buf;
+ vector<int32_t> fds;
+ tie(buf, fds) = IPADataSerializer<RPiConfigureParams>::serialize(ipaConfig, &cs);
+
+ RPiConfigureParams ipaConfigOut = IPADataSerializer<RPiConfigureParams>::deserialize(buf, fds, &cs);
+
+
+ if (!equals(ipaConfig, ipaConfigOut)) {
+ cerr << "Deserialized config doesn't match original" << endl;
+ return TestFail;
+ } else {
+ cerr << "Pass!" << endl;
+ }
+
+ return TestPass;
+ }
+
+private:
+ bool equals(const RPiStaggeredWritePayload &lhs, const RPiStaggeredWritePayload &rhs)
+ {
+ return lhs.gainDelay_ == rhs.gainDelay_ &&
+ lhs.exposureDelay_ == rhs.exposureDelay_ &&
+ lhs.sensorMetadata_ == rhs.sensorMetadata_;
+ }
+
+ bool equals(const RPiConfigurePayload &lhs, const RPiConfigurePayload &rhs)
+ {
+ bool matches = true;
+
+ if (lhs.op_ != rhs.op_) {
+ matches = false;
+ cerr << "expected " << lhs.op_ << " got " << rhs.op_;
+ }
+
+ if (lhs.lsTableHandle_.fd() != rhs.lsTableHandle_.fd()) {
+ matches = false;
+ cerr << "expected " << lhs.lsTableHandle_.fd() << " got " << rhs.lsTableHandle_.fd();
+ }
+
+ if (lhs.bufferFd_ != rhs.bufferFd_) {
+ matches = false;
+ cerr << "expected " << lhs.bufferFd_ << " got " << rhs.bufferFd_;
+ }
+
+ if (!equals(lhs.staggeredWriteResult_, rhs.staggeredWriteResult_)) {
+ matches = false;
+ cerr << "expected {"
+ << lhs.staggeredWriteResult_.gainDelay_ << ", "
+ << lhs.staggeredWriteResult_.exposureDelay_ << ", "
+ << lhs.staggeredWriteResult_.sensorMetadata_ << "} "
+ << " got {"
+ << rhs.staggeredWriteResult_.gainDelay_ << ", "
+ << rhs.staggeredWriteResult_.exposureDelay_ << ", "
+ << rhs.staggeredWriteResult_.sensorMetadata_ << "} ";
+ }
+
+ if (!SerializationTest::equals(lhs.controls_, rhs.controls_)) {
+ matches = false;
+ }
+
+ return matches;
+ }
+
+ bool equals(const RPiConfigureParams &lhs, const RPiConfigureParams &rhs)
+ {
+ bool matches = true;
+
+ if (lhs.payload_.size() != rhs.payload_.size()) {
+ cerr << "non-matching size" << endl;
+ return false;
+ }
+
+ size_t len = lhs.payload_.size();
+ for (unsigned int i = 0; i < len; i++) {
+ cerr << "[" << i << "]: " << endl;
+ if (!equals(lhs.payload_[i], rhs.payload_[i]))
+ matches = false;
+ }
+
+ return matches;
+ }
+
+};
+
+TEST_REGISTER(RPiConfigSerializationTest)
--
2.27.0
More information about the libcamera-devel
mailing list