Merge lp:~robertcarr/mir/refactor-input-acceptance-take-1 into lp:mir
- refactor-input-acceptance-take-1
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Robert Carr |
Approved revision: | no longer in the source branch. |
Merged at revision: | 1210 |
Proposed branch: | lp:~robertcarr/mir/refactor-input-acceptance-take-1 |
Merge into: | lp:mir |
Prerequisite: | lp:~robertcarr/mir/client-input-report |
Diff against target: |
1323 lines (+479/-665) 4 files modified
include/test/mir_test/client_event_matchers.h (+119/-0) include/test/mir_test_framework/input_testing_server_configuration.h (+2/-10) tests/acceptance-tests/test_client_input.cpp (+358/-543) tests/mir_test_framework/input_testing_server_options.cpp (+0/-112) |
To merge this branch: | bzr merge lp:~robertcarr/mir/refactor-input-acceptance-take-1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexandros Frantzis (community) | Approve | ||
Alan Griffiths | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+194265@code.launchpad.net |
This proposal supersedes a proposal from 2013-11-07.
Commit message
Some cleanups to test_client_
Description of the change
First take at simplifying input acceptance tests with a better shared fixture.
Merged client-input-report to debug an issue, so hopefully we can merge this after.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1206
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1207
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
Looks sane in general. I am not familiar enough with the functionality to be sure everything is covered adequately.
Alexandros Frantzis (afrantzis) wrote : | # |
Looks good.
Robert Carr (robertcarr) wrote : | # |
The tests are basically identical. Mostly about.
1. Removing some dead code (wait_until_
2. Factoring out highly repetitive code: i.e. this sort of stuff
918 - struct ServerConfiguration : mtf::InputTesti
919 - {
920 - mtf::CrossProce
921 -
922 - ServerConfigura
923 - : input_cb_
--------------
0 - void inject_input()
301 - {
302 - wait_until_
303 - input_cb_
304 -
309 - } server_
310 - launch_
-------------
311 -
312 - struct KeyReceivingClient : InputClient
313 - {
314 - KeyReceivingCli
3. Removing some noise from the file, i.e. client_
I can't quite see the end yet so am trying to make small improvements like this, but would eventually like to get to something super succint:
mtf::InputRecei
client.
Preview Diff
1 | === added file 'include/test/mir_test/client_event_matchers.h' | |||
2 | --- include/test/mir_test/client_event_matchers.h 1970-01-01 00:00:00 +0000 | |||
3 | +++ include/test/mir_test/client_event_matchers.h 2013-11-07 17:24:53 +0000 | |||
4 | @@ -0,0 +1,119 @@ | |||
5 | 1 | /* | ||
6 | 2 | * Copyright © 2013 Canonical Ltd. | ||
7 | 3 | * | ||
8 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
9 | 5 | * under the terms of the GNU General Public License version 3, | ||
10 | 6 | * as published by the Free Software Foundation. | ||
11 | 7 | * | ||
12 | 8 | * This program is distributed in the hope that it will be useful, | ||
13 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | 11 | * GNU General Public License for more details. | ||
16 | 12 | * | ||
17 | 13 | * You should have received a copy of the GNU General Public License | ||
18 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | 15 | * | ||
20 | 16 | * Authored by: Robert Carr <robert.carr@canonical.com> | ||
21 | 17 | */ | ||
22 | 18 | |||
23 | 19 | #include "mir_toolkit/event.h" | ||
24 | 20 | |||
25 | 21 | #include <xkbcommon/xkbcommon.h> | ||
26 | 22 | #include <xkbcommon/xkbcommon-keysyms.h> | ||
27 | 23 | |||
28 | 24 | #include <gmock/gmock.h> | ||
29 | 25 | |||
30 | 26 | namespace mir | ||
31 | 27 | { | ||
32 | 28 | namespace test | ||
33 | 29 | { | ||
34 | 30 | |||
35 | 31 | MATCHER(KeyDownEvent, "") | ||
36 | 32 | { | ||
37 | 33 | if (arg->type != mir_event_type_key) | ||
38 | 34 | return false; | ||
39 | 35 | if (arg->key.action != mir_key_action_down) // Key down | ||
40 | 36 | return false; | ||
41 | 37 | |||
42 | 38 | return true; | ||
43 | 39 | } | ||
44 | 40 | MATCHER_P(KeyOfSymbol, keysym, "") | ||
45 | 41 | { | ||
46 | 42 | if (static_cast<xkb_keysym_t>(arg->key.key_code) == (uint)keysym) | ||
47 | 43 | return true; | ||
48 | 44 | return false; | ||
49 | 45 | } | ||
50 | 46 | |||
51 | 47 | MATCHER(HoverEnterEvent, "") | ||
52 | 48 | { | ||
53 | 49 | if (arg->type != mir_event_type_motion) | ||
54 | 50 | return false; | ||
55 | 51 | if (arg->motion.action != mir_motion_action_hover_enter) | ||
56 | 52 | return false; | ||
57 | 53 | |||
58 | 54 | return true; | ||
59 | 55 | } | ||
60 | 56 | MATCHER(HoverExitEvent, "") | ||
61 | 57 | { | ||
62 | 58 | if (arg->type != mir_event_type_motion) | ||
63 | 59 | return false; | ||
64 | 60 | if (arg->motion.action != mir_motion_action_hover_exit) | ||
65 | 61 | return false; | ||
66 | 62 | |||
67 | 63 | return true; | ||
68 | 64 | } | ||
69 | 65 | |||
70 | 66 | MATCHER_P2(ButtonDownEvent, x, y, "") | ||
71 | 67 | { | ||
72 | 68 | if (arg->type != mir_event_type_motion) | ||
73 | 69 | return false; | ||
74 | 70 | if (arg->motion.action != mir_motion_action_down) | ||
75 | 71 | return false; | ||
76 | 72 | if (arg->motion.button_state == 0) | ||
77 | 73 | return false; | ||
78 | 74 | if (arg->motion.pointer_coordinates[0].x != x) | ||
79 | 75 | return false; | ||
80 | 76 | if (arg->motion.pointer_coordinates[0].y != y) | ||
81 | 77 | return false; | ||
82 | 78 | return true; | ||
83 | 79 | } | ||
84 | 80 | |||
85 | 81 | MATCHER_P2(ButtonUpEvent, x, y, "") | ||
86 | 82 | { | ||
87 | 83 | if (arg->type != mir_event_type_motion) | ||
88 | 84 | return false; | ||
89 | 85 | if (arg->motion.action != mir_motion_action_up) | ||
90 | 86 | return false; | ||
91 | 87 | if (arg->motion.pointer_coordinates[0].x != x) | ||
92 | 88 | return false; | ||
93 | 89 | if (arg->motion.pointer_coordinates[0].y != y) | ||
94 | 90 | return false; | ||
95 | 91 | return true; | ||
96 | 92 | } | ||
97 | 93 | |||
98 | 94 | MATCHER_P2(MotionEventWithPosition, x, y, "") | ||
99 | 95 | { | ||
100 | 96 | if (arg->type != mir_event_type_motion) | ||
101 | 97 | return false; | ||
102 | 98 | if (arg->motion.action != mir_motion_action_move && | ||
103 | 99 | arg->motion.action != mir_motion_action_hover_move) | ||
104 | 100 | return false; | ||
105 | 101 | if (arg->motion.pointer_coordinates[0].x != x) | ||
106 | 102 | return false; | ||
107 | 103 | if (arg->motion.pointer_coordinates[0].y != y) | ||
108 | 104 | return false; | ||
109 | 105 | return true; | ||
110 | 106 | } | ||
111 | 107 | |||
112 | 108 | MATCHER(MovementEvent, "") | ||
113 | 109 | { | ||
114 | 110 | if (arg->type != mir_event_type_motion) | ||
115 | 111 | return false; | ||
116 | 112 | if (arg->motion.action != mir_motion_action_move && | ||
117 | 113 | arg->motion.action != mir_motion_action_hover_move) | ||
118 | 114 | return false; | ||
119 | 115 | return true; | ||
120 | 116 | } | ||
121 | 117 | |||
122 | 118 | } | ||
123 | 119 | } | ||
124 | 0 | 120 | ||
125 | === modified file 'include/test/mir_test_framework/input_testing_server_configuration.h' | |||
126 | --- include/test/mir_test_framework/input_testing_server_configuration.h 2013-08-28 03:41:48 +0000 | |||
127 | +++ include/test/mir_test_framework/input_testing_server_configuration.h 2013-11-07 17:24:53 +0000 | |||
128 | @@ -52,8 +52,6 @@ | |||
129 | 52 | namespace mir_test_framework | 52 | namespace mir_test_framework |
130 | 53 | { | 53 | { |
131 | 54 | 54 | ||
132 | 55 | enum ClientLifecycleState { starting, appeared, vanished }; | ||
133 | 56 | |||
134 | 57 | class InputTestingServerConfiguration : public TestingServerConfiguration | 55 | class InputTestingServerConfiguration : public TestingServerConfiguration |
135 | 58 | { | 56 | { |
136 | 59 | public: | 57 | public: |
137 | @@ -63,24 +61,18 @@ | |||
138 | 63 | void on_exit(); | 61 | void on_exit(); |
139 | 64 | 62 | ||
140 | 65 | std::shared_ptr<mir::input::InputConfiguration> the_input_configuration() override; | 63 | std::shared_ptr<mir::input::InputConfiguration> the_input_configuration() override; |
142 | 66 | std::shared_ptr<mir::frontend::Shell> the_frontend_shell() override; | 64 | |
143 | 65 | mir::input::android::FakeEventHub* fake_event_hub; | ||
144 | 67 | 66 | ||
145 | 68 | protected: | 67 | protected: |
146 | 69 | virtual void inject_input() = 0; | 68 | virtual void inject_input() = 0; |
147 | 70 | mir::input::android::FakeEventHub* fake_event_hub; | ||
148 | 71 | 69 | ||
149 | 72 | void wait_until_client_appears(std::string const& surface_name); | 70 | void wait_until_client_appears(std::string const& surface_name); |
150 | 73 | 71 | ||
151 | 74 | private: | 72 | private: |
152 | 75 | std::mutex lifecycle_lock; | ||
153 | 76 | |||
154 | 77 | std::condition_variable lifecycle_condition; | ||
155 | 78 | std::map<std::string, ClientLifecycleState> client_lifecycles; | ||
156 | 79 | |||
157 | 80 | std::thread input_injection_thread; | 73 | std::thread input_injection_thread; |
158 | 81 | 74 | ||
159 | 82 | std::shared_ptr<mir::test::doubles::FakeEventHubInputConfiguration> input_configuration; | 75 | std::shared_ptr<mir::test::doubles::FakeEventHubInputConfiguration> input_configuration; |
160 | 83 | std::shared_ptr<mir::frontend::Shell> frontend_shell; | ||
161 | 84 | }; | 76 | }; |
162 | 85 | 77 | ||
163 | 86 | } | 78 | } |
164 | 87 | 79 | ||
165 | === modified file 'tests/acceptance-tests/test_client_input.cpp' | |||
166 | --- tests/acceptance-tests/test_client_input.cpp 2013-10-28 21:41:27 +0000 | |||
167 | +++ tests/acceptance-tests/test_client_input.cpp 2013-11-07 17:24:53 +0000 | |||
168 | @@ -35,6 +35,7 @@ | |||
169 | 35 | #include "mir_test/fake_event_hub.h" | 35 | #include "mir_test/fake_event_hub.h" |
170 | 36 | #include "mir_test/event_factory.h" | 36 | #include "mir_test/event_factory.h" |
171 | 37 | #include "mir_test/wait_condition.h" | 37 | #include "mir_test/wait_condition.h" |
172 | 38 | #include "mir_test/client_event_matchers.h" | ||
173 | 38 | #include "mir_test_framework/cross_process_sync.h" | 39 | #include "mir_test_framework/cross_process_sync.h" |
174 | 39 | #include "mir_test_framework/display_server_test_fixture.h" | 40 | #include "mir_test_framework/display_server_test_fixture.h" |
175 | 40 | #include "mir_test_framework/input_testing_server_configuration.h" | 41 | #include "mir_test_framework/input_testing_server_configuration.h" |
176 | @@ -42,9 +43,6 @@ | |||
177 | 42 | #include <gtest/gtest.h> | 43 | #include <gtest/gtest.h> |
178 | 43 | #include <gmock/gmock.h> | 44 | #include <gmock/gmock.h> |
179 | 44 | 45 | ||
180 | 45 | #include <xkbcommon/xkbcommon.h> | ||
181 | 46 | #include <xkbcommon/xkbcommon-keysyms.h> | ||
182 | 47 | |||
183 | 48 | #include <thread> | 46 | #include <thread> |
184 | 49 | #include <functional> | 47 | #include <functional> |
185 | 50 | #include <map> | 48 | #include <map> |
186 | @@ -213,281 +211,6 @@ | |||
187 | 213 | static int const surface_height = 100; | 211 | static int const surface_height = 100; |
188 | 214 | }; | 212 | }; |
189 | 215 | 213 | ||
190 | 216 | MATCHER(KeyDownEvent, "") | ||
191 | 217 | { | ||
192 | 218 | if (arg->type != mir_event_type_key) | ||
193 | 219 | return false; | ||
194 | 220 | if (arg->key.action != mir_key_action_down) // Key down | ||
195 | 221 | return false; | ||
196 | 222 | |||
197 | 223 | return true; | ||
198 | 224 | } | ||
199 | 225 | MATCHER_P(KeyOfSymbol, keysym, "") | ||
200 | 226 | { | ||
201 | 227 | if (static_cast<xkb_keysym_t>(arg->key.key_code) == (uint)keysym) | ||
202 | 228 | return true; | ||
203 | 229 | return false; | ||
204 | 230 | } | ||
205 | 231 | |||
206 | 232 | MATCHER(HoverEnterEvent, "") | ||
207 | 233 | { | ||
208 | 234 | if (arg->type != mir_event_type_motion) | ||
209 | 235 | return false; | ||
210 | 236 | if (arg->motion.action != mir_motion_action_hover_enter) | ||
211 | 237 | return false; | ||
212 | 238 | |||
213 | 239 | return true; | ||
214 | 240 | } | ||
215 | 241 | MATCHER(HoverExitEvent, "") | ||
216 | 242 | { | ||
217 | 243 | if (arg->type != mir_event_type_motion) | ||
218 | 244 | return false; | ||
219 | 245 | if (arg->motion.action != mir_motion_action_hover_exit) | ||
220 | 246 | return false; | ||
221 | 247 | |||
222 | 248 | return true; | ||
223 | 249 | } | ||
224 | 250 | |||
225 | 251 | MATCHER_P2(ButtonDownEvent, x, y, "") | ||
226 | 252 | { | ||
227 | 253 | if (arg->type != mir_event_type_motion) | ||
228 | 254 | return false; | ||
229 | 255 | if (arg->motion.action != mir_motion_action_down) | ||
230 | 256 | return false; | ||
231 | 257 | if (arg->motion.button_state == 0) | ||
232 | 258 | return false; | ||
233 | 259 | if (arg->motion.pointer_coordinates[0].x != x) | ||
234 | 260 | return false; | ||
235 | 261 | if (arg->motion.pointer_coordinates[0].y != y) | ||
236 | 262 | return false; | ||
237 | 263 | return true; | ||
238 | 264 | } | ||
239 | 265 | |||
240 | 266 | MATCHER_P2(ButtonUpEvent, x, y, "") | ||
241 | 267 | { | ||
242 | 268 | if (arg->type != mir_event_type_motion) | ||
243 | 269 | return false; | ||
244 | 270 | if (arg->motion.action != mir_motion_action_up) | ||
245 | 271 | return false; | ||
246 | 272 | if (arg->motion.pointer_coordinates[0].x != x) | ||
247 | 273 | return false; | ||
248 | 274 | if (arg->motion.pointer_coordinates[0].y != y) | ||
249 | 275 | return false; | ||
250 | 276 | return true; | ||
251 | 277 | } | ||
252 | 278 | |||
253 | 279 | MATCHER_P2(MotionEventWithPosition, x, y, "") | ||
254 | 280 | { | ||
255 | 281 | if (arg->type != mir_event_type_motion) | ||
256 | 282 | return false; | ||
257 | 283 | if (arg->motion.action != mir_motion_action_move && | ||
258 | 284 | arg->motion.action != mir_motion_action_hover_move) | ||
259 | 285 | return false; | ||
260 | 286 | if (arg->motion.pointer_coordinates[0].x != x) | ||
261 | 287 | return false; | ||
262 | 288 | if (arg->motion.pointer_coordinates[0].y != y) | ||
263 | 289 | return false; | ||
264 | 290 | return true; | ||
265 | 291 | } | ||
266 | 292 | |||
267 | 293 | MATCHER(MovementEvent, "") | ||
268 | 294 | { | ||
269 | 295 | if (arg->type != mir_event_type_motion) | ||
270 | 296 | return false; | ||
271 | 297 | if (arg->motion.action != mir_motion_action_move && | ||
272 | 298 | arg->motion.action != mir_motion_action_hover_move) | ||
273 | 299 | return false; | ||
274 | 300 | return true; | ||
275 | 301 | } | ||
276 | 302 | |||
277 | 303 | } | ||
278 | 304 | |||
279 | 305 | |||
280 | 306 | using TestClientInput = BespokeDisplayServerTestFixture; | ||
281 | 307 | |||
282 | 308 | TEST_F(TestClientInput, clients_receive_key_input) | ||
283 | 309 | { | ||
284 | 310 | using namespace ::testing; | ||
285 | 311 | |||
286 | 312 | int const num_events_produced = 3; | ||
287 | 313 | static std::string const test_client_name = "1"; | ||
288 | 314 | |||
289 | 315 | mtf::CrossProcessSync fence; | ||
290 | 316 | |||
291 | 317 | struct ServerConfiguration : mtf::InputTestingServerConfiguration | ||
292 | 318 | { | ||
293 | 319 | mtf::CrossProcessSync input_cb_setup_fence; | ||
294 | 320 | |||
295 | 321 | ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence) | ||
296 | 322 | : input_cb_setup_fence(input_cb_setup_fence) | ||
297 | 323 | { | ||
298 | 324 | } | ||
299 | 325 | |||
300 | 326 | void inject_input() | ||
301 | 327 | { | ||
302 | 328 | wait_until_client_appears(test_client_name); | ||
303 | 329 | input_cb_setup_fence.wait_for_signal_ready_for(); | ||
304 | 330 | |||
305 | 331 | for (int i = 0; i < num_events_produced; i++) | ||
306 | 332 | fake_event_hub->synthesize_event(mis::a_key_down_event() | ||
307 | 333 | .of_scancode(KEY_ENTER)); | ||
308 | 334 | } | ||
309 | 335 | } server_config(fence); | ||
310 | 336 | launch_server_process(server_config); | ||
311 | 337 | |||
312 | 338 | struct KeyReceivingClient : InputClient | ||
313 | 339 | { | ||
314 | 340 | KeyReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {} | ||
315 | 341 | void expect_input(mt::WaitCondition& events_received) override | ||
316 | 342 | { | ||
317 | 343 | using namespace ::testing; | ||
318 | 344 | InSequence seq; | ||
319 | 345 | |||
320 | 346 | EXPECT_CALL(*handler, handle_input(KeyDownEvent())).Times(2); | ||
321 | 347 | EXPECT_CALL(*handler, handle_input(KeyDownEvent())).Times(1) | ||
322 | 348 | .WillOnce(mt::WakeUp(&events_received)); | ||
323 | 349 | } | ||
324 | 350 | } client_config(fence); | ||
325 | 351 | launch_client_process(client_config); | ||
326 | 352 | } | ||
327 | 353 | |||
328 | 354 | TEST_F(TestClientInput, clients_receive_us_english_mapped_keys) | ||
329 | 355 | { | ||
330 | 356 | using namespace ::testing; | ||
331 | 357 | static std::string const test_client_name = "1"; | ||
332 | 358 | mtf::CrossProcessSync fence; | ||
333 | 359 | |||
334 | 360 | struct ServerConfiguration : mtf::InputTestingServerConfiguration | ||
335 | 361 | { | ||
336 | 362 | mtf::CrossProcessSync input_cb_setup_fence; | ||
337 | 363 | |||
338 | 364 | ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence) | ||
339 | 365 | : input_cb_setup_fence(input_cb_setup_fence) | ||
340 | 366 | { | ||
341 | 367 | } | ||
342 | 368 | |||
343 | 369 | void inject_input() | ||
344 | 370 | { | ||
345 | 371 | wait_until_client_appears(test_client_name); | ||
346 | 372 | input_cb_setup_fence.wait_for_signal_ready_for(); | ||
347 | 373 | |||
348 | 374 | fake_event_hub->synthesize_event(mis::a_key_down_event() | ||
349 | 375 | .of_scancode(KEY_LEFTSHIFT)); | ||
350 | 376 | fake_event_hub->synthesize_event(mis::a_key_down_event() | ||
351 | 377 | .of_scancode(KEY_4)); | ||
352 | 378 | |||
353 | 379 | } | ||
354 | 380 | } server_config{fence}; | ||
355 | 381 | launch_server_process(server_config); | ||
356 | 382 | |||
357 | 383 | struct KeyReceivingClient : InputClient | ||
358 | 384 | { | ||
359 | 385 | KeyReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {} | ||
360 | 386 | |||
361 | 387 | void expect_input(mt::WaitCondition& events_received) override | ||
362 | 388 | { | ||
363 | 389 | using namespace ::testing; | ||
364 | 390 | |||
365 | 391 | InSequence seq; | ||
366 | 392 | EXPECT_CALL(*handler, handle_input(AllOf(KeyDownEvent(), KeyOfSymbol(XKB_KEY_Shift_L)))).Times(1); | ||
367 | 393 | EXPECT_CALL(*handler, handle_input(AllOf(KeyDownEvent(), KeyOfSymbol(XKB_KEY_dollar)))).Times(1) | ||
368 | 394 | .WillOnce(mt::WakeUp(&events_received)); | ||
369 | 395 | } | ||
370 | 396 | } client_config{fence}; | ||
371 | 397 | launch_client_process(client_config); | ||
372 | 398 | } | ||
373 | 399 | |||
374 | 400 | TEST_F(TestClientInput, clients_receive_motion_inside_window) | ||
375 | 401 | { | ||
376 | 402 | using namespace ::testing; | ||
377 | 403 | static std::string const test_client_name = "1"; | ||
378 | 404 | mtf::CrossProcessSync fence; | ||
379 | 405 | |||
380 | 406 | struct ServerConfiguration : public mtf::InputTestingServerConfiguration | ||
381 | 407 | { | ||
382 | 408 | mtf::CrossProcessSync input_cb_setup_fence; | ||
383 | 409 | |||
384 | 410 | ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence) | ||
385 | 411 | : input_cb_setup_fence(input_cb_setup_fence) | ||
386 | 412 | { | ||
387 | 413 | } | ||
388 | 414 | |||
389 | 415 | void inject_input() | ||
390 | 416 | { | ||
391 | 417 | wait_until_client_appears(test_client_name); | ||
392 | 418 | input_cb_setup_fence.wait_for_signal_ready_for(); | ||
393 | 419 | |||
394 | 420 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(InputClient::surface_width - 1, | ||
395 | 421 | InputClient::surface_height - 1)); | ||
396 | 422 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,2)); | ||
397 | 423 | } | ||
398 | 424 | } server_config{fence}; | ||
399 | 425 | launch_server_process(server_config); | ||
400 | 426 | |||
401 | 427 | struct MotionReceivingClient : InputClient | ||
402 | 428 | { | ||
403 | 429 | MotionReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {} | ||
404 | 430 | |||
405 | 431 | void expect_input(mt::WaitCondition& events_received) override | ||
406 | 432 | { | ||
407 | 433 | using namespace ::testing; | ||
408 | 434 | |||
409 | 435 | InSequence seq; | ||
410 | 436 | |||
411 | 437 | // We should see the cursor enter | ||
412 | 438 | EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1); | ||
413 | 439 | EXPECT_CALL(*handler, handle_input( | ||
414 | 440 | MotionEventWithPosition(InputClient::surface_width - 1, | ||
415 | 441 | InputClient::surface_height - 1))).Times(1) | ||
416 | 442 | .WillOnce(mt::WakeUp(&events_received)); | ||
417 | 443 | // But we should not receive an event for the second movement outside of our surface! | ||
418 | 444 | } | ||
419 | 445 | } client_config{fence}; | ||
420 | 446 | launch_client_process(client_config); | ||
421 | 447 | } | ||
422 | 448 | |||
423 | 449 | TEST_F(TestClientInput, clients_receive_button_events_inside_window) | ||
424 | 450 | { | ||
425 | 451 | using namespace ::testing; | ||
426 | 452 | |||
427 | 453 | static std::string const test_client_name = "1"; | ||
428 | 454 | mtf::CrossProcessSync fence; | ||
429 | 455 | |||
430 | 456 | struct ServerConfiguration : public mtf::InputTestingServerConfiguration | ||
431 | 457 | { | ||
432 | 458 | mtf::CrossProcessSync input_cb_setup_fence; | ||
433 | 459 | |||
434 | 460 | ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence) | ||
435 | 461 | : input_cb_setup_fence(input_cb_setup_fence) | ||
436 | 462 | { | ||
437 | 463 | } | ||
438 | 464 | |||
439 | 465 | void inject_input() | ||
440 | 466 | { | ||
441 | 467 | wait_until_client_appears(test_client_name); | ||
442 | 468 | input_cb_setup_fence.wait_for_signal_ready_for(); | ||
443 | 469 | |||
444 | 470 | fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); | ||
445 | 471 | } | ||
446 | 472 | } server_config{fence}; | ||
447 | 473 | launch_server_process(server_config); | ||
448 | 474 | |||
449 | 475 | struct ButtonReceivingClient : InputClient | ||
450 | 476 | { | ||
451 | 477 | ButtonReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {} | ||
452 | 478 | |||
453 | 479 | void expect_input(mt::WaitCondition& events_received) override | ||
454 | 480 | { | ||
455 | 481 | using namespace ::testing; | ||
456 | 482 | |||
457 | 483 | InSequence seq; | ||
458 | 484 | |||
459 | 485 | // The cursor starts at (0, 0). | ||
460 | 486 | EXPECT_CALL(*handler, handle_input(ButtonDownEvent(0, 0))).Times(1) | ||
461 | 487 | .WillOnce(mt::WakeUp(&events_received)); | ||
462 | 488 | } | ||
463 | 489 | } client_config{fence}; | ||
464 | 490 | launch_client_process(client_config); | ||
465 | 491 | } | 214 | } |
466 | 492 | 215 | ||
467 | 493 | namespace | 216 | namespace |
468 | @@ -497,34 +220,251 @@ | |||
469 | 497 | 220 | ||
470 | 498 | struct StaticPlacementStrategy : public msh::PlacementStrategy | 221 | struct StaticPlacementStrategy : public msh::PlacementStrategy |
471 | 499 | { | 222 | { |
473 | 500 | StaticPlacementStrategy(GeometryMap const& positions, | 223 | StaticPlacementStrategy(std::shared_ptr<msh::PlacementStrategy> const& underlying_strategy, |
474 | 224 | GeometryMap const& positions, | ||
475 | 501 | DepthMap const& depths) | 225 | DepthMap const& depths) |
477 | 502 | : surface_geometry_by_name(positions), | 226 | : underlying_strategy(underlying_strategy), |
478 | 227 | surface_geometry_by_name(positions), | ||
479 | 503 | surface_depths_by_name(depths) | 228 | surface_depths_by_name(depths) |
480 | 504 | { | 229 | { |
481 | 505 | } | 230 | } |
482 | 506 | 231 | ||
485 | 507 | StaticPlacementStrategy(GeometryMap const& positions) | 232 | StaticPlacementStrategy(std::shared_ptr<msh::PlacementStrategy> const& underlying_strategy, |
486 | 508 | : StaticPlacementStrategy(positions, DepthMap()) | 233 | GeometryMap const& positions) |
487 | 234 | : StaticPlacementStrategy(underlying_strategy, positions, DepthMap()) | ||
488 | 509 | { | 235 | { |
489 | 510 | } | 236 | } |
490 | 511 | 237 | ||
492 | 512 | msh::SurfaceCreationParameters place(msh::Session const&, msh::SurfaceCreationParameters const& request_parameters) | 238 | msh::SurfaceCreationParameters place(msh::Session const& session, msh::SurfaceCreationParameters const& request_parameters) |
493 | 513 | { | 239 | { |
494 | 514 | auto placed = request_parameters; | 240 | auto placed = request_parameters; |
495 | 515 | auto const& name = request_parameters.name; | 241 | auto const& name = request_parameters.name; |
500 | 516 | auto geometry = surface_geometry_by_name[name]; | 242 | |
501 | 517 | 243 | auto it = surface_geometry_by_name.find(name); | |
502 | 518 | placed.top_left = geometry.top_left; | 244 | if (it != surface_geometry_by_name.end()) |
503 | 519 | placed.size = geometry.size; | 245 | { |
504 | 246 | auto const& geometry = it->second; | ||
505 | 247 | placed.top_left = geometry.top_left; | ||
506 | 248 | placed.size = geometry.size; | ||
507 | 249 | } | ||
508 | 250 | else | ||
509 | 251 | { | ||
510 | 252 | placed = underlying_strategy->place(session, placed); | ||
511 | 253 | } | ||
512 | 520 | placed.depth = surface_depths_by_name[name]; | 254 | placed.depth = surface_depths_by_name[name]; |
513 | 521 | 255 | ||
514 | 522 | return placed; | 256 | return placed; |
515 | 523 | } | 257 | } |
516 | 258 | |||
517 | 259 | std::shared_ptr<msh::PlacementStrategy> const underlying_strategy; | ||
518 | 524 | GeometryMap surface_geometry_by_name; | 260 | GeometryMap surface_geometry_by_name; |
519 | 525 | DepthMap surface_depths_by_name; | 261 | DepthMap surface_depths_by_name; |
520 | 526 | }; | 262 | }; |
521 | 527 | 263 | ||
522 | 264 | std::shared_ptr<mtf::InputTestingServerConfiguration> | ||
523 | 265 | make_event_producing_server(mtf::CrossProcessSync const& client_ready_fence, | ||
524 | 266 | int number_of_clients, | ||
525 | 267 | std::function<void(mtf::InputTestingServerConfiguration& server)> const& produce_events, | ||
526 | 268 | GeometryMap const& client_geometry_map, DepthMap const& client_depth_map) | ||
527 | 269 | { | ||
528 | 270 | struct ServerConfiguration : mtf::InputTestingServerConfiguration | ||
529 | 271 | { | ||
530 | 272 | mtf::CrossProcessSync input_cb_setup_fence; | ||
531 | 273 | int const number_of_clients; | ||
532 | 274 | std::function<void(mtf::InputTestingServerConfiguration& server)> const produce_events; | ||
533 | 275 | GeometryMap const client_geometry; | ||
534 | 276 | DepthMap const client_depth; | ||
535 | 277 | |||
536 | 278 | ServerConfiguration(mtf::CrossProcessSync const& input_cb_setup_fence, int number_of_clients, | ||
537 | 279 | std::function<void(mtf::InputTestingServerConfiguration& server)> const& produce_events, | ||
538 | 280 | GeometryMap const& client_geometry, DepthMap const& client_depth) | ||
539 | 281 | : input_cb_setup_fence(input_cb_setup_fence), | ||
540 | 282 | number_of_clients(number_of_clients), | ||
541 | 283 | produce_events(produce_events), | ||
542 | 284 | client_geometry(client_geometry), | ||
543 | 285 | client_depth(client_depth) | ||
544 | 286 | { | ||
545 | 287 | } | ||
546 | 288 | |||
547 | 289 | std::shared_ptr<msh::PlacementStrategy> the_shell_placement_strategy() override | ||
548 | 290 | { | ||
549 | 291 | return std::make_shared<StaticPlacementStrategy>(InputTestingServerConfiguration::the_shell_placement_strategy(), | ||
550 | 292 | client_geometry, client_depth); | ||
551 | 293 | } | ||
552 | 294 | |||
553 | 295 | void inject_input() | ||
554 | 296 | { | ||
555 | 297 | for (int i = 1; i < number_of_clients + 1; i++) | ||
556 | 298 | EXPECT_EQ(i, input_cb_setup_fence.wait_for_signal_ready_for()); | ||
557 | 299 | produce_events(*this); | ||
558 | 300 | } | ||
559 | 301 | }; | ||
560 | 302 | return std::make_shared<ServerConfiguration>(client_ready_fence, number_of_clients, | ||
561 | 303 | produce_events, client_geometry_map, client_depth_map); | ||
562 | 304 | } | ||
563 | 305 | |||
564 | 306 | std::shared_ptr<mtf::InputTestingServerConfiguration> | ||
565 | 307 | make_event_producing_server(mtf::CrossProcessSync const& client_ready_fence, int number_of_clients, | ||
566 | 308 | std::function<void(mtf::InputTestingServerConfiguration& server)> const& produce_events) | ||
567 | 309 | { | ||
568 | 310 | return make_event_producing_server(client_ready_fence, number_of_clients, | ||
569 | 311 | produce_events, GeometryMap(), DepthMap()); | ||
570 | 312 | } | ||
571 | 313 | |||
572 | 314 | std::shared_ptr<InputClient> | ||
573 | 315 | make_event_expecting_client(std::string const& client_name, mtf::CrossProcessSync const& client_ready_fence, | ||
574 | 316 | std::function<void(MockInputHandler &, mt::WaitCondition&)> const& expect_input) | ||
575 | 317 | { | ||
576 | 318 | struct EventReceivingClient : InputClient | ||
577 | 319 | { | ||
578 | 320 | std::function<void(MockInputHandler&, mt::WaitCondition&)> const expect_cb; | ||
579 | 321 | |||
580 | 322 | EventReceivingClient(mtf::CrossProcessSync const& client_ready_fence, std::string const& client_name, | ||
581 | 323 | std::function<void(MockInputHandler&, mt::WaitCondition&)> const& expect_cb) | ||
582 | 324 | : InputClient(client_ready_fence, client_name), | ||
583 | 325 | expect_cb(expect_cb) | ||
584 | 326 | { | ||
585 | 327 | } | ||
586 | 328 | void expect_input(mt::WaitCondition& events_received) override | ||
587 | 329 | { | ||
588 | 330 | expect_cb(*handler, events_received); | ||
589 | 331 | } | ||
590 | 332 | }; | ||
591 | 333 | return std::make_shared<EventReceivingClient>(client_ready_fence, client_name, expect_input); | ||
592 | 334 | } | ||
593 | 335 | |||
594 | 336 | std::shared_ptr<InputClient> | ||
595 | 337 | make_event_expecting_client(mtf::CrossProcessSync const& client_ready_fence, | ||
596 | 338 | std::function<void(MockInputHandler &, mt::WaitCondition&)> const& expect_input) | ||
597 | 339 | { | ||
598 | 340 | return make_event_expecting_client("input-test-client", client_ready_fence, expect_input); | ||
599 | 341 | } | ||
600 | 342 | |||
601 | 343 | } | ||
602 | 344 | |||
603 | 345 | |||
604 | 346 | using TestClientInput = BespokeDisplayServerTestFixture; | ||
605 | 347 | |||
606 | 348 | TEST_F(TestClientInput, clients_receive_key_input) | ||
607 | 349 | { | ||
608 | 350 | using namespace ::testing; | ||
609 | 351 | |||
610 | 352 | static std::string const test_client_name = "1"; | ||
611 | 353 | |||
612 | 354 | mtf::CrossProcessSync fence; | ||
613 | 355 | |||
614 | 356 | auto server_config = make_event_producing_server(fence, 1, | ||
615 | 357 | [&](mtf::InputTestingServerConfiguration& server) | ||
616 | 358 | { | ||
617 | 359 | int const num_events_produced = 3; | ||
618 | 360 | |||
619 | 361 | for (int i = 0; i < num_events_produced; i++) | ||
620 | 362 | server.fake_event_hub->synthesize_event(mis::a_key_down_event() | ||
621 | 363 | .of_scancode(KEY_ENTER)); | ||
622 | 364 | }); | ||
623 | 365 | launch_server_process(*server_config); | ||
624 | 366 | |||
625 | 367 | auto client_config = make_event_expecting_client(fence, | ||
626 | 368 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) | ||
627 | 369 | { | ||
628 | 370 | using namespace ::testing; | ||
629 | 371 | InSequence seq; | ||
630 | 372 | |||
631 | 373 | EXPECT_CALL(handler, handle_input(mt::KeyDownEvent())).Times(2); | ||
632 | 374 | EXPECT_CALL(handler, handle_input(mt::KeyDownEvent())).Times(1) | ||
633 | 375 | .WillOnce(mt::WakeUp(&events_received)); | ||
634 | 376 | |||
635 | 377 | }); | ||
636 | 378 | launch_client_process(*client_config); | ||
637 | 379 | } | ||
638 | 380 | |||
639 | 381 | TEST_F(TestClientInput, clients_receive_us_english_mapped_keys) | ||
640 | 382 | { | ||
641 | 383 | using namespace ::testing; | ||
642 | 384 | static std::string const test_client_name = "1"; | ||
643 | 385 | mtf::CrossProcessSync fence; | ||
644 | 386 | |||
645 | 387 | auto server_config = make_event_producing_server(fence, 1, | ||
646 | 388 | [&](mtf::InputTestingServerConfiguration& server) | ||
647 | 389 | { | ||
648 | 390 | server.fake_event_hub->synthesize_event(mis::a_key_down_event() | ||
649 | 391 | .of_scancode(KEY_LEFTSHIFT)); | ||
650 | 392 | server.fake_event_hub->synthesize_event(mis::a_key_down_event() | ||
651 | 393 | .of_scancode(KEY_4)); | ||
652 | 394 | }); | ||
653 | 395 | launch_server_process(*server_config); | ||
654 | 396 | |||
655 | 397 | auto client_config = make_event_expecting_client(fence, | ||
656 | 398 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) | ||
657 | 399 | { | ||
658 | 400 | using namespace ::testing; | ||
659 | 401 | InSequence seq; | ||
660 | 402 | |||
661 | 403 | EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_L)))).Times(1); | ||
662 | 404 | EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_dollar)))).Times(1) | ||
663 | 405 | .WillOnce(mt::WakeUp(&events_received)); | ||
664 | 406 | }); | ||
665 | 407 | launch_client_process(*client_config); | ||
666 | 408 | } | ||
667 | 409 | |||
668 | 410 | TEST_F(TestClientInput, clients_receive_motion_inside_window) | ||
669 | 411 | { | ||
670 | 412 | using namespace ::testing; | ||
671 | 413 | static std::string const test_client_name = "1"; | ||
672 | 414 | mtf::CrossProcessSync fence; | ||
673 | 415 | |||
674 | 416 | auto server_config = make_event_producing_server(fence, 1, | ||
675 | 417 | [&](mtf::InputTestingServerConfiguration& server) | ||
676 | 418 | { | ||
677 | 419 | server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(InputClient::surface_width - 1, | ||
678 | 420 | InputClient::surface_height - 1)); | ||
679 | 421 | server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,2)); | ||
680 | 422 | }); | ||
681 | 423 | launch_server_process(*server_config); | ||
682 | 424 | |||
683 | 425 | auto client_config = make_event_expecting_client(fence, | ||
684 | 426 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) | ||
685 | 427 | { | ||
686 | 428 | using namespace ::testing; | ||
687 | 429 | InSequence seq; | ||
688 | 430 | |||
689 | 431 | // We should see the cursor enter | ||
690 | 432 | EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(1); | ||
691 | 433 | EXPECT_CALL(handler, handle_input( | ||
692 | 434 | mt::MotionEventWithPosition(InputClient::surface_width - 1, | ||
693 | 435 | InputClient::surface_height - 1))).Times(1) | ||
694 | 436 | .WillOnce(mt::WakeUp(&events_received)); | ||
695 | 437 | // But we should not receive an event for the second movement outside of our surface! | ||
696 | 438 | }); | ||
697 | 439 | launch_client_process(*client_config); | ||
698 | 440 | } | ||
699 | 441 | |||
700 | 442 | TEST_F(TestClientInput, clients_receive_button_events_inside_window) | ||
701 | 443 | { | ||
702 | 444 | using namespace ::testing; | ||
703 | 445 | |||
704 | 446 | static std::string const test_client_name = "1"; | ||
705 | 447 | mtf::CrossProcessSync fence; | ||
706 | 448 | |||
707 | 449 | auto server_config = make_event_producing_server(fence, 1, | ||
708 | 450 | [&](mtf::InputTestingServerConfiguration& server) | ||
709 | 451 | { | ||
710 | 452 | server.fake_event_hub->synthesize_event(mis::a_button_down_event() | ||
711 | 453 | .of_button(BTN_LEFT).with_action(mis::EventAction::Down)); | ||
712 | 454 | }); | ||
713 | 455 | launch_server_process(*server_config); | ||
714 | 456 | |||
715 | 457 | auto client_config = make_event_expecting_client(fence, | ||
716 | 458 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) | ||
717 | 459 | { | ||
718 | 460 | using namespace ::testing; | ||
719 | 461 | InSequence seq; | ||
720 | 462 | |||
721 | 463 | // The cursor starts at (0, 0). | ||
722 | 464 | EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(0, 0))).Times(1) | ||
723 | 465 | .WillOnce(mt::WakeUp(&events_received)); | ||
724 | 466 | }); | ||
725 | 467 | launch_client_process(*client_config); | ||
726 | 528 | } | 468 | } |
727 | 529 | 469 | ||
728 | 530 | TEST_F(TestClientInput, multiple_clients_receive_motion_inside_windows) | 470 | TEST_F(TestClientInput, multiple_clients_receive_motion_inside_windows) |
729 | @@ -539,77 +479,42 @@ | |||
730 | 539 | static std::string const test_client_2 = "2"; | 479 | static std::string const test_client_2 = "2"; |
731 | 540 | mtf::CrossProcessSync fence; | 480 | mtf::CrossProcessSync fence; |
732 | 541 | 481 | ||
760 | 542 | struct ServerConfiguration : mtf::InputTestingServerConfiguration | 482 | static GeometryMap positions; |
761 | 543 | { | 483 | positions[test_client_1] = geom::Rectangle{geom::Point{0, 0}, |
762 | 544 | mtf::CrossProcessSync input_cb_setup_fence; | 484 | geom::Size{client_width, client_height}}; |
763 | 545 | 485 | positions[test_client_2] = geom::Rectangle{geom::Point{screen_width/2, screen_height/2}, | |
764 | 546 | ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence) | 486 | geom::Size{client_width, client_height}}; |
765 | 547 | : input_cb_setup_fence(input_cb_setup_fence) | 487 | |
766 | 548 | { | 488 | auto server_config = make_event_producing_server(fence, 2, |
767 | 549 | } | 489 | [&](mtf::InputTestingServerConfiguration& server) |
768 | 550 | 490 | { | |
742 | 551 | std::shared_ptr<msh::PlacementStrategy> the_shell_placement_strategy() override | ||
743 | 552 | { | ||
744 | 553 | static GeometryMap positions; | ||
745 | 554 | positions[test_client_1] = geom::Rectangle{geom::Point{0, 0}, | ||
746 | 555 | geom::Size{client_width, client_height}}; | ||
747 | 556 | positions[test_client_2] = geom::Rectangle{geom::Point{screen_width/2, screen_height/2}, | ||
748 | 557 | geom::Size{client_width, client_height}}; | ||
749 | 558 | |||
750 | 559 | return std::make_shared<StaticPlacementStrategy>(positions); | ||
751 | 560 | } | ||
752 | 561 | |||
753 | 562 | void inject_input() override | ||
754 | 563 | { | ||
755 | 564 | wait_until_client_appears(test_client_1); | ||
756 | 565 | EXPECT_EQ(1, input_cb_setup_fence.wait_for_signal_ready_for()); | ||
757 | 566 | wait_until_client_appears(test_client_2); | ||
758 | 567 | EXPECT_EQ(2, input_cb_setup_fence.wait_for_signal_ready_for()); | ||
759 | 568 | |||
769 | 569 | // In the bounds of the first surface | 491 | // In the bounds of the first surface |
771 | 570 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2-1, screen_height/2-1)); | 492 | server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2-1, screen_height/2-1)); |
772 | 571 | // In the bounds of the second surface | 493 | // In the bounds of the second surface |
814 | 572 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2, screen_height/2)); | 494 | server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2, screen_height/2)); |
815 | 573 | } | 495 | }, positions, DepthMap()); |
816 | 574 | } server_config{fence}; | 496 | launch_server_process(*server_config); |
817 | 575 | 497 | ||
818 | 576 | launch_server_process(server_config); | 498 | auto client_1 = make_event_expecting_client(test_client_1, fence, |
819 | 577 | 499 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) | |
820 | 578 | struct InputClientOne : InputClient | 500 | { |
821 | 579 | { | 501 | InSequence seq; |
822 | 580 | InputClientOne(const mtf::CrossProcessSync& fence) | 502 | EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(1); |
823 | 581 | : InputClient(fence, test_client_1) | 503 | EXPECT_CALL(handler, handle_input(mt::MotionEventWithPosition(client_width - 1, client_height - 1))).Times(1); |
824 | 582 | { | 504 | EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(1) |
825 | 583 | } | 505 | .WillOnce(mt::WakeUp(&events_received)); |
826 | 584 | 506 | }); | |
827 | 585 | void expect_input(mt::WaitCondition& events_received) override | 507 | auto client_2 = make_event_expecting_client(test_client_2, fence, |
828 | 586 | { | 508 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) |
829 | 587 | InSequence seq; | 509 | { |
830 | 588 | EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1); | 510 | InSequence seq; |
831 | 589 | EXPECT_CALL(*handler, handle_input(MotionEventWithPosition(client_width - 1, client_height - 1))).Times(1); | 511 | EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(1); |
832 | 590 | EXPECT_CALL(*handler, handle_input(HoverExitEvent())).Times(1) | 512 | EXPECT_CALL(handler, handle_input(mt::MotionEventWithPosition(client_width - 1, client_height - 1))).Times(1) |
833 | 591 | .WillOnce(mt::WakeUp(&events_received)); | 513 | .WillOnce(mt::WakeUp(&events_received)); |
834 | 592 | } | 514 | }); |
835 | 593 | } client_1{fence}; | 515 | |
836 | 594 | 516 | launch_client_process(*client_1); | |
837 | 595 | struct InputClientTwo : InputClient | 517 | launch_client_process(*client_2); |
797 | 596 | { | ||
798 | 597 | InputClientTwo(const mtf::CrossProcessSync& fence) | ||
799 | 598 | : InputClient(fence, test_client_2) | ||
800 | 599 | { | ||
801 | 600 | } | ||
802 | 601 | |||
803 | 602 | void expect_input(mt::WaitCondition& events_received) override | ||
804 | 603 | { | ||
805 | 604 | InSequence seq; | ||
806 | 605 | EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1); | ||
807 | 606 | EXPECT_CALL(*handler, handle_input(MotionEventWithPosition(client_width - 1, client_height - 1))).Times(1) | ||
808 | 607 | .WillOnce(mt::WakeUp(&events_received)); | ||
809 | 608 | } | ||
810 | 609 | } client_2{fence}; | ||
811 | 610 | |||
812 | 611 | launch_client_process(client_1); | ||
813 | 612 | launch_client_process(client_2); | ||
838 | 613 | } | 518 | } |
839 | 614 | 519 | ||
840 | 615 | namespace | 520 | namespace |
841 | @@ -639,6 +544,7 @@ | |||
842 | 639 | std::vector<geom::Rectangle> const input_rectangles; | 544 | std::vector<geom::Rectangle> const input_rectangles; |
843 | 640 | }; | 545 | }; |
844 | 641 | } | 546 | } |
845 | 547 | |||
846 | 642 | TEST_F(TestClientInput, clients_do_not_receive_motion_outside_input_region) | 548 | TEST_F(TestClientInput, clients_do_not_receive_motion_outside_input_region) |
847 | 643 | { | 549 | { |
848 | 644 | using namespace ::testing; | 550 | using namespace ::testing; |
849 | @@ -670,7 +576,7 @@ | |||
850 | 670 | static GeometryMap positions; | 576 | static GeometryMap positions; |
851 | 671 | positions[test_client_name] = screen_geometry; | 577 | positions[test_client_name] = screen_geometry; |
852 | 672 | 578 | ||
854 | 673 | return std::make_shared<StaticPlacementStrategy>(positions); | 579 | return std::make_shared<StaticPlacementStrategy>(InputTestingServerConfiguration::the_shell_placement_strategy(), positions); |
855 | 674 | } | 580 | } |
856 | 675 | std::shared_ptr<msh::SurfaceFactory> the_shell_surface_factory() override | 581 | std::shared_ptr<msh::SurfaceFactory> the_shell_surface_factory() override |
857 | 676 | { | 582 | { |
858 | @@ -680,7 +586,6 @@ | |||
859 | 680 | 586 | ||
860 | 681 | void inject_input() override | 587 | void inject_input() override |
861 | 682 | { | 588 | { |
862 | 683 | wait_until_client_appears(test_client_name); | ||
863 | 684 | input_cb_setup_fence.wait_for_signal_ready_for(); | 589 | input_cb_setup_fence.wait_for_signal_ready_for(); |
864 | 685 | 590 | ||
865 | 686 | // First we will move the cursor in to the input region on the left side of the window. We should see a click here | 591 | // First we will move the cursor in to the input region on the left side of the window. We should see a click here |
866 | @@ -697,35 +602,26 @@ | |||
867 | 697 | fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT)); | 602 | fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT)); |
868 | 698 | } | 603 | } |
869 | 699 | } server_config{fence}; | 604 | } server_config{fence}; |
870 | 700 | |||
871 | 701 | launch_server_process(server_config); | 605 | launch_server_process(server_config); |
872 | 702 | 606 | ||
886 | 703 | struct ClientConfig : InputClient | 607 | auto client_config = make_event_expecting_client(test_client_name, fence, |
887 | 704 | { | 608 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) |
888 | 705 | ClientConfig(const mtf::CrossProcessSync& fence) | 609 | { |
889 | 706 | : InputClient(fence, test_client_name) | 610 | EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber()); |
890 | 707 | { | 611 | EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber()); |
891 | 708 | } | 612 | EXPECT_CALL(handler, handle_input(mt::MovementEvent())).Times(AnyNumber()); |
879 | 709 | |||
880 | 710 | void expect_input(mt::WaitCondition& events_received) override | ||
881 | 711 | { | ||
882 | 712 | |||
883 | 713 | EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(AnyNumber()); | ||
884 | 714 | EXPECT_CALL(*handler, handle_input(HoverExitEvent())).Times(AnyNumber()); | ||
885 | 715 | EXPECT_CALL(*handler, handle_input(MovementEvent())).Times(AnyNumber()); | ||
892 | 716 | 613 | ||
893 | 717 | { | 614 | { |
894 | 718 | // We should see two of the three button pairs. | 615 | // We should see two of the three button pairs. |
895 | 719 | InSequence seq; | 616 | InSequence seq; |
900 | 720 | EXPECT_CALL(*handler, handle_input(ButtonDownEvent(1, 1))).Times(1); | 617 | EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(1, 1))).Times(1); |
901 | 721 | EXPECT_CALL(*handler, handle_input(ButtonUpEvent(1, 1))).Times(1); | 618 | EXPECT_CALL(handler, handle_input(mt::ButtonUpEvent(1, 1))).Times(1); |
902 | 722 | EXPECT_CALL(*handler, handle_input(ButtonDownEvent(99, 99))).Times(1); | 619 | EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(99, 99))).Times(1); |
903 | 723 | EXPECT_CALL(*handler, handle_input(ButtonUpEvent(99, 99))).Times(1) | 620 | EXPECT_CALL(handler, handle_input(mt::ButtonUpEvent(99, 99))).Times(1) |
904 | 724 | .WillOnce(mt::WakeUp(&events_received)); | 621 | .WillOnce(mt::WakeUp(&events_received)); |
905 | 725 | } | 622 | } |
909 | 726 | } | 623 | }); |
910 | 727 | } client_config{fence}; | 624 | launch_client_process(*client_config); |
908 | 728 | launch_client_process(client_config); | ||
911 | 729 | } | 625 | } |
912 | 730 | 626 | ||
913 | 731 | TEST_F(TestClientInput, surfaces_obscure_motion_events_by_stacking) | 627 | TEST_F(TestClientInput, surfaces_obscure_motion_events_by_stacking) |
914 | @@ -742,98 +638,62 @@ | |||
915 | 742 | static geom::Rectangle const screen_geometry{geom::Point{0, 0}, | 638 | static geom::Rectangle const screen_geometry{geom::Point{0, 0}, |
916 | 743 | geom::Size{screen_width, screen_height}}; | 639 | geom::Size{screen_width, screen_height}}; |
917 | 744 | 640 | ||
950 | 745 | struct ServerConfiguration : mtf::InputTestingServerConfiguration | 641 | static GeometryMap positions; |
951 | 746 | { | 642 | positions[test_client_name_1] = screen_geometry; |
952 | 747 | mtf::CrossProcessSync input_cb_setup_fence; | 643 | |
953 | 748 | 644 | auto smaller_geometry = screen_geometry; | |
954 | 749 | ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence) | 645 | smaller_geometry.size.width = geom::Width{screen_width/2}; |
955 | 750 | : input_cb_setup_fence(input_cb_setup_fence) | 646 | positions[test_client_name_2] = smaller_geometry; |
956 | 751 | { | 647 | |
957 | 752 | } | 648 | static DepthMap depths; |
958 | 753 | 649 | depths[test_client_name_1] = ms::DepthId{0}; | |
959 | 754 | std::shared_ptr<msh::PlacementStrategy> the_shell_placement_strategy() override | 650 | depths[test_client_name_2] = ms::DepthId{1}; |
960 | 755 | { | 651 | |
961 | 756 | static GeometryMap positions; | 652 | auto server_config = make_event_producing_server(fence, 2, |
962 | 757 | positions[test_client_name_1] = screen_geometry; | 653 | [&](mtf::InputTestingServerConfiguration& server) |
963 | 758 | 654 | { | |
932 | 759 | auto smaller_geometry = screen_geometry; | ||
933 | 760 | smaller_geometry.size.width = geom::Width{screen_width/2}; | ||
934 | 761 | positions[test_client_name_2] = smaller_geometry; | ||
935 | 762 | |||
936 | 763 | static DepthMap depths; | ||
937 | 764 | depths[test_client_name_1] = ms::DepthId{0}; | ||
938 | 765 | depths[test_client_name_2] = ms::DepthId{1}; | ||
939 | 766 | |||
940 | 767 | return std::make_shared<StaticPlacementStrategy>(positions, depths); | ||
941 | 768 | } | ||
942 | 769 | |||
943 | 770 | void inject_input() override | ||
944 | 771 | { | ||
945 | 772 | wait_until_client_appears(test_client_name_1); | ||
946 | 773 | input_cb_setup_fence.wait_for_signal_ready_for(); | ||
947 | 774 | wait_until_client_appears(test_client_name_2); | ||
948 | 775 | input_cb_setup_fence.wait_for_signal_ready_for(); | ||
949 | 776 | |||
964 | 777 | // First we will move the cursor in to the region where client 2 obscures client 1 | 655 | // First we will move the cursor in to the region where client 2 obscures client 1 |
968 | 778 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 1)); | 656 | server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 1)); |
969 | 779 | fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); | 657 | server.fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); |
970 | 780 | fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT)); | 658 | server.fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT)); |
971 | 781 | // Now we move to the unobscured region of client 1 | 659 | // Now we move to the unobscured region of client 1 |
993 | 782 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(50, 0)); | 660 | server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(50, 0)); |
994 | 783 | fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); | 661 | server.fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); |
995 | 784 | fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT)); | 662 | server.fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT)); |
996 | 785 | } | 663 | }, positions, depths); |
997 | 786 | } server_config{fence}; | 664 | launch_server_process(*server_config); |
998 | 787 | 665 | ||
999 | 788 | launch_server_process(server_config); | 666 | auto client_config_1 = make_event_expecting_client(test_client_name_1, fence, |
1000 | 789 | 667 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) | |
1001 | 790 | struct ClientConfigOne : InputClient | 668 | { |
1002 | 791 | { | 669 | EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber()); |
1003 | 792 | ClientConfigOne(const mtf::CrossProcessSync& fence) | 670 | EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber()); |
1004 | 793 | : InputClient(fence, test_client_name_1) | 671 | EXPECT_CALL(handler, handle_input(mt::MovementEvent())).Times(AnyNumber()); |
984 | 794 | { | ||
985 | 795 | } | ||
986 | 796 | |||
987 | 797 | void expect_input(mt::WaitCondition& events_received) override | ||
988 | 798 | { | ||
989 | 799 | EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(AnyNumber()); | ||
990 | 800 | EXPECT_CALL(*handler, handle_input(HoverExitEvent())).Times(AnyNumber()); | ||
991 | 801 | EXPECT_CALL(*handler, handle_input(MovementEvent())).Times(AnyNumber()); | ||
992 | 802 | |||
1005 | 803 | { | 672 | { |
1006 | 804 | // We should only see one button event sequence. | 673 | // We should only see one button event sequence. |
1007 | 805 | InSequence seq; | 674 | InSequence seq; |
1010 | 806 | EXPECT_CALL(*handler, handle_input(ButtonDownEvent(51, 1))).Times(1); | 675 | EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(51, 1))).Times(1); |
1011 | 807 | EXPECT_CALL(*handler, handle_input(ButtonUpEvent(51, 1))).Times(1) | 676 | EXPECT_CALL(handler, handle_input(mt::ButtonUpEvent(51, 1))).Times(1) |
1012 | 808 | .WillOnce(mt::WakeUp(&events_received)); | 677 | .WillOnce(mt::WakeUp(&events_received)); |
1013 | 809 | } | 678 | } |
1031 | 810 | } | 679 | }); |
1032 | 811 | } client_config_1{fence}; | 680 | auto client_config_2 = make_event_expecting_client(test_client_name_2, fence, |
1033 | 812 | launch_client_process(client_config_1); | 681 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) |
1034 | 813 | 682 | { | |
1035 | 814 | struct ClientConfigTwo : InputClient | 683 | EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber()); |
1036 | 815 | { | 684 | EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber()); |
1037 | 816 | ClientConfigTwo(const mtf::CrossProcessSync& fence) | 685 | EXPECT_CALL(handler, handle_input(mt::MovementEvent())).Times(AnyNumber()); |
1021 | 817 | : InputClient(fence, test_client_name_2) | ||
1022 | 818 | { | ||
1023 | 819 | } | ||
1024 | 820 | |||
1025 | 821 | void expect_input(mt::WaitCondition& events_received) override | ||
1026 | 822 | { | ||
1027 | 823 | EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(AnyNumber()); | ||
1028 | 824 | EXPECT_CALL(*handler, handle_input(HoverExitEvent())).Times(AnyNumber()); | ||
1029 | 825 | EXPECT_CALL(*handler, handle_input(MovementEvent())).Times(AnyNumber()); | ||
1030 | 826 | |||
1038 | 827 | { | 686 | { |
1039 | 828 | // Likewise we should only see one button sequence. | 687 | // Likewise we should only see one button sequence. |
1040 | 829 | InSequence seq; | 688 | InSequence seq; |
1043 | 830 | EXPECT_CALL(*handler, handle_input(ButtonDownEvent(1, 1))).Times(1); | 689 | EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(1, 1))).Times(1); |
1044 | 831 | EXPECT_CALL(*handler, handle_input(ButtonUpEvent(1, 1))).Times(1) | 690 | EXPECT_CALL(handler, handle_input(mt::ButtonUpEvent(1, 1))).Times(1) |
1045 | 832 | .WillOnce(mt::WakeUp(&events_received)); | 691 | .WillOnce(mt::WakeUp(&events_received)); |
1046 | 833 | } | 692 | } |
1050 | 834 | } | 693 | }); |
1051 | 835 | } client_config_2{fence}; | 694 | |
1052 | 836 | launch_client_process(client_config_2); | 695 | launch_client_process(*client_config_1); |
1053 | 696 | launch_client_process(*client_config_2); | ||
1054 | 837 | } | 697 | } |
1055 | 838 | 698 | ||
1056 | 839 | namespace | 699 | namespace |
1057 | @@ -853,93 +713,48 @@ | |||
1058 | 853 | static std::string const test_client_name = "1"; | 713 | static std::string const test_client_name = "1"; |
1059 | 854 | static std::string const test_client_2_name = "2"; | 714 | static std::string const test_client_2_name = "2"; |
1060 | 855 | mtf::CrossProcessSync fence, first_client_ready_fence, second_client_done_fence; | 715 | mtf::CrossProcessSync fence, first_client_ready_fence, second_client_done_fence; |
1089 | 856 | 716 | ||
1090 | 857 | struct ServerConfiguration : public mtf::InputTestingServerConfiguration | 717 | static DepthMap depths; |
1091 | 858 | { | 718 | depths[test_client_name] = ms::DepthId{0}; |
1092 | 859 | mtf::CrossProcessSync input_cb_setup_fence; | 719 | depths[test_client_2_name] = ms::DepthId{1}; |
1093 | 860 | mtf::CrossProcessSync second_client_done_fence; | 720 | |
1094 | 861 | 721 | auto server_config = make_event_producing_server(fence, 2, | |
1095 | 862 | ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence, | 722 | [&](mtf::InputTestingServerConfiguration& server) |
1096 | 863 | const mtf::CrossProcessSync& second_client_done_fence) | 723 | { |
1069 | 864 | : input_cb_setup_fence(input_cb_setup_fence), | ||
1070 | 865 | second_client_done_fence(second_client_done_fence) | ||
1071 | 866 | { | ||
1072 | 867 | } | ||
1073 | 868 | |||
1074 | 869 | void hide_session_by_name(std::string const& session_name) | ||
1075 | 870 | { | ||
1076 | 871 | the_shell_session_container()->for_each([&](std::shared_ptr<msh::Session> const& session) -> void | ||
1077 | 872 | { | ||
1078 | 873 | if (session->name() == session_name) | ||
1079 | 874 | session->hide(); | ||
1080 | 875 | }); | ||
1081 | 876 | } | ||
1082 | 877 | |||
1083 | 878 | void inject_input() | ||
1084 | 879 | { | ||
1085 | 880 | wait_until_client_appears(test_client_name); | ||
1086 | 881 | wait_until_client_appears(test_client_2_name); | ||
1087 | 882 | input_cb_setup_fence.wait_for_signal_ready_for(); | ||
1088 | 883 | |||
1097 | 884 | // We send one event and then hide the surface on top before sending the next. | 724 | // We send one event and then hide the surface on top before sending the next. |
1101 | 885 | // So we expect each of the two surfaces to receive one event pair. | 725 | // So we expect each of the two surfaces to receive one even |
1102 | 886 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1,1)); | 726 | server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1,1)); |
1100 | 887 | |||
1103 | 888 | // We use a fence to ensure we do not hide the client | 727 | // We use a fence to ensure we do not hide the client |
1104 | 889 | // before event dispatch occurs | 728 | // before event dispatch occurs |
1105 | 890 | second_client_done_fence.wait_for_signal_ready_for(); | 729 | second_client_done_fence.wait_for_signal_ready_for(); |
1125 | 891 | hide_session_by_name(test_client_2_name); | 730 | |
1126 | 892 | 731 | server.the_shell_session_container()->for_each([&](std::shared_ptr<msh::Session> const& session) -> void | |
1127 | 893 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1,1)); | 732 | { |
1128 | 894 | } | 733 | if (session->name() == test_client_2_name) |
1129 | 895 | } server_config{fence, second_client_done_fence}; | 734 | session->hide(); |
1130 | 896 | launch_server_process(server_config); | 735 | }); |
1131 | 897 | 736 | ||
1132 | 898 | struct ButtonClientOne : InputClient | 737 | server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1,1)); |
1133 | 899 | { | 738 | }, GeometryMap(), depths); |
1134 | 900 | ButtonClientOne(const mtf::CrossProcessSync& fence) | 739 | launch_server_process(*server_config); |
1135 | 901 | : InputClient(fence, test_client_name) | 740 | |
1136 | 902 | { | 741 | auto client_config_1 = make_event_expecting_client(test_client_name, fence, |
1137 | 903 | } | 742 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) |
1138 | 904 | 743 | { | |
1139 | 905 | void expect_input(mt::WaitCondition& events_received) override | 744 | EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber()); |
1140 | 906 | { | 745 | EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber()); |
1141 | 907 | EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(AnyNumber()); | 746 | EXPECT_CALL(handler, handle_input(mt::MotionEventWithPosition(2, 2))).Times(1) |
1123 | 908 | EXPECT_CALL(*handler, handle_input(HoverExitEvent())).Times(AnyNumber()); | ||
1124 | 909 | EXPECT_CALL(*handler, handle_input(MotionEventWithPosition(2, 2))).Times(1) | ||
1142 | 910 | .WillOnce(mt::WakeUp(&events_received)); | 747 | .WillOnce(mt::WakeUp(&events_received)); |
1177 | 911 | } | 748 | }); |
1178 | 912 | } client_1{first_client_ready_fence}; | 749 | auto client_config_2 = make_event_expecting_client(test_client_2_name, fence, |
1179 | 913 | struct ButtonClientTwo : InputClient | 750 | [&](MockInputHandler& handler, mt::WaitCondition& events_received) |
1180 | 914 | { | 751 | { |
1181 | 915 | mtf::CrossProcessSync first_client_ready; | 752 | EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber()); |
1182 | 916 | mtf::CrossProcessSync done_fence; | 753 | EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber()); |
1183 | 917 | 754 | EXPECT_CALL(handler, handle_input(mt::MotionEventWithPosition(1, 1))).Times(1) | |
1184 | 918 | ButtonClientTwo(mtf::CrossProcessSync const& fence, mtf::CrossProcessSync const& first_client_ready, | 755 | .WillOnce(DoAll(SignalFence(&second_client_done_fence), mt::WakeUp(&events_received))); |
1185 | 919 | mtf::CrossProcessSync const& done_fence) | 756 | }); |
1186 | 920 | : InputClient(fence, test_client_2_name), | 757 | |
1187 | 921 | first_client_ready(first_client_ready), | 758 | launch_client_process(*client_config_1); |
1188 | 922 | done_fence(done_fence) | 759 | launch_client_process(*client_config_2); |
1155 | 923 | { | ||
1156 | 924 | } | ||
1157 | 925 | void exec() | ||
1158 | 926 | { | ||
1159 | 927 | // Ensure we stack on top of the first client | ||
1160 | 928 | first_client_ready.wait_for_signal_ready_for(); | ||
1161 | 929 | InputClient::exec(); | ||
1162 | 930 | } | ||
1163 | 931 | |||
1164 | 932 | void expect_input(mt::WaitCondition& events_received) override | ||
1165 | 933 | { | ||
1166 | 934 | EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(AnyNumber()); | ||
1167 | 935 | EXPECT_CALL(*handler, handle_input(HoverExitEvent())).Times(AnyNumber()); | ||
1168 | 936 | EXPECT_CALL(*handler, handle_input(MotionEventWithPosition(1, 1))).Times(1) | ||
1169 | 937 | .WillOnce(DoAll(SignalFence(&done_fence), mt::WakeUp(&events_received))); | ||
1170 | 938 | } | ||
1171 | 939 | } client_2{fence, first_client_ready_fence, second_client_done_fence}; | ||
1172 | 940 | |||
1173 | 941 | // Client 2 is launched second so will be the first to receive input | ||
1174 | 942 | |||
1175 | 943 | launch_client_process(client_1); | ||
1176 | 944 | launch_client_process(client_2); | ||
1189 | 945 | } | 760 | } |
1190 | 946 | 761 | ||
1191 | === modified file 'tests/mir_test_framework/input_testing_server_options.cpp' | |||
1192 | --- tests/mir_test_framework/input_testing_server_options.cpp 2013-09-19 18:18:35 +0000 | |||
1193 | +++ tests/mir_test_framework/input_testing_server_options.cpp 2013-11-07 17:24:53 +0000 | |||
1194 | @@ -36,8 +36,6 @@ | |||
1195 | 36 | 36 | ||
1196 | 37 | namespace mtf = mir_test_framework; | 37 | namespace mtf = mir_test_framework; |
1197 | 38 | 38 | ||
1198 | 39 | namespace ms = mir::surfaces; | ||
1199 | 40 | namespace msh = mir::shell; | ||
1200 | 41 | namespace mf = mir::frontend; | 39 | namespace mf = mir::frontend; |
1201 | 42 | namespace mg = mir::graphics; | 40 | namespace mg = mir::graphics; |
1202 | 43 | namespace mi = mir::input; | 41 | namespace mi = mir::input; |
1203 | @@ -45,63 +43,6 @@ | |||
1204 | 45 | namespace geom = mir::geometry; | 43 | namespace geom = mir::geometry; |
1205 | 46 | namespace mtd = mir::test::doubles; | 44 | namespace mtd = mir::test::doubles; |
1206 | 47 | 45 | ||
1207 | 48 | namespace | ||
1208 | 49 | { | ||
1209 | 50 | class SurfaceReadinessListener | ||
1210 | 51 | { | ||
1211 | 52 | public: | ||
1212 | 53 | virtual ~SurfaceReadinessListener() = default; | ||
1213 | 54 | |||
1214 | 55 | virtual void channel_ready_for_input(std::string const& channel_name) = 0; | ||
1215 | 56 | |||
1216 | 57 | protected: | ||
1217 | 58 | SurfaceReadinessListener() = default; | ||
1218 | 59 | SurfaceReadinessListener(SurfaceReadinessListener const&) = delete; | ||
1219 | 60 | SurfaceReadinessListener& operator=(SurfaceReadinessListener const&) = delete; | ||
1220 | 61 | }; | ||
1221 | 62 | |||
1222 | 63 | class ProxyShell : public mf::Shell | ||
1223 | 64 | { | ||
1224 | 65 | public: | ||
1225 | 66 | ProxyShell(std::shared_ptr<mf::Shell> const& underlying_shell, | ||
1226 | 67 | std::shared_ptr<SurfaceReadinessListener> const listener) | ||
1227 | 68 | : underlying_shell(underlying_shell), | ||
1228 | 69 | listener(listener) | ||
1229 | 70 | { | ||
1230 | 71 | } | ||
1231 | 72 | |||
1232 | 73 | ~ProxyShell() noexcept(true) = default; | ||
1233 | 74 | |||
1234 | 75 | mf::SurfaceId create_surface_for(std::shared_ptr<mf::Session> const& session, | ||
1235 | 76 | msh::SurfaceCreationParameters const& params) | ||
1236 | 77 | { | ||
1237 | 78 | return underlying_shell->create_surface_for(session, params); | ||
1238 | 79 | } | ||
1239 | 80 | |||
1240 | 81 | std::shared_ptr<mf::Session> open_session(std::string const& name, | ||
1241 | 82 | std::shared_ptr<mf::EventSink> const& sink) | ||
1242 | 83 | { | ||
1243 | 84 | return underlying_shell->open_session(name, sink); | ||
1244 | 85 | } | ||
1245 | 86 | |||
1246 | 87 | void close_session(std::shared_ptr<mf::Session> const& session) | ||
1247 | 88 | { | ||
1248 | 89 | underlying_shell->close_session(session); | ||
1249 | 90 | } | ||
1250 | 91 | |||
1251 | 92 | void handle_surface_created(std::shared_ptr<mf::Session> const& session) | ||
1252 | 93 | { | ||
1253 | 94 | underlying_shell->handle_surface_created(session); | ||
1254 | 95 | listener->channel_ready_for_input(session->name()); | ||
1255 | 96 | } | ||
1256 | 97 | |||
1257 | 98 | private: | ||
1258 | 99 | std::shared_ptr<mf::Shell> const underlying_shell; | ||
1259 | 100 | std::shared_ptr<SurfaceReadinessListener> const listener; | ||
1260 | 101 | }; | ||
1261 | 102 | |||
1262 | 103 | } | ||
1263 | 104 | |||
1264 | 105 | mtf::InputTestingServerConfiguration::InputTestingServerConfiguration() | 46 | mtf::InputTestingServerConfiguration::InputTestingServerConfiguration() |
1265 | 106 | { | 47 | { |
1266 | 107 | } | 48 | } |
1267 | @@ -136,56 +77,3 @@ | |||
1268 | 136 | 77 | ||
1269 | 137 | return input_configuration; | 78 | return input_configuration; |
1270 | 138 | } | 79 | } |
1271 | 139 | |||
1272 | 140 | std::shared_ptr<mf::Shell> mtf::InputTestingServerConfiguration::the_frontend_shell() | ||
1273 | 141 | { | ||
1274 | 142 | struct LifecycleTracker : public SurfaceReadinessListener | ||
1275 | 143 | { | ||
1276 | 144 | LifecycleTracker(std::mutex& lifecycle_lock, | ||
1277 | 145 | std::condition_variable &lifecycle_condition, | ||
1278 | 146 | std::map<std::string, mtf::ClientLifecycleState> &client_lifecycles) | ||
1279 | 147 | : lifecycle_lock(lifecycle_lock), | ||
1280 | 148 | lifecycle_condition(lifecycle_condition), | ||
1281 | 149 | client_lifecycles(client_lifecycles) | ||
1282 | 150 | { | ||
1283 | 151 | } | ||
1284 | 152 | void channel_ready_for_input(std::string const& channel_name) | ||
1285 | 153 | { | ||
1286 | 154 | std::unique_lock<std::mutex> lg(lifecycle_lock); | ||
1287 | 155 | client_lifecycles[channel_name] = mtf::ClientLifecycleState::appeared; | ||
1288 | 156 | lifecycle_condition.notify_all(); | ||
1289 | 157 | } | ||
1290 | 158 | |||
1291 | 159 | std::mutex &lifecycle_lock; | ||
1292 | 160 | std::condition_variable &lifecycle_condition; | ||
1293 | 161 | std::map<std::string, mtf::ClientLifecycleState> &client_lifecycles; | ||
1294 | 162 | }; | ||
1295 | 163 | |||
1296 | 164 | if (!frontend_shell) | ||
1297 | 165 | { | ||
1298 | 166 | auto readiness_listener = std::make_shared<LifecycleTracker>(lifecycle_lock, | ||
1299 | 167 | lifecycle_condition, | ||
1300 | 168 | client_lifecycles); | ||
1301 | 169 | frontend_shell = std::make_shared<ProxyShell>(DefaultServerConfiguration::the_frontend_shell(), readiness_listener); | ||
1302 | 170 | } | ||
1303 | 171 | |||
1304 | 172 | return frontend_shell; | ||
1305 | 173 | } | ||
1306 | 174 | |||
1307 | 175 | void mtf::InputTestingServerConfiguration::wait_until_client_appears(std::string const& channel_name) | ||
1308 | 176 | { | ||
1309 | 177 | std::unique_lock<std::mutex> lg(lifecycle_lock); | ||
1310 | 178 | |||
1311 | 179 | std::chrono::minutes timeout(2); | ||
1312 | 180 | auto end_time = std::chrono::system_clock::now() + timeout; | ||
1313 | 181 | |||
1314 | 182 | if (client_lifecycles[channel_name] == vanished) | ||
1315 | 183 | { | ||
1316 | 184 | BOOST_THROW_EXCEPTION(std::runtime_error("Waiting for a client (" + channel_name + ") to appear but it has already vanished")); | ||
1317 | 185 | } | ||
1318 | 186 | while (client_lifecycles[channel_name] != appeared) | ||
1319 | 187 | { | ||
1320 | 188 | if (lifecycle_condition.wait_until(lg, end_time) == std::cv_status::timeout) | ||
1321 | 189 | BOOST_THROW_EXCEPTION(std::runtime_error("Timed out waiting for client (" + channel_name + ") to appear")); | ||
1322 | 190 | } | ||
1323 | 191 | } |
FAILED: Continuous integration, rev:1205 /code.launchpad .net/~robertcar r/mir/refactor- input-acceptanc e-take- 1/+merge/ 194265/ +edit-commit- message
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http:// jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- ci/347/ jenkins. qa.ubuntu. com/job/ mir-android- trusty- i386-build/ 139 jenkins. qa.ubuntu. com/job/ mir-clang- trusty- amd64-build/ 136/console jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- amd64-ci/ 76/console jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- armhf-ci/ 76 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- armhf-ci/ 76/artifact/ work/output/ *zip*/output. zip
Executed test runs:
SUCCESS: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: 10.97.0. 26:8080/ job/mir- team-mir- development- branch- ci/347/ rebuild
http://