Message ID | 20201123221234.485933-3-niklas.soderlund@ragnatech.se |
---|---|
State | Superseded |
Delegated to: | Niklas Söderlund |
Headers | show |
Series |
|
Related | show |
Hi Niklas, On Mon, Nov 23, 2020 at 11:12:28PM +0100, Niklas Söderlund wrote: > Add a test-case for DelayedControls that exercise the setting of exercizes > controls and reading back what controls where used for a particular s/where/were > frame. Also exercise corner case such as a V4L2 devices that do not > reset its sequence number to 0 at stream on and sequence number wrapping > around the uint32_t value space. > > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> > --- > * Changes since v2 > - Remove a unused LoC. > - Add and remove blank lines. > - Align number of loop iterations. > --- > test/delayed_contols.cpp | 300 +++++++++++++++++++++++++++++++++++++++ > test/meson.build | 1 + > 2 files changed, 301 insertions(+) > create mode 100644 test/delayed_contols.cpp > > diff --git a/test/delayed_contols.cpp b/test/delayed_contols.cpp > new file mode 100644 > index 0000000000000000..aaf321bf4f1b4cd8 > --- /dev/null > +++ b/test/delayed_contols.cpp > @@ -0,0 +1,300 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2020, Google Inc. > + * > + * libcamera delayed controls tests We usually prefix this with the file name. ie. * delayed_controls.cpp - libcamera delayed controls test > + */ > + > +#include <iostream> > + > +#include "libcamera/internal/delayed_controls.h" > +#include "libcamera/internal/device_enumerator.h" > +#include "libcamera/internal/media_device.h" > +#include "libcamera/internal/v4l2_videodevice.h" > + > +#include "test.h" > + > +using namespace std; > +using namespace libcamera; > + > +class DelayedControlsTest : public Test > +{ > +public: > + DelayedControlsTest() > + : dev_(nullptr) > + { > + } > + These following methods are usually in the protected scope. Also missing 'override' keyword > + int init() > + { > + enumerator_ = 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("vivid"); > + dm.add("vivid-000-vid-cap"); > + > + media_ = enumerator_->search(dm); > + if (!media_) { > + cerr << "vivid video device found" << endl; > + return TestSkip; > + } > + > + MediaEntity *entity = media_->getEntityByName("vivid-000-vid-cap"); > + dev_ = new V4L2VideoDevice(entity->deviceNode()); > + if (dev_->open()) { > + cerr << "Failed to open video device" << endl; > + return TestFail; > + } > + > + const ControlInfoMap &infoMap = dev_->controls(); > + > + /* Test control enumeration. */ > + if (infoMap.empty()) { > + cerr << "Failed to enumerate controls" << endl; > + return TestFail; > + } > + > + if (infoMap.find(V4L2_CID_BRIGHTNESS) == infoMap.end() || > + infoMap.find(V4L2_CID_CONTRAST) == infoMap.end()) { > + cerr << "Missing controls" << endl; > + return TestFail; > + } > + > + return TestPass; > + } > + > + int singleControlNoDelay() > + { > + std::unordered_map<uint32_t, unsigned int> delays = { > + { V4L2_CID_BRIGHTNESS, 0 }, > + }; > + std::unique_ptr<DelayedControls> delayed = > + std::make_unique<DelayedControls>(dev_, delays); > + ControlList ctrls; > + > + /* Reset control to value not used in test. */ > + ctrls.set(V4L2_CID_BRIGHTNESS, 1); > + delayed->reset(&ctrls); > + > + /* Test control without delay are set at once. */ > + for (int32_t i = 0; i < 100; i++) { > + int32_t value = 100 + i; > + > + ctrls.set(V4L2_CID_BRIGHTNESS, value); > + delayed->push(ctrls); > + > + delayed->frameStart(i); > + > + ControlList result = delayed->get(i); > + int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>(); > + if (brightness != value) { > + cerr << "Failed single control without delay" > + << " frame " << i > + << " expected " << value > + << " got " << brightness > + << endl; > + return TestFail; > + } > + } > + > + return TestPass; > + } > + > + int singleControlWithDelay() > + { > + std::unordered_map<uint32_t, unsigned int> delays = { > + { V4L2_CID_BRIGHTNESS, 1 }, > + }; > + std::unique_ptr<DelayedControls> delayed = > + std::make_unique<DelayedControls>(dev_, delays); > + ControlList ctrls; > + > + /* Reset control to value that will be first in test. */ > + int32_t expected = 4; > + ctrls.set(V4L2_CID_BRIGHTNESS, expected); > + delayed->reset(&ctrls); > + > + /* Test single control with delay. */ > + for (int32_t i = 0; i < 100; i++) { > + int32_t value = 10 + i; > + > + ctrls.set(V4L2_CID_BRIGHTNESS, value); > + delayed->push(ctrls); > + > + delayed->frameStart(i); > + > + ControlList result = delayed->get(i); > + int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>(); > + if (brightness != expected) { > + cerr << "Failed single control with delay" > + << " frame " << i > + << " expected " << expected > + << " got " << brightness > + << endl; > + return TestFail; > + } > + > + expected = value; > + } > + > + return TestPass; > + } > + > + int dualControlsWithDelay(uint32_t startOffset) > + { > + std::unordered_map<uint32_t, unsigned int> delays = { > + { V4L2_CID_BRIGHTNESS, 1 }, > + { V4L2_CID_CONTRAST, 2 }, > + }; > + std::unique_ptr<DelayedControls> delayed = > + std::make_unique<DelayedControls>(dev_, delays); > + ControlList ctrls; > + > + /* Reset control to value that will be first two frames in test. */ > + int32_t expected = 200; > + ctrls.set(V4L2_CID_BRIGHTNESS, expected); > + ctrls.set(V4L2_CID_CONTRAST, expected); > + delayed->reset(&ctrls); > + > + /* Test dual control with delay. */ > + for (int32_t i = 0; i < 100; i++) { > + uint32_t frame = startOffset + i; > + int32_t value = 10 + i; > + > + ctrls.set(V4L2_CID_BRIGHTNESS, value); > + ctrls.set(V4L2_CID_CONTRAST, value); > + delayed->push(ctrls); > + > + delayed->frameStart(frame); > + > + ControlList result = delayed->get(frame); > + int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>(); > + int32_t contrast = result.get(V4L2_CID_CONTRAST).get<int32_t>(); > + if (brightness != expected || contrast != expected) { > + cerr << "Failed dual controls" > + << " frame " << frame > + << " brightness " << brightness > + << " contrast " << contrast > + << " expected " << expected > + << endl; > + return TestFail; > + } > + > + expected = i < 1 ? expected : value - 1; > + } > + > + return TestPass; > + } > + > + int dualControlsMultiQueue() > + { > + std::unordered_map<uint32_t, unsigned int> delays = { > + { V4L2_CID_BRIGHTNESS, 1 }, > + { V4L2_CID_CONTRAST, 2 }, > + }; > + std::unique_ptr<DelayedControls> delayed = > + std::make_unique<DelayedControls>(dev_, delays); > + ControlList ctrls; > + > + /* Reset control to value that will be first two frames in test. */ > + int32_t expected = 100; > + ctrls.set(V4L2_CID_BRIGHTNESS, expected); > + ctrls.set(V4L2_CID_CONTRAST, expected); > + delayed->reset(&ctrls); nit: the &ctrls does not align with the prototype of push(const ControlList &ctrls); Instead of defining DelayedControls::reset(ControlList *ctrls = nullptr); I would override this as: DelayedControls::reset(); DelayedControls::reset(const ControlList &ctrls); it should be trivial to define one based on the other and you will have a nicer: DelayedControls::reset(const ControlList &ctrls); DelayedControls::push(const ControlList &ctrls); > + > + /* > + * Queue all controls before any fake frame start. Note we > + * can't queue up more then the delayed controls history size > + * which is 16. Where one spot is used by the reset control. > + */ > + for (int32_t i = 0; i < 15; i++) { i can be unsigned int here and in the next loop ? Test looks good Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> Thanks j > + int32_t value = 10 + i; > + > + ctrls.set(V4L2_CID_BRIGHTNESS, value); > + ctrls.set(V4L2_CID_CONTRAST, value); > + delayed->push(ctrls); > + } > + > + /* Process all queued controls. */ > + for (int32_t i = 0; i < 16; i++) { > + int32_t value = 10 + i; > + > + delayed->frameStart(i); > + > + ControlList result = delayed->get(i); > + > + int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>(); > + int32_t contrast = result.get(V4L2_CID_CONTRAST).get<int32_t>(); > + if (brightness != expected || contrast != expected) { > + cerr << "Failed multi queue" > + << " frame " << i > + << " brightness " << brightness > + << " contrast " << contrast > + << " expected " << expected > + << endl; > + return TestFail; > + } > + > + expected = i < 1 ? expected : value - 1; > + } > + > + return TestPass; > + } > + > + int run() > + { > + int ret; > + > + /* Test single control without delay. */ > + ret = singleControlNoDelay(); > + if (ret) > + return ret; > + > + /* Test single control with delay. */ > + ret = singleControlWithDelay(); > + if (ret) > + return ret; > + > + /* Test dual controls with different delays. */ > + ret = dualControlsWithDelay(0); > + if (ret) > + return ret; > + > + /* Test dual controls with non-zero sequence start. */ > + ret = dualControlsWithDelay(10000); > + if (ret) > + return ret; > + > + /* Test dual controls with sequence number wraparound. */ > + ret = dualControlsWithDelay(UINT32_MAX - 50); > + if (ret) > + return ret; > + > + /* Test control values produced faster then consumed. */ > + ret = dualControlsMultiQueue(); > + if (ret) > + return ret; > + > + return TestPass; > + } > + > + void cleanup() > + { > + delete dev_; > + } > + > +private: > + std::unique_ptr<DeviceEnumerator> enumerator_; > + std::shared_ptr<MediaDevice> media_; > + V4L2VideoDevice *dev_; > +}; > + > +TEST_REGISTER(DelayedControlsTest) > diff --git a/test/meson.build b/test/meson.build > index 0a1d434e399641bb..a683a657a439b4ff 100644 > --- a/test/meson.build > +++ b/test/meson.build > @@ -25,6 +25,7 @@ public_tests = [ > internal_tests = [ > ['byte-stream-buffer', 'byte-stream-buffer.cpp'], > ['camera-sensor', 'camera-sensor.cpp'], > + ['delayed_contols', 'delayed_contols.cpp'], > ['event', 'event.cpp'], > ['event-dispatcher', 'event-dispatcher.cpp'], > ['event-thread', 'event-thread.cpp'], > -- > 2.29.2 > > _______________________________________________ > libcamera-devel mailing list > libcamera-devel@lists.libcamera.org > https://lists.libcamera.org/listinfo/libcamera-devel
diff --git a/test/delayed_contols.cpp b/test/delayed_contols.cpp new file mode 100644 index 0000000000000000..aaf321bf4f1b4cd8 --- /dev/null +++ b/test/delayed_contols.cpp @@ -0,0 +1,300 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * libcamera delayed controls tests + */ + +#include <iostream> + +#include "libcamera/internal/delayed_controls.h" +#include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/media_device.h" +#include "libcamera/internal/v4l2_videodevice.h" + +#include "test.h" + +using namespace std; +using namespace libcamera; + +class DelayedControlsTest : public Test +{ +public: + DelayedControlsTest() + : dev_(nullptr) + { + } + + int init() + { + enumerator_ = 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("vivid"); + dm.add("vivid-000-vid-cap"); + + media_ = enumerator_->search(dm); + if (!media_) { + cerr << "vivid video device found" << endl; + return TestSkip; + } + + MediaEntity *entity = media_->getEntityByName("vivid-000-vid-cap"); + dev_ = new V4L2VideoDevice(entity->deviceNode()); + if (dev_->open()) { + cerr << "Failed to open video device" << endl; + return TestFail; + } + + const ControlInfoMap &infoMap = dev_->controls(); + + /* Test control enumeration. */ + if (infoMap.empty()) { + cerr << "Failed to enumerate controls" << endl; + return TestFail; + } + + if (infoMap.find(V4L2_CID_BRIGHTNESS) == infoMap.end() || + infoMap.find(V4L2_CID_CONTRAST) == infoMap.end()) { + cerr << "Missing controls" << endl; + return TestFail; + } + + return TestPass; + } + + int singleControlNoDelay() + { + std::unordered_map<uint32_t, unsigned int> delays = { + { V4L2_CID_BRIGHTNESS, 0 }, + }; + std::unique_ptr<DelayedControls> delayed = + std::make_unique<DelayedControls>(dev_, delays); + ControlList ctrls; + + /* Reset control to value not used in test. */ + ctrls.set(V4L2_CID_BRIGHTNESS, 1); + delayed->reset(&ctrls); + + /* Test control without delay are set at once. */ + for (int32_t i = 0; i < 100; i++) { + int32_t value = 100 + i; + + ctrls.set(V4L2_CID_BRIGHTNESS, value); + delayed->push(ctrls); + + delayed->frameStart(i); + + ControlList result = delayed->get(i); + int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>(); + if (brightness != value) { + cerr << "Failed single control without delay" + << " frame " << i + << " expected " << value + << " got " << brightness + << endl; + return TestFail; + } + } + + return TestPass; + } + + int singleControlWithDelay() + { + std::unordered_map<uint32_t, unsigned int> delays = { + { V4L2_CID_BRIGHTNESS, 1 }, + }; + std::unique_ptr<DelayedControls> delayed = + std::make_unique<DelayedControls>(dev_, delays); + ControlList ctrls; + + /* Reset control to value that will be first in test. */ + int32_t expected = 4; + ctrls.set(V4L2_CID_BRIGHTNESS, expected); + delayed->reset(&ctrls); + + /* Test single control with delay. */ + for (int32_t i = 0; i < 100; i++) { + int32_t value = 10 + i; + + ctrls.set(V4L2_CID_BRIGHTNESS, value); + delayed->push(ctrls); + + delayed->frameStart(i); + + ControlList result = delayed->get(i); + int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>(); + if (brightness != expected) { + cerr << "Failed single control with delay" + << " frame " << i + << " expected " << expected + << " got " << brightness + << endl; + return TestFail; + } + + expected = value; + } + + return TestPass; + } + + int dualControlsWithDelay(uint32_t startOffset) + { + std::unordered_map<uint32_t, unsigned int> delays = { + { V4L2_CID_BRIGHTNESS, 1 }, + { V4L2_CID_CONTRAST, 2 }, + }; + std::unique_ptr<DelayedControls> delayed = + std::make_unique<DelayedControls>(dev_, delays); + ControlList ctrls; + + /* Reset control to value that will be first two frames in test. */ + int32_t expected = 200; + ctrls.set(V4L2_CID_BRIGHTNESS, expected); + ctrls.set(V4L2_CID_CONTRAST, expected); + delayed->reset(&ctrls); + + /* Test dual control with delay. */ + for (int32_t i = 0; i < 100; i++) { + uint32_t frame = startOffset + i; + int32_t value = 10 + i; + + ctrls.set(V4L2_CID_BRIGHTNESS, value); + ctrls.set(V4L2_CID_CONTRAST, value); + delayed->push(ctrls); + + delayed->frameStart(frame); + + ControlList result = delayed->get(frame); + int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>(); + int32_t contrast = result.get(V4L2_CID_CONTRAST).get<int32_t>(); + if (brightness != expected || contrast != expected) { + cerr << "Failed dual controls" + << " frame " << frame + << " brightness " << brightness + << " contrast " << contrast + << " expected " << expected + << endl; + return TestFail; + } + + expected = i < 1 ? expected : value - 1; + } + + return TestPass; + } + + int dualControlsMultiQueue() + { + std::unordered_map<uint32_t, unsigned int> delays = { + { V4L2_CID_BRIGHTNESS, 1 }, + { V4L2_CID_CONTRAST, 2 }, + }; + std::unique_ptr<DelayedControls> delayed = + std::make_unique<DelayedControls>(dev_, delays); + ControlList ctrls; + + /* Reset control to value that will be first two frames in test. */ + int32_t expected = 100; + ctrls.set(V4L2_CID_BRIGHTNESS, expected); + ctrls.set(V4L2_CID_CONTRAST, expected); + delayed->reset(&ctrls); + + /* + * Queue all controls before any fake frame start. Note we + * can't queue up more then the delayed controls history size + * which is 16. Where one spot is used by the reset control. + */ + for (int32_t i = 0; i < 15; i++) { + int32_t value = 10 + i; + + ctrls.set(V4L2_CID_BRIGHTNESS, value); + ctrls.set(V4L2_CID_CONTRAST, value); + delayed->push(ctrls); + } + + /* Process all queued controls. */ + for (int32_t i = 0; i < 16; i++) { + int32_t value = 10 + i; + + delayed->frameStart(i); + + ControlList result = delayed->get(i); + + int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>(); + int32_t contrast = result.get(V4L2_CID_CONTRAST).get<int32_t>(); + if (brightness != expected || contrast != expected) { + cerr << "Failed multi queue" + << " frame " << i + << " brightness " << brightness + << " contrast " << contrast + << " expected " << expected + << endl; + return TestFail; + } + + expected = i < 1 ? expected : value - 1; + } + + return TestPass; + } + + int run() + { + int ret; + + /* Test single control without delay. */ + ret = singleControlNoDelay(); + if (ret) + return ret; + + /* Test single control with delay. */ + ret = singleControlWithDelay(); + if (ret) + return ret; + + /* Test dual controls with different delays. */ + ret = dualControlsWithDelay(0); + if (ret) + return ret; + + /* Test dual controls with non-zero sequence start. */ + ret = dualControlsWithDelay(10000); + if (ret) + return ret; + + /* Test dual controls with sequence number wraparound. */ + ret = dualControlsWithDelay(UINT32_MAX - 50); + if (ret) + return ret; + + /* Test control values produced faster then consumed. */ + ret = dualControlsMultiQueue(); + if (ret) + return ret; + + return TestPass; + } + + void cleanup() + { + delete dev_; + } + +private: + std::unique_ptr<DeviceEnumerator> enumerator_; + std::shared_ptr<MediaDevice> media_; + V4L2VideoDevice *dev_; +}; + +TEST_REGISTER(DelayedControlsTest) diff --git a/test/meson.build b/test/meson.build index 0a1d434e399641bb..a683a657a439b4ff 100644 --- a/test/meson.build +++ b/test/meson.build @@ -25,6 +25,7 @@ public_tests = [ internal_tests = [ ['byte-stream-buffer', 'byte-stream-buffer.cpp'], ['camera-sensor', 'camera-sensor.cpp'], + ['delayed_contols', 'delayed_contols.cpp'], ['event', 'event.cpp'], ['event-dispatcher', 'event-dispatcher.cpp'], ['event-thread', 'event-thread.cpp'],
Add a test-case for DelayedControls that exercise the setting of controls and reading back what controls where used for a particular frame. Also exercise corner case such as a V4L2 devices that do not reset its sequence number to 0 at stream on and sequence number wrapping around the uint32_t value space. Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> --- * Changes since v2 - Remove a unused LoC. - Add and remove blank lines. - Align number of loop iterations. --- test/delayed_contols.cpp | 300 +++++++++++++++++++++++++++++++++++++++ test/meson.build | 1 + 2 files changed, 301 insertions(+) create mode 100644 test/delayed_contols.cpp