idl_namespace.py revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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""" 7IDLNamespace for PPAPI 8 9This file defines the behavior of the AST namespace which allows for resolving 10a symbol as one or more AST nodes given a release or range of releases. 11""" 12 13import sys 14 15from idl_option import GetOption, Option, ParseOptions 16from idl_log import ErrOut, InfoOut, WarnOut 17from idl_release import IDLRelease, IDLReleaseList 18 19Option('label', 'Use the specifed label blocks.', default='Chrome') 20Option('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# 30class 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# 84class 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 114errors = 0 115# 116# DumpFailure 117# 118# Dumps all the information relevant to an add failure. 119def 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 133def 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 139def 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 147def 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. 156def 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. 165def 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 179def 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 247if __name__ == '__main__': 248 sys.exit(Main(sys.argv[1:])) 249 250