• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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                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
369if __name__ == '__main__':
370    v = VhalTest(vhal_consts_2_0.vhal_types_2_0)
371    v.runTests()
372