1# Copyright 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import sys
6import traceback
7
8import module as mojom
9
10# Support for writing mojom test cases.
11# RunTest(fn) will execute fn, catching any exceptions. fn should return
12# the number of errors that are encountered.
13#
14# EXPECT_EQ(a, b) and EXPECT_TRUE(b) will print error information if the
15# expectations are not true and return a non zero value. This allows test cases
16# to be written like this
17#
18# def Foo():
19#   errors = 0
20#   errors += EXPECT_EQ('test', test())
21#   ...
22#   return errors
23#
24# RunTest(foo)
25
26def FieldsAreEqual(field1, field2):
27  if field1 == field2:
28    return True
29  return field1.name == field2.name and \
30      KindsAreEqual(field1.kind, field2.kind) and \
31      field1.ordinal == field2.ordinal and \
32      field1.default == field2.default
33
34
35def KindsAreEqual(kind1, kind2):
36  if kind1 == kind2:
37    return True
38  if kind1.__class__ != kind2.__class__ or kind1.spec != kind2.spec:
39    return False
40  if kind1.__class__ == mojom.Kind:
41    return kind1.spec == kind2.spec
42  if kind1.__class__ == mojom.Struct:
43    if kind1.name != kind2.name or \
44        kind1.spec != kind2.spec or \
45        len(kind1.fields) != len(kind2.fields):
46      return False
47    for i in range(len(kind1.fields)):
48      if not FieldsAreEqual(kind1.fields[i], kind2.fields[i]):
49        return False
50    return True
51  if kind1.__class__ == mojom.Array:
52    return KindsAreEqual(kind1.kind, kind2.kind)
53  print 'Unknown Kind class: ', kind1.__class__.__name__
54  return False
55
56
57def ParametersAreEqual(parameter1, parameter2):
58  if parameter1 == parameter2:
59    return True
60  return parameter1.name == parameter2.name and \
61     parameter1.ordinal == parameter2.ordinal and \
62     parameter1.default == parameter2.default and \
63     KindsAreEqual(parameter1.kind, parameter2.kind)
64
65
66def MethodsAreEqual(method1, method2):
67  if method1 == method2:
68    return True
69  if method1.name != method2.name or \
70      method1.ordinal != method2.ordinal or \
71      len(method1.parameters) != len(method2.parameters):
72    return False
73  for i in range(len(method1.parameters)):
74    if not ParametersAreEqual(method1.parameters[i], method2.parameters[i]):
75      return False
76  return True
77
78
79def InterfacesAreEqual(interface1, interface2):
80  if interface1 == interface2:
81    return True
82  if interface1.name != interface2.name or \
83      len(interface1.methods) != len(interface2.methods):
84    return False
85  for i in range(len(interface1.methods)):
86    if not MethodsAreEqual(interface1.methods[i], interface2.methods[i]):
87      return False
88  return True
89
90
91def ModulesAreEqual(module1, module2):
92  if module1 == module2:
93    return True
94  if module1.name != module2.name or \
95      module1.namespace != module2.namespace or \
96      len(module1.structs) != len(module2.structs) or \
97      len(module1.interfaces) != len(module2.interfaces):
98    return False
99  for i in range(len(module1.structs)):
100    if not KindsAreEqual(module1.structs[i], module2.structs[i]):
101      return False
102  for i in range(len(module1.interfaces)):
103    if not InterfacesAreEqual(module1.interfaces[i], module2.interfaces[i]):
104      return False
105  return True
106
107
108# Builds and returns a Module suitable for testing/
109def BuildTestModule():
110  module = mojom.Module('test', 'testspace')
111  struct = module.AddStruct('teststruct')
112  struct.AddField('testfield1', mojom.INT32)
113  struct.AddField('testfield2', mojom.Array(mojom.INT32), 42)
114
115  interface = module.AddInterface('Server')
116  method = interface.AddMethod('Foo', 42)
117  method.AddParameter('foo', mojom.INT32)
118  method.AddParameter('bar', mojom.Array(struct))
119
120  return module
121
122
123# Tests if |module| is as built by BuildTestModule(). Returns the number of
124# errors
125def TestTestModule(module):
126  errors = 0
127
128  errors += EXPECT_EQ('test', module.name)
129  errors += EXPECT_EQ('testspace', module.namespace)
130  errors += EXPECT_EQ(1, len(module.structs))
131  errors += EXPECT_EQ('teststruct', module.structs[0].name)
132  errors += EXPECT_EQ(2, len(module.structs[0].fields))
133  errors += EXPECT_EQ('testfield1', module.structs[0].fields[0].name)
134  errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[0].kind)
135  errors += EXPECT_EQ('testfield2', module.structs[0].fields[1].name)
136  errors += EXPECT_EQ(mojom.Array, module.structs[0].fields[1].kind.__class__)
137  errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[1].kind.kind)
138
139  errors += EXPECT_EQ(1, len(module.interfaces))
140  errors += EXPECT_EQ('Server', module.interfaces[0].name)
141  errors += EXPECT_EQ(1, len(module.interfaces[0].methods))
142  errors += EXPECT_EQ('Foo', module.interfaces[0].methods[0].name)
143  errors += EXPECT_EQ(2, len(module.interfaces[0].methods[0].parameters))
144  errors += EXPECT_EQ('foo', module.interfaces[0].methods[0].parameters[0].name)
145  errors += EXPECT_EQ(mojom.INT32,
146                      module.interfaces[0].methods[0].parameters[0].kind)
147  errors += EXPECT_EQ('bar', module.interfaces[0].methods[0].parameters[1].name)
148  errors += EXPECT_EQ(
149    mojom.Array,
150    module.interfaces[0].methods[0].parameters[1].kind.__class__)
151  errors += EXPECT_EQ(
152    module.structs[0],
153    module.interfaces[0].methods[0].parameters[1].kind.kind)
154  return errors
155
156
157def PrintFailure(string):
158  stack = traceback.extract_stack()
159  frame = stack[len(stack)-3]
160  sys.stderr.write("ERROR at %s:%d, %s\n" % (frame[0], frame[1], string))
161  print "Traceback:"
162  for line in traceback.format_list(stack[:len(stack)-2]):
163    sys.stderr.write(line)
164
165
166def EXPECT_EQ(a, b):
167  if a != b:
168    PrintFailure("%s != %s" % (a, b))
169    return 1
170  return 0
171
172
173def EXPECT_TRUE(a):
174  if not a:
175    PrintFailure('Expecting True')
176    return 1
177  return 0
178
179
180def RunTest(fn):
181  sys.stdout.write('Running %s...' % fn.__name__)
182  try:
183    errors = fn()
184  except:
185    traceback.print_exc(sys.stderr)
186    errors = 1
187  if errors == 0:
188    sys.stdout.write('OK\n')
189  elif errors == 1:
190    sys.stdout.write('1 ERROR\n')
191  else:
192    sys.stdout.write('%d ERRORS\n' % errors)
193  return errors
194