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