Mir

Merge lp:~andreas-pokorny/mir/rebase-fix-1550050 into lp:mir

Proposed by Andreas Pokorny
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 3397
Proposed branch: lp:~andreas-pokorny/mir/rebase-fix-1550050
Merge into: lp:mir
Diff against target: 343 lines (+153/-31)
5 files modified
src/server/input/default_configuration.cpp (+14/-2)
src/server/input/default_input_device_hub.cpp (+14/-8)
src/server/input/key_repeat_dispatcher.cpp (+41/-0)
src/server/input/key_repeat_dispatcher.h (+7/-4)
tests/unit-tests/input/test_key_repeat_dispatcher.cpp (+77/-17)
To merge this branch: bzr merge lp:~andreas-pokorny/mir/rebase-fix-1550050
Reviewer Review Type Date Requested Status
Alan Griffiths Approve
Mir CI Bot continuous-integration Needs Fixing
Andreas Pokorny (community) Needs Information
Daniel van Vugt Abstain
Cemil Azizoglu (community) Approve
Review via email: mp+288945@code.launchpad.net

Commit message

Fixes the potential endless repeat on unplugged devices (LP: #1550050)

The key repeat dispatcher now reacts on the removal of input devices by resetting potentially still active repeat alarms. The integration here is a bit clumsy, since the input device hub needs the input dispatcher to forward device events. While the input dispatcher needs it to register at the input device hub as an observer.

Description of the change

This fix registers for device changes and then stops sending repeat events for removed devices.

A different approach to this one which requires adding a two step initialization will be proposed soon, but requires an ABI change in the input platform interface.

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3386
https://mir-jenkins.ubuntu.com/job/mir-ci/560/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/448
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/478
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/470
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/470
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/457
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/457/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/457
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/457/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/457
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/457/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/457
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/457/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/457
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/457/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/560/rebuild

review: Approve (continuous-integration)
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

LGTM

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

The version is incorrect. Should be 0.21.0 in lp:mir

Also the changelog is incorrect for the same reason.

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

Separate MPs are up to fix the above issues. You can just remove your version changes here...

review: Needs Fixing
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

ok removed the 'unreleased' changelog entry.
My understanding was that we will make another 0.20.3 release using what is currently in lp:mir - since there are no ABI breaks yet.

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3387
https://mir-jenkins.ubuntu.com/job/mir-ci/572/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/467/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/497
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/489
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/489
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/476
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/476/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/476/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/476
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/476/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/476
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/476/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/476
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/476/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/572/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

lp:mir is series 0.21, which is clear in the launchpad config:
https://launchpad.net/mir

So lp:mir should always call itself series 0.21 until such time as that's released and we're working on series 0.22.

If you want to release a 0.20.3 then that must occur in lp:mir/0.20.

review: Abstain
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

Agreed. Depending on where we release from we do not need this MP!

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

We probably don't need this on trunk. Either we:

1. release 0.20.x from mir/0.20 - which already has this fix;
2. release a better solution (which I understand will break ABI) as 0.21; or,
3. we release 0.21 with this solution and apply the better solution in 0.22

I think we intend to release 0.20.3 and don't need this (yet).

review: Disapprove
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3387
https://mir-jenkins.ubuntu.com/job/mir-ci/578/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/475/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/505
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/497
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/497
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/484
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/484/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/484
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/484/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/484
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/484/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/484
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/484/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/484/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/578/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> We probably don't need this on trunk. Either we:
>
> 1. release 0.20.x from mir/0.20 - which already has this fix;
> 2. release a better solution (which I understand will break ABI) as 0.21; or,
> 3. we release 0.21 with this solution and apply the better solution in 0.22
>
> I think we intend to release 0.20.3 and don't need this (yet).

...we went for option 3

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

The failure is due to more random display configuration API failures. We've had a lot of them lately.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/input/default_configuration.cpp'
2--- src/server/input/default_configuration.cpp 2016-03-09 11:19:15 +0000
3+++ src/server/input/default_configuration.cpp 2016-03-15 07:45:18 +0000
4@@ -295,12 +295,18 @@
5 return default_input_device_hub(
6 [this]()
7 {
8- return std::make_shared<mi::DefaultInputDeviceHub>(
9+ auto input_dispatcher = the_input_dispatcher();
10+ auto key_repeater = std::dynamic_pointer_cast<mi::KeyRepeatDispatcher>(input_dispatcher);
11+ auto hub = std::make_shared<mi::DefaultInputDeviceHub>(
12 the_global_event_sink(),
13 the_seat(),
14 the_input_reading_multiplexer(),
15 the_main_loop(),
16 the_cookie_authority());
17+
18+ if (key_repeater)
19+ key_repeater->set_input_device_hub(hub);
20+ return hub;
21 });
22 }
23
24@@ -309,11 +315,17 @@
25 return default_input_device_hub(
26 [this]()
27 {
28- return std::make_shared<mi::DefaultInputDeviceHub>(
29+ auto input_dispatcher = the_input_dispatcher();
30+ auto key_repeater = std::dynamic_pointer_cast<mi::KeyRepeatDispatcher>(input_dispatcher);
31+ auto hub = std::make_shared<mi::DefaultInputDeviceHub>(
32 the_global_event_sink(),
33 the_seat(),
34 the_input_reading_multiplexer(),
35 the_main_loop(),
36 the_cookie_authority());
37+
38+ if (key_repeater)
39+ key_repeater->set_input_device_hub(hub);
40+ return hub;
41 });
42 }
43
44=== modified file 'src/server/input/default_input_device_hub.cpp'
45--- src/server/input/default_input_device_hub.cpp 2016-03-08 20:43:41 +0000
46+++ src/server/input/default_input_device_hub.cpp 2016-03-15 07:45:18 +0000
47@@ -244,17 +244,23 @@
48 void mi::DefaultInputDeviceHub::remove_device_handle(MirInputDeviceId id)
49 {
50 std::unique_lock<std::mutex> lock(observer_guard);
51- auto handle_it = remove_if(begin(handles),
52- end(handles),
53- [&id](auto const& handle){return handle->id() == id;});
54+ auto handle_it = remove_if(
55+ begin(handles),
56+ end(handles),
57+ [this,&id](auto const& handle)
58+ {
59+ if (handle->id() != id)
60+ return false;
61+ for (auto const& observer : observers)
62+ {
63+ observer->device_removed(handle);
64+ observer->changes_complete();
65+ }
66+ return true;
67+ });
68
69 if (handle_it == end(handles))
70 return;
71- for (auto const& observer : observers)
72- {
73- observer->device_removed(*handle_it);
74- observer->changes_complete();
75- }
76
77 handles.erase(handle_it, end(handles));
78 sink->handle_input_device_change(handles);
79
80=== modified file 'src/server/input/key_repeat_dispatcher.cpp'
81--- src/server/input/key_repeat_dispatcher.cpp 2016-01-29 08:18:22 +0000
82+++ src/server/input/key_repeat_dispatcher.cpp 2016-03-15 07:45:18 +0000
83@@ -18,6 +18,8 @@
84
85 #include "key_repeat_dispatcher.h"
86
87+#include "mir/input/device.h"
88+#include "mir/input/input_device_hub.h"
89 #include "mir/time/alarm_factory.h"
90 #include "mir/time/alarm.h"
91 #include "mir/events/event_private.h"
92@@ -31,6 +33,34 @@
93
94 namespace mi = mir::input;
95
96+namespace
97+{
98+struct DeviceRemovalFilter : mi::InputDeviceObserver
99+{
100+ DeviceRemovalFilter(std::function<void(MirInputDeviceId)> const& on_removal)
101+ : on_removal(on_removal) {}
102+
103+ void device_added(std::shared_ptr<mi::Device> const&) override
104+ {
105+ }
106+
107+ void device_changed(std::shared_ptr<mi::Device> const&) override
108+ {
109+ }
110+
111+ void device_removed(std::shared_ptr<mi::Device> const& device) override
112+ {
113+ on_removal(device->id());
114+ }
115+
116+ void changes_complete() override
117+ {
118+ }
119+ std::function<void(MirInputDeviceId)> on_removal;
120+};
121+
122+}
123+
124 mi::KeyRepeatDispatcher::KeyRepeatDispatcher(
125 std::shared_ptr<mi::InputDispatcher> const& next_dispatcher,
126 std::shared_ptr<mir::time::AlarmFactory> const& factory,
127@@ -47,6 +77,17 @@
128 {
129 }
130
131+void mi::KeyRepeatDispatcher::set_input_device_hub(std::shared_ptr<InputDeviceHub> const& hub)
132+{
133+ hub->add_observer(std::make_shared<DeviceRemovalFilter>(
134+ [this](MirInputDeviceId id)
135+ {
136+ std::unique_lock<std::mutex> lock(repeat_state_mutex);
137+ repeat_state_by_device.erase(id); // destructor cancels alarms
138+ }
139+ ));
140+}
141+
142 mi::KeyRepeatDispatcher::KeyboardState& mi::KeyRepeatDispatcher::ensure_state_for_device_locked(std::lock_guard<std::mutex> const&, MirInputDeviceId id)
143 {
144 repeat_state_by_device.insert(std::make_pair(id, KeyboardState()));
145
146=== modified file 'src/server/input/key_repeat_dispatcher.h'
147--- src/server/input/key_repeat_dispatcher.h 2016-01-29 08:18:22 +0000
148+++ src/server/input/key_repeat_dispatcher.h 2016-03-15 07:45:18 +0000
149@@ -1,5 +1,5 @@
150 /*
151- * Copyright © 2015 Canonical Ltd.
152+ * Copyright © 2015-2016 Canonical Ltd.
153 *
154 * This program is free software: you can redistribute it and/or modify it
155 * under the terms of the GNU General Public License version 3,
156@@ -20,6 +20,7 @@
157 #define MIR_INPUT_KEY_REPEAT_DISPATCHER_H_
158
159 #include "mir/input/input_dispatcher.h"
160+#include "mir/input/input_device_observer.h"
161
162 #include <memory>
163 #include <chrono>
164@@ -39,8 +40,8 @@
165 }
166 namespace input
167 {
168-
169-class KeyRepeatDispatcher : public mir::input::InputDispatcher
170+class InputDeviceHub;
171+class KeyRepeatDispatcher : public InputDispatcher
172 {
173 public:
174 KeyRepeatDispatcher(std::shared_ptr<InputDispatcher> const& next_dispatcher,
175@@ -54,7 +55,9 @@
176 bool dispatch(MirEvent const& event) override;
177 void start() override;
178 void stop() override;
179-
180+
181+ void set_input_device_hub(std::shared_ptr<InputDeviceHub> const& hub);
182+
183 private:
184 std::mutex repeat_state_mutex;
185
186
187=== modified file 'tests/unit-tests/input/test_key_repeat_dispatcher.cpp'
188--- tests/unit-tests/input/test_key_repeat_dispatcher.cpp 2016-01-29 08:18:22 +0000
189+++ tests/unit-tests/input/test_key_repeat_dispatcher.cpp 2016-03-15 07:45:18 +0000
190@@ -23,9 +23,15 @@
191 #include "mir/time/alarm.h"
192 #include "mir/time/alarm_factory.h"
193 #include "mir/cookie/authority.h"
194+#include "mir/input/input_device_observer.h"
195+#include "mir/input/pointer_configuration.h"
196+#include "mir/input/touchpad_configuration.h"
197+#include "mir/input/device.h"
198
199+#include "mir/test/fake_shared.h"
200 #include "mir/test/event_matchers.h"
201 #include "mir/test/doubles/mock_input_dispatcher.h"
202+#include "mir/test/doubles/mock_input_device_hub.h"
203
204 #include <gtest/gtest.h>
205 #include <gmock/gmock.h>
206@@ -35,6 +41,8 @@
207 namespace mt = mir::test;
208 namespace mtd = mt::doubles;
209
210+using namespace ::testing;
211+
212 namespace
213 {
214 struct MockAlarm : public mir::time::Alarm
215@@ -43,6 +51,12 @@
216 MOCK_CONST_METHOD0(state, mir::time::Alarm::State());
217 MOCK_METHOD1(reschedule_in, bool(std::chrono::milliseconds));
218 MOCK_METHOD1(reschedule_for, bool(mir::time::Timestamp));
219+
220+ // destructor cancels the alarm
221+ ~MockAlarm()
222+ {
223+ cancel();
224+ }
225 };
226
227 struct MockAlarmFactory : public mir::time::AlarmFactory
228@@ -59,25 +73,60 @@
229 }
230 };
231
232+struct StubDevice : public mi::Device
233+{
234+ MirInputDeviceId device_id;
235+ StubDevice(MirInputDeviceId id) : device_id(id) {}
236+ MirInputDeviceId id() const { return device_id;}
237+ mi::DeviceCapabilities capabilities() const {return mi::DeviceCapability::keyboard;}
238+ std::string name() const {return {};}
239+ std::string unique_id() const {return {};}
240+
241+ mir::optional_value<mi::PointerConfiguration> pointer_configuration() const {return {};}
242+ void apply_pointer_configuration(mi::PointerConfiguration const&) {;}
243+ mir::optional_value<mi::TouchpadConfiguration> touchpad_configuration() const {return {};}
244+ void apply_touchpad_configuration(mi::TouchpadConfiguration const&) {}
245+};
246+
247 struct KeyRepeatDispatcher : public testing::Test
248 {
249 KeyRepeatDispatcher()
250 : dispatcher(mock_next_dispatcher, mock_alarm_factory, cookie_authority, true, repeat_time, repeat_delay)
251 {
252- }
253+ ON_CALL(hub,add_observer(_)).WillByDefault(SaveArg<0>(&observer));
254+ dispatcher.set_input_device_hub(mt::fake_shared(hub));
255+ }
256+ void simulate_device_removal()
257+ {
258+ StubDevice dev(test_device);
259+ observer->device_removed(mt::fake_shared(dev));
260+ observer->changes_complete();
261+ }
262+
263+ const MirInputDeviceId test_device = 123;
264 std::shared_ptr<mtd::MockInputDispatcher> mock_next_dispatcher = std::make_shared<mtd::MockInputDispatcher>();
265 std::shared_ptr<MockAlarmFactory> mock_alarm_factory = std::make_shared<MockAlarmFactory>();
266 std::shared_ptr<mir::cookie::Authority> cookie_authority = mir::cookie::Authority::create();
267 std::chrono::milliseconds const repeat_time{2};
268 std::chrono::milliseconds const repeat_delay{1};
269+ std::shared_ptr<mi::InputDeviceObserver> observer;
270+ NiceMock<mtd::MockInputDeviceHub> hub;
271 mi::KeyRepeatDispatcher dispatcher;
272+
273+ mir::EventUPtr a_key_down_event()
274+ {
275+ return mev::make_event(test_device, std::chrono::nanoseconds(0), std::vector<uint8_t>{}, mir_keyboard_action_down, 0, 0, mir_input_event_modifier_alt);
276+ }
277+
278+ mir::EventUPtr a_key_up_event()
279+ {
280+ return mev::make_event(test_device, std::chrono::nanoseconds(0), std::vector<uint8_t>{}, mir_keyboard_action_up, 0, 0, mir_input_event_modifier_alt);
281+ }
282 };
283 }
284
285 TEST_F(KeyRepeatDispatcher, forwards_start_stop)
286 {
287- using namespace ::testing;
288-
289 InSequence seq;
290 EXPECT_CALL(*mock_next_dispatcher, start()).Times(1);
291 EXPECT_CALL(*mock_next_dispatcher, stop()).Times(1);
292@@ -86,22 +135,8 @@
293 dispatcher.stop();
294 }
295
296-namespace
297-{
298-mir::EventUPtr a_key_down_event()
299-{
300- return mev::make_event(0, std::chrono::nanoseconds(0), std::vector<uint8_t>{}, mir_keyboard_action_down, 0, 0, mir_input_event_modifier_alt);
301-}
302-mir::EventUPtr a_key_up_event()
303-{
304- return mev::make_event(0, std::chrono::nanoseconds(0), std::vector<uint8_t>{}, mir_keyboard_action_up, 0, 0, mir_input_event_modifier_alt);
305-}
306-}
307-
308 TEST_F(KeyRepeatDispatcher, schedules_alarm_to_repeat_key_down)
309 {
310- using namespace ::testing;
311-
312 MockAlarm *mock_alarm = new MockAlarm; // deleted by AlarmFactory
313 std::function<void()> alarm_function;
314
315@@ -122,3 +157,28 @@
316 // Trigger the cancel
317 dispatcher.dispatch(*a_key_up_event());
318 }
319+
320+TEST_F(KeyRepeatDispatcher, stops_repeat_on_device_removal)
321+{
322+ MockAlarm *mock_alarm = new MockAlarm;
323+ std::function<void()> alarm_function;
324+ bool alarm_canceled = false;
325+
326+ InSequence seq;
327+ EXPECT_CALL(*mock_alarm_factory, create_alarm_adapter(_)).Times(1).
328+ WillOnce(DoAll(SaveArg<0>(&alarm_function), Return(mock_alarm)));
329+ // Once for initial down and again when invoked
330+ EXPECT_CALL(*mock_alarm, reschedule_in(repeat_time)).Times(1).WillOnce(Return(true));
331+ EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyDownEvent())).Times(1);
332+ EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyRepeatEvent())).Times(1);
333+ EXPECT_CALL(*mock_alarm, reschedule_in(repeat_delay)).Times(1).WillOnce(Return(true));
334+ ON_CALL(*mock_alarm, cancel()).WillByDefault(Invoke([&](){alarm_canceled = true; return true;}));
335+
336+ dispatcher.dispatch(*a_key_down_event());
337+
338+ alarm_function();
339+ Mock::VerifyAndClearExpectations(mock_alarm); // mock_alarm will be deleted after this
340+
341+ simulate_device_removal();
342+ EXPECT_THAT(alarm_canceled, Eq(true));
343+}

Subscribers

People subscribed via source and target branches