• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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