Skip to content

Commit 491f824

Browse files
committed
tests: Add simple GestureController test
1 parent 2720039 commit 491f824

File tree

6 files changed

+249
-0
lines changed

6 files changed

+249
-0
lines changed

meson.build

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ subdir('plugins/template')
166166
if get_option('documentation')
167167
subdir('docs')
168168
endif
169+
if get_option('tests')
170+
subdir('tests')
171+
endif
169172
subdir('po')
170173

171174
vapigen = find_program('vapigen', required: false)

meson_options.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
option ('documentation', type : 'boolean', value : false)
2+
option ('tests', type : 'boolean', value : true)
23
option ('systemd', type : 'boolean', value : true)
34
option ('systemduserunitdir', type : 'string', value : '')

tests/MockWindowManager.vala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2025 elementary, Inc. (https://elementary.io)
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*/
5+
6+
public class Gala.MockWindowManager : Meta.Plugin, WindowManager {
7+
public Clutter.Actor ui_group { get; protected set; }
8+
public Clutter.Stage stage { get; protected set; }
9+
public Clutter.Actor window_group { get; protected set; }
10+
public Clutter.Actor top_window_group { get; protected set; }
11+
public Meta.BackgroundGroup background_group { get; protected set; }
12+
13+
public virtual ModalProxy push_modal (Clutter.Actor actor, bool grab) {
14+
return new ModalProxy ();
15+
}
16+
17+
public virtual void pop_modal (ModalProxy proxy) {
18+
}
19+
20+
public virtual bool is_modal () {
21+
return false;
22+
}
23+
24+
public virtual bool modal_proxy_valid (ModalProxy proxy) {
25+
return true;
26+
}
27+
28+
public virtual void perform_action (ActionType type) {
29+
}
30+
31+
public virtual void move_window (Meta.Window? window, Meta.Workspace workspace, uint32 timestamp) {
32+
}
33+
34+
public virtual void switch_to_next_workspace (Meta.MotionDirection direction, uint32 timestamp) {
35+
}
36+
37+
public virtual void launch_action (string action_key) {
38+
}
39+
40+
public virtual bool filter_action (GestureAction action) {
41+
return false;
42+
}
43+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
* Copyright 2025 elementary, Inc. (https://elementary.io)
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*/
5+
6+
public class Gala.MockRootTarget : Object, GestureTarget, RootTarget {
7+
public struct ReceivedUpdate {
8+
public UpdateType type;
9+
public GestureAction action;
10+
public double progress;
11+
}
12+
13+
public Clutter.Actor? actor { get { return null; } }
14+
15+
public Gee.LinkedList<ReceivedUpdate?> received_updates { get; construct; }
16+
17+
construct {
18+
received_updates = new Gee.LinkedList<ReceivedUpdate?> ();
19+
}
20+
21+
public override void propagate (UpdateType update_type, GestureAction action, double progress) {
22+
received_updates.add (ReceivedUpdate () {
23+
type = update_type,
24+
action = action,
25+
progress = progress
26+
});
27+
}
28+
}
29+
30+
internal class Gala.MockGestureBackend : Object, GestureBackend {
31+
}
32+
33+
public class Gala.GestureControllerTest : TestCase {
34+
private Settings gala_settings;
35+
36+
private MockGestureBackend? backend;
37+
private GestureController? controller;
38+
private MockRootTarget? root_target;
39+
40+
public GestureControllerTest () {
41+
Object (name: "GestureController");
42+
}
43+
44+
construct {
45+
new GestureSettings (); // Ensure static variables are initialized
46+
47+
// Make sure we configure swipe up to trigger multitasking view
48+
gala_settings = new GLib.Settings ("io.elementary.desktop.wm.gestures");
49+
gala_settings.delay ();
50+
gala_settings.set_string ("three-finger-swipe-up", "multitasking-view");
51+
52+
add_test ("initial update", test_initial_update);
53+
add_test ("simple propagation", test_simple_propagation);
54+
}
55+
56+
public override void set_up () {
57+
backend = new MockGestureBackend ();
58+
59+
controller = new GestureController (MULTITASKING_VIEW, new MockWindowManager ());
60+
controller.enable_backend (backend);
61+
62+
root_target = new MockRootTarget ();
63+
root_target.add_gesture_controller (controller);
64+
}
65+
66+
public override void tear_down () {
67+
backend = null;
68+
controller = null;
69+
root_target = null;
70+
}
71+
72+
private void test_initial_update () {
73+
// Test that we got the initial update when the controller was attached
74+
assert_cmpint (root_target.received_updates.size, EQ, 1);
75+
76+
var sync_update = root_target.received_updates.poll ();
77+
78+
assert_true (sync_update != null);
79+
assert_cmpint (sync_update.type, EQ, GestureTarget.UpdateType.UPDATE);
80+
assert_cmpint (sync_update.action, EQ, GestureAction.MULTITASKING_VIEW);
81+
assert_cmpfloat (sync_update.progress, EQ, 0.0);
82+
83+
assert_true (root_target.received_updates.is_empty);
84+
}
85+
86+
private void test_simple_propagation () {
87+
root_target.received_updates.poll (); // Remove the initial update
88+
89+
// Test a simple propagation flow
90+
var gesture = new Gesture () {
91+
type = TOUCHPAD_SWIPE,
92+
direction = UP,
93+
fingers = 3,
94+
};
95+
96+
var recognizing = backend.on_gesture_detected (gesture, 0);
97+
98+
assert_true (recognizing);
99+
assert_true (root_target.received_updates.is_empty);
100+
assert_true (controller.recognizing);
101+
102+
backend.on_begin (0, 0);
103+
104+
assert_true (root_target.received_updates.size == 1);
105+
106+
var update = root_target.received_updates.poll ();
107+
assert_true (update != null);
108+
assert_true (update.type == START);
109+
assert_true (update.action == MULTITASKING_VIEW);
110+
assert_true (update.progress == 0.0);
111+
112+
backend.on_update (0.25, 0);
113+
backend.on_update (0.5, 0);
114+
115+
assert_cmpfloat (controller.progress, EQ, 0.5);
116+
117+
backend.on_update (0.75, 0);
118+
backend.on_update (1.0, 0);
119+
120+
assert_true (root_target.received_updates.size == 4);
121+
122+
for (int i = 0; i < 4; i++) {
123+
update = root_target.received_updates.poll ();
124+
assert_true (update != null);
125+
assert_true (update.type == UPDATE);
126+
assert_true (update.action == MULTITASKING_VIEW);
127+
assert_cmpfloat (update.progress, EQ, (i + 1) * 0.25);
128+
}
129+
130+
backend.on_end (1.0, 0);
131+
132+
assert_true (root_target.received_updates.size == 3);
133+
134+
update = root_target.received_updates.poll ();
135+
assert_true (update != null);
136+
assert_true (update.type == UPDATE);
137+
assert_true (update.action == MULTITASKING_VIEW);
138+
assert_cmpfloat (update.progress, EQ, 1.0);
139+
140+
update = root_target.received_updates.poll ();
141+
assert_true (update != null);
142+
assert_true (update.type == COMMIT);
143+
assert_true (update.action == MULTITASKING_VIEW);
144+
assert_cmpfloat (update.progress, EQ, 1.0);
145+
146+
update = root_target.received_updates.poll ();
147+
assert_true (update != null);
148+
assert_true (update.type == END);
149+
assert_true (update.action == MULTITASKING_VIEW);
150+
assert_cmpfloat (update.progress, EQ, 1.0);
151+
152+
assert_true (root_target.received_updates.is_empty);
153+
154+
assert_false (controller.recognizing);
155+
assert_cmpfloat (controller.progress, EQ, 1.0);
156+
157+
assert_finalize_object<MockRootTarget> (ref root_target);
158+
assert_finalize_object<GestureController> (ref controller);
159+
assert_finalize_object<MockGestureBackend> (ref backend);
160+
}
161+
}
162+
163+
public int main (string[] args) {
164+
return new Gala.GestureControllerTest ().run (args);
165+
}

tests/lib/meson.build

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
lib_test_sources = files(
2+
meson.project_source_root() / 'lib' / 'Gestures' / 'Gesture.vala',
3+
meson.project_source_root() / 'lib' / 'Gestures' / 'GestureBackend.vala',
4+
meson.project_source_root() / 'lib' / 'Gestures' / 'GestureController.vala',
5+
meson.project_source_root() / 'lib' / 'Gestures' / 'GestureSettings.vala',
6+
meson.project_source_root() / 'lib' / 'Gestures' / 'GestureTarget.vala',
7+
meson.project_source_root() / 'lib' / 'Gestures' / 'RootTarget.vala',
8+
meson.project_source_root() / 'lib' / 'Gestures' / 'ScrollBackend.vala',
9+
meson.project_source_root() / 'lib' / 'Gestures' / 'SpringTimeline.vala',
10+
meson.project_source_root() / 'lib' / 'Gestures' / 'ToucheggBackend.vala',
11+
meson.project_source_root() / 'lib' / 'Gestures' / 'TouchpadBackend.vala',
12+
)
13+
14+
tests = [
15+
'GestureControllerTest',
16+
]
17+
18+
foreach test : tests
19+
test_executable = executable(
20+
test,
21+
'@[email protected]'.format(test),
22+
common_test_sources,
23+
lib_test_sources,
24+
dependencies: gala_base_dep,
25+
install: false,
26+
)
27+
28+
test(test, test_executable, suite: ['Gala', 'Gala/lib'])
29+
endforeach

tests/meson.build

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
common_test_sources = files(
2+
'MockWindowManager.vala',
3+
'TestCase.vala',
4+
meson.project_source_root() / 'lib' / 'CommonEnums.vala',
5+
meson.project_source_root() / 'lib' / 'WindowManager.vala',
6+
)
7+
8+
subdir('lib')

0 commit comments

Comments
 (0)