text: #!/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.
  """
  
  from __future__ import print_function
  
  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:]))
  