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