1 #!/usr/bin/python2.7 2 3 # Copyright (C) 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 # Generate a CTS test XML file from a text file containing every single class#method per line 19 # 20 # For example, given an input file: 21 # 22 # foo.txt: 23 # com.android.ClassName#methodNameA 24 # com.android.ClassName#methodNameB 25 # 26 # Will generate the output file: 27 # 28 # TestPackage.xml: 29 # <TestPackage> 30 # <TestSuite name="com"> 31 # <TestSuite name="android"> 32 # <TestCase name="ClassName"> 33 # <Test name="methodNameA" /> 34 # <Test name="methodNameB" /> 35 # </TestCase> 36 # </TestSuite> 37 # </TestSuite> 38 # </TestPackage> 39 # 40 41 import argparse 42 import sys 43 44 INDENTATION_INCREASE=2 45 46 class BaseNode(object): 47 def __init__(self, name=None): 48 self._children = [] 49 self._name = name 50 self._properties = [] 51 52 def _get_children(self): 53 return self._children 54 55 def _set_children(self, value): 56 self._children = value 57 58 children = property(_get_children, _set_children, doc="Get/set list of children BaseNode") 59 60 def append_child(self, child): 61 self._children.append(child) 62 63 def has_children(self): 64 return not not self._children 65 66 def _get_name(self): 67 return self._name 68 69 def _set_name(self, value): 70 self._name = value 71 72 name = property(_get_name, _set_name, doc="Get/set the name property of the current XML node") 73 74 def _get_type_name(self): 75 return type(self).__name__ 76 77 type_name = property(_get_type_name, doc="Get the name of the current XML node") 78 79 def _set_properties(self, value): 80 self._properties = value 81 82 def _get_properties(self): 83 return self._properties 84 85 properties = property(_get_properties, _set_properties, doc="Get/set additional XML properties such as appPackageName (as a dict)") 86 87 def write_xml(self, out, indent=0): 88 out.write(' ' * indent) 89 out.write('<' + self.type_name) 90 91 if self.name is not None: 92 out.write(' name="') 93 out.write(self.name) 94 out.write('"') 95 96 if self.properties: 97 for key, value in self.properties.iteritems(): 98 out.write(' ' + key + '="' + value + '"') 99 100 if not self.has_children(): 101 out.write(' />') 102 out.write('\n') 103 return 104 105 out.write('>\n') 106 107 #TODO: print all the properties 108 109 for child in self.children: 110 child.write_xml(out, indent + INDENTATION_INCREASE) 111 112 out.write(' ' * indent) 113 out.write('</' + self.type_name + '>') 114 out.write('\n') 115 116 class _SuiteContainer(BaseNode): 117 def get_or_create_suite(self, package_list): 118 debug_print("get_or_create_suite, package_list = " + str(package_list)) 119 debug_print("name = " + self.name) 120 # If we are empty, then we just reached the TestSuite which we actually wanted. Return. 121 if not package_list: 122 return self 123 124 current_package = package_list[0] 125 rest_of_packages = package_list[1:] 126 127 # If a suite already exists for the requested package, then have it look/create recursively. 128 for child in self.children: 129 if child.name == current_package: 130 return child.get_or_create_suite(rest_of_packages) 131 132 # No suite exists yet, create it recursively 133 new_suite = TestSuite(name=current_package) 134 self.append_child(new_suite) 135 return new_suite.get_or_create_suite(rest_of_packages) 136 137 class TestPackage(_SuiteContainer): 138 def add_class_and_method(self, fq_class_name, method): 139 debug_print("add_class_and_method, fq_class_name=" + fq_class_name + ", method=" + method) 140 package_list = fq_class_name.split(".")[:-1] # a.b.c -> ['a', 'b'] 141 just_class_name = fq_class_name.split(".")[-1] # a.b.c -> 'c' 142 143 test_suite = self.get_or_create_suite(package_list) 144 145 if test_suite == self: 146 raise Exception("The suite cannot be the package") 147 148 return test_suite.add_class_and_method(just_class_name, method) 149 150 class TestSuite(_SuiteContainer): 151 def add_class_and_method(self, just_class_name, method_name): 152 test_case = self.get_or_create_test_case(just_class_name) 153 return test_case.add_method(method_name) 154 155 def get_or_create_test_case(self, just_class_name): 156 for child in self.children: 157 if child.name == just_class_name: 158 return child 159 160 new_test_case = TestCase(name=just_class_name) 161 self.append_child(new_test_case) 162 return new_test_case 163 164 class TestCase(BaseNode): 165 def add_method(self, method_name): 166 tst = Test(name=method_name) 167 self.append_child(tst) 168 return tst 169 170 class Test(BaseNode): 171 def __init__(self, name): 172 super(Test, self).__init__(name) 173 self._children = None 174 175 def debug_print(x): 176 #print x 177 pass 178 179 def build_xml_test_package(input, name, xml_properties): 180 root = TestPackage(name=name) 181 182 for line in input: 183 class_and_method_name = line.split('#') 184 fq_class_name = class_and_method_name[0].strip() 185 method_name = class_and_method_name[1].strip() 186 187 root.add_class_and_method(fq_class_name, method_name) 188 189 root.properties = xml_properties 190 return root 191 192 def write_xml(out, test_package): 193 out.write('<?xml version="1.0" encoding="UTF-8"?>\n') 194 test_package.write_xml(out) 195 196 def main(): 197 parser = argparse.ArgumentParser(description='Process a test methods list file to generate CTS test xml.') 198 199 # Named required 200 parser.add_argument('--cts-name', help="name (e.g. CtsJdwp)", required=True) 201 parser.add_argument('--app-package-name', help="appPackageName (e.g. android.jdwp)", required=True) 202 parser.add_argument('--jar-path', help="jarPath (e.g. CtsJdwp.jar)", required=True) 203 204 # Named optionals 205 parser.add_argument('--test-type', help="testType (default testNGDeviceTest)", 206 default="testNGDeviceTest") 207 parser.add_argument('--runtime-args', help="runtimeArgs (e.g. -XXlib:libart.so)") 208 parser.add_argument('--version', help="version (default 1.0)", default="1.0") 209 210 # Positional optionals 211 parser.add_argument('input-filename', nargs='?', 212 help='name of the cts test file (stdin by default)') 213 parser.add_argument('output-filename', nargs='?', 214 help='name of the cts output file (stdout by default)') 215 216 # Map named arguments into the xml <TestPackage> property key name 217 argv_to_xml = { 218 'app_package_name' : 'appPackageName', 219 'jar_path' : 'jarPath', 220 'test_type' : 'testType', 221 'runtime_args' : 'runtimeArgs', 222 'version' : 'version' 223 } 224 225 args = parser.parse_args() 226 argv = vars(args) # convert Namespace to Dict 227 228 xml_properties = {} 229 for key, value in argv_to_xml.iteritems(): 230 if argv.get(key): 231 xml_properties[value] = argv[key] 232 233 debug_print(argv['input-filename']) 234 debug_print(argv['output-filename']) 235 236 name_in = argv['input-filename'] 237 name_out = argv['output-filename'] 238 239 file_in = name_in and open(name_in, "r") or sys.stdin 240 file_out = name_out and open(name_out, "w+") or sys.stdout 241 242 # read all the input 243 test_package = build_xml_test_package(file_in, args.cts_name, xml_properties) 244 # write all the output 245 write_xml(file_out, test_package) 246 247 if __name__ == "__main__": 248 main() 249