2
* Copyright © 2013 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* published by the Free Software Foundation.
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.
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/>.
16
* Authored by: Kevin DuBois <kevin.dubois@canonical.com>
19
#include "src/server/scene/mediating_display_changer.h"
20
#include "src/server/scene/session_container.h"
21
#include "mir/graphics/display_configuration_policy.h"
22
#include "src/server/scene/broadcasting_session_event_sink.h"
23
#include "mir/server_action_queue.h"
25
#include "mir_test_doubles/mock_display.h"
26
#include "mir_test_doubles/mock_compositor.h"
27
#include "mir_test_doubles/null_display_configuration.h"
28
#include "mir_test_doubles/stub_display_configuration.h"
29
#include "mir_test_doubles/mock_scene_session.h"
30
#include "mir_test_doubles/stub_scene_session.h"
31
#include "mir_test/fake_shared.h"
32
#include "mir_test/display_config_matchers.h"
36
#include <gmock/gmock.h>
37
#include <gtest/gtest.h>
39
namespace mt = mir::test;
40
namespace mtd = mir::test::doubles;
41
namespace mf = mir::frontend;
42
namespace ms = mir::scene;
43
namespace mg = mir::graphics;
48
class MockDisplayConfigurationPolicy : public mg::DisplayConfigurationPolicy
51
~MockDisplayConfigurationPolicy() noexcept {}
52
MOCK_METHOD1(apply_to, void(mg::DisplayConfiguration&));
55
class StubSessionContainer : public ms::SessionContainer
58
void insert_session(std::shared_ptr<ms::Session> const& session)
60
sessions.push_back(session);
63
void remove_session(std::shared_ptr<ms::Session> const&)
67
void for_each(std::function<void(std::shared_ptr<ms::Session> const&)> f) const
69
for (auto const& session : sessions)
73
std::shared_ptr<ms::Session> successor_of(std::shared_ptr<ms::Session> const&) const
79
std::vector<std::shared_ptr<ms::Session>> sessions;
82
struct MockDisplay : public mtd::MockDisplay
84
std::unique_ptr<mg::DisplayConfiguration> configuration() const override
86
conf_ptr = new mtd::StubDisplayConfig{};
87
return std::unique_ptr<mg::DisplayConfiguration>(conf_ptr);
90
mutable mg::DisplayConfiguration* conf_ptr;
93
struct StubServerActionQueue : mir::ServerActionQueue
97
std::lock_guard<std::mutex> lock{mutex};
98
for (auto const& action : actions)
103
void enqueue(void const* /*owner*/, mir::ServerAction const& action) override
105
std::lock_guard<std::mutex> lock{mutex};
106
actions.push_back(action);
109
void pause_processing_for(void const* /*owner*/) override {}
110
void resume_processing_for(void const* /*owner*/) override {}
112
std::vector<mir::ServerAction> actions;
116
struct MockServerActionQueue : mir::ServerActionQueue
118
MOCK_METHOD2(enqueue, void(void const*, mir::ServerAction const&));
119
MOCK_METHOD1(pause_processing_for, void(void const*));
120
MOCK_METHOD1(resume_processing_for, void(void const*));
123
struct MediatingDisplayChangerTest : public ::testing::Test
125
MediatingDisplayChangerTest()
127
using namespace testing;
129
changer = std::make_shared<ms::MediatingDisplayChanger>(
130
mt::fake_shared(mock_display),
131
mt::fake_shared(mock_compositor),
132
mt::fake_shared(mock_conf_policy),
133
mt::fake_shared(stub_session_container),
134
mt::fake_shared(session_event_sink),
135
mt::fake_shared(server_action_queue));
138
testing::NiceMock<MockDisplay> mock_display;
139
testing::NiceMock<mtd::MockCompositor> mock_compositor;
140
testing::NiceMock<MockDisplayConfigurationPolicy> mock_conf_policy;
141
StubSessionContainer stub_session_container;
142
ms::BroadcastingSessionEventSink session_event_sink;
143
mtd::StubDisplayConfig base_config;
144
StubServerActionQueue server_action_queue;
145
std::shared_ptr<ms::MediatingDisplayChanger> changer;
150
TEST_F(MediatingDisplayChangerTest, returns_active_configuration_from_display)
152
using namespace testing;
154
auto returned_conf = changer->active_configuration();
155
EXPECT_EQ(mock_display.conf_ptr, returned_conf.get());
158
TEST_F(MediatingDisplayChangerTest, pauses_system_when_applying_new_configuration_for_focused_session)
160
using namespace testing;
161
mtd::NullDisplayConfiguration conf;
162
auto session = std::make_shared<mtd::StubSceneSession>();
165
EXPECT_CALL(mock_compositor, stop());
167
EXPECT_CALL(mock_display, configure(Ref(conf)));
169
EXPECT_CALL(mock_compositor, start());
171
session_event_sink.handle_focus_change(session);
172
changer->configure(session,
173
mt::fake_shared(conf));
175
server_action_queue.flush();
178
TEST_F(MediatingDisplayChangerTest, doesnt_apply_config_for_unfocused_session)
180
using namespace testing;
181
mtd::NullDisplayConfiguration conf;
183
EXPECT_CALL(mock_compositor, stop()).Times(0);
184
EXPECT_CALL(mock_display, configure(Ref(conf))).Times(0);
185
EXPECT_CALL(mock_compositor, start()).Times(0);
187
changer->configure(std::make_shared<mtd::StubSceneSession>(),
188
mt::fake_shared(conf));
190
server_action_queue.flush();
193
TEST_F(MediatingDisplayChangerTest, handles_hardware_change_properly_when_pausing_system)
195
using namespace testing;
196
mtd::NullDisplayConfiguration conf;
199
EXPECT_CALL(mock_conf_policy, apply_to(Ref(conf)));
201
EXPECT_CALL(mock_compositor, stop());
202
EXPECT_CALL(mock_display, configure(Ref(conf)));
203
EXPECT_CALL(mock_compositor, start());
205
changer->configure_for_hardware_change(mt::fake_shared(conf),
206
mir::DisplayChanger::PauseResumeSystem);
208
server_action_queue.flush();
211
TEST_F(MediatingDisplayChangerTest, handles_hardware_change_properly_when_retaining_system_state)
213
using namespace testing;
214
mtd::NullDisplayConfiguration conf;
216
EXPECT_CALL(mock_compositor, stop()).Times(0);
217
EXPECT_CALL(mock_compositor, start()).Times(0);
220
EXPECT_CALL(mock_conf_policy, apply_to(Ref(conf)));
221
EXPECT_CALL(mock_display, configure(Ref(conf)));
223
changer->configure_for_hardware_change(mt::fake_shared(conf),
224
mir::DisplayChanger::RetainSystemState);
226
server_action_queue.flush();
229
TEST_F(MediatingDisplayChangerTest, hardware_change_doesnt_apply_base_config_if_per_session_config_is_active)
231
using namespace testing;
233
auto conf = std::make_shared<mtd::NullDisplayConfiguration>();
234
auto session1 = std::make_shared<mtd::StubSceneSession>();
236
stub_session_container.insert_session(session1);
237
changer->configure(session1, conf);
239
session_event_sink.handle_focus_change(session1);
241
server_action_queue.flush();
242
Mock::VerifyAndClearExpectations(&mock_compositor);
243
Mock::VerifyAndClearExpectations(&mock_display);
246
EXPECT_CALL(mock_compositor, stop()).Times(0);
247
EXPECT_CALL(mock_display, configure(_)).Times(0);
248
EXPECT_CALL(mock_compositor, start()).Times(0);
250
changer->configure_for_hardware_change(conf,
251
mir::DisplayChanger::PauseResumeSystem);
253
server_action_queue.flush();
256
TEST_F(MediatingDisplayChangerTest, notifies_all_sessions_on_hardware_config_change)
258
using namespace testing;
259
mtd::NullDisplayConfiguration conf;
260
mtd::MockSceneSession mock_session1;
261
mtd::MockSceneSession mock_session2;
263
stub_session_container.insert_session(mt::fake_shared(mock_session1));
264
stub_session_container.insert_session(mt::fake_shared(mock_session2));
266
EXPECT_CALL(mock_session1, send_display_config(_));
267
EXPECT_CALL(mock_session2, send_display_config(_));
269
changer->configure_for_hardware_change(mt::fake_shared(conf),
270
mir::DisplayChanger::PauseResumeSystem);
272
server_action_queue.flush();
275
TEST_F(MediatingDisplayChangerTest, focusing_a_session_with_attached_config_applies_config)
277
using namespace testing;
278
auto conf = std::make_shared<mtd::NullDisplayConfiguration>();
279
auto session1 = std::make_shared<mtd::StubSceneSession>();
281
stub_session_container.insert_session(session1);
282
changer->configure(session1, conf);
285
EXPECT_CALL(mock_compositor, stop());
286
EXPECT_CALL(mock_display, configure(Ref(*conf)));
287
EXPECT_CALL(mock_compositor, start());
289
session_event_sink.handle_focus_change(session1);
291
server_action_queue.flush();
294
TEST_F(MediatingDisplayChangerTest, focusing_a_session_without_attached_config_applies_base_config)
296
using namespace testing;
297
auto conf = std::make_shared<mtd::NullDisplayConfiguration>();
298
auto session1 = std::make_shared<mtd::StubSceneSession>();
299
auto session2 = std::make_shared<mtd::StubSceneSession>();
301
stub_session_container.insert_session(session1);
302
changer->configure(session1, conf);
304
session_event_sink.handle_focus_change(session1);
306
server_action_queue.flush();
307
Mock::VerifyAndClearExpectations(&mock_compositor);
308
Mock::VerifyAndClearExpectations(&mock_display);
311
EXPECT_CALL(mock_compositor, stop());
312
EXPECT_CALL(mock_display, configure(mt::DisplayConfigMatches(std::cref(base_config))));
313
EXPECT_CALL(mock_compositor, start());
315
session_event_sink.handle_focus_change(session2);
317
server_action_queue.flush();
320
TEST_F(MediatingDisplayChangerTest, losing_focus_applies_base_config)
322
using namespace testing;
323
auto conf = std::make_shared<mtd::NullDisplayConfiguration>();
324
auto session1 = std::make_shared<mtd::StubSceneSession>();
326
stub_session_container.insert_session(session1);
327
changer->configure(session1, conf);
329
session_event_sink.handle_focus_change(session1);
331
server_action_queue.flush();
332
Mock::VerifyAndClearExpectations(&mock_compositor);
333
Mock::VerifyAndClearExpectations(&mock_display);
336
EXPECT_CALL(mock_compositor, stop());
337
EXPECT_CALL(mock_display, configure(mt::DisplayConfigMatches(std::cref(base_config))));
338
EXPECT_CALL(mock_compositor, start());
340
session_event_sink.handle_no_focus();
342
server_action_queue.flush();
345
TEST_F(MediatingDisplayChangerTest, base_config_is_not_applied_if_already_active)
347
using namespace testing;
348
auto conf = std::make_shared<mtd::NullDisplayConfiguration>();
349
auto session1 = std::make_shared<mtd::StubSceneSession>();
350
auto session2 = std::make_shared<mtd::StubSceneSession>();
352
EXPECT_CALL(mock_compositor, stop()).Times(0);
353
EXPECT_CALL(mock_display, configure(_)).Times(0);
354
EXPECT_CALL(mock_compositor, start()).Times(0);
356
stub_session_container.insert_session(session1);
357
stub_session_container.insert_session(session2);
359
session_event_sink.handle_focus_change(session1);
360
session_event_sink.handle_focus_change(session2);
361
session_event_sink.handle_no_focus();
363
server_action_queue.flush();
366
TEST_F(MediatingDisplayChangerTest, hardware_change_invalidates_session_configs)
368
using namespace testing;
369
auto conf = std::make_shared<mtd::NullDisplayConfiguration>();
370
auto session1 = std::make_shared<mtd::StubSceneSession>();
372
stub_session_container.insert_session(session1);
373
changer->configure(session1, conf);
375
changer->configure_for_hardware_change(conf,
376
mir::DisplayChanger::PauseResumeSystem);
378
server_action_queue.flush();
379
Mock::VerifyAndClearExpectations(&mock_compositor);
380
Mock::VerifyAndClearExpectations(&mock_display);
383
* Session1 had a config, but it should have been invalidated by the hardware
384
* change, so expect no reconfiguration.
386
EXPECT_CALL(mock_compositor, stop()).Times(0);
387
EXPECT_CALL(mock_display, configure(_)).Times(0);
388
EXPECT_CALL(mock_compositor, start()).Times(0);
390
session_event_sink.handle_focus_change(session1);
392
server_action_queue.flush();
395
TEST_F(MediatingDisplayChangerTest, session_stopping_invalidates_session_config)
397
using namespace testing;
398
auto conf = std::make_shared<mtd::NullDisplayConfiguration>();
399
auto session1 = std::make_shared<mtd::StubSceneSession>();
401
stub_session_container.insert_session(session1);
402
changer->configure(session1, conf);
404
session_event_sink.handle_session_stopping(session1);
406
server_action_queue.flush();
407
Mock::VerifyAndClearExpectations(&mock_compositor);
408
Mock::VerifyAndClearExpectations(&mock_display);
411
* Session1 had a config, but it should have been invalidated by the
412
* session stopping event, so expect no reconfiguration.
414
EXPECT_CALL(mock_compositor, stop()).Times(0);
415
EXPECT_CALL(mock_display, configure(_)).Times(0);
416
EXPECT_CALL(mock_compositor, start()).Times(0);
418
session_event_sink.handle_focus_change(session1);
420
server_action_queue.flush();
423
TEST_F(MediatingDisplayChangerTest, uses_server_action_queue_for_configuration_actions)
425
using namespace testing;
427
auto const conf = std::make_shared<mtd::NullDisplayConfiguration>();
428
auto const session1 = std::make_shared<mtd::StubSceneSession>();
429
auto const session2 = std::make_shared<mtd::StubSceneSession>();
430
MockServerActionQueue mock_server_action_queue;
432
stub_session_container.insert_session(session1);
433
stub_session_container.insert_session(session2);
435
ms::MediatingDisplayChanger display_changer(
436
mt::fake_shared(mock_display),
437
mt::fake_shared(mock_compositor),
438
mt::fake_shared(mock_conf_policy),
439
mt::fake_shared(stub_session_container),
440
mt::fake_shared(session_event_sink),
441
mt::fake_shared(mock_server_action_queue));
443
void const* owner{nullptr};
445
EXPECT_CALL(mock_server_action_queue, enqueue(_, _))
446
.WillOnce(SaveArg<0>(&owner));
447
display_changer.configure(session1, conf);
448
Mock::VerifyAndClearExpectations(&mock_server_action_queue);
450
EXPECT_CALL(mock_server_action_queue, enqueue(owner, _));
451
display_changer.configure_for_hardware_change(
453
mir::DisplayChanger::PauseResumeSystem);
454
Mock::VerifyAndClearExpectations(&mock_server_action_queue);
456
EXPECT_CALL(mock_server_action_queue, enqueue(owner, _));
457
session_event_sink.handle_focus_change(session1);
458
Mock::VerifyAndClearExpectations(&mock_server_action_queue);
460
EXPECT_CALL(mock_server_action_queue, enqueue(owner, _));
461
session_event_sink.handle_focus_change(session2);
462
Mock::VerifyAndClearExpectations(&mock_server_action_queue);
464
EXPECT_CALL(mock_server_action_queue, enqueue(owner, _));
465
session_event_sink.handle_no_focus();
466
Mock::VerifyAndClearExpectations(&mock_server_action_queue);
468
EXPECT_CALL(mock_server_action_queue, pause_processing_for(owner));
469
display_changer.pause_display_config_processing();
470
Mock::VerifyAndClearExpectations(&mock_server_action_queue);
472
EXPECT_CALL(mock_server_action_queue, resume_processing_for(owner));
473
display_changer.resume_display_config_processing();
474
Mock::VerifyAndClearExpectations(&mock_server_action_queue);