1#!/usr/bin/env python 2# 3# Copyright 2017 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18# Create the configuration files for a hidl hal test. 19# This script copy a template which contains Android.mk and AndroidTest.xml 20# files under test/vts-testcases/hal/ based on the hal package name. 21 22import datetime 23import os 24import sys 25 26from build.vts_spec_parser import VtsSpecParser 27from xml.dom import minidom 28from xml.etree import cElementTree as ET 29from xml.sax.saxutils import unescape 30 31VTS_TEST_CASE_PATH = 'test/vts-testcase/hal' 32HAL_INTERFACE_PATH = 'hardware/interfaces' 33HAL_TRACE_PATH = 'test/vts-testcase/hal-trace' 34HAL_PACKAGE_PREFIX = 'android.hardware.' 35 36ANDROID_MK_FILE_NAME = 'Android.mk' 37ANDROID_TEST_XML_FILE_NAME = 'AndroidTest.xml' 38 39 40class TestCaseCreator(object): 41 """Init a test case directory with helloworld test case. 42 43 Attributes: 44 hal_package_name: string, package name of the testing hal. e.g. android.hardware.nfc@1.0. 45 hal_name: name of the testing hal, derived from hal_package_name. e.g. nfc. 46 hal_version: version of the testing hal, derived from hal_package_name. 47 test_type: string, type of the test, currently support host and target. 48 test_name_prefix: prefix of generated test name. e.g. android.hardware.nfc@1.0-test-target. 49 test_name: test name generated. e.g. android.hardware.nfc@1.0-test-target-profiling. 50 test_dir: string, test case absolute directory. 51 time_out: string, timeout of the test, default is 1m. 52 is_profiling: boolean, whether to create a profiling test case. 53 stop_runtime: boolean whether to stop framework before the test. 54 build_top: string, equal to environment variable ANDROID_BUILD_TOP. 55 vts_spec_parser: tools that generates and parses vts spec with hidl-gen. 56 current_year: current year. 57 """ 58 59 def __init__(self, vts_spec_parser, hal_package_name): 60 '''Initialize class attributes.''' 61 self._hal_package_name = hal_package_name 62 63 build_top = os.getenv('ANDROID_BUILD_TOP') 64 if not build_top: 65 print('Error: Missing ANDROID_BUILD_TOP env variable. Please run ' 66 '\'. build/envsetup.sh; lunch <build target>\' Exiting...') 67 sys.exit(1) 68 self._build_top = build_top 69 self._vts_spec_parser = vts_spec_parser 70 71 [package, version] = hal_package_name.split('@') 72 self._hal_name = package[len(HAL_PACKAGE_PREFIX):] 73 self._hal_version = version 74 75 self._current_year = datetime.datetime.now().year 76 77 def LaunchTestCase(self, 78 test_type, 79 time_out='1m', 80 is_profiling=False, 81 is_replay=False, 82 stop_runtime=False, 83 update_only=False): 84 """Create the necessary configuration files to launch a test case. 85 86 Args: 87 test_type: type of the test. 88 time_out: timeout of the test. 89 is_profiling: whether to create a profiling test case. 90 stop_runtime: whether to stop framework before the test. 91 update_only: flag to only update existing test configure. 92 93 Returns: 94 boolean, whether created/updated a test case successfully. 95 """ 96 self._test_type = test_type 97 self._time_out = time_out 98 self._is_profiling = is_profiling 99 self._is_replay = is_replay 100 self._stop_runtime = stop_runtime 101 102 self._test_module_name = self.GetVtsHalTestModuleName() 103 self._test_name = self._test_module_name 104 if is_replay: 105 self._test_name = self._test_module_name + 'Replay' 106 if is_profiling: 107 self._test_name = self._test_module_name + 'Profiling' 108 109 self._test_dir = self.GetHalTestCasePath() 110 # Check whether the host side test script and target test binary is available. 111 if self._test_type == 'host': 112 test_script_file = self.GetVtsHostTestScriptFileName() 113 if not os.path.exists(test_script_file): 114 print('Could not find the host side test script: %s.' % 115 test_script_file) 116 return False 117 else: 118 test_binary_file = self.GetVtsTargetTestSourceFileName() 119 if not os.path.exists(test_binary_file): 120 print('Could not find the target side test binary: %s.' % 121 test_binary_file) 122 return False 123 124 if os.path.exists(self._test_dir): 125 print 'WARNING: Test directory already exists. Continuing...' 126 elif not update_only: 127 try: 128 os.makedirs(self._test_dir) 129 except: 130 print('Error: Failed to create test directory at %s. ' 131 'Exiting...' % self.test_dir) 132 return False 133 else: 134 print('WARNING: Test directory does not exists, stop updating.') 135 return True 136 137 self.CreateAndroidMk() 138 self.CreateAndroidTestXml() 139 return True 140 141 def GetVtsTargetTestSourceFileName(self): 142 """Get the name of target side test source file .""" 143 test_binary_name = self._test_module_name + 'Test.cpp' 144 return os.path.join(self.GetHalInterfacePath(), 'vts/functional', 145 test_binary_name) 146 147 def GetVtsHostTestScriptFileName(self): 148 """Get the name of host side test script file .""" 149 test_script_name = self._test_module_name + 'Test.py' 150 return os.path.join( 151 self.GetHalTestCasePath(ignore_profiling=True), test_script_name) 152 153 def GetVtsHalTestModuleName(self): 154 """Get the test model name with format VtsHalHalNameVersionTestType.""" 155 sub_names = self._hal_name.split('.') 156 hal_name_upper_camel = ''.join(x.title() for x in sub_names) 157 return 'VtsHal' + hal_name_upper_camel + self.GetHalVersionToken( 158 ) + self._test_type.title() 159 160 def GetVtsHalReplayTraceFiles(self): 161 """Get the trace files for replay test.""" 162 trace_files = [] 163 for filename in os.listdir(self.GetHalTracePath()): 164 if filename.endswith(".trace"): 165 trace_files.append(filename) 166 return trace_files 167 168 def GetHalPath(self): 169 """Get the hal path based on hal name.""" 170 return self._hal_name.replace('.', '/') 171 172 def GetHalVersionToken(self): 173 """Get a string of the hal version.""" 174 return 'V' + self._hal_version.replace('.', '_') 175 176 def GetHalInterfacePath(self): 177 """Get the directory that stores the .hal files.""" 178 return os.path.join(self._build_top, HAL_INTERFACE_PATH, 179 self.GetHalPath(), self._hal_version) 180 181 def GetHalTestCasePath(self, ignore_profiling=False): 182 """Get the directory that stores the test case.""" 183 test_dir = self._test_type 184 if self._is_replay: 185 test_dir = test_dir + '_replay' 186 if self._is_profiling and not ignore_profiling: 187 test_dir = test_dir + '_profiling' 188 return os.path.join(self._build_top, VTS_TEST_CASE_PATH, 189 self.GetHalPath(), 190 self.GetHalVersionToken(), test_dir) 191 192 def GetHalTracePath(self): 193 """Get the directory that stores the hal trace files.""" 194 return os.path.join(self._build_top, HAL_TRACE_PATH, 195 self.GetHalPath(), self.GetHalVersionToken()) 196 197 def CreateAndroidMk(self): 198 """Create Android.mk.""" 199 vts_dir = os.path.join(self._build_top, VTS_TEST_CASE_PATH) 200 target = os.path.join(self._test_dir, ANDROID_MK_FILE_NAME) 201 202 with open(target, 'w') as f: 203 print 'Creating %s' % target 204 f.write(LICENSE_STATEMENT_POUND.format(year=self._current_year)) 205 f.write('\n') 206 f.write( 207 ANDROID_MK_TEMPLATE.format( 208 test_name=self._test_name, 209 config_src_dir=self._test_dir[len(vts_dir) + 1:])) 210 211 def CreateAndroidTestXml(self): 212 """Create AndroidTest.xml.""" 213 VTS_FILE_PUSHER = 'com.android.compatibility.common.tradefed.targetprep.VtsFilePusher' 214 VTS_PYTHON_PREPARER = 'com.android.tradefed.targetprep.VtsPythonVirtualenvPreparer' 215 VTS_TEST_CLASS = 'com.android.tradefed.testtype.VtsMultiDeviceTest' 216 217 configuration = ET.Element('configuration', { 218 'description': 'Config for VTS ' + self._test_name + ' test cases' 219 }) 220 file_pusher = ET.SubElement(configuration, 'target_preparer', 221 {'class': VTS_FILE_PUSHER}) 222 223 self.GeneratePushFileConfigure(file_pusher) 224 python_preparer = ET.SubElement(configuration, 'target_preparer', 225 {'class': VTS_PYTHON_PREPARER}) 226 test = ET.SubElement(configuration, 'test', {'class': VTS_TEST_CLASS}) 227 228 self.GenerateTestOptionConfigure(test) 229 230 target = os.path.join(self._test_dir, ANDROID_TEST_XML_FILE_NAME) 231 with open(target, 'w') as f: 232 print 'Creating %s' % target 233 f.write(XML_HEADER) 234 f.write(LICENSE_STATEMENT_XML.format(year=self._current_year)) 235 f.write(self.Prettify(configuration)) 236 237 def GeneratePushFileConfigure(self, file_pusher): 238 """Create the push file configuration within AndroidTest.xml 239 240 Args: 241 file_pusher: parent xml element for push file configure. 242 """ 243 ET.SubElement(file_pusher, 'option', 244 {'name': 'abort-on-push-failure', 245 'value': 'false'}) 246 247 if self._test_type == 'target': 248 if self._is_replay: 249 ET.SubElement(file_pusher, 'option', { 250 'name': 'push-group', 251 'value': 'HalHidlHostTest.push' 252 }) 253 elif self._is_profiling: 254 ET.SubElement(file_pusher, 'option', { 255 'name': 'push-group', 256 'value': 'HalHidlTargetProfilingTest.push' 257 }) 258 else: 259 ET.SubElement(file_pusher, 'option', { 260 'name': 'push-group', 261 'value': 'HalHidlTargetTest.push' 262 }) 263 else: 264 if self._is_profiling: 265 ET.SubElement(file_pusher, 'option', { 266 'name': 'push-group', 267 'value': 'HalHidlHostProfilingTest.push' 268 }) 269 else: 270 ET.SubElement(file_pusher, 'option', { 271 'name': 'push-group', 272 'value': 'HalHidlHostTest.push' 273 }) 274 275 imported_package_lists = self._vts_spec_parser.ImportedPackagesList( 276 self._hal_name, self._hal_version) 277 imported_package_lists.append(self._hal_package_name) 278 # Generate additional push files e.g driver/profiler/vts_spec 279 if self._test_type == 'host' or self._is_replay: 280 ET.SubElement(file_pusher, 'option', 281 {'name': 'cleanup', 282 'value': 'true'}) 283 for imported_package in imported_package_lists: 284 if imported_package.startswith(HAL_PACKAGE_PREFIX): 285 imported_package_str, imported_package_version = imported_package.split( 286 '@') 287 imported_package_name = imported_package_str[len( 288 HAL_PACKAGE_PREFIX):] 289 imported_vts_spec_lists = self._vts_spec_parser.VtsSpecNames( 290 imported_package_name, imported_package_version) 291 for vts_spec in imported_vts_spec_lists: 292 push_spec = VTS_SPEC_PUSH_TEMPLATE.format( 293 hal_path=imported_package_name.replace('.', '/'), 294 hal_version=imported_package_version, 295 package_path=imported_package_str.replace('.', 296 '/'), 297 vts_file=vts_spec) 298 ET.SubElement(file_pusher, 'option', 299 {'name': 'push', 300 'value': push_spec}) 301 302 dirver_package_name = imported_package + '-vts.driver.so' 303 push_driver = VTS_LIB_PUSH_TEMPLATE_32.format( 304 lib_name=dirver_package_name) 305 ET.SubElement(file_pusher, 'option', 306 {'name': 'push', 307 'value': push_driver}) 308 push_driver = VTS_LIB_PUSH_TEMPLATE_64.format( 309 lib_name=dirver_package_name) 310 ET.SubElement(file_pusher, 'option', 311 {'name': 'push', 312 'value': push_driver}) 313 314 if self._is_profiling: 315 if self._test_type == 'target': 316 ET.SubElement(file_pusher, 'option', 317 {'name': 'cleanup', 318 'value': 'true'}) 319 for imported_package in imported_package_lists: 320 if imported_package.startswith(HAL_PACKAGE_PREFIX): 321 profiler_package_name = imported_package + '-vts.profiler.so' 322 push_profiler = VTS_LIB_PUSH_TEMPLATE_32.format( 323 lib_name=profiler_package_name) 324 ET.SubElement(file_pusher, 'option', 325 {'name': 'push', 326 'value': push_profiler}) 327 push_profiler = VTS_LIB_PUSH_TEMPLATE_64.format( 328 lib_name=profiler_package_name) 329 ET.SubElement(file_pusher, 'option', 330 {'name': 'push', 331 'value': push_profiler}) 332 333 def GenerateTestOptionConfigure(self, test): 334 """Create the test option configuration within AndroidTest.xml 335 336 Args: 337 test: parent xml element for test option configure. 338 """ 339 ET.SubElement(test, 'option', 340 {'name': 'test-module-name', 341 'value': self._test_name}) 342 343 if self._test_type == 'target': 344 if self._is_replay: 345 ET.SubElement(test, 'option', { 346 'name': 'binary-test-type', 347 'value': 'hal_hidl_replay_test' 348 }) 349 for trace in self.GetVtsHalReplayTraceFiles(): 350 ET.SubElement( 351 test, 352 'option', { 353 'name': 'hal-hidl-replay-test-trace-path', 354 'value': TEST_TRACE_TEMPLATE.format( 355 hal_path=self.GetHalPath(), 356 hal_version=self.GetHalVersionToken(), 357 trace_file=trace) 358 }) 359 ET.SubElement(test, 'option', { 360 'name': 'hal-hidl-package-name', 361 'value': self._hal_package_name 362 }) 363 else: 364 test_binary = TEST_BINEARY_TEMPLATE_32.format( 365 test_name=self._test_module_name + 'Test') 366 ET.SubElement(test, 'option', { 367 'name': 'binary-test-source', 368 'value': test_binary 369 }) 370 test_binary = TEST_BINEARY_TEMPLATE_64.format( 371 test_name=self._test_module_name + 'Test') 372 ET.SubElement(test, 'option', { 373 'name': 'binary-test-source', 374 'value': test_binary 375 }) 376 ET.SubElement(test, 'option', { 377 'name': 'binary-test-type', 378 'value': 'hal_hidl_gtest' 379 }) 380 if self._stop_runtime: 381 ET.SubElement(test, 'option', { 382 'name': 'binary-test-disable-framework', 383 'value': 'true' 384 }) 385 ET.SubElement(test, 'option', { 386 'name': 'binary-test-stop-native-servers', 387 'value': 'true' 388 }) 389 else: 390 test_script = TEST_SCRIPT_TEMPLATE.format( 391 hal_path=self.GetHalPath(), 392 hal_version=self.GetHalVersionToken(), 393 test_name=self._test_module_name + 'Test') 394 ET.SubElement(test, 'option', 395 {'name': 'test-case-path', 396 'value': test_script}) 397 398 if self._is_profiling: 399 ET.SubElement(test, 'option', 400 {'name': 'enable-profiling', 401 'value': 'true'}) 402 403 ET.SubElement(test, 'option', { 404 'name': 'precondition-lshal', 405 'value': self._hal_package_name 406 }) 407 ET.SubElement(test, 'option', 408 {'name': 'test-timeout', 409 'value': self._time_out}) 410 411 def Prettify(self, elem): 412 """Create a pretty-printed XML string for the Element. 413 414 Args: 415 elem: a xml element. 416 417 Regurns: 418 A pretty-printed XML string for the Element. 419 """ 420 if elem: 421 doc = minidom.Document() 422 declaration = doc.toxml() 423 rough_string = ET.tostring(elem, 'utf-8') 424 reparsed = minidom.parseString(rough_string) 425 return unescape( 426 reparsed.toprettyxml(indent=" ")[len(declaration) + 1:]) 427 428 429LICENSE_STATEMENT_POUND = """# 430# Copyright (C) {year} The Android Open Source Project 431# 432# Licensed under the Apache License, Version 2.0 (the "License"); 433# you may not use this file except in compliance with the License. 434# You may obtain a copy of the License at 435# 436# http://www.apache.org/licenses/LICENSE-2.0 437# 438# Unless required by applicable law or agreed to in writing, software 439# distributed under the License is distributed on an "AS IS" BASIS, 440# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 441# See the License for the specific language governing permissions and 442# limitations under the License. 443# 444""" 445 446LICENSE_STATEMENT_XML = """<!-- Copyright (C) {year} The Android Open Source Project 447 448 Licensed under the Apache License, Version 2.0 (the "License"); 449 you may not use this file except in compliance with the License. 450 You may obtain a copy of the License at 451 452 http://www.apache.org/licenses/LICENSE-2.0 453 454 Unless required by applicable law or agreed to in writing, software 455 distributed under the License is distributed on an "AS IS" BASIS, 456 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 457 See the License for the specific language governing permissions and 458 limitations under the License. 459--> 460""" 461 462ANDROID_MK_TEMPLATE = """LOCAL_PATH := $(call my-dir) 463 464include $(CLEAR_VARS) 465 466LOCAL_MODULE := {test_name} 467VTS_CONFIG_SRC_DIR := testcases/hal/{config_src_dir} 468include test/vts/tools/build/Android.host_config.mk 469""" 470 471XML_HEADER = """<?xml version="1.0" encoding="utf-8"?> 472""" 473 474TEST_BINEARY_TEMPLATE_32 = '_32bit::DATA/nativetest/{test_name}/{test_name}' 475TEST_BINEARY_TEMPLATE_64 = '_64bit::DATA/nativetest64/{test_name}/{test_name}' 476 477TEST_SCRIPT_TEMPLATE = 'vts/testcases/hal/{hal_path}/{hal_version}/host/{test_name}' 478TEST_TRACE_TEMPLATE = 'test/vts-testcase/hal-trace/{hal_path}/{hal_version}/{trace_file}' 479VTS_SPEC_PUSH_TEMPLATE = ( 480 'spec/hardware/interfaces/{hal_path}/{hal_version}/vts/{vts_file}->' 481 '/data/local/tmp/spec/{package_path}/{hal_version}/{vts_file}') 482VTS_LIB_PUSH_TEMPLATE_32 = 'DATA/lib/{lib_name}->/data/local/tmp/32/{lib_name}' 483VTS_LIB_PUSH_TEMPLATE_64 = 'DATA/lib64/{lib_name}->/data/local/tmp/64/{lib_name}' 484