summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/bayer-format.cpp2
-rw-r--r--test/byte-stream-buffer.cpp2
-rw-r--r--test/camera-sensor.cpp11
-rw-r--r--test/camera/buffer_import.cpp21
-rw-r--r--test/camera/capture.cpp22
-rw-r--r--test/controls/control_info.cpp2
-rw-r--r--test/controls/control_info_map.cpp2
-rw-r--r--test/controls/control_list.cpp52
-rw-r--r--test/controls/control_value.cpp82
-rw-r--r--test/delayed_controls.cpp2
-rw-r--r--test/event-dispatcher.cpp2
-rw-r--r--test/event-thread.cpp40
-rw-r--r--test/event.cpp2
-rw-r--r--test/fence.cpp77
-rw-r--r--test/file.cpp2
-rw-r--r--test/flags.cpp2
-rw-r--r--test/geometry.cpp27
-rw-r--r--test/gstreamer/gstreamer_device_provider_test.cpp13
-rw-r--r--test/gstreamer/gstreamer_memory_lifetime_test.cpp90
-rw-r--r--test/gstreamer/gstreamer_multi_stream_test.cpp2
-rw-r--r--test/gstreamer/gstreamer_single_stream_test.cpp29
-rw-r--r--test/gstreamer/gstreamer_test.cpp40
-rw-r--r--test/gstreamer/gstreamer_test.h2
-rw-r--r--test/gstreamer/meson.build14
-rw-r--r--test/hotplug-cameras.cpp2
-rw-r--r--test/ipa/ipa_interface_test.cpp17
-rw-r--r--test/ipa/ipa_module_test.cpp4
-rw-r--r--test/ipa/libipa/fixedpoint.cpp108
-rw-r--r--test/ipa/libipa/histogram.cpp66
-rw-r--r--test/ipa/libipa/interpolator.cpp54
-rw-r--r--test/ipa/libipa/meson.build19
-rw-r--r--test/ipa/meson.build10
-rw-r--r--test/ipc/unixsocket.cpp15
-rw-r--r--test/ipc/unixsocket_ipc.cpp2
-rw-r--r--test/libtest/buffer_source.h2
-rw-r--r--test/libtest/camera_test.h2
-rw-r--r--test/libtest/test.cpp2
-rw-r--r--test/libtest/test.h2
-rw-r--r--test/log/log_api.cpp2
-rw-r--r--test/log/log_process.cpp14
-rw-r--r--test/matrix.cpp53
-rw-r--r--test/media_device/media_device_link_test.cpp2
-rw-r--r--test/media_device/media_device_print_test.cpp2
-rw-r--r--test/media_device/media_device_test.cpp2
-rw-r--r--test/media_device/media_device_test.h2
-rw-r--r--test/meson.build14
-rw-r--r--test/message.cpp56
-rw-r--r--test/object-delete.cpp32
-rw-r--r--test/object-invoke.cpp2
-rw-r--r--test/object.cpp2
-rw-r--r--test/process/process_test.cpp2
-rw-r--r--test/public-api.cpp2
-rw-r--r--test/py/meson.build14
-rwxr-xr-xtest/py/unittests.py2
-rw-r--r--test/serialization/control_serialization.cpp2
-rw-r--r--test/serialization/generated_serializer/generated_serializer_test.cpp3
-rw-r--r--test/serialization/generated_serializer/include/libcamera/ipa/meson.build9
-rw-r--r--test/serialization/ipa_data_serializer_test.cpp4
-rw-r--r--test/serialization/serialization_test.cpp2
-rw-r--r--test/serialization/serialization_test.h2
-rw-r--r--test/shared-fd.cpp2
-rw-r--r--test/signal-threads.cpp26
-rw-r--r--test/signal.cpp2
-rw-r--r--test/span.cpp8
-rw-r--r--test/stream/stream_colorspace.cpp2
-rw-r--r--test/stream/stream_formats.cpp2
-rw-r--r--test/threads.cpp50
-rw-r--r--test/timer-fail.cpp109
-rw-r--r--test/timer-thread.cpp39
-rw-r--r--test/timer.cpp2
-rw-r--r--test/transform.cpp2
-rw-r--r--test/unique-fd.cpp2
-rw-r--r--test/utils.cpp19
-rwxr-xr-xtest/v4l2_compat/v4l2_compat_test.py2
-rw-r--r--test/v4l2_subdevice/v4l2_subdevice_test.cpp2
-rw-r--r--test/v4l2_subdevice/v4l2_subdevice_test.h2
-rw-r--r--test/v4l2_videodevice/capture_async.cpp11
-rw-r--r--test/v4l2_videodevice/controls.cpp2
-rw-r--r--test/v4l2_videodevice/v4l2_videodevice_test.cpp7
-rw-r--r--test/v4l2_videodevice/v4l2_videodevice_test.h4
-rw-r--r--test/vector.cpp100
-rw-r--r--test/yaml-parser.cpp54
82 files changed, 1211 insertions, 308 deletions
diff --git a/test/bayer-format.cpp b/test/bayer-format.cpp
index 54f03487..f8d19804 100644
--- a/test/bayer-format.cpp
+++ b/test/bayer-format.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Sebastian Fricke
*
- * bayer_format.cpp - BayerFormat class tests
+ * BayerFormat class tests
*/
#include <iostream>
diff --git a/test/byte-stream-buffer.cpp b/test/byte-stream-buffer.cpp
index 04ff0571..04aab3d2 100644
--- a/test/byte-stream-buffer.cpp
+++ b/test/byte-stream-buffer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * byte_stream_buffer.cpp - ByteStreamBuffer tests
+ * ByteStreamBuffer tests
*/
#include <array>
diff --git a/test/camera-sensor.cpp b/test/camera-sensor.cpp
index 2a17cc79..869c7889 100644
--- a/test/camera-sensor.cpp
+++ b/test/camera-sensor.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera-sensor.cpp - Camera sensor tests
+ * Camera sensor tests
*/
#include <algorithm>
@@ -52,8 +52,8 @@ protected:
return TestFail;
}
- sensor_ = new CameraSensor(entity);
- if (sensor_->init() < 0) {
+ sensor_ = CameraSensorFactoryBase::create(entity);
+ if (!sensor_) {
cerr << "Unable to initialise camera sensor" << endl;
return TestFail;
}
@@ -100,7 +100,7 @@ protected:
MEDIA_BUS_FMT_SBGGR10_1X10,
MEDIA_BUS_FMT_BGR888_1X24 },
Size(1024, 768));
- if (format.mbus_code != MEDIA_BUS_FMT_SBGGR10_1X10 ||
+ if (format.code != MEDIA_BUS_FMT_SBGGR10_1X10 ||
format.size != Size(4096, 2160)) {
cerr << "Failed to get a suitable format, expected 4096x2160-0x"
<< utils::hex(MEDIA_BUS_FMT_SBGGR10_1X10)
@@ -118,13 +118,12 @@ protected:
void cleanup()
{
- delete sensor_;
}
private:
std::unique_ptr<DeviceEnumerator> enumerator_;
std::shared_ptr<MediaDevice> media_;
- CameraSensor *sensor_;
+ std::unique_ptr<CameraSensor> sensor_;
CameraLens *lens_;
};
diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp
index 92884004..815d1cae 100644
--- a/test/camera/buffer_import.cpp
+++ b/test/camera/buffer_import.cpp
@@ -63,6 +63,8 @@ protected:
request->reuse();
request->addBuffer(stream, buffer);
camera_->queueRequest(request);
+
+ dispatcher_->interrupt();
}
int init() override
@@ -76,6 +78,8 @@ protected:
return TestFail;
}
+ dispatcher_ = Thread::current()->eventDispatcher();
+
return TestPass;
}
@@ -133,17 +137,20 @@ protected:
}
}
- EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
+ const unsigned int nFrames = cfg.bufferCount * 2;
Timer timer;
- timer.start(1000ms);
- while (timer.isRunning())
- dispatcher->processEvents();
+ timer.start(500ms * nFrames);
+ while (timer.isRunning()) {
+ dispatcher_->processEvents();
+ if (completeRequestsCount_ > nFrames)
+ break;
+ }
- if (completeRequestsCount_ < cfg.bufferCount * 2) {
+ if (completeRequestsCount_ < nFrames) {
std::cout << "Failed to capture enough frames (got "
<< completeRequestsCount_ << " expected at least "
- << cfg.bufferCount * 2 << ")" << std::endl;
+ << nFrames << ")" << std::endl;
return TestFail;
}
@@ -161,6 +168,8 @@ protected:
}
private:
+ EventDispatcher *dispatcher_;
+
std::vector<std::unique_ptr<Request>> requests_;
unsigned int completeBuffersCount_;
diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp
index de824083..8766fb19 100644
--- a/test/camera/capture.cpp
+++ b/test/camera/capture.cpp
@@ -59,6 +59,8 @@ protected:
request->reuse();
request->addBuffer(stream, buffer);
camera_->queueRequest(request);
+
+ dispatcher_->interrupt();
}
int init() override
@@ -73,6 +75,7 @@ protected:
}
allocator_ = new FrameBufferAllocator(camera_);
+ dispatcher_ = Thread::current()->eventDispatcher();
return TestPass;
}
@@ -135,19 +138,20 @@ protected:
}
}
- EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
+ unsigned int nFrames = allocator_->buffers(stream).size() * 2;
Timer timer;
- timer.start(1000ms);
- while (timer.isRunning())
- dispatcher->processEvents();
-
- unsigned int nbuffers = allocator_->buffers(stream).size();
+ timer.start(500ms * nFrames);
+ while (timer.isRunning()) {
+ dispatcher_->processEvents();
+ if (completeRequestsCount_ > nFrames)
+ break;
+ }
- if (completeRequestsCount_ < nbuffers * 2) {
+ if (completeRequestsCount_ < nFrames) {
cout << "Failed to capture enough frames (got "
<< completeRequestsCount_ << " expected at least "
- << nbuffers * 2 << ")" << endl;
+ << nFrames * 2 << ")" << endl;
return TestFail;
}
@@ -164,6 +168,8 @@ protected:
return TestPass;
}
+ EventDispatcher *dispatcher_;
+
std::vector<std::unique_ptr<Request>> requests_;
std::unique_ptr<CameraConfiguration> config_;
diff --git a/test/controls/control_info.cpp b/test/controls/control_info.cpp
index 1176a502..e1bb43f0 100644
--- a/test/controls/control_info.cpp
+++ b/test/controls/control_info.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * control_info.cpp - ControlInfo tests
+ * ControlInfo tests
*/
#include <iostream>
diff --git a/test/controls/control_info_map.cpp b/test/controls/control_info_map.cpp
index 29b33515..b0be14b5 100644
--- a/test/controls/control_info_map.cpp
+++ b/test/controls/control_info_map.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * control_info.cpp - ControlInfoMap tests
+ * ControlInfoMap tests
*/
#include <iostream>
diff --git a/test/controls/control_list.cpp b/test/controls/control_list.cpp
index c03f230e..e27325c3 100644
--- a/test/controls/control_list.cpp
+++ b/test/controls/control_list.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * control_list.cpp - ControlList tests
+ * ControlList tests
*/
#include <iostream>
@@ -196,6 +196,56 @@ protected:
return TestFail;
}
+ /*
+ * Create two lists with overlapping controls. Merge them with
+ * overwriteExisting = true, verifying that the existing control
+ * values *get* overwritten.
+ */
+ mergeList.clear();
+ mergeList.set(controls::Brightness, 0.7f);
+ mergeList.set(controls::Saturation, 0.4f);
+
+ list.clear();
+ list.set(controls::Brightness, 0.5f);
+ list.set(controls::Contrast, 1.1f);
+
+ mergeList.merge(list, ControlList::MergePolicy::OverwriteExisting);
+ if (mergeList.size() != 3) {
+ cout << "Merged list should contain three elements" << endl;
+ return TestFail;
+ }
+
+ if (list.size() != 2) {
+ cout << "The list to merge should contain two elements"
+ << endl;
+ return TestFail;
+ }
+
+ if (!mergeList.get(controls::Brightness) ||
+ !mergeList.get(controls::Contrast) ||
+ !mergeList.get(controls::Saturation)) {
+ cout << "Merged list does not contain all controls" << endl;
+ return TestFail;
+ }
+
+ if (mergeList.get(controls::Brightness) != 0.5f) {
+ cout << "Brightness control value did not change after merging lists"
+ << endl;
+ return TestFail;
+ }
+
+ if (mergeList.get(controls::Contrast) != 1.1f) {
+ cout << "Contrast control value changed after merging lists"
+ << endl;
+ return TestFail;
+ }
+
+ if (mergeList.get(controls::Saturation) != 0.4f) {
+ cout << "Saturation control value changed after merging lists"
+ << endl;
+ return TestFail;
+ }
+
return TestPass;
}
};
diff --git a/test/controls/control_value.cpp b/test/controls/control_value.cpp
index ad8e05d0..5084fd0c 100644
--- a/test/controls/control_value.cpp
+++ b/test/controls/control_value.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * control_value.cpp - ControlValue tests
+ * ControlValue tests
*/
#include <algorithm>
@@ -110,6 +110,86 @@ protected:
}
/*
+ * Unsigned Integer16 type.
+ */
+ value.set(static_cast<uint16_t>(42));
+ if (value.isNone() || value.isArray() ||
+ value.type() != ControlTypeUnsigned16) {
+ cerr << "Control type mismatch after setting to uint16_t" << endl;
+ return TestFail;
+ }
+
+ if (value.get<uint16_t>() != 42) {
+ cerr << "Control value mismatch after setting to uint16_t" << endl;
+ return TestFail;
+ }
+
+ if (value.toString() != "42") {
+ cerr << "Control string mismatch after setting to uint16_t" << endl;
+ return TestFail;
+ }
+
+ std::array<uint16_t, 4> uint16s{ 3, 14, 15, 9 };
+ value.set(Span<uint16_t>(uint16s));
+ if (value.isNone() || !value.isArray() ||
+ value.type() != ControlTypeUnsigned16) {
+ cerr << "Control type mismatch after setting to uint16_t array" << endl;
+ return TestFail;
+ }
+
+ Span<const uint16_t> uint16sResult = value.get<Span<const uint16_t>>();
+ if (uint16s.size() != uint16sResult.size() ||
+ !std::equal(uint16s.begin(), uint16s.end(), uint16sResult.begin())) {
+ cerr << "Control value mismatch after setting to uint16_t array" << endl;
+ return TestFail;
+ }
+
+ if (value.toString() != "[ 3, 14, 15, 9 ]") {
+ cerr << "Control string mismatch after setting to uint16_t array" << endl;
+ return TestFail;
+ }
+
+ /*
+ * Unsigned Integer32 type.
+ */
+ value.set(static_cast<uint32_t>(42));
+ if (value.isNone() || value.isArray() ||
+ value.type() != ControlTypeUnsigned32) {
+ cerr << "Control type mismatch after setting to uint32_t" << endl;
+ return TestFail;
+ }
+
+ if (value.get<uint32_t>() != 42) {
+ cerr << "Control value mismatch after setting to uint32_t" << endl;
+ return TestFail;
+ }
+
+ if (value.toString() != "42") {
+ cerr << "Control string mismatch after setting to uint32_t" << endl;
+ return TestFail;
+ }
+
+ std::array<uint32_t, 4> uint32s{ 3, 14, 15, 9 };
+ value.set(Span<uint32_t>(uint32s));
+ if (value.isNone() || !value.isArray() ||
+ value.type() != ControlTypeUnsigned32) {
+ cerr << "Control type mismatch after setting to uint32_t array" << endl;
+ return TestFail;
+ }
+
+ Span<const uint32_t> uint32sResult = value.get<Span<const uint32_t>>();
+ if (uint32s.size() != uint32sResult.size() ||
+ !std::equal(uint32s.begin(), uint32s.end(), uint32sResult.begin())) {
+ cerr << "Control value mismatch after setting to uint32_t array" << endl;
+ return TestFail;
+ }
+
+ if (value.toString() != "[ 3, 14, 15, 9 ]") {
+ cerr << "Control string mismatch after setting to uint32_t array" << endl;
+ return TestFail;
+ }
+
+ /*
* Integer32 type.
*/
value.set(0x42000000);
diff --git a/test/delayed_controls.cpp b/test/delayed_controls.cpp
index a8ce9828..7bd30e7a 100644
--- a/test/delayed_controls.cpp
+++ b/test/delayed_controls.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * delayed_controls.cpp - libcamera delayed controls test
+ * libcamera delayed controls test
*/
#include <iostream>
diff --git a/test/event-dispatcher.cpp b/test/event-dispatcher.cpp
index 9b07ab2b..f71c1c0d 100644
--- a/test/event-dispatcher.cpp
+++ b/test/event-dispatcher.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event-dispatcher.cpp - Event dispatcher test
+ * Event dispatcher test
*/
#include <chrono>
diff --git a/test/event-thread.cpp b/test/event-thread.cpp
index ef8a52c3..5499bbf8 100644
--- a/test/event-thread.cpp
+++ b/test/event-thread.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event-thread.cpp - Threaded event test
+ * Threaded event test
*/
#include <chrono>
@@ -11,6 +11,7 @@
#include <unistd.h>
#include <libcamera/base/event_notifier.h>
+#include <libcamera/base/object.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
@@ -84,10 +85,17 @@ private:
class EventThreadTest : public Test
{
protected:
+ int init()
+ {
+ thread_.start();
+
+ handler_ = new EventHandler();
+
+ return TestPass;
+ }
+
int run()
{
- Thread thread;
- thread.start();
/*
* Fire the event notifier and then move the notifier to a
@@ -97,23 +105,33 @@ protected:
* different thread will correctly process already pending
* events in the new thread.
*/
- EventHandler handler;
- handler.notify();
- handler.moveToThread(&thread);
+ handler_->notify();
+ handler_->moveToThread(&thread_);
this_thread::sleep_for(chrono::milliseconds(100));
- /* Must stop thread before destroying the handler. */
- thread.exit(0);
- thread.wait();
-
- if (!handler.notified()) {
+ if (!handler_->notified()) {
cout << "Thread event handling test failed" << endl;
return TestFail;
}
return TestPass;
}
+
+ void cleanup()
+ {
+ /*
+ * Object class instances must be destroyed from the thread
+ * they live in.
+ */
+ handler_->deleteLater();
+ thread_.exit(0);
+ thread_.wait();
+ }
+
+private:
+ EventHandler *handler_;
+ Thread thread_;
};
TEST_REGISTER(EventThreadTest)
diff --git a/test/event.cpp b/test/event.cpp
index 19dceae1..9f7b1ed4 100644
--- a/test/event.cpp
+++ b/test/event.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event.cpp - Event test
+ * Event test
*/
#include <iostream>
diff --git a/test/fence.cpp b/test/fence.cpp
index 1e38bc2f..8095b228 100644
--- a/test/fence.cpp
+++ b/test/fence.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * fence.cpp - Fence test
+ * Fence test
*/
#include <iostream>
@@ -43,7 +43,6 @@ private:
void signalFence();
- std::unique_ptr<Fence> fence_;
EventDispatcher *dispatcher_;
UniqueFD eventFd_;
UniqueFD eventFd2_;
@@ -58,8 +57,11 @@ private:
bool expectedCompletionResult_ = true;
bool setFence_ = true;
- unsigned int completedRequest_;
-
+ /*
+ * Request IDs track the number of requests that have completed. They
+ * are one-based, and don't wrap.
+ */
+ unsigned int completedRequestId_;
unsigned int signalledRequestId_;
unsigned int expiredRequestId_;
unsigned int nbuffers_;
@@ -127,8 +129,19 @@ int FenceTest::init()
return TestFail;
}
- signalledRequestId_ = nbuffers_ - 2;
- expiredRequestId_ = nbuffers_ - 1;
+ completedRequestId_ = 0;
+
+ /*
+ * All but two requests are queued without a fence. Request
+ * expiredRequestId_ will be queued with a fence that we won't signal
+ * (which is then expected to expire), and request signalledRequestId_
+ * will be queued with a fence that gets signalled. Select nbuffers_
+ * and nbuffers_ * 2 for those two requests, to space them by a few
+ * frames while still not requiring a long time for the test to
+ * complete.
+ */
+ expiredRequestId_ = nbuffers_;
+ signalledRequestId_ = nbuffers_ * 2;
return TestPass;
}
@@ -190,16 +203,16 @@ void FenceTest::requestRequeue(Request *request)
const Request::BufferMap &buffers = request->buffers();
const Stream *stream = buffers.begin()->first;
FrameBuffer *buffer = buffers.begin()->second;
- uint64_t cookie = request->cookie();
request->reuse();
- if (cookie == signalledRequestId_ && setFence_) {
+ if (completedRequestId_ == signalledRequestId_ - nbuffers_ && setFence_) {
/*
- * The second time this request is queued add a fence to it.
- *
- * The main loop signals it by using a timer to write to the
- * efd2_ file descriptor before the fence expires.
+ * This is the request that will be used to test fence
+ * signalling when it completes next time. Add a fence to it,
+ * using efd2_. The main loop will signal the fence by using a
+ * timer to write to the efd2_ file descriptor before the fence
+ * expires.
*/
std::unique_ptr<Fence> fence =
std::make_unique<Fence>(std::move(eventFd2_));
@@ -214,16 +227,15 @@ void FenceTest::requestRequeue(Request *request)
void FenceTest::requestComplete(Request *request)
{
- uint64_t cookie = request->cookie();
- completedRequest_ = cookie;
+ completedRequestId_++;
/*
- * The last request is expected to fail as its fence has not been
- * signaled.
+ * Request expiredRequestId_ is expected to fail as its fence has not
+ * been signalled.
*
* Validate the fence status but do not re-queue it.
*/
- if (cookie == expiredRequestId_) {
+ if (completedRequestId_ == expiredRequestId_) {
if (validateExpiredRequest(request) != TestPass)
expectedCompletionResult_ = false;
@@ -231,7 +243,7 @@ void FenceTest::requestComplete(Request *request)
return;
}
- /* Validate all requests but the last. */
+ /* Validate all other requests. */
if (validateRequest(request) != TestPass) {
expectedCompletionResult_ = false;
@@ -272,15 +284,16 @@ int FenceTest::run()
}
int ret;
- if (i == expiredRequestId_) {
+ if (i == expiredRequestId_ - 1) {
/* This request will have a fence, and it will expire. */
- fence_ = std::make_unique<Fence>(std::move(eventFd_));
- if (!fence_->isValid()) {
+ std::unique_ptr<Fence> fence =
+ std::make_unique<Fence>(std::move(eventFd_));
+ if (!fence->isValid()) {
cerr << "Fence should be valid" << endl;
return TestFail;
}
- ret = request->addBuffer(stream_, buffer.get(), std::move(fence_));
+ ret = request->addBuffer(stream_, buffer.get(), std::move(fence));
} else {
/* All other requests will have no Fence. */
ret = request->addBuffer(stream_, buffer.get());
@@ -314,15 +327,21 @@ int FenceTest::run()
Timer fenceTimer;
fenceTimer.timeout.connect(this, &FenceTest::signalFence);
- /* Loop for one second. */
+ /*
+ * Loop long enough for all requests to complete, allowing 500ms per
+ * request.
+ */
Timer timer;
- timer.start(1000ms);
- while (timer.isRunning() && expectedCompletionResult_) {
- if (completedRequest_ == signalledRequestId_ && setFence_)
+ timer.start(500ms * (signalledRequestId_ + 1));
+ while (timer.isRunning() && expectedCompletionResult_ &&
+ completedRequestId_ <= signalledRequestId_ + 1) {
+ if (completedRequestId_ == signalledRequestId_ - 1 && setFence_)
/*
- * signalledRequestId_ has just completed and it has
- * been re-queued with a fence. Start the timer to
- * signal the fence in 10 msec.
+ * The request just before signalledRequestId_ has just
+ * completed. Request signalledRequestId_ has been
+ * queued with a fence, and libcamera is likely already
+ * waiting on the fence, or will soon. Start the timer
+ * to signal the fence in 10 msec.
*/
fenceTimer.start(10ms);
diff --git a/test/file.cpp b/test/file.cpp
index 5c978ebf..170e6ccd 100644
--- a/test/file.cpp
+++ b/test/file.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * file.cpp - File I/O operations tests
+ * File I/O operations tests
*/
#include <fstream>
diff --git a/test/flags.cpp b/test/flags.cpp
index 2177e247..85c34788 100644
--- a/test/flags.cpp
+++ b/test/flags.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * flags.cpp - Flags tests
+ * Flags tests
*/
#include <iostream>
diff --git a/test/geometry.cpp b/test/geometry.cpp
index 008d51ea..11df043b 100644
--- a/test/geometry.cpp
+++ b/test/geometry.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * geometry.cpp - Geometry classes tests
+ * Geometry classes tests
*/
#include <iostream>
@@ -481,6 +481,31 @@ protected:
return TestFail;
}
+ Point topLeft(3, 3);
+ Point bottomRight(30, 30);
+ Point topRight(30, 3);
+ Point bottomLeft(3, 30);
+ Rectangle rect1(topLeft, bottomRight);
+ Rectangle rect2(topRight, bottomLeft);
+ Rectangle rect3(bottomRight, topLeft);
+ Rectangle rect4(bottomLeft, topRight);
+
+ if (rect1 != rect2 || rect1 != rect3 || rect1 != rect4) {
+ cout << "Point-to-point construction failed" << endl;
+ return TestFail;
+ }
+
+ Rectangle f1 = Rectangle(100, 200, 3000, 2000);
+ Rectangle f2 = Rectangle(200, 300, 1500, 1000);
+ /* Bottom right quarter of the corresponding frames. */
+ Rectangle r1 = Rectangle(100 + 1500, 200 + 1000, 1500, 1000);
+ Rectangle r2 = Rectangle(200 + 750, 300 + 500, 750, 500);
+ if (r1.transformedBetween(f1, f2) != r2 ||
+ r2.transformedBetween(f2, f1) != r1) {
+ cout << "Rectangle::transformedBetween() test failed" << endl;
+ return TestFail;
+ }
+
return TestPass;
}
};
diff --git a/test/gstreamer/gstreamer_device_provider_test.cpp b/test/gstreamer/gstreamer_device_provider_test.cpp
index 237af8cd..521c60b8 100644
--- a/test/gstreamer/gstreamer_device_provider_test.cpp
+++ b/test/gstreamer/gstreamer_device_provider_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Umang Jain <umang.jain@ideasonboard.com>
*
- * gstreamer_single_stream_test.cpp - GStreamer single stream capture test
+ * GStreamer single stream capture test
*/
#include <vector>
@@ -52,19 +52,12 @@ protected:
for (l = devices; l != NULL; l = g_list_next(l)) {
GstDevice *device = GST_DEVICE(l->data);
g_autofree gchar *gst_name;
- bool matched = false;
g_autoptr(GstElement) element = gst_device_create_element(device, NULL);
g_object_get(element, "camera-name", &gst_name, NULL);
- for (auto name : cameraNames) {
- if (strcmp(name.c_str(), gst_name) == 0) {
- matched = true;
- break;
- }
- }
-
- if (!matched)
+ if (std::find(cameraNames.begin(), cameraNames.end(), gst_name) ==
+ cameraNames.end())
return TestFail;
}
diff --git a/test/gstreamer/gstreamer_memory_lifetime_test.cpp b/test/gstreamer/gstreamer_memory_lifetime_test.cpp
new file mode 100644
index 00000000..1738cf56
--- /dev/null
+++ b/test/gstreamer/gstreamer_memory_lifetime_test.cpp
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Nicolas Dufresne
+ *
+ * gstreamer_memory_lifetime_test.cpp - GStreamer memory lifetime test
+ */
+
+#include <iostream>
+#include <unistd.h>
+
+#include <gst/app/app.h>
+#include <gst/gst.h>
+
+#include "gstreamer_test.h"
+#include "test.h"
+
+using namespace std;
+
+class GstreamerMemoryLifetimeTest : public GstreamerTest, public Test
+{
+public:
+ GstreamerMemoryLifetimeTest()
+ : GstreamerTest()
+ {
+ }
+
+protected:
+ int init() override
+ {
+ if (status_ != TestPass)
+ return status_;
+
+ appsink_ = gst_element_factory_make("appsink", nullptr);
+ if (!appsink_) {
+ g_printerr("Your installation is missing 'appsink'\n");
+ return TestFail;
+ }
+ g_object_ref_sink(appsink_);
+
+ return createPipeline();
+ }
+
+ int run() override
+ {
+ /* Build the pipeline */
+ gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, appsink_, nullptr);
+ if (gst_element_link(libcameraSrc_, appsink_) != TRUE) {
+ g_printerr("Elements could not be linked.\n");
+ return TestFail;
+ }
+
+ if (startPipeline() != TestPass)
+ return TestFail;
+
+ sample_ = gst_app_sink_try_pull_sample(GST_APP_SINK(appsink_), GST_SECOND * 5);
+ if (!sample_) {
+ /* Failed to obtain a sample. Abort the test */
+ gst_element_set_state(pipeline_, GST_STATE_NULL);
+ return TestFail;
+ }
+
+ /*
+ * Keep the sample referenced and set the pipeline state to
+ * NULL. This causes the libcamerasrc element to synchronously
+ * release resources it holds. The sample will be released
+ * later in cleanup().
+ *
+ * The test case verifies that libcamerasrc keeps alive long
+ * enough all the resources that are needed until memory
+ * allocated for frames gets freed. We return TestPass at this
+ * stage, and any use-after-free will be caught by the test
+ * crashing in cleanup().
+ */
+ gst_element_set_state(pipeline_, GST_STATE_NULL);
+
+ return TestPass;
+ }
+
+ void cleanup() override
+ {
+ g_clear_pointer(&sample_, gst_sample_unref);
+ g_clear_object(&appsink_);
+ }
+
+private:
+ GstElement *appsink_;
+ GstSample *sample_;
+};
+
+TEST_REGISTER(GstreamerMemoryLifetimeTest)
diff --git a/test/gstreamer/gstreamer_multi_stream_test.cpp b/test/gstreamer/gstreamer_multi_stream_test.cpp
index cd669308..263d1e86 100644
--- a/test/gstreamer/gstreamer_multi_stream_test.cpp
+++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Vedant Paranjape
*
- * gstreamer_multi_stream_test.cpp - GStreamer multi stream capture test
+ * GStreamer multi stream capture test
*/
#include <iostream>
diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp
index a0dd12cf..3ef2d323 100644
--- a/test/gstreamer/gstreamer_single_stream_test.cpp
+++ b/test/gstreamer/gstreamer_single_stream_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Vedant Paranjape
*
- * gstreamer_single_stream_test.cpp - GStreamer single stream capture test
+ * GStreamer single stream capture test
*/
#include <iostream>
@@ -29,30 +29,21 @@ protected:
if (status_ != TestPass)
return status_;
- const gchar *streamDescription = "videoconvert ! fakesink";
- g_autoptr(GError) error0 = NULL;
- stream0_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
- NULL,
- GST_PARSE_FLAG_FATAL_ERRORS,
- &error0);
-
- if (!stream0_) {
- g_printerr("Bin could not be created (%s)\n", error0->message);
+ fakesink_ = gst_element_factory_make("fakesink", nullptr);
+ if (!fakesink_) {
+ g_printerr("Your installation is missing 'fakesink'\n");
return TestFail;
}
- g_object_ref_sink(stream0_);
-
- if (createPipeline() != TestPass)
- return TestFail;
+ g_object_ref_sink(fakesink_);
- return TestPass;
+ return createPipeline();
}
int run() override
{
/* Build the pipeline */
- gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, stream0_, NULL);
- if (gst_element_link(libcameraSrc_, stream0_) != TRUE) {
+ gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, fakesink_, nullptr);
+ if (!gst_element_link(libcameraSrc_, fakesink_)) {
g_printerr("Elements could not be linked.\n");
return TestFail;
}
@@ -68,11 +59,11 @@ protected:
void cleanup() override
{
- g_clear_object(&stream0_);
+ g_clear_object(&fakesink_);
}
private:
- GstElement *stream0_;
+ GstElement *fakesink_;
};
TEST_REGISTER(GstreamerSingleStreamTest)
diff --git a/test/gstreamer/gstreamer_test.cpp b/test/gstreamer/gstreamer_test.cpp
index 6ad0c15c..a15fef0e 100644
--- a/test/gstreamer/gstreamer_test.cpp
+++ b/test/gstreamer/gstreamer_test.cpp
@@ -9,12 +9,17 @@
#include <libcamera/base/utils.h>
+#if HAVE_ASAN
+#include <sanitizer/asan_interface.h>
+#endif
+
#include "gstreamer_test.h"
#include "test.h"
using namespace std;
+#if HAVE_ASAN
extern "C" {
const char *__asan_default_options()
{
@@ -26,20 +31,20 @@ const char *__asan_default_options()
return "detect_leaks=false";
}
}
+#endif
GstreamerTest::GstreamerTest(unsigned int numStreams)
: pipeline_(nullptr), libcameraSrc_(nullptr)
{
/*
- * GStreamer by default spawns a process to run the
- * gst-plugin-scanner helper. If libcamera is compiled with ASan
- * enabled, and as GStreamer is most likely not, this causes the
- * ASan link order check to fail when gst-plugin-scanner
- * dlopen()s the plugin as many libraries will have already been
- * loaded by then. Fix this issue by disabling spawning of a
- * child helper process when scanning the build directory for
- * plugins.
- */
+ * GStreamer by default spawns a process to run the gst-plugin-scanner
+ * helper. If libcamera is compiled with ASan enabled, and as GStreamer
+ * is most likely not, this causes the ASan link order check to fail
+ * when gst-plugin-scanner dlopen()s the plugin as many libraries will
+ * have already been loaded by then. Fix this issue by disabling
+ * spawning of a child helper process when scanning the build directory
+ * for plugins.
+ */
gst_registry_fork_set_enabled(false);
/* Initialize GStreamer */
@@ -53,23 +58,6 @@ GstreamerTest::GstreamerTest(unsigned int numStreams)
}
/*
- * Remove the system libcamera plugin, if any, and add the
- * plugin from the build directory.
- */
- GstRegistry *registry = gst_registry_get();
- g_autoptr(GstPlugin) plugin = gst_registry_lookup(registry, "libgstlibcamera.so");
- if (plugin)
- gst_registry_remove_plugin(registry, plugin);
-
- std::string path = libcamera::utils::libcameraBuildPath() + "src/gstreamer";
- if (!gst_registry_scan_path(registry, path.c_str())) {
- g_printerr("Failed to add plugin to registry\n");
-
- status_ = TestFail;
- return;
- }
-
- /*
* Atleast one camera should be available with numStreams streams,
* otherwise skip the test entirely.
*/
diff --git a/test/gstreamer/gstreamer_test.h b/test/gstreamer/gstreamer_test.h
index aa2261e2..abb37c1b 100644
--- a/test/gstreamer/gstreamer_test.h
+++ b/test/gstreamer/gstreamer_test.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Vedant Paranjape
*
- * gstreamer_test.cpp - GStreamer test base class
+ * GStreamer test base class
*/
#pragma once
diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build
index a5c003b6..e066c582 100644
--- a/test/gstreamer/meson.build
+++ b/test/gstreamer/meson.build
@@ -8,14 +8,24 @@ gstreamer_tests = [
{'name': 'single_stream_test', 'sources': ['gstreamer_single_stream_test.cpp']},
{'name': 'multi_stream_test', 'sources': ['gstreamer_multi_stream_test.cpp']},
{'name': 'device_provider_test', 'sources': ['gstreamer_device_provider_test.cpp']},
+ {'name': 'memory_lifetime_test', 'sources': ['gstreamer_memory_lifetime_test.cpp']},
]
gstreamer_dep = dependency('gstreamer-1.0', required : true)
+gstapp_dep = dependency('gstreamer-app-1.0', required : true)
+
+gstreamer_test_args = []
+
+if asan_enabled
+ gstreamer_test_args += ['-D', 'HAVE_ASAN=1']
+endif
foreach test : gstreamer_tests
exe = executable(test['name'], test['sources'], 'gstreamer_test.cpp',
- dependencies : [libcamera_private, gstreamer_dep],
+ cpp_args : gstreamer_test_args,
+ dependencies : [libcamera_private, gstreamer_dep, gstapp_dep],
link_with : test_libraries,
include_directories : test_includes_internal)
- test(test['name'], exe, suite : 'gstreamer', is_parallel : false)
+ test(test['name'], exe, suite : 'gstreamer', is_parallel : false,
+ env : gst_env, should_fail : test.get('should_fail', false))
endforeach
diff --git a/test/hotplug-cameras.cpp b/test/hotplug-cameras.cpp
index 5d9260a2..530e9a31 100644
--- a/test/hotplug-cameras.cpp
+++ b/test/hotplug-cameras.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Umang Jain <email@uajain.com>
*
- * hotplug-cameras.cpp - Test cameraAdded/cameraRemoved signals in CameraManager
+ * Test cameraAdded/cameraRemoved signals in CameraManager
*/
#include <dirent.h>
diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp
index 051ef96e..b8178366 100644
--- a/test/ipa/ipa_interface_test.cpp
+++ b/test/ipa/ipa_interface_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_interface_test.cpp - Test the IPA interface
+ * Test the IPA interface
*/
#include <fcntl.h>
@@ -16,14 +16,15 @@
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/event_notifier.h>
+#include <libcamera/base/object.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
+#include "libcamera/internal/camera_manager.h"
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/ipa_manager.h"
#include "libcamera/internal/ipa_module.h"
#include "libcamera/internal/pipeline_handler.h"
-#include "libcamera/internal/process.h"
#include "test.h"
@@ -43,20 +44,20 @@ public:
{
delete notifier_;
ipa_.reset();
- ipaManager_.reset();
+ cameraManager_.reset();
}
protected:
int init() override
{
- ipaManager_ = make_unique<IPAManager>();
+ 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() == "PipelineHandlerVimc") {
- pipe_ = factory->create(nullptr);
+ if (factory->name() == "vimc") {
+ pipe_ = factory->create(cameraManager_.get());
break;
}
}
@@ -170,11 +171,9 @@ private:
}
}
- ProcessManager processManager_;
-
std::shared_ptr<PipelineHandler> pipe_;
std::unique_ptr<ipa::vimc::IPAProxyVimc> ipa_;
- std::unique_ptr<IPAManager> ipaManager_;
+ std::unique_ptr<CameraManager> cameraManager_;
enum ipa::vimc::IPAOperationCode trace_;
EventNotifier *notifier_;
int fd_;
diff --git a/test/ipa/ipa_module_test.cpp b/test/ipa/ipa_module_test.cpp
index bd5e0e4c..1c97da32 100644
--- a/test/ipa/ipa_module_test.cpp
+++ b/test/ipa/ipa_module_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_module_test.cpp - Test loading of the VIMC IPA module and verify its info
+ * Test loading of the VIMC IPA module and verify its info
*/
#include <iostream>
@@ -57,7 +57,7 @@ protected:
const struct IPAModuleInfo testInfo = {
IPA_MODULE_API_VERSION,
0,
- "PipelineHandlerVimc",
+ "vimc",
"vimc",
};
diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp
new file mode 100644
index 00000000..99eb662d
--- /dev/null
+++ b/test/ipa/libipa/fixedpoint.cpp
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Fixed / Floating point utility tests
+ */
+
+#include <cmath>
+#include <iostream>
+#include <map>
+#include <stdint.h>
+
+#include "../src/ipa/libipa/fixedpoint.h"
+
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+using namespace ipa;
+
+class FixedPointUtilsTest : public Test
+{
+protected:
+ /* R for real, I for integer */
+ template<unsigned int IntPrec, unsigned int FracPrec, typename I, typename R>
+ int testFixedToFloat(I input, R expected)
+ {
+ R out = fixedToFloatingPoint<IntPrec, FracPrec, R>(input);
+ R prec = 1.0 / (1 << FracPrec);
+ if (std::abs(out - expected) > prec) {
+ cerr << "Reverse conversion expected " << input
+ << " to convert to " << expected
+ << ", got " << out << std::endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+
+ template<unsigned int IntPrec, unsigned int FracPrec, typename T>
+ int testSingleFixedPoint(double input, T expected)
+ {
+ T ret = floatingToFixedPoint<IntPrec, FracPrec, T>(input);
+ if (ret != expected) {
+ cerr << "Expected " << input << " to convert to "
+ << expected << ", got " << ret << std::endl;
+ return TestFail;
+ }
+
+ /*
+ * The precision check is fairly arbitrary but is based on what
+ * the rkisp1 is capable of in the crosstalk module.
+ */
+ double f = fixedToFloatingPoint<IntPrec, FracPrec, double>(ret);
+ if (std::abs(f - input) > 0.005) {
+ cerr << "Reverse conversion expected " << ret
+ << " to convert to " << input
+ << ", got " << f << std::endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+
+ int testFixedPoint()
+ {
+ /*
+ * The second 7.992 test is to test that unused bits don't
+ * affect the result.
+ */
+ std::map<double, uint16_t> testCases = {
+ { 7.992, 0x3ff },
+ { 0.2, 0x01a },
+ { -0.2, 0x7e6 },
+ { -0.8, 0x79a },
+ { -0.4, 0x7cd },
+ { -1.4, 0x74d },
+ { -8, 0x400 },
+ { 0, 0 },
+ };
+
+ int ret;
+ for (const auto &testCase : testCases) {
+ ret = testSingleFixedPoint<4, 7, uint16_t>(testCase.first,
+ testCase.second);
+ if (ret != TestPass)
+ return ret;
+ }
+
+ /* Special case with a superfluous one in the unused bits */
+ ret = testFixedToFloat<4, 7, uint16_t, double>(0xbff, 7.992);
+ if (ret != TestPass)
+ return ret;
+
+ return TestPass;
+ }
+
+ int run()
+ {
+ /* fixed point conversion test */
+ if (testFixedPoint() != TestPass)
+ return TestFail;
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(FixedPointUtilsTest)
diff --git a/test/ipa/libipa/histogram.cpp b/test/ipa/libipa/histogram.cpp
new file mode 100644
index 00000000..77ff31a6
--- /dev/null
+++ b/test/ipa/libipa/histogram.cpp
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Histogram tests
+ */
+
+#include "../src/ipa/libipa/histogram.h"
+
+#include <cmath>
+#include <iostream>
+#include <map>
+#include <stdint.h>
+
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+using namespace ipa;
+
+#define ASSERT_EQ(a, b) \
+ if (!((a) == (b))) { \
+ std::cout << #a " != " #b << std::endl; \
+ return TestFail; \
+ }
+
+class HistogramTest : public Test
+{
+protected:
+ int run()
+ {
+ auto hist = Histogram({ { 50, 50 } });
+
+ ASSERT_EQ(hist.bins(), 2);
+ ASSERT_EQ(hist.total(), 100);
+
+ ASSERT_EQ(hist.cumulativeFrequency(1.0), 50);
+ ASSERT_EQ(hist.cumulativeFrequency(1.5), 75);
+ ASSERT_EQ(hist.cumulativeFrequency(2.0), 100);
+
+ ASSERT_EQ(hist.quantile(0.0), 0.0);
+ ASSERT_EQ(hist.quantile(1.0), 2.0);
+ ASSERT_EQ(hist.quantile(0.5), 1.0);
+
+ /* Test quantile in the middle of a bin. */
+ ASSERT_EQ(hist.quantile(0.75), 1.5);
+
+ /* Test quantile smaller than the smallest histogram step. */
+ ASSERT_EQ(hist.quantile(0.001), 0.002);
+
+ ASSERT_EQ(hist.interQuantileMean(0.0, 1.0), 1.0);
+ ASSERT_EQ(hist.interQuantileMean(0.0, 0.5), 0.5);
+ ASSERT_EQ(hist.interQuantileMean(0.5, 1.0), 1.5);
+
+ /* Test interquantile mean that starts and ends in the middle of a bin. */
+ ASSERT_EQ(hist.interQuantileMean(0.25, 0.75), 1.0);
+
+ /* Test small ranges at the borders of the histogram. */
+ ASSERT_EQ(hist.interQuantileMean(0.0, 0.1), 0.1);
+ ASSERT_EQ(hist.interQuantileMean(0.9, 1.0), 1.9);
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(HistogramTest)
diff --git a/test/ipa/libipa/interpolator.cpp b/test/ipa/libipa/interpolator.cpp
new file mode 100644
index 00000000..6abb7760
--- /dev/null
+++ b/test/ipa/libipa/interpolator.cpp
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Interpolator tests
+ */
+
+#include "../src/ipa/libipa/interpolator.h"
+
+#include <cmath>
+#include <iostream>
+#include <map>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+using namespace ipa;
+
+#define ASSERT_EQ(a, b) \
+ if ((a) != (b)) { \
+ printf(#a " != " #b "\n"); \
+ return TestFail; \
+ }
+
+class InterpolatorTest : public Test
+{
+protected:
+ int run()
+ {
+ Interpolator<int> interpolator;
+ interpolator.setData({ { 10, 100 }, { 20, 200 }, { 30, 300 } });
+
+ ASSERT_EQ(interpolator.getInterpolated(0), 100);
+ ASSERT_EQ(interpolator.getInterpolated(10), 100);
+ ASSERT_EQ(interpolator.getInterpolated(20), 200);
+ ASSERT_EQ(interpolator.getInterpolated(25), 250);
+ ASSERT_EQ(interpolator.getInterpolated(30), 300);
+ ASSERT_EQ(interpolator.getInterpolated(40), 300);
+
+ interpolator.setQuantization(10);
+ unsigned int q = 0;
+ ASSERT_EQ(interpolator.getInterpolated(25, &q), 300);
+ ASSERT_EQ(q, 30);
+ ASSERT_EQ(interpolator.getInterpolated(24, &q), 200);
+ ASSERT_EQ(q, 20);
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(InterpolatorTest)
diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build
new file mode 100644
index 00000000..8c63ebd8
--- /dev/null
+++ b/test/ipa/libipa/meson.build
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: CC0-1.0
+
+libipa_test = [
+ {'name': 'fixedpoint', 'sources': ['fixedpoint.cpp']},
+ {'name': 'histogram', 'sources': ['histogram.cpp']},
+ {'name': 'interpolator', 'sources': ['interpolator.cpp']},
+]
+
+foreach test : libipa_test
+ exe = executable(test['name'], test['sources'],
+ dependencies : [libcamera_private, libipa_dep],
+ implicit_include_directories : false,
+ link_with : [test_libraries],
+ include_directories : [test_includes_internal,
+ '../../../src/ipa/libipa/'])
+
+ test(test['name'], exe, suite : 'ipa',
+ should_fail : test.get('should_fail', false))
+endforeach
diff --git a/test/ipa/meson.build b/test/ipa/meson.build
index 180b0da0..ceed15ba 100644
--- a/test/ipa/meson.build
+++ b/test/ipa/meson.build
@@ -1,15 +1,17 @@
# SPDX-License-Identifier: CC0-1.0
+subdir('libipa')
+
ipa_test = [
{'name': 'ipa_module_test', 'sources': ['ipa_module_test.cpp']},
{'name': 'ipa_interface_test', 'sources': ['ipa_interface_test.cpp']},
]
foreach test : ipa_test
- exe = executable(test['name'], test['sources'], libcamera_generated_ipa_headers,
- dependencies : libcamera_private,
- link_with : [libipa, test_libraries],
- include_directories : [libipa_includes, test_includes_internal])
+ exe = executable(test['name'], test['sources'],
+ dependencies : [libcamera_private, libipa_dep],
+ link_with : [test_libraries],
+ include_directories : [test_includes_internal])
test(test['name'], exe, suite : 'ipa')
endforeach
diff --git a/test/ipc/unixsocket.cpp b/test/ipc/unixsocket.cpp
index 304e613b..f39bd986 100644
--- a/test/ipc/unixsocket.cpp
+++ b/test/ipc/unixsocket.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * unixsocket.cpp - Unix socket IPC test
+ * Unix socket IPC test
*/
#include <algorithm>
@@ -15,6 +15,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <vector>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/thread.h>
@@ -34,6 +35,8 @@ using namespace libcamera;
using namespace std;
using namespace std::chrono_literals;
+namespace {
+
int calculateLength(int fd)
{
lseek(fd, 0, 0);
@@ -43,6 +46,8 @@ int calculateLength(int fd)
return size;
}
+} /* namespace */
+
class UnixSocketTestSlave
{
public:
@@ -336,14 +341,14 @@ protected:
for (unsigned int i = 0; i < std::size(strings); i++) {
unsigned int len = strlen(strings[i]);
- char buf[len];
+ std::vector<char> buf(len);
close(fds[i]);
- if (read(response.fds[0], &buf, len) <= 0)
+ if (read(response.fds[0], buf.data(), len) <= 0)
return TestFail;
- if (memcmp(buf, strings[i], len))
+ if (memcmp(buf.data(), strings[i], len))
return TestFail;
}
@@ -431,7 +436,7 @@ private:
if (ret)
return ret;
- timeout.start(200ms);
+ timeout.start(2s);
while (!callDone_) {
if (!timeout.isRunning()) {
cerr << "Call timeout!" << endl;
diff --git a/test/ipc/unixsocket_ipc.cpp b/test/ipc/unixsocket_ipc.cpp
index 3ee6017e..df7d9c2b 100644
--- a/test/ipc/unixsocket_ipc.cpp
+++ b/test/ipc/unixsocket_ipc.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * unixsocket_ipc.cpp - Unix socket IPC test
+ * Unix socket IPC test
*/
#include <algorithm>
diff --git a/test/libtest/buffer_source.h b/test/libtest/buffer_source.h
index 0cc71aa5..495da8a9 100644
--- a/test/libtest/buffer_source.h
+++ b/test/libtest/buffer_source.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * buffer_source.h - libcamera camera test helper to create FrameBuffers
+ * libcamera camera test helper to create FrameBuffers
*/
#pragma once
diff --git a/test/libtest/camera_test.h b/test/libtest/camera_test.h
index 0b178bc2..713b503f 100644
--- a/test/libtest/camera_test.h
+++ b/test/libtest/camera_test.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_test.h - libcamera camera test base class
+ * libcamera camera test base class
*/
#pragma once
diff --git a/test/libtest/test.cpp b/test/libtest/test.cpp
index af37b4dd..4e03def9 100644
--- a/test/libtest/test.cpp
+++ b/test/libtest/test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * test.cpp - libcamera test base class
+ * libcamera test base class
*/
#include <stdlib.h>
diff --git a/test/libtest/test.h b/test/libtest/test.h
index 23b07743..3a90885d 100644
--- a/test/libtest/test.h
+++ b/test/libtest/test.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * test.h - libcamera test base class
+ * libcamera test base class
*/
#pragma once
diff --git a/test/log/log_api.cpp b/test/log/log_api.cpp
index 53118960..0b999738 100644
--- a/test/log/log_api.cpp
+++ b/test/log/log_api.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * log.cpp - log API test
+ * log API test
*/
#include <algorithm>
diff --git a/test/log/log_process.cpp b/test/log/log_process.cpp
index 966b80cf..9609e23d 100644
--- a/test/log/log_process.cpp
+++ b/test/log/log_process.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * log_process.cpp - Logging in isolated child process test
+ * Logging in isolated child process test
*/
#include <fcntl.h>
@@ -81,12 +81,13 @@ protected:
return TestFail;
}
- timeout.start(200ms);
+ timeout.start(2s);
while (timeout.isRunning())
dispatcher->processEvents();
if (exitStatus_ != Process::NormalExit) {
- cerr << "process did not exit normally" << endl;
+ cerr << "process did not exit normally: " << exitStatus_
+ << endl;
return TestFail;
}
@@ -115,8 +116,11 @@ protected:
close(fd);
string str(buf);
- if (str.find(message) == string::npos)
+ if (str.find(message) == string::npos) {
+ cerr << "Received message is not correct (received "
+ << str.length() << " bytes)" << endl;
return TestFail;
+ }
return TestPass;
}
@@ -136,7 +140,7 @@ private:
ProcessManager processManager_;
Process proc_;
- Process::ExitStatus exitStatus_;
+ Process::ExitStatus exitStatus_ = Process::NotExited;
string logPath_;
int exitCode_;
int num_;
diff --git a/test/matrix.cpp b/test/matrix.cpp
new file mode 100644
index 00000000..4afae2da
--- /dev/null
+++ b/test/matrix.cpp
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Matrix tests
+ */
+
+#include "libcamera/internal/matrix.h"
+
+#include <cmath>
+#include <iostream>
+
+#include "test.h"
+
+using namespace libcamera;
+
+#define ASSERT_EQ(a, b) \
+ if ((a) != (b)) { \
+ std::cout << #a " != " #b << " (line " << __LINE__ << ")" \
+ << std::endl; \
+ return TestFail; \
+ }
+
+class MatrixTest : public Test
+{
+protected:
+ int run()
+ {
+ Matrix<double, 3, 3> m1;
+
+ ASSERT_EQ(m1[0][0], 0.0);
+ ASSERT_EQ(m1[0][1], 0.0);
+
+ constexpr Matrix<float, 2, 2> m2 = Matrix<float, 2, 2>().identity();
+ ASSERT_EQ(m2[0][0], 1.0);
+ ASSERT_EQ(m2[0][1], 0.0);
+ ASSERT_EQ(m2[1][0], 0.0);
+ ASSERT_EQ(m2[1][1], 1.0);
+
+ Matrix<float, 2, 2> m3{ { 2.0, 0.0, 0.0, 2.0 } };
+ Matrix<float, 2, 2> m4 = m3.inverse();
+
+ Matrix<float, 2, 2> m5 = m3 * m4;
+ ASSERT_EQ(m5[0][0], 1.0);
+ ASSERT_EQ(m5[0][1], 0.0);
+ ASSERT_EQ(m5[1][0], 0.0);
+ ASSERT_EQ(m5[1][1], 1.0);
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(MatrixTest)
diff --git a/test/media_device/media_device_link_test.cpp b/test/media_device/media_device_link_test.cpp
index e11f6b78..31528000 100644
--- a/test/media_device/media_device_link_test.cpp
+++ b/test/media_device/media_device_link_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * media_device_link_test.cpp - Tests link handling on VIMC media device
+ * Tests link handling on VIMC media device
*/
#include <iostream>
diff --git a/test/media_device/media_device_print_test.cpp b/test/media_device/media_device_print_test.cpp
index cdec5b8d..63aeed48 100644
--- a/test/media_device/media_device_print_test.cpp
+++ b/test/media_device/media_device_print_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018-2019, Google Inc.
*
- * media_device_print_test.cpp - Print out media devices
+ * Print out media devices
*/
#include <iostream>
diff --git a/test/media_device/media_device_test.cpp b/test/media_device/media_device_test.cpp
index 1397d123..3e41d0f0 100644
--- a/test/media_device/media_device_test.cpp
+++ b/test/media_device/media_device_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * media_device_test.cpp - libcamera media device test base class
+ * libcamera media device test base class
*/
#include <iostream>
diff --git a/test/media_device/media_device_test.h b/test/media_device/media_device_test.h
index 9b226f1a..5223b760 100644
--- a/test/media_device/media_device_test.h
+++ b/test/media_device/media_device_test.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * media_device_test.h - libcamera media device test base class
+ * libcamera media device test base class
*/
#pragma once
diff --git a/test/meson.build b/test/meson.build
index 189e1428..52f04364 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -60,6 +60,7 @@ internal_tests = [
{'name': 'file', 'sources': ['file.cpp']},
{'name': 'flags', 'sources': ['flags.cpp']},
{'name': 'hotplug-cameras', 'sources': ['hotplug-cameras.cpp']},
+ {'name': 'matrix', 'sources': ['matrix.cpp']},
{'name': 'message', 'sources': ['message.cpp']},
{'name': 'object', 'sources': ['object.cpp']},
{'name': 'object-delete', 'sources': ['object-delete.cpp']},
@@ -69,9 +70,11 @@ internal_tests = [
{'name': 'signal-threads', 'sources': ['signal-threads.cpp']},
{'name': 'threads', 'sources': 'threads.cpp', 'dependencies': [libthreads]},
{'name': 'timer', 'sources': ['timer.cpp']},
+ {'name': 'timer-fail', 'sources': ['timer-fail.cpp'], 'should_fail': true},
{'name': 'timer-thread', 'sources': ['timer-thread.cpp']},
{'name': 'unique-fd', 'sources': ['unique-fd.cpp']},
{'name': 'utils', 'sources': ['utils.cpp']},
+ {'name': 'vector', 'sources': ['vector.cpp']},
{'name': 'yaml-parser', 'sources': ['yaml-parser.cpp']},
]
@@ -88,10 +91,11 @@ foreach test : public_tests
exe = executable(test['name'], test['sources'],
dependencies : deps,
+ implicit_include_directories : false,
link_with : test_libraries,
include_directories : test_includes_public)
- test(test['name'], exe)
+ test(test['name'], exe, should_fail : test.get('should_fail', false))
endforeach
foreach test : internal_tests
@@ -102,10 +106,11 @@ foreach test : internal_tests
exe = executable(test['name'], test['sources'],
dependencies : deps,
+ implicit_include_directories : false,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(test['name'], exe)
+ test(test['name'], exe, should_fail : test.get('should_fail', false))
endforeach
foreach test : internal_non_parallel_tests
@@ -116,8 +121,11 @@ foreach test : internal_non_parallel_tests
exe = executable(test['name'], test['sources'],
dependencies : deps,
+ implicit_include_directories : false,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(test['name'], exe, is_parallel : false)
+ test(test['name'], exe,
+ is_parallel : false,
+ should_fail : test.get('should_fail', false))
endforeach
diff --git a/test/message.cpp b/test/message.cpp
index d148a13d..19e6646d 100644
--- a/test/message.cpp
+++ b/test/message.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * message.cpp - Messages test
+ * Messages test
*/
#include <chrono>
@@ -11,6 +11,7 @@
#include <thread>
#include <libcamera/base/message.h>
+#include <libcamera/base/object.h>
#include <libcamera/base/thread.h>
#include "test.h"
@@ -92,25 +93,6 @@ private:
bool success_;
};
-class SlowMessageReceiver : public Object
-{
-protected:
- void message(Message *msg)
- {
- if (msg->type() != Message::None) {
- Object::message(msg);
- return;
- }
-
- /*
- * Don't access any member of the object here (including the
- * vtable) as the object will be deleted by the main thread
- * while we're sleeping.
- */
- this_thread::sleep_for(chrono::milliseconds(100));
- }
-};
-
class MessageTest : public Test
{
protected:
@@ -127,16 +109,19 @@ protected:
return TestFail;
}
- MessageReceiver receiver;
- receiver.moveToThread(&thread_);
+ MessageReceiver *receiver = new MessageReceiver();
+ receiver->moveToThread(&thread_);
thread_.start();
- receiver.postMessage(std::make_unique<Message>(Message::None));
+ receiver->postMessage(std::make_unique<Message>(Message::None));
this_thread::sleep_for(chrono::milliseconds(100));
- switch (receiver.status()) {
+ MessageReceiver::Status status = receiver->status();
+ receiver->deleteLater();
+
+ switch (status) {
case MessageReceiver::NoMessage:
cout << "No message received" << endl;
return TestFail;
@@ -148,28 +133,12 @@ protected:
}
/*
- * Test for races between message delivery and object deletion.
- * Failures result in assertion errors, there is no need for
- * explicit checks.
- */
- SlowMessageReceiver *slowReceiver = new SlowMessageReceiver();
- slowReceiver->moveToThread(&thread_);
- slowReceiver->postMessage(std::make_unique<Message>(Message::None));
-
- this_thread::sleep_for(chrono::milliseconds(10));
-
- delete slowReceiver;
-
- this_thread::sleep_for(chrono::milliseconds(100));
-
- /*
* Test recursive calls to Thread::dispatchMessages(). Messages
* should be delivered correctly, without crashes or memory
* leaks. Two messages need to be posted to ensure we don't only
* test the simple case of a queue containing a single message.
*/
- std::unique_ptr<RecursiveMessageReceiver> recursiveReceiver =
- std::make_unique<RecursiveMessageReceiver>();
+ RecursiveMessageReceiver *recursiveReceiver = new RecursiveMessageReceiver();
recursiveReceiver->moveToThread(&thread_);
recursiveReceiver->postMessage(std::make_unique<Message>(Message::None));
@@ -177,7 +146,10 @@ protected:
this_thread::sleep_for(chrono::milliseconds(10));
- if (!recursiveReceiver->success()) {
+ bool success = recursiveReceiver->success();
+ recursiveReceiver->deleteLater();
+
+ if (!success) {
cout << "Recursive message delivery failed" << endl;
return TestFail;
}
diff --git a/test/object-delete.cpp b/test/object-delete.cpp
index eabefe93..676c3970 100644
--- a/test/object-delete.cpp
+++ b/test/object-delete.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * object.cpp - Object deletion tests
+ * Object deletion tests
*/
#include <iostream>
@@ -33,10 +33,10 @@ public:
unsigned int *deleteCount_;
};
-class NewThread : public Thread
+class DeleterThread : public Thread
{
public:
- NewThread(Object *obj)
+ DeleterThread(Object *obj)
: object_(obj)
{
}
@@ -63,9 +63,9 @@ protected:
unsigned int count = 0;
TestObject *obj = new TestObject(&count);
- NewThread thread(obj);
- thread.start();
- thread.wait();
+ DeleterThread delThread(obj);
+ delThread.start();
+ delThread.wait();
Thread::current()->dispatchMessages(Message::Type::DeferredDelete);
@@ -89,6 +89,26 @@ protected:
return TestFail;
}
+ /*
+ * Test that deleteLater() works properly when called just
+ * before the object's thread exits.
+ */
+ Thread boundThread;
+ boundThread.start();
+
+ count = 0;
+ obj = new TestObject(&count);
+ obj->moveToThread(&boundThread);
+
+ obj->deleteLater();
+ boundThread.exit();
+ boundThread.wait();
+
+ if (count != 1) {
+ cout << "Object deletion right before thread exit failed (" << count << ")" << endl;
+ return TestFail;
+ }
+
return TestPass;
}
};
diff --git a/test/object-invoke.cpp b/test/object-invoke.cpp
index b1c0f473..def1e61e 100644
--- a/test/object-invoke.cpp
+++ b/test/object-invoke.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * object-invoke.cpp - Cross-thread Object method invocation test
+ * Cross-thread Object method invocation test
*/
#include <iostream>
diff --git a/test/object.cpp b/test/object.cpp
index cbd0d3ec..95dc1ef3 100644
--- a/test/object.cpp
+++ b/test/object.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * object.cpp - Object tests
+ * Object tests
*/
#include <iostream>
diff --git a/test/process/process_test.cpp b/test/process/process_test.cpp
index cb6940c6..e9f5e7e9 100644
--- a/test/process/process_test.cpp
+++ b/test/process/process_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * process_test.cpp - Process test
+ * Process test
*/
#include <iostream>
diff --git a/test/public-api.cpp b/test/public-api.cpp
index a1cebcf9..b1336f75 100644
--- a/test/public-api.cpp
+++ b/test/public-api.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * public-api.cpp - Public API validation
+ * Public API validation
*/
#include <libcamera/libcamera.h>
diff --git a/test/py/meson.build b/test/py/meson.build
index 0b679d31..b922e857 100644
--- a/test/py/meson.build
+++ b/test/py/meson.build
@@ -13,15 +13,25 @@ if asan_runtime_missing
subdir_done()
endif
+py_env = environment()
+
pymod = import('python')
py3 = pymod.find_installation('python3')
pypathdir = meson.project_build_root() / 'src' / 'py'
-py_env = ['PYTHONPATH=' + pypathdir]
+py_env.append('PYTHONPATH', pypathdir)
if asan_enabled
+ py_env.append('LD_PRELOAD', asan_runtime)
+
+ # Preload the C++ standard library to work around a bug in ASan when
+ # dynamically loading C++ .so modules.
+ stdlib = run_command(cxx, '-print-file-name=' + cxx_stdlib + '.so',
+ check : true).stdout().strip()
+ py_env.append('LD_PRELOAD', stdlib)
+
# Disable leak detection as the Python interpreter is full of leaks.
- py_env += ['LD_PRELOAD=' + asan_runtime, 'ASAN_OPTIONS=detect_leaks=0']
+ py_env.append('ASAN_OPTIONS', 'detect_leaks=0')
endif
test('pyunittests',
diff --git a/test/py/unittests.py b/test/py/unittests.py
index 1caea98e..8cb850d4 100755
--- a/test/py/unittests.py
+++ b/test/py/unittests.py
@@ -66,7 +66,7 @@ class SimpleTestMethods(BaseTestCase):
libcam.log_set_level('Camera', 'FATAL')
with self.assertRaises(RuntimeError):
cam.acquire()
- libcam.log_set_level('Camera', 'ERROR')
+ libcam.log_set_level('Camera', 'INFO')
cam.release()
diff --git a/test/serialization/control_serialization.cpp b/test/serialization/control_serialization.cpp
index a507d98a..06c572b7 100644
--- a/test/serialization/control_serialization.cpp
+++ b/test/serialization/control_serialization.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * control_serialization.cpp - Serialize and deserialize controls
+ * Serialize and deserialize controls
*/
#include <iostream>
diff --git a/test/serialization/generated_serializer/generated_serializer_test.cpp b/test/serialization/generated_serializer/generated_serializer_test.cpp
index 4670fe46..dd696885 100644
--- a/test/serialization/generated_serializer/generated_serializer_test.cpp
+++ b/test/serialization/generated_serializer/generated_serializer_test.cpp
@@ -2,10 +2,11 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * generated_serializer_test.cpp - Test generated serializer
+ * Test generated serializer
*/
#include <algorithm>
+#include <iostream>
#include <tuple>
#include <vector>
diff --git a/test/serialization/generated_serializer/include/libcamera/ipa/meson.build b/test/serialization/generated_serializer/include/libcamera/ipa/meson.build
index 6f8794c1..ae08e9be 100644
--- a/test/serialization/generated_serializer/include/libcamera/ipa/meson.build
+++ b/test/serialization/generated_serializer/include/libcamera/ipa/meson.build
@@ -9,7 +9,8 @@ mojom = custom_target('test_mojom_module',
'--output-root', meson.project_build_root(),
'--input-root', meson.project_source_root(),
'--mojoms', '@INPUT@'
- ])
+ ],
+ env : py_build_env)
# test_ipa_interface.h
generated_test_header = custom_target('test_ipa_interface_h',
@@ -23,7 +24,8 @@ generated_test_header = custom_target('test_ipa_interface_h',
'--libcamera_generate_header',
'--libcamera_output_path=@OUTPUT@',
'./' +'@INPUT@'
- ])
+ ],
+ env : py_build_env)
# test_ipa_serializer.h
generated_test_serializer = custom_target('test_ipa_serializer_h',
@@ -37,4 +39,5 @@ generated_test_serializer = custom_target('test_ipa_serializer_h',
'--libcamera_generate_serializer',
'--libcamera_output_path=@OUTPUT@',
'./' +'@INPUT@'
- ])
+ ],
+ env : py_build_env)
diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp
index 377ecdb0..afea93a6 100644
--- a/test/serialization/ipa_data_serializer_test.cpp
+++ b/test/serialization/ipa_data_serializer_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * ipa_data_serializer_test.cpp - Test serializing/deserializing with IPADataSerializer
+ * Test serializing/deserializing with IPADataSerializer
*/
#include <algorithm>
@@ -29,7 +29,7 @@ using namespace std;
using namespace libcamera;
static const ControlInfoMap Controls = ControlInfoMap({
- { &controls::AeEnable, ControlInfo(false, true) },
+ { &controls::DebugMetadataEnable, ControlInfo(false, true) },
{ &controls::ExposureTime, ControlInfo(0, 999999) },
{ &controls::AnalogueGain, ControlInfo(1.0f, 32.0f) },
{ &controls::ColourGains, ControlInfo(0.0f, 32.0f) },
diff --git a/test/serialization/serialization_test.cpp b/test/serialization/serialization_test.cpp
index 11d0f0f3..af9969fd 100644
--- a/test/serialization/serialization_test.cpp
+++ b/test/serialization/serialization_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * serialization_test.cpp - Base class for serialization tests
+ * Base class for serialization tests
*/
#include "serialization_test.h"
diff --git a/test/serialization/serialization_test.h b/test/serialization/serialization_test.h
index 609f9fdf..760e3721 100644
--- a/test/serialization/serialization_test.h
+++ b/test/serialization/serialization_test.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * serialization_test.h - Base class for serialization tests
+ * Base class for serialization tests
*/
#pragma once
diff --git a/test/shared-fd.cpp b/test/shared-fd.cpp
index 997d7be1..57199dfe 100644
--- a/test/shared-fd.cpp
+++ b/test/shared-fd.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * shared_fd.cpp - SharedFD test
+ * SharedFD test
*/
#include <fcntl.h>
diff --git a/test/signal-threads.cpp b/test/signal-threads.cpp
index d5e2eb66..c4789c83 100644
--- a/test/signal-threads.cpp
+++ b/test/signal-threads.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * signal-threads.cpp - Cross-thread signal delivery test
+ * Cross-thread signal delivery test
*/
#include <chrono>
@@ -10,6 +10,7 @@
#include <thread>
#include <libcamera/base/message.h>
+#include <libcamera/base/object.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/utils.h>
@@ -58,15 +59,20 @@ private:
class SignalThreadsTest : public Test
{
protected:
- int run()
+ int init()
{
- SignalReceiver receiver;
- signal_.connect(&receiver, &SignalReceiver::slot);
+ receiver_ = new SignalReceiver();
+ signal_.connect(receiver_, &SignalReceiver::slot);
+
+ return TestPass;
+ }
+ int run()
+ {
/* Test that a signal is received in the main thread. */
signal_.emit(0);
- switch (receiver.status()) {
+ switch (receiver_->status()) {
case SignalReceiver::NoSignal:
cout << "No signal received for direct connection" << endl;
return TestFail;
@@ -82,8 +88,8 @@ protected:
* Move the object to a thread and verify that the signal is
* correctly delivered, with the correct data.
*/
- receiver.reset();
- receiver.moveToThread(&thread_);
+ receiver_->reset();
+ receiver_->moveToThread(&thread_);
thread_.start();
@@ -91,7 +97,7 @@ protected:
this_thread::sleep_for(chrono::milliseconds(100));
- switch (receiver.status()) {
+ switch (receiver_->status()) {
case SignalReceiver::NoSignal:
cout << "No signal received for message connection" << endl;
return TestFail;
@@ -103,7 +109,7 @@ protected:
break;
}
- if (receiver.value() != 42) {
+ if (receiver_->value() != 42) {
cout << "Signal received with incorrect value" << endl;
return TestFail;
}
@@ -113,11 +119,13 @@ protected:
void cleanup()
{
+ receiver_->deleteLater();
thread_.exit(0);
thread_.wait();
}
private:
+ SignalReceiver *receiver_;
Thread thread_;
Signal<int> signal_;
diff --git a/test/signal.cpp b/test/signal.cpp
index 5c6b304d..3f596b22 100644
--- a/test/signal.cpp
+++ b/test/signal.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * signal.cpp - Signal test
+ * Signal test
*/
#include <iostream>
diff --git a/test/span.cpp b/test/span.cpp
index abf3a5d6..4b9f3279 100644
--- a/test/span.cpp
+++ b/test/span.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * span.cpp - Span tests
+ * Span tests
*/
/*
@@ -143,9 +143,9 @@ protected:
Span<const int>{ v };
/* Span<float>{ v }; */
- Span<const int>{ v };
- /* Span<int>{ v }; */
- /* Span<const float>{ v }; */
+ Span<const int>{ cv };
+ /* Span<int>{ cv }; */
+ /* Span<const float>{ cv }; */
Span<int> dynamicSpan{ i };
Span<int>{ dynamicSpan };
diff --git a/test/stream/stream_colorspace.cpp b/test/stream/stream_colorspace.cpp
index 1b7afe65..4c904c4c 100644
--- a/test/stream/stream_colorspace.cpp
+++ b/test/stream/stream_colorspace.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy.
*
- * stream_colorspace.cpp - Stream colorspace adjustment test
+ * Stream colorspace adjustment test
*/
#include <iostream>
diff --git a/test/stream/stream_formats.cpp b/test/stream/stream_formats.cpp
index 99fa0385..553b59aa 100644
--- a/test/stream/stream_formats.cpp
+++ b/test/stream/stream_formats.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * stream_formats.cpp - StreamFormats test
+ * StreamFormats test
*/
#include <iostream>
diff --git a/test/threads.cpp b/test/threads.cpp
index 8f366c9d..c00d95a4 100644
--- a/test/threads.cpp
+++ b/test/threads.cpp
@@ -2,16 +2,18 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * threads.cpp - Threads test
+ * Threads test
*/
#include <chrono>
#include <iostream>
#include <memory>
#include <pthread.h>
+#include <sched.h>
#include <thread>
#include <time.h>
+#include <libcamera/base/object.h>
#include <libcamera/base/thread.h>
#include "test.h"
@@ -50,14 +52,8 @@ protected:
{
cancelled_ = true;
- /*
- * Cancel the thread and call a guaranteed cancellation point
- * (nanosleep).
- */
pthread_cancel(pthread_self());
-
- struct timespec req{ 0, 100*000*000 };
- nanosleep(&req, nullptr);
+ pthread_testcancel();
cancelled_ = false;
}
@@ -66,6 +62,27 @@ private:
bool &cancelled_;
};
+class CpuSetTester : public Object
+{
+public:
+ CpuSetTester(unsigned int cpuset)
+ : cpuset_(cpuset) {}
+
+ bool testCpuSet()
+ {
+ int ret = sched_getcpu();
+ if (static_cast<int>(cpuset_) != ret) {
+ cout << "Invalid cpuset: " << ret << ", expecting: " << cpuset_ << endl;
+ return false;
+ }
+
+ return true;
+ }
+
+private:
+ const unsigned int cpuset_;
+};
+
class ThreadTest : public Test
{
protected:
@@ -165,6 +182,23 @@ protected:
return TestFail;
}
+ const unsigned int numCpus = std::thread::hardware_concurrency();
+ for (unsigned int i = 0; i < numCpus; ++i) {
+ thread = std::make_unique<Thread>();
+ const std::array<const unsigned int, 1> cpus{ i };
+ thread->setThreadAffinity(cpus);
+ thread->start();
+
+ CpuSetTester tester(i);
+ tester.moveToThread(thread.get());
+
+ if (!tester.invokeMethod(&CpuSetTester::testCpuSet, ConnectionTypeBlocking))
+ return TestFail;
+
+ thread->exit(0);
+ thread->wait();
+ }
+
return TestPass;
}
diff --git a/test/timer-fail.cpp b/test/timer-fail.cpp
new file mode 100644
index 00000000..0ced6441
--- /dev/null
+++ b/test/timer-fail.cpp
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Threaded timer failure test
+ */
+
+#include <chrono>
+#include <iostream>
+
+#include <libcamera/base/event_dispatcher.h>
+#include <libcamera/base/object.h>
+#include <libcamera/base/thread.h>
+#include <libcamera/base/timer.h>
+
+#include "test.h"
+
+using namespace libcamera;
+using namespace std;
+using namespace std::chrono_literals;
+
+class TimeoutHandler : public Object
+{
+public:
+ TimeoutHandler()
+ : timer_(this), timeout_(false)
+ {
+ timer_.timeout.connect(this, &TimeoutHandler::timeoutHandler);
+ }
+
+ void start()
+ {
+ timer_.start(100ms);
+ }
+
+ bool timeout() const
+ {
+ return timeout_;
+ }
+
+private:
+ void timeoutHandler()
+ {
+ timeout_ = true;
+ }
+
+ Timer timer_;
+ bool timeout_;
+};
+
+class TimerFailTest : public Test
+{
+protected:
+ int init()
+ {
+ thread_.start();
+
+ timeout_ = new TimeoutHandler();
+ timeout_->moveToThread(&thread_);
+
+ return TestPass;
+ }
+
+ int run()
+ {
+ /*
+ * Test that the forbidden operation of starting the timer from
+ * another thread results in a failure. We need to interrupt the
+ * event dispatcher to make sure we don't succeed simply because
+ * the event dispatcher hasn't noticed the timer restart.
+ */
+ timeout_->start();
+ thread_.eventDispatcher()->interrupt();
+
+ this_thread::sleep_for(chrono::milliseconds(200));
+
+ /*
+ * The wrong start() call should result in an assertion in debug
+ * builds, and a timeout in release builds. The test is
+ * therefore marked in meson.build as expected to fail. We need
+ * to return TestPass in the unexpected (usually known as
+ * "fail") case, and TestFail otherwise.
+ */
+ if (timeout_->timeout()) {
+ cout << "Timer start from wrong thread succeeded unexpectedly"
+ << endl;
+ return TestPass;
+ }
+
+ return TestFail;
+ }
+
+ void cleanup()
+ {
+ /*
+ * Object class instances must be destroyed from the thread
+ * they live in.
+ */
+ timeout_->deleteLater();
+ thread_.exit(0);
+ thread_.wait();
+ }
+
+private:
+ TimeoutHandler *timeout_;
+ Thread thread_;
+};
+
+TEST_REGISTER(TimerFailTest)
diff --git a/test/timer-thread.cpp b/test/timer-thread.cpp
index 61821753..55e5cfdf 100644
--- a/test/timer-thread.cpp
+++ b/test/timer-thread.cpp
@@ -2,13 +2,14 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * timer-thread.cpp - Threaded timer test
+ * Threaded timer test
*/
#include <chrono>
#include <iostream>
#include <libcamera/base/event_dispatcher.h>
+#include <libcamera/base/object.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
@@ -28,12 +29,6 @@ public:
timer_.start(100ms);
}
- void restart()
- {
- timeout_ = false;
- timer_.start(100ms);
- }
-
bool timeout() const
{
return timeout_;
@@ -55,7 +50,9 @@ protected:
int init()
{
thread_.start();
- timeout_.moveToThread(&thread_);
+
+ timeout_ = new TimeoutHandler();
+ timeout_->moveToThread(&thread_);
return TestPass;
}
@@ -68,39 +65,27 @@ protected:
*/
this_thread::sleep_for(chrono::milliseconds(200));
- if (!timeout_.timeout()) {
+ if (!timeout_->timeout()) {
cout << "Timer expiration test failed" << endl;
return TestFail;
}
- /*
- * Test that starting the timer from another thread fails. We
- * need to interrupt the event dispatcher to make sure we don't
- * succeed simply because the event dispatcher hasn't noticed
- * the timer restart.
- */
- timeout_.restart();
- thread_.eventDispatcher()->interrupt();
-
- this_thread::sleep_for(chrono::milliseconds(200));
-
- if (timeout_.timeout()) {
- cout << "Timer restart test failed" << endl;
- return TestFail;
- }
-
return TestPass;
}
void cleanup()
{
- /* Must stop thread before destroying timeout. */
+ /*
+ * Object class instances must be destroyed from the thread
+ * they live in.
+ */
+ timeout_->deleteLater();
thread_.exit(0);
thread_.wait();
}
private:
- TimeoutHandler timeout_;
+ TimeoutHandler *timeout_;
Thread thread_;
};
diff --git a/test/timer.cpp b/test/timer.cpp
index 0f01c3cb..2eacc059 100644
--- a/test/timer.cpp
+++ b/test/timer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * timer.cpp - Timer test
+ * Timer test
*/
#include <chrono>
diff --git a/test/transform.cpp b/test/transform.cpp
index fbc0308c..4ec7a1eb 100644
--- a/test/transform.cpp
+++ b/test/transform.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Ideas On Board Oy
*
- * transform.cpp - Transform and Orientation tests
+ * Transform and Orientation tests
*/
#include <iostream>
diff --git a/test/unique-fd.cpp b/test/unique-fd.cpp
index eb3b591f..e556439e 100644
--- a/test/unique-fd.cpp
+++ b/test/unique-fd.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * unique-fd.cpp - UniqueFD test
+ * UniqueFD test
*/
#include <fcntl.h>
diff --git a/test/utils.cpp b/test/utils.cpp
index fc56e14e..d25475cb 100644
--- a/test/utils.cpp
+++ b/test/utils.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * utils.cpp - Miscellaneous utility tests
+ * Miscellaneous utility tests
*/
#include <iostream>
@@ -176,6 +176,14 @@ protected:
std::ostringstream os;
std::string ref;
+ os << utils::hex(static_cast<int8_t>(0x42)) << " ";
+ ref += "0x42 ";
+ os << utils::hex(static_cast<uint8_t>(0x42)) << " ";
+ ref += "0x42 ";
+ os << utils::hex(static_cast<int16_t>(0x42)) << " ";
+ ref += "0x0042 ";
+ os << utils::hex(static_cast<uint16_t>(0x42)) << " ";
+ ref += "0x0042 ";
os << utils::hex(static_cast<int32_t>(0x42)) << " ";
ref += "0x00000042 ";
os << utils::hex(static_cast<uint32_t>(0x42)) << " ";
@@ -184,6 +192,15 @@ protected:
ref += "0x0000000000000042 ";
os << utils::hex(static_cast<uint64_t>(0x42)) << " ";
ref += "0x0000000000000042 ";
+
+ os << utils::hex(static_cast<int8_t>(0x42), 6) << " ";
+ ref += "0x000042 ";
+ os << utils::hex(static_cast<uint8_t>(0x42), 1) << " ";
+ ref += "0x42 ";
+ os << utils::hex(static_cast<int16_t>(0x42), 6) << " ";
+ ref += "0x000042 ";
+ os << utils::hex(static_cast<uint16_t>(0x42), 1) << " ";
+ ref += "0x42 ";
os << utils::hex(static_cast<int32_t>(0x42), 4) << " ";
ref += "0x0042 ";
os << utils::hex(static_cast<uint32_t>(0x42), 1) << " ";
diff --git a/test/v4l2_compat/v4l2_compat_test.py b/test/v4l2_compat/v4l2_compat_test.py
index bd89d496..443babc2 100755
--- a/test/v4l2_compat/v4l2_compat_test.py
+++ b/test/v4l2_compat/v4l2_compat_test.py
@@ -4,7 +4,7 @@
#
# Author: Paul Elder <paul.elder@ideasonboard.com>
#
-# v4l2_compat_test.py - Test the V4L2 compatibility layer
+# Test the V4L2 compatibility layer
import argparse
import glob
diff --git a/test/v4l2_subdevice/v4l2_subdevice_test.cpp b/test/v4l2_subdevice/v4l2_subdevice_test.cpp
index d8fbfd9f..c349c9e3 100644
--- a/test/v4l2_subdevice/v4l2_subdevice_test.cpp
+++ b/test/v4l2_subdevice/v4l2_subdevice_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_subdevice_test.cpp - VIMC-based V4L2 subdevice test
+ * VIMC-based V4L2 subdevice test
*/
#include <iostream>
diff --git a/test/v4l2_subdevice/v4l2_subdevice_test.h b/test/v4l2_subdevice/v4l2_subdevice_test.h
index e73c583b..89b78302 100644
--- a/test/v4l2_subdevice/v4l2_subdevice_test.h
+++ b/test/v4l2_subdevice/v4l2_subdevice_test.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_subdevice_test.h - VIMC-based V4L2 subdevice test
+ * VIMC-based V4L2 subdevice test
*/
#pragma once
diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp
index 42e1e671..67366461 100644
--- a/test/v4l2_videodevice/capture_async.cpp
+++ b/test/v4l2_videodevice/capture_async.cpp
@@ -61,10 +61,12 @@ protected:
if (ret)
return TestFail;
- timeout.start(10000ms);
+ const unsigned int nFrames = 30;
+
+ timeout.start(500ms * nFrames);
while (timeout.isRunning()) {
dispatcher->processEvents();
- if (frames > 30)
+ if (frames > nFrames)
break;
}
@@ -73,8 +75,9 @@ protected:
return TestFail;
}
- if (frames < 30) {
- std::cout << "Failed to capture 30 frames within timeout." << std::endl;
+ if (frames < nFrames) {
+ std::cout << "Failed to capture " << nFrames
+ << " frames within timeout." << std::endl;
return TestFail;
}
diff --git a/test/v4l2_videodevice/controls.cpp b/test/v4l2_videodevice/controls.cpp
index 0f603b85..b0130295 100644
--- a/test/v4l2_videodevice/controls.cpp
+++ b/test/v4l2_videodevice/controls.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * controls.cpp - V4L2 device controls handling test
+ * V4L2 device controls handling test
*/
#include <algorithm>
diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.cpp b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
index 125aafd6..9fbd24cc 100644
--- a/test/v4l2_videodevice/v4l2_videodevice_test.cpp
+++ b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
@@ -64,8 +64,8 @@ int V4L2VideoDeviceTest::init()
format.size.height = 480;
if (driver_ == "vimc") {
- sensor_ = new CameraSensor(media_->getEntityByName("Sensor A"));
- if (sensor_->init())
+ sensor_ = CameraSensorFactoryBase::create(media_->getEntityByName("Sensor A"));
+ if (!sensor_)
return TestFail;
debayer_ = new V4L2Subdevice(media_->getEntityByName("Debayer A"));
@@ -75,7 +75,7 @@ int V4L2VideoDeviceTest::init()
format.fourcc = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8);
V4L2SubdeviceFormat subformat = {};
- subformat.mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8;
+ subformat.code = MEDIA_BUS_FMT_SBGGR8_1X8;
subformat.size = format.size;
if (sensor_->setFormat(&subformat))
@@ -98,6 +98,5 @@ void V4L2VideoDeviceTest::cleanup()
capture_->close();
delete debayer_;
- delete sensor_;
delete capture_;
}
diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.h b/test/v4l2_videodevice/v4l2_videodevice_test.h
index d2de1a6d..7c9003ec 100644
--- a/test/v4l2_videodevice/v4l2_videodevice_test.h
+++ b/test/v4l2_videodevice/v4l2_videodevice_test.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * vl42device_test.h - libcamera v4l2device test base class
+ * libcamera v4l2device test base class
*/
#pragma once
@@ -36,7 +36,7 @@ protected:
std::string entity_;
std::unique_ptr<libcamera::DeviceEnumerator> enumerator_;
std::shared_ptr<libcamera::MediaDevice> media_;
- libcamera::CameraSensor *sensor_;
+ std::unique_ptr<libcamera::CameraSensor> sensor_;
libcamera::V4L2Subdevice *debayer_;
libcamera::V4L2VideoDevice *capture_;
std::vector<std::unique_ptr<libcamera::FrameBuffer>> buffers_;
diff --git a/test/vector.cpp b/test/vector.cpp
new file mode 100644
index 00000000..4fae960d
--- /dev/null
+++ b/test/vector.cpp
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Vector tests
+ */
+
+#include "libcamera/internal/vector.h"
+
+#include <cmath>
+#include <iostream>
+
+#include "test.h"
+
+using namespace libcamera;
+
+#define ASSERT_EQ(a, b) \
+if ((a) != (b)) { \
+ std::cout << #a " != " #b << " (line " << __LINE__ << ")" \
+ << std::endl; \
+ return TestFail; \
+}
+
+class VectorTest : public Test
+{
+protected:
+ int run()
+ {
+ Vector<double, 3> v1{ 0.0 };
+
+ ASSERT_EQ(v1[0], 0.0);
+ ASSERT_EQ(v1[1], 0.0);
+ ASSERT_EQ(v1[2], 0.0);
+
+ ASSERT_EQ(v1.length(), 0.0);
+ ASSERT_EQ(v1.length2(), 0.0);
+
+ Vector<double, 3> v2{{ 1.0, 4.0, 8.0 }};
+
+ ASSERT_EQ(v2[0], 1.0);
+ ASSERT_EQ(v2[1], 4.0);
+ ASSERT_EQ(v2[2], 8.0);
+
+ ASSERT_EQ(v2.x(), 1.0);
+ ASSERT_EQ(v2.y(), 4.0);
+ ASSERT_EQ(v2.z(), 8.0);
+
+ ASSERT_EQ(v2.r(), 1.0);
+ ASSERT_EQ(v2.g(), 4.0);
+ ASSERT_EQ(v2.b(), 8.0);
+
+ ASSERT_EQ(v2.length2(), 81.0);
+ ASSERT_EQ(v2.length(), 9.0);
+ ASSERT_EQ(v2.sum(), 13.0);
+
+ Vector<double, 3> v3{ v2 };
+
+ ASSERT_EQ(v2, v3);
+
+ v3 = Vector<double, 3>{{ 4.0, 4.0, 4.0 }};
+
+ ASSERT_EQ(v2 + v3, (Vector<double, 3>{{ 5.0, 8.0, 12.0 }}));
+ ASSERT_EQ(v2 + 4.0, (Vector<double, 3>{{ 5.0, 8.0, 12.0 }}));
+ ASSERT_EQ(v2 - v3, (Vector<double, 3>{{ -3.0, 0.0, 4.0 }}));
+ ASSERT_EQ(v2 - 4.0, (Vector<double, 3>{{ -3.0, 0.0, 4.0 }}));
+ ASSERT_EQ(v2 * v3, (Vector<double, 3>{{ 4.0, 16.0, 32.0 }}));
+ ASSERT_EQ(v2 * 4.0, (Vector<double, 3>{{ 4.0, 16.0, 32.0 }}));
+ ASSERT_EQ(v2 / v3, (Vector<double, 3>{{ 0.25, 1.0, 2.0 }}));
+ ASSERT_EQ(v2 / 4.0, (Vector<double, 3>{{ 0.25, 1.0, 2.0 }}));
+
+ ASSERT_EQ(v2.min(v3), (Vector<double, 3>{{ 1.0, 4.0, 4.0 }}));
+ ASSERT_EQ(v2.min(4.0), (Vector<double, 3>{{ 1.0, 4.0, 4.0 }}));
+ ASSERT_EQ(v2.max(v3), (Vector<double, 3>{{ 4.0, 4.0, 8.0 }}));
+ ASSERT_EQ(v2.max(4.0), (Vector<double, 3>{{ 4.0, 4.0, 8.0 }}));
+
+ ASSERT_EQ(v2.dot(v3), 52.0);
+
+ v2 += v3;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 5.0, 8.0, 12.0 }}));
+ v2 -= v3;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 1.0, 4.0, 8.0 }}));
+ v2 *= v3;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 4.0, 16.0, 32.0 }}));
+ v2 /= v3;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 1.0, 4.0, 8.0 }}));
+
+ v2 += 4.0;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 5.0, 8.0, 12.0 }}));
+ v2 -= 4.0;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 1.0, 4.0, 8.0 }}));
+ v2 *= 4.0;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 4.0, 16.0, 32.0 }}));
+ v2 /= 4.0;
+ ASSERT_EQ(v2, (Vector<double, 3>{{ 1.0, 4.0, 8.0 }}));
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(VectorTest)
diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp
index 2d92463a..1b22c87b 100644
--- a/test/yaml-parser.cpp
+++ b/test/yaml-parser.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Google Inc.
*
- * yaml-parser.cpp - YAML parser operations tests
+ * YAML parser operations tests
*/
#include <array>
@@ -34,10 +34,12 @@ static const string testYaml =
"list:\n"
" - James\n"
" - Mary\n"
+ " - \n"
"dictionary:\n"
" a: 1\n"
" c: 3\n"
" b: 2\n"
+ " empty:\n"
"level1:\n"
" level2:\n"
" - [1, 2]\n"
@@ -430,9 +432,10 @@ protected:
if (testObjectType(listObj, "list", Type::List) != TestPass)
return TestFail;
- static constexpr std::array<const char *, 2> listValues{
+ static constexpr std::array<const char *, 3> listValues{
"James",
"Mary",
+ "",
};
if (listObj.size() != listValues.size()) {
@@ -465,16 +468,23 @@ protected:
i++;
}
+ /* Ensure that empty objects get parsed as empty strings. */
+ if (!listObj[2].isValue()) {
+ cerr << "Empty object is not a value" << std::endl;
+ return TestFail;
+ }
+
/* Test dictionary object */
auto &dictObj = (*root)["dictionary"];
if (testObjectType(dictObj, "dictionary", Type::Dictionary) != TestPass)
return TestFail;
- static constexpr std::array<std::pair<const char *, int>, 3> dictValues{ {
+ static constexpr std::array<std::pair<const char *, int>, 4> dictValues{ {
{ "a", 1 },
{ "c", 3 },
{ "b", 2 },
+ { "empty", -100 },
} };
size_t dictSize = dictValues.size();
@@ -505,7 +515,7 @@ protected:
return TestFail;
}
- if (elem.get<int32_t>(0) != item.second) {
+ if (elem.get<int32_t>(-100) != item.second) {
std::cerr << "Dictionary element " << i << " has wrong value"
<< std::endl;
return TestFail;
@@ -514,6 +524,42 @@ protected:
i++;
}
+ /* Ensure that empty objects get parsed as empty strings. */
+ if (!dictObj["empty"].isValue()) {
+ cerr << "Empty object is not of type value" << std::endl;
+ return TestFail;
+ }
+
+ /* Ensure that keys without values are added to a dict. */
+ if (!dictObj.contains("empty")) {
+ cerr << "Empty element is missing in dict" << std::endl;
+ return TestFail;
+ }
+
+ /* Test access to nonexistent member. */
+ if (dictObj["nonexistent"].get<std::string>("default") != "default") {
+ cerr << "Accessing nonexistent dict entry fails to return default" << std::endl;
+ return TestFail;
+ }
+
+ /* Test nonexistent object has value type empty. */
+ if (!dictObj["nonexistent"].isEmpty()) {
+ cerr << "Accessing nonexistent object returns non-empty object" << std::endl;
+ return TestFail;
+ }
+
+ /* Test explicit cast to bool on an empty object returns true. */
+ if (!!dictObj["empty"] != true) {
+ cerr << "Casting empty entry to bool returns false" << std::endl;
+ return TestFail;
+ }
+
+ /* Test explicit cast to bool on nonexistent object returns false. */
+ if (!!dictObj["nonexistent"] != false) {
+ cerr << "Casting nonexistent dict entry to bool returns true" << std::endl;
+ return TestFail;
+ }
+
/* Make sure utils::map_keys() works on the adapter. */
(void)utils::map_keys(dictObj.asDict());