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 os
7import unittest
8
9from checker import Checker
10from processor import FileCache, Processor
11
12
13ASSERT_FILE = os.path.join("..", "..", "ui", "webui", "resources", "js",
14    "assert.js")
15CR_FILE = os.path.join("..", "..", "ui", "webui", "resources", "js", "cr.js")
16UI_FILE = os.path.join("..", "..", "ui", "webui", "resources", "js", "cr",
17    "ui.js")
18
19
20def rel_to_abs(rel_path):
21  script_path = os.path.dirname(os.path.abspath(__file__))
22  return os.path.join(script_path, rel_path)
23
24
25class CompilerCustomizationTest(unittest.TestCase):
26  _ASSERT_DEFINITION = Processor(rel_to_abs(ASSERT_FILE)).contents
27  _CR_DEFINE_DEFINITION = Processor(rel_to_abs(CR_FILE)).contents
28  _CR_UI_DECORATE_DEFINITION = Processor(rel_to_abs(UI_FILE)).contents
29
30  def setUp(self):
31    self._checker = Checker()
32
33  def _runChecker(self, source_code):
34    file_path = "/script.js"
35    FileCache._cache[file_path] = source_code
36    return self._checker.check(file_path)
37
38  def _runCheckerTestExpectError(self, source_code, expected_error):
39    _, output = self._runChecker(source_code)
40
41    self.assertTrue(expected_error in output,
42        msg="Expected chunk: \n%s\n\nOutput:\n%s\n" % (
43            expected_error, output))
44
45  def _runCheckerTestExpectSuccess(self, source_code):
46    return_code, output = self._runChecker(source_code)
47
48    self.assertTrue(return_code == 0,
49        msg="Expected success, got return code %d\n\nOutput:\n%s\n" % (
50            return_code, output))
51
52  def testGetInstance(self):
53    self._runCheckerTestExpectError("""
54var cr = {
55  /** @param {!Function} ctor */
56  addSingletonGetter: function(ctor) {
57    ctor.getInstance = function() {
58      return ctor.instance_ || (ctor.instance_ = new ctor());
59    };
60  }
61};
62
63/** @constructor */
64function Class() {
65  /** @param {number} num */
66  this.needsNumber = function(num) {};
67}
68
69cr.addSingletonGetter(Class);
70Class.getInstance().needsNumber("wrong type");
71""", "ERROR - actual parameter 1 of Class.needsNumber does not match formal "
72        "parameter")
73
74  def testCrDefineFunctionDefinition(self):
75    self._runCheckerTestExpectError(self._CR_DEFINE_DEFINITION + """
76cr.define('a.b.c', function() {
77  /** @param {number} num */
78  function internalName(num) {}
79
80  return {
81    needsNumber: internalName
82  };
83});
84
85a.b.c.needsNumber("wrong type");
86""", "ERROR - actual parameter 1 of a.b.c.needsNumber does not match formal "
87        "parameter")
88
89  def testCrDefineFunctionAssignment(self):
90    self._runCheckerTestExpectError(self._CR_DEFINE_DEFINITION + """
91cr.define('a.b.c', function() {
92  /** @param {number} num */
93  var internalName = function(num) {};
94
95  return {
96    needsNumber: internalName
97  };
98});
99
100a.b.c.needsNumber("wrong type");
101""", "ERROR - actual parameter 1 of a.b.c.needsNumber does not match formal "
102        "parameter")
103
104  def testCrDefineConstructorDefinitionPrototypeMethod(self):
105    self._runCheckerTestExpectError(self._CR_DEFINE_DEFINITION + """
106cr.define('a.b.c', function() {
107  /** @constructor */
108  function ClassInternalName() {}
109
110  ClassInternalName.prototype = {
111    /** @param {number} num */
112    method: function(num) {}
113  };
114
115  return {
116    ClassExternalName: ClassInternalName
117  };
118});
119
120new a.b.c.ClassExternalName().method("wrong type");
121""", "ERROR - actual parameter 1 of a.b.c.ClassExternalName.prototype.method "
122        "does not match formal parameter")
123
124  def testCrDefineConstructorAssignmentPrototypeMethod(self):
125    self._runCheckerTestExpectError(self._CR_DEFINE_DEFINITION + """
126cr.define('a.b.c', function() {
127  /** @constructor */
128  var ClassInternalName = function() {};
129
130  ClassInternalName.prototype = {
131    /** @param {number} num */
132    method: function(num) {}
133  };
134
135  return {
136    ClassExternalName: ClassInternalName
137  };
138});
139
140new a.b.c.ClassExternalName().method("wrong type");
141""", "ERROR - actual parameter 1 of a.b.c.ClassExternalName.prototype.method "
142        "does not match formal parameter")
143
144  def testCrDefineEnum(self):
145    self._runCheckerTestExpectError(self._CR_DEFINE_DEFINITION + """
146cr.define('a.b.c', function() {
147  /** @enum {string} */
148  var internalNameForEnum = {key: 'wrong_type'};
149
150  return {
151    exportedEnum: internalNameForEnum
152  };
153});
154
155/** @param {number} num */
156function needsNumber(num) {}
157
158needsNumber(a.b.c.exportedEnum.key);
159""", "ERROR - actual parameter 1 of needsNumber does not match formal "
160        "parameter")
161
162  def testObjectDefineProperty(self):
163    self._runCheckerTestExpectSuccess("""
164/** @constructor */
165function Class() {}
166
167Object.defineProperty(Class.prototype, 'myProperty', {});
168
169alert(new Class().myProperty);
170""")
171
172  def testCrDefineProperty(self):
173    self._runCheckerTestExpectSuccess(self._CR_DEFINE_DEFINITION + """
174/** @constructor */
175function Class() {}
176
177cr.defineProperty(Class.prototype, 'myProperty', cr.PropertyKind.JS);
178
179alert(new Class().myProperty);
180""")
181
182  def testCrDefinePropertyTypeChecking(self):
183    self._runCheckerTestExpectError(self._CR_DEFINE_DEFINITION + """
184/** @constructor */
185function Class() {}
186
187cr.defineProperty(Class.prototype, 'booleanProp', cr.PropertyKind.BOOL_ATTR);
188
189/** @param {number} num */
190function needsNumber(num) {}
191
192needsNumber(new Class().booleanProp);
193""", "ERROR - actual parameter 1 of needsNumber does not match formal "
194        "parameter")
195
196  def testCrDefineOnCrWorks(self):
197    self._runCheckerTestExpectSuccess(self._CR_DEFINE_DEFINITION + """
198cr.define('cr', function() {
199  return {};
200});
201""")
202
203  def testAssertWorks(self):
204    self._runCheckerTestExpectSuccess(self._ASSERT_DEFINITION + """
205/** @return {?string} */
206function f() {
207  return "string";
208}
209
210/** @type {!string} */
211var a = assert(f());
212""")
213
214  def testAssertInstanceofWorks(self):
215    self._runCheckerTestExpectSuccess(self._ASSERT_DEFINITION + """
216/** @constructor */
217function Class() {}
218
219/** @return {Class} */
220function f() {
221  var a = document.createElement('div');
222  return assertInstanceof(a, Class);
223}
224""")
225
226  def testCrUiDecorateWorks(self):
227    self._runCheckerTestExpectSuccess(self._CR_DEFINE_DEFINITION +
228        self._CR_UI_DECORATE_DEFINITION + """
229/** @constructor */
230function Class() {}
231
232/** @return {Class} */
233function f() {
234  var a = document.createElement('div');
235  cr.ui.decorate(a, Class);
236  return a;
237}
238""")
239
240
241if __name__ == "__main__":
242  unittest.main()
243