Home | History | Annotate | Download | only in emulator
      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 #
      4 # Copyright 2017 Google Inc.
      5 #
      6 # Licensed under the Apache License, Version 2.0 (the "License");
      7 # you may not use this file except in compliance with the License.
      8 # You may obtain a copy of the License at
      9 #
     10 #   http://www.apache.org/licenses/LICENSE-2.0
     11 #
     12 # Unless required by applicable law or agreed to in writing, software
     13 # distributed under the License is distributed on an "AS IS" BASIS,
     14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 # See the License for the specific language governing permissions and
     16 # limitations under the License.
     17 #
     18 
     19 """
     20     This module tests the Vehicle HAL using adb socket.
     21 
     22     Protocol Buffer:
     23         This module relies on VehicleHalProto_pb2.py being in sync with the protobuf in the VHAL.
     24         If the VehicleHalProto.proto file has changed, re-generate the python version using
     25         a command of the form:
     26             protoc -I=<proto_dir> --python_out=<out_dir> <proto_dir>/VehicleHalProto.proto
     27         For example:
     28             protoDir=~/android/master/hardware/interfaces/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto
     29             outDir=~/android/master/packages/services/Car/tools/emulator
     30             protoc -I=$protoDir --python_out=$outDir $protoDir/VehicleHalProto.proto
     31 """
     32 
     33 from __future__ import print_function
     34 
     35 # Suppress .pyc files
     36 import sys
     37 sys.dont_write_bytecode = True
     38 
     39 import VehicleHalProto_pb2
     40 import vhal_consts_2_0
     41 import vhal_emulator
     42 import logging
     43 
     44 class VhalTest:
     45     # Global vars
     46     _badProps = [0, 0x3FFFFFFF]     # List of bad properties to try for negative tests
     47     _configs = 0                    # List of configs from DUT
     48     _log = 0                        # Logger module
     49     _vhal = 0                       # Handle to VHAL object that communicates over socket to DUT
     50     # TODO: b/38203109 - Fix OBD2 values, implement handling for complex properties
     51     _skipProps = [
     52                     vhal_consts_2_0.VEHICLEPROPERTY_OBD2_LIVE_FRAME,
     53                     vhal_consts_2_0.VEHICLEPROPERTY_OBD2_FREEZE_FRAME,
     54                     vhal_consts_2_0.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO,
     55                     vhal_consts_2_0.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR,
     56                     vhal_consts_2_0.VEHICLEPROPERTY_VEHICLE_MAP_SERVICE,
     57                     vhal_consts_2_0.VEHICLEPROPERTY_WHEEL_TICK,     # Need to support complex properties
     58                     0x21E00666      # FakeDataControllingProperty - an internal test property
     59                  ]
     60 
     61     def _getMidpoint(self, minVal, maxVal):
     62         retVal =  minVal + (maxVal - minVal)/2
     63         return retVal
     64 
     65     # Generates a test value based on the config
     66     def _generateTestValue(self, cfg, idx, origValue):
     67         valType = cfg.value_type
     68         if valType in self._types.TYPE_STRING:
     69             testValue = "test string"
     70         elif valType in self._types.TYPE_BYTES:
     71             # Generate array of integers counting from 0
     72             testValue = list(range(len(origValue)))
     73         elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
     74             testValue = origValue ^ 1
     75         elif valType in self._types.TYPE_INT32:
     76             try:
     77                 testValue = self._getMidpoint(cfg.area_configs[idx].min_int32_value,
     78                                               cfg.area_configs[idx].max_int32_value)
     79             except:
     80                 # min/max values aren't set.  Set a hard-coded value
     81                 testValue = 123
     82         elif valType in self._types.TYPE_INT64:
     83             try:
     84                 testValue = self._getMidpoint(cfg.area_configs[idx].min_int64_value,
     85                                               cfg.area_configs[idx].max_int64_value)
     86             except:
     87                 # min/max values aren't set.  Set a large hard-coded value
     88                 testValue = 1 << 50
     89         elif valType in self._types.TYPE_FLOAT:
     90             try:
     91                 testValue = self._getMidpoint(cfg.area_configs[idx].min_float_value,
     92                                               cfg.area_configs[idx].max_float_value)
     93             except:
     94                 # min/max values aren't set.  Set a hard-coded value
     95                 testValue = 123.456
     96             # Truncate float to 5 decimal places
     97             testValue = "%.5f" % testValue
     98             testValue = float(testValue)
     99         else:
    100             self._log.error("generateTestValue:  valType=0x%X is not handled", valType)
    101             testValue = None
    102         return testValue
    103 
    104     # Helper function to extract values array from rxMsg
    105     def _getValueFromMsg(self, rxMsg):
    106         # Check to see only one property value is returned
    107         if len(rxMsg.value) != 1:
    108             self._log.error("getValueFromMsg:  Received invalid value")
    109             value = None
    110         else:
    111             valType = rxMsg.value[0].value_type
    112             try:
    113                 if valType in self._types.TYPE_STRING:
    114                     value = rxMsg.value[0].string_value
    115                 elif valType in self._types.TYPE_BYTES:
    116                     value = rxMsg.value[0].bytes_value
    117                 elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
    118                     value = rxMsg.value[0].int32_values[0]
    119                 elif valType in self._types.TYPE_INT32:
    120                     value = rxMsg.value[0].int32_values[0]
    121                 elif valType in self._types.TYPE_INT64:
    122                     value = rxMsg.value[0].int64_values[0]
    123                 elif valType in self._types.TYPE_FLOAT:
    124                     value = rxMsg.value[0].float_values[0]
    125                     # Truncate float to 5 decimal places
    126                     value = "%.5f" % value
    127                     value = float(value)
    128                 else:
    129                     self._log.error("getValueFromMsg:  valType=0x%X is not handled", valType)
    130                     value = None
    131             except IndexError:
    132               self._log.error("getValueFromMsg:  Received malformed message: %s", str(rxMsg))
    133               value = None
    134         return value
    135 
    136     def _validateVmsMessage(self, rxMsg):
    137         return (len(rxMsg.value) == 1 and rxMsg.value[0].value_type in self._types.TYPE_MIXED and
    138             len(rxMsg.value[0].int32_values) > 0 and
    139             vhal_consts_2_0.VMSMESSAGETYPE_SUBSCRIBE <= rxMsg.value[0].int32_values[0]
    140                 <= vhal_consts_2_0.VMSMESSAGETYPE_LAST_VMS_MESSAGE_TYPE)
    141 
    142     def _getVmsMessageTypeFromMsg(self, rxMsg):
    143         if self._validateVmsMessage(rxMsg):
    144             value = rxMsg.value[0].int32_values[
    145                 vhal_consts_2_0.VMSBASEMESSAGEINTEGERVALUESINDEX_MESSAGE_TYPE]
    146         else:
    147             self._log.error("getVmsMessageTypeFromMsg:  Received invalid message")
    148             value = None
    149         return value
    150 
    151     # Helper function to receive a message and validate the type and status
    152     #   retVal = 1 if no errors
    153     #   retVal = 0 if errors detected
    154     def _rxMsgAndValidate(self, expectedType, expectedStatus):
    155         retVal = 1
    156         rxMsg = self._vhal.rxMsg()
    157         if rxMsg.msg_type != expectedType:
    158             self._log.error("rxMsg Type expected: 0x%X, received: 0x%X", expectedType, rxMsg.msg_type)
    159             retVal = 0
    160         if rxMsg.status != expectedStatus:
    161             self._log.error("rxMsg Status expected: 0x%X, received: 0x%X", expectedStatus, rxMsg.status)
    162             retVal = 0
    163         return rxMsg, retVal
    164 
    165     # Calls getConfig() on each individual property ID and verifies it matches with the config
    166     #   received in getConfigAll()
    167     def testGetConfig(self):
    168         self._log.info("Starting testGetConfig...")
    169         for cfg in self._configs:
    170             self._log.debug("  Getting config for propId=0x%X", cfg.prop)
    171             self._vhal.getConfig(cfg.prop)
    172             rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP,
    173                                                    VehicleHalProto_pb2.RESULT_OK)
    174             if retVal:
    175                 if rxMsg.config[0] != cfg:
    176                     self._log.error("testGetConfig failed.  prop=0x%X, expected:\n%s\nreceived:\n%s",
    177                                cfg.prop, str(cfg), str(rxMsg.config))
    178         self._log.info("  Finished testGetConfig!")
    179 
    180     # Calls getConfig() on invalid property ID and verifies it generates an error
    181     def testGetBadConfig(self):
    182         self._log.info("Starting testGetBadConfig...")
    183         for prop in self._badProps:
    184             self._log.debug("  Testing bad propId=0x%X", prop)
    185             self._vhal.getConfig(prop)
    186             rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP,
    187                                                    VehicleHalProto_pb2.ERROR_INVALID_PROPERTY)
    188             if retVal:
    189                 for cfg in rxMsg.config:
    190                     self._log.error("testGetBadConfig  prop=0x%X, expected:None, received:\n%s",
    191                                     cfg.prop, str(rxMsg.config))
    192         self._log.info("  Finished testGetBadConfig!")
    193 
    194     def testGetPropertyAll(self):
    195         self._log.info("Starting testGetPropertyAll...")
    196         self._vhal.getPropertyAll()
    197         rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_ALL_RESP,
    198                                                VehicleHalProto_pb2.RESULT_OK)
    199         if retVal == 0:
    200             self._log.error("testGetPropertyAll:  Failed to receive proper rxMsg")
    201 
    202         # TODO: Finish writing this test.  What should we be testing, anyway?
    203 
    204         self._log.info("  Finished testGetPropertyAll!")
    205 
    206     def testGetSet(self):
    207         self._log.info("Starting testGetSet()...")
    208         for cfg in self._configs:
    209             if cfg.prop in self._skipProps:
    210                 # Skip properties that cannot be handled properly by this test.
    211                 self._log.warning("  Skipping propId=0x%X", cfg.prop)
    212                 continue
    213 
    214             areas = cfg.supported_areas
    215             idx = -1
    216             while (idx == -1) | (areas != 0):
    217                 idx += 1
    218                 # Get the area to test
    219                 area = areas & (areas -1)
    220                 area ^= areas
    221 
    222                 # Remove the area from areas
    223                 areas ^= area
    224 
    225                 self._log.debug("  Testing propId=0x%X, area=0x%X", cfg.prop, area)
    226 
    227                 # Get the current value
    228                 self._vhal.getProperty(cfg.prop, area)
    229                 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP,
    230                                                        VehicleHalProto_pb2.RESULT_OK)
    231 
    232                 # Save the original value
    233                 origValue = self._getValueFromMsg(rxMsg)
    234                 if origValue == None:
    235                     self._log.error("testGetSet:  Could not get value for prop=0x%X, area=0x%X",
    236                                     cfg.prop, area)
    237                     continue
    238 
    239                 # Generate the test value
    240                 testValue = self._generateTestValue(cfg, idx, origValue)
    241                 if testValue == None:
    242                     self._log.error("testGetSet:  Cannot generate test value for prop=0x%X, area=0x%X",
    243                                     cfg.prop, area)
    244                     continue
    245 
    246                 # Send the new value
    247                 self._vhal.setProperty(cfg.prop, area, testValue)
    248                 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP,
    249                                                         VehicleHalProto_pb2.RESULT_OK)
    250 
    251                 # Get the new value and verify it
    252                 self._vhal.getProperty(cfg.prop, area)
    253                 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP,
    254                                                        VehicleHalProto_pb2.RESULT_OK)
    255                 newValue = self._getValueFromMsg(rxMsg)
    256                 if newValue != testValue:
    257                     self._log.error("testGetSet: set failed for propId=0x%X, area=0x%X", cfg.prop, area)
    258                     print("testValue= ", testValue, "newValue= ", newValue)
    259                     continue
    260 
    261                 # Reset the value to what it was before
    262                 self._vhal.setProperty(cfg.prop, area, origValue)
    263                 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP,
    264                                                        VehicleHalProto_pb2.RESULT_OK)
    265         self._log.info("  Finished testGetSet()!")
    266 
    267     def testGetBadProperty(self):
    268         self._log.info("Starting testGetBadProperty()...")
    269         for prop in self._badProps:
    270             self._log.debug("  Testing bad propId=0x%X", prop)
    271             self._vhal.getProperty(prop, 0)
    272             rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP,
    273                                                    VehicleHalProto_pb2.ERROR_INVALID_PROPERTY)
    274             if retVal:
    275                 for value in rxMsg.value:
    276                     self._log.error("testGetBadProperty  prop=0x%X, expected:None, received:\n%s",
    277                                     prop, str(rxMsg))
    278         self._log.info("  Finished testGetBadProperty()!")
    279 
    280     def testSetBadProperty(self):
    281         self._log.info("Starting testSetBadProperty()...")
    282         area = 1
    283         value = 100
    284         for prop in self._badProps:
    285             self._log.debug("  Testing bad propId=0x%X", prop)
    286             area = area + 1
    287             value = value + 1
    288             try:
    289                 self._vhal.setProperty(prop, area, value)
    290                 self._log.error("testGetBadProperty failed.  prop=0x%X, area=0x%X, value=%d",
    291                                 prop, area, value)
    292             except ValueError as e:
    293                 # Received expected error
    294                 pass
    295         self._log.info("  Finished testSetBadProperty()!")
    296 
    297     def testGetVmsAvailability(self):
    298         self._log.info("Starting testVms()...")
    299 
    300         # Request the availability from the VmsCore.
    301         value = {'int32_values' : [vhal_consts_2_0.VMSMESSAGETYPE_AVAILABILITY_REQUEST] }
    302         self._vhal.setProperty(
    303             vhal_consts_2_0.VEHICLEPROPERTY_VEHICLE_MAP_SERVICE, 0, value)
    304 
    305         rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP,
    306                                                VehicleHalProto_pb2.RESULT_OK)
    307 
    308         # The Vms Core should immediately respond
    309         rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_ASYNC,
    310                                                VehicleHalProto_pb2.RESULT_OK)
    311 
    312         if self._getVmsMessageTypeFromMsg(rxMsg) != vhal_consts_2_0.VMSMESSAGETYPE_AVAILABILITY_RESPONSE:
    313             self._log.error("testVms: VmsCore did not respond with AvailabilityResponse: %s", str(rxMsg))
    314 
    315 
    316         # Test that we can get the property on command
    317         self._vhal.getProperty(
    318             vhal_consts_2_0.VEHICLEPROPERTY_VEHICLE_MAP_SERVICE, 0)
    319         rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP,
    320                                                VehicleHalProto_pb2.RESULT_OK)
    321 
    322         if self._getVmsMessageTypeFromMsg(rxMsg) != vhal_consts_2_0.VMSMESSAGETYPE_AVAILABILITY_RESPONSE:
    323             self._log.error("testVms: VmsCore did not respond with AvailabilityResponse: %s", str(rxMsg))
    324         else:
    325             # Parse Availability Response
    326             layers = rxMsg.value[0].int32_values[
    327                 vhal_consts_2_0.VMSAVAILABILITYSTATEINTEGERVALUESINDEX_NUMBER_OF_ASSOCIATED_LAYERS]
    328             index = vhal_consts_2_0.VMSAVAILABILITYSTATEINTEGERVALUESINDEX_LAYERS_START
    329             numPublishersIndex = vhal_consts_2_0.VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_VERSION
    330             self._log.info("testVms: %d available layers", layers)
    331             for layer in xrange(layers):
    332                 self._log.info("testVms: Available layer: %s",
    333                                rxMsg.value[0].int32_values[index:index+numPublishersIndex])
    334                 index += numPublishersIndex + 1 + rxMsg.value[0].int32_values[index+numPublishersIndex]
    335 
    336             if len(rxMsg.value[0].int32_values) != index:
    337               self._log.error("testVms: Malformed AvailabilityResponse: index: %d %s", index, str(rxMsg))
    338 
    339     def runTests(self):
    340         self.testGetConfig()
    341         self.testGetBadConfig()
    342         self.testGetPropertyAll()
    343         self.testGetSet()
    344         self.testGetBadProperty()
    345         self.testSetBadProperty()
    346         self.testGetVmsAvailability()
    347         # Add new tests here to be run
    348 
    349 
    350     # Valid logLevels:
    351     #   CRITICAL    50
    352     #   ERRROR      40
    353     #   WARNING     30
    354     #   INFO        20
    355     #   DEBUG       10
    356     #   NOTSET      0
    357     def __init__(self, types, logLevel=20):
    358         self._types = types
    359         # Configure the logger
    360         logging.basicConfig()
    361         self._log = logging.getLogger('vhal_emulator_test')
    362         self._log.setLevel(logLevel)
    363         # Start the VHAL Emulator
    364         self._vhal = vhal_emulator.Vhal(types)
    365         # Get the list of configs
    366         self._vhal.getConfigAll()
    367         self._configs = self._vhal.rxMsg().config
    368 
    369 if __name__ == '__main__':
    370     v = VhalTest(vhal_consts_2_0.vhal_types_2_0)
    371     v.runTests()
    372