From 2af4017e6ae977046bf886d29df78c46130ee920 Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Sat, 27 Sep 2025 19:33:30 +0200 Subject: [PATCH 1/3] tests: Add an initial TestCase class --- tests/TestCase.vala | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/TestCase.vala diff --git a/tests/TestCase.vala b/tests/TestCase.vala new file mode 100644 index 000000000..d35535165 --- /dev/null +++ b/tests/TestCase.vala @@ -0,0 +1,52 @@ +/* + * Copyright 2025 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/** + * A simple test case class. To use it inherit from it and add test methods + * in the constructor using {@link add_test}. Override {@link set_up} and {@link tear_down} + * to provide per-test-method setup and teardown. Then add a `main()` function + * and return the result of {@link run}. + */ +public abstract class Gala.TestCase : Object { + public delegate void TestMethod (); + + public string name { get; construct; } + + private GLib.TestSuite suite; + + construct { + suite = new GLib.TestSuite (name); + } + + public int run (string[] args) { + Test.init (ref args); + TestSuite.get_root ().add_suite ((owned) suite); + return Test.run (); + } + + protected void add_test (string name, TestMethod test) { + var test_case = new GLib.TestCase ( + name, + set_up, + (TestFixtureFunc) test, + tear_down + ); + + suite.add ((owned) test_case); + } + + public virtual void set_up () { + } + + public virtual void tear_down () { + } + + public void assert_finalize_object (ref G data) { + unowned var weak_pointer = data; + ((Object) data).add_weak_pointer (&weak_pointer); + data = null; + assert_null (weak_pointer); + } +} From 3b0168a8ef1f9447a1f2953ff10840b89625a120 Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Sat, 27 Sep 2025 19:52:10 +0200 Subject: [PATCH 2/3] tests: Add simple PropertyTarget test --- meson.build | 3 + meson_options.txt | 1 + tests/lib/PropertyTargetTest.vala | 157 ++++++++++++++++++++++++++++++ tests/lib/meson.build | 22 +++++ tests/meson.build | 5 + 5 files changed, 188 insertions(+) create mode 100644 tests/lib/PropertyTargetTest.vala create mode 100644 tests/lib/meson.build create mode 100644 tests/meson.build diff --git a/meson.build b/meson.build index bff4da79c..054408e3f 100644 --- a/meson.build +++ b/meson.build @@ -168,6 +168,9 @@ subdir('plugins/template') if get_option('documentation') subdir('docs') endif +if get_option('tests') + subdir('tests') +endif subdir('po') vapigen = find_program('vapigen', required: false) diff --git a/meson_options.txt b/meson_options.txt index 54b3ac94c..2fbd384a6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,4 @@ option ('documentation', type : 'boolean', value : false) +option ('tests', type : 'boolean', value : false) option ('systemd', type : 'boolean', value : true) option ('systemduserunitdir', type : 'string', value : '') diff --git a/tests/lib/PropertyTargetTest.vala b/tests/lib/PropertyTargetTest.vala new file mode 100644 index 000000000..054da2207 --- /dev/null +++ b/tests/lib/PropertyTargetTest.vala @@ -0,0 +1,157 @@ +/* + * Copyright 2025 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +public class MockObject : Object { + public int int_value { get; set; } + public double double_value { get; set; } +} + +public class Gala.PropertyTargetTest : TestCase { + private MockObject? target; + private PropertyTarget? default_int_prop_target; + + public PropertyTargetTest () { + Object (name: "PropertyTarget"); + } + + construct { + add_test ("simple propagation", test_simple_propagation); + add_test ("double propagation", test_double_propagation); + add_test ("other actions", test_other_actions); + add_test ("finalize object first", test_finalize_object_first); + add_test ("finalize property target first", test_finalize_property_target_first); + } + + public override void set_up () { + target = new MockObject (); + default_int_prop_target = new PropertyTarget ( + MULTITASKING_VIEW, + target, + "int-value", + typeof (int), + 0, + 10 + ); + } + + public override void tear_down () { + target = null; + default_int_prop_target = null; + } + + private void test_simple_propagation () { + assert_nonnull (&default_int_prop_target); + assert_nonnull (&target); + assert_cmpint (target.int_value, EQ, 0); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.0); + assert_cmpint (target.int_value, EQ, 0); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.5); + assert_cmpint (target.int_value, EQ, 5); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 1.0); + assert_cmpint (target.int_value, EQ, 10); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.8); + assert_cmpint (target.int_value, EQ, 8); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.3); + assert_cmpint (target.int_value, EQ, 3); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.6); + assert_cmpint (target.int_value, EQ, 6); + + assert_finalize_object (ref target); + assert_finalize_object (ref default_int_prop_target); + } + + private void test_double_propagation () { + var double_prop_target = new PropertyTarget ( + MULTITASKING_VIEW, + target, + "double-value", + typeof (double), + 0.0, + 2.0 + ); + + assert_nonnull (&double_prop_target); + assert_nonnull (&target); + assert_cmpfloat (target.double_value, EQ, 0.0); + + double_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.0); + assert_cmpfloat (target.double_value, EQ, 0.0); + + double_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.5); + assert_cmpfloat (target.double_value, EQ, 1.0); + + double_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 1.0); + assert_cmpfloat (target.double_value, EQ, 2.0); + + double_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.8); + assert_cmpfloat (target.double_value, EQ, 1.6); + + double_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.3); + assert_cmpfloat (target.double_value, EQ, 0.6); + + double_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.6); + assert_cmpfloat (target.double_value, EQ, 1.2); + + assert_finalize_object (ref target); + assert_finalize_object (ref double_prop_target); + } + + private void test_other_actions () { + assert_nonnull (&default_int_prop_target); + + assert_nonnull (&target); + assert_cmpint (target.int_value, EQ, 0); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.0); + assert_cmpint (target.int_value, EQ, 0); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.5); + assert_cmpint (target.int_value, EQ, 5); + + default_int_prop_target.propagate (UPDATE, SWITCH_WORKSPACE, 1.0); + assert_cmpint (target.int_value, EQ, 5); + + default_int_prop_target.propagate (UPDATE, CUSTOM, 1.0); + assert_cmpint (target.int_value, EQ, 5); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 1.0); + assert_cmpint (target.int_value, EQ, 10); + + assert_finalize_object (ref target); + assert_finalize_object (ref default_int_prop_target); + } + + private void test_finalize_object_first () { + assert_nonnull (&target); + assert_nonnull (&default_int_prop_target); + + // We can finalize the object before the property target because it doesn't hold a strong reference to it + assert_finalize_object (ref target); + + default_int_prop_target.propagate (UPDATE, MULTITASKING_VIEW, 0.5); + + assert_finalize_object (ref default_int_prop_target); + } + + private void test_finalize_property_target_first () { + assert_nonnull (&target); + assert_nonnull (&default_int_prop_target); + + // Finalize the property target before the object and make sure we don't have weak references + // to the object (i.e. we don't crash when finalizing the object) + assert_finalize_object (ref default_int_prop_target); + assert_finalize_object (ref target); + } +} + +public int main (string[] args) { + return new Gala.PropertyTargetTest ().run (args); +} diff --git a/tests/lib/meson.build b/tests/lib/meson.build new file mode 100644 index 000000000..18d01ab23 --- /dev/null +++ b/tests/lib/meson.build @@ -0,0 +1,22 @@ +lib_test_sources = files( + meson.project_source_root() / 'lib' / 'Gestures' / 'Gesture.vala', + meson.project_source_root() / 'lib' / 'Gestures' / 'GestureTarget.vala', + meson.project_source_root() / 'lib' / 'Gestures' / 'PropertyTarget.vala', +) + +tests = [ + 'PropertyTargetTest', +] + +foreach test : tests + test_executable = executable( + test, + '@0@.vala'.format(test), + common_test_sources, + lib_test_sources, + dependencies: gala_base_dep, + install: false, + ) + + test(test, test_executable, suite: ['Gala', 'Gala/lib']) +endforeach diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 000000000..a24aa529b --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,5 @@ +common_test_sources = files( + 'TestCase.vala', +) + +subdir('lib') From 82a35a6e924f63ef3e8a231a5f1e3cbec3c3ffcc Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Sat, 27 Sep 2025 19:55:43 +0200 Subject: [PATCH 3/3] CI: Run tests and lint tests --- .github/workflows/main.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 24ce3f063..151d99588 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ on: - synchronize jobs: - build: + build-and-test: runs-on: ubuntu-22.04 @@ -36,9 +36,12 @@ jobs: env: DESTDIR: out run: | - meson build -Ddocumentation=true + meson build -Ddocumentation=true -Dtests=true ninja -C build ninja -C build install + - name: Run Tests + run: | + meson test -v -C build fedora: runs-on: ubuntu-latest @@ -93,3 +96,4 @@ jobs: io.elementary.vala-lint -d lib io.elementary.vala-lint -d plugins io.elementary.vala-lint -d src + io.elementary.vala-lint -d tests