Mir

Merge lp:~afrantzis/mir/fix-1342567-test-client-cursor-api into lp:mir

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Alexandros Frantzis
Approved revision: no longer in the source branch.
Merged at revision: 1894
Proposed branch: lp:~afrantzis/mir/fix-1342567-test-client-cursor-api
Merge into: lp:mir
Diff against target: 766 lines (+265/-382)
1 file modified
tests/acceptance-tests/test_client_cursor_api.cpp (+265/-382)
To merge this branch: bzr merge lp:~afrantzis/mir/fix-1342567-test-client-cursor-api
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Daniel van Vugt Approve
Kevin DuBois (community) Approve
Review via email: mp+233316@code.launchpad.net

Commit message

tests: Refactor the TestClientCursorAPI tests to simplify them and fix races (LP: #1342567)

Description of the change

tests: Refactor the TestClientCursorAPI tests to simplify them and fix races

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
Kevin DuBois (kdub) wrote :

looks good to me

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Sure, why not.

review: Approve
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) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/acceptance-tests/test_client_cursor_api.cpp'
2--- tests/acceptance-tests/test_client_cursor_api.cpp 2014-09-01 04:53:25 +0000
3+++ tests/acceptance-tests/test_client_cursor_api.cpp 2014-09-04 08:51:10 +0000
4@@ -1,5 +1,5 @@
5 /*
6- * Copyright © 2013 Canonical Ltd.
7+ * Copyright © 2013-2014 Canonical Ltd.
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 3 as
11@@ -14,40 +14,34 @@
12 * along with this program. If not, see <http://www.gnu.org/licenses/>.
13 *
14 * Authored by: Robert Carr <robert.carr@canonical.com>
15+ * Alexandros Frantzis <alexandros.frantzis@canonical.com>
16 */
17
18 #include "mir/graphics/cursor.h"
19 #include "mir/graphics/cursor_image.h"
20 #include "mir/input/cursor_images.h"
21-#include "mir/scene/surface.h"
22-#include "mir/scene/surface_factory.h"
23-#include "mir/scene/null_observer.h"
24-#include "mir/scene/null_surface_observer.h"
25-
26-#include "mir_toolkit/mir_client_library.h"
27-
28-#include "mir_test/barrier.h"
29-#include "mir_test/fake_event_hub.h"
30+
31+#include "mir_test_framework/in_process_server.h"
32+#include "mir_test_framework/fake_event_hub_server_configuration.h"
33+#include "mir_test_framework/declarative_placement_strategy.h"
34+#include "mir_test_framework/using_stub_client_platform.h"
35+
36 #include "mir_test/fake_shared.h"
37+#include "mir_test/spin_wait.h"
38 #include "mir_test/wait_condition.h"
39-#include "mir_test_framework/input_testing_server_configuration.h"
40-#include "mir_test_framework/testing_client_configuration.h"
41-#include "mir_test_framework/declarative_placement_strategy.h"
42-#include "mir_test_framework/deferred_in_process_server.h"
43-#include "mir_test_framework/using_stub_client_platform.h"
44+#include "mir_test/fake_event_hub.h"
45+
46+#include "mir_toolkit/mir_client_library.h"
47
48 #include <gtest/gtest.h>
49 #include <gmock/gmock.h>
50
51+namespace mt = mir::test;
52+namespace mtf = mir_test_framework;
53+namespace geom = mir::geometry;
54 namespace mg = mir::graphics;
55-namespace ms = mir::scene;
56-namespace msh = mir::shell;
57 namespace mi = mir::input;
58 namespace mis = mir::input::synthesis;
59-namespace geom = mir::geometry;
60-
61-namespace mt = mir::test;
62-namespace mtf = mir_test_framework;
63
64 namespace
65 {
66@@ -59,9 +53,7 @@
67
68 // We are not interested in mocking the motion in these tests as we
69 // generate it ourself.
70- void move_to(geom::Point)
71- {
72- }
73+ void move_to(geom::Point) override {}
74 };
75
76 struct NamedCursorImage : public mg::CursorImage
77@@ -71,16 +63,17 @@
78 {
79 }
80
81- void const* as_argb_8888() const { return nullptr; }
82- geom::Size size() const { return geom::Size{}; }
83- geom::Displacement hotspot() const { return geom::Displacement{0, 0}; }
84+ void const* as_argb_8888() const override { return nullptr; }
85+ geom::Size size() const override { return geom::Size{}; }
86+ geom::Displacement hotspot() const override { return geom::Displacement{0, 0}; }
87
88 std::string const cursor_name;
89 };
90
91 struct NamedCursorImages : public mi::CursorImages
92 {
93- std::shared_ptr<mg::CursorImage> image(std::string const& name, geom::Size const& /* size */)
94+ std::shared_ptr<mg::CursorImage>
95+ image(std::string const& name, geom::Size const& /* size */) override
96 {
97 return std::make_shared<NamedCursorImage>(name);
98 }
99@@ -90,7 +83,7 @@
100 {
101 auto image = dynamic_cast<NamedCursorImage const*>(&i);
102 assert(image);
103-
104+
105 return image->cursor_name == name;
106 }
107
108@@ -104,84 +97,122 @@
109 return cursor_is_named(arg, name);
110 }
111
112-struct ClientConfig : mtf::TestingClientConfiguration
113+struct CursorClient
114 {
115- std::string connect_string;
116-
117+ CursorClient(std::string const& connect_string, std::string const& client_name)
118+ : connect_string{connect_string}, client_name{client_name}
119+ {
120+ }
121+
122+ virtual ~CursorClient()
123+ {
124+ teardown.wake_up_everyone();
125+ if (client_thread.joinable())
126+ client_thread.join();
127+ }
128+
129+ void run()
130+ {
131+ mir::test::WaitCondition setup_done;
132+
133+ client_thread = std::thread{
134+ [this,&setup_done]
135+ {
136+ auto const connection =
137+ mir_connect_sync(connect_string.c_str(), client_name.c_str());
138+
139+ MirSurfaceParameters const request_params =
140+ {
141+ client_name.c_str(),
142+ // For this fixture, we force geometry on server side
143+ 0, 0,
144+ mir_pixel_format_abgr_8888,
145+ mir_buffer_usage_hardware,
146+ mir_display_output_id_invalid
147+ };
148+ auto const surface =
149+ mir_connection_create_surface_sync(connection, &request_params);
150+
151+ mir_surface_swap_buffers_sync(surface);
152+
153+ wait_for_surface_to_become_focused_and_exposed(surface);
154+
155+ setup_cursor(surface);
156+
157+ setup_done.wake_up_everyone();
158+
159+ teardown.wait_for_at_most_seconds(10);
160+ mir_surface_release_sync(surface);
161+ mir_connection_release(connection);
162+ }};
163+
164+ setup_done.wait_for_at_most_seconds(5);
165+ }
166+
167+ virtual void setup_cursor(MirSurface*)
168+ {
169+ }
170+
171+ void wait_for_surface_to_become_focused_and_exposed(MirSurface* surface)
172+ {
173+ bool success = mt::spin_wait_for_condition_or_timeout(
174+ [surface]
175+ {
176+ return mir_surface_get_visibility(surface) == mir_surface_visibility_exposed &&
177+ mir_surface_get_focus(surface) == mir_surface_focused;
178+ },
179+ std::chrono::seconds{5});
180+
181+ if (!success)
182+ throw std::runtime_error("Timeout waiting for surface to become focused and exposed");
183+ }
184+
185+ std::string const connect_string;
186 std::string const client_name;
187
188- mt::Barrier& set_cursor_complete;
189- mt::Barrier& client_may_exit;
190-
191- std::function<void(MirSurface*)> set_cursor;
192-
193- ClientConfig(std::string const& client_name,
194- mt::Barrier& cursor_ready_fence,
195- mt::Barrier& client_may_exit_fence)
196- : client_name(client_name),
197- set_cursor_complete(cursor_ready_fence),
198- client_may_exit(client_may_exit_fence)
199- {
200- }
201-
202- virtual void thread_exec()
203- {
204- auto connection = mir_connect_sync(connect_string.c_str(),
205- client_name.c_str());
206-
207- ASSERT_TRUE(connection != NULL);
208- MirSurfaceParameters const request_params =
209- {
210- client_name.c_str(),
211- // For this fixture, we force geometry on server side
212- 0, 0,
213- mir_pixel_format_abgr_8888,
214- mir_buffer_usage_hardware,
215- mir_display_output_id_invalid
216- };
217- auto surface = mir_connection_create_surface_sync(connection, &request_params);
218-
219- set_cursor(surface);
220- set_cursor_complete.ready();
221-
222- client_may_exit.ready();
223-
224- mir_surface_release_sync(surface);
225- mir_connection_release(connection);
226- }
227- void tear_down() { if (thread.joinable()) thread.join(); }
228-
229- void exec() override
230- {
231- thread = std::thread([this]{ thread_exec(); });
232- }
233-private:
234- std::thread thread;
235-};
236-
237-struct ServerConfiguration : mtf::InputTestingServerConfiguration
238-{
239- mt::Barrier& cursor_configured_fence;
240- mt::Barrier& client_may_exit_fence;
241-
242- std::function<void(MockCursor&, mt::WaitCondition&)> expect_cursor_states;
243- std::function<void(ServerConfiguration*)> synthesize_cursor_motion;
244-
245- mtf::SurfaceGeometries client_geometries;
246- mtf::SurfaceDepths client_depths;
247-
248- MockCursor cursor;
249-
250- ServerConfiguration(mt::Barrier& cursor_configured_fence, mt::Barrier& client_may_exit_fence)
251- : cursor_configured_fence(cursor_configured_fence),
252- client_may_exit_fence(client_may_exit_fence)
253- {
254- }
255-
256- std::shared_ptr<ms::PlacementStrategy> the_placement_strategy() override
257+ std::thread client_thread;
258+ mir::test::WaitCondition teardown;
259+};
260+
261+struct DisabledCursorClient : CursorClient
262+{
263+ using CursorClient::CursorClient;
264+
265+ void setup_cursor(MirSurface* surface) override
266+ {
267+ auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
268+ mir_wait_for(mir_surface_configure_cursor(surface, conf));
269+ mir_cursor_configuration_destroy(conf);
270+ }
271+};
272+
273+struct NamedCursorClient : CursorClient
274+{
275+ NamedCursorClient(
276+ std::string const& connect_string,
277+ std::string const& client_name,
278+ std::string const& cursor_name)
279+ : CursorClient{connect_string, client_name},
280+ cursor_name{cursor_name}
281+ {
282+ }
283+
284+ void setup_cursor(MirSurface* surface) override
285+ {
286+ auto conf = mir_cursor_configuration_from_name(cursor_name.c_str());
287+ mir_wait_for(mir_surface_configure_cursor(surface, conf));
288+ mir_cursor_configuration_destroy(conf);
289+ }
290+
291+ std::string const cursor_name;
292+};
293+
294+struct TestServerConfiguration : mtf::FakeEventHubServerConfiguration
295+{
296+ std::shared_ptr<mir::scene::PlacementStrategy> the_placement_strategy() override
297 {
298 return std::make_shared<mtf::DeclarativePlacementStrategy>(
299- InputTestingServerConfiguration::the_placement_strategy(),
300+ FakeEventHubServerConfiguration::the_placement_strategy(),
301 client_geometries, client_depths);
302 }
303
304@@ -189,83 +220,51 @@
305 {
306 return mt::fake_shared(cursor);
307 }
308-
309+
310 std::shared_ptr<mi::CursorImages> the_cursor_images() override
311 {
312 return std::make_shared<NamedCursorImages>();
313 }
314-
315- void inject_input()
316- {
317- using namespace ::testing;
318- cursor_configured_fence.ready();
319-
320- mt::WaitCondition expectations_satisfied;
321-
322- // Clear any states applied during server initialization.
323- Mock::VerifyAndClearExpectations(&cursor);
324- expect_cursor_states(cursor, expectations_satisfied);
325-
326- // We are only interested in the cursor image changes, not
327- // the synthetic motion.
328-
329- synthesize_cursor_motion(this);
330- expectations_satisfied.wait_for_at_most_seconds(60);
331-
332- Mock::VerifyAndClearExpectations(&cursor);
333+
334+ MockCursor cursor;
335+ mtf::SurfaceGeometries client_geometries;
336+ mtf::SurfaceDepths client_depths;
337+};
338+
339+struct TestClientCursorAPI : mtf::InProcessServer
340+{
341+ mir::DefaultServerConfiguration& server_config() override
342+ {
343+ return server_configuration_;
344+ }
345+
346+ TestServerConfiguration& test_server_config()
347+ {
348+ return server_configuration_;
349+ }
350+
351+ mir::input::android::FakeEventHub* fake_event_hub()
352+ {
353+ return server_configuration_.fake_event_hub;
354+ }
355+
356+ void client_shutdown_expectations()
357+ {
358+ using namespace testing;
359+ Mock::VerifyAndClearExpectations(&test_server_config().cursor);
360
361 // Client shutdown
362- EXPECT_CALL(cursor, show(_)).Times(AnyNumber());
363- EXPECT_CALL(cursor, hide()).Times(AnyNumber());
364- client_may_exit_fence.ready();
365+ EXPECT_CALL(test_server_config().cursor, show(_)).Times(AnyNumber());
366+ EXPECT_CALL(test_server_config().cursor, hide()).Times(AnyNumber());
367 }
368-
369-};
370
371-struct TestClientCursorAPI : mtf::DeferredInProcessServer
372-{
373- std::string const client_name_1 = "1";
374- std::string const client_name_2 = "2";
375- std::string const client_cursor_1 = "cursor-1";
376- std::string const client_cursor_2 = "cursor-2";
377-
378- // Reset to higher values for more clients.
379- mt::Barrier cursor_configured_fence{2};
380- mt::Barrier client_may_exit_fence{2};
381-
382- ServerConfiguration server_configuration{cursor_configured_fence, client_may_exit_fence};
383- mir::DefaultServerConfiguration& server_config() override { return server_configuration; }
384+ std::string const client_name_1{"client-1"};
385+ std::string const client_name_2{"client-2"};
386+ std::string const client_cursor_1{"cursor-1"};
387+ std::string const client_cursor_2{"cursor-2"};
388+ TestServerConfiguration server_configuration_;
389+ mir::test::WaitCondition expectations_satisfied;
390 mtf::UsingStubClientPlatform using_stub_client_platform;
391-
392- ClientConfig client_config_1{client_name_1, cursor_configured_fence, client_may_exit_fence};
393- ClientConfig client_config_2{client_name_2, cursor_configured_fence, client_may_exit_fence};
394-
395- // Default number allows one client.
396- void set_client_count(unsigned count)
397- {
398- cursor_configured_fence.reset(count + 1);
399- client_may_exit_fence.reset(count + 1);
400- }
401-
402- void start_server()
403- {
404- DeferredInProcessServer::start_server();
405- server_configuration.exec();
406- }
407-
408- void start_client(ClientConfig& config)
409- {
410- config.connect_string = new_connection();
411- config.exec();
412- }
413-
414- void TearDown()
415- {
416- client_config_1.tear_down();
417- client_config_2.tear_down();
418- server_configuration.on_exit();
419- DeferredInProcessServer::TearDown();
420- }
421 };
422
423 }
424@@ -277,238 +276,107 @@
425 {
426 using namespace ::testing;
427
428- server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
429- geom::Size{geom::Width{1}, geom::Height{1}}};
430-
431-
432- server_configuration.expect_cursor_states = [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
433- {
434- EXPECT_CALL(cursor, hide()).Times(1)
435- .WillOnce(mt::WakeUp(&expectations_satisfied));
436- };
437- server_configuration.synthesize_cursor_motion = [](ServerConfiguration *server)
438- {
439- server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
440- };
441- start_server();
442-
443- client_config_1.set_cursor = [](MirSurface *surface)
444- {
445- auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
446- mir_wait_for(mir_surface_configure_cursor(surface, conf));
447- mir_cursor_configuration_destroy(conf);
448- };
449- start_client(client_config_1);
450+ test_server_config().client_geometries[client_name_1] =
451+ geom::Rectangle{{1, 0}, {1, 1}};
452+
453+ DisabledCursorClient client{new_connection(), client_name_1};
454+ client.run();
455+
456+ EXPECT_CALL(test_server_config().cursor, hide())
457+ .WillOnce(mt::WakeUp(&expectations_satisfied));
458+
459+ fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
460+
461+ expectations_satisfied.wait_for_at_most_seconds(5);
462+
463+ client_shutdown_expectations();
464 }
465
466 TEST_F(TestClientCursorAPI, cursor_restored_when_leaving_surface)
467 {
468 using namespace ::testing;
469
470- server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
471- geom::Size{geom::Width{1}, geom::Height{1}}};
472-
473- mtf::CrossProcessSync client_ready_fence, client_may_exit_fence;
474-
475- server_configuration.expect_cursor_states = [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
476- {
477- InSequence seq;
478- EXPECT_CALL(cursor, hide()).Times(1);
479- EXPECT_CALL(cursor, show(DefaultCursorImage())).Times(1)
480- .WillOnce(mt::WakeUp(&expectations_satisfied));
481- };
482- server_configuration.synthesize_cursor_motion = [](ServerConfiguration *server)
483- {
484- server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
485- server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,0));
486- };
487- start_server();
488-
489-
490- client_config_1.set_cursor = [](MirSurface *surface)
491- {
492- // Disable cursor
493- auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
494- mir_wait_for(mir_surface_configure_cursor(surface, conf));
495- mir_cursor_configuration_destroy(conf);
496- };
497- start_client(client_config_1);
498+ test_server_config().client_geometries[client_name_1] =
499+ geom::Rectangle{{1, 0}, {1, 1}};
500+
501+ DisabledCursorClient client{new_connection(), client_name_1};
502+ client.run();
503+
504+ InSequence seq;
505+ EXPECT_CALL(test_server_config().cursor, hide());
506+ EXPECT_CALL(test_server_config().cursor, show(DefaultCursorImage()))
507+ .WillOnce(mt::WakeUp(&expectations_satisfied));
508+
509+ fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
510+ fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(2, 0));
511+
512+ expectations_satisfied.wait_for_at_most_seconds(5);
513+
514+ client_shutdown_expectations();
515 }
516
517 TEST_F(TestClientCursorAPI, cursor_changed_when_crossing_surface_boundaries)
518 {
519 using namespace ::testing;
520
521- server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
522- geom::Size{geom::Width{1}, geom::Height{1}}};
523- server_configuration.client_geometries[client_name_2] = geom::Rectangle{geom::Point{geom::X{2}, geom::Y{0}},
524- geom::Size{geom::Width{1}, geom::Height{1}}};
525- set_client_count(2);
526-
527- server_configuration.expect_cursor_states =
528- [this](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
529- {
530- InSequence seq;
531- EXPECT_CALL(cursor, show(CursorNamed(client_cursor_1))).Times(1);
532- EXPECT_CALL(cursor, show(CursorNamed(client_cursor_2))).Times(1)
533- .WillOnce(mt::WakeUp(&expectations_satisfied));
534- };
535- server_configuration.synthesize_cursor_motion =
536- [](ServerConfiguration *server)
537- {
538- server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
539- server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
540- };
541- start_server();
542-
543- client_config_1.set_cursor =
544- [this](MirSurface *surface)
545- {
546- auto conf = mir_cursor_configuration_from_name(client_cursor_1.c_str());
547- mir_wait_for(mir_surface_configure_cursor(surface, conf));
548- mir_cursor_configuration_destroy(conf);
549- };
550- start_client(client_config_1);
551-
552- client_config_2.set_cursor =
553- [this](MirSurface *surface)
554- {
555- auto conf = mir_cursor_configuration_from_name(client_cursor_2.c_str());
556- mir_wait_for(mir_surface_configure_cursor(surface, conf));
557- mir_cursor_configuration_destroy(conf);
558- };
559- start_client(client_config_2);
560+ test_server_config().client_geometries[client_name_1] =
561+ geom::Rectangle{{1, 0}, {1, 1}};
562+ test_server_config().client_geometries[client_name_2] =
563+ geom::Rectangle{{2, 0}, {1, 1}};
564+
565+ NamedCursorClient client_1{new_connection(), client_name_1, client_cursor_1};
566+ NamedCursorClient client_2{new_connection(), client_name_2, client_cursor_2};
567+ client_1.run();
568+ client_2.run();
569+
570+ InSequence seq;
571+ EXPECT_CALL(test_server_config().cursor, show(CursorNamed(client_cursor_1)));
572+ EXPECT_CALL(test_server_config().cursor, show(CursorNamed(client_cursor_2)))
573+ .WillOnce(mt::WakeUp(&expectations_satisfied));
574+
575+ fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
576+ fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
577+
578+ expectations_satisfied.wait_for_at_most_seconds(5);
579+
580+ client_shutdown_expectations();
581 }
582
583 TEST_F(TestClientCursorAPI, cursor_request_taken_from_top_surface)
584 {
585 using namespace ::testing;
586
587- server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
588- geom::Size{geom::Width{1}, geom::Height{1}}};
589- server_configuration.client_geometries[client_name_2] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
590- geom::Size{geom::Width{1}, geom::Height{1}}};
591- server_configuration.client_depths[client_name_1] = ms::DepthId{0};
592- server_configuration.client_depths[client_name_2] = ms::DepthId{1};
593-
594- set_client_count(2);
595-
596- server_configuration.expect_cursor_states =
597- [this](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
598- {
599- InSequence seq;
600- EXPECT_CALL(cursor, show(CursorNamed(client_cursor_2))).Times(1)
601- .WillOnce(mt::WakeUp(&expectations_satisfied));
602- };
603- server_configuration.synthesize_cursor_motion =
604- [](ServerConfiguration *server)
605- {
606- server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
607- };
608- start_server();
609-
610-
611- client_config_1.set_cursor =
612- [this](MirSurface *surface)
613- {
614- auto conf = mir_cursor_configuration_from_name(client_cursor_1.c_str());
615- mir_wait_for(mir_surface_configure_cursor(surface, conf));
616- mir_cursor_configuration_destroy(conf);
617- };
618- client_config_2.set_cursor =
619- [this](MirSurface *surface)
620- {
621- auto conf = mir_cursor_configuration_from_name(client_cursor_2.c_str());
622- mir_wait_for(mir_surface_configure_cursor(surface, conf));
623- mir_cursor_configuration_destroy(conf);
624- };
625- start_client(client_config_1);
626- start_client(client_config_2);
627-}
628-
629-namespace
630-{
631-
632-// In the following test the cursor changes are not responsive
633-// to cursor motion so we need a different synchronization model.
634-struct WaitsToChangeCursorClient : ClientConfig
635-{
636- WaitsToChangeCursorClient(std::string const& client_name,
637- mt::Barrier& cursor_ready_fence,
638- mt::Barrier& client_may_exit_fence)
639- : ClientConfig(client_name, cursor_ready_fence, client_may_exit_fence)
640- {
641- }
642-
643- void thread_exec() override
644- {
645- auto connection = mir_connect_sync(connect_string.c_str(),
646- client_name.c_str());
647-
648- ASSERT_TRUE(connection != NULL);
649- MirSurfaceParameters const request_params =
650- {
651- client_name.c_str(),
652- // For this fixture, we force geometry on server side
653- 0, 0,
654- mir_pixel_format_abgr_8888,
655- mir_buffer_usage_hardware,
656- mir_display_output_id_invalid
657- };
658- auto surface = mir_connection_create_surface_sync(connection, &request_params);
659-
660- set_cursor_complete.ready();
661- set_cursor(surface);
662-
663- client_may_exit.ready();
664-
665- mir_surface_release_sync(surface);
666- mir_connection_release(connection);
667- }
668-};
669-
670-struct TestClientCursorAPINoMotion : TestClientCursorAPI
671-{
672- mt::Barrier client_may_change_cursor{2};
673- WaitsToChangeCursorClient waiting_client{client_name_1, cursor_configured_fence, client_may_exit_fence};
674-
675- void TearDown() override
676- {
677- waiting_client.tear_down();
678- TestClientCursorAPI::TearDown();
679- }
680-};
681-
682-}
683-
684-TEST_F(TestClientCursorAPINoMotion, cursor_request_applied_without_cursor_motion)
685+ test_server_config().client_geometries[client_name_1] =
686+ geom::Rectangle{{1, 0}, {1, 1}};
687+ test_server_config().client_geometries[client_name_2] =
688+ geom::Rectangle{{1, 0}, {1, 1}};
689+
690+ NamedCursorClient client_1{new_connection(), client_name_1, client_cursor_1};
691+ NamedCursorClient client_2{new_connection(), client_name_2, client_cursor_2};
692+ client_1.run();
693+ client_2.run();
694+
695+ EXPECT_CALL(test_server_config().cursor, show(CursorNamed(client_cursor_2)))
696+ .WillOnce(mt::WakeUp(&expectations_satisfied));
697+
698+ fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
699+
700+ expectations_satisfied.wait_for_at_most_seconds(5);
701+
702+ client_shutdown_expectations();
703+}
704+
705+TEST_F(TestClientCursorAPI, cursor_request_applied_without_cursor_motion)
706 {
707 using namespace ::testing;
708
709- server_configuration.client_geometries[client_name_1] =
710- geom::Rectangle{geom::Point{geom::X{0}, geom::Y{0}},
711- geom::Size{geom::Width{1}, geom::Height{1}}};
712-
713- server_configuration.expect_cursor_states =
714- [this](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
715- {
716- InSequence seq;
717- EXPECT_CALL(cursor, show(CursorNamed(client_cursor_1))).Times(1);
718- EXPECT_CALL(cursor, hide()).Times(1)
719- .WillOnce(mt::WakeUp(&expectations_satisfied));
720- };
721- server_configuration.synthesize_cursor_motion =
722- [this](ServerConfiguration * /* server */)
723- {
724- client_may_change_cursor.ready();
725- };
726- start_server();
727+ struct ChangingCursorClient : NamedCursorClient
728+ {
729+ using NamedCursorClient::NamedCursorClient;
730
731- waiting_client.set_cursor =
732- [this](MirSurface *surface)
733+ void setup_cursor(MirSurface* surface) override
734 {
735- client_may_change_cursor.ready();
736- auto conf1 = mir_cursor_configuration_from_name(client_cursor_1.c_str());
737+ auto conf1 = mir_cursor_configuration_from_name(cursor_name.c_str());
738 auto conf2 = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
739
740 mir_wait_for(mir_surface_configure_cursor(surface, conf1));
741@@ -516,7 +384,22 @@
742
743 mir_cursor_configuration_destroy(conf1);
744 mir_cursor_configuration_destroy(conf2);
745- };
746- start_client(waiting_client);
747+ }
748+ };
749+
750+ test_server_config().client_geometries[client_name_1] =
751+ geom::Rectangle{{0, 0}, {1, 1}};
752+
753+ ChangingCursorClient client{new_connection(), client_name_1, client_cursor_1};
754+
755+ InSequence seq;
756+ EXPECT_CALL(test_server_config().cursor, show(CursorNamed(client_cursor_1)));
757+ EXPECT_CALL(test_server_config().cursor, hide())
758+ .WillOnce(mt::WakeUp(&expectations_satisfied));
759+
760+ client.run();
761+
762+ expectations_satisfied.wait_for_at_most_seconds(5);
763+
764+ client_shutdown_expectations();
765 }
766-

Subscribers

People subscribed via source and target branches