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 961. 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 type tree can have. Includes the class object in the tree. 41 # Increasing this increases the number of generated files significantly. This 42 # value was chosen as it is fairly quick to run and very comprehensive, checking 43 # every possible interface tree up to 5 layers deep. 44 MAX_IFACE_DEPTH = 5 45 46 class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin): 47 """ 48 A Main.java file containing the Main class and the main function. It will run 49 all the test functions we have. 50 """ 51 52 MAIN_CLASS_TEMPLATE = """{copyright} 53 class Main {{ 54 {test_groups} 55 {main_func} 56 }} 57 """ 58 59 MAIN_FUNCTION_TEMPLATE = """ 60 public static void main(String[] args) {{ 61 {test_group_invoke} 62 }} 63 """ 64 65 TEST_GROUP_INVOKE_TEMPLATE = """ 66 {test_name}(); 67 """ 68 69 def __init__(self): 70 """ 71 Initialize this MainClass. We start out with no tests. 72 """ 73 self.tests = set() 74 75 def get_expected(self): 76 """ 77 Get the expected output of this test. 78 """ 79 all_tests = sorted(self.tests) 80 return filter_blanks("\n".join(a.get_expected() for a in all_tests)) 81 82 def add_test(self, ty): 83 """ 84 Add a test for the concrete type 'ty' 85 """ 86 self.tests.add(Func(ty)) 87 88 def get_name(self): 89 """ 90 Get the name of this class 91 """ 92 return "Main" 93 94 def __str__(self): 95 """ 96 Print the MainClass java code. 97 """ 98 all_tests = sorted(self.tests) 99 test_invoke = "" 100 test_groups = "" 101 for t in all_tests: 102 test_groups += str(t) 103 for t in all_tests: 104 test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name()) 105 main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke) 106 107 return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright("java"), 108 test_groups = test_groups, 109 main_func = main_func) 110 111 class Func(mixins.Named, mixins.NameComparableMixin): 112 """ 113 A function that tests the functionality of a concrete type. Should only be 114 constructed by MainClass.add_test. 115 """ 116 117 TEST_FUNCTION_TEMPLATE = """ 118 public static void {fname}() {{ 119 try {{ 120 {farg} v = new {farg}(); 121 System.out.printf("%s calls default method on %s\\n", 122 v.CalledClassName(), 123 v.CalledInterfaceName()); 124 return; 125 }} catch (Error e) {{ 126 e.printStackTrace(System.out); 127 return; 128 }} 129 }} 130 """ 131 132 def __init__(self, farg): 133 """ 134 Initialize a test function for the given argument 135 """ 136 self.farg = farg 137 138 def get_expected(self): 139 """ 140 Get the expected output calling this function. 141 """ 142 return "{tree} calls default method on {iface_tree}".format( 143 tree = self.farg.get_tree(), iface_tree = self.farg.get_called().get_tree()) 144 145 def get_name(self): 146 """ 147 Get the name of this function 148 """ 149 return "TEST_FUNC_{}".format(self.farg.get_name()) 150 151 def __str__(self): 152 """ 153 Print the java code of this function. 154 """ 155 return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg.get_name()) 156 157 class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): 158 """ 159 A class that will be instantiated to test default method resolution order. 160 """ 161 162 TEST_CLASS_TEMPLATE = """{copyright} 163 public class {class_name} implements {iface_name} {{ 164 public String CalledClassName() {{ 165 return "{tree}"; 166 }} 167 }} 168 """ 169 170 def __init__(self, iface): 171 """ 172 Initialize this test class which implements the given interface 173 """ 174 self.iface = iface 175 self.class_name = "CLASS_"+gensym() 176 177 def get_name(self): 178 """ 179 Get the name of this class 180 """ 181 return self.class_name 182 183 def get_tree(self): 184 """ 185 Print out a representation of the type tree of this class 186 """ 187 return "[{class_name} {iface_tree}]".format(class_name = self.class_name, 188 iface_tree = self.iface.get_tree()) 189 190 def __iter__(self): 191 """ 192 Step through all interfaces implemented transitively by this class 193 """ 194 yield self.iface 195 yield from self.iface 196 197 def get_called(self): 198 """ 199 Get the interface whose default method would be called when calling the 200 CalledInterfaceName function. 201 """ 202 all_ifaces = set(iface for iface in self if iface.default) 203 for i in all_ifaces: 204 if all(map(lambda j: i not in j.get_super_types(), all_ifaces)): 205 return i 206 raise Exception("UNREACHABLE! Unable to find default method!") 207 208 def __str__(self): 209 """ 210 Print the java code of this class. 211 """ 212 return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('java'), 213 iface_name = self.iface.get_name(), 214 tree = self.get_tree(), 215 class_name = self.class_name) 216 217 class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): 218 """ 219 An interface that will be used to test default method resolution order. 220 """ 221 222 TEST_INTERFACE_TEMPLATE = """{copyright} 223 public interface {class_name} {extends} {ifaces} {{ 224 public String CalledClassName(); 225 226 {funcs} 227 }} 228 """ 229 230 DEFAULT_FUNC_TEMPLATE = """ 231 public default String CalledInterfaceName() {{ 232 return "{tree}"; 233 }} 234 """ 235 236 def __init__(self, ifaces, default): 237 """ 238 Initialize interface with the given super-interfaces 239 """ 240 self.ifaces = sorted(ifaces) 241 self.default = default 242 end = "_DEFAULT" if default else "" 243 self.class_name = "INTERFACE_"+gensym()+end 244 245 def get_super_types(self): 246 """ 247 Returns a set of all the supertypes of this interface 248 """ 249 return set(i2 for i2 in self) 250 251 def get_name(self): 252 """ 253 Get the name of this class 254 """ 255 return self.class_name 256 257 def get_tree(self): 258 """ 259 Print out a representation of the type tree of this class 260 """ 261 return "[{class_name} {iftree}]".format(class_name = self.get_name(), 262 iftree = print_tree(self.ifaces)) 263 264 def __iter__(self): 265 """ 266 Performs depth-first traversal of the interface tree this interface is the 267 root of. Does not filter out repeats. 268 """ 269 for i in self.ifaces: 270 yield i 271 yield from i 272 273 def __str__(self): 274 """ 275 Print the java code of this interface. 276 """ 277 j_ifaces = " " 278 for i in self.ifaces: 279 j_ifaces += " {},".format(i.get_name()) 280 j_ifaces = j_ifaces[0:-1] 281 if self.default: 282 funcs = self.DEFAULT_FUNC_TEMPLATE.format(ifaces = j_ifaces, 283 tree = self.get_tree(), 284 class_name = self.class_name) 285 else: 286 funcs = "" 287 return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('java'), 288 extends = "extends" if len(self.ifaces) else "", 289 ifaces = j_ifaces, 290 funcs = funcs, 291 tree = self.get_tree(), 292 class_name = self.class_name) 293 294 def print_tree(ifaces): 295 """ 296 Prints a list of iface trees 297 """ 298 return " ".join(i.get_tree() for i in ifaces) 299 300 # The deduplicated output of subtree_sizes for each size up to 301 # MAX_LEAF_IFACE_PER_OBJECT. 302 SUBTREES = [set(tuple(sorted(l)) for l in subtree_sizes(i)) 303 for i in range(MAX_IFACE_DEPTH + 1)] 304 305 def create_interface_trees(): 306 """ 307 Return all legal interface trees 308 """ 309 def dump_supers(s): 310 """ 311 Does depth first traversal of all the interfaces in the list. 312 """ 313 for i in s: 314 yield i 315 yield from i 316 317 def create_interface_trees_inner(num, allow_default): 318 for split in SUBTREES[num]: 319 ifaces = [] 320 for sub in split: 321 if sub == 1: 322 ifaces.append([TestInterface([], allow_default)]) 323 if allow_default: 324 ifaces[-1].append(TestInterface([], False)) 325 else: 326 ifaces.append(list(create_interface_trees_inner(sub, allow_default))) 327 for supers in itertools.product(*ifaces): 328 all_supers = sorted(set(dump_supers(supers)) - set(supers)) 329 for i in range(len(all_supers) + 1): 330 for combo in itertools.combinations(all_supers, i): 331 yield TestInterface(list(combo) + list(supers), allow_default) 332 if allow_default: 333 for i in range(len(split)): 334 ifaces = [] 335 for sub, cs in zip(split, itertools.count()): 336 if sub == 1: 337 ifaces.append([TestInterface([], i == cs)]) 338 else: 339 ifaces.append(list(create_interface_trees_inner(sub, i == cs))) 340 for supers in itertools.product(*ifaces): 341 all_supers = sorted(set(dump_supers(supers)) - set(supers)) 342 for i in range(len(all_supers) + 1): 343 for combo in itertools.combinations(all_supers, i): 344 yield TestInterface(list(combo) + list(supers), False) 345 346 for num in range(1, MAX_IFACE_DEPTH): 347 yield from create_interface_trees_inner(num, True) 348 349 def create_all_test_files(): 350 """ 351 Creates all the objects representing the files in this test. They just need to 352 be dumped. 353 """ 354 mc = MainClass() 355 classes = {mc} 356 for tree in create_interface_trees(): 357 classes.add(tree) 358 for i in tree: 359 classes.add(i) 360 test_class = TestClass(tree) 361 mc.add_test(test_class) 362 classes.add(test_class) 363 return mc, classes 364 365 def main(argv): 366 java_dir = Path(argv[1]) 367 if not java_dir.exists() or not java_dir.is_dir(): 368 print("{} is not a valid java dir".format(java_dir), file=sys.stderr) 369 sys.exit(1) 370 expected_txt = Path(argv[2]) 371 mainclass, all_files = create_all_test_files() 372 with expected_txt.open('w') as out: 373 print(mainclass.get_expected(), file=out) 374 for f in all_files: 375 f.dump(java_dir) 376 377 if __name__ == '__main__': 378 main(sys.argv) 379