Home | History | Annotate | Download | only in generators
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """
      7 IDLNamespace for PPAPI
      8 
      9 This file defines the behavior of the AST namespace which allows for resolving
     10 a symbol as one or more AST nodes given a release or range of releases.
     11 """
     12 
     13 import sys
     14 
     15 from idl_option import GetOption, Option, ParseOptions
     16 from idl_log import ErrOut, InfoOut, WarnOut
     17 from idl_release import IDLRelease, IDLReleaseList
     18 
     19 Option('label', 'Use the specifed label blocks.', default='Chrome')
     20 Option('namespace_debug', 'Use the specified release')
     21 
     22 
     23 #
     24 # IDLNamespace
     25 #
     26 # IDLNamespace provides a mapping between a symbol name and an IDLReleaseList
     27 # which contains IDLRelease objects.  It provides an interface for fetching
     28 # one or more IDLNodes based on a release or range of releases.
     29 #
     30 class IDLNamespace(object):
     31   def __init__(self, parent):
     32     self._name_to_releases = {}
     33     self._parent = parent
     34 
     35   def Dump(self):
     36     for name in self._name_to_releases:
     37       InfoOut.Log('NAME=%s' % name)
     38       for cver in self._name_to_releases[name].GetReleases():
     39         InfoOut.Log('  %s' % cver)
     40       InfoOut.Log('')
     41 
     42   def FindRelease(self, name, release):
     43     verlist = self._name_to_releases.get(name, None)
     44     if verlist == None:
     45       if self._parent:
     46         return self._parent.FindRelease(name, release)
     47       else:
     48         return None
     49     return verlist.FindRelease(release)
     50 
     51   def FindRange(self, name, rmin, rmax):
     52     verlist = self._name_to_releases.get(name, None)
     53     if verlist == None:
     54       if self._parent:
     55         return self._parent.FindRange(name, rmin, rmax)
     56       else:
     57         return []
     58     return verlist.FindRange(rmin, rmax)
     59 
     60   def FindList(self, name):
     61     verlist = self._name_to_releases.get(name, None)
     62     if verlist == None:
     63       if self._parent:
     64         return self._parent.FindList(name)
     65     return verlist
     66 
     67   def AddNode(self, node):
     68     name = node.GetName()
     69     verlist = self._name_to_releases.setdefault(name,IDLReleaseList())
     70     if GetOption('namespace_debug'):
     71         print "Adding to namespace: %s" % node
     72     return verlist.AddNode(node)
     73 
     74 
     75 #
     76 # Testing Code
     77 #
     78 
     79 #
     80 # MockNode
     81 #
     82 # Mocks the IDLNode to support error, warning handling, and string functions.
     83 #
     84 class MockNode(IDLRelease):
     85   def __init__(self, name, rmin, rmax):
     86     self.name = name
     87     self.rmin = rmin
     88     self.rmax = rmax
     89     self.errors = []
     90     self.warns = []
     91     self.properties = {
     92         'NAME': name,
     93         'release': rmin,
     94         'deprecate' : rmax
     95         }
     96 
     97   def __str__(self):
     98     return '%s (%s : %s)' % (self.name, self.rmin, self.rmax)
     99 
    100   def GetName(self):
    101     return self.name
    102 
    103   def Error(self, msg):
    104     if GetOption('release_debug'): print 'Error: %s' % msg
    105     self.errors.append(msg)
    106 
    107   def Warn(self, msg):
    108     if GetOption('release_debug'): print 'Warn: %s' % msg
    109     self.warns.append(msg)
    110 
    111   def GetProperty(self, name):
    112     return self.properties.get(name, None)
    113 
    114 errors = 0
    115 #
    116 # DumpFailure
    117 #
    118 # Dumps all the information relevant  to an add failure.
    119 def DumpFailure(namespace, node, msg):
    120   global errors
    121   print '\n******************************'
    122   print 'Failure: %s %s' % (node, msg)
    123   for warn in node.warns:
    124     print '  WARN: %s' % warn
    125   for err in node.errors:
    126     print '  ERROR: %s' % err
    127   print '\n'
    128   namespace.Dump()
    129   print '******************************\n'
    130   errors += 1
    131 
    132 # Add expecting no errors or warnings
    133 def AddOkay(namespace, node):
    134   okay = namespace.AddNode(node)
    135   if not okay or node.errors or node.warns:
    136     DumpFailure(namespace, node, 'Expected success')
    137 
    138 # Add expecting a specific warning
    139 def AddWarn(namespace, node, msg):
    140   okay = namespace.AddNode(node)
    141   if not okay or node.errors or not node.warns:
    142     DumpFailure(namespace, node, 'Expected warnings')
    143   if msg not in node.warns:
    144     DumpFailure(namespace, node, 'Expected warning: %s' % msg)
    145 
    146 # Add expecting a specific error any any number of warnings
    147 def AddError(namespace, node, msg):
    148   okay = namespace.AddNode(node)
    149   if okay or not node.errors:
    150     DumpFailure(namespace, node, 'Expected errors')
    151   if msg not in node.errors:
    152     DumpFailure(namespace, node, 'Expected error: %s' % msg)
    153     print ">>%s<<\n>>%s<<\n" % (node.errors[0], msg)
    154 
    155 # Verify that a FindRelease call on the namespace returns the expected node.
    156 def VerifyFindOne(namespace, name, release, node):
    157   global errors
    158   if (namespace.FindRelease(name, release) != node):
    159     print "Failed to find %s as release %f of %s" % (node, release, name)
    160     namespace.Dump()
    161     print "\n"
    162     errors += 1
    163 
    164 # Verify that a FindRage call on the namespace returns a set of expected nodes.
    165 def VerifyFindAll(namespace, name, rmin, rmax, nodes):
    166   global errors
    167   out = namespace.FindRange(name, rmin, rmax)
    168   if (out != nodes):
    169     print "Found [%s] instead of[%s] for releases %f to %f of %s" % (
    170         ' '.join([str(x) for x in out]),
    171         ' '.join([str(x) for x in nodes]),
    172         rmin,
    173         rmax,
    174         name)
    175     namespace.Dump()
    176     print "\n"
    177     errors += 1
    178 
    179 def Main(args):
    180   global errors
    181   ParseOptions(args)
    182 
    183   InfoOut.SetConsole(True)
    184 
    185   namespace = IDLNamespace(None)
    186 
    187   FooXX = MockNode('foo', None, None)
    188   Foo1X = MockNode('foo', 1.0, None)
    189   Foo2X = MockNode('foo', 2.0, None)
    190   Foo3X = MockNode('foo', 3.0, None)
    191 
    192   # Verify we succeed with undeprecated adds
    193   AddOkay(namespace, FooXX)
    194   AddOkay(namespace, Foo1X)
    195   AddOkay(namespace, Foo3X)
    196   # Verify we fail to add a node between undeprecated releases
    197   AddError(namespace, Foo2X,
    198            'Overlap in releases: 3.0 vs 2.0 when adding foo (2.0 : None)')
    199 
    200   BarXX = MockNode('bar', None, None)
    201   Bar12 = MockNode('bar', 1.0, 2.0)
    202   Bar23 = MockNode('bar', 2.0, 3.0)
    203   Bar34 = MockNode('bar', 3.0, 4.0)
    204 
    205 
    206   # Verify we succeed with fully qualified releases
    207   namespace = IDLNamespace(namespace)
    208   AddOkay(namespace, BarXX)
    209   AddOkay(namespace, Bar12)
    210   # Verify we warn when detecting a gap
    211   AddWarn(namespace, Bar34, 'Gap in release numbers.')
    212   # Verify we fail when inserting into this gap
    213   # (NOTE: while this could be legal, it is sloppy so we disallow it)
    214   AddError(namespace, Bar23, 'Declarations out of order.')
    215 
    216   # Verify local namespace
    217   VerifyFindOne(namespace, 'bar', 0.0, BarXX)
    218   VerifyFindAll(namespace, 'bar', 0.5, 1.5, [BarXX, Bar12])
    219 
    220   # Verify the correct release of the object is found recursively
    221   VerifyFindOne(namespace, 'foo', 0.0, FooXX)
    222   VerifyFindOne(namespace, 'foo', 0.5, FooXX)
    223   VerifyFindOne(namespace, 'foo', 1.0, Foo1X)
    224   VerifyFindOne(namespace, 'foo', 1.5, Foo1X)
    225   VerifyFindOne(namespace, 'foo', 3.0, Foo3X)
    226   VerifyFindOne(namespace, 'foo', 100.0, Foo3X)
    227 
    228   # Verify the correct range of objects is found
    229   VerifyFindAll(namespace, 'foo', 0.0, 1.0, [FooXX])
    230   VerifyFindAll(namespace, 'foo', 0.5, 1.0, [FooXX])
    231   VerifyFindAll(namespace, 'foo', 1.0, 1.1, [Foo1X])
    232   VerifyFindAll(namespace, 'foo', 0.5, 1.5, [FooXX, Foo1X])
    233   VerifyFindAll(namespace, 'foo', 0.0, 3.0, [FooXX, Foo1X])
    234   VerifyFindAll(namespace, 'foo', 3.0, 100.0, [Foo3X])
    235 
    236   FooBar = MockNode('foobar', 1.0, 2.0)
    237   namespace = IDLNamespace(namespace)
    238   AddOkay(namespace, FooBar)
    239 
    240   if errors:
    241     print 'Test failed with %d errors.' % errors
    242   else:
    243     print 'Passed.'
    244   return errors
    245 
    246 
    247 if __name__ == '__main__':
    248   sys.exit(Main(sys.argv[1:]))
    249 
    250