Mir

Merge lp:~andreas-pokorny/mir/move-config-changer-to-input-thread into lp:mir

Proposed by Andreas Pokorny
Status: Merged
Approved by: Andreas Pokorny
Approved revision: 4091
Merged at revision: 4109
Proposed branch: lp:~andreas-pokorny/mir/move-config-changer-to-input-thread
Merge into: lp:mir
Diff against target: 1036 lines (+408/-127)
13 files modified
src/include/server/mir/default_server_configuration.h (+1/-0)
src/server/input/default_configuration.cpp (+7/-3)
src/server/input/default_input_device_hub.cpp (+119/-35)
src/server/input/default_input_device_hub.h (+25/-7)
tests/acceptance-tests/test_client_input.cpp (+3/-2)
tests/acceptance-tests/test_input_device_hub.cpp (+4/-2)
tests/acceptance-tests/test_nested_input.cpp (+8/-7)
tests/include/mir/test/doubles/mock_device.h (+71/-0)
tests/integration-tests/input/test_single_seat_setup.cpp (+26/-26)
tests/unit-tests/input/CMakeLists.txt (+1/-0)
tests/unit-tests/input/test_config_changer.cpp (+3/-30)
tests/unit-tests/input/test_default_input_device_hub.cpp (+21/-15)
tests/unit-tests/input/test_external_input_device_hub.cpp (+119/-0)
To merge this branch: bzr merge lp:~andreas-pokorny/mir/move-config-changer-to-input-thread
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Approve
Alexandros Frantzis (community) Approve
Alan Griffiths Approve
Review via email: mp+319840@code.launchpad.net

Commit message

Move execution of InputDeviceObservers into input thread

To ensure that the messages about available input devices reach clients before respective input events and input device state events the InputDeviceObservers are now executed inside the same thread that dispatches the input events. Since downstream shells might also implement InputDeviceObservers, the existing behavior is kept due to a new wrapper around DefaultInputDeviceHub that runs inside the ServerActionQueue.

Description of the change

This is the alternative to
lp:~andreas-pokorny/mir/wait-for-input-config-before-emitting-input-state

Instead of catching the case inside the nested input platform the ordering is guaranteed because the mir::input::ConfigChanger receives the new devices before they get advertised to the seat or allowed to emit input events (via mir::InputDevice::start).

Maybe the attached bugs have already been fixed or are just more rare due to other changes:
* removal of the per-surface fds
* updated surface input dispatcher logic in mir::input::SurfaceInputDispatcher::for_each)

Still this change is closely related as it ensures that the input device state events will occur after respective input config messages. If the Input device state event happens before the first input config message containing the input devices it would be filtered out in the nested platform.

To post a comment you must log in.
4086. By Andreas Pokorny on 2017-03-14

remove unneeded functions

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

PASSED: Continuous integration, rev:4085
https://mir-jenkins.ubuntu.com/job/mir-ci/3159/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4238
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4325
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4315
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4315
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4315
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4265
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4265/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4265
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4265/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4265
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4265/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/4265
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4265/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/4265
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4265/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4265
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4265/artifact/output/*zip*/output.zip

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

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

PASSED: Continuous integration, rev:4086
https://mir-jenkins.ubuntu.com/job/mir-ci/3160/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4240
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4327
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4317
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4317
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4317
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4267
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4267/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4267
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4267/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4267
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4267/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/4267
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4267/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/4267
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4267/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4267
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4267/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
4087. By Andreas Pokorny on 2017-03-15

Merge lp:mir

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

PASSED: Continuous integration, rev:4087
https://mir-jenkins.ubuntu.com/job/mir-ci/3165/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4247
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4334
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4324
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4324
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4324
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4274
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4274/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4274
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4274/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4274
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4274/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/4274
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4274/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/4274
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4274/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4274
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4274/artifact/output/*zip*/output.zip

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

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

OK

review: Approve
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

+ return std::make_shared<mi::ExternalInputDeviceHub>(the_default_input_device_hub(),
+ the_main_loop());

The ExternalInputDeviceHub doesn't hold a strong reference to the the_default_input_device_hub(), so
depending on the order of initialization (i.e., if we haven't already stored a strong reference to default_input_device_hub()) we will end up immediately destroying the default hub we just created.

Perhaps this doesn't actually happen in our current configuration, but it is currently not easy to reason about the correctness of the lifecycle, and this could be a pain point in future refactorings.

> void mi::ExternalInputDeviceHub::remove_observer
> mi::ExternalInputDeviceHub::for_each_input_device
> mi::ExternalInputDeviceHub::for_each_mutable_input_device

In the above function we are using the result of weak_ptr.lock() without checking if it's valid. Is it safe to do so?

Looks good otherwise.

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

> + return
> std::make_shared<mi::ExternalInputDeviceHub>(the_default_input_device_hub(),
> + the_main_loop());
>
> The ExternalInputDeviceHub doesn't hold a strong reference to the
> the_default_input_device_hub(), so
> depending on the order of initialization (i.e., if we haven't already stored a
> strong reference to default_input_device_hub()) we will end up immediately
> destroying the default hub we just created.
>
> Perhaps this doesn't actually happen in our current configuration, but it is
> currently not easy to reason about the correctness of the lifecycle, and this
> could be a pain point in future refactorings.

Yes the actual input device hub is kept alive via loaded platform / input manager / DisplayServer and also via ipc factory / Connector / DisplayServer. I am using a weak_ptr to avoid a cycle between hub and observers.

> > void mi::ExternalInputDeviceHub::remove_observer
> > mi::ExternalInputDeviceHub::for_each_input_device
> > mi::ExternalInputDeviceHub::for_each_mutable_input_device
>
> In the above function we are using the result of weak_ptr.lock() without
> checking if it's valid. Is it safe to do so?

Changed that..

I think I could also wrap weak_ptr to make it match Element concept of ThreadSafeList

4088. By Andreas Pokorny on 2017-03-22

review findings

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

PASSED: Continuous integration, rev:4088
https://mir-jenkins.ubuntu.com/job/mir-ci/3210/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4321
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4408
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4398
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4398
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4398
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4353
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4353/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4353
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4353/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4353
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4353/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/4353
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4353/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/4353
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4353/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4353
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4353/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
4089. By Andreas Pokorny on 2017-03-22

merge lp:mir

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

PASSED: Continuous integration, rev:4089
https://mir-jenkins.ubuntu.com/job/mir-ci/3212/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4323
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4410
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4400
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4400
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4400
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4355
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4355/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4355
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4355/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4355
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4355/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/4355
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4355/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/4355
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4355/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4355
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4355/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> Yes the actual input device hub is kept alive via loaded platform / input manager / DisplayServer
> and also via ipc factory / Connector / DisplayServer. I am using a weak_ptr to avoid a cycle
> between hub and observers.

Hmm, still... the fact that if one calls mir::DefaultServerConfiguration::the_input_device_hub() as the first or only input-related method, they will get a broken InputDeviceHub is super-unintuitive and makes me very uneasy.

Couldn't mi::ExternalInputDeviceHub hold a shared_ptr<> to the hub? It makes sense for a wrapper to hold a strong reference, not a weak one. The Internal/Observer class would continue to hold a weak_ptr (or even a raw pointer, if we can guarantee the related lifetimes) to break the cycle.

4090. By Andreas Pokorny on 2017-03-22

keep a strong reference of the wrapped hub

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

> > Yes the actual input device hub is kept alive via loaded platform / input
> manager / DisplayServer
> > and also via ipc factory / Connector / DisplayServer. I am using a weak_ptr
> to avoid a cycle
> > between hub and observers.
>
> Hmm, still... the fact that if one calls
> mir::DefaultServerConfiguration::the_input_device_hub() as the first or only
> input-related method, they will get a broken InputDeviceHub is super-
> unintuitive and makes me very uneasy.
>
> Couldn't mi::ExternalInputDeviceHub hold a shared_ptr<> to the hub? It makes
> sense for a wrapper to hold a strong reference, not a weak one. The
> Internal/Observer class would continue to hold a weak_ptr (or even a raw
> pointer, if we can guarantee the related lifetimes) to break the cycle.

Oh I have not thought of that... pushed.

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

PASSED: Continuous integration, rev:4090
https://mir-jenkins.ubuntu.com/job/mir-ci/3216/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4328
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4415
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4405
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4405
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4405
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4360
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4360/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4360
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4360/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4360
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4360/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/4360
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4360/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/4360
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4360/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4360
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4360/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> + std::weak_ptr<InputDeviceHub> hub;

Seems this is not needed anymore?

> + auto observer = obs.lock();

Probably needs a validity check, unless there is an implied validity guarantee I am not aware of.

review: Needs Fixing
4091. By Andreas Pokorny on 2017-03-23

more findings

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

> > + std::weak_ptr<InputDeviceHub> hub;
>
> Seems this is not needed anymore?

oh right..

>
> > + auto observer = obs.lock();
>
> Probably needs a validity check, unless there is an implied validity guarantee
> I am not aware of.

Hm only that nothing happens if the validity check fails..

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

> Hm only that nothing happens if the validity check fails..

Good point.

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

PASSED: Continuous integration, rev:4091
https://mir-jenkins.ubuntu.com/job/mir-ci/3226/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4341
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4428
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4418
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4418
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4418
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4373
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4373/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4373
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4373/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4373
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4373/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/4373
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4373/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/4373
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4373/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4373
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4373/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/include/server/mir/default_server_configuration.h'
2--- src/include/server/mir/default_server_configuration.h 2017-03-13 08:12:52 +0000
3+++ src/include/server/mir/default_server_configuration.h 2017-03-23 16:31:26 +0000
4@@ -384,6 +384,7 @@
5 CachedPtr<input::InputManager> input_manager;
6 CachedPtr<input::SurfaceInputDispatcher> surface_input_dispatcher;
7 CachedPtr<input::DefaultInputDeviceHub> default_input_device_hub;
8+ CachedPtr<input::InputDeviceHub> input_device_hub;
9 CachedPtr<dispatch::MultiplexingDispatchable> input_reading_multiplexer;
10 CachedPtr<input::InputDispatcher> input_dispatcher;
11 CachedPtr<shell::InputTargeter> input_targeter;
12
13=== modified file 'src/server/input/default_configuration.cpp'
14--- src/server/input/default_configuration.cpp 2017-03-14 02:26:28 +0000
15+++ src/server/input/default_configuration.cpp 2017-03-23 16:31:26 +0000
16@@ -301,7 +301,12 @@
17
18 std::shared_ptr<mi::InputDeviceHub> mir::DefaultServerConfiguration::the_input_device_hub()
19 {
20- return the_default_input_device_hub();
21+ return input_device_hub(
22+ [this]()
23+ {
24+ return std::make_shared<mi::ExternalInputDeviceHub>(the_default_input_device_hub(),
25+ the_main_loop());
26+ });
27 }
28
29 std::shared_ptr<mi::DefaultInputDeviceHub> mir::DefaultServerConfiguration::the_default_input_device_hub()
30@@ -314,7 +319,6 @@
31 auto hub = std::make_shared<mi::DefaultInputDeviceHub>(
32 the_seat(),
33 the_input_reading_multiplexer(),
34- the_main_loop(),
35 the_cookie_authority(),
36 the_key_mapper(),
37 the_server_status_listener());
38@@ -359,7 +363,7 @@
39 return input_configuration_changer(
40 [this]()
41 {
42- return std::make_shared<mi::ConfigChanger>(the_input_manager(), the_input_device_hub(), the_session_container(), the_session_event_handler_register());
43+ return std::make_shared<mi::ConfigChanger>(the_input_manager(), the_default_input_device_hub(), the_session_container(), the_session_event_handler_register());
44 }
45 );
46 }
47
48=== modified file 'src/server/input/default_input_device_hub.cpp'
49--- src/server/input/default_input_device_hub.cpp 2017-03-14 02:26:28 +0000
50+++ src/server/input/default_input_device_hub.cpp 2017-03-23 16:31:26 +0000
51@@ -41,16 +41,122 @@
52
53 namespace mi = mir::input;
54
55+struct mi::ExternalInputDeviceHub::Internal : InputDeviceObserver
56+{
57+ Internal(std::shared_ptr<ServerActionQueue> const& queue) :
58+ observer_queue{queue}
59+ {}
60+ void device_added(std::shared_ptr<Device> const& device) override;
61+ void device_changed(std::shared_ptr<Device> const& device) override;
62+ void device_removed(std::shared_ptr<Device> const& device) override;
63+ void changes_complete() override;
64+
65+ std::shared_ptr<ServerActionQueue> const observer_queue;
66+ ThreadSafeList<std::shared_ptr<InputDeviceObserver>> observers;
67+ std::vector<std::shared_ptr<Device>> devices_added;
68+ std::vector<std::shared_ptr<Device>> devices_changed;
69+ std::vector<std::shared_ptr<Device>> devices_removed;
70+ std::vector<std::shared_ptr<Device>> handles;
71+};
72+
73+mi::ExternalInputDeviceHub::ExternalInputDeviceHub(std::shared_ptr<mi::InputDeviceHub> const& hub, std::shared_ptr<mir::ServerActionQueue> const& queue)
74+ : data{std::make_shared<mi::ExternalInputDeviceHub::Internal>(queue)}, hub{hub}
75+{
76+ hub->add_observer(data);
77+}
78+
79+mi::ExternalInputDeviceHub::~ExternalInputDeviceHub()
80+{
81+ data->observer_queue->pause_processing_for(data.get());
82+}
83+
84+void mi::ExternalInputDeviceHub::add_observer(std::shared_ptr<InputDeviceObserver> const& observer)
85+{
86+ data->observer_queue->enqueue(
87+ data.get(),
88+ [observer, data = this->data]
89+ {
90+ for (auto const& item : data->handles)
91+ observer->device_added(item);
92+ observer->changes_complete();
93+ data->observers.add(observer);
94+ });
95+}
96+
97+void mi::ExternalInputDeviceHub::remove_observer(std::weak_ptr<InputDeviceObserver> const& obs)
98+{
99+ auto observer = obs.lock();
100+ if (observer)
101+ data->observers.remove(observer);
102+}
103+
104+void mi::ExternalInputDeviceHub::for_each_input_device(std::function<void(Device const& device)> const& callback)
105+{
106+ hub->for_each_input_device(callback);
107+}
108+
109+void mi::ExternalInputDeviceHub::for_each_mutable_input_device(std::function<void(Device& device)> const& callback)
110+{
111+ hub->for_each_mutable_input_device(callback);
112+}
113+
114+void mi::ExternalInputDeviceHub::Internal::device_added(std::shared_ptr<Device> const& device)
115+{
116+ devices_added.push_back(device);
117+}
118+
119+void mi::ExternalInputDeviceHub::Internal::device_changed(std::shared_ptr<Device> const& device)
120+{
121+ devices_changed.push_back(device);
122+}
123+
124+void mi::ExternalInputDeviceHub::Internal::device_removed(std::shared_ptr<Device> const& device)
125+{
126+ devices_removed.push_back(device);
127+}
128+
129+void mi::ExternalInputDeviceHub::Internal::changes_complete()
130+{
131+ decltype(devices_added) added, changed, removed;
132+
133+ std::swap(devices_added, added);
134+ std::swap(devices_changed, changed);
135+ std::swap(devices_removed, removed);
136+
137+ if (!(added.empty() && changed.empty() && removed.empty()))
138+ observer_queue->enqueue(
139+ this,
140+ [this, added, changed, removed]
141+ {
142+ observers.for_each([&](std::shared_ptr<InputDeviceObserver> const& observer)
143+ {
144+ for (auto const& dev : added)
145+ observer->device_added(dev);
146+ for (auto const& dev : changed)
147+ observer->device_changed(dev);
148+ for (auto const& dev : removed)
149+ observer->device_removed(dev);
150+ observer->changes_complete();
151+ });
152+
153+ auto end_it = handles.end();
154+ for (auto const& dev : removed)
155+ end_it = remove(begin(handles), end(handles), dev);
156+ if (end_it != handles.end())
157+ handles.erase(end_it, end(handles));
158+ for (auto const& dev : added)
159+ handles.push_back(dev);
160+ });
161+}
162+
163 mi::DefaultInputDeviceHub::DefaultInputDeviceHub(
164 std::shared_ptr<mi::Seat> const& seat,
165 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
166- std::shared_ptr<mir::ServerActionQueue> const& observer_queue,
167 std::shared_ptr<mir::cookie::Authority> const& cookie_authority,
168 std::shared_ptr<mi::KeyMapper> const& key_mapper,
169 std::shared_ptr<mir::ServerStatusListener> const& server_status_listener)
170 : seat{seat},
171 input_dispatchable{input_multiplexer},
172- observer_queue(observer_queue),
173 device_queue(std::make_shared<dispatch::ActionQueue>()),
174 cookie_authority(cookie_authority),
175 key_mapper(key_mapper),
176@@ -81,17 +187,10 @@
177 device, id, input_dispatchable, cookie_authority, handle));
178
179 auto const& dev = devices.back();
180+ add_device_handle(handle);
181
182 seat->add_device(*handle);
183 dev->start(seat);
184-
185- // pass input device handle to observer loop..
186- observer_queue->enqueue(this,
187- [this, handle]()
188- {
189- add_device_handle(handle);
190- });
191-
192 }
193 else
194 {
195@@ -118,13 +217,7 @@
196 seat->remove_device(*item->handle);
197 item->stop();
198 }
199- // send input device info to observer queue..
200- observer_queue->enqueue(
201- this,
202- [this,id = item->id()]()
203- {
204- remove_device_handle(id);
205- });
206+ remove_device_handle(item->id());
207
208 return true;
209 }
210@@ -230,18 +323,15 @@
211
212 void mi::DefaultInputDeviceHub::add_observer(std::shared_ptr<InputDeviceObserver> const& observer)
213 {
214- observer_queue->enqueue(
215- this,
216- [observer,this]
217+ device_queue->enqueue(
218+ [this,observer]()
219 {
220+ std::unique_lock<std::mutex> lock(handles_guard);
221+ for (auto const& item : handles)
222+ observer->device_added(item);
223+ observer->changes_complete();
224 observers.add(observer);
225- for (auto const& item : handles)
226- {
227- observer->device_added(item);
228- }
229- observer->changes_complete();
230- }
231- );
232+ });
233 }
234
235 void mi::DefaultInputDeviceHub::for_each_input_device(std::function<void(Device const&)> const& callback)
236@@ -270,12 +360,7 @@
237 void mi::DefaultInputDeviceHub::remove_observer(std::weak_ptr<InputDeviceObserver> const& element)
238 {
239 auto observer = element.lock();
240-
241- observer_queue->enqueue(this,
242- [observer, this]
243- {
244- observers.remove(observer);
245- });
246+ observers.remove(observer);
247 }
248
249 void mi::DefaultInputDeviceHub::add_device_handle(std::shared_ptr<DefaultDevice> const& handle)
250@@ -285,7 +370,7 @@
251 handles.push_back(handle);
252 }
253
254- observers.for_each([&handle](std::shared_ptr<InputDeviceObserver> const& observer)
255+ observers.for_each([&handle, this](std::shared_ptr<InputDeviceObserver> const& observer)
256 {
257 observer->device_added(handle);
258 observer->changes_complete();
259@@ -395,4 +480,3 @@
260 });
261 }
262 }
263-
264
265=== modified file 'src/server/input/default_input_device_hub.h'
266--- src/server/input/default_input_device_hub.h 2017-03-20 11:44:40 +0000
267+++ src/server/input/default_input_device_hub.h 2017-03-23 16:31:26 +0000
268@@ -25,6 +25,7 @@
269 #include "mir/input/input_sink.h"
270 #include "mir/input/seat.h"
271 #include "mir/input/input_device_hub.h"
272+#include "mir/input/input_device_observer.h"
273 #include "mir/input/input_device_info.h"
274 #include "mir/input/mir_input_config.h"
275 #include "mir/thread_safe_list.h"
276@@ -57,13 +58,32 @@
277 class DefaultDevice;
278 class Seat;
279 class KeyMapper;
280-
281-class DefaultInputDeviceHub : public InputDeviceRegistry, public InputDeviceHub
282+class DefaultInputDeviceHub;
283+
284+struct ExternalInputDeviceHub : InputDeviceHub
285+{
286+ ExternalInputDeviceHub(std::shared_ptr<InputDeviceHub> const& actual_hub,
287+ std::shared_ptr<ServerActionQueue> const& observer_queue);
288+ ~ExternalInputDeviceHub();
289+
290+ void add_observer(std::shared_ptr<InputDeviceObserver> const&) override;
291+ void remove_observer(std::weak_ptr<InputDeviceObserver> const&) override;
292+ void for_each_input_device(std::function<void(Device const& device)> const& callback) override;
293+ void for_each_mutable_input_device(std::function<void(Device& device)> const& callback) override;
294+
295+private:
296+ struct Internal;
297+ std::shared_ptr<Internal> data;
298+ std::shared_ptr<InputDeviceHub> hub;
299+};
300+
301+class DefaultInputDeviceHub :
302+ public InputDeviceRegistry,
303+ public InputDeviceHub
304 {
305 public:
306 DefaultInputDeviceHub(std::shared_ptr<Seat> const& seat,
307 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
308- std::shared_ptr<ServerActionQueue> const& observer_queue,
309 std::shared_ptr<cookie::Authority> const& cookie_authority,
310 std::shared_ptr<KeyMapper> const& key_mapper,
311 std::shared_ptr<ServerStatusListener> const& server_status_listener);
312@@ -77,18 +97,16 @@
313 void remove_observer(std::weak_ptr<InputDeviceObserver> const&) override;
314 void for_each_input_device(std::function<void(Device const& device)> const& callback) override;
315 void for_each_mutable_input_device(std::function<void(Device& device)> const& callback) override;
316-
317-
318 private:
319 void add_device_handle(std::shared_ptr<DefaultDevice> const& handle);
320 void remove_device_handle(MirInputDeviceId id);
321 void device_changed(Device* dev);
322 void emit_changed_devices();
323 MirInputDeviceId create_new_device_id();
324+
325 std::shared_ptr<Seat> const seat;
326 std::shared_ptr<dispatch::MultiplexingDispatchable> const input_dispatchable;
327- std::mutex handles_guard;
328- std::shared_ptr<ServerActionQueue> const observer_queue;
329+ std::mutex mutable handles_guard;
330 std::shared_ptr<dispatch::ActionQueue> const device_queue;
331 std::shared_ptr<cookie::Authority> const cookie_authority;
332 std::shared_ptr<KeyMapper> const key_mapper;
333
334=== modified file 'tests/acceptance-tests/test_client_input.cpp'
335--- tests/acceptance-tests/test_client_input.cpp 2017-03-22 07:01:56 +0000
336+++ tests/acceptance-tests/test_client_input.cpp 2017-03-23 16:31:26 +0000
337@@ -343,12 +343,13 @@
338 devices_available.raise();
339 });
340
341- server.the_input_device_hub()->add_observer(counter);
342+ auto hub = server.the_input_device_hub();
343+ hub->add_observer(counter);
344
345 devices_available.wait_for(5s);
346 ASSERT_THAT(counter->count_devices, Eq(expected_number_of_input_devices));
347
348- server.the_input_device_hub()->remove_observer(counter);
349+ hub->remove_observer(counter);
350 }
351
352 MirInputDevice const* get_device_with_capabilities(MirInputConfig const* config, MirInputDeviceCapabilities caps)
353
354=== modified file 'tests/acceptance-tests/test_input_device_hub.cpp'
355--- tests/acceptance-tests/test_input_device_hub.cpp 2017-01-18 02:29:37 +0000
356+++ tests/acceptance-tests/test_input_device_hub.cpp 2017-03-23 16:31:26 +0000
357@@ -60,7 +60,8 @@
358 EXPECT_CALL(observer, changes_complete())
359 .WillOnce(mt::WakeUp(&observer_registered));
360
361- server.the_input_device_hub()->add_observer(mt::fake_shared(observer));
362+ auto device_hub = server.the_input_device_hub();
363+ device_hub->add_observer(mt::fake_shared(observer));
364 observer_registered.wait_for(std::chrono::seconds{4});
365 }
366
367@@ -75,7 +76,8 @@
368 EXPECT_CALL(observer, changes_complete())
369 .WillOnce(mt::WakeUp(&callbacks_received));
370
371- server.the_input_device_hub()->add_observer(mt::fake_shared(observer));
372+ auto device_hub = server.the_input_device_hub();
373+ device_hub->add_observer(mt::fake_shared(observer));
374 observer_registered.wait_for(std::chrono::seconds{4});
375
376 keep_on_living = mtf::add_fake_input_device(mi::InputDeviceInfo{"keyboard", "keyboard-uid", mir::input::DeviceCapability::keyboard});
377
378=== modified file 'tests/acceptance-tests/test_nested_input.cpp'
379--- tests/acceptance-tests/test_nested_input.cpp 2017-03-16 03:23:51 +0000
380+++ tests/acceptance-tests/test_nested_input.cpp 2017-03-23 16:31:26 +0000
381@@ -112,13 +112,12 @@
382
383 struct NestedInput : public mtf::HeadlessInProcessServer
384 {
385-
386 void SetUp()
387 {
388 initial_display_layout(display_geometry);
389 mtf::HeadlessInProcessServer::SetUp();
390 }
391-
392+
393 mtd::NestedMockEGL mock_egl;
394
395 std::unique_ptr<mtf::FakeInputDevice> fake_keyboard{
396@@ -301,13 +300,12 @@
397 NestedServerWithMockEventFilter nested_mir{new_connection()};
398 auto nested_hub = nested_mir.server.the_input_device_hub();
399
400- EXPECT_CALL(*mock_observer, device_added(_)).Times(1);
401- EXPECT_CALL(*mock_observer, changes_complete())
402- .Times(1)
403+ EXPECT_CALL(*mock_observer, device_added(_))
404 .WillOnce(mt::WakeUp(&input_device_changes_complete));
405
406 nested_hub->add_observer(mock_observer);
407 input_device_changes_complete.wait_for(10s);
408+ nested_hub->remove_observer(mock_observer);
409 }
410
411 TEST_F(NestedInput, device_added_on_host_triggeres_nested_device_observer)
412@@ -315,15 +313,18 @@
413 NestedServerWithMockEventFilter nested_mir{new_connection()};
414 auto nested_hub = nested_mir.server.the_input_device_hub();
415
416- EXPECT_CALL(*mock_observer, changes_complete()).Times(1)
417+ // wait until we see the keyboard from the fixture:
418+ EXPECT_CALL(*mock_observer, device_added(_)).Times(1)
419 .WillOnce(mt::WakeUp(&input_device_changes_complete));
420 nested_hub->add_observer(mock_observer);
421
422 input_device_changes_complete.wait_for(10s);
423 EXPECT_THAT(input_device_changes_complete.raised(), Eq(true));
424 input_device_changes_complete.reset();
425+ ::testing::Mock::VerifyAndClearExpectations(&mock_observer);
426
427- EXPECT_CALL(*mock_observer, changes_complete())
428+ // wait for the fake mouse
429+ EXPECT_CALL(*mock_observer, device_added(_)).Times(1)
430 .WillOnce(mt::WakeUp(&input_device_changes_complete));
431
432 auto mouse = mtf::add_fake_input_device(mi::InputDeviceInfo{"mouse", "mouse-uid" , mi::DeviceCapability::pointer});
433
434=== added file 'tests/include/mir/test/doubles/mock_device.h'
435--- tests/include/mir/test/doubles/mock_device.h 1970-01-01 00:00:00 +0000
436+++ tests/include/mir/test/doubles/mock_device.h 2017-03-23 16:31:26 +0000
437@@ -0,0 +1,71 @@
438+/*
439+ * Copyright © 2017 Canonical Ltd.
440+ *
441+ * This program is free software: you can redistribute it and/or modify
442+ * it under the terms of the GNU General Public License version 3 as
443+ * published by the Free Software Foundation.
444+ *
445+ * This program is distributed in the hope that it will be useful,
446+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
447+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
448+ * GNU General Public License for more details.
449+ *
450+ * You should have received a copy of the GNU General Public License
451+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
452+ *
453+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
454+ */
455+
456+#ifndef MIR_TEST_DOUBLES_MOCK_DEVICE_H_
457+#define MIR_TEST_DOUBLES_MOCK_DEVICE_H_
458+
459+#include "mir/input/device.h"
460+#include "mir/input/device_capability.h"
461+#include "mir/input/mir_pointer_config.h"
462+#include "mir/input/mir_keyboard_config.h"
463+#include "mir/input/mir_touchpad_config.h"
464+#include "mir/input/mir_touchscreen_config.h"
465+#include "mir/optional_value.h"
466+
467+#include <string>
468+
469+namespace mir
470+{
471+namespace test
472+{
473+namespace doubles
474+{
475+
476+struct MockDevice : input::Device
477+{
478+ MockDevice(MirInputDeviceId id, input::DeviceCapabilities caps, std::string const& name, std::string const& unique_id)
479+ {
480+ ON_CALL(*this, id()).WillByDefault(testing::Return(id));
481+ ON_CALL(*this, name()).WillByDefault(testing::Return(name));
482+ ON_CALL(*this, unique_id()).WillByDefault(testing::Return(unique_id));
483+ ON_CALL(*this, capabilities()).WillByDefault(testing::Return(caps));
484+ ON_CALL(*this, pointer_configuration()).WillByDefault(testing::Return(MirPointerConfig{}));
485+ ON_CALL(*this, keyboard_configuration()).WillByDefault(testing::Return(MirKeyboardConfig{}));
486+ ON_CALL(*this, touchpad_configuration()).WillByDefault(testing::Return(MirTouchpadConfig{}));
487+ ON_CALL(*this, touchscreen_configuration()).WillByDefault(testing::Return(MirTouchscreenConfig{}));
488+ }
489+
490+ MOCK_CONST_METHOD0(id, MirInputDeviceId());
491+ MOCK_CONST_METHOD0(capabilities, input::DeviceCapabilities());
492+ MOCK_CONST_METHOD0(name, std::string());
493+ MOCK_CONST_METHOD0(unique_id, std::string());
494+ MOCK_CONST_METHOD0(pointer_configuration, optional_value<MirPointerConfig>());
495+ MOCK_CONST_METHOD0(touchpad_configuration, optional_value<MirTouchpadConfig>());
496+ MOCK_CONST_METHOD0(keyboard_configuration, optional_value<MirKeyboardConfig>());
497+ MOCK_CONST_METHOD0(touchscreen_configuration, optional_value<MirTouchscreenConfig>());
498+ MOCK_METHOD1(apply_pointer_configuration, void(MirPointerConfig const&));
499+ MOCK_METHOD1(apply_touchpad_configuration, void(MirTouchpadConfig const&));
500+ MOCK_METHOD1(apply_keyboard_configuration, void(MirKeyboardConfig const&));
501+ MOCK_METHOD1(apply_touchscreen_configuration, void(MirTouchscreenConfig const&));
502+};
503+}
504+}
505+}
506+
507+#endif
508+
509
510=== modified file 'tests/integration-tests/input/test_single_seat_setup.cpp'
511--- tests/integration-tests/input/test_single_seat_setup.cpp 2017-03-14 02:26:28 +0000
512+++ tests/integration-tests/input/test_single_seat_setup.cpp 2017-03-23 16:31:26 +0000
513@@ -51,6 +51,7 @@
514 #include "mir/input/input_device_info.h"
515 #include "mir/geometry/rectangles.h"
516 #include "mir/test/input_config_matchers.h"
517+#include "mir/test/fd_utils.h"
518
519 #include <gmock/gmock.h>
520 #include <gtest/gtest.h>
521@@ -131,9 +132,9 @@
522 mt::fake_shared(mock_cursor_listener), mt::fake_shared(display_config),
523 mt::fake_shared(key_mapper), mt::fake_shared(clock),
524 mt::fake_shared(mock_seat_observer)};
525- mi::DefaultInputDeviceHub hub{mt::fake_shared(seat), mt::fake_shared(multiplexer),
526- mt::fake_shared(observer_loop), cookie_authority,
527- mt::fake_shared(key_mapper), mt::fake_shared(mock_status_listener)};
528+ mi::DefaultInputDeviceHub hub{mt::fake_shared(seat), mt::fake_shared(multiplexer),
529+ cookie_authority, mt::fake_shared(key_mapper),
530+ mt::fake_shared(mock_status_listener)};
531 NiceMock<mtd::MockInputDeviceObserver> mock_observer;
532 mi::ConfigChanger changer{
533 mt::fake_shared(mock_input_manager),
534@@ -151,6 +152,21 @@
535
536 std::chrono::nanoseconds arbitrary_timestamp;
537
538+ void SetUp()
539+ {
540+ // execute registration of ConfigChanger
541+ expect_and_execute_multiplexer();
542+ }
543+
544+ void expect_and_execute_multiplexer(int count = 1)
545+ {
546+ for (int i = 0; i != count; ++i)
547+ {
548+ mt::fd_becomes_readable(multiplexer.watch_fd(), 5s);
549+ multiplexer.dispatch(mir::dispatch::FdEvent::readable);
550+ }
551+ }
552+
553 void capture_input_sink(NiceMock<mtd::MockInputDevice>& dev, mi::InputSink*& sink, mi::EventBuilder*& builder)
554 {
555 ON_CALL(dev,start(_,_))
556@@ -173,14 +189,13 @@
557
558 capture_input_sink(device, sink, builder);
559
560- EXPECT_CALL(mock_observer,device_added(_))
561+ EXPECT_CALL(mock_observer, device_added(_))
562 .WillOnce(SaveArg<0>(&handle));
563
564 hub.add_observer(mt::fake_shared(mock_observer));
565+ expect_and_execute_multiplexer(1);
566 hub.add_device(mt::fake_shared(device));
567
568- observer_loop.trigger_server_actions();
569-
570 auto event = builder->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0,
571 KEY_A);
572
573@@ -198,8 +213,6 @@
574
575 hub.add_device(mt::fake_shared(device));
576
577- observer_loop.trigger_server_actions();
578-
579 auto touch_event_1 = builder->touch_event(
580 arbitrary_timestamp,
581 {{0, mir_touch_action_down, mir_touch_tooltype_finger, 21.0f, 34.0f, 50.0f, 15.0f, 5.0f, 4.0f}});
582@@ -245,7 +258,6 @@
583 capture_input_sink(device, sink, builder);
584
585 hub.add_device(mt::fake_shared(device));
586- observer_loop.trigger_server_actions();
587 sink->handle_input(
588 *builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, 10.0f, 10.0f));
589 sink->handle_input(
590@@ -265,7 +277,6 @@
591 mi::EventBuilder* builder;
592 capture_input_sink(device, sink, builder);
593 hub.add_device(mt::fake_shared(device));
594- observer_loop.trigger_server_actions();
595
596 sink->handle_input(
597 *builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, 10.0f, 20.0f));
598@@ -295,8 +306,8 @@
599 ON_CALL(mock_observer, device_added(_)).WillByDefault(SaveArg<0>(&dev));
600
601 hub.add_observer(mt::fake_shared(mock_observer));
602+ expect_and_execute_multiplexer();
603 hub.add_device(mt::fake_shared(touchpad));
604- observer_loop.trigger_server_actions();
605
606 EXPECT_CALL(touchpad, apply_settings(Matcher<mi::PointerSettings const&>(_)));
607
608@@ -311,8 +322,8 @@
609 ON_CALL(mock_observer, device_added(_)).WillByDefault(SaveArg<0>(&dev));
610
611 hub.add_observer(mt::fake_shared(mock_observer));
612+ expect_and_execute_multiplexer();
613 hub.add_device(mt::fake_shared(touchpad));
614- observer_loop.trigger_server_actions();
615
616 EXPECT_CALL(touchpad, apply_settings(Matcher<mi::TouchpadSettings const&>(_)));
617
618@@ -334,9 +345,9 @@
619 .WillOnce(SaveArg<0>(&key_handle));
620
621 hub.add_observer(mt::fake_shared(mock_observer));
622+ expect_and_execute_multiplexer();
623 hub.add_device(mt::fake_shared(another_device));
624
625- observer_loop.trigger_server_actions();
626
627 const MirInputEventModifiers shift_left = mir_input_event_modifier_shift_left | mir_input_event_modifier_shift;
628 auto shift_down =
629@@ -370,11 +381,10 @@
630 .WillOnce(SaveArg<0>(&key_handle));
631
632 hub.add_observer(mt::fake_shared(mock_observer));
633+ expect_and_execute_multiplexer();
634 hub.add_device(mt::fake_shared(device));
635 hub.add_device(mt::fake_shared(another_device));
636
637- observer_loop.trigger_server_actions();
638-
639 const MirInputEventModifiers r_alt_modifier = mir_input_event_modifier_alt_right | mir_input_event_modifier_alt;
640 auto key =
641 key_event_builder->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTALT);
642@@ -415,6 +425,7 @@
643 .WillOnce(SaveArg<0>(&key_handle_2));
644
645 hub.add_observer(mt::fake_shared(mock_observer));
646+ expect_and_execute_multiplexer();
647 hub.add_device(mt::fake_shared(device));
648 hub.add_device(mt::fake_shared(another_device));
649 hub.add_device(mt::fake_shared(third_device));
650@@ -423,8 +434,6 @@
651 const MirInputEventModifiers l_ctrl_modifier = mir_input_event_modifier_ctrl_left | mir_input_event_modifier_ctrl;
652 const MirInputEventModifiers combined_modifier = r_alt_modifier | l_ctrl_modifier;
653
654- observer_loop.trigger_server_actions();
655-
656 auto alt_down = key_event_builder_1->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTALT);
657 auto ctrl_down = key_event_builder_2->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_LEFTCTRL);
658 auto ctrl_up = key_event_builder_2->key_event(arbitrary_timestamp, mir_keyboard_action_up, 0, KEY_LEFTCTRL);
659@@ -462,8 +471,6 @@
660 hub.add_device(mt::fake_shared(device));
661 hub.add_device(mt::fake_shared(another_device));
662
663- observer_loop.trigger_server_actions();
664-
665 auto motion_1 =
666 mouse_event_builder_1->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 23, 20);
667 auto motion_2 =
668@@ -495,8 +502,6 @@
669 hub.add_device(mt::fake_shared(device));
670 hub.add_device(mt::fake_shared(another_device));
671
672- observer_loop.trigger_server_actions();
673-
674 auto motion_1 =
675 mouse_event_builder_1->pointer_event(arbitrary_timestamp, mir_pointer_action_button_down, mir_pointer_button_primary, 0, 0, 0, 0);
676 auto motion_2 =
677@@ -525,7 +530,6 @@
678
679 EXPECT_CALL(session, send_input_config(UnorderedElementsAre(DeviceMatches(device.get_device_info()))));
680 hub.add_device(mt::fake_shared(device));
681- observer_loop.trigger_server_actions();
682 }
683
684 TEST_F(SingleSeatInputDeviceHubSetup, input_device_changes_sent_to_session_multiple_devices)
685@@ -534,25 +538,21 @@
686 stub_session_container.insert_session(mt::fake_shared(session));
687
688 hub.add_device(mt::fake_shared(device));
689- observer_loop.trigger_server_actions();
690
691 EXPECT_CALL(session,
692 send_input_config(UnorderedElementsAre(DeviceMatches(device.get_device_info()),
693 DeviceMatches(another_device.get_device_info()))));
694 hub.add_device(mt::fake_shared(another_device));
695- observer_loop.trigger_server_actions();
696 }
697
698 TEST_F(SingleSeatInputDeviceHubSetup, input_device_changes_sent_to_sink_removal)
699 {
700 hub.add_device(mt::fake_shared(device));
701 hub.add_device(mt::fake_shared(another_device));
702- observer_loop.trigger_server_actions();
703
704 NiceMock<mtd::MockSceneSession> session;
705 stub_session_container.insert_session(mt::fake_shared(session));
706 EXPECT_CALL(session,
707 send_input_config(UnorderedElementsAre(DeviceMatches(another_device.get_device_info()))));
708 hub.remove_device(mt::fake_shared(device));
709- observer_loop.trigger_server_actions();
710 }
711
712=== modified file 'tests/unit-tests/input/CMakeLists.txt'
713--- tests/unit-tests/input/CMakeLists.txt 2017-03-13 08:12:52 +0000
714+++ tests/unit-tests/input/CMakeLists.txt 2017-03-23 16:31:26 +0000
715@@ -8,6 +8,7 @@
716 ${CMAKE_CURRENT_SOURCE_DIR}/test_input_event.cpp
717 ${CMAKE_CURRENT_SOURCE_DIR}/test_config_changer.cpp
718 ${CMAKE_CURRENT_SOURCE_DIR}/test_event_builders.cpp
719+ ${CMAKE_CURRENT_SOURCE_DIR}/test_external_input_device_hub.cpp
720 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_device.cpp
721 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_input_device_hub.cpp
722 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_input_manager.cpp
723
724=== modified file 'tests/unit-tests/input/test_config_changer.cpp'
725--- tests/unit-tests/input/test_config_changer.cpp 2017-02-28 08:53:57 +0000
726+++ tests/unit-tests/input/test_config_changer.cpp 2017-03-23 16:31:26 +0000
727@@ -29,6 +29,7 @@
728
729 #include "mir/test/doubles/mock_input_device_hub.h"
730 #include "mir/test/doubles/mock_input_manager.h"
731+#include "mir/test/doubles/mock_device.h"
732 #include "mir/test/doubles/mock_scene_session.h"
733 #include "mir/test/doubles/stub_session.h"
734 #include "mir/test/doubles/stub_session_container.h"
735@@ -47,34 +48,6 @@
736 namespace
737 {
738
739-struct MockDevice : mir::input::Device
740-{
741- MockDevice(MirInputDeviceId id, mi::DeviceCapabilities caps, std::string const& name, std::string const& unique_id)
742- {
743- ON_CALL(*this, id()).WillByDefault(Return(id));
744- ON_CALL(*this, name()).WillByDefault(Return(name));
745- ON_CALL(*this, unique_id()).WillByDefault(Return(unique_id));
746- ON_CALL(*this, capabilities()).WillByDefault(Return(caps));
747- ON_CALL(*this, pointer_configuration()).WillByDefault(Return(MirPointerConfig{}));
748- ON_CALL(*this, keyboard_configuration()).WillByDefault(Return(MirKeyboardConfig{}));
749- ON_CALL(*this, touchpad_configuration()).WillByDefault(Return(MirTouchpadConfig{}));
750- ON_CALL(*this, touchscreen_configuration()).WillByDefault(Return(MirTouchscreenConfig{}));
751- }
752-
753- MOCK_CONST_METHOD0(id, MirInputDeviceId());
754- MOCK_CONST_METHOD0(capabilities, mir::input::DeviceCapabilities());
755- MOCK_CONST_METHOD0(name, std::string());
756- MOCK_CONST_METHOD0(unique_id, std::string());
757- MOCK_CONST_METHOD0(pointer_configuration, mir::optional_value<MirPointerConfig>());
758- MOCK_CONST_METHOD0(touchpad_configuration, mir::optional_value<MirTouchpadConfig>());
759- MOCK_CONST_METHOD0(keyboard_configuration, mir::optional_value<MirKeyboardConfig>());
760- MOCK_CONST_METHOD0(touchscreen_configuration, mir::optional_value<MirTouchscreenConfig>());
761- MOCK_METHOD1(apply_pointer_configuration, void(MirPointerConfig const&));
762- MOCK_METHOD1(apply_touchpad_configuration, void(MirTouchpadConfig const&));
763- MOCK_METHOD1(apply_keyboard_configuration, void(MirKeyboardConfig const&));
764- MOCK_METHOD1(apply_touchscreen_configuration, void(MirTouchscreenConfig const&));
765-};
766-
767 struct FakeInputDeviceHub : mir::input::InputDeviceHub
768 {
769 std::shared_ptr<mi::InputDeviceObserver> observer;
770@@ -86,8 +59,8 @@
771 mi::DeviceCapability::keyboard | mi::DeviceCapability::alpha_numeric |
772 mi::DeviceCapability::touchscreen;
773
774- NiceMock<MockDevice> first_device{first_id, caps, first, first};
775- NiceMock<MockDevice> second_device{second_id, caps, second, second};
776+ NiceMock<mtd::MockDevice> first_device{first_id, caps, first, first};
777+ NiceMock<mtd::MockDevice> second_device{second_id, caps, second, second};
778 std::vector<std::shared_ptr<mir::input::Device>> active_devices;
779
780 void add_observer(std::shared_ptr<mi::InputDeviceObserver> const& obs) override
781
782=== modified file 'tests/unit-tests/input/test_default_input_device_hub.cpp'
783--- tests/unit-tests/input/test_default_input_device_hub.cpp 2017-03-13 08:12:52 +0000
784+++ tests/unit-tests/input/test_default_input_device_hub.cpp 2017-03-23 16:31:26 +0000
785@@ -30,6 +30,7 @@
786 #include "mir/test/doubles/triggered_main_loop.h"
787 #include "mir/test/event_matchers.h"
788 #include "mir/test/fake_shared.h"
789+#include "mir/test/fd_utils.h"
790
791 #include "mir/dispatch/action_queue.h"
792 #include "mir/geometry/rectangles.h"
793@@ -77,14 +78,14 @@
794
795 struct InputDeviceHubTest : ::testing::Test
796 {
797- mtd::TriggeredMainLoop observer_loop;
798 std::shared_ptr<mir::cookie::Authority> cookie_authority = mir::cookie::Authority::create();
799 mir::dispatch::MultiplexingDispatchable multiplexer;
800 NiceMock<mtd::MockInputSeat> mock_seat;
801 NiceMock<mtd::MockKeyMapper> mock_key_mapper;
802 NiceMock<mtd::MockServerStatusListener> mock_server_status_listener;
803 mi::DefaultInputDeviceHub hub{mt::fake_shared(mock_seat), mt::fake_shared(multiplexer),
804- mt::fake_shared(observer_loop), cookie_authority, mt::fake_shared(mock_key_mapper), mt::fake_shared(mock_server_status_listener)};
805+ cookie_authority, mt::fake_shared(mock_key_mapper),
806+ mt::fake_shared(mock_server_status_listener)};
807 NiceMock<mtd::MockInputDeviceObserver> mock_observer;
808 NiceMock<mtd::MockInputDevice> device{"device","dev-1", mi::DeviceCapability::unknown};
809 NiceMock<mtd::MockInputDevice> another_device{"another_device","dev-2", mi::DeviceCapability::keyboard};
810@@ -104,6 +105,12 @@
811 }
812 ));
813 }
814+
815+ void expect_and_execute_multiplexer()
816+ {
817+ mt::fd_becomes_readable(multiplexer.watch_fd(), 2s);
818+ multiplexer.dispatch(mir::dispatch::FdEvent::readable);
819+ }
820 };
821
822 TEST_F(InputDeviceHubTest, input_device_hub_starts_device)
823@@ -161,8 +168,7 @@
824 hub.add_device(mt::fake_shared(another_device));
825 hub.add_observer(mt::fake_shared(mock_observer));
826
827- observer_loop.trigger_server_actions();
828-
829+ expect_and_execute_multiplexer();
830 EXPECT_THAT(handle_1,Ne(handle_2));
831 EXPECT_THAT(handle_1->unique_id(),Ne(handle_2->unique_id()));
832 }
833@@ -208,10 +214,13 @@
834 EXPECT_CALL(mock_observer, changes_complete());
835
836 hub.add_observer(mt::fake_shared(mock_observer));
837+ expect_and_execute_multiplexer();
838+
839 hub.add_device(mt::fake_shared(device));
840+ expect_and_execute_multiplexer();
841+
842 hub.remove_device(mt::fake_shared(device));
843-
844- observer_loop.trigger_server_actions();
845+ expect_and_execute_multiplexer();
846 }
847
848 TEST_F(InputDeviceHubTest, emit_ready_to_receive_input_after_first_device_added)
849@@ -219,8 +228,6 @@
850 EXPECT_CALL(mock_server_status_listener, ready_for_user_input()).Times(1);
851 hub.add_device(mt::fake_shared(device));
852 hub.add_device(mt::fake_shared(another_device));
853-
854- observer_loop.trigger_server_actions();
855 }
856
857 TEST_F(InputDeviceHubTest, emit_stop_receiving_input_after_last_device_added)
858@@ -231,7 +238,6 @@
859
860 hub.remove_device(mt::fake_shared(device));
861 hub.remove_device(mt::fake_shared(another_device));
862- observer_loop.trigger_server_actions();
863 }
864
865 TEST_F(InputDeviceHubTest, when_pointer_configuration_is_applied_successfully_observer_is_triggerd)
866@@ -243,13 +249,13 @@
867
868 hub.add_device(mt::fake_shared(mouse));
869 hub.add_observer(mt::fake_shared(mock_observer));
870- observer_loop.trigger_server_actions();
871+ expect_and_execute_multiplexer();
872
873 EXPECT_CALL(mock_observer, device_changed(WithName("mouse")));
874 EXPECT_CALL(mock_observer, changes_complete());
875
876 dev_ptr->apply_pointer_configuration(pointer_conf);
877- observer_loop.trigger_server_actions();
878+ expect_and_execute_multiplexer();
879 }
880
881 TEST_F(InputDeviceHubTest, when_tpd_configuration_is_applied_successfully_observer_is_triggerd)
882@@ -261,13 +267,13 @@
883
884 hub.add_device(mt::fake_shared(touchpad));
885 hub.add_observer(mt::fake_shared(mock_observer));
886- observer_loop.trigger_server_actions();
887+ expect_and_execute_multiplexer();
888
889 EXPECT_CALL(mock_observer, device_changed(WithName("tpd")));
890 EXPECT_CALL(mock_observer, changes_complete());
891
892 dev_ptr->apply_touchpad_configuration(tpd_conf);
893- observer_loop.trigger_server_actions();
894+ expect_and_execute_multiplexer();
895 }
896
897 TEST_F(InputDeviceHubTest, when_configuration_attempt_fails_observer_is_not_triggerd)
898@@ -279,12 +285,12 @@
899
900 hub.add_device(mt::fake_shared(mouse));
901 hub.add_observer(mt::fake_shared(mock_observer));
902- observer_loop.trigger_server_actions();
903+ expect_and_execute_multiplexer();
904
905 EXPECT_CALL(mock_observer, device_changed(WithName("mouse"))).Times(0);
906 EXPECT_CALL(mock_observer, changes_complete()).Times(0);
907
908 try {dev_ptr->apply_touchpad_configuration(tpd_conf); } catch (...) {}
909- observer_loop.trigger_server_actions();
910+ expect_and_execute_multiplexer();
911 ::testing::Mock::VerifyAndClearExpectations(&mock_observer);
912 }
913
914=== added file 'tests/unit-tests/input/test_external_input_device_hub.cpp'
915--- tests/unit-tests/input/test_external_input_device_hub.cpp 1970-01-01 00:00:00 +0000
916+++ tests/unit-tests/input/test_external_input_device_hub.cpp 2017-03-23 16:31:26 +0000
917@@ -0,0 +1,119 @@
918+/*
919+ * Copyright © 2017 Canonical Ltd.
920+ *
921+ * This program is free software: you can redistribute it and/or modify
922+ * it under the terms of the GNU General Public License version 3 as
923+ * published by the Free Software Foundation.
924+ *
925+ * This program is distributed in the hope that it will be useful,
926+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
927+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
928+ * GNU General Public License for more details.
929+ *
930+ * You should have received a copy of the GNU General Public License
931+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
932+ *
933+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
934+ */
935+
936+#include "src/server/input/default_input_device_hub.h"
937+#include "mir/test/doubles/mock_input_device_hub.h"
938+#include "mir/test/doubles/mock_device.h"
939+#include "mir/test/doubles/mock_input_device_observer.h"
940+#include "mir/test/doubles/triggered_main_loop.h"
941+#include "mir/test/fake_shared.h"
942+
943+namespace mt = mir::test;
944+namespace mtd = mt::doubles;
945+namespace mi = mir::input;
946+using namespace testing;
947+
948+namespace
949+{
950+
951+struct WrappedHub : mtd::MockInputDeviceHub
952+{
953+ std::shared_ptr<mi::InputDeviceObserver> external_hub;
954+ void add_observer(std::shared_ptr<mi::InputDeviceObserver> const& observer)
955+ {
956+ external_hub = observer;
957+ }
958+};
959+
960+struct ExternalInputDeviceHub : ::testing::Test
961+{
962+ mtd::TriggeredMainLoop server_actions;
963+ std::shared_ptr<WrappedHub> wrapped_hub{std::make_shared<WrappedHub>()};
964+ mi::ExternalInputDeviceHub hub{wrapped_hub, mt::fake_shared(server_actions)};
965+ NiceMock<mtd::MockInputDeviceObserver> mock_observer;
966+
967+ NiceMock<mtd::MockDevice> device{1, mi::DeviceCapability::unknown, "name", "name-id-1"};
968+ NiceMock<mtd::MockDevice> another_device{2, mi::DeviceCapability::keyboard, "keyboard", "keyboard-id-2"};
969+
970+ void submit_device_to_hub(std::shared_ptr<mi::Device> const& dev)
971+ {
972+ wrapped_hub->external_hub->device_added(dev);
973+ wrapped_hub->external_hub->changes_complete();
974+ }
975+
976+ void remove_device_to_hub(std::shared_ptr<mi::Device> const& dev)
977+ {
978+ wrapped_hub->external_hub->device_removed(dev);
979+ wrapped_hub->external_hub->changes_complete();
980+ }
981+};
982+}
983+
984+TEST_F(ExternalInputDeviceHub, is_observer_to_wrapped_hub)
985+{
986+ EXPECT_THAT(wrapped_hub->external_hub, Ne(nullptr));
987+}
988+
989+TEST_F(ExternalInputDeviceHub, new_observer_is_called_through_server_action)
990+{
991+ EXPECT_CALL(mock_observer, changes_complete()).Times(1);
992+ hub.add_observer(mt::fake_shared(mock_observer));
993+ server_actions.trigger_server_actions();
994+}
995+
996+TEST_F(ExternalInputDeviceHub, informs_observer_about_existing_devices)
997+{
998+ submit_device_to_hub(mt::fake_shared(device));
999+ submit_device_to_hub(mt::fake_shared(another_device));
1000+
1001+ EXPECT_CALL(mock_observer, device_added(_)).Times(2);
1002+ EXPECT_CALL(mock_observer, changes_complete()).Times(1);
1003+ hub.add_observer(mt::fake_shared(mock_observer));
1004+ server_actions.trigger_server_actions();
1005+}
1006+
1007+TEST_F(ExternalInputDeviceHub, informs_observer_about_added_devices)
1008+{
1009+ InSequence seq;
1010+
1011+ EXPECT_CALL(mock_observer, changes_complete());
1012+ EXPECT_CALL(mock_observer, device_added(_));
1013+ EXPECT_CALL(mock_observer, changes_complete());
1014+ hub.add_observer(mt::fake_shared(mock_observer));
1015+ server_actions.trigger_server_actions();
1016+
1017+ submit_device_to_hub(mt::fake_shared(device));
1018+
1019+ server_actions.trigger_server_actions();
1020+}
1021+
1022+TEST_F(ExternalInputDeviceHub, triggers_observer_on_removed_devices)
1023+{
1024+ submit_device_to_hub(mt::fake_shared(device));
1025+
1026+ EXPECT_CALL(mock_observer, device_added(_)).Times(1);
1027+ EXPECT_CALL(mock_observer, changes_complete()).Times(1);
1028+ hub.add_observer(mt::fake_shared(mock_observer));
1029+ server_actions.trigger_server_actions();
1030+
1031+ EXPECT_CALL(mock_observer, device_removed(_)).Times(1);
1032+ EXPECT_CALL(mock_observer, changes_complete()).Times(1);
1033+
1034+ remove_device_to_hub(mt::fake_shared(device));
1035+ server_actions.trigger_server_actions();
1036+}

Subscribers

People subscribed via source and target branches