blob: a205cd6ce06529e8925bfaec20e8f4f706b6c041 [file] [log] [blame]
Alex Lighteb7c1442015-08-31 13:17:42 -07001#!/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"""
Alex Lightd204ba52016-03-01 14:33:51 -080018Generate Java test files for test 961.
Alex Lighteb7c1442015-08-31 13:17:42 -070019"""
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
Alex Lightd204ba52016-03-01 14:33:51 -080046class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin):
Alex Lighteb7c1442015-08-31 13:17:42 -070047 """
Alex Lightd204ba52016-03-01 14:33:51 -080048 A Main.java file containing the Main class and the main function. It will run
Alex Lighteb7c1442015-08-31 13:17:42 -070049 all the test functions we have.
50 """
51
52 MAIN_CLASS_TEMPLATE = """{copyright}
Alex Lightd204ba52016-03-01 14:33:51 -080053class Main {{
Alex Lighteb7c1442015-08-31 13:17:42 -070054{test_groups}
Alex Lighteb7c1442015-08-31 13:17:42 -070055{main_func}
Alex Lightd204ba52016-03-01 14:33:51 -080056}}
Alex Lighteb7c1442015-08-31 13:17:42 -070057"""
58
59 MAIN_FUNCTION_TEMPLATE = """
Alex Lightd204ba52016-03-01 14:33:51 -080060 public static void main(String[] args) {{
Alex Lighteb7c1442015-08-31 13:17:42 -070061 {test_group_invoke}
Alex Lightd204ba52016-03-01 14:33:51 -080062 }}
Alex Lighteb7c1442015-08-31 13:17:42 -070063"""
64
65 TEST_GROUP_INVOKE_TEMPLATE = """
Alex Lightd204ba52016-03-01 14:33:51 -080066 {test_name}();
Alex Lighteb7c1442015-08-31 13:17:42 -070067"""
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 """
Alex Lightd204ba52016-03-01 14:33:51 -080096 Print the MainClass java code.
Alex Lighteb7c1442015-08-31 13:17:42 -070097 """
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
Alex Lightd204ba52016-03-01 14:33:51 -0800107 return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright("java"),
Alex Lighteb7c1442015-08-31 13:17:42 -0700108 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 = """
Alex Lightd204ba52016-03-01 14:33:51 -0800118 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 }}
Alex Lighteb7c1442015-08-31 13:17:42 -0700130"""
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 """
Alex Lightd204ba52016-03-01 14:33:51 -0800153 Print the java code of this function.
Alex Lighteb7c1442015-08-31 13:17:42 -0700154 """
155 return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg.get_name())
156
Alex Lightd204ba52016-03-01 14:33:51 -0800157class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin):
Alex Lighteb7c1442015-08-31 13:17:42 -0700158 """
159 A class that will be instantiated to test default method resolution order.
160 """
161
162 TEST_CLASS_TEMPLATE = """{copyright}
Alex Lightd204ba52016-03-01 14:33:51 -0800163public class {class_name} implements {iface_name} {{
164 public String CalledClassName() {{
165 return "{tree}";
166 }}
167}}
Alex Lighteb7c1442015-08-31 13:17:42 -0700168"""
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 """
Alex Lightd204ba52016-03-01 14:33:51 -0800210 Print the java code of this class.
Alex Lighteb7c1442015-08-31 13:17:42 -0700211 """
Alex Lightd204ba52016-03-01 14:33:51 -0800212 return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('java'),
Alex Lighteb7c1442015-08-31 13:17:42 -0700213 iface_name = self.iface.get_name(),
214 tree = self.get_tree(),
215 class_name = self.class_name)
216
Alex Lightd204ba52016-03-01 14:33:51 -0800217class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin):
Alex Lighteb7c1442015-08-31 13:17:42 -0700218 """
219 An interface that will be used to test default method resolution order.
220 """
221
222 TEST_INTERFACE_TEMPLATE = """{copyright}
Alex Lightd204ba52016-03-01 14:33:51 -0800223public interface {class_name} {extends} {ifaces} {{
224 public String CalledClassName();
Alex Lighteb7c1442015-08-31 13:17:42 -0700225
226{funcs}
Alex Lightd204ba52016-03-01 14:33:51 -0800227}}
Alex Lighteb7c1442015-08-31 13:17:42 -0700228"""
229
230 DEFAULT_FUNC_TEMPLATE = """
Alex Lightd204ba52016-03-01 14:33:51 -0800231 public default String CalledInterfaceName() {{
232 return "{tree}";
233 }}
Alex Lighteb7c1442015-08-31 13:17:42 -0700234"""
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 """
Alex Lightd204ba52016-03-01 14:33:51 -0800275 Print the java code of this interface.
Alex Lighteb7c1442015-08-31 13:17:42 -0700276 """
Alex Lighteb7c1442015-08-31 13:17:42 -0700277 j_ifaces = " "
278 for i in self.ifaces:
Alex Lighteb7c1442015-08-31 13:17:42 -0700279 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 = ""
Alex Lightd204ba52016-03-01 14:33:51 -0800287 return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('java'),
Alex Lighteb7c1442015-08-31 13:17:42 -0700288 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):
Alex Lightd204ba52016-03-01 14:33:51 -0800366 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)
Alex Lighteb7c1442015-08-31 13:17:42 -0700369 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:
Alex Lightd204ba52016-03-01 14:33:51 -0800375 f.dump(java_dir)
Alex Lighteb7c1442015-08-31 13:17:42 -0700376
377if __name__ == '__main__':
378 main(sys.argv)