[gjs/wip/3v1n0/toggle-queue-tests: 1/4] tests: Move internal API tests into a different test binary
- From: Marco Trevisan <marcotrevi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/3v1n0/toggle-queue-tests: 1/4] tests: Move internal API tests into a different test binary
- Date: Sun, 16 May 2021 14:56:46 +0000 (UTC)
commit fa24e37d0a813581a4d76038829c663407298395
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date: Sun May 16 16:50:18 2021 +0200
tests: Move internal API tests into a different test binary
installed-tests/js/libgjstesttools/meson.build | 3 +
meson.build | 36 +-
test/gjs-test-toggle-queue.cpp | 688 +++++++++++++++++++++++++
test/gjs-tests-internal.cpp | 25 +
test/gjs-tests.cpp | 3 -
test/meson.build | 36 +-
6 files changed, 766 insertions(+), 25 deletions(-)
---
diff --git a/installed-tests/js/libgjstesttools/meson.build b/installed-tests/js/libgjstesttools/meson.build
index 036d7076..2e57483a 100644
--- a/installed-tests/js/libgjstesttools/meson.build
+++ b/installed-tests/js/libgjstesttools/meson.build
@@ -17,3 +17,6 @@ gjstest_tools_gir = gnome.generate_gir(libgjstesttools,
install: get_option('installed_tests'), install_dir_gir: false,
install_dir_typelib: installed_tests_execdir)
gjstest_tools_typelib = gjstest_tools_gir[1]
+libgjstesttools_dep = declare_dependency(
+ link_with: libgjstesttools,
+ include_directories: include_directories('.'))
diff --git a/meson.build b/meson.build
index 8d11ec84..2f1a03c7 100644
--- a/meson.build
+++ b/meson.build
@@ -496,12 +496,20 @@ if host_machine.system() == 'windows'
libgjs_cpp_args += ['-DWIN32', '-DXP_WIN']
endif
+base_build_dep = declare_dependency(
+ compile_args: libgjs_cpp_args,
+ dependencies: libgjs_dependencies)
+
libgjs_jsapi = static_library(meson.project_name() + '-jsapi',
libgjs_jsapi_sources, probes_header, probes_objfile,
- cpp_args: libgjs_cpp_args,
- dependencies: libgjs_dependencies,
+ dependencies: base_build_dep,
install: false)
+libgjs_internal = static_library(meson.project_name() + '-internal',
+ libgjs_sources, probes_header, probes_objfile,
+ dependencies: base_build_dep,
+ link_with: libgjs_jsapi)
+
link_args = []
symbol_map = files('libgjs.map')
symbol_list = files('libgjs.symbols')
@@ -513,12 +521,10 @@ link_args += cxx.get_supported_link_arguments([
])
libgjs = shared_library(meson.project_name(),
- libgjs_sources, libgjs_private_sources, module_resource_srcs,
- probes_header, probes_objfile,
- cpp_args: libgjs_cpp_args,
+ sources: [ libgjs_private_sources, module_resource_srcs ],
link_args: link_args, link_depends: [symbol_map, symbol_list],
- link_with: libgjs_jsapi,
- dependencies: libgjs_dependencies,
+ link_whole: libgjs_internal,
+ dependencies: base_build_dep,
version: '0.0.0', soversion: '0',
gnu_symbol_visibility: 'hidden',
install: true)
@@ -527,7 +533,7 @@ install_headers(gjs_public_headers, subdir: api_name / 'gjs')
# Allow using libgjs as a subproject
libgjs_dep = declare_dependency(link_with: [libgjs, libgjs_jsapi],
- dependencies: libgjs_dependencies, include_directories: top_include)
+ dependencies: base_build_dep, include_directories: top_include)
### Build GjsPrivate introspection library #####################################
@@ -543,7 +549,6 @@ gjs_private_typelib = gjs_private_gir[1]
gjs_console_srcs = ['gjs/console.cpp']
gjs_console = executable('gjs-console', gjs_console_srcs,
- cpp_args: libgjs_cpp_args,
dependencies: libgjs_dep, install: true)
meson.add_install_script('build/symlink-gjs.py', get_option('bindir'))
@@ -585,6 +590,7 @@ libgjs_test_tools_builddir = js_tests_builddir / 'libgjstesttools'
tests_environment.set('TOP_BUILDDIR', meson.build_root())
tests_environment.set('GJS_USE_UNINSTALLED_FILES', '1')
tests_environment.set('GJS_PATH', '')
+tests_environment.set('GJS_DEBUG_OUTPUT', 'stderr')
tests_environment.prepend('GI_TYPELIB_PATH', meson.current_build_dir(),
js_tests_builddir, libgjs_test_tools_builddir)
tests_environment.prepend('LD_LIBRARY_PATH', meson.current_build_dir(),
@@ -624,18 +630,18 @@ endif
### Tests and test setups ######################################################
-# Note: The test program in test/ needs to be ported
-# to Windows before we can build it on Windows.
-if host_machine.system() != 'windows'
- subdir('test')
-endif
-
if not get_option('skip_gtk_tests')
have_gtk4 = dependency('gtk4', required: false).found()
endif
subdir('installed-tests')
+# Note: The test program in test/ needs to be ported
+# to Windows before we can build it on Windows.
+if host_machine.system() != 'windows'
+ subdir('test')
+endif
+
valgrind_environment = environment()
valgrind_environment.set('G_SLICE', 'always-malloc,debug-blocks')
valgrind_environment.set('G_DEBUG',
diff --git a/test/gjs-test-toggle-queue.cpp b/test/gjs-test-toggle-queue.cpp
new file mode 100644
index 00000000..8b25b2c2
--- /dev/null
+++ b/test/gjs-test-toggle-queue.cpp
@@ -0,0 +1,688 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2021 Canonical, Ltd.
+// SPDX-FileContributor: Marco Trevisan <marco trevisan canonical com>
+
+#include <config.h>
+
+#include <algorithm> // for copy
+#include <atomic>
+#include <chrono>
+#include <deque>
+#include <memory>
+#include <thread>
+#include <tuple> // for tie
+#include <utility> // for pair
+
+#include <girepository.h>
+#include <glib-object.h>
+#include <glib.h>
+
+#include <js/GCAPI.h> // for JS_GC
+#include <js/TypeDecls.h>
+
+#include "gi/object.h"
+#include "gi/toggle.h"
+#include "gjs/context.h"
+#include "gjs/jsapi-util.h"
+#include "installed-tests/js/libgjstesttools/gjs-test-tools.h"
+#include "test/gjs-test-utils.h"
+
+namespace Gjs {
+namespace Test {
+
+static GMutex s_gc_lock;
+static GCond s_gc_finished;
+static std::atomic_int s_gc_counter;
+static std::deque<std::pair<::ObjectInstance*, ::ToggleQueue::Direction>>
+ s_toggle_history;
+
+struct ObjectInstance : ::ObjectInstance {
+ using ::ObjectInstance::ensure_uses_toggle_ref;
+ using ::ObjectInstance::new_for_gobject;
+ using ::ObjectInstance::wrapper_is_rooted;
+};
+
+struct ToggleQueue {
+ static decltype(::ToggleQueue::get_default()) get_default() {
+ return ::ToggleQueue::get_default();
+ }
+ static void reset_queue() {
+ auto tq = get_default();
+ tq->m_shutdown = false;
+ g_clear_handle_id(&tq->m_idle_id, g_source_remove);
+ tq->q.clear();
+ }
+ static decltype(::ToggleQueue::q) queue() { return get_default()->q; }
+ static ::ToggleQueue::Handler handler() {
+ return get_default()->m_toggle_handler;
+ }
+};
+
+namespace TQ {
+
+static void on_gc(JSContext*, JSGCStatus status, JS::GCReason, void*) {
+ if (status != JSGC_END)
+ return;
+
+ g_mutex_lock(&s_gc_lock);
+ s_gc_counter.fetch_add(1);
+ g_cond_broadcast(&s_gc_finished);
+ g_mutex_unlock(&s_gc_lock);
+}
+
+static void setup(GjsUnitTestFixture* fx, const void*) {
+ g_irepository_prepend_search_path(g_getenv("TOP_BUILDDIR"));
+ gjs_test_tools_init();
+ gjs_unit_test_fixture_setup(fx, nullptr);
+ JS_SetGCCallback(fx->cx, on_gc, fx);
+
+ GjsAutoError error;
+ int code;
+
+ const char* gi_initializer = "imports.gi;";
+ g_assert_true(gjs_context_eval(fx->gjs_context, gi_initializer, -1,
+ "<gjs-test-toggle>", &code, error.out()));
+ g_assert_no_error(error);
+}
+
+static void wait_for_gc(GjsUnitTestFixture* fx) {
+ int count = s_gc_counter.load();
+
+ JS_GC(fx->cx);
+
+ g_mutex_lock(&s_gc_lock);
+ while (count == s_gc_counter.load()) {
+ g_cond_wait(&s_gc_finished, &s_gc_lock);
+ }
+ g_mutex_unlock(&s_gc_lock);
+}
+
+static void teardown(GjsUnitTestFixture* fx, const void*) {
+ for (auto pair : s_toggle_history)
+ ToggleQueue::get_default()->cancel(pair.first);
+
+ s_toggle_history.clear();
+ gjs_unit_test_fixture_teardown(fx, nullptr);
+
+ g_assert_true(ToggleQueue::queue().empty());
+ ToggleQueue::reset_queue();
+ gjs_test_tools_reset();
+}
+
+} // namespace TQ
+
+static ::ObjectInstance* new_test_gobject(GjsUnitTestFixture* fx) {
+ GjsAutoUnref<GObject> gobject(
+ G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr)));
+ auto* object = ObjectInstance::new_for_gobject(fx->cx, gobject);
+ static_cast<ObjectInstance*>(object)->ensure_uses_toggle_ref(fx->cx);
+ return object;
+}
+
+static void wait_for(int interval) {
+ GjsAutoPointer<GMainLoop, GMainLoop, g_main_loop_unref> loop(
+ g_main_loop_new(nullptr, false));
+ g_timeout_add_full(
+ G_PRIORITY_LOW, interval,
+ [](void* data) {
+ g_main_loop_quit(static_cast<GMainLoop*>(data));
+ return G_SOURCE_REMOVE;
+ },
+ loop, nullptr);
+ g_main_loop_run(loop);
+}
+
+static void toggles_handler(::ObjectInstance* object,
+ ::ToggleQueue::Direction direction) {
+ s_toggle_history.emplace_back(object, direction);
+}
+
+static void test_toggle_queue_unlock_empty(GjsUnitTestFixture*, const void*) {
+ assert_equal(ToggleQueue::get_default()->cancel(nullptr), false, false);
+}
+
+static void test_toggle_queue_unlock_same_thread(GjsUnitTestFixture*,
+ const void*) {
+ auto tq = ToggleQueue::get_default();
+ assert_equal(tq->cancel(nullptr), false, false);
+ assert_equal(ToggleQueue::get_default()->cancel(nullptr), false, false);
+}
+
+static void test_toggle_blocks_other_thread(GjsUnitTestFixture*, const void*) {
+ struct LockedQueue {
+ decltype(ToggleQueue::get_default()) tq = ToggleQueue::get_default();
+ };
+
+ auto locked_queue = std::make_unique<LockedQueue>();
+ assert_equal(locked_queue->tq->cancel(nullptr), false, false);
+
+ std::atomic_bool other_thread_running(false);
+ std::atomic_bool accessed_from_other_thread(false);
+ auto th = std::thread([&accessed_from_other_thread, &other_thread_running] {
+ other_thread_running.store(true);
+ auto locked_queue = std::make_unique<LockedQueue>();
+ accessed_from_other_thread.store(true);
+ assert_equal(ToggleQueue::get_default()->cancel(nullptr), false, false);
+ other_thread_running = false;
+ });
+
+ while (!other_thread_running.load())
+ g_assert_false(accessed_from_other_thread.load());
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ g_assert_true(other_thread_running);
+ g_assert_false(accessed_from_other_thread);
+
+ auto other_queue = std::make_unique<LockedQueue>();
+ assert_equal(other_queue->tq->cancel(nullptr), false, false);
+
+ other_queue.reset();
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ g_assert_true(other_thread_running);
+ g_assert_false(accessed_from_other_thread);
+
+ // Ok, now other thread may get the lock...
+ locked_queue.reset();
+ while (!accessed_from_other_thread.load()) {
+ }
+ g_assert_true(accessed_from_other_thread);
+
+ // Can enter again from main thread!
+ th.join();
+ g_assert_false(other_thread_running);
+ assert_equal(ToggleQueue::get_default()->cancel(nullptr), false, false);
+}
+
+static void test_toggle_queue_empty(GjsUnitTestFixture*, const void*) {
+ auto tq = ToggleQueue::get_default();
+ tq->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_empty_cancel(GjsUnitTestFixture*, const void*) {
+ auto tq = ToggleQueue::get_default();
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(nullptr);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+}
+
+static void test_toggle_queue_enqueue_one(GjsUnitTestFixture* fx, const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+
+ tq->handle_all_toggles(toggles_handler);
+ assert_equal(s_toggle_history.size(), 1LU);
+ assert_equal(s_toggle_history.front(), instance,
+ ::ToggleQueue::Direction::UP);
+}
+
+static void test_toggle_queue_enqueue_one_cancel(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_true(toggle_up_queued);
+
+ tq->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_enqueue_many_equal(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+ tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler);
+
+ tq->handle_all_toggles(toggles_handler);
+ assert_equal(s_toggle_history.size(), 0LU);
+
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+}
+
+static void test_toggle_queue_enqueue_many_equal_cancel(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+ tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler);
+
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+}
+
+static void test_toggle_queue_enqueue_more_up(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+
+ tq->handle_all_toggles(toggles_handler);
+ assert_equal(s_toggle_history.size(), 2LU);
+ assert_equal(s_toggle_history.at(0), instance,
+ ::ToggleQueue::Direction::UP);
+ assert_equal(s_toggle_history.at(1), instance,
+ ::ToggleQueue::Direction::UP);
+
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+}
+
+static void test_toggle_queue_enqueue_only_up(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+
+ tq->handle_all_toggles(toggles_handler);
+ assert_equal(s_toggle_history.size(), 4LU);
+ assert_equal(s_toggle_history.at(0), instance,
+ ::ToggleQueue::Direction::UP);
+ assert_equal(s_toggle_history.at(1), instance,
+ ::ToggleQueue::Direction::UP);
+ assert_equal(s_toggle_history.at(2), instance,
+ ::ToggleQueue::Direction::UP);
+ assert_equal(s_toggle_history.at(3), instance,
+ ::ToggleQueue::Direction::UP);
+
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+}
+
+static void test_toggle_queue_handle_more_up(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+
+ wait_for(50);
+
+ assert_equal(s_toggle_history.size(), 2LU);
+ assert_equal(s_toggle_history.at(0), instance,
+ ::ToggleQueue::Direction::UP);
+ assert_equal(s_toggle_history.at(1), instance,
+ ::ToggleQueue::Direction::UP);
+
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+}
+
+static void test_toggle_queue_handle_only_up(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+
+ wait_for(50);
+
+ assert_equal(s_toggle_history.size(), 4LU);
+ assert_equal(s_toggle_history.at(0), instance,
+ ::ToggleQueue::Direction::UP);
+ assert_equal(s_toggle_history.at(1), instance,
+ ::ToggleQueue::Direction::UP);
+ assert_equal(s_toggle_history.at(2), instance,
+ ::ToggleQueue::Direction::UP);
+ assert_equal(s_toggle_history.at(3), instance,
+ ::ToggleQueue::Direction::UP);
+
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+}
+
+static void test_toggle_queue_enqueue_only_up_cancel(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+ tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler);
+
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_true(toggle_up_queued);
+
+ tq->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_from_main_thread(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto tq = ToggleQueue::get_default();
+
+ GjsAutoUnref<GObject> reffed(instance->ptr(), GjsAutoTakeOwnership());
+
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+
+ tq->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_from_main_thread_already_enqueued(
+ GjsUnitTestFixture* fx, const void*) {
+ auto* instance = new_test_gobject(fx);
+ GjsAutoUnref<GObject> reffed;
+ GjsAutoError error;
+
+ reffed = instance->ptr();
+ gjs_test_tools_ref_other_thread(reffed, error.out());
+ g_assert_no_error(error);
+
+ assert_equal(ToggleQueue::queue().size(), 1LU);
+ assert_equal(ToggleQueue::queue().at(0).object, instance);
+ assert_equal(ToggleQueue::queue().at(0).direction,
+ ::ToggleQueue::Direction::UP);
+
+ auto tq = ToggleQueue::get_default();
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_true(toggle_up_queued);
+
+ tq->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_from_main_thread_unref_already_enqueued(
+ GjsUnitTestFixture* fx, const void*) {
+ auto* instance = new_test_gobject(fx);
+ GjsAutoUnref<GObject> reffed;
+ GjsAutoError error;
+
+ reffed = instance->ptr();
+ gjs_test_tools_ref_other_thread(reffed, error.out());
+ g_assert_no_error(error);
+ assert_equal(ToggleQueue::queue().size(), 1LU);
+ assert_equal(ToggleQueue::queue().at(0).direction,
+ ::ToggleQueue::Direction::UP);
+
+ reffed.reset();
+ g_assert_true(ToggleQueue::queue().empty());
+
+ auto tq = ToggleQueue::get_default();
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+
+ tq->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_from_other_thread_ref_unref(
+ GjsUnitTestFixture* fx, const void*) {
+ auto* instance = new_test_gobject(fx);
+
+ GjsAutoError error;
+ gjs_test_tools_ref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ assert_equal(ToggleQueue::queue().size(), 1LU);
+ assert_equal(ToggleQueue::queue().at(0).direction,
+ ::ToggleQueue::Direction::UP);
+
+ gjs_test_tools_unref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ g_assert_true(ToggleQueue::queue().empty());
+
+ auto tq = ToggleQueue::get_default();
+ bool toggle_down_queued, toggle_up_queued;
+ std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance);
+ g_assert_false(toggle_down_queued);
+ g_assert_false(toggle_up_queued);
+
+ tq->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_handle_up(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto* instance_test = reinterpret_cast<ObjectInstance*>(instance);
+
+ GjsAutoError error;
+ gjs_test_tools_ref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ GjsAutoUnref<GObject> reffed(instance->ptr());
+ assert_equal(ToggleQueue::queue().size(), 1LU);
+ assert_equal(ToggleQueue::queue().at(0).direction,
+ ::ToggleQueue::Direction::UP);
+
+ wait_for(50);
+ g_assert_true(instance_test->wrapper_is_rooted());
+ ToggleQueue::get_default()->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_handle_up_down(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto* instance_test = reinterpret_cast<ObjectInstance*>(instance);
+
+ GjsAutoError error;
+ gjs_test_tools_ref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ assert_equal(ToggleQueue::queue().size(), 1LU);
+ assert_equal(ToggleQueue::queue().at(0).direction,
+ ::ToggleQueue::Direction::UP);
+
+ gjs_test_tools_unref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ g_assert_true(ToggleQueue::queue().empty());
+
+ wait_for(50);
+ g_assert_false(instance_test->wrapper_is_rooted());
+ ToggleQueue::get_default()->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_handle_up_down_delayed(
+ GjsUnitTestFixture* fx, const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto* instance_test = reinterpret_cast<ObjectInstance*>(instance);
+
+ GjsAutoError error;
+ gjs_test_tools_ref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ assert_equal(ToggleQueue::queue().size(), 1LU);
+ assert_equal(ToggleQueue::queue().at(0).direction,
+ ::ToggleQueue::Direction::UP);
+
+ wait_for(50);
+ g_assert_true(instance_test->wrapper_is_rooted());
+ ToggleQueue::get_default()->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+
+ gjs_test_tools_unref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ assert_equal(ToggleQueue::queue().size(), 1LU);
+ assert_equal(ToggleQueue::queue().at(0).direction,
+ ::ToggleQueue::Direction::DOWN);
+
+ wait_for(50);
+ g_assert_false(instance_test->wrapper_is_rooted());
+ ToggleQueue::get_default()->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_handle_up_down_on_gc(
+ GjsUnitTestFixture* fx, const void*) {
+ auto* instance = new_test_gobject(fx);
+
+ GjsAutoError error;
+ gjs_test_tools_ref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ assert_equal(ToggleQueue::queue().size(), 1LU);
+ assert_equal(ToggleQueue::queue().at(0).direction,
+ ::ToggleQueue::Direction::UP);
+
+ gjs_test_tools_unref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ g_assert_true(ToggleQueue::queue().empty());
+
+ GWeakRef weak_ref;
+ g_weak_ref_init(&weak_ref, instance->ptr());
+
+ TQ::wait_for_gc(fx);
+ g_assert_null(g_weak_ref_get(&weak_ref));
+
+ ToggleQueue::get_default()->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_handle_many_up(GjsUnitTestFixture* fx,
+ const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto* instance_test = reinterpret_cast<ObjectInstance*>(instance);
+
+ GjsAutoError error;
+ gjs_test_tools_ref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ GjsAutoUnref<GObject> reffed(instance->ptr());
+ // Simulating the case where late threads are causing this...
+ ToggleQueue::get_default()->enqueue(instance, ::ToggleQueue::Direction::UP,
+ ToggleQueue().handler());
+
+ assert_equal(ToggleQueue::queue().size(), 2LU);
+ assert_equal(ToggleQueue::queue().at(0).direction,
+ ::ToggleQueue::Direction::UP);
+ assert_equal(ToggleQueue::queue().at(1).direction,
+ ::ToggleQueue::Direction::UP);
+
+ wait_for(50);
+ g_assert_true(instance_test->wrapper_is_rooted());
+ ToggleQueue::get_default()->handle_all_toggles(toggles_handler);
+ g_assert_true(s_toggle_history.empty());
+}
+
+static void test_toggle_queue_object_handle_many_up_and_down(
+ GjsUnitTestFixture* fx, const void*) {
+ auto* instance = new_test_gobject(fx);
+ auto* instance_test = reinterpret_cast<ObjectInstance*>(instance);
+
+ // This is something similar to what is happening on #297
+ GjsAutoError error;
+ gjs_test_tools_ref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ ToggleQueue::get_default()->enqueue(instance, ::ToggleQueue::Direction::UP,
+ ToggleQueue().handler());
+ gjs_test_tools_unref_other_thread(instance->ptr(), error.out());
+ g_assert_no_error(error);
+ ToggleQueue::get_default()->enqueue(
+ instance, ::ToggleQueue::Direction::DOWN, ToggleQueue().handler());
+
+ g_assert_true(ToggleQueue::queue().empty());
+
+ wait_for(50);
+ g_assert_false(instance_test->wrapper_is_rooted());
+ g_assert_true(ToggleQueue::queue().empty());
+
+ GWeakRef weak_ref;
+ g_assert_true(G_IS_OBJECT(instance->ptr()));
+ g_weak_ref_init(&weak_ref, instance->ptr());
+
+ TQ::wait_for_gc(fx);
+ g_assert_null(g_weak_ref_get(&weak_ref));
+ g_assert_true(ToggleQueue::queue().empty());
+}
+
+void add_tests_for_toggle_queue() {
+#define ADD_TOGGLE_QUEUE_TEST(path, f) \
+ g_test_add("/toggle-queue/" path, GjsUnitTestFixture, nullptr, TQ::setup, \
+ f, TQ::teardown);
+
+ ADD_TOGGLE_QUEUE_TEST("spin-lock/unlock-empty",
+ test_toggle_queue_unlock_empty);
+ ADD_TOGGLE_QUEUE_TEST("spin-lock/unlock-same-thread",
+ test_toggle_queue_unlock_same_thread);
+ ADD_TOGGLE_QUEUE_TEST("spin-lock/blocks-other-thread",
+ test_toggle_blocks_other_thread);
+
+ ADD_TOGGLE_QUEUE_TEST("empty", test_toggle_queue_empty);
+ ADD_TOGGLE_QUEUE_TEST("empty_cancel", test_toggle_queue_empty_cancel);
+ ADD_TOGGLE_QUEUE_TEST("enqueue_one", test_toggle_queue_enqueue_one);
+ ADD_TOGGLE_QUEUE_TEST("enqueue_one_cancel",
+ test_toggle_queue_enqueue_one_cancel);
+ ADD_TOGGLE_QUEUE_TEST("enqueue_many_equal",
+ test_toggle_queue_enqueue_many_equal);
+ ADD_TOGGLE_QUEUE_TEST("enqueue_many_equal_cancel",
+ test_toggle_queue_enqueue_many_equal_cancel);
+ ADD_TOGGLE_QUEUE_TEST("enqueue_more_up", test_toggle_queue_enqueue_more_up);
+ ADD_TOGGLE_QUEUE_TEST("enqueue_only_up", test_toggle_queue_enqueue_only_up);
+ ADD_TOGGLE_QUEUE_TEST("enqueue_only_up_cancel",
+ test_toggle_queue_enqueue_only_up_cancel);
+ ADD_TOGGLE_QUEUE_TEST("handle_more_up", test_toggle_queue_handle_more_up);
+ ADD_TOGGLE_QUEUE_TEST("handle_only_up", test_toggle_queue_handle_only_up);
+
+ ADD_TOGGLE_QUEUE_TEST("object/not-enqueued_main_thread",
+ test_toggle_queue_object_from_main_thread);
+ ADD_TOGGLE_QUEUE_TEST(
+ "object/already_enqueued_main_thread",
+ test_toggle_queue_object_from_main_thread_already_enqueued);
+ ADD_TOGGLE_QUEUE_TEST(
+ "object/already_enqueued_unref_main_thread",
+ test_toggle_queue_object_from_main_thread_unref_already_enqueued);
+ ADD_TOGGLE_QUEUE_TEST("object/ref_unref_other_thread",
+ test_toggle_queue_object_from_other_thread_ref_unref);
+ ADD_TOGGLE_QUEUE_TEST("object/handle_up",
+ test_toggle_queue_object_handle_up);
+ ADD_TOGGLE_QUEUE_TEST("object/handle_up_down",
+ test_toggle_queue_object_handle_up_down);
+ ADD_TOGGLE_QUEUE_TEST("object/handle_up_down_delayed",
+ test_toggle_queue_object_handle_up_down_delayed);
+ ADD_TOGGLE_QUEUE_TEST("object/handle_up_down_on_gc",
+ test_toggle_queue_object_handle_up_down_on_gc);
+ ADD_TOGGLE_QUEUE_TEST("object/handle_many_up",
+ test_toggle_queue_object_handle_many_up);
+ ADD_TOGGLE_QUEUE_TEST("object/handle_many_up_and_down",
+ test_toggle_queue_object_handle_many_up_and_down);
+
+#undef ADD_TOGGLE_QUEUE_TEST
+}
+
+} // namespace Test
+} // namespace Gjs
diff --git a/test/gjs-tests-internal.cpp b/test/gjs-tests-internal.cpp
new file mode 100644
index 00000000..4f8f74ba
--- /dev/null
+++ b/test/gjs-tests-internal.cpp
@@ -0,0 +1,25 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2021 Canonical, Ltd.
+// SPDX-FileContributor: Marco Trevisan <marco trevisan canonical com>
+
+#include <config.h>
+#include <glib.h>
+
+#include "test/gjs-test-utils.h"
+
+int main(int argc, char** argv) {
+ /* Avoid interference in the tests from stray environment variable */
+ g_unsetenv("GJS_ENABLE_PROFILER");
+ g_unsetenv("GJS_TRACE_FD");
+
+ g_test_init(&argc, &argv, nullptr);
+
+ gjs_test_add_tests_for_rooting();
+ gjs_test_add_tests_for_parse_call_args();
+ gjs_test_add_tests_for_jsapi_utils();
+
+ g_test_run();
+
+ return 0;
+}
diff --git a/test/gjs-tests.cpp b/test/gjs-tests.cpp
index 8eda4e27..9034d879 100644
--- a/test/gjs-tests.cpp
+++ b/test/gjs-tests.cpp
@@ -955,9 +955,6 @@ main(int argc,
#undef ADD_JSAPI_UTIL_TEST
gjs_test_add_tests_for_coverage ();
- gjs_test_add_tests_for_parse_call_args();
- gjs_test_add_tests_for_rooting();
- gjs_test_add_tests_for_jsapi_utils();
g_test_run();
diff --git a/test/meson.build b/test/meson.build
index 83c63067..dd902877 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -7,21 +7,43 @@ mock_js_resources_files = gnome.compile_resources('mock-js-resources',
'mock-js-resources.gresource.xml', c_name: 'mock_js_resources',
source_dir: '..')
+libgjs_tests_common = static_library('libgjs-tests-common',
+ sources: [
+ 'gjs-test-utils.cpp', 'gjs-test-utils.h',
+ 'gjs-test-common.cpp', 'gjs-test-common.h',
+ ],
+ cpp_args: libgjs_cpp_args,
+ include_directories: top_include, dependencies: libgjs_dependencies,
+)
+
gjs_tests_sources = [
'gjs-tests.cpp',
- 'gjs-test-common.cpp', 'gjs-test-common.h',
- 'gjs-test-utils.cpp', 'gjs-test-utils.h',
- 'gjs-test-call-args.cpp',
'gjs-test-coverage.cpp',
- 'gjs-test-rooting.cpp',
- 'gjs-test-jsapi-utils.cpp',
'gjs-test-no-introspection-object.cpp', 'gjs-test-no-introspection-object.h',
]
gjs_tests = executable('gjs-tests', gjs_tests_sources, mock_js_resources_files,
- cpp_args: ['-DGJS_COMPILATION'] + directory_defines,
- include_directories: top_include, dependencies: libgjs_dep)
+ include_directories: top_include, dependencies: libgjs_dep,
+ link_with: libgjs_tests_common)
test('API tests', gjs_tests, args: ['--tap', '--keep-going', '--verbose'],
depends: gjs_private_typelib, env: tests_environment, protocol: 'tap',
suite: 'C', timeout: 60)
+
+gjs_tests_internal = executable('gjs-tests-internal',
+ sources: [
+ 'gjs-tests-internal.cpp',
+ 'gjs-test-call-args.cpp',
+ 'gjs-test-rooting.cpp',
+ 'gjs-test-jsapi-utils.cpp',
+ module_resource_srcs,
+ ],
+ include_directories: top_include,
+ cpp_args: libgjs_cpp_args,
+ dependencies: [libgjs_dependencies, libgjstesttools_dep],
+ link_with: [libgjs_tests_common, libgjs_internal])
+
+test('Internal API tests', gjs_tests_internal,
+ args: ['--tap', '--keep-going', '--verbose'],
+ env: tests_environment, protocol: 'tap',
+ suite: ['C', 'thread-safe'])
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]