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_1
     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_1.VEHICLEPROPERTY_OBD2_LIVE_FRAME,
     53                     vhal_consts_2_1.VEHICLEPROPERTY_OBD2_FREEZE_FRAME,
     54                     vhal_consts_2_1.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO,
     55                     vhal_consts_2_1.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR,
     56                     vhal_consts_2_1.VEHICLEPROPERTY_VEHICLE_MAP_SERVICE,
     57                     vhal_consts_2_1.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_1.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             if valType in self._types.TYPE_STRING:
    113                 value = rxMsg.value[0].string_value
    114             elif valType in self._types.TYPE_BYTES:
    115                 value = rxMsg.value[0].bytes_value
    116             elif valType == vhal_consts_2_1.VEHICLEPROPERTYTYPE_BOOLEAN:
    117                 value = rxMsg.value[0].int32_values[0]
    118             elif valType in self._types.TYPE_INT32:
    119                 value = rxMsg.value[0].int32_values[0]
    120             elif valType in self._types.TYPE_INT64:
    121                 value = rxMsg.value[0].int64_values[0]
    122             elif valType in self._types.TYPE_FLOAT:
    123                 value = rxMsg.value[0].float_values[0]
    124                 # Truncate float to 5 decimal places
    125                 value = "%.5f" % value
    126                 value = float(value)
    127             else:
    128                 self._log.error("getValueFromMsg:  valType=0x%X is not handled", valType)
    129                 value = None
    130         return value
    131 
    132     # Helper function to receive a message and validate the type and status
    133     #   retVal = 1 if no errors
    134     #   retVal = 0 if errors detected
    135     def _rxMsgAndValidate(self, expectedType, expectedStatus):
    136         retVal = 1
    137         rxMsg = self._vhal.rxMsg()
    138         if rxMsg.msg_type != expectedType:
    139             self._log.error("rxMsg Type expected: 0x%X, received: 0x%X", expectedType, rxMsg.msg_type)
    140             retVal = 0
    141         if rxMsg.status != expectedStatus:
    142             self._log.error("rxMsg Status expected: 0x%X, received: 0x%X", expectedStatus, rxMsg.status)
    143             retVal = 0
    144         return rxMsg, retVal
    145 
    146     # Calls getConfig() on each individual property ID and verifies it matches with the config
    147     #   received in getConfigAll()
    148     def testGetConfig(self):
    149         self._log.info("Starting testGetConfig...")
    150         for cfg in self._configs:
    151             self._log.debug("  Getting config for propId=0x%X", cfg.prop)
    152             self._vhal.getConfig(cfg.prop)
    153             rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP,
    154                                                    VehicleHalProto_pb2.RESULT_OK)
    155             if retVal:
    156                 if rxMsg.config[0] != cfg:
    157                     self._log.error("testGetConfig failed.  prop=0x%X, expected:\n%s\nreceived:\n%s",
    158                                cfg.prop, str(cfg), str(rxMsg.config))
    159         self._log.info("  Finished testGetConfig!")
    160 
    161     # Calls getConfig() on invalid property ID and verifies it generates an error
    162     def testGetBadConfig(self):
    163         self._log.info("Starting testGetBadConfig...")
    164         for prop in self._badProps:
    165             self._log.debug("  Testing bad propId=0x%X", prop)
    166             self._vhal.getConfig(prop)
    167             rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP,
    168                                                    VehicleHalProto_pb2.ERROR_INVALID_PROPERTY)
    169             if retVal:
    170                 for cfg in rxMsg.config:
    171                     self._log.error("testGetBadConfig  prop=0x%X, expected:None, received:\n%s",
    172                                     cfg.prop, str(rxMsg.config))
    173         self._log.info("  Finished testGetBadConfig!")
    174 
    175     def testGetPropertyAll(self):
    176         self._log.info("Starting testGetPropertyAll...")
    177         self._vhal.getPropertyAll()
    178         rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_ALL_RESP,
    179                                                VehicleHalProto_pb2.RESULT_OK)
    180         if retVal == 0:
    181             self._log.error("testGetPropertyAll:  Failed to receive proper rxMsg")
    182 
    183         # TODO: Finish writing this test.  What should we be testing, anyway?
    184 
    185         self._log.info("  Finished testGetPropertyAll!")
    186 
    187     def testGetSet(self):
    188         self._log.info("Starting testGetSet()...")
    189         for cfg in self._configs:
    190             if cfg.prop in self._skipProps:
    191                 # Skip properties that cannot be handled properly by this test.
    192                 self._log.warning("  Skipping propId=0x%X", cfg.prop)
    193                 continue
    194 
    195             areas = cfg.supported_areas
    196             idx = -1
    197             while (idx == -1) | (areas != 0):
    198                 idx += 1
    199                 # Get the area to test
    200                 area = areas & (areas -1)
    201                 area ^= areas
    202 
    203                 # Remove the area from areas
    204                 areas ^= area
    205 
    206                 self._log.debug("  Testing propId=0x%X, area=0x%X", cfg.prop, area)
    207 
    208                 # Get the current value
    209                 self._vhal.getProperty(cfg.prop, area)
    210                 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP,
    211                                                        VehicleHalProto_pb2.RESULT_OK)
    212 
    213                 # Save the original value
    214                 origValue = self._getValueFromMsg(rxMsg)
    215                 if origValue == None:
    216                     self._log.error("testGetSet:  Could not get value for prop=0x%X, area=0x%X",
    217                                     cfg.prop, area)
    218                     continue
    219 
    220                 # Generate the test value
    221                 testValue = self._generateTestValue(cfg, idx, origValue)
    222                 if testValue == None:
    223                     self._log.error("testGetSet:  Cannot generate test value for prop=0x%X, area=0x%X",
    224                                     cfg.prop, area)
    225                     continue
    226 
    227                 # Send the new value
    228                 self._vhal.setProperty(cfg.prop, area, testValue)
    229                 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP,
    230                                                         VehicleHalProto_pb2.RESULT_OK)
    231 
    232                 # Get the new value and verify it
    233                 self._vhal.getProperty(cfg.prop, area)
    234                 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP,
    235                                                        VehicleHalProto_pb2.RESULT_OK)
    236                 newValue = self._getValueFromMsg(rxMsg)
    237                 if newValue != testValue:
    238                     self._log.error("testGetSet: set failed for propId=0x%X, area=0x%X", cfg.prop, area)
    239                     print("testValue= ", testValue, "newValue= ", newValue)
    240                     continue
    241 
    242                 # Reset the value to what it was before
    243                 self._vhal.setProperty(cfg.prop, area, origValue)
    244                 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP,
    245                                                        VehicleHalProto_pb2.RESULT_OK)
    246         self._log.info("  Finished testGetSet()!")
    247 
    248     def testGetBadProperty(self):
    249         self._log.info("Starting testGetBadProperty()...")
    250         for prop in self._badProps:
    251             self._log.debug("  Testing bad propId=0x%X", prop)
    252             self._vhal.getProperty(prop, 0)
    253             rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP,
    254                                                    VehicleHalProto_pb2.ERROR_INVALID_PROPERTY)
    255             if retVal:
    256                 for value in rxMsg.value:
    257                     self._log.error("testGetBadProperty  prop=0x%X, expected:None, received:\n%s",
    258                                     prop, str(rxMsg))
    259         self._log.info("  Finished testGetBadProperty()!")
    260 
    261     def testSetBadProperty(self):
    262         self._log.info("Starting testSetBadProperty()...")
    263         area = 1
    264         value = 100
    265         for prop in self._badProps:
    266             self._log.debug("  Testing bad propId=0x%X", prop)
    267             area = area + 1
    268             value = value + 1
    269             try:
    270                 self._vhal.setProperty(prop, area, value)
    271                 self._log.error("testGetBadProperty failed.  prop=0x%X, area=0x%X, value=%d",
    272                                 prop, area, value)
    273             except ValueError as e:
    274                 # Received expected error
    275                 pass
    276         self._log.info("  Finished testSetBadProperty()!")
    277 
    278     def runTests(self):
    279         self.testGetConfig()
    280         self.testGetBadConfig()
    281         self.testGetPropertyAll()
    282         self.testGetSet()
    283         self.testGetBadProperty()
    284         self.testSetBadProperty()
    285         # Add new tests here to be run
    286 
    287 
    288     # Valid logLevels:
    289     #   CRITICAL    50
    290     #   ERRROR      40
    291     #   WARNING     30
    292     #   INFO        20
    293     #   DEBUG       10
    294     #   NOTSET      0
    295     def __init__(self, types, logLevel=20):
    296         self._types = types
    297         # Configure the logger
    298         logging.basicConfig()
    299         self._log = logging.getLogger('vhal_emulator_test')
    300         self._log.setLevel(logLevel)
    301         # Start the VHAL Emulator
    302         self._vhal = vhal_emulator.Vhal(types)
    303         # Get the list of configs
    304         self._vhal.getConfigAll()
    305         self._configs = self._vhal.rxMsg().config
    306 
    307 if __name__ == '__main__':
    308     v = VhalTest(vhal_consts_2_1.vhal_types_2_0)
    309     v.runTests()
    310