xref: /aosp_15_r20/art/test/utils/python/generate_java_main.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/python3
2*795d594fSAndroid Build Coastguard Worker#
3*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2015 The Android Open Source Project
4*795d594fSAndroid Build Coastguard Worker#
5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*795d594fSAndroid Build Coastguard Worker#
9*795d594fSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*795d594fSAndroid Build Coastguard Worker#
11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*795d594fSAndroid Build Coastguard Worker# limitations under the License.
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker"""
18*795d594fSAndroid Build Coastguard WorkerGenerate Java Main file from a classes.xml file.
19*795d594fSAndroid Build Coastguard Worker"""
20*795d594fSAndroid Build Coastguard Worker
21*795d594fSAndroid Build Coastguard Workerimport os
22*795d594fSAndroid Build Coastguard Workerimport sys
23*795d594fSAndroid Build Coastguard Workerfrom pathlib import Path
24*795d594fSAndroid Build Coastguard Worker
25*795d594fSAndroid Build Coastguard WorkerBUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
26*795d594fSAndroid Build Coastguard Workerif BUILD_TOP is None:
27*795d594fSAndroid Build Coastguard Worker  print("ANDROID_BUILD_TOP not set. Please run build/envsetup.sh", file=sys.stderr)
28*795d594fSAndroid Build Coastguard Worker  sys.exit(1)
29*795d594fSAndroid Build Coastguard Worker
30*795d594fSAndroid Build Coastguard Worker# Allow us to import utils and mixins.
31*795d594fSAndroid Build Coastguard Workersys.path.append(str(Path(BUILD_TOP)/"art"/"test"/"utils"/"python"))
32*795d594fSAndroid Build Coastguard Worker
33*795d594fSAndroid Build Coastguard Workerfrom testgen.utils import get_copyright
34*795d594fSAndroid Build Coastguard Workerimport testgen.mixins as mixins
35*795d594fSAndroid Build Coastguard Worker
36*795d594fSAndroid Build Coastguard Workerfrom collections import namedtuple
37*795d594fSAndroid Build Coastguard Workerimport itertools
38*795d594fSAndroid Build Coastguard Workerimport functools
39*795d594fSAndroid Build Coastguard Workerimport xml.etree.ElementTree as ET
40*795d594fSAndroid Build Coastguard Worker
41*795d594fSAndroid Build Coastguard Workerclass MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin):
42*795d594fSAndroid Build Coastguard Worker  """
43*795d594fSAndroid Build Coastguard Worker  A mainclass and main method for this test.
44*795d594fSAndroid Build Coastguard Worker  """
45*795d594fSAndroid Build Coastguard Worker
46*795d594fSAndroid Build Coastguard Worker  MAIN_CLASS_TEMPLATE = """{copyright}
47*795d594fSAndroid Build Coastguard Workerclass Main {{
48*795d594fSAndroid Build Coastguard Worker{test_groups}
49*795d594fSAndroid Build Coastguard Worker{test_funcs}
50*795d594fSAndroid Build Coastguard Worker{main_func}
51*795d594fSAndroid Build Coastguard Worker}}
52*795d594fSAndroid Build Coastguard Worker"""
53*795d594fSAndroid Build Coastguard Worker
54*795d594fSAndroid Build Coastguard Worker  MAIN_FUNCTION_TEMPLATE = """
55*795d594fSAndroid Build Coastguard Worker  public static void main(String[] args) {{
56*795d594fSAndroid Build Coastguard Worker    {test_group_invoke}
57*795d594fSAndroid Build Coastguard Worker  }}
58*795d594fSAndroid Build Coastguard Worker"""
59*795d594fSAndroid Build Coastguard Worker
60*795d594fSAndroid Build Coastguard Worker  TEST_GROUP_INVOKE_TEMPLATE = """
61*795d594fSAndroid Build Coastguard Worker    {test_name}();
62*795d594fSAndroid Build Coastguard Worker"""
63*795d594fSAndroid Build Coastguard Worker
64*795d594fSAndroid Build Coastguard Worker  def __init__(self):
65*795d594fSAndroid Build Coastguard Worker    """
66*795d594fSAndroid Build Coastguard Worker    Initialize this MainClass
67*795d594fSAndroid Build Coastguard Worker    """
68*795d594fSAndroid Build Coastguard Worker    self.tests = set()
69*795d594fSAndroid Build Coastguard Worker    self.global_funcs = set()
70*795d594fSAndroid Build Coastguard Worker
71*795d594fSAndroid Build Coastguard Worker  def add_instance(self, it):
72*795d594fSAndroid Build Coastguard Worker    """
73*795d594fSAndroid Build Coastguard Worker    Add an instance test for the given class
74*795d594fSAndroid Build Coastguard Worker    """
75*795d594fSAndroid Build Coastguard Worker    self.tests.add(it)
76*795d594fSAndroid Build Coastguard Worker
77*795d594fSAndroid Build Coastguard Worker  def add_func(self, f):
78*795d594fSAndroid Build Coastguard Worker    """
79*795d594fSAndroid Build Coastguard Worker    Add a function to the class
80*795d594fSAndroid Build Coastguard Worker    """
81*795d594fSAndroid Build Coastguard Worker    self.global_funcs.add(f)
82*795d594fSAndroid Build Coastguard Worker
83*795d594fSAndroid Build Coastguard Worker  def get_name(self):
84*795d594fSAndroid Build Coastguard Worker    """
85*795d594fSAndroid Build Coastguard Worker    Get the name of this class
86*795d594fSAndroid Build Coastguard Worker    """
87*795d594fSAndroid Build Coastguard Worker    return "Main"
88*795d594fSAndroid Build Coastguard Worker
89*795d594fSAndroid Build Coastguard Worker  def __str__(self):
90*795d594fSAndroid Build Coastguard Worker    """
91*795d594fSAndroid Build Coastguard Worker    Print this class
92*795d594fSAndroid Build Coastguard Worker    """
93*795d594fSAndroid Build Coastguard Worker    all_tests = sorted(self.tests)
94*795d594fSAndroid Build Coastguard Worker    test_invoke = ""
95*795d594fSAndroid Build Coastguard Worker    test_groups = ""
96*795d594fSAndroid Build Coastguard Worker    for t in all_tests:
97*795d594fSAndroid Build Coastguard Worker      test_groups += str(t)
98*795d594fSAndroid Build Coastguard Worker    for t in sorted(all_tests):
99*795d594fSAndroid Build Coastguard Worker      test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name())
100*795d594fSAndroid Build Coastguard Worker    main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke)
101*795d594fSAndroid Build Coastguard Worker
102*795d594fSAndroid Build Coastguard Worker    funcs = ""
103*795d594fSAndroid Build Coastguard Worker    for f in sorted(self.global_funcs):
104*795d594fSAndroid Build Coastguard Worker      funcs += str(f)
105*795d594fSAndroid Build Coastguard Worker    return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('java'),
106*795d594fSAndroid Build Coastguard Worker                                           test_groups=test_groups,
107*795d594fSAndroid Build Coastguard Worker                                           main_func=main_func, test_funcs=funcs)
108*795d594fSAndroid Build Coastguard Worker
109*795d594fSAndroid Build Coastguard Worker
110*795d594fSAndroid Build Coastguard Workerclass InstanceTest(mixins.Named, mixins.NameComparableMixin):
111*795d594fSAndroid Build Coastguard Worker  """
112*795d594fSAndroid Build Coastguard Worker  A method that runs tests for a particular concrete type, It calls the test
113*795d594fSAndroid Build Coastguard Worker  cases for running it in all possible ways.
114*795d594fSAndroid Build Coastguard Worker  """
115*795d594fSAndroid Build Coastguard Worker
116*795d594fSAndroid Build Coastguard Worker  INSTANCE_TEST_TEMPLATE = """
117*795d594fSAndroid Build Coastguard Worker  public static void {test_name}() {{
118*795d594fSAndroid Build Coastguard Worker    System.out.println("Testing for type {ty}");
119*795d594fSAndroid Build Coastguard Worker    String s = "{ty}";
120*795d594fSAndroid Build Coastguard Worker    {ty} v = new {ty}();
121*795d594fSAndroid Build Coastguard Worker
122*795d594fSAndroid Build Coastguard Worker    {invokes}
123*795d594fSAndroid Build Coastguard Worker
124*795d594fSAndroid Build Coastguard Worker    System.out.println("End testing for type {ty}");
125*795d594fSAndroid Build Coastguard Worker  }}
126*795d594fSAndroid Build Coastguard Worker"""
127*795d594fSAndroid Build Coastguard Worker
128*795d594fSAndroid Build Coastguard Worker  TEST_INVOKE_TEMPLATE = """
129*795d594fSAndroid Build Coastguard Worker    {fname}(s, v);
130*795d594fSAndroid Build Coastguard Worker"""
131*795d594fSAndroid Build Coastguard Worker
132*795d594fSAndroid Build Coastguard Worker  def __init__(self, main, ty):
133*795d594fSAndroid Build Coastguard Worker    """
134*795d594fSAndroid Build Coastguard Worker    Initialize this test group for the given type
135*795d594fSAndroid Build Coastguard Worker    """
136*795d594fSAndroid Build Coastguard Worker    self.ty = ty
137*795d594fSAndroid Build Coastguard Worker    self.main = main
138*795d594fSAndroid Build Coastguard Worker    self.funcs = set()
139*795d594fSAndroid Build Coastguard Worker    self.main.add_instance(self)
140*795d594fSAndroid Build Coastguard Worker
141*795d594fSAndroid Build Coastguard Worker  def get_name(self):
142*795d594fSAndroid Build Coastguard Worker    """
143*795d594fSAndroid Build Coastguard Worker    Get the name of this test group
144*795d594fSAndroid Build Coastguard Worker    """
145*795d594fSAndroid Build Coastguard Worker    return "TEST_NAME_"+self.ty
146*795d594fSAndroid Build Coastguard Worker
147*795d594fSAndroid Build Coastguard Worker  def add_func(self, f):
148*795d594fSAndroid Build Coastguard Worker    """
149*795d594fSAndroid Build Coastguard Worker    Add a test function to this test group
150*795d594fSAndroid Build Coastguard Worker    """
151*795d594fSAndroid Build Coastguard Worker    self.main.add_func(f)
152*795d594fSAndroid Build Coastguard Worker    self.funcs.add(f)
153*795d594fSAndroid Build Coastguard Worker
154*795d594fSAndroid Build Coastguard Worker  def __str__(self):
155*795d594fSAndroid Build Coastguard Worker    """
156*795d594fSAndroid Build Coastguard Worker    Returns the java code for this function
157*795d594fSAndroid Build Coastguard Worker    """
158*795d594fSAndroid Build Coastguard Worker    func_invokes = ""
159*795d594fSAndroid Build Coastguard Worker    for f in sorted(self.funcs, key=lambda a: (a.func, a.farg)):
160*795d594fSAndroid Build Coastguard Worker      func_invokes += self.TEST_INVOKE_TEMPLATE.format(fname=f.get_name(),
161*795d594fSAndroid Build Coastguard Worker                                                       farg=f.farg)
162*795d594fSAndroid Build Coastguard Worker
163*795d594fSAndroid Build Coastguard Worker    return self.INSTANCE_TEST_TEMPLATE.format(test_name=self.get_name(), ty=self.ty,
164*795d594fSAndroid Build Coastguard Worker                                              invokes=func_invokes)
165*795d594fSAndroid Build Coastguard Worker
166*795d594fSAndroid Build Coastguard Workerclass Func(mixins.Named, mixins.NameComparableMixin):
167*795d594fSAndroid Build Coastguard Worker  """
168*795d594fSAndroid Build Coastguard Worker  A single test case that attempts to invoke a function on receiver of a given type.
169*795d594fSAndroid Build Coastguard Worker  """
170*795d594fSAndroid Build Coastguard Worker
171*795d594fSAndroid Build Coastguard Worker  TEST_FUNCTION_TEMPLATE = """
172*795d594fSAndroid Build Coastguard Worker  public static void {fname}(String s, {farg} v) {{
173*795d594fSAndroid Build Coastguard Worker    try {{
174*795d594fSAndroid Build Coastguard Worker      System.out.printf("%s-{invoke_type:<9} {farg:>9}.{callfunc}()='%s'\\n", s, v.{callfunc}());
175*795d594fSAndroid Build Coastguard Worker      return;
176*795d594fSAndroid Build Coastguard Worker    }} catch (Error e) {{
177*795d594fSAndroid Build Coastguard Worker      System.out.printf("%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n", s);
178*795d594fSAndroid Build Coastguard Worker      if (e instanceof IncompatibleClassChangeError) {{
179*795d594fSAndroid Build Coastguard Worker        System.out.printf("Exception is of type %s\\n", e.getClass().getName());
180*795d594fSAndroid Build Coastguard Worker      }} else {{
181*795d594fSAndroid Build Coastguard Worker        e.printStackTrace(System.out);
182*795d594fSAndroid Build Coastguard Worker      }}
183*795d594fSAndroid Build Coastguard Worker    }}
184*795d594fSAndroid Build Coastguard Worker  }}
185*795d594fSAndroid Build Coastguard Worker"""
186*795d594fSAndroid Build Coastguard Worker
187*795d594fSAndroid Build Coastguard Worker  def __init__(self, func, farg, invoke):
188*795d594fSAndroid Build Coastguard Worker    """
189*795d594fSAndroid Build Coastguard Worker    Initialize this test function for the given invoke type and argument
190*795d594fSAndroid Build Coastguard Worker    """
191*795d594fSAndroid Build Coastguard Worker    self.func = func
192*795d594fSAndroid Build Coastguard Worker    self.farg = farg
193*795d594fSAndroid Build Coastguard Worker    self.invoke = invoke
194*795d594fSAndroid Build Coastguard Worker
195*795d594fSAndroid Build Coastguard Worker  def get_name(self):
196*795d594fSAndroid Build Coastguard Worker    """
197*795d594fSAndroid Build Coastguard Worker    Get the name of this test
198*795d594fSAndroid Build Coastguard Worker    """
199*795d594fSAndroid Build Coastguard Worker    return "Test_Func_{}_{}_{}".format(self.func, self.farg, self.invoke)
200*795d594fSAndroid Build Coastguard Worker
201*795d594fSAndroid Build Coastguard Worker  def __str__(self):
202*795d594fSAndroid Build Coastguard Worker    """
203*795d594fSAndroid Build Coastguard Worker    Get the java code for this test function
204*795d594fSAndroid Build Coastguard Worker    """
205*795d594fSAndroid Build Coastguard Worker    return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(),
206*795d594fSAndroid Build Coastguard Worker                                              farg=self.farg,
207*795d594fSAndroid Build Coastguard Worker                                              invoke_type=self.invoke,
208*795d594fSAndroid Build Coastguard Worker                                              callfunc=self.func)
209*795d594fSAndroid Build Coastguard Worker
210*795d594fSAndroid Build Coastguard Workerdef flatten_classes(classes, c):
211*795d594fSAndroid Build Coastguard Worker  """
212*795d594fSAndroid Build Coastguard Worker  Iterate over all the classes 'c' can be used as
213*795d594fSAndroid Build Coastguard Worker  """
214*795d594fSAndroid Build Coastguard Worker  while c:
215*795d594fSAndroid Build Coastguard Worker    yield c
216*795d594fSAndroid Build Coastguard Worker    c = classes.get(c.super_class)
217*795d594fSAndroid Build Coastguard Worker
218*795d594fSAndroid Build Coastguard Workerdef flatten_class_methods(classes, c):
219*795d594fSAndroid Build Coastguard Worker  """
220*795d594fSAndroid Build Coastguard Worker  Iterate over all the methods 'c' can call
221*795d594fSAndroid Build Coastguard Worker  """
222*795d594fSAndroid Build Coastguard Worker  for c1 in flatten_classes(classes, c):
223*795d594fSAndroid Build Coastguard Worker    yield from c1.methods
224*795d594fSAndroid Build Coastguard Worker
225*795d594fSAndroid Build Coastguard Workerdef flatten_interfaces(dat, c):
226*795d594fSAndroid Build Coastguard Worker  """
227*795d594fSAndroid Build Coastguard Worker  Iterate over all the interfaces 'c' transitively implements
228*795d594fSAndroid Build Coastguard Worker  """
229*795d594fSAndroid Build Coastguard Worker  def get_ifaces(cl):
230*795d594fSAndroid Build Coastguard Worker    for i2 in cl.implements:
231*795d594fSAndroid Build Coastguard Worker      yield dat.interfaces[i2]
232*795d594fSAndroid Build Coastguard Worker      yield from get_ifaces(dat.interfaces[i2])
233*795d594fSAndroid Build Coastguard Worker
234*795d594fSAndroid Build Coastguard Worker  for cl in flatten_classes(dat.classes, c):
235*795d594fSAndroid Build Coastguard Worker    yield from get_ifaces(cl)
236*795d594fSAndroid Build Coastguard Worker
237*795d594fSAndroid Build Coastguard Workerdef flatten_interface_methods(dat, i):
238*795d594fSAndroid Build Coastguard Worker  """
239*795d594fSAndroid Build Coastguard Worker  Iterate over all the interface methods 'c' can call
240*795d594fSAndroid Build Coastguard Worker  """
241*795d594fSAndroid Build Coastguard Worker  yield from i.methods
242*795d594fSAndroid Build Coastguard Worker  for i2 in flatten_interfaces(dat, i):
243*795d594fSAndroid Build Coastguard Worker    yield from i2.methods
244*795d594fSAndroid Build Coastguard Worker
245*795d594fSAndroid Build Coastguard Workerdef make_main_class(dat):
246*795d594fSAndroid Build Coastguard Worker  """
247*795d594fSAndroid Build Coastguard Worker  Creates a Main.java file that runs all the tests
248*795d594fSAndroid Build Coastguard Worker  """
249*795d594fSAndroid Build Coastguard Worker  m = MainClass()
250*795d594fSAndroid Build Coastguard Worker  for c in dat.classes.values():
251*795d594fSAndroid Build Coastguard Worker    i = InstanceTest(m, c.name)
252*795d594fSAndroid Build Coastguard Worker    for clazz in flatten_classes(dat.classes, c):
253*795d594fSAndroid Build Coastguard Worker      for meth in flatten_class_methods(dat.classes, clazz):
254*795d594fSAndroid Build Coastguard Worker        i.add_func(Func(meth, clazz.name, 'virtual'))
255*795d594fSAndroid Build Coastguard Worker      for iface in flatten_interfaces(dat, clazz):
256*795d594fSAndroid Build Coastguard Worker        for meth in flatten_interface_methods(dat, iface):
257*795d594fSAndroid Build Coastguard Worker          i.add_func(Func(meth, clazz.name, 'virtual'))
258*795d594fSAndroid Build Coastguard Worker          i.add_func(Func(meth, iface.name, 'interface'))
259*795d594fSAndroid Build Coastguard Worker  return m
260*795d594fSAndroid Build Coastguard Worker
261*795d594fSAndroid Build Coastguard Workerclass TestData(namedtuple("TestData", ['classes', 'interfaces'])):
262*795d594fSAndroid Build Coastguard Worker  """
263*795d594fSAndroid Build Coastguard Worker  A class representing the classes.xml document.
264*795d594fSAndroid Build Coastguard Worker  """
265*795d594fSAndroid Build Coastguard Worker  pass
266*795d594fSAndroid Build Coastguard Worker
267*795d594fSAndroid Build Coastguard Workerclass Clazz(namedtuple("Clazz", ["name", "methods", "super_class", "implements"])):
268*795d594fSAndroid Build Coastguard Worker  """
269*795d594fSAndroid Build Coastguard Worker  A class representing a class element in the classes.xml document.
270*795d594fSAndroid Build Coastguard Worker  """
271*795d594fSAndroid Build Coastguard Worker  pass
272*795d594fSAndroid Build Coastguard Worker
273*795d594fSAndroid Build Coastguard Workerclass IFace(namedtuple("IFace", ["name", "methods", "super_class", "implements"])):
274*795d594fSAndroid Build Coastguard Worker  """
275*795d594fSAndroid Build Coastguard Worker  A class representing an interface element in the classes.xml document.
276*795d594fSAndroid Build Coastguard Worker  """
277*795d594fSAndroid Build Coastguard Worker  pass
278*795d594fSAndroid Build Coastguard Worker
279*795d594fSAndroid Build Coastguard Workerdef parse_xml(xml):
280*795d594fSAndroid Build Coastguard Worker  """
281*795d594fSAndroid Build Coastguard Worker  Parse the xml description of this test.
282*795d594fSAndroid Build Coastguard Worker  """
283*795d594fSAndroid Build Coastguard Worker  classes = dict()
284*795d594fSAndroid Build Coastguard Worker  ifaces  = dict()
285*795d594fSAndroid Build Coastguard Worker  root = ET.fromstring(xml)
286*795d594fSAndroid Build Coastguard Worker  for iface in root.find("interfaces"):
287*795d594fSAndroid Build Coastguard Worker    name = iface.attrib['name']
288*795d594fSAndroid Build Coastguard Worker    implements = [a.text for a in iface.find("implements")]
289*795d594fSAndroid Build Coastguard Worker    methods = [a.text for a in iface.find("methods")]
290*795d594fSAndroid Build Coastguard Worker    ifaces[name] = IFace(name = name,
291*795d594fSAndroid Build Coastguard Worker                         super_class = iface.attrib['super'],
292*795d594fSAndroid Build Coastguard Worker                         methods = methods,
293*795d594fSAndroid Build Coastguard Worker                         implements = implements)
294*795d594fSAndroid Build Coastguard Worker  for clazz in root.find('classes'):
295*795d594fSAndroid Build Coastguard Worker    name = clazz.attrib['name']
296*795d594fSAndroid Build Coastguard Worker    implements = [a.text for a in clazz.find("implements")]
297*795d594fSAndroid Build Coastguard Worker    methods = [a.text for a in clazz.find("methods")]
298*795d594fSAndroid Build Coastguard Worker    classes[name] = Clazz(name = name,
299*795d594fSAndroid Build Coastguard Worker                          super_class = clazz.attrib['super'],
300*795d594fSAndroid Build Coastguard Worker                          methods = methods,
301*795d594fSAndroid Build Coastguard Worker                          implements = implements)
302*795d594fSAndroid Build Coastguard Worker  return TestData(classes, ifaces)
303*795d594fSAndroid Build Coastguard Worker
304*795d594fSAndroid Build Coastguard Workerdef main(argv):
305*795d594fSAndroid Build Coastguard Worker  java_dir = Path(argv[1])
306*795d594fSAndroid Build Coastguard Worker  if not java_dir.exists() or not java_dir.is_dir():
307*795d594fSAndroid Build Coastguard Worker    print("{} is not a valid java dir".format(java_dir), file=sys.stderr)
308*795d594fSAndroid Build Coastguard Worker    sys.exit(1)
309*795d594fSAndroid Build Coastguard Worker  class_data = parse_xml((java_dir / "classes.xml").open().read())
310*795d594fSAndroid Build Coastguard Worker  make_main_class(class_data).dump(java_dir)
311*795d594fSAndroid Build Coastguard Worker
312*795d594fSAndroid Build Coastguard Workerif __name__ == '__main__':
313*795d594fSAndroid Build Coastguard Worker  main(sys.argv)
314