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.cpp15
-rw-r--r--test/camera/camera_reconfigure.cpp4
-rw-r--r--test/camera/meson.build18
-rw-r--r--test/color-space.cpp105
-rw-r--r--test/controls/control_info.cpp12
-rw-r--r--test/controls/control_info_map.cpp9
-rw-r--r--test/controls/control_list.cpp70
-rw-r--r--test/controls/control_value.cpp2
-rw-r--r--test/controls/meson.build14
-rw-r--r--test/delayed_controls.cpp25
-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.cpp2
-rw-r--r--test/file.cpp2
-rw-r--r--test/flags.cpp2
-rw-r--r--test/geometry.cpp2
-rw-r--r--test/gstreamer/gstreamer_device_provider_test.cpp77
-rw-r--r--test/gstreamer/gstreamer_multi_stream_test.cpp25
-rw-r--r--test/gstreamer/gstreamer_single_stream_test.cpp29
-rw-r--r--test/gstreamer/gstreamer_test.cpp65
-rw-r--r--test/gstreamer/gstreamer_test.h12
-rw-r--r--test/gstreamer/meson.build13
-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/meson.build10
-rw-r--r--test/ipc/meson.build10
-rw-r--r--test/ipc/unixsocket.cpp4
-rw-r--r--test/ipc/unixsocket_ipc.cpp2
-rw-r--r--test/libtest/buffer_source.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/log/meson.build10
-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/media_device/meson.build12
-rw-r--r--test/meson.build122
-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/pipeline/ipu3/ipu3_pipeline_test.cpp126
-rw-r--r--test/pipeline/ipu3/meson.build14
-rw-r--r--test/pipeline/meson.build4
-rw-r--r--test/pipeline/rkisp1/meson.build14
-rw-r--r--test/pipeline/rkisp1/rkisp1_pipeline_test.cpp115
-rw-r--r--test/process/meson.build8
-rw-r--r--test/process/process_test.cpp2
-rw-r--r--test/public-api.cpp2
-rw-r--r--test/py/meson.build17
-rwxr-xr-xtest/py/unittests.py151
-rw-r--r--test/serialization/control_serialization.cpp2
-rw-r--r--test/serialization/generated_serializer/generated_serializer_test.cpp27
-rw-r--r--test/serialization/generated_serializer/include/libcamera/ipa/test.mojom10
-rw-r--r--test/serialization/ipa_data_serializer_test.cpp6
-rw-r--r--test/serialization/meson.build10
-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.cpp2
-rw-r--r--test/stream/meson.build9
-rw-r--r--test/stream/stream_colorspace.cpp96
-rw-r--r--test/stream/stream_formats.cpp2
-rw-r--r--test/threads.cpp49
-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.cpp329
-rw-r--r--test/unique-fd.cpp2
-rw-r--r--test/utils.cpp11
-rw-r--r--test/v4l2_compat/meson.build33
-rwxr-xr-xtest/v4l2_compat/v4l2_compat_test.py20
-rw-r--r--test/v4l2_subdevice/meson.build10
-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/controls.cpp2
-rw-r--r--test/v4l2_videodevice/meson.build26
-rw-r--r--test/v4l2_videodevice/v4l2_m2mdevice.cpp5
-rw-r--r--test/v4l2_videodevice/v4l2_videodevice_test.cpp2
-rw-r--r--test/v4l2_videodevice/v4l2_videodevice_test.h2
-rw-r--r--test/yaml-parser.cpp463
92 files changed, 1658 insertions, 971 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 d3dcb510..1d402c43 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>
@@ -12,6 +12,7 @@
#include <libcamera/base/utils.h>
+#include "libcamera/internal/camera_lens.h"
#include "libcamera/internal/camera_sensor.h"
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/media_device.h"
@@ -57,6 +58,10 @@ protected:
return TestFail;
}
+ lens_ = sensor_->focusLens();
+ if (lens_)
+ cout << "Found lens controller" << endl;
+
return TestPass;
}
@@ -95,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)
@@ -103,6 +108,11 @@ protected:
return TestFail;
}
+ if (lens_ && lens_->setFocusPosition(10)) {
+ cerr << "Failed to set lens focus position" << endl;
+ return TestFail;
+ }
+
return TestPass;
}
@@ -115,6 +125,7 @@ private:
std::unique_ptr<DeviceEnumerator> enumerator_;
std::shared_ptr<MediaDevice> media_;
CameraSensor *sensor_;
+ CameraLens *lens_;
};
TEST_REGISTER(CameraSensorTest)
diff --git a/test/camera/camera_reconfigure.cpp b/test/camera/camera_reconfigure.cpp
index f6076baa..06c87730 100644
--- a/test/camera/camera_reconfigure.cpp
+++ b/test/camera/camera_reconfigure.cpp
@@ -98,7 +98,7 @@ private:
return TestFail;
}
- requests_.push_back(move(request));
+ requests_.push_back(std::move(request));
}
camera_->requestCompleted.connect(this, &CameraReconfigure::requestComplete);
@@ -179,7 +179,7 @@ private:
continue;
string pname("/proc/" + string(ptr->d_name) + "/comm");
- if (File::exists(pname.c_str())) {
+ if (File::exists(pname)) {
ifstream pfile(pname.c_str());
string comm;
getline(pfile, comm);
diff --git a/test/camera/meson.build b/test/camera/meson.build
index 668d5c03..4f9f8c8c 100644
--- a/test/camera/meson.build
+++ b/test/camera/meson.build
@@ -3,18 +3,18 @@
# Tests are listed in order of complexity.
# They are not alphabetically sorted.
camera_tests = [
- ['configuration_default', 'configuration_default.cpp'],
- ['configuration_set', 'configuration_set.cpp'],
- ['buffer_import', 'buffer_import.cpp'],
- ['statemachine', 'statemachine.cpp'],
- ['capture', 'capture.cpp'],
- ['camera_reconfigure', 'camera_reconfigure.cpp'],
+ {'name': 'configuration_default', 'sources': ['configuration_default.cpp']},
+ {'name': 'configuration_set', 'sources': ['configuration_set.cpp']},
+ {'name': 'buffer_import', 'sources': ['buffer_import.cpp']},
+ {'name': 'statemachine', 'sources': ['statemachine.cpp']},
+ {'name': 'capture', 'sources': ['capture.cpp']},
+ {'name': 'camera_reconfigure', 'sources': ['camera_reconfigure.cpp']},
]
-foreach t : camera_tests
- exe = executable(t[0], t[1],
+foreach test : camera_tests
+ exe = executable(test['name'], test['sources'],
dependencies : libcamera_private,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'camera', is_parallel : false)
+ test(test['name'], exe, suite : 'camera', is_parallel : false)
endforeach
diff --git a/test/color-space.cpp b/test/color-space.cpp
new file mode 100644
index 00000000..7d45b217
--- /dev/null
+++ b/test/color-space.cpp
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2022, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * libcamera ColorSpace test
+ */
+
+#include <array>
+#include <iostream>
+
+#include <libcamera/color_space.h>
+
+#include "test.h"
+
+using namespace libcamera;
+using namespace std;
+
+class ColorSpaceTest : public Test
+{
+protected:
+ int run()
+ {
+ if (ColorSpace::toString(std::nullopt) != "Unset") {
+ std::cerr << "Conversion from nullopt to string failed" << std::endl;
+ return TestFail;
+ }
+
+ const std::array<std::pair<ColorSpace, std::string>, 10> colorSpaces = { {
+ { ColorSpace::Raw, "RAW" },
+ { ColorSpace::Srgb, "sRGB" },
+ { ColorSpace::Sycc, "sYCC" },
+ { ColorSpace::Smpte170m, "SMPTE170M" },
+ { ColorSpace::Rec709, "Rec709" },
+ { ColorSpace::Rec2020, "Rec2020" },
+ {
+ ColorSpace{
+ ColorSpace::Primaries::Raw,
+ ColorSpace::TransferFunction::Linear,
+ ColorSpace::YcbcrEncoding::None,
+ ColorSpace::Range::Limited
+ },
+ "RAW/Linear/None/Limited"
+ }, {
+ ColorSpace{
+ ColorSpace::Primaries::Smpte170m,
+ ColorSpace::TransferFunction::Srgb,
+ ColorSpace::YcbcrEncoding::Rec601,
+ ColorSpace::Range::Full
+ },
+ "SMPTE170M/sRGB/Rec601/Full"
+ }, {
+ ColorSpace{
+ ColorSpace::Primaries::Rec709,
+ ColorSpace::TransferFunction::Rec709,
+ ColorSpace::YcbcrEncoding::Rec709,
+ ColorSpace::Range::Full
+ },
+ "Rec709/Rec709/Rec709/Full"
+ }, {
+ ColorSpace{
+ ColorSpace::Primaries::Rec2020,
+ ColorSpace::TransferFunction::Linear,
+ ColorSpace::YcbcrEncoding::Rec2020,
+ ColorSpace::Range::Limited
+ },
+ "Rec2020/Linear/Rec2020/Limited"
+ },
+ } };
+
+ for (const auto &[colorSpace, name] : colorSpaces) {
+ if (colorSpace.toString() != name) {
+ std::cerr
+ << "Conversion from ColorSpace to string failed: "
+ << "expected " << name
+ << ", got " << colorSpace.toString()
+ << std::endl;
+ return TestFail;
+ }
+
+ if (ColorSpace::fromString(name) != colorSpace) {
+ std::cerr
+ << "Conversion from string "
+ << name << " to ColorSpace failed"
+ << std::endl;
+ return TestFail;
+ }
+ }
+
+ if (ColorSpace::fromString("Invalid")) {
+ std::cerr << "Conversion from invalid name string to color space succeeded"
+ << std::endl;
+ return TestFail;
+ }
+
+ if (ColorSpace::fromString("Rec709/Rec709/Rec710/Limited")) {
+ std::cerr << "Conversion from invalid component string to color space succeeded"
+ << std::endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(ColorSpaceTest)
diff --git a/test/controls/control_info.cpp b/test/controls/control_info.cpp
index 2827473b..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>
@@ -26,20 +26,22 @@ protected:
*/
ControlInfo brightness;
- if (brightness.min().get<int32_t>() != 0 ||
- brightness.max().get<int32_t>() != 0) {
+ if (brightness.min().type() != ControlType::ControlTypeNone ||
+ brightness.max().type() != ControlType::ControlTypeNone ||
+ brightness.def().type() != ControlType::ControlTypeNone) {
cout << "Invalid control range for Brightness" << endl;
return TestFail;
}
/*
* Test information retrieval from a control with a minimum and
- * a maximum value.
+ * a maximum value, and an implicit default value.
*/
ControlInfo contrast(10, 200);
if (contrast.min().get<int32_t>() != 10 ||
- contrast.max().get<int32_t>() != 200) {
+ contrast.max().get<int32_t>() != 200 ||
+ !contrast.def().isNone()) {
cout << "Invalid control range for Contrast" << endl;
return TestFail;
}
diff --git a/test/controls/control_info_map.cpp b/test/controls/control_info_map.cpp
index db95945a..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>
@@ -75,6 +75,13 @@ protected:
return TestFail;
}
+ /* Test looking up a control on a default-constructed infoMap */
+ const ControlInfoMap emptyInfoMap;
+ if (emptyInfoMap.find(12345) != emptyInfoMap.end()) {
+ cerr << "find() on empty ControlInfoMap failed" << endl;
+ return TestFail;
+ }
+
return TestPass;
}
};
diff --git a/test/controls/control_list.cpp b/test/controls/control_list.cpp
index 70cf61b8..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>
@@ -50,7 +50,7 @@ protected:
return TestFail;
}
- if (list.contains(controls::Brightness)) {
+ if (list.get(controls::Brightness)) {
cout << "List should not contain Brightness control" << endl;
return TestFail;
}
@@ -80,7 +80,7 @@ protected:
return TestFail;
}
- if (!list.contains(controls::Brightness)) {
+ if (!list.get(controls::Brightness)) {
cout << "List should contain Brightness control" << endl;
return TestFail;
}
@@ -99,7 +99,7 @@ protected:
return TestFail;
}
- if (list.contains(controls::Contrast)) {
+ if (list.get(controls::Contrast)) {
cout << "List should not contain Contract control" << endl;
return TestFail;
}
@@ -108,8 +108,8 @@ protected:
list.set(controls::Brightness, 0.0f);
list.set(controls::Contrast, 1.5f);
- if (!list.contains(controls::Brightness) ||
- !list.contains(controls::Contrast)) {
+ if (!list.get(controls::Brightness) ||
+ !list.get(controls::Contrast)) {
cout << "List should contain Brightness and Contrast controls"
<< endl;
return TestFail;
@@ -145,7 +145,7 @@ protected:
*/
list.set(controls::AwbEnable, true);
- if (list.contains(controls::AwbEnable)) {
+ if (list.get(controls::AwbEnable)) {
cout << "List shouldn't contain AwbEnable control" << endl;
return TestFail;
}
@@ -171,9 +171,9 @@ protected:
return TestFail;
}
- if (!mergeList.contains(controls::Brightness) ||
- !mergeList.contains(controls::Contrast) ||
- !mergeList.contains(controls::Saturation)) {
+ if (!mergeList.get(controls::Brightness) ||
+ !mergeList.get(controls::Contrast) ||
+ !mergeList.get(controls::Saturation)) {
cout << "Merged list does not contain all controls" << endl;
return TestFail;
}
@@ -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..344107fa 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>
diff --git a/test/controls/meson.build b/test/controls/meson.build
index 0103543e..763f8905 100644
--- a/test/controls/meson.build
+++ b/test/controls/meson.build
@@ -1,16 +1,16 @@
# SPDX-License-Identifier: CC0-1.0
control_tests = [
- ['control_info', 'control_info.cpp'],
- ['control_info_map', 'control_info_map.cpp'],
- ['control_list', 'control_list.cpp'],
- ['control_value', 'control_value.cpp'],
+ {'name': 'control_info', 'sources': ['control_info.cpp']},
+ {'name': 'control_info_map', 'sources': ['control_info_map.cpp']},
+ {'name': 'control_list', 'sources': ['control_list.cpp']},
+ {'name': 'control_value', 'sources': ['control_value.cpp']},
]
-foreach t : control_tests
- exe = executable(t[0], t[1],
+foreach test : control_tests
+ exe = executable(test['name'], test['sources'],
dependencies : libcamera_public,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'controls', is_parallel : false)
+ test(test['name'], exe, suite : 'controls', is_parallel : false)
endforeach
diff --git a/test/delayed_controls.cpp b/test/delayed_controls.cpp
index c6f195b7..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>
@@ -155,7 +155,7 @@ protected:
return TestPass;
}
- int dualControlsWithDelay(uint32_t startOffset)
+ int dualControlsWithDelay()
{
static const unsigned int maxDelay = 2;
@@ -175,25 +175,24 @@ protected:
delayed->reset();
/* Trigger the first frame start event */
- delayed->applyControls(startOffset);
+ delayed->applyControls(0);
/* Test dual control with delay. */
for (unsigned int i = 1; 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 + 1);
delayed->push(ctrls);
- delayed->applyControls(frame);
+ delayed->applyControls(i);
- ControlList result = delayed->get(frame);
+ 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 + 1) {
cerr << "Failed dual controls"
- << " frame " << frame
+ << " frame " << i
<< " brightness " << brightness
<< " contrast " << contrast
<< " expected " << expected
@@ -283,17 +282,7 @@ protected:
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);
+ ret = dualControlsWithDelay();
if (ret)
return ret;
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..ada650ff 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>
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..64169206 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>
diff --git a/test/gstreamer/gstreamer_device_provider_test.cpp b/test/gstreamer/gstreamer_device_provider_test.cpp
new file mode 100644
index 00000000..8b8e7cba
--- /dev/null
+++ b/test/gstreamer/gstreamer_device_provider_test.cpp
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2023, Umang Jain <umang.jain@ideasonboard.com>
+ *
+ * GStreamer single stream capture test
+ */
+
+#include <vector>
+
+#include <libcamera/libcamera.h>
+#include <gst/gst.h>
+
+#include "gstreamer_test.h"
+#include "test.h"
+
+using namespace std;
+
+class GstreamerDeviceProviderTest : public GstreamerTest, public Test
+{
+public:
+ GstreamerDeviceProviderTest()
+ : GstreamerTest()
+ {
+ }
+
+protected:
+ int init() override
+ {
+ if (status_ != TestPass)
+ return status_;
+
+ return TestPass;
+ }
+
+ int run() override
+ {
+ g_autoptr(GstDeviceProvider) provider = NULL;
+ GList *devices, *l;
+ std::vector<std::string> cameraNames;
+ std::unique_ptr<libcamera::CameraManager> cm;
+
+ cm = std::make_unique<libcamera::CameraManager>();
+ cm->start();
+ for (auto &camera : cm->cameras())
+ cameraNames.push_back(camera->id());
+ cm->stop();
+ cm.reset();
+
+ provider = gst_device_provider_factory_get_by_name("libcameraprovider");
+ devices = gst_device_provider_get_devices(provider);
+
+ 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)
+ return TestFail;
+ }
+
+ g_list_free_full(devices, (GDestroyNotify)gst_object_unref);
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(GstreamerDeviceProviderTest)
diff --git a/test/gstreamer/gstreamer_multi_stream_test.cpp b/test/gstreamer/gstreamer_multi_stream_test.cpp
index 112f1dee..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>
@@ -29,7 +29,7 @@ class GstreamerMultiStreamTest : public GstreamerTest, public Test
{
public:
GstreamerMultiStreamTest()
- : GstreamerTest()
+ : GstreamerTest(2)
{
}
@@ -39,24 +39,6 @@ protected:
if (status_ != TestPass)
return status_;
- /* Check if platform supports multistream capture */
- libcamera::CameraManager cm;
- cm.start();
- bool cameraFound = false;
- for (auto &camera : cm.cameras()) {
- if (camera->streams().size() > 1) {
- cameraName_ = camera->id();
- cameraFound = true;
- cm.stop();
- break;
- }
- }
-
- if (!cameraFound) {
- cm.stop();
- return TestSkip;
- }
-
const gchar *streamDescription = "queue ! fakesink";
g_autoptr(GError) error = NULL;
@@ -88,8 +70,6 @@ protected:
int run() override
{
- g_object_set(libcameraSrc_, "camera-name", cameraName_.c_str(), NULL);
-
/* Build the pipeline */
gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,
stream0_, stream1_, NULL);
@@ -124,7 +104,6 @@ protected:
}
private:
- std::string cameraName_;
GstElement *stream0_;
GstElement *stream1_;
};
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 227a5c37..e8119b85 100644
--- a/test/gstreamer/gstreamer_test.cpp
+++ b/test/gstreamer/gstreamer_test.cpp
@@ -5,6 +5,10 @@
* libcamera Gstreamer element API tests
*/
+#include <libcamera/libcamera.h>
+
+#include <libcamera/base/utils.h>
+
#include "gstreamer_test.h"
#include "test.h"
@@ -23,19 +27,18 @@ const char *__asan_default_options()
}
}
-GstreamerTest::GstreamerTest()
+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 */
@@ -49,25 +52,38 @@ GstreamerTest::GstreamerTest()
}
/*
- * 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;
+ * Atleast one camera should be available with numStreams streams,
+ * otherwise skip the test entirely.
+ */
+ if (!checkMinCameraStreamsAndSetCameraName(numStreams)) {
+ status_ = TestSkip;
return;
}
status_ = TestPass;
}
+bool GstreamerTest::checkMinCameraStreamsAndSetCameraName(unsigned int numStreams)
+{
+ libcamera::CameraManager cm;
+ bool cameraFound = false;
+
+ cm.start();
+
+ for (auto &camera : cm.cameras()) {
+ if (camera->streams().size() < numStreams)
+ continue;
+
+ cameraFound = true;
+ cameraName_ = camera->id();
+ break;
+ }
+
+ cm.stop();
+
+ return cameraFound;
+}
+
GstreamerTest::~GstreamerTest()
{
g_clear_object(&pipeline_);
@@ -82,12 +98,13 @@ int GstreamerTest::createPipeline()
pipeline_ = gst_pipeline_new("test-pipeline");
if (!libcameraSrc_ || !pipeline_) {
- g_printerr("Unable to create create pipeline %p.%p\n",
+ g_printerr("Unable to create pipeline %p.%p\n",
libcameraSrc_, pipeline_);
return TestFail;
}
+ g_object_set(libcameraSrc_, "camera-name", cameraName_.c_str(), NULL);
g_object_ref_sink(libcameraSrc_);
return TestPass;
diff --git a/test/gstreamer/gstreamer_test.h b/test/gstreamer/gstreamer_test.h
index 9869d252..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
@@ -10,16 +10,12 @@
#include <iostream>
#include <unistd.h>
-#include <libcamera/base/utils.h>
-
-#include "libcamera/internal/source_paths.h"
-
#include <gst/gst.h>
class GstreamerTest
{
public:
- GstreamerTest();
+ GstreamerTest(unsigned int numStreams = 1);
virtual ~GstreamerTest();
protected:
@@ -28,7 +24,11 @@ protected:
int processEvent();
void printError(GstMessage *msg);
+ std::string cameraName_;
GstElement *pipeline_;
GstElement *libcameraSrc_;
int status_;
+
+private:
+ bool checkMinCameraStreamsAndSetCameraName(unsigned int numStreams);
};
diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build
index 10058fc5..f3ba5a23 100644
--- a/test/gstreamer/meson.build
+++ b/test/gstreamer/meson.build
@@ -5,16 +5,17 @@ if not gst_enabled
endif
gstreamer_tests = [
- ['single_stream_test', 'gstreamer_single_stream_test.cpp'],
- ['multi_stream_test', 'gstreamer_multi_stream_test.cpp'],
+ {'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']},
]
-gstreamer_dep = dependency('gstreamer-1.0', required: true)
+gstreamer_dep = dependency('gstreamer-1.0', required : true)
-foreach t : gstreamer_tests
- exe = executable(t[0], t[1], 'gstreamer_test.cpp',
+foreach test : gstreamer_tests
+ exe = executable(test['name'], test['sources'], 'gstreamer_test.cpp',
dependencies : [libcamera_private, gstreamer_dep],
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'gstreamer', is_parallel : false)
+ test(test['name'], exe, suite : 'gstreamer', is_parallel : false, env : gst_env)
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 3c0df843..e840f6ab 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,6 +16,7 @@
#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>
@@ -52,10 +53,10 @@ protected:
ipaManager_ = make_unique<IPAManager>();
/* Create a pipeline handler for vimc. */
- std::vector<PipelineHandlerFactory *> &factories =
- PipelineHandlerFactory::factories();
- for (PipelineHandlerFactory *factory : factories) {
- if (factory->name() == "PipelineHandlerVimc") {
+ const std::vector<PipelineHandlerFactoryBase *> &factories =
+ PipelineHandlerFactoryBase::factories();
+ for (const PipelineHandlerFactoryBase *factory : factories) {
+ if (factory->name() == "vimc") {
pipe_ = factory->create(nullptr);
break;
}
@@ -106,7 +107,11 @@ protected:
/* Test initialization of IPA module. */
std::string conf = ipa_->configurationFile("vimc.conf");
- int ret = ipa_->init(IPASettings{ conf, "vimc" });
+ Flags<ipa::vimc::TestFlag> inFlags;
+ Flags<ipa::vimc::TestFlag> outFlags;
+ int ret = ipa_->init(IPASettings{ conf, "vimc" },
+ ipa::vimc::IPAOperationInit,
+ inFlags, &outFlags);
if (ret < 0) {
cerr << "IPA interface init() failed" << endl;
return TestFail;
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/meson.build b/test/ipa/meson.build
index 7938633e..180b0da0 100644
--- a/test/ipa/meson.build
+++ b/test/ipa/meson.build
@@ -1,15 +1,15 @@
# SPDX-License-Identifier: CC0-1.0
ipa_test = [
- ['ipa_module_test', 'ipa_module_test.cpp'],
- ['ipa_interface_test', 'ipa_interface_test.cpp'],
+ {'name': 'ipa_module_test', 'sources': ['ipa_module_test.cpp']},
+ {'name': 'ipa_interface_test', 'sources': ['ipa_interface_test.cpp']},
]
-foreach t : ipa_test
- exe = executable(t[0], [t[1], libcamera_generated_ipa_headers],
+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])
- test(t[0], exe, suite : 'ipa')
+ test(test['name'], exe, suite : 'ipa')
endforeach
diff --git a/test/ipc/meson.build b/test/ipc/meson.build
index 2a6cd7fb..8e447d22 100644
--- a/test/ipc/meson.build
+++ b/test/ipc/meson.build
@@ -1,15 +1,15 @@
# SPDX-License-Identifier: CC0-1.0
ipc_tests = [
- ['unixsocket_ipc', 'unixsocket_ipc.cpp'],
- ['unixsocket', 'unixsocket.cpp'],
+ {'name': 'unixsocket_ipc', 'sources': ['unixsocket_ipc.cpp']},
+ {'name': 'unixsocket', 'sources': ['unixsocket.cpp']},
]
-foreach t : ipc_tests
- exe = executable(t[0], t[1],
+foreach test : ipc_tests
+ exe = executable(test['name'], test['sources'],
dependencies : libcamera_private,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'ipc')
+ test(test['name'], exe, suite : 'ipc')
endforeach
diff --git a/test/ipc/unixsocket.cpp b/test/ipc/unixsocket.cpp
index 304e613b..f44ab9c9 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>
@@ -431,7 +431,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.cpp b/test/libtest/buffer_source.cpp
index 1b261697..dde11f36 100644
--- a/test/libtest/buffer_source.cpp
+++ b/test/libtest/buffer_source.cpp
@@ -72,7 +72,7 @@ int BufferSource::allocate(const StreamConfiguration &config)
}
format.size = config.size;
- format.fourcc = V4L2PixelFormat::fromPixelFormat(config.pixelFormat);
+ format.fourcc = video->toV4L2PixelFormat(config.pixelFormat);
if (video->setFormat(&format)) {
std::cout << "Failed to set format on output device" << std::endl;
return TestFail;
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/log/meson.build b/test/log/meson.build
index ac87841a..2298ff84 100644
--- a/test/log/meson.build
+++ b/test/log/meson.build
@@ -1,15 +1,15 @@
# SPDX-License-Identifier: CC0-1.0
log_test = [
- ['log_api', 'log_api.cpp'],
- ['log_process', 'log_process.cpp'],
+ {'name': 'log_api', 'sources': ['log_api.cpp']},
+ {'name': 'log_process', 'sources': ['log_process.cpp']},
]
-foreach t : log_test
- exe = executable(t[0], t[1],
+foreach test : log_test
+ exe = executable(test['name'], test['sources'],
dependencies : libcamera_private,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'log')
+ test(test['name'], exe, suite : 'log')
endforeach
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/media_device/meson.build b/test/media_device/meson.build
index 83dfe8f1..84966c97 100644
--- a/test/media_device/meson.build
+++ b/test/media_device/meson.build
@@ -5,20 +5,20 @@ lib_mdev_test_sources = files([
])
media_device_tests = [
- ['media_device_acquire', 'media_device_acquire.cpp'],
- ['media_device_print_test', 'media_device_print_test.cpp'],
- ['media_device_link_test', 'media_device_link_test.cpp'],
+ {'name': 'media_device_acquire', 'sources': ['media_device_acquire.cpp']},
+ {'name': 'media_device_print_test', 'sources': ['media_device_print_test.cpp']},
+ {'name': 'media_device_link_test', 'sources': ['media_device_link_test.cpp']},
]
lib_mdev_test = static_library('lib_mdev_test', lib_mdev_test_sources,
dependencies : libcamera_private,
include_directories : test_includes_internal)
-foreach t : media_device_tests
- exe = executable(t[0], t[1],
+foreach test : media_device_tests
+ exe = executable(test['name'], test['sources'],
dependencies : libcamera_private,
link_with : [test_libraries, lib_mdev_test],
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'media_device', is_parallel : false)
+ test(test['name'], exe, suite : 'media_device', is_parallel : false)
endforeach
diff --git a/test/meson.build b/test/meson.build
index d050bfa1..5ed052ed 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -7,6 +7,22 @@ endif
test_enabled = true
+# When ASan is enabled, find the path to the ASan runtime needed by multiple
+# tests. This currently works with gcc only, as clang uses different file names
+# depending on the compiler version and target architecture.
+asan_enabled = false
+asan_runtime_missing = false
+
+if get_option('b_sanitize').contains('address')
+ asan_enabled = true
+
+ if cc.get_id() == 'gcc'
+ asan_runtime = run_command(cc, '-print-file-name=libasan.so', check : true).stdout().strip()
+ else
+ asan_runtime_missing = true
+ endif
+endif
+
subdir('libtest')
subdir('camera')
@@ -16,7 +32,6 @@ subdir('ipa')
subdir('ipc')
subdir('log')
subdir('media_device')
-subdir('pipeline')
subdir('process')
subdir('py')
subdir('serialization')
@@ -26,66 +41,89 @@ subdir('v4l2_subdevice')
subdir('v4l2_videodevice')
public_tests = [
- ['geometry', 'geometry.cpp'],
- ['public-api', 'public-api.cpp'],
- ['signal', 'signal.cpp'],
- ['span', 'span.cpp'],
+ {'name': 'color-space', 'sources': ['color-space.cpp']},
+ {'name': 'geometry', 'sources': ['geometry.cpp']},
+ {'name': 'public-api', 'sources': ['public-api.cpp']},
+ {'name': 'signal', 'sources': ['signal.cpp']},
+ {'name': 'span', 'sources': ['span.cpp']},
+ {'name': 'transform', 'sources': ['transform.cpp']},
]
internal_tests = [
- ['bayer-format', 'bayer-format.cpp'],
- ['byte-stream-buffer', 'byte-stream-buffer.cpp'],
- ['camera-sensor', 'camera-sensor.cpp'],
- ['delayed_controls', 'delayed_controls.cpp'],
- ['event', 'event.cpp'],
- ['event-dispatcher', 'event-dispatcher.cpp'],
- ['event-thread', 'event-thread.cpp'],
- ['file', 'file.cpp'],
- ['flags', 'flags.cpp'],
- ['hotplug-cameras', 'hotplug-cameras.cpp'],
- ['message', 'message.cpp'],
- ['object', 'object.cpp'],
- ['object-delete', 'object-delete.cpp'],
- ['object-invoke', 'object-invoke.cpp'],
- ['pixel-format', 'pixel-format.cpp'],
- ['shared-fd', 'shared-fd.cpp'],
- ['signal-threads', 'signal-threads.cpp'],
- ['threads', 'threads.cpp'],
- ['timer', 'timer.cpp'],
- ['timer-thread', 'timer-thread.cpp'],
- ['unique-fd', 'unique-fd.cpp'],
- ['utils', 'utils.cpp'],
- ['yaml-parser', 'yaml-parser.cpp'],
+ {'name': 'bayer-format', 'sources': ['bayer-format.cpp']},
+ {'name': 'byte-stream-buffer', 'sources': ['byte-stream-buffer.cpp']},
+ {'name': 'camera-sensor', 'sources': ['camera-sensor.cpp']},
+ {'name': 'delayed_controls', 'sources': ['delayed_controls.cpp']},
+ {'name': 'event', 'sources': ['event.cpp']},
+ {'name': 'event-dispatcher', 'sources': ['event-dispatcher.cpp']},
+ {'name': 'event-thread', 'sources': ['event-thread.cpp']},
+ {'name': 'file', 'sources': ['file.cpp']},
+ {'name': 'flags', 'sources': ['flags.cpp']},
+ {'name': 'hotplug-cameras', 'sources': ['hotplug-cameras.cpp']},
+ {'name': 'message', 'sources': ['message.cpp']},
+ {'name': 'object', 'sources': ['object.cpp']},
+ {'name': 'object-delete', 'sources': ['object-delete.cpp']},
+ {'name': 'object-invoke', 'sources': ['object-invoke.cpp']},
+ {'name': 'pixel-format', 'sources': ['pixel-format.cpp']},
+ {'name': 'shared-fd', 'sources': ['shared-fd.cpp']},
+ {'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': 'yaml-parser', 'sources': ['yaml-parser.cpp']},
]
internal_non_parallel_tests = [
- ['fence', 'fence.cpp'],
- ['mapped-buffer', 'mapped-buffer.cpp'],
+ {'name': 'fence', 'sources': ['fence.cpp']},
+ {'name': 'mapped-buffer', 'sources': ['mapped-buffer.cpp']},
]
-foreach t : public_tests
- exe = executable(t[0], t[1],
- dependencies : libcamera_public,
+foreach test : public_tests
+ deps = [libcamera_public]
+ if 'dependencies' in test
+ deps += test['dependencies']
+ endif
+
+ exe = executable(test['name'], test['sources'],
+ dependencies : deps,
+ implicit_include_directories : false,
link_with : test_libraries,
include_directories : test_includes_public)
- test(t[0], exe)
+ test(test['name'], exe, should_fail : test.get('should_fail', false))
endforeach
-foreach t : internal_tests
- exe = executable(t[0], t[1],
- dependencies : libcamera_private,
+foreach test : internal_tests
+ deps = [libcamera_private]
+ if 'dependencies' in test
+ deps += test['dependencies']
+ endif
+
+ exe = executable(test['name'], test['sources'],
+ dependencies : deps,
+ implicit_include_directories : false,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe)
+ test(test['name'], exe, should_fail : test.get('should_fail', false))
endforeach
-foreach t : internal_non_parallel_tests
- exe = executable(t[0], t[1],
- dependencies : libcamera_private,
+foreach test : internal_non_parallel_tests
+ deps = [libcamera_private]
+ if 'dependencies' in test
+ deps += test['dependencies']
+ endif
+
+ exe = executable(test['name'], test['sources'],
+ dependencies : deps,
+ implicit_include_directories : false,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], 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/pipeline/ipu3/ipu3_pipeline_test.cpp b/test/pipeline/ipu3/ipu3_pipeline_test.cpp
deleted file mode 100644
index 9e647af5..00000000
--- a/test/pipeline/ipu3/ipu3_pipeline_test.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * ipu3_pipeline_test.cpp - Intel IPU3 pipeline test
- */
-
-#include <iostream>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <libcamera/camera.h>
-#include <libcamera/camera_manager.h>
-
-#include "libcamera/internal/device_enumerator.h"
-#include "libcamera/internal/media_device.h"
-#include "libcamera/internal/media_object.h"
-
-#include "test.h"
-
-using namespace std;
-using namespace libcamera;
-
-/*
- * Verify that the Intel IPU3 pipeline handler gets matched and cameras
- * are enumerated correctly.
- *
- * The test is supposed to be run on an IPU3 platform, otherwise it gets
- * skipped.
- *
- * The test lists all cameras registered in the system, if any camera is
- * available at all.
- */
-class IPU3PipelineTest : public Test
-{
-protected:
- int init();
- int run();
- void cleanup();
-
-private:
- CameraManager *cameraManager_;
- unsigned int sensors_;
-};
-
-int IPU3PipelineTest::init()
-{
- unique_ptr<DeviceEnumerator> 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 imgu_dm("ipu3-imgu");
- DeviceMatch cio2_dm("ipu3-cio2");
-
- if (!enumerator->search(imgu_dm)) {
- cerr << "Failed to find IPU3 IMGU: test skip" << endl;
- return TestSkip;
- }
-
- std::shared_ptr<MediaDevice> cio2 = enumerator->search(cio2_dm);
- if (!cio2) {
- cerr << "Failed to find IPU3 CIO2: test skip" << endl;
- return TestSkip;
- }
-
- /*
- * Camera sensor are connected to the CIO2 unit.
- * Count how many sensors are connected in the system
- * and later verify this matches the number of registered
- * cameras.
- */
- int ret = cio2->populate();
- if (ret) {
- cerr << "Failed to populate media device " << cio2->deviceNode() << endl;
- return TestFail;
- }
-
- sensors_ = 0;
- const vector<MediaEntity *> &entities = cio2->entities();
- for (MediaEntity *entity : entities) {
- if (entity->function() == MEDIA_ENT_F_CAM_SENSOR)
- sensors_++;
- }
-
- enumerator.reset(nullptr);
-
- cameraManager_ = new CameraManager();
- ret = cameraManager_->start();
- if (ret) {
- cerr << "Failed to start the CameraManager" << endl;
- return TestFail;
- }
-
- return 0;
-}
-
-int IPU3PipelineTest::run()
-{
- auto cameras = cameraManager_->cameras();
- for (const std::shared_ptr<Camera> &cam : cameras)
- cout << "Found camera '" << cam->id() << "'" << endl;
-
- if (cameras.size() != sensors_) {
- cerr << cameras.size() << " cameras registered, but " << sensors_
- << " were expected" << endl;
- return TestFail;
- }
-
- return TestPass;
-}
-
-void IPU3PipelineTest::cleanup()
-{
- cameraManager_->stop();
- delete cameraManager_;
-}
-
-TEST_REGISTER(IPU3PipelineTest)
diff --git a/test/pipeline/ipu3/meson.build b/test/pipeline/ipu3/meson.build
deleted file mode 100644
index 16701080..00000000
--- a/test/pipeline/ipu3/meson.build
+++ /dev/null
@@ -1,14 +0,0 @@
-# SPDX-License-Identifier: CC0-1.0
-
-ipu3_test = [
- ['ipu3_pipeline_test', 'ipu3_pipeline_test.cpp'],
-]
-
-foreach t : ipu3_test
- exe = executable(t[0], t[1],
- dependencies : libcamera_private,
- link_with : test_libraries,
- include_directories : test_includes_internal)
-
- test(t[0], exe, suite : 'ipu3', is_parallel : false)
-endforeach
diff --git a/test/pipeline/meson.build b/test/pipeline/meson.build
deleted file mode 100644
index 6e7901fe..00000000
--- a/test/pipeline/meson.build
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: CC0-1.0
-
-subdir('ipu3')
-subdir('rkisp1')
diff --git a/test/pipeline/rkisp1/meson.build b/test/pipeline/rkisp1/meson.build
deleted file mode 100644
index 364b5711..00000000
--- a/test/pipeline/rkisp1/meson.build
+++ /dev/null
@@ -1,14 +0,0 @@
-# SPDX-License-Identifier: CC0-1.0
-
-rkisp1_test = [
- ['rkisp1_pipeline_test', 'rkisp1_pipeline_test.cpp'],
-]
-
-foreach t : rkisp1_test
- exe = executable(t[0], t[1],
- dependencies : libcamera_private,
- link_with : test_libraries,
- include_directories : test_includes_internal)
-
- test(t[0], exe, suite : 'rkisp1', is_parallel : false)
-endforeach
diff --git a/test/pipeline/rkisp1/rkisp1_pipeline_test.cpp b/test/pipeline/rkisp1/rkisp1_pipeline_test.cpp
deleted file mode 100644
index acaf3c33..00000000
--- a/test/pipeline/rkisp1/rkisp1_pipeline_test.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2020, Linaro
- *
- * Based on test/pipeline/ipu3/ipu3_pipeline_test.cpp
- *
- * rkisp1_pipeline_test.cpp - Rockchip RK3399 rkisp1 pipeline test
- */
-
-#include <iostream>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <libcamera/camera.h>
-#include <libcamera/camera_manager.h>
-
-#include "libcamera/internal/device_enumerator.h"
-#include "libcamera/internal/media_device.h"
-#include "libcamera/internal/media_object.h"
-
-#include "test.h"
-
-using namespace std;
-using namespace libcamera;
-
-/*
- * Verify that the RK3399 pipeline handler gets matched and cameras
- * are enumerated correctly.
- *
- * The test is supposed to be run on rockchip platform.
- *
- * The test lists all cameras registered in the system, if any camera is
- * available at all.
- */
-class RKISP1PipelineTest : public Test
-{
-protected:
- int init();
- int run();
- void cleanup();
-
-private:
- CameraManager *cameraManager_;
- unsigned int sensors_;
-};
-
-int RKISP1PipelineTest::init()
-{
- unique_ptr<DeviceEnumerator> 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("rkisp1");
-
- std::shared_ptr<MediaDevice> rkisp1 = enumerator->search(dm);
- if (!rkisp1) {
- cerr << "Failed to find rkisp1: test skip" << endl;
- return TestSkip;
- }
-
- int ret = rkisp1->populate();
- if (ret) {
- cerr << "Failed to populate media device "
- << rkisp1->deviceNode() << endl;
- return TestFail;
- }
-
- sensors_ = 0;
- const vector<MediaEntity *> &entities = rkisp1->entities();
- for (MediaEntity *entity : entities) {
- if (entity->function() == MEDIA_ENT_F_CAM_SENSOR)
- sensors_++;
- }
-
- cameraManager_ = new CameraManager();
- ret = cameraManager_->start();
- if (ret) {
- cerr << "Failed to start the CameraManager" << endl;
- return TestFail;
- }
-
- return 0;
-}
-
-int RKISP1PipelineTest::run()
-{
- auto cameras = cameraManager_->cameras();
- for (const std::shared_ptr<Camera> &cam : cameras)
- cout << "Found camera '" << cam->id() << "'" << endl;
-
- if (cameras.size() != sensors_) {
- cerr << cameras.size() << " cameras registered, but " << sensors_
- << " were expected" << endl;
- return TestFail;
- }
-
- return TestPass;
-}
-
-void RKISP1PipelineTest::cleanup()
-{
- cameraManager_->stop();
- delete cameraManager_;
-}
-
-TEST_REGISTER(RKISP1PipelineTest)
diff --git a/test/process/meson.build b/test/process/meson.build
index af86b277..a80dc2d9 100644
--- a/test/process/meson.build
+++ b/test/process/meson.build
@@ -1,14 +1,14 @@
# SPDX-License-Identifier: CC0-1.0
process_tests = [
- ['process_test', 'process_test.cpp'],
+ {'name': 'process_test', 'sources': ['process_test.cpp']},
]
-foreach t : process_tests
- exe = executable(t[0], t[1],
+foreach test : process_tests
+ exe = executable(test['name'], test['sources'],
dependencies : libcamera_private,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'process', is_parallel : false)
+ test(test['name'], exe, suite : 'process', is_parallel : false)
endforeach
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 2affdbd4..0b679d31 100644
--- a/test/py/meson.build
+++ b/test/py/meson.build
@@ -4,14 +4,29 @@ if not pycamera_enabled
subdir_done()
endif
+# If ASan is enabled, the link order runtime check will fail as Python is not
+# linked to ASan. LD_PRELOAD the ASan runtime if available, or skip the test
+# otherwise.
+
+if asan_runtime_missing
+ warning('Unable to get path to ASan runtime, Python test disabled')
+ subdir_done()
+endif
+
pymod = import('python')
py3 = pymod.find_installation('python3')
pypathdir = meson.project_build_root() / 'src' / 'py'
+py_env = ['PYTHONPATH=' + pypathdir]
+
+if asan_enabled
+ # Disable leak detection as the Python interpreter is full of leaks.
+ py_env += ['LD_PRELOAD=' + asan_runtime, 'ASAN_OPTIONS=detect_leaks=0']
+endif
test('pyunittests',
py3,
args : files('unittests.py'),
- env : ['PYTHONPATH=' + pypathdir],
+ env : py_env,
suite : 'pybindings',
is_parallel : false)
diff --git a/test/py/unittests.py b/test/py/unittests.py
index 9adc4337..1caea98e 100755
--- a/test/py/unittests.py
+++ b/test/py/unittests.py
@@ -4,11 +4,9 @@
# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
from collections import defaultdict
-import errno
import gc
import libcamera as libcam
import selectors
-import time
import typing
import unittest
import weakref
@@ -18,6 +16,18 @@ class BaseTestCase(unittest.TestCase):
def assertZero(self, a, msg=None):
self.assertEqual(a, 0, msg)
+ def assertIsAlive(self, wr, msg='object not alive'):
+ self.assertIsNotNone(wr(), msg)
+
+ def assertIsDead(self, wr, msg='object not dead'):
+ self.assertIsNone(wr(), msg)
+
+ def assertIsAllAlive(self, wr_list, msg='object not alive'):
+ self.assertTrue(all([wr() for wr in wr_list]), msg)
+
+ def assertIsAllDead(self, wr_list, msg='object not dead'):
+ self.assertTrue(all([not wr() for wr in wr_list]), msg)
+
class SimpleTestMethods(BaseTestCase):
def test_get_ref(self):
@@ -28,45 +38,44 @@ class SimpleTestMethods(BaseTestCase):
self.assertIsNotNone(cam)
wr_cam = weakref.ref(cam)
- cm = None
+ del cm
gc.collect()
- self.assertIsNotNone(wr_cm())
+ self.assertIsAlive(wr_cm)
- cam = None
+ del cam
gc.collect()
- self.assertIsNone(wr_cm())
- self.assertIsNone(wr_cam())
+ self.assertIsDead(wr_cm)
+ self.assertIsDead(wr_cam)
def test_acquire_release(self):
cm = libcam.CameraManager.singleton()
cam = cm.get('platform/vimc.0 Sensor B')
self.assertIsNotNone(cam)
- ret = cam.acquire()
- self.assertZero(ret)
+ cam.acquire()
- ret = cam.release()
- self.assertZero(ret)
+ cam.release()
def test_double_acquire(self):
cm = libcam.CameraManager.singleton()
cam = cm.get('platform/vimc.0 Sensor B')
self.assertIsNotNone(cam)
- ret = cam.acquire()
- self.assertZero(ret)
+ cam.acquire()
libcam.log_set_level('Camera', 'FATAL')
- ret = cam.acquire()
- self.assertEqual(ret, -errno.EBUSY)
+ with self.assertRaises(RuntimeError):
+ cam.acquire()
libcam.log_set_level('Camera', 'ERROR')
- ret = cam.release()
- self.assertZero(ret)
+ cam.release()
- ret = cam.release()
- # I expected EBUSY, but looks like double release works fine
- self.assertZero(ret)
+ # I expected exception here, but looks like double release works fine
+ cam.release()
+
+ def test_version(self):
+ cm = libcam.CameraManager.singleton()
+ self.assertIsInstance(cm.version, str)
class CameraTesterBase(BaseTestCase):
@@ -80,11 +89,7 @@ class CameraTesterBase(BaseTestCase):
self.cm = None
self.skipTest('No vimc found')
- ret = self.cam.acquire()
- if ret != 0:
- self.cam = None
- self.cm = None
- raise Exception('Failed to acquire camera')
+ self.cam.acquire()
self.wr_cam = weakref.ref(self.cam)
self.wr_cm = weakref.ref(self.cm)
@@ -93,15 +98,13 @@ class CameraTesterBase(BaseTestCase):
# If a test fails, the camera may be in running state. So always stop.
self.cam.stop()
- ret = self.cam.release()
- if ret != 0:
- raise Exception('Failed to release camera')
+ self.cam.release()
self.cam = None
self.cm = None
- self.assertIsNone(self.wr_cm())
- self.assertIsNone(self.wr_cam())
+ self.assertIsDead(self.wr_cm)
+ self.assertIsDead(self.wr_cam)
class AllocatorTestMethods(CameraTesterBase):
@@ -115,49 +118,48 @@ class AllocatorTestMethods(CameraTesterBase):
streamconfig = camconfig.at(0)
wr_streamconfig = weakref.ref(streamconfig)
- ret = cam.configure(camconfig)
- self.assertZero(ret)
+ cam.configure(camconfig)
stream = streamconfig.stream
wr_stream = weakref.ref(stream)
# stream should keep streamconfig and camconfig alive
- streamconfig = None
- camconfig = None
+ del streamconfig
+ del camconfig
gc.collect()
- self.assertIsNotNone(wr_camconfig())
- self.assertIsNotNone(wr_streamconfig())
+ self.assertIsAlive(wr_camconfig)
+ self.assertIsAlive(wr_streamconfig)
allocator = libcam.FrameBufferAllocator(cam)
- ret = allocator.allocate(stream)
- self.assertTrue(ret > 0)
+ num_bufs = allocator.allocate(stream)
+ self.assertTrue(num_bufs > 0)
wr_allocator = weakref.ref(allocator)
buffers = allocator.buffers(stream)
self.assertIsNotNone(buffers)
- buffers = None
+ del buffers
buffer = allocator.buffers(stream)[0]
self.assertIsNotNone(buffer)
wr_buffer = weakref.ref(buffer)
- allocator = None
+ del allocator
gc.collect()
- self.assertIsNotNone(wr_buffer())
- self.assertIsNotNone(wr_allocator())
- self.assertIsNotNone(wr_stream())
+ self.assertIsAlive(wr_buffer)
+ self.assertIsAlive(wr_allocator)
+ self.assertIsAlive(wr_stream)
- buffer = None
+ del buffer
gc.collect()
- self.assertIsNone(wr_buffer())
- self.assertIsNone(wr_allocator())
- self.assertIsNotNone(wr_stream())
+ self.assertIsDead(wr_buffer)
+ self.assertIsDead(wr_allocator)
+ self.assertIsAlive(wr_stream)
- stream = None
+ del stream
gc.collect()
- self.assertIsNone(wr_stream())
- self.assertIsNone(wr_camconfig())
- self.assertIsNone(wr_streamconfig())
+ self.assertIsDead(wr_stream)
+ self.assertIsDead(wr_camconfig)
+ self.assertIsDead(wr_streamconfig)
class SimpleCaptureMethods(CameraTesterBase):
@@ -173,14 +175,13 @@ class SimpleCaptureMethods(CameraTesterBase):
self.assertIsNotNone(fmts)
fmts = None
- ret = cam.configure(camconfig)
- self.assertZero(ret)
+ cam.configure(camconfig)
stream = streamconfig.stream
allocator = libcam.FrameBufferAllocator(cam)
- ret = allocator.allocate(stream)
- self.assertTrue(ret > 0)
+ num_bufs = allocator.allocate(stream)
+ self.assertTrue(num_bufs > 0)
num_bufs = len(allocator.buffers(stream))
@@ -190,26 +191,30 @@ class SimpleCaptureMethods(CameraTesterBase):
self.assertIsNotNone(req)
buffer = allocator.buffers(stream)[i]
- ret = req.add_buffer(stream, buffer)
- self.assertZero(ret)
+ req.add_buffer(stream, buffer)
reqs.append(req)
buffer = None
- ret = cam.start()
- self.assertZero(ret)
+ cam.start()
for req in reqs:
- ret = cam.queue_request(req)
- self.assertZero(ret)
+ cam.queue_request(req)
reqs = None
gc.collect()
+ sel = selectors.DefaultSelector()
+ sel.register(cm.event_fd, selectors.EVENT_READ)
+
reqs = []
while True:
+ events = sel.select()
+ if not events:
+ continue
+
ready_reqs = cm.get_ready_requests()
reqs += ready_reqs
@@ -223,8 +228,7 @@ class SimpleCaptureMethods(CameraTesterBase):
reqs = None
gc.collect()
- ret = cam.stop()
- self.assertZero(ret)
+ cam.stop()
def test_select(self):
cm = self.cm
@@ -238,14 +242,13 @@ class SimpleCaptureMethods(CameraTesterBase):
self.assertIsNotNone(fmts)
fmts = None
- ret = cam.configure(camconfig)
- self.assertZero(ret)
+ cam.configure(camconfig)
stream = streamconfig.stream
allocator = libcam.FrameBufferAllocator(cam)
- ret = allocator.allocate(stream)
- self.assertTrue(ret > 0)
+ num_bufs = allocator.allocate(stream)
+ self.assertTrue(num_bufs > 0)
num_bufs = len(allocator.buffers(stream))
@@ -255,19 +258,16 @@ class SimpleCaptureMethods(CameraTesterBase):
self.assertIsNotNone(req)
buffer = allocator.buffers(stream)[i]
- ret = req.add_buffer(stream, buffer)
- self.assertZero(ret)
+ req.add_buffer(stream, buffer)
reqs.append(req)
buffer = None
- ret = cam.start()
- self.assertZero(ret)
+ cam.start()
for req in reqs:
- ret = cam.queue_request(req)
- self.assertZero(ret)
+ cam.queue_request(req)
reqs = None
gc.collect()
@@ -280,7 +280,7 @@ class SimpleCaptureMethods(CameraTesterBase):
running = True
while running:
events = sel.select()
- for key, _ in events:
+ for _ in events:
ready_reqs = cm.get_ready_requests()
reqs += ready_reqs
@@ -296,8 +296,7 @@ class SimpleCaptureMethods(CameraTesterBase):
reqs = None
gc.collect()
- ret = cam.stop()
- self.assertZero(ret)
+ cam.stop()
# Recursively expand slist's objects into olist, using seen to track already
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 698c81d6..4b11d67f 100644
--- a/test/serialization/generated_serializer/generated_serializer_test.cpp
+++ b/test/serialization/generated_serializer/generated_serializer_test.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * generated_serializer_test.cpp - Test generated serializer
+ * Test generated serializer
*/
#include <algorithm>
@@ -35,6 +35,13 @@ if (struct1.field != struct2.field) { \
return TestFail; \
}
+#define TEST_SCOPED_ENUM_EQUALITY(struct1, struct2, field) \
+if (struct1.field != struct2.field) { \
+ cerr << #field << " field incorrect" << endl; \
+ return TestFail; \
+}
+
+
ipa::test::TestStruct t, u;
t.m = {
@@ -51,6 +58,13 @@ if (struct1.field != struct2.field) { \
t.s2 = "goodbye";
t.s3 = "lorem ipsum";
t.i = 58527;
+ t.c = ipa::test::IPAOperationInit;
+ t.e = ipa::test::ErrorFlags::Error1;
+
+ Flags<ipa::test::ErrorFlags> flags;
+ flags |= ipa::test::ErrorFlags::Error1;
+ flags |= ipa::test::ErrorFlags::Error2;
+ t.f = flags;
std::vector<uint8_t> serialized;
@@ -69,7 +83,10 @@ if (struct1.field != struct2.field) { \
TEST_FIELD_EQUALITY(t, u, s2);
TEST_FIELD_EQUALITY(t, u, s3);
TEST_FIELD_EQUALITY(t, u, i);
+ TEST_FIELD_EQUALITY(t, u, c);
+ TEST_SCOPED_ENUM_EQUALITY(t, u, e);
+ TEST_SCOPED_ENUM_EQUALITY(t, u, f);
/* Test vector of generated structs */
std::vector<ipa::test::TestStruct> v = { t, u };
@@ -92,11 +109,19 @@ if (struct1.field != struct2.field) { \
TEST_FIELD_EQUALITY(v[0], w[0], s2);
TEST_FIELD_EQUALITY(v[0], w[0], s3);
TEST_FIELD_EQUALITY(v[0], w[0], i);
+ TEST_FIELD_EQUALITY(v[0], w[0], c);
+
+ TEST_SCOPED_ENUM_EQUALITY(v[0], w[0], e);
+ TEST_SCOPED_ENUM_EQUALITY(v[0], w[0], f);
TEST_FIELD_EQUALITY(v[1], w[1], s1);
TEST_FIELD_EQUALITY(v[1], w[1], s2);
TEST_FIELD_EQUALITY(v[1], w[1], s3);
TEST_FIELD_EQUALITY(v[1], w[1], i);
+ TEST_FIELD_EQUALITY(v[1], w[1], c);
+
+ TEST_SCOPED_ENUM_EQUALITY(v[1], w[1], e);
+ TEST_SCOPED_ENUM_EQUALITY(v[1], w[1], f);
return TestPass;
}
diff --git a/test/serialization/generated_serializer/include/libcamera/ipa/test.mojom b/test/serialization/generated_serializer/include/libcamera/ipa/test.mojom
index 5f200885..91c31642 100644
--- a/test/serialization/generated_serializer/include/libcamera/ipa/test.mojom
+++ b/test/serialization/generated_serializer/include/libcamera/ipa/test.mojom
@@ -9,6 +9,13 @@ enum IPAOperationCode {
IPAOperationStop,
};
+[scopedEnum] enum ErrorFlags {
+ Error1 = 0x1,
+ Error2 = 0x2,
+ Error3 = 0x4,
+ Error4 = 0x8,
+};
+
struct IPASettings {};
struct TestStruct {
@@ -18,6 +25,9 @@ struct TestStruct {
string s2;
int32 i;
string s3;
+ IPAOperationCode c;
+ ErrorFlags e;
+ [flags] ErrorFlags f;
};
interface IPATestInterface {
diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp
index d2050a86..aea63c73 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>
@@ -20,11 +20,7 @@
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
-#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/ipa_data_serializer.h"
-#include "libcamera/internal/ipa_manager.h"
-#include "libcamera/internal/ipa_module.h"
-#include "libcamera/internal/pipeline_handler.h"
#include "serialization_test.h"
#include "test.h"
diff --git a/test/serialization/meson.build b/test/serialization/meson.build
index 26e42b15..a6e8d793 100644
--- a/test/serialization/meson.build
+++ b/test/serialization/meson.build
@@ -3,14 +3,14 @@
subdir('generated_serializer')
serialization_tests = [
- ['control_serialization', 'control_serialization.cpp'],
- ['ipa_data_serializer_test', 'ipa_data_serializer_test.cpp'],
+ {'name': 'control_serialization', 'sources': ['control_serialization.cpp']},
+ {'name': 'ipa_data_serializer_test', 'sources': ['ipa_data_serializer_test.cpp']},
]
-foreach t : serialization_tests
- exe = executable(t[0], [t[1], 'serialization_test.cpp'],
+foreach test : serialization_tests
+ exe = executable(test['name'], test['sources'], 'serialization_test.cpp',
dependencies : libcamera_private,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'serialization', is_parallel : false)
+ test(test['name'], exe, suite : 'serialization', is_parallel : false)
endforeach
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..5452967d 100644
--- a/test/span.cpp
+++ b/test/span.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * span.cpp - Span tests
+ * Span tests
*/
/*
diff --git a/test/stream/meson.build b/test/stream/meson.build
index 73608ffd..dd77f2f7 100644
--- a/test/stream/meson.build
+++ b/test/stream/meson.build
@@ -1,13 +1,14 @@
# SPDX-License-Identifier: CC0-1.0
stream_tests = [
- ['stream_formats', 'stream_formats.cpp'],
+ {'name': 'stream_colorspace', 'sources': ['stream_colorspace.cpp']},
+ {'name': 'stream_formats', 'sources': ['stream_formats.cpp']},
]
-foreach t : stream_tests
- exe = executable(t[0], t[1],
+foreach test : stream_tests
+ exe = executable(test['name'], test['sources'],
dependencies : libcamera_public,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite: 'stream')
+ test(test['name'], exe, suite : 'stream')
endforeach
diff --git a/test/stream/stream_colorspace.cpp b/test/stream/stream_colorspace.cpp
new file mode 100644
index 00000000..4c904c4c
--- /dev/null
+++ b/test/stream/stream_colorspace.cpp
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2022, Ideas on Board Oy.
+ *
+ * Stream colorspace adjustment test
+ */
+
+#include <iostream>
+
+#include <libcamera/camera.h>
+#include <libcamera/formats.h>
+#include <libcamera/stream.h>
+
+#include "test.h"
+
+using namespace libcamera;
+using namespace std;
+
+class TestCameraConfiguration : public CameraConfiguration
+{
+public:
+ TestCameraConfiguration()
+ : CameraConfiguration()
+ {
+ }
+
+ Status validate() override
+ {
+ return validateColorSpaces();
+ }
+};
+
+class StreamColorSpaceTest : public Test
+{
+protected:
+ int run()
+ {
+ TestCameraConfiguration config;
+
+ StreamConfiguration cfg;
+ cfg.size = { 640, 320 };
+ cfg.pixelFormat = formats::YUV422;
+ cfg.colorSpace = ColorSpace::Srgb;
+ config.addConfiguration(cfg);
+
+ StreamConfiguration &streamCfg = config.at(0);
+
+ /*
+ * YUV pixelformat with sRGB colorspace should have Y'CbCr encoding
+ * adjusted.
+ */
+ config.validate();
+ if (streamCfg.colorSpace->ycbcrEncoding == ColorSpace::YcbcrEncoding::None) {
+ cerr << "YUV format must have YCbCr encoding" << endl;
+ return TestFail;
+ }
+
+ /*
+ * For YUV pixelFormat, encoding should be picked up according
+ * to primaries and transfer function, if 'None' is specified.
+ */
+ streamCfg.pixelFormat = formats::YUV422;
+ streamCfg.colorSpace = ColorSpace(ColorSpace::Primaries::Rec2020,
+ ColorSpace::TransferFunction::Rec709,
+ ColorSpace::YcbcrEncoding::None,
+ ColorSpace::Range::Limited);
+ config.validate();
+ if (streamCfg.colorSpace->ycbcrEncoding != ColorSpace::YcbcrEncoding::Rec2020) {
+ cerr << "Failed to adjust colorspace Y'CbCr encoding according"
+ << " to primaries and transfer function" << endl;
+ return TestFail;
+ }
+
+ /* For RGB pixelFormat, Sycc colorspace should get adjusted to sRGB. */
+ streamCfg.pixelFormat = formats::RGB888;
+ streamCfg.colorSpace = ColorSpace::Sycc;
+ config.validate();
+ if (streamCfg.colorSpace != ColorSpace::Srgb) {
+ cerr << "RGB format's colorspace should be set to Srgb" << endl;
+ return TestFail;
+ }
+
+ /* Raw formats should always set colorspace to ColorSpace::Raw. */
+ streamCfg.pixelFormat = formats::SBGGR8;
+ streamCfg.colorSpace = ColorSpace::Rec709;
+ config.validate();
+ if (streamCfg.colorSpace != ColorSpace::Raw) {
+ cerr << "Raw format must always have Raw colorspace" << endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(StreamColorSpaceTest)
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 d83b5833..ceb4fa0f 100644
--- a/test/threads.cpp
+++ b/test/threads.cpp
@@ -2,13 +2,15 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * threads.cpp - Threads test
+ * Threads test
*/
#include <chrono>
#include <iostream>
#include <memory>
+#include <pthread.h>
#include <thread>
+#include <time.h>
#include <libcamera/base/thread.h>
@@ -35,6 +37,35 @@ private:
chrono::steady_clock::duration duration_;
};
+class CancelThread : public Thread
+{
+public:
+ CancelThread(bool &cancelled)
+ : cancelled_(cancelled)
+ {
+ }
+
+protected:
+ void run()
+ {
+ 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);
+
+ cancelled_ = false;
+ }
+
+private:
+ bool &cancelled_;
+};
+
class ThreadTest : public Test
{
protected:
@@ -118,6 +149,22 @@ protected:
return TestFail;
}
+ /* Test thread cleanup upon abnormal termination. */
+ bool cancelled = false;
+ bool finished = false;
+
+ thread = std::make_unique<CancelThread>(cancelled);
+ thread->finished.connect(this, [&finished]() { finished = true; });
+
+ thread->start();
+ thread->exit(0);
+ thread->wait(chrono::milliseconds(1000));
+
+ if (!cancelled || !finished) {
+ cout << "Cleanup failed upon abnormal termination" << endl;
+ return TestFail;
+ }
+
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
new file mode 100644
index 00000000..4ec7a1eb
--- /dev/null
+++ b/test/transform.cpp
@@ -0,0 +1,329 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2023, Ideas On Board Oy
+ *
+ * Transform and Orientation tests
+ */
+
+#include <iostream>
+
+#include <libcamera/orientation.h>
+#include <libcamera/transform.h>
+
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+
+class TransformTest : public Test
+{
+protected:
+ int run();
+};
+
+int TransformTest::run()
+{
+ /*
+ * RotationTestEntry collects two Orientation and one Transform that
+ * gets combined to validate that (o1 / o2 = T) and (o1 = o2 * T)
+ *
+ * o1 / o2 = t computes the Transform to apply to o2 to obtain o1
+ * o2 * t = o1 combines o2 with t by applying o2 first then t
+ *
+ * The comments on the (most complex) transform show how applying to
+ * an image with orientation o2 the Transform t allows to obtain o1.
+ *
+ * The image with basic rotation0 is assumed to be:
+ *
+ * AB
+ * CD
+ *
+ * And the Transform operators are:
+ *
+ * V = vertical flip
+ * H = horizontal flip
+ * T = transpose
+ *
+ * the operator '* (T|V)' applies V first then T.
+ */
+ static const struct RotationTestEntry {
+ Orientation o1;
+ Orientation o2;
+ Transform t;
+ } testEntries[] = {
+ /* Test identities transforms first. */
+ {
+ Orientation::Rotate0, Orientation::Rotate0,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate0Mirror, Orientation::Rotate0Mirror,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate180, Orientation::Rotate180,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate180Mirror, Orientation::Rotate180Mirror,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate90, Orientation::Rotate90,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate90Mirror, Orientation::Rotate90Mirror,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate270, Orientation::Rotate270,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate270Mirror, Orientation::Rotate270Mirror,
+ Transform::Identity,
+ },
+ /*
+ * Combine 0 and 180 degrees rotation as they're the most common
+ * ones.
+ */
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CD * (H|V) = BA AB
+ * BA CD CD
+ */
+ Orientation::Rotate0, Orientation::Rotate180,
+ Transform::Rot180,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * AB * (H|V) = CD DC
+ * CD AB BA
+ */
+ Orientation::Rotate180, Orientation::Rotate0,
+ Transform::Rot180
+ },
+ /* Test that transpositions are handled correctly. */
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * AB * (T|V) = CD CA
+ * CD AB DB
+ */
+ Orientation::Rotate90, Orientation::Rotate0,
+ Transform::Rot90,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CA * (T|H) = AC AB
+ * DB BD CD
+ */
+ Orientation::Rotate0, Orientation::Rotate90,
+ Transform::Rot270,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * AB * (T|H) = BA BD
+ * CD DC AC
+ */
+ Orientation::Rotate270, Orientation::Rotate0,
+ Transform::Rot270,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * BD * (T|V) = AC AB
+ * AC BD CD
+ */
+ Orientation::Rotate0, Orientation::Rotate270,
+ Transform::Rot90,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CD * (T|H) = DC DA
+ * BA AB CB
+ */
+ Orientation::Rotate90, Orientation::Rotate180,
+ Transform::Rot270,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * DA * (T|V) = CB CD
+ * CB DA BA
+ */
+ Orientation::Rotate180, Orientation::Rotate90,
+ Transform::Rot90,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CD * (T|V) = BA BC
+ * BA CD AD
+ */
+ Orientation::Rotate270, Orientation::Rotate180,
+ Transform::Rot90,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * BC * (T|H) = CB CD
+ * AD DA BA
+ */
+ Orientation::Rotate180, Orientation::Rotate270,
+ Transform::Rot270,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * DA * (V|H) = AD BC
+ * CB BC AD
+ */
+ Orientation::Rotate270, Orientation::Rotate90,
+ Transform::Rot180,
+ },
+ /* Test that mirroring is handled correctly. */
+ {
+ Orientation::Rotate0, Orientation::Rotate0Mirror,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate0Mirror, Orientation::Rotate0,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate180, Orientation::Rotate180Mirror,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate180Mirror, Orientation::Rotate180,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate90, Orientation::Rotate90Mirror,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate90Mirror, Orientation::Rotate90,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate270, Orientation::Rotate270Mirror,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate270Mirror, Orientation::Rotate270,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate0, Orientation::Rotate0Mirror,
+ Transform::HFlip
+ },
+ /*
+ * More exotic transforms which include Transpositions and
+ * mirroring.
+ */
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * BC * (V) = AD
+ * AD BC
+ */
+ Orientation::Rotate90Mirror, Orientation::Rotate270,
+ Transform::VFlip,
+ },
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * CB * (T) = CD
+ * DA BA
+ */
+ Orientation::Rotate180, Orientation::Rotate270Mirror,
+ Transform::Transpose,
+ },
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * AD * (T) = AB
+ * BC DC
+ */
+ Orientation::Rotate0, Orientation::Rotate90Mirror,
+ Transform::Transpose,
+ },
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * AD * (V) = BC
+ * BC AD
+ */
+ Orientation::Rotate270, Orientation::Rotate90Mirror,
+ Transform::VFlip,
+ },
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * DA * (V) = CB
+ * CB DA
+ */
+ Orientation::Rotate270Mirror, Orientation::Rotate90,
+ Transform::VFlip,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CB * (V|H) = BC AD
+ * DA AD BC
+ */
+ Orientation::Rotate90Mirror, Orientation::Rotate270Mirror,
+ Transform::Rot180,
+ },
+ };
+
+ for (const auto &entry : testEntries) {
+ Transform transform = entry.o1 / entry.o2;
+ if (transform != entry.t) {
+ cerr << "Failed to validate: " << entry.o1
+ << " / " << entry.o2
+ << " = " << transformToString(entry.t) << endl;
+ cerr << "Got back: "
+ << transformToString(transform) << endl;
+ return TestFail;
+ }
+
+ Orientation adjusted = entry.o2 * entry.t;
+ if (adjusted != entry.o1) {
+ cerr << "Failed to validate: " << entry.o2
+ << " * " << transformToString(entry.t)
+ << " = " << entry.o1 << endl;
+ cerr << "Got back: " << adjusted << endl;
+ return TestFail;
+ }
+ }
+
+ return TestPass;
+}
+
+TEST_REGISTER(TransformTest)
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 d65467b5..41af954b 100644
--- a/test/utils.cpp
+++ b/test/utils.cpp
@@ -2,11 +2,12 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * utils.cpp - Miscellaneous utility tests
+ * Miscellaneous utility tests
*/
#include <iostream>
#include <map>
+#include <optional>
#include <sstream>
#include <string>
#include <vector>
@@ -226,6 +227,14 @@ protected:
return TestFail;
}
+ const auto &split = utils::split(path, ":");
+ dirs = std::vector<std::string>{ split.begin(), split.end() };
+
+ if (dirs != elements) {
+ cerr << "utils::split() LegacyInputIterator test failed" << endl;
+ return TestFail;
+ }
+
/* utils::join() with conversion function test. */
std::vector<Size> sizes = { { 0, 0 }, { 100, 100 } };
s = utils::join(sizes, "/", [](const Size &size) {
diff --git a/test/v4l2_compat/meson.build b/test/v4l2_compat/meson.build
index 87809589..2691eacf 100644
--- a/test/v4l2_compat/meson.build
+++ b/test/v4l2_compat/meson.build
@@ -1,20 +1,29 @@
# SPDX-License-Identifier: CC0-1.0
-# If ASan is enabled, the link order runtime check will fail as v4l2-ctl and
-# v4l2-compliance are not linked to ASan. Skip the test in that case.
-#
-# TODO: Find a way to LD_PRELOAD the ASan dynamic library instead, in a
-# cross-platform way with support for both gcc and clang.
+if not is_variable('v4l2_compat')
+ subdir_done()
+endif
+
+# If ASan is enabled but the ASan runtime shared library is missing,
+# v4l2_compat_test.py won't be able to LD_PRELOAD it, resulting in a link order
+# runtime check failure as v4l2-ctl and v4l2-compliance are not linked to ASan.
+# Skip the test in that case.
-if get_option('b_sanitize').contains('address')
+if asan_runtime_missing
+ warning('Unable to get path to ASan runtime, v4l2_compat test disabled')
subdir_done()
endif
-if is_variable('v4l2_compat')
- v4l2_compat_test = files('v4l2_compat_test.py')
+v4l2_compat_test = files('v4l2_compat_test.py')
+v4l2_compat_args = []
- test('v4l2_compat_test', v4l2_compat_test,
- args : v4l2_compat,
- suite : 'v4l2_compat',
- timeout : 60)
+if asan_enabled
+ v4l2_compat_args += ['-s', asan_runtime]
endif
+
+v4l2_compat_args += [v4l2_compat]
+
+test('v4l2_compat_test', v4l2_compat_test,
+ args : v4l2_compat_args,
+ suite : 'v4l2_compat',
+ timeout : 60)
diff --git a/test/v4l2_compat/v4l2_compat_test.py b/test/v4l2_compat/v4l2_compat_test.py
index a77585fc..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
@@ -57,8 +57,8 @@ def extract_result(result):
return ret
-def test_v4l2_compliance(v4l2_compliance, v4l2_compat, device, base_driver):
- ret, output = run_with_stdout(v4l2_compliance, '-s', '-d', device, env={'LD_PRELOAD': v4l2_compat})
+def test_v4l2_compliance(v4l2_compliance, ld_preload, device, base_driver):
+ ret, output = run_with_stdout(v4l2_compliance, '-s', '-d', device, env={'LD_PRELOAD': ld_preload})
if ret < 0:
output.append(f'Test for {device} terminated due to signal {signal.Signals(-ret).name}')
return TestFail, output
@@ -82,13 +82,21 @@ def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--all', action='store_true',
help='Test all available cameras')
+ parser.add_argument('-s', '--sanitizer', type=str,
+ help='Path to the address sanitizer (ASan) runtime')
parser.add_argument('-v', '--verbose', action='store_true',
help='Make the output verbose')
parser.add_argument('v4l2_compat', type=str,
help='Path to v4l2-compat.so')
args = parser.parse_args(argv[1:])
- v4l2_compat = args.v4l2_compat
+ # Compute the LD_PRELOAD value by first loading ASan (if specified) and
+ # then the V4L2 compat layer.
+ ld_preload = []
+ if args.sanitizer:
+ ld_preload.append(args.sanitizer)
+ ld_preload.append(args.v4l2_compat)
+ ld_preload = ':'.join(ld_preload)
v4l2_compliance = shutil.which('v4l2-compliance')
if v4l2_compliance is None:
@@ -118,7 +126,7 @@ def main(argv):
failed = []
drivers_tested = {}
for device in dev_nodes:
- ret, out = run_with_stdout(v4l2_ctl, '-D', '-d', device, env={'LD_PRELOAD': v4l2_compat})
+ ret, out = run_with_stdout(v4l2_ctl, '-D', '-d', device, env={'LD_PRELOAD': ld_preload})
if ret < 0:
failed.append(device)
print(f'v4l2-ctl failed on {device} with v4l2-compat')
@@ -144,7 +152,7 @@ def main(argv):
continue
print(f'Testing {device} with {driver} driver... ', end='')
- ret, msg = test_v4l2_compliance(v4l2_compliance, v4l2_compat, device, driver)
+ ret, msg = test_v4l2_compliance(v4l2_compliance, ld_preload, device, driver)
if ret == TestFail:
failed.append(device)
print('failed')
diff --git a/test/v4l2_subdevice/meson.build b/test/v4l2_subdevice/meson.build
index d82be3c6..277f29bb 100644
--- a/test/v4l2_subdevice/meson.build
+++ b/test/v4l2_subdevice/meson.build
@@ -1,14 +1,14 @@
# SPDX-License-Identifier: CC0-1.0
v4l2_subdevice_tests = [
- ['list_formats', 'list_formats.cpp'],
- ['test_formats', 'test_formats.cpp'],
+ {'name': 'list_formats', 'sources': ['list_formats.cpp']},
+ {'name': 'test_formats', 'sources': ['test_formats.cpp']},
]
-foreach t : v4l2_subdevice_tests
- exe = executable(t[0], [t[1], 'v4l2_subdevice_test.cpp'],
+foreach test : v4l2_subdevice_tests
+ exe = executable(test['name'], test['sources'], 'v4l2_subdevice_test.cpp',
dependencies : libcamera_private,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'v4l2_subdevice', is_parallel : false)
+ test(test['name'], exe, suite : 'v4l2_subdevice', is_parallel : false)
endforeach
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/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/meson.build b/test/v4l2_videodevice/meson.build
index 7a26f53d..87ea4f96 100644
--- a/test/v4l2_videodevice/meson.build
+++ b/test/v4l2_videodevice/meson.build
@@ -3,22 +3,22 @@
# Tests are listed in order of complexity.
# They are not alphabetically sorted.
v4l2_videodevice_tests = [
- ['double_open', 'double_open.cpp'],
- ['controls', 'controls.cpp'],
- ['formats', 'formats.cpp'],
- ['dequeue_watchdog', 'dequeue_watchdog.cpp'],
- ['request_buffers', 'request_buffers.cpp'],
- ['buffer_cache', 'buffer_cache.cpp'],
- ['stream_on_off', 'stream_on_off.cpp'],
- ['capture_async', 'capture_async.cpp'],
- ['buffer_sharing', 'buffer_sharing.cpp'],
- ['v4l2_m2mdevice', 'v4l2_m2mdevice.cpp'],
+ {'name': 'double_open', 'sources': ['double_open.cpp']},
+ {'name': 'controls', 'sources': ['controls.cpp']},
+ {'name': 'formats', 'sources': ['formats.cpp']},
+ {'name': 'dequeue_watchdog', 'sources': ['dequeue_watchdog.cpp']},
+ {'name': 'request_buffers', 'sources': ['request_buffers.cpp']},
+ {'name': 'buffer_cache', 'sources': ['buffer_cache.cpp']},
+ {'name': 'stream_on_off', 'sources': ['stream_on_off.cpp']},
+ {'name': 'capture_async', 'sources': ['capture_async.cpp']},
+ {'name': 'buffer_sharing', 'sources': ['buffer_sharing.cpp']},
+ {'name': 'v4l2_m2mdevice', 'sources': ['v4l2_m2mdevice.cpp']},
]
-foreach t : v4l2_videodevice_tests
- exe = executable(t[0], [t[1], 'v4l2_videodevice_test.cpp'],
+foreach test : v4l2_videodevice_tests
+ exe = executable(test['name'], [test['sources'], 'v4l2_videodevice_test.cpp'],
dependencies : libcamera_private,
link_with : test_libraries,
include_directories : test_includes_internal)
- test(t[0], exe, suite : 'v4l2_videodevice', is_parallel : false)
+ test(test['name'], exe, suite : 'v4l2_videodevice', is_parallel : false)
endforeach
diff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp
index 852b853f..c45f581a 100644
--- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp
+++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp
@@ -95,6 +95,11 @@ protected:
V4L2VideoDevice *capture = vim2m_->capture();
V4L2VideoDevice *output = vim2m_->output();
+ if (capture->controls().empty() || output->controls().empty()) {
+ cerr << "VIM2M device has no control" << endl;
+ return TestFail;
+ }
+
V4L2DeviceFormat format = {};
if (capture->getFormat(&format)) {
cerr << "Failed to get capture format" << endl;
diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.cpp b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
index 125aafd6..1113cf5b 100644
--- a/test/v4l2_videodevice/v4l2_videodevice_test.cpp
+++ b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
@@ -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))
diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.h b/test/v4l2_videodevice/v4l2_videodevice_test.h
index d2de1a6d..b5871ce6 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
diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp
index 38f84823..81c82983 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>
@@ -24,16 +24,20 @@ using namespace std;
static const string testYaml =
"string: libcamera\n"
"double: 3.14159\n"
- "uint32_t: 100\n"
- "int32_t: -100\n"
+ "int8_t: -100\n"
+ "uint8_t: 100\n"
+ "int16_t: -1000\n"
+ "uint16_t: 1000\n"
+ "int32_t: -100000\n"
+ "uint32_t: 100000\n"
"size: [1920, 1080]\n"
"list:\n"
" - James\n"
" - Mary\n"
"dictionary:\n"
" a: 1\n"
- " b: 2\n"
" c: 3\n"
+ " b: 2\n"
"level1:\n"
" level2:\n"
" - [1, 2]\n"
@@ -72,309 +76,359 @@ protected:
return TestPass;
}
- int run()
+ enum class Type {
+ String,
+ Int8,
+ UInt8,
+ Int16,
+ UInt16,
+ Int32,
+ UInt32,
+ Double,
+ Size,
+ List,
+ Dictionary,
+ };
+
+ int testObjectType(const YamlObject &obj, const char *name, Type type)
{
- /* Test invalid YAML file */
- File file{ invalidYamlFile_ };
- if (!file.open(File::OpenModeFlag::ReadOnly)) {
- cerr << "Fail to open invalid YAML file" << std::endl;
- return TestFail;
- }
+ bool isList = type == Type::List || type == Type::Size;
+ bool isScalar = !isList && type != Type::Dictionary;
+ bool isInteger8 = type == Type::Int8 || type == Type::UInt8;
+ bool isInteger16 = type == Type::Int16 || type == Type::UInt16;
+ bool isInteger32 = type == Type::Int32 || type == Type::UInt32;
+ bool isIntegerUpTo16 = isInteger8 || isInteger16;
+ bool isIntegerUpTo32 = isIntegerUpTo16 || isInteger32;
+ bool isSigned = type == Type::Int8 || type == Type::Int16 ||
+ type == Type::Int32;
- std::unique_ptr<YamlObject> root = YamlParser::parse(file);
- if (root) {
- cerr << "Invalid YAML file parse successfully" << std::endl;
+ if ((isScalar && !obj.isValue()) || (!isScalar && obj.isValue())) {
+ std::cerr
+ << "Object " << name << " type mismatch when compared to "
+ << "value" << std::endl;
return TestFail;
}
- /* Test YAML file */
- file.close();
- file.setFileName(testYamlFile_);
- if (!file.open(File::OpenModeFlag::ReadOnly)) {
- cerr << "Fail to open test YAML file" << std::endl;
+ if ((isList && !obj.isList()) || (!isList && obj.isList())) {
+ std::cerr
+ << "Object " << name << " type mismatch when compared to "
+ << "list" << std::endl;
return TestFail;
}
- root = YamlParser::parse(file);
-
- if (!root) {
- cerr << "Fail to parse test YAML file: " << std::endl;
+ if ((type == Type::Dictionary && !obj.isDictionary()) ||
+ (type != Type::Dictionary && obj.isDictionary())) {
+ std::cerr
+ << "Object " << name << " type mismatch when compared to "
+ << "dictionary" << std::endl;
return TestFail;
}
- if (!root->isDictionary()) {
- cerr << "YAML root is not dictionary" << std::endl;
+ if (!isScalar && obj.get<std::string>()) {
+ std::cerr
+ << "Object " << name << " didn't fail to parse as "
+ << "string" << std::endl;
return TestFail;
}
- if (!root->contains("string")) {
- cerr << "Missing string object in YAML root" << std::endl;
+ if (!isInteger8 && obj.get<int8_t>()) {
+ std::cerr
+ << "Object " << name << " didn't fail to parse as "
+ << "int8_t" << std::endl;
return TestFail;
}
- if (!root->contains("double")) {
- cerr << "Missing double object in YAML root" << std::endl;
+ if ((!isInteger8 || isSigned) && obj.get<uint8_t>()) {
+ std::cerr
+ << "Object " << name << " didn't fail to parse as "
+ << "uint8_t" << std::endl;
return TestFail;
}
- if (!root->contains("int32_t")) {
- cerr << "Missing int32_t object in YAML root" << std::endl;
+ if (!isIntegerUpTo16 && obj.get<int16_t>()) {
+ std::cerr
+ << "Object " << name << " didn't fail to parse as "
+ << "int16_t" << std::endl;
return TestFail;
}
- if (!root->contains("uint32_t")) {
- cerr << "Missing uint32_t object in YAML root" << std::endl;
+ if ((!isIntegerUpTo16 || isSigned) && obj.get<uint16_t>()) {
+ std::cerr
+ << "Object " << name << " didn't fail to parse as "
+ << "uint16_t" << std::endl;
return TestFail;
}
- if (!root->contains("size")) {
- cerr << "Missing Size object in YAML root" << std::endl;
+ if (!isIntegerUpTo32 && obj.get<int32_t>()) {
+ std::cerr
+ << "Object " << name << " didn't fail to parse as "
+ << "int32_t" << std::endl;
return TestFail;
}
- if (!root->contains("list")) {
- cerr << "Missing list object in YAML root" << std::endl;
+ if ((!isIntegerUpTo32 || isSigned) && obj.get<uint32_t>()) {
+ std::cerr
+ << "Object " << name << " didn't fail to parse as "
+ << "uint32_t" << std::endl;
return TestFail;
}
- if (!root->contains("dictionary")) {
- cerr << "Missing dictionary object in YAML root" << std::endl;
+ if (!isIntegerUpTo32 && type != Type::Double && obj.get<double>()) {
+ std::cerr
+ << "Object " << name << " didn't fail to parse as "
+ << "double" << std::endl;
return TestFail;
}
- if (!root->contains("level1")) {
- cerr << "Missing leveled object in YAML root" << std::endl;
+ if (type != Type::Size && obj.get<Size>()) {
+ std::cerr
+ << "Object " << name << " didn't fail to parse as "
+ << "Size" << std::endl;
return TestFail;
}
- /* Test string object */
- bool ok;
- auto &strObj = (*root)["string"];
+ return TestPass;
+ }
- if (strObj.isDictionary()) {
- cerr << "String object parse as Dictionary" << std::endl;
- return TestFail;
- }
+ int testIntegerObject(const YamlObject &obj, const char *name, Type type,
+ int64_t value)
+ {
+ uint64_t unsignedValue = static_cast<uint64_t>(value);
+ std::string strValue = std::to_string(value);
+ bool isInteger8 = type == Type::Int8 || type == Type::UInt8;
+ bool isInteger16 = type == Type::Int16 || type == Type::UInt16;
+ bool isSigned = type == Type::Int8 || type == Type::Int16 ||
+ type == Type::Int32;
- if (strObj.isList()) {
- cerr << "String object parse as List" << std::endl;
- return TestFail;
- }
+ /* All integers can be parsed as strings or double. */
- if (strObj.get<string>("", &ok) != "libcamera" || !ok) {
- cerr << "String object parse as wrong content" << std::endl;
+ if (obj.get<string>().value_or("") != strValue ||
+ obj.get<string>("") != strValue) {
+ std::cerr
+ << "Object " << name << " failed to parse as "
+ << "string" << std::endl;
return TestFail;
}
- if (strObj.get<int32_t>(-1, &ok) != -1 || ok) {
- cerr << "String object parse as integer" << std::endl;
+ if (obj.get<double>().value_or(0.0) != value ||
+ obj.get<double>(0.0) != value) {
+ std::cerr
+ << "Object " << name << " failed to parse as "
+ << "double" << std::endl;
return TestFail;
}
- if (strObj.get<uint32_t>(1, &ok) != 1 || ok) {
- cerr << "String object parse as unsigned integer" << std::endl;
- return TestFail;
+ if (isInteger8) {
+ if (obj.get<int8_t>().value_or(0) != value ||
+ obj.get<int8_t>(0) != value) {
+ std::cerr
+ << "Object " << name << " failed to parse as "
+ << "int8_t" << std::endl;
+ return TestFail;
+ }
}
- if (strObj.get<double>(1.0, &ok) != 1.0 || ok) {
- cerr << "String object parse as double" << std::endl;
- return TestFail;
+ if (isInteger8 && !isSigned) {
+ if (obj.get<uint8_t>().value_or(0) != unsignedValue ||
+ obj.get<uint8_t>(0) != unsignedValue) {
+ std::cerr
+ << "Object " << name << " failed to parse as "
+ << "uint8_t" << std::endl;
+ return TestFail;
+ }
}
- if (strObj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) {
- cerr << "String object parse as Size" << std::endl;
- return TestFail;
+ if (isInteger8 || isInteger16) {
+ if (obj.get<int16_t>().value_or(0) != value ||
+ obj.get<int16_t>(0) != value) {
+ std::cerr
+ << "Object " << name << " failed to parse as "
+ << "int16_t" << std::endl;
+ return TestFail;
+ }
}
- /* Test int32_t object */
- auto &int32Obj = (*root)["int32_t"];
-
- if (int32Obj.isDictionary()) {
- cerr << "Integer object parse as Dictionary" << std::endl;
- return TestFail;
+ if ((isInteger8 || isInteger16) && !isSigned) {
+ if (obj.get<uint16_t>().value_or(0) != unsignedValue ||
+ obj.get<uint16_t>(0) != unsignedValue) {
+ std::cerr
+ << "Object " << name << " failed to parse as "
+ << "uint16_t" << std::endl;
+ return TestFail;
+ }
}
- if (int32Obj.isList()) {
- cerr << "Integer object parse as Integer" << std::endl;
+ if (obj.get<int32_t>().value_or(0) != value ||
+ obj.get<int32_t>(0) != value) {
+ std::cerr
+ << "Object " << name << " failed to parse as "
+ << "int32_t" << std::endl;
return TestFail;
}
- if (int32Obj.get<int32_t>(-100, &ok) != -100 || !ok) {
- cerr << "Integer object parse as wrong value" << std::endl;
- return TestFail;
+ if (!isSigned) {
+ if (obj.get<uint32_t>().value_or(0) != unsignedValue ||
+ obj.get<uint32_t>(0) != unsignedValue) {
+ std::cerr
+ << "Object " << name << " failed to parse as "
+ << "uint32_t" << std::endl;
+ return TestFail;
+ }
}
- if (int32Obj.get<string>("", &ok) != "-100" || !ok) {
- cerr << "Integer object fail to parse as string" << std::endl;
- return TestFail;
- }
+ return TestPass;
+ }
- if (int32Obj.get<double>(1.0, &ok) != -100.0 || !ok) {
- cerr << "Integer object fail to parse as double" << std::endl;
+ int run()
+ {
+ /* Test invalid YAML file */
+ File file{ invalidYamlFile_ };
+ if (!file.open(File::OpenModeFlag::ReadOnly)) {
+ cerr << "Fail to open invalid YAML file" << std::endl;
return TestFail;
}
- if (int32Obj.get<uint32_t>(1, &ok) != 1 || ok) {
- cerr << "Negative integer object parse as unsigned integer" << std::endl;
+ std::unique_ptr<YamlObject> root = YamlParser::parse(file);
+ if (root) {
+ cerr << "Invalid YAML file parse successfully" << std::endl;
return TestFail;
}
- if (int32Obj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) {
- cerr << "Integer object parse as Size" << std::endl;
+ /* Test YAML file */
+ file.close();
+ file.setFileName(testYamlFile_);
+ if (!file.open(File::OpenModeFlag::ReadOnly)) {
+ cerr << "Fail to open test YAML file" << std::endl;
return TestFail;
}
- /* Test uint32_t object */
- auto &uint32Obj = (*root)["uint32_t"];
+ root = YamlParser::parse(file);
- if (uint32Obj.isDictionary()) {
- cerr << "Unsigned integer object parse as Dictionary" << std::endl;
+ if (!root) {
+ cerr << "Fail to parse test YAML file: " << std::endl;
return TestFail;
}
- if (uint32Obj.isList()) {
- cerr << "Unsigned integer object parse as List" << std::endl;
+ if (!root->isDictionary()) {
+ cerr << "YAML root is not dictionary" << std::endl;
return TestFail;
}
- if (uint32Obj.get<int32_t>(-1, &ok) != 100 || !ok) {
- cerr << "Unsigned integer object fail to parse as integer" << std::endl;
- return TestFail;
- }
+ std::vector<const char *> rootElemNames = {
+ "string", "double", "int8_t", "uint8_t", "int16_t",
+ "uint16_t", "int32_t", "uint32_t", "size", "list",
+ "dictionary", "level1",
+ };
- if (uint32Obj.get<string>("", &ok) != "100" || !ok) {
- cerr << "Unsigned integer object fail to parse as string" << std::endl;
- return TestFail;
+ for (const char *name : rootElemNames) {
+ if (!root->contains(name)) {
+ cerr << "Missing " << name << " object in YAML root"
+ << std::endl;
+ return TestFail;
+ }
}
- if (uint32Obj.get<double>(1.0, &ok) != 100.0 || !ok) {
- cerr << "Unsigned integer object fail to parse as double" << std::endl;
- return TestFail;
- }
+ /* Test string object */
+ auto &strObj = (*root)["string"];
- if (uint32Obj.get<uint32_t>(100, &ok) != 100 || !ok) {
- cerr << "Unsigned integer object parsed as wrong value" << std::endl;
+ if (testObjectType(strObj, "string", Type::String) != TestPass)
return TestFail;
- }
- if (uint32Obj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) {
- cerr << "Unsigned integer object parsed as Size" << std::endl;
+ if (strObj.get<string>().value_or("") != "libcamera" ||
+ strObj.get<string>("") != "libcamera") {
+ cerr << "String object parse as wrong content" << std::endl;
return TestFail;
}
- /* Test double value */
- auto &doubleObj = (*root)["double"];
+ /* Test int8_t object */
+ auto &int8Obj = (*root)["int8_t"];
- if (doubleObj.isDictionary()) {
- cerr << "Double object parse as Dictionary" << std::endl;
+ if (testObjectType(int8Obj, "int8_t", Type::Int8) != TestPass)
return TestFail;
- }
- if (doubleObj.isList()) {
- cerr << "Double object parse as List" << std::endl;
+ if (testIntegerObject(int8Obj, "int8_t", Type::Int8, -100) != TestPass)
return TestFail;
- }
- if (doubleObj.get<string>("", &ok) != "3.14159" || !ok) {
- cerr << "Double object fail to parse as string" << std::endl;
- return TestFail;
- }
+ /* Test uint8_t object */
+ auto &uint8Obj = (*root)["uint8_t"];
- if (doubleObj.get<double>(1.0, &ok) != 3.14159 || !ok) {
- cerr << "Double object parse as wrong value" << std::endl;
+ if (testObjectType(uint8Obj, "uint8_t", Type::UInt8) != TestPass)
return TestFail;
- }
- if (doubleObj.get<int32_t>(-1, &ok) != -1 || ok) {
- cerr << "Double object parse as integer" << std::endl;
+ if (testIntegerObject(uint8Obj, "uint8_t", Type::UInt8, 100) != TestPass)
return TestFail;
- }
- if (doubleObj.get<uint32_t>(1, &ok) != 1 || ok) {
- cerr << "Double object parse as unsigned integer" << std::endl;
+ /* Test int16_t object */
+ auto &int16Obj = (*root)["int16_t"];
+
+ if (testObjectType(int16Obj, "int16_t", Type::Int16) != TestPass)
return TestFail;
- }
- if (doubleObj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) {
- cerr << "Double object parse as Size" << std::endl;
+ if (testIntegerObject(int16Obj, "int16_t", Type::Int16, -1000) != TestPass)
return TestFail;
- }
- /* Test Size value */
- auto &sizeObj = (*root)["size"];
+ /* Test uint16_t object */
+ auto &uint16Obj = (*root)["uint16_t"];
- if (sizeObj.isDictionary()) {
- cerr << "Size object parse as Dictionary" << std::endl;
+ if (testObjectType(uint16Obj, "uint16_t", Type::UInt16) != TestPass)
return TestFail;
- }
- if (!sizeObj.isList()) {
- cerr << "Size object parse as List" << std::endl;
+ if (testIntegerObject(uint16Obj, "uint16_t", Type::UInt16, 1000) != TestPass)
return TestFail;
- }
- if (sizeObj.get<string>("", &ok) != "" || ok) {
- cerr << "Size object parse as string" << std::endl;
- return TestFail;
- }
+ /* Test int32_t object */
+ auto &int32Obj = (*root)["int32_t"];
- if (sizeObj.get<double>(1.0, &ok) != 1.0 || ok) {
- cerr << "Size object parse as double" << std::endl;
+ if (testObjectType(int32Obj, "int32_t", Type::Int32) != TestPass)
return TestFail;
- }
- if (sizeObj.get<int32_t>(-1, &ok) != -1 || ok) {
- cerr << "Size object parse as integer" << std::endl;
+ if (testIntegerObject(int32Obj, "int32_t", Type::Int32, -100000) != TestPass)
return TestFail;
- }
- if (sizeObj.get<uint32_t>(1, &ok) != 1 || ok) {
- cerr << "Size object parse as unsigned integer" << std::endl;
+ /* Test uint32_t object */
+ auto &uint32Obj = (*root)["uint32_t"];
+
+ if (testObjectType(uint32Obj, "uint32_t", Type::UInt32) != TestPass)
return TestFail;
- }
- if (sizeObj.get<Size>(Size(0, 0), &ok) != Size(1920, 1080) || !ok) {
- cerr << "Size object parse as wrong value" << std::endl;
+ if (testIntegerObject(uint32Obj, "uint32_t", Type::UInt32, 100000) != TestPass)
return TestFail;
- }
- /* Test list object */
- auto &listObj = (*root)["list"];
+ /* Test double value */
+ auto &doubleObj = (*root)["double"];
- if (listObj.isDictionary()) {
- cerr << "List object parse as Dictionary" << std::endl;
+ if (testObjectType(doubleObj, "double", Type::Double) != TestPass)
return TestFail;
- }
- if (!listObj.isList()) {
- cerr << "List object fail to parse as List" << std::endl;
+ if (doubleObj.get<string>().value_or("") != "3.14159" ||
+ doubleObj.get<string>("") != "3.14159") {
+ cerr << "Double object fail to parse as string" << std::endl;
return TestFail;
}
- if (listObj.get<string>("", &ok) != "" || ok) {
- cerr << "List object parse as string" << std::endl;
+ if (doubleObj.get<double>().value_or(0.0) != 3.14159 ||
+ doubleObj.get<double>(0.0) != 3.14159) {
+ cerr << "Double object parse as wrong value" << std::endl;
return TestFail;
}
- if (listObj.get<double>(1.0, &ok) != 1.0 || ok) {
- cerr << "List object parse as double" << std::endl;
- return TestFail;
- }
+ /* Test Size value */
+ auto &sizeObj = (*root)["size"];
- if (listObj.get<int32_t>(-1, &ok) != -1 || ok) {
- cerr << "List object parse as integer" << std::endl;
+ if (testObjectType(sizeObj, "size", Type::Size) != TestPass)
return TestFail;
- }
- if (listObj.get<uint32_t>(1, &ok) != 1 || ok) {
- cerr << "List object parse as unsigne integer" << std::endl;
+ if (sizeObj.get<Size>().value_or(Size(0, 0)) != Size(1920, 1080) ||
+ sizeObj.get<Size>(Size(0, 0)) != Size(1920, 1080)) {
+ cerr << "Size object parse as wrong value" << std::endl;
return TestFail;
}
- if (listObj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) {
- cerr << "String list object parse as Size" << std::endl;
+ /* Test list object */
+ auto &listObj = (*root)["list"];
+
+ if (testObjectType(listObj, "list", Type::List) != TestPass)
return TestFail;
- }
static constexpr std::array<const char *, 2> listValues{
"James",
@@ -414,45 +468,13 @@ protected:
/* Test dictionary object */
auto &dictObj = (*root)["dictionary"];
- if (!dictObj.isDictionary()) {
- cerr << "Dictionary object fail to parse as Dictionary" << std::endl;
+ if (testObjectType(dictObj, "dictionary", Type::Dictionary) != TestPass)
return TestFail;
- }
- if (dictObj.isList()) {
- cerr << "Dictionary object parse as List" << std::endl;
- return TestFail;
- }
-
- if (dictObj.get<string>("", &ok) != "" || ok) {
- cerr << "Dictionary object parse as string" << std::endl;
- return TestFail;
- }
-
- if (dictObj.get<double>(1.0, &ok) != 1.0 || ok) {
- cerr << "Dictionary object parse as double" << std::endl;
- return TestFail;
- }
-
- if (dictObj.get<int32_t>(-1, &ok) != -1 || ok) {
- cerr << "Dictionary object parse as integer" << std::endl;
- return TestFail;
- }
-
- if (dictObj.get<uint32_t>(1, &ok) != 1 || ok) {
- cerr << "Dictionary object parse as unsigned integer" << std::endl;
- return TestFail;
- }
-
- if (dictObj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) {
- cerr << "Dictionary object parse as Size" << std::endl;
- return TestFail;
- }
-
- std::map<std::string, int> dictValues{ {
+ static constexpr std::array<std::pair<const char *, int>, 3> dictValues{ {
{ "a", 1 },
- { "b", 2 },
{ "c", 3 },
+ { "b", 2 },
} };
size_t dictSize = dictValues.size();
@@ -470,8 +492,8 @@ protected:
return TestFail;
}
- const auto item = dictValues.find(key);
- if (item == dictValues.end()) {
+ const auto &item = dictValues[i];
+ if (item.first != key) {
std::cerr << "Dictionary key " << i << " has wrong value"
<< std::endl;
return TestFail;
@@ -483,17 +505,12 @@ protected:
return TestFail;
}
- if (elem.get<int32_t>(0) != item->second) {
+ if (elem.get<int32_t>(0) != item.second) {
std::cerr << "Dictionary element " << i << " has wrong value"
<< std::endl;
return TestFail;
}
- /*
- * Erase the item to make sure that each iteration
- * produces a different value.
- */
- dictValues.erase(item);
i++;
}
@@ -524,6 +541,12 @@ protected:
return TestFail;
}
+ const auto &values = firstElement.getList<uint16_t>();
+ if (!values || values->size() != 2 || (*values)[0] != 1 || (*values)[1] != 2) {
+ cerr << "getList() failed to return correct vector" << std::endl;
+ return TestFail;
+ }
+
auto &secondElement = level2Obj[1];
if (!secondElement.isDictionary() ||
!secondElement.contains("one") ||