1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# 4# Copyright 2017 The Android Open Source Project 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_BUILD_TOP/hardware/interfaces/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto 29 outDir=$ANDROID_BUILD_TOP/packages/services/Car/tools/emulator 30 protoc -I=$protoDir --python_out=$outDir $protoDir/VehicleHalProto.proto 31""" 32 33from __future__ import print_function 34 35# Suppress .pyc files 36import sys 37sys.dont_write_bytecode = True 38 39import VehicleHalProto_pb2 40import vhal_consts_2_0 41import vhal_emulator 42import logging 43 44class 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 elif valType in self._types.TYPE_MIXED: 129 # Quick stub to unblock most tests 130 # Todo: proper implement according to VehiclePropertyType in types.hal 131 value = rxMsg.value[0].float_values[0] 132 value = "%.5f" % value 133 value = float(value) 134 else: 135 self._log.error("getValueFromMsg: valType=0x%X is not handled", valType) 136 value = None 137 except IndexError: 138 self._log.error("getValueFromMsg: Received malformed message: %s", str(rxMsg)) 139 value = None 140 return value 141 142 def _validateVmsMessage(self, rxMsg): 143 return (len(rxMsg.value) == 1 and rxMsg.value[0].value_type in self._types.TYPE_MIXED and 144 len(rxMsg.value[0].int32_values) > 0 and 145 vhal_consts_2_0.VMSMESSAGETYPE_SUBSCRIBE <= rxMsg.value[0].int32_values[0] 146 <= vhal_consts_2_0.VMSMESSAGETYPE_LAST_VMS_MESSAGE_TYPE) 147 148 def _getVmsMessageTypeFromMsg(self, rxMsg): 149 if self._validateVmsMessage(rxMsg): 150 value = rxMsg.value[0].int32_values[ 151 vhal_consts_2_0.VMSBASEMESSAGEINTEGERVALUESINDEX_MESSAGE_TYPE] 152 else: 153 self._log.error("getVmsMessageTypeFromMsg: Received invalid message") 154 value = None 155 return value 156 157 # Helper function to receive a message and validate the type and status 158 # retVal = 1 if no errors 159 # retVal = 0 if errors detected 160 def _rxMsgAndValidate(self, expectedType, expectedStatus): 161 retVal = 1 162 rxMsg = self._vhal.rxMsg() 163 if rxMsg.msg_type != expectedType: 164 self._log.error("rxMsg Type expected: 0x%X, received: 0x%X", expectedType, rxMsg.msg_type) 165 retVal = 0 166 if rxMsg.status != expectedStatus: 167 self._log.error("rxMsg Status expected: 0x%X, received: 0x%X", expectedStatus, rxMsg.status) 168 retVal = 0 169 return rxMsg, retVal 170 171 # Calls getConfig() on each individual property ID and verifies it matches with the config 172 # received in getConfigAll() 173 def testGetConfig(self): 174 self._log.info("Starting testGetConfig...") 175 for cfg in self._configs: 176 self._log.debug(" Getting config for propId=0x%X", cfg.prop) 177 self._vhal.getConfig(cfg.prop) 178 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP, 179 VehicleHalProto_pb2.RESULT_OK) 180 if retVal: 181 if rxMsg.config[0] != cfg: 182 self._log.error("testGetConfig failed. prop=0x%X, expected:\n%s\nreceived:\n%s", 183 cfg.prop, str(cfg), str(rxMsg.config)) 184 self._log.info(" Finished testGetConfig!") 185 186 # Calls getConfig() on invalid property ID and verifies it generates an error 187 def testGetBadConfig(self): 188 self._log.info("Starting testGetBadConfig...") 189 for prop in self._badProps: 190 self._log.debug(" Testing bad propId=0x%X", prop) 191 self._vhal.getConfig(prop) 192 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP, 193 VehicleHalProto_pb2.ERROR_INVALID_PROPERTY) 194 if retVal: 195 for cfg in rxMsg.config: 196 self._log.error("testGetBadConfig prop=0x%X, expected:None, received:\n%s", 197 cfg.prop, str(rxMsg.config)) 198 self._log.info(" Finished testGetBadConfig!") 199 200 def testGetPropertyAll(self): 201 self._log.info("Starting testGetPropertyAll...") 202 self._vhal.getPropertyAll() 203 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_ALL_RESP, 204 VehicleHalProto_pb2.RESULT_OK) 205 if retVal == 0: 206 self._log.error("testGetPropertyAll: Failed to receive proper rxMsg") 207 208 # TODO: Finish writing this test. What should we be testing, anyway? 209 210 self._log.info(" Finished testGetPropertyAll!") 211 212 def testGetSet(self): 213 self._log.info("Starting testGetSet()...") 214 for cfg in self._configs: 215 if cfg.prop in self._skipProps: 216 # Skip properties that cannot be handled properly by this test. 217 self._log.warning(" Skipping propId=0x%X", cfg.prop) 218 continue 219 220 areas = cfg.supported_areas 221 idx = -1 222 while (idx == -1) | (areas != 0): 223 idx += 1 224 # Get the area to test 225 area = areas & (areas -1) 226 area ^= areas 227 228 # Remove the area from areas 229 areas ^= area 230 231 self._log.debug(" Testing propId=0x%X, area=0x%X", cfg.prop, area) 232 233 # Get the current value 234 self._vhal.getProperty(cfg.prop, area) 235 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP, 236 VehicleHalProto_pb2.RESULT_OK) 237 238 # Save the original value 239 origValue = self._getValueFromMsg(rxMsg) 240 if origValue == None: 241 self._log.error("testGetSet: Could not get value for prop=0x%X, area=0x%X", 242 cfg.prop, area) 243 continue 244 245 # Generate the test value 246 testValue = self._generateTestValue(cfg, idx, origValue) 247 if testValue == None: 248 self._log.error("testGetSet: Cannot generate test value for prop=0x%X, area=0x%X", 249 cfg.prop, area) 250 continue 251 252 # Send the new value 253 self._vhal.setProperty(cfg.prop, area, testValue) 254 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP, 255 VehicleHalProto_pb2.RESULT_OK) 256 257 # Get the new value and verify it 258 self._vhal.getProperty(cfg.prop, area) 259 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP, 260 VehicleHalProto_pb2.RESULT_OK) 261 newValue = self._getValueFromMsg(rxMsg) 262 if newValue != testValue: 263 self._log.error("testGetSet: set failed for propId=0x%X, area=0x%X", cfg.prop, area) 264 print("testValue= ", testValue, "newValue= ", newValue) 265 continue 266 267 # Reset the value to what it was before 268 self._vhal.setProperty(cfg.prop, area, origValue) 269 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP, 270 VehicleHalProto_pb2.RESULT_OK) 271 self._log.info(" Finished testGetSet()!") 272 273 def testGetBadProperty(self): 274 self._log.info("Starting testGetBadProperty()...") 275 for prop in self._badProps: 276 self._log.debug(" Testing bad propId=0x%X", prop) 277 self._vhal.getProperty(prop, 0) 278 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP, 279 VehicleHalProto_pb2.ERROR_INVALID_PROPERTY) 280 if retVal: 281 for value in rxMsg.value: 282 self._log.error("testGetBadProperty prop=0x%X, expected:None, received:\n%s", 283 prop, str(rxMsg)) 284 self._log.info(" Finished testGetBadProperty()!") 285 286 def testSetBadProperty(self): 287 self._log.info("Starting testSetBadProperty()...") 288 area = 1 289 value = 100 290 for prop in self._badProps: 291 self._log.debug(" Testing bad propId=0x%X", prop) 292 area = area + 1 293 value = value + 1 294 try: 295 self._vhal.setProperty(prop, area, value) 296 self._log.error("testGetBadProperty failed. prop=0x%X, area=0x%X, value=%d", 297 prop, area, value) 298 except ValueError as e: 299 # Received expected error 300 pass 301 self._log.info(" Finished testSetBadProperty()!") 302 303 def testGetVmsAvailability(self): 304 self._log.info("Starting testVms()...") 305 306 # Request the availability from the VmsCore. 307 value = {'int32_values' : [vhal_consts_2_0.VMSMESSAGETYPE_AVAILABILITY_REQUEST] } 308 self._vhal.setProperty( 309 vhal_consts_2_0.VEHICLEPROPERTY_VEHICLE_MAP_SERVICE, 0, value) 310 311 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP, 312 VehicleHalProto_pb2.RESULT_OK) 313 314 # The Vms Core should immediately respond 315 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_ASYNC, 316 VehicleHalProto_pb2.RESULT_OK) 317 318 if self._getVmsMessageTypeFromMsg(rxMsg) != vhal_consts_2_0.VMSMESSAGETYPE_AVAILABILITY_RESPONSE: 319 self._log.error("testVms: VmsCore did not respond with AvailabilityResponse: %s", str(rxMsg)) 320 321 322 # Test that we can get the property on command 323 self._vhal.getProperty( 324 vhal_consts_2_0.VEHICLEPROPERTY_VEHICLE_MAP_SERVICE, 0) 325 rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP, 326 VehicleHalProto_pb2.RESULT_OK) 327 328 if self._getVmsMessageTypeFromMsg(rxMsg) != vhal_consts_2_0.VMSMESSAGETYPE_AVAILABILITY_RESPONSE: 329 self._log.error("testVms: VmsCore did not respond with AvailabilityResponse: %s", str(rxMsg)) 330 else: 331 # Parse Availability Response 332 layers = rxMsg.value[0].int32_values[ 333 vhal_consts_2_0.VMSAVAILABILITYSTATEINTEGERVALUESINDEX_NUMBER_OF_ASSOCIATED_LAYERS] 334 index = vhal_consts_2_0.VMSAVAILABILITYSTATEINTEGERVALUESINDEX_LAYERS_START 335 numPublishersIndex = vhal_consts_2_0.VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_VERSION 336 self._log.info("testVms: %d available layers", layers) 337 for layer in xrange(layers): 338 self._log.info("testVms: Available layer: %s", 339 rxMsg.value[0].int32_values[index:index+numPublishersIndex]) 340 index += numPublishersIndex + 1 + rxMsg.value[0].int32_values[index+numPublishersIndex] 341 342 if len(rxMsg.value[0].int32_values) != index: 343 self._log.error("testVms: Malformed AvailabilityResponse: index: %d %s", index, str(rxMsg)) 344 345 def runTests(self): 346 self.testGetConfig() 347 self.testGetBadConfig() 348 self.testGetPropertyAll() 349 self.testGetSet() 350 self.testGetBadProperty() 351 self.testSetBadProperty() 352 self.testGetVmsAvailability() 353 # Add new tests here to be run 354 355 356 # Valid logLevels: 357 # CRITICAL 50 358 # ERRROR 40 359 # WARNING 30 360 # INFO 20 361 # DEBUG 10 362 # NOTSET 0 363 def __init__(self, types, logLevel=20): 364 self._types = types 365 # Configure the logger 366 logging.basicConfig() 367 self._log = logging.getLogger('vhal_emulator_test') 368 self._log.setLevel(logLevel) 369 # Start the VHAL Emulator 370 self._vhal = vhal_emulator.Vhal(types) 371 # Get the list of configs 372 self._vhal.getConfigAll() 373 self._configs = self._vhal.rxMsg().config 374 375if __name__ == '__main__': 376 v = VhalTest(vhal_consts_2_0.vhal_types_2_0) 377 v.runTests() 378