def update_availability_graph(file_system, channel_info): version_filename = assert_not_none( self._GetApiSchemaFilename(api_name, file_system, channel_info.version)) version_stat = assert_not_none(file_system.Stat(version_filename)) # Important optimisation: only re-parse the graph if the file changed in # the last revision. Parsing the same schema and forming a graph on every # iteration is really expensive. if version_stat == previous.stat: version_graph = previous.graph else: # Keep track of any new schema elements from this version by adding # them to |availability_graph|. # # Calling |availability_graph|.Lookup() on the nodes being updated # will return the |annotation| object -- the current |channel_info|. version_graph = APISchemaGraph( self._GetApiSchema(api_name, file_system, channel_info.version)) availability_graph.Update( version_graph.Subtract(availability_graph), annotation=channel_info) previous.stat = version_stat previous.graph = version_graph # Continue looping until there are no longer differences between this # version and trunk. return version_stat != trunk_stat
def GetAPINodeAvailability(self, api_name): schema_graph = APISchemaGraph() api_graph = APISchemaGraph( json.loads(CANNED_MASTER_FS_DATA['api'][api_name + '.json'])) # Give the graph fake ChannelInfo; it's not used in tests. channel_info = ChannelInfo('stable', '28', 28) schema_graph.Update(api_graph, lambda _: channel_info) return schema_graph
def update_availability_graph(file_system, channel_info): version_graph = APISchemaGraph(get_schema(api_name, file_system)) # Keep track of any new schema elements from this version by adding # them to |availability_graph|. # # Calling |availability_graph|.Lookup() on the nodes being updated # will return the |annotation| object. availability_graph.Update( version_graph.Subtract(availability_graph), annotation=channel_info) # Continue looping until there are no longer differences between this # version and trunk. return trunk_graph != version_graph
def GetAPINodeAvailability(self, api_name): '''Returns an APISchemaGraph annotated with each node's availability (the ChannelInfo at the oldest channel it's available in). ''' availability_graph = self._node_level_object_store.Get(api_name).Get() if availability_graph is not None: return availability_graph def assert_not_none(value): assert value is not None return value availability_graph = APISchemaGraph() host_fs = self._host_file_system trunk_stat = assert_not_none( host_fs.Stat(self._GetAPISchemaFilename(api_name, host_fs, 'trunk'))) # Weird object thing here because nonlocal is Python 3. previous = type('previous', (object, ), {'stat': None, 'graph': None}) def update_availability_graph(file_system, channel_info): version_filename = assert_not_none( self._GetAPISchemaFilename(api_name, file_system, channel_info.version)) version_stat = assert_not_none(file_system.Stat(version_filename)) # Important optimisation: only re-parse the graph if the file changed in # the last revision. Parsing the same schema and forming a graph on every # iteration is really expensive. if version_stat == previous.stat: version_graph = previous.graph else: # Keep track of any new schema elements from this version by adding # them to |availability_graph|. # # Calling |availability_graph|.Lookup() on the nodes being updated # will return the |annotation| object -- the current |channel_info|. version_graph = APISchemaGraph( self._GetAPISchema(api_name, file_system, channel_info.version)) availability_graph.Update( version_graph.Subtract(availability_graph), annotation=channel_info) previous.stat = version_stat previous.graph = version_graph # Continue looping until there are no longer differences between this # version and trunk. return version_stat != trunk_stat self._file_system_iterator.Ascending( self.GetAPIAvailability(api_name).channel_info, update_availability_graph) self._node_level_object_store.Set(api_name, availability_graph) return availability_graph
def update_availability_graph(file_system, channel_info): # If we can't find a filename, skip checking at this branch. # For example, something could have a predetermined availability of 23, # but it doesn't show up in the file system until 26. # We know that the file will become available at some point. # # The problem with this is that at the first version where the API file # exists, we'll get a huge chunk of new objects that don't match # the predetermined API availability. version_filename = _GetAPISchemaFilename(api_name, file_system, channel_info.version) if version_filename is None: # Continue the loop at the next version. return True version_stat = assert_not_none(file_system.Stat(version_filename)) # Important optimisation: only re-parse the graph if the file changed in # the last revision. Parsing the same schema and forming a graph on every # iteration is really expensive. if version_stat == previous.stat: version_graph = previous.graph else: # Keep track of any new schema elements from this version by adding # them to |availability_graph|. # # Calling |availability_graph|.Lookup() on the nodes being updated # will return the |annotation| object -- the current |channel_info|. version_graph = APISchemaGraph( api_schema=self._GetAPISchema(api_name, file_system, channel_info.version)) def annotator(node_name): return self._CheckAPINodeAvailability('%s.%s' % (api_name, node_name), channel_info) availability_graph.Update(version_graph.Subtract(availability_graph), annotator) previous.stat = version_stat previous.graph = version_graph # Continue looping until there are no longer differences between this # version and master. return version_stat != master_stat
def GetApiNodeAvailability(self, api_name): '''Returns an APISchemaGraph annotated with each node's availability (the ChannelInfo at the oldest channel it's available in). ''' availability_graph = self._node_level_object_store.Get(api_name).Get() if availability_graph is not None: return availability_graph def get_schema(api_name, file_system): return _GetApiSchema( api_name, self._compiled_fs_factory.ForApiSchema(file_system)) availability_graph = APISchemaGraph() trunk_graph = APISchemaGraph( get_schema(api_name, self._host_file_system)) def update_availability_graph(file_system, channel_info): version_graph = APISchemaGraph(get_schema(api_name, file_system)) # Keep track of any new schema elements from this version by adding # them to |availability_graph|. # # Calling |availability_graph|.Lookup() on the nodes being updated # will return the |annotation| object. availability_graph.Update( version_graph.Subtract(availability_graph), annotation=channel_info) # Continue looping until there are no longer differences between this # version and trunk. return trunk_graph != version_graph self._file_system_iterator.Ascending(self.GetApiAvailability(api_name), update_availability_graph) self._node_level_object_store.Set(api_name, availability_graph) return availability_graph
def testIsEmpty(self): # A few assertions to make sure that Lookup works on empty sets. empty_graph = APISchemaGraph({}) self.assertTrue(empty_graph.IsEmpty()) self.assertEqual(LookupResult(False, None), empty_graph.Lookup('tabs', 'properties', 'TAB_PROPERTY_ONE')) self.assertEqual(LookupResult(False, None), empty_graph.Lookup('tabs', 'functions', 'get', 'parameters', 'tab')) self.assertEqual(LookupResult(False, None), empty_graph.Lookup('tabs', 'functions', 'get', 'parameters', 'tabId')) self.assertEqual(LookupResult(False, None), empty_graph.Lookup('tabs', 'events', 'onActivated', 'parameters', 'activeInfo')) self.assertEqual(LookupResult(False, None), empty_graph.Lookup('tabs', 'events', 'onUpdated', 'parameters', 'updateInfo'))
def GetAPINodeAvailability(self, api_name): '''Returns an APISchemaGraph annotated with each node's availability (the ChannelInfo at the oldest channel it's available in). ''' availability_graph = self._node_level_object_store.Get(api_name).Get() if availability_graph is not None: return availability_graph def assert_not_none(value): assert value is not None return value availability_graph = APISchemaGraph() host_fs = self._host_file_system master_stat = assert_not_none( host_fs.Stat(_GetAPISchemaFilename(api_name, host_fs, 'master'))) # Weird object thing here because nonlocal is Python 3. previous = type('previous', (object, ), {'stat': None, 'graph': None}) def update_availability_graph(file_system, channel_info): # If we can't find a filename, skip checking at this branch. # For example, something could have a predetermined availability of 23, # but it doesn't show up in the file system until 26. # We know that the file will become available at some point. # # The problem with this is that at the first version where the API file # exists, we'll get a huge chunk of new objects that don't match # the predetermined API availability. version_filename = _GetAPISchemaFilename(api_name, file_system, channel_info.version) if version_filename is None: # Continue the loop at the next version. return True version_stat = assert_not_none(file_system.Stat(version_filename)) # Important optimisation: only re-parse the graph if the file changed in # the last revision. Parsing the same schema and forming a graph on every # iteration is really expensive. if version_stat == previous.stat: version_graph = previous.graph else: # Keep track of any new schema elements from this version by adding # them to |availability_graph|. # # Calling |availability_graph|.Lookup() on the nodes being updated # will return the |annotation| object -- the current |channel_info|. version_graph = APISchemaGraph(api_schema=self._GetAPISchema( api_name, file_system, channel_info.version)) def annotator(node_name): return self._CheckAPINodeAvailability( '%s.%s' % (api_name, node_name), channel_info) availability_graph.Update( version_graph.Subtract(availability_graph), annotator) previous.stat = version_stat previous.graph = version_graph # Continue looping until there are no longer differences between this # version and master. return version_stat != master_stat self._file_system_iterator.Ascending( self.GetAPIAvailability(api_name).channel_info, update_availability_graph) self._node_level_object_store.Set(api_name, availability_graph) return availability_graph
def testSubtractEmpty(self): self._testAPISchema( APISchemaGraph(API_SCHEMA).Subtract(APISchemaGraph({})))
def testLookup(self): self._testAPISchema(APISchemaGraph(API_SCHEMA))
def testUpdate(self): result = APISchemaGraph(API_SCHEMA) to_add = APISchemaGraph({ 'tabs': { 'properties': { 'TAB_PROPERTY_THREE': { 'description': 'better than two' }, 'TAB_PROPERTY_FOUR': { 'value': 4 } }, 'functions': { 'get': { 'name': {}, 'parameters': { 'tab': { 'type': {}, 'name': {}, 'description': {}, 'surprise': {} } } }, 'getAllInWindow': { 'parameters': { 'windowId': { 'type': 'object' } } } } } }) result.Update(to_add, annotation='first') # Looking up elements that were originally available in |result|. Because # of this, no |annotation| object should be attached to the LookupResult # object. self.assertEqual(LookupResult(True, None), result.Lookup('tabs')) self.assertEqual( LookupResult(True, None), result.Lookup('tabs', 'functions', 'get', 'parameters')) self.assertEqual( LookupResult(True, None), result.Lookup('tabs', 'properties', 'TAB_PROPERTY_ONE')) self.assertEqual( LookupResult(True, None), result.Lookup('tabs', 'properties', 'TAB_PROPERTY_ONE')) self.assertEqual( LookupResult(True, None), result.Lookup('tabs', 'functions', 'get', 'parameters', 'tabId')) # Looking up elements that were just added to |result|. self.assertEqual( LookupResult(True, 'first'), result.Lookup('tabs', 'properties', 'TAB_PROPERTY_THREE')) self.assertEqual( LookupResult(True, 'first'), result.Lookup('tabs', 'properties', 'TAB_PROPERTY_FOUR')) self.assertEqual( LookupResult(True, 'first'), result.Lookup('tabs', 'functions', 'getAllInWindow', 'parameters', 'windowId')) self.assertEqual( LookupResult(True, 'first'), result.Lookup('tabs', 'functions', 'get', 'parameters', 'tab', 'surprise')) to_add = APISchemaGraph({ 'tabs': { 'properties': { 'TAB_PROPERTY_FIVE': { 'description': 'stayin\' alive' } }, 'functions': { 'getAllInWindow': { 'parameters': { 'callback': { 'type': 'function' } } } } } }) result.Update(to_add, annotation='second') # Looking up the second group of elements added to |result|. self.assertEqual( LookupResult(True, 'first'), result.Lookup('tabs', 'properties', 'TAB_PROPERTY_FOUR')) self.assertEqual( LookupResult(True, 'second'), result.Lookup('tabs', 'properties', 'TAB_PROPERTY_FIVE')) self.assertEqual( LookupResult(True, 'first'), result.Lookup('tabs', 'functions', 'getAllInWindow', 'parameters', 'windowId')) self.assertEqual( LookupResult(True, 'second'), result.Lookup('tabs', 'functions', 'getAllInWindow', 'parameters', 'callback')) self.assertEqual(LookupResult(True, 'first'), result.Lookup('tabs', 'functions', 'getAllInWindow'))
def testSubtractSuperset(self): difference = APISchemaGraph(API_SCHEMA).Subtract( APISchemaGraph({ 'tabs': { 'namespace': {}, 'properties': { 'lowercase': { 'properties': { 'one': { 'value': {} }, 'two': { 'description': {} } } }, 'TAB_PROPERTY_ONE': { 'value': {} }, 'TAB_PROPERTY_TWO': {}, 'TAB_PROPERTY_THREE': {} }, 'types': { 'Tab': { 'id': {}, 'properties': { 'id': {}, 'url': {} } }, 'UpdateInfo': {} }, 'functions': { 'get': { 'name': {}, 'parameters': { 'tab': { 'name': {}, 'type': {}, 'description': {} }, 'tabId': { 'name': {} } } }, 'getById': { 'parameters': { 'tabId': {} } } }, 'events': { 'onActivated': { 'name': {}, 'parameters': { 'activeInfo': { 'name': {} } } }, 'onUpdated': { 'name': {}, 'parameters': { 'updateInfo': { 'name': {} } } }, 'onClicked': { 'name': {}, 'parameters': { 'clickInfo': {} } } } } })) self.assertEqual(LookupResult(False, None), difference.Lookup('tabs')) self.assertEqual( LookupResult(False, None), difference.Lookup('tabs', 'properties', 'TAB_PROPERTY_TWO')) self.assertEqual(LookupResult(False, None), difference.Lookup('tabs', 'properties')) self.assertEqual( LookupResult(False, None), difference.Lookup('tabs', 'types', 'Tab', 'properties', 'url')) self.assertEqual( LookupResult(False, None), difference.Lookup('tabs', 'types', 'Tab', 'properties', 'id')) self.assertEqual( LookupResult(False, None), difference.Lookup('tabs', 'events', 'onUpdated', 'parameters', 'updateInfo')) self.assertEqual( LookupResult(False, None), difference.Lookup('tabs', 'properties', 'TAB_PROPERTY_ONE')) self.assertEqual( LookupResult(False, None), difference.Lookup('tabs', 'functions', 'get', 'parameters', 'tabId')) self.assertEqual( LookupResult(False, None), difference.Lookup('events', 'onUpdated', 'parameters', 'updateInfo'))
def testSubtractDisjointSet(self): difference = APISchemaGraph(API_SCHEMA).Subtract( APISchemaGraph({ 'contextMenus': { 'properties': { 'CONTEXT_MENU_PROPERTY_ONE': {} }, 'types': { 'Menu': { 'properties': { 'id': {}, 'width': {} } } }, 'functions': { 'get': { 'parameters': { 'callback': {} } } }, 'events': { 'onClicked': { 'parameters': { 'clickInfo': {} } }, 'onUpdated': { 'parameters': { 'updateInfo': {} } } } } })) self.assertEqual( LookupResult(True, None), difference.Lookup('tabs', 'properties', 'TAB_PROPERTY_ONE')) self.assertEqual( LookupResult(True, None), difference.Lookup('tabs', 'functions', 'get', 'parameters', 'tab')) self.assertEqual( LookupResult(True, None), difference.Lookup('tabs', 'events', 'onUpdated', 'parameters', 'updateInfo')) self.assertEqual( LookupResult(True, None), difference.Lookup('tabs', 'functions', 'get', 'parameters', 'tabId')) self.assertEqual( LookupResult(False, None), difference.Lookup('contextMenus', 'properties', 'CONTEXT_MENU_PROPERTY_ONE')) self.assertEqual(LookupResult(False, None), difference.Lookup('contextMenus', 'types', 'Menu')) self.assertEqual( LookupResult(False, None), difference.Lookup('contextMenus', 'types', 'Menu', 'properties', 'id')) self.assertEqual(LookupResult(False, None), difference.Lookup('contextMenus', 'functions')) self.assertEqual( LookupResult(False, None), difference.Lookup('contextMenus', 'events', 'onClicked', 'parameters', 'clickInfo')) self.assertEqual( LookupResult(False, None), difference.Lookup('contextMenus', 'events', 'onUpdated', 'parameters', 'updateInfo'))
def testSubtractSelf(self): self.assertTrue( APISchemaGraph(API_SCHEMA).Subtract( APISchemaGraph(API_SCHEMA)).IsEmpty())