github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/ifacestate/udevmonitor/udevmon_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package udevmonitor_test 21 22 import ( 23 "testing" 24 "time" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/interfaces/hotplug" 29 "github.com/snapcore/snapd/osutil/udev/netlink" 30 "github.com/snapcore/snapd/overlord/ifacestate/udevmonitor" 31 "github.com/snapcore/snapd/testutil" 32 ) 33 34 func TestHotplug(t *testing.T) { TestingT(t) } 35 36 type udevMonitorSuite struct{} 37 38 var _ = Suite(&udevMonitorSuite{}) 39 40 func (s *udevMonitorSuite) TestSmoke(c *C) { 41 mon := udevmonitor.New(nil, nil, nil) 42 c.Assert(mon, NotNil) 43 c.Assert(mon.Connect(), IsNil) 44 c.Assert(mon.Run(), IsNil) 45 c.Assert(mon.Stop(), IsNil) 46 } 47 48 func (s *udevMonitorSuite) TestDiscovery(c *C) { 49 var addInfos []*hotplug.HotplugDeviceInfo 50 var remInfo *hotplug.HotplugDeviceInfo 51 var enumerationDone bool 52 53 callbackChannel := make(chan struct{}) 54 defer close(callbackChannel) 55 56 added := func(inf *hotplug.HotplugDeviceInfo) { 57 addInfos = append(addInfos, inf) 58 callbackChannel <- struct{}{} 59 } 60 removed := func(inf *hotplug.HotplugDeviceInfo) { 61 // we should see just one removal 62 c.Check(remInfo, IsNil) 63 remInfo = inf 64 callbackChannel <- struct{}{} 65 } 66 enumerationFinished := func() { 67 // enumerationDone is signalled after udevadm parsing ends and before other devices are reported 68 c.Assert(addInfos, HasLen, 2) 69 c.Check(addInfos[0].DevicePath(), Equals, "/sys/a/path") 70 c.Check(addInfos[1].DevicePath(), Equals, "/sys/def") 71 72 enumerationDone = true 73 callbackChannel <- struct{}{} 74 } 75 76 cmd := testutil.MockCommand(c, "udevadm", `#!/bin/sh 77 cat << __END__ 78 P: /a/path 79 N: name 80 E: DEVNAME=name 81 E: foo=bar 82 E: DEVPATH=/a/path 83 E: SUBSYSTEM=tty 84 85 P: def 86 N: bar 87 E: DEVPATH=def 88 E: SUBSYSTEM=tty 89 E: MINOR=3 90 E: MAJOR=0 91 E: DEVNAME=ghi 92 E: DEVTYPE=bzz 93 __END__ 94 `) 95 defer cmd.Restore() 96 97 udevmon := udevmonitor.New(added, removed, enumerationFinished).(*udevmonitor.Monitor) 98 events := udevmon.EventsChannel() 99 100 c.Assert(udevmon.Run(), IsNil) 101 102 go func() { 103 events <- netlink.UEvent{ 104 Action: netlink.ADD, 105 KObj: "foo", 106 Env: map[string]string{ 107 "DEVPATH": "abc", 108 "SUBSYSTEM": "tty", 109 "MINOR": "1", 110 "MAJOR": "2", 111 "DEVNAME": "def", 112 "DEVTYPE": "boo", 113 }, 114 } 115 // the 2nd device will be ignored by de-duplication logic since it's also reported by udevadm mock. 116 events <- netlink.UEvent{ 117 Action: netlink.ADD, 118 KObj: "foo", 119 Env: map[string]string{ 120 "DEVPATH": "/a/path", 121 "SUBSYSTEM": "tty", 122 "DEVNAME": "name", 123 }, 124 } 125 events <- netlink.UEvent{ 126 Action: netlink.REMOVE, 127 KObj: "bar", 128 Env: map[string]string{ 129 "DEVPATH": "def", 130 "SUBSYSTEM": "tty", 131 "MINOR": "3", 132 "MAJOR": "0", 133 "DEVNAME": "ghi", 134 "DEVTYPE": "bzz", 135 }, 136 } 137 }() 138 139 calls := 0 140 Loop: 141 for { 142 select { 143 case <-callbackChannel: 144 calls++ 145 if calls == 5 { 146 break Loop 147 } 148 case <-time.After(3 * time.Second): 149 c.Error("Did not receive expected devices before timeout") 150 break Loop 151 } 152 } 153 154 c.Check(calls, Equals, 5) 155 c.Check(enumerationDone, Equals, true) 156 // expect three add events - one from udev event, two from enumeration. 157 c.Assert(addInfos, HasLen, 3) 158 c.Assert(remInfo, NotNil) 159 160 stopChannel := make(chan struct{}) 161 defer close(stopChannel) 162 go func() { 163 c.Assert(udevmon.Stop(), IsNil) 164 stopChannel <- struct{}{} 165 }() 166 select { 167 case <-stopChannel: 168 case <-time.After(3 * time.Second): 169 c.Error("udev monitor did not stop before timeout") 170 } 171 172 addInfo := addInfos[0] 173 c.Assert(addInfo.DeviceName(), Equals, "name") 174 c.Assert(addInfo.DevicePath(), Equals, "/sys/a/path") 175 c.Assert(addInfo.Subsystem(), Equals, "tty") 176 177 addInfo = addInfos[1] 178 c.Assert(addInfo.DeviceName(), Equals, "ghi") 179 c.Assert(addInfo.DevicePath(), Equals, "/sys/def") 180 c.Assert(addInfo.Subsystem(), Equals, "tty") 181 182 addInfo = addInfos[2] 183 c.Assert(addInfo.DeviceName(), Equals, "def") 184 c.Assert(addInfo.DeviceType(), Equals, "boo") 185 c.Assert(addInfo.Subsystem(), Equals, "tty") 186 c.Assert(addInfo.DevicePath(), Equals, "/sys/abc") 187 c.Assert(addInfo.Major(), Equals, "2") 188 c.Assert(addInfo.Minor(), Equals, "1") 189 190 c.Assert(remInfo.DeviceName(), Equals, "ghi") 191 c.Assert(remInfo.DeviceType(), Equals, "bzz") 192 c.Assert(remInfo.Subsystem(), Equals, "tty") 193 c.Assert(remInfo.DevicePath(), Equals, "/sys/def") 194 c.Assert(remInfo.Major(), Equals, "0") 195 c.Assert(remInfo.Minor(), Equals, "3") 196 }