1#!/usr/bin/env python
2# Copyright 2013 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.
5import os
6import sys
7import unittest
8
9import api_schema_graph
10from availability_finder import AvailabilityFinder, AvailabilityInfo
11from branch_utility import BranchUtility, ChannelInfo
12from compiled_file_system import CompiledFileSystem
13from fake_host_file_system_provider import FakeHostFileSystemProvider
14from fake_url_fetcher import FakeUrlFetcher
15from host_file_system_iterator import HostFileSystemIterator
16from mock_function import MockFunction
17from object_store_creator import ObjectStoreCreator
18from platform_util import GetPlatforms
19from test_data.canned_data import (CANNED_API_FILE_SYSTEM_DATA, CANNED_BRANCHES)
20from test_data.object_level_availability.tabs import TABS_SCHEMA_BRANCHES
21from test_util import Server2Path
22from schema_processor import SchemaProcessorFactoryForTest
23
24
25TABS_UNMODIFIED_VERSIONS = (16, 20, 23, 24)
26
27class AvailabilityFinderTest(unittest.TestCase):
28
29  def _create_availability_finder(self,
30                                  host_fs_creator,
31                                  host_fs_iterator,
32                                  platform):
33    test_object_store = ObjectStoreCreator.ForTest()
34    return AvailabilityFinder(
35        self._branch_utility,
36        CompiledFileSystem.Factory(test_object_store),
37        host_fs_iterator,
38        host_fs_creator.GetMaster(),
39        test_object_store,
40        platform,
41        SchemaProcessorFactoryForTest())
42
43  def setUp(self):
44    self._branch_utility = BranchUtility(
45        os.path.join('branch_utility', 'first.json'),
46        os.path.join('branch_utility', 'second.json'),
47        FakeUrlFetcher(Server2Path('test_data')),
48        ObjectStoreCreator.ForTest())
49    self._api_fs_creator = FakeHostFileSystemProvider(
50        CANNED_API_FILE_SYSTEM_DATA)
51    self._node_fs_creator = FakeHostFileSystemProvider(TABS_SCHEMA_BRANCHES)
52    self._api_fs_iterator = HostFileSystemIterator(self._api_fs_creator,
53                                                   self._branch_utility)
54    self._node_fs_iterator = HostFileSystemIterator(self._node_fs_creator,
55                                                    self._branch_utility)
56
57    # Imitate the actual SVN file system by incrementing the stats for paths
58    # where an API schema has changed.
59    last_stat = type('last_stat', (object,), {'val': 0})
60
61    def stat_paths(file_system, channel_info):
62      if channel_info.version not in TABS_UNMODIFIED_VERSIONS:
63        last_stat.val += 1
64      # HACK: |file_system| is a MockFileSystem backed by a TestFileSystem.
65      # Increment the TestFileSystem stat count.
66      file_system._file_system.IncrementStat(by=last_stat.val)
67      # Continue looping. The iterator will stop after 'master' automatically.
68      return True
69
70    # Use the HostFileSystemIterator created above to change global stat values
71    # for the TestFileSystems that it creates.
72    self._node_fs_iterator.Ascending(
73        # The earliest version represented with the tabs' test data is 13.
74        self._branch_utility.GetStableChannelInfo(13),
75        stat_paths)
76
77  def testGraphOptimization(self):
78    for platform in GetPlatforms():
79      # Keep track of how many times the APISchemaGraph constructor is called.
80      original_constructor = api_schema_graph.APISchemaGraph
81      mock_constructor = MockFunction(original_constructor)
82      api_schema_graph.APISchemaGraph = mock_constructor
83
84      node_avail_finder = self._create_availability_finder(
85          self._node_fs_creator, self._node_fs_iterator, platform)
86      try:
87        # The test data includes an extra branch where the API does not exist.
88        num_versions = len(TABS_SCHEMA_BRANCHES) - 1
89        # We expect an APISchemaGraph to be created only when an API schema file
90        # has different stat data from the previous version's schema file.
91        num_graphs_created = num_versions - len(TABS_UNMODIFIED_VERSIONS)
92
93        # Run the logic for object-level availability for an API.
94        node_avail_finder.GetAPINodeAvailability('tabs')
95
96        self.assertTrue(*api_schema_graph.APISchemaGraph.CheckAndReset(
97            num_graphs_created))
98      finally:
99        # Ensure that the APISchemaGraph constructor is reset to be the original
100        # constructor.
101        api_schema_graph.APISchemaGraph = original_constructor
102
103  def testGetAPIAvailability(self):
104    # Key: Using 'channel' (i.e. 'beta') to represent an availability listing
105    # for an API in a _features.json file, and using |channel| (i.e. |dev|) to
106    # represent the development channel, or phase of development, where an API's
107    # availability is being checked.
108
109    def assertGet(ch_info, api, only_on=None, scheduled=None):
110      for platform in GetPlatforms():
111        get_availability = self._create_availability_finder(
112            self._api_fs_creator,
113            self._api_fs_iterator,
114            platform if only_on is None else only_on).GetAPIAvailability
115        self.assertEqual(AvailabilityInfo(ch_info, scheduled=scheduled),
116                         get_availability(api))
117
118    # Testing APIs with predetermined availability.
119    assertGet(ChannelInfo('master', 'master', 'master'), 'jsonMasterAPI')
120    assertGet(ChannelInfo('dev', CANNED_BRANCHES[31], 31), 'jsonDevAPI')
121    assertGet(ChannelInfo('beta', CANNED_BRANCHES[30], 30), 'jsonBetaAPI')
122    assertGet(ChannelInfo('stable', CANNED_BRANCHES[20], 20), 'jsonStableAPI')
123
124    # Testing a whitelisted API.
125    assertGet(ChannelInfo('beta', CANNED_BRANCHES[30], 30),
126              'declarativeWebRequest')
127
128    # Testing APIs found only by checking file system existence.
129    assertGet(ChannelInfo('stable', CANNED_BRANCHES[23], 23), 'windows')
130    assertGet(ChannelInfo('stable', CANNED_BRANCHES[18], 18), 'tabs')
131    assertGet(ChannelInfo('stable', CANNED_BRANCHES[18], 18), 'input.ime')
132
133    # Testing API channel existence for _api_features.json.
134    # Listed as 'dev' on |beta|, 'dev' on |dev|.
135    assertGet(ChannelInfo('dev', CANNED_BRANCHES[31], 31), 'systemInfo.stuff')
136    # Listed as 'stable' on |beta|.
137    assertGet(ChannelInfo('beta', CANNED_BRANCHES[30], 30),
138              'systemInfo.cpu',
139              scheduled=31)
140
141    # Testing API channel existence for _manifest_features.json.
142    # Listed as 'master' on all channels.
143    assertGet(ChannelInfo('master', 'master', 'master'), 'sync')
144    # No records of API until |master|.
145    assertGet(ChannelInfo('master', 'master', 'master'), 'history')
146    # Listed as 'dev' on |dev|.
147    assertGet(ChannelInfo('dev', CANNED_BRANCHES[31], 31), 'storage')
148    # Stable in _manifest_features and into pre-18 versions.
149    assertGet(ChannelInfo('stable', CANNED_BRANCHES[8], 8), 'pageAction')
150
151    # Testing API channel existence for _permission_features.json.
152    # Listed as 'beta' on |master|.
153    assertGet(ChannelInfo('master', 'master', 'master'), 'falseBetaAPI')
154    # Listed as 'master' on |master|.
155    assertGet(ChannelInfo('master', 'master', 'master'), 'masterAPI')
156    # Listed as 'master' on all development channels.
157    assertGet(ChannelInfo('master', 'master', 'master'), 'declarativeContent')
158    # Listed as 'dev' on all development channels.
159    assertGet(ChannelInfo('dev', CANNED_BRANCHES[31], 31), 'bluetooth')
160    # Listed as 'dev' on |dev|.
161    assertGet(ChannelInfo('dev', CANNED_BRANCHES[31], 31), 'cookies')
162    # Treated as 'stable' APIs.
163    assertGet(ChannelInfo('stable', CANNED_BRANCHES[24], 24), 'alarms')
164    assertGet(ChannelInfo('stable', CANNED_BRANCHES[21], 21), 'bookmarks')
165
166    # Testing older API existence using extension_api.json.
167    assertGet(ChannelInfo('stable', CANNED_BRANCHES[6], 6), 'menus')
168    assertGet(ChannelInfo('stable', CANNED_BRANCHES[5], 5), 'idle')
169
170    # Switches between _features.json files across branches.
171    # Listed as 'master' on all channels, in _api, _permission, or _manifest.
172    assertGet(ChannelInfo('master', 'master', 'master'), 'contextMenus')
173    # Moves between _permission and _manifest as file system is traversed.
174    assertGet(ChannelInfo('stable', CANNED_BRANCHES[23], 23),
175              'systemInfo.display')
176    assertGet(ChannelInfo('stable', CANNED_BRANCHES[17], 17), 'webRequest')
177
178    # Mid-upgrade cases:
179    # Listed as 'dev' on |beta| and 'beta' on |dev|.
180    assertGet(ChannelInfo('dev', CANNED_BRANCHES[31], 31), 'notifications')
181    # Listed as 'beta' on |stable|, 'dev' on |beta|...until |stable| on master.
182    assertGet(ChannelInfo('master', 'master', 'master'), 'events')
183
184    # Check for differing availability across apps|extensions
185    assertGet(ChannelInfo('stable', CANNED_BRANCHES[26], 26),
186              'appsFirst',
187              only_on='extensions')
188    assertGet(ChannelInfo('stable', CANNED_BRANCHES[25], 25),
189              'appsFirst',
190              only_on='apps')
191
192  def testGetAPINodeAvailability(self):
193    def assertEquals(found, channel_info, actual, scheduled=None):
194      lookup_result = api_schema_graph.LookupResult
195      if channel_info is None:
196        self.assertEquals(lookup_result(found, None), actual)
197      else:
198        self.assertEquals(lookup_result(found, AvailabilityInfo(channel_info,
199            scheduled=scheduled)), actual)
200
201    for platform in GetPlatforms():
202      # Allow the LookupResult constructions below to take just one line.
203      avail_finder = self._create_availability_finder(
204          self._node_fs_creator,
205          self._node_fs_iterator,
206          platform)
207      tabs_graph = avail_finder.GetAPINodeAvailability('tabs')
208      fake_tabs_graph = avail_finder.GetAPINodeAvailability('fakeTabs')
209
210      assertEquals(True, self._branch_utility.GetChannelInfo('master'),
211          tabs_graph.Lookup('tabs', 'properties', 'fakeTabsProperty3'))
212      assertEquals(True, self._branch_utility.GetChannelInfo('dev'),
213          tabs_graph.Lookup('tabs', 'events', 'onActivated', 'parameters',
214              'activeInfo', 'properties', 'windowId'), scheduled=31)
215      assertEquals(True, self._branch_utility.GetChannelInfo('dev'),
216          tabs_graph.Lookup('tabs', 'events', 'onUpdated', 'parameters', 'tab'),
217          scheduled=31)
218      assertEquals(True, self._branch_utility.GetChannelInfo('beta'),
219          tabs_graph.Lookup('tabs', 'events', 'onActivated'), scheduled=30)
220      assertEquals(True, self._branch_utility.GetChannelInfo('beta'),
221          tabs_graph.Lookup('tabs', 'functions', 'get', 'parameters', 'tabId'),
222          scheduled=30)
223      assertEquals(True, self._branch_utility.GetChannelInfo('stable'),
224          tabs_graph.Lookup('tabs', 'types', 'InjectDetails', 'properties',
225              'code'))
226      assertEquals(True, self._branch_utility.GetChannelInfo('stable'),
227          tabs_graph.Lookup('tabs', 'types', 'InjectDetails', 'properties',
228              'file'))
229      assertEquals(True, self._branch_utility.GetStableChannelInfo(25),
230          tabs_graph.Lookup('tabs', 'types', 'InjectDetails'))
231
232      # Test inlined type.
233      assertEquals(True, self._branch_utility.GetChannelInfo('master'),
234          tabs_graph.Lookup('tabs', 'types', 'InlinedType'))
235
236      # Test implicitly inlined type.
237      assertEquals(True, self._branch_utility.GetStableChannelInfo(25),
238          fake_tabs_graph.Lookup('fakeTabs', 'types',
239              'WasImplicitlyInlinedType'))
240
241      # Test a node that was restricted to dev channel when it was introduced.
242      assertEquals(True, self._branch_utility.GetChannelInfo('beta'),
243          tabs_graph.Lookup('tabs', 'functions', 'restrictedFunc'),
244          scheduled=30)
245
246      # Test an explicitly scheduled node.
247      assertEquals(True, self._branch_utility.GetChannelInfo('dev'),
248          tabs_graph.Lookup('tabs', 'functions', 'scheduledFunc'),
249          scheduled=31)
250
251      # Nothing new in version 24 or 23.
252
253      assertEquals(True, self._branch_utility.GetStableChannelInfo(22),
254          tabs_graph.Lookup('tabs', 'types', 'Tab', 'properties', 'windowId'))
255      assertEquals(True, self._branch_utility.GetStableChannelInfo(21),
256          tabs_graph.Lookup('tabs', 'types', 'Tab', 'properties', 'selected'))
257
258      # Nothing new in version 20.
259
260      assertEquals(True, self._branch_utility.GetStableChannelInfo(19),
261          tabs_graph.Lookup('tabs', 'functions', 'getCurrent'))
262      assertEquals(True, self._branch_utility.GetStableChannelInfo(18),
263          tabs_graph.Lookup('tabs', 'types', 'Tab', 'properties', 'index'))
264      assertEquals(True, self._branch_utility.GetStableChannelInfo(17),
265          tabs_graph.Lookup('tabs', 'events', 'onUpdated', 'parameters',
266              'changeInfo'))
267
268      # Nothing new in version 16.
269
270      assertEquals(True, self._branch_utility.GetStableChannelInfo(15),
271          tabs_graph.Lookup('tabs', 'properties', 'fakeTabsProperty2'))
272
273      # Everything else is available at the API's release, version 14 here.
274      assertEquals(True, self._branch_utility.GetStableChannelInfo(14),
275          tabs_graph.Lookup('tabs', 'types', 'Tab'))
276      assertEquals(True, self._branch_utility.GetStableChannelInfo(14),
277          tabs_graph.Lookup('tabs', 'types', 'Tab', 'properties', 'url'))
278      assertEquals(True, self._branch_utility.GetStableChannelInfo(14),
279          tabs_graph.Lookup('tabs', 'properties', 'fakeTabsProperty1'))
280      assertEquals(True, self._branch_utility.GetStableChannelInfo(14),
281          tabs_graph.Lookup('tabs', 'functions', 'get', 'parameters',
282              'callback'))
283      assertEquals(True, self._branch_utility.GetStableChannelInfo(14),
284          tabs_graph.Lookup('tabs', 'events', 'onUpdated'))
285
286      # Test things that aren't available.
287      assertEquals(False, None, tabs_graph.Lookup('tabs', 'types',
288          'UpdateInfo'))
289      assertEquals(False, None, tabs_graph.Lookup('tabs', 'functions', 'get',
290          'parameters', 'callback', 'parameters', 'tab', 'id'))
291      assertEquals(False, None, tabs_graph.Lookup('functions'))
292      assertEquals(False, None, tabs_graph.Lookup('events', 'onActivated',
293          'parameters', 'activeInfo', 'tabId'))
294
295
296if __name__ == '__main__':
297  unittest.main()
298