#!/usr/bin/python2.7 # Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Generate a CTS test XML file from a text file containing every single class#method per line # # For example, given an input file: # # foo.txt: # com.android.ClassName#methodNameA # com.android.ClassName#methodNameB # # Will generate the output file: # # TestPackage.xml: # <TestPackage> # <TestSuite name="com"> # <TestSuite name="android"> # <TestCase name="ClassName"> # <Test name="methodNameA" /> # <Test name="methodNameB" /> # </TestCase> # </TestSuite> # </TestSuite> # </TestPackage> # import argparse import sys INDENTATION_INCREASE=2 class BaseNode(object): def __init__(self, name=None): self._children = [] self._name = name self._properties = [] def _get_children(self): return self._children def _set_children(self, value): self._children = value children = property(_get_children, _set_children, doc="Get/set list of children BaseNode") def append_child(self, child): self._children.append(child) def has_children(self): return not not self._children def _get_name(self): return self._name def _set_name(self, value): self._name = value name = property(_get_name, _set_name, doc="Get/set the name property of the current XML node") def _get_type_name(self): return type(self).__name__ type_name = property(_get_type_name, doc="Get the name of the current XML node") def _set_properties(self, value): self._properties = value def _get_properties(self): return self._properties properties = property(_get_properties, _set_properties, doc="Get/set additional XML properties such as appPackageName (as a dict)") def write_xml(self, out, indent=0): out.write(' ' * indent) out.write('<' + self.type_name) if self.name is not None: out.write(' name="') out.write(self.name) out.write('"') if self.properties: for key, value in self.properties.iteritems(): out.write(' ' + key + '="' + value + '"') if not self.has_children(): out.write(' />') out.write('\n') return out.write('>\n') #TODO: print all the properties for child in self.children: child.write_xml(out, indent + INDENTATION_INCREASE) out.write(' ' * indent) out.write('</' + self.type_name + '>') out.write('\n') class _SuiteContainer(BaseNode): def get_or_create_suite(self, package_list): debug_print("get_or_create_suite, package_list = " + str(package_list)) debug_print("name = " + self.name) # If we are empty, then we just reached the TestSuite which we actually wanted. Return. if not package_list: return self current_package = package_list[0] rest_of_packages = package_list[1:] # If a suite already exists for the requested package, then have it look/create recursively. for child in self.children: if child.name == current_package: return child.get_or_create_suite(rest_of_packages) # No suite exists yet, create it recursively new_suite = TestSuite(name=current_package) self.append_child(new_suite) return new_suite.get_or_create_suite(rest_of_packages) class TestPackage(_SuiteContainer): def add_class_and_method(self, fq_class_name, method): debug_print("add_class_and_method, fq_class_name=" + fq_class_name + ", method=" + method) package_list = fq_class_name.split(".")[:-1] # a.b.c -> ['a', 'b'] just_class_name = fq_class_name.split(".")[-1] # a.b.c -> 'c' test_suite = self.get_or_create_suite(package_list) if test_suite == self: raise Exception("The suite cannot be the package") return test_suite.add_class_and_method(just_class_name, method) class TestSuite(_SuiteContainer): def add_class_and_method(self, just_class_name, method_name): test_case = self.get_or_create_test_case(just_class_name) return test_case.add_method(method_name) def get_or_create_test_case(self, just_class_name): for child in self.children: if child.name == just_class_name: return child new_test_case = TestCase(name=just_class_name) self.append_child(new_test_case) return new_test_case class TestCase(BaseNode): def add_method(self, method_name): tst = Test(name=method_name) self.append_child(tst) return tst class Test(BaseNode): def __init__(self, name): super(Test, self).__init__(name) self._children = None def debug_print(x): #print x pass def build_xml_test_package(input, name, xml_properties): root = TestPackage(name=name) for line in input: class_and_method_name = line.split('#') fq_class_name = class_and_method_name[0].strip() method_name = class_and_method_name[1].strip() root.add_class_and_method(fq_class_name, method_name) root.properties = xml_properties return root def write_xml(out, test_package): out.write('<?xml version="1.0" encoding="UTF-8"?>\n') test_package.write_xml(out) def main(): parser = argparse.ArgumentParser(description='Process a test methods list file to generate CTS test xml.') # Named required parser.add_argument('--cts-name', help="name (e.g. CtsJdwp)", required=True) parser.add_argument('--app-package-name', help="appPackageName (e.g. android.jdwp)", required=True) parser.add_argument('--jar-path', help="jarPath (e.g. CtsJdwp.jar)", required=True) # Named optionals parser.add_argument('--test-type', help="testType (default testNGDeviceTest)", default="testNGDeviceTest") parser.add_argument('--runtime-args', help="runtimeArgs (e.g. -XXlib:libart.so)") parser.add_argument('--version', help="version (default 1.0)", default="1.0") # Positional optionals parser.add_argument('input-filename', nargs='?', help='name of the cts test file (stdin by default)') parser.add_argument('output-filename', nargs='?', help='name of the cts output file (stdout by default)') # Map named arguments into the xml <TestPackage> property key name argv_to_xml = { 'app_package_name' : 'appPackageName', 'jar_path' : 'jarPath', 'test_type' : 'testType', 'runtime_args' : 'runtimeArgs', 'version' : 'version' } args = parser.parse_args() argv = vars(args) # convert Namespace to Dict xml_properties = {} for key, value in argv_to_xml.iteritems(): if argv.get(key): xml_properties[value] = argv[key] debug_print(argv['input-filename']) debug_print(argv['output-filename']) name_in = argv['input-filename'] name_out = argv['output-filename'] file_in = name_in and open(name_in, "r") or sys.stdin file_out = name_out and open(name_out, "w+") or sys.stdout # read all the input test_package = build_xml_test_package(file_in, args.cts_name, xml_properties) # write all the output write_xml(file_out, test_package) if __name__ == "__main__": main()