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
=== modified file 'tests/acceptance-tests/test_client_cursor_api.cpp'
--- tests/acceptance-tests/test_client_cursor_api.cpp 2014-09-01 04:53:25 +0000
+++ tests/acceptance-tests/test_client_cursor_api.cpp 2014-09-04 08:51:10 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2013 Canonical Ltd.2 * Copyright © 2013-2014 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as5 * it under the terms of the GNU General Public License version 3 as
@@ -14,40 +14,34 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *15 *
16 * Authored by: Robert Carr <robert.carr@canonical.com>16 * Authored by: Robert Carr <robert.carr@canonical.com>
17 * Alexandros Frantzis <alexandros.frantzis@canonical.com>
17 */18 */
1819
19#include "mir/graphics/cursor.h"20#include "mir/graphics/cursor.h"
20#include "mir/graphics/cursor_image.h"21#include "mir/graphics/cursor_image.h"
21#include "mir/input/cursor_images.h"22#include "mir/input/cursor_images.h"
22#include "mir/scene/surface.h"23
23#include "mir/scene/surface_factory.h"24#include "mir_test_framework/in_process_server.h"
24#include "mir/scene/null_observer.h"25#include "mir_test_framework/fake_event_hub_server_configuration.h"
25#include "mir/scene/null_surface_observer.h"26#include "mir_test_framework/declarative_placement_strategy.h"
2627#include "mir_test_framework/using_stub_client_platform.h"
27#include "mir_toolkit/mir_client_library.h"28
28
29#include "mir_test/barrier.h"
30#include "mir_test/fake_event_hub.h"
31#include "mir_test/fake_shared.h"29#include "mir_test/fake_shared.h"
30#include "mir_test/spin_wait.h"
32#include "mir_test/wait_condition.h"31#include "mir_test/wait_condition.h"
33#include "mir_test_framework/input_testing_server_configuration.h"32#include "mir_test/fake_event_hub.h"
34#include "mir_test_framework/testing_client_configuration.h"33
35#include "mir_test_framework/declarative_placement_strategy.h"34#include "mir_toolkit/mir_client_library.h"
36#include "mir_test_framework/deferred_in_process_server.h"
37#include "mir_test_framework/using_stub_client_platform.h"
3835
39#include <gtest/gtest.h>36#include <gtest/gtest.h>
40#include <gmock/gmock.h>37#include <gmock/gmock.h>
4138
39namespace mt = mir::test;
40namespace mtf = mir_test_framework;
41namespace geom = mir::geometry;
42namespace mg = mir::graphics;42namespace mg = mir::graphics;
43namespace ms = mir::scene;
44namespace msh = mir::shell;
45namespace mi = mir::input;43namespace mi = mir::input;
46namespace mis = mir::input::synthesis;44namespace mis = mir::input::synthesis;
47namespace geom = mir::geometry;
48
49namespace mt = mir::test;
50namespace mtf = mir_test_framework;
5145
52namespace46namespace
53{47{
@@ -59,9 +53,7 @@
5953
60 // We are not interested in mocking the motion in these tests as we54 // We are not interested in mocking the motion in these tests as we
61 // generate it ourself.55 // generate it ourself.
62 void move_to(geom::Point)56 void move_to(geom::Point) override {}
63 {
64 }
65};57};
6658
67struct NamedCursorImage : public mg::CursorImage59struct NamedCursorImage : public mg::CursorImage
@@ -71,16 +63,17 @@
71 {63 {
72 }64 }
7365
74 void const* as_argb_8888() const { return nullptr; }66 void const* as_argb_8888() const override { return nullptr; }
75 geom::Size size() const { return geom::Size{}; }67 geom::Size size() const override { return geom::Size{}; }
76 geom::Displacement hotspot() const { return geom::Displacement{0, 0}; }68 geom::Displacement hotspot() const override { return geom::Displacement{0, 0}; }
7769
78 std::string const cursor_name;70 std::string const cursor_name;
79};71};
8072
81struct NamedCursorImages : public mi::CursorImages73struct NamedCursorImages : public mi::CursorImages
82{74{
83 std::shared_ptr<mg::CursorImage> image(std::string const& name, geom::Size const& /* size */)75 std::shared_ptr<mg::CursorImage>
76 image(std::string const& name, geom::Size const& /* size */) override
84 {77 {
85 return std::make_shared<NamedCursorImage>(name);78 return std::make_shared<NamedCursorImage>(name);
86 }79 }
@@ -90,7 +83,7 @@
90{83{
91 auto image = dynamic_cast<NamedCursorImage const*>(&i);84 auto image = dynamic_cast<NamedCursorImage const*>(&i);
92 assert(image);85 assert(image);
93 86
94 return image->cursor_name == name;87 return image->cursor_name == name;
95}88}
9689
@@ -104,84 +97,122 @@
104 return cursor_is_named(arg, name);97 return cursor_is_named(arg, name);
105}98}
10699
107struct ClientConfig : mtf::TestingClientConfiguration100struct CursorClient
108{101{
109 std::string connect_string;102 CursorClient(std::string const& connect_string, std::string const& client_name)
110103 : connect_string{connect_string}, client_name{client_name}
104 {
105 }
106
107 virtual ~CursorClient()
108 {
109 teardown.wake_up_everyone();
110 if (client_thread.joinable())
111 client_thread.join();
112 }
113
114 void run()
115 {
116 mir::test::WaitCondition setup_done;
117
118 client_thread = std::thread{
119 [this,&setup_done]
120 {
121 auto const connection =
122 mir_connect_sync(connect_string.c_str(), client_name.c_str());
123
124 MirSurfaceParameters const request_params =
125 {
126 client_name.c_str(),
127 // For this fixture, we force geometry on server side
128 0, 0,
129 mir_pixel_format_abgr_8888,
130 mir_buffer_usage_hardware,
131 mir_display_output_id_invalid
132 };
133 auto const surface =
134 mir_connection_create_surface_sync(connection, &request_params);
135
136 mir_surface_swap_buffers_sync(surface);
137
138 wait_for_surface_to_become_focused_and_exposed(surface);
139
140 setup_cursor(surface);
141
142 setup_done.wake_up_everyone();
143
144 teardown.wait_for_at_most_seconds(10);
145 mir_surface_release_sync(surface);
146 mir_connection_release(connection);
147 }};
148
149 setup_done.wait_for_at_most_seconds(5);
150 }
151
152 virtual void setup_cursor(MirSurface*)
153 {
154 }
155
156 void wait_for_surface_to_become_focused_and_exposed(MirSurface* surface)
157 {
158 bool success = mt::spin_wait_for_condition_or_timeout(
159 [surface]
160 {
161 return mir_surface_get_visibility(surface) == mir_surface_visibility_exposed &&
162 mir_surface_get_focus(surface) == mir_surface_focused;
163 },
164 std::chrono::seconds{5});
165
166 if (!success)
167 throw std::runtime_error("Timeout waiting for surface to become focused and exposed");
168 }
169
170 std::string const connect_string;
111 std::string const client_name;171 std::string const client_name;
112172
113 mt::Barrier& set_cursor_complete;173 std::thread client_thread;
114 mt::Barrier& client_may_exit;174 mir::test::WaitCondition teardown;
115 175};
116 std::function<void(MirSurface*)> set_cursor;176
117177struct DisabledCursorClient : CursorClient
118 ClientConfig(std::string const& client_name,178{
119 mt::Barrier& cursor_ready_fence,179 using CursorClient::CursorClient;
120 mt::Barrier& client_may_exit_fence)180
121 : client_name(client_name),181 void setup_cursor(MirSurface* surface) override
122 set_cursor_complete(cursor_ready_fence),182 {
123 client_may_exit(client_may_exit_fence)183 auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
124 {184 mir_wait_for(mir_surface_configure_cursor(surface, conf));
125 }185 mir_cursor_configuration_destroy(conf);
126 186 }
127 virtual void thread_exec()187};
128 {188
129 auto connection = mir_connect_sync(connect_string.c_str(),189struct NamedCursorClient : CursorClient
130 client_name.c_str());190{
131 191 NamedCursorClient(
132 ASSERT_TRUE(connection != NULL);192 std::string const& connect_string,
133 MirSurfaceParameters const request_params =193 std::string const& client_name,
134 {194 std::string const& cursor_name)
135 client_name.c_str(),195 : CursorClient{connect_string, client_name},
136 // For this fixture, we force geometry on server side196 cursor_name{cursor_name}
137 0, 0,197 {
138 mir_pixel_format_abgr_8888,198 }
139 mir_buffer_usage_hardware,199
140 mir_display_output_id_invalid200 void setup_cursor(MirSurface* surface) override
141 };201 {
142 auto surface = mir_connection_create_surface_sync(connection, &request_params);202 auto conf = mir_cursor_configuration_from_name(cursor_name.c_str());
143 203 mir_wait_for(mir_surface_configure_cursor(surface, conf));
144 set_cursor(surface);204 mir_cursor_configuration_destroy(conf);
145 set_cursor_complete.ready();205 }
146 206
147 client_may_exit.ready();207 std::string const cursor_name;
148 208};
149 mir_surface_release_sync(surface);209
150 mir_connection_release(connection);210struct TestServerConfiguration : mtf::FakeEventHubServerConfiguration
151 }211{
152 void tear_down() { if (thread.joinable()) thread.join(); }212 std::shared_ptr<mir::scene::PlacementStrategy> the_placement_strategy() override
153
154 void exec() override
155 {
156 thread = std::thread([this]{ thread_exec(); });
157 }
158private:
159 std::thread thread;
160};
161
162struct ServerConfiguration : mtf::InputTestingServerConfiguration
163{
164 mt::Barrier& cursor_configured_fence;
165 mt::Barrier& client_may_exit_fence;
166
167 std::function<void(MockCursor&, mt::WaitCondition&)> expect_cursor_states;
168 std::function<void(ServerConfiguration*)> synthesize_cursor_motion;
169
170 mtf::SurfaceGeometries client_geometries;
171 mtf::SurfaceDepths client_depths;
172
173 MockCursor cursor;
174
175 ServerConfiguration(mt::Barrier& cursor_configured_fence, mt::Barrier& client_may_exit_fence)
176 : cursor_configured_fence(cursor_configured_fence),
177 client_may_exit_fence(client_may_exit_fence)
178 {
179 }
180
181 std::shared_ptr<ms::PlacementStrategy> the_placement_strategy() override
182 {213 {
183 return std::make_shared<mtf::DeclarativePlacementStrategy>(214 return std::make_shared<mtf::DeclarativePlacementStrategy>(
184 InputTestingServerConfiguration::the_placement_strategy(),215 FakeEventHubServerConfiguration::the_placement_strategy(),
185 client_geometries, client_depths);216 client_geometries, client_depths);
186 }217 }
187218
@@ -189,83 +220,51 @@
189 {220 {
190 return mt::fake_shared(cursor);221 return mt::fake_shared(cursor);
191 }222 }
192 223
193 std::shared_ptr<mi::CursorImages> the_cursor_images() override224 std::shared_ptr<mi::CursorImages> the_cursor_images() override
194 {225 {
195 return std::make_shared<NamedCursorImages>();226 return std::make_shared<NamedCursorImages>();
196 }227 }
197 228
198 void inject_input()229 MockCursor cursor;
199 {230 mtf::SurfaceGeometries client_geometries;
200 using namespace ::testing;231 mtf::SurfaceDepths client_depths;
201 cursor_configured_fence.ready();232};
202 233
203 mt::WaitCondition expectations_satisfied;234struct TestClientCursorAPI : mtf::InProcessServer
204235{
205 // Clear any states applied during server initialization.236 mir::DefaultServerConfiguration& server_config() override
206 Mock::VerifyAndClearExpectations(&cursor);237 {
207 expect_cursor_states(cursor, expectations_satisfied);238 return server_configuration_;
208 239 }
209 // We are only interested in the cursor image changes, not240
210 // the synthetic motion.241 TestServerConfiguration& test_server_config()
211242 {
212 synthesize_cursor_motion(this);243 return server_configuration_;
213 expectations_satisfied.wait_for_at_most_seconds(60);244 }
214 245
215 Mock::VerifyAndClearExpectations(&cursor);246 mir::input::android::FakeEventHub* fake_event_hub()
247 {
248 return server_configuration_.fake_event_hub;
249 }
250
251 void client_shutdown_expectations()
252 {
253 using namespace testing;
254 Mock::VerifyAndClearExpectations(&test_server_config().cursor);
216255
217 // Client shutdown256 // Client shutdown
218 EXPECT_CALL(cursor, show(_)).Times(AnyNumber());257 EXPECT_CALL(test_server_config().cursor, show(_)).Times(AnyNumber());
219 EXPECT_CALL(cursor, hide()).Times(AnyNumber());258 EXPECT_CALL(test_server_config().cursor, hide()).Times(AnyNumber());
220 client_may_exit_fence.ready();
221 }259 }
222
223};
224260
225struct TestClientCursorAPI : mtf::DeferredInProcessServer261 std::string const client_name_1{"client-1"};
226{262 std::string const client_name_2{"client-2"};
227 std::string const client_name_1 = "1";263 std::string const client_cursor_1{"cursor-1"};
228 std::string const client_name_2 = "2";264 std::string const client_cursor_2{"cursor-2"};
229 std::string const client_cursor_1 = "cursor-1";265 TestServerConfiguration server_configuration_;
230 std::string const client_cursor_2 = "cursor-2";266 mir::test::WaitCondition expectations_satisfied;
231
232 // Reset to higher values for more clients.
233 mt::Barrier cursor_configured_fence{2};
234 mt::Barrier client_may_exit_fence{2};
235
236 ServerConfiguration server_configuration{cursor_configured_fence, client_may_exit_fence};
237 mir::DefaultServerConfiguration& server_config() override { return server_configuration; }
238 mtf::UsingStubClientPlatform using_stub_client_platform;267 mtf::UsingStubClientPlatform using_stub_client_platform;
239
240 ClientConfig client_config_1{client_name_1, cursor_configured_fence, client_may_exit_fence};
241 ClientConfig client_config_2{client_name_2, cursor_configured_fence, client_may_exit_fence};
242
243 // Default number allows one client.
244 void set_client_count(unsigned count)
245 {
246 cursor_configured_fence.reset(count + 1);
247 client_may_exit_fence.reset(count + 1);
248 }
249
250 void start_server()
251 {
252 DeferredInProcessServer::start_server();
253 server_configuration.exec();
254 }
255
256 void start_client(ClientConfig& config)
257 {
258 config.connect_string = new_connection();
259 config.exec();
260 }
261
262 void TearDown()
263 {
264 client_config_1.tear_down();
265 client_config_2.tear_down();
266 server_configuration.on_exit();
267 DeferredInProcessServer::TearDown();
268 }
269};268};
270269
271}270}
@@ -277,238 +276,107 @@
277{276{
278 using namespace ::testing;277 using namespace ::testing;
279278
280 server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},279 test_server_config().client_geometries[client_name_1] =
281 geom::Size{geom::Width{1}, geom::Height{1}}};280 geom::Rectangle{{1, 0}, {1, 1}};
282 281
283282 DisabledCursorClient client{new_connection(), client_name_1};
284 server_configuration.expect_cursor_states = [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)283 client.run();
285 {284
286 EXPECT_CALL(cursor, hide()).Times(1)285 EXPECT_CALL(test_server_config().cursor, hide())
287 .WillOnce(mt::WakeUp(&expectations_satisfied));286 .WillOnce(mt::WakeUp(&expectations_satisfied));
288 };287
289 server_configuration.synthesize_cursor_motion = [](ServerConfiguration *server)288 fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
290 {289
291 server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));290 expectations_satisfied.wait_for_at_most_seconds(5);
292 };291
293 start_server();292 client_shutdown_expectations();
294
295 client_config_1.set_cursor = [](MirSurface *surface)
296 {
297 auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
298 mir_wait_for(mir_surface_configure_cursor(surface, conf));
299 mir_cursor_configuration_destroy(conf);
300 };
301 start_client(client_config_1);
302}293}
303294
304TEST_F(TestClientCursorAPI, cursor_restored_when_leaving_surface)295TEST_F(TestClientCursorAPI, cursor_restored_when_leaving_surface)
305{296{
306 using namespace ::testing;297 using namespace ::testing;
307298
308 server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},299 test_server_config().client_geometries[client_name_1] =
309 geom::Size{geom::Width{1}, geom::Height{1}}};300 geom::Rectangle{{1, 0}, {1, 1}};
310 301
311 mtf::CrossProcessSync client_ready_fence, client_may_exit_fence;302 DisabledCursorClient client{new_connection(), client_name_1};
312303 client.run();
313 server_configuration.expect_cursor_states = [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)304
314 {305 InSequence seq;
315 InSequence seq;306 EXPECT_CALL(test_server_config().cursor, hide());
316 EXPECT_CALL(cursor, hide()).Times(1);307 EXPECT_CALL(test_server_config().cursor, show(DefaultCursorImage()))
317 EXPECT_CALL(cursor, show(DefaultCursorImage())).Times(1)308 .WillOnce(mt::WakeUp(&expectations_satisfied));
318 .WillOnce(mt::WakeUp(&expectations_satisfied));309
319 };310 fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
320 server_configuration.synthesize_cursor_motion = [](ServerConfiguration *server)311 fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(2, 0));
321 {312
322 server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));313 expectations_satisfied.wait_for_at_most_seconds(5);
323 server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,0));314
324 };315 client_shutdown_expectations();
325 start_server();
326
327
328 client_config_1.set_cursor = [](MirSurface *surface)
329 {
330 // Disable cursor
331 auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
332 mir_wait_for(mir_surface_configure_cursor(surface, conf));
333 mir_cursor_configuration_destroy(conf);
334 };
335 start_client(client_config_1);
336}316}
337317
338TEST_F(TestClientCursorAPI, cursor_changed_when_crossing_surface_boundaries)318TEST_F(TestClientCursorAPI, cursor_changed_when_crossing_surface_boundaries)
339{319{
340 using namespace ::testing;320 using namespace ::testing;
341321
342 server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},322 test_server_config().client_geometries[client_name_1] =
343 geom::Size{geom::Width{1}, geom::Height{1}}};323 geom::Rectangle{{1, 0}, {1, 1}};
344 server_configuration.client_geometries[client_name_2] = geom::Rectangle{geom::Point{geom::X{2}, geom::Y{0}},324 test_server_config().client_geometries[client_name_2] =
345 geom::Size{geom::Width{1}, geom::Height{1}}};325 geom::Rectangle{{2, 0}, {1, 1}};
346 set_client_count(2);326
347 327 NamedCursorClient client_1{new_connection(), client_name_1, client_cursor_1};
348 server_configuration.expect_cursor_states =328 NamedCursorClient client_2{new_connection(), client_name_2, client_cursor_2};
349 [this](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)329 client_1.run();
350 {330 client_2.run();
351 InSequence seq;331
352 EXPECT_CALL(cursor, show(CursorNamed(client_cursor_1))).Times(1);332 InSequence seq;
353 EXPECT_CALL(cursor, show(CursorNamed(client_cursor_2))).Times(1)333 EXPECT_CALL(test_server_config().cursor, show(CursorNamed(client_cursor_1)));
354 .WillOnce(mt::WakeUp(&expectations_satisfied));334 EXPECT_CALL(test_server_config().cursor, show(CursorNamed(client_cursor_2)))
355 };335 .WillOnce(mt::WakeUp(&expectations_satisfied));
356 server_configuration.synthesize_cursor_motion = 336
357 [](ServerConfiguration *server)337 fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
358 {338 fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
359 server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));339
360 server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));340 expectations_satisfied.wait_for_at_most_seconds(5);
361 };341
362 start_server();342 client_shutdown_expectations();
363
364 client_config_1.set_cursor =
365 [this](MirSurface *surface)
366 {
367 auto conf = mir_cursor_configuration_from_name(client_cursor_1.c_str());
368 mir_wait_for(mir_surface_configure_cursor(surface, conf));
369 mir_cursor_configuration_destroy(conf);
370 };
371 start_client(client_config_1);
372
373 client_config_2.set_cursor =
374 [this](MirSurface *surface)
375 {
376 auto conf = mir_cursor_configuration_from_name(client_cursor_2.c_str());
377 mir_wait_for(mir_surface_configure_cursor(surface, conf));
378 mir_cursor_configuration_destroy(conf);
379 };
380 start_client(client_config_2);
381}343}
382344
383TEST_F(TestClientCursorAPI, cursor_request_taken_from_top_surface)345TEST_F(TestClientCursorAPI, cursor_request_taken_from_top_surface)
384{346{
385 using namespace ::testing;347 using namespace ::testing;
386348
387 server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},349 test_server_config().client_geometries[client_name_1] =
388 geom::Size{geom::Width{1}, geom::Height{1}}};350 geom::Rectangle{{1, 0}, {1, 1}};
389 server_configuration.client_geometries[client_name_2] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},351 test_server_config().client_geometries[client_name_2] =
390 geom::Size{geom::Width{1}, geom::Height{1}}};352 geom::Rectangle{{1, 0}, {1, 1}};
391 server_configuration.client_depths[client_name_1] = ms::DepthId{0};353
392 server_configuration.client_depths[client_name_2] = ms::DepthId{1};354 NamedCursorClient client_1{new_connection(), client_name_1, client_cursor_1};
393 355 NamedCursorClient client_2{new_connection(), client_name_2, client_cursor_2};
394 set_client_count(2);356 client_1.run();
395357 client_2.run();
396 server_configuration.expect_cursor_states = 358
397 [this](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)359 EXPECT_CALL(test_server_config().cursor, show(CursorNamed(client_cursor_2)))
398 {360 .WillOnce(mt::WakeUp(&expectations_satisfied));
399 InSequence seq;361
400 EXPECT_CALL(cursor, show(CursorNamed(client_cursor_2))).Times(1)362 fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 0));
401 .WillOnce(mt::WakeUp(&expectations_satisfied));363
402 };364 expectations_satisfied.wait_for_at_most_seconds(5);
403 server_configuration.synthesize_cursor_motion = 365
404 [](ServerConfiguration *server)366 client_shutdown_expectations();
405 {367}
406 server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));368
407 };369TEST_F(TestClientCursorAPI, cursor_request_applied_without_cursor_motion)
408 start_server();
409
410
411 client_config_1.set_cursor =
412 [this](MirSurface *surface)
413 {
414 auto conf = mir_cursor_configuration_from_name(client_cursor_1.c_str());
415 mir_wait_for(mir_surface_configure_cursor(surface, conf));
416 mir_cursor_configuration_destroy(conf);
417 };
418 client_config_2.set_cursor =
419 [this](MirSurface *surface)
420 {
421 auto conf = mir_cursor_configuration_from_name(client_cursor_2.c_str());
422 mir_wait_for(mir_surface_configure_cursor(surface, conf));
423 mir_cursor_configuration_destroy(conf);
424 };
425 start_client(client_config_1);
426 start_client(client_config_2);
427}
428
429namespace
430{
431
432// In the following test the cursor changes are not responsive
433// to cursor motion so we need a different synchronization model.
434struct WaitsToChangeCursorClient : ClientConfig
435{
436 WaitsToChangeCursorClient(std::string const& client_name,
437 mt::Barrier& cursor_ready_fence,
438 mt::Barrier& client_may_exit_fence)
439 : ClientConfig(client_name, cursor_ready_fence, client_may_exit_fence)
440 {
441 }
442
443 void thread_exec() override
444 {
445 auto connection = mir_connect_sync(connect_string.c_str(),
446 client_name.c_str());
447
448 ASSERT_TRUE(connection != NULL);
449 MirSurfaceParameters const request_params =
450 {
451 client_name.c_str(),
452 // For this fixture, we force geometry on server side
453 0, 0,
454 mir_pixel_format_abgr_8888,
455 mir_buffer_usage_hardware,
456 mir_display_output_id_invalid
457 };
458 auto surface = mir_connection_create_surface_sync(connection, &request_params);
459
460 set_cursor_complete.ready();
461 set_cursor(surface);
462
463 client_may_exit.ready();
464
465 mir_surface_release_sync(surface);
466 mir_connection_release(connection);
467 }
468};
469
470struct TestClientCursorAPINoMotion : TestClientCursorAPI
471{
472 mt::Barrier client_may_change_cursor{2};
473 WaitsToChangeCursorClient waiting_client{client_name_1, cursor_configured_fence, client_may_exit_fence};
474
475 void TearDown() override
476 {
477 waiting_client.tear_down();
478 TestClientCursorAPI::TearDown();
479 }
480};
481
482}
483
484TEST_F(TestClientCursorAPINoMotion, cursor_request_applied_without_cursor_motion)
485{370{
486 using namespace ::testing;371 using namespace ::testing;
487372
488 server_configuration.client_geometries[client_name_1] = 373 struct ChangingCursorClient : NamedCursorClient
489 geom::Rectangle{geom::Point{geom::X{0}, geom::Y{0}},374 {
490 geom::Size{geom::Width{1}, geom::Height{1}}};375 using NamedCursorClient::NamedCursorClient;
491
492 server_configuration.expect_cursor_states =
493 [this](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
494 {
495 InSequence seq;
496 EXPECT_CALL(cursor, show(CursorNamed(client_cursor_1))).Times(1);
497 EXPECT_CALL(cursor, hide()).Times(1)
498 .WillOnce(mt::WakeUp(&expectations_satisfied));
499 };
500 server_configuration.synthesize_cursor_motion =
501 [this](ServerConfiguration * /* server */)
502 {
503 client_may_change_cursor.ready();
504 };
505 start_server();
506376
507 waiting_client.set_cursor = 377 void setup_cursor(MirSurface* surface) override
508 [this](MirSurface *surface)
509 {378 {
510 client_may_change_cursor.ready();379 auto conf1 = mir_cursor_configuration_from_name(cursor_name.c_str());
511 auto conf1 = mir_cursor_configuration_from_name(client_cursor_1.c_str());
512 auto conf2 = mir_cursor_configuration_from_name(mir_disabled_cursor_name);380 auto conf2 = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
513381
514 mir_wait_for(mir_surface_configure_cursor(surface, conf1));382 mir_wait_for(mir_surface_configure_cursor(surface, conf1));
@@ -516,7 +384,22 @@
516384
517 mir_cursor_configuration_destroy(conf1);385 mir_cursor_configuration_destroy(conf1);
518 mir_cursor_configuration_destroy(conf2);386 mir_cursor_configuration_destroy(conf2);
519 };387 }
520 start_client(waiting_client);388 };
389
390 test_server_config().client_geometries[client_name_1] =
391 geom::Rectangle{{0, 0}, {1, 1}};
392
393 ChangingCursorClient client{new_connection(), client_name_1, client_cursor_1};
394
395 InSequence seq;
396 EXPECT_CALL(test_server_config().cursor, show(CursorNamed(client_cursor_1)));
397 EXPECT_CALL(test_server_config().cursor, hide())
398 .WillOnce(mt::WakeUp(&expectations_satisfied));
399
400 client.run();
401
402 expectations_satisfied.wait_for_at_most_seconds(5);
403
404 client_shutdown_expectations();
521}405}
522

Subscribers

People subscribed via source and target branches