1#!/usr/bin/env python 2# Copyright 2014 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 6import json 7import os 8import unittest 9 10from jsc_view import GetEventByNameFromEvents 11from api_schema_graph import APISchemaGraph 12from availability_finder import AvailabilityFinder, AvailabilityInfo 13from branch_utility import BranchUtility, ChannelInfo 14from compiled_file_system import CompiledFileSystem 15from extensions_paths import CHROME_EXTENSIONS 16from fake_host_file_system_provider import FakeHostFileSystemProvider 17from fake_url_fetcher import FakeUrlFetcher 18from features_bundle import FeaturesBundle 19from future import Future 20from host_file_system_iterator import HostFileSystemIterator 21from jsc_view import JSCView, _FormatValue 22from object_store_creator import ObjectStoreCreator 23from schema_processor import SchemaProcessorFactoryForTest 24from server_instance import ServerInstance 25from test_data.api_data_source.canned_master_fs import CANNED_MASTER_FS_DATA 26from test_data.canned_data import CANNED_API_FILE_SYSTEM_DATA 27from test_data.object_level_availability.tabs import TABS_SCHEMA_BRANCHES 28from test_file_system import TestFileSystem 29from test_util import Server2Path 30 31 32class _FakeTemplateCache(object): 33 34 def GetFromFile(self, key): 35 return Future(value='motemplate %s' % key) 36 37 38class _FakeFeaturesBundle(object): 39 def GetAPIFeatures(self): 40 return Future(value={ 41 'bluetooth': {'value': True}, 42 'contextMenus': {'value': True}, 43 'jsonStableAPI': {'value': True}, 44 'idle': {'value': True}, 45 'input.ime': {'value': True}, 46 'tabs': {'value': True} 47 }) 48 49 50class _FakeAvailabilityFinder(object): 51 def __init__(self, fake_availability): 52 self._fake_availability = fake_availability 53 54 def GetAPIAvailability(self, api_name): 55 return self._fake_availability 56 57 def GetAPINodeAvailability(self, api_name): 58 schema_graph = APISchemaGraph() 59 api_graph = APISchemaGraph(json.loads( 60 CANNED_MASTER_FS_DATA['api'][api_name + '.json'])) 61 # Give the graph fake ChannelInfo; it's not used in tests. 62 channel_info = ChannelInfo('stable', '28', 28) 63 schema_graph.Update(api_graph, lambda _: channel_info) 64 return schema_graph 65 66 67class JSCViewTest(unittest.TestCase): 68 def setUp(self): 69 self._base_path = Server2Path('test_data', 'test_json') 70 71 server_instance = ServerInstance.ForTest( 72 TestFileSystem(CANNED_MASTER_FS_DATA, relative_to=CHROME_EXTENSIONS)) 73 file_system = server_instance.host_file_system_provider.GetMaster() 74 self._json_cache = server_instance.compiled_fs_factory.ForJson(file_system) 75 self._features_bundle = FeaturesBundle(file_system, 76 server_instance.compiled_fs_factory, 77 server_instance.object_store_creator, 78 'extensions') 79 self._api_models = server_instance.platform_bundle.GetAPIModels( 80 'extensions') 81 self._fake_availability = AvailabilityInfo(ChannelInfo('stable', '396', 5)) 82 83 def _ReadLocalFile(self, filename): 84 with open(os.path.join(self._base_path, filename), 'r') as f: 85 return f.read() 86 87 def _LoadJSON(self, filename): 88 return json.loads(self._ReadLocalFile(filename)) 89 90 def _FakeLoadAddRulesSchema(self): 91 events = self._LoadJSON('add_rules_def_test.json') 92 return Future(value=GetEventByNameFromEvents(events)) 93 94 def testFormatValue(self): 95 self.assertEquals('1,234,567', _FormatValue(1234567)) 96 self.assertEquals('67', _FormatValue(67)) 97 self.assertEquals('234,567', _FormatValue(234567)) 98 99 def testGetEventByNameFromEvents(self): 100 events = {} 101 # Missing 'types' completely. 102 self.assertRaises(AssertionError, GetEventByNameFromEvents, events) 103 104 events['types'] = [] 105 # No type 'Event' defined. 106 self.assertRaises(AssertionError, GetEventByNameFromEvents, events) 107 108 events['types'].append({ 'name': 'Event', 109 'functions': []}) 110 add_rules = { "name": "addRules" } 111 events['types'][0]['functions'].append(add_rules) 112 self.assertEqual(add_rules, 113 GetEventByNameFromEvents(events)['addRules']) 114 115 events['types'][0]['functions'].append(add_rules) 116 # Duplicates are an error. 117 self.assertRaises(AssertionError, GetEventByNameFromEvents, events) 118 119 def testCreateId(self): 120 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) 121 dict_ = JSCView(self._api_models.GetContentScriptAPIs().Get(), 122 self._api_models.GetModel('tester').Get(), 123 fake_avail_finder, 124 self._json_cache, 125 _FakeTemplateCache(), 126 self._features_bundle, 127 None, 128 'extensions').ToDict() 129 self.assertEquals('type-TypeA', dict_['types'][0]['id']) 130 self.assertEquals('property-TypeA-b', 131 dict_['types'][0]['properties'][0]['id']) 132 self.assertEquals('method-get', dict_['functions'][0]['id']) 133 self.assertEquals('event-EventA', dict_['events'][0]['id']) 134 135 # TODO(kalman): re-enable this when we have a rebase option. 136 def DISABLED_testToDict(self): 137 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) 138 expected_json = self._LoadJSON('expected_tester.json') 139 dict_ = JSCView(self._api_models.GetContentScriptAPIs().Get(), 140 self._api_models.GetModel('tester').Get(), 141 fake_avail_finder, 142 self._json_cache, 143 _FakeTemplateCache(), 144 self._features_bundle, 145 None, 146 'extensions').ToDict() 147 self.assertEquals(expected_json, dict_) 148 149 def testAddRules(self): 150 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) 151 dict_ = JSCView(self._api_models.GetContentScriptAPIs().Get(), 152 self._api_models.GetModel('add_rules_tester').Get(), 153 fake_avail_finder, 154 self._json_cache, 155 _FakeTemplateCache(), 156 self._features_bundle, 157 self._FakeLoadAddRulesSchema(), 158 'extensions').ToDict() 159 160 # Check that the first event has the addRulesFunction defined. 161 self.assertEquals('add_rules_tester', dict_['name']) 162 self.assertEquals('rules', dict_['events'][0]['name']) 163 self.assertEquals('notable_name_to_check_for', 164 dict_['events'][0]['byName']['addRules'][ 165 'parameters'][0]['name']) 166 167 # Check that the second event has addListener defined. 168 self.assertEquals('noRules', dict_['events'][1]['name']) 169 self.assertEquals('add_rules_tester', dict_['name']) 170 self.assertEquals('noRules', dict_['events'][1]['name']) 171 self.assertEquals('callback', 172 dict_['events'][0]['byName']['addListener'][ 173 'parameters'][0]['name']) 174 175 def testGetIntroList(self): 176 fake_avail_finder = _FakeAvailabilityFinder(self._fake_availability) 177 model = JSCView(self._api_models.GetContentScriptAPIs().Get(), 178 self._api_models.GetModel('tester').Get(), 179 fake_avail_finder, 180 self._json_cache, 181 _FakeTemplateCache(), 182 self._features_bundle, 183 None, 184 'extensions') 185 expected_list = [ 186 { 'title': 'Description', 187 'content': [ 188 { 'text': 'a test api' } 189 ] 190 }, 191 { 'title': 'Availability', 192 'content': [ 193 { 'partial': 'motemplate chrome/common/extensions/docs/' + 194 'templates/private/intro_tables/stable_message.html', 195 'version': 5, 196 'scheduled': None 197 } 198 ] 199 }, 200 { 'title': 'Permissions', 201 'content': [ 202 { 'class': 'override', 203 'text': '"tester"' 204 }, 205 { 'text': 'is an API for testing things.' } 206 ] 207 }, 208 { 'title': 'Manifest', 209 'content': [ 210 { 'class': 'code', 211 'text': '"tester": {...}' 212 } 213 ] 214 }, 215 { 'title': 'Content Scripts', 216 'content': [ 217 { 218 'partial': 'motemplate chrome/common/extensions/docs' + 219 '/templates/private/intro_tables/content_scripts.html', 220 'contentScriptSupport': { 221 'name': 'tester', 222 'restrictedTo': None 223 } 224 } 225 ] 226 }, 227 { 'title': 'Learn More', 228 'content': [ 229 { 'link': 'https://tester.test.com/welcome.html', 230 'text': 'Welcome!' 231 } 232 ] 233 } 234 ] 235 self.assertEquals(model._GetIntroTableList(), expected_list) 236 237 # Tests the same data with a scheduled availability. 238 fake_avail_finder = _FakeAvailabilityFinder( 239 AvailabilityInfo(ChannelInfo('beta', '1453', 27), scheduled=28)) 240 model = JSCView(self._api_models.GetContentScriptAPIs().Get(), 241 self._api_models.GetModel('tester').Get(), 242 fake_avail_finder, 243 self._json_cache, 244 _FakeTemplateCache(), 245 self._features_bundle, 246 None, 247 'extensions') 248 expected_list[1] = { 249 'title': 'Availability', 250 'content': [ 251 { 'partial': 'motemplate chrome/common/extensions/docs/' + 252 'templates/private/intro_tables/beta_message.html', 253 'version': 27, 254 'scheduled': 28 255 } 256 ] 257 } 258 self.assertEquals(model._GetIntroTableList(), expected_list) 259 260 261class JSCViewWithoutNodeAvailabilityTest(unittest.TestCase): 262 def setUp(self): 263 server_instance = ServerInstance.ForTest( 264 file_system_provider=FakeHostFileSystemProvider( 265 CANNED_API_FILE_SYSTEM_DATA)) 266 self._api_models = server_instance.platform_bundle.GetAPIModels( 267 'extensions') 268 self._json_cache = server_instance.compiled_fs_factory.ForJson( 269 server_instance.host_file_system_provider.GetMaster()) 270 self._avail_finder = server_instance.platform_bundle.GetAvailabilityFinder( 271 'extensions') 272 273 274 def testGetAPIAvailability(self): 275 api_availabilities = { 276 'bluetooth': 31, 277 'contextMenus': 'master', 278 'jsonStableAPI': 20, 279 'idle': 5, 280 'input.ime': 18, 281 'tabs': 18 282 } 283 for api_name, availability in api_availabilities.iteritems(): 284 model_dict = JSCView( 285 self._api_models.GetContentScriptAPIs().Get(), 286 self._api_models.GetModel(api_name).Get(), 287 self._avail_finder, 288 self._json_cache, 289 _FakeTemplateCache(), 290 _FakeFeaturesBundle(), 291 None, 292 'extensions').ToDict() 293 self.assertEquals(availability, 294 model_dict['introList'][1]['content'][0]['version']) 295 296 297class JSCViewWithNodeAvailabilityTest(unittest.TestCase): 298 def setUp(self): 299 tabs_unmodified_versions = (16, 20, 23, 24) 300 self._branch_utility = BranchUtility( 301 os.path.join('branch_utility', 'first.json'), 302 os.path.join('branch_utility', 'second.json'), 303 FakeUrlFetcher(Server2Path('test_data')), 304 ObjectStoreCreator.ForTest()) 305 self._node_fs_creator = FakeHostFileSystemProvider(TABS_SCHEMA_BRANCHES) 306 self._node_fs_iterator = HostFileSystemIterator(self._node_fs_creator, 307 self._branch_utility) 308 test_object_store = ObjectStoreCreator.ForTest() 309 self._avail_finder = AvailabilityFinder( 310 self._branch_utility, 311 CompiledFileSystem.Factory(test_object_store), 312 self._node_fs_iterator, 313 self._node_fs_creator.GetMaster(), 314 test_object_store, 315 'extensions', 316 SchemaProcessorFactoryForTest()) 317 318 server_instance = ServerInstance.ForTest( 319 file_system_provider=FakeHostFileSystemProvider( 320 TABS_SCHEMA_BRANCHES)) 321 self._api_models = server_instance.platform_bundle.GetAPIModels( 322 'extensions') 323 self._json_cache = server_instance.compiled_fs_factory.ForJson( 324 server_instance.host_file_system_provider.GetMaster()) 325 326 # Imitate the actual SVN file system by incrementing the stats for paths 327 # where an API schema has changed. 328 last_stat = type('last_stat', (object,), {'val': 0}) 329 330 def stat_paths(file_system, channel_info): 331 if channel_info.version not in tabs_unmodified_versions: 332 last_stat.val += 1 333 # HACK: |file_system| is a MockFileSystem backed by a TestFileSystem. 334 # Increment the TestFileSystem stat count. 335 file_system._file_system.IncrementStat(by=last_stat.val) 336 # Continue looping. The iterator will stop after 'master' automatically. 337 return True 338 339 # Use the HostFileSystemIterator created above to change global stat values 340 # for the TestFileSystems that it creates. 341 self._node_fs_iterator.Ascending( 342 # The earliest version represented with the tabs' test data is 13. 343 self._branch_utility.GetStableChannelInfo(13), 344 stat_paths) 345 346 def testGetAPINodeAvailability(self): 347 def assertEquals(node, actual): 348 node_availabilities = { 349 'tabs.Tab': None, 350 'tabs.fakeTabsProperty1': None, 351 'tabs.get': None, 352 'tabs.onUpdated': None, 353 'tabs.InjectDetails': 25, 354 'tabs.fakeTabsProperty2': 15, 355 'tabs.getCurrent': 19, 356 'tabs.onActivated': 30 357 } 358 self.assertEquals(node_availabilities[node], actual) 359 360 model_dict = JSCView( 361 self._api_models.GetContentScriptAPIs().Get(), 362 self._api_models.GetModel('tabs').Get(), 363 self._avail_finder, 364 self._json_cache, 365 _FakeTemplateCache(), 366 _FakeFeaturesBundle(), 367 None, 368 'extensions').ToDict() 369 370 # Test nodes that have the same availability as their parent. 371 372 # Test type. 373 assertEquals('tabs.Tab', model_dict['types'][0]['availability']) 374 # Test property. 375 assertEquals('tabs.fakeTabsProperty1', 376 model_dict['properties'][0]['availability']) 377 # Test function. 378 assertEquals('tabs.get', model_dict['functions'][1]['availability']) 379 # Test event. 380 assertEquals('tabs.onUpdated', model_dict['events'][1]['availability']) 381 382 # Test nodes with varying availabilities. 383 384 # Test type. 385 assertEquals('tabs.InjectDetails', 386 model_dict['types'][1]['availability']['version']) 387 # Test property. 388 assertEquals('tabs.fakeTabsProperty2', 389 model_dict['properties'][2]['availability']['version']) 390 # Test function. 391 assertEquals('tabs.getCurrent', 392 model_dict['functions'][0]['availability']['version']) 393 # Test event. 394 assertEquals('tabs.onActivated', 395 model_dict['events'][0]['availability']['version']) 396 397 # Test a node that became deprecated. 398 self.assertEquals({ 399 'scheduled': None, 400 'version': 26, 401 'partial': 'motemplate chrome/common/extensions/docs/templates/' + 402 'private/intro_tables/deprecated_message.html' 403 }, model_dict['types'][2]['availability']) 404 405if __name__ == '__main__': 406 unittest.main() 407