/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later * * Copyright (c) 2020 Marco Trevisan */ #include #include #include // for NULL #include // for move, swap #include "gjs/jsapi-util.h" struct _GjsTestObject { GObject parent_instance; int stuff; }; G_DECLARE_FINAL_TYPE(GjsTestObject, gjs_test_object, GJS_TEST, OBJECT, GObject) G_DEFINE_TYPE(GjsTestObject, gjs_test_object, G_TYPE_OBJECT) struct Fixture { GjsTestObject* ptr; }; static void gjs_test_object_init(GjsTestObject*) {} void gjs_test_object_class_init(GjsTestObjectClass*) {} static GjsTestObject* gjs_test_object_new() { return GJS_TEST_OBJECT(g_object_new(gjs_test_object_get_type(), NULL)); } static void setup(Fixture* fx, const void*) { fx->ptr = gjs_test_object_new(); g_assert_nonnull(fx->ptr); g_object_add_weak_pointer(G_OBJECT(fx->ptr), reinterpret_cast(&fx->ptr)); } static void teardown(Fixture* fx, const void*) { // Weak pointer will have reset the pointer to null if the last reference // was dropped g_assert_null(fx->ptr); } using GjsAutoTestObject = GjsAutoPointer; static void test_gjs_autopointer_size() { g_assert_cmpuint(sizeof(GjsAutoTestObject), ==, sizeof(GjsTestObject*)); } static void test_gjs_autopointer_ctor_empty() { GjsAutoTestObject autoptr; g_assert_null(autoptr.get()); g_assert_null(autoptr); } static void test_gjs_autopointer_ctor_basic(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); } static void test_gjs_autopointer_ctor_take_ownership(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr, GjsAutoTakeOwnership()); g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_ctor_assign(Fixture* fx, const void*) { GjsAutoTestObject autoptr = fx->ptr; g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); } static void test_gjs_autopointer_ctor_assign_other(Fixture* fx, const void*) { GjsAutoTestObject autoptr1 = fx->ptr; GjsAutoTestObject autoptr2 = autoptr1; g_assert_true(autoptr1 == fx->ptr); g_assert_true(autoptr1.get() == fx->ptr); g_assert_true(autoptr2 == fx->ptr); g_assert_true(autoptr2.get() == fx->ptr); } static void test_gjs_autopointer_dtor(Fixture* fx, const void*) { g_object_ref(fx->ptr); { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); } g_assert_nonnull(fx->ptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_dtor_cpp() { bool deleted = false; auto dtor_callback = [&deleted] { deleted = true; }; struct TestStruct { explicit TestStruct(decltype(dtor_callback) cb) : _delete_cb(cb) {} ~TestStruct() { _delete_cb(); } decltype(dtor_callback) _delete_cb; }; g_assert_false(deleted); { auto* ptr = new TestStruct(dtor_callback); GjsAutoCppPointer autoptr(ptr); g_assert_true(ptr == autoptr); } g_assert_true(deleted); } static void test_gjs_autopointer_dtor_cpp_array() { unsigned deleted = 0; auto dtor_callback = [&deleted] { deleted++; }; struct TestStruct { TestStruct(decltype(dtor_callback) cb) // NOLINT(runtime/explicit) : _delete_cb(cb) {} ~TestStruct() { _delete_cb(); } int val = 5; decltype(dtor_callback) _delete_cb; }; g_assert_cmpint(deleted, ==, 0); { // using GjsAutoCppPointer1 = GjsAutoPointer>; TestStruct* ptrs = new TestStruct[3]{dtor_callback, dtor_callback, dtor_callback}; GjsAutoCppPointer autoptr(ptrs); g_assert_cmpint(autoptr[0].val, ==, 5); g_assert_cmpint(autoptr[1].val, ==, 5); g_assert_cmpint(autoptr[2].val, ==, 5); } g_assert_cmpuint(deleted, ==, 3); } static void test_gjs_autopointer_dtor_take_ownership(Fixture* fx, const void*) { { GjsAutoTestObject autoptr(fx->ptr, GjsAutoTakeOwnership()); g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); } g_assert_nonnull(fx->ptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_dtor_default_free() { GjsAutoPointer autoptr(g_strdup("Please, FREE ME!")); g_assert_cmpstr(autoptr, ==, "Please, FREE ME!"); } static void test_gjs_autopointer_dtor_no_free_pointer() { const char* str = "DO NOT FREE ME"; GjsAutoPointer autoptr(const_cast(str)); g_assert_cmpstr(autoptr, ==, "DO NOT FREE ME"); } static void gobject_free(GObject* p) { g_object_unref(p); } static GObject* gobject_copy(GObject* p) { return static_cast(g_object_ref(p)); } static void test_gjs_autopointer_cast_free_func_type() { // No assertions; this test fails to compile if the casts are wrong using TypedAutoPointer = GjsAutoPointer; TypedAutoPointer autoptr{gjs_test_object_new()}; TypedAutoPointer copy{autoptr.copy()}; } static void test_gjs_autopointer_assign_operator() { GjsAutoTestObject autoptr; auto* ptr = gjs_test_object_new(); autoptr = ptr; g_assert_true(autoptr == ptr); g_assert_true(autoptr.get() == ptr); } static void test_gjs_autopointer_assign_operator_other_ptr() { auto* ptr1 = gjs_test_object_new(); auto* ptr2 = gjs_test_object_new(); g_object_add_weak_pointer(G_OBJECT(ptr1), reinterpret_cast(&ptr1)); GjsAutoTestObject autoptr(ptr1); g_object_ref(ptr1); autoptr = ptr2; g_assert_true(autoptr == ptr2); g_assert_nonnull(ptr1); g_object_unref(ptr1); g_assert_null(ptr1); } static void test_gjs_autopointer_assign_operator_self_ptr(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_object_ref(fx->ptr); autoptr = fx->ptr; g_assert_true(autoptr == fx->ptr); } static void test_gjs_autopointer_assign_operator_object(Fixture* fx, const void*) { GjsAutoTestObject autoptr1; GjsAutoTestObject autoptr2; autoptr1 = fx->ptr; autoptr2 = autoptr1; g_assert_true(autoptr1 == autoptr2); g_assert_true(autoptr2.get() == fx->ptr); } static void test_gjs_autopointer_assign_operator_other_object() { auto* ptr1 = gjs_test_object_new(); auto* ptr2 = gjs_test_object_new(); g_object_add_weak_pointer(G_OBJECT(ptr1), reinterpret_cast(&ptr1)); g_object_add_weak_pointer(G_OBJECT(ptr2), reinterpret_cast(&ptr2)); { GjsAutoTestObject autoptr1(ptr1); GjsAutoTestObject autoptr2(ptr2); g_object_ref(ptr1); autoptr1 = autoptr2; g_assert_true(autoptr1 == ptr2); g_assert_true(autoptr2 == ptr2); g_assert_nonnull(ptr1); g_object_unref(ptr1); } g_assert_null(ptr1); g_assert_null(ptr2); } static void test_gjs_autopointer_assign_operator_self_object(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); autoptr = *&autoptr; g_assert_true(autoptr == fx->ptr); } static void test_gjs_autopointer_assign_operator_copy_and_swap(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); auto* ptr = fx->ptr; auto test_copy_fun = [ptr](GjsAutoTestObject data) { g_assert_true(data == ptr); }; test_copy_fun(autoptr); g_assert_true(autoptr == fx->ptr); } static void test_gjs_autopointer_operator_move(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); void* ptr = fx->ptr; auto test_move_fun = [ptr](GjsAutoTestObject&& data) { g_assert_true(ptr == data); }; // Accessing a value after moving out of it is bad in general, but here it // is done on purpose, to test that the autoptr's move constructor empties // the old autoptr. test_move_fun(std::move(autoptr)); g_assert_nonnull(autoptr); // cppcheck-suppress accessMoved // cppcheck-suppress accessMoved GjsAutoTestObject autoptr2 = std::move(autoptr); g_assert_true(autoptr2 == fx->ptr); g_assert_null(autoptr); // cppcheck-suppress accessMoved } static void test_gjs_autopointer_operator_swap(Fixture* fx, const void*) { GjsAutoTestObject autoptr1(fx->ptr); GjsAutoTestObject autoptr2; std::swap(autoptr1, autoptr2); g_assert_null(autoptr1); g_assert_true(autoptr2 == fx->ptr); } static void test_gjs_autopointer_assign_operator_arrow(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); int value = g_random_int(); autoptr->stuff = value; g_assert_cmpint(autoptr->stuff, ==, value); } static void test_gjs_autopointer_assign_operator_deference(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); fx->ptr->stuff = g_random_int(); GjsTestObject tobj = *autoptr; g_assert_cmpint(fx->ptr->stuff, ==, tobj.stuff); } static void test_gjs_autopointer_assign_operator_bool(Fixture* fx, const void*) { auto bool_to_gboolean = [](bool v) -> gboolean { return !!v; }; g_assert_false(bool_to_gboolean(GjsAutoTestObject())); g_assert_true(bool_to_gboolean(GjsAutoTestObject(gjs_test_object_new()))); GjsAutoTestObject autoptr(fx->ptr); autoptr.reset(); g_assert_false(bool_to_gboolean(autoptr)); } static void test_gjs_autopointer_assign_operator_array() { auto* ptrs = g_new0(GjsTestObject, 5); GjsAutoPointer autopointers(ptrs); for (int i = 0; i < 5; i++) { autopointers[i].stuff = i; g_assert_cmpint(ptrs[i].stuff, ==, i); g_assert_cmpint(autopointers[i].stuff, ==, i); } } static void test_gjs_autopointer_get(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(fx->ptr == autoptr.get()); } static void test_gjs_autopointer_out(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(fx->ptr == *(autoptr.out())); } static void test_gjs_autopointer_release(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_nonnull(autoptr); auto* released = autoptr.release(); g_assert_true(released == fx->ptr); g_assert_null(autoptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_reset_nullptr(Fixture* fx, const void*) { GjsAutoTestObject empty; empty.reset(); g_assert_null(empty); GjsAutoTestObject autoptr(fx->ptr); g_assert_nonnull(autoptr); g_object_ref(fx->ptr); autoptr.reset(); g_assert_null(autoptr); g_assert_nonnull(fx->ptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_reset_self_ptr(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(autoptr == fx->ptr); g_object_ref(fx->ptr); autoptr.reset(fx->ptr); g_assert_true(autoptr == fx->ptr); g_assert_nonnull(fx->ptr); } static void test_gjs_autopointer_reset_other_ptr() { auto* ptr1 = gjs_test_object_new(); auto* ptr2 = gjs_test_object_new(); g_object_add_weak_pointer(G_OBJECT(ptr1), reinterpret_cast(&ptr1)); g_object_add_weak_pointer(G_OBJECT(ptr2), reinterpret_cast(&ptr2)); { GjsAutoTestObject autoptr(ptr1); g_assert_true(autoptr == ptr1); g_object_ref(ptr1); autoptr.reset(ptr2); g_assert_true(autoptr == ptr2); g_assert_nonnull(ptr1); g_assert_nonnull(ptr2); g_object_unref(ptr1); } g_assert_null(ptr1); g_assert_null(ptr2); } static void test_gjs_autopointer_swap_other_ptr(Fixture* fx, const void*) { GjsAutoTestObject autoptr1(fx->ptr); GjsAutoTestObject autoptr2; autoptr1.swap(autoptr2); g_assert_null(autoptr1); g_assert_true(autoptr2 == fx->ptr); g_assert_nonnull(fx->ptr); } static void test_gjs_autopointer_swap_self_ptr(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); autoptr.swap(autoptr); g_assert_true(autoptr == fx->ptr); g_assert_nonnull(fx->ptr); } static void test_gjs_autopointer_swap_empty(Fixture* fx, const void*) { GjsAutoTestObject autoptr1(fx->ptr); GjsAutoTestObject autoptr2; autoptr1.swap(autoptr2); g_assert_null(autoptr1); g_assert_true(autoptr2 == fx->ptr); g_assert_nonnull(fx->ptr); } static void test_gjs_autopointer_copy(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(fx->ptr == autoptr.copy()); g_object_unref(fx->ptr); } static void test_gjs_autopointer_as() { GjsAutoTestObject autoptr(gjs_test_object_new()); g_assert_cmpuint(autoptr.as()->ref_count, ==, 1); } static void test_gjs_autochar_init() { char* str = g_strdup("FoooBar"); GjsAutoChar autoptr = str; g_assert_cmpstr(autoptr, ==, "FoooBar"); g_assert_cmpuint(autoptr[4], ==, 'B'); g_assert_true(autoptr == str); } static void test_gjs_autochar_init_take_ownership() { const char* str = "FoooBarConst"; GjsAutoChar autoptr(str, GjsAutoTakeOwnership()); g_assert_cmpstr(autoptr, ==, str); g_assert_cmpuint(autoptr[4], ==, 'B'); g_assert_true(autoptr != str); } static void test_gjs_autochar_copy() { GjsAutoChar autoptr = g_strdup("FoooBar"); char* copy = autoptr.copy(); g_assert_cmpstr(autoptr, ==, copy); g_assert_true(autoptr != copy); g_free(copy); } static void test_gjs_autostrv_init() { const char* strv[] = {"FOO", "Bar", "BAZ", nullptr}; GjsAutoStrv autoptr = g_strdupv(const_cast(strv)); g_assert_true(g_strv_equal(strv, autoptr)); for (int i = g_strv_length(const_cast(strv)); i >= 0; i--) g_assert_cmpstr(autoptr[i], ==, strv[i]); } static void test_gjs_autostrv_init_take_ownership() { const char* strv[] = {"FOO", "Bar", "BAZ", nullptr}; GjsAutoStrv autoptr(const_cast(strv), GjsAutoTakeOwnership()); for (int i = g_strv_length(const_cast(strv)); i >= 0; i--) g_assert_cmpstr(autoptr[i], ==, strv[i]); g_assert_false(autoptr == strv); } static void test_gjs_autostrv_copy() { const char* strv[] = {"FOO", "Bar", "BAZ", nullptr}; GjsAutoStrv autoptr = g_strdupv(const_cast(strv)); char** copy = autoptr.copy(); for (int i = g_strv_length(const_cast(strv)); i >= 0; i--) g_assert_cmpstr(copy[i], ==, strv[i]); g_assert_false(autoptr == copy); g_strfreev(copy); } static void test_gjs_autotypeclass_init() { GjsAutoTypeClass autoclass(gjs_test_object_get_type()); g_assert_nonnull(autoclass); g_assert_cmpint(autoclass->g_type_class.g_type, ==, gjs_test_object_get_type()); } #define ADD_AUTOPTRTEST(path, func) \ g_test_add(path, Fixture, nullptr, setup, func, teardown); void gjs_test_add_tests_for_jsapi_utils(void) { g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/size", test_gjs_autopointer_size); g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/constructor/empty", test_gjs_autopointer_ctor_empty); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/constructor/basic", test_gjs_autopointer_ctor_basic); ADD_AUTOPTRTEST( "/gjs/jsapi-utils/gjs-autopointer/constructor/take_ownership", test_gjs_autopointer_ctor_take_ownership); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/constructor/assignment", test_gjs_autopointer_ctor_assign); ADD_AUTOPTRTEST( "/gjs/jsapi-utils/gjs-autopointer/constructor/assignment/other", test_gjs_autopointer_ctor_assign_other); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/destructor", test_gjs_autopointer_dtor); ADD_AUTOPTRTEST( "/gjs/jsapi-utils/gjs-autopointer/destructor/take_ownership", test_gjs_autopointer_dtor_take_ownership); g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/destructor/default_free", test_gjs_autopointer_dtor_default_free); g_test_add_func( "/gjs/jsapi-utils/gjs-autopointer/destructor/no_free_pointer", test_gjs_autopointer_dtor_no_free_pointer); g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/free_and_ref_funcs", test_gjs_autopointer_cast_free_func_type); g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/destructor/c++", test_gjs_autopointer_dtor_cpp); g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/destructor/c++-array", test_gjs_autopointer_dtor_cpp_array); g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/operator/assign", test_gjs_autopointer_assign_operator); g_test_add_func( "/gjs/jsapi-utils/gjs-autopointer/operator/assign/other_ptr", test_gjs_autopointer_assign_operator_other_ptr); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/operator/assign/self_ptr", test_gjs_autopointer_assign_operator_self_ptr); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/operator/assign/object", test_gjs_autopointer_assign_operator_object); g_test_add_func( "/gjs/jsapi-utils/gjs-autopointer/operator/assign/other_object", test_gjs_autopointer_assign_operator_other_object); ADD_AUTOPTRTEST( "/gjs/jsapi-utils/gjs-autopointer/operator/assign/self_object", test_gjs_autopointer_assign_operator_self_object); ADD_AUTOPTRTEST( "/gjs/jsapi-utils/gjs-autopointer/operator/assign/copy_and_swap", test_gjs_autopointer_assign_operator_copy_and_swap); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/operator/move", test_gjs_autopointer_operator_move); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/operator/swap", test_gjs_autopointer_operator_swap); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/operator/arrow", test_gjs_autopointer_assign_operator_arrow); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/operator/deference", test_gjs_autopointer_assign_operator_deference); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/operator/bool", test_gjs_autopointer_assign_operator_bool); g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/operator/array", test_gjs_autopointer_assign_operator_array); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/method/get", test_gjs_autopointer_get); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/method/out", test_gjs_autopointer_out); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/method/release", test_gjs_autopointer_release); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/method/reset/nullptr", test_gjs_autopointer_reset_nullptr); g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/method/reset/other_ptr", test_gjs_autopointer_reset_other_ptr); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/method/reset/self_ptr", test_gjs_autopointer_reset_self_ptr); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/method/swap/other_ptr", test_gjs_autopointer_swap_other_ptr); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/method/swap/self_ptr", test_gjs_autopointer_swap_self_ptr); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/method/swap/empty", test_gjs_autopointer_swap_empty); ADD_AUTOPTRTEST("/gjs/jsapi-utils/gjs-autopointer/method/copy", test_gjs_autopointer_copy); g_test_add_func("/gjs/jsapi-utils/gjs-autopointer/method/as", test_gjs_autopointer_as); // Other implementations g_test_add_func("/gjs/jsapi-utils/gjs-autochar/init", test_gjs_autochar_init); g_test_add_func("/gjs/jsapi-utils/gjs-autochar/init/take_ownership", test_gjs_autochar_init_take_ownership); g_test_add_func("/gjs/jsapi-utils/gjs-autochar/copy", test_gjs_autochar_copy); g_test_add_func("/gjs/jsapi-utils/gjs-autostrv/init", test_gjs_autostrv_init); g_test_add_func("/gjs/jsapi-utils/gjs-autostrv/init/take_ownership", test_gjs_autostrv_init_take_ownership); g_test_add_func("/gjs/jsapi-utils/gjs-autostrv/copy", test_gjs_autostrv_copy); g_test_add_func("/gjs/jsapi-utils/gjs-autotypeclass/init", test_gjs_autotypeclass_init); }