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.
5
6import json
7import unittest
8
9from api_models import APIModels
10from compiled_file_system import CompiledFileSystem
11from extensions_paths import API_PATHS, CHROME_API, CHROME_EXTENSIONS
12from features_bundle import FeaturesBundle
13from file_system import FileNotFoundError
14from mock_file_system import MockFileSystem
15from object_store_creator import ObjectStoreCreator
16from test_file_system import TestFileSystem
17from test_util import ReadFile
18from future import Future
19from schema_processor import SchemaProcessorFactoryForTest
20
21
22_TEST_DATA = {
23  'api': {
24    'devtools': {
25      'inspected_window.json': ReadFile(
26          CHROME_API, 'devtools', 'inspected_window.json'),
27    },
28    '_api_features.json': json.dumps({
29      'alarms': {},
30      'app': {},
31      'app.runtime': {'noparent': True},
32      'app.runtime.foo': {},
33      'declarativeWebRequest': {},
34      'devtools.inspectedWindow': {},
35      'input': {},
36      'input.ime': {},
37      'storage': {},
38    }),
39    '_manifest_features.json': '{}',
40    '_permission_features.json': '{}',
41    'alarms.idl': ReadFile(CHROME_API, 'alarms.idl'),
42    'input_ime.json': ReadFile(CHROME_API, 'input_ime.json'),
43    'page_action.json': ReadFile(CHROME_API, 'page_action.json'),
44  },
45  'docs': {
46    'templates': {
47      'json': {
48        'manifest.json': '{}',
49        'permissions.json': '{}',
50      }
51    }
52  },
53}
54
55
56class APIModelsTest(unittest.TestCase):
57  def setUp(self):
58    object_store_creator = ObjectStoreCreator.ForTest()
59    compiled_fs_factory = CompiledFileSystem.Factory(object_store_creator)
60    self._mock_file_system = MockFileSystem(
61        TestFileSystem(_TEST_DATA, relative_to=CHROME_EXTENSIONS))
62    features_bundle = FeaturesBundle(self._mock_file_system,
63                                     compiled_fs_factory,
64                                     object_store_creator,
65                                     'extensions')
66    self._api_models = APIModels(features_bundle,
67                                 compiled_fs_factory,
68                                 self._mock_file_system,
69                                 object_store_creator,
70                                 'extensions',
71                                 SchemaProcessorFactoryForTest())
72
73  def testGetNames(self):
74    # Both 'app' and 'app.runtime' appear here because 'app.runtime' has
75    # noparent:true, but 'app.runtime.foo' etc doesn't so it's a sub-feature of
76    # 'app.runtime' not a separate API. 'devtools.inspectedWindow' is an API
77    # because there is no 'devtools'.
78    self.assertEqual(
79        ['alarms', 'app', 'app.runtime', 'declarativeWebRequest',
80         'devtools.inspectedWindow', 'input', 'storage'],
81        sorted(self._api_models.GetNames()))
82
83  def testGetModel(self):
84    def get_model_name(api_name):
85      return self._api_models.GetModel(api_name).Get().name
86    self.assertEqual('devtools.inspectedWindow',
87                     get_model_name('devtools.inspectedWindow'))
88    self.assertEqual('devtools.inspectedWindow',
89                     get_model_name('devtools/inspected_window.json'))
90    self.assertEqual('devtools.inspectedWindow',
91                     get_model_name(CHROME_API +
92                                    'devtools/inspected_window.json'))
93    self.assertEqual('alarms', get_model_name('alarms'))
94    self.assertEqual('alarms', get_model_name('alarms.idl'))
95    self.assertEqual('alarms', get_model_name(CHROME_API + 'alarms.idl'))
96    self.assertEqual('input.ime', get_model_name('input.ime'))
97    self.assertEqual('input.ime', get_model_name('input_ime.json'))
98    self.assertEqual('input.ime',
99                     get_model_name(CHROME_API + 'input_ime.json'))
100    self.assertEqual('pageAction', get_model_name('pageAction'))
101    self.assertEqual('pageAction', get_model_name('page_action.json'))
102    self.assertEqual('pageAction', get_model_name(CHROME_API +
103                                                  'page_action.json'))
104
105  def testGetNonexistentModel(self):
106    self.assertRaises(FileNotFoundError,
107                      self._api_models.GetModel('declarativeWebRequest').Get)
108    self.assertRaises(FileNotFoundError,
109                      self._api_models.GetModel(
110                          'declarative_web_request.json').Get)
111    self.assertRaises(FileNotFoundError,
112                      self._api_models.GetModel(
113                          CHROME_API + 'declarative_web_request.json').Get)
114    self.assertRaises(FileNotFoundError,
115                      self._api_models.GetModel('notfound').Get)
116    self.assertRaises(FileNotFoundError,
117                      self._api_models.GetModel('notfound.json').Get)
118    self.assertRaises(FileNotFoundError,
119                      self._api_models.GetModel(CHROME_API +
120                                                'notfound.json').Get)
121    self.assertRaises(FileNotFoundError,
122                      self._api_models.GetModel(CHROME_API +
123                                                'alarms.json').Get)
124    self.assertRaises(FileNotFoundError,
125                      self._api_models.GetModel('storage').Get)
126    self.assertRaises(FileNotFoundError,
127                      self._api_models.GetModel(CHROME_API +
128                                                'storage.json').Get)
129    self.assertRaises(FileNotFoundError,
130                      self._api_models.GetModel(CHROME_API +
131                                                'storage.idl').Get)
132
133  def testSingleFile(self):
134    # 2 stats (1 for JSON and 1 for IDL) for each available API path.
135    # 1 read (for IDL file which existed).
136    future = self._api_models.GetModel('alarms')
137    self.assertTrue(*self._mock_file_system.CheckAndReset(
138        read_count=1, stat_count=len(API_PATHS)*2))
139
140    # 1 read-resolve (for the IDL file).
141    #
142    # The important part here and above is that it's only doing a single read;
143    # any more would break the contract that only a single file is accessed -
144    # see the SingleFile annotation in api_models._CreateAPIModel.
145    future.Get()
146    self.assertTrue(*self._mock_file_system.CheckAndReset(
147        read_resolve_count=1))
148
149    # 2 stats (1 for JSON and 1 for IDL) for each available API path.
150    # No reads (still cached).
151    future = self._api_models.GetModel('alarms')
152    self.assertTrue(*self._mock_file_system.CheckAndReset(
153        stat_count=len(API_PATHS)*2))
154    future.Get()
155    self.assertTrue(*self._mock_file_system.CheckAndReset())
156
157
158if __name__ == '__main__':
159  unittest.main()
160