blob: b2df49f70ee6c81d09702f29968638480463bcdb [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 964.
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 tree can have.
41MAX_IFACE_DEPTH = 3
42
Alex Lightd204ba52016-03-01 14:33:51 -080043class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin):
Alex Lighteb7c1442015-08-31 13:17:42 -070044 """
Alex Lightd204ba52016-03-01 14:33:51 -080045 A Main.java file containing the Main class and the main function. It will run
Alex Lighteb7c1442015-08-31 13:17:42 -070046 all the test functions we have.
47 """
48
49 MAIN_CLASS_TEMPLATE = """{copyright}
Alex Lightd204ba52016-03-01 14:33:51 -080050class Main {{
Alex Lighteb7c1442015-08-31 13:17:42 -070051{test_groups}
Alex Lighteb7c1442015-08-31 13:17:42 -070052{main_func}
Alex Lightd204ba52016-03-01 14:33:51 -080053}}
Alex Lighteb7c1442015-08-31 13:17:42 -070054"""
55
56 MAIN_FUNCTION_TEMPLATE = """
Alex Lightd204ba52016-03-01 14:33:51 -080057 public static void main(String[] args) {{
Alex Lighteb7c1442015-08-31 13:17:42 -070058 {test_group_invoke}
Alex Lightd204ba52016-03-01 14:33:51 -080059 }}
Alex Lighteb7c1442015-08-31 13:17:42 -070060"""
61
62 TEST_GROUP_INVOKE_TEMPLATE = """
Alex Lightd204ba52016-03-01 14:33:51 -080063 {test_name}();
Alex Lighteb7c1442015-08-31 13:17:42 -070064"""
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 """
Alex Lightd204ba52016-03-01 14:33:51 -080093 Print the java code for this test.
Alex Lighteb7c1442015-08-31 13:17:42 -070094 """
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
Alex Lightd204ba52016-03-01 14:33:51 -0800104 return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('java'),
Alex Lighteb7c1442015-08-31 13:17:42 -0700105 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 = """
Alex Lightd204ba52016-03-01 14:33:51 -0800115 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 }}
Alex Lighteb7c1442015-08-31 13:17:42 -0700128"""
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 """
Alex Lightd204ba52016-03-01 14:33:51 -0800146 Print the java code for this test function.
Alex Lighteb7c1442015-08-31 13:17:42 -0700147 """
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
Alex Lightd204ba52016-03-01 14:33:51 -0800167class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin):
Alex Lighteb7c1442015-08-31 13:17:42 -0700168 """
169 A class that will be instantiated to test interface initialization order.
170 """
171
172 TEST_CLASS_TEMPLATE = """{copyright}
Alex Lightd204ba52016-03-01 14:33:51 -0800173public class {class_name} implements {ifaces} {{
174 public void marker() {{
175 return;
176 }}
Alex Lighteb7c1442015-08-31 13:17:42 -0700177
Alex Lightd204ba52016-03-01 14:33:51 -0800178 public void touchAll() {{
179{touch_calls}
180 }}
181}}
Alex Lighteb7c1442015-08-31 13:17:42 -0700182"""
183
184 TOUCH_CALL_TEMPLATE = """
Alex Lightd204ba52016-03-01 14:33:51 -0800185 System.out.println("{class_name} touching {iface_name}");
186 {iface_name}.field.touch();
Alex Lighteb7c1442015-08-31 13:17:42 -0700187"""
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 """
Alex Lightd204ba52016-03-01 14:33:51 -0800231 Print the java code for this class.
Alex Lighteb7c1442015-08-31 13:17:42 -0700232 """
Alex Lighteb7c1442015-08-31 13:17:42 -0700233 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()))
Alex Lightd204ba52016-03-01 14:33:51 -0800237 return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('java'),
Alex Lighteb7c1442015-08-31 13:17:42 -0700238 ifaces = j_ifaces,
239 class_name = self.class_name,
240 touch_calls = touches)
241
Alex Lightd204ba52016-03-01 14:33:51 -0800242class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin):
Alex Lighteb7c1442015-08-31 13:17:42 -0700243 """
244 An interface that will be used to test default method resolution order.
245 """
246
247 TEST_INTERFACE_TEMPLATE = """{copyright}
Alex Lightd204ba52016-03-01 14:33:51 -0800248public interface {class_name} {extends} {ifaces} {{
249 public static final Displayer field = new Displayer("{tree}");
250 public void marker();
Alex Lighteb7c1442015-08-31 13:17:42 -0700251{funcs}
Alex Lightd204ba52016-03-01 14:33:51 -0800252}}
Alex Lighteb7c1442015-08-31 13:17:42 -0700253"""
254
255 DEFAULT_FUNC_TEMPLATE = """
Alex Lightd204ba52016-03-01 14:33:51 -0800256 public default void {class_name}_DEFAULT_FUNC() {{ return; }}
Alex Lighteb7c1442015-08-31 13:17:42 -0700257"""
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 """
Alex Lightd204ba52016-03-01 14:33:51 -0800323 Print the java code for this interface.
Alex Lighteb7c1442015-08-31 13:17:42 -0700324 """
Alex Lighteb7c1442015-08-31 13:17:42 -0700325 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 = ""
Alex Lightd204ba52016-03-01 14:33:51 -0800330 return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('java'),
Alex Lighteb7c1442015-08-31 13:17:42 -0700331 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)))
Alex Light6484b802015-11-12 15:38:24 -0800368 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)))
Alex Lighteb7c1442015-08-31 13:17:42 -0700374
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):
Alex Lightd204ba52016-03-01 14:33:51 -0800407 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)
Alex Lighteb7c1442015-08-31 13:17:42 -0700410 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:
Alex Lightd204ba52016-03-01 14:33:51 -0800416 f.dump(java_dir)
Alex Lighteb7c1442015-08-31 13:17:42 -0700417
418if __name__ == '__main__':
419 main(sys.argv)