|
1 import WebIDL |
|
2 import itertools |
|
3 import string |
|
4 |
|
5 # We'd like to use itertools.chain but it's 2.6 or higher. |
|
6 def chain(*iterables): |
|
7 # chain('ABC', 'DEF') --> A B C D E F |
|
8 for it in iterables: |
|
9 for element in it: |
|
10 yield element |
|
11 |
|
12 # We'd like to use itertools.combinations but it's 2.6 or higher. |
|
13 def combinations(iterable, r): |
|
14 # combinations('ABCD', 2) --> AB AC AD BC BD CD |
|
15 # combinations(range(4), 3) --> 012 013 023 123 |
|
16 pool = tuple(iterable) |
|
17 n = len(pool) |
|
18 if r > n: |
|
19 return |
|
20 indices = range(r) |
|
21 yield tuple(pool[i] for i in indices) |
|
22 while True: |
|
23 for i in reversed(range(r)): |
|
24 if indices[i] != i + n - r: |
|
25 break |
|
26 else: |
|
27 return |
|
28 indices[i] += 1 |
|
29 for j in range(i+1, r): |
|
30 indices[j] = indices[j-1] + 1 |
|
31 yield tuple(pool[i] for i in indices) |
|
32 |
|
33 # We'd like to use itertools.combinations_with_replacement but it's 2.7 or |
|
34 # higher. |
|
35 def combinations_with_replacement(iterable, r): |
|
36 # combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC |
|
37 pool = tuple(iterable) |
|
38 n = len(pool) |
|
39 if not n and r: |
|
40 return |
|
41 indices = [0] * r |
|
42 yield tuple(pool[i] for i in indices) |
|
43 while True: |
|
44 for i in reversed(range(r)): |
|
45 if indices[i] != n - 1: |
|
46 break |
|
47 else: |
|
48 return |
|
49 indices[i:] = [indices[i] + 1] * (r - i) |
|
50 yield tuple(pool[i] for i in indices) |
|
51 |
|
52 def WebIDLTest(parser, harness): |
|
53 types = ["float", |
|
54 "double", |
|
55 "short", |
|
56 "unsigned short", |
|
57 "long", |
|
58 "unsigned long", |
|
59 "long long", |
|
60 "unsigned long long", |
|
61 "boolean", |
|
62 "byte", |
|
63 "octet", |
|
64 "DOMString", |
|
65 "ByteString", |
|
66 #"sequence<float>", |
|
67 "object", |
|
68 "ArrayBuffer", |
|
69 #"Date", |
|
70 "TestInterface1", |
|
71 "TestInterface2"] |
|
72 |
|
73 testPre = """ |
|
74 interface TestInterface1 { |
|
75 }; |
|
76 interface TestInterface2 { |
|
77 }; |
|
78 """ |
|
79 |
|
80 interface = testPre + """ |
|
81 interface PrepareForTest { |
|
82 """ |
|
83 for (i, type) in enumerate(types): |
|
84 interface += string.Template(""" |
|
85 readonly attribute ${type} attr${i}; |
|
86 """).substitute(i=i, type=type) |
|
87 interface += """ |
|
88 }; |
|
89 """ |
|
90 |
|
91 parser.parse(interface) |
|
92 results = parser.finish() |
|
93 |
|
94 iface = results[2] |
|
95 |
|
96 parser = parser.reset() |
|
97 |
|
98 def typesAreDistinguishable(t): |
|
99 return all(u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2)) |
|
100 def typesAreNotDistinguishable(t): |
|
101 return any(not u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2)) |
|
102 def unionTypeName(t): |
|
103 if len(t) > 2: |
|
104 t[0:2] = [unionTypeName(t[0:2])] |
|
105 return "(" + " or ".join(t) + ")" |
|
106 |
|
107 # typeCombinations is an iterable of tuples containing the name of the type |
|
108 # as a string and the parsed IDL type. |
|
109 def unionTypes(typeCombinations, predicate): |
|
110 for c in typeCombinations: |
|
111 if predicate(t[1] for t in c): |
|
112 yield unionTypeName([t[0] for t in c]) |
|
113 |
|
114 # We limit invalid union types with a union member type to the subset of 3 |
|
115 # types with one invalid combination. |
|
116 # typeCombinations is an iterable of tuples containing the name of the type |
|
117 # as a string and the parsed IDL type. |
|
118 def invalidUnionWithUnion(typeCombinations): |
|
119 for c in typeCombinations: |
|
120 if (typesAreNotDistinguishable((c[0][1], c[1][1])) and |
|
121 typesAreDistinguishable((c[1][1], c[2][1])) and |
|
122 typesAreDistinguishable((c[0][1], c[2][1]))): |
|
123 yield unionTypeName([t[0] for t in c]) |
|
124 |
|
125 # Create a list of tuples containing the name of the type as a string and |
|
126 # the parsed IDL type. |
|
127 types = zip(types, (a.type for a in iface.members)) |
|
128 |
|
129 validUnionTypes = chain(unionTypes(combinations(types, 2), typesAreDistinguishable), |
|
130 unionTypes(combinations(types, 3), typesAreDistinguishable)) |
|
131 invalidUnionTypes = chain(unionTypes(combinations_with_replacement(types, 2), typesAreNotDistinguishable), |
|
132 invalidUnionWithUnion(combinations(types, 3))) |
|
133 interface = testPre + """ |
|
134 interface TestUnion { |
|
135 """ |
|
136 for (i, type) in enumerate(validUnionTypes): |
|
137 interface += string.Template(""" |
|
138 void method${i}(${type} arg); |
|
139 ${type} returnMethod${i}(); |
|
140 attribute ${type} attr${i}; |
|
141 void arrayMethod${i}(${type}[] arg); |
|
142 ${type}[] arrayReturnMethod${i}(); |
|
143 attribute ${type}[] arrayAttr${i}; |
|
144 void optionalMethod${i}(${type}? arg); |
|
145 """).substitute(i=i, type=type) |
|
146 interface += """ |
|
147 }; |
|
148 """ |
|
149 parser.parse(interface) |
|
150 results = parser.finish() |
|
151 |
|
152 parser = parser.reset() |
|
153 |
|
154 for invalid in invalidUnionTypes: |
|
155 interface = testPre + string.Template(""" |
|
156 interface TestUnion { |
|
157 void method(${type} arg); |
|
158 }; |
|
159 """).substitute(type=invalid) |
|
160 |
|
161 threw = False |
|
162 try: |
|
163 parser.parse(interface) |
|
164 results = parser.finish() |
|
165 except: |
|
166 threw = True |
|
167 |
|
168 harness.ok(threw, "Should have thrown.") |
|
169 |
|
170 parser = parser.reset() |