1#!/usr/bin/env python
2#
3# Copyright 2016 - 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# Initiate a test case directory.
19# This script copy a template which contains Android.mk, __init__.py files,
20# AndroidTest.xml and a test case python file into a given relative directory
21# under testcases/ using the given test name.
22
23import os
24import sys
25import datetime
26import re
27import shutil
28import argparse
29
30VTS_PATH = 'test/vts'
31VTS_TEST_CASE_PATH = os.path.join(VTS_PATH, 'testcases')
32PYTHON_INIT_FILE_NAME = '__init__.py'
33ANDROID_MK_FILE_NAME = 'Android.mk'
34ANDROID_TEST_XML_FILE_NAME = 'AndroidTest.xml'
35
36
37class TestCaseCreator(object):
38    '''Init a test case directory with helloworld test case.
39
40    Attributes:
41        test_name: string, test case name in UpperCamel
42        build_top: string, equal to environment variable ANDROID_BUILD_TOP
43        test_dir: string, test case absolute directory
44        test_name: string, test case name in UpperCamel
45        test_plan: string, the plan that the test belongs to
46        test_type: test type, such as HidlHalTest, HostDrivenTest, etc
47        current_year: current year
48        vts_test_case_dir: absolute dir of vts testcases directory
49    '''
50
51    def __init__(self, test_name, test_plan, test_dir_under_testcases,
52                 test_type):
53        '''Initialize class attributes.
54
55        Args:
56            test_name: string, test case name in UpperCamel
57            test_plan: string, the plan that the test belongs to
58            test_dir_under_testcases: string, test case relative directory under
59                                      test/vts/testcases.
60        '''
61        if not test_dir_under_testcases:
62            print 'Error: Empty test directory entered. Exiting'
63            sys.exit(3)
64        test_dir_under_testcases = os.path.normpath(
65            test_dir_under_testcases.strip())
66
67        if not self.IsUpperCamel(test_name):
68            print 'Error: Test name not in UpperCamel case. Exiting'
69            sys.exit(4)
70        self.test_name = test_name
71
72        if not test_plan:
73            self.test_plan = 'vts-misc'
74        else:
75            self.test_plan = test_plan
76
77        if not test_type:
78            self.test_type = 'HidlHalTest'
79        else:
80            self.test_type = test_type
81
82        self.build_top = os.getenv('ANDROID_BUILD_TOP')
83        if not self.build_top:
84            print('Error: Missing ANDROID_BUILD_TOP env variable. Please run '
85                  '\'. build/envsetup.sh; lunch <build target>\' Exiting...')
86            sys.exit(1)
87
88        self.vts_test_case_dir = os.path.abspath(
89            os.path.join(self.build_top, VTS_TEST_CASE_PATH))
90
91        self.test_dir = os.path.abspath(
92            os.path.join(self.vts_test_case_dir, test_dir_under_testcases))
93
94        self.current_year = datetime.datetime.now().year
95
96    def InitTestCaseDir(self):
97        '''Start init test case directory'''
98        if os.path.exists(self.test_dir):
99            print 'Error: Test directory already exists. Exiting...'
100            sys.exit(2)
101        try:
102            os.makedirs(self.test_dir)
103        except:
104            print('Error: Failed to create test directory at %s. '
105                  'Exiting...' % self.test_dir)
106            sys.exit(2)
107
108        self.CreatePythonInitFile()
109        self.CreateAndroidMk()
110        self.CreateAndroidTestXml()
111        self.CreateTestCasePy()
112
113    def UpperCamelToLowerUnderScore(self, name):
114        '''Convert UpperCamel name to lower_under_score name.
115
116        Args:
117            name: string in UpperCamel.
118
119        Returns:
120            a lower_under_score version of the given name
121        '''
122        return re.sub('(?!^)([A-Z]+)', r'_\1', name).lower()
123
124    def IsUpperCamel(self, name):
125        '''Check whether a given name is UpperCamel case.
126
127        Args:
128            name: string.
129
130        Returns:
131            True if name is in UpperCamel case, False otherwise
132        '''
133        regex = re.compile('((?:[A-Z][a-z]+)[0-9]*)+')
134        match = regex.match(name)
135        return match and (match.end() - match.start() == len(name))
136
137    def CreatePythonInitFile(self):
138        '''Populate test case directory and parent directories with __init__.py.
139        '''
140        if not self.test_dir.startswith(self.vts_test_case_dir):
141            print 'Error: Test case directory is not under VTS test case directory.'
142            sys.exit(4)
143
144        path = self.test_dir
145        while not path == self.vts_test_case_dir:
146            target = os.path.join(path, PYTHON_INIT_FILE_NAME)
147            if not os.path.exists(target):
148                print 'Creating %s' % target
149                with open(target, 'w') as f:
150                    pass
151            path = os.path.dirname(path)
152
153    def CreateAndroidMk(self):
154        '''Populate test case directory and parent directories with Android.mk
155        '''
156        vts_dir = os.path.join(self.build_top, VTS_PATH)
157
158        target = os.path.join(self.test_dir, ANDROID_MK_FILE_NAME)
159        with open(target, 'w') as f:
160            print 'Creating %s' % target
161            f.write(LICENSE_STATEMENT_POUND.format(year=self.current_year))
162            f.write('\n')
163            f.write(
164                ANDROID_MK_TEMPLATE.format(
165                    test_name=self.test_name,
166                    config_src_dir=self.test_dir[len(vts_dir) + 1:]))
167
168        path = self.test_dir
169        while not path == vts_dir:
170            target = os.path.join(path, ANDROID_MK_FILE_NAME)
171            if not os.path.exists(target):
172                print 'Creating %s' % target
173                with open(target, 'w') as f:
174                    f.write(
175                        LICENSE_STATEMENT_POUND.format(year=self.current_year))
176                    f.write(ANDROID_MK_CALL_SUB)
177            path = os.path.dirname(path)
178
179    def CreateAndroidTestXml(self):
180        '''Create AndroidTest.xml'''
181        target = os.path.join(self.test_dir, ANDROID_TEST_XML_FILE_NAME)
182        with open(target, 'w') as f:
183            print 'Creating %s' % target
184            f.write(XML_HEADER)
185            f.write(LICENSE_STATEMENT_XML.format(year=self.current_year))
186            f.write(
187                ANDROID_TEST_XML_TEMPLATE.format(
188                    test_name=self.test_name,
189                    test_plan=self.test_plan,
190                    test_type=self.test_type,
191                    test_path_under_vts=self.test_dir[
192                        len(os.path.join(self.build_top, VTS_PATH)) + 1:],
193                    test_case_file_without_extension=self.test_name))
194
195    def CreateTestCasePy(self):
196        '''Create <test_case_name>.py'''
197        target = os.path.join(self.test_dir, '%s.py' % self.test_name)
198        with open(target, 'w') as f:
199            print 'Creating %s' % target
200            f.write(PY_HEADER)
201            f.write(LICENSE_STATEMENT_POUND.format(year=self.current_year))
202            f.write('\n')
203            f.write(TEST_CASE_PY_TEMPLATE.format(test_name=self.test_name))
204
205
206def main():
207    parser = argparse.ArgumentParser(description='Initiate a test case.')
208    parser.add_argument(
209        '--name',
210        dest='test_name',
211        required=True,
212        help='Test case name in UpperCamel. Example: VtsKernelLtp')
213    parser.add_argument(
214        '--plan',
215        dest='test_plan',
216        required=False,
217        help='The plan that the test belongs to. Example: vts-kernel')
218    parser.add_argument(
219        '--dir',
220        dest='test_dir',
221        required=True,
222        help='Test case relative directory under test/vts/testcses.')
223    parser.add_argument(
224        '--type',
225        dest='test_type',
226        required=False,
227        help='Test type, such as HidlHalTest, HostDrivenTest, etc.')
228
229    args = parser.parse_args()
230    test_case_creater = TestCaseCreator(args.test_name, args.test_plan,
231                                        args.test_dir, args.test_type)
232    test_case_creater.InitTestCaseDir()
233
234
235LICENSE_STATEMENT_POUND = '''#
236# Copyright (C) {year} The Android Open Source Project
237#
238# Licensed under the Apache License, Version 2.0 (the "License");
239# you may not use this file except in compliance with the License.
240# You may obtain a copy of the License at
241#
242#      http://www.apache.org/licenses/LICENSE-2.0
243#
244# Unless required by applicable law or agreed to in writing, software
245# distributed under the License is distributed on an "AS IS" BASIS,
246# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
247# See the License for the specific language governing permissions and
248# limitations under the License.
249#
250'''
251
252LICENSE_STATEMENT_XML = '''<!-- Copyright (C) {year} The Android Open Source Project
253
254     Licensed under the Apache License, Version 2.0 (the "License");
255     you may not use this file except in compliance with the License.
256     You may obtain a copy of the License at
257
258          http://www.apache.org/licenses/LICENSE-2.0
259
260     Unless required by applicable law or agreed to in writing, software
261     distributed under the License is distributed on an "AS IS" BASIS,
262     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
263     See the License for the specific language governing permissions and
264     limitations under the License.
265-->
266'''
267
268ANDROID_MK_TEMPLATE = '''LOCAL_PATH := $(call my-dir)
269
270include $(call all-subdir-makefiles)
271
272include $(CLEAR_VARS)
273
274LOCAL_MODULE := {test_name}
275include test/vts/tools/build/Android.host_config.mk
276'''
277
278ANDROID_MK_CALL_SUB = '''LOCAL_PATH := $(call my-dir)
279
280include $(call all-subdir-makefiles)
281'''
282
283XML_HEADER = '''<?xml version="1.0" encoding="utf-8"?>
284'''
285
286ANDROID_TEST_XML_TEMPLATE = '''<configuration description="Config for VTS {test_name} test cases">
287    <option name="config-descriptor:metadata" key="plan" value="{test_plan}" />
288    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
289        <option name="push-group" value="{test_type}.push" />
290    </target_preparer>
291    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
292        <option name="test-module-name" value="{test_name}" />
293        <option name="test-case-path" value="vts/{test_path_under_vts}/{test_case_file_without_extension}" />
294    </test>
295</configuration>
296'''
297
298PY_HEADER = '''#!/usr/bin/env python
299'''
300
301TEST_CASE_PY_TEMPLATE = '''import logging
302
303from vts.runners.host import asserts
304from vts.runners.host import base_test
305from vts.runners.host import const
306from vts.runners.host import test_runner
307
308
309
310class {test_name}(base_test.BaseTestClass):
311    """Two hello world test cases which use the shell driver."""
312
313    def setUpClass(self):
314        self.dut = self.android_devices[0]
315        self.shell = self.dut.shell
316
317    def testEcho1(self):
318        """A simple testcase which sends a command."""
319        results = self.shell.Execute("echo hello_world")  # runs a shell command.
320        logging.info(str(results[const.STDOUT]))  # prints the stdout
321        asserts.assertEqual(results[const.STDOUT][0].strip(), "hello_world")  # checks the stdout
322        asserts.assertEqual(results[const.EXIT_CODE][0], 0)  # checks the exit code
323
324    def testEcho2(self):
325        """A simple testcase which sends two commands."""
326        results = self.shell.Execute(["echo hello", "echo world"])
327        logging.info(str(results[const.STDOUT]))
328        asserts.assertEqual(len(results[const.STDOUT]), 2)  # check the number of processed commands
329        asserts.assertEqual(results[const.STDOUT][0].strip(), "hello")
330        asserts.assertEqual(results[const.STDOUT][1].strip(), "world")
331        asserts.assertEqual(results[const.EXIT_CODE][0], 0)
332        asserts.assertEqual(results[const.EXIT_CODE][1], 0)
333
334
335if __name__ == "__main__":
336    test_runner.main()
337'''
338
339if __name__ == '__main__':
340    main()
341