gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/interfaces/builtin/serial_port_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 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 builtin_test
    21  
    22  import (
    23  	"fmt"
    24  
    25  	. "gopkg.in/check.v1"
    26  
    27  	"github.com/snapcore/snapd/dirs"
    28  	"github.com/snapcore/snapd/interfaces"
    29  	"github.com/snapcore/snapd/interfaces/apparmor"
    30  	"github.com/snapcore/snapd/interfaces/builtin"
    31  	"github.com/snapcore/snapd/interfaces/hotplug"
    32  	"github.com/snapcore/snapd/interfaces/udev"
    33  	"github.com/snapcore/snapd/snap"
    34  	"github.com/snapcore/snapd/snap/snaptest"
    35  	"github.com/snapcore/snapd/testutil"
    36  )
    37  
    38  type SerialPortInterfaceSuite struct {
    39  	testutil.BaseTest
    40  	iface interfaces.Interface
    41  
    42  	// OS Snap
    43  	testSlot1            *interfaces.ConnectedSlot
    44  	testSlot1Info        *snap.SlotInfo
    45  	testSlot2            *interfaces.ConnectedSlot
    46  	testSlot2Info        *snap.SlotInfo
    47  	testSlot3            *interfaces.ConnectedSlot
    48  	testSlot3Info        *snap.SlotInfo
    49  	testSlot4            *interfaces.ConnectedSlot
    50  	testSlot4Info        *snap.SlotInfo
    51  	testSlot5            *interfaces.ConnectedSlot
    52  	testSlot5Info        *snap.SlotInfo
    53  	testSlot6            *interfaces.ConnectedSlot
    54  	testSlot6Info        *snap.SlotInfo
    55  	testSlot7            *interfaces.ConnectedSlot
    56  	testSlot7Info        *snap.SlotInfo
    57  	testSlot8            *interfaces.ConnectedSlot
    58  	testSlot8Info        *snap.SlotInfo
    59  	testSlot9            *interfaces.ConnectedSlot
    60  	testSlot9Info        *snap.SlotInfo
    61  	testSlot10           *interfaces.ConnectedSlot
    62  	testSlot10Info       *snap.SlotInfo
    63  	testSlot11           *interfaces.ConnectedSlot
    64  	testSlot11Info       *snap.SlotInfo
    65  	testSlotCleaned      *interfaces.ConnectedSlot
    66  	testSlotCleanedInfo  *snap.SlotInfo
    67  	missingPathSlot      *interfaces.ConnectedSlot
    68  	missingPathSlotInfo  *snap.SlotInfo
    69  	badPathSlot1         *interfaces.ConnectedSlot
    70  	badPathSlot1Info     *snap.SlotInfo
    71  	badPathSlot2         *interfaces.ConnectedSlot
    72  	badPathSlot2Info     *snap.SlotInfo
    73  	badPathSlot3         *interfaces.ConnectedSlot
    74  	badPathSlot3Info     *snap.SlotInfo
    75  	badPathSlot4         *interfaces.ConnectedSlot
    76  	badPathSlot4Info     *snap.SlotInfo
    77  	badPathSlot5         *interfaces.ConnectedSlot
    78  	badPathSlot5Info     *snap.SlotInfo
    79  	badPathSlot6         *interfaces.ConnectedSlot
    80  	badPathSlot6Info     *snap.SlotInfo
    81  	badPathSlot7         *interfaces.ConnectedSlot
    82  	badPathSlot7Info     *snap.SlotInfo
    83  	badPathSlot8         *interfaces.ConnectedSlot
    84  	badPathSlot8Info     *snap.SlotInfo
    85  	badPathSlot9         *interfaces.ConnectedSlot
    86  	badPathSlot9Info     *snap.SlotInfo
    87  	badPathSlot10        *interfaces.ConnectedSlot
    88  	badPathSlot10Info    *snap.SlotInfo
    89  	badPathSlot11        *interfaces.ConnectedSlot
    90  	badPathSlot11Info    *snap.SlotInfo
    91  	badPathSlot12        *interfaces.ConnectedSlot
    92  	badPathSlot12Info    *snap.SlotInfo
    93  	badPathSlot13        *interfaces.ConnectedSlot
    94  	badPathSlot13Info    *snap.SlotInfo
    95  	badInterfaceSlot     *interfaces.ConnectedSlot
    96  	badInterfaceSlotInfo *snap.SlotInfo
    97  
    98  	// Gadget Snap
    99  	testUDev1             *interfaces.ConnectedSlot
   100  	testUDev1Info         *snap.SlotInfo
   101  	testUDev2             *interfaces.ConnectedSlot
   102  	testUDev2Info         *snap.SlotInfo
   103  	testUDev3             *interfaces.ConnectedSlot
   104  	testUDev3Info         *snap.SlotInfo
   105  	testUDevBadValue1     *interfaces.ConnectedSlot
   106  	testUDevBadValue1Info *snap.SlotInfo
   107  	testUDevBadValue2Info *snap.SlotInfo
   108  	testUDevBadValue3     *interfaces.ConnectedSlot
   109  	testUDevBadValue3Info *snap.SlotInfo
   110  	testUDevBadValue4     *interfaces.ConnectedSlot
   111  	testUDevBadValue4Info *snap.SlotInfo
   112  	testUDevBadValue5     *interfaces.ConnectedSlot
   113  	testUDevBadValue5Info *snap.SlotInfo
   114  
   115  	// Consuming Snap
   116  	testPlugPort1     *interfaces.ConnectedPlug
   117  	testPlugPort1Info *snap.PlugInfo
   118  	testPlugPort2     *interfaces.ConnectedPlug
   119  	testPlugPort2Info *snap.PlugInfo
   120  	testPlugPort3     *interfaces.ConnectedPlug
   121  	testPlugPort3Info *snap.PlugInfo
   122  }
   123  
   124  var _ = Suite(&SerialPortInterfaceSuite{
   125  	iface: builtin.MustInterface("serial-port"),
   126  })
   127  
   128  func (s *SerialPortInterfaceSuite) SetUpTest(c *C) {
   129  	osSnapInfo := snaptest.MockInfo(c, `
   130  name: ubuntu-core
   131  version: 0
   132  type: os
   133  slots:
   134      test-port-1:
   135          interface: serial-port
   136          path: /dev/ttyS0
   137      test-port-2:
   138          interface: serial-port
   139          path: /dev/ttyUSB927
   140      test-port-3:
   141          interface: serial-port
   142          path: /dev/ttyS42
   143      test-port-4:
   144          interface: serial-port
   145          path: /dev/ttyO0
   146      test-port-5:
   147          interface: serial-port
   148          path: /dev/ttyACM0
   149      test-port-6:
   150          interface: serial-port
   151          path: /dev/ttyAMA0
   152      test-port-7:
   153          interface: serial-port
   154          path: /dev/ttyXRUSB0
   155      test-port-8:
   156          interface: serial-port
   157          path: /dev/ttymxc2
   158      test-port-9:
   159          interface: serial-port
   160          path: /dev/ttySC0
   161      test-port-10:
   162          interface: serial-port
   163          path: /dev/ttyMSM0
   164      test-port-11:
   165          interface: serial-port
   166          path: /dev/ttyHS0
   167      test-port-unclean:
   168          interface: serial-port
   169          path: /dev/./././ttyS1////
   170      missing-path: serial-port
   171      bad-path-1:
   172          interface: serial-port
   173          path: path
   174      bad-path-2:
   175          interface: serial-port
   176          path: /dev/tty
   177      bad-path-3:
   178          interface: serial-port
   179          path: /dev/tty0
   180      bad-path-4:
   181          interface: serial-port
   182          path: /dev/tty63
   183      bad-path-5:
   184          interface: serial-port
   185          path: /dev/ttyUSB
   186      bad-path-6:
   187          interface: serial-port
   188          path: /dev/usb
   189      bad-path-7:
   190          interface: serial-port
   191          path: /dev/ttyprintk
   192      bad-path-8:
   193          interface: serial-port
   194          path: /dev/ttyO
   195      bad-path-9:
   196          interface: serial-port
   197          path: /dev/ttyS
   198      bad-path-10:
   199          interface: serial-port
   200          path: /dev/ttySC
   201      bad-path-11:
   202          interface: serial-port
   203          path: /dev/ttyMSM
   204      bad-path-12:
   205          interface: serial-port
   206          path: /dev/ttyHS
   207      bad-path-13:
   208          interface: serial-port
   209          path: /dev/ttyillegal0
   210      bad-interface: other-interface
   211  `, nil)
   212  	s.testSlot1Info = osSnapInfo.Slots["test-port-1"]
   213  	s.testSlot1 = interfaces.NewConnectedSlot(s.testSlot1Info, nil, nil)
   214  	s.testSlot2Info = osSnapInfo.Slots["test-port-2"]
   215  	s.testSlot2 = interfaces.NewConnectedSlot(s.testSlot2Info, nil, nil)
   216  	s.testSlot3Info = osSnapInfo.Slots["test-port-3"]
   217  	s.testSlot3 = interfaces.NewConnectedSlot(s.testSlot3Info, nil, nil)
   218  	s.testSlot4Info = osSnapInfo.Slots["test-port-4"]
   219  	s.testSlot4 = interfaces.NewConnectedSlot(s.testSlot4Info, nil, nil)
   220  	s.testSlot5Info = osSnapInfo.Slots["test-port-5"]
   221  	s.testSlot5 = interfaces.NewConnectedSlot(s.testSlot5Info, nil, nil)
   222  	s.testSlot6Info = osSnapInfo.Slots["test-port-6"]
   223  	s.testSlot6 = interfaces.NewConnectedSlot(s.testSlot6Info, nil, nil)
   224  	s.testSlot7Info = osSnapInfo.Slots["test-port-7"]
   225  	s.testSlot7 = interfaces.NewConnectedSlot(s.testSlot7Info, nil, nil)
   226  	s.testSlot8Info = osSnapInfo.Slots["test-port-8"]
   227  	s.testSlot8 = interfaces.NewConnectedSlot(s.testSlot8Info, nil, nil)
   228  	s.testSlot9Info = osSnapInfo.Slots["test-port-9"]
   229  	s.testSlot9 = interfaces.NewConnectedSlot(s.testSlot9Info, nil, nil)
   230  	s.testSlot10Info = osSnapInfo.Slots["test-port-10"]
   231  	s.testSlot10 = interfaces.NewConnectedSlot(s.testSlot10Info, nil, nil)
   232  	s.testSlot11Info = osSnapInfo.Slots["test-port-11"]
   233  	s.testSlot11 = interfaces.NewConnectedSlot(s.testSlot11Info, nil, nil)
   234  	s.testSlotCleanedInfo = osSnapInfo.Slots["test-port-unclean"]
   235  	s.testSlotCleaned = interfaces.NewConnectedSlot(s.testSlotCleanedInfo, nil, nil)
   236  	s.missingPathSlotInfo = osSnapInfo.Slots["missing-path"]
   237  	s.missingPathSlot = interfaces.NewConnectedSlot(s.missingPathSlotInfo, nil, nil)
   238  	s.badPathSlot1Info = osSnapInfo.Slots["bad-path-1"]
   239  	s.badPathSlot1 = interfaces.NewConnectedSlot(s.badPathSlot1Info, nil, nil)
   240  	s.badPathSlot2Info = osSnapInfo.Slots["bad-path-2"]
   241  	s.badPathSlot2 = interfaces.NewConnectedSlot(s.badPathSlot2Info, nil, nil)
   242  	s.badPathSlot3Info = osSnapInfo.Slots["bad-path-3"]
   243  	s.badPathSlot3 = interfaces.NewConnectedSlot(s.badPathSlot3Info, nil, nil)
   244  	s.badPathSlot4Info = osSnapInfo.Slots["bad-path-4"]
   245  	s.badPathSlot4 = interfaces.NewConnectedSlot(s.badPathSlot4Info, nil, nil)
   246  	s.badPathSlot5Info = osSnapInfo.Slots["bad-path-5"]
   247  	s.badPathSlot5 = interfaces.NewConnectedSlot(s.badPathSlot5Info, nil, nil)
   248  	s.badPathSlot6Info = osSnapInfo.Slots["bad-path-6"]
   249  	s.badPathSlot6 = interfaces.NewConnectedSlot(s.badPathSlot6Info, nil, nil)
   250  	s.badPathSlot7Info = osSnapInfo.Slots["bad-path-7"]
   251  	s.badPathSlot7 = interfaces.NewConnectedSlot(s.badPathSlot7Info, nil, nil)
   252  	s.badPathSlot8Info = osSnapInfo.Slots["bad-path-8"]
   253  	s.badPathSlot8 = interfaces.NewConnectedSlot(s.badPathSlot8Info, nil, nil)
   254  	s.badPathSlot9Info = osSnapInfo.Slots["bad-path-9"]
   255  	s.badPathSlot9 = interfaces.NewConnectedSlot(s.badPathSlot9Info, nil, nil)
   256  	s.badPathSlot10Info = osSnapInfo.Slots["bad-path-10"]
   257  	s.badPathSlot10 = interfaces.NewConnectedSlot(s.badPathSlot10Info, nil, nil)
   258  	s.badPathSlot11Info = osSnapInfo.Slots["bad-path-11"]
   259  	s.badPathSlot11 = interfaces.NewConnectedSlot(s.badPathSlot11Info, nil, nil)
   260  	s.badPathSlot12Info = osSnapInfo.Slots["bad-path-12"]
   261  	s.badPathSlot12 = interfaces.NewConnectedSlot(s.badPathSlot12Info, nil, nil)
   262  	s.badPathSlot13Info = osSnapInfo.Slots["bad-path-13"]
   263  	s.badPathSlot13 = interfaces.NewConnectedSlot(s.badPathSlot13Info, nil, nil)
   264  	s.badInterfaceSlotInfo = osSnapInfo.Slots["bad-interface"]
   265  	s.badInterfaceSlot = interfaces.NewConnectedSlot(s.badInterfaceSlotInfo, nil, nil)
   266  
   267  	gadgetSnapInfo := snaptest.MockInfo(c, `
   268  name: some-device
   269  version: 0
   270  type: gadget
   271  slots:
   272    test-udev-1:
   273        interface: serial-port
   274        usb-vendor: 0x0001
   275        usb-product: 0x0001
   276        path: /dev/serial-port-zigbee
   277    test-udev-2:
   278        interface: serial-port
   279        usb-vendor: 0xffff
   280        usb-product: 0xffff
   281        path: /dev/serial-port-mydevice
   282    test-udev-3:
   283        interface: serial-port
   284        usb-vendor: 0xabcd
   285        usb-product: 0x1234
   286        usb-interface-number: 0
   287        path: /dev/serial-port-myserial
   288    test-udev-bad-value-1:
   289        interface: serial-port
   290        usb-vendor: -1
   291        usb-product: 0xffff
   292        path: /dev/serial-port-mydevice
   293    test-udev-bad-value-2:
   294        interface: serial-port
   295        usb-vendor: 0x1234
   296        usb-product: 0x10000
   297        path: /dev/serial-port-mydevice
   298    test-udev-bad-value-3:
   299        interface: serial-port
   300        usb-vendor: 0x789a
   301        usb-product: 0x4321
   302        path: /dev/my-device
   303    test-udev-bad-value-4:
   304        interface: serial-port
   305        usb-vendor: 0x1234
   306        usb-product: 0x4321
   307        usb-interface-number: -1
   308        path: /dev/serial-port-mybadinterface
   309    test-udev-bad-value-5:
   310        interface: serial-port
   311        usb-vendor: 0x1234
   312        usb-product: 0x4321
   313        usb-interface-number: 32
   314        path: /dev/serial-port-overinterfacenumber
   315  `, nil)
   316  	s.testUDev1Info = gadgetSnapInfo.Slots["test-udev-1"]
   317  	s.testUDev1 = interfaces.NewConnectedSlot(s.testUDev1Info, nil, nil)
   318  	s.testUDev2Info = gadgetSnapInfo.Slots["test-udev-2"]
   319  	s.testUDev2 = interfaces.NewConnectedSlot(s.testUDev2Info, nil, nil)
   320  	s.testUDev3Info = gadgetSnapInfo.Slots["test-udev-3"]
   321  	s.testUDev3 = interfaces.NewConnectedSlot(s.testUDev3Info, nil, nil)
   322  	s.testUDevBadValue1Info = gadgetSnapInfo.Slots["test-udev-bad-value-1"]
   323  	s.testUDevBadValue1 = interfaces.NewConnectedSlot(s.testUDevBadValue1Info, nil, nil)
   324  	s.testUDevBadValue2Info = gadgetSnapInfo.Slots["test-udev-bad-value-2"]
   325  	s.testUDevBadValue3 = interfaces.NewConnectedSlot(s.testUDevBadValue2Info, nil, nil)
   326  	s.testUDevBadValue3Info = gadgetSnapInfo.Slots["test-udev-bad-value-3"]
   327  	s.testUDevBadValue3 = interfaces.NewConnectedSlot(s.testUDevBadValue3Info, nil, nil)
   328  	s.testUDevBadValue4Info = gadgetSnapInfo.Slots["test-udev-bad-value-4"]
   329  	s.testUDevBadValue4 = interfaces.NewConnectedSlot(s.testUDevBadValue4Info, nil, nil)
   330  	s.testUDevBadValue5Info = gadgetSnapInfo.Slots["test-udev-bad-value-5"]
   331  	s.testUDevBadValue5 = interfaces.NewConnectedSlot(s.testUDevBadValue5Info, nil, nil)
   332  
   333  	consumingSnapInfo := snaptest.MockInfo(c, `
   334  name: client-snap
   335  version: 0
   336  plugs:
   337      plug-for-port-1:
   338          interface: serial-port
   339      plug-for-port-2:
   340          interface: serial-port
   341      plug-for-port-3:
   342          interface: serial-port
   343  
   344  apps:
   345      app-accessing-1-port:
   346          command: foo
   347          plugs: [serial-port]
   348      app-accessing-2-ports:
   349          command: bar
   350          plugs: [plug-for-port-1, plug-for-port-2]
   351      app-accessing-3rd-port:
   352          command: foo
   353          plugs: [plug-for-port-3]
   354  `, nil)
   355  	s.testPlugPort1Info = consumingSnapInfo.Plugs["plug-for-port-1"]
   356  	s.testPlugPort1 = interfaces.NewConnectedPlug(s.testPlugPort1Info, nil, nil)
   357  	s.testPlugPort2Info = consumingSnapInfo.Plugs["plug-for-port-2"]
   358  	s.testPlugPort2 = interfaces.NewConnectedPlug(s.testPlugPort2Info, nil, nil)
   359  	s.testPlugPort3Info = consumingSnapInfo.Plugs["plug-for-port-3"]
   360  	s.testPlugPort3 = interfaces.NewConnectedPlug(s.testPlugPort3Info, nil, nil)
   361  }
   362  
   363  func (s *SerialPortInterfaceSuite) TestName(c *C) {
   364  	c.Assert(s.iface.Name(), Equals, "serial-port")
   365  }
   366  
   367  func (s *SerialPortInterfaceSuite) TestSanitizeCoreSnapSlots(c *C) {
   368  	for _, slot := range []*snap.SlotInfo{s.testSlot1Info, s.testSlot2Info, s.testSlot3Info, s.testSlot4Info, s.testSlot5Info, s.testSlot6Info, s.testSlot7Info, s.testSlot8Info, s.testSlot9Info, s.testSlot10Info, s.testSlot11Info} {
   369  		c.Assert(interfaces.BeforePrepareSlot(s.iface, slot), IsNil)
   370  	}
   371  }
   372  
   373  func (s *SerialPortInterfaceSuite) TestSanitizeBadCoreSnapSlots(c *C) {
   374  	// Slots without the "path" attribute are rejected.
   375  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.missingPathSlotInfo), ErrorMatches, `serial-port slot must have a path attribute`)
   376  
   377  	// Slots with incorrect value of the "path" attribute are rejected.
   378  	for _, slot := range []*snap.SlotInfo{s.badPathSlot1Info, s.badPathSlot2Info, s.badPathSlot3Info, s.badPathSlot4Info, s.badPathSlot5Info, s.badPathSlot6Info, s.badPathSlot7Info, s.badPathSlot8Info, s.badPathSlot9Info, s.badPathSlot10Info, s.badPathSlot11Info, s.badPathSlot12Info, s.badPathSlot13Info} {
   379  		c.Assert(interfaces.BeforePrepareSlot(s.iface, slot), ErrorMatches, "serial-port path attribute must be a valid device node")
   380  	}
   381  }
   382  
   383  func (s *SerialPortInterfaceSuite) TestSanitizeGadgetSnapSlots(c *C) {
   384  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDev1Info), IsNil)
   385  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDev2Info), IsNil)
   386  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDev3Info), IsNil)
   387  }
   388  
   389  func (s *SerialPortInterfaceSuite) TestSanitizeBadGadgetSnapSlots(c *C) {
   390  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue1Info), ErrorMatches, "serial-port usb-vendor attribute not valid: -1")
   391  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue2Info), ErrorMatches, "serial-port usb-product attribute not valid: 65536")
   392  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue3Info), ErrorMatches, "serial-port path attribute specifies invalid symlink location")
   393  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue4Info), ErrorMatches, "serial-port usb-interface-number attribute cannot be negative or larger than 31")
   394  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.testUDevBadValue5Info), ErrorMatches, "serial-port usb-interface-number attribute cannot be negative or larger than 31")
   395  }
   396  
   397  func (s *SerialPortInterfaceSuite) TestPermanentSlotUDevSnippets(c *C) {
   398  	spec := &udev.Specification{}
   399  	for _, slot := range []*snap.SlotInfo{s.testSlot1Info, s.testSlot2Info, s.testSlot3Info, s.testSlot4Info} {
   400  		err := spec.AddPermanentSlot(s.iface, slot)
   401  		c.Assert(err, IsNil)
   402  		c.Assert(spec.Snippets(), HasLen, 0)
   403  	}
   404  
   405  	expectedSnippet1 := `# serial-port
   406  IMPORT{builtin}="usb_id"
   407  SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", SYMLINK+="serial-port-zigbee"`
   408  	err := spec.AddPermanentSlot(s.iface, s.testUDev1Info)
   409  	c.Assert(err, IsNil)
   410  	c.Assert(spec.Snippets(), HasLen, 1)
   411  	snippet := spec.Snippets()[0]
   412  	c.Assert(snippet, Equals, expectedSnippet1)
   413  
   414  	spec = &udev.Specification{}
   415  	expectedSnippet2 := `# serial-port
   416  IMPORT{builtin}="usb_id"
   417  SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", SYMLINK+="serial-port-mydevice"`
   418  	err = spec.AddPermanentSlot(s.iface, s.testUDev2Info)
   419  	c.Assert(err, IsNil)
   420  	c.Assert(spec.Snippets(), HasLen, 1)
   421  	snippet = spec.Snippets()[0]
   422  	c.Assert(snippet, Equals, expectedSnippet2)
   423  
   424  	spec = &udev.Specification{}
   425  	// The ENV{ID_USB_INTERFACE_NUM} is set to two hex digits
   426  	// For instance, the expectedSnippet3 is set to 00
   427  	expectedSnippet3 := `# serial-port
   428  IMPORT{builtin}="usb_id"
   429  SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="abcd", ATTRS{idProduct}=="1234", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="serial-port-myserial"`
   430  	err = spec.AddPermanentSlot(s.iface, s.testUDev3Info)
   431  	c.Assert(err, IsNil)
   432  	c.Assert(spec.Snippets(), HasLen, 1)
   433  	snippet = spec.Snippets()[0]
   434  	c.Assert(snippet, Equals, expectedSnippet3)
   435  }
   436  
   437  func (s *SerialPortInterfaceSuite) TestConnectedPlugUDevSnippets(c *C) {
   438  	// add the plug for the slot with just path
   439  	spec := &udev.Specification{}
   440  	err := spec.AddConnectedPlug(s.iface, s.testPlugPort1, s.testSlot1)
   441  	c.Assert(err, IsNil)
   442  	c.Assert(spec.Snippets(), HasLen, 2)
   443  	snippet := spec.Snippets()[0]
   444  	expectedSnippet1 := `# serial-port
   445  SUBSYSTEM=="tty", KERNEL=="ttyS0", TAG+="snap_client-snap_app-accessing-2-ports"`
   446  	c.Assert(snippet, Equals, expectedSnippet1)
   447  	extraSnippet := spec.Snippets()[1]
   448  	expectedExtraSnippet1 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-2-ports", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-2-ports $devpath $major:$minor"`, dirs.DistroLibExecDir)
   449  	c.Assert(extraSnippet, Equals, expectedExtraSnippet1)
   450  
   451  	// add plug for the first slot with product and vendor ids
   452  	spec = &udev.Specification{}
   453  	err = spec.AddConnectedPlug(s.iface, s.testPlugPort1, s.testUDev1)
   454  	c.Assert(err, IsNil)
   455  	c.Assert(spec.Snippets(), HasLen, 2)
   456  	snippet = spec.Snippets()[0]
   457  	expectedSnippet2 := `# serial-port
   458  IMPORT{builtin}="usb_id"
   459  SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-2-ports"`
   460  	c.Assert(snippet, Equals, expectedSnippet2)
   461  	extraSnippet = spec.Snippets()[1]
   462  	expectedExtraSnippet2 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-2-ports", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-2-ports $devpath $major:$minor"`, dirs.DistroLibExecDir)
   463  	c.Assert(extraSnippet, Equals, expectedExtraSnippet2)
   464  
   465  	// add plug for the first slot with product and vendor ids
   466  	spec = &udev.Specification{}
   467  	err = spec.AddConnectedPlug(s.iface, s.testPlugPort2, s.testUDev2)
   468  	c.Assert(err, IsNil)
   469  	c.Assert(spec.Snippets(), HasLen, 2)
   470  	snippet = spec.Snippets()[0]
   471  	expectedSnippet3 := `# serial-port
   472  IMPORT{builtin}="usb_id"
   473  SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", TAG+="snap_client-snap_app-accessing-2-ports"`
   474  	c.Assert(snippet, Equals, expectedSnippet3)
   475  	extraSnippet = spec.Snippets()[1]
   476  	expectedExtraSnippet3 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-2-ports", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-2-ports $devpath $major:$minor"`, dirs.DistroLibExecDir)
   477  	c.Assert(extraSnippet, Equals, expectedExtraSnippet3)
   478  
   479  	// add plug for the first slot with product and vendor ids and usb interface number
   480  	spec = &udev.Specification{}
   481  	err = spec.AddConnectedPlug(s.iface, s.testPlugPort2, s.testUDev3)
   482  	c.Assert(err, IsNil)
   483  	c.Assert(spec.Snippets(), HasLen, 2)
   484  	snippet = spec.Snippets()[0]
   485  	expectedSnippet4 := `# serial-port
   486  IMPORT{builtin}="usb_id"
   487  SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="abcd", ATTRS{idProduct}=="1234", ENV{ID_USB_INTERFACE_NUM}=="00", TAG+="snap_client-snap_app-accessing-2-ports"`
   488  	c.Assert(snippet, Equals, expectedSnippet4)
   489  	extraSnippet = spec.Snippets()[1]
   490  	expectedExtraSnippet4 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-2-ports", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-2-ports $devpath $major:$minor"`, dirs.DistroLibExecDir)
   491  	c.Assert(extraSnippet, Equals, expectedExtraSnippet4)
   492  }
   493  
   494  func (s *SerialPortInterfaceSuite) TestConnectedPlugAppArmorSnippets(c *C) {
   495  	checkConnectedPlugSnippet := func(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot, expectedSnippet string) {
   496  		apparmorSpec := &apparmor.Specification{}
   497  		err := apparmorSpec.AddConnectedPlug(s.iface, plug, slot)
   498  		c.Assert(err, IsNil)
   499  
   500  		c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.client-snap.app-accessing-2-ports"})
   501  		snippet := apparmorSpec.SnippetForTag("snap.client-snap.app-accessing-2-ports")
   502  		c.Assert(snippet, DeepEquals, expectedSnippet)
   503  	}
   504  
   505  	expectedSnippet1 := `/dev/ttyS0 rwk,`
   506  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot1, expectedSnippet1)
   507  	expectedSnippet2 := `/dev/ttyUSB927 rwk,`
   508  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot2, expectedSnippet2)
   509  
   510  	expectedSnippet3 := `/dev/ttyS42 rwk,`
   511  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot3, expectedSnippet3)
   512  
   513  	expectedSnippet4 := `/dev/ttyO0 rwk,`
   514  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot4, expectedSnippet4)
   515  
   516  	expectedSnippet5 := `/dev/ttyACM0 rwk,`
   517  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot5, expectedSnippet5)
   518  
   519  	expectedSnippet6 := `/dev/ttyAMA0 rwk,`
   520  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot6, expectedSnippet6)
   521  
   522  	expectedSnippet7 := `/dev/ttyXRUSB0 rwk,`
   523  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot7, expectedSnippet7)
   524  
   525  	expectedSnippet8 := `/dev/ttymxc2 rwk,`
   526  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot8, expectedSnippet8)
   527  
   528  	expectedSnippet9 := `/dev/ttySC0 rwk,`
   529  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot9, expectedSnippet9)
   530  
   531  	expectedSnippet10 := `/dev/ttyMSM0 rwk,`
   532  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot10, expectedSnippet10)
   533  
   534  	expectedSnippet11 := `/dev/ttyHS0 rwk,`
   535  	checkConnectedPlugSnippet(s.testPlugPort1, s.testSlot11, expectedSnippet11)
   536  
   537  	expectedSnippet12 := `/dev/tty[A-Z]*[0-9] rwk,`
   538  	checkConnectedPlugSnippet(s.testPlugPort1, s.testUDev1, expectedSnippet12)
   539  
   540  	expectedSnippet13 := `/dev/tty[A-Z]*[0-9] rwk,`
   541  	checkConnectedPlugSnippet(s.testPlugPort2, s.testUDev2, expectedSnippet13)
   542  
   543  	expectedSnippet14 := `/dev/tty[A-Z]*[0-9] rwk,`
   544  	checkConnectedPlugSnippet(s.testPlugPort2, s.testUDev3, expectedSnippet14)
   545  }
   546  
   547  func (s *SerialPortInterfaceSuite) TestConnectedPlugUDevSnippetsForPath(c *C) {
   548  	checkConnectedPlugSnippet := func(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot, expectedSnippet string, expectedExtraSnippet string) {
   549  		udevSpec := &udev.Specification{}
   550  		err := udevSpec.AddConnectedPlug(s.iface, plug, slot)
   551  		c.Assert(err, IsNil)
   552  
   553  		c.Assert(udevSpec.Snippets(), HasLen, 2)
   554  		snippet := udevSpec.Snippets()[0]
   555  		c.Assert(snippet, Equals, expectedSnippet)
   556  		extraSnippet := udevSpec.Snippets()[1]
   557  		c.Assert(extraSnippet, Equals, expectedExtraSnippet)
   558  	}
   559  
   560  	// these have only path
   561  	expectedSnippet1 := `# serial-port
   562  SUBSYSTEM=="tty", KERNEL=="ttyS0", TAG+="snap_client-snap_app-accessing-3rd-port"`
   563  	expectedExtraSnippet1 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   564  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot1, expectedSnippet1, expectedExtraSnippet1)
   565  
   566  	expectedSnippet2 := `# serial-port
   567  SUBSYSTEM=="tty", KERNEL=="ttyUSB927", TAG+="snap_client-snap_app-accessing-3rd-port"`
   568  	expectedExtraSnippet2 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   569  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot2, expectedSnippet2, expectedExtraSnippet2)
   570  
   571  	expectedSnippet3 := `# serial-port
   572  SUBSYSTEM=="tty", KERNEL=="ttyS42", TAG+="snap_client-snap_app-accessing-3rd-port"`
   573  	expectedExtraSnippet3 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   574  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot3, expectedSnippet3, expectedExtraSnippet3)
   575  
   576  	expectedSnippet4 := `# serial-port
   577  SUBSYSTEM=="tty", KERNEL=="ttyO0", TAG+="snap_client-snap_app-accessing-3rd-port"`
   578  	expectedExtraSnippet4 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   579  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot4, expectedSnippet4, expectedExtraSnippet4)
   580  
   581  	expectedSnippet5 := `# serial-port
   582  SUBSYSTEM=="tty", KERNEL=="ttyACM0", TAG+="snap_client-snap_app-accessing-3rd-port"`
   583  	expectedExtraSnippet5 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   584  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot5, expectedSnippet5, expectedExtraSnippet5)
   585  
   586  	expectedSnippet6 := `# serial-port
   587  SUBSYSTEM=="tty", KERNEL=="ttyAMA0", TAG+="snap_client-snap_app-accessing-3rd-port"`
   588  	expectedExtraSnippet6 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   589  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot6, expectedSnippet6, expectedExtraSnippet6)
   590  
   591  	expectedSnippet7 := `# serial-port
   592  SUBSYSTEM=="tty", KERNEL=="ttyXRUSB0", TAG+="snap_client-snap_app-accessing-3rd-port"`
   593  	expectedExtraSnippet7 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   594  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot7, expectedSnippet7, expectedExtraSnippet7)
   595  
   596  	expectedSnippet8 := `# serial-port
   597  SUBSYSTEM=="tty", KERNEL=="ttymxc2", TAG+="snap_client-snap_app-accessing-3rd-port"`
   598  	expectedExtraSnippet8 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   599  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot8, expectedSnippet8, expectedExtraSnippet8)
   600  
   601  	expectedSnippet9 := `# serial-port
   602  SUBSYSTEM=="tty", KERNEL=="ttySC0", TAG+="snap_client-snap_app-accessing-3rd-port"`
   603  	expectedExtraSnippet9 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   604  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot9, expectedSnippet9, expectedExtraSnippet9)
   605  
   606  	expectedSnippet10 := `# serial-port
   607  SUBSYSTEM=="tty", KERNEL=="ttyMSM0", TAG+="snap_client-snap_app-accessing-3rd-port"`
   608  	expectedExtraSnippet10 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   609  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot10, expectedSnippet10, expectedExtraSnippet10)
   610  
   611  	expectedSnippet11 := `# serial-port
   612  SUBSYSTEM=="tty", KERNEL=="ttyHS0", TAG+="snap_client-snap_app-accessing-3rd-port"`
   613  	expectedExtraSnippet11 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   614  	checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot11, expectedSnippet11, expectedExtraSnippet11)
   615  
   616  	// these have product and vendor ids
   617  	expectedSnippet12 := `# serial-port
   618  IMPORT{builtin}="usb_id"
   619  SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-3rd-port"`
   620  	expectedExtraSnippet12 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   621  	checkConnectedPlugSnippet(s.testPlugPort3, s.testUDev1, expectedSnippet12, expectedExtraSnippet12)
   622  
   623  	expectedSnippet13 := `# serial-port
   624  IMPORT{builtin}="usb_id"
   625  SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", TAG+="snap_client-snap_app-accessing-3rd-port"`
   626  	expectedExtraSnippet13 := fmt.Sprintf(`TAG=="snap_client-snap_app-accessing-3rd-port", RUN+="%v/snap-device-helper $env{ACTION} snap_client-snap_app-accessing-3rd-port $devpath $major:$minor"`, dirs.DistroLibExecDir)
   627  	checkConnectedPlugSnippet(s.testPlugPort3, s.testUDev2, expectedSnippet13, expectedExtraSnippet13)
   628  }
   629  
   630  func (s *SerialPortInterfaceSuite) TestHotplugDeviceDetected(c *C) {
   631  	hotplugIface := s.iface.(hotplug.Definer)
   632  	di, err := hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/ttyUSB0", "ID_VENDOR_ID": "1234", "ID_MODEL_ID": "5678", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"})
   633  	c.Assert(err, IsNil)
   634  	proposedSlot, err := hotplugIface.HotplugDeviceDetected(di)
   635  	c.Assert(err, IsNil)
   636  	c.Assert(proposedSlot, DeepEquals, &hotplug.ProposedSlot{Attrs: map[string]interface{}{"path": "/dev/ttyUSB0", "usb-vendor": "1234", "usb-product": "5678"}})
   637  }
   638  
   639  func (s *SerialPortInterfaceSuite) TestHotplugDeviceDetectedNotSerialPort(c *C) {
   640  	hotplugIface := s.iface.(hotplug.Definer)
   641  	di, err := hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/other", "ID_VENDOR_ID": "1234", "ID_MODEL_ID": "5678", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"})
   642  	c.Assert(err, IsNil)
   643  	proposedSlot, err := hotplugIface.HotplugDeviceDetected(di)
   644  	c.Assert(err, IsNil)
   645  	c.Assert(proposedSlot, IsNil)
   646  }
   647  
   648  func (s *SerialPortInterfaceSuite) TestHotplugHandledByGadget(c *C) {
   649  	byGadgetPred := s.iface.(hotplug.HandledByGadgetPredicate)
   650  	di, err := hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/ttyXRUSB0", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"})
   651  	c.Assert(err, IsNil)
   652  
   653  	c.Assert(byGadgetPred.HandledByGadget(di, s.testSlot5Info), Equals, false)
   654  	// matching path /dev/ttyXRUSB0
   655  	c.Assert(byGadgetPred.HandledByGadget(di, s.testSlot7Info), Equals, true)
   656  
   657  	// matching on vendor, model, usb interface num
   658  	di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "abcd", "ID_MODEL_ID": "1234", "ID_USB_INTERFACE_NUM": "00", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"})
   659  	c.Assert(err, IsNil)
   660  	c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev3Info), Equals, true)
   661  	// model doesn't match, everything else matches
   662  	di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "abcd", "ID_MODEL_ID": "ffff", "ID_USB_INTERFACE_NUM": "00", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"})
   663  	c.Assert(err, IsNil)
   664  	c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev3Info), Equals, false)
   665  	// vendor doesn't match, everything else matches
   666  	di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "eeee", "ID_MODEL_ID": "1234", "ID_USB_INTERFACE_NUM": "00", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"})
   667  	c.Assert(err, IsNil)
   668  	c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev3Info), Equals, false)
   669  	// usb interface doesn't match, everything else matches
   670  	di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "abcd", "ID_MODEL_ID": "1234", "ID_USB_INTERFACE_NUM": "ff", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"})
   671  	c.Assert(err, IsNil)
   672  	c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev3Info), Equals, false)
   673  
   674  	// usb interface num is optional, match on vendor/model
   675  	di, err = hotplug.NewHotplugDeviceInfo(map[string]string{"DEVPATH": "/sys/foo/bar", "DEVNAME": "/dev/path", "ID_VENDOR_ID": "ffff", "ID_MODEL_ID": "ffff", "ACTION": "add", "SUBSYSTEM": "tty", "ID_BUS": "usb"})
   676  	c.Assert(err, IsNil)
   677  	c.Assert(byGadgetPred.HandledByGadget(di, s.testUDev2Info), Equals, true)
   678  }
   679  
   680  func (s *SerialPortInterfaceSuite) TestInterfaces(c *C) {
   681  	c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
   682  }