diff options
Diffstat (limited to 'test/gstreamer')
-rw-r--r-- | test/gstreamer/gstreamer_memory_lifetime_test.cpp | 90 | ||||
-rw-r--r-- | test/gstreamer/gstreamer_test.cpp | 6 | ||||
-rw-r--r-- | test/gstreamer/meson.build | 14 |
3 files changed, 108 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/gstreamer_test.cpp b/test/gstreamer/gstreamer_test.cpp index e8119b85..a15fef0e 100644 --- a/test/gstreamer/gstreamer_test.cpp +++ b/test/gstreamer/gstreamer_test.cpp @@ -9,12 +9,17 @@ #include <libcamera/base/utils.h> +#if HAVE_ASAN +#include <sanitizer/asan_interface.h> +#endif + #include "gstreamer_test.h" #include "test.h" using namespace std; +#if HAVE_ASAN extern "C" { const char *__asan_default_options() { @@ -26,6 +31,7 @@ const char *__asan_default_options() return "detect_leaks=false"; } } +#endif GstreamerTest::GstreamerTest(unsigned int numStreams) : pipeline_(nullptr), libcameraSrc_(nullptr) diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build index f3ba5a23..e066c582 100644 --- a/test/gstreamer/meson.build +++ b/test/gstreamer/meson.build @@ -8,14 +8,24 @@ gstreamer_tests = [ {'name': 'single_stream_test', 'sources': ['gstreamer_single_stream_test.cpp']}, {'name': 'multi_stream_test', 'sources': ['gstreamer_multi_stream_test.cpp']}, {'name': 'device_provider_test', 'sources': ['gstreamer_device_provider_test.cpp']}, + {'name': 'memory_lifetime_test', 'sources': ['gstreamer_memory_lifetime_test.cpp']}, ] gstreamer_dep = dependency('gstreamer-1.0', required : true) +gstapp_dep = dependency('gstreamer-app-1.0', required : true) + +gstreamer_test_args = [] + +if asan_enabled + gstreamer_test_args += ['-D', 'HAVE_ASAN=1'] +endif foreach test : gstreamer_tests exe = executable(test['name'], test['sources'], 'gstreamer_test.cpp', - dependencies : [libcamera_private, gstreamer_dep], + cpp_args : gstreamer_test_args, + dependencies : [libcamera_private, gstreamer_dep, gstapp_dep], link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'gstreamer', is_parallel : false, env : gst_env) + test(test['name'], exe, suite : 'gstreamer', is_parallel : false, + env : gst_env, should_fail : test.get('should_fail', false)) endforeach |