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
6import json
7import os
8import sys
9import unittest
10
11from api_data_source import (_JSCModel,
12                             _FormatValue,
13                             _GetEventByNameFromEvents)
14from branch_utility import ChannelInfo
15from extensions_paths import EXTENSIONS
16from file_system import FileNotFoundError
17from future import Future
18from object_store_creator import ObjectStoreCreator
19from reference_resolver import ReferenceResolver
20from server_instance import ServerInstance
21from test_data.canned_data import (CANNED_API_FILE_SYSTEM_DATA, CANNED_BRANCHES)
22from test_data.api_data_source.canned_trunk_fs import CANNED_TRUNK_FS_DATA
23from test_file_system import TestFileSystem
24from third_party.json_schema_compiler.memoize import memoize
25
26
27def _MakeLink(href, text):
28  return '<a href="%s">%s</a>' % (href, text)
29
30
31def _GetType(dict_, name):
32  for type_ in dict_['types']:
33    if type_['name'] == name:
34      return type_
35
36
37class _FakeAvailabilityFinder(object):
38
39  def GetApiAvailability(self, version):
40    return ChannelInfo('stable', '396', 5)
41
42
43class _FakeHostFileSystemProvider(object):
44
45  def __init__(self, file_system_data):
46    self._file_system_data = file_system_data
47
48  def GetTrunk(self):
49    return self.GetBranch('trunk')
50
51  @memoize
52  def GetBranch(self, branch):
53    return TestFileSystem(self._file_system_data[str(branch)])
54
55
56class _FakeSamplesDataSource(object):
57
58  def Create(self, request):
59    return {}
60
61
62# Sad irony :(
63class _FakeAPIDataSource(object):
64
65  def __init__(self, json_data):
66    self._json = json_data
67
68  def Create(self, *args, **kwargs):
69    return self
70
71  def get(self, key, disable_refs=False):
72    if key not in self._json:
73      raise FileNotFoundError(key)
74    return self._json[key]
75
76
77class _FakeAPIModels(object):
78
79  def __init__(self, names):
80    self._names = names
81
82  def GetNames(self):
83    return self._names
84
85
86class _FakeTemplateCache(object):
87
88  def GetFromFile(self, key):
89    return Future(value='handlebar %s' % key)
90
91
92class APIDataSourceTest(unittest.TestCase):
93
94  def setUp(self):
95    self._base_path = os.path.join(sys.path[0], 'test_data', 'test_json')
96
97    server_instance = ServerInstance.ForTest(
98        TestFileSystem(CANNED_TRUNK_FS_DATA, relative_to=EXTENSIONS))
99    self._json_cache = server_instance.compiled_fs_factory.ForJson(
100        server_instance.host_file_system_provider.GetTrunk())
101    self._api_models = server_instance.api_models
102
103    # Used for testGetApiAvailability() so that valid-ish data is processed.
104    server_instance = ServerInstance.ForTest(
105        file_system_provider=_FakeHostFileSystemProvider(
106            CANNED_API_FILE_SYSTEM_DATA))
107    self._avail_api_models = server_instance.api_models
108    self._avail_json_cache = server_instance.compiled_fs_factory.ForJson(
109        server_instance.host_file_system_provider.GetTrunk())
110    self._avail_finder = server_instance.availability_finder
111
112  def _ReadLocalFile(self, filename):
113    with open(os.path.join(self._base_path, filename), 'r') as f:
114      return f.read()
115
116  def _CreateRefResolver(self, filename):
117    test_data = self._LoadJSON(filename)
118    return ReferenceResolver.Factory(_FakeAPIDataSource(test_data),
119                                     _FakeAPIModels(test_data),
120                                     ObjectStoreCreator.ForTest()).Create()
121
122  def _LoadJSON(self, filename):
123    return json.loads(self._ReadLocalFile(filename))
124
125  def testCreateId(self):
126    dict_ = _JSCModel('tester',
127                      self._api_models,
128                      self._CreateRefResolver('test_file_data_source.json'),
129                      False,
130                      _FakeAvailabilityFinder(),
131                      self._json_cache,
132                      _FakeTemplateCache(),
133                      None).ToDict()
134    self.assertEquals('type-TypeA', dict_['types'][0]['id'])
135    self.assertEquals('property-TypeA-b',
136                      dict_['types'][0]['properties'][0]['id'])
137    self.assertEquals('method-get', dict_['functions'][0]['id'])
138    self.assertEquals('event-EventA', dict_['events'][0]['id'])
139
140  # TODO(kalman): re-enable this when we have a rebase option.
141  def DISABLED_testToDict(self):
142    expected_json = self._LoadJSON('expected_tester.json')
143    dict_ = _JSCModel('tester',
144                      self._api_models,
145                      False,
146                      self._CreateRefResolver('test_file_data_source.json'),
147                      _FakeAvailabilityFinder(),
148                      self._json_cache,
149                      _FakeTemplateCache(),
150                      None).ToDict()
151    self.assertEquals(expected_json, dict_)
152
153  def testFormatValue(self):
154    self.assertEquals('1,234,567', _FormatValue(1234567))
155    self.assertEquals('67', _FormatValue(67))
156    self.assertEquals('234,567', _FormatValue(234567))
157
158  def testFormatDescription(self):
159    dict_ = _JSCModel('ref_test',
160                      self._api_models,
161                      self._CreateRefResolver('ref_test_data_source.json'),
162                      False,
163                      _FakeAvailabilityFinder(),
164                      self._json_cache,
165                      _FakeTemplateCache(),
166                      None).ToDict()
167    self.assertEquals(_MakeLink('ref_test.html#type-type2', 'type2'),
168                      _GetType(dict_, 'type1')['description'])
169    self.assertEquals(
170        'A %s, or %s' % (_MakeLink('ref_test.html#type-type3', 'type3'),
171                         _MakeLink('ref_test.html#type-type2', 'type2')),
172        _GetType(dict_, 'type2')['description'])
173    self.assertEquals(
174        '%s != %s' % (_MakeLink('other.html#type-type2', 'other.type2'),
175                      _MakeLink('ref_test.html#type-type2', 'type2')),
176        _GetType(dict_, 'type3')['description'])
177
178
179  def testGetApiAvailability(self):
180    api_availabilities = {
181      'bluetooth': ChannelInfo('dev', CANNED_BRANCHES[28], 28),
182      'contextMenus': ChannelInfo('trunk', CANNED_BRANCHES['trunk'], 'trunk'),
183      'jsonStableAPI': ChannelInfo('stable', CANNED_BRANCHES[20], 20),
184      'idle': ChannelInfo('stable', CANNED_BRANCHES[5], 5),
185      'input.ime': ChannelInfo('stable', CANNED_BRANCHES[18], 18),
186      'tabs': ChannelInfo('stable', CANNED_BRANCHES[18], 18)
187    }
188    for api_name, availability in api_availabilities.iteritems():
189      model = _JSCModel(api_name,
190                        self._avail_api_models,
191                        None,
192                        True,
193                        self._avail_finder,
194                        self._avail_json_cache,
195                        _FakeTemplateCache(),
196                        None)
197      self.assertEquals(availability, model._GetApiAvailability())
198
199  def testGetIntroList(self):
200    model = _JSCModel('tester',
201                      self._api_models,
202                      self._CreateRefResolver('test_file_data_source.json'),
203                      False,
204                      _FakeAvailabilityFinder(),
205                      self._json_cache,
206                      _FakeTemplateCache(),
207                      None)
208    expected_list = [
209      { 'title': 'Description',
210        'content': [
211          { 'text': 'a test api' }
212        ]
213      },
214      { 'title': 'Availability',
215        'content': [
216          { 'partial': 'handlebar chrome/common/extensions/docs/' +
217                       'templates/private/intro_tables/stable_message.html',
218            'version': 5
219          }
220        ]
221      },
222      { 'title': 'Permissions',
223        'content': [
224          { 'class': 'override',
225            'text': '"tester"'
226          },
227          { 'text': 'is an API for testing things.' }
228        ]
229      },
230      { 'title': 'Manifest',
231        'content': [
232          { 'class': 'code',
233            'text': '"tester": {...}'
234          }
235        ]
236      },
237      { 'title': 'Learn More',
238        'content': [
239          { 'link': 'https://tester.test.com/welcome.html',
240            'text': 'Welcome!'
241          }
242        ]
243      }
244    ]
245    self.assertEquals(model._GetIntroTableList(), expected_list)
246
247  def testGetEventByNameFromEvents(self):
248    events = {}
249    # Missing 'types' completely.
250    self.assertRaises(AssertionError, _GetEventByNameFromEvents, events)
251
252    events['types'] = []
253    # No type 'Event' defined.
254    self.assertRaises(AssertionError, _GetEventByNameFromEvents, events)
255
256    events['types'].append({ 'name': 'Event',
257                             'functions': []})
258    add_rules = { "name": "addRules" }
259    events['types'][0]['functions'].append(add_rules)
260    self.assertEqual(add_rules,
261                     _GetEventByNameFromEvents(events)['addRules'])
262
263    events['types'][0]['functions'].append(add_rules)
264    # Duplicates are an error.
265    self.assertRaises(AssertionError, _GetEventByNameFromEvents, events)
266
267  def _FakeLoadAddRulesSchema(self):
268    events = self._LoadJSON('add_rules_def_test.json')
269    return _GetEventByNameFromEvents(events)
270
271  def testAddRules(self):
272    dict_ = _JSCModel('add_rules_tester',
273                      self._api_models,
274                      self._CreateRefResolver('test_file_data_source.json'),
275                      False,
276                      _FakeAvailabilityFinder(),
277                      self._json_cache,
278                      _FakeTemplateCache(),
279                      self._FakeLoadAddRulesSchema).ToDict()
280
281    # Check that the first event has the addRulesFunction defined.
282    self.assertEquals('add_rules_tester', dict_['name'])
283    self.assertEquals('rules', dict_['events'][0]['name'])
284    self.assertEquals('notable_name_to_check_for',
285                      dict_['events'][0]['byName']['addRules'][
286                          'parameters'][0]['name'])
287
288    # Check that the second event has addListener defined.
289    self.assertEquals('noRules', dict_['events'][1]['name'])
290    self.assertEquals('add_rules_tester', dict_['name'])
291    self.assertEquals('noRules', dict_['events'][1]['name'])
292    self.assertEquals('callback',
293                      dict_['events'][0]['byName']['addListener'][
294                          'parameters'][0]['name'])
295
296if __name__ == '__main__':
297  unittest.main()
298