~alan-griffiths/mir/migrate-tests-to-miral

« back to all changes in this revision

Viewing changes to tests/miral/client_mediated_gestures.cpp

  • Committer: Christopher James Halse Rogers
  • Date: 2017-09-07 05:58:13 UTC
  • mfrom: (4242 development-branch)
  • mto: (4243.1.1 mir3)
  • mto: This revision was merged to the branch mainline in revision 4244.
  • Revision ID: christopher.halse.rogers@canonical.com-20170907055813-4qsg25nirybc8jj3
Merge trunk, resolving conflict

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2017 Canonical Ltd.
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify it
 
5
 * under the terms of the GNU General Public License version 2 or 3 as
 
6
 * published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authored by: Alan Griffiths <alan@octopull.co.uk>
 
17
 */
 
18
 
 
19
#include <mir/client/surface.h>
 
20
#include <mir/client/window_spec.h>
 
21
 
 
22
#include <mir_toolkit/mir_window.h>
 
23
#include <mir_toolkit/mir_blob.h>
 
24
 
 
25
#include <mir/geometry/displacement.h>
 
26
#include <mir/input/input_device_info.h>
 
27
#include <mir/input/device_capability.h>
 
28
#include <mir/shell/canonical_window_manager.h>
 
29
#include <mir/shell/shell.h>
 
30
 
 
31
#include <mir_test_framework/connected_client_with_a_window.h>
 
32
#include <mir_test_framework/fake_input_device.h>
 
33
#include <mir_test_framework/stub_server_platform_factory.h>
 
34
#include <mir/test/event_factory.h>
 
35
#include <mir/test/fake_shared.h>
 
36
#include <mir/test/signal.h>
 
37
 
 
38
#include <gmock/gmock.h>
 
39
#include <gtest/gtest.h>
 
40
 
 
41
#include <linux/input.h>
 
42
 
 
43
#include <atomic>
 
44
 
 
45
using namespace std::chrono_literals;
 
46
using namespace mir::geometry;
 
47
using namespace testing;
 
48
using mir::test::fake_shared;
 
49
using mir::test::Signal;
 
50
 
 
51
namespace
 
52
{
 
53
class Cookie
 
54
{
 
55
public:
 
56
    Cookie() = default;
 
57
 
 
58
    explicit Cookie(MirCookie const* cookie) : self{cookie, deleter} {}
 
59
 
 
60
    operator MirCookie const*() const { return self.get(); }
 
61
 
 
62
    auto get() const -> MirCookie const* { return self.get(); }
 
63
 
 
64
    void reset() { self.reset(); }
 
65
 
 
66
    void reset(MirCookie const* cookie) { self.reset(cookie, deleter); }
 
67
 
 
68
private:
 
69
    static void deleter(MirCookie const* cookie) { mir_cookie_release(cookie); }
 
70
 
 
71
    std::shared_ptr<MirCookie const> self;
 
72
};
 
73
 
 
74
void mir_cookie_release(Cookie const&) = delete;
 
75
 
 
76
struct MockWindowManager : mir::shell::CanonicalWindowManager
 
77
{
 
78
#if defined(__clang__)
 
79
    #pragma GCC diagnostic push
 
80
    #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 
81
#endif
 
82
    using mir::shell::CanonicalWindowManager::CanonicalWindowManager;
 
83
#if defined(__clang__)
 
84
    #pragma GCC diagnostic pop
 
85
#endif
 
86
 
 
87
    MOCK_METHOD3(handle_request_move,
 
88
        void(std::shared_ptr<mir::scene::Session> const&, std::shared_ptr<mir::scene::Surface> const&, uint64_t));
 
89
};
 
90
 
 
91
struct MouseMoverAndFaker
 
92
{
 
93
    void start_dragging_mouse()
 
94
    {
 
95
        using namespace mir::input::synthesis;
 
96
        fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT));
 
97
    }
 
98
 
 
99
    void move_mouse(Displacement const& displacement)
 
100
    {
 
101
        using mir::input::synthesis::a_pointer_event;
 
102
        fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int()));
 
103
    }
 
104
 
 
105
    void release_mouse()
 
106
    {
 
107
        using namespace mir::input::synthesis;
 
108
        fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT));
 
109
    }
 
110
 
 
111
private:
 
112
    std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{
 
113
        mir_test_framework::add_fake_input_device(
 
114
            mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})
 
115
    };
 
116
};
 
117
 
 
118
Rectangle const screen_geometry{{0,   0}, {800, 600}};
 
119
auto const receive_event_timeout = 90s;
 
120
 
 
121
struct ClientMediatedUserGestures : mir_test_framework::ConnectedClientWithAWindow,
 
122
                                    MouseMoverAndFaker
 
123
{
 
124
    void SetUp() override
 
125
    {
 
126
        initial_display_layout({screen_geometry});
 
127
        server.override_the_window_manager_builder([this](mir::shell::FocusController* focus_controller)
 
128
            {
 
129
                return window_manager =
 
130
                    std::make_shared<MockWindowManager>(focus_controller, server.the_shell_display_layout());
 
131
            });
 
132
 
 
133
        mir_test_framework::ConnectedClientWithAWindow::SetUp();
 
134
        mir_window_set_event_handler(window, &window_event_handler, this);
 
135
 
 
136
        paint_window();
 
137
 
 
138
        center_mouse();
 
139
    }
 
140
 
 
141
    void TearDown() override
 
142
    {
 
143
        reset_window_event_handler();
 
144
        window_manager.reset();
 
145
        surface.reset();
 
146
        mir_test_framework::ConnectedClientWithAWindow::TearDown();
 
147
    }
 
148
 
 
149
    auto user_initiates_gesture() -> Cookie;
 
150
 
 
151
    std::shared_ptr<MockWindowManager> window_manager;
 
152
 
 
153
private:
 
154
    void center_mouse();
 
155
    void paint_window();
 
156
    void set_window_event_handler(std::function<void(MirEvent const* event)> const& handler);
 
157
    void reset_window_event_handler();
 
158
    void invoke_window_event_handler(MirEvent const* event)
 
159
    {
 
160
        std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
 
161
        window_event_handler_(event);
 
162
    }
 
163
 
 
164
    mir::client::Surface surface;
 
165
 
 
166
    std::mutex window_event_handler_mutex;
 
167
    std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {};
 
168
 
 
169
    static void window_event_handler(MirWindow* window, MirEvent const* event, void* context);
 
170
};
 
171
 
 
172
void ClientMediatedUserGestures::set_window_event_handler(std::function<void(MirEvent const* event)> const& handler)
 
173
{
 
174
    std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
 
175
    window_event_handler_ = handler;
 
176
}
 
177
 
 
178
void ClientMediatedUserGestures::reset_window_event_handler()
 
179
{
 
180
    std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
 
181
    window_event_handler_ = [](MirEvent const*) {};
 
182
}
 
183
 
 
184
void ClientMediatedUserGestures::window_event_handler(MirWindow* /*window*/, MirEvent const* event, void* context)
 
185
{
 
186
    static_cast<ClientMediatedUserGestures*>(context)->invoke_window_event_handler(event);
 
187
}
 
188
 
 
189
void ClientMediatedUserGestures::paint_window()
 
190
{
 
191
    {
 
192
        surface = mir::client::Surface{mir_connection_create_render_surface_sync(connection, 42, 42)};
 
193
        auto const spec = mir::client::WindowSpec::for_changes(connection);
 
194
        mir_window_spec_add_render_surface(spec, surface, 42, 42, 0, 0);
 
195
        mir_window_apply_spec(window, spec);
 
196
    }
 
197
 
 
198
    Signal have_focus;
 
199
 
 
200
    set_window_event_handler([&](MirEvent const* event)
 
201
        {
 
202
            if (mir_event_get_type(event) != mir_event_type_window)
 
203
                return;
 
204
 
 
205
            auto const window_event = mir_event_get_window_event(event);
 
206
            if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus)
 
207
                return;
 
208
 
 
209
            if (mir_window_event_get_attribute_value(window_event))
 
210
                have_focus.raise();
 
211
        });
 
212
 
 
213
    mir_buffer_stream_swap_buffers_sync(mir_render_surface_get_buffer_stream(surface, 42, 42, mir_pixel_format_argb_8888));
 
214
 
 
215
    EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true));
 
216
 
 
217
    reset_window_event_handler();
 
218
}
 
219
 
 
220
void ClientMediatedUserGestures::center_mouse()
 
221
{
 
222
    Signal have_mouseover;
 
223
 
 
224
    set_window_event_handler([&](MirEvent const* event)
 
225
        {
 
226
            if (mir_event_get_type(event) != mir_event_type_input)
 
227
                return;
 
228
 
 
229
            auto const input_event = mir_event_get_input_event(event);
 
230
 
 
231
            if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
 
232
                return;
 
233
 
 
234
            auto const pointer_event = mir_input_event_get_pointer_event(input_event);
 
235
 
 
236
            if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
 
237
                return;
 
238
 
 
239
            have_mouseover.raise();
 
240
        });
 
241
 
 
242
    move_mouse(0.5 * as_displacement(screen_geometry.size));
 
243
 
 
244
// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
 
245
// But it isn't essential for the test and we've probably waited long enough
 
246
// for the mouse-down needed by the test to reach the window.
 
247
//    EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
 
248
    have_mouseover.wait_for(receive_event_timeout);
 
249
 
 
250
    reset_window_event_handler();
 
251
}
 
252
 
 
253
auto ClientMediatedUserGestures::user_initiates_gesture() -> Cookie
 
254
{
 
255
    Cookie cookie;
 
256
    Signal have_cookie;
 
257
 
 
258
    set_window_event_handler([&](MirEvent const* event)
 
259
        {
 
260
            if (mir_event_get_type(event) != mir_event_type_input)
 
261
                return;
 
262
 
 
263
            auto const input_event = mir_event_get_input_event(event);
 
264
 
 
265
            if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
 
266
                return;
 
267
 
 
268
            auto const pointer_event = mir_input_event_get_pointer_event(input_event);
 
269
 
 
270
            if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down)
 
271
                return;
 
272
 
 
273
            cookie = Cookie{mir_input_event_get_cookie(input_event)};
 
274
            have_cookie.raise();
 
275
        });
 
276
 
 
277
    start_dragging_mouse();
 
278
 
 
279
    EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true));
 
280
 
 
281
    reset_window_event_handler();
 
282
    return cookie;
 
283
}
 
284
}
 
285
 
 
286
TEST_F(ClientMediatedUserGestures, when_user_initiates_gesture_client_receives_cookie)
 
287
{
 
288
    auto const cookie = user_initiates_gesture();
 
289
 
 
290
    EXPECT_THAT(cookie.get(), NotNull());
 
291
}
 
292
 
 
293
TEST_F(ClientMediatedUserGestures, when_client_initiates_move_window_manager_handles_request)
 
294
{
 
295
    auto const cookie = user_initiates_gesture();
 
296
    Signal have_request;
 
297
    EXPECT_CALL(*window_manager, handle_request_move(_, _, _)).WillOnce(InvokeWithoutArgs([&]{ have_request.raise(); }));
 
298
 
 
299
    mir_window_request_user_move(window, cookie);
 
300
 
 
301
    EXPECT_THAT(have_request.wait_for(receive_event_timeout), Eq(true));
 
302
}