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: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
19
#include "mir_test_framework/udev_environment.h"
20
#include "mir/udev/wrapper.h"
22
#include <gtest/gtest.h>
26
// At some point gnu libstdc++ will have a <regex> header that contains
27
// actual functions. Until then, Boost to the rescue.
28
#include <boost/regex.hpp>
34
namespace mtf=mir::mir_test_framework;
36
class UdevWrapperTest : public ::testing::Test
39
mtf::UdevEnvironment udev_environment;
42
TEST_F(UdevWrapperTest, IteratesOverCorrectNumberOfDevices)
44
udev_environment.add_device("drm", "fakedev1", NULL, {}, {});
45
udev_environment.add_device("drm", "fakedev2", NULL, {}, {});
46
udev_environment.add_device("drm", "fakedev3", NULL, {}, {});
47
udev_environment.add_device("drm", "fakedev4", NULL, {}, {});
48
udev_environment.add_device("drm", "fakedev5", NULL, {}, {});
50
auto ctx = std::make_shared<mir::udev::Context>();
51
mir::udev::Enumerator enumerator(ctx);
53
enumerator.scan_devices();
56
for (auto& device : enumerator)
58
// Silence unused variable warning
59
static_cast<void>(device);
63
ASSERT_EQ(device_count, 5);
66
TEST_F(UdevWrapperTest, EnumeratorMatchSubsystemIncludesCorrectDevices)
68
udev_environment.add_device("drm", "fakedrm1", NULL, {}, {});
69
udev_environment.add_device("scsi", "fakescsi1", NULL, {}, {});
70
udev_environment.add_device("drm", "fakedrm2", NULL, {}, {});
71
udev_environment.add_device("usb", "fakeusb1", NULL, {}, {});
72
udev_environment.add_device("usb", "fakeusb2", NULL, {}, {});
74
auto ctx = std::make_shared<mir::udev::Context>();
75
mir::udev::Enumerator devices(ctx);
77
devices.match_subsystem("drm");
78
devices.scan_devices();
79
for (auto& device : devices)
81
ASSERT_STREQ("drm", device.subsystem());
85
TEST_F(UdevWrapperTest, UdevDeviceHasCorrectDevType)
87
auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {"DEVTYPE", "drm_minor"});
89
mir::udev::Context ctx;
90
auto dev = ctx.device_from_syspath(sysfs_path);
91
ASSERT_STREQ("drm_minor", dev->devtype());
94
TEST_F(UdevWrapperTest, UdevDeviceHasCorrectDevPath)
96
auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {});
98
mir::udev::Context ctx;
99
auto dev = ctx.device_from_syspath(sysfs_path);
100
ASSERT_STREQ("/devices/card0", dev->devpath());
103
TEST_F(UdevWrapperTest, UdevDeviceHasCorrectDevNode)
105
auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {"DEVNAME", "/dev/dri/card0"});
107
mir::udev::Context ctx;
108
auto dev = ctx.device_from_syspath(sysfs_path);
110
ASSERT_STREQ("/dev/dri/card0", dev->devnode());
113
TEST_F(UdevWrapperTest, UdevDeviceComparisonIsReflexive)
115
auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {});
117
mir::udev::Context ctx;
118
auto dev = ctx.device_from_syspath(sysfs_path);
120
EXPECT_TRUE(*dev == *dev);
123
TEST_F(UdevWrapperTest, UdevDeviceComparisonIsSymmetric)
125
auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {});
127
mir::udev::Context ctx;
128
std::shared_ptr<mir::udev::Device> same_one = ctx.device_from_syspath(sysfs_path);
129
std::shared_ptr<mir::udev::Device> same_two = ctx.device_from_syspath(sysfs_path);
131
EXPECT_TRUE(*same_one == *same_two);
132
EXPECT_TRUE(*same_two == *same_one);
135
TEST_F(UdevWrapperTest, UdevDeviceDifferentDevicesCompareFalse)
137
auto path_one = udev_environment.add_device("drm", "card0", NULL, {}, {});
138
auto path_two = udev_environment.add_device("drm", "card1", NULL, {}, {});
140
mir::udev::Context ctx;
141
auto dev_one = ctx.device_from_syspath(path_one);
142
auto dev_two = ctx.device_from_syspath(path_two);
144
EXPECT_FALSE(*dev_one == *dev_two);
145
EXPECT_FALSE(*dev_two == *dev_one);
148
TEST_F(UdevWrapperTest, UdevDeviceDifferentDevicesAreNotEqual)
150
auto path_one = udev_environment.add_device("drm", "card0", NULL, {}, {});
151
auto path_two = udev_environment.add_device("drm", "card1", NULL, {}, {});
153
mir::udev::Context ctx;
154
auto dev_one = ctx.device_from_syspath(path_one);
155
auto dev_two = ctx.device_from_syspath(path_two);
157
EXPECT_TRUE(*dev_one != *dev_two);
158
EXPECT_TRUE(*dev_two != *dev_one);
161
TEST_F(UdevWrapperTest, UdevDeviceSameDeviceIsNotNotEqual)
163
auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {});
165
mir::udev::Context ctx;
166
auto same_one = ctx.device_from_syspath(sysfs_path);
167
auto same_two = ctx.device_from_syspath(sysfs_path);
169
EXPECT_FALSE(*same_one != *same_two);
170
EXPECT_FALSE(*same_two != *same_one);
173
TEST_F(UdevWrapperTest, EnumeratorMatchParentMatchesOnlyChildren)
175
auto card0_syspath = udev_environment.add_device("drm", "card0", NULL, {}, {});
176
udev_environment.add_device("usb", "fakeusb", NULL, {}, {});
178
udev_environment.add_device("drm", "card0-HDMI1", "/sys/devices/card0", {}, {});
179
udev_environment.add_device("drm", "card0-VGA1", "/sys/devices/card0", {}, {});
180
udev_environment.add_device("drm", "card0-LVDS1", "/sys/devices/card0", {}, {});
182
auto ctx = std::make_shared<mir::udev::Context>();
184
mir::udev::Enumerator devices(ctx);
185
auto drm_device = ctx->device_from_syspath(card0_syspath);
187
devices.match_parent(*drm_device);
188
devices.scan_devices();
191
for (auto& device : devices)
193
EXPECT_STREQ("drm", device.subsystem());
196
EXPECT_EQ(4, child_count);
199
TEST_F(UdevWrapperTest, EnumeratorThrowsLogicErrorIfIteratedBeforeScanned)
201
auto ctx = std::make_shared<mir::udev::Context>();
203
mir::udev::Enumerator devices(ctx);
205
EXPECT_THROW({ devices.begin(); },
209
TEST_F(UdevWrapperTest, EnumeratorLogicErrorHasSensibleMessage)
211
auto ctx = std::make_shared<mir::udev::Context>();
213
mir::udev::Enumerator devices(ctx);
214
std::string error_msg;
220
catch (std::logic_error& e)
222
error_msg = e.what();
224
EXPECT_STREQ("Attempted to iterate over udev devices without first scanning", error_msg.c_str());
227
TEST_F(UdevWrapperTest, EnumeratorEnumeratesEmptyList)
229
auto ctx = std::make_shared<mir::udev::Context>();
231
mir::udev::Enumerator devices(ctx);
233
devices.scan_devices();
235
for (auto& device : devices)
236
ADD_FAILURE() << "Unexpected udev device: " << device.devpath();
239
TEST_F(UdevWrapperTest, EnumeratorAddMatchSysnameIncludesCorrectDevices)
241
auto drm_sysfspath = udev_environment.add_device("drm", "card0", NULL, {}, {});
242
udev_environment.add_device("drm", "control64D", NULL, {}, {});
243
udev_environment.add_device("drm", "card0-LVDS1", drm_sysfspath.c_str(), {}, {});
244
udev_environment.add_device("drm", "card1", NULL, {}, {});
246
auto ctx = std::make_shared<mir::udev::Context>();
248
mir::udev::Enumerator devices(ctx);
250
devices.match_sysname("card[0-9]");
251
devices.scan_devices();
252
for (auto& device : devices)
254
EXPECT_TRUE(boost::regex_match(device.devpath(), boost::regex(".*card[0-9].*")))
255
<< "Unexpected device with devpath:" << device.devpath();
259
TEST_F(UdevWrapperTest, UdevMonitorDoesNotTriggerBeforeEnabling)
261
mir::udev::Monitor monitor{mir::udev::Context()};
262
bool event_handler_called = false;
264
udev_environment.add_device("drm", "control64D", NULL, {}, {});
266
monitor.process_events([&event_handler_called](mir::udev::Monitor::EventType,
267
mir::udev::Device const&) {event_handler_called = true;});
269
EXPECT_FALSE(event_handler_called);
272
TEST_F(UdevWrapperTest, UdevMonitorTriggersAfterEnabling)
274
mir::udev::Monitor monitor{mir::udev::Context()};
275
bool event_handler_called = false;
279
udev_environment.add_device("drm", "control64D", NULL, {}, {});
281
monitor.process_events([&event_handler_called](mir::udev::Monitor::EventType,
282
mir::udev::Device const&) {event_handler_called = true;});
284
EXPECT_TRUE(event_handler_called);
287
TEST_F(UdevWrapperTest, UdevMonitorSendsRemoveEvent)
289
mir::udev::Monitor monitor{mir::udev::Context()};
290
bool remove_event_received = false;
294
auto test_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {});
295
udev_environment.remove_device(test_sysfspath);
297
monitor.process_events([&remove_event_received]
298
(mir::udev::Monitor::EventType action, mir::udev::Device const&)
300
if (action == mir::udev::Monitor::EventType::REMOVED)
301
remove_event_received = true;
304
EXPECT_TRUE(remove_event_received);
307
TEST_F(UdevWrapperTest, UdevMonitorSendsChangedEvent)
309
mir::udev::Monitor monitor{mir::udev::Context()};
310
bool changed_event_received = false;
314
auto test_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {});
315
udev_environment.emit_device_changed(test_sysfspath);
317
monitor.process_events([&changed_event_received]
318
(mir::udev::Monitor::EventType action, mir::udev::Device const&)
320
if (action == mir::udev::Monitor::EventType::CHANGED)
321
changed_event_received = true;
324
EXPECT_TRUE(changed_event_received);
327
TEST_F(UdevWrapperTest, UdevMonitorEventHasCorrectDeviceDetails)
329
mir::udev::Context ctx;
331
mir::udev::Monitor monitor{mir::udev::Context()};
332
bool event_handler_called = false;
336
auto sysfs_path = udev_environment.add_device("drm", "control64D", NULL, {}, {});
337
auto device = ctx.device_from_syspath(sysfs_path);
339
monitor.process_events(
340
[&event_handler_called, device](mir::udev::Monitor::EventType, mir::udev::Device const& dev)
342
event_handler_called = true;
343
EXPECT_EQ(*device, dev);
346
ASSERT_TRUE(event_handler_called);
349
TEST_F(UdevWrapperTest, UdevMonitorFdIsReadableWhenEventsAvailable)
351
mir::udev::Monitor monitor{mir::udev::Context()};
355
udev_environment.add_device("drm", "card0", NULL, {}, {});
358
fds.fd = monitor.fd();
361
ASSERT_GT(poll(&fds, 1, 0), 0);
363
EXPECT_TRUE(fds.revents & POLLIN);
366
TEST_F(UdevWrapperTest, UdevMonitorFdIsUnreadableAfterProcessingEvents)
368
mir::udev::Monitor monitor{mir::udev::Context()};
372
udev_environment.add_device("drm", "card0", NULL, {}, {});
373
udev_environment.add_device("drm", "card1", NULL, {}, {});
374
udev_environment.add_device("usb", "mightymouse", NULL, {}, {});
377
fds.fd = monitor.fd();
380
ASSERT_GT(poll(&fds, 1, 0), 0);
381
ASSERT_TRUE(fds.revents & POLLIN);
383
monitor.process_events([](mir::udev::Monitor::EventType, mir::udev::Device const&){});
385
EXPECT_EQ(poll(&fds, 1, 0), 0);
388
TEST_F(UdevWrapperTest, UdevMonitorFiltersByPathAndType)
390
mir::udev::Context ctx;
392
mir::udev::Monitor monitor{mir::udev::Context()};
393
bool event_received = false;
395
monitor.filter_by_subsystem_and_type("drm", "drm_minor");
399
auto test_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {"DEVTYPE", "drm_minor"});
400
auto minor_device = ctx.device_from_syspath(test_sysfspath);
401
udev_environment.add_device("drm", "card0-LVDS1", test_sysfspath.c_str(), {}, {});
402
udev_environment.add_device("usb", "mightymouse", NULL, {}, {});
404
monitor.process_events([&event_received, minor_device]
405
(mir::udev::Monitor::EventType, mir::udev::Device const& dev)
407
EXPECT_EQ(dev, *minor_device);
408
event_received = true;
411
ASSERT_TRUE(event_received);
414
TEST_F(UdevWrapperTest, UdevMonitorFiltersAreAdditive)
416
mir::udev::Context ctx;
418
mir::udev::Monitor monitor{mir::udev::Context()};
419
bool usb_event_received = false, drm_event_recieved = false;
421
monitor.filter_by_subsystem_and_type("drm", "drm_minor");
422
monitor.filter_by_subsystem_and_type("usb", "hid");
426
auto drm_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {"DEVTYPE", "drm_minor"});
427
auto drm_device = ctx.device_from_syspath(drm_sysfspath);
428
udev_environment.add_device("drm", "card0-LVDS1", drm_sysfspath.c_str(), {}, {});
429
auto usb_sysfspath = udev_environment.add_device("usb", "mightymouse", NULL, {}, {"DEVTYPE", "hid"});
430
auto usb_device = ctx.device_from_syspath(usb_sysfspath);
432
monitor.process_events([&drm_event_recieved, drm_device, &usb_event_received, usb_device]
433
(mir::udev::Monitor::EventType, mir::udev::Device const& dev)
435
if (dev == *drm_device)
436
drm_event_recieved = true;
437
if (dev == *usb_device)
438
usb_event_received = true;
441
EXPECT_TRUE(drm_event_recieved);
442
EXPECT_TRUE(usb_event_received);
445
TEST_F(UdevWrapperTest, UdevMonitorFiltersApplyAfterEnable)
447
mir::udev::Context ctx;
449
mir::udev::Monitor monitor{mir::udev::Context()};
450
bool event_received = false;
454
monitor.filter_by_subsystem_and_type("drm", "drm_minor");
456
auto test_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {"DEVTYPE", "drm_minor"});
457
auto minor_device = ctx.device_from_syspath(test_sysfspath);
458
udev_environment.add_device("drm", "card0-LVDS1", test_sysfspath.c_str(), {}, {});
459
udev_environment.add_device("usb", "mightymouse", NULL, {}, {});
461
monitor.process_events([&event_received, minor_device]
462
(mir::udev::Monitor::EventType, mir::udev::Device const& dev)
464
EXPECT_EQ(dev, *minor_device);
465
event_received = true;
468
ASSERT_TRUE(event_received);