|
OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python | |
2 # | |
3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 """ | |
8 IDLNamespace for PPAPI | |
9 | |
10 This file defines the behavior of the AST namespace which allows for resolving | |
11 a symbol as one or more AST nodes given a version or range of versions. | |
12 """ | |
13 | |
14 import sys | |
15 | |
16 from idl_option import GetOption, Option, ParseOptions | |
17 from idl_log import ErrOut, InfoOut, WarnOut | |
18 from idl_version import IDLVersion | |
19 | |
20 Option('label', 'Use the specifed label blocks.', default='Chrome') | |
21 Option('version', 'Use the specified version', default='M14') | |
22 Option('namespace_debug', 'Use the specified version') | |
23 | |
24 | |
25 # | |
26 # IDLVersionList | |
27 # | |
28 # IDLVersionList is a list based container for holding IDLVersion | |
29 # objects. The IDLVersionList can be added to, and searched by | |
30 # range. | |
31 # | |
32 class IDLVersionList(object): | |
33 def __init__(self): | |
34 self.nodes = [] | |
35 | |
36 def FindVersion(self, version): | |
37 assert type(version) == float | |
38 | |
39 for node in self.nodes: | |
40 if node.IsVersion(version): | |
41 return node | |
42 return None | |
43 | |
44 def FindRange(self, vmin, vmax): | |
45 assert type(vmin) == float | |
46 assert type(vmax) == float | |
47 assert vmin != vmax | |
48 | |
49 out = [] | |
50 for node in self.nodes: | |
51 if node.InRange(vmin, vmax): | |
52 out.append(node) | |
53 return out | |
54 | |
55 def AddNode(self, node): | |
56 if GetOption('version_debug'): InfoOut.Log('\nAdding %s' % node) | |
57 last = None | |
58 | |
59 # Check current versions in that namespace | |
60 for cver in self.nodes: | |
61 if GetOption('version_debug'): InfoOut.Log(' Checking %s' % cver) | |
62 | |
63 # We should only be missing a 'version' tag for the first item. | |
64 if not node.vmin: | |
65 node.Error('Missing version on overload of previous %s.' % | |
66 cver.Location()) | |
67 raise RuntimeError('DSFSD') | |
68 return False | |
69 | |
70 # If the node has no max, then set it to this one | |
71 if not cver.vmax: | |
72 cver.vmax = node.vmin | |
73 if GetOption('version_debug'): InfoOut.Log(' Update %s' % cver) | |
74 | |
75 # if the max and min overlap, than's an error | |
sehr (please use chromium)
2011/07/01 22:58:51
This requires that versioned information appear in
noelallen1
2011/07/02 19:51:32
Done.
| |
76 if cver.vmax > node.vmin: | |
77 if node.vmax and cver.vmin >= node.vmax: | |
78 node.Error('Declarations out of order.') | |
79 else: | |
80 node.Error('Overlap in versions.') | |
81 return False | |
82 last = cver | |
83 | |
84 # Otherwise, the previous max and current min should match | |
85 # unless this is the unlikely case of something being only | |
86 # temporarily deprecated. | |
87 if last and last.vmax != node.vmin: | |
88 node.Warn('Gap in version numbers.') | |
89 | |
90 # If we made it here, this new node must be the 'newest' | |
91 # and does not overlap with anything previously added, so | |
92 # we can add it to the end of the list. | |
93 if GetOption('version_debug'): InfoOut.Log('Done %s' % node) | |
94 self.nodes.append(node) | |
95 return True | |
96 | |
97 # | |
98 # IDLVersionMap | |
99 # | |
100 # A version map, can map from an float interface version, to a global | |
101 # release string. | |
102 # | |
103 class IDLVersionMap(object): | |
104 def __init__(self): | |
105 self.version_to_release = {} | |
106 self.release_to_version = {} | |
107 self.versions = [] | |
108 | |
109 def AddReleaseVersionMapping(self, release, version): | |
110 self.version_to_release[version] = release | |
111 self.release_to_version[release] = version | |
112 self.versions = sorted(self.version_to_release.keys()) | |
113 | |
114 def GetRelease(self, version): | |
115 # Check for exact match | |
116 if version in self.versions: | |
117 return self.version_to_release[version] | |
118 | |
119 def GetVersion(self, release): | |
120 return self.release_to_version[release] | |
121 | |
122 | |
123 | |
124 # | |
125 # IDLNamespace | |
126 # | |
127 # IDLNamespace provides a mapping between a symbol name and an IDLVersionList | |
128 # which contains IDLVersion objects. It provides an interface for fetching | |
129 # one or more IDLNodes based on a version or range of versions. | |
130 # | |
131 class IDLNamespace(object): | |
132 def __init__(self, parent): | |
133 self.namespace = {} | |
134 self.parent = parent | |
135 | |
136 def Dump(self): | |
137 for name in self.namespace: | |
138 InfoOut.Log('NAME=%s' % name) | |
139 for cver in self.namespace[name]: | |
140 InfoOut.Log(' %s' % cver) | |
141 InfoOut.Log('') | |
142 | |
143 def FindVersion(self, name, version): | |
144 list = self.namespace.get(name, None) | |
145 if list == None: | |
146 if self.parent: | |
147 return self.parent.FindVersion(name, version) | |
148 else: | |
149 return None | |
150 return list.FindVersion(version) | |
151 | |
152 def FindRange(self, name, vmin, vmax): | |
153 list = self.namespace.get(name, None) | |
154 if list == None: | |
155 if self.parent: | |
156 return self.parent.FindRange(name, vmin, vmax) | |
157 else: | |
158 return [] | |
159 return list.FindRange(vmin, vmax) | |
160 | |
161 def FindList(self, name): | |
162 list = self.namespace.get(name, None) | |
163 if list == None: | |
164 if self.parent: | |
165 return self.parent.FindList(name) | |
166 return list | |
167 | |
168 def AddNode(self, node): | |
169 name = node.GetName() | |
170 list = self.namespace.setdefault(name,IDLVersionList()) | |
171 if GetOption('namespace_debug'): | |
172 print "Adding to namespace: %s" % node | |
173 return list.AddNode(node) | |
174 | |
175 | |
176 | |
177 # | |
178 # Testing Code | |
179 # | |
180 | |
181 # | |
182 # MockNode | |
183 # | |
184 # Mocks the IDLNode to support error, warning handling, and string functions. | |
185 # | |
186 class MockNode(IDLVersion): | |
187 def __init__(self, name, vmin, vmax): | |
188 self.name = name | |
189 self.vmin = vmin | |
190 self.vmax = vmax | |
191 self.errors = [] | |
192 self.warns = [] | |
193 self.properties = { | |
194 'NAME': name, | |
195 'version': vmin, | |
196 'deprecate' : vmax | |
197 } | |
198 | |
199 def __str__(self): | |
200 return '%s (%s : %s)' % (self.name, self.vmin, self.vmax) | |
201 | |
202 def GetName(self): | |
203 return self.name | |
204 | |
205 def Error(self, msg): | |
206 if GetOption('version_debug'): print 'Error: %s' % msg | |
207 self.errors.append(msg) | |
208 | |
209 def Warn(self, msg): | |
210 if GetOption('version_debug'): print 'Warn: %s' % msg | |
211 self.warns.append(msg) | |
212 | |
213 def GetProperty(self, name): | |
214 return self.properties.get(name, None) | |
215 | |
216 errors = 0 | |
217 # | |
218 # DumpFailure | |
219 # | |
220 # Dumps all the information relavent to an add failure. | |
sehr (please use chromium)
2011/07/01 22:58:51
relevant
noelallen1
2011/07/02 19:51:32
Done.
| |
221 def DumpFailure(namespace, node, msg): | |
222 global errors | |
223 print '\n******************************' | |
224 print 'Failure: %s %s' % (node, msg) | |
225 for warn in node.warns: | |
226 print ' WARN: %s' % warn | |
227 for err in node.errors: | |
228 print ' ERROR: %s' % err | |
229 print '\n' | |
230 namespace.Dump() | |
231 print '******************************\n' | |
232 errors += 1 | |
233 | |
234 # Add expecting no errors or warnings | |
235 def AddOkay(namespace, node): | |
236 okay = namespace.AddNode(node) | |
237 if not okay or node.errors or node.warns: | |
238 DumpFailure(namespace, node, 'Expected success') | |
239 | |
240 # Add expecting a specific warning | |
241 def AddWarn(namespace, node, msg): | |
242 okay = namespace.AddNode(node) | |
243 if not okay or node.errors or not node.warns: | |
244 DumpFailure(namespace, node, 'Expected warnings') | |
245 if msg not in node.warns: | |
246 DumpFailure(namespace, node, 'Expected warning: %s' % msg) | |
247 | |
248 # Add expecting a specific error any any number of warnings | |
249 def AddError(namespace, node, msg): | |
250 okay = namespace.AddNode(node) | |
251 if okay or not node.errors: | |
252 DumpFailure(namespace, node, 'Expected errors') | |
253 if msg not in node.errors: | |
254 DumpFailure(namespace, node, 'Expected error: %s' % msg) | |
255 | |
256 # Verify the namespace find the correct node based on version | |
257 def VerifyFindOne(namespace, name, version, node): | |
258 global errors | |
259 if (namespace.FindVersion(name, version) != node): | |
260 print "Failed to find %s as version %f of %s" % (node, version, name) | |
261 namespace.Dump() | |
262 print "\n" | |
263 errors += 1 | |
264 | |
265 # Verify the namespace find the correct nodes based on range | |
sehr (please use chromium)
2011/07/01 22:58:51
Can't parse this comment.
noelallen1
2011/07/02 19:51:32
Done.
| |
266 def VerifyFindAll(namespace, name, vmin, vmax, nodes): | |
267 global errors | |
268 out = namespace.FindRange(name, vmin, vmax) | |
269 if (out != nodes): | |
270 print "Found [%s] instead of[%s] for versions %f to %f of %s" % ( | |
271 ' '.join([str(x) for x in out]), | |
272 ' '.join([str(x) for x in nodes]), | |
273 vmin, | |
274 vmax, | |
275 name) | |
276 namespace.Dump() | |
277 print "\n" | |
278 errors += 1 | |
279 | |
280 def Main(args): | |
281 global errors | |
282 ParseOptions(args) | |
283 | |
284 InfoOut.SetConsole(True) | |
285 | |
286 namespace = IDLNamespace(None) | |
287 | |
288 FooXX = MockNode('foo', None, None) | |
289 Foo1X = MockNode('foo', 1.0, None) | |
290 Foo2X = MockNode('foo', 2.0, None) | |
291 Foo3X = MockNode('foo', 3.0, None) | |
292 | |
293 #Verify we succeed with undeprecated adds | |
sehr (please use chromium)
2011/07/01 22:58:51
s/#V/# V/
noelallen1
2011/07/02 19:51:32
Done.
| |
294 AddOkay(namespace, FooXX) | |
295 AddOkay(namespace, Foo1X) | |
296 AddOkay(namespace, Foo3X) | |
297 # Verify we fail to add a node between undeprecated versions | |
298 AddError(namespace, Foo2X, 'Overlap in versions.') | |
299 | |
300 BarXX = MockNode('bar', None, None) | |
301 Bar12 = MockNode('bar', 1.0, 2.0) | |
302 Bar23 = MockNode('bar', 2.0, 3.0) | |
303 Bar34 = MockNode('bar', 3.0, 4.0) | |
304 | |
305 # Verify we succeed with fully qualified versions | |
306 namespace = IDLNamespace(namespace) | |
307 AddOkay(namespace, BarXX) | |
308 AddOkay(namespace, Bar12) | |
309 # Verify we warn when detecting a gap | |
310 AddWarn(namespace, Bar34, 'Gap in version numbers.') | |
311 # Verify we fail when inserting into this gap | |
312 # (NOTE: while this could be legal, it is sloppy so we disallow it) | |
313 AddError(namespace, Bar23, 'Declarations out of order.') | |
314 | |
315 # Verify local namespace | |
316 VerifyFindOne(namespace, 'bar', 0.0, BarXX) | |
317 VerifyFindAll(namespace, 'bar', 0.5, 1.5, [BarXX, Bar12]) | |
318 | |
319 # Verify the correct version of the object is found recursively | |
320 VerifyFindOne(namespace, 'foo', 0.0, FooXX) | |
321 VerifyFindOne(namespace, 'foo', 0.5, FooXX) | |
322 VerifyFindOne(namespace, 'foo', 1.0, Foo1X) | |
323 VerifyFindOne(namespace, 'foo', 1.5, Foo1X) | |
324 VerifyFindOne(namespace, 'foo', 3.0, Foo3X) | |
325 VerifyFindOne(namespace, 'foo', 100.0, Foo3X) | |
326 | |
327 # Verify the correct range of objects is found | |
328 VerifyFindAll(namespace, 'foo', 0.0, 1.0, [FooXX]) | |
329 VerifyFindAll(namespace, 'foo', 0.5, 1.0, [FooXX]) | |
330 VerifyFindAll(namespace, 'foo', 1.0, 1.1, [Foo1X]) | |
331 VerifyFindAll(namespace, 'foo', 0.5, 1.5, [FooXX, Foo1X]) | |
332 VerifyFindAll(namespace, 'foo', 0.0, 3.0, [FooXX, Foo1X]) | |
333 VerifyFindAll(namespace, 'foo', 3.0, 100.0, [Foo3X]) | |
334 | |
335 | |
336 | |
337 if errors: | |
338 print 'Test failed with %d errors.' % errors | |
339 else: | |
340 print 'Passed.' | |
341 return errors | |
342 | |
343 if __name__ == '__main__': | |
344 sys.exit(Main(sys.argv[1:])) | |
345 | |
OLD | NEW |