Message ID | 20191108205409.18845-24-laurent.pinchart@ideasonboard.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Hi Laurent, Thanks for your patch. On 2019-11-08 22:54:08 +0200, Laurent Pinchart wrote: > Wrap an IPAInterface in an IPAInterfaceWrapper in an IPAContextWrapper, > and verify that the translation between C and C++ APIs work correctly. > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> > --- > test/ipa/ipa_wrappers_test.cpp | 390 +++++++++++++++++++++++++++++++++ > test/ipa/meson.build | 5 +- > 2 files changed, 393 insertions(+), 2 deletions(-) > create mode 100644 test/ipa/ipa_wrappers_test.cpp > > diff --git a/test/ipa/ipa_wrappers_test.cpp b/test/ipa/ipa_wrappers_test.cpp > new file mode 100644 > index 000000000000..c0717f0e301b > --- /dev/null > +++ b/test/ipa/ipa_wrappers_test.cpp > @@ -0,0 +1,390 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2019, Google Inc. > + * > + * ipa_wrappers_test.cpp - Test the IPA interface and context wrappers > + */ > + > +#include <fcntl.h> > +#include <iostream> > +#include <memory> > +#include <linux/videodev2.h> > +#include <sys/stat.h> > +#include <unistd.h> > + > +#include <libcamera/controls.h> > +#include <libipa/ipa_interface_wrapper.h> > + > +#include "device_enumerator.h" > +#include "ipa_context_wrapper.h" > +#include "media_device.h" > +#include "utils.h" > +#include "v4l2_subdevice.h" > + > +#include "test.h" > + > +using namespace libcamera; > +using namespace std; > + > +enum Operation { > + Op_init, > + Op_configure, > + Op_mapBuffers, > + Op_unmapBuffers, > + Op_processEvent, > +}; > + > +class TestIPAInterface : public IPAInterface > +{ > +public: > + TestIPAInterface() > + : sequence_(0) > + { > + } > + > + int init() override > + { > + report(Op_init, TestPass); > + return 0; > + } > + > + void configure(const std::map<unsigned int, IPAStream> &streamConfig, > + const std::map<unsigned int, const ControlInfoMap &> &entityControls) override > + { > + /* Verify streamConfig. */ > + if (streamConfig.size() != 2) { > + cerr << "configure(): Invalid number of streams " > + << streamConfig.size() << endl; > + return report(Op_configure, TestFail); > + } > + > + auto iter = streamConfig.find(1); > + if (iter == streamConfig.end()) { > + cerr << "configure(): No configuration for stream 1" << endl; > + return report(Op_configure, TestFail); > + } > + const IPAStream *stream = &iter->second; > + if (stream->pixelFormat != V4L2_PIX_FMT_YUYV || > + stream->size != Size{ 1024, 768 }) { > + cerr << "configure(): Invalid configuration for stream 1" << endl; > + return report(Op_configure, TestFail); > + } > + > + iter = streamConfig.find(2); > + if (iter == streamConfig.end()) { > + cerr << "configure(): No configuration for stream 2" << endl; > + return report(Op_configure, TestFail); > + } > + stream = &iter->second; > + if (stream->pixelFormat != V4L2_PIX_FMT_NV12 || > + stream->size != Size{ 800, 600 }) { > + cerr << "configure(): Invalid configuration for stream 2" << endl; > + return report(Op_configure, TestFail); > + } > + > + /* Verify entityControls. */ > + auto ctrlIter = entityControls.find(42); > + if (ctrlIter == entityControls.end()) { > + cerr << "configure(): Controls not found" << endl; > + return report(Op_configure, TestFail); > + } > + > + const ControlInfoMap &infoMap = ctrlIter->second; > + > + if (infoMap.count(V4L2_CID_BRIGHTNESS) != 1 || > + infoMap.count(V4L2_CID_CONTRAST) != 1 || > + infoMap.count(V4L2_CID_SATURATION) != 1) { > + cerr << "configure(): Invalid control IDs" << endl; > + return report(Op_configure, TestFail); > + } > + > + report(Op_configure, TestPass); > + } > + > + void mapBuffers(const std::vector<IPABuffer> &buffers) override > + { > + if (buffers.size() != 2) { > + cerr << "mapBuffers(): Invalid number of buffers " > + << buffers.size() << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + if (buffers[0].id != 10 || > + buffers[1].id != 11) { > + cerr << "mapBuffers(): Invalid buffer IDs" << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + if (buffers[0].memory.planes().size() != 3 || > + buffers[1].memory.planes().size() != 3) { > + cerr << "mapBuffers(): Invalid number of planes" << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + if (buffers[0].memory.planes()[0].length() != 4096 || > + buffers[0].memory.planes()[1].length() != 0 || > + buffers[0].memory.planes()[2].length() != 0 || > + buffers[0].memory.planes()[0].length() != 4096 || > + buffers[1].memory.planes()[1].length() != 4096 || > + buffers[1].memory.planes()[2].length() != 0) { > + cerr << "mapBuffers(): Invalid length" << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + if (buffers[0].memory.planes()[0].dmabuf() == -1 || > + buffers[0].memory.planes()[1].dmabuf() != -1 || > + buffers[0].memory.planes()[2].dmabuf() != -1 || > + buffers[0].memory.planes()[0].dmabuf() == -1 || > + buffers[1].memory.planes()[1].dmabuf() == -1 || > + buffers[1].memory.planes()[2].dmabuf() != -1) { > + cerr << "mapBuffers(): Invalid dmabuf" << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + report(Op_mapBuffers, TestPass); > + } > + > + void unmapBuffers(const std::vector<unsigned int> &ids) override > + { > + if (ids.size() != 2) { > + cerr << "unmapBuffers(): Invalid number of ids " > + << ids.size() << endl; > + return report(Op_unmapBuffers, TestFail); > + } > + > + if (ids[0] != 10 || ids[1] != 11) { > + cerr << "unmapBuffers(): Invalid buffer IDs" << endl; > + return report(Op_unmapBuffers, TestFail); > + } > + > + report(Op_unmapBuffers, TestPass); > + } > + > + void processEvent(const IPAOperationData &data) override > + { > + /* Verify operation and data. */ > + if (data.operation != Op_processEvent) { > + cerr << "processEvent(): Invalid operation " > + << data.operation << endl; > + return report(Op_processEvent, TestFail); > + } > + > + if (data.data != std::vector<unsigned int>{ 1, 2, 3, 4 }) { > + cerr << "processEvent(): Invalid data" << endl; > + return report(Op_processEvent, TestFail); > + } > + > + /* Verify controls. */ > + if (data.controls.size() != 1) { > + cerr << "processEvent(): Controls not found" << endl; > + return report(Op_processEvent, TestFail); > + } > + > + const ControlList &controls = data.controls[0]; > + if (controls.get(V4L2_CID_BRIGHTNESS).get<int32_t>() != 10 || > + controls.get(V4L2_CID_CONTRAST).get<int32_t>() != 20 || > + controls.get(V4L2_CID_SATURATION).get<int32_t>() != 30) { > + cerr << "processEvent(): Invalid controls" << endl; > + return report(Op_processEvent, TestFail); > + } > + > + report(Op_processEvent, TestPass); > + } > + > +private: > + void report(Operation op, int status) > + { > + IPAOperationData data; > + data.operation = op; > + data.data.resize(1); > + data.data[0] = status; > + queueFrameAction.emit(sequence_++, data); > + } > + > + unsigned int sequence_; > +}; > + > +#define INVOKE(method, ...) \ > + invoke(&IPAInterface::method, Op_##method, #method, ##__VA_ARGS__) > + > +class IPAWrappersTest : public Test > +{ > +public: > + IPAWrappersTest() > + : subdev_(nullptr), wrapper_(nullptr), sequence_(0), fd_(-1) > + { > + } > + > +protected: > + int init() override > + { > + /* Locate the VIMC Sensor B subdevice. */ > + enumerator_ = unique_ptr<DeviceEnumerator>(DeviceEnumerator::create()); > + if (!enumerator_) { > + cerr << "Failed to create device enumerator" << endl; > + return TestFail; > + } > + > + if (enumerator_->enumerate()) { > + cerr << "Failed to enumerate media devices" << endl; > + return TestFail; > + } > + > + DeviceMatch dm("vimc"); > + media_ = enumerator_->search(dm); > + if (!media_) { > + cerr << "No VIMC media device found: skip test" << endl; > + return TestSkip; > + } > + > + MediaEntity *entity = media_->getEntityByName("Sensor A"); > + if (!entity) { > + cerr << "Unable to find media entity 'Sensor A'" << endl; > + return TestFail; > + } > + > + subdev_ = new V4L2Subdevice(entity); > + if (subdev_->open() < 0) { > + cerr << "Unable to open 'Sensor A' subdevice" << endl; > + return TestFail; > + } > + > + /* Force usage of the C API as that's what we want to test. */ > + int ret = setenv("LIBCAMERA_IPA_FORCE_C_API", "", 1); > + if (ret) > + return TestFail; > + > + std::unique_ptr<IPAInterface> intf = utils::make_unique<TestIPAInterface>(); > + wrapper_ = new IPAContextWrapper(new IPAInterfaceWrapper(std::move(intf))); > + wrapper_->queueFrameAction.connect(this, &IPAWrappersTest::queueFrameAction); > + > + /* Create a file descriptor for the buffer-related operations. */ > + fd_ = open("/tmp", O_TMPFILE | O_RDWR, 0600); > + if (fd_ == -1) > + return TestFail; > + > + ftruncate(fd_, 4096); > + > + return TestPass; > + } > + > + int run() override > + { > + int ret; > + > + /* Test configure(). */ > + std::map<unsigned int, IPAStream> config{ > + { 1, { V4L2_PIX_FMT_YUYV, { 1024, 768 } } }, > + { 2, { V4L2_PIX_FMT_NV12, { 800, 600 } } }, > + }; > + std::map<unsigned int, const ControlInfoMap &> controlInfo; > + controlInfo.emplace(42, subdev_->controls()); > + ret = INVOKE(configure, config, controlInfo); > + if (ret == TestFail) > + return TestFail; > + > + /* Test mapBuffers(). */ > + std::vector<IPABuffer> buffers(2); > + buffers[0].memory.planes().resize(3); > + buffers[0].id = 10; > + buffers[0].memory.planes()[0].setDmabuf(fd_, 4096); > + buffers[1].id = 11; > + buffers[1].memory.planes().resize(3); > + buffers[1].memory.planes()[0].setDmabuf(fd_, 4096); > + buffers[1].memory.planes()[1].setDmabuf(fd_, 4096); > + > + ret = INVOKE(mapBuffers, buffers); > + if (ret == TestFail) > + return TestFail; > + > + /* Test unmapBuffers(). */ > + std::vector<unsigned int> bufferIds = { 10, 11 }; > + ret = INVOKE(unmapBuffers, bufferIds); > + if (ret == TestFail) > + return TestFail; > + > + /* Test processEvent(). */ > + IPAOperationData data; > + data.operation = Op_processEvent; > + data.data = { 1, 2, 3, 4 }; > + data.controls.emplace_back(subdev_->controls()); > + > + ControlList &controls = data.controls.back(); > + controls.set(V4L2_CID_BRIGHTNESS, static_cast<int32_t>(10)); > + controls.set(V4L2_CID_CONTRAST, static_cast<int32_t>(20)); > + controls.set(V4L2_CID_SATURATION, static_cast<int32_t>(30)); > + > + ret = INVOKE(processEvent, data); > + if (ret == TestFail) > + return TestFail; > + > + /* > + * Test init() last to ensure nothing in the wrappers or > + * serializer depends on init() being called first. > + */ > + ret = INVOKE(init); > + if (ret == TestFail) > + return TestFail; > + > + return TestPass; > + } > + > + void cleanup() override > + { > + delete wrapper_; > + delete subdev_; > + > + if (fd_ != -1) > + close(fd_); > + } > + > +private: > + template<typename T, typename... Args1, typename... Args2> > + int invoke(T (IPAInterface::*func)(Args1...), Operation op, > + const char *name, Args2... args) > + { > + data_ = IPAOperationData(); > + (wrapper_->*func)(args...); > + > + if (frame_ != sequence_) { > + cerr << "IPAInterface::" << name > + << "(): invalid frame number " << frame_ > + << ", expected " << sequence_; > + return TestFail; > + } > + > + sequence_++; > + > + if (data_.operation != op) { > + cerr << "IPAInterface::" << name > + << "(): failed to propagate" << endl; > + return TestFail; > + } > + > + if (data_.data[0] != TestPass) { > + cerr << "IPAInterface::" << name > + << "(): reported an error" << endl; > + return TestFail; > + } > + > + return TestPass; > + } > + > + void queueFrameAction(unsigned int frame, const IPAOperationData &data) > + { > + frame_ = frame; > + data_ = data; > + } > + > + std::shared_ptr<MediaDevice> media_; > + std::unique_ptr<DeviceEnumerator> enumerator_; > + V4L2Subdevice *subdev_; > + > + IPAContextWrapper *wrapper_; > + IPAOperationData data_; > + unsigned int sequence_; > + unsigned int frame_; > + int fd_; > +}; > + > +TEST_REGISTER(IPAWrappersTest) > diff --git a/test/ipa/meson.build b/test/ipa/meson.build > index c501fcf99aed..f925c50a085e 100644 > --- a/test/ipa/meson.build > +++ b/test/ipa/meson.build > @@ -1,13 +1,14 @@ > ipa_test = [ > ['ipa_module_test', 'ipa_module_test.cpp'], > ['ipa_interface_test', 'ipa_interface_test.cpp'], > + ['ipa_wrappers_test', 'ipa_wrappers_test.cpp'], > ] > > foreach t : ipa_test > exe = executable(t[0], t[1], > dependencies : libcamera_dep, > - link_with : test_libraries, > - include_directories : test_includes_internal) > + link_with : [libipa, test_libraries], > + include_directories : [libipa_includes, test_includes_internal]) > > test(t[0], exe, suite : 'ipa') > endforeach > -- > Regards, > > Laurent Pinchart > > _______________________________________________ > libcamera-devel mailing list > libcamera-devel@lists.libcamera.org > https://lists.libcamera.org/listinfo/libcamera-devel
Hi Laurent, On Fri, Nov 08, 2019 at 10:54:08PM +0200, Laurent Pinchart wrote: > Wrap an IPAInterface in an IPAInterfaceWrapper in an IPAContextWrapper, > and verify that the translation between C and C++ APIs work correctly. > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > --- > test/ipa/ipa_wrappers_test.cpp | 390 +++++++++++++++++++++++++++++++++ > test/ipa/meson.build | 5 +- > 2 files changed, 393 insertions(+), 2 deletions(-) > create mode 100644 test/ipa/ipa_wrappers_test.cpp > > diff --git a/test/ipa/ipa_wrappers_test.cpp b/test/ipa/ipa_wrappers_test.cpp > new file mode 100644 > index 000000000000..c0717f0e301b > --- /dev/null > +++ b/test/ipa/ipa_wrappers_test.cpp > @@ -0,0 +1,390 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2019, Google Inc. > + * > + * ipa_wrappers_test.cpp - Test the IPA interface and context wrappers > + */ > + > +#include <fcntl.h> > +#include <iostream> > +#include <memory> > +#include <linux/videodev2.h> > +#include <sys/stat.h> > +#include <unistd.h> > + > +#include <libcamera/controls.h> > +#include <libipa/ipa_interface_wrapper.h> > + > +#include "device_enumerator.h" > +#include "ipa_context_wrapper.h" > +#include "media_device.h" > +#include "utils.h" > +#include "v4l2_subdevice.h" > + > +#include "test.h" > + > +using namespace libcamera; > +using namespace std; > + > +enum Operation { > + Op_init, > + Op_configure, > + Op_mapBuffers, > + Op_unmapBuffers, > + Op_processEvent, > +}; > + > +class TestIPAInterface : public IPAInterface > +{ > +public: > + TestIPAInterface() > + : sequence_(0) > + { > + } > + > + int init() override > + { > + report(Op_init, TestPass); > + return 0; > + } > + > + void configure(const std::map<unsigned int, IPAStream> &streamConfig, > + const std::map<unsigned int, const ControlInfoMap &> &entityControls) override > + { > + /* Verify streamConfig. */ > + if (streamConfig.size() != 2) { > + cerr << "configure(): Invalid number of streams " > + << streamConfig.size() << endl; > + return report(Op_configure, TestFail); > + } > + > + auto iter = streamConfig.find(1); > + if (iter == streamConfig.end()) { > + cerr << "configure(): No configuration for stream 1" << endl; > + return report(Op_configure, TestFail); > + } > + const IPAStream *stream = &iter->second; > + if (stream->pixelFormat != V4L2_PIX_FMT_YUYV || > + stream->size != Size{ 1024, 768 }) { > + cerr << "configure(): Invalid configuration for stream 1" << endl; > + return report(Op_configure, TestFail); > + } > + > + iter = streamConfig.find(2); > + if (iter == streamConfig.end()) { > + cerr << "configure(): No configuration for stream 2" << endl; > + return report(Op_configure, TestFail); > + } > + stream = &iter->second; > + if (stream->pixelFormat != V4L2_PIX_FMT_NV12 || > + stream->size != Size{ 800, 600 }) { > + cerr << "configure(): Invalid configuration for stream 2" << endl; > + return report(Op_configure, TestFail); > + } > + > + /* Verify entityControls. */ > + auto ctrlIter = entityControls.find(42); > + if (ctrlIter == entityControls.end()) { > + cerr << "configure(): Controls not found" << endl; > + return report(Op_configure, TestFail); > + } > + > + const ControlInfoMap &infoMap = ctrlIter->second; > + > + if (infoMap.count(V4L2_CID_BRIGHTNESS) != 1 || > + infoMap.count(V4L2_CID_CONTRAST) != 1 || > + infoMap.count(V4L2_CID_SATURATION) != 1) { > + cerr << "configure(): Invalid control IDs" << endl; > + return report(Op_configure, TestFail); > + } > + > + report(Op_configure, TestPass); > + } > + > + void mapBuffers(const std::vector<IPABuffer> &buffers) override > + { > + if (buffers.size() != 2) { > + cerr << "mapBuffers(): Invalid number of buffers " > + << buffers.size() << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + if (buffers[0].id != 10 || > + buffers[1].id != 11) { > + cerr << "mapBuffers(): Invalid buffer IDs" << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + if (buffers[0].memory.planes().size() != 3 || > + buffers[1].memory.planes().size() != 3) { > + cerr << "mapBuffers(): Invalid number of planes" << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + if (buffers[0].memory.planes()[0].length() != 4096 || > + buffers[0].memory.planes()[1].length() != 0 || > + buffers[0].memory.planes()[2].length() != 0 || > + buffers[0].memory.planes()[0].length() != 4096 || > + buffers[1].memory.planes()[1].length() != 4096 || > + buffers[1].memory.planes()[2].length() != 0) { > + cerr << "mapBuffers(): Invalid length" << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + if (buffers[0].memory.planes()[0].dmabuf() == -1 || > + buffers[0].memory.planes()[1].dmabuf() != -1 || > + buffers[0].memory.planes()[2].dmabuf() != -1 || > + buffers[0].memory.planes()[0].dmabuf() == -1 || > + buffers[1].memory.planes()[1].dmabuf() == -1 || > + buffers[1].memory.planes()[2].dmabuf() != -1) { > + cerr << "mapBuffers(): Invalid dmabuf" << endl; > + return report(Op_mapBuffers, TestFail); > + } > + > + report(Op_mapBuffers, TestPass); > + } > + > + void unmapBuffers(const std::vector<unsigned int> &ids) override > + { > + if (ids.size() != 2) { > + cerr << "unmapBuffers(): Invalid number of ids " > + << ids.size() << endl; > + return report(Op_unmapBuffers, TestFail); > + } > + > + if (ids[0] != 10 || ids[1] != 11) { > + cerr << "unmapBuffers(): Invalid buffer IDs" << endl; > + return report(Op_unmapBuffers, TestFail); > + } > + > + report(Op_unmapBuffers, TestPass); > + } > + > + void processEvent(const IPAOperationData &data) override > + { > + /* Verify operation and data. */ > + if (data.operation != Op_processEvent) { > + cerr << "processEvent(): Invalid operation " > + << data.operation << endl; > + return report(Op_processEvent, TestFail); > + } > + > + if (data.data != std::vector<unsigned int>{ 1, 2, 3, 4 }) { > + cerr << "processEvent(): Invalid data" << endl; > + return report(Op_processEvent, TestFail); > + } > + > + /* Verify controls. */ > + if (data.controls.size() != 1) { > + cerr << "processEvent(): Controls not found" << endl; > + return report(Op_processEvent, TestFail); > + } > + > + const ControlList &controls = data.controls[0]; > + if (controls.get(V4L2_CID_BRIGHTNESS).get<int32_t>() != 10 || > + controls.get(V4L2_CID_CONTRAST).get<int32_t>() != 20 || > + controls.get(V4L2_CID_SATURATION).get<int32_t>() != 30) { > + cerr << "processEvent(): Invalid controls" << endl; > + return report(Op_processEvent, TestFail); > + } > + > + report(Op_processEvent, TestPass); > + } > + > +private: > + void report(Operation op, int status) > + { > + IPAOperationData data; > + data.operation = op; > + data.data.resize(1); > + data.data[0] = status; > + queueFrameAction.emit(sequence_++, data); > + } > + > + unsigned int sequence_; > +}; > + > +#define INVOKE(method, ...) \ > + invoke(&IPAInterface::method, Op_##method, #method, ##__VA_ARGS__) > + > +class IPAWrappersTest : public Test > +{ > +public: > + IPAWrappersTest() > + : subdev_(nullptr), wrapper_(nullptr), sequence_(0), fd_(-1) > + { > + } > + > +protected: > + int init() override > + { > + /* Locate the VIMC Sensor B subdevice. */ > + enumerator_ = unique_ptr<DeviceEnumerator>(DeviceEnumerator::create()); > + if (!enumerator_) { > + cerr << "Failed to create device enumerator" << endl; > + return TestFail; > + } > + > + if (enumerator_->enumerate()) { > + cerr << "Failed to enumerate media devices" << endl; > + return TestFail; > + } > + > + DeviceMatch dm("vimc"); > + media_ = enumerator_->search(dm); > + if (!media_) { > + cerr << "No VIMC media device found: skip test" << endl; > + return TestSkip; > + } > + > + MediaEntity *entity = media_->getEntityByName("Sensor A"); > + if (!entity) { > + cerr << "Unable to find media entity 'Sensor A'" << endl; > + return TestFail; > + } > + > + subdev_ = new V4L2Subdevice(entity); > + if (subdev_->open() < 0) { > + cerr << "Unable to open 'Sensor A' subdevice" << endl; > + return TestFail; > + } > + > + /* Force usage of the C API as that's what we want to test. */ > + int ret = setenv("LIBCAMERA_IPA_FORCE_C_API", "", 1); > + if (ret) > + return TestFail; > + > + std::unique_ptr<IPAInterface> intf = utils::make_unique<TestIPAInterface>(); > + wrapper_ = new IPAContextWrapper(new IPAInterfaceWrapper(std::move(intf))); > + wrapper_->queueFrameAction.connect(this, &IPAWrappersTest::queueFrameAction); > + > + /* Create a file descriptor for the buffer-related operations. */ > + fd_ = open("/tmp", O_TMPFILE | O_RDWR, 0600); > + if (fd_ == -1) > + return TestFail; > + > + ftruncate(fd_, 4096); When building on Chromium, which by default enables the -Wunused-result flag, this line results in a build error. libcamera/test/ipa/ipa_wrappers_test.cpp:266:3: error: ignoring return value of function declared with 'warn_unused_result' attribute [-Werror,-Wunused-result] > + > + return TestPass; > + } > + > + int run() override > + { > + int ret; > + > + /* Test configure(). */ > + std::map<unsigned int, IPAStream> config{ > + { 1, { V4L2_PIX_FMT_YUYV, { 1024, 768 } } }, > + { 2, { V4L2_PIX_FMT_NV12, { 800, 600 } } }, > + }; > + std::map<unsigned int, const ControlInfoMap &> controlInfo; > + controlInfo.emplace(42, subdev_->controls()); > + ret = INVOKE(configure, config, controlInfo); > + if (ret == TestFail) > + return TestFail; > + > + /* Test mapBuffers(). */ > + std::vector<IPABuffer> buffers(2); > + buffers[0].memory.planes().resize(3); > + buffers[0].id = 10; > + buffers[0].memory.planes()[0].setDmabuf(fd_, 4096); > + buffers[1].id = 11; > + buffers[1].memory.planes().resize(3); > + buffers[1].memory.planes()[0].setDmabuf(fd_, 4096); > + buffers[1].memory.planes()[1].setDmabuf(fd_, 4096); > + > + ret = INVOKE(mapBuffers, buffers); > + if (ret == TestFail) > + return TestFail; > + > + /* Test unmapBuffers(). */ > + std::vector<unsigned int> bufferIds = { 10, 11 }; > + ret = INVOKE(unmapBuffers, bufferIds); > + if (ret == TestFail) > + return TestFail; > + > + /* Test processEvent(). */ > + IPAOperationData data; > + data.operation = Op_processEvent; > + data.data = { 1, 2, 3, 4 }; > + data.controls.emplace_back(subdev_->controls()); > + > + ControlList &controls = data.controls.back(); > + controls.set(V4L2_CID_BRIGHTNESS, static_cast<int32_t>(10)); > + controls.set(V4L2_CID_CONTRAST, static_cast<int32_t>(20)); > + controls.set(V4L2_CID_SATURATION, static_cast<int32_t>(30)); > + > + ret = INVOKE(processEvent, data); > + if (ret == TestFail) > + return TestFail; > + > + /* > + * Test init() last to ensure nothing in the wrappers or > + * serializer depends on init() being called first. > + */ > + ret = INVOKE(init); > + if (ret == TestFail) > + return TestFail; > + > + return TestPass; > + } > + > + void cleanup() override > + { > + delete wrapper_; > + delete subdev_; > + > + if (fd_ != -1) > + close(fd_); > + } > + > +private: > + template<typename T, typename... Args1, typename... Args2> > + int invoke(T (IPAInterface::*func)(Args1...), Operation op, > + const char *name, Args2... args) > + { > + data_ = IPAOperationData(); > + (wrapper_->*func)(args...); > + > + if (frame_ != sequence_) { > + cerr << "IPAInterface::" << name > + << "(): invalid frame number " << frame_ > + << ", expected " << sequence_; > + return TestFail; > + } > + > + sequence_++; > + > + if (data_.operation != op) { > + cerr << "IPAInterface::" << name > + << "(): failed to propagate" << endl; > + return TestFail; > + } > + > + if (data_.data[0] != TestPass) { > + cerr << "IPAInterface::" << name > + << "(): reported an error" << endl; > + return TestFail; > + } > + > + return TestPass; > + } > + > + void queueFrameAction(unsigned int frame, const IPAOperationData &data) > + { > + frame_ = frame; > + data_ = data; > + } > + > + std::shared_ptr<MediaDevice> media_; > + std::unique_ptr<DeviceEnumerator> enumerator_; > + V4L2Subdevice *subdev_; > + > + IPAContextWrapper *wrapper_; > + IPAOperationData data_; > + unsigned int sequence_; > + unsigned int frame_; > + int fd_; > +}; > + > +TEST_REGISTER(IPAWrappersTest) > diff --git a/test/ipa/meson.build b/test/ipa/meson.build > index c501fcf99aed..f925c50a085e 100644 > --- a/test/ipa/meson.build > +++ b/test/ipa/meson.build > @@ -1,13 +1,14 @@ > ipa_test = [ > ['ipa_module_test', 'ipa_module_test.cpp'], > ['ipa_interface_test', 'ipa_interface_test.cpp'], > + ['ipa_wrappers_test', 'ipa_wrappers_test.cpp'], > ] > > foreach t : ipa_test > exe = executable(t[0], t[1], > dependencies : libcamera_dep, > - link_with : test_libraries, > - include_directories : test_includes_internal) > + link_with : [libipa, test_libraries], > + include_directories : [libipa_includes, test_includes_internal]) > > test(t[0], exe, suite : 'ipa') > endforeach > -- > Regards, > > Laurent Pinchart > > _______________________________________________ > libcamera-devel mailing list > libcamera-devel@lists.libcamera.org > https://lists.libcamera.org/listinfo/libcamera-devel
Hi Kieran, On Tue, Nov 19, 2019 at 05:21:53PM +0100, Jacopo Mondi wrote: > On Fri, Nov 08, 2019 at 10:54:08PM +0200, Laurent Pinchart wrote: > > Wrap an IPAInterface in an IPAInterfaceWrapper in an IPAContextWrapper, > > and verify that the translation between C and C++ APIs work correctly. > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > > --- > > test/ipa/ipa_wrappers_test.cpp | 390 +++++++++++++++++++++++++++++++++ > > test/ipa/meson.build | 5 +- > > 2 files changed, 393 insertions(+), 2 deletions(-) > > create mode 100644 test/ipa/ipa_wrappers_test.cpp > > > > diff --git a/test/ipa/ipa_wrappers_test.cpp b/test/ipa/ipa_wrappers_test.cpp > > new file mode 100644 > > index 000000000000..c0717f0e301b > > --- /dev/null > > +++ b/test/ipa/ipa_wrappers_test.cpp > > @@ -0,0 +1,390 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > +/* > > + * Copyright (C) 2019, Google Inc. > > + * > > + * ipa_wrappers_test.cpp - Test the IPA interface and context wrappers > > + */ > > + > > +#include <fcntl.h> > > +#include <iostream> > > +#include <memory> > > +#include <linux/videodev2.h> > > +#include <sys/stat.h> > > +#include <unistd.h> > > + > > +#include <libcamera/controls.h> > > +#include <libipa/ipa_interface_wrapper.h> > > + > > +#include "device_enumerator.h" > > +#include "ipa_context_wrapper.h" > > +#include "media_device.h" > > +#include "utils.h" > > +#include "v4l2_subdevice.h" > > + > > +#include "test.h" > > + > > +using namespace libcamera; > > +using namespace std; > > + > > +enum Operation { > > + Op_init, > > + Op_configure, > > + Op_mapBuffers, > > + Op_unmapBuffers, > > + Op_processEvent, > > +}; > > + > > +class TestIPAInterface : public IPAInterface > > +{ > > +public: > > + TestIPAInterface() > > + : sequence_(0) > > + { > > + } > > + > > + int init() override > > + { > > + report(Op_init, TestPass); > > + return 0; > > + } > > + > > + void configure(const std::map<unsigned int, IPAStream> &streamConfig, > > + const std::map<unsigned int, const ControlInfoMap &> &entityControls) override > > + { > > + /* Verify streamConfig. */ > > + if (streamConfig.size() != 2) { > > + cerr << "configure(): Invalid number of streams " > > + << streamConfig.size() << endl; > > + return report(Op_configure, TestFail); > > + } > > + > > + auto iter = streamConfig.find(1); > > + if (iter == streamConfig.end()) { > > + cerr << "configure(): No configuration for stream 1" << endl; > > + return report(Op_configure, TestFail); > > + } > > + const IPAStream *stream = &iter->second; > > + if (stream->pixelFormat != V4L2_PIX_FMT_YUYV || > > + stream->size != Size{ 1024, 768 }) { > > + cerr << "configure(): Invalid configuration for stream 1" << endl; > > + return report(Op_configure, TestFail); > > + } > > + > > + iter = streamConfig.find(2); > > + if (iter == streamConfig.end()) { > > + cerr << "configure(): No configuration for stream 2" << endl; > > + return report(Op_configure, TestFail); > > + } > > + stream = &iter->second; > > + if (stream->pixelFormat != V4L2_PIX_FMT_NV12 || > > + stream->size != Size{ 800, 600 }) { > > + cerr << "configure(): Invalid configuration for stream 2" << endl; > > + return report(Op_configure, TestFail); > > + } > > + > > + /* Verify entityControls. */ > > + auto ctrlIter = entityControls.find(42); > > + if (ctrlIter == entityControls.end()) { > > + cerr << "configure(): Controls not found" << endl; > > + return report(Op_configure, TestFail); > > + } > > + > > + const ControlInfoMap &infoMap = ctrlIter->second; > > + > > + if (infoMap.count(V4L2_CID_BRIGHTNESS) != 1 || > > + infoMap.count(V4L2_CID_CONTRAST) != 1 || > > + infoMap.count(V4L2_CID_SATURATION) != 1) { > > + cerr << "configure(): Invalid control IDs" << endl; > > + return report(Op_configure, TestFail); > > + } > > + > > + report(Op_configure, TestPass); > > + } > > + > > + void mapBuffers(const std::vector<IPABuffer> &buffers) override > > + { > > + if (buffers.size() != 2) { > > + cerr << "mapBuffers(): Invalid number of buffers " > > + << buffers.size() << endl; > > + return report(Op_mapBuffers, TestFail); > > + } > > + > > + if (buffers[0].id != 10 || > > + buffers[1].id != 11) { > > + cerr << "mapBuffers(): Invalid buffer IDs" << endl; > > + return report(Op_mapBuffers, TestFail); > > + } > > + > > + if (buffers[0].memory.planes().size() != 3 || > > + buffers[1].memory.planes().size() != 3) { > > + cerr << "mapBuffers(): Invalid number of planes" << endl; > > + return report(Op_mapBuffers, TestFail); > > + } > > + > > + if (buffers[0].memory.planes()[0].length() != 4096 || > > + buffers[0].memory.planes()[1].length() != 0 || > > + buffers[0].memory.planes()[2].length() != 0 || > > + buffers[0].memory.planes()[0].length() != 4096 || > > + buffers[1].memory.planes()[1].length() != 4096 || > > + buffers[1].memory.planes()[2].length() != 0) { > > + cerr << "mapBuffers(): Invalid length" << endl; > > + return report(Op_mapBuffers, TestFail); > > + } > > + > > + if (buffers[0].memory.planes()[0].dmabuf() == -1 || > > + buffers[0].memory.planes()[1].dmabuf() != -1 || > > + buffers[0].memory.planes()[2].dmabuf() != -1 || > > + buffers[0].memory.planes()[0].dmabuf() == -1 || > > + buffers[1].memory.planes()[1].dmabuf() == -1 || > > + buffers[1].memory.planes()[2].dmabuf() != -1) { > > + cerr << "mapBuffers(): Invalid dmabuf" << endl; > > + return report(Op_mapBuffers, TestFail); > > + } > > + > > + report(Op_mapBuffers, TestPass); > > + } > > + > > + void unmapBuffers(const std::vector<unsigned int> &ids) override > > + { > > + if (ids.size() != 2) { > > + cerr << "unmapBuffers(): Invalid number of ids " > > + << ids.size() << endl; > > + return report(Op_unmapBuffers, TestFail); > > + } > > + > > + if (ids[0] != 10 || ids[1] != 11) { > > + cerr << "unmapBuffers(): Invalid buffer IDs" << endl; > > + return report(Op_unmapBuffers, TestFail); > > + } > > + > > + report(Op_unmapBuffers, TestPass); > > + } > > + > > + void processEvent(const IPAOperationData &data) override > > + { > > + /* Verify operation and data. */ > > + if (data.operation != Op_processEvent) { > > + cerr << "processEvent(): Invalid operation " > > + << data.operation << endl; > > + return report(Op_processEvent, TestFail); > > + } > > + > > + if (data.data != std::vector<unsigned int>{ 1, 2, 3, 4 }) { > > + cerr << "processEvent(): Invalid data" << endl; > > + return report(Op_processEvent, TestFail); > > + } > > + > > + /* Verify controls. */ > > + if (data.controls.size() != 1) { > > + cerr << "processEvent(): Controls not found" << endl; > > + return report(Op_processEvent, TestFail); > > + } > > + > > + const ControlList &controls = data.controls[0]; > > + if (controls.get(V4L2_CID_BRIGHTNESS).get<int32_t>() != 10 || > > + controls.get(V4L2_CID_CONTRAST).get<int32_t>() != 20 || > > + controls.get(V4L2_CID_SATURATION).get<int32_t>() != 30) { > > + cerr << "processEvent(): Invalid controls" << endl; > > + return report(Op_processEvent, TestFail); > > + } > > + > > + report(Op_processEvent, TestPass); > > + } > > + > > +private: > > + void report(Operation op, int status) > > + { > > + IPAOperationData data; > > + data.operation = op; > > + data.data.resize(1); > > + data.data[0] = status; > > + queueFrameAction.emit(sequence_++, data); > > + } > > + > > + unsigned int sequence_; > > +}; > > + > > +#define INVOKE(method, ...) \ > > + invoke(&IPAInterface::method, Op_##method, #method, ##__VA_ARGS__) > > + > > +class IPAWrappersTest : public Test > > +{ > > +public: > > + IPAWrappersTest() > > + : subdev_(nullptr), wrapper_(nullptr), sequence_(0), fd_(-1) > > + { > > + } > > + > > +protected: > > + int init() override > > + { > > + /* Locate the VIMC Sensor B subdevice. */ > > + enumerator_ = unique_ptr<DeviceEnumerator>(DeviceEnumerator::create()); > > + if (!enumerator_) { > > + cerr << "Failed to create device enumerator" << endl; > > + return TestFail; > > + } > > + > > + if (enumerator_->enumerate()) { > > + cerr << "Failed to enumerate media devices" << endl; > > + return TestFail; > > + } > > + > > + DeviceMatch dm("vimc"); > > + media_ = enumerator_->search(dm); > > + if (!media_) { > > + cerr << "No VIMC media device found: skip test" << endl; > > + return TestSkip; > > + } > > + > > + MediaEntity *entity = media_->getEntityByName("Sensor A"); > > + if (!entity) { > > + cerr << "Unable to find media entity 'Sensor A'" << endl; > > + return TestFail; > > + } > > + > > + subdev_ = new V4L2Subdevice(entity); > > + if (subdev_->open() < 0) { > > + cerr << "Unable to open 'Sensor A' subdevice" << endl; > > + return TestFail; > > + } > > + > > + /* Force usage of the C API as that's what we want to test. */ > > + int ret = setenv("LIBCAMERA_IPA_FORCE_C_API", "", 1); > > + if (ret) > > + return TestFail; > > + > > + std::unique_ptr<IPAInterface> intf = utils::make_unique<TestIPAInterface>(); > > + wrapper_ = new IPAContextWrapper(new IPAInterfaceWrapper(std::move(intf))); > > + wrapper_->queueFrameAction.connect(this, &IPAWrappersTest::queueFrameAction); > > + > > + /* Create a file descriptor for the buffer-related operations. */ > > + fd_ = open("/tmp", O_TMPFILE | O_RDWR, 0600); > > + if (fd_ == -1) > > + return TestFail; > > + > > + ftruncate(fd_, 4096); > > When building on Chromium, which by default enables the -Wunused-result flag, > this line results in a build error. > > libcamera/test/ipa/ipa_wrappers_test.cpp:266:3: error: ignoring return value of function declared with 'warn_unused_result' attribute [-Werror,-Wunused-result] Could it be a libc version issue ? Adding -Wunused-result didn't produce any warning for me. I'll fix it nonetheless. > > + > > + return TestPass; > > + } > > + > > + int run() override > > + { > > + int ret; > > + > > + /* Test configure(). */ > > + std::map<unsigned int, IPAStream> config{ > > + { 1, { V4L2_PIX_FMT_YUYV, { 1024, 768 } } }, > > + { 2, { V4L2_PIX_FMT_NV12, { 800, 600 } } }, > > + }; > > + std::map<unsigned int, const ControlInfoMap &> controlInfo; > > + controlInfo.emplace(42, subdev_->controls()); > > + ret = INVOKE(configure, config, controlInfo); > > + if (ret == TestFail) > > + return TestFail; > > + > > + /* Test mapBuffers(). */ > > + std::vector<IPABuffer> buffers(2); > > + buffers[0].memory.planes().resize(3); > > + buffers[0].id = 10; > > + buffers[0].memory.planes()[0].setDmabuf(fd_, 4096); > > + buffers[1].id = 11; > > + buffers[1].memory.planes().resize(3); > > + buffers[1].memory.planes()[0].setDmabuf(fd_, 4096); > > + buffers[1].memory.planes()[1].setDmabuf(fd_, 4096); > > + > > + ret = INVOKE(mapBuffers, buffers); > > + if (ret == TestFail) > > + return TestFail; > > + > > + /* Test unmapBuffers(). */ > > + std::vector<unsigned int> bufferIds = { 10, 11 }; > > + ret = INVOKE(unmapBuffers, bufferIds); > > + if (ret == TestFail) > > + return TestFail; > > + > > + /* Test processEvent(). */ > > + IPAOperationData data; > > + data.operation = Op_processEvent; > > + data.data = { 1, 2, 3, 4 }; > > + data.controls.emplace_back(subdev_->controls()); > > + > > + ControlList &controls = data.controls.back(); > > + controls.set(V4L2_CID_BRIGHTNESS, static_cast<int32_t>(10)); > > + controls.set(V4L2_CID_CONTRAST, static_cast<int32_t>(20)); > > + controls.set(V4L2_CID_SATURATION, static_cast<int32_t>(30)); > > + > > + ret = INVOKE(processEvent, data); > > + if (ret == TestFail) > > + return TestFail; > > + > > + /* > > + * Test init() last to ensure nothing in the wrappers or > > + * serializer depends on init() being called first. > > + */ > > + ret = INVOKE(init); > > + if (ret == TestFail) > > + return TestFail; > > + > > + return TestPass; > > + } > > + > > + void cleanup() override > > + { > > + delete wrapper_; > > + delete subdev_; > > + > > + if (fd_ != -1) > > + close(fd_); > > + } > > + > > +private: > > + template<typename T, typename... Args1, typename... Args2> > > + int invoke(T (IPAInterface::*func)(Args1...), Operation op, > > + const char *name, Args2... args) > > + { > > + data_ = IPAOperationData(); > > + (wrapper_->*func)(args...); > > + > > + if (frame_ != sequence_) { > > + cerr << "IPAInterface::" << name > > + << "(): invalid frame number " << frame_ > > + << ", expected " << sequence_; > > + return TestFail; > > + } > > + > > + sequence_++; > > + > > + if (data_.operation != op) { > > + cerr << "IPAInterface::" << name > > + << "(): failed to propagate" << endl; > > + return TestFail; > > + } > > + > > + if (data_.data[0] != TestPass) { > > + cerr << "IPAInterface::" << name > > + << "(): reported an error" << endl; > > + return TestFail; > > + } > > + > > + return TestPass; > > + } > > + > > + void queueFrameAction(unsigned int frame, const IPAOperationData &data) > > + { > > + frame_ = frame; > > + data_ = data; > > + } > > + > > + std::shared_ptr<MediaDevice> media_; > > + std::unique_ptr<DeviceEnumerator> enumerator_; > > + V4L2Subdevice *subdev_; > > + > > + IPAContextWrapper *wrapper_; > > + IPAOperationData data_; > > + unsigned int sequence_; > > + unsigned int frame_; > > + int fd_; > > +}; > > + > > +TEST_REGISTER(IPAWrappersTest) > > diff --git a/test/ipa/meson.build b/test/ipa/meson.build > > index c501fcf99aed..f925c50a085e 100644 > > --- a/test/ipa/meson.build > > +++ b/test/ipa/meson.build > > @@ -1,13 +1,14 @@ > > ipa_test = [ > > ['ipa_module_test', 'ipa_module_test.cpp'], > > ['ipa_interface_test', 'ipa_interface_test.cpp'], > > + ['ipa_wrappers_test', 'ipa_wrappers_test.cpp'], > > ] > > > > foreach t : ipa_test > > exe = executable(t[0], t[1], > > dependencies : libcamera_dep, > > - link_with : test_libraries, > > - include_directories : test_includes_internal) > > + link_with : [libipa, test_libraries], > > + include_directories : [libipa_includes, test_includes_internal]) > > > > test(t[0], exe, suite : 'ipa') > > endforeach
diff --git a/test/ipa/ipa_wrappers_test.cpp b/test/ipa/ipa_wrappers_test.cpp new file mode 100644 index 000000000000..c0717f0e301b --- /dev/null +++ b/test/ipa/ipa_wrappers_test.cpp @@ -0,0 +1,390 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * ipa_wrappers_test.cpp - Test the IPA interface and context wrappers + */ + +#include <fcntl.h> +#include <iostream> +#include <memory> +#include <linux/videodev2.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <libcamera/controls.h> +#include <libipa/ipa_interface_wrapper.h> + +#include "device_enumerator.h" +#include "ipa_context_wrapper.h" +#include "media_device.h" +#include "utils.h" +#include "v4l2_subdevice.h" + +#include "test.h" + +using namespace libcamera; +using namespace std; + +enum Operation { + Op_init, + Op_configure, + Op_mapBuffers, + Op_unmapBuffers, + Op_processEvent, +}; + +class TestIPAInterface : public IPAInterface +{ +public: + TestIPAInterface() + : sequence_(0) + { + } + + int init() override + { + report(Op_init, TestPass); + return 0; + } + + void configure(const std::map<unsigned int, IPAStream> &streamConfig, + const std::map<unsigned int, const ControlInfoMap &> &entityControls) override + { + /* Verify streamConfig. */ + if (streamConfig.size() != 2) { + cerr << "configure(): Invalid number of streams " + << streamConfig.size() << endl; + return report(Op_configure, TestFail); + } + + auto iter = streamConfig.find(1); + if (iter == streamConfig.end()) { + cerr << "configure(): No configuration for stream 1" << endl; + return report(Op_configure, TestFail); + } + const IPAStream *stream = &iter->second; + if (stream->pixelFormat != V4L2_PIX_FMT_YUYV || + stream->size != Size{ 1024, 768 }) { + cerr << "configure(): Invalid configuration for stream 1" << endl; + return report(Op_configure, TestFail); + } + + iter = streamConfig.find(2); + if (iter == streamConfig.end()) { + cerr << "configure(): No configuration for stream 2" << endl; + return report(Op_configure, TestFail); + } + stream = &iter->second; + if (stream->pixelFormat != V4L2_PIX_FMT_NV12 || + stream->size != Size{ 800, 600 }) { + cerr << "configure(): Invalid configuration for stream 2" << endl; + return report(Op_configure, TestFail); + } + + /* Verify entityControls. */ + auto ctrlIter = entityControls.find(42); + if (ctrlIter == entityControls.end()) { + cerr << "configure(): Controls not found" << endl; + return report(Op_configure, TestFail); + } + + const ControlInfoMap &infoMap = ctrlIter->second; + + if (infoMap.count(V4L2_CID_BRIGHTNESS) != 1 || + infoMap.count(V4L2_CID_CONTRAST) != 1 || + infoMap.count(V4L2_CID_SATURATION) != 1) { + cerr << "configure(): Invalid control IDs" << endl; + return report(Op_configure, TestFail); + } + + report(Op_configure, TestPass); + } + + void mapBuffers(const std::vector<IPABuffer> &buffers) override + { + if (buffers.size() != 2) { + cerr << "mapBuffers(): Invalid number of buffers " + << buffers.size() << endl; + return report(Op_mapBuffers, TestFail); + } + + if (buffers[0].id != 10 || + buffers[1].id != 11) { + cerr << "mapBuffers(): Invalid buffer IDs" << endl; + return report(Op_mapBuffers, TestFail); + } + + if (buffers[0].memory.planes().size() != 3 || + buffers[1].memory.planes().size() != 3) { + cerr << "mapBuffers(): Invalid number of planes" << endl; + return report(Op_mapBuffers, TestFail); + } + + if (buffers[0].memory.planes()[0].length() != 4096 || + buffers[0].memory.planes()[1].length() != 0 || + buffers[0].memory.planes()[2].length() != 0 || + buffers[0].memory.planes()[0].length() != 4096 || + buffers[1].memory.planes()[1].length() != 4096 || + buffers[1].memory.planes()[2].length() != 0) { + cerr << "mapBuffers(): Invalid length" << endl; + return report(Op_mapBuffers, TestFail); + } + + if (buffers[0].memory.planes()[0].dmabuf() == -1 || + buffers[0].memory.planes()[1].dmabuf() != -1 || + buffers[0].memory.planes()[2].dmabuf() != -1 || + buffers[0].memory.planes()[0].dmabuf() == -1 || + buffers[1].memory.planes()[1].dmabuf() == -1 || + buffers[1].memory.planes()[2].dmabuf() != -1) { + cerr << "mapBuffers(): Invalid dmabuf" << endl; + return report(Op_mapBuffers, TestFail); + } + + report(Op_mapBuffers, TestPass); + } + + void unmapBuffers(const std::vector<unsigned int> &ids) override + { + if (ids.size() != 2) { + cerr << "unmapBuffers(): Invalid number of ids " + << ids.size() << endl; + return report(Op_unmapBuffers, TestFail); + } + + if (ids[0] != 10 || ids[1] != 11) { + cerr << "unmapBuffers(): Invalid buffer IDs" << endl; + return report(Op_unmapBuffers, TestFail); + } + + report(Op_unmapBuffers, TestPass); + } + + void processEvent(const IPAOperationData &data) override + { + /* Verify operation and data. */ + if (data.operation != Op_processEvent) { + cerr << "processEvent(): Invalid operation " + << data.operation << endl; + return report(Op_processEvent, TestFail); + } + + if (data.data != std::vector<unsigned int>{ 1, 2, 3, 4 }) { + cerr << "processEvent(): Invalid data" << endl; + return report(Op_processEvent, TestFail); + } + + /* Verify controls. */ + if (data.controls.size() != 1) { + cerr << "processEvent(): Controls not found" << endl; + return report(Op_processEvent, TestFail); + } + + const ControlList &controls = data.controls[0]; + if (controls.get(V4L2_CID_BRIGHTNESS).get<int32_t>() != 10 || + controls.get(V4L2_CID_CONTRAST).get<int32_t>() != 20 || + controls.get(V4L2_CID_SATURATION).get<int32_t>() != 30) { + cerr << "processEvent(): Invalid controls" << endl; + return report(Op_processEvent, TestFail); + } + + report(Op_processEvent, TestPass); + } + +private: + void report(Operation op, int status) + { + IPAOperationData data; + data.operation = op; + data.data.resize(1); + data.data[0] = status; + queueFrameAction.emit(sequence_++, data); + } + + unsigned int sequence_; +}; + +#define INVOKE(method, ...) \ + invoke(&IPAInterface::method, Op_##method, #method, ##__VA_ARGS__) + +class IPAWrappersTest : public Test +{ +public: + IPAWrappersTest() + : subdev_(nullptr), wrapper_(nullptr), sequence_(0), fd_(-1) + { + } + +protected: + int init() override + { + /* Locate the VIMC Sensor B subdevice. */ + enumerator_ = unique_ptr<DeviceEnumerator>(DeviceEnumerator::create()); + if (!enumerator_) { + cerr << "Failed to create device enumerator" << endl; + return TestFail; + } + + if (enumerator_->enumerate()) { + cerr << "Failed to enumerate media devices" << endl; + return TestFail; + } + + DeviceMatch dm("vimc"); + media_ = enumerator_->search(dm); + if (!media_) { + cerr << "No VIMC media device found: skip test" << endl; + return TestSkip; + } + + MediaEntity *entity = media_->getEntityByName("Sensor A"); + if (!entity) { + cerr << "Unable to find media entity 'Sensor A'" << endl; + return TestFail; + } + + subdev_ = new V4L2Subdevice(entity); + if (subdev_->open() < 0) { + cerr << "Unable to open 'Sensor A' subdevice" << endl; + return TestFail; + } + + /* Force usage of the C API as that's what we want to test. */ + int ret = setenv("LIBCAMERA_IPA_FORCE_C_API", "", 1); + if (ret) + return TestFail; + + std::unique_ptr<IPAInterface> intf = utils::make_unique<TestIPAInterface>(); + wrapper_ = new IPAContextWrapper(new IPAInterfaceWrapper(std::move(intf))); + wrapper_->queueFrameAction.connect(this, &IPAWrappersTest::queueFrameAction); + + /* Create a file descriptor for the buffer-related operations. */ + fd_ = open("/tmp", O_TMPFILE | O_RDWR, 0600); + if (fd_ == -1) + return TestFail; + + ftruncate(fd_, 4096); + + return TestPass; + } + + int run() override + { + int ret; + + /* Test configure(). */ + std::map<unsigned int, IPAStream> config{ + { 1, { V4L2_PIX_FMT_YUYV, { 1024, 768 } } }, + { 2, { V4L2_PIX_FMT_NV12, { 800, 600 } } }, + }; + std::map<unsigned int, const ControlInfoMap &> controlInfo; + controlInfo.emplace(42, subdev_->controls()); + ret = INVOKE(configure, config, controlInfo); + if (ret == TestFail) + return TestFail; + + /* Test mapBuffers(). */ + std::vector<IPABuffer> buffers(2); + buffers[0].memory.planes().resize(3); + buffers[0].id = 10; + buffers[0].memory.planes()[0].setDmabuf(fd_, 4096); + buffers[1].id = 11; + buffers[1].memory.planes().resize(3); + buffers[1].memory.planes()[0].setDmabuf(fd_, 4096); + buffers[1].memory.planes()[1].setDmabuf(fd_, 4096); + + ret = INVOKE(mapBuffers, buffers); + if (ret == TestFail) + return TestFail; + + /* Test unmapBuffers(). */ + std::vector<unsigned int> bufferIds = { 10, 11 }; + ret = INVOKE(unmapBuffers, bufferIds); + if (ret == TestFail) + return TestFail; + + /* Test processEvent(). */ + IPAOperationData data; + data.operation = Op_processEvent; + data.data = { 1, 2, 3, 4 }; + data.controls.emplace_back(subdev_->controls()); + + ControlList &controls = data.controls.back(); + controls.set(V4L2_CID_BRIGHTNESS, static_cast<int32_t>(10)); + controls.set(V4L2_CID_CONTRAST, static_cast<int32_t>(20)); + controls.set(V4L2_CID_SATURATION, static_cast<int32_t>(30)); + + ret = INVOKE(processEvent, data); + if (ret == TestFail) + return TestFail; + + /* + * Test init() last to ensure nothing in the wrappers or + * serializer depends on init() being called first. + */ + ret = INVOKE(init); + if (ret == TestFail) + return TestFail; + + return TestPass; + } + + void cleanup() override + { + delete wrapper_; + delete subdev_; + + if (fd_ != -1) + close(fd_); + } + +private: + template<typename T, typename... Args1, typename... Args2> + int invoke(T (IPAInterface::*func)(Args1...), Operation op, + const char *name, Args2... args) + { + data_ = IPAOperationData(); + (wrapper_->*func)(args...); + + if (frame_ != sequence_) { + cerr << "IPAInterface::" << name + << "(): invalid frame number " << frame_ + << ", expected " << sequence_; + return TestFail; + } + + sequence_++; + + if (data_.operation != op) { + cerr << "IPAInterface::" << name + << "(): failed to propagate" << endl; + return TestFail; + } + + if (data_.data[0] != TestPass) { + cerr << "IPAInterface::" << name + << "(): reported an error" << endl; + return TestFail; + } + + return TestPass; + } + + void queueFrameAction(unsigned int frame, const IPAOperationData &data) + { + frame_ = frame; + data_ = data; + } + + std::shared_ptr<MediaDevice> media_; + std::unique_ptr<DeviceEnumerator> enumerator_; + V4L2Subdevice *subdev_; + + IPAContextWrapper *wrapper_; + IPAOperationData data_; + unsigned int sequence_; + unsigned int frame_; + int fd_; +}; + +TEST_REGISTER(IPAWrappersTest) diff --git a/test/ipa/meson.build b/test/ipa/meson.build index c501fcf99aed..f925c50a085e 100644 --- a/test/ipa/meson.build +++ b/test/ipa/meson.build @@ -1,13 +1,14 @@ ipa_test = [ ['ipa_module_test', 'ipa_module_test.cpp'], ['ipa_interface_test', 'ipa_interface_test.cpp'], + ['ipa_wrappers_test', 'ipa_wrappers_test.cpp'], ] foreach t : ipa_test exe = executable(t[0], t[1], dependencies : libcamera_dep, - link_with : test_libraries, - include_directories : test_includes_internal) + link_with : [libipa, test_libraries], + include_directories : [libipa_includes, test_includes_internal]) test(t[0], exe, suite : 'ipa') endforeach
Wrap an IPAInterface in an IPAInterfaceWrapper in an IPAContextWrapper, and verify that the translation between C and C++ APIs work correctly. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> --- test/ipa/ipa_wrappers_test.cpp | 390 +++++++++++++++++++++++++++++++++ test/ipa/meson.build | 5 +- 2 files changed, 393 insertions(+), 2 deletions(-) create mode 100644 test/ipa/ipa_wrappers_test.cpp