[libcamera-devel,v9,0/3] IPA isolation tests
mbox series

Message ID 20210301065226.11095-1-paul.elder@ideasonboard.com
Headers show
Series
  • IPA isolation tests
Related show

Message

Paul Elder March 1, 2021, 6:52 a.m. UTC
This used to be part 3 of the IPA isolation series. The documentation
has been broken out (v8 waiting for review), and this series only
contains tests for IPA isolation.

1/3 tests the IPADataSerializer and 2/3 tests the IPCUnixSocket, both
in the same manner as how the generated IPA proxies would use them.

3/3 tests the generated serializer. v7 adds a test to test
(de)serialization of a vector of *generated* structs.

Changes in v9:
- just lots of style fixes in the tests

Changes in v8:
- (from v7.1) fix bullet points and update wordings in the ipa writer
  guide

Changes in v7:
- add test to test serdes of a vector of *generated* structs
- remove printing values of vectors/maps
- use the new sendSync/sendAsync API
- update IPA guide

Changes in v6:
- no longer need to initialize rpi ControlInfoMap, and no longer
  necessary it pass it to the ControlList serializer
- update documentation about the required namespacing, customizable
  start(), and that {pipeline_name}.h is no longer required
- use namespacing in the mojom file and test


Paul Elder (4):
  tests: Add IPADataSerializer test
  tests: Add test for IPCPipeUnixSocket
  Documentation: Add IPA writers guide
  tests: Test IPA serializer generation

 Documentation/guides/ipa.rst                  | 474 ++++++++++++++++++
 Documentation/index.rst                       |   1 +
 Documentation/meson.build                     |   1 +
 test/ipc/meson.build                          |   3 +-
 test/ipc/unixsocket_ipc.cpp                   | 233 +++++++++
 .../generated_serializer_test.cpp             | 156 ++++++
 .../generated_serializer/meson.build          |  49 ++
 .../generated_serializer/vimc.mojom           |  33 ++
 .../ipa_data_serializer_test.cpp              | 378 ++++++++++++++
 test/serialization/meson.build                |   5 +-
 10 files changed, 1331 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/guides/ipa.rst
 create mode 100644 test/ipc/unixsocket_ipc.cpp
 create mode 100644 test/serialization/generated_serializer/generated_serializer_test.cpp
 create mode 100644 test/serialization/generated_serializer/meson.build
 create mode 100644 test/serialization/generated_serializer/vimc.mojom
 create mode 100644 test/serialization/ipa_data_serializer_test.cpp

Comments

Laurent Pinchart March 2, 2021, 12:55 a.m. UTC | #1
On Mon, Mar 01, 2021 at 03:52:25PM +0900, Paul Elder wrote:
> Test the IPC functions of IPCPipeUnixSocket.
> 
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> ---
> Changes in v9:
> - change cmd codes to enum
> - rename setVal and getVal to setValue and getValue
> - remove readlink, and use "/proc/self/exe" directly
> - make the test values into consts
> 
> No change in v8
> 
> Changes in v7:
> - use the new sendSync/sendAsync API
> - remove redundant error messages
> - use PATH_MAX instead of 100
> 
> No change in v6
> 
> Changes in v5:
> - rename IPAIPCUnixSocket to IPCPipeUnixSocket
> - use IPCMessage
> 
> No change in v4
> 
> 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 | 229 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 231 insertions(+), 1 deletion(-)
>  create mode 100644 test/ipc/unixsocket_ipc.cpp
> 
> diff --git a/test/ipc/meson.build b/test/ipc/meson.build
> index 9f413ff6..ad47b2fe 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..5c9f7938
> --- /dev/null
> +++ b/test/ipc/unixsocket_ipc.cpp
> @@ -0,0 +1,229 @@
> +/* 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 <limits.h>
> +#include <stdlib.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +
> +#include "libcamera/internal/event_dispatcher.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 "libcamera/internal/thread.h"
> +#include "libcamera/internal/timer.h"
> +#include "libcamera/internal/utils.h"
> +
> +#include "test.h"
> +
> +
> +using namespace std;
> +using namespace libcamera;
> +
> +enum cmd {

You can omit the type name, or called it Cmd as we Capitalize type
names.

> +	CmdExit = 0,
> +	CmdGetSync = 1,
> +	CmdSetAsync = 2,
> +};
> +
> +const int32_t initialValue = 1337;
> +const int32_t changedValue = 9001;

Maybe kInitialValue and kChangedValue ?

> +
> +class UnixSocketTestIPCSlave
> +{
> +public:
> +	UnixSocketTestIPCSlave()
> +		: value_(initialValue), 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;
> +		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>("", "/proc/self/exe");
> +		if (!ipc_->isConnected()) {
> +			cerr << "Failed to create IPCPipe" << endl;
> +			return TestFail;
> +		}
> +
> +		int ret = getValue();
> +		if (ret != initialValue) {
> +			cerr << "Wrong inital value, expected " << initialValue << ", got " << ret << endl;

Line wrap ?

> +			return TestFail;
> +		}
> +
> +		ret = setValue(changedValue);
> +		if (ret < 0) {
> +			cerr << "Failed to set value: " << strerror(-ret) << endl;
> +			return TestFail;
> +		}
> +
> +		ret = getValue();
> +		if (ret != changedValue) {
> +			cerr << "Wrong set value, expected " << changedValue << ", got " << ret << endl;

Same here.

> +			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) {
> +		int ipcfd = std::stoi(argv[2]);
> +		UnixSocketTestIPCSlave slave;
> +		return slave.run(ipcfd);
> +	}
> +
> +	return UnixSocketTestIPC().execute();
> +}