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