github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/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  }