Home | History | Annotate | Download | only in utils
      1 #!/usr/bin/env python
      2 # Copyright 2016 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 # pylint: disable=protected-access
      7 
      8 """
      9 Unit tests for the contents of find_usb_devices.py.
     10 
     11 Device tree for these tests is as follows:
     12 Bus 001:
     13 1: Device 011 "foo"
     14 2: Device 012 "bar"
     15 3: Device 013 "baz"
     16 
     17 Bus 002:
     18 1: Device 011 "quux"
     19 2: Device 020 "My Test HUB" #hub 1
     20 2:1: Device 021 "battor_p7_h1_t0" #physical port 7 on hub 1, on ttyUSB0
     21 2:3: Device 022 "battor_p5_h1_t1" #physical port 5 on hub 1, on ttyUSB1
     22 2:4: Device 023 "My Test Internal HUB" #internal section of hub 1
     23 2:4:2: Device 024 "battor_p3_h1_t2" #physical port 3 on hub 1, on ttyUSB2
     24 2:4:3: Device 026 "Not a Battery Monitor" #physical port 1 on hub 1, on ttyUSB3
     25 2:4:4: Device 025 "battor_p1_h1_t3" #physical port 1 on hub 1, on ttyUSB3
     26 3: Device 100 "My Test HUB" #hub 2
     27 3:4: Device 101 "My Test Internal HUB" #internal section of hub 2
     28 3:4:4: Device 102 "battor_p1_h2_t4" #physical port 1 on hub 2, on ttyusb4
     29 """
     30 
     31 import logging
     32 import os
     33 import unittest
     34 
     35 from devil import devil_env
     36 from devil.utils import battor_device_mapping
     37 from devil.utils import find_usb_devices
     38 from devil.utils import lsusb
     39 from devil.utils import usb_hubs
     40 
     41 with devil_env.SysPath(devil_env.PYMOCK_PATH):
     42   import mock # pylint: disable=import-error
     43 
     44 # Output of lsusb.lsusb().
     45 # We just test that the dictionary is working by creating an
     46 # "ID number" equal to (bus_num*1000)+device_num and seeing if
     47 # it is picked up correctly. Also we test the description
     48 
     49 DEVLIST = [(1, 11, 'foo'),
     50            (1, 12, 'bar'),
     51            (1, 13, 'baz'),
     52            (2, 11, 'quux'),
     53            (2, 20, 'My Test HUB'),
     54            (2, 21, 'ID 0403:6001 battor_p7_h1_t0'),
     55            (2, 22, 'ID 0403:6001 battor_p5_h1_t1'),
     56            (2, 23, 'My Test Internal HUB'),
     57            (2, 24, 'ID 0403:6001 battor_p3_h1_t2'),
     58            (2, 25, 'ID 0403:6001 battor_p1_h1_t3'),
     59            (2, 26, 'Not a Battery Monitor'),
     60            (2, 100, 'My Test HUB'),
     61            (2, 101, 'My Test Internal HUB'),
     62            (2, 102, 'ID 0403:6001 battor_p1_h1_t4')]
     63 
     64 LSUSB_OUTPUT = [
     65   {'bus': b, 'device': d, 'desc': t, 'id': (1000*b)+d}
     66        for (b, d, t) in DEVLIST]
     67 
     68 
     69 # Note: "Lev", "Cnt", "Spd", and "MxCh" are not used by parser,
     70 # so we just leave them as zeros here. Also note that the port
     71 # numbers reported here start at 0, so they're 1 less than the
     72 # port numbers reported elsewhere.
     73 USB_DEVICES_OUTPUT = '''
     74 T:  Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 11 Spd=000 MxCh=00
     75 S:  SerialNumber=FooSerial
     76 T:  Bus=01 Lev=00 Prnt=00 Port=01 Cnt=00 Dev#= 12 Spd=000 MxCh=00
     77 S:  SerialNumber=BarSerial
     78 T:  Bus=01 Lev=00 Prnt=00 Port=02 Cnt=00 Dev#= 13 Spd=000 MxCh=00
     79 S:  SerialNumber=BazSerial
     80 
     81 T:  Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 11 Spd=000 MxCh=00
     82 
     83 T:  Bus=02 Lev=00 Prnt=00 Port=01 Cnt=00 Dev#= 20 Spd=000 MxCh=00
     84 T:  Bus=02 Lev=00 Prnt=20 Port=00 Cnt=00 Dev#= 21 Spd=000 MxCh=00
     85 S:  SerialNumber=BattOr0
     86 T:  Bus=02 Lev=00 Prnt=20 Port=02 Cnt=00 Dev#= 22 Spd=000 MxCh=00
     87 S:  SerialNumber=BattOr1
     88 T:  Bus=02 Lev=00 Prnt=20 Port=03 Cnt=00 Dev#= 23 Spd=000 MxCh=00
     89 T:  Bus=02 Lev=00 Prnt=23 Port=01 Cnt=00 Dev#= 24 Spd=000 MxCh=00
     90 S:  SerialNumber=BattOr2
     91 T:  Bus=02 Lev=00 Prnt=23 Port=03 Cnt=00 Dev#= 25 Spd=000 MxCh=00
     92 S:  SerialNumber=BattOr3
     93 T:  Bus=02 Lev=00 Prnt=23 Port=02 Cnt=00 Dev#= 26 Spd=000 MxCh=00
     94 
     95 T:  Bus=02 Lev=00 Prnt=00 Port=02 Cnt=00 Dev#=100 Spd=000 MxCh=00
     96 T:  Bus=02 Lev=00 Prnt=100 Port=03 Cnt=00 Dev#=101 Spd=000 MxCh=00
     97 T:  Bus=02 Lev=00 Prnt=101 Port=03 Cnt=00 Dev#=102 Spd=000 MxCh=00
     98 '''
     99 
    100 RAW_LSUSB_OUTPUT = '''
    101 Bus 001 Device 011: FAST foo
    102 Bus 001 Device 012: FAST bar
    103 Bus 001 Device 013: baz
    104 Bus 002 Device 011: quux
    105 Bus 002 Device 020: My Test HUB
    106 Bus 002 Device 021: ID 0403:6001 battor_p7_h1_t0
    107 Bus 002 Device 022: ID 0403:6001 battor_p5_h1_t1
    108 Bus 002 Device 023: My Test Internal HUB
    109 Bus 002 Device 024: ID 0403:6001 battor_p3_h1_t2
    110 Bus 002 Device 025: ID 0403:6001 battor_p1_h1_t3
    111 Bus 002 Device 026: Not a Battery Monitor
    112 Bus 002 Device 100: My Test HUB
    113 Bus 002 Device 101: My Test Internal HUB
    114 Bus 002 Device 102: ID 0403:6001 battor_p1_h1_t4
    115 '''
    116 
    117 LIST_TTY_OUTPUT = '''
    118 ttyUSB0
    119 Something-else-0
    120 ttyUSB1
    121 ttyUSB2
    122 Something-else-1
    123 ttyUSB3
    124 ttyUSB4
    125 Something-else-2
    126 ttyUSB5
    127 '''
    128 
    129 # Note: The real output will have multiple lines with
    130 # ATTRS{busnum} and ATTRS{devnum}, but only the first
    131 # one counts. Thus the test output duplicates this.
    132 UDEVADM_USBTTY0_OUTPUT = '''
    133 ATTRS{busnum}=="2"
    134 ATTRS{devnum}=="21"
    135 ATTRS{busnum}=="0"
    136 ATTRS{devnum}=="0"
    137 '''
    138 
    139 UDEVADM_USBTTY1_OUTPUT = '''
    140 ATTRS{busnum}=="2"
    141 ATTRS{devnum}=="22"
    142 ATTRS{busnum}=="0"
    143 ATTRS{devnum}=="0"
    144 '''
    145 
    146 UDEVADM_USBTTY2_OUTPUT = '''
    147 ATTRS{busnum}=="2"
    148 ATTRS{devnum}=="24"
    149 ATTRS{busnum}=="0"
    150 ATTRS{devnum}=="0"
    151 '''
    152 
    153 UDEVADM_USBTTY3_OUTPUT = '''
    154 ATTRS{busnum}=="2"
    155 ATTRS{devnum}=="25"
    156 ATTRS{busnum}=="0"
    157 ATTRS{devnum}=="0"
    158 '''
    159 
    160 UDEVADM_USBTTY4_OUTPUT = '''
    161 ATTRS{busnum}=="2"
    162 ATTRS{devnum}=="102"
    163 ATTRS{busnum}=="0"
    164 ATTRS{devnum}=="0"
    165 '''
    166 
    167 UDEVADM_USBTTY5_OUTPUT = '''
    168 ATTRS{busnum}=="2"
    169 ATTRS{devnum}=="26"
    170 ATTRS{busnum}=="0"
    171 ATTRS{devnum}=="0"
    172 '''
    173 
    174 UDEVADM_OUTPUT_DICT = {
    175   'ttyUSB0': UDEVADM_USBTTY0_OUTPUT,
    176   'ttyUSB1': UDEVADM_USBTTY1_OUTPUT,
    177   'ttyUSB2': UDEVADM_USBTTY2_OUTPUT,
    178   'ttyUSB3': UDEVADM_USBTTY3_OUTPUT,
    179   'ttyUSB4': UDEVADM_USBTTY4_OUTPUT,
    180   'ttyUSB5': UDEVADM_USBTTY5_OUTPUT}
    181 
    182 # Identification criteria for Plugable 7-Port Hub
    183 def isTestHub(node):
    184   """Check if a node is a Plugable 7-Port Hub
    185   (Model USB2-HUB7BC)
    186   The topology of this device is a 4-port hub,
    187   with another 4-port hub connected on port 4.
    188   """
    189   if not isinstance(node, find_usb_devices.USBDeviceNode):
    190     return False
    191   if 'Test HUB' not in node.desc:
    192     return False
    193   if not node.HasPort(4):
    194     return False
    195   return 'Test Internal HUB' in node.PortToDevice(4).desc
    196 
    197 TEST_HUB = usb_hubs.HubType(isTestHub,
    198                             {1:7,
    199                              2:6,
    200                              3:5,
    201                              4:{1:4, 2:3, 3:2, 4:1}})
    202 
    203 class USBScriptTest(unittest.TestCase):
    204   def setUp(self):
    205     find_usb_devices._GetTtyUSBInfo = mock.Mock(
    206         side_effect=lambda x: UDEVADM_OUTPUT_DICT[x])
    207     find_usb_devices._GetParsedLSUSBOutput = mock.Mock(
    208         return_value=LSUSB_OUTPUT)
    209     find_usb_devices._GetUSBDevicesOutput = mock.Mock(
    210         return_value=USB_DEVICES_OUTPUT)
    211     find_usb_devices._GetCommList = mock.Mock(
    212         return_value=LIST_TTY_OUTPUT)
    213     lsusb.raw_lsusb = mock.Mock(
    214         return_value=RAW_LSUSB_OUTPUT)
    215 
    216   def testIsBattOr(self):
    217     bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
    218     self.assertTrue(battor_device_mapping.IsBattOr('ttyUSB3', bd))
    219     self.assertFalse(battor_device_mapping.IsBattOr('ttyUSB5', bd))
    220 
    221   def testGetBattOrs(self):
    222     bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
    223     self.assertEquals(battor_device_mapping.GetBattOrList(bd),
    224                           ['ttyUSB0', 'ttyUSB1', 'ttyUSB2',
    225                            'ttyUSB3', 'ttyUSB4'])
    226 
    227   def testGetTTYDevices(self):
    228     pp = find_usb_devices.GetAllPhysicalPortToTTYMaps([TEST_HUB])
    229     result = list(pp)
    230     self.assertEquals(result[0], {7:'ttyUSB0',
    231                                   5:'ttyUSB1',
    232                                   3:'ttyUSB2',
    233                                   2:'ttyUSB5',
    234                                   1:'ttyUSB3'})
    235     self.assertEquals(result[1], {1:'ttyUSB4'})
    236 
    237   def testGetPortDeviceMapping(self):
    238     pp = find_usb_devices.GetAllPhysicalPortToBusDeviceMaps([TEST_HUB])
    239     result = list(pp)
    240     self.assertEquals(result[0], {7:(2, 21),
    241                                   5:(2, 22),
    242                                   3:(2, 24),
    243                                   2:(2, 26),
    244                                   1:(2, 25)})
    245     self.assertEquals(result[1], {1:(2, 102)})
    246 
    247   def testGetSerialMapping(self):
    248     pp = find_usb_devices.GetAllPhysicalPortToSerialMaps([TEST_HUB])
    249     result = list(pp)
    250     self.assertEquals(result[0], {7:'BattOr0',
    251                                   5:'BattOr1',
    252                                   3:'BattOr2',
    253                                   1:'BattOr3'})
    254     self.assertEquals(result[1], {})
    255 
    256   def testFastDeviceDescriptions(self):
    257     bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
    258     dev_foo = bd[1].FindDeviceNumber(11)
    259     dev_bar = bd[1].FindDeviceNumber(12)
    260     dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
    261     self.assertEquals(dev_foo.desc, 'FAST foo')
    262     self.assertEquals(dev_bar.desc, 'FAST bar')
    263     self.assertEquals(dev_battor_p7_h1_t0.desc,
    264         'ID 0403:6001 battor_p7_h1_t0')
    265 
    266   def testDeviceDescriptions(self):
    267     bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
    268     dev_foo = bd[1].FindDeviceNumber(11)
    269     dev_bar = bd[1].FindDeviceNumber(12)
    270     dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
    271     self.assertEquals(dev_foo.desc, 'foo')
    272     self.assertEquals(dev_bar.desc, 'bar')
    273     self.assertEquals(dev_battor_p7_h1_t0.desc,
    274         'ID 0403:6001 battor_p7_h1_t0')
    275 
    276   def testDeviceInformation(self):
    277     bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
    278     dev_foo = bd[1].FindDeviceNumber(11)
    279     dev_bar = bd[1].FindDeviceNumber(12)
    280     dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
    281     self.assertEquals(dev_foo.info['id'], 1011)
    282     self.assertEquals(dev_bar.info['id'], 1012)
    283     self.assertEquals(dev_battor_p7_h1_t0.info['id'], 2021)
    284 
    285   def testSerialNumber(self):
    286     bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
    287     dev_foo = bd[1].FindDeviceNumber(11)
    288     dev_bar = bd[1].FindDeviceNumber(12)
    289     dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
    290     self.assertEquals(dev_foo.serial, 'FooSerial')
    291     self.assertEquals(dev_bar.serial, 'BarSerial')
    292     self.assertEquals(dev_battor_p7_h1_t0.serial, 'BattOr0')
    293 
    294   def testBattOrDictMapping(self):
    295     map_dict = {'Phone1':'BattOr1', 'Phone2':'BattOr2', 'Phone3':'BattOr3'}
    296     a1 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
    297              'Phone1', serial_map=map_dict)
    298     a2 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
    299              'Phone2', serial_map=map_dict)
    300     a3 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
    301              'Phone3', serial_map=map_dict)
    302     self.assertEquals(a1, '/dev/ttyUSB1')
    303     self.assertEquals(a2, '/dev/ttyUSB2')
    304     self.assertEquals(a3, '/dev/ttyUSB3')
    305 
    306   def testBattOrDictFromFileMapping(self):
    307     try:
    308       map_dict = {'Phone1':'BattOr1', 'Phone2':'BattOr2', 'Phone3':'BattOr3'}
    309       curr_dir = os.path.dirname(os.path.realpath(__file__))
    310       filename = os.path.join(curr_dir, 'test', 'data', 'test_write_map.json')
    311       battor_device_mapping.WriteSerialMapFile(filename, map_dict)
    312       a1 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
    313                'Phone1', serial_map_file=filename)
    314       a2 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
    315                'Phone2', serial_map_file=filename)
    316       a3 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
    317                'Phone3', serial_map_file=filename)
    318     finally:
    319       os.remove(filename)
    320     self.assertEquals(a1, '/dev/ttyUSB1')
    321     self.assertEquals(a2, '/dev/ttyUSB2')
    322     self.assertEquals(a3, '/dev/ttyUSB3')
    323 
    324   def testReadSerialMapFile(self):
    325     curr_dir = os.path.dirname(os.path.realpath(__file__))
    326     map_dict = battor_device_mapping.ReadSerialMapFile(
    327         os.path.join(curr_dir, 'test', 'data', 'test_serial_map.json'))
    328     self.assertEquals(len(map_dict.keys()), 3)
    329     self.assertEquals(map_dict['Phone1'], 'BattOr1')
    330     self.assertEquals(map_dict['Phone2'], 'BattOr2')
    331     self.assertEquals(map_dict['Phone3'], 'BattOr3')
    332 
    333 original_PPTSM = find_usb_devices.GetAllPhysicalPortToSerialMaps
    334 original_PPTTM = find_usb_devices.GetAllPhysicalPortToTTYMaps
    335 original_GBL = battor_device_mapping.GetBattOrList
    336 original_GBNDM = find_usb_devices.GetBusNumberToDeviceTreeMap
    337 original_IB = battor_device_mapping.IsBattOr
    338 original_GBSM = battor_device_mapping.GetBattOrSerialNumbers
    339 
    340 def setup_battor_test(serial, tty, battor, bser=None):
    341   serial_mapper = mock.Mock(return_value=serial)
    342   tty_mapper = mock.Mock(return_value=tty)
    343   battor_lister = mock.Mock(return_value=battor)
    344   devtree = mock.Mock(return_value=None)
    345   is_battor = mock.Mock(side_effect=lambda x, y: x in battor)
    346   battor_serials = mock.Mock(return_value=bser)
    347   find_usb_devices.GetAllPhysicalPortToSerialMaps = serial_mapper
    348   find_usb_devices.GetAllPhysicalPortToTTYMaps = tty_mapper
    349   battor_device_mapping.GetBattOrList = battor_lister
    350   find_usb_devices.GetBusNumberToDeviceTreeMap = devtree
    351   battor_device_mapping.IsBattOr = is_battor
    352   battor_device_mapping.GetBattOrSerialNumbers = battor_serials
    353 
    354 class BattOrMappingTest(unittest.TestCase):
    355   def tearDown(self):
    356     find_usb_devices.GetAllPhysicalPortToSerialMaps = original_PPTSM
    357     find_usb_devices.GetAllPhysicalPortToTTYMaps = original_PPTTM
    358     battor_device_mapping.GetBattOrList = original_GBL
    359     find_usb_devices.GetBusNumberToDeviceTreeMap = original_GBNDM
    360     battor_device_mapping.IsBattOr = original_IB
    361     battor_device_mapping.GetBattOrSerialNumbers = original_GBSM
    362 
    363   def test_generate_serial_map(self):
    364     setup_battor_test([{1:'Phn1', 2:'Phn2', 3:'Phn3'},
    365                        {1:'Bat1', 2:'Bat2', 3:'Bat3'}],
    366                       [{},
    367                        {1:'ttyUSB0', 2:'ttyUSB1', 3:'ttyUSB2'}],
    368                       ['ttyUSB0', 'ttyUSB1', 'ttyUSB2'],
    369                       ['Bat1', 'Bat2', 'Bat3'])
    370     result = battor_device_mapping.GenerateSerialMap()
    371     self.assertEqual(len(result), 3)
    372     self.assertEqual(result['Phn1'], 'Bat1')
    373     self.assertEqual(result['Phn2'], 'Bat2')
    374     self.assertEqual(result['Phn3'], 'Bat3')
    375 
    376 
    377 if __name__ == "__main__":
    378   logging.getLogger().setLevel(logging.DEBUG)
    379   unittest.main(verbosity=2)
    380