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
6from cpp_namespace_environment import CppNamespaceEnvironment
7from cpp_type_generator import CppTypeGenerator
8from json_schema import CachedLoad
9import idl_schema
10import model
11import unittest
12
13from collections import defaultdict
14
15class _FakeSchemaLoader(object):
16  def __init__(self, model):
17    self._model = model
18
19  def ResolveType(self, type_name, default):
20    parts = type_name.rsplit('.', 1)
21    if len(parts) == 1:
22      return default if type_name in default.types else None
23    return self._model.namespaces[parts[0]]
24
25class CppTypeGeneratorTest(unittest.TestCase):
26  def setUp(self):
27    self.models = defaultdict(model.Model)
28
29    forbidden_json = CachedLoad('test/forbidden.json')
30    self.models['forbidden'].AddNamespace(
31        forbidden_json[0], 'path/to/forbidden.json')
32
33    permissions_json = CachedLoad('test/permissions.json')
34    self.permissions = self.models['permissions'].AddNamespace(
35        permissions_json[0], 'path/to/permissions.json')
36
37    self.windows_json = CachedLoad('test/windows.json')
38    self.windows = self.models['windows'].AddNamespace(self.windows_json[0],
39                                                       'path/to/window.json')
40    self.tabs_json = CachedLoad('test/tabs.json')
41    self.tabs = self.models['tabs'].AddNamespace(self.tabs_json[0],
42                                                 'path/to/tabs.json')
43
44    self.browser_action_json = CachedLoad('test/browser_action.json')
45    self.browser_action = self.models['browser_action'].AddNamespace(
46        self.browser_action_json[0], 'path/to/browser_action.json')
47
48    self.font_settings_json = CachedLoad('test/font_settings.json')
49    self.font_settings = self.models['font_settings'].AddNamespace(
50        self.font_settings_json[0], 'path/to/font_settings.json')
51
52    self.dependency_tester_json = CachedLoad('test/dependency_tester.json')
53    self.models['dependency_tester'].AddNamespace(
54        self.dependency_tester_json[0], 'path/to/dependency_tester.json')
55
56    content_settings_json = CachedLoad('test/content_settings.json')
57    self.models['content_settings'].AddNamespace(
58        content_settings_json[0], 'path/to/content_settings.json')
59
60    objects_movable_idl = idl_schema.Load('test/objects_movable.idl')
61    self.objects_movable = self.models['objects_movable'].AddNamespace(
62        objects_movable_idl[0], 'path/to/objects_movable.idl',
63        include_compiler_options=True)
64
65    self.simple_api_json = CachedLoad('test/simple_api.json')
66    self.models['simple_api'].AddNamespace(
67        self.simple_api_json[0], 'path/to/simple_api.json')
68
69    self.crossref_enums_json = CachedLoad('test/crossref_enums.json')
70    self.models['crossref_enums'].AddNamespace(
71        self.crossref_enums_json[0], 'path/to/crossref_enums.json')
72
73    self.crossref_enums_array_json = CachedLoad(
74        'test/crossref_enums_array.json')
75    self.models['crossref_enums_array'].AddNamespace(
76        self.crossref_enums_array_json[0],
77        'path/to/crossref_enums_array.json')
78
79  def testGenerateIncludesAndForwardDeclarations(self):
80    m = model.Model()
81    m.AddNamespace(self.windows_json[0],
82                   'path/to/windows.json',
83                   environment=CppNamespaceEnvironment('%(namespace)s'))
84    m.AddNamespace(self.tabs_json[0],
85                   'path/to/tabs.json',
86                   environment=CppNamespaceEnvironment('%(namespace)s'))
87    manager = CppTypeGenerator(m, _FakeSchemaLoader(m))
88
89    self.assertEquals('', manager.GenerateIncludes().Render())
90    self.assertEquals('#include "path/to/tabs.h"',
91                      manager.GenerateIncludes(include_soft=True).Render())
92    self.assertEquals(
93        'namespace tabs {\n'
94        'struct Tab;\n'
95        '}  // namespace tabs',
96        manager.GenerateForwardDeclarations().Render())
97
98    m = model.Model()
99    m.AddNamespace(self.windows_json[0],
100                   'path/to/windows.json',
101                   environment=CppNamespaceEnvironment(
102                       'foo::bar::%(namespace)s'))
103    m.AddNamespace(self.tabs_json[0],
104                   'path/to/tabs.json',
105                   environment=CppNamespaceEnvironment(
106                       'foo::bar::%(namespace)s'))
107    manager = CppTypeGenerator(m, _FakeSchemaLoader(m))
108    self.assertEquals(
109        'namespace foo {\n'
110        'namespace bar {\n'
111        'namespace tabs {\n'
112        'struct Tab;\n'
113        '}  // namespace tabs\n'
114        '}  // namespace bar\n'
115        '}  // namespace foo',
116        manager.GenerateForwardDeclarations().Render())
117    manager = CppTypeGenerator(self.models.get('permissions'),
118                               _FakeSchemaLoader(m))
119    self.assertEquals('', manager.GenerateIncludes().Render())
120    self.assertEquals('', manager.GenerateIncludes().Render())
121    self.assertEquals('', manager.GenerateForwardDeclarations().Render())
122    manager = CppTypeGenerator(self.models.get('content_settings'),
123                               _FakeSchemaLoader(m))
124    self.assertEquals('', manager.GenerateIncludes().Render())
125
126  def testGenerateIncludesAndForwardDeclarationsDependencies(self):
127    m = model.Model()
128    # Insert 'font_settings' before 'browser_action' in order to test that
129    # CppTypeGenerator sorts them properly.
130    m.AddNamespace(self.font_settings_json[0], 'path/to/font_settings.json')
131    m.AddNamespace(self.browser_action_json[0], 'path/to/browser_action.json')
132    dependency_tester = m.AddNamespace(self.dependency_tester_json[0],
133                                       'path/to/dependency_tester.json')
134    manager = CppTypeGenerator(m,
135                               _FakeSchemaLoader(m),
136                               default_namespace=dependency_tester)
137    self.assertEquals('#include "path/to/browser_action.h"\n'
138                      '#include "path/to/font_settings.h"',
139                      manager.GenerateIncludes().Render())
140    self.assertEquals('', manager.GenerateForwardDeclarations().Render())
141
142  def testGetCppTypeSimple(self):
143    manager = CppTypeGenerator(self.models.get('tabs'), _FakeSchemaLoader(None))
144    self.assertEquals(
145        'int',
146        manager.GetCppType(self.tabs.types['Tab'].properties['id'].type_))
147    self.assertEquals(
148        'std::string',
149        manager.GetCppType(self.tabs.types['Tab'].properties['status'].type_))
150    self.assertEquals(
151        'bool',
152        manager.GetCppType(self.tabs.types['Tab'].properties['selected'].type_))
153
154  def testStringAsType(self):
155    manager = CppTypeGenerator(self.models.get('font_settings'),
156                               _FakeSchemaLoader(None))
157    self.assertEquals(
158        'std::string',
159        manager.GetCppType(self.font_settings.types['FakeStringType']))
160
161  def testArrayAsType(self):
162    manager = CppTypeGenerator(self.models.get('browser_action'),
163                               _FakeSchemaLoader(None))
164    self.assertEquals(
165        'std::vector<int>',
166        manager.GetCppType(self.browser_action.types['ColorArray']))
167
168  def testGetCppTypeArray(self):
169    manager = CppTypeGenerator(self.models.get('windows'),
170                                _FakeSchemaLoader(None))
171    self.assertEquals(
172        'std::vector<Window>',
173        manager.GetCppType(
174            self.windows.functions['getAll'].returns_async.params[0].type_))
175    manager = CppTypeGenerator(self.models.get('permissions'),
176                               _FakeSchemaLoader(None))
177    self.assertEquals(
178        'std::vector<std::string>',
179        manager.GetCppType(
180            self.permissions.types['Permissions'].properties['origins'].type_))
181
182    manager = CppTypeGenerator(self.models.get('objects_movable'),
183                               _FakeSchemaLoader(None))
184    self.assertEquals(
185        'std::vector<MovablePod>',
186        manager.GetCppType(
187            self.objects_movable.types['MovableParent'].
188                properties['pods'].type_))
189
190  def testGetCppTypeLocalRef(self):
191    manager = CppTypeGenerator(self.models.get('tabs'), _FakeSchemaLoader(None))
192    self.assertEquals(
193        'Tab',
194        manager.GetCppType(
195            self.tabs.functions['get'].returns_async.params[0].type_))
196
197  def testGetCppTypeIncludedRef(self):
198    m = model.Model()
199    m.AddNamespace(self.windows_json[0],
200                   'path/to/windows.json',
201                   environment=CppNamespaceEnvironment('%(namespace)s'))
202    m.AddNamespace(self.tabs_json[0],
203                   'path/to/tabs.json',
204                   environment=CppNamespaceEnvironment('%(namespace)s'))
205    manager = CppTypeGenerator(m, _FakeSchemaLoader(m))
206    self.assertEquals(
207        'std::vector<tabs::Tab>',
208        manager.GetCppType(
209            self.windows.types['Window'].properties['tabs'].type_))
210
211  def testGetCppTypeWithPadForGeneric(self):
212    manager = CppTypeGenerator(self.models.get('permissions'),
213                               _FakeSchemaLoader(None))
214    self.assertEquals('std::vector<std::string>',
215        manager.GetCppType(
216            self.permissions.types['Permissions'].properties['origins'].type_,
217            is_in_container=False))
218    self.assertEquals('std::vector<std::string>',
219        manager.GetCppType(
220            self.permissions.types['Permissions'].properties['origins'].type_,
221            is_in_container=True))
222    self.assertEquals(
223        'bool',
224        manager.GetCppType(self.permissions.functions['contains'].returns_async.
225                           params[0].type_, is_in_container=True))
226
227  def testHardIncludesForEnums(self):
228    """Tests that enums generate hard includes. Note that it's important to use
229    use a separate file (cross_enums) here to isolate the test case so that
230    other types don't cause the hard-dependency.
231    """
232    m = model.Model()
233    m.AddNamespace(self.crossref_enums_json[0],
234                   'path/to/crossref_enums.json',
235                   environment=CppNamespaceEnvironment('%(namespace)s'))
236    m.AddNamespace(self.simple_api_json[0],
237                   'path/to/simple_api.json',
238                   environment=CppNamespaceEnvironment('%(namespace)s'))
239    manager = CppTypeGenerator(self.models.get('crossref_enums'),
240                               _FakeSchemaLoader(m))
241
242    self.assertEquals('#include "path/to/simple_api.h"',
243                      manager.GenerateIncludes().Render())
244
245  def testHardIncludesForEnumArrays(self):
246    """Tests that enums in arrays generate hard includes. Note that it's
247    important to use a separate file (cross_enums_array) here to isolate the
248    test case so that other types don't cause the hard-dependency.
249    """
250    m = model.Model()
251    m.AddNamespace(self.crossref_enums_array_json[0],
252                   'path/to/crossref_enums_array.json',
253                   environment=CppNamespaceEnvironment('%(namespace)s'))
254    m.AddNamespace(self.simple_api_json[0],
255                   'path/to/simple_api.json',
256                   environment=CppNamespaceEnvironment('%(namespace)s'))
257    manager = CppTypeGenerator(self.models.get('crossref_enums_array'),
258                               _FakeSchemaLoader(m))
259
260    self.assertEquals('#include "path/to/simple_api.h"',
261                      manager.GenerateIncludes().Render())
262
263if __name__ == '__main__':
264  unittest.main()
265