spidermqtt/spider-cam/libcamera/test/ipc/unixsocket_ipc.cpp

234 lines
4.3 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2020, Google Inc.
*
* Unix socket IPC test
*/
#include <algorithm>
#include <fcntl.h>
#include <iostream>
#include <limits.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
#include <libcamera/base/utils.h>
#include "libcamera/internal/ipa_data_serializer.h"
#include "libcamera/internal/ipc_pipe.h"
#include "libcamera/internal/ipc_pipe_unixsocket.h"
#include "libcamera/internal/process.h"
#include "test.h"
using namespace std;
using namespace libcamera;
enum {
CmdExit = 0,
CmdGetSync = 1,
CmdSetAsync = 2,
};
const int32_t kInitialValue = 1337;
const int32_t kChangedValue = 9001;
class UnixSocketTestIPCSlave
{
public:
UnixSocketTestIPCSlave()
: value_(kInitialValue), exitCode_(EXIT_FAILURE), exit_(false)
{
dispatcher_ = Thread::current()->eventDispatcher();
ipc_.readyRead.connect(this, &UnixSocketTestIPCSlave::readyRead);
}
int run(UniqueFD fd)
{
if (ipc_.bind(std::move(fd))) {
cerr << "Failed to connect to IPC channel" << endl;
return EXIT_FAILURE;
}
while (!exit_)
dispatcher_->processEvents();
ipc_.close();
return exitCode_;
}
private:
void readyRead()
{
IPCUnixSocket::Payload message;
int ret;
ret = ipc_.receive(&message);
if (ret) {
cerr << "Receive message failed: " << ret << endl;
return;
}
IPCMessage ipcMessage(message);
uint32_t cmd = ipcMessage.header().cmd;
switch (cmd) {
case CmdExit: {
exit_ = true;
break;
}
case CmdGetSync: {
IPCMessage::Header header = { cmd, ipcMessage.header().cookie };
IPCMessage response(header);
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.payload());
if (ret < 0) {
cerr << "Reply failed" << endl;
stop(ret);
}
break;
}
case CmdSetAsync: {
value_ = IPADataSerializer<int32_t>::deserialize(ipcMessage.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 setValue(int32_t val)
{
IPCMessage msg(CmdSetAsync);
tie(msg.data(), ignore) = IPADataSerializer<int32_t>::serialize(val);
int ret = ipc_->sendAsync(msg);
if (ret < 0) {
cerr << "Failed to call set value" << endl;
return ret;
}
return 0;
}
int getValue()
{
IPCMessage msg(CmdGetSync);
IPCMessage buf;
int ret = ipc_->sendSync(msg, &buf);
if (ret < 0) {
cerr << "Failed to call get value" << endl;
return ret;
}
return IPADataSerializer<int32_t>::deserialize(buf.data());
}
int exit()
{
IPCMessage msg(CmdExit);
int ret = ipc_->sendAsync(msg);
if (ret < 0) {
cerr << "Failed to call exit" << endl;
return ret;
}
return 0;
}
int run()
{
ipc_ = std::make_unique<IPCPipeUnixSocket>("", self().c_str());
if (!ipc_->isConnected()) {
cerr << "Failed to create IPCPipe" << endl;
return TestFail;
}
int ret = getValue();
if (ret != kInitialValue) {
cerr << "Wrong initial value, expected "
<< kInitialValue << ", got " << ret << endl;
return TestFail;
}
ret = setValue(kChangedValue);
if (ret < 0) {
cerr << "Failed to set value: " << strerror(-ret) << endl;
return TestFail;
}
ret = getValue();
if (ret != kChangedValue) {
cerr << "Wrong set value, expected " << kChangedValue
<< ", got " << ret << endl;
return TestFail;
}
ret = exit();
if (ret < 0) {
cerr << "Failed to exit: " << strerror(-ret) << endl;
return TestFail;
}
return TestPass;
}
private:
ProcessManager processManager_;
unique_ptr<IPCPipeUnixSocket> ipc_;
};
/*
* Can't use TEST_REGISTER() as single binary needs to act as both client and
* server
*/
int main(int argc, char **argv)
{
/* IPCPipeUnixSocket passes IPA module path in argv[1] */
if (argc == 3) {
UniqueFD ipcfd = UniqueFD(std::stoi(argv[2]));
UnixSocketTestIPCSlave slave;
return slave.run(std::move(ipcfd));
}
UnixSocketTestIPC test;
test.setArgs(argc, argv);
return test.execute();
}