Merge lp:~alan-griffiths/mir/move-miral-test-to-mir into lp:mir
- move-miral-test-to-mir
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Gerry Boland |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4236 |
Proposed branch: | lp:~alan-griffiths/mir/move-miral-test-to-mir |
Merge into: | lp:mir |
Prerequisite: | lp:~alan-griffiths/mir/move-miral-to-mir |
Diff against target: |
4933 lines (+4803/-1) 23 files modified
include/common/mir/input/mir_input_config.h (+3/-1) tests/CMakeLists.txt (+2/-0) tests/miral/CMakeLists.txt (+57/-0) tests/miral/active_outputs.cpp (+205/-0) tests/miral/active_window.cpp (+412/-0) tests/miral/client_mediated_gestures.cpp (+302/-0) tests/miral/display_reconfiguration.cpp (+87/-0) tests/miral/drag_active_window.cpp (+153/-0) tests/miral/drag_and_drop.cpp (+656/-0) tests/miral/modify_window_state.cpp (+105/-0) tests/miral/mru_window_list.cpp (+193/-0) tests/miral/raise_tree.cpp (+85/-0) tests/miral/runner.cpp (+49/-0) tests/miral/select_active_window.cpp (+121/-0) tests/miral/test_server.cpp (+198/-0) tests/miral/test_server.h (+90/-0) tests/miral/test_window_manager_tools.h (+197/-0) tests/miral/window_id.cpp (+114/-0) tests/miral/window_placement.cpp (+554/-0) tests/miral/window_placement_anchors_to_parent.cpp (+208/-0) tests/miral/window_placement_client_api.cpp (+141/-0) tests/miral/window_properties.cpp (+164/-0) tests/miral/workspaces.cpp (+707/-0) |
To merge this branch: | bzr merge lp:~alan-griffiths/mir/move-miral-test-to-mir |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gerry Boland (community) | Approve | ||
Brandon Schaefer (community) | Approve | ||
Mir CI Bot | continuous-integration | Approve | |
Review via email: mp+329464@code.launchpad.net |
Commit message
Incorporate miral project into mir source tree - part 2 (miral-test)
Description of the change
Incorporate miral project into mir source tree
This is a second-cut:
1. The utility script for generating the libmiral symbols file hasn't been ported
2. There's no attempt to remove code obsoleted by MirAL
3. There's no reworking of the generated docs to include miral
Mir CI Bot (mir-ci-bot) wrote : | # |
Alan Griffiths (alan-griffiths) wrote : | # |
18:05:38 15/33 Test #15: miral-test .......
18:05:38 /<<BUILDDIR>
Well... at least that proves the test is there!
Alan Griffiths (alan-griffiths) wrote : | # |
>
> 18:05:38 15/33 Test #15: miral-test
> .......
> 18:05:38 /<<BUILDDIR>
> /miral-test.bin: error while loading shared libraries: libmiral.so.2: cannot
> open shared object file: No such file or directory
>
> Well... at least that proves the test is there!
Doh! "miral-test.bin" not "miral-test"
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4250
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4252
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4254
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Brandon Schaefer (brandontschaefer) wrote : | # |
Sounds reasonable, the other branch is ready to land. LGTM
Gerry Boland (gerboland) wrote : | # |
Builds ok, tests ok.
Preview Diff
1 | === modified file 'include/common/mir/input/mir_input_config.h' | |||
2 | --- include/common/mir/input/mir_input_config.h 2017-07-28 17:00:43 +0000 | |||
3 | +++ include/common/mir/input/mir_input_config.h 2017-08-24 15:19:58 +0000 | |||
4 | @@ -75,7 +75,9 @@ | |||
5 | 75 | std::unique_ptr<Implementation> impl; | 75 | std::unique_ptr<Implementation> impl; |
6 | 76 | }; | 76 | }; |
7 | 77 | 77 | ||
9 | 78 | class MirInputConfig | 78 | // We use "struct", not "class" for consistency with mirclient/mir_toolkit/client_types.h:395 |
10 | 79 | // (To be nice to downstreams that use clang with its pointless warnings about this.) | ||
11 | 80 | struct MirInputConfig | ||
12 | 79 | { | 81 | { |
13 | 80 | public: | 82 | public: |
14 | 81 | MirInputConfig(); | 83 | MirInputConfig(); |
15 | 82 | 84 | ||
16 | === modified file 'tests/CMakeLists.txt' | |||
17 | --- tests/CMakeLists.txt 2017-05-08 03:04:26 +0000 | |||
18 | +++ tests/CMakeLists.txt 2017-08-24 15:19:58 +0000 | |||
19 | @@ -70,6 +70,8 @@ | |||
20 | 70 | add_subdirectory(privileged-tests/) | 70 | add_subdirectory(privileged-tests/) |
21 | 71 | endif(MIR_BUILD_PRIVILEGED_TESTS) | 71 | endif(MIR_BUILD_PRIVILEGED_TESTS) |
22 | 72 | 72 | ||
23 | 73 | add_subdirectory(miral) | ||
24 | 74 | |||
25 | 73 | # Private test headers used by integration and unit tests | 75 | # Private test headers used by integration and unit tests |
26 | 74 | include_directories( | 76 | include_directories( |
27 | 75 | include | 77 | include |
28 | 76 | 78 | ||
29 | === added directory 'tests/miral' | |||
30 | === added file 'tests/miral/CMakeLists.txt' | |||
31 | --- tests/miral/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
32 | +++ tests/miral/CMakeLists.txt 2017-08-24 15:19:58 +0000 | |||
33 | @@ -0,0 +1,57 @@ | |||
34 | 1 | # We can't tell which version of gtest we're building against and INSTANTIATE_TEST_CASE_P changed in | ||
35 | 2 | # a way that relies on a gcc extension to support backward-compatible code, So... | ||
36 | 3 | check_cxx_compiler_flag(-Wno-gnu-zero-variadic-macro-arguments MIRAL_COMPILE_WITH_W_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS) | ||
37 | 4 | check_cxx_compiler_flag(-Wno-pedantic MIRAL_COMPILE_WITH_W_NO_PEDANTIC) | ||
38 | 5 | if ("${CMAKE_CXX_COMPILER}" MATCHES ".*clang.*" AND MIRAL_COMPILE_WITH_W_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS) | ||
39 | 6 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments") # clang | ||
40 | 7 | elseif(MIRAL_COMPILE_WITH_W_NO_PEDANTIC) | ||
41 | 8 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pedantic") #gcc | ||
42 | 9 | endif() | ||
43 | 10 | |||
44 | 11 | include_directories( | ||
45 | 12 | ${PROJECT_SOURCE_DIR}/src/miral | ||
46 | 13 | ${MIRTEST_INCLUDE_DIRS} | ||
47 | 14 | ${GMOCK_INCLUDE_DIR} | ||
48 | 15 | ${GTEST_INCLUDE_DIR} | ||
49 | 16 | ) | ||
50 | 17 | |||
51 | 18 | if(${CMAKE_COMPILER_IS_GNUCXX}) | ||
52 | 19 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto") | ||
53 | 20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") | ||
54 | 21 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -flto") | ||
55 | 22 | set(CMAKE_AR "gcc-ar") | ||
56 | 23 | set(CMAKE_NM "gcc-nm") | ||
57 | 24 | set(CMAKE_RANLIB "gcc-ranlib") | ||
58 | 25 | endif() | ||
59 | 26 | |||
60 | 27 | mir_add_wrapped_executable(miral-test NOINSTALL | ||
61 | 28 | mru_window_list.cpp | ||
62 | 29 | active_outputs.cpp | ||
63 | 30 | window_id.cpp | ||
64 | 31 | runner.cpp | ||
65 | 32 | select_active_window.cpp | ||
66 | 33 | window_placement.cpp | ||
67 | 34 | window_placement_anchors_to_parent.cpp | ||
68 | 35 | window_placement_client_api.cpp | ||
69 | 36 | window_properties.cpp | ||
70 | 37 | drag_active_window.cpp | ||
71 | 38 | modify_window_state.cpp | ||
72 | 39 | test_server.cpp test_server.h | ||
73 | 40 | test_window_manager_tools.h | ||
74 | 41 | display_reconfiguration.cpp | ||
75 | 42 | active_window.cpp | ||
76 | 43 | raise_tree.cpp | ||
77 | 44 | workspaces.cpp | ||
78 | 45 | drag_and_drop.cpp | ||
79 | 46 | client_mediated_gestures.cpp | ||
80 | 47 | ) | ||
81 | 48 | |||
82 | 49 | target_link_libraries(miral-test | ||
83 | 50 | ${GTEST_BOTH_LIBRARIES} | ||
84 | 51 | ${GMOCK_LIBRARIES} | ||
85 | 52 | miral | ||
86 | 53 | miral-internal | ||
87 | 54 | mir-test-assist | ||
88 | 55 | ) | ||
89 | 56 | |||
90 | 57 | add_test(miral-test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/miral-test) | ||
91 | 0 | 58 | ||
92 | === added file 'tests/miral/active_outputs.cpp' | |||
93 | --- tests/miral/active_outputs.cpp 1970-01-01 00:00:00 +0000 | |||
94 | +++ tests/miral/active_outputs.cpp 2017-08-24 15:19:58 +0000 | |||
95 | @@ -0,0 +1,205 @@ | |||
96 | 1 | /* | ||
97 | 2 | * Copyright © 2016 Canonical Ltd. | ||
98 | 3 | * | ||
99 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
100 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
101 | 6 | * published by the Free Software Foundation. | ||
102 | 7 | * | ||
103 | 8 | * This program is distributed in the hope that it will be useful, | ||
104 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
105 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
106 | 11 | * GNU General Public License for more details. | ||
107 | 12 | * | ||
108 | 13 | * You should have received a copy of the GNU General Public License | ||
109 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
110 | 15 | * | ||
111 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
112 | 17 | */ | ||
113 | 18 | |||
114 | 19 | #include "miral/active_outputs.h" | ||
115 | 20 | #include "miral/output.h" | ||
116 | 21 | |||
117 | 22 | #include <mir/shell/display_configuration_controller.h> | ||
118 | 23 | |||
119 | 24 | #include <mir_test_framework/headless_test.h> | ||
120 | 25 | |||
121 | 26 | #include <mir/test/doubles/fake_display.h> | ||
122 | 27 | #include <mir/test/doubles/stub_display_configuration.h> | ||
123 | 28 | #include <mir/test/fake_shared.h> | ||
124 | 29 | #include <mir/test/signal.h> | ||
125 | 30 | |||
126 | 31 | #include <gtest/gtest.h> | ||
127 | 32 | #include <gmock/gmock.h> | ||
128 | 33 | |||
129 | 34 | namespace mg = mir::graphics; | ||
130 | 35 | namespace mt = mir::test; | ||
131 | 36 | namespace mtd = mir::test::doubles; | ||
132 | 37 | namespace mtf = mir_test_framework; | ||
133 | 38 | |||
134 | 39 | using namespace miral; | ||
135 | 40 | using namespace testing; | ||
136 | 41 | |||
137 | 42 | namespace | ||
138 | 43 | { | ||
139 | 44 | struct MockActiveOutputsListener : ActiveOutputsListener | ||
140 | 45 | { | ||
141 | 46 | MOCK_METHOD0(advise_output_begin, void()); | ||
142 | 47 | MOCK_METHOD0(advise_output_end, void()); | ||
143 | 48 | |||
144 | 49 | MOCK_METHOD1(advise_output_create, void(Output const&)); | ||
145 | 50 | MOCK_METHOD2(advise_output_update, void(Output const&, Output const&)); | ||
146 | 51 | MOCK_METHOD1(advise_output_delete, void(Output const&)); | ||
147 | 52 | }; | ||
148 | 53 | |||
149 | 54 | std::vector<Rectangle> const output_rects{ | ||
150 | 55 | {{0,0}, {640,480}}, | ||
151 | 56 | {{640,0}, {640,480}} | ||
152 | 57 | }; | ||
153 | 58 | |||
154 | 59 | struct ActiveOutputs : mtf::HeadlessTest | ||
155 | 60 | { | ||
156 | 61 | ActiveOutputs() | ||
157 | 62 | { | ||
158 | 63 | add_to_environment("MIR_SERVER_NO_FILE", ""); | ||
159 | 64 | } | ||
160 | 65 | |||
161 | 66 | void SetUp() override | ||
162 | 67 | { | ||
163 | 68 | mtf::HeadlessTest::SetUp(); | ||
164 | 69 | preset_display(mt::fake_shared(display)); | ||
165 | 70 | active_outputs_monitor(server); | ||
166 | 71 | active_outputs_monitor.add_listener(&active_outputs_listener); | ||
167 | 72 | } | ||
168 | 73 | |||
169 | 74 | void TearDown() override | ||
170 | 75 | { | ||
171 | 76 | active_outputs_monitor.delete_listener(&active_outputs_listener); | ||
172 | 77 | mtf::HeadlessTest::TearDown(); | ||
173 | 78 | } | ||
174 | 79 | |||
175 | 80 | mtd::FakeDisplay display{output_rects}; | ||
176 | 81 | ActiveOutputsMonitor active_outputs_monitor; | ||
177 | 82 | NiceMock<MockActiveOutputsListener> active_outputs_listener; | ||
178 | 83 | |||
179 | 84 | void update_outputs(std::vector<Rectangle> const& displays) | ||
180 | 85 | { | ||
181 | 86 | mt::Signal signal; | ||
182 | 87 | EXPECT_CALL(active_outputs_listener, advise_output_end()).WillOnce(Invoke([&]{signal.raise(); })); | ||
183 | 88 | |||
184 | 89 | mtd::StubDisplayConfig changed_stub_display_config{displays}; | ||
185 | 90 | display.emit_configuration_change_event(mt::fake_shared(changed_stub_display_config)); | ||
186 | 91 | |||
187 | 92 | signal.wait_for(std::chrono::seconds(10)); | ||
188 | 93 | ASSERT_TRUE(signal.raised()); | ||
189 | 94 | } | ||
190 | 95 | |||
191 | 96 | void invert_outputs_in_base_configuration() | ||
192 | 97 | { | ||
193 | 98 | mt::Signal signal; | ||
194 | 99 | EXPECT_CALL(active_outputs_listener, advise_output_end()).WillOnce(Invoke([&]{signal.raise(); })); | ||
195 | 100 | |||
196 | 101 | auto configuration = server.the_display()->configuration(); | ||
197 | 102 | configuration->for_each_output([](mg::UserDisplayConfigurationOutput& output) | ||
198 | 103 | { | ||
199 | 104 | output.orientation = mir_orientation_inverted; | ||
200 | 105 | }); | ||
201 | 106 | |||
202 | 107 | server.the_display_configuration_controller()->set_base_configuration(std::move(configuration)); | ||
203 | 108 | |||
204 | 109 | signal.wait_for(std::chrono::seconds(10)); | ||
205 | 110 | ASSERT_TRUE(signal.raised()); | ||
206 | 111 | } | ||
207 | 112 | }; | ||
208 | 113 | |||
209 | 114 | struct RunServer | ||
210 | 115 | { | ||
211 | 116 | RunServer(mtf::HeadlessTest* self) : self{self} { self->start_server(); } | ||
212 | 117 | ~RunServer() { self->stop_server(); } | ||
213 | 118 | |||
214 | 119 | mtf::HeadlessTest* const self; | ||
215 | 120 | }; | ||
216 | 121 | } | ||
217 | 122 | |||
218 | 123 | TEST_F(ActiveOutputs, on_startup_listener_is_advised) | ||
219 | 124 | { | ||
220 | 125 | InSequence seq; | ||
221 | 126 | EXPECT_CALL(active_outputs_listener, advise_output_begin()); | ||
222 | 127 | EXPECT_CALL(active_outputs_listener, advise_output_create(_)).Times(2); | ||
223 | 128 | RunServer runner{this}; | ||
224 | 129 | |||
225 | 130 | Mock::VerifyAndClearExpectations(&active_outputs_listener); // before shutdown | ||
226 | 131 | } | ||
227 | 132 | |||
228 | 133 | TEST_F(ActiveOutputs, when_output_unplugged_listener_is_advised) | ||
229 | 134 | { | ||
230 | 135 | RunServer runner{this}; | ||
231 | 136 | |||
232 | 137 | InSequence seq; | ||
233 | 138 | EXPECT_CALL(active_outputs_listener, advise_output_begin()); | ||
234 | 139 | EXPECT_CALL(active_outputs_listener, advise_output_delete(_)).Times(1); | ||
235 | 140 | update_outputs({{{0,0}, {640,480}}}); | ||
236 | 141 | |||
237 | 142 | Mock::VerifyAndClearExpectations(&active_outputs_listener); // before shutdown | ||
238 | 143 | } | ||
239 | 144 | |||
240 | 145 | TEST_F(ActiveOutputs, when_output_added_listener_is_advised) | ||
241 | 146 | { | ||
242 | 147 | RunServer runner{this}; | ||
243 | 148 | |||
244 | 149 | auto new_output_rects = output_rects; | ||
245 | 150 | new_output_rects.emplace_back(Point{1280,0}, Size{640,480}); | ||
246 | 151 | |||
247 | 152 | InSequence seq; | ||
248 | 153 | EXPECT_CALL(active_outputs_listener, advise_output_begin()); | ||
249 | 154 | EXPECT_CALL(active_outputs_listener, advise_output_create(_)).Times(1); | ||
250 | 155 | update_outputs(new_output_rects); | ||
251 | 156 | |||
252 | 157 | Mock::VerifyAndClearExpectations(&active_outputs_listener); // before shutdown | ||
253 | 158 | } | ||
254 | 159 | |||
255 | 160 | TEST_F(ActiveOutputs, when_output_resized_listener_is_advised) | ||
256 | 161 | { | ||
257 | 162 | RunServer runner{this}; | ||
258 | 163 | |||
259 | 164 | auto new_output_rects = output_rects; | ||
260 | 165 | new_output_rects[1] = {Point{640,0}, Size{1080,768}}; | ||
261 | 166 | |||
262 | 167 | InSequence seq; | ||
263 | 168 | EXPECT_CALL(active_outputs_listener, advise_output_begin()); | ||
264 | 169 | EXPECT_CALL(active_outputs_listener, advise_output_update(_, _)).Times(1); | ||
265 | 170 | update_outputs(new_output_rects); | ||
266 | 171 | |||
267 | 172 | Mock::VerifyAndClearExpectations(&active_outputs_listener); // before shutdown | ||
268 | 173 | } | ||
269 | 174 | |||
270 | 175 | TEST_F(ActiveOutputs, when_base_configuration_is_updated_listener_is_advised) | ||
271 | 176 | { | ||
272 | 177 | RunServer runner{this}; | ||
273 | 178 | |||
274 | 179 | InSequence seq; | ||
275 | 180 | EXPECT_CALL(active_outputs_listener, advise_output_begin()); | ||
276 | 181 | EXPECT_CALL(active_outputs_listener, advise_output_update(_, _)).Times(2); | ||
277 | 182 | invert_outputs_in_base_configuration(); | ||
278 | 183 | |||
279 | 184 | Mock::VerifyAndClearExpectations(&active_outputs_listener); // before shutdown | ||
280 | 185 | } | ||
281 | 186 | |||
282 | 187 | TEST_F(ActiveOutputs, available_to_process) | ||
283 | 188 | { | ||
284 | 189 | RunServer runner{this}; | ||
285 | 190 | |||
286 | 191 | active_outputs_monitor.process_outputs([](std::vector<Output> const& outputs) | ||
287 | 192 | { EXPECT_THAT(outputs.size(), Eq(output_rects.size())); }); | ||
288 | 193 | } | ||
289 | 194 | |||
290 | 195 | TEST_F(ActiveOutputs, updates_are_available_to_process) | ||
291 | 196 | { | ||
292 | 197 | RunServer runner{this}; | ||
293 | 198 | |||
294 | 199 | auto new_output_rects = output_rects; | ||
295 | 200 | new_output_rects.emplace_back(Point{1280,0}, Size{640,480}); | ||
296 | 201 | update_outputs(new_output_rects); | ||
297 | 202 | |||
298 | 203 | active_outputs_monitor.process_outputs([&](std::vector<Output> const& outputs) | ||
299 | 204 | { EXPECT_THAT(outputs.size(), Eq(new_output_rects.size())); }); | ||
300 | 205 | } | ||
301 | 0 | 206 | ||
302 | === added file 'tests/miral/active_window.cpp' | |||
303 | --- tests/miral/active_window.cpp 1970-01-01 00:00:00 +0000 | |||
304 | +++ tests/miral/active_window.cpp 2017-08-24 15:19:58 +0000 | |||
305 | @@ -0,0 +1,412 @@ | |||
306 | 1 | /* | ||
307 | 2 | * Copyright © 2016 Canonical Ltd. | ||
308 | 3 | * | ||
309 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
310 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
311 | 6 | * published by the Free Software Foundation. | ||
312 | 7 | * | ||
313 | 8 | * This program is distributed in the hope that it will be useful, | ||
314 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
315 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
316 | 11 | * GNU General Public License for more details. | ||
317 | 12 | * | ||
318 | 13 | * You should have received a copy of the GNU General Public License | ||
319 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
320 | 15 | * | ||
321 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
322 | 17 | */ | ||
323 | 18 | |||
324 | 19 | #include "test_server.h" | ||
325 | 20 | |||
326 | 21 | #include <mir/client/surface.h> | ||
327 | 22 | #include <mir/client/window.h> | ||
328 | 23 | #include <mir/client/window_spec.h> | ||
329 | 24 | #include <mir_toolkit/mir_buffer_stream.h> | ||
330 | 25 | |||
331 | 26 | #include <miral/application_info.h> | ||
332 | 27 | |||
333 | 28 | #include <mir/test/signal.h> | ||
334 | 29 | |||
335 | 30 | #include <gtest/gtest.h> | ||
336 | 31 | #include <gmock/gmock.h> | ||
337 | 32 | |||
338 | 33 | using namespace testing; | ||
339 | 34 | using namespace mir::client; | ||
340 | 35 | using namespace std::chrono_literals; | ||
341 | 36 | using miral::WindowManagerTools; | ||
342 | 37 | |||
343 | 38 | |||
344 | 39 | namespace | ||
345 | 40 | { | ||
346 | 41 | class FocusChangeSync | ||
347 | 42 | { | ||
348 | 43 | public: | ||
349 | 44 | void exec(std::function<void()> const& f) | ||
350 | 45 | { | ||
351 | 46 | signal.reset(); | ||
352 | 47 | f(); | ||
353 | 48 | signal.wait_for(100ms); | ||
354 | 49 | } | ||
355 | 50 | |||
356 | 51 | static void raise_signal_on_focus_change(MirWindow* /*surface*/, MirEvent const* event, void* context) | ||
357 | 52 | { | ||
358 | 53 | if (mir_event_get_type(event) == mir_event_type_window && | ||
359 | 54 | mir_window_event_get_attribute(mir_event_get_window_event(event)) == mir_window_attrib_focus) | ||
360 | 55 | { | ||
361 | 56 | ((FocusChangeSync*)context)->signal.raise(); | ||
362 | 57 | } | ||
363 | 58 | } | ||
364 | 59 | |||
365 | 60 | auto signal_raised() -> bool { return signal.raised(); } | ||
366 | 61 | |||
367 | 62 | private: | ||
368 | 63 | mir::test::Signal signal; | ||
369 | 64 | }; | ||
370 | 65 | |||
371 | 66 | struct TestWindow : Surface, Window | ||
372 | 67 | { | ||
373 | 68 | using Surface::operator=; | ||
374 | 69 | using Window::operator=; | ||
375 | 70 | }; | ||
376 | 71 | |||
377 | 72 | struct ActiveWindow : public miral::TestServer | ||
378 | 73 | { | ||
379 | 74 | FocusChangeSync sync1; | ||
380 | 75 | FocusChangeSync sync2; | ||
381 | 76 | |||
382 | 77 | void paint(Surface const& surface) | ||
383 | 78 | { | ||
384 | 79 | mir_buffer_stream_swap_buffers_sync( | ||
385 | 80 | mir_render_surface_get_buffer_stream(surface, 50, 50, mir_pixel_format_argb_8888)); | ||
386 | 81 | } | ||
387 | 82 | |||
388 | 83 | auto create_window(Connection const& connection, char const* name, FocusChangeSync& sync) -> TestWindow | ||
389 | 84 | { | ||
390 | 85 | TestWindow result; | ||
391 | 86 | |||
392 | 87 | result = Surface{mir_connection_create_render_surface_sync(connection, 50, 50)}; | ||
393 | 88 | |||
394 | 89 | auto const spec = WindowSpec::for_normal_window(connection, 50, 50) | ||
395 | 90 | .set_event_handler(&FocusChangeSync::raise_signal_on_focus_change, &sync) | ||
396 | 91 | .add_surface(result, 50, 50, 0, 0) | ||
397 | 92 | .set_name(name); | ||
398 | 93 | |||
399 | 94 | result = Window{spec.create_window()}; | ||
400 | 95 | |||
401 | 96 | sync.exec([&]{ paint(result); }); | ||
402 | 97 | |||
403 | 98 | EXPECT_TRUE(sync.signal_raised()); | ||
404 | 99 | |||
405 | 100 | return result; | ||
406 | 101 | } | ||
407 | 102 | |||
408 | 103 | auto create_tip(Connection const& connection, char const* name, Window const& parent, FocusChangeSync& sync) -> TestWindow | ||
409 | 104 | { | ||
410 | 105 | TestWindow result; | ||
411 | 106 | result = Surface{mir_connection_create_render_surface_sync(connection, 50, 50)}; | ||
412 | 107 | |||
413 | 108 | MirRectangle aux_rect{10, 10, 10, 10}; | ||
414 | 109 | auto const spec = WindowSpec::for_tip(connection, 50, 50, parent, &aux_rect, mir_edge_attachment_any) | ||
415 | 110 | .set_event_handler(&FocusChangeSync::raise_signal_on_focus_change, &sync) | ||
416 | 111 | .add_surface(result, 50, 50, 0, 0) | ||
417 | 112 | .set_name(name); | ||
418 | 113 | |||
419 | 114 | result = Window{spec.create_window()}; | ||
420 | 115 | |||
421 | 116 | // Expect this to timeout: A tip should not receive focus | ||
422 | 117 | sync.exec([&]{ paint(result); }); | ||
423 | 118 | EXPECT_FALSE(sync.signal_raised()); | ||
424 | 119 | |||
425 | 120 | return result; | ||
426 | 121 | } | ||
427 | 122 | |||
428 | 123 | auto create_dialog(Connection const& connection, char const* name, Window const& parent, FocusChangeSync& sync) -> TestWindow | ||
429 | 124 | { | ||
430 | 125 | TestWindow result; | ||
431 | 126 | result = Surface{mir_connection_create_render_surface_sync(connection, 50, 50)}; | ||
432 | 127 | |||
433 | 128 | auto const spec = WindowSpec::for_dialog(connection, 50, 50, parent) | ||
434 | 129 | .set_event_handler(&FocusChangeSync::raise_signal_on_focus_change, &sync) | ||
435 | 130 | .add_surface(result, 50, 50, 0, 0) | ||
436 | 131 | .set_name(name); | ||
437 | 132 | |||
438 | 133 | result = Window{spec.create_window()}; | ||
439 | 134 | |||
440 | 135 | sync.exec([&]{ paint(result); }); | ||
441 | 136 | EXPECT_TRUE(sync.signal_raised()); | ||
442 | 137 | |||
443 | 138 | return result; | ||
444 | 139 | } | ||
445 | 140 | |||
446 | 141 | void assert_no_active_window() | ||
447 | 142 | { | ||
448 | 143 | invoke_tools([&](WindowManagerTools& tools) | ||
449 | 144 | { | ||
450 | 145 | auto const window = tools.active_window(); | ||
451 | 146 | ASSERT_FALSE(window); | ||
452 | 147 | }); | ||
453 | 148 | } | ||
454 | 149 | |||
455 | 150 | void assert_active_window_is(char const* const name) | ||
456 | 151 | { | ||
457 | 152 | invoke_tools([&](WindowManagerTools& tools) | ||
458 | 153 | { | ||
459 | 154 | auto const window = tools.active_window(); | ||
460 | 155 | ASSERT_TRUE(window); | ||
461 | 156 | auto const& window_info = tools.info_for(window); | ||
462 | 157 | ASSERT_THAT(window_info.name(), Eq(name)); | ||
463 | 158 | }); | ||
464 | 159 | } | ||
465 | 160 | }; | ||
466 | 161 | |||
467 | 162 | auto const another_name = "second"; | ||
468 | 163 | } | ||
469 | 164 | |||
470 | 165 | TEST_F(ActiveWindow, a_single_window_when_ready_becomes_active) | ||
471 | 166 | { | ||
472 | 167 | char const* const test_name = __PRETTY_FUNCTION__; | ||
473 | 168 | auto const connection = connect_client(test_name); | ||
474 | 169 | |||
475 | 170 | auto const window = create_window(connection, test_name, sync1); | ||
476 | 171 | |||
477 | 172 | assert_active_window_is(test_name); | ||
478 | 173 | } | ||
479 | 174 | |||
480 | 175 | TEST_F(ActiveWindow, a_single_window_when_hiding_becomes_inactive) | ||
481 | 176 | { | ||
482 | 177 | char const* const test_name = __PRETTY_FUNCTION__; | ||
483 | 178 | auto const connection = connect_client(test_name); | ||
484 | 179 | auto const window = create_window(connection, test_name, sync1); | ||
485 | 180 | |||
486 | 181 | sync1.exec([&]{ mir_window_set_state(window, mir_window_state_hidden); }); | ||
487 | 182 | |||
488 | 183 | EXPECT_TRUE(sync1.signal_raised()); | ||
489 | 184 | assert_no_active_window(); | ||
490 | 185 | } | ||
491 | 186 | |||
492 | 187 | TEST_F(ActiveWindow, a_single_window_when_unhiding_becomes_active) | ||
493 | 188 | { | ||
494 | 189 | char const* const test_name = __PRETTY_FUNCTION__; | ||
495 | 190 | auto const connection = connect_client(test_name); | ||
496 | 191 | auto const window = create_window(connection, test_name, sync1); | ||
497 | 192 | |||
498 | 193 | sync1.exec([&]{ mir_window_set_state(window, mir_window_state_hidden); }); | ||
499 | 194 | |||
500 | 195 | sync1.exec([&]{ mir_window_set_state(window, mir_window_state_restored); }); | ||
501 | 196 | |||
502 | 197 | EXPECT_TRUE(sync1.signal_raised()); | ||
503 | 198 | |||
504 | 199 | assert_active_window_is(test_name); | ||
505 | 200 | } | ||
506 | 201 | |||
507 | 202 | TEST_F(ActiveWindow, a_second_window_when_ready_becomes_active) | ||
508 | 203 | { | ||
509 | 204 | char const* const test_name = __PRETTY_FUNCTION__; | ||
510 | 205 | auto const connection = connect_client(test_name); | ||
511 | 206 | |||
512 | 207 | auto const first_window = create_window(connection, "first", sync1); | ||
513 | 208 | auto const window = create_window(connection, test_name, sync2); | ||
514 | 209 | |||
515 | 210 | assert_active_window_is(test_name); | ||
516 | 211 | } | ||
517 | 212 | |||
518 | 213 | TEST_F(ActiveWindow, a_second_window_hiding_makes_first_active) | ||
519 | 214 | { | ||
520 | 215 | char const* const test_name = __PRETTY_FUNCTION__; | ||
521 | 216 | auto const connection = connect_client(test_name); | ||
522 | 217 | |||
523 | 218 | auto const first_window = create_window(connection, test_name, sync1); | ||
524 | 219 | auto const window = create_window(connection, another_name, sync2); | ||
525 | 220 | |||
526 | 221 | sync2.exec([&]{ mir_window_set_state(window, mir_window_state_hidden); }); | ||
527 | 222 | |||
528 | 223 | EXPECT_TRUE(sync2.signal_raised()); | ||
529 | 224 | assert_active_window_is(test_name); | ||
530 | 225 | } | ||
531 | 226 | |||
532 | 227 | TEST_F(ActiveWindow, a_second_window_unhiding_leaves_first_active) | ||
533 | 228 | { | ||
534 | 229 | char const* const test_name = __PRETTY_FUNCTION__; | ||
535 | 230 | auto const connection = connect_client(test_name); | ||
536 | 231 | |||
537 | 232 | auto const first_window = create_window(connection, test_name, sync1); | ||
538 | 233 | auto const window = create_window(connection, another_name, sync2); | ||
539 | 234 | |||
540 | 235 | sync1.exec([&]{ mir_window_set_state(window, mir_window_state_hidden); }); | ||
541 | 236 | |||
542 | 237 | // Expect this to timeout | ||
543 | 238 | sync2.exec([&]{ mir_window_set_state(window, mir_window_state_restored); }); | ||
544 | 239 | |||
545 | 240 | EXPECT_THAT(sync2.signal_raised(), Eq(false)); | ||
546 | 241 | assert_active_window_is(test_name); | ||
547 | 242 | } | ||
548 | 243 | |||
549 | 244 | TEST_F(ActiveWindow, switching_from_a_second_window_makes_first_active) | ||
550 | 245 | { | ||
551 | 246 | char const* const test_name = __PRETTY_FUNCTION__; | ||
552 | 247 | auto const connection = connect_client(test_name); | ||
553 | 248 | |||
554 | 249 | auto const first_window = create_window(connection, test_name, sync1); | ||
555 | 250 | auto const window = create_window(connection, another_name, sync2); | ||
556 | 251 | |||
557 | 252 | sync1.exec([&]{ invoke_tools([](WindowManagerTools& tools){ tools.focus_next_within_application(); }); }); | ||
558 | 253 | |||
559 | 254 | EXPECT_TRUE(sync1.signal_raised()); | ||
560 | 255 | assert_active_window_is(test_name); | ||
561 | 256 | } | ||
562 | 257 | |||
563 | 258 | TEST_F(ActiveWindow, switching_from_a_second_application_makes_first_active) | ||
564 | 259 | { | ||
565 | 260 | char const* const test_name = __PRETTY_FUNCTION__; | ||
566 | 261 | auto const connection = connect_client(test_name); | ||
567 | 262 | auto const second_connection = connect_client(another_name); | ||
568 | 263 | |||
569 | 264 | auto const first_window = create_window(connection, test_name, sync1); | ||
570 | 265 | auto const window = create_window(second_connection, another_name, sync2); | ||
571 | 266 | |||
572 | 267 | sync1.exec([&]{ invoke_tools([](WindowManagerTools& tools){ tools.focus_next_application(); }); }); | ||
573 | 268 | |||
574 | 269 | EXPECT_TRUE(sync1.signal_raised()); | ||
575 | 270 | assert_active_window_is(test_name); | ||
576 | 271 | } | ||
577 | 272 | |||
578 | 273 | TEST_F(ActiveWindow, closing_a_second_application_makes_first_active) | ||
579 | 274 | { | ||
580 | 275 | char const* const test_name = __PRETTY_FUNCTION__; | ||
581 | 276 | auto const connection = connect_client(test_name); | ||
582 | 277 | |||
583 | 278 | auto const first_window = create_window(connection, test_name, sync1); | ||
584 | 279 | |||
585 | 280 | sync1.exec([&] | ||
586 | 281 | { | ||
587 | 282 | auto const second_connection = connect_client(another_name); | ||
588 | 283 | auto const window = create_window(second_connection, another_name, sync2); | ||
589 | 284 | assert_active_window_is(another_name); | ||
590 | 285 | }); | ||
591 | 286 | |||
592 | 287 | EXPECT_TRUE(sync1.signal_raised()); | ||
593 | 288 | assert_active_window_is(test_name); | ||
594 | 289 | } | ||
595 | 290 | |||
596 | 291 | TEST_F(ActiveWindow, selecting_a_tip_makes_parent_active) | ||
597 | 292 | { | ||
598 | 293 | char const* const test_name = __PRETTY_FUNCTION__; | ||
599 | 294 | auto const connection = connect_client(test_name); | ||
600 | 295 | |||
601 | 296 | auto const parent = create_window(connection, test_name, sync1); | ||
602 | 297 | |||
603 | 298 | miral::Window parent_window; | ||
604 | 299 | invoke_tools([&](WindowManagerTools& tools){ parent_window = tools.active_window(); }); | ||
605 | 300 | |||
606 | 301 | // Steal the focus | ||
607 | 302 | auto second_connection = connect_client(another_name); | ||
608 | 303 | auto second_surface = create_window(second_connection, another_name, sync2); | ||
609 | 304 | |||
610 | 305 | auto const tip = create_tip(connection, "tip", parent, sync2); | ||
611 | 306 | |||
612 | 307 | sync1.exec([&] | ||
613 | 308 | { | ||
614 | 309 | invoke_tools([&](WindowManagerTools& tools) | ||
615 | 310 | { tools.select_active_window(*tools.info_for(parent_window).children().begin()); }); | ||
616 | 311 | }); | ||
617 | 312 | EXPECT_TRUE(sync1.signal_raised()); | ||
618 | 313 | |||
619 | 314 | assert_active_window_is(test_name); | ||
620 | 315 | } | ||
621 | 316 | |||
622 | 317 | TEST_F(ActiveWindow, selecting_a_parent_makes_dialog_active) | ||
623 | 318 | { | ||
624 | 319 | char const* const test_name = __PRETTY_FUNCTION__; | ||
625 | 320 | auto const dialog_name = "dialog"; | ||
626 | 321 | auto const connection = connect_client(test_name); | ||
627 | 322 | |||
628 | 323 | auto const parent = create_window(connection, test_name, sync1); | ||
629 | 324 | |||
630 | 325 | miral::Window parent_window; | ||
631 | 326 | invoke_tools([&](WindowManagerTools& tools){ parent_window = tools.active_window(); }); | ||
632 | 327 | |||
633 | 328 | auto const dialog = create_dialog(connection, dialog_name, parent, sync2); | ||
634 | 329 | |||
635 | 330 | // Steal the focus | ||
636 | 331 | auto second_connection = connect_client(another_name); | ||
637 | 332 | auto second_surface = create_window(second_connection, another_name, sync1); | ||
638 | 333 | |||
639 | 334 | sync2.exec([&]{ invoke_tools([&](WindowManagerTools& tools){ tools.select_active_window(parent_window); }); }); | ||
640 | 335 | |||
641 | 336 | EXPECT_TRUE(sync2.signal_raised()); | ||
642 | 337 | assert_active_window_is(dialog_name); | ||
643 | 338 | } | ||
644 | 339 | |||
645 | 340 | TEST_F(ActiveWindow, input_methods_are_not_focussed) | ||
646 | 341 | { | ||
647 | 342 | char const* const test_name = __PRETTY_FUNCTION__; | ||
648 | 343 | auto const connection = connect_client(test_name); | ||
649 | 344 | |||
650 | 345 | auto const parent = create_window(connection, test_name, sync1); | ||
651 | 346 | auto const input_method = WindowSpec::for_input_method(connection, 50, 50, parent).create_window(); | ||
652 | 347 | |||
653 | 348 | assert_active_window_is(test_name); | ||
654 | 349 | |||
655 | 350 | invoke_tools([&](WindowManagerTools& tools) | ||
656 | 351 | { | ||
657 | 352 | auto const& info = tools.info_for(tools.active_window()); | ||
658 | 353 | tools.select_active_window(info.children().at(0)); | ||
659 | 354 | }); | ||
660 | 355 | |||
661 | 356 | assert_active_window_is(test_name); | ||
662 | 357 | } | ||
663 | 358 | |||
664 | 359 | TEST_F(ActiveWindow, satellites_are_not_focussed) | ||
665 | 360 | { | ||
666 | 361 | char const* const test_name = __PRETTY_FUNCTION__; | ||
667 | 362 | auto const connection = connect_client(test_name); | ||
668 | 363 | |||
669 | 364 | auto const parent = create_window(connection, test_name, sync1); | ||
670 | 365 | auto const satellite = WindowSpec::for_satellite(connection, 50, 50, parent).create_window(); | ||
671 | 366 | |||
672 | 367 | assert_active_window_is(test_name); | ||
673 | 368 | |||
674 | 369 | invoke_tools([&](WindowManagerTools& tools) | ||
675 | 370 | { | ||
676 | 371 | auto const& info = tools.info_for(tools.active_window()); | ||
677 | 372 | tools.select_active_window(info.children().at(0)); | ||
678 | 373 | }); | ||
679 | 374 | |||
680 | 375 | assert_active_window_is(test_name); | ||
681 | 376 | } | ||
682 | 377 | |||
683 | 378 | // lp:1671072 | ||
684 | 379 | TEST_F(ActiveWindow, hiding_active_dialog_makes_parent_active) | ||
685 | 380 | { | ||
686 | 381 | char const* const parent_name = __PRETTY_FUNCTION__; | ||
687 | 382 | auto const dialog_name = "dialog"; | ||
688 | 383 | auto const connection = connect_client(parent_name); | ||
689 | 384 | |||
690 | 385 | auto const parent = create_window(connection, parent_name, sync1); | ||
691 | 386 | auto const dialog = create_dialog(connection, dialog_name, parent, sync2); | ||
692 | 387 | |||
693 | 388 | sync1.exec([&]{ mir_window_set_state(dialog, mir_window_state_hidden); }); | ||
694 | 389 | |||
695 | 390 | EXPECT_TRUE(sync1.signal_raised()); | ||
696 | 391 | |||
697 | 392 | assert_active_window_is(parent_name); | ||
698 | 393 | } | ||
699 | 394 | |||
700 | 395 | TEST_F(ActiveWindow, when_another_window_is_about_hiding_active_dialog_makes_parent_active) | ||
701 | 396 | { | ||
702 | 397 | FocusChangeSync sync3; | ||
703 | 398 | char const* const parent_name = __PRETTY_FUNCTION__; | ||
704 | 399 | auto const dialog_name = "dialog"; | ||
705 | 400 | auto const another_window_name = "another window"; | ||
706 | 401 | auto const connection = connect_client(parent_name); | ||
707 | 402 | |||
708 | 403 | auto const parent = create_window(connection, parent_name, sync1); | ||
709 | 404 | auto const another_window = create_window(connection, another_window_name, sync2); | ||
710 | 405 | auto const dialog = create_dialog(connection, dialog_name, parent, sync3); | ||
711 | 406 | |||
712 | 407 | sync1.exec([&]{ mir_window_set_state(dialog, mir_window_state_hidden); }); | ||
713 | 408 | |||
714 | 409 | EXPECT_TRUE(sync1.signal_raised()); | ||
715 | 410 | |||
716 | 411 | assert_active_window_is(parent_name); | ||
717 | 412 | } | ||
718 | 0 | 413 | ||
719 | === added file 'tests/miral/client_mediated_gestures.cpp' | |||
720 | --- tests/miral/client_mediated_gestures.cpp 1970-01-01 00:00:00 +0000 | |||
721 | +++ tests/miral/client_mediated_gestures.cpp 2017-08-24 15:19:58 +0000 | |||
722 | @@ -0,0 +1,302 @@ | |||
723 | 1 | /* | ||
724 | 2 | * Copyright © 2017 Canonical Ltd. | ||
725 | 3 | * | ||
726 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
727 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
728 | 6 | * published by the Free Software Foundation. | ||
729 | 7 | * | ||
730 | 8 | * This program is distributed in the hope that it will be useful, | ||
731 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
732 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
733 | 11 | * GNU General Public License for more details. | ||
734 | 12 | * | ||
735 | 13 | * You should have received a copy of the GNU General Public License | ||
736 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
737 | 15 | * | ||
738 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
739 | 17 | */ | ||
740 | 18 | |||
741 | 19 | #include <mir/client/surface.h> | ||
742 | 20 | #include <mir/client/window_spec.h> | ||
743 | 21 | |||
744 | 22 | #include <mir_toolkit/mir_window.h> | ||
745 | 23 | #include <mir_toolkit/mir_blob.h> | ||
746 | 24 | |||
747 | 25 | #include <mir/geometry/displacement.h> | ||
748 | 26 | #include <mir/input/input_device_info.h> | ||
749 | 27 | #include <mir/input/device_capability.h> | ||
750 | 28 | #include <mir/shell/canonical_window_manager.h> | ||
751 | 29 | #include <mir/shell/shell.h> | ||
752 | 30 | |||
753 | 31 | #include <mir_test_framework/connected_client_with_a_window.h> | ||
754 | 32 | #include <mir_test_framework/fake_input_device.h> | ||
755 | 33 | #include <mir_test_framework/stub_server_platform_factory.h> | ||
756 | 34 | #include <mir/test/event_factory.h> | ||
757 | 35 | #include <mir/test/fake_shared.h> | ||
758 | 36 | #include <mir/test/signal.h> | ||
759 | 37 | |||
760 | 38 | #include <gmock/gmock.h> | ||
761 | 39 | #include <gtest/gtest.h> | ||
762 | 40 | |||
763 | 41 | #include <linux/input.h> | ||
764 | 42 | |||
765 | 43 | #include <atomic> | ||
766 | 44 | |||
767 | 45 | using namespace std::chrono_literals; | ||
768 | 46 | using namespace mir::geometry; | ||
769 | 47 | using namespace testing; | ||
770 | 48 | using mir::test::fake_shared; | ||
771 | 49 | using mir::test::Signal; | ||
772 | 50 | |||
773 | 51 | namespace | ||
774 | 52 | { | ||
775 | 53 | class Cookie | ||
776 | 54 | { | ||
777 | 55 | public: | ||
778 | 56 | Cookie() = default; | ||
779 | 57 | |||
780 | 58 | explicit Cookie(MirCookie const* cookie) : self{cookie, deleter} {} | ||
781 | 59 | |||
782 | 60 | operator MirCookie const*() const { return self.get(); } | ||
783 | 61 | |||
784 | 62 | auto get() const -> MirCookie const* { return self.get(); } | ||
785 | 63 | |||
786 | 64 | void reset() { self.reset(); } | ||
787 | 65 | |||
788 | 66 | void reset(MirCookie const* cookie) { self.reset(cookie, deleter); } | ||
789 | 67 | |||
790 | 68 | private: | ||
791 | 69 | static void deleter(MirCookie const* cookie) { mir_cookie_release(cookie); } | ||
792 | 70 | |||
793 | 71 | std::shared_ptr<MirCookie const> self; | ||
794 | 72 | }; | ||
795 | 73 | |||
796 | 74 | void mir_cookie_release(Cookie const&) = delete; | ||
797 | 75 | |||
798 | 76 | struct MockWindowManager : mir::shell::CanonicalWindowManager | ||
799 | 77 | { | ||
800 | 78 | #if defined(__clang__) | ||
801 | 79 | #pragma GCC diagnostic push | ||
802 | 80 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | ||
803 | 81 | #endif | ||
804 | 82 | using mir::shell::CanonicalWindowManager::CanonicalWindowManager; | ||
805 | 83 | #if defined(__clang__) | ||
806 | 84 | #pragma GCC diagnostic pop | ||
807 | 85 | #endif | ||
808 | 86 | |||
809 | 87 | MOCK_METHOD3(handle_request_move, | ||
810 | 88 | void(std::shared_ptr<mir::scene::Session> const&, std::shared_ptr<mir::scene::Surface> const&, uint64_t)); | ||
811 | 89 | }; | ||
812 | 90 | |||
813 | 91 | struct MouseMoverAndFaker | ||
814 | 92 | { | ||
815 | 93 | void start_dragging_mouse() | ||
816 | 94 | { | ||
817 | 95 | using namespace mir::input::synthesis; | ||
818 | 96 | fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT)); | ||
819 | 97 | } | ||
820 | 98 | |||
821 | 99 | void move_mouse(Displacement const& displacement) | ||
822 | 100 | { | ||
823 | 101 | using mir::input::synthesis::a_pointer_event; | ||
824 | 102 | fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int())); | ||
825 | 103 | } | ||
826 | 104 | |||
827 | 105 | void release_mouse() | ||
828 | 106 | { | ||
829 | 107 | using namespace mir::input::synthesis; | ||
830 | 108 | fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT)); | ||
831 | 109 | } | ||
832 | 110 | |||
833 | 111 | private: | ||
834 | 112 | std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{ | ||
835 | 113 | mir_test_framework::add_fake_input_device( | ||
836 | 114 | mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer}) | ||
837 | 115 | }; | ||
838 | 116 | }; | ||
839 | 117 | |||
840 | 118 | Rectangle const screen_geometry{{0, 0}, {800, 600}}; | ||
841 | 119 | auto const receive_event_timeout = 90s; | ||
842 | 120 | |||
843 | 121 | struct ClientMediatedUserGestures : mir_test_framework::ConnectedClientWithAWindow, | ||
844 | 122 | MouseMoverAndFaker | ||
845 | 123 | { | ||
846 | 124 | void SetUp() override | ||
847 | 125 | { | ||
848 | 126 | initial_display_layout({screen_geometry}); | ||
849 | 127 | server.override_the_window_manager_builder([this](mir::shell::FocusController* focus_controller) | ||
850 | 128 | { | ||
851 | 129 | return window_manager = | ||
852 | 130 | std::make_shared<MockWindowManager>(focus_controller, server.the_shell_display_layout()); | ||
853 | 131 | }); | ||
854 | 132 | |||
855 | 133 | mir_test_framework::ConnectedClientWithAWindow::SetUp(); | ||
856 | 134 | mir_window_set_event_handler(window, &window_event_handler, this); | ||
857 | 135 | |||
858 | 136 | paint_window(); | ||
859 | 137 | |||
860 | 138 | center_mouse(); | ||
861 | 139 | } | ||
862 | 140 | |||
863 | 141 | void TearDown() override | ||
864 | 142 | { | ||
865 | 143 | reset_window_event_handler(); | ||
866 | 144 | window_manager.reset(); | ||
867 | 145 | surface.reset(); | ||
868 | 146 | mir_test_framework::ConnectedClientWithAWindow::TearDown(); | ||
869 | 147 | } | ||
870 | 148 | |||
871 | 149 | auto user_initiates_gesture() -> Cookie; | ||
872 | 150 | |||
873 | 151 | std::shared_ptr<MockWindowManager> window_manager; | ||
874 | 152 | |||
875 | 153 | private: | ||
876 | 154 | void center_mouse(); | ||
877 | 155 | void paint_window(); | ||
878 | 156 | void set_window_event_handler(std::function<void(MirEvent const* event)> const& handler); | ||
879 | 157 | void reset_window_event_handler(); | ||
880 | 158 | void invoke_window_event_handler(MirEvent const* event) | ||
881 | 159 | { | ||
882 | 160 | std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex}; | ||
883 | 161 | window_event_handler_(event); | ||
884 | 162 | } | ||
885 | 163 | |||
886 | 164 | mir::client::Surface surface; | ||
887 | 165 | |||
888 | 166 | std::mutex window_event_handler_mutex; | ||
889 | 167 | std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {}; | ||
890 | 168 | |||
891 | 169 | static void window_event_handler(MirWindow* window, MirEvent const* event, void* context); | ||
892 | 170 | }; | ||
893 | 171 | |||
894 | 172 | void ClientMediatedUserGestures::set_window_event_handler(std::function<void(MirEvent const* event)> const& handler) | ||
895 | 173 | { | ||
896 | 174 | std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex}; | ||
897 | 175 | window_event_handler_ = handler; | ||
898 | 176 | } | ||
899 | 177 | |||
900 | 178 | void ClientMediatedUserGestures::reset_window_event_handler() | ||
901 | 179 | { | ||
902 | 180 | std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex}; | ||
903 | 181 | window_event_handler_ = [](MirEvent const*) {}; | ||
904 | 182 | } | ||
905 | 183 | |||
906 | 184 | void ClientMediatedUserGestures::window_event_handler(MirWindow* /*window*/, MirEvent const* event, void* context) | ||
907 | 185 | { | ||
908 | 186 | static_cast<ClientMediatedUserGestures*>(context)->invoke_window_event_handler(event); | ||
909 | 187 | } | ||
910 | 188 | |||
911 | 189 | void ClientMediatedUserGestures::paint_window() | ||
912 | 190 | { | ||
913 | 191 | { | ||
914 | 192 | surface = mir::client::Surface{mir_connection_create_render_surface_sync(connection, 42, 42)}; | ||
915 | 193 | auto const spec = mir::client::WindowSpec::for_changes(connection); | ||
916 | 194 | mir_window_spec_add_render_surface(spec, surface, 42, 42, 0, 0); | ||
917 | 195 | mir_window_apply_spec(window, spec); | ||
918 | 196 | } | ||
919 | 197 | |||
920 | 198 | Signal have_focus; | ||
921 | 199 | |||
922 | 200 | set_window_event_handler([&](MirEvent const* event) | ||
923 | 201 | { | ||
924 | 202 | if (mir_event_get_type(event) != mir_event_type_window) | ||
925 | 203 | return; | ||
926 | 204 | |||
927 | 205 | auto const window_event = mir_event_get_window_event(event); | ||
928 | 206 | if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus) | ||
929 | 207 | return; | ||
930 | 208 | |||
931 | 209 | if (mir_window_event_get_attribute_value(window_event)) | ||
932 | 210 | have_focus.raise(); | ||
933 | 211 | }); | ||
934 | 212 | |||
935 | 213 | mir_buffer_stream_swap_buffers_sync(mir_render_surface_get_buffer_stream(surface, 42, 42, mir_pixel_format_argb_8888)); | ||
936 | 214 | |||
937 | 215 | EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true)); | ||
938 | 216 | |||
939 | 217 | reset_window_event_handler(); | ||
940 | 218 | } | ||
941 | 219 | |||
942 | 220 | void ClientMediatedUserGestures::center_mouse() | ||
943 | 221 | { | ||
944 | 222 | Signal have_mouseover; | ||
945 | 223 | |||
946 | 224 | set_window_event_handler([&](MirEvent const* event) | ||
947 | 225 | { | ||
948 | 226 | if (mir_event_get_type(event) != mir_event_type_input) | ||
949 | 227 | return; | ||
950 | 228 | |||
951 | 229 | auto const input_event = mir_event_get_input_event(event); | ||
952 | 230 | |||
953 | 231 | if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) | ||
954 | 232 | return; | ||
955 | 233 | |||
956 | 234 | auto const pointer_event = mir_input_event_get_pointer_event(input_event); | ||
957 | 235 | |||
958 | 236 | if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter) | ||
959 | 237 | return; | ||
960 | 238 | |||
961 | 239 | have_mouseover.raise(); | ||
962 | 240 | }); | ||
963 | 241 | |||
964 | 242 | move_mouse(0.5 * as_displacement(screen_geometry.size)); | ||
965 | 243 | |||
966 | 244 | // We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20). | ||
967 | 245 | // But it isn't essential for the test and we've probably waited long enough | ||
968 | 246 | // for the mouse-down needed by the test to reach the window. | ||
969 | 247 | // EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true)); | ||
970 | 248 | have_mouseover.wait_for(receive_event_timeout); | ||
971 | 249 | |||
972 | 250 | reset_window_event_handler(); | ||
973 | 251 | } | ||
974 | 252 | |||
975 | 253 | auto ClientMediatedUserGestures::user_initiates_gesture() -> Cookie | ||
976 | 254 | { | ||
977 | 255 | Cookie cookie; | ||
978 | 256 | Signal have_cookie; | ||
979 | 257 | |||
980 | 258 | set_window_event_handler([&](MirEvent const* event) | ||
981 | 259 | { | ||
982 | 260 | if (mir_event_get_type(event) != mir_event_type_input) | ||
983 | 261 | return; | ||
984 | 262 | |||
985 | 263 | auto const input_event = mir_event_get_input_event(event); | ||
986 | 264 | |||
987 | 265 | if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) | ||
988 | 266 | return; | ||
989 | 267 | |||
990 | 268 | auto const pointer_event = mir_input_event_get_pointer_event(input_event); | ||
991 | 269 | |||
992 | 270 | if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down) | ||
993 | 271 | return; | ||
994 | 272 | |||
995 | 273 | cookie = Cookie{mir_input_event_get_cookie(input_event)}; | ||
996 | 274 | have_cookie.raise(); | ||
997 | 275 | }); | ||
998 | 276 | |||
999 | 277 | start_dragging_mouse(); | ||
1000 | 278 | |||
1001 | 279 | EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true)); | ||
1002 | 280 | |||
1003 | 281 | reset_window_event_handler(); | ||
1004 | 282 | return cookie; | ||
1005 | 283 | } | ||
1006 | 284 | } | ||
1007 | 285 | |||
1008 | 286 | TEST_F(ClientMediatedUserGestures, when_user_initiates_gesture_client_receives_cookie) | ||
1009 | 287 | { | ||
1010 | 288 | auto const cookie = user_initiates_gesture(); | ||
1011 | 289 | |||
1012 | 290 | EXPECT_THAT(cookie.get(), NotNull()); | ||
1013 | 291 | } | ||
1014 | 292 | |||
1015 | 293 | TEST_F(ClientMediatedUserGestures, when_client_initiates_move_window_manager_handles_request) | ||
1016 | 294 | { | ||
1017 | 295 | auto const cookie = user_initiates_gesture(); | ||
1018 | 296 | Signal have_request; | ||
1019 | 297 | EXPECT_CALL(*window_manager, handle_request_move(_, _, _)).WillOnce(InvokeWithoutArgs([&]{ have_request.raise(); })); | ||
1020 | 298 | |||
1021 | 299 | mir_window_request_user_move(window, cookie); | ||
1022 | 300 | |||
1023 | 301 | EXPECT_THAT(have_request.wait_for(receive_event_timeout), Eq(true)); | ||
1024 | 302 | } | ||
1025 | 0 | 303 | ||
1026 | === added file 'tests/miral/display_reconfiguration.cpp' | |||
1027 | --- tests/miral/display_reconfiguration.cpp 1970-01-01 00:00:00 +0000 | |||
1028 | +++ tests/miral/display_reconfiguration.cpp 2017-08-24 15:19:58 +0000 | |||
1029 | @@ -0,0 +1,87 @@ | |||
1030 | 1 | /* | ||
1031 | 2 | * Copyright © 2016 Canonical Ltd. | ||
1032 | 3 | * | ||
1033 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1034 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
1035 | 6 | * published by the Free Software Foundation. | ||
1036 | 7 | * | ||
1037 | 8 | * This program is distributed in the hope that it will be useful, | ||
1038 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1039 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1040 | 11 | * GNU General Public License for more details. | ||
1041 | 12 | * | ||
1042 | 13 | * You should have received a copy of the GNU General Public License | ||
1043 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1044 | 15 | * | ||
1045 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
1046 | 17 | */ | ||
1047 | 18 | |||
1048 | 19 | #include "test_window_manager_tools.h" | ||
1049 | 20 | #include <mir/event_printer.h> | ||
1050 | 21 | |||
1051 | 22 | using namespace miral; | ||
1052 | 23 | using namespace testing; | ||
1053 | 24 | namespace mt = mir::test; | ||
1054 | 25 | using mir::operator<<; | ||
1055 | 26 | |||
1056 | 27 | namespace | ||
1057 | 28 | { | ||
1058 | 29 | X const display_left{0}; | ||
1059 | 30 | Y const display_top{0}; | ||
1060 | 31 | Width const display_width{640}; | ||
1061 | 32 | Height const display_height{480}; | ||
1062 | 33 | |||
1063 | 34 | Rectangle const display_area{{display_left, display_top}, | ||
1064 | 35 | {display_width, display_height}}; | ||
1065 | 36 | |||
1066 | 37 | struct DisplayConfiguration : TestWindowManagerTools | ||
1067 | 38 | { | ||
1068 | 39 | Size const initial_window_size{600, 400}; | ||
1069 | 40 | |||
1070 | 41 | Window window; | ||
1071 | 42 | |||
1072 | 43 | void SetUp() override | ||
1073 | 44 | { | ||
1074 | 45 | basic_window_manager.add_display_for_testing(display_area); | ||
1075 | 46 | basic_window_manager.add_session(session); | ||
1076 | 47 | } | ||
1077 | 48 | |||
1078 | 49 | void create_fullscreen_window() | ||
1079 | 50 | { | ||
1080 | 51 | mir::scene::SurfaceCreationParameters creation_parameters; | ||
1081 | 52 | creation_parameters.type = mir_window_type_normal; | ||
1082 | 53 | creation_parameters.size = initial_window_size; | ||
1083 | 54 | creation_parameters.state = mir_window_state_fullscreen; | ||
1084 | 55 | creation_parameters.output_id = mir::graphics::DisplayConfigurationOutputId{0}; | ||
1085 | 56 | |||
1086 | 57 | EXPECT_CALL(*window_manager_policy, advise_new_window(_)) | ||
1087 | 58 | .WillOnce( | ||
1088 | 59 | Invoke( | ||
1089 | 60 | [this](WindowInfo const& window_info) | ||
1090 | 61 | { window = window_info.window(); })); | ||
1091 | 62 | |||
1092 | 63 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
1093 | 64 | basic_window_manager.select_active_window(window); | ||
1094 | 65 | |||
1095 | 66 | // Clear the expectations used to capture parent & child | ||
1096 | 67 | Mock::VerifyAndClearExpectations(window_manager_policy); | ||
1097 | 68 | } | ||
1098 | 69 | }; | ||
1099 | 70 | } | ||
1100 | 71 | |||
1101 | 72 | // This is the scenario behind lp:1640557 | ||
1102 | 73 | TEST_F(DisplayConfiguration, given_fullscreen_windows_reconfiguring_displays_doesnt_crash) | ||
1103 | 74 | { | ||
1104 | 75 | create_fullscreen_window(); | ||
1105 | 76 | |||
1106 | 77 | WindowSpecification mods; | ||
1107 | 78 | mods.state() = mir_window_state_hidden; | ||
1108 | 79 | window_manager_tools.modify_window(window, mods); | ||
1109 | 80 | mods.state() = mir_window_state_fullscreen; | ||
1110 | 81 | window_manager_tools.modify_window(window, mods); | ||
1111 | 82 | |||
1112 | 83 | Rectangle const new_display{display_area.top_left+as_displacement({display_width, Height{0}}), display_area.size}; | ||
1113 | 84 | |||
1114 | 85 | basic_window_manager.add_display_for_testing(new_display); | ||
1115 | 86 | basic_window_manager.remove_display(new_display); | ||
1116 | 87 | } | ||
1117 | 0 | 88 | ||
1118 | === added file 'tests/miral/drag_active_window.cpp' | |||
1119 | --- tests/miral/drag_active_window.cpp 1970-01-01 00:00:00 +0000 | |||
1120 | +++ tests/miral/drag_active_window.cpp 2017-08-24 15:19:58 +0000 | |||
1121 | @@ -0,0 +1,153 @@ | |||
1122 | 1 | /* | ||
1123 | 2 | * Copyright © 2016 Canonical Ltd. | ||
1124 | 3 | * | ||
1125 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1126 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
1127 | 6 | * published by the Free Software Foundation. | ||
1128 | 7 | * | ||
1129 | 8 | * This program is distributed in the hope that it will be useful, | ||
1130 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1131 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1132 | 11 | * GNU General Public License for more details. | ||
1133 | 12 | * | ||
1134 | 13 | * You should have received a copy of the GNU General Public License | ||
1135 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1136 | 15 | * | ||
1137 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
1138 | 17 | */ | ||
1139 | 18 | |||
1140 | 19 | #include "test_window_manager_tools.h" | ||
1141 | 20 | #include <mir/event_printer.h> | ||
1142 | 21 | |||
1143 | 22 | using namespace miral; | ||
1144 | 23 | using namespace testing; | ||
1145 | 24 | namespace mt = mir::test; | ||
1146 | 25 | using mir::operator<<; | ||
1147 | 26 | |||
1148 | 27 | namespace | ||
1149 | 28 | { | ||
1150 | 29 | X const display_left{0}; | ||
1151 | 30 | Y const display_top{0}; | ||
1152 | 31 | Width const display_width{640}; | ||
1153 | 32 | Height const display_height{480}; | ||
1154 | 33 | |||
1155 | 34 | Rectangle const display_area{{display_left, display_top}, | ||
1156 | 35 | {display_width, display_height}}; | ||
1157 | 36 | |||
1158 | 37 | struct DragActiveWindow : TestWindowManagerTools, WithParamInterface<MirWindowType> | ||
1159 | 38 | { | ||
1160 | 39 | Size const initial_parent_size{600, 400}; | ||
1161 | 40 | |||
1162 | 41 | Window window; | ||
1163 | 42 | |||
1164 | 43 | void SetUp() override | ||
1165 | 44 | { | ||
1166 | 45 | basic_window_manager.add_display_for_testing(display_area); | ||
1167 | 46 | basic_window_manager.add_session(session); | ||
1168 | 47 | } | ||
1169 | 48 | |||
1170 | 49 | void create_window_of_type(MirWindowType type) | ||
1171 | 50 | { | ||
1172 | 51 | mir::scene::SurfaceCreationParameters creation_parameters; | ||
1173 | 52 | creation_parameters.type = type; | ||
1174 | 53 | creation_parameters.size = initial_parent_size; | ||
1175 | 54 | |||
1176 | 55 | EXPECT_CALL(*window_manager_policy, advise_new_window(_)) | ||
1177 | 56 | .WillOnce( | ||
1178 | 57 | Invoke( | ||
1179 | 58 | [this](WindowInfo const& window_info) | ||
1180 | 59 | { window = window_info.window(); })); | ||
1181 | 60 | |||
1182 | 61 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
1183 | 62 | basic_window_manager.select_active_window(window); | ||
1184 | 63 | |||
1185 | 64 | // Clear the expectations used to capture parent & child | ||
1186 | 65 | Mock::VerifyAndClearExpectations(window_manager_policy); | ||
1187 | 66 | } | ||
1188 | 67 | }; | ||
1189 | 68 | |||
1190 | 69 | using ForMoveableTypes = DragActiveWindow; | ||
1191 | 70 | using ForUnmoveableTypes = DragActiveWindow; | ||
1192 | 71 | |||
1193 | 72 | TEST_P(ForMoveableTypes, moves) | ||
1194 | 73 | { | ||
1195 | 74 | create_window_of_type(GetParam()); | ||
1196 | 75 | |||
1197 | 76 | Displacement const movement{10, 10}; | ||
1198 | 77 | auto const initial_position = window.top_left(); | ||
1199 | 78 | auto const expected_position = initial_position + movement; | ||
1200 | 79 | |||
1201 | 80 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
1202 | 81 | |||
1203 | 82 | window_manager_tools.drag_active_window(movement); | ||
1204 | 83 | |||
1205 | 84 | EXPECT_THAT(window.top_left(), Eq(expected_position)) | ||
1206 | 85 | << "Type: " << GetParam(); | ||
1207 | 86 | } | ||
1208 | 87 | |||
1209 | 88 | TEST_P(ForUnmoveableTypes, doesnt_move) | ||
1210 | 89 | { | ||
1211 | 90 | create_window_of_type(GetParam()); | ||
1212 | 91 | |||
1213 | 92 | Displacement const movement{10, 10}; | ||
1214 | 93 | auto const expected_position = window.top_left(); | ||
1215 | 94 | |||
1216 | 95 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, _)).Times(0); | ||
1217 | 96 | |||
1218 | 97 | window_manager_tools.drag_active_window(movement); | ||
1219 | 98 | |||
1220 | 99 | EXPECT_THAT(window.top_left(), Eq(expected_position)) | ||
1221 | 100 | << "Type: " << GetParam(); | ||
1222 | 101 | } | ||
1223 | 102 | } | ||
1224 | 103 | |||
1225 | 104 | // When a surface is moved interactively | ||
1226 | 105 | // ------------------------------------- | ||
1227 | 106 | // Regular, floating regular, dialog, and satellite surfaces should be user-movable. | ||
1228 | 107 | // Popups, glosses, and tips should not be. | ||
1229 | 108 | // Freestyle surfaces may or may not be, as specified by the app. | ||
1230 | 109 | // Mir and Unity: Surfaces, input, and displays (v0.3) | ||
1231 | 110 | INSTANTIATE_TEST_CASE_P(DragActiveWindow, ForMoveableTypes, ::testing::Values( | ||
1232 | 111 | mir_window_type_normal, | ||
1233 | 112 | mir_window_type_utility, | ||
1234 | 113 | mir_window_type_dialog, | ||
1235 | 114 | // mir_window_type_gloss, | ||
1236 | 115 | mir_window_type_freestyle | ||
1237 | 116 | // mir_window_type_menu, | ||
1238 | 117 | // mir_window_type_inputmethod, | ||
1239 | 118 | // mir_window_type_satellite, | ||
1240 | 119 | // mir_window_type_tip, | ||
1241 | 120 | // mir_window_types | ||
1242 | 121 | )); | ||
1243 | 122 | |||
1244 | 123 | |||
1245 | 124 | INSTANTIATE_TEST_CASE_P(DragActiveWindow, ForUnmoveableTypes, ::testing::Values( | ||
1246 | 125 | // mir_window_type_normal, | ||
1247 | 126 | // mir_window_type_utility, | ||
1248 | 127 | // mir_window_type_dialog, | ||
1249 | 128 | mir_window_type_gloss, | ||
1250 | 129 | // mir_window_type_freestyle, | ||
1251 | 130 | mir_window_type_menu, | ||
1252 | 131 | mir_window_type_inputmethod, | ||
1253 | 132 | // mir_window_type_satellite, | ||
1254 | 133 | mir_window_type_tip | ||
1255 | 134 | // mir_window_types | ||
1256 | 135 | )); | ||
1257 | 136 | |||
1258 | 137 | using DragWindow = DragActiveWindow; | ||
1259 | 138 | |||
1260 | 139 | TEST_F(DragWindow, can_drag_satellite) | ||
1261 | 140 | { | ||
1262 | 141 | create_window_of_type(mir_window_type_satellite); | ||
1263 | 142 | |||
1264 | 143 | Displacement const movement{10, 10}; | ||
1265 | 144 | auto const initial_position = window.top_left(); | ||
1266 | 145 | auto const expected_position = initial_position + movement; | ||
1267 | 146 | |||
1268 | 147 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
1269 | 148 | |||
1270 | 149 | window_manager_tools.drag_window(window, movement); | ||
1271 | 150 | |||
1272 | 151 | EXPECT_THAT(window.top_left(), Eq(expected_position)) | ||
1273 | 152 | << "Type: " << GetParam(); | ||
1274 | 153 | } | ||
1275 | 0 | \ No newline at end of file | 154 | \ No newline at end of file |
1276 | 1 | 155 | ||
1277 | === added file 'tests/miral/drag_and_drop.cpp' | |||
1278 | --- tests/miral/drag_and_drop.cpp 1970-01-01 00:00:00 +0000 | |||
1279 | +++ tests/miral/drag_and_drop.cpp 2017-08-24 15:19:58 +0000 | |||
1280 | @@ -0,0 +1,656 @@ | |||
1281 | 1 | /* | ||
1282 | 2 | * Copyright © 2017 Canonical Ltd. | ||
1283 | 3 | * | ||
1284 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1285 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
1286 | 6 | * published by the Free Software Foundation. | ||
1287 | 7 | * | ||
1288 | 8 | * This program is distributed in the hope that it will be useful, | ||
1289 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1290 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1291 | 11 | * GNU General Public License for more details. | ||
1292 | 12 | * | ||
1293 | 13 | * You should have received a copy of the GNU General Public License | ||
1294 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1295 | 15 | * | ||
1296 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
1297 | 17 | */ | ||
1298 | 18 | |||
1299 | 19 | #include <miral/window_management_policy_addendum2.h> | ||
1300 | 20 | |||
1301 | 21 | #include <mir/client/blob.h> | ||
1302 | 22 | #include <mir/client/cookie.h> | ||
1303 | 23 | #include <mir/client/surface.h> | ||
1304 | 24 | #include <mir/client/window.h> | ||
1305 | 25 | #include <mir/client/window_spec.h> | ||
1306 | 26 | #include <mir_toolkit/mir_buffer_stream.h> | ||
1307 | 27 | #include <mir_toolkit/extensions/drag_and_drop.h> | ||
1308 | 28 | |||
1309 | 29 | #include <mir/geometry/displacement.h> | ||
1310 | 30 | #include <mir/input/input_device_info.h> | ||
1311 | 31 | #include <mir/input/device_capability.h> | ||
1312 | 32 | #include <mir/shell/shell.h> | ||
1313 | 33 | |||
1314 | 34 | #include "test_server.h" | ||
1315 | 35 | #include <mir_test_framework/fake_input_device.h> | ||
1316 | 36 | #include <mir_test_framework/stub_server_platform_factory.h> | ||
1317 | 37 | #include <mir/test/event_factory.h> | ||
1318 | 38 | #include <mir/test/signal.h> | ||
1319 | 39 | |||
1320 | 40 | #include <gmock/gmock.h> | ||
1321 | 41 | #include <gtest/gtest.h> | ||
1322 | 42 | |||
1323 | 43 | #include <linux/input.h> | ||
1324 | 44 | #include <uuid/uuid.h> | ||
1325 | 45 | |||
1326 | 46 | #include <boost/throw_exception.hpp> | ||
1327 | 47 | #include <atomic> | ||
1328 | 48 | |||
1329 | 49 | using namespace std::chrono_literals; | ||
1330 | 50 | using namespace mir::client; | ||
1331 | 51 | using namespace mir::geometry; | ||
1332 | 52 | using namespace testing; | ||
1333 | 53 | using mir::test::Signal; | ||
1334 | 54 | |||
1335 | 55 | namespace | ||
1336 | 56 | { | ||
1337 | 57 | struct MouseMoverAndFaker | ||
1338 | 58 | { | ||
1339 | 59 | void start_dragging_mouse() | ||
1340 | 60 | { | ||
1341 | 61 | using namespace mir::input::synthesis; | ||
1342 | 62 | fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT)); | ||
1343 | 63 | } | ||
1344 | 64 | |||
1345 | 65 | void move_mouse(Displacement const& displacement) | ||
1346 | 66 | { | ||
1347 | 67 | using mir::input::synthesis::a_pointer_event; | ||
1348 | 68 | fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int())); | ||
1349 | 69 | } | ||
1350 | 70 | |||
1351 | 71 | void release_mouse() | ||
1352 | 72 | { | ||
1353 | 73 | using namespace mir::input::synthesis; | ||
1354 | 74 | fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT)); | ||
1355 | 75 | } | ||
1356 | 76 | |||
1357 | 77 | private: | ||
1358 | 78 | std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{ | ||
1359 | 79 | mir_test_framework::add_fake_input_device( | ||
1360 | 80 | mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})}; | ||
1361 | 81 | }; | ||
1362 | 82 | |||
1363 | 83 | Rectangle const screen_geometry{{0,0}, {800,600}}; | ||
1364 | 84 | auto const receive_event_timeout = 1s; //90s; | ||
1365 | 85 | |||
1366 | 86 | struct ConnectedClientWithAWindow : miral::TestServer | ||
1367 | 87 | { | ||
1368 | 88 | Connection connection; | ||
1369 | 89 | Surface surface; | ||
1370 | 90 | Window window; | ||
1371 | 91 | |||
1372 | 92 | void SetUp() override | ||
1373 | 93 | { | ||
1374 | 94 | miral::TestServer::SetUp(); | ||
1375 | 95 | connection = connect_client(__func__); | ||
1376 | 96 | auto const width = surface_size.width.as_int(); | ||
1377 | 97 | auto const height = surface_size.height.as_int(); | ||
1378 | 98 | surface = Surface{mir_connection_create_render_surface_sync(connection, width, height)}; | ||
1379 | 99 | window = WindowSpec::for_normal_window(connection, width, height) | ||
1380 | 100 | .set_name("ConnectedClientWithAWindow") | ||
1381 | 101 | .add_surface(surface, width, height, 0, 0) | ||
1382 | 102 | .create_window(); | ||
1383 | 103 | } | ||
1384 | 104 | |||
1385 | 105 | void TearDown() override | ||
1386 | 106 | { | ||
1387 | 107 | window.reset(); | ||
1388 | 108 | surface.reset(); | ||
1389 | 109 | connection.reset(); | ||
1390 | 110 | miral::TestServer::TearDown(); | ||
1391 | 111 | } | ||
1392 | 112 | |||
1393 | 113 | mir::geometry::Size const surface_size {640, 480}; | ||
1394 | 114 | }; | ||
1395 | 115 | |||
1396 | 116 | struct DragAndDrop : ConnectedClientWithAWindow, | ||
1397 | 117 | MouseMoverAndFaker | ||
1398 | 118 | { | ||
1399 | 119 | MirDragAndDropV1 const* dnd = nullptr; | ||
1400 | 120 | |||
1401 | 121 | void SetUp() override | ||
1402 | 122 | { | ||
1403 | 123 | mir_test_framework::set_next_display_rects(std::unique_ptr<std::vector<Rectangle>>(new std::vector<Rectangle>({screen_geometry}))); | ||
1404 | 124 | |||
1405 | 125 | ConnectedClientWithAWindow::SetUp(); | ||
1406 | 126 | dnd = mir_drag_and_drop_v1(connection); | ||
1407 | 127 | mir_window_set_event_handler(window, &window_event_handler, this); | ||
1408 | 128 | if (dnd) dnd->set_start_drag_and_drop_callback(window, &window_dnd_start_handler, this); | ||
1409 | 129 | |||
1410 | 130 | create_target_window(); | ||
1411 | 131 | |||
1412 | 132 | paint_window(surface, window); | ||
1413 | 133 | |||
1414 | 134 | center_mouse(); | ||
1415 | 135 | } | ||
1416 | 136 | |||
1417 | 137 | void TearDown() override | ||
1418 | 138 | { | ||
1419 | 139 | reset_window_event_handler(target_window); | ||
1420 | 140 | reset_window_event_handler(window); | ||
1421 | 141 | target_window.reset(); | ||
1422 | 142 | target_surface.reset(); | ||
1423 | 143 | another_connection.reset(); | ||
1424 | 144 | ConnectedClientWithAWindow::TearDown(); | ||
1425 | 145 | } | ||
1426 | 146 | |||
1427 | 147 | auto user_initiates_drag() -> Cookie; | ||
1428 | 148 | auto client_requests_drag(Cookie const& cookie) -> Blob; | ||
1429 | 149 | auto handle_from_mouse_move() -> Blob; | ||
1430 | 150 | auto handle_from_mouse_leave() -> Blob; | ||
1431 | 151 | auto handle_from_mouse_enter() -> Blob; | ||
1432 | 152 | auto handle_from_mouse_release() -> Blob; | ||
1433 | 153 | auto count_of_handles_when_moving_mouse() -> int; | ||
1434 | 154 | |||
1435 | 155 | private: | ||
1436 | 156 | auto build_window_manager_policy(miral::WindowManagerTools const& tools) -> std::unique_ptr<TestWindowManagerPolicy> override; | ||
1437 | 157 | void center_mouse(); | ||
1438 | 158 | void paint_window(MirRenderSurface* s, MirWindow* w); | ||
1439 | 159 | void set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler); | ||
1440 | 160 | void set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler); | ||
1441 | 161 | void reset_window_event_handler(MirWindow* window); | ||
1442 | 162 | |||
1443 | 163 | void create_target_window() | ||
1444 | 164 | { | ||
1445 | 165 | another_connection = connect_client("another_connection"); | ||
1446 | 166 | auto const height = screen_geometry.size.height.as_int(); | ||
1447 | 167 | auto const width = screen_geometry.size.width.as_int(); | ||
1448 | 168 | target_surface = Surface{mir_connection_create_render_surface_sync(another_connection, width,height)}; | ||
1449 | 169 | target_window = WindowSpec::for_normal_window(another_connection, width, height) | ||
1450 | 170 | .set_name("target_window") | ||
1451 | 171 | .add_surface(target_surface, width, height, 0, 0) | ||
1452 | 172 | .set_event_handler(&window_event_handler, this) | ||
1453 | 173 | .create_window(); | ||
1454 | 174 | |||
1455 | 175 | paint_window(target_surface, target_window); | ||
1456 | 176 | } | ||
1457 | 177 | |||
1458 | 178 | void invoke_window_event_handler(MirWindow* window, MirEvent const* event) | ||
1459 | 179 | { | ||
1460 | 180 | std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex}; | ||
1461 | 181 | if (window == this->window) window_event_handler_(event); | ||
1462 | 182 | if (window == target_window) target_window_event_handler_(event); | ||
1463 | 183 | } | ||
1464 | 184 | |||
1465 | 185 | void invoke_window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event) | ||
1466 | 186 | { | ||
1467 | 187 | std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex}; | ||
1468 | 188 | if (window == this->window) window_dnd_start_(event); | ||
1469 | 189 | } | ||
1470 | 190 | |||
1471 | 191 | std::mutex window_event_handler_mutex; | ||
1472 | 192 | std::function<void(MirDragAndDropEvent const* event)> window_dnd_start_ = [](MirDragAndDropEvent const*) {}; | ||
1473 | 193 | std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {}; | ||
1474 | 194 | std::function<void(MirEvent const* event)> target_window_event_handler_ = [](MirEvent const*) {}; | ||
1475 | 195 | |||
1476 | 196 | static void window_event_handler(MirWindow* window, MirEvent const* event, void* context); | ||
1477 | 197 | static void window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context); | ||
1478 | 198 | |||
1479 | 199 | Connection another_connection; | ||
1480 | 200 | Surface target_surface; | ||
1481 | 201 | Window target_window; | ||
1482 | 202 | }; | ||
1483 | 203 | |||
1484 | 204 | void DragAndDrop::set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler) | ||
1485 | 205 | { | ||
1486 | 206 | std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex}; | ||
1487 | 207 | if (window == this->window) window_event_handler_ = handler; | ||
1488 | 208 | if (window == target_window) target_window_event_handler_ = handler; | ||
1489 | 209 | } | ||
1490 | 210 | |||
1491 | 211 | void DragAndDrop::set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler) | ||
1492 | 212 | { | ||
1493 | 213 | std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex}; | ||
1494 | 214 | if (window == this->window) window_dnd_start_ = handler; | ||
1495 | 215 | } | ||
1496 | 216 | |||
1497 | 217 | |||
1498 | 218 | void DragAndDrop::reset_window_event_handler(MirWindow* window) | ||
1499 | 219 | { | ||
1500 | 220 | if (window == this->window) window_event_handler_ = [](MirEvent const*) {}; | ||
1501 | 221 | if (window == target_window) target_window_event_handler_ = [](MirEvent const*) {}; | ||
1502 | 222 | } | ||
1503 | 223 | |||
1504 | 224 | void DragAndDrop::paint_window(MirRenderSurface* s, MirWindow* w) | ||
1505 | 225 | { | ||
1506 | 226 | Signal have_focus; | ||
1507 | 227 | |||
1508 | 228 | set_window_event_handler(w, [&](MirEvent const* event) | ||
1509 | 229 | { | ||
1510 | 230 | if (mir_event_get_type(event) != mir_event_type_window) | ||
1511 | 231 | return; | ||
1512 | 232 | |||
1513 | 233 | auto const window_event = mir_event_get_window_event(event); | ||
1514 | 234 | if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus) | ||
1515 | 235 | return; | ||
1516 | 236 | |||
1517 | 237 | if (mir_window_event_get_attribute_value(window_event)) | ||
1518 | 238 | have_focus.raise(); | ||
1519 | 239 | }); | ||
1520 | 240 | |||
1521 | 241 | int width; | ||
1522 | 242 | int height; | ||
1523 | 243 | mir_render_surface_get_size(s, &width, &height); | ||
1524 | 244 | mir_buffer_stream_swap_buffers_sync( | ||
1525 | 245 | mir_render_surface_get_buffer_stream(s, width, height, mir_pixel_format_argb_8888)); | ||
1526 | 246 | |||
1527 | 247 | EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true)); | ||
1528 | 248 | |||
1529 | 249 | reset_window_event_handler(w); | ||
1530 | 250 | } | ||
1531 | 251 | |||
1532 | 252 | void DragAndDrop::center_mouse() | ||
1533 | 253 | { | ||
1534 | 254 | Signal have_mouseover; | ||
1535 | 255 | |||
1536 | 256 | set_window_event_handler(window, [&](MirEvent const* event) | ||
1537 | 257 | { | ||
1538 | 258 | if (mir_event_get_type(event) != mir_event_type_input) | ||
1539 | 259 | return; | ||
1540 | 260 | |||
1541 | 261 | auto const input_event = mir_event_get_input_event(event); | ||
1542 | 262 | |||
1543 | 263 | if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) | ||
1544 | 264 | return; | ||
1545 | 265 | |||
1546 | 266 | auto const pointer_event = mir_input_event_get_pointer_event(input_event); | ||
1547 | 267 | |||
1548 | 268 | if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter) | ||
1549 | 269 | return; | ||
1550 | 270 | |||
1551 | 271 | have_mouseover.raise(); | ||
1552 | 272 | }); | ||
1553 | 273 | |||
1554 | 274 | move_mouse(0.5 * as_displacement(screen_geometry.size)); | ||
1555 | 275 | |||
1556 | 276 | // We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20). | ||
1557 | 277 | // But it isn't essential for the test and we've probably waited long enough | ||
1558 | 278 | // for the mouse-down needed by the test to reach the window. | ||
1559 | 279 | // EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true)); | ||
1560 | 280 | have_mouseover.wait_for(receive_event_timeout); | ||
1561 | 281 | |||
1562 | 282 | reset_window_event_handler(window); | ||
1563 | 283 | } | ||
1564 | 284 | |||
1565 | 285 | void DragAndDrop::window_event_handler(MirWindow* window, MirEvent const* event, void* context) | ||
1566 | 286 | { | ||
1567 | 287 | static_cast<DragAndDrop*>(context)->invoke_window_event_handler(window, event); | ||
1568 | 288 | } | ||
1569 | 289 | |||
1570 | 290 | void DragAndDrop::window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context) | ||
1571 | 291 | { | ||
1572 | 292 | static_cast<DragAndDrop*>(context)->invoke_window_dnd_start_handler(window, event); | ||
1573 | 293 | } | ||
1574 | 294 | |||
1575 | 295 | |||
1576 | 296 | auto DragAndDrop::user_initiates_drag() -> Cookie | ||
1577 | 297 | { | ||
1578 | 298 | Cookie cookie; | ||
1579 | 299 | Signal have_cookie; | ||
1580 | 300 | |||
1581 | 301 | set_window_event_handler(window, [&](MirEvent const* event) | ||
1582 | 302 | { | ||
1583 | 303 | if (mir_event_get_type(event) != mir_event_type_input) | ||
1584 | 304 | return; | ||
1585 | 305 | |||
1586 | 306 | auto const input_event = mir_event_get_input_event(event); | ||
1587 | 307 | |||
1588 | 308 | if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) | ||
1589 | 309 | return; | ||
1590 | 310 | |||
1591 | 311 | auto const pointer_event = mir_input_event_get_pointer_event(input_event); | ||
1592 | 312 | |||
1593 | 313 | if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down) | ||
1594 | 314 | return; | ||
1595 | 315 | |||
1596 | 316 | cookie = Cookie{mir_input_event_get_cookie(input_event)}; | ||
1597 | 317 | have_cookie.raise(); | ||
1598 | 318 | }); | ||
1599 | 319 | |||
1600 | 320 | start_dragging_mouse(); | ||
1601 | 321 | |||
1602 | 322 | EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true)); | ||
1603 | 323 | |||
1604 | 324 | reset_window_event_handler(window); | ||
1605 | 325 | return cookie; | ||
1606 | 326 | } | ||
1607 | 327 | |||
1608 | 328 | auto DragAndDrop::client_requests_drag(Cookie const& cookie) -> Blob | ||
1609 | 329 | { | ||
1610 | 330 | Blob blob; | ||
1611 | 331 | Signal initiated; | ||
1612 | 332 | |||
1613 | 333 | set_window_dnd_start_handler(window, [&](MirDragAndDropEvent const* event) | ||
1614 | 334 | { | ||
1615 | 335 | if (dnd) | ||
1616 | 336 | blob.reset(dnd->start_drag_and_drop(event)); | ||
1617 | 337 | |||
1618 | 338 | if (blob) | ||
1619 | 339 | initiated.raise(); | ||
1620 | 340 | }); | ||
1621 | 341 | |||
1622 | 342 | EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension"; | ||
1623 | 343 | |||
1624 | 344 | if (dnd) | ||
1625 | 345 | dnd->request_drag_and_drop(window, cookie); | ||
1626 | 346 | |||
1627 | 347 | EXPECT_TRUE(initiated.wait_for(receive_event_timeout)); | ||
1628 | 348 | |||
1629 | 349 | reset_window_event_handler(window); | ||
1630 | 350 | return blob; | ||
1631 | 351 | } | ||
1632 | 352 | |||
1633 | 353 | auto DragAndDrop::handle_from_mouse_move() -> Blob | ||
1634 | 354 | { | ||
1635 | 355 | Blob blob; | ||
1636 | 356 | Signal have_blob; | ||
1637 | 357 | |||
1638 | 358 | set_window_event_handler(window, [&](MirEvent const* event) | ||
1639 | 359 | { | ||
1640 | 360 | if (mir_event_get_type(event) != mir_event_type_input) | ||
1641 | 361 | return; | ||
1642 | 362 | |||
1643 | 363 | auto const input_event = mir_event_get_input_event(event); | ||
1644 | 364 | |||
1645 | 365 | if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) | ||
1646 | 366 | return; | ||
1647 | 367 | |||
1648 | 368 | auto const pointer_event = mir_input_event_get_pointer_event(input_event); | ||
1649 | 369 | |||
1650 | 370 | EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension"; | ||
1651 | 371 | |||
1652 | 372 | if (dnd) | ||
1653 | 373 | blob.reset(dnd->pointer_drag_and_drop(pointer_event)); | ||
1654 | 374 | |||
1655 | 375 | if (blob) | ||
1656 | 376 | have_blob.raise(); | ||
1657 | 377 | }); | ||
1658 | 378 | |||
1659 | 379 | move_mouse({1,1}); | ||
1660 | 380 | |||
1661 | 381 | EXPECT_TRUE(have_blob.wait_for(receive_event_timeout)); | ||
1662 | 382 | |||
1663 | 383 | reset_window_event_handler(window); | ||
1664 | 384 | return blob; | ||
1665 | 385 | } | ||
1666 | 386 | |||
1667 | 387 | auto DragAndDrop::handle_from_mouse_leave() -> Blob | ||
1668 | 388 | { | ||
1669 | 389 | Blob blob; | ||
1670 | 390 | Signal have_blob; | ||
1671 | 391 | |||
1672 | 392 | set_window_event_handler(window, [&](MirEvent const* event) | ||
1673 | 393 | { | ||
1674 | 394 | if (mir_event_get_type(event) != mir_event_type_input) | ||
1675 | 395 | return; | ||
1676 | 396 | |||
1677 | 397 | auto const input_event = mir_event_get_input_event(event); | ||
1678 | 398 | |||
1679 | 399 | if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) | ||
1680 | 400 | return; | ||
1681 | 401 | |||
1682 | 402 | auto const pointer_event = mir_input_event_get_pointer_event(input_event); | ||
1683 | 403 | |||
1684 | 404 | if (mir_pointer_event_action(pointer_event) != mir_pointer_action_leave) | ||
1685 | 405 | return; | ||
1686 | 406 | |||
1687 | 407 | EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension"; | ||
1688 | 408 | |||
1689 | 409 | if (dnd) | ||
1690 | 410 | blob.reset(dnd->pointer_drag_and_drop(pointer_event)); | ||
1691 | 411 | |||
1692 | 412 | if (blob) | ||
1693 | 413 | have_blob.raise(); | ||
1694 | 414 | }); | ||
1695 | 415 | |||
1696 | 416 | move_mouse({1,1}); | ||
1697 | 417 | move_mouse(0.5 * as_displacement(surface_size)); | ||
1698 | 418 | |||
1699 | 419 | EXPECT_TRUE(have_blob.wait_for(receive_event_timeout)); | ||
1700 | 420 | |||
1701 | 421 | reset_window_event_handler(window); | ||
1702 | 422 | return blob; | ||
1703 | 423 | } | ||
1704 | 424 | |||
1705 | 425 | auto DragAndDrop::handle_from_mouse_enter() -> Blob | ||
1706 | 426 | { | ||
1707 | 427 | Blob blob; | ||
1708 | 428 | Signal have_blob; | ||
1709 | 429 | |||
1710 | 430 | set_window_event_handler(target_window, [&](MirEvent const* event) | ||
1711 | 431 | { | ||
1712 | 432 | if (mir_event_get_type(event) != mir_event_type_input) | ||
1713 | 433 | return; | ||
1714 | 434 | |||
1715 | 435 | auto const input_event = mir_event_get_input_event(event); | ||
1716 | 436 | |||
1717 | 437 | if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) | ||
1718 | 438 | return; | ||
1719 | 439 | |||
1720 | 440 | auto const pointer_event = mir_input_event_get_pointer_event(input_event); | ||
1721 | 441 | |||
1722 | 442 | if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter) | ||
1723 | 443 | return; | ||
1724 | 444 | |||
1725 | 445 | EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension"; | ||
1726 | 446 | |||
1727 | 447 | if (dnd) | ||
1728 | 448 | blob.reset(dnd->pointer_drag_and_drop(pointer_event)); | ||
1729 | 449 | |||
1730 | 450 | if (blob) | ||
1731 | 451 | have_blob.raise(); | ||
1732 | 452 | }); | ||
1733 | 453 | |||
1734 | 454 | move_mouse({1,1}); | ||
1735 | 455 | move_mouse(0.5 * as_displacement(surface_size)); | ||
1736 | 456 | |||
1737 | 457 | EXPECT_TRUE(have_blob.wait_for(receive_event_timeout)); | ||
1738 | 458 | |||
1739 | 459 | reset_window_event_handler(target_window); | ||
1740 | 460 | return blob; | ||
1741 | 461 | } | ||
1742 | 462 | |||
1743 | 463 | auto DragAndDrop::handle_from_mouse_release() -> Blob | ||
1744 | 464 | { | ||
1745 | 465 | Blob blob; | ||
1746 | 466 | Signal have_blob; | ||
1747 | 467 | |||
1748 | 468 | set_window_event_handler(target_window, [&](MirEvent const* event) | ||
1749 | 469 | { | ||
1750 | 470 | if (mir_event_get_type(event) != mir_event_type_input) | ||
1751 | 471 | return; | ||
1752 | 472 | |||
1753 | 473 | auto const input_event = mir_event_get_input_event(event); | ||
1754 | 474 | |||
1755 | 475 | if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) | ||
1756 | 476 | return; | ||
1757 | 477 | |||
1758 | 478 | auto const pointer_event = mir_input_event_get_pointer_event(input_event); | ||
1759 | 479 | |||
1760 | 480 | if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_up) | ||
1761 | 481 | return; | ||
1762 | 482 | |||
1763 | 483 | EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension"; | ||
1764 | 484 | |||
1765 | 485 | if (dnd) | ||
1766 | 486 | blob.reset(dnd->pointer_drag_and_drop(pointer_event)); | ||
1767 | 487 | |||
1768 | 488 | if (blob) | ||
1769 | 489 | have_blob.raise(); | ||
1770 | 490 | }); | ||
1771 | 491 | |||
1772 | 492 | move_mouse({1,1}); | ||
1773 | 493 | move_mouse(0.5 * as_displacement(surface_size)); | ||
1774 | 494 | release_mouse(); | ||
1775 | 495 | |||
1776 | 496 | EXPECT_TRUE(have_blob.wait_for(receive_event_timeout)); | ||
1777 | 497 | |||
1778 | 498 | reset_window_event_handler(target_window); | ||
1779 | 499 | return blob; | ||
1780 | 500 | } | ||
1781 | 501 | |||
1782 | 502 | auto DragAndDrop::count_of_handles_when_moving_mouse() -> int | ||
1783 | 503 | { | ||
1784 | 504 | Signal have_3_events; | ||
1785 | 505 | std::atomic<int> events{0}; | ||
1786 | 506 | std::atomic<int> handles{0}; | ||
1787 | 507 | |||
1788 | 508 | auto counter = [&](MirEvent const* event) | ||
1789 | 509 | { | ||
1790 | 510 | if (mir_event_get_type(event) != mir_event_type_input) | ||
1791 | 511 | return; | ||
1792 | 512 | |||
1793 | 513 | auto const input_event = mir_event_get_input_event(event); | ||
1794 | 514 | |||
1795 | 515 | if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) | ||
1796 | 516 | return; | ||
1797 | 517 | |||
1798 | 518 | auto const pointer_event = mir_input_event_get_pointer_event(input_event); | ||
1799 | 519 | |||
1800 | 520 | EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension"; | ||
1801 | 521 | |||
1802 | 522 | Blob blob; | ||
1803 | 523 | if (dnd) | ||
1804 | 524 | blob.reset(dnd->pointer_drag_and_drop(pointer_event)); | ||
1805 | 525 | |||
1806 | 526 | if (blob) | ||
1807 | 527 | handles.fetch_add(1); | ||
1808 | 528 | |||
1809 | 529 | if (events.fetch_add(1) == 2) | ||
1810 | 530 | have_3_events.raise(); | ||
1811 | 531 | }; | ||
1812 | 532 | |||
1813 | 533 | set_window_event_handler(window, counter); | ||
1814 | 534 | set_window_event_handler(target_window, counter); | ||
1815 | 535 | |||
1816 | 536 | start_dragging_mouse(); | ||
1817 | 537 | move_mouse({1,1}); | ||
1818 | 538 | release_mouse(); | ||
1819 | 539 | |||
1820 | 540 | EXPECT_TRUE(have_3_events.wait_for(receive_event_timeout)); | ||
1821 | 541 | |||
1822 | 542 | reset_window_event_handler(window); | ||
1823 | 543 | reset_window_event_handler(target_window); | ||
1824 | 544 | return handles; | ||
1825 | 545 | } | ||
1826 | 546 | |||
1827 | 547 | auto DragAndDrop::build_window_manager_policy(miral::WindowManagerTools const& tools) -> std::unique_ptr<TestWindowManagerPolicy> | ||
1828 | 548 | { | ||
1829 | 549 | struct DnDWindowManagerPolicy : miral::TestServer::TestWindowManagerPolicy, miral::WindowManagementPolicyAddendum2 | ||
1830 | 550 | { | ||
1831 | 551 | using miral::TestServer::TestWindowManagerPolicy::TestWindowManagerPolicy; | ||
1832 | 552 | |||
1833 | 553 | void handle_request_drag_and_drop(miral::WindowInfo& window_info) override | ||
1834 | 554 | { | ||
1835 | 555 | uuid_t uuid; | ||
1836 | 556 | uuid_generate(uuid); | ||
1837 | 557 | std::vector<uint8_t> const handle{std::begin(uuid), std::end(uuid)}; | ||
1838 | 558 | |||
1839 | 559 | tools.start_drag_and_drop(window_info, handle); | ||
1840 | 560 | } | ||
1841 | 561 | |||
1842 | 562 | void handle_request_move(miral::WindowInfo&, MirInputEvent const*) override {} | ||
1843 | 563 | }; | ||
1844 | 564 | |||
1845 | 565 | return std::make_unique<DnDWindowManagerPolicy>(tools, *this); | ||
1846 | 566 | } | ||
1847 | 567 | |||
1848 | 568 | MATCHER_P(BlobContentEq, p, "") | ||
1849 | 569 | { | ||
1850 | 570 | if (!arg || !p) | ||
1851 | 571 | return false; | ||
1852 | 572 | if (mir_blob_size(arg) != mir_blob_size(p)) | ||
1853 | 573 | return false; | ||
1854 | 574 | return !memcmp(mir_blob_data(arg), mir_blob_data(p), mir_blob_size(p)); | ||
1855 | 575 | } | ||
1856 | 576 | } | ||
1857 | 577 | |||
1858 | 578 | TEST_F(DragAndDrop, when_user_initiates_drag_client_receives_cookie) | ||
1859 | 579 | { | ||
1860 | 580 | auto const cookie = user_initiates_drag(); | ||
1861 | 581 | |||
1862 | 582 | EXPECT_THAT(cookie, Ne(nullptr)); | ||
1863 | 583 | } | ||
1864 | 584 | |||
1865 | 585 | TEST_F(DragAndDrop, when_client_requests_drags_it_receives_handle) | ||
1866 | 586 | { | ||
1867 | 587 | auto const cookie = user_initiates_drag(); | ||
1868 | 588 | ASSERT_THAT(cookie, Ne(nullptr)); | ||
1869 | 589 | |||
1870 | 590 | auto const handle = client_requests_drag(cookie); | ||
1871 | 591 | |||
1872 | 592 | EXPECT_THAT(handle, Ne(nullptr)); | ||
1873 | 593 | } | ||
1874 | 594 | |||
1875 | 595 | TEST_F(DragAndDrop, during_drag_when_user_moves_mouse_client_receives_handle) | ||
1876 | 596 | { | ||
1877 | 597 | auto const cookie = user_initiates_drag(); | ||
1878 | 598 | ASSERT_THAT(cookie, Ne(nullptr)); | ||
1879 | 599 | auto const handle_from_request = client_requests_drag(cookie); | ||
1880 | 600 | |||
1881 | 601 | auto const handle = handle_from_mouse_move(); | ||
1882 | 602 | |||
1883 | 603 | EXPECT_THAT(handle, Ne(nullptr)); | ||
1884 | 604 | EXPECT_THAT(handle, BlobContentEq(handle_from_request)); | ||
1885 | 605 | } | ||
1886 | 606 | |||
1887 | 607 | TEST_F(DragAndDrop, when_drag_moves_from_window_leave_event_contains_handle) | ||
1888 | 608 | { | ||
1889 | 609 | auto const cookie = user_initiates_drag(); | ||
1890 | 610 | ASSERT_THAT(cookie, Ne(nullptr)); | ||
1891 | 611 | auto const handle_from_request = client_requests_drag(cookie); | ||
1892 | 612 | |||
1893 | 613 | auto const handle = handle_from_mouse_leave(); | ||
1894 | 614 | |||
1895 | 615 | EXPECT_THAT(handle, Ne(nullptr)); | ||
1896 | 616 | EXPECT_THAT(handle, BlobContentEq(handle_from_request)); | ||
1897 | 617 | } | ||
1898 | 618 | |||
1899 | 619 | TEST_F(DragAndDrop, when_drag_enters_target_window_enter_event_contains_handle) | ||
1900 | 620 | { | ||
1901 | 621 | auto const cookie = user_initiates_drag(); | ||
1902 | 622 | ASSERT_THAT(cookie, Ne(nullptr)); | ||
1903 | 623 | auto const handle_from_request = client_requests_drag(cookie); | ||
1904 | 624 | |||
1905 | 625 | auto const handle = handle_from_mouse_enter(); | ||
1906 | 626 | |||
1907 | 627 | EXPECT_THAT(handle, Ne(nullptr)); | ||
1908 | 628 | EXPECT_THAT(handle, BlobContentEq(handle_from_request)); | ||
1909 | 629 | } | ||
1910 | 630 | |||
1911 | 631 | TEST_F(DragAndDrop, when_drag_releases_target_window_release_event_contains_handle) | ||
1912 | 632 | { | ||
1913 | 633 | auto const cookie = user_initiates_drag(); | ||
1914 | 634 | ASSERT_THAT(cookie, Ne(nullptr)); | ||
1915 | 635 | auto const handle_from_request = client_requests_drag(cookie); | ||
1916 | 636 | |||
1917 | 637 | auto const handle = handle_from_mouse_release(); | ||
1918 | 638 | |||
1919 | 639 | EXPECT_THAT(handle, Ne(nullptr)); | ||
1920 | 640 | EXPECT_THAT(handle, BlobContentEq(handle_from_request)); | ||
1921 | 641 | } | ||
1922 | 642 | |||
1923 | 643 | TEST_F(DragAndDrop, after_drag_finishes_pointer_events_no_longer_contain_handle) | ||
1924 | 644 | { | ||
1925 | 645 | auto const cookie = user_initiates_drag(); | ||
1926 | 646 | ASSERT_THAT(cookie, Ne(nullptr)); | ||
1927 | 647 | client_requests_drag(cookie); | ||
1928 | 648 | handle_from_mouse_release(); | ||
1929 | 649 | |||
1930 | 650 | invoke_tools([](miral::WindowManagerTools& tools) { tools.end_drag_and_drop(); }); | ||
1931 | 651 | |||
1932 | 652 | // Allow old pointer events to drain | ||
1933 | 653 | std::this_thread::sleep_for(2ms); | ||
1934 | 654 | |||
1935 | 655 | EXPECT_THAT(count_of_handles_when_moving_mouse(), Eq(0)); | ||
1936 | 656 | } | ||
1937 | 0 | 657 | ||
1938 | === added file 'tests/miral/modify_window_state.cpp' | |||
1939 | --- tests/miral/modify_window_state.cpp 1970-01-01 00:00:00 +0000 | |||
1940 | +++ tests/miral/modify_window_state.cpp 2017-08-24 15:19:58 +0000 | |||
1941 | @@ -0,0 +1,105 @@ | |||
1942 | 1 | /* | ||
1943 | 2 | * Copyright © 2016 Canonical Ltd. | ||
1944 | 3 | * | ||
1945 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1946 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
1947 | 6 | * published by the Free Software Foundation. | ||
1948 | 7 | * | ||
1949 | 8 | * This program is distributed in the hope that it will be useful, | ||
1950 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1951 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1952 | 11 | * GNU General Public License for more details. | ||
1953 | 12 | * | ||
1954 | 13 | * You should have received a copy of the GNU General Public License | ||
1955 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1956 | 15 | * | ||
1957 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
1958 | 17 | */ | ||
1959 | 18 | |||
1960 | 19 | #include "test_window_manager_tools.h" | ||
1961 | 20 | #include <mir/event_printer.h> | ||
1962 | 21 | |||
1963 | 22 | using namespace miral; | ||
1964 | 23 | using namespace testing; | ||
1965 | 24 | namespace mt = mir::test; | ||
1966 | 25 | using mir::operator<<; | ||
1967 | 26 | |||
1968 | 27 | namespace | ||
1969 | 28 | { | ||
1970 | 29 | X const display_left{0}; | ||
1971 | 30 | Y const display_top{0}; | ||
1972 | 31 | Width const display_width{640}; | ||
1973 | 32 | Height const display_height{480}; | ||
1974 | 33 | |||
1975 | 34 | Rectangle const display_area{{display_left, display_top}, | ||
1976 | 35 | {display_width, display_height}}; | ||
1977 | 36 | |||
1978 | 37 | struct ModifyWindowState : TestWindowManagerTools, WithParamInterface<MirWindowType> | ||
1979 | 38 | { | ||
1980 | 39 | Size const initial_parent_size{600, 400}; | ||
1981 | 40 | |||
1982 | 41 | Window window; | ||
1983 | 42 | |||
1984 | 43 | void SetUp() override | ||
1985 | 44 | { | ||
1986 | 45 | basic_window_manager.add_display_for_testing(display_area); | ||
1987 | 46 | basic_window_manager.add_session(session); | ||
1988 | 47 | } | ||
1989 | 48 | |||
1990 | 49 | void create_window_of_type(MirWindowType type) | ||
1991 | 50 | { | ||
1992 | 51 | mir::scene::SurfaceCreationParameters creation_parameters; | ||
1993 | 52 | creation_parameters.type = type; | ||
1994 | 53 | creation_parameters.size = initial_parent_size; | ||
1995 | 54 | |||
1996 | 55 | EXPECT_CALL(*window_manager_policy, advise_new_window(_)) | ||
1997 | 56 | .WillOnce( | ||
1998 | 57 | Invoke( | ||
1999 | 58 | [this](WindowInfo const& window_info) | ||
2000 | 59 | { window = window_info.window(); })); | ||
2001 | 60 | |||
2002 | 61 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
2003 | 62 | basic_window_manager.select_active_window(window); | ||
2004 | 63 | |||
2005 | 64 | // Clear the expectations used to capture parent & child | ||
2006 | 65 | Mock::VerifyAndClearExpectations(window_manager_policy); | ||
2007 | 66 | } | ||
2008 | 67 | }; | ||
2009 | 68 | |||
2010 | 69 | using ForNormalSurface = ModifyWindowState; | ||
2011 | 70 | |||
2012 | 71 | TEST_P(ForNormalSurface, state) | ||
2013 | 72 | { | ||
2014 | 73 | auto const original_state = mir_window_state_restored; | ||
2015 | 74 | auto const new_state = MirWindowState(GetParam()); | ||
2016 | 75 | auto const state_is_visible = (new_state != mir_window_state_minimized) && (new_state != mir_window_state_hidden); | ||
2017 | 76 | |||
2018 | 77 | create_window_of_type(mir_window_type_normal); | ||
2019 | 78 | auto const& info = window_manager_tools.info_for(window); | ||
2020 | 79 | |||
2021 | 80 | WindowSpecification mods; | ||
2022 | 81 | mods.state() = new_state; | ||
2023 | 82 | window_manager_tools.modify_window(window, mods); | ||
2024 | 83 | EXPECT_THAT(std::shared_ptr<mir::scene::Surface>(window)->state(), Eq(new_state)); | ||
2025 | 84 | EXPECT_THAT(info.state(), Eq(new_state)); | ||
2026 | 85 | EXPECT_THAT(info.is_visible(), Eq(state_is_visible)) << "State is " << new_state; | ||
2027 | 86 | |||
2028 | 87 | mods.state() = original_state; | ||
2029 | 88 | window_manager_tools.modify_window(window, mods); | ||
2030 | 89 | EXPECT_THAT(std::shared_ptr<mir::scene::Surface>(window)->state(), Eq(original_state)); | ||
2031 | 90 | EXPECT_THAT(info.state(), Eq(original_state)); | ||
2032 | 91 | EXPECT_TRUE(info.is_visible()); | ||
2033 | 92 | } | ||
2034 | 93 | } | ||
2035 | 94 | |||
2036 | 95 | INSTANTIATE_TEST_CASE_P(ModifyWindowState, ForNormalSurface, ::testing::Values( | ||
2037 | 96 | // mir_window_state_unknown, | ||
2038 | 97 | mir_window_state_restored, | ||
2039 | 98 | mir_window_state_minimized, | ||
2040 | 99 | mir_window_state_maximized, | ||
2041 | 100 | mir_window_state_vertmaximized, | ||
2042 | 101 | mir_window_state_fullscreen, | ||
2043 | 102 | mir_window_state_horizmaximized, | ||
2044 | 103 | mir_window_state_hidden | ||
2045 | 104 | // mir_window_states | ||
2046 | 105 | )); | ||
2047 | 0 | 106 | ||
2048 | === added file 'tests/miral/mru_window_list.cpp' | |||
2049 | --- tests/miral/mru_window_list.cpp 1970-01-01 00:00:00 +0000 | |||
2050 | +++ tests/miral/mru_window_list.cpp 2017-08-24 15:19:58 +0000 | |||
2051 | @@ -0,0 +1,193 @@ | |||
2052 | 1 | /* | ||
2053 | 2 | * Copyright © 2016 Canonical Ltd. | ||
2054 | 3 | * | ||
2055 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2056 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
2057 | 6 | * published by the Free Software Foundation. | ||
2058 | 7 | * | ||
2059 | 8 | * This program is distributed in the hope that it will be useful, | ||
2060 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2061 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2062 | 11 | * GNU General Public License for more details. | ||
2063 | 12 | * | ||
2064 | 13 | * You should have received a copy of the GNU General Public License | ||
2065 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2066 | 15 | * | ||
2067 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
2068 | 17 | */ | ||
2069 | 18 | |||
2070 | 19 | #include "mru_window_list.h" | ||
2071 | 20 | |||
2072 | 21 | #include <mir/test/doubles/stub_surface.h> | ||
2073 | 22 | #include <mir/test/doubles/stub_session.h> | ||
2074 | 23 | |||
2075 | 24 | #include <gtest/gtest.h> | ||
2076 | 25 | #include <gmock/gmock.h> | ||
2077 | 26 | |||
2078 | 27 | using namespace testing; | ||
2079 | 28 | |||
2080 | 29 | namespace | ||
2081 | 30 | { | ||
2082 | 31 | struct StubSurface : mir::test::doubles::StubSurface | ||
2083 | 32 | { | ||
2084 | 33 | bool visible() const override { return visible_; } | ||
2085 | 34 | |||
2086 | 35 | bool visible_ = true; | ||
2087 | 36 | }; | ||
2088 | 37 | |||
2089 | 38 | struct StubSession : mir::test::doubles::StubSession | ||
2090 | 39 | { | ||
2091 | 40 | StubSession(int number_of_surfaces) | ||
2092 | 41 | { | ||
2093 | 42 | for (auto i = 0; i != number_of_surfaces; ++i) | ||
2094 | 43 | surfaces.push_back(std::make_shared<StubSurface>()); | ||
2095 | 44 | } | ||
2096 | 45 | |||
2097 | 46 | std::shared_ptr<mir::scene::Surface> surface(mir::frontend::SurfaceId surface) const override | ||
2098 | 47 | { | ||
2099 | 48 | return surfaces.at(surface.as_value()); | ||
2100 | 49 | } | ||
2101 | 50 | |||
2102 | 51 | std::vector<std::shared_ptr<StubSurface>> surfaces; | ||
2103 | 52 | }; | ||
2104 | 53 | |||
2105 | 54 | MATCHER(IsNullWindow, std::string("is not null")) | ||
2106 | 55 | { | ||
2107 | 56 | return !arg; | ||
2108 | 57 | } | ||
2109 | 58 | } | ||
2110 | 59 | |||
2111 | 60 | struct MRUWindowList : testing::Test | ||
2112 | 61 | { | ||
2113 | 62 | static auto const window_a_id = 0; | ||
2114 | 63 | static auto const window_b_id = 1; | ||
2115 | 64 | |||
2116 | 65 | miral::MRUWindowList mru_list; | ||
2117 | 66 | |||
2118 | 67 | std::shared_ptr<StubSession> const stub_session{std::make_shared<StubSession>(3)}; | ||
2119 | 68 | miral::Application app{stub_session}; | ||
2120 | 69 | miral::Window window_a{app, stub_session->surface(mir::frontend::SurfaceId{window_a_id})}; | ||
2121 | 70 | miral::Window window_b{app, stub_session->surface(mir::frontend::SurfaceId{window_b_id})}; | ||
2122 | 71 | miral::Window window_c{app, stub_session->surface(mir::frontend::SurfaceId{2})}; | ||
2123 | 72 | |||
2124 | 73 | void hide_window(int window_id) | ||
2125 | 74 | { | ||
2126 | 75 | stub_session->surfaces[window_id]->visible_ = false; | ||
2127 | 76 | } | ||
2128 | 77 | |||
2129 | 78 | void show_window(int window_id) | ||
2130 | 79 | { | ||
2131 | 80 | stub_session->surfaces[window_id]->visible_ = true; | ||
2132 | 81 | } | ||
2133 | 82 | }; | ||
2134 | 83 | |||
2135 | 84 | TEST_F(MRUWindowList, when_created_is_empty) | ||
2136 | 85 | { | ||
2137 | 86 | EXPECT_THAT(mru_list.top(), IsNullWindow()); | ||
2138 | 87 | } | ||
2139 | 88 | |||
2140 | 89 | TEST_F(MRUWindowList, given_empty_list_when_a_window_pushed_that_window_is_top) | ||
2141 | 90 | { | ||
2142 | 91 | mru_list.push(window_a); | ||
2143 | 92 | EXPECT_THAT(mru_list.top(), Eq(window_a)); | ||
2144 | 93 | } | ||
2145 | 94 | |||
2146 | 95 | TEST_F(MRUWindowList, given_non_empty_list_when_a_window_pushed_that_window_is_top) | ||
2147 | 96 | { | ||
2148 | 97 | mru_list.push(window_a); | ||
2149 | 98 | mru_list.push(window_b); | ||
2150 | 99 | mru_list.push(window_c); | ||
2151 | 100 | EXPECT_THAT(mru_list.top(), Eq(window_c)); | ||
2152 | 101 | } | ||
2153 | 102 | |||
2154 | 103 | TEST_F(MRUWindowList, given_non_empty_list_when_top_window_is_erased_that_window_is_no_longer_on_top) | ||
2155 | 104 | { | ||
2156 | 105 | mru_list.push(window_a); | ||
2157 | 106 | mru_list.push(window_b); | ||
2158 | 107 | mru_list.push(window_c); | ||
2159 | 108 | mru_list.erase(window_c); | ||
2160 | 109 | EXPECT_THAT(mru_list.top(), Ne(window_c)); | ||
2161 | 110 | } | ||
2162 | 111 | |||
2163 | 112 | TEST_F(MRUWindowList, a_window_pushed_twice_is_not_enumerated_twice) | ||
2164 | 113 | { | ||
2165 | 114 | mru_list.push(window_a); | ||
2166 | 115 | mru_list.push(window_b); | ||
2167 | 116 | mru_list.push(window_a); | ||
2168 | 117 | |||
2169 | 118 | int count{0}; | ||
2170 | 119 | |||
2171 | 120 | mru_list.enumerate([&](miral::Window& window) | ||
2172 | 121 | { if (window == window_a) ++count; return true; }); | ||
2173 | 122 | |||
2174 | 123 | EXPECT_THAT(count, Eq(1)); | ||
2175 | 124 | } | ||
2176 | 125 | |||
2177 | 126 | TEST_F(MRUWindowList, after_multiple_pushes_windows_are_enumerated_in_mru_order) | ||
2178 | 127 | { | ||
2179 | 128 | mru_list.push(window_a); | ||
2180 | 129 | mru_list.push(window_b); | ||
2181 | 130 | mru_list.push(window_c); | ||
2182 | 131 | mru_list.push(window_a); | ||
2183 | 132 | mru_list.push(window_b); | ||
2184 | 133 | mru_list.push(window_a); | ||
2185 | 134 | |||
2186 | 135 | mru_list.push(window_c); | ||
2187 | 136 | mru_list.push(window_b); | ||
2188 | 137 | mru_list.push(window_a); | ||
2189 | 138 | |||
2190 | 139 | std::vector<miral::Window> as_enumerated; | ||
2191 | 140 | |||
2192 | 141 | mru_list.enumerate([&](miral::Window& window) | ||
2193 | 142 | { as_enumerated.push_back(window); return true; }); | ||
2194 | 143 | |||
2195 | 144 | EXPECT_THAT(as_enumerated, ElementsAre(window_a, window_b, window_c)); | ||
2196 | 145 | } | ||
2197 | 146 | |||
2198 | 147 | TEST_F(MRUWindowList, when_enumerator_returns_false_enumeration_is_short_circuited) | ||
2199 | 148 | { | ||
2200 | 149 | mru_list.push(window_a); | ||
2201 | 150 | mru_list.push(window_b); | ||
2202 | 151 | mru_list.push(window_c); | ||
2203 | 152 | |||
2204 | 153 | int count{0}; | ||
2205 | 154 | |||
2206 | 155 | mru_list.enumerate([&](miral::Window&) { ++count; return false; }); | ||
2207 | 156 | |||
2208 | 157 | EXPECT_THAT(count, Eq(1)); | ||
2209 | 158 | } | ||
2210 | 159 | |||
2211 | 160 | TEST_F(MRUWindowList, a_hidden_window_is_not_enumerated) | ||
2212 | 161 | { | ||
2213 | 162 | mru_list.push(window_a); | ||
2214 | 163 | mru_list.push(window_b); | ||
2215 | 164 | mru_list.push(window_c); | ||
2216 | 165 | |||
2217 | 166 | int count{0}; | ||
2218 | 167 | |||
2219 | 168 | hide_window(window_a_id); | ||
2220 | 169 | mru_list.enumerate([&](miral::Window& window) | ||
2221 | 170 | { if (window == window_a) ++count; return true; }); | ||
2222 | 171 | |||
2223 | 172 | EXPECT_THAT(count, Eq(0)); | ||
2224 | 173 | } | ||
2225 | 174 | |||
2226 | 175 | TEST_F(MRUWindowList, hiding_then_showing_windows_retains_order) | ||
2227 | 176 | { | ||
2228 | 177 | mru_list.push(window_a); | ||
2229 | 178 | mru_list.push(window_b); | ||
2230 | 179 | mru_list.push(window_c); | ||
2231 | 180 | |||
2232 | 181 | hide_window(window_a_id); | ||
2233 | 182 | hide_window(window_b_id); | ||
2234 | 183 | show_window(window_a_id); | ||
2235 | 184 | show_window(window_b_id); | ||
2236 | 185 | |||
2237 | 186 | std::vector<miral::Window> as_enumerated; | ||
2238 | 187 | |||
2239 | 188 | mru_list.enumerate([&](miral::Window& window) | ||
2240 | 189 | { as_enumerated.push_back(window); return true; }); | ||
2241 | 190 | |||
2242 | 191 | EXPECT_THAT(as_enumerated, ElementsAre(window_c, window_b, window_a)); | ||
2243 | 192 | } | ||
2244 | 193 | |||
2245 | 0 | 194 | ||
2246 | === added file 'tests/miral/raise_tree.cpp' | |||
2247 | --- tests/miral/raise_tree.cpp 1970-01-01 00:00:00 +0000 | |||
2248 | +++ tests/miral/raise_tree.cpp 2017-08-24 15:19:58 +0000 | |||
2249 | @@ -0,0 +1,85 @@ | |||
2250 | 1 | /* | ||
2251 | 2 | * Copyright © 2017 Canonical Ltd. | ||
2252 | 3 | * | ||
2253 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2254 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
2255 | 6 | * published by the Free Software Foundation. | ||
2256 | 7 | * | ||
2257 | 8 | * This program is distributed in the hope that it will be useful, | ||
2258 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2259 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2260 | 11 | * GNU General Public License for more details. | ||
2261 | 12 | * | ||
2262 | 13 | * You should have received a copy of the GNU General Public License | ||
2263 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2264 | 15 | * | ||
2265 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
2266 | 17 | */ | ||
2267 | 18 | |||
2268 | 19 | #include "test_window_manager_tools.h" | ||
2269 | 20 | |||
2270 | 21 | using namespace miral; | ||
2271 | 22 | using namespace testing; | ||
2272 | 23 | namespace mt = mir::test; | ||
2273 | 24 | |||
2274 | 25 | namespace | ||
2275 | 26 | { | ||
2276 | 27 | X const display_left{0}; | ||
2277 | 28 | Y const display_top{0}; | ||
2278 | 29 | Width const display_width{640}; | ||
2279 | 30 | Height const display_height{480}; | ||
2280 | 31 | |||
2281 | 32 | Rectangle const display_area{{display_left, display_top}, {display_width, display_height}}; | ||
2282 | 33 | |||
2283 | 34 | struct RaiseTree : TestWindowManagerTools | ||
2284 | 35 | { | ||
2285 | 36 | Size const initial_parent_size{600, 400}; | ||
2286 | 37 | Size const initial_child_size{300, 300}; | ||
2287 | 38 | |||
2288 | 39 | Window parent; | ||
2289 | 40 | Window child; | ||
2290 | 41 | Window another_window; | ||
2291 | 42 | |||
2292 | 43 | void SetUp() override | ||
2293 | 44 | { | ||
2294 | 45 | basic_window_manager.add_display_for_testing(display_area); | ||
2295 | 46 | |||
2296 | 47 | mir::scene::SurfaceCreationParameters creation_parameters; | ||
2297 | 48 | basic_window_manager.add_session(session); | ||
2298 | 49 | |||
2299 | 50 | EXPECT_CALL(*window_manager_policy, advise_new_window(_)) | ||
2300 | 51 | .WillOnce(Invoke([this](WindowInfo const& window_info){ parent = window_info.window(); })) | ||
2301 | 52 | .WillOnce(Invoke([this](WindowInfo const& window_info){ child = window_info.window(); })) | ||
2302 | 53 | .WillOnce(Invoke([this](WindowInfo const& window_info){ another_window = window_info.window(); })); | ||
2303 | 54 | |||
2304 | 55 | creation_parameters.size = initial_parent_size; | ||
2305 | 56 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
2306 | 57 | |||
2307 | 58 | creation_parameters.type = mir_window_type_menu; | ||
2308 | 59 | creation_parameters.parent = parent; | ||
2309 | 60 | creation_parameters.size = initial_child_size; | ||
2310 | 61 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
2311 | 62 | |||
2312 | 63 | creation_parameters.type = mir_window_type_normal; | ||
2313 | 64 | creation_parameters.parent.reset(); | ||
2314 | 65 | creation_parameters.size = display_area.size; | ||
2315 | 66 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
2316 | 67 | |||
2317 | 68 | // Clear the expectations used to capture parent & child | ||
2318 | 69 | Mock::VerifyAndClearExpectations(window_manager_policy); | ||
2319 | 70 | } | ||
2320 | 71 | }; | ||
2321 | 72 | } | ||
2322 | 73 | |||
2323 | 74 | TEST_F(RaiseTree, when_parent_is_raised_child_is_raised) | ||
2324 | 75 | { | ||
2325 | 76 | EXPECT_CALL(*window_manager_policy, advise_raise(ElementsAre(parent, child))); | ||
2326 | 77 | basic_window_manager.raise_tree(parent); | ||
2327 | 78 | } | ||
2328 | 79 | |||
2329 | 80 | TEST_F(RaiseTree, when_child_is_raised_parent_is_raised) | ||
2330 | 81 | { | ||
2331 | 82 | EXPECT_CALL(*window_manager_policy, advise_raise(ElementsAre(parent, child))); | ||
2332 | 83 | EXPECT_CALL(*window_manager_policy, advise_raise(ElementsAre(child))); | ||
2333 | 84 | basic_window_manager.raise_tree(child); | ||
2334 | 85 | } | ||
2335 | 0 | 86 | ||
2336 | === added file 'tests/miral/runner.cpp' | |||
2337 | --- tests/miral/runner.cpp 1970-01-01 00:00:00 +0000 | |||
2338 | +++ tests/miral/runner.cpp 2017-08-24 15:19:58 +0000 | |||
2339 | @@ -0,0 +1,49 @@ | |||
2340 | 1 | /* | ||
2341 | 2 | * Copyright © 2016 Canonical Ltd. | ||
2342 | 3 | * | ||
2343 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2344 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
2345 | 6 | * published by the Free Software Foundation. | ||
2346 | 7 | * | ||
2347 | 8 | * This program is distributed in the hope that it will be useful, | ||
2348 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2349 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2350 | 11 | * GNU General Public License for more details. | ||
2351 | 12 | * | ||
2352 | 13 | * You should have received a copy of the GNU General Public License | ||
2353 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2354 | 15 | * | ||
2355 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
2356 | 17 | */ | ||
2357 | 18 | |||
2358 | 19 | #include "test_server.h" | ||
2359 | 20 | |||
2360 | 21 | #include <gtest/gtest.h> | ||
2361 | 22 | #include <gmock/gmock.h> | ||
2362 | 23 | |||
2363 | 24 | namespace | ||
2364 | 25 | { | ||
2365 | 26 | struct Runner : miral::TestServer | ||
2366 | 27 | { | ||
2367 | 28 | void SetUp() override | ||
2368 | 29 | { | ||
2369 | 30 | } | ||
2370 | 31 | |||
2371 | 32 | MOCK_METHOD0(callback, void()); | ||
2372 | 33 | }; | ||
2373 | 34 | } | ||
2374 | 35 | |||
2375 | 36 | TEST_F(Runner, stop_callback_is_called) | ||
2376 | 37 | { | ||
2377 | 38 | runner.add_stop_callback([this] { callback(); }); | ||
2378 | 39 | miral::TestServer::SetUp(); | ||
2379 | 40 | EXPECT_CALL(*this, callback()); | ||
2380 | 41 | } | ||
2381 | 42 | |||
2382 | 43 | TEST_F(Runner, start_callback_is_called) | ||
2383 | 44 | { | ||
2384 | 45 | runner.add_start_callback([this] { callback(); }); | ||
2385 | 46 | EXPECT_CALL(*this, callback()); | ||
2386 | 47 | miral::TestServer::SetUp(); | ||
2387 | 48 | testing::Mock::VerifyAndClearExpectations(this); | ||
2388 | 49 | } | ||
2389 | 0 | \ No newline at end of file | 50 | \ No newline at end of file |
2390 | 1 | 51 | ||
2391 | === added file 'tests/miral/select_active_window.cpp' | |||
2392 | --- tests/miral/select_active_window.cpp 1970-01-01 00:00:00 +0000 | |||
2393 | +++ tests/miral/select_active_window.cpp 2017-08-24 15:19:58 +0000 | |||
2394 | @@ -0,0 +1,121 @@ | |||
2395 | 1 | /* | ||
2396 | 2 | * Copyright © 2016 Canonical Ltd. | ||
2397 | 3 | * | ||
2398 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2399 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
2400 | 6 | * published by the Free Software Foundation. | ||
2401 | 7 | * | ||
2402 | 8 | * This program is distributed in the hope that it will be useful, | ||
2403 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2404 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2405 | 11 | * GNU General Public License for more details. | ||
2406 | 12 | * | ||
2407 | 13 | * You should have received a copy of the GNU General Public License | ||
2408 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2409 | 15 | * | ||
2410 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
2411 | 17 | */ | ||
2412 | 18 | |||
2413 | 19 | #include "test_window_manager_tools.h" | ||
2414 | 20 | |||
2415 | 21 | using namespace miral; | ||
2416 | 22 | using namespace testing; | ||
2417 | 23 | namespace mt = mir::test; | ||
2418 | 24 | |||
2419 | 25 | namespace | ||
2420 | 26 | { | ||
2421 | 27 | X const display_left{0}; | ||
2422 | 28 | Y const display_top{0}; | ||
2423 | 29 | Width const display_width{1280}; | ||
2424 | 30 | Height const display_height{720}; | ||
2425 | 31 | |||
2426 | 32 | Rectangle const display_area{{display_left, display_top}, | ||
2427 | 33 | {display_width, display_height}}; | ||
2428 | 34 | |||
2429 | 35 | struct SelectActiveWindow : TestWindowManagerTools | ||
2430 | 36 | { | ||
2431 | 37 | |||
2432 | 38 | void SetUp() override | ||
2433 | 39 | { | ||
2434 | 40 | basic_window_manager.add_display_for_testing(display_area); | ||
2435 | 41 | basic_window_manager.add_session(session); | ||
2436 | 42 | } | ||
2437 | 43 | |||
2438 | 44 | auto create_window(mir::scene::SurfaceCreationParameters creation_parameters) -> Window | ||
2439 | 45 | { | ||
2440 | 46 | Window result; | ||
2441 | 47 | |||
2442 | 48 | EXPECT_CALL(*window_manager_policy, advise_new_window(_)) | ||
2443 | 49 | .WillOnce( | ||
2444 | 50 | Invoke( | ||
2445 | 51 | [&result](WindowInfo const& window_info) | ||
2446 | 52 | { result = window_info.window(); })); | ||
2447 | 53 | |||
2448 | 54 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
2449 | 55 | basic_window_manager.select_active_window(result); | ||
2450 | 56 | |||
2451 | 57 | // Clear the expectations used to capture parent & child | ||
2452 | 58 | Mock::VerifyAndClearExpectations(window_manager_policy); | ||
2453 | 59 | |||
2454 | 60 | return result; | ||
2455 | 61 | } | ||
2456 | 62 | }; | ||
2457 | 63 | } | ||
2458 | 64 | |||
2459 | 65 | namespace std | ||
2460 | 66 | { | ||
2461 | 67 | auto operator<<(std::ostream& out, miral::Window const& window) -> std::ostream& | ||
2462 | 68 | { | ||
2463 | 69 | if (std::shared_ptr<mir::scene::Surface> surface = window) | ||
2464 | 70 | return out << surface->name(); | ||
2465 | 71 | else | ||
2466 | 72 | return out << "(nul)"; | ||
2467 | 73 | } | ||
2468 | 74 | } | ||
2469 | 75 | |||
2470 | 76 | // lp:1626659 | ||
2471 | 77 | // "If the surface has a child dialog, the deepest descendant | ||
2472 | 78 | // dialog should receive input focus." | ||
2473 | 79 | TEST_F(SelectActiveWindow, given_a_child_dialog_when_selecting_the_parent_the_dialog_receives_focus) | ||
2474 | 80 | { | ||
2475 | 81 | mir::scene::SurfaceCreationParameters creation_parameters; | ||
2476 | 82 | creation_parameters.name = "parent"; | ||
2477 | 83 | creation_parameters.type = mir_window_type_normal; | ||
2478 | 84 | creation_parameters.size = Size{600, 400}; | ||
2479 | 85 | |||
2480 | 86 | auto parent = create_window(creation_parameters); | ||
2481 | 87 | |||
2482 | 88 | creation_parameters.name = "dialog"; | ||
2483 | 89 | creation_parameters.type = mir_window_type_dialog; | ||
2484 | 90 | creation_parameters.parent = parent; | ||
2485 | 91 | |||
2486 | 92 | auto dialog = create_window(creation_parameters); | ||
2487 | 93 | |||
2488 | 94 | auto actual = basic_window_manager.select_active_window(parent); | ||
2489 | 95 | EXPECT_THAT(actual, Eq(dialog)) | ||
2490 | 96 | << "actual=" << actual << ", expected=" << dialog; | ||
2491 | 97 | } | ||
2492 | 98 | |||
2493 | 99 | TEST_F(SelectActiveWindow, given_a_hidden_child_dialog_when_selecting_the_parent_the_parent_receives_focus) | ||
2494 | 100 | { | ||
2495 | 101 | mir::scene::SurfaceCreationParameters creation_parameters; | ||
2496 | 102 | creation_parameters.name = "parent"; | ||
2497 | 103 | creation_parameters.type = mir_window_type_normal; | ||
2498 | 104 | creation_parameters.size = Size{600, 400}; | ||
2499 | 105 | |||
2500 | 106 | auto parent = create_window(creation_parameters); | ||
2501 | 107 | |||
2502 | 108 | creation_parameters.name = "dialog"; | ||
2503 | 109 | creation_parameters.type = mir_window_type_dialog; | ||
2504 | 110 | creation_parameters.parent = parent; | ||
2505 | 111 | |||
2506 | 112 | auto dialog = create_window(creation_parameters); | ||
2507 | 113 | |||
2508 | 114 | WindowSpecification mods; | ||
2509 | 115 | mods.state() = mir_window_state_hidden; | ||
2510 | 116 | basic_window_manager.modify_window(basic_window_manager.info_for(dialog), mods); | ||
2511 | 117 | |||
2512 | 118 | auto actual = basic_window_manager.select_active_window(parent); | ||
2513 | 119 | EXPECT_THAT(actual, Eq(parent)) | ||
2514 | 120 | << "actual=" << actual << ", expected=" << parent; | ||
2515 | 121 | } | ||
2516 | 0 | 122 | ||
2517 | === added file 'tests/miral/test_server.cpp' | |||
2518 | --- tests/miral/test_server.cpp 1970-01-01 00:00:00 +0000 | |||
2519 | +++ tests/miral/test_server.cpp 2017-08-24 15:19:58 +0000 | |||
2520 | @@ -0,0 +1,198 @@ | |||
2521 | 1 | /* | ||
2522 | 2 | * Copyright © 2016 Canonical Ltd. | ||
2523 | 3 | * | ||
2524 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2525 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
2526 | 6 | * published by the Free Software Foundation. | ||
2527 | 7 | * | ||
2528 | 8 | * This program is distributed in the hope that it will be useful, | ||
2529 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2530 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2531 | 11 | * GNU General Public License for more details. | ||
2532 | 12 | * | ||
2533 | 13 | * You should have received a copy of the GNU General Public License | ||
2534 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2535 | 15 | * | ||
2536 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
2537 | 17 | */ | ||
2538 | 18 | |||
2539 | 19 | #include "test_server.h" | ||
2540 | 20 | #include "basic_window_manager.h" | ||
2541 | 21 | |||
2542 | 22 | #include <miral/command_line_option.h> | ||
2543 | 23 | #include <miral/set_window_management_policy.h> | ||
2544 | 24 | |||
2545 | 25 | #include <mir_test_framework/executable_path.h> | ||
2546 | 26 | #include <mir_test_framework/stub_server_platform_factory.h> | ||
2547 | 27 | #include <mir_test_framework/headless_display_buffer_compositor_factory.h> | ||
2548 | 28 | #include <mir/test/doubles/null_logger.h> | ||
2549 | 29 | |||
2550 | 30 | #include <mir/fd.h> | ||
2551 | 31 | #include <mir/main_loop.h> | ||
2552 | 32 | #include <mir/server.h> | ||
2553 | 33 | #include <mir/version.h> | ||
2554 | 34 | |||
2555 | 35 | #include <boost/throw_exception.hpp> | ||
2556 | 36 | |||
2557 | 37 | |||
2558 | 38 | using namespace miral; | ||
2559 | 39 | using namespace testing; | ||
2560 | 40 | namespace mtf = mir_test_framework; | ||
2561 | 41 | namespace msh = mir::shell; | ||
2562 | 42 | |||
2563 | 43 | namespace | ||
2564 | 44 | { | ||
2565 | 45 | std::chrono::seconds const timeout{20}; | ||
2566 | 46 | char const* dummy_args[2] = { "TestServer", nullptr }; | ||
2567 | 47 | } | ||
2568 | 48 | |||
2569 | 49 | miral::TestServer::TestWindowManagerPolicy::TestWindowManagerPolicy( | ||
2570 | 50 | WindowManagerTools const& tools, TestServer& test_fixture) : | ||
2571 | 51 | CanonicalWindowManagerPolicy{tools} | ||
2572 | 52 | { | ||
2573 | 53 | test_fixture.tools = tools; | ||
2574 | 54 | } | ||
2575 | 55 | |||
2576 | 56 | miral::TestServer::TestServer() : | ||
2577 | 57 | runner{1, dummy_args} | ||
2578 | 58 | { | ||
2579 | 59 | add_to_environment("MIR_SERVER_PLATFORM_GRAPHICS_LIB", mtf::server_platform("graphics-dummy.so").c_str()); | ||
2580 | 60 | add_to_environment("MIR_SERVER_PLATFORM_INPUT_LIB", mtf::server_platform("input-stub.so").c_str()); | ||
2581 | 61 | add_to_environment("MIR_SERVER_NO_FILE", "on"); | ||
2582 | 62 | } | ||
2583 | 63 | |||
2584 | 64 | auto miral::TestServer::build_window_manager_policy(WindowManagerTools const& tools) | ||
2585 | 65 | -> std::unique_ptr<TestWindowManagerPolicy> | ||
2586 | 66 | { | ||
2587 | 67 | return std::make_unique<TestWindowManagerPolicy>(tools, *this); | ||
2588 | 68 | } | ||
2589 | 69 | |||
2590 | 70 | void miral::TestServer::SetUp() | ||
2591 | 71 | { | ||
2592 | 72 | mir::test::AutoJoinThread t([this] | ||
2593 | 73 | { | ||
2594 | 74 | auto init = [this](mir::Server& server) | ||
2595 | 75 | { | ||
2596 | 76 | server.add_init_callback([&] | ||
2597 | 77 | { | ||
2598 | 78 | auto const main_loop = server.the_main_loop(); | ||
2599 | 79 | // By enqueuing the notification code in the main loop, we are | ||
2600 | 80 | // ensuring that the server has really and fully started before | ||
2601 | 81 | // leaving start_mir_server(). | ||
2602 | 82 | main_loop->enqueue(this, [&] | ||
2603 | 83 | { | ||
2604 | 84 | std::lock_guard<std::mutex> lock(mutex); | ||
2605 | 85 | server_running = &server; | ||
2606 | 86 | started.notify_one(); | ||
2607 | 87 | }); | ||
2608 | 88 | }); | ||
2609 | 89 | |||
2610 | 90 | server.override_the_display_buffer_compositor_factory([] | ||
2611 | 91 | { | ||
2612 | 92 | return std::make_shared<mtf::HeadlessDisplayBufferCompositorFactory>(); | ||
2613 | 93 | }); | ||
2614 | 94 | |||
2615 | 95 | server.override_the_window_manager_builder([this, &server](msh::FocusController* focus_controller) | ||
2616 | 96 | -> std::shared_ptr<msh::WindowManager> | ||
2617 | 97 | { | ||
2618 | 98 | auto const display_layout = server.the_shell_display_layout(); | ||
2619 | 99 | |||
2620 | 100 | auto const persistent_surface_store = server.the_persistent_surface_store(); | ||
2621 | 101 | |||
2622 | 102 | auto builder = [this](WindowManagerTools const& tools) -> std::unique_ptr<miral::WindowManagementPolicy> | ||
2623 | 103 | { | ||
2624 | 104 | return build_window_manager_policy(tools); | ||
2625 | 105 | }; | ||
2626 | 106 | |||
2627 | 107 | auto wm = std::make_shared<miral::BasicWindowManager>( | ||
2628 | 108 | focus_controller, | ||
2629 | 109 | display_layout, | ||
2630 | 110 | persistent_surface_store, | ||
2631 | 111 | *server.the_display_configuration_observer_registrar(), | ||
2632 | 112 | builder); | ||
2633 | 113 | window_manager = wm; | ||
2634 | 114 | return wm; | ||
2635 | 115 | }); | ||
2636 | 116 | }; | ||
2637 | 117 | |||
2638 | 118 | try | ||
2639 | 119 | { | ||
2640 | 120 | namespace mtd = mir::test::doubles; | ||
2641 | 121 | // Ignore the --logging flag passed to mir tests | ||
2642 | 122 | CommandLineOption logging{[](bool) {}, mtd::logging_opt, mtd::logging_descr, false}; | ||
2643 | 123 | runner.run_with({init, logging}); | ||
2644 | 124 | } | ||
2645 | 125 | catch (std::exception const& e) | ||
2646 | 126 | { | ||
2647 | 127 | FAIL() << e.what(); | ||
2648 | 128 | } | ||
2649 | 129 | |||
2650 | 130 | std::lock_guard<std::mutex> lock(mutex); | ||
2651 | 131 | server_running = nullptr; | ||
2652 | 132 | started.notify_one(); | ||
2653 | 133 | }); | ||
2654 | 134 | |||
2655 | 135 | std::unique_lock<std::mutex> lock(mutex); | ||
2656 | 136 | started.wait_for(lock, timeout, [&] { return server_running; }); | ||
2657 | 137 | |||
2658 | 138 | if (!server_running) | ||
2659 | 139 | BOOST_THROW_EXCEPTION(std::runtime_error{"Failed to start server thread"}); | ||
2660 | 140 | |||
2661 | 141 | server_thread = std::move(t); | ||
2662 | 142 | } | ||
2663 | 143 | |||
2664 | 144 | void miral::TestServer::TearDown() | ||
2665 | 145 | { | ||
2666 | 146 | std::unique_lock<std::mutex> lock(mutex); | ||
2667 | 147 | |||
2668 | 148 | runner.stop(); | ||
2669 | 149 | |||
2670 | 150 | started.wait_for(lock, timeout, [&] { return !server_running; }); | ||
2671 | 151 | |||
2672 | 152 | if (server_running) | ||
2673 | 153 | BOOST_THROW_EXCEPTION(std::logic_error{"Failed to stop server"}); | ||
2674 | 154 | |||
2675 | 155 | server_thread.stop(); | ||
2676 | 156 | } | ||
2677 | 157 | |||
2678 | 158 | auto miral::TestServer::connect_client(std::string name) -> mir::client::Connection | ||
2679 | 159 | { | ||
2680 | 160 | std::lock_guard<std::mutex> lock(mutex); | ||
2681 | 161 | |||
2682 | 162 | if (!server_running) | ||
2683 | 163 | BOOST_THROW_EXCEPTION(std::runtime_error{"Server not running"}); | ||
2684 | 164 | |||
2685 | 165 | char connect_string[64] = {0}; | ||
2686 | 166 | sprintf(connect_string, "fd://%d", dup(server_running->open_client_socket())); | ||
2687 | 167 | |||
2688 | 168 | return mir::client::Connection{mir_connect_sync(connect_string, name.c_str())}; | ||
2689 | 169 | } | ||
2690 | 170 | |||
2691 | 171 | void miral::TestServer::invoke_tools(std::function<void(WindowManagerTools& tools)> const& f) | ||
2692 | 172 | { | ||
2693 | 173 | tools.invoke_under_lock([&]{f(tools); }); | ||
2694 | 174 | } | ||
2695 | 175 | |||
2696 | 176 | void miral::TestServer::invoke_window_manager(std::function<void(mir::shell::WindowManager& wm)> const& f) | ||
2697 | 177 | { | ||
2698 | 178 | if (auto const wm = window_manager.lock()) | ||
2699 | 179 | f(*wm); | ||
2700 | 180 | else | ||
2701 | 181 | BOOST_THROW_EXCEPTION(std::runtime_error{"Server not running"}); | ||
2702 | 182 | |||
2703 | 183 | } | ||
2704 | 184 | |||
2705 | 185 | void miral::TestRuntimeEnvironment::add_to_environment(char const* key, char const* value) | ||
2706 | 186 | { | ||
2707 | 187 | env.emplace_back(key, value); | ||
2708 | 188 | } | ||
2709 | 189 | |||
2710 | 190 | using miral::TestServer; | ||
2711 | 191 | |||
2712 | 192 | // Minimal test to ensure the server runs and exits | ||
2713 | 193 | TEST_F(TestServer, connect_client_works) | ||
2714 | 194 | { | ||
2715 | 195 | auto const connection = connect_client(__PRETTY_FUNCTION__); | ||
2716 | 196 | |||
2717 | 197 | EXPECT_TRUE(mir_connection_is_valid(connection)); | ||
2718 | 198 | } | ||
2719 | 0 | 199 | ||
2720 | === added file 'tests/miral/test_server.h' | |||
2721 | --- tests/miral/test_server.h 1970-01-01 00:00:00 +0000 | |||
2722 | +++ tests/miral/test_server.h 2017-08-24 15:19:58 +0000 | |||
2723 | @@ -0,0 +1,90 @@ | |||
2724 | 1 | /* | ||
2725 | 2 | * Copyright © 2016 Canonical Ltd. | ||
2726 | 3 | * | ||
2727 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2728 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
2729 | 6 | * published by the Free Software Foundation. | ||
2730 | 7 | * | ||
2731 | 8 | * This program is distributed in the hope that it will be useful, | ||
2732 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2733 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2734 | 11 | * GNU General Public License for more details. | ||
2735 | 12 | * | ||
2736 | 13 | * You should have received a copy of the GNU General Public License | ||
2737 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2738 | 15 | * | ||
2739 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
2740 | 17 | */ | ||
2741 | 18 | |||
2742 | 19 | #ifndef MIRAL_TEST_SERVER_H | ||
2743 | 20 | #define MIRAL_TEST_SERVER_H | ||
2744 | 21 | |||
2745 | 22 | #include <mir/client/connection.h> | ||
2746 | 23 | |||
2747 | 24 | #include <miral/canonical_window_manager.h> | ||
2748 | 25 | #include <miral/runner.h> | ||
2749 | 26 | #include <miral/window_manager_tools.h> | ||
2750 | 27 | |||
2751 | 28 | #include <mir/test/auto_unblock_thread.h> | ||
2752 | 29 | #include <mir_test_framework/temporary_environment_value.h> | ||
2753 | 30 | |||
2754 | 31 | #include <gtest/gtest.h> | ||
2755 | 32 | |||
2756 | 33 | #include <condition_variable> | ||
2757 | 34 | #include <list> | ||
2758 | 35 | #include <mutex> | ||
2759 | 36 | |||
2760 | 37 | namespace mir { namespace shell { class WindowManager; }} | ||
2761 | 38 | |||
2762 | 39 | namespace miral | ||
2763 | 40 | { | ||
2764 | 41 | class WindowManagementPolicy; | ||
2765 | 42 | class TestRuntimeEnvironment | ||
2766 | 43 | { | ||
2767 | 44 | public: | ||
2768 | 45 | void add_to_environment(char const* key, char const* value); | ||
2769 | 46 | |||
2770 | 47 | private: | ||
2771 | 48 | std::list<mir_test_framework::TemporaryEnvironmentValue> env; | ||
2772 | 49 | }; | ||
2773 | 50 | |||
2774 | 51 | struct TestServer : testing::Test, private TestRuntimeEnvironment | ||
2775 | 52 | { | ||
2776 | 53 | TestServer(); | ||
2777 | 54 | |||
2778 | 55 | void SetUp() override; | ||
2779 | 56 | void TearDown() override; | ||
2780 | 57 | |||
2781 | 58 | auto connect_client(std::string name) -> mir::client::Connection; | ||
2782 | 59 | |||
2783 | 60 | using TestRuntimeEnvironment::add_to_environment; | ||
2784 | 61 | |||
2785 | 62 | MirRunner runner; | ||
2786 | 63 | |||
2787 | 64 | void invoke_tools(std::function<void(WindowManagerTools& tools)> const& f); | ||
2788 | 65 | void invoke_window_manager(std::function<void(mir::shell::WindowManager& wm)> const& f); | ||
2789 | 66 | |||
2790 | 67 | struct TestWindowManagerPolicy; | ||
2791 | 68 | virtual auto build_window_manager_policy(WindowManagerTools const& tools) -> std::unique_ptr<TestWindowManagerPolicy>; | ||
2792 | 69 | |||
2793 | 70 | private: | ||
2794 | 71 | WindowManagerTools tools{nullptr}; | ||
2795 | 72 | std::weak_ptr<mir::shell::WindowManager> window_manager; | ||
2796 | 73 | mir::test::AutoJoinThread server_thread; | ||
2797 | 74 | std::mutex mutex; | ||
2798 | 75 | std::condition_variable started; | ||
2799 | 76 | mir::Server* server_running{nullptr}; | ||
2800 | 77 | }; | ||
2801 | 78 | |||
2802 | 79 | struct TestServer::TestWindowManagerPolicy : CanonicalWindowManagerPolicy | ||
2803 | 80 | { | ||
2804 | 81 | TestWindowManagerPolicy(WindowManagerTools const& tools, TestServer& test_fixture); | ||
2805 | 82 | |||
2806 | 83 | bool handle_keyboard_event(MirKeyboardEvent const*) override { return false; } | ||
2807 | 84 | bool handle_pointer_event(MirPointerEvent const*) override { return false; } | ||
2808 | 85 | bool handle_touch_event(MirTouchEvent const*) override { return false; } | ||
2809 | 86 | }; | ||
2810 | 87 | |||
2811 | 88 | } | ||
2812 | 89 | |||
2813 | 90 | #endif //MIRAL_TEST_SERVER_H | ||
2814 | 0 | 91 | ||
2815 | === added file 'tests/miral/test_window_manager_tools.h' | |||
2816 | --- tests/miral/test_window_manager_tools.h 1970-01-01 00:00:00 +0000 | |||
2817 | +++ tests/miral/test_window_manager_tools.h 2017-08-24 15:19:58 +0000 | |||
2818 | @@ -0,0 +1,197 @@ | |||
2819 | 1 | /* | ||
2820 | 2 | * Copyright © 2016 Canonical Ltd. | ||
2821 | 3 | * | ||
2822 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2823 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
2824 | 6 | * published by the Free Software Foundation. | ||
2825 | 7 | * | ||
2826 | 8 | * This program is distributed in the hope that it will be useful, | ||
2827 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2828 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2829 | 11 | * GNU General Public License for more details. | ||
2830 | 12 | * | ||
2831 | 13 | * You should have received a copy of the GNU General Public License | ||
2832 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2833 | 15 | * | ||
2834 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
2835 | 17 | */ | ||
2836 | 18 | |||
2837 | 19 | #ifndef MIRAL_TEST_WINDOW_MANAGER_TOOLS_H | ||
2838 | 20 | #define MIRAL_TEST_WINDOW_MANAGER_TOOLS_H | ||
2839 | 21 | |||
2840 | 22 | #include "basic_window_manager.h" | ||
2841 | 23 | |||
2842 | 24 | #include <miral/canonical_window_manager.h> | ||
2843 | 25 | |||
2844 | 26 | #include <mir/scene/surface_creation_parameters.h> | ||
2845 | 27 | #include <mir/shell/display_layout.h> | ||
2846 | 28 | #include <mir/shell/focus_controller.h> | ||
2847 | 29 | #include <mir/shell/persistent_surface_store.h> | ||
2848 | 30 | #include <mir/version.h> | ||
2849 | 31 | |||
2850 | 32 | #include <mir/test/doubles/stub_session.h> | ||
2851 | 33 | #include <mir/test/doubles/stub_surface.h> | ||
2852 | 34 | #include <mir/test/fake_shared.h> | ||
2853 | 35 | |||
2854 | 36 | #include <gtest/gtest.h> | ||
2855 | 37 | #include <gmock/gmock.h> | ||
2856 | 38 | |||
2857 | 39 | #include <atomic> | ||
2858 | 40 | |||
2859 | 41 | struct StubFocusController : mir::shell::FocusController | ||
2860 | 42 | { | ||
2861 | 43 | void focus_next_session() override {} | ||
2862 | 44 | |||
2863 | 45 | auto focused_session() const -> std::shared_ptr<mir::scene::Session> override { return {}; } | ||
2864 | 46 | |||
2865 | 47 | void set_focus_to( | ||
2866 | 48 | std::shared_ptr<mir::scene::Session> const& /*focus_session*/, | ||
2867 | 49 | std::shared_ptr<mir::scene::Surface> const& /*focus_surface*/) override {} | ||
2868 | 50 | |||
2869 | 51 | auto focused_surface() const -> std::shared_ptr<mir::scene::Surface> override { return {}; } | ||
2870 | 52 | |||
2871 | 53 | void raise(mir::shell::SurfaceSet const& /*windows*/) override {} | ||
2872 | 54 | |||
2873 | 55 | virtual auto surface_at(mir::geometry::Point /*cursor*/) const -> std::shared_ptr<mir::scene::Surface> override | ||
2874 | 56 | { return {}; } | ||
2875 | 57 | |||
2876 | 58 | #if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0) | ||
2877 | 59 | void set_drag_and_drop_handle(std::vector<uint8_t> const& /*handle*/) override {} | ||
2878 | 60 | |||
2879 | 61 | void clear_drag_and_drop_handle() override {} | ||
2880 | 62 | #endif | ||
2881 | 63 | }; | ||
2882 | 64 | |||
2883 | 65 | struct StubDisplayLayout : mir::shell::DisplayLayout | ||
2884 | 66 | { | ||
2885 | 67 | void clip_to_output(mir::geometry::Rectangle& /*rect*/) override {} | ||
2886 | 68 | |||
2887 | 69 | void size_to_output(mir::geometry::Rectangle& /*rect*/) override {} | ||
2888 | 70 | |||
2889 | 71 | bool place_in_output(mir::graphics::DisplayConfigurationOutputId /*id*/, mir::geometry::Rectangle& /*rect*/) override | ||
2890 | 72 | { return false; } | ||
2891 | 73 | }; | ||
2892 | 74 | |||
2893 | 75 | struct StubPersistentSurfaceStore : mir::shell::PersistentSurfaceStore | ||
2894 | 76 | { | ||
2895 | 77 | Id id_for_surface(std::shared_ptr<mir::scene::Surface> const& /*surface*/) override { return {}; } | ||
2896 | 78 | |||
2897 | 79 | auto surface_for_id(Id const& /*id*/) const -> std::shared_ptr<mir::scene::Surface> override { return {}; } | ||
2898 | 80 | }; | ||
2899 | 81 | |||
2900 | 82 | struct StubSurface : mir::test::doubles::StubSurface | ||
2901 | 83 | { | ||
2902 | 84 | StubSurface(std::string name, MirWindowType type, mir::geometry::Point top_left, mir::geometry::Size size) : | ||
2903 | 85 | name_{name}, type_{type}, top_left_{top_left}, size_{size} {} | ||
2904 | 86 | |||
2905 | 87 | std::string name() const override { return name_; }; | ||
2906 | 88 | MirWindowType type() const override { return type_; } | ||
2907 | 89 | |||
2908 | 90 | mir::geometry::Point top_left() const override { return top_left_; } | ||
2909 | 91 | void move_to(mir::geometry::Point const& top_left) override { top_left_ = top_left; } | ||
2910 | 92 | |||
2911 | 93 | mir::geometry::Size size() const override { return size_; } | ||
2912 | 94 | void resize(mir::geometry::Size const& size) override { size_ = size; } | ||
2913 | 95 | |||
2914 | 96 | auto state() const -> MirWindowState override { return state_; } | ||
2915 | 97 | auto configure(MirWindowAttrib attrib, int value) -> int override { | ||
2916 | 98 | switch (attrib) | ||
2917 | 99 | { | ||
2918 | 100 | case mir_window_attrib_state: | ||
2919 | 101 | state_ = MirWindowState(value); | ||
2920 | 102 | return state_; | ||
2921 | 103 | default: | ||
2922 | 104 | return value; | ||
2923 | 105 | } | ||
2924 | 106 | } | ||
2925 | 107 | |||
2926 | 108 | bool visible() const override { return state() != mir_window_state_hidden; } | ||
2927 | 109 | |||
2928 | 110 | std::string name_; | ||
2929 | 111 | MirWindowType type_; | ||
2930 | 112 | mir::geometry::Point top_left_; | ||
2931 | 113 | mir::geometry::Size size_; | ||
2932 | 114 | MirWindowState state_ = mir_window_state_restored; | ||
2933 | 115 | }; | ||
2934 | 116 | |||
2935 | 117 | struct StubStubSession : mir::test::doubles::StubSession | ||
2936 | 118 | { | ||
2937 | 119 | mir::frontend::SurfaceId create_surface( | ||
2938 | 120 | mir::scene::SurfaceCreationParameters const& params, | ||
2939 | 121 | std::shared_ptr<mir::frontend::EventSink> const& /*sink*/) override | ||
2940 | 122 | { | ||
2941 | 123 | auto id = mir::frontend::SurfaceId{next_surface_id.fetch_add(1)}; | ||
2942 | 124 | auto surface = std::make_shared<StubSurface>(params.name, params.type.value(), params.top_left, params.size); | ||
2943 | 125 | surfaces[id] = surface; | ||
2944 | 126 | return id; | ||
2945 | 127 | } | ||
2946 | 128 | |||
2947 | 129 | std::shared_ptr<mir::scene::Surface> surface(mir::frontend::SurfaceId surface) const override | ||
2948 | 130 | { | ||
2949 | 131 | return surfaces.at(surface); | ||
2950 | 132 | } | ||
2951 | 133 | |||
2952 | 134 | private: | ||
2953 | 135 | std::atomic<int> next_surface_id; | ||
2954 | 136 | std::map<mir::frontend::SurfaceId, std::shared_ptr<mir::scene::Surface>> surfaces; | ||
2955 | 137 | }; | ||
2956 | 138 | |||
2957 | 139 | struct MockWindowManagerPolicy : miral::CanonicalWindowManagerPolicy | ||
2958 | 140 | { | ||
2959 | 141 | using miral::CanonicalWindowManagerPolicy::CanonicalWindowManagerPolicy; | ||
2960 | 142 | |||
2961 | 143 | bool handle_touch_event(MirTouchEvent const* /*event*/) { return false; } | ||
2962 | 144 | bool handle_pointer_event(MirPointerEvent const* /*event*/) { return false; } | ||
2963 | 145 | bool handle_keyboard_event(MirKeyboardEvent const* /*event*/) { return false; } | ||
2964 | 146 | |||
2965 | 147 | MOCK_METHOD1(advise_new_window, void (miral::WindowInfo const& window_info)); | ||
2966 | 148 | MOCK_METHOD2(advise_move_to, void(miral::WindowInfo const& window_info, mir::geometry::Point top_left)); | ||
2967 | 149 | MOCK_METHOD2(advise_resize, void(miral::WindowInfo const& window_info, mir::geometry::Size const& new_size)); | ||
2968 | 150 | MOCK_METHOD1(advise_raise, void(std::vector<miral::Window> const&)); | ||
2969 | 151 | }; | ||
2970 | 152 | |||
2971 | 153 | struct StubDisplayConfigurationObserver : mir::ObserverRegistrar<mir::graphics::DisplayConfigurationObserver> | ||
2972 | 154 | { | ||
2973 | 155 | void register_interest(std::weak_ptr<mir::graphics::DisplayConfigurationObserver> const&) override {} | ||
2974 | 156 | |||
2975 | 157 | void register_interest(std::weak_ptr<mir::graphics::DisplayConfigurationObserver> const&, mir::Executor&) override {} | ||
2976 | 158 | |||
2977 | 159 | void unregister_interest(mir::graphics::DisplayConfigurationObserver const&) override {} | ||
2978 | 160 | }; | ||
2979 | 161 | |||
2980 | 162 | struct TestWindowManagerTools : testing::Test | ||
2981 | 163 | { | ||
2982 | 164 | StubFocusController focus_controller; | ||
2983 | 165 | StubDisplayLayout display_layout; | ||
2984 | 166 | StubPersistentSurfaceStore persistent_surface_store; | ||
2985 | 167 | StubDisplayConfigurationObserver display_configuration_observer; | ||
2986 | 168 | std::shared_ptr<StubStubSession> session{std::make_shared<StubStubSession>()}; | ||
2987 | 169 | |||
2988 | 170 | MockWindowManagerPolicy* window_manager_policy{nullptr}; | ||
2989 | 171 | miral::WindowManagerTools window_manager_tools{nullptr}; | ||
2990 | 172 | |||
2991 | 173 | miral::BasicWindowManager basic_window_manager{ | ||
2992 | 174 | &focus_controller, | ||
2993 | 175 | mir::test::fake_shared(display_layout), | ||
2994 | 176 | mir::test::fake_shared(persistent_surface_store), | ||
2995 | 177 | display_configuration_observer, | ||
2996 | 178 | [this](miral::WindowManagerTools const& tools) -> std::unique_ptr<miral::WindowManagementPolicy> | ||
2997 | 179 | { | ||
2998 | 180 | auto policy = std::make_unique<testing::NiceMock<MockWindowManagerPolicy>>(tools); | ||
2999 | 181 | window_manager_policy = policy.get(); | ||
3000 | 182 | window_manager_tools = tools; | ||
3001 | 183 | return std::move(policy); | ||
3002 | 184 | } | ||
3003 | 185 | }; | ||
3004 | 186 | |||
3005 | 187 | static auto create_surface( | ||
3006 | 188 | std::shared_ptr<mir::scene::Session> const& session, | ||
3007 | 189 | mir::scene::SurfaceCreationParameters const& params) -> mir::frontend::SurfaceId | ||
3008 | 190 | { | ||
3009 | 191 | // This type is Mir-internal, I hope we don't need to create it here | ||
3010 | 192 | std::shared_ptr<mir::frontend::EventSink> const sink; | ||
3011 | 193 | return session->create_surface(params, sink); | ||
3012 | 194 | } | ||
3013 | 195 | }; | ||
3014 | 196 | |||
3015 | 197 | #endif //MIRAL_TEST_WINDOW_MANAGER_TOOLS_H | ||
3016 | 0 | 198 | ||
3017 | === added file 'tests/miral/window_id.cpp' | |||
3018 | --- tests/miral/window_id.cpp 1970-01-01 00:00:00 +0000 | |||
3019 | +++ tests/miral/window_id.cpp 2017-08-24 15:19:58 +0000 | |||
3020 | @@ -0,0 +1,114 @@ | |||
3021 | 1 | /* | ||
3022 | 2 | * Copyright © 2016 Canonical Ltd. | ||
3023 | 3 | * | ||
3024 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3025 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
3026 | 6 | * published by the Free Software Foundation. | ||
3027 | 7 | * | ||
3028 | 8 | * This program is distributed in the hope that it will be useful, | ||
3029 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3030 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3031 | 11 | * GNU General Public License for more details. | ||
3032 | 12 | * | ||
3033 | 13 | * You should have received a copy of the GNU General Public License | ||
3034 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3035 | 15 | * | ||
3036 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
3037 | 17 | */ | ||
3038 | 18 | |||
3039 | 19 | #include <mir/client/window_id.h> | ||
3040 | 20 | #include <mir/client/window.h> | ||
3041 | 21 | #include <mir/client/window_spec.h> | ||
3042 | 22 | |||
3043 | 23 | #include <miral/application_info.h> | ||
3044 | 24 | |||
3045 | 25 | #include <mir/version.h> | ||
3046 | 26 | |||
3047 | 27 | #include <gtest/gtest.h> | ||
3048 | 28 | #include <gmock/gmock.h> | ||
3049 | 29 | |||
3050 | 30 | #include "test_server.h" | ||
3051 | 31 | |||
3052 | 32 | using namespace testing; | ||
3053 | 33 | |||
3054 | 34 | |||
3055 | 35 | struct WindowId : public miral::TestServer | ||
3056 | 36 | { | ||
3057 | 37 | auto get_first_window(miral::WindowManagerTools& tools) -> miral::Window | ||
3058 | 38 | { | ||
3059 | 39 | auto app = tools.find_application([&](miral::ApplicationInfo const& /*info*/){return true;}); | ||
3060 | 40 | auto app_info = tools.info_for(app); | ||
3061 | 41 | return app_info.windows().at(0); | ||
3062 | 42 | } | ||
3063 | 43 | }; | ||
3064 | 44 | |||
3065 | 45 | TEST_F(WindowId, server_can_identify_window_specified_by_client) | ||
3066 | 46 | { | ||
3067 | 47 | char const* const test_name = __PRETTY_FUNCTION__; | ||
3068 | 48 | using namespace mir::client; | ||
3069 | 49 | |||
3070 | 50 | auto const connection = connect_client(test_name); | ||
3071 | 51 | auto const spec = WindowSpec::for_normal_window(connection, 50, 50) | ||
3072 | 52 | .set_name(test_name); | ||
3073 | 53 | |||
3074 | 54 | Window const surface{spec.create_window()}; | ||
3075 | 55 | |||
3076 | 56 | mir::client::WindowId client_surface_id{surface}; | ||
3077 | 57 | |||
3078 | 58 | invoke_tools([&](miral::WindowManagerTools& tools) | ||
3079 | 59 | { | ||
3080 | 60 | auto const& window_info = tools.info_for_window_id(client_surface_id.c_str()); | ||
3081 | 61 | |||
3082 | 62 | ASSERT_TRUE(window_info.window()); | ||
3083 | 63 | ASSERT_THAT(window_info.name(), Eq(test_name)); | ||
3084 | 64 | }); | ||
3085 | 65 | } | ||
3086 | 66 | |||
3087 | 67 | TEST_F(WindowId, server_returns_correct_id_for_window) | ||
3088 | 68 | { | ||
3089 | 69 | char const* const test_name = __PRETTY_FUNCTION__; | ||
3090 | 70 | using namespace mir::client; | ||
3091 | 71 | |||
3092 | 72 | auto const connection = connect_client(test_name); | ||
3093 | 73 | auto const spec = WindowSpec::for_normal_window(connection, 50, 50) | ||
3094 | 74 | .set_name(test_name); | ||
3095 | 75 | |||
3096 | 76 | Window const surface{spec.create_window()}; | ||
3097 | 77 | |||
3098 | 78 | mir::client::WindowId client_surface_id{surface}; | ||
3099 | 79 | |||
3100 | 80 | invoke_tools([&](miral::WindowManagerTools& tools) | ||
3101 | 81 | { | ||
3102 | 82 | auto window = get_first_window(tools); | ||
3103 | 83 | auto id = tools.id_for_window(window); | ||
3104 | 84 | |||
3105 | 85 | ASSERT_THAT(client_surface_id.c_str(), Eq(id)); | ||
3106 | 86 | }); | ||
3107 | 87 | } | ||
3108 | 88 | |||
3109 | 89 | TEST_F(WindowId, server_fails_gracefully_to_identify_window_from_garbage_id) | ||
3110 | 90 | { | ||
3111 | 91 | char const* const test_name = __PRETTY_FUNCTION__; | ||
3112 | 92 | using namespace mir::client; | ||
3113 | 93 | |||
3114 | 94 | auto const connection = connect_client(test_name); | ||
3115 | 95 | auto const spec = WindowSpec::for_normal_window(connection, 50, 50).set_name(test_name); | ||
3116 | 96 | |||
3117 | 97 | Window const surface{spec.create_window()}; | ||
3118 | 98 | |||
3119 | 99 | mir::client::WindowId client_surface_id{surface}; | ||
3120 | 100 | |||
3121 | 101 | invoke_tools([](miral::WindowManagerTools& tools) | ||
3122 | 102 | { | ||
3123 | 103 | EXPECT_THROW(tools.info_for_window_id("garbage"), std::exception); | ||
3124 | 104 | }); | ||
3125 | 105 | } | ||
3126 | 106 | |||
3127 | 107 | TEST_F(WindowId, server_fails_gracefully_when_id_for_null_window_requested) | ||
3128 | 108 | { | ||
3129 | 109 | invoke_tools([](miral::WindowManagerTools& tools) | ||
3130 | 110 | { | ||
3131 | 111 | miral::Window window; | ||
3132 | 112 | EXPECT_THROW(tools.id_for_window(window), std::runtime_error); | ||
3133 | 113 | }); | ||
3134 | 114 | } | ||
3135 | 0 | 115 | ||
3136 | === added file 'tests/miral/window_placement.cpp' | |||
3137 | --- tests/miral/window_placement.cpp 1970-01-01 00:00:00 +0000 | |||
3138 | +++ tests/miral/window_placement.cpp 2017-08-24 15:19:58 +0000 | |||
3139 | @@ -0,0 +1,554 @@ | |||
3140 | 1 | /* | ||
3141 | 2 | * Copyright © 2016 Canonical Ltd. | ||
3142 | 3 | * | ||
3143 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3144 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
3145 | 6 | * published by the Free Software Foundation. | ||
3146 | 7 | * | ||
3147 | 8 | * This program is distributed in the hope that it will be useful, | ||
3148 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3149 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3150 | 11 | * GNU General Public License for more details. | ||
3151 | 12 | * | ||
3152 | 13 | * You should have received a copy of the GNU General Public License | ||
3153 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3154 | 15 | * | ||
3155 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
3156 | 17 | */ | ||
3157 | 18 | |||
3158 | 19 | #include "test_window_manager_tools.h" | ||
3159 | 20 | |||
3160 | 21 | using namespace miral; | ||
3161 | 22 | using namespace testing; | ||
3162 | 23 | namespace mt = mir::test; | ||
3163 | 24 | |||
3164 | 25 | namespace | ||
3165 | 26 | { | ||
3166 | 27 | X const display_left{0}; | ||
3167 | 28 | Y const display_top{0}; | ||
3168 | 29 | Width const display_width{640}; | ||
3169 | 30 | Height const display_height{480}; | ||
3170 | 31 | |||
3171 | 32 | Rectangle const display_area{{display_left, display_top}, {display_width, display_height}}; | ||
3172 | 33 | |||
3173 | 34 | auto const null_window = Window{}; | ||
3174 | 35 | |||
3175 | 36 | mir::shell::SurfaceSpecification edge_attachment(Rectangle const& aux_rect, MirEdgeAttachment attachment) | ||
3176 | 37 | { | ||
3177 | 38 | mir::shell::SurfaceSpecification result; | ||
3178 | 39 | result.aux_rect = aux_rect; | ||
3179 | 40 | result.edge_attachment = attachment; | ||
3180 | 41 | return result; | ||
3181 | 42 | } | ||
3182 | 43 | |||
3183 | 44 | struct WindowPlacement : TestWindowManagerTools | ||
3184 | 45 | { | ||
3185 | 46 | Size const initial_parent_size{600, 400}; | ||
3186 | 47 | Size const initial_child_size{300, 300}; | ||
3187 | 48 | Rectangle const rectangle_away_from_rhs{{20, 20}, {20, 20}}; | ||
3188 | 49 | Rectangle const rectangle_near_rhs{{590, 20}, {10, 20}}; | ||
3189 | 50 | Rectangle const rectangle_away_from_bottom{{20, 20}, {20, 20}}; | ||
3190 | 51 | Rectangle const rectangle_near_bottom{{20, 380}, {20, 20}}; | ||
3191 | 52 | Rectangle const rectangle_near_both_sides{{0, 20}, {600, 20}}; | ||
3192 | 53 | Rectangle const rectangle_near_both_sides_and_bottom{{0, 380}, {600, 20}}; | ||
3193 | 54 | Rectangle const rectangle_near_all_sides{{0, 20}, {600, 380}}; | ||
3194 | 55 | Rectangle const rectangle_near_both_bottom_right{{400, 380}, {200, 20}}; | ||
3195 | 56 | |||
3196 | 57 | Window parent; | ||
3197 | 58 | Window child; | ||
3198 | 59 | |||
3199 | 60 | WindowSpecification modification; | ||
3200 | 61 | |||
3201 | 62 | void SetUp() override | ||
3202 | 63 | { | ||
3203 | 64 | basic_window_manager.add_display_for_testing(display_area); | ||
3204 | 65 | |||
3205 | 66 | mir::scene::SurfaceCreationParameters creation_parameters; | ||
3206 | 67 | basic_window_manager.add_session(session); | ||
3207 | 68 | |||
3208 | 69 | EXPECT_CALL(*window_manager_policy, advise_new_window(_)) | ||
3209 | 70 | .WillOnce(Invoke([this](WindowInfo const& window_info){ parent = window_info.window(); })) | ||
3210 | 71 | .WillOnce(Invoke([this](WindowInfo const& window_info){ child = window_info.window(); })); | ||
3211 | 72 | |||
3212 | 73 | creation_parameters.size = initial_parent_size; | ||
3213 | 74 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
3214 | 75 | |||
3215 | 76 | creation_parameters.type = mir_window_type_menu; | ||
3216 | 77 | creation_parameters.parent = parent; | ||
3217 | 78 | creation_parameters.size = initial_child_size; | ||
3218 | 79 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
3219 | 80 | |||
3220 | 81 | // Clear the expectations used to capture parent & child | ||
3221 | 82 | Mock::VerifyAndClearExpectations(window_manager_policy); | ||
3222 | 83 | } | ||
3223 | 84 | |||
3224 | 85 | void TearDown() override | ||
3225 | 86 | { | ||
3226 | 87 | // std::cerr << "DEBUG parent position:" << Rectangle{parent.top_left(), parent.size()} << '\n'; | ||
3227 | 88 | // std::cerr << "DEBUG child position :" << Rectangle{child.top_left(), child.size()} << '\n'; | ||
3228 | 89 | } | ||
3229 | 90 | |||
3230 | 91 | auto aux_rect_position() -> Rectangle | ||
3231 | 92 | { | ||
3232 | 93 | auto const rectangle = modification.aux_rect().value(); | ||
3233 | 94 | return {rectangle.top_left + (parent.top_left() - Point{}), rectangle.size}; | ||
3234 | 95 | } | ||
3235 | 96 | |||
3236 | 97 | auto on_top_edge() -> Point | ||
3237 | 98 | { | ||
3238 | 99 | return aux_rect_position().top_left - as_displacement(child.size()).dy; | ||
3239 | 100 | } | ||
3240 | 101 | |||
3241 | 102 | auto on_right_edge() -> Point | ||
3242 | 103 | { | ||
3243 | 104 | return aux_rect_position().top_right(); | ||
3244 | 105 | } | ||
3245 | 106 | |||
3246 | 107 | auto on_left_edge() -> Point | ||
3247 | 108 | { | ||
3248 | 109 | return aux_rect_position().top_left - as_displacement(child.size()).dx; | ||
3249 | 110 | } | ||
3250 | 111 | |||
3251 | 112 | auto on_bottom_edge() -> Point | ||
3252 | 113 | { | ||
3253 | 114 | return aux_rect_position().bottom_left(); | ||
3254 | 115 | } | ||
3255 | 116 | }; | ||
3256 | 117 | } | ||
3257 | 118 | |||
3258 | 119 | TEST_F(WindowPlacement, fixture_sets_up_parent_and_child) | ||
3259 | 120 | { | ||
3260 | 121 | ASSERT_THAT(parent, Ne(null_window)); | ||
3261 | 122 | ASSERT_THAT(parent.size(), Eq(initial_parent_size)); | ||
3262 | 123 | ASSERT_THAT(basic_window_manager.info_for(parent).children(), ElementsAre(child)); | ||
3263 | 124 | ASSERT_THAT(basic_window_manager.info_for(parent).type(), Eq(mir_window_type_normal)); | ||
3264 | 125 | |||
3265 | 126 | ASSERT_THAT(child, Ne(null_window)); | ||
3266 | 127 | ASSERT_THAT(child.size(), Eq(initial_child_size)); | ||
3267 | 128 | ASSERT_THAT(basic_window_manager.info_for(child).parent(), Eq(parent)); | ||
3268 | 129 | ASSERT_THAT(basic_window_manager.info_for(child).type(), Eq(mir_window_type_menu)); | ||
3269 | 130 | } | ||
3270 | 131 | |||
3271 | 132 | |||
3272 | 133 | /* From the Mir client API: | ||
3273 | 134 | * Positioning of the surface is specified with respect to the parent surface | ||
3274 | 135 | * via an adjacency rectangle. The server will attempt to choose an edge of the | ||
3275 | 136 | * adjacency rectangle on which to place the surface taking in to account | ||
3276 | 137 | * screen-edge proximity or similar constraints. In addition, the server can use | ||
3277 | 138 | * the edge affinity hint to consider only horizontal or only vertical adjacency | ||
3278 | 139 | * edges in the given rectangle. | ||
3279 | 140 | */ | ||
3280 | 141 | |||
3281 | 142 | TEST_F(WindowPlacement, given_aux_rect_away_from_right_side_edge_attachment_vertical_attaches_to_right_edge) | ||
3282 | 143 | { | ||
3283 | 144 | modification = edge_attachment(rectangle_away_from_rhs, mir_edge_attachment_vertical); | ||
3284 | 145 | |||
3285 | 146 | auto const expected_position = on_right_edge(); | ||
3286 | 147 | |||
3287 | 148 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3288 | 149 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3289 | 150 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3290 | 151 | } | ||
3291 | 152 | |||
3292 | 153 | TEST_F(WindowPlacement, given_aux_rect_near_right_side_edge_attachment_vertical_attaches_to_left_edge) | ||
3293 | 154 | { | ||
3294 | 155 | modification = edge_attachment(rectangle_near_rhs, mir_edge_attachment_vertical); | ||
3295 | 156 | |||
3296 | 157 | auto const expected_position = on_left_edge(); | ||
3297 | 158 | |||
3298 | 159 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3299 | 160 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3300 | 161 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3301 | 162 | } | ||
3302 | 163 | |||
3303 | 164 | TEST_F(WindowPlacement, given_aux_rect_near_both_sides_edge_attachment_vertical_attaches_to_right_edge) | ||
3304 | 165 | { | ||
3305 | 166 | modification = edge_attachment(rectangle_near_both_sides, mir_edge_attachment_vertical); | ||
3306 | 167 | |||
3307 | 168 | auto const expected_position = on_right_edge(); | ||
3308 | 169 | |||
3309 | 170 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3310 | 171 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3311 | 172 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3312 | 173 | } | ||
3313 | 174 | |||
3314 | 175 | TEST_F(WindowPlacement, given_aux_rect_away_from_bottom_edge_attachment_horizontal_attaches_to_bottom_edge) | ||
3315 | 176 | { | ||
3316 | 177 | modification = edge_attachment(rectangle_away_from_bottom, mir_edge_attachment_horizontal); | ||
3317 | 178 | |||
3318 | 179 | auto const expected_position = on_bottom_edge(); | ||
3319 | 180 | |||
3320 | 181 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3321 | 182 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3322 | 183 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3323 | 184 | } | ||
3324 | 185 | |||
3325 | 186 | TEST_F(WindowPlacement, given_aux_rect_near_bottom_edge_attachment_horizontal_attaches_to_top_edge) | ||
3326 | 187 | { | ||
3327 | 188 | modification = edge_attachment(rectangle_near_bottom, mir_edge_attachment_horizontal); | ||
3328 | 189 | |||
3329 | 190 | auto const expected_position = on_top_edge(); | ||
3330 | 191 | |||
3331 | 192 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3332 | 193 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3333 | 194 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3334 | 195 | } | ||
3335 | 196 | |||
3336 | 197 | TEST_F(WindowPlacement, given_aux_rect_near_both_sides_edge_attachment_any_attaches_to_bottom_edge) | ||
3337 | 198 | { | ||
3338 | 199 | modification = edge_attachment(rectangle_near_both_sides, mir_edge_attachment_any); | ||
3339 | 200 | |||
3340 | 201 | auto const expected_position = on_bottom_edge(); | ||
3341 | 202 | |||
3342 | 203 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3343 | 204 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3344 | 205 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3345 | 206 | } | ||
3346 | 207 | |||
3347 | 208 | TEST_F(WindowPlacement, given_aux_rect_near_both_sides_and_bottom_edge_attachment_any_attaches_to_top_edge) | ||
3348 | 209 | { | ||
3349 | 210 | modification = edge_attachment(rectangle_near_both_sides_and_bottom, mir_edge_attachment_any); | ||
3350 | 211 | |||
3351 | 212 | auto const expected_position = on_top_edge(); | ||
3352 | 213 | |||
3353 | 214 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3354 | 215 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3355 | 216 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3356 | 217 | } | ||
3357 | 218 | |||
3358 | 219 | TEST_F(WindowPlacement, given_aux_rect_near_all_sides_attachment_any_attaches_to_right_edge) | ||
3359 | 220 | { | ||
3360 | 221 | modification = edge_attachment(rectangle_near_all_sides, mir_edge_attachment_any); | ||
3361 | 222 | |||
3362 | 223 | auto const expected_position = on_right_edge(); | ||
3363 | 224 | |||
3364 | 225 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3365 | 226 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3366 | 227 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3367 | 228 | } | ||
3368 | 229 | |||
3369 | 230 | namespace | ||
3370 | 231 | { | ||
3371 | 232 | MirPlacementGravity const all_gravities[] = | ||
3372 | 233 | { | ||
3373 | 234 | mir_placement_gravity_northwest, | ||
3374 | 235 | mir_placement_gravity_north, | ||
3375 | 236 | mir_placement_gravity_northeast, | ||
3376 | 237 | mir_placement_gravity_west, | ||
3377 | 238 | mir_placement_gravity_center, | ||
3378 | 239 | mir_placement_gravity_east, | ||
3379 | 240 | mir_placement_gravity_southwest, | ||
3380 | 241 | mir_placement_gravity_south, | ||
3381 | 242 | mir_placement_gravity_southeast, | ||
3382 | 243 | }; | ||
3383 | 244 | |||
3384 | 245 | auto position_of(MirPlacementGravity rect_gravity, Rectangle rectangle) -> Point | ||
3385 | 246 | { | ||
3386 | 247 | auto const displacement = as_displacement(rectangle.size); | ||
3387 | 248 | |||
3388 | 249 | switch (rect_gravity) | ||
3389 | 250 | { | ||
3390 | 251 | case mir_placement_gravity_northwest: | ||
3391 | 252 | return rectangle.top_left; | ||
3392 | 253 | |||
3393 | 254 | case mir_placement_gravity_north: | ||
3394 | 255 | return rectangle.top_left + Displacement{0.5 * displacement.dx, 0}; | ||
3395 | 256 | |||
3396 | 257 | case mir_placement_gravity_northeast: | ||
3397 | 258 | return rectangle.top_right(); | ||
3398 | 259 | |||
3399 | 260 | case mir_placement_gravity_west: | ||
3400 | 261 | return rectangle.top_left + Displacement{0, 0.5 * displacement.dy}; | ||
3401 | 262 | |||
3402 | 263 | case mir_placement_gravity_center: | ||
3403 | 264 | return rectangle.top_left + 0.5 * displacement; | ||
3404 | 265 | |||
3405 | 266 | case mir_placement_gravity_east: | ||
3406 | 267 | return rectangle.top_right() + Displacement{0, 0.5 * displacement.dy}; | ||
3407 | 268 | |||
3408 | 269 | case mir_placement_gravity_southwest: | ||
3409 | 270 | return rectangle.bottom_left(); | ||
3410 | 271 | |||
3411 | 272 | case mir_placement_gravity_south: | ||
3412 | 273 | return rectangle.bottom_left() + Displacement{0.5 * displacement.dx, 0}; | ||
3413 | 274 | |||
3414 | 275 | case mir_placement_gravity_southeast: | ||
3415 | 276 | return rectangle.bottom_right(); | ||
3416 | 277 | |||
3417 | 278 | default: | ||
3418 | 279 | throw std::runtime_error("bad placement gravity"); | ||
3419 | 280 | } | ||
3420 | 281 | |||
3421 | 282 | } | ||
3422 | 283 | } | ||
3423 | 284 | |||
3424 | 285 | TEST_F(WindowPlacement, given_no_hints_can_attach_by_every_gravity) | ||
3425 | 286 | { | ||
3426 | 287 | modification.aux_rect() = Rectangle{{100, 50}, { 20, 20}}; | ||
3427 | 288 | modification.placement_hints() = MirPlacementHints{}; | ||
3428 | 289 | |||
3429 | 290 | for (auto const rect_gravity : all_gravities) | ||
3430 | 291 | { | ||
3431 | 292 | modification.aux_rect_placement_gravity() = rect_gravity; | ||
3432 | 293 | |||
3433 | 294 | auto const rect_anchor = position_of(rect_gravity, aux_rect_position()); | ||
3434 | 295 | |||
3435 | 296 | for (auto const window_gravity : all_gravities) | ||
3436 | 297 | { | ||
3437 | 298 | modification.window_placement_gravity() = window_gravity; | ||
3438 | 299 | |||
3439 | 300 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, _)); | ||
3440 | 301 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3441 | 302 | |||
3442 | 303 | Rectangle child_rect{child.top_left(), child.size()}; | ||
3443 | 304 | |||
3444 | 305 | EXPECT_THAT(position_of(window_gravity, child_rect), Eq(rect_anchor)) | ||
3445 | 306 | << "rect_gravity=" << rect_gravity << ", window_gravity=" << window_gravity; | ||
3446 | 307 | Mock::VerifyAndClearExpectations(window_manager_policy); | ||
3447 | 308 | } | ||
3448 | 309 | } | ||
3449 | 310 | } | ||
3450 | 311 | |||
3451 | 312 | TEST_F(WindowPlacement, given_no_hints_can_attach_by_offset_at_every_gravity) | ||
3452 | 313 | { | ||
3453 | 314 | auto const offset = Displacement{42, 13}; | ||
3454 | 315 | |||
3455 | 316 | modification.aux_rect() = Rectangle{{100, 50}, { 20, 20}}; | ||
3456 | 317 | modification.placement_hints() = MirPlacementHints{}; | ||
3457 | 318 | modification.aux_rect_placement_offset() = offset; | ||
3458 | 319 | |||
3459 | 320 | for (auto const rect_gravity : all_gravities) | ||
3460 | 321 | { | ||
3461 | 322 | modification.aux_rect_placement_gravity() = rect_gravity; | ||
3462 | 323 | |||
3463 | 324 | auto const rect_anchor = position_of(rect_gravity, aux_rect_position()) + offset; | ||
3464 | 325 | |||
3465 | 326 | for (auto const window_gravity : all_gravities) | ||
3466 | 327 | { | ||
3467 | 328 | modification.window_placement_gravity() = window_gravity; | ||
3468 | 329 | |||
3469 | 330 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, _)); | ||
3470 | 331 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3471 | 332 | |||
3472 | 333 | Rectangle child_rect{child.top_left(), child.size()}; | ||
3473 | 334 | |||
3474 | 335 | EXPECT_THAT(position_of(window_gravity, child_rect), Eq(rect_anchor)) | ||
3475 | 336 | << "rect_gravity=" << rect_gravity << ", window_gravity=" << window_gravity; | ||
3476 | 337 | Mock::VerifyAndClearExpectations(window_manager_policy); | ||
3477 | 338 | } | ||
3478 | 339 | } | ||
3479 | 340 | } | ||
3480 | 341 | |||
3481 | 342 | TEST_F(WindowPlacement, given_aux_rect_near_right_side_and_offset_placement_is_flipped) | ||
3482 | 343 | { | ||
3483 | 344 | DeltaX const x_offset{42}; | ||
3484 | 345 | DeltaY const y_offset{13}; | ||
3485 | 346 | |||
3486 | 347 | modification.aux_rect() = rectangle_near_rhs; | ||
3487 | 348 | modification.placement_hints() = mir_placement_hints_flip_x; | ||
3488 | 349 | modification.aux_rect_placement_offset() = Displacement{x_offset, y_offset}; | ||
3489 | 350 | modification.window_placement_gravity() = mir_placement_gravity_northwest; | ||
3490 | 351 | modification.aux_rect_placement_gravity() = mir_placement_gravity_northeast; | ||
3491 | 352 | |||
3492 | 353 | auto const expected_position = on_left_edge() + Displacement{-1*x_offset, y_offset}; | ||
3493 | 354 | |||
3494 | 355 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3495 | 356 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3496 | 357 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3497 | 358 | } | ||
3498 | 359 | |||
3499 | 360 | TEST_F(WindowPlacement, given_aux_rect_near_bottom_and_offset_placement_is_flipped) | ||
3500 | 361 | { | ||
3501 | 362 | DeltaX const x_offset{42}; | ||
3502 | 363 | DeltaY const y_offset{13}; | ||
3503 | 364 | |||
3504 | 365 | modification.aux_rect() = rectangle_near_bottom; | ||
3505 | 366 | modification.placement_hints() = mir_placement_hints_flip_y; | ||
3506 | 367 | modification.aux_rect_placement_offset() = Displacement{x_offset, y_offset}; | ||
3507 | 368 | modification.window_placement_gravity() = mir_placement_gravity_northwest; | ||
3508 | 369 | modification.aux_rect_placement_gravity() = mir_placement_gravity_southwest; | ||
3509 | 370 | |||
3510 | 371 | auto const expected_position = on_top_edge() + Displacement{x_offset, -1*y_offset}; | ||
3511 | 372 | |||
3512 | 373 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3513 | 374 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3514 | 375 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3515 | 376 | } | ||
3516 | 377 | |||
3517 | 378 | TEST_F(WindowPlacement, given_aux_rect_near_bottom_right_and_offset_placement_is_flipped_both_ways) | ||
3518 | 379 | { | ||
3519 | 380 | Displacement const displacement{42, 13}; | ||
3520 | 381 | |||
3521 | 382 | modification.aux_rect() = rectangle_near_both_bottom_right; | ||
3522 | 383 | modification.placement_hints() = mir_placement_hints_flip_any; | ||
3523 | 384 | modification.aux_rect_placement_offset() = displacement; | ||
3524 | 385 | modification.window_placement_gravity() = mir_placement_gravity_northwest; | ||
3525 | 386 | modification.aux_rect_placement_gravity() = mir_placement_gravity_southeast; | ||
3526 | 387 | |||
3527 | 388 | auto const expected_position = aux_rect_position().top_left - as_displacement(child.size()) - displacement; | ||
3528 | 389 | |||
3529 | 390 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3530 | 391 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3531 | 392 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3532 | 393 | } | ||
3533 | 394 | |||
3534 | 395 | TEST_F(WindowPlacement, given_aux_rect_near_right_side_placement_can_slide_in_x) | ||
3535 | 396 | { | ||
3536 | 397 | modification.aux_rect() = rectangle_near_rhs; | ||
3537 | 398 | modification.placement_hints() = mir_placement_hints_slide_x; | ||
3538 | 399 | modification.window_placement_gravity() = mir_placement_gravity_northwest; | ||
3539 | 400 | modification.aux_rect_placement_gravity() = mir_placement_gravity_northeast; | ||
3540 | 401 | |||
3541 | 402 | Point const expected_position{display_area.top_right().x - as_displacement(child.size()).dx, aux_rect_position().top_left.y}; | ||
3542 | 403 | |||
3543 | 404 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3544 | 405 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3545 | 406 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3546 | 407 | } | ||
3547 | 408 | |||
3548 | 409 | TEST_F(WindowPlacement, given_aux_rect_near_left_side_placement_can_slide_in_x) | ||
3549 | 410 | { | ||
3550 | 411 | Rectangle const rectangle_near_left_side{{0, 20}, {20, 20}}; | ||
3551 | 412 | |||
3552 | 413 | modification.aux_rect() = rectangle_near_left_side; | ||
3553 | 414 | modification.placement_hints() = mir_placement_hints_slide_x; | ||
3554 | 415 | modification.window_placement_gravity() = mir_placement_gravity_northeast; | ||
3555 | 416 | modification.aux_rect_placement_gravity() = mir_placement_gravity_northwest; | ||
3556 | 417 | |||
3557 | 418 | Point const expected_position{display_area.top_left.x, aux_rect_position().top_left.y}; | ||
3558 | 419 | |||
3559 | 420 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3560 | 421 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3561 | 422 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3562 | 423 | } | ||
3563 | 424 | |||
3564 | 425 | TEST_F(WindowPlacement, given_aux_rect_near_bottom_placement_can_slide_in_y) | ||
3565 | 426 | { | ||
3566 | 427 | modification.aux_rect() = rectangle_near_bottom; | ||
3567 | 428 | modification.placement_hints() = mir_placement_hints_slide_y; | ||
3568 | 429 | modification.window_placement_gravity() = mir_placement_gravity_northwest; | ||
3569 | 430 | modification.aux_rect_placement_gravity() = mir_placement_gravity_southwest; | ||
3570 | 431 | |||
3571 | 432 | Point const expected_position{ | ||
3572 | 433 | aux_rect_position().top_left.x, | ||
3573 | 434 | (display_area.bottom_left() - as_displacement(child.size())).y}; | ||
3574 | 435 | |||
3575 | 436 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3576 | 437 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3577 | 438 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3578 | 439 | } | ||
3579 | 440 | |||
3580 | 441 | TEST_F(WindowPlacement, given_aux_rect_near_top_placement_can_slide_in_y) | ||
3581 | 442 | { | ||
3582 | 443 | modification.aux_rect() = rectangle_near_all_sides; | ||
3583 | 444 | modification.placement_hints() = mir_placement_hints_slide_y; | ||
3584 | 445 | modification.window_placement_gravity() = mir_placement_gravity_southwest; | ||
3585 | 446 | modification.aux_rect_placement_gravity() = mir_placement_gravity_northwest; | ||
3586 | 447 | |||
3587 | 448 | Point const expected_position{aux_rect_position().top_left.x, display_area.top_left.y}; | ||
3588 | 449 | |||
3589 | 450 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3590 | 451 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3591 | 452 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3592 | 453 | } | ||
3593 | 454 | |||
3594 | 455 | TEST_F(WindowPlacement, given_aux_rect_near_bottom_right_and_offset_placement_can_slide_in_x_and_y) | ||
3595 | 456 | { | ||
3596 | 457 | modification.aux_rect() = rectangle_near_both_bottom_right; | ||
3597 | 458 | modification.placement_hints() = mir_placement_hints_slide_any; | ||
3598 | 459 | modification.window_placement_gravity() = mir_placement_gravity_northwest; | ||
3599 | 460 | modification.aux_rect_placement_gravity() = mir_placement_gravity_southwest; | ||
3600 | 461 | |||
3601 | 462 | auto const expected_position = display_area.bottom_right() - as_displacement(child.size()); | ||
3602 | 463 | |||
3603 | 464 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3604 | 465 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3605 | 466 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3606 | 467 | } | ||
3607 | 468 | |||
3608 | 469 | TEST_F(WindowPlacement, given_aux_rect_near_right_side_placement_can_resize_in_x) | ||
3609 | 470 | { | ||
3610 | 471 | modification.aux_rect() = rectangle_near_rhs; | ||
3611 | 472 | modification.placement_hints() = mir_placement_hints_resize_x; | ||
3612 | 473 | modification.window_placement_gravity() = mir_placement_gravity_northwest; | ||
3613 | 474 | modification.aux_rect_placement_gravity() = mir_placement_gravity_northeast; | ||
3614 | 475 | |||
3615 | 476 | auto const expected_position = aux_rect_position().top_right(); | ||
3616 | 477 | Size const expected_size{as_size(display_area.bottom_right()-aux_rect_position().bottom_right()).width, child.size().height}; | ||
3617 | 478 | |||
3618 | 479 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3619 | 480 | EXPECT_CALL(*window_manager_policy, advise_resize(_, expected_size)); | ||
3620 | 481 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3621 | 482 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3622 | 483 | ASSERT_THAT(child.size(), Eq(expected_size)); | ||
3623 | 484 | } | ||
3624 | 485 | |||
3625 | 486 | TEST_F(WindowPlacement, given_aux_rect_near_left_side_placement_can_resize_in_x) | ||
3626 | 487 | { | ||
3627 | 488 | Rectangle const rectangle_near_left_side{{0, 20}, {20, 20}}; | ||
3628 | 489 | |||
3629 | 490 | modification.aux_rect() = rectangle_near_left_side; | ||
3630 | 491 | modification.placement_hints() = mir_placement_hints_resize_x; | ||
3631 | 492 | modification.window_placement_gravity() = mir_placement_gravity_northeast; | ||
3632 | 493 | modification.aux_rect_placement_gravity() = mir_placement_gravity_northwest; | ||
3633 | 494 | |||
3634 | 495 | Point const expected_position{display_area.top_left.x, aux_rect_position().top_left.y}; | ||
3635 | 496 | Size const expected_size{as_size(aux_rect_position().top_left-display_area.top_left).width, child.size().height}; | ||
3636 | 497 | |||
3637 | 498 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3638 | 499 | EXPECT_CALL(*window_manager_policy, advise_resize(_, expected_size)); | ||
3639 | 500 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3640 | 501 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3641 | 502 | ASSERT_THAT(child.size(), Eq(expected_size)); | ||
3642 | 503 | } | ||
3643 | 504 | |||
3644 | 505 | TEST_F(WindowPlacement, given_aux_rect_near_bottom_placement_can_resize_in_y) | ||
3645 | 506 | { | ||
3646 | 507 | modification.aux_rect() = rectangle_near_bottom; | ||
3647 | 508 | modification.placement_hints() = mir_placement_hints_resize_y; | ||
3648 | 509 | modification.window_placement_gravity() = mir_placement_gravity_northwest; | ||
3649 | 510 | modification.aux_rect_placement_gravity() = mir_placement_gravity_southwest; | ||
3650 | 511 | |||
3651 | 512 | auto const expected_position = aux_rect_position().bottom_left(); | ||
3652 | 513 | Size const expected_size{child.size().width, as_size(display_area.bottom_left()-aux_rect_position().bottom_left()).height}; | ||
3653 | 514 | |||
3654 | 515 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3655 | 516 | EXPECT_CALL(*window_manager_policy, advise_resize(_, expected_size)); | ||
3656 | 517 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3657 | 518 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3658 | 519 | ASSERT_THAT(child.size(), Eq(expected_size)); | ||
3659 | 520 | } | ||
3660 | 521 | |||
3661 | 522 | TEST_F(WindowPlacement, given_aux_rect_near_top_placement_can_resize_in_y) | ||
3662 | 523 | { | ||
3663 | 524 | modification.aux_rect() = rectangle_near_all_sides; | ||
3664 | 525 | modification.placement_hints() = mir_placement_hints_resize_y; | ||
3665 | 526 | modification.window_placement_gravity() = mir_placement_gravity_southwest; | ||
3666 | 527 | modification.aux_rect_placement_gravity() = mir_placement_gravity_northwest; | ||
3667 | 528 | |||
3668 | 529 | Point const expected_position{aux_rect_position().top_left.x, display_area.top_left.y}; | ||
3669 | 530 | Size const expected_size{child.size().width, as_size(aux_rect_position().top_left-display_area.top_left).height}; | ||
3670 | 531 | |||
3671 | 532 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3672 | 533 | EXPECT_CALL(*window_manager_policy, advise_resize(_, expected_size)); | ||
3673 | 534 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3674 | 535 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3675 | 536 | ASSERT_THAT(child.size(), Eq(expected_size)); | ||
3676 | 537 | } | ||
3677 | 538 | |||
3678 | 539 | TEST_F(WindowPlacement, given_aux_rect_near_bottom_right_and_offset_placement_can_resize_in_x_and_y) | ||
3679 | 540 | { | ||
3680 | 541 | modification.aux_rect() = rectangle_near_both_bottom_right; | ||
3681 | 542 | modification.placement_hints() = mir_placement_hints_resize_any; | ||
3682 | 543 | modification.window_placement_gravity() = mir_placement_gravity_northwest; | ||
3683 | 544 | modification.aux_rect_placement_gravity() = mir_placement_gravity_southeast; | ||
3684 | 545 | |||
3685 | 546 | auto const expected_position = aux_rect_position().bottom_right(); | ||
3686 | 547 | auto const expected_size = as_size(display_area.bottom_right()-expected_position); | ||
3687 | 548 | |||
3688 | 549 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3689 | 550 | EXPECT_CALL(*window_manager_policy, advise_resize(_, expected_size)); | ||
3690 | 551 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3691 | 552 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3692 | 553 | ASSERT_THAT(child.size(), Eq(expected_size)); | ||
3693 | 554 | } | ||
3694 | 0 | 555 | ||
3695 | === added file 'tests/miral/window_placement_anchors_to_parent.cpp' | |||
3696 | --- tests/miral/window_placement_anchors_to_parent.cpp 1970-01-01 00:00:00 +0000 | |||
3697 | +++ tests/miral/window_placement_anchors_to_parent.cpp 2017-08-24 15:19:58 +0000 | |||
3698 | @@ -0,0 +1,208 @@ | |||
3699 | 1 | /* | ||
3700 | 2 | * Copyright © 2016 Canonical Ltd. | ||
3701 | 3 | * | ||
3702 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3703 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
3704 | 6 | * published by the Free Software Foundation. | ||
3705 | 7 | * | ||
3706 | 8 | * This program is distributed in the hope that it will be useful, | ||
3707 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3708 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3709 | 11 | * GNU General Public License for more details. | ||
3710 | 12 | * | ||
3711 | 13 | * You should have received a copy of the GNU General Public License | ||
3712 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3713 | 15 | * | ||
3714 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
3715 | 17 | */ | ||
3716 | 18 | |||
3717 | 19 | #include "test_window_manager_tools.h" | ||
3718 | 20 | |||
3719 | 21 | using namespace miral; | ||
3720 | 22 | using namespace testing; | ||
3721 | 23 | namespace mt = mir::test; | ||
3722 | 24 | |||
3723 | 25 | namespace | ||
3724 | 26 | { | ||
3725 | 27 | auto const display_area = Rectangle{{0, 0}, {800, 600}}; | ||
3726 | 28 | auto const parent_width = 400; | ||
3727 | 29 | auto const parent_height = 300; | ||
3728 | 30 | |||
3729 | 31 | auto placement( | ||
3730 | 32 | Rectangle const& aux_rect, | ||
3731 | 33 | MirPlacementGravity aux_rect_placement_gravity, | ||
3732 | 34 | MirPlacementGravity window_placement_gravity, | ||
3733 | 35 | MirPlacementHints placement_hints) -> WindowSpecification | ||
3734 | 36 | { | ||
3735 | 37 | WindowSpecification modification; | ||
3736 | 38 | |||
3737 | 39 | modification.aux_rect() = aux_rect; | ||
3738 | 40 | modification.aux_rect_placement_gravity() = aux_rect_placement_gravity; | ||
3739 | 41 | modification.window_placement_gravity() = window_placement_gravity; | ||
3740 | 42 | modification.placement_hints() = placement_hints; | ||
3741 | 43 | |||
3742 | 44 | return modification; | ||
3743 | 45 | } | ||
3744 | 46 | |||
3745 | 47 | struct WindowPlacementAnchorsToParent : TestWindowManagerTools | ||
3746 | 48 | { | ||
3747 | 49 | Size const parent_size{parent_width, parent_height}; | ||
3748 | 50 | Size const initial_child_size{100, 50}; | ||
3749 | 51 | |||
3750 | 52 | Window parent; | ||
3751 | 53 | Window child; | ||
3752 | 54 | |||
3753 | 55 | Point parent_position; | ||
3754 | 56 | WindowSpecification modification; | ||
3755 | 57 | |||
3756 | 58 | void SetUp() override | ||
3757 | 59 | { | ||
3758 | 60 | TestWindowManagerTools::SetUp(); | ||
3759 | 61 | |||
3760 | 62 | basic_window_manager.add_display_for_testing(display_area); | ||
3761 | 63 | |||
3762 | 64 | mir::scene::SurfaceCreationParameters creation_parameters; | ||
3763 | 65 | basic_window_manager.add_session(session); | ||
3764 | 66 | |||
3765 | 67 | EXPECT_CALL(*window_manager_policy, advise_new_window(_)) | ||
3766 | 68 | .WillOnce(Invoke([this](WindowInfo const& window_info){ parent = window_info.window(); })) | ||
3767 | 69 | .WillOnce(Invoke([this](WindowInfo const& window_info){ child = window_info.window(); })); | ||
3768 | 70 | |||
3769 | 71 | creation_parameters.size = parent_size; | ||
3770 | 72 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
3771 | 73 | |||
3772 | 74 | creation_parameters.type = mir_window_type_tip; | ||
3773 | 75 | creation_parameters.parent = parent; | ||
3774 | 76 | creation_parameters.size = initial_child_size; | ||
3775 | 77 | basic_window_manager.add_surface(session, creation_parameters, &create_surface); | ||
3776 | 78 | |||
3777 | 79 | // Clear the expectations used to capture parent & child | ||
3778 | 80 | Mock::VerifyAndClearExpectations(window_manager_policy); | ||
3779 | 81 | |||
3780 | 82 | parent_position = parent.top_left(); | ||
3781 | 83 | } | ||
3782 | 84 | }; | ||
3783 | 85 | } | ||
3784 | 86 | |||
3785 | 87 | // there was an IRC conversation to sort this out between myself William and Thomas. | ||
3786 | 88 | // I think the resulting consensus was: | ||
3787 | 89 | // | ||
3788 | 90 | // 1. Mir will constrain the placement anchor of the aux_rect to the parent | ||
3789 | 91 | // surface. I don't think we agreed exactly how (e.g. do we "clip" the | ||
3790 | 92 | // rect? What happens if there is *no* intersection?) | ||
3791 | 93 | // | ||
3792 | 94 | // 2. Mir will constrain the the offset placement anchor to the parent surface. | ||
3793 | 95 | // Again I don't think we agreed how. (Slide it horizontally and/or vertically | ||
3794 | 96 | // the minimum amount?) | ||
3795 | 97 | // - alan_g (mir-devel, Mon, 5 Sep 2016 17:21:01 +0100) | ||
3796 | 98 | // | ||
3797 | 99 | // What we have implemented is to constrain the result of offsetting to the parent. That | ||
3798 | 100 | // seems to provide reasonable behaviour. Are there test cases that require something more? | ||
3799 | 101 | |||
3800 | 102 | TEST_F(WindowPlacementAnchorsToParent, given_rect_anchor_right_of_parent_client_is_anchored_to_parent) | ||
3801 | 103 | { | ||
3802 | 104 | auto const rect_size = 10; | ||
3803 | 105 | Rectangle const overlapping_right{{parent_width-rect_size/2, parent_height/2}, {rect_size, rect_size}}; | ||
3804 | 106 | |||
3805 | 107 | modification = placement( | ||
3806 | 108 | overlapping_right, | ||
3807 | 109 | mir_placement_gravity_northeast, | ||
3808 | 110 | mir_placement_gravity_northwest, | ||
3809 | 111 | MirPlacementHints(mir_placement_hints_slide_y|mir_placement_hints_resize_x)); | ||
3810 | 112 | |||
3811 | 113 | auto const expected_position = parent_position + Displacement{parent_width, parent_height/2}; | ||
3812 | 114 | |||
3813 | 115 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3814 | 116 | EXPECT_CALL(*window_manager_policy, advise_resize(_, _)).Times(0); | ||
3815 | 117 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3816 | 118 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3817 | 119 | ASSERT_THAT(child.size(), Eq(initial_child_size)); | ||
3818 | 120 | } | ||
3819 | 121 | |||
3820 | 122 | TEST_F(WindowPlacementAnchorsToParent, given_rect_anchor_above_parent_client_is_anchored_to_parent) | ||
3821 | 123 | { | ||
3822 | 124 | auto const rect_size = 10; | ||
3823 | 125 | Rectangle const overlapping_above{{parent_width/2, -rect_size/2}, {rect_size, rect_size}}; | ||
3824 | 126 | |||
3825 | 127 | modification = placement( | ||
3826 | 128 | overlapping_above, | ||
3827 | 129 | mir_placement_gravity_northeast, | ||
3828 | 130 | mir_placement_gravity_southeast, | ||
3829 | 131 | mir_placement_hints_slide_x); | ||
3830 | 132 | |||
3831 | 133 | auto const expected_position = parent_position + DeltaX{parent_width/2 + rect_size} | ||
3832 | 134 | - as_displacement(initial_child_size); | ||
3833 | 135 | |||
3834 | 136 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3835 | 137 | EXPECT_CALL(*window_manager_policy, advise_resize(_, _)).Times(0); | ||
3836 | 138 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3837 | 139 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3838 | 140 | ASSERT_THAT(child.size(), Eq(initial_child_size)); | ||
3839 | 141 | } | ||
3840 | 142 | |||
3841 | 143 | TEST_F(WindowPlacementAnchorsToParent, given_offset_right_of_parent_client_is_anchored_to_parent) | ||
3842 | 144 | { | ||
3843 | 145 | auto const rect_size = 10; | ||
3844 | 146 | Rectangle const mid_right{{parent_width-rect_size, parent_height/2}, {rect_size, rect_size}}; | ||
3845 | 147 | |||
3846 | 148 | modification = placement( | ||
3847 | 149 | mid_right, | ||
3848 | 150 | mir_placement_gravity_northeast, | ||
3849 | 151 | mir_placement_gravity_northwest, | ||
3850 | 152 | MirPlacementHints(mir_placement_hints_slide_y|mir_placement_hints_resize_x)); | ||
3851 | 153 | |||
3852 | 154 | modification.aux_rect_placement_offset() = Displacement{rect_size, 0}; | ||
3853 | 155 | |||
3854 | 156 | auto const expected_position = parent_position + Displacement{parent_width, parent_height/2}; | ||
3855 | 157 | |||
3856 | 158 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3857 | 159 | EXPECT_CALL(*window_manager_policy, advise_resize(_, _)).Times(0); | ||
3858 | 160 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3859 | 161 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3860 | 162 | ASSERT_THAT(child.size(), Eq(initial_child_size)); | ||
3861 | 163 | } | ||
3862 | 164 | |||
3863 | 165 | TEST_F(WindowPlacementAnchorsToParent, given_offset_above_parent_client_is_anchored_to_parent) | ||
3864 | 166 | { | ||
3865 | 167 | auto const rect_size = 10; | ||
3866 | 168 | Rectangle const mid_top{{parent_width/2, 0}, {rect_size, rect_size}}; | ||
3867 | 169 | |||
3868 | 170 | modification = placement( | ||
3869 | 171 | mid_top, | ||
3870 | 172 | mir_placement_gravity_northeast, | ||
3871 | 173 | mir_placement_gravity_southeast, | ||
3872 | 174 | mir_placement_hints_slide_x); | ||
3873 | 175 | |||
3874 | 176 | modification.aux_rect_placement_offset() = Displacement{0, -rect_size}; | ||
3875 | 177 | |||
3876 | 178 | auto const expected_position = parent_position + DeltaX{parent_width/2 + rect_size} | ||
3877 | 179 | - as_displacement(initial_child_size); | ||
3878 | 180 | |||
3879 | 181 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3880 | 182 | EXPECT_CALL(*window_manager_policy, advise_resize(_, _)).Times(0); | ||
3881 | 183 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3882 | 184 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3883 | 185 | ASSERT_THAT(child.size(), Eq(initial_child_size)); | ||
3884 | 186 | } | ||
3885 | 187 | |||
3886 | 188 | TEST_F(WindowPlacementAnchorsToParent, given_rect_and_offset_below_left_parent_client_is_anchored_to_parent) | ||
3887 | 189 | { | ||
3888 | 190 | auto const rect_size = 10; | ||
3889 | 191 | Rectangle const below_left{{-rect_size, parent_height}, {rect_size, rect_size}}; | ||
3890 | 192 | |||
3891 | 193 | modification = placement( | ||
3892 | 194 | below_left, | ||
3893 | 195 | mir_placement_gravity_southwest, | ||
3894 | 196 | mir_placement_gravity_northeast, | ||
3895 | 197 | mir_placement_hints_resize_any); | ||
3896 | 198 | |||
3897 | 199 | modification.aux_rect_placement_offset() = Displacement{-rect_size, rect_size}; | ||
3898 | 200 | |||
3899 | 201 | auto const expected_position = parent_position + DeltaY{parent_height} - as_displacement(initial_child_size).dx; | ||
3900 | 202 | |||
3901 | 203 | EXPECT_CALL(*window_manager_policy, advise_move_to(_, expected_position)); | ||
3902 | 204 | EXPECT_CALL(*window_manager_policy, advise_resize(_, _)).Times(0); | ||
3903 | 205 | basic_window_manager.modify_window(basic_window_manager.info_for(child), modification); | ||
3904 | 206 | ASSERT_THAT(child.top_left(), Eq(expected_position)); | ||
3905 | 207 | ASSERT_THAT(child.size(), Eq(initial_child_size)); | ||
3906 | 208 | } | ||
3907 | 0 | 209 | ||
3908 | === added file 'tests/miral/window_placement_client_api.cpp' | |||
3909 | --- tests/miral/window_placement_client_api.cpp 1970-01-01 00:00:00 +0000 | |||
3910 | +++ tests/miral/window_placement_client_api.cpp 2017-08-24 15:19:58 +0000 | |||
3911 | @@ -0,0 +1,141 @@ | |||
3912 | 1 | /* | ||
3913 | 2 | * Copyright © 2016 Canonical Ltd. | ||
3914 | 3 | * | ||
3915 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3916 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
3917 | 6 | * published by the Free Software Foundation. | ||
3918 | 7 | * | ||
3919 | 8 | * This program is distributed in the hope that it will be useful, | ||
3920 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3921 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3922 | 11 | * GNU General Public License for more details. | ||
3923 | 12 | * | ||
3924 | 13 | * You should have received a copy of the GNU General Public License | ||
3925 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3926 | 15 | * | ||
3927 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
3928 | 17 | */ | ||
3929 | 18 | |||
3930 | 19 | #include <mir_toolkit/events/window_placement.h> | ||
3931 | 20 | |||
3932 | 21 | #include <mir/client/window_spec.h> | ||
3933 | 22 | #include <mir/client/window.h> | ||
3934 | 23 | |||
3935 | 24 | #include <mir/test/signal.h> | ||
3936 | 25 | #include "test_server.h" | ||
3937 | 26 | |||
3938 | 27 | #include <gtest/gtest.h> | ||
3939 | 28 | #include <gmock/gmock.h> | ||
3940 | 29 | |||
3941 | 30 | using namespace std::literals::chrono_literals; | ||
3942 | 31 | |||
3943 | 32 | using namespace testing; | ||
3944 | 33 | namespace mt = mir::test; | ||
3945 | 34 | namespace mtf = mir_test_framework; | ||
3946 | 35 | |||
3947 | 36 | using namespace mir::client; | ||
3948 | 37 | |||
3949 | 38 | namespace | ||
3950 | 39 | { | ||
3951 | 40 | struct WindowPlacementClientAPI : miral::TestServer | ||
3952 | 41 | { | ||
3953 | 42 | void SetUp() override | ||
3954 | 43 | { | ||
3955 | 44 | miral::TestServer::SetUp(); | ||
3956 | 45 | |||
3957 | 46 | char const* const test_name = __PRETTY_FUNCTION__; | ||
3958 | 47 | |||
3959 | 48 | connection = connect_client(test_name); | ||
3960 | 49 | auto spec = WindowSpec::for_normal_window(connection, 400, 400) | ||
3961 | 50 | .set_name(test_name); | ||
3962 | 51 | |||
3963 | 52 | parent = spec.create_window(); | ||
3964 | 53 | } | ||
3965 | 54 | |||
3966 | 55 | void TearDown() override | ||
3967 | 56 | { | ||
3968 | 57 | child.reset(); | ||
3969 | 58 | parent.reset(); | ||
3970 | 59 | connection.reset(); | ||
3971 | 60 | |||
3972 | 61 | miral::TestServer::TearDown(); | ||
3973 | 62 | } | ||
3974 | 63 | |||
3975 | 64 | Connection connection; | ||
3976 | 65 | Window parent; | ||
3977 | 66 | Window child; | ||
3978 | 67 | }; | ||
3979 | 68 | } | ||
3980 | 69 | |||
3981 | 70 | namespace | ||
3982 | 71 | { | ||
3983 | 72 | struct CheckPlacement | ||
3984 | 73 | { | ||
3985 | 74 | CheckPlacement(int left, int top, unsigned int width, unsigned int height) : | ||
3986 | 75 | expected{left, top, width, height} {} | ||
3987 | 76 | |||
3988 | 77 | void check(MirWindowPlacementEvent const* placement_event) | ||
3989 | 78 | { | ||
3990 | 79 | auto relative_position = mir_window_placement_get_relative_position(placement_event); | ||
3991 | 80 | |||
3992 | 81 | EXPECT_THAT(relative_position.top, Eq(expected.top)); | ||
3993 | 82 | EXPECT_THAT(relative_position.left, Eq(expected.left)); | ||
3994 | 83 | EXPECT_THAT(relative_position.height, Eq(expected.height)); | ||
3995 | 84 | EXPECT_THAT(relative_position.width, Eq(expected.width)); | ||
3996 | 85 | |||
3997 | 86 | received.raise(); | ||
3998 | 87 | } | ||
3999 | 88 | |||
4000 | 89 | static void callback(MirWindow* /*surface*/, MirEvent const* event, void* context) | ||
4001 | 90 | { | ||
4002 | 91 | if (mir_event_get_type(event) == mir_event_type_window_placement) | ||
4003 | 92 | { | ||
4004 | 93 | auto const placement_event = mir_event_get_window_placement_event(event); | ||
4005 | 94 | static_cast<CheckPlacement*>(context)->check(placement_event); | ||
4006 | 95 | } | ||
4007 | 96 | } | ||
4008 | 97 | |||
4009 | 98 | ~CheckPlacement() | ||
4010 | 99 | { | ||
4011 | 100 | EXPECT_TRUE(received.wait_for(400ms)); | ||
4012 | 101 | } | ||
4013 | 102 | |||
4014 | 103 | private: | ||
4015 | 104 | MirRectangle const expected; | ||
4016 | 105 | mt::Signal received; | ||
4017 | 106 | }; | ||
4018 | 107 | } | ||
4019 | 108 | |||
4020 | 109 | // It would be nice to verify creation and movement placement notifications in separate tests, | ||
4021 | 110 | // However, to avoid a racy test, we need to detect both anyway. This seems like a good trade-off. | ||
4022 | 111 | TEST_F(WindowPlacementClientAPI, given_menu_placements_away_from_edges_when_notified_result_is_as_requested) | ||
4023 | 112 | { | ||
4024 | 113 | char const* const test_name = __PRETTY_FUNCTION__; | ||
4025 | 114 | int const dx = 30; | ||
4026 | 115 | int const dy = 40; | ||
4027 | 116 | |||
4028 | 117 | // initial placement | ||
4029 | 118 | { | ||
4030 | 119 | MirRectangle aux_rect{10, 20, 3, 4}; | ||
4031 | 120 | CheckPlacement expected{aux_rect.left+(int)aux_rect.width, aux_rect.top, dx, dy}; | ||
4032 | 121 | |||
4033 | 122 | auto const spec = WindowSpec:: | ||
4034 | 123 | for_menu(connection, dx, dy, parent, &aux_rect, mir_edge_attachment_any) | ||
4035 | 124 | .set_event_handler(&CheckPlacement::callback, &expected) | ||
4036 | 125 | .set_name(test_name); | ||
4037 | 126 | |||
4038 | 127 | child = spec.create_window(); | ||
4039 | 128 | } | ||
4040 | 129 | |||
4041 | 130 | // subsequent movement | ||
4042 | 131 | { | ||
4043 | 132 | MirRectangle aux_rect{50, 60, 5, 7}; | ||
4044 | 133 | CheckPlacement expected{aux_rect.left-dx, aux_rect.top, dx, dy}; | ||
4045 | 134 | |||
4046 | 135 | auto const spec = WindowSpec::for_changes(connection) | ||
4047 | 136 | .set_event_handler(&CheckPlacement::callback, &expected) | ||
4048 | 137 | .set_placement(&aux_rect, mir_placement_gravity_northwest, mir_placement_gravity_northeast, mir_placement_hints_flip_x, 0, 0); | ||
4049 | 138 | |||
4050 | 139 | spec.apply_to(child); | ||
4051 | 140 | } | ||
4052 | 141 | } | ||
4053 | 0 | 142 | ||
4054 | === added file 'tests/miral/window_properties.cpp' | |||
4055 | --- tests/miral/window_properties.cpp 1970-01-01 00:00:00 +0000 | |||
4056 | +++ tests/miral/window_properties.cpp 2017-08-24 15:19:58 +0000 | |||
4057 | @@ -0,0 +1,164 @@ | |||
4058 | 1 | /* | ||
4059 | 2 | * Copyright © 2017 Canonical Ltd. | ||
4060 | 3 | * | ||
4061 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
4062 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
4063 | 6 | * published by the Free Software Foundation. | ||
4064 | 7 | * | ||
4065 | 8 | * This program is distributed in the hope that it will be useful, | ||
4066 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4067 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4068 | 11 | * GNU General Public License for more details. | ||
4069 | 12 | * | ||
4070 | 13 | * You should have received a copy of the GNU General Public License | ||
4071 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
4072 | 15 | * | ||
4073 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
4074 | 17 | */ | ||
4075 | 18 | |||
4076 | 19 | #include <miral/window_manager_tools.h> | ||
4077 | 20 | |||
4078 | 21 | #include <mir/client/surface.h> | ||
4079 | 22 | #include <mir/client/window.h> | ||
4080 | 23 | #include <mir/client/window_spec.h> | ||
4081 | 24 | #include <mir_toolkit/mir_buffer_stream.h> | ||
4082 | 25 | |||
4083 | 26 | #include "test_server.h" | ||
4084 | 27 | |||
4085 | 28 | #include <gmock/gmock.h> | ||
4086 | 29 | #include <mir/test/signal.h> | ||
4087 | 30 | |||
4088 | 31 | |||
4089 | 32 | using namespace testing; | ||
4090 | 33 | using namespace mir::client; | ||
4091 | 34 | using namespace std::chrono_literals; | ||
4092 | 35 | using miral::WindowManagerTools; | ||
4093 | 36 | |||
4094 | 37 | namespace | ||
4095 | 38 | { | ||
4096 | 39 | std::string const a_window{"a window"}; | ||
4097 | 40 | |||
4098 | 41 | struct WindowProperties; | ||
4099 | 42 | |||
4100 | 43 | struct WindowProperties : public miral::TestServer | ||
4101 | 44 | { | ||
4102 | 45 | void SetUp() override | ||
4103 | 46 | { | ||
4104 | 47 | miral::TestServer::SetUp(); | ||
4105 | 48 | client_connection = connect_client("WindowProperties"); | ||
4106 | 49 | surface = Surface{mir_connection_create_render_surface_sync(client_connection, 40, 40)}; | ||
4107 | 50 | } | ||
4108 | 51 | |||
4109 | 52 | void TearDown() override | ||
4110 | 53 | { | ||
4111 | 54 | surface.reset(); | ||
4112 | 55 | client_connection.reset(); | ||
4113 | 56 | miral::TestServer::TearDown(); | ||
4114 | 57 | } | ||
4115 | 58 | |||
4116 | 59 | Connection client_connection; | ||
4117 | 60 | Surface surface; | ||
4118 | 61 | |||
4119 | 62 | std::unique_ptr<TestWindowManagerPolicy> build_window_manager_policy(WindowManagerTools const& tools) override; | ||
4120 | 63 | |||
4121 | 64 | void paint(Surface const& surface) | ||
4122 | 65 | { | ||
4123 | 66 | mir_buffer_stream_swap_buffers_sync( | ||
4124 | 67 | mir_render_surface_get_buffer_stream(surface, 50, 50, mir_pixel_format_argb_8888)); | ||
4125 | 68 | } | ||
4126 | 69 | |||
4127 | 70 | mir::test::Signal window_ready; | ||
4128 | 71 | }; | ||
4129 | 72 | |||
4130 | 73 | auto WindowProperties::build_window_manager_policy(WindowManagerTools const& tools) | ||
4131 | 74 | -> std::unique_ptr<miral::TestServer::TestWindowManagerPolicy> | ||
4132 | 75 | { | ||
4133 | 76 | struct MockWindowManagerPolicy : miral::TestServer::TestWindowManagerPolicy | ||
4134 | 77 | { | ||
4135 | 78 | using miral::TestServer::TestWindowManagerPolicy::TestWindowManagerPolicy; | ||
4136 | 79 | MOCK_METHOD1(advise_focus_gained, void (miral::WindowInfo const& window_info)); | ||
4137 | 80 | }; | ||
4138 | 81 | |||
4139 | 82 | auto result = std::make_unique<MockWindowManagerPolicy>(tools, *this); | ||
4140 | 83 | |||
4141 | 84 | ON_CALL(*result, advise_focus_gained(_)) | ||
4142 | 85 | .WillByDefault(InvokeWithoutArgs([this] { window_ready.raise(); })); | ||
4143 | 86 | |||
4144 | 87 | return std::move(result); | ||
4145 | 88 | } | ||
4146 | 89 | } | ||
4147 | 90 | |||
4148 | 91 | TEST_F(WindowProperties, on_creation_default_shell_chrome_is_normal) | ||
4149 | 92 | { | ||
4150 | 93 | auto const window = WindowSpec::for_normal_window(client_connection, 50, 50) | ||
4151 | 94 | .set_name(a_window.c_str()) | ||
4152 | 95 | .add_surface(surface, 50, 50, 0, 0) | ||
4153 | 96 | .create_window(); | ||
4154 | 97 | |||
4155 | 98 | paint(surface); | ||
4156 | 99 | ASSERT_TRUE(window_ready.wait_for(400ms)); | ||
4157 | 100 | |||
4158 | 101 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4159 | 102 | { | ||
4160 | 103 | EXPECT_THAT(tools.info_for(tools.active_window()).shell_chrome(), Eq(mir_shell_chrome_normal)); | ||
4161 | 104 | }); | ||
4162 | 105 | } | ||
4163 | 106 | |||
4164 | 107 | TEST_F(WindowProperties, on_creation_client_setting_shell_chrome_low_is_seen_by_window_manager) | ||
4165 | 108 | { | ||
4166 | 109 | auto const window = WindowSpec::for_normal_window(client_connection, 50, 50) | ||
4167 | 110 | .set_name(a_window.c_str()) | ||
4168 | 111 | .set_shell_chrome(mir_shell_chrome_low) | ||
4169 | 112 | .add_surface(surface, 50, 50, 0, 0) | ||
4170 | 113 | .create_window(); | ||
4171 | 114 | |||
4172 | 115 | paint(surface); | ||
4173 | 116 | ASSERT_TRUE(window_ready.wait_for(400ms)); | ||
4174 | 117 | |||
4175 | 118 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4176 | 119 | { | ||
4177 | 120 | EXPECT_THAT(tools.info_for(tools.active_window()).shell_chrome(), Eq(mir_shell_chrome_low)); | ||
4178 | 121 | }); | ||
4179 | 122 | } | ||
4180 | 123 | |||
4181 | 124 | TEST_F(WindowProperties, after_creation_client_setting_shell_chrome_low_is_seen_by_window_manager) | ||
4182 | 125 | { | ||
4183 | 126 | auto const window = WindowSpec::for_normal_window(client_connection, 50, 50) | ||
4184 | 127 | .set_name(a_window.c_str()) | ||
4185 | 128 | .add_surface(surface, 50, 50, 0, 0) | ||
4186 | 129 | .create_window(); | ||
4187 | 130 | |||
4188 | 131 | WindowSpec::for_changes(client_connection) | ||
4189 | 132 | .set_shell_chrome(mir_shell_chrome_low) | ||
4190 | 133 | .apply_to(window); | ||
4191 | 134 | |||
4192 | 135 | paint(surface); | ||
4193 | 136 | |||
4194 | 137 | ASSERT_TRUE(window_ready.wait_for(400ms)); | ||
4195 | 138 | |||
4196 | 139 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4197 | 140 | { | ||
4198 | 141 | EXPECT_THAT(tools.info_for(tools.active_window()).shell_chrome(), Eq(mir_shell_chrome_low)); | ||
4199 | 142 | }); | ||
4200 | 143 | } | ||
4201 | 144 | |||
4202 | 145 | TEST_F(WindowProperties, after_creation_client_setting_shell_chrome_normal_is_seen_by_window_manager) | ||
4203 | 146 | { | ||
4204 | 147 | auto const window = WindowSpec::for_normal_window(client_connection, 50, 50) | ||
4205 | 148 | .set_name(a_window.c_str()) | ||
4206 | 149 | .set_shell_chrome(mir_shell_chrome_low) | ||
4207 | 150 | .add_surface(surface, 50, 50, 0, 0) | ||
4208 | 151 | .create_window(); | ||
4209 | 152 | |||
4210 | 153 | WindowSpec::for_changes(client_connection) | ||
4211 | 154 | .set_shell_chrome(mir_shell_chrome_normal) | ||
4212 | 155 | .apply_to(window); | ||
4213 | 156 | |||
4214 | 157 | paint(surface); | ||
4215 | 158 | ASSERT_TRUE(window_ready.wait_for(400ms)); | ||
4216 | 159 | |||
4217 | 160 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4218 | 161 | { | ||
4219 | 162 | EXPECT_THAT(tools.info_for(tools.active_window()).shell_chrome(), Eq(mir_shell_chrome_normal)); | ||
4220 | 163 | }); | ||
4221 | 164 | } | ||
4222 | 0 | 165 | ||
4223 | === added file 'tests/miral/workspaces.cpp' | |||
4224 | --- tests/miral/workspaces.cpp 1970-01-01 00:00:00 +0000 | |||
4225 | +++ tests/miral/workspaces.cpp 2017-08-24 15:19:58 +0000 | |||
4226 | @@ -0,0 +1,707 @@ | |||
4227 | 1 | /* | ||
4228 | 2 | * Copyright © 2017 Canonical Ltd. | ||
4229 | 3 | * | ||
4230 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
4231 | 5 | * under the terms of the GNU General Public License version 2 or 3 as | ||
4232 | 6 | * published by the Free Software Foundation. | ||
4233 | 7 | * | ||
4234 | 8 | * This program is distributed in the hope that it will be useful, | ||
4235 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4236 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4237 | 11 | * GNU General Public License for more details. | ||
4238 | 12 | * | ||
4239 | 13 | * You should have received a copy of the GNU General Public License | ||
4240 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
4241 | 15 | * | ||
4242 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
4243 | 17 | */ | ||
4244 | 18 | |||
4245 | 19 | #include <miral/workspace_policy.h> | ||
4246 | 20 | #include <miral/window_manager_tools.h> | ||
4247 | 21 | |||
4248 | 22 | #include <mir/client/surface.h> | ||
4249 | 23 | #include <mir/client/window.h> | ||
4250 | 24 | #include <mir/client/window_spec.h> | ||
4251 | 25 | #include <mir_toolkit/mir_buffer_stream.h> | ||
4252 | 26 | |||
4253 | 27 | #include "test_server.h" | ||
4254 | 28 | |||
4255 | 29 | #include <gmock/gmock.h> | ||
4256 | 30 | #include <mir/test/signal.h> | ||
4257 | 31 | |||
4258 | 32 | |||
4259 | 33 | using namespace testing; | ||
4260 | 34 | using namespace mir::client; | ||
4261 | 35 | using namespace std::chrono_literals; | ||
4262 | 36 | using miral::WindowManagerTools; | ||
4263 | 37 | |||
4264 | 38 | namespace | ||
4265 | 39 | { | ||
4266 | 40 | std::string const top_level{"top level"}; | ||
4267 | 41 | std::string const dialog{"dialog"}; | ||
4268 | 42 | std::string const tip{"tip"}; | ||
4269 | 43 | std::string const a_window{"a window"}; | ||
4270 | 44 | std::string const another_window{"another window"}; | ||
4271 | 45 | |||
4272 | 46 | struct Workspaces; | ||
4273 | 47 | |||
4274 | 48 | struct WorkspacesWindowManagerPolicy : miral::TestServer::TestWindowManagerPolicy, miral::WorkspacePolicy | ||
4275 | 49 | { | ||
4276 | 50 | WorkspacesWindowManagerPolicy(WindowManagerTools const& tools, Workspaces& test_fixture); | ||
4277 | 51 | ~WorkspacesWindowManagerPolicy(); | ||
4278 | 52 | |||
4279 | 53 | void advise_new_window(miral::WindowInfo const& window_info); | ||
4280 | 54 | void handle_window_ready(miral::WindowInfo& window_info); | ||
4281 | 55 | |||
4282 | 56 | MOCK_METHOD2(advise_adding_to_workspace, | ||
4283 | 57 | void(std::shared_ptr<miral::Workspace> const&, std::vector<miral::Window> const&)); | ||
4284 | 58 | |||
4285 | 59 | MOCK_METHOD2(advise_removing_from_workspace, | ||
4286 | 60 | void(std::shared_ptr<miral::Workspace> const&, std::vector<miral::Window> const&)); | ||
4287 | 61 | |||
4288 | 62 | MOCK_METHOD1(advise_focus_gained, void(miral::WindowInfo const&)); | ||
4289 | 63 | |||
4290 | 64 | MOCK_METHOD1(advise_window_ready, void(miral::WindowInfo const&)); | ||
4291 | 65 | |||
4292 | 66 | Workspaces& test_fixture; | ||
4293 | 67 | }; | ||
4294 | 68 | |||
4295 | 69 | struct TestWindow : Surface, Window | ||
4296 | 70 | { | ||
4297 | 71 | using Surface::operator=; | ||
4298 | 72 | using Window::operator=; | ||
4299 | 73 | }; | ||
4300 | 74 | |||
4301 | 75 | struct Workspaces : public miral::TestServer | ||
4302 | 76 | { | ||
4303 | 77 | auto create_window(std::string const& name) -> TestWindow | ||
4304 | 78 | { | ||
4305 | 79 | TestWindow result; | ||
4306 | 80 | |||
4307 | 81 | result = Surface{mir_connection_create_render_surface_sync(client_connection, 50, 50)}; | ||
4308 | 82 | result = WindowSpec::for_normal_window(client_connection, 50, 50) | ||
4309 | 83 | .set_name(name.c_str()) | ||
4310 | 84 | .add_surface(result, 50, 50, 0, 0) | ||
4311 | 85 | .create_window(); | ||
4312 | 86 | |||
4313 | 87 | client_windows[name] = result; | ||
4314 | 88 | init_window(result); | ||
4315 | 89 | |||
4316 | 90 | return result; | ||
4317 | 91 | } | ||
4318 | 92 | |||
4319 | 93 | void init_window(TestWindow const& window) | ||
4320 | 94 | { | ||
4321 | 95 | mir::test::Signal window_ready; | ||
4322 | 96 | EXPECT_CALL(policy(), advise_window_ready(_)).WillOnce(InvokeWithoutArgs([&]{ window_ready.raise(); })); | ||
4323 | 97 | |||
4324 | 98 | mir_buffer_stream_swap_buffers_sync( | ||
4325 | 99 | mir_render_surface_get_buffer_stream(window, 50, 50, mir_pixel_format_argb_8888)); | ||
4326 | 100 | |||
4327 | 101 | EXPECT_TRUE(window_ready.wait_for(1s)); | ||
4328 | 102 | } | ||
4329 | 103 | |||
4330 | 104 | auto create_tip(std::string const& name, Window const& parent) -> TestWindow | ||
4331 | 105 | { | ||
4332 | 106 | TestWindow result; | ||
4333 | 107 | |||
4334 | 108 | result = Surface{mir_connection_create_render_surface_sync(client_connection, 50, 50)}; | ||
4335 | 109 | |||
4336 | 110 | MirRectangle aux_rect{10, 10, 10, 10}; | ||
4337 | 111 | result = WindowSpec::for_tip(client_connection, 50, 50, parent, &aux_rect, mir_edge_attachment_any) | ||
4338 | 112 | .set_name(name.c_str()) | ||
4339 | 113 | .add_surface(result, 50, 50, 0, 0) | ||
4340 | 114 | .create_window(); | ||
4341 | 115 | |||
4342 | 116 | client_windows[name] = result; | ||
4343 | 117 | init_window(result); | ||
4344 | 118 | |||
4345 | 119 | return result; | ||
4346 | 120 | } | ||
4347 | 121 | |||
4348 | 122 | auto create_dialog(std::string const& name, Window const& parent) -> TestWindow | ||
4349 | 123 | { | ||
4350 | 124 | TestWindow result; | ||
4351 | 125 | |||
4352 | 126 | result = Surface{mir_connection_create_render_surface_sync(client_connection, 50, 50)}; | ||
4353 | 127 | |||
4354 | 128 | result = WindowSpec::for_dialog(client_connection, 50, 50, parent) | ||
4355 | 129 | .set_name(name.c_str()) | ||
4356 | 130 | .add_surface(result, 50, 50, 0, 0) | ||
4357 | 131 | .create_window(); | ||
4358 | 132 | |||
4359 | 133 | client_windows[name] = result; | ||
4360 | 134 | init_window(result); | ||
4361 | 135 | |||
4362 | 136 | return result; | ||
4363 | 137 | } | ||
4364 | 138 | |||
4365 | 139 | auto create_workspace() -> std::shared_ptr<miral::Workspace> | ||
4366 | 140 | { | ||
4367 | 141 | std::shared_ptr<miral::Workspace> result; | ||
4368 | 142 | |||
4369 | 143 | invoke_tools([&](WindowManagerTools& tools) | ||
4370 | 144 | { result = tools.create_workspace(); }); | ||
4371 | 145 | |||
4372 | 146 | return result; | ||
4373 | 147 | } | ||
4374 | 148 | |||
4375 | 149 | void SetUp() override | ||
4376 | 150 | { | ||
4377 | 151 | miral::TestServer::SetUp(); | ||
4378 | 152 | EXPECT_CALL(policy(), advise_adding_to_workspace(_, _)).Times(AnyNumber()); | ||
4379 | 153 | EXPECT_CALL(policy(), advise_removing_from_workspace(_, _)).Times(AnyNumber()); | ||
4380 | 154 | EXPECT_CALL(policy(), advise_focus_gained(_)).Times(AnyNumber()); | ||
4381 | 155 | |||
4382 | 156 | client_connection = connect_client("Workspaces"); | ||
4383 | 157 | create_window(top_level); | ||
4384 | 158 | create_dialog(dialog, client_windows[top_level]); | ||
4385 | 159 | create_tip(tip, client_windows[dialog]); | ||
4386 | 160 | |||
4387 | 161 | EXPECT_THAT(client_windows.size(), Eq(3u)); | ||
4388 | 162 | EXPECT_THAT(server_windows.size(), Eq(3u)); | ||
4389 | 163 | } | ||
4390 | 164 | |||
4391 | 165 | void TearDown() override | ||
4392 | 166 | { | ||
4393 | 167 | client_windows.clear(); | ||
4394 | 168 | client_connection.reset(); | ||
4395 | 169 | miral::TestServer::TearDown(); | ||
4396 | 170 | } | ||
4397 | 171 | |||
4398 | 172 | Connection client_connection; | ||
4399 | 173 | |||
4400 | 174 | auto server_window(std::string const& key) -> miral::Window | ||
4401 | 175 | { | ||
4402 | 176 | std::lock_guard<decltype(mutex)> lock{mutex}; | ||
4403 | 177 | return server_windows[key]; | ||
4404 | 178 | } | ||
4405 | 179 | |||
4406 | 180 | auto client_window(std::string const& key) -> Window& | ||
4407 | 181 | { | ||
4408 | 182 | return client_windows[key]; | ||
4409 | 183 | } | ||
4410 | 184 | |||
4411 | 185 | auto windows_in_workspace(std::shared_ptr<miral::Workspace> const& workspace) -> std::vector<miral::Window> | ||
4412 | 186 | { | ||
4413 | 187 | std::vector<miral::Window> result; | ||
4414 | 188 | |||
4415 | 189 | auto enumerate = [&result](miral::Window const& window) | ||
4416 | 190 | { | ||
4417 | 191 | result.push_back(window); | ||
4418 | 192 | }; | ||
4419 | 193 | |||
4420 | 194 | invoke_tools([&](WindowManagerTools& tools) | ||
4421 | 195 | { tools.for_each_window_in_workspace(workspace, enumerate); }); | ||
4422 | 196 | |||
4423 | 197 | return result; | ||
4424 | 198 | } | ||
4425 | 199 | |||
4426 | 200 | auto workspaces_containing_window(miral::Window const& window) -> std::vector<std::shared_ptr<miral::Workspace>> | ||
4427 | 201 | { | ||
4428 | 202 | std::vector<std::shared_ptr<miral::Workspace>> result; | ||
4429 | 203 | |||
4430 | 204 | auto enumerate = [&result](std::shared_ptr<miral::Workspace> const& workspace) | ||
4431 | 205 | { | ||
4432 | 206 | result.push_back(workspace); | ||
4433 | 207 | }; | ||
4434 | 208 | |||
4435 | 209 | invoke_tools([&](WindowManagerTools& tools) | ||
4436 | 210 | { tools.for_each_workspace_containing(window, enumerate); }); | ||
4437 | 211 | |||
4438 | 212 | return result; | ||
4439 | 213 | } | ||
4440 | 214 | |||
4441 | 215 | auto policy() -> WorkspacesWindowManagerPolicy& | ||
4442 | 216 | { | ||
4443 | 217 | if (!the_policy) throw std::logic_error("the_policy isn't valid"); | ||
4444 | 218 | return *the_policy; | ||
4445 | 219 | } | ||
4446 | 220 | |||
4447 | 221 | private: | ||
4448 | 222 | std::mutex mutable mutex; | ||
4449 | 223 | std::map<std::string, TestWindow> client_windows; | ||
4450 | 224 | std::map<std::string, miral::Window> server_windows; | ||
4451 | 225 | WorkspacesWindowManagerPolicy* the_policy{nullptr}; | ||
4452 | 226 | |||
4453 | 227 | friend struct WorkspacesWindowManagerPolicy; | ||
4454 | 228 | |||
4455 | 229 | auto build_window_manager_policy(WindowManagerTools const& tools) | ||
4456 | 230 | -> std::unique_ptr<TestWindowManagerPolicy> override | ||
4457 | 231 | { | ||
4458 | 232 | return std::make_unique<WorkspacesWindowManagerPolicy>(tools, *this); | ||
4459 | 233 | } | ||
4460 | 234 | }; | ||
4461 | 235 | |||
4462 | 236 | WorkspacesWindowManagerPolicy::WorkspacesWindowManagerPolicy(WindowManagerTools const& tools, Workspaces& test_fixture) : | ||
4463 | 237 | TestWindowManagerPolicy(tools, test_fixture), test_fixture{test_fixture} | ||
4464 | 238 | { | ||
4465 | 239 | test_fixture.the_policy = this; | ||
4466 | 240 | } | ||
4467 | 241 | |||
4468 | 242 | WorkspacesWindowManagerPolicy::~WorkspacesWindowManagerPolicy() | ||
4469 | 243 | { | ||
4470 | 244 | test_fixture.the_policy = nullptr; | ||
4471 | 245 | } | ||
4472 | 246 | |||
4473 | 247 | |||
4474 | 248 | void WorkspacesWindowManagerPolicy::advise_new_window(miral::WindowInfo const& window_info) | ||
4475 | 249 | { | ||
4476 | 250 | miral::TestServer::TestWindowManagerPolicy::advise_new_window(window_info); | ||
4477 | 251 | |||
4478 | 252 | std::lock_guard<decltype(test_fixture.mutex)> lock{test_fixture.mutex}; | ||
4479 | 253 | test_fixture.server_windows[window_info.name()] = window_info.window(); | ||
4480 | 254 | } | ||
4481 | 255 | |||
4482 | 256 | void WorkspacesWindowManagerPolicy::handle_window_ready(miral::WindowInfo& window_info) | ||
4483 | 257 | { | ||
4484 | 258 | miral::CanonicalWindowManagerPolicy::handle_window_ready(window_info); | ||
4485 | 259 | advise_window_ready(window_info); | ||
4486 | 260 | } | ||
4487 | 261 | } | ||
4488 | 262 | |||
4489 | 263 | TEST_F(Workspaces, before_a_tree_is_added_to_workspace_it_is_empty) | ||
4490 | 264 | { | ||
4491 | 265 | auto const workspace = create_workspace(); | ||
4492 | 266 | |||
4493 | 267 | EXPECT_THAT(windows_in_workspace(workspace).size(), Eq(0u)); | ||
4494 | 268 | } | ||
4495 | 269 | |||
4496 | 270 | TEST_F(Workspaces, when_a_tree_is_added_to_workspace_all_surfaces_in_tree_are_added) | ||
4497 | 271 | { | ||
4498 | 272 | auto const workspace = create_workspace(); | ||
4499 | 273 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4500 | 274 | { tools.add_tree_to_workspace(server_window(dialog), workspace); }); | ||
4501 | 275 | |||
4502 | 276 | EXPECT_THAT(windows_in_workspace(workspace).size(), Eq(3u)); | ||
4503 | 277 | } | ||
4504 | 278 | |||
4505 | 279 | TEST_F(Workspaces, when_a_tree_is_removed_from_workspace_all_surfaces_in_tree_are_removed) | ||
4506 | 280 | { | ||
4507 | 281 | auto const workspace = create_workspace(); | ||
4508 | 282 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4509 | 283 | { tools.add_tree_to_workspace(server_window(dialog), workspace); }); | ||
4510 | 284 | |||
4511 | 285 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4512 | 286 | { tools.remove_tree_from_workspace(server_window(tip), workspace); }); | ||
4513 | 287 | |||
4514 | 288 | EXPECT_THAT(windows_in_workspace(workspace).size(), Eq(0u)); | ||
4515 | 289 | } | ||
4516 | 290 | |||
4517 | 291 | TEST_F(Workspaces, given_a_tree_in_a_workspace_when_another_tree_is_added_and_removed_from_workspace_the_original_tree_remains) | ||
4518 | 292 | { | ||
4519 | 293 | auto const workspace = create_workspace(); | ||
4520 | 294 | auto const original_tree = "original_tree"; | ||
4521 | 295 | auto const client_window = create_window(original_tree); | ||
4522 | 296 | auto const original_window= server_window(original_tree); | ||
4523 | 297 | |||
4524 | 298 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4525 | 299 | { tools.add_tree_to_workspace(original_window, workspace); }); | ||
4526 | 300 | |||
4527 | 301 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4528 | 302 | { tools.add_tree_to_workspace(server_window(top_level), workspace); }); | ||
4529 | 303 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4530 | 304 | { tools.remove_tree_from_workspace(server_window(top_level), workspace); }); | ||
4531 | 305 | |||
4532 | 306 | EXPECT_THAT(windows_in_workspace(workspace), ElementsAre(original_window)); | ||
4533 | 307 | } | ||
4534 | 308 | |||
4535 | 309 | TEST_F(Workspaces, when_a_tree_is_added_to_a_workspace_all_surfaces_are_contained_in_the_workspace) | ||
4536 | 310 | { | ||
4537 | 311 | auto const workspace = create_workspace(); | ||
4538 | 312 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4539 | 313 | { tools.add_tree_to_workspace(server_window(dialog), workspace); }); | ||
4540 | 314 | |||
4541 | 315 | EXPECT_THAT(workspaces_containing_window(server_window(top_level)), ElementsAre(workspace)); | ||
4542 | 316 | EXPECT_THAT(workspaces_containing_window(server_window(dialog)), ElementsAre(workspace)); | ||
4543 | 317 | EXPECT_THAT(workspaces_containing_window(server_window(tip)), ElementsAre(workspace)); | ||
4544 | 318 | } | ||
4545 | 319 | |||
4546 | 320 | |||
4547 | 321 | TEST_F(Workspaces, when_a_tree_is_added_to_a_workspaces_twice_surfaces_are_contained_in_one_workspace) | ||
4548 | 322 | { | ||
4549 | 323 | auto const workspace = create_workspace(); | ||
4550 | 324 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4551 | 325 | { | ||
4552 | 326 | tools.add_tree_to_workspace(server_window(dialog), workspace); | ||
4553 | 327 | tools.add_tree_to_workspace(server_window(dialog), workspace); | ||
4554 | 328 | }); | ||
4555 | 329 | |||
4556 | 330 | EXPECT_THAT(workspaces_containing_window(server_window(top_level)), ElementsAre(workspace)); | ||
4557 | 331 | EXPECT_THAT(workspaces_containing_window(server_window(dialog)), ElementsAre(workspace)); | ||
4558 | 332 | EXPECT_THAT(workspaces_containing_window(server_window(tip)), ElementsAre(workspace)); | ||
4559 | 333 | |||
4560 | 334 | EXPECT_THAT(workspaces_containing_window(server_window(top_level)).size(), Eq(1u)); | ||
4561 | 335 | EXPECT_THAT(workspaces_containing_window(server_window(dialog)).size(), Eq(1u)); | ||
4562 | 336 | EXPECT_THAT(workspaces_containing_window(server_window(tip)).size(), Eq(1u)); | ||
4563 | 337 | } | ||
4564 | 338 | |||
4565 | 339 | TEST_F(Workspaces, when_a_tree_is_added_to_two_workspaces_all_surfaces_are_contained_in_two_workspaces) | ||
4566 | 340 | { | ||
4567 | 341 | auto const workspace1 = create_workspace(); | ||
4568 | 342 | auto const workspace2 = create_workspace(); | ||
4569 | 343 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4570 | 344 | { | ||
4571 | 345 | tools.add_tree_to_workspace(server_window(dialog), workspace1); | ||
4572 | 346 | tools.add_tree_to_workspace(server_window(dialog), workspace2); | ||
4573 | 347 | }); | ||
4574 | 348 | |||
4575 | 349 | EXPECT_THAT(workspaces_containing_window(server_window(top_level)).size(), Eq(2u)); | ||
4576 | 350 | EXPECT_THAT(workspaces_containing_window(server_window(dialog)).size(), Eq(2u)); | ||
4577 | 351 | EXPECT_THAT(workspaces_containing_window(server_window(tip)).size(), Eq(2u)); | ||
4578 | 352 | } | ||
4579 | 353 | |||
4580 | 354 | TEST_F(Workspaces, when_workspace_is_closed_surfaces_are_no_longer_contained_in_it) | ||
4581 | 355 | { | ||
4582 | 356 | auto const workspace1 = create_workspace(); | ||
4583 | 357 | auto workspace2 = create_workspace(); | ||
4584 | 358 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4585 | 359 | { | ||
4586 | 360 | tools.add_tree_to_workspace(server_window(dialog), workspace1); | ||
4587 | 361 | tools.add_tree_to_workspace(server_window(dialog), workspace2); | ||
4588 | 362 | }); | ||
4589 | 363 | |||
4590 | 364 | workspace2.reset(); | ||
4591 | 365 | |||
4592 | 366 | EXPECT_THAT(workspaces_containing_window(server_window(top_level)), ElementsAre(workspace1)); | ||
4593 | 367 | EXPECT_THAT(workspaces_containing_window(server_window(dialog)), ElementsAre(workspace1)); | ||
4594 | 368 | EXPECT_THAT(workspaces_containing_window(server_window(tip)), ElementsAre(workspace1)); | ||
4595 | 369 | |||
4596 | 370 | EXPECT_THAT(workspaces_containing_window(server_window(top_level)).size(), Eq(1u)); | ||
4597 | 371 | EXPECT_THAT(workspaces_containing_window(server_window(dialog)).size(), Eq(1u)); | ||
4598 | 372 | EXPECT_THAT(workspaces_containing_window(server_window(tip)).size(), Eq(1u)); | ||
4599 | 373 | } | ||
4600 | 374 | |||
4601 | 375 | TEST_F(Workspaces, when_a_tree_is_added_to_a_workspace_the_policy_is_notified) | ||
4602 | 376 | { | ||
4603 | 377 | auto const workspace = create_workspace(); | ||
4604 | 378 | |||
4605 | 379 | EXPECT_CALL(policy(), advise_adding_to_workspace(workspace, | ||
4606 | 380 | ElementsAre(server_window(top_level), server_window(dialog), server_window(tip)))); | ||
4607 | 381 | |||
4608 | 382 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4609 | 383 | { tools.add_tree_to_workspace(server_window(dialog), workspace); }); | ||
4610 | 384 | } | ||
4611 | 385 | |||
4612 | 386 | TEST_F(Workspaces, when_a_tree_is_added_to_a_workspaces_twice_the_policy_is_notified_once) | ||
4613 | 387 | { | ||
4614 | 388 | auto const workspace = create_workspace(); | ||
4615 | 389 | |||
4616 | 390 | EXPECT_CALL(policy(), advise_adding_to_workspace(workspace, | ||
4617 | 391 | ElementsAre(server_window(top_level), server_window(dialog), server_window(tip)))); | ||
4618 | 392 | |||
4619 | 393 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4620 | 394 | { | ||
4621 | 395 | tools.add_tree_to_workspace(server_window(dialog), workspace); | ||
4622 | 396 | tools.add_tree_to_workspace(server_window(dialog), workspace); | ||
4623 | 397 | }); | ||
4624 | 398 | } | ||
4625 | 399 | |||
4626 | 400 | TEST_F(Workspaces, when_a_tree_is_removed_from_a_workspace_the_policy_is_notified) | ||
4627 | 401 | { | ||
4628 | 402 | auto const workspace = create_workspace(); | ||
4629 | 403 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4630 | 404 | { tools.add_tree_to_workspace(server_window(dialog), workspace); }); | ||
4631 | 405 | |||
4632 | 406 | EXPECT_CALL(policy(), advise_removing_from_workspace(workspace, | ||
4633 | 407 | ElementsAre(server_window(top_level), server_window(dialog), server_window(tip)))); | ||
4634 | 408 | |||
4635 | 409 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4636 | 410 | { tools.remove_tree_from_workspace(server_window(tip), workspace); }); | ||
4637 | 411 | } | ||
4638 | 412 | |||
4639 | 413 | TEST_F(Workspaces, when_a_tree_is_removed_from_a_workspace_twice_the_policy_is_notified_once) | ||
4640 | 414 | { | ||
4641 | 415 | auto const workspace = create_workspace(); | ||
4642 | 416 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4643 | 417 | { tools.add_tree_to_workspace(server_window(dialog), workspace); }); | ||
4644 | 418 | |||
4645 | 419 | EXPECT_CALL(policy(), advise_removing_from_workspace(workspace, | ||
4646 | 420 | ElementsAre(server_window(top_level), server_window(dialog), server_window(tip)))); | ||
4647 | 421 | |||
4648 | 422 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4649 | 423 | { | ||
4650 | 424 | tools.remove_tree_from_workspace(server_window(top_level), workspace); | ||
4651 | 425 | tools.remove_tree_from_workspace(server_window(tip), workspace); | ||
4652 | 426 | }); | ||
4653 | 427 | } | ||
4654 | 428 | |||
4655 | 429 | TEST_F(Workspaces, a_child_window_is_added_to_workspace_of_parent) | ||
4656 | 430 | { | ||
4657 | 431 | auto const workspace = create_workspace(); | ||
4658 | 432 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4659 | 433 | { tools.add_tree_to_workspace(server_window(dialog), workspace); }); | ||
4660 | 434 | |||
4661 | 435 | EXPECT_CALL(policy(), advise_adding_to_workspace(workspace, ElementsAre(_))); | ||
4662 | 436 | |||
4663 | 437 | create_dialog(a_window, client_window(top_level)); | ||
4664 | 438 | } | ||
4665 | 439 | |||
4666 | 440 | TEST_F(Workspaces, a_closing_window_is_removed_from_workspace) | ||
4667 | 441 | { | ||
4668 | 442 | auto const workspace = create_workspace(); | ||
4669 | 443 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4670 | 444 | { tools.add_tree_to_workspace(server_window(dialog), workspace); }); | ||
4671 | 445 | |||
4672 | 446 | create_dialog(a_window, client_window(dialog)); | ||
4673 | 447 | |||
4674 | 448 | EXPECT_CALL(policy(), advise_removing_from_workspace(workspace, ElementsAre(server_window(a_window)))); | ||
4675 | 449 | |||
4676 | 450 | client_window(a_window).reset(); | ||
4677 | 451 | } | ||
4678 | 452 | |||
4679 | 453 | TEST_F(Workspaces, when_a_window_in_a_workspace_closes_focus_remains_in_workspace) | ||
4680 | 454 | { | ||
4681 | 455 | auto const workspace = create_workspace(); | ||
4682 | 456 | |||
4683 | 457 | create_window(a_window); | ||
4684 | 458 | create_window(another_window); | ||
4685 | 459 | |||
4686 | 460 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4687 | 461 | { | ||
4688 | 462 | tools.add_tree_to_workspace(server_window(a_window), workspace); | ||
4689 | 463 | tools.add_tree_to_workspace(server_window(another_window), workspace); | ||
4690 | 464 | |||
4691 | 465 | tools.select_active_window(server_window(dialog)); | ||
4692 | 466 | tools.select_active_window(server_window(a_window)); | ||
4693 | 467 | }); | ||
4694 | 468 | |||
4695 | 469 | client_window(a_window).reset(); | ||
4696 | 470 | |||
4697 | 471 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4698 | 472 | { | ||
4699 | 473 | EXPECT_THAT(tools.active_window(), Eq(server_window(another_window))) | ||
4700 | 474 | << "tools.active_window() . . . .: " << tools.info_for(tools.active_window()).name() << "\n" | ||
4701 | 475 | << "server_window(another_window): " << tools.info_for(server_window(another_window)).name(); | ||
4702 | 476 | }); | ||
4703 | 477 | } | ||
4704 | 478 | |||
4705 | 479 | TEST_F(Workspaces, with_two_applications_when_a_window_in_a_workspace_closes_focus_remains_in_workspace) | ||
4706 | 480 | { | ||
4707 | 481 | auto const workspace = create_workspace(); | ||
4708 | 482 | |||
4709 | 483 | create_window(another_window); | ||
4710 | 484 | |||
4711 | 485 | { | ||
4712 | 486 | auto const another_app = connect_client("another app"); | ||
4713 | 487 | TestWindow window; | ||
4714 | 488 | window = Surface{mir_connection_create_render_surface_sync(another_app, 50, 50)}; | ||
4715 | 489 | window = WindowSpec::for_normal_window(another_app, 50, 50) | ||
4716 | 490 | .set_name(a_window.c_str()) | ||
4717 | 491 | .add_surface(window, 50, 50, 0, 0) | ||
4718 | 492 | .create_window(); | ||
4719 | 493 | |||
4720 | 494 | init_window(window); | ||
4721 | 495 | |||
4722 | 496 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4723 | 497 | { | ||
4724 | 498 | tools.add_tree_to_workspace(server_window(top_level), workspace); | ||
4725 | 499 | tools.add_tree_to_workspace(server_window(a_window), workspace); | ||
4726 | 500 | }); | ||
4727 | 501 | } | ||
4728 | 502 | |||
4729 | 503 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4730 | 504 | { | ||
4731 | 505 | EXPECT_THAT(tools.active_window(), Eq(server_window(dialog))) | ||
4732 | 506 | << "tools.active_window(): " << tools.info_for(tools.active_window()).name() << "\n" | ||
4733 | 507 | << "server_window(dialog): " << tools.info_for(server_window(dialog)).name(); | ||
4734 | 508 | }); | ||
4735 | 509 | } | ||
4736 | 510 | |||
4737 | 511 | TEST_F(Workspaces, when_a_window_in_a_workspace_hides_focus_remains_in_workspace) | ||
4738 | 512 | { | ||
4739 | 513 | auto const workspace = create_workspace(); | ||
4740 | 514 | |||
4741 | 515 | create_window(a_window); | ||
4742 | 516 | create_window(another_window); | ||
4743 | 517 | |||
4744 | 518 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4745 | 519 | { | ||
4746 | 520 | tools.add_tree_to_workspace(server_window(a_window), workspace); | ||
4747 | 521 | tools.add_tree_to_workspace(server_window(another_window), workspace); | ||
4748 | 522 | |||
4749 | 523 | tools.select_active_window(server_window(dialog)); | ||
4750 | 524 | tools.select_active_window(server_window(a_window)); | ||
4751 | 525 | }); | ||
4752 | 526 | |||
4753 | 527 | mir::test::Signal focus_changed; | ||
4754 | 528 | EXPECT_CALL(policy(), advise_focus_gained(_)).WillOnce(InvokeWithoutArgs([&]{ focus_changed.raise(); })); | ||
4755 | 529 | |||
4756 | 530 | mir_window_set_state(client_window(a_window), mir_window_state_hidden); | ||
4757 | 531 | |||
4758 | 532 | EXPECT_TRUE(focus_changed.wait_for(1s)); | ||
4759 | 533 | |||
4760 | 534 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4761 | 535 | { | ||
4762 | 536 | EXPECT_THAT(tools.active_window(), Eq(server_window(another_window))) | ||
4763 | 537 | << "tools.active_window() . . . .: " << tools.info_for(tools.active_window()).name() << "\n" | ||
4764 | 538 | << "server_window(another_window): " << tools.info_for(server_window(another_window)).name(); | ||
4765 | 539 | }); | ||
4766 | 540 | } | ||
4767 | 541 | |||
4768 | 542 | |||
4769 | 543 | TEST_F(Workspaces, with_two_applications_when_a_window_in_a_workspace_hides_focus_remains_in_workspace) | ||
4770 | 544 | { | ||
4771 | 545 | auto const workspace = create_workspace(); | ||
4772 | 546 | |||
4773 | 547 | create_window(another_window); | ||
4774 | 548 | |||
4775 | 549 | auto const another_app = connect_client("another app"); | ||
4776 | 550 | TestWindow window; | ||
4777 | 551 | window = Surface{mir_connection_create_render_surface_sync(another_app, 50, 50)}; | ||
4778 | 552 | window = WindowSpec::for_normal_window(another_app, 50, 50) | ||
4779 | 553 | .set_name(a_window.c_str()) | ||
4780 | 554 | .add_surface(window, 50, 50, 0, 0) | ||
4781 | 555 | .create_window(); | ||
4782 | 556 | |||
4783 | 557 | init_window(window); | ||
4784 | 558 | |||
4785 | 559 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4786 | 560 | { | ||
4787 | 561 | tools.add_tree_to_workspace(server_window(top_level), workspace); | ||
4788 | 562 | tools.add_tree_to_workspace(server_window(a_window), workspace); | ||
4789 | 563 | }); | ||
4790 | 564 | |||
4791 | 565 | |||
4792 | 566 | mir::test::Signal focus_changed; | ||
4793 | 567 | EXPECT_CALL(policy(), advise_focus_gained(_)).WillOnce(InvokeWithoutArgs([&]{ focus_changed.raise(); })); | ||
4794 | 568 | |||
4795 | 569 | mir_window_set_state(window, mir_window_state_hidden); | ||
4796 | 570 | |||
4797 | 571 | EXPECT_TRUE(focus_changed.wait_for(1s)); | ||
4798 | 572 | |||
4799 | 573 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4800 | 574 | { | ||
4801 | 575 | EXPECT_THAT(tools.active_window(), Eq(server_window(dialog))) | ||
4802 | 576 | << "tools.active_window(): " << tools.info_for(tools.active_window()).name() << "\n" | ||
4803 | 577 | << "server_window(dialog): " << tools.info_for(server_window(dialog)).name(); | ||
4804 | 578 | }); | ||
4805 | 579 | |||
4806 | 580 | Mock::VerifyAndClearExpectations(&policy()); // before shutdown | ||
4807 | 581 | } | ||
4808 | 582 | |||
4809 | 583 | TEST_F(Workspaces, focus_next_within_application_keeps_focus_in_workspace) | ||
4810 | 584 | { | ||
4811 | 585 | auto const workspace = create_workspace(); | ||
4812 | 586 | |||
4813 | 587 | create_window(another_window); | ||
4814 | 588 | create_window(a_window); | ||
4815 | 589 | |||
4816 | 590 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4817 | 591 | { | ||
4818 | 592 | tools.add_tree_to_workspace(server_window(a_window), workspace); | ||
4819 | 593 | tools.add_tree_to_workspace(server_window(dialog), workspace); | ||
4820 | 594 | |||
4821 | 595 | tools.focus_next_within_application(); | ||
4822 | 596 | |||
4823 | 597 | EXPECT_THAT(tools.active_window(), Eq(server_window(dialog))) | ||
4824 | 598 | << "tools.active_window(): " << tools.info_for(tools.active_window()).name() << "\n" | ||
4825 | 599 | << "server_window(dialog): " << tools.info_for(server_window(dialog)).name(); | ||
4826 | 600 | |||
4827 | 601 | tools.focus_next_within_application(); | ||
4828 | 602 | |||
4829 | 603 | EXPECT_THAT(tools.active_window(), Eq(server_window(a_window))) | ||
4830 | 604 | << "tools.active_window(). : " << tools.info_for(tools.active_window()).name() << "\n" | ||
4831 | 605 | << "server_window(a_window): " << tools.info_for(server_window(a_window)).name(); | ||
4832 | 606 | }); | ||
4833 | 607 | } | ||
4834 | 608 | |||
4835 | 609 | TEST_F(Workspaces, focus_next_application_keeps_focus_in_workspace) | ||
4836 | 610 | { | ||
4837 | 611 | auto const workspace = create_workspace(); | ||
4838 | 612 | create_window(another_window); | ||
4839 | 613 | |||
4840 | 614 | auto const another_app = connect_client("another app"); | ||
4841 | 615 | TestWindow window; | ||
4842 | 616 | window = Surface{mir_connection_create_render_surface_sync(another_app, 50, 50)}; | ||
4843 | 617 | window = WindowSpec::for_normal_window(another_app, 50, 50) | ||
4844 | 618 | .set_name(a_window.c_str()) | ||
4845 | 619 | .add_surface(window, 50, 50, 0, 0) | ||
4846 | 620 | .create_window(); | ||
4847 | 621 | |||
4848 | 622 | init_window(window); | ||
4849 | 623 | |||
4850 | 624 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4851 | 625 | { | ||
4852 | 626 | tools.add_tree_to_workspace(server_window(top_level), workspace); | ||
4853 | 627 | tools.add_tree_to_workspace(server_window(a_window), workspace); | ||
4854 | 628 | |||
4855 | 629 | tools.focus_next_application(); | ||
4856 | 630 | |||
4857 | 631 | EXPECT_THAT(tools.active_window(), Eq(server_window(dialog))) | ||
4858 | 632 | << "tools.active_window(): " << tools.info_for(tools.active_window()).name() << "\n" | ||
4859 | 633 | << "server_window(dialog): " << tools.info_for(server_window(dialog)).name(); | ||
4860 | 634 | |||
4861 | 635 | tools.focus_next_application(); | ||
4862 | 636 | |||
4863 | 637 | EXPECT_THAT(tools.active_window(), Eq(server_window(a_window))) | ||
4864 | 638 | << "tools.active_window(). : " << tools.info_for(tools.active_window()).name() << "\n" | ||
4865 | 639 | << "server_window(a_window): " << tools.info_for(server_window(a_window)).name(); | ||
4866 | 640 | }); | ||
4867 | 641 | } | ||
4868 | 642 | |||
4869 | 643 | TEST_F(Workspaces, move_windows_from_one_workspace_to_another) | ||
4870 | 644 | { | ||
4871 | 645 | auto const pre_workspace = create_workspace(); | ||
4872 | 646 | auto const from_workspace = create_workspace(); | ||
4873 | 647 | auto const to_workspace = create_workspace(); | ||
4874 | 648 | auto const post_workspace = create_workspace(); | ||
4875 | 649 | |||
4876 | 650 | create_window(a_window); | ||
4877 | 651 | create_window(another_window); | ||
4878 | 652 | |||
4879 | 653 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4880 | 654 | { | ||
4881 | 655 | tools.add_tree_to_workspace(server_window(a_window), pre_workspace); | ||
4882 | 656 | tools.add_tree_to_workspace(server_window(top_level), from_workspace); | ||
4883 | 657 | tools.add_tree_to_workspace(server_window(another_window), post_workspace); | ||
4884 | 658 | |||
4885 | 659 | tools.move_workspace_content_to_workspace(to_workspace, from_workspace); | ||
4886 | 660 | }); | ||
4887 | 661 | |||
4888 | 662 | EXPECT_THAT(windows_in_workspace(from_workspace).size(), Eq(0u)); | ||
4889 | 663 | |||
4890 | 664 | EXPECT_THAT(workspaces_containing_window(server_window(a_window)), ElementsAre(pre_workspace)); | ||
4891 | 665 | EXPECT_THAT(workspaces_containing_window(server_window(top_level)), ElementsAre(to_workspace)); | ||
4892 | 666 | EXPECT_THAT(workspaces_containing_window(server_window(dialog)), ElementsAre(to_workspace)); | ||
4893 | 667 | EXPECT_THAT(workspaces_containing_window(server_window(tip)), ElementsAre(to_workspace)); | ||
4894 | 668 | EXPECT_THAT(workspaces_containing_window(server_window(another_window)), ElementsAre(post_workspace)); | ||
4895 | 669 | } | ||
4896 | 670 | |||
4897 | 671 | TEST_F(Workspaces, when_moving_windows_from_one_workspace_to_another_windows_only_appear_once_in_target_workspace) | ||
4898 | 672 | { | ||
4899 | 673 | auto const from_workspace = create_workspace(); | ||
4900 | 674 | auto const to_workspace = create_workspace(); | ||
4901 | 675 | |||
4902 | 676 | create_window(a_window); | ||
4903 | 677 | create_window(another_window); | ||
4904 | 678 | |||
4905 | 679 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4906 | 680 | { | ||
4907 | 681 | tools.add_tree_to_workspace(server_window(a_window), from_workspace); | ||
4908 | 682 | tools.add_tree_to_workspace(server_window(another_window), from_workspace); | ||
4909 | 683 | tools.add_tree_to_workspace(server_window(a_window), to_workspace); | ||
4910 | 684 | |||
4911 | 685 | tools.move_workspace_content_to_workspace(to_workspace, from_workspace); | ||
4912 | 686 | }); | ||
4913 | 687 | |||
4914 | 688 | EXPECT_THAT(windows_in_workspace(to_workspace), ElementsAre(server_window(a_window), server_window(another_window))); | ||
4915 | 689 | } | ||
4916 | 690 | |||
4917 | 691 | TEST_F(Workspaces, when_workspace_content_is_moved_the_policy_is_notified) | ||
4918 | 692 | { | ||
4919 | 693 | auto const from_workspace = create_workspace(); | ||
4920 | 694 | auto const to_workspace = create_workspace(); | ||
4921 | 695 | |||
4922 | 696 | EXPECT_CALL(policy(), advise_removing_from_workspace(from_workspace, | ||
4923 | 697 | ElementsAre(server_window(top_level), server_window(dialog), server_window(tip)))); | ||
4924 | 698 | |||
4925 | 699 | EXPECT_CALL(policy(), advise_adding_to_workspace(to_workspace, | ||
4926 | 700 | ElementsAre(server_window(top_level), server_window(dialog), server_window(tip)))); | ||
4927 | 701 | |||
4928 | 702 | invoke_tools([&, this](WindowManagerTools& tools) | ||
4929 | 703 | { | ||
4930 | 704 | tools.add_tree_to_workspace(server_window(dialog), from_workspace); | ||
4931 | 705 | tools.move_workspace_content_to_workspace(to_workspace, from_workspace); | ||
4932 | 706 | }); | ||
4933 | 707 | } |
FAILED: Continuous integration, rev:4249 /mir-jenkins. ubuntu. com/job/ mir-ci/ 3576/ /mir-jenkins. ubuntu. com/job/ build-mir/ 4899/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/5121 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= artful/ 5110 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial/ 5110 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= zesty/5110 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= artful/ 4938/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= zesty/4938/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= artful/ 4938/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial/ 4938/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= zesty/4938/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= artful/ 4938 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= artful/ 4938/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= zesty/4938 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= zesty/4938/ artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial/ 4938/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 3576/rebuild
https:/