Files
external_libcamera/test/ipa/ipa_interface_test.cpp
Laurent Pinchart bcaed97380 test: ipa: ipa_interface: Replace FIFO with pipe
The ipa_interface unit test uses a FIFO to communicate with the vimc IPA
module. FIFOs are named pipes, created in the file system. The path to
the FIFO is hardcoded so that both the unit test and IPA module know
where to locate the file.

If the ipa_interface crashes for any reason, the FIFO will not be
removed, and subsequent usage of the vimc IPA module will hang when
trying to write to the FIFO in the IPA module.

Fix this by replacing the FIFO with a pipe. Pipes are unidirectional
data channels that are represented by a pair of file descriptors,
without any presence in the file system. The write end of the pipe is
passed to the vimc IPA module init() function, and then used the same
way as the FIFO.

While at it, use a std::unique_ptr to manage the notifier in the unit
test instead of manually allocating and deleting the object.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>
2026-04-24 01:17:47 +03:00

181 lines
4.5 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* Test the IPA interface
*/
#include <fcntl.h>
#include <iostream>
#include <memory>
#include <string.h>
#include <unistd.h>
#include <libcamera/ipa/vimc_ipa_proxy.h>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/event_notifier.h>
#include <libcamera/base/object.h>
#include <libcamera/base/shared_fd.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
#include <libcamera/base/unique_fd.h>
#include "libcamera/internal/camera_manager.h"
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/global_configuration.h"
#include "libcamera/internal/ipa_manager.h"
#include "libcamera/internal/ipa_module.h"
#include "libcamera/internal/pipeline_handler.h"
#include "test.h"
using namespace libcamera;
using namespace std;
using namespace std::chrono_literals;
class IPAInterfaceTest : public Test, public Object
{
public:
IPAInterfaceTest()
: trace_(ipa::vimc::IPAOperationNone)
{
}
~IPAInterfaceTest()
{
notifier_.reset();
ipa_.reset();
ipaManager_.reset();
config_.reset();
cameraManager_.reset();
}
protected:
int init() override
{
cameraManager_ = make_unique<CameraManager>();
/* Create a pipeline handler for vimc. */
const std::vector<PipelineHandlerFactoryBase *> &factories =
PipelineHandlerFactoryBase::factories();
for (const PipelineHandlerFactoryBase *factory : factories) {
if (factory->name() == "vimc") {
pipe_ = factory->create(cameraManager_.get());
break;
}
}
if (!pipe_) {
cerr << "Vimc pipeline not found" << endl;
return TestPass;
}
/* Create the communication pipe. */
int pipefds[2];
int ret = pipe2(pipefds, O_NONBLOCK);
if (ret) {
ret = errno;
cerr << "Failed to create IPA test pipe: " << strerror(ret)
<< endl;
return TestFail;
}
pipeReadFd_ = UniqueFD(pipefds[0]);
pipeWriteFd_ = SharedFD(pipefds[1]);
notifier_ = std::make_unique<EventNotifier>(pipeReadFd_.get(),
EventNotifier::Read,
this);
notifier_->activated.connect(this, &IPAInterfaceTest::readTrace);
/* Create the IPA manager. */
config_ = std::make_unique<GlobalConfiguration>();
ipaManager_ = std::make_unique<IPAManager>(*config_);
return TestPass;
}
int run() override
{
EventDispatcher *dispatcher = thread()->eventDispatcher();
Timer timer;
ipa_ = ipaManager_->createIPA<ipa::vimc::IPAProxyVimc>(pipe_.get(), 0, 0);
if (!ipa_) {
cerr << "Failed to create VIMC IPA interface" << endl;
return TestFail;
}
/* Test initialization of IPA module. */
std::string conf = ipa_->configurationFile("vimc.conf");
Flags<ipa::vimc::TestFlag> inFlags;
Flags<ipa::vimc::TestFlag> outFlags;
int ret = ipa_->init(IPASettings{ conf, "vimc" }, pipeWriteFd_,
ipa::vimc::IPAOperationInit,
inFlags, &outFlags);
if (ret < 0) {
cerr << "IPA interface init() failed" << endl;
return TestFail;
}
timer.start(1000ms);
while (timer.isRunning() && trace_ != ipa::vimc::IPAOperationInit)
dispatcher->processEvents();
if (trace_ != ipa::vimc::IPAOperationInit) {
cerr << "Failed to test IPA initialization sequence"
<< endl;
return TestFail;
}
/* Test start of IPA module. */
ipa_->start();
timer.start(1000ms);
while (timer.isRunning() && trace_ != ipa::vimc::IPAOperationStart)
dispatcher->processEvents();
if (trace_ != ipa::vimc::IPAOperationStart) {
cerr << "Failed to test IPA start sequence" << endl;
return TestFail;
}
/* Test stop of IPA module. */
ipa_->stop();
timer.start(1000ms);
while (timer.isRunning() && trace_ != ipa::vimc::IPAOperationStop)
dispatcher->processEvents();
if (trace_ != ipa::vimc::IPAOperationStop) {
cerr << "Failed to test IPA stop sequence" << endl;
return TestFail;
}
return TestPass;
}
private:
void readTrace()
{
ssize_t s = read(notifier_->fd(), &trace_, sizeof(trace_));
if (s < 0) {
int ret = errno;
cerr << "Failed to read from IPA test pipe: " << strerror(ret)
<< endl;
trace_ = ipa::vimc::IPAOperationNone;
}
}
std::shared_ptr<PipelineHandler> pipe_;
std::unique_ptr<ipa::vimc::IPAProxyVimc> ipa_;
std::unique_ptr<CameraManager> cameraManager_;
std::unique_ptr<GlobalConfiguration> config_;
std::unique_ptr<IPAManager> ipaManager_;
enum ipa::vimc::IPAOperationCode trace_;
std::unique_ptr<EventNotifier> notifier_;
UniqueFD pipeReadFd_;
SharedFD pipeWriteFd_;
};
TEST_REGISTER(IPAInterfaceTest)