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 961. 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 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. 44MAX_IFACE_DEPTH = 5 45 46class 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} 53class 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 111class 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 157class 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} 163public 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 217class 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} 223public 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 294def 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. 302SUBTREES = [set(tuple(sorted(l)) for l in subtree_sizes(i)) 303 for i in range(MAX_IFACE_DEPTH + 1)] 304 305def 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 349def 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 365def 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 377if __name__ == '__main__': 378 main(sys.argv) 379