Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
ninja -C build install
- name: Run Tests
run: |
meson test -v -C build
meson test -v -C build --no-suite Gala/mutter

fedora:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ common_test_sources = files(
)

subdir('lib')
subdir('mutter')
77 changes: 77 additions & 0 deletions tests/mutter/MutterTestCase.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2026 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
*/

public class Gala.MockPlugin : Meta.Plugin {

}

/**
* Base class for tests that need to interact with Mutter and Clutter.
* Sets up a Meta.Context, starts it, and provides access to the context
* and stage for derived test classes. Note that the context and therefore
* stage are shared between all tests and not recreated during set_up/tear_down,
* because Mutter doesn't allow that.
* If you need the main loop to run use {@link run_main_loop} and {@link quit_main_loop}
* instead of {@link Meta.Context.run_main_loop} and {@link Meta.Context.terminate}.
*/
public abstract class Gala.MutterTestCase : Gala.TestCase {
private const string[] MUTTER_ARGS = {
"--wayland", "--headless", "--sm-disable", "--no-x11",
"--wayland-display", "wayland-1",
"--virtual-monitor", "1280x720@60"
};

protected Meta.Context context { get; private set; }
protected Clutter.Stage stage { get; private set; }

private MainLoop? main_loop;

construct {
context = new Meta.Context ("");

unowned var unowned_args = MUTTER_ARGS;
try {
context.configure (ref unowned_args);
} catch (Error e) {
assert_no_error (e);
}

context.set_plugin_gtype (typeof (MockPlugin));

try {
context.setup ();
} catch (Error e) {
assert_no_error (e);
}

try {
context.start ();
} catch (Error e) {
assert_no_error (e);
}

stage = (Clutter.Stage) context.get_backend ().get_stage ();
}

public override void set_up () {
main_loop = new MainLoop (null, false);
}

public override void tear_down () {
main_loop = null;
}

protected void run_main_loop () {
assert_true (main_loop != null);
main_loop.run ();
}

protected void quit_main_loop () {
assert_true (main_loop != null);
main_loop.quit ();
}
}
94 changes: 94 additions & 0 deletions tests/mutter/SetupTest.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2026 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
*/

/**
* More of a test for our testing infrastructure and not really
* for testing any specific functionality of Gala.
* Check that the MutterTestCase base class successfully sets up meta and clutter
* and allows us to interact with it, e.g. by creating a Clutter actor.
*/
public class Gala.SetupTest : MutterTestCase {
public SetupTest () {
Object (name: "SetupTest");
}

construct {
add_test ("Test setup successful", test_setup_successful);
add_test ("Test main loop", test_main_loop);
add_test ("Test main loop can run twice", test_main_loop);
add_test ("Test actor animation", test_actor_animation);
}

/**
* Check whether setup was successful, i.e. we have a
* backend, clutter backend, context, etc.
*/
private void test_setup_successful () {
assert_true (context != null);

var display = context.get_display ();
assert_true (display != null);

var backend = context.get_backend ();
assert_true (backend != null);

var stage = backend.get_stage ();
assert_true (stage != null);
assert_true (stage is Clutter.Stage);
assert_true (this.stage == stage);

// Creating an actor requires clutter machinery to be set up, so check this
var actor = new Clutter.Actor ();
assert_true (actor != null);
}

private void test_main_loop () {
assert_true (context != null);

var ran = false;

Idle.add_once (() => ran = true);
Idle.add_once (quit_main_loop);

run_main_loop ();

assert_true (ran);
}

private void test_actor_animation () {
assert_true (stage != null);

var frames = 0;

var timeline = new Clutter.Timeline.for_actor (stage, 100);
timeline.new_frame.connect (() => frames++);

stage.show ();

timeline.start ();

Timeout.add (50, () => {
assert_true (timeline.is_playing ());
return Source.REMOVE;
});

Timeout.add (150, () => {
assert_false (timeline.is_playing ());
quit_main_loop ();
return Source.REMOVE;
});

run_main_loop ();

Test.message ("Got %d frames", frames);
assert_cmpint (frames, GT, 0);
}
}

public int main (string[] args) {
return new Gala.SetupTest ().run (args);
}
25 changes: 25 additions & 0 deletions tests/mutter/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
mutter_test_sources = files(
'MutterTestCase.vala',
meson.project_source_root() / 'lib' / 'CommonEnums.vala',
meson.project_source_root() / 'lib' / 'WindowManager.vala',
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 = [
'SetupTest',
]

foreach test : tests
test_executable = executable(
test,
'@0@.vala'.format(test),
common_test_sources,
mutter_test_sources,
dependencies: gala_base_dep,
install: false,
)

test(test, test_executable, suite: ['Gala', 'Gala/mutter'])
endforeach