Home | History | Annotate | Download | only in util-src
      1 #!/usr/bin/python3
      2 #
      3 # Copyright (C) 2015 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 java test files for test 964.
     19 """
     20 
     21 import os
     22 import sys
     23 from pathlib import Path
     24 
     25 BUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
     26 if BUILD_TOP is None:
     27   print("ANDROID_BUILD_TOP not set. Please run build/envsetup.sh", file=sys.stderr)
     28   sys.exit(1)
     29 
     30 # Allow us to import utils and mixins.
     31 sys.path.append(str(Path(BUILD_TOP)/"art"/"test"/"utils"/"python"))
     32 
     33 from testgen.utils import get_copyright, subtree_sizes, gensym, filter_blanks
     34 import testgen.mixins as mixins
     35 
     36 from functools import total_ordering
     37 import itertools
     38 import string
     39 
     40 # The max depth the tree can have.
     41 MAX_IFACE_DEPTH = 3
     42 
     43 class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin):
     44   """
     45   A Main.java file containing the Main class and the main function. It will run
     46   all the test functions we have.
     47   """
     48 
     49   MAIN_CLASS_TEMPLATE = """{copyright}
     50 class Main {{
     51 {test_groups}
     52 {main_func}
     53 }}
     54 """
     55 
     56   MAIN_FUNCTION_TEMPLATE = """
     57   public static void main(String[] args) {{
     58     {test_group_invoke}
     59   }}
     60 """
     61 
     62   TEST_GROUP_INVOKE_TEMPLATE = """
     63     {test_name}();
     64 """
     65 
     66   def __init__(self):
     67     """
     68     Initialize this MainClass. We start out with no tests.
     69     """
     70     self.tests = set()
     71 
     72   def add_test(self, ty):
     73     """
     74     Add a test for the concrete type 'ty'
     75     """
     76     self.tests.add(Func(ty))
     77 
     78   def get_expected(self):
     79     """
     80     Get the expected output of this test.
     81     """
     82     all_tests = sorted(self.tests)
     83     return filter_blanks("\n".join(a.get_expected() for a in all_tests))
     84 
     85   def get_name(self):
     86     """
     87     Gets the name of this class
     88     """
     89     return "Main"
     90 
     91   def __str__(self):
     92     """
     93     Print the java code for this test.
     94     """
     95     all_tests = sorted(self.tests)
     96     test_invoke = ""
     97     test_groups = ""
     98     for t in all_tests:
     99       test_groups += str(t)
    100     for t in all_tests:
    101       test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name())
    102     main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke)
    103 
    104     return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('java'),
    105                                            test_groups = test_groups,
    106                                            main_func = main_func)
    107 
    108 class Func(mixins.Named, mixins.NameComparableMixin):
    109   """
    110   A function that tests the functionality of a concrete type. Should only be
    111   constructed by MainClass.add_test.
    112   """
    113 
    114   TEST_FUNCTION_TEMPLATE = """
    115   public static void {fname}() {{
    116     try {{
    117       System.out.println("About to initialize {tree}");
    118       {farg} v = new {farg}();
    119       System.out.println("Initialized {tree}");
    120       v.touchAll();
    121       System.out.println("All of {tree} hierarchy initialized");
    122       return;
    123     }} catch (Error e) {{
    124       e.printStackTrace(System.out);
    125       return;
    126     }}
    127   }}
    128 """
    129 
    130   OUTPUT_FORMAT = """
    131 About to initialize {tree}
    132 {initialize_output}
    133 Initialized {tree}
    134 {touch_output}
    135 All of {tree} hierarchy initialized
    136 """.strip()
    137 
    138   def __init__(self, farg):
    139     """
    140     Initialize a test function for the given argument
    141     """
    142     self.farg = farg
    143 
    144   def __str__(self):
    145     """
    146     Print the java code for this test function.
    147     """
    148     return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(),
    149                                               farg=self.farg.get_name(),
    150                                               tree = self.farg.get_tree())
    151 
    152   def get_name(self):
    153     """
    154     Gets the name of this test function
    155     """
    156     return "TEST_FUNC_{}".format(self.farg.get_name())
    157 
    158   def get_expected(self):
    159     """
    160     Get the expected output of this function.
    161     """
    162     return self.OUTPUT_FORMAT.format(
    163         tree = self.farg.get_tree(),
    164         initialize_output = self.farg.get_initialize_output().strip(),
    165         touch_output = self.farg.get_touch_output().strip())
    166 
    167 class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin):
    168   """
    169   A class that will be instantiated to test interface initialization order.
    170   """
    171 
    172   TEST_CLASS_TEMPLATE = """{copyright}
    173 public class {class_name} implements {ifaces} {{
    174   public void marker() {{
    175     return;
    176   }}
    177 
    178   public void touchAll() {{
    179 {touch_calls}
    180   }}
    181 }}
    182 """
    183 
    184   TOUCH_CALL_TEMPLATE = """
    185     System.out.println("{class_name} touching {iface_name}");
    186     {iface_name}.field.touch();
    187 """
    188 
    189   TOUCH_OUTPUT_TEMPLATE = """
    190 {class_name} touching {iface_name}
    191 {touch_output}
    192 """.strip()
    193 
    194   def __init__(self, ifaces):
    195     """
    196     Initialize this test class which implements the given interfaces
    197     """
    198     self.ifaces = ifaces
    199     self.class_name = "CLASS_"+gensym()
    200 
    201   def get_name(self):
    202     """
    203     Gets the name of this interface
    204     """
    205     return self.class_name
    206 
    207   def get_tree(self):
    208     """
    209     Print out a representation of the type tree of this class
    210     """
    211     return "[{fname} {iftree}]".format(fname = self.get_name(), iftree = print_tree(self.ifaces))
    212 
    213   def get_initialize_output(self):
    214     return "\n".join(map(lambda i: i.get_initialize_output().strip(), dump_tree(self.ifaces)))
    215 
    216   def get_touch_output(self):
    217     return "\n".join(map(lambda a: self.TOUCH_OUTPUT_TEMPLATE.format(
    218                                       class_name = self.class_name,
    219                                       iface_name = a.get_name(),
    220                                       touch_output = a.get_touch_output()).strip(),
    221                          self.get_all_interfaces()))
    222 
    223   def get_all_interfaces(self):
    224     """
    225     Returns a set of all interfaces this class transitively implements
    226     """
    227     return sorted(set(dump_tree(self.ifaces)))
    228 
    229   def __str__(self):
    230     """
    231     Print the java code for this class.
    232     """
    233     j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces))
    234     touches  = '\n'.join(map(lambda a: self.TOUCH_CALL_TEMPLATE.format(class_name = self.class_name,
    235                                                                        iface_name = a.get_name()),
    236                              self.get_all_interfaces()))
    237     return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('java'),
    238                                            ifaces = j_ifaces,
    239                                            class_name = self.class_name,
    240                                            touch_calls = touches)
    241 
    242 class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin):
    243   """
    244   An interface that will be used to test default method resolution order.
    245   """
    246 
    247   TEST_INTERFACE_TEMPLATE = """{copyright}
    248 public interface {class_name} {extends} {ifaces} {{
    249   public static final Displayer field = new Displayer("{tree}");
    250   public void marker();
    251 {funcs}
    252 }}
    253 """
    254 
    255   DEFAULT_FUNC_TEMPLATE = """
    256   public default void {class_name}_DEFAULT_FUNC() {{ return; }}
    257 """
    258 
    259   OUTPUT_TEMPLATE = "initialization of {tree}"
    260 
    261   def __init__(self, ifaces, default):
    262     """
    263     Initialize interface with the given super-interfaces
    264     """
    265     self.ifaces = ifaces
    266     self.default = default
    267     end = "_DEFAULT" if default else ""
    268     self.class_name = "INTERFACE_"+gensym()+end
    269     self.cloned = False
    270     self.initialized = False
    271 
    272   def clone(self):
    273     """
    274     Clones this interface, returning a new one with the same structure but
    275     different name.
    276     """
    277     return TestInterface(tuple(map(lambda a: a.clone(), self.ifaces)), self.default)
    278 
    279   def get_name(self):
    280     """
    281     Gets the name of this interface
    282     """
    283     return self.class_name
    284 
    285   def __iter__(self):
    286     """
    287     Performs depth-first traversal of the interface tree this interface is the
    288     root of. Does not filter out repeats.
    289     """
    290     for i in self.ifaces:
    291       yield i
    292       yield from i
    293 
    294   def get_tree(self):
    295     """
    296     Print out a representation of the type tree of this class
    297     """
    298     return "[{class_name} {iftree}]".format(class_name = self.get_name(),
    299                                             iftree = print_tree(self.ifaces))
    300 
    301   def get_initialize_output(self):
    302     """
    303     Returns the expected output upon the class that implements this interface being initialized.
    304     """
    305     if self.default and not self.initialized:
    306       self.initialized = True
    307       return self.OUTPUT_TEMPLATE.format(tree = self.get_tree())
    308     else:
    309       return ""
    310 
    311   def get_touch_output(self):
    312     """
    313     Returns the expected output upon this interface being touched.
    314     """
    315     if not self.default and not self.initialized:
    316       self.initialized = True
    317       return self.OUTPUT_TEMPLATE.format(tree = self.get_tree())
    318     else:
    319       return ""
    320 
    321   def __str__(self):
    322     """
    323     Print the java code for this interface.
    324     """
    325     j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces))
    326     if self.default:
    327       funcs = self.DEFAULT_FUNC_TEMPLATE.format(class_name = self.class_name)
    328     else:
    329       funcs = ""
    330     return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('java'),
    331                                                extends = "extends" if len(self.ifaces) else "",
    332                                                ifaces = j_ifaces,
    333                                                funcs = funcs,
    334                                                tree = self.get_tree(),
    335                                                class_name = self.class_name)
    336 
    337 def dump_tree(ifaces):
    338   """
    339   Yields all the interfaces transitively implemented by the set in
    340   reverse-depth-first order
    341   """
    342   for i in ifaces:
    343     yield from dump_tree(i.ifaces)
    344     yield i
    345 
    346 def print_tree(ifaces):
    347   """
    348   Prints the tree for the given ifaces.
    349   """
    350   return " ".join(i.get_tree() for i in  ifaces)
    351 
    352 def clone_all(l):
    353   return tuple(a.clone() for a in l)
    354 
    355 # Cached output of subtree_sizes for speed of access.
    356 SUBTREES = [set(tuple(l) for l in subtree_sizes(i))
    357             for i in range(MAX_IFACE_DEPTH + 1)]
    358 
    359 def create_test_classes():
    360   """
    361   Yield all the test classes with the different interface trees
    362   """
    363   for num in range(1, MAX_IFACE_DEPTH + 1):
    364     for split in SUBTREES[num]:
    365       ifaces = []
    366       for sub in split:
    367         ifaces.append(list(create_interface_trees(sub)))
    368       for supers in itertools.product(*ifaces):
    369         yield TestClass(clone_all(supers))
    370         for i in range(len(set(dump_tree(supers)) - set(supers))):
    371           ns = clone_all(supers)
    372           selected = sorted(set(dump_tree(ns)) - set(ns))[i]
    373           yield TestClass(tuple([selected] + list(ns)))
    374 
    375 def create_interface_trees(num):
    376   """
    377   Yield all the interface trees up to 'num' depth.
    378   """
    379   if num == 0:
    380     yield TestInterface(tuple(), False)
    381     yield TestInterface(tuple(), True)
    382     return
    383   for split in SUBTREES[num]:
    384     ifaces = []
    385     for sub in split:
    386       ifaces.append(list(create_interface_trees(sub)))
    387     for supers in itertools.product(*ifaces):
    388       yield TestInterface(clone_all(supers), False)
    389       yield TestInterface(clone_all(supers), True)
    390       # TODO Should add on some from higher up the tree.
    391 
    392 def create_all_test_files():
    393   """
    394   Creates all the objects representing the files in this test. They just need to
    395   be dumped.
    396   """
    397   mc = MainClass()
    398   classes = {mc}
    399   for clazz in create_test_classes():
    400     classes.add(clazz)
    401     for i in dump_tree(clazz.ifaces):
    402       classes.add(i)
    403     mc.add_test(clazz)
    404   return mc, classes
    405 
    406 def main(argv):
    407   java_dir = Path(argv[1])
    408   if not java_dir.exists() or not java_dir.is_dir():
    409     print("{} is not a valid java dir".format(java_dir), file=sys.stderr)
    410     sys.exit(1)
    411   expected_txt = Path(argv[2])
    412   mainclass, all_files = create_all_test_files()
    413   with expected_txt.open('w') as out:
    414     print(mainclass.get_expected(), file=out)
    415   for f in all_files:
    416     f.dump(java_dir)
    417 
    418 if __name__ == '__main__':
    419   main(sys.argv)
    420