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