| #!/usr/bin/env python |
| # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| IDLNamespace for PPAPI |
| |
| This file defines the behavior of the AST namespace which allows for resolving |
| a symbol as one or more AST nodes given a release or range of releases. |
| """ |
| |
| import sys |
| |
| from idl_option import GetOption, Option, ParseOptions |
| from idl_log import ErrOut, InfoOut, WarnOut |
| from idl_release import IDLRelease, IDLReleaseList |
| |
| Option('label', 'Use the specifed label blocks.', default='Chrome') |
| Option('namespace_debug', 'Use the specified release') |
| |
| |
| # |
| # IDLNamespace |
| # |
| # IDLNamespace provides a mapping between a symbol name and an IDLReleaseList |
| # which contains IDLRelease objects. It provides an interface for fetching |
| # one or more IDLNodes based on a release or range of releases. |
| # |
| class IDLNamespace(object): |
| def __init__(self, parent): |
| self._name_to_releases = {} |
| self._parent = parent |
| |
| def Dump(self): |
| for name in self._name_to_releases: |
| InfoOut.Log('NAME=%s' % name) |
| for cver in self._name_to_releases[name].GetReleases(): |
| InfoOut.Log(' %s' % cver) |
| InfoOut.Log('') |
| |
| def FindRelease(self, name, release): |
| verlist = self._name_to_releases.get(name, None) |
| if verlist == None: |
| if self._parent: |
| return self._parent.FindRelease(name, release) |
| else: |
| return None |
| return verlist.FindRelease(release) |
| |
| def FindRange(self, name, rmin, rmax): |
| verlist = self._name_to_releases.get(name, None) |
| if verlist == None: |
| if self._parent: |
| return self._parent.FindRange(name, rmin, rmax) |
| else: |
| return [] |
| return verlist.FindRange(rmin, rmax) |
| |
| def FindList(self, name): |
| verlist = self._name_to_releases.get(name, None) |
| if verlist == None: |
| if self._parent: |
| return self._parent.FindList(name) |
| return verlist |
| |
| def AddNode(self, node): |
| name = node.GetName() |
| verlist = self._name_to_releases.setdefault(name,IDLReleaseList()) |
| if GetOption('namespace_debug'): |
| print "Adding to namespace: %s" % node |
| return verlist.AddNode(node) |
| |
| |
| # |
| # Testing Code |
| # |
| |
| # |
| # MockNode |
| # |
| # Mocks the IDLNode to support error, warning handling, and string functions. |
| # |
| class MockNode(IDLRelease): |
| def __init__(self, name, rmin, rmax): |
| self.name = name |
| self.rmin = rmin |
| self.rmax = rmax |
| self.errors = [] |
| self.warns = [] |
| self.properties = { |
| 'NAME': name, |
| 'release': rmin, |
| 'deprecate' : rmax |
| } |
| |
| def __str__(self): |
| return '%s (%s : %s)' % (self.name, self.rmin, self.rmax) |
| |
| def GetName(self): |
| return self.name |
| |
| def Error(self, msg): |
| if GetOption('release_debug'): print 'Error: %s' % msg |
| self.errors.append(msg) |
| |
| def Warn(self, msg): |
| if GetOption('release_debug'): print 'Warn: %s' % msg |
| self.warns.append(msg) |
| |
| def GetProperty(self, name): |
| return self.properties.get(name, None) |
| |
| errors = 0 |
| # |
| # DumpFailure |
| # |
| # Dumps all the information relevant to an add failure. |
| def DumpFailure(namespace, node, msg): |
| global errors |
| print '\n******************************' |
| print 'Failure: %s %s' % (node, msg) |
| for warn in node.warns: |
| print ' WARN: %s' % warn |
| for err in node.errors: |
| print ' ERROR: %s' % err |
| print '\n' |
| namespace.Dump() |
| print '******************************\n' |
| errors += 1 |
| |
| # Add expecting no errors or warnings |
| def AddOkay(namespace, node): |
| okay = namespace.AddNode(node) |
| if not okay or node.errors or node.warns: |
| DumpFailure(namespace, node, 'Expected success') |
| |
| # Add expecting a specific warning |
| def AddWarn(namespace, node, msg): |
| okay = namespace.AddNode(node) |
| if not okay or node.errors or not node.warns: |
| DumpFailure(namespace, node, 'Expected warnings') |
| if msg not in node.warns: |
| DumpFailure(namespace, node, 'Expected warning: %s' % msg) |
| |
| # Add expecting a specific error any any number of warnings |
| def AddError(namespace, node, msg): |
| okay = namespace.AddNode(node) |
| if okay or not node.errors: |
| DumpFailure(namespace, node, 'Expected errors') |
| if msg not in node.errors: |
| DumpFailure(namespace, node, 'Expected error: %s' % msg) |
| print ">>%s<<\n>>%s<<\n" % (node.errors[0], msg) |
| |
| # Verify that a FindRelease call on the namespace returns the expected node. |
| def VerifyFindOne(namespace, name, release, node): |
| global errors |
| if (namespace.FindRelease(name, release) != node): |
| print "Failed to find %s as release %f of %s" % (node, release, name) |
| namespace.Dump() |
| print "\n" |
| errors += 1 |
| |
| # Verify that a FindRage call on the namespace returns a set of expected nodes. |
| def VerifyFindAll(namespace, name, rmin, rmax, nodes): |
| global errors |
| out = namespace.FindRange(name, rmin, rmax) |
| if (out != nodes): |
| print "Found [%s] instead of[%s] for releases %f to %f of %s" % ( |
| ' '.join([str(x) for x in out]), |
| ' '.join([str(x) for x in nodes]), |
| rmin, |
| rmax, |
| name) |
| namespace.Dump() |
| print "\n" |
| errors += 1 |
| |
| def Main(args): |
| global errors |
| ParseOptions(args) |
| |
| InfoOut.SetConsole(True) |
| |
| namespace = IDLNamespace(None) |
| |
| FooXX = MockNode('foo', None, None) |
| Foo1X = MockNode('foo', 1.0, None) |
| Foo2X = MockNode('foo', 2.0, None) |
| Foo3X = MockNode('foo', 3.0, None) |
| |
| # Verify we succeed with undeprecated adds |
| AddOkay(namespace, FooXX) |
| AddOkay(namespace, Foo1X) |
| AddOkay(namespace, Foo3X) |
| # Verify we fail to add a node between undeprecated releases |
| AddError(namespace, Foo2X, |
| 'Overlap in releases: 3.0 vs 2.0 when adding foo (2.0 : None)') |
| |
| BarXX = MockNode('bar', None, None) |
| Bar12 = MockNode('bar', 1.0, 2.0) |
| Bar23 = MockNode('bar', 2.0, 3.0) |
| Bar34 = MockNode('bar', 3.0, 4.0) |
| |
| |
| # Verify we succeed with fully qualified releases |
| namespace = IDLNamespace(namespace) |
| AddOkay(namespace, BarXX) |
| AddOkay(namespace, Bar12) |
| # Verify we warn when detecting a gap |
| AddWarn(namespace, Bar34, 'Gap in release numbers.') |
| # Verify we fail when inserting into this gap |
| # (NOTE: while this could be legal, it is sloppy so we disallow it) |
| AddError(namespace, Bar23, 'Declarations out of order.') |
| |
| # Verify local namespace |
| VerifyFindOne(namespace, 'bar', 0.0, BarXX) |
| VerifyFindAll(namespace, 'bar', 0.5, 1.5, [BarXX, Bar12]) |
| |
| # Verify the correct release of the object is found recursively |
| VerifyFindOne(namespace, 'foo', 0.0, FooXX) |
| VerifyFindOne(namespace, 'foo', 0.5, FooXX) |
| VerifyFindOne(namespace, 'foo', 1.0, Foo1X) |
| VerifyFindOne(namespace, 'foo', 1.5, Foo1X) |
| VerifyFindOne(namespace, 'foo', 3.0, Foo3X) |
| VerifyFindOne(namespace, 'foo', 100.0, Foo3X) |
| |
| # Verify the correct range of objects is found |
| VerifyFindAll(namespace, 'foo', 0.0, 1.0, [FooXX]) |
| VerifyFindAll(namespace, 'foo', 0.5, 1.0, [FooXX]) |
| VerifyFindAll(namespace, 'foo', 1.0, 1.1, [Foo1X]) |
| VerifyFindAll(namespace, 'foo', 0.5, 1.5, [FooXX, Foo1X]) |
| VerifyFindAll(namespace, 'foo', 0.0, 3.0, [FooXX, Foo1X]) |
| VerifyFindAll(namespace, 'foo', 3.0, 100.0, [Foo3X]) |
| |
| FooBar = MockNode('foobar', 1.0, 2.0) |
| namespace = IDLNamespace(namespace) |
| AddOkay(namespace, FooBar) |
| |
| if errors: |
| print 'Test failed with %d errors.' % errors |
| else: |
| print 'Passed.' |
| return errors |
| |
| |
| if __name__ == '__main__': |
| sys.exit(Main(sys.argv[1:])) |