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