summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/gstreamer/gstreamer_memory_lifetime_test.cpp90
-rw-r--r--test/gstreamer/meson.build7
-rw-r--r--test/utils.cpp17
3 files changed, 112 insertions, 2 deletions
diff --git a/test/gstreamer/gstreamer_memory_lifetime_test.cpp b/test/gstreamer/gstreamer_memory_lifetime_test.cpp
new file mode 100644
index 00000000..1738cf56
--- /dev/null
+++ b/test/gstreamer/gstreamer_memory_lifetime_test.cpp
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024, Nicolas Dufresne
+ *
+ * gstreamer_memory_lifetime_test.cpp - GStreamer memory lifetime test
+ */
+
+#include <iostream>
+#include <unistd.h>
+
+#include <gst/app/app.h>
+#include <gst/gst.h>
+
+#include "gstreamer_test.h"
+#include "test.h"
+
+using namespace std;
+
+class GstreamerMemoryLifetimeTest : public GstreamerTest, public Test
+{
+public:
+ GstreamerMemoryLifetimeTest()
+ : GstreamerTest()
+ {
+ }
+
+protected:
+ int init() override
+ {
+ if (status_ != TestPass)
+ return status_;
+
+ appsink_ = gst_element_factory_make("appsink", nullptr);
+ if (!appsink_) {
+ g_printerr("Your installation is missing 'appsink'\n");
+ return TestFail;
+ }
+ g_object_ref_sink(appsink_);
+
+ return createPipeline();
+ }
+
+ int run() override
+ {
+ /* Build the pipeline */
+ gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, appsink_, nullptr);
+ if (gst_element_link(libcameraSrc_, appsink_) != TRUE) {
+ g_printerr("Elements could not be linked.\n");
+ return TestFail;
+ }
+
+ if (startPipeline() != TestPass)
+ return TestFail;
+
+ sample_ = gst_app_sink_try_pull_sample(GST_APP_SINK(appsink_), GST_SECOND * 5);
+ if (!sample_) {
+ /* Failed to obtain a sample. Abort the test */
+ gst_element_set_state(pipeline_, GST_STATE_NULL);
+ return TestFail;
+ }
+
+ /*
+ * Keep the sample referenced and set the pipeline state to
+ * NULL. This causes the libcamerasrc element to synchronously
+ * release resources it holds. The sample will be released
+ * later in cleanup().
+ *
+ * The test case verifies that libcamerasrc keeps alive long
+ * enough all the resources that are needed until memory
+ * allocated for frames gets freed. We return TestPass at this
+ * stage, and any use-after-free will be caught by the test
+ * crashing in cleanup().
+ */
+ gst_element_set_state(pipeline_, GST_STATE_NULL);
+
+ return TestPass;
+ }
+
+ void cleanup() override
+ {
+ g_clear_pointer(&sample_, gst_sample_unref);
+ g_clear_object(&appsink_);
+ }
+
+private:
+ GstElement *appsink_;
+ GstSample *sample_;
+};
+
+TEST_REGISTER(GstreamerMemoryLifetimeTest)
diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build
index 37ad125e..e066c582 100644
--- a/test/gstreamer/meson.build
+++ b/test/gstreamer/meson.build
@@ -8,8 +8,10 @@ gstreamer_tests = [
{'name': 'single_stream_test', 'sources': ['gstreamer_single_stream_test.cpp']},
{'name': 'multi_stream_test', 'sources': ['gstreamer_multi_stream_test.cpp']},
{'name': 'device_provider_test', 'sources': ['gstreamer_device_provider_test.cpp']},
+ {'name': 'memory_lifetime_test', 'sources': ['gstreamer_memory_lifetime_test.cpp']},
]
gstreamer_dep = dependency('gstreamer-1.0', required : true)
+gstapp_dep = dependency('gstreamer-app-1.0', required : true)
gstreamer_test_args = []
@@ -20,9 +22,10 @@ endif
foreach test : gstreamer_tests
exe = executable(test['name'], test['sources'], 'gstreamer_test.cpp',
cpp_args : gstreamer_test_args,
- dependencies : [libcamera_private, gstreamer_dep],
+ dependencies : [libcamera_private, gstreamer_dep, gstapp_dep],
link_with : test_libraries,
include_directories : test_includes_internal)
- test(test['name'], exe, suite : 'gstreamer', is_parallel : false, env : gst_env)
+ test(test['name'], exe, suite : 'gstreamer', is_parallel : false,
+ env : gst_env, should_fail : test.get('should_fail', false))
endforeach
diff --git a/test/utils.cpp b/test/utils.cpp
index 41af954b..d25475cb 100644
--- a/test/utils.cpp
+++ b/test/utils.cpp
@@ -176,6 +176,14 @@ protected:
std::ostringstream os;
std::string ref;
+ os << utils::hex(static_cast<int8_t>(0x42)) << " ";
+ ref += "0x42 ";
+ os << utils::hex(static_cast<uint8_t>(0x42)) << " ";
+ ref += "0x42 ";
+ os << utils::hex(static_cast<int16_t>(0x42)) << " ";
+ ref += "0x0042 ";
+ os << utils::hex(static_cast<uint16_t>(0x42)) << " ";
+ ref += "0x0042 ";
os << utils::hex(static_cast<int32_t>(0x42)) << " ";
ref += "0x00000042 ";
os << utils::hex(static_cast<uint32_t>(0x42)) << " ";
@@ -184,6 +192,15 @@ protected:
ref += "0x0000000000000042 ";
os << utils::hex(static_cast<uint64_t>(0x42)) << " ";
ref += "0x0000000000000042 ";
+
+ os << utils::hex(static_cast<int8_t>(0x42), 6) << " ";
+ ref += "0x000042 ";
+ os << utils::hex(static_cast<uint8_t>(0x42), 1) << " ";
+ ref += "0x42 ";
+ os << utils::hex(static_cast<int16_t>(0x42), 6) << " ";
+ ref += "0x000042 ";
+ os << utils::hex(static_cast<uint16_t>(0x42), 1) << " ";
+ ref += "0x42 ";
os << utils::hex(static_cast<int32_t>(0x42), 4) << " ";
ref += "0x0042 ";
os << utils::hex(static_cast<uint32_t>(0x42), 1) << " ";