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 
     31 VTS_TEST_CASE_PATH = 'test/vts-testcase/hal'
     32 HAL_INTERFACE_PATH = 'hardware/interfaces'
     33 HAL_TRACE_PATH = 'test/vts-testcase/hal-trace'
     34 HAL_PACKAGE_PREFIX = 'android.hardware.'
     35 
     36 ANDROID_MK_FILE_NAME = 'Android.mk'
     37 ANDROID_TEST_XML_FILE_NAME = 'AndroidTest.xml'
     38 
     39 
     40 class 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 (at] 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 (at] 1.0-test-target.
     49         test_name: test name generated. e.g. android.hardware.nfc (at] 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 
    429 LICENSE_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 
    446 LICENSE_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 
    462 ANDROID_MK_TEMPLATE = """LOCAL_PATH := $(call my-dir)
    463 
    464 include $(CLEAR_VARS)
    465 
    466 LOCAL_MODULE := {test_name}
    467 VTS_CONFIG_SRC_DIR := testcases/hal/{config_src_dir}
    468 include test/vts/tools/build/Android.host_config.mk
    469 """
    470 
    471 XML_HEADER = """<?xml version="1.0" encoding="utf-8"?>
    472 """
    473 
    474 TEST_BINEARY_TEMPLATE_32 = '_32bit::DATA/nativetest/{test_name}/{test_name}'
    475 TEST_BINEARY_TEMPLATE_64 = '_64bit::DATA/nativetest64/{test_name}/{test_name}'
    476 
    477 TEST_SCRIPT_TEMPLATE = 'vts/testcases/hal/{hal_path}/{hal_version}/host/{test_name}'
    478 TEST_TRACE_TEMPLATE = 'test/vts-testcase/hal-trace/{hal_path}/{hal_version}/{trace_file}'
    479 VTS_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}')
    482 VTS_LIB_PUSH_TEMPLATE_32 = 'DATA/lib/{lib_name}->/data/local/tmp/32/{lib_name}'
    483 VTS_LIB_PUSH_TEMPLATE_64 = 'DATA/lib64/{lib_name}->/data/local/tmp/64/{lib_name}'
    484