/* -*- 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 #include #include // for copy #include #include #include #include #include #include // for tie #include // for pair #include #include #include #include // for JS_GC #include #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> 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, "", &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( G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr))); auto* object = ObjectInstance::new_for_gobject(fx->cx, gobject); g_assert_true( static_cast(object)->ensure_uses_toggle_ref(fx->cx)); return object; } static void wait_for(int interval) { GjsAutoPointer loop( g_main_loop_new(nullptr, false)); g_timeout_add_full( G_PRIORITY_LOW, interval, [](void* data) { g_main_loop_quit(static_cast(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(); 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(); 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(); 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 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 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 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(instance); GjsAutoError error; gjs_test_tools_ref_other_thread(instance->ptr(), error.out()); g_assert_no_error(error); GjsAutoUnref 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(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(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(instance); GjsAutoError error; gjs_test_tools_ref_other_thread(instance->ptr(), error.out()); g_assert_no_error(error); GjsAutoUnref 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(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