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