Merge lp:~robertcarr/mir/improve-input-acceptance into lp:~mir-team/mir/trunk
- improve-input-acceptance
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~robertcarr/mir/improve-input-acceptance |
Merge into: | lp:~mir-team/mir/trunk |
Diff against target: |
977 lines (+472/-253) 4 files modified
include/test/mir_test_framework/input_testing_server_configuration.h (+97/-0) tests/acceptance-tests/test_client_input.cpp (+126/-253) tests/mir_test_framework/CMakeLists.txt (+2/-0) tests/mir_test_framework/input_testing_server_options.cpp (+247/-0) |
To merge this branch: | bzr merge lp:~robertcarr/mir/improve-input-acceptance |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Daniel van Vugt | Needs Fixing | ||
Review via email: mp+169311@code.launchpad.net |
This proposal has been superseded by a proposal from 2013-06-14.
Commit message
Refactor input acceptance test fixtures.
Description of the change
Work on making it easier to define a variety of input acceptance tests by pulling out and refactoring the input test fixture from test_client_
PS Jenkins bot (ps-jenkins) wrote : | # |
Daniel van Vugt (vanvugt) wrote : | # |
I feel overwhelmed by class names that make no sense to me (e.g. InputRegistrarL
In other cases, the names are unnecessarily verbose. For example "InputReceiving
But some more concrete issues:
1. Lines 38-58: Might be about time to indent the namespace declarations so they're readable.
2. This test hangs(!):
Start 5: acceptance-
and specifically hangs at:
[----------] 5 tests from TestClientInput
[ RUN ] TestClientInput
[ OK ] TestClientInput
[ RUN ] TestClientInput
[ OK ] TestClientInput
[ RUN ] TestClientInput
[ OK ] TestClientInput
[ RUN ] TestClientInput
[ OK ] TestClientInput
[ RUN ] TestClientInput
Robert Carr (robertcarr) wrote : | # |
Whoops at the hang! r745 was supposed to be a harmless cleanup which of course broke everything.Fixed
I made some renames, InputRegistrarL
>> Lines 38-58: Might be about time to indent the namespace declarations so they're readable.
I don't want to have a different style only in this file. Perhaps it is indicative that mir_test_framework should be mir::test:
Robert Carr (robertcarr) wrote : | # |
InputReceivingC
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:747
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === added file 'include/test/mir_test_framework/input_testing_server_configuration.h' |
2 | --- include/test/mir_test_framework/input_testing_server_configuration.h 1970-01-01 00:00:00 +0000 |
3 | +++ include/test/mir_test_framework/input_testing_server_configuration.h 2013-06-14 16:12:27 +0000 |
4 | @@ -0,0 +1,97 @@ |
5 | +/* |
6 | + * Copyright © 2013 Canonical Ltd. |
7 | + * |
8 | + * This program is free software: you can redistribute it and/or modify it |
9 | + * under the terms of the GNU General Public License version 3, |
10 | + * as published by the Free Software Foundation. |
11 | + * |
12 | + * This program is distributed in the hope that it will be useful, |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | + * GNU General Public License for more details. |
16 | + * |
17 | + * You should have received a copy of the GNU General Public License |
18 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | + * |
20 | + * Authored by: Robert Carr <robert.carr@canonical.com> |
21 | + */ |
22 | + |
23 | + |
24 | +#ifndef MIR_TEST_INPUT_TESTING_SERVER_CONFIGURATION_H_ |
25 | +#define MIR_TEST_INPUT_TESTING_SERVER_CONFIGURATION_H_ |
26 | + |
27 | +#include "mir_test_framework/testing_server_configuration.h" |
28 | + |
29 | +#include "mir/geometry/rectangle.h" |
30 | + |
31 | +#include <map> |
32 | +#include <string> |
33 | +#include <mutex> |
34 | +#include <condition_variable> |
35 | +#include <atomic> |
36 | +#include <thread> |
37 | + |
38 | +namespace mir |
39 | +{ |
40 | +namespace input |
41 | +{ |
42 | +namespace android |
43 | +{ |
44 | +class FakeEventHub; |
45 | +} |
46 | +} |
47 | +namespace graphics |
48 | +{ |
49 | +class ViewableArea; |
50 | +} |
51 | +namespace test |
52 | +{ |
53 | +namespace doubles |
54 | +{ |
55 | +class FakeEventHubInputConfiguration; |
56 | +} |
57 | +} |
58 | +} |
59 | + |
60 | +namespace mir_test_framework |
61 | +{ |
62 | + |
63 | +enum ClientLifecycleState { starting, appeared, vanished }; |
64 | + |
65 | +class InputTestingServerConfiguration : public TestingServerConfiguration |
66 | +{ |
67 | +public: |
68 | + InputTestingServerConfiguration(); |
69 | + |
70 | + void exec(); |
71 | + void on_exit(); |
72 | + |
73 | + std::shared_ptr<mir::input::InputConfiguration> the_input_configuration() override; |
74 | + std::shared_ptr<mir::surfaces::InputRegistrar> the_input_registrar() override; |
75 | + std::shared_ptr<mir::graphics::ViewableArea> the_viewable_area() override; |
76 | + |
77 | +protected: |
78 | + virtual mir::geometry::Rectangle the_screen_geometry(); |
79 | + |
80 | + virtual void inject_input() = 0; |
81 | + mir::input::android::FakeEventHub* fake_event_hub; |
82 | + |
83 | + void wait_until_client_appears(std::string const& surface_name); |
84 | + void wait_until_client_vanishes(std::string const& surface_name); |
85 | + |
86 | +private: |
87 | + std::mutex lifecycle_lock; |
88 | + |
89 | + std::condition_variable lifecycle_condition; |
90 | + std::map<std::string, ClientLifecycleState> client_lifecycles; |
91 | + |
92 | + std::thread input_injection_thread; |
93 | + |
94 | + std::shared_ptr<mir::test::doubles::FakeEventHubInputConfiguration> input_configuration; |
95 | + std::shared_ptr<mir::surfaces::InputRegistrar> input_registrar; |
96 | + std::shared_ptr<mir::graphics::ViewableArea> view_area; |
97 | +}; |
98 | + |
99 | +} |
100 | + |
101 | +#endif /* MIR_TEST_INPUT_TESTING_SERVER_CONFIGURATION_H_ */ |
102 | |
103 | === modified file 'tests/acceptance-tests/test_client_input.cpp' |
104 | --- tests/acceptance-tests/test_client_input.cpp 2013-05-30 19:24:29 +0000 |
105 | +++ tests/acceptance-tests/test_client_input.cpp 2013-06-14 16:12:27 +0000 |
106 | @@ -27,11 +27,11 @@ |
107 | #include "mir_toolkit/mir_client_library.h" |
108 | |
109 | #include "mir_test/fake_shared.h" |
110 | -#include "mir_test/fake_event_hub_input_configuration.h" |
111 | #include "mir_test/fake_event_hub.h" |
112 | #include "mir_test/event_factory.h" |
113 | #include "mir_test/wait_condition.h" |
114 | #include "mir_test_framework/display_server_test_fixture.h" |
115 | +#include "mir_test_framework/input_testing_server_configuration.h" |
116 | |
117 | #include <gtest/gtest.h> |
118 | #include <gmock/gmock.h> |
119 | @@ -60,86 +60,12 @@ |
120 | char const* const mir_test_socket = mtf::test_socket_file().c_str(); |
121 | } |
122 | |
123 | -#include "mir/input/surface_target.h" |
124 | - |
125 | namespace |
126 | { |
127 | -struct FocusNotifyingInputTargeter : public msh::InputTargeter |
128 | -{ |
129 | - FocusNotifyingInputTargeter(std::shared_ptr<msh::InputTargeter> const& real_targeter, |
130 | - std::function<void(void)> const& focus_set) |
131 | - : real_targeter(real_targeter), |
132 | - focus_set(focus_set) |
133 | - { |
134 | - |
135 | - } |
136 | - virtual ~FocusNotifyingInputTargeter() noexcept(true) {} |
137 | - |
138 | - void focus_changed(std::shared_ptr<mi::SurfaceTarget const> const& surface) override |
139 | - { |
140 | - real_targeter->focus_changed(surface); |
141 | - |
142 | - // We need a synchronization primitive inorder to halt test event injection |
143 | - // until after a surface has taken focus (lest the events be discarded). |
144 | - focus_set(); |
145 | - } |
146 | - void focus_cleared() |
147 | - { |
148 | - real_targeter->focus_cleared(); |
149 | - } |
150 | - |
151 | - std::shared_ptr<msh::InputTargeter> const real_targeter; |
152 | - std::function<void(void)> focus_set; |
153 | -}; |
154 | - |
155 | -struct FakeInputServerConfiguration : public mir_test_framework::TestingServerConfiguration |
156 | -{ |
157 | - FakeInputServerConfiguration() |
158 | - : input_config(the_event_filters(), the_display(), std::shared_ptr<mi::CursorListener>(), the_input_report()) |
159 | - { |
160 | - } |
161 | - |
162 | - virtual void inject_input_after_focus() |
163 | - { |
164 | - } |
165 | - |
166 | - std::shared_ptr<mi::InputConfiguration> the_input_configuration() override |
167 | - { |
168 | - fake_event_hub = input_config.the_fake_event_hub(); |
169 | - fake_event_hub->synthesize_builtin_keyboard_added(); |
170 | - fake_event_hub->synthesize_builtin_cursor_added(); |
171 | - fake_event_hub->synthesize_device_scan_complete(); |
172 | - |
173 | - return mt::fake_shared(input_config); |
174 | - } |
175 | - |
176 | - std::shared_ptr<mi::InputManager> the_input_manager() override |
177 | - { |
178 | - return DefaultServerConfiguration::the_input_manager(); |
179 | - } |
180 | - std::shared_ptr<ms::InputRegistrar> the_input_registrar() override |
181 | - { |
182 | - return DefaultServerConfiguration::the_input_registrar(); |
183 | - } |
184 | - |
185 | - std::shared_ptr<msh::InputTargeter> |
186 | - the_input_targeter() override |
187 | - { |
188 | - return input_targeter( |
189 | - [this]() |
190 | - { |
191 | - return std::make_shared<FocusNotifyingInputTargeter>(DefaultServerConfiguration::the_input_targeter(), std::bind(std::mem_fn(&FakeInputServerConfiguration::inject_input_after_focus), this)); |
192 | - }); |
193 | - } |
194 | - |
195 | - mtd::FakeEventHubInputConfiguration input_config; |
196 | - mia::FakeEventHub* fake_event_hub; |
197 | -}; |
198 | - |
199 | - |
200 | -struct ClientConfigCommon : TestingClientConfiguration |
201 | -{ |
202 | - ClientConfigCommon() : |
203 | + |
204 | +struct ClientConfig : mtf::TestingClientConfiguration |
205 | +{ |
206 | + ClientConfig() : |
207 | connection(0), |
208 | surface(0) |
209 | { |
210 | @@ -147,19 +73,19 @@ |
211 | |
212 | static void connection_callback(MirConnection* connection, void* context) |
213 | { |
214 | - ClientConfigCommon* config = reinterpret_cast<ClientConfigCommon *>(context); |
215 | + ClientConfig* config = reinterpret_cast<ClientConfig *>(context); |
216 | config->connection = connection; |
217 | } |
218 | |
219 | static void create_surface_callback(MirSurface* surface, void* context) |
220 | { |
221 | - ClientConfigCommon* config = reinterpret_cast<ClientConfigCommon *>(context); |
222 | + ClientConfig* config = reinterpret_cast<ClientConfig *>(context); |
223 | config->surface_created(surface); |
224 | } |
225 | |
226 | static void release_surface_callback(MirSurface* surface, void* context) |
227 | { |
228 | - ClientConfigCommon* config = reinterpret_cast<ClientConfigCommon *>(context); |
229 | + ClientConfig* config = reinterpret_cast<ClientConfig *>(context); |
230 | config->surface_released(surface); |
231 | } |
232 | |
233 | @@ -184,29 +110,24 @@ |
234 | |
235 | struct MockInputHandler |
236 | { |
237 | - MOCK_METHOD1(handle_input, bool(MirEvent const*)); |
238 | + MOCK_METHOD1(handle_input, void(MirEvent const*)); |
239 | }; |
240 | |
241 | -struct InputReceivingClient : ClientConfigCommon |
242 | +struct InputClient : ClientConfig |
243 | { |
244 | - InputReceivingClient(int events_to_receive) |
245 | - : events_to_receive(events_to_receive), |
246 | - events_received(0) |
247 | + InputClient(std::string const& surface_name) |
248 | + : surface_name(surface_name) |
249 | { |
250 | - assert(events_to_receive <= max_events_to_receive); |
251 | } |
252 | |
253 | static void handle_input(MirSurface* /* surface */, MirEvent const* ev, void* context) |
254 | { |
255 | - auto client = static_cast<InputReceivingClient *>(context); |
256 | + auto client = static_cast<InputClient *>(context); |
257 | |
258 | - if (client->handler->handle_input(ev)) |
259 | - { |
260 | - client->event_received[client->events_received].wake_up_everyone(); |
261 | - client->events_received++; |
262 | - } |
263 | + client->handler->handle_input(ev); |
264 | } |
265 | - virtual void expect_input() |
266 | + |
267 | + virtual void expect_input(mt::WaitCondition&) |
268 | { |
269 | } |
270 | |
271 | @@ -214,7 +135,7 @@ |
272 | { |
273 | MirSurfaceParameters const request_params = |
274 | { |
275 | - __PRETTY_FUNCTION__, |
276 | + surface_name.c_str(), |
277 | surface_width, surface_height, |
278 | mir_pixel_format_abgr_8888, |
279 | mir_buffer_usage_hardware |
280 | @@ -226,7 +147,7 @@ |
281 | { |
282 | handler = std::make_shared<MockInputHandler>(); |
283 | |
284 | - expect_input(); |
285 | + expect_input(events_received); |
286 | |
287 | mir_wait_for(mir_connect( |
288 | mir_test_socket, |
289 | @@ -245,9 +166,7 @@ |
290 | |
291 | mir_surface_set_event_handler(surface, &event_delegate); |
292 | |
293 | - |
294 | - for (int i = 0; i < events_to_receive; i++) |
295 | - event_received[i].wait_for_at_most_seconds(5); |
296 | + events_received.wait_for_at_most_seconds(60); |
297 | |
298 | mir_surface_release_sync(surface); |
299 | |
300 | @@ -259,12 +178,10 @@ |
301 | } |
302 | |
303 | std::shared_ptr<MockInputHandler> handler; |
304 | - static int const max_events_to_receive = 4; |
305 | - mt::WaitCondition event_received[max_events_to_receive]; |
306 | - |
307 | - int events_to_receive; |
308 | - int events_received; |
309 | - |
310 | + mt::WaitCondition events_received; |
311 | + |
312 | + std::string const surface_name; |
313 | + |
314 | static int const surface_width = 100; |
315 | static int const surface_height = 100; |
316 | }; |
317 | @@ -343,13 +260,13 @@ |
318 | using namespace ::testing; |
319 | |
320 | int const num_events_produced = 3; |
321 | + static std::string const test_client_name = "1"; |
322 | |
323 | - struct InputProducingServerConfiguration : FakeInputServerConfiguration |
324 | + struct ServerConfiguration : mtf::InputTestingServerConfiguration |
325 | { |
326 | - void inject_input_after_focus() |
327 | + void inject_input() |
328 | { |
329 | - // We send multiple events in order to verify the client is handling them |
330 | - // and server dispatch does not become backed up. |
331 | + wait_until_client_appears(test_client_name); |
332 | for (int i = 0; i < num_events_produced; i++) |
333 | fake_event_hub->synthesize_event(mis::a_key_down_event() |
334 | .of_scancode(KEY_ENTER)); |
335 | @@ -357,14 +274,17 @@ |
336 | } server_config; |
337 | launch_server_process(server_config); |
338 | |
339 | - struct KeyReceivingClient : InputReceivingClient |
340 | + struct KeyReceivingClient : InputClient |
341 | { |
342 | - KeyReceivingClient() : InputReceivingClient(num_events_produced) {} |
343 | - void expect_input() |
344 | + KeyReceivingClient() : InputClient(test_client_name) {} |
345 | + void expect_input(mt::WaitCondition& events_received) override |
346 | { |
347 | using namespace ::testing; |
348 | - EXPECT_CALL(*handler, handle_input(KeyDownEvent())).Times(num_events_produced) |
349 | - .WillRepeatedly(Return(true)); |
350 | + InSequence seq; |
351 | + |
352 | + EXPECT_CALL(*handler, handle_input(KeyDownEvent())).Times(2); |
353 | + EXPECT_CALL(*handler, handle_input(KeyDownEvent())).Times(1) |
354 | + .WillOnce(mt::WakeUp(&events_received)); |
355 | } |
356 | } client_config; |
357 | launch_client_process(client_config); |
358 | @@ -373,11 +293,14 @@ |
359 | TEST_F(TestClientInput, clients_receive_us_english_mapped_keys) |
360 | { |
361 | using namespace ::testing; |
362 | + static std::string const test_client_name = "1"; |
363 | |
364 | - struct InputProducingServerConfiguration : FakeInputServerConfiguration |
365 | + struct ServerConfiguration : mtf::InputTestingServerConfiguration |
366 | { |
367 | - void inject_input_after_focus() |
368 | + void inject_input() |
369 | { |
370 | + wait_until_client_appears(test_client_name); |
371 | + |
372 | fake_event_hub->synthesize_event(mis::a_key_down_event() |
373 | .of_scancode(KEY_LEFTSHIFT)); |
374 | fake_event_hub->synthesize_event(mis::a_key_down_event() |
375 | @@ -387,55 +310,57 @@ |
376 | } server_config; |
377 | launch_server_process(server_config); |
378 | |
379 | - struct KeyReceivingClient : InputReceivingClient |
380 | + struct KeyReceivingClient : InputClient |
381 | { |
382 | - KeyReceivingClient() : InputReceivingClient(2) {} |
383 | + KeyReceivingClient() : InputClient(test_client_name) {} |
384 | |
385 | - void expect_input() |
386 | + void expect_input(mt::WaitCondition& events_received) override |
387 | { |
388 | using namespace ::testing; |
389 | |
390 | InSequence seq; |
391 | - EXPECT_CALL(*handler, handle_input(AllOf(KeyDownEvent(), KeyOfSymbol(XKB_KEY_Shift_L)))).Times(1) |
392 | - .WillOnce(Return(true)); |
393 | + EXPECT_CALL(*handler, handle_input(AllOf(KeyDownEvent(), KeyOfSymbol(XKB_KEY_Shift_L)))).Times(1); |
394 | EXPECT_CALL(*handler, handle_input(AllOf(KeyDownEvent(), KeyOfSymbol(XKB_KEY_dollar)))).Times(1) |
395 | - .WillOnce(Return(true)); |
396 | + .WillOnce(mt::WakeUp(&events_received)); |
397 | } |
398 | } client_config; |
399 | launch_client_process(client_config); |
400 | } |
401 | |
402 | -// TODO: This assumes that clients are placed by shell at 0,0. Which probably isn't quite safe! |
403 | TEST_F(TestClientInput, clients_receive_motion_inside_window) |
404 | { |
405 | using namespace ::testing; |
406 | + static std::string const test_client_name = "1"; |
407 | |
408 | - struct InputProducingServerConfiguration : FakeInputServerConfiguration |
409 | + struct ServerConfiguration : public mtf::InputTestingServerConfiguration |
410 | { |
411 | - void inject_input_after_focus() |
412 | + void inject_input() |
413 | { |
414 | - fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(InputReceivingClient::surface_width, |
415 | - InputReceivingClient::surface_height)); |
416 | + wait_until_client_appears(test_client_name); |
417 | + |
418 | + fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(InputClient::surface_width, |
419 | + InputClient::surface_height)); |
420 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,2)); |
421 | } |
422 | } server_config; |
423 | launch_server_process(server_config); |
424 | |
425 | - struct MotionReceivingClient : InputReceivingClient |
426 | + struct MotionReceivingClient : InputClient |
427 | { |
428 | - MotionReceivingClient() : InputReceivingClient(2) {} |
429 | + MotionReceivingClient() : InputClient(test_client_name) {} |
430 | |
431 | - void expect_input() |
432 | + void expect_input(mt::WaitCondition& events_received) override |
433 | { |
434 | using namespace ::testing; |
435 | |
436 | InSequence seq; |
437 | |
438 | // We should see the cursor enter |
439 | - EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1).WillOnce(Return(true)); |
440 | + EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1); |
441 | EXPECT_CALL(*handler, handle_input( |
442 | - MotionEventWithPosition(InputReceivingClient::surface_width, |
443 | - InputReceivingClient::surface_height))).Times(1).WillOnce(Return(true)); |
444 | + MotionEventWithPosition(InputClient::surface_width, |
445 | + InputClient::surface_height))).Times(1) |
446 | + .WillOnce(mt::WakeUp(&events_received)); |
447 | // But we should not receive an event for the second movement outside of our surface! |
448 | } |
449 | } client_config; |
450 | @@ -446,27 +371,31 @@ |
451 | { |
452 | using namespace ::testing; |
453 | |
454 | - struct InputProducingServerConfiguration : FakeInputServerConfiguration |
455 | + static std::string const test_client_name = "1"; |
456 | + |
457 | + struct ServerConfiguration : public mtf::InputTestingServerConfiguration |
458 | { |
459 | - void inject_input_after_focus() |
460 | + void inject_input() |
461 | { |
462 | + wait_until_client_appears(test_client_name); |
463 | fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); |
464 | } |
465 | } server_config; |
466 | launch_server_process(server_config); |
467 | |
468 | - struct ButtonReceivingClient : InputReceivingClient |
469 | + struct ButtonReceivingClient : InputClient |
470 | { |
471 | - ButtonReceivingClient() : InputReceivingClient(1) {} |
472 | + ButtonReceivingClient() : InputClient(test_client_name) {} |
473 | |
474 | - void expect_input() |
475 | + void expect_input(mt::WaitCondition& events_received) override |
476 | { |
477 | using namespace ::testing; |
478 | |
479 | InSequence seq; |
480 | |
481 | // The cursor starts at (0, 0). |
482 | - EXPECT_CALL(*handler, handle_input(ButtonDownEvent(0, 0))).Times(1).WillOnce(Return(true)); |
483 | + EXPECT_CALL(*handler, handle_input(ButtonDownEvent(0, 0))).Times(1) |
484 | + .WillOnce(mt::WakeUp(&events_received)); |
485 | } |
486 | } client_config; |
487 | launch_client_process(client_config); |
488 | @@ -474,44 +403,26 @@ |
489 | |
490 | namespace |
491 | { |
492 | -struct StubViewableArea : public mg::ViewableArea |
493 | -{ |
494 | - StubViewableArea(int width, int height) : |
495 | - width(width), |
496 | - height(height), |
497 | - x(0), |
498 | - y(0) |
499 | - { |
500 | - } |
501 | - |
502 | - geom::Rectangle view_area() const |
503 | - { |
504 | - return geom::Rectangle{geom::Point{x, y}, |
505 | - geom::Size{width, height}}; |
506 | - } |
507 | - |
508 | - geom::Width const width; |
509 | - geom::Height const height; |
510 | - geom::X const x; |
511 | - geom::Y const y; |
512 | -}; |
513 | - |
514 | -typedef std::map<std::string, geom::Point> PositionList; |
515 | +typedef std::map<std::string, geom::Rectangle> GeometryList; |
516 | |
517 | struct StaticPlacementStrategy : public msh::PlacementStrategy |
518 | { |
519 | - StaticPlacementStrategy(PositionList positions) |
520 | - : surface_positions_by_name(positions) |
521 | + StaticPlacementStrategy(GeometryList const& positions) |
522 | + : surface_geometry_by_name(positions) |
523 | { |
524 | } |
525 | |
526 | msh::SurfaceCreationParameters place(msh::SurfaceCreationParameters const& request_parameters) |
527 | { |
528 | auto placed = request_parameters; |
529 | - placed.top_left = surface_positions_by_name[request_parameters.name]; |
530 | + auto geometry = surface_geometry_by_name[request_parameters.name]; |
531 | + |
532 | + placed.top_left = geometry.top_left; |
533 | + placed.size = geometry.size; |
534 | + |
535 | return placed; |
536 | } |
537 | - PositionList surface_positions_by_name; |
538 | + GeometryList surface_geometry_by_name; |
539 | }; |
540 | |
541 | } |
542 | @@ -520,115 +431,77 @@ |
543 | { |
544 | using namespace ::testing; |
545 | |
546 | - int const screen_width = 1000; |
547 | - int const screen_height = 800; |
548 | - int const client_width = screen_width/2; |
549 | - int const client_height = screen_height; |
550 | - std::string const surface1_name = "1"; |
551 | - std::string const surface2_name = "2"; |
552 | + static int const screen_width = 1000; |
553 | + static int const screen_height = 800; |
554 | + static int const client_height = screen_width/2; |
555 | + static int const client_width = screen_width/2; |
556 | + static std::string const test_client_1 = "1"; |
557 | + static std::string const test_client_2 = "2"; |
558 | |
559 | - PositionList positions; |
560 | - positions[surface1_name] = geom::Point{geom::X{0}, geom::Y{0}}; |
561 | - positions[surface2_name] = geom::Point{geom::X{screen_width/2}, geom::Y{0}}; |
562 | - |
563 | - struct TestServerConfiguration : FakeInputServerConfiguration |
564 | + struct ServerConfiguration : mtf::InputTestingServerConfiguration |
565 | { |
566 | - TestServerConfiguration(int screen_width, int screen_height, PositionList positions) |
567 | - : screen_width(screen_width), |
568 | - screen_height(screen_height), |
569 | - positions(positions), |
570 | - clients_ready(0) |
571 | - { |
572 | - } |
573 | - |
574 | - std::shared_ptr<mg::ViewableArea> the_viewable_area() override |
575 | - { |
576 | - return std::make_shared<StubViewableArea>(screen_width, screen_height); |
577 | - } |
578 | std::shared_ptr<msh::PlacementStrategy> the_shell_placement_strategy() override |
579 | { |
580 | + static GeometryList positions; |
581 | + positions[test_client_1] = geom::Rectangle{geom::Point{geom::X{0}, geom::Y{0}}, |
582 | + geom::Size{geom::Width{client_width}, geom::Height{client_height}}}; |
583 | + positions[test_client_2] = geom::Rectangle{geom::Point{geom::X{screen_width/2}, geom::Y{0}}, |
584 | + geom::Size{geom::Width{client_width}, geom::Height{client_height}}}; |
585 | + |
586 | return std::make_shared<StaticPlacementStrategy>(positions); |
587 | } |
588 | + |
589 | + geom::Rectangle the_screen_geometry() override |
590 | + { |
591 | + return geom::Rectangle{geom::Point{geom::X{0}, geom::Y{0}}, |
592 | + geom::Size{geom::Width{screen_width}, geom::Height{screen_height}}}; |
593 | + } |
594 | |
595 | - void inject_input_after_focus() override |
596 | + void inject_input() override |
597 | { |
598 | - clients_ready++; |
599 | - if (clients_ready != 2) // We wait until both clients have connected and registered with the input stack. |
600 | - return; |
601 | - |
602 | + wait_until_client_appears(test_client_1); |
603 | + wait_until_client_appears(test_client_2); |
604 | // In the bounds of the first surface |
605 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2-1, 0)); |
606 | // In the bounds of the second surface |
607 | fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2, 0)); |
608 | } |
609 | - int screen_width, screen_height; |
610 | - PositionList positions; |
611 | - |
612 | - int clients_ready; |
613 | - } server_config(screen_width, screen_height, positions); |
614 | + } server_config; |
615 | |
616 | launch_server_process(server_config); |
617 | |
618 | - MirSurfaceParameters const surface1_params = |
619 | - { |
620 | - surface1_name.c_str(), |
621 | - client_width, client_height, |
622 | - mir_pixel_format_abgr_8888, |
623 | - mir_buffer_usage_hardware |
624 | - }; |
625 | - MirSurfaceParameters const surface2_params = |
626 | - { |
627 | - surface2_name.c_str(), |
628 | - client_width, client_height, |
629 | - mir_pixel_format_abgr_8888, |
630 | - mir_buffer_usage_hardware |
631 | - }; |
632 | - struct ParameterizedClient : InputReceivingClient |
633 | + struct InputClientOne : InputClient |
634 | { |
635 | - ParameterizedClient(MirSurfaceParameters params, int events_to_expect) : |
636 | - InputReceivingClient(events_to_expect), |
637 | - params(params) |
638 | + InputClientOne() |
639 | + : InputClient(test_client_1) |
640 | { |
641 | } |
642 | |
643 | - MirSurfaceParameters parameters() override |
644 | + void expect_input(mt::WaitCondition& events_received) override |
645 | { |
646 | - return params; |
647 | + InSequence seq; |
648 | + EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1); |
649 | + EXPECT_CALL(*handler, handle_input(MotionEventWithPosition(client_width - 1, 0))).Times(1); |
650 | + EXPECT_CALL(*handler, handle_input(HoverExitEvent())).Times(1) |
651 | + .WillOnce(mt::WakeUp(&events_received)); |
652 | } |
653 | + } client_1; |
654 | |
655 | - MirSurfaceParameters params; |
656 | - }; |
657 | - struct InputClientOne : ParameterizedClient |
658 | - { |
659 | - InputClientOne(MirSurfaceParameters params) : |
660 | - ParameterizedClient(params, 3) |
661 | - { |
662 | - } |
663 | - |
664 | - void expect_input() override |
665 | - { |
666 | - InSequence seq; |
667 | - EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1).WillOnce(Return(true)); |
668 | - EXPECT_CALL(*handler, handle_input( |
669 | - MotionEventWithPosition(params.width - 1, 0))).Times(1).WillOnce(Return(true)); |
670 | - EXPECT_CALL(*handler, handle_input(HoverExitEvent())).Times(1).WillOnce(Return(true)); |
671 | - } |
672 | - } client_1(surface1_params); |
673 | - struct InputClientTwo : ParameterizedClient |
674 | - { |
675 | - InputClientTwo(MirSurfaceParameters params) : |
676 | - ParameterizedClient(params, 2) |
677 | - { |
678 | - } |
679 | - |
680 | - void expect_input() override |
681 | - { |
682 | - InSequence seq; |
683 | - EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1).WillOnce(Return(true)); |
684 | - EXPECT_CALL(*handler, handle_input( |
685 | - MotionEventWithPosition(params.width - 1, 0))).Times(1).WillOnce(Return(true)); |
686 | - } |
687 | - } client_2(surface2_params); |
688 | + struct InputClientTwo : InputClient |
689 | + { |
690 | + InputClientTwo() |
691 | + : InputClient(test_client_2) |
692 | + { |
693 | + } |
694 | + |
695 | + void expect_input(mt::WaitCondition& events_received) override |
696 | + { |
697 | + InSequence seq; |
698 | + EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1); |
699 | + EXPECT_CALL(*handler, handle_input(MotionEventWithPosition(client_width - 1, 0))).Times(1) |
700 | + .WillOnce(mt::WakeUp(&events_received)); |
701 | + } |
702 | + } client_2; |
703 | |
704 | launch_client_process(client_1); |
705 | launch_client_process(client_2); |
706 | |
707 | === modified file 'tests/mir_test_framework/CMakeLists.txt' |
708 | --- tests/mir_test_framework/CMakeLists.txt 2013-04-24 05:22:20 +0000 |
709 | +++ tests/mir_test_framework/CMakeLists.txt 2013-06-14 16:12:27 +0000 |
710 | @@ -8,6 +8,7 @@ |
711 | TEST_FRAMEWORK_SRCS |
712 | |
713 | testing_server_options.cpp |
714 | + input_testing_server_options.cpp |
715 | testing_process_manager.cpp |
716 | display_server_test_fixture.cpp |
717 | process.cpp |
718 | @@ -20,6 +21,7 @@ |
719 | add_library( |
720 | mir-test-framework STATIC |
721 | ${TEST_FRAMEWORK_SRCS}) |
722 | +uses_android_input(mir-test-framework) |
723 | |
724 | target_link_libraries( |
725 | mir-test-framework |
726 | |
727 | === added file 'tests/mir_test_framework/input_testing_server_options.cpp' |
728 | --- tests/mir_test_framework/input_testing_server_options.cpp 1970-01-01 00:00:00 +0000 |
729 | +++ tests/mir_test_framework/input_testing_server_options.cpp 2013-06-14 16:12:27 +0000 |
730 | @@ -0,0 +1,247 @@ |
731 | +/* |
732 | + * Copyright © 2013 Canonical Ltd. |
733 | + * |
734 | + * This program is free software: you can redistribute it and/or modify it |
735 | + * under the terms of the GNU General Public License version 3, |
736 | + * as published by the Free Software Foundation. |
737 | + * |
738 | + * This program is distributed in the hope that it will be useful, |
739 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
740 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
741 | + * GNU General Public License for more details. |
742 | + * |
743 | + * You should have received a copy of the GNU General Public License |
744 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
745 | + * |
746 | + * Authored by: Robert Carr <robert.carr@canonical.com> |
747 | + */ |
748 | + |
749 | +#include "mir_test_framework/input_testing_server_configuration.h" |
750 | + |
751 | +#include "mir/input/surface_target.h" |
752 | +#include "mir/surfaces/input_registrar.h" |
753 | +#include "mir/graphics/display.h" |
754 | +#include "mir/graphics/viewable_area.h" |
755 | + |
756 | +#include "mir_test/fake_event_hub.h" |
757 | +#include "mir_test/fake_event_hub_input_configuration.h" |
758 | + |
759 | +#include <boost/throw_exception.hpp> |
760 | + |
761 | +#include <functional> |
762 | +#include <stdexcept> |
763 | + |
764 | +#include <time.h> |
765 | + |
766 | +namespace mtf = mir_test_framework; |
767 | + |
768 | +namespace ms = mir::surfaces; |
769 | +namespace mg = mir::graphics; |
770 | +namespace mi = mir::input; |
771 | +namespace mia = mi::android; |
772 | +namespace geom = mir::geometry; |
773 | +namespace mtd = mir::test::doubles; |
774 | + |
775 | +namespace |
776 | +{ |
777 | +class SurfaceReadinessListener |
778 | +{ |
779 | +public: |
780 | + virtual ~SurfaceReadinessListener() = default; |
781 | + |
782 | + virtual void surface_ready_for_input(std::string const& surface_name) = 0; |
783 | + virtual void surface_finished_for_input(std::string const& surface_name) = 0; |
784 | + |
785 | +protected: |
786 | + SurfaceReadinessListener() = default; |
787 | + SurfaceReadinessListener(SurfaceReadinessListener const&) = delete; |
788 | + SurfaceReadinessListener& operator=(SurfaceReadinessListener const&) = delete; |
789 | +}; |
790 | + |
791 | +class ProxyInputRegistrar : public ms::InputRegistrar |
792 | +{ |
793 | +public: |
794 | + ProxyInputRegistrar(std::shared_ptr<ms::InputRegistrar> const underlying_registrar, |
795 | + std::shared_ptr<SurfaceReadinessListener> const listener) |
796 | + : underlying_registrar(underlying_registrar), |
797 | + listener(listener) |
798 | + { |
799 | + } |
800 | + |
801 | + ~ProxyInputRegistrar() noexcept(true) = default; |
802 | + |
803 | + void input_surface_opened(std::shared_ptr<mi::SurfaceTarget> const& opened_surface) |
804 | + { |
805 | + underlying_registrar->input_surface_opened(opened_surface); |
806 | + listener->surface_ready_for_input(opened_surface->name()); |
807 | + } |
808 | + void input_surface_closed(std::shared_ptr<mi::SurfaceTarget> const& closed_surface) |
809 | + { |
810 | + underlying_registrar->input_surface_closed(closed_surface); |
811 | + listener->surface_finished_for_input(closed_surface->name()); |
812 | + } |
813 | + |
814 | +private: |
815 | + std::shared_ptr<ms::InputRegistrar> const underlying_registrar; |
816 | + std::shared_ptr<SurfaceReadinessListener> const listener; |
817 | +}; |
818 | + |
819 | +struct SizedViewArea : public mg::ViewableArea |
820 | +{ |
821 | + SizedViewArea(geom::Rectangle const& area) |
822 | + : area(area) |
823 | + { |
824 | + } |
825 | + geom::Rectangle view_area() const override |
826 | + { |
827 | + return area; |
828 | + } |
829 | + geom::Rectangle area; |
830 | +}; |
831 | +} |
832 | + |
833 | +mtf::InputTestingServerConfiguration::InputTestingServerConfiguration() |
834 | +{ |
835 | +} |
836 | + |
837 | +void mtf::InputTestingServerConfiguration::exec() |
838 | +{ |
839 | + input_injection_thread = std::thread(std::mem_fn(&mtf::InputTestingServerConfiguration::inject_input), this); |
840 | +} |
841 | + |
842 | +void mtf::InputTestingServerConfiguration::on_exit() |
843 | +{ |
844 | + input_injection_thread.join(); |
845 | +} |
846 | + |
847 | +std::shared_ptr<mi::InputConfiguration> mtf::InputTestingServerConfiguration::the_input_configuration() |
848 | +{ |
849 | + if (!input_configuration) |
850 | + { |
851 | + std::shared_ptr<mi::CursorListener> null_cursor_listener{nullptr}; |
852 | + |
853 | + input_configuration = std::make_shared<mtd::FakeEventHubInputConfiguration>(the_event_filters(), |
854 | + the_display(), |
855 | + null_cursor_listener, |
856 | + the_input_report()); |
857 | + fake_event_hub = input_configuration->the_fake_event_hub(); |
858 | + |
859 | + fake_event_hub->synthesize_builtin_keyboard_added(); |
860 | + fake_event_hub->synthesize_builtin_cursor_added(); |
861 | + fake_event_hub->synthesize_device_scan_complete(); |
862 | + } |
863 | + |
864 | + return input_configuration; |
865 | +} |
866 | + |
867 | +std::shared_ptr<ms::InputRegistrar> mtf::InputTestingServerConfiguration::the_input_registrar() |
868 | +{ |
869 | + struct LifecycleTracker : public SurfaceReadinessListener |
870 | + { |
871 | + LifecycleTracker(std::mutex& lifecycle_lock, |
872 | + std::condition_variable &lifecycle_condition, |
873 | + std::map<std::string, mtf::ClientLifecycleState> &client_lifecycles) |
874 | + : lifecycle_lock(lifecycle_lock), |
875 | + lifecycle_condition(lifecycle_condition), |
876 | + client_lifecycles(client_lifecycles) |
877 | + { |
878 | + } |
879 | + void surface_ready_for_input(std::string const& surface_name) |
880 | + { |
881 | + std::unique_lock<std::mutex> lg(lifecycle_lock); |
882 | + client_lifecycles[surface_name] = mtf::ClientLifecycleState::appeared; |
883 | + lifecycle_condition.notify_all(); |
884 | + } |
885 | + |
886 | + void surface_finished_for_input(std::string const& surface_name) |
887 | + { |
888 | + std::unique_lock<std::mutex> lg(lifecycle_lock); |
889 | + client_lifecycles[surface_name] = mtf::ClientLifecycleState::vanished; |
890 | + lifecycle_condition.notify_all(); |
891 | + } |
892 | + std::mutex &lifecycle_lock; |
893 | + std::condition_variable &lifecycle_condition; |
894 | + std::map<std::string, mtf::ClientLifecycleState> &client_lifecycles; |
895 | + }; |
896 | + |
897 | + if (!input_registrar) |
898 | + { |
899 | + auto registrar_listener = std::make_shared<LifecycleTracker>(lifecycle_lock, |
900 | + lifecycle_condition, |
901 | + client_lifecycles); |
902 | + input_registrar = std::make_shared<ProxyInputRegistrar>(the_input_configuration()->the_input_registrar(), |
903 | + registrar_listener); |
904 | + } |
905 | + |
906 | + return input_registrar; |
907 | +} |
908 | + |
909 | +geom::Rectangle mtf::InputTestingServerConfiguration::the_screen_geometry() |
910 | +{ |
911 | + static geom::Rectangle const default_geometry{geom::Point{geom::X{0}, geom::Y{0}}, |
912 | + geom::Size{geom::Width{1600}, geom::Height{1600}}}; |
913 | + return default_geometry; |
914 | +} |
915 | + |
916 | +std::shared_ptr<mg::ViewableArea> mtf::InputTestingServerConfiguration::the_viewable_area() |
917 | +{ |
918 | + if (!view_area) |
919 | + view_area = std::make_shared<SizedViewArea>(the_screen_geometry()); |
920 | + return view_area; |
921 | +} |
922 | + |
923 | +void mtf::InputTestingServerConfiguration::wait_until_client_appears(std::string const& surface_name) |
924 | +{ |
925 | + std::unique_lock<std::mutex> lg(lifecycle_lock); |
926 | + |
927 | + time_t end_time = ::time(NULL) + 60; |
928 | + |
929 | + if (client_lifecycles[surface_name] == appeared) |
930 | + { |
931 | + return; |
932 | + } |
933 | + else if (client_lifecycles[surface_name] == vanished) |
934 | + { |
935 | + BOOST_THROW_EXCEPTION(std::runtime_error("Waiting for a client (" + surface_name + ") to appear but it has already vanished")); |
936 | + } |
937 | + else |
938 | + { |
939 | + while (::time(NULL) < end_time) |
940 | + { |
941 | + lifecycle_condition.wait_for(lg, std::chrono::seconds(60)); |
942 | + if (client_lifecycles[surface_name] == appeared) |
943 | + { |
944 | + return; |
945 | + } |
946 | + } |
947 | + BOOST_THROW_EXCEPTION(std::runtime_error("Timed out waiting for client (" + surface_name + ") to appear")); |
948 | + } |
949 | +} |
950 | + |
951 | +void mtf::InputTestingServerConfiguration::wait_until_client_vanishes(std::string const& surface_name) |
952 | +{ |
953 | + std::unique_lock<std::mutex> lg(lifecycle_lock); |
954 | + |
955 | + time_t end_time = ::time(NULL) + 60; |
956 | + |
957 | + if (client_lifecycles[surface_name] == vanished) |
958 | + { |
959 | + return; |
960 | + } |
961 | + else if (client_lifecycles[surface_name] == starting) |
962 | + { |
963 | + BOOST_THROW_EXCEPTION(std::runtime_error("Waiting for a client (" + surface_name + ") to vanish but it has not started")); |
964 | + } |
965 | + else |
966 | + { |
967 | + while (::time(NULL) < end_time) |
968 | + { |
969 | + lifecycle_condition.wait_for(lg, std::chrono::seconds(60)); |
970 | + if (client_lifecycles[surface_name] == vanished) |
971 | + { |
972 | + return; |
973 | + } |
974 | + } |
975 | + BOOST_THROW_EXCEPTION(std::runtime_error("Timed out waiting for client (" + surface_name + ") to vanish")); |
976 | + } |
977 | +} |
FAILED: Continuous integration, rev:745 /code.launchpad .net/~robertcar r/mir/improve- input-acceptanc e/+merge/ 169311/ +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-ci/ 728/ jenkins. qa.ubuntu. com/job/ mir-android- raring- i386-build/ 905 jenkins. qa.ubuntu. com/job/ mir-clang- raring- amd64-build/ 787 jenkins. qa.ubuntu. com/job/ mir-raring- amd64-ci/ 213/console
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ mir-ci/ 728/rebuild
http://