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