Mir

Merge lp:~robertcarr/mir/refactor-input-acceptance-take-1 into lp:mir

Proposed by Robert Carr
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
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_input.cpp

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.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Looks sane in general. I am not familiar enough with the functionality to be sure everything is covered adequately.

review: Approve
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Revision history for this message
Robert Carr (robertcarr) wrote :

The tests are basically identical. Mostly about.

1. Removing some dead code (wait_until_client_appears, etc).
2. Factoring out highly repetitive code: i.e. this sort of stuff
918 - struct ServerConfiguration : mtf::InputTestingServerConfiguration
919 - {
920 - mtf::CrossProcessSync input_cb_setup_fence;
921 -
922 - ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
923 - : input_cb_setup_fence(input_cb_setup_fence)
--------------
0 - void inject_input()
301 - {
302 - wait_until_client_appears(test_client_name);
303 - input_cb_setup_fence.wait_for_signal_ready_for();
304 -

309 - } server_config(fence);
310 - launch_server_process(server_config);
-------------
311 -
312 - struct KeyReceivingClient : InputClient
313 - {
314 - KeyReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {}
3. Removing some noise from the file, i.e. client_event_matchers.h

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::InputReceivingClient client;
client.expects_input(mis::a_button_down_event()).expects_input(mis::a_button_up_event()).exits();

Preview Diff

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

Subscribers

People subscribed via source and target branches