Igor Murashkin | 0b4e136 | 2016-04-07 16:54:33 -0700 | [diff] [blame] | 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() |