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""" 18Generate java test files for test 964. 19""" 20 21import os 22import sys 23from pathlib import Path 24 25BUILD_TOP = os.getenv("ANDROID_BUILD_TOP") 26if 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. 31sys.path.append(str(Path(BUILD_TOP)/"art"/"test"/"utils"/"python")) 32 33from testgen.utils import get_copyright, subtree_sizes, gensym, filter_blanks 34import testgen.mixins as mixins 35 36from functools import total_ordering 37import itertools 38import string 39 40# The max depth the tree can have. 41MAX_IFACE_DEPTH = 3 42 43class 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} 50class 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 108class 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 = """ 131About to initialize {tree} 132{initialize_output} 133Initialized {tree} 134{touch_output} 135All 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 167class 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} 173public 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 242class 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} 248public 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 337def 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 346def print_tree(ifaces): 347 """ 348 Prints the tree for the given ifaces. 349 """ 350 return " ".join(i.get_tree() for i in ifaces) 351 352def clone_all(l): 353 return tuple(a.clone() for a in l) 354 355# Cached output of subtree_sizes for speed of access. 356SUBTREES = [set(tuple(l) for l in subtree_sizes(i)) 357 for i in range(MAX_IFACE_DEPTH + 1)] 358 359def 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 375def 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 392def 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 406def 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 418if __name__ == '__main__': 419 main(sys.argv) 420