Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / test_presubmit.py
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
6 """Unit tests for Web Development Style Guide checker."""
7
8 import os
9 import re
10 import sys
11 import unittest
12
13 test_dir = os.path.dirname(os.path.abspath(__file__))
14 sys.path.extend([
15     os.path.normpath(os.path.join(test_dir, '..', '..', 'tools')),
16     os.path.join(test_dir),
17 ])
18
19 import find_depot_tools # pylint: disable=W0611
20 from testing_support.super_mox import SuperMoxTestBase
21 from web_dev_style import css_checker, js_checker # pylint: disable=F0401
22
23
24 class JsStyleGuideTest(SuperMoxTestBase):
25   def setUp(self):
26     SuperMoxTestBase.setUp(self)
27
28     input_api = self.mox.CreateMockAnything()
29     input_api.re = re
30     output_api = self.mox.CreateMockAnything()
31     self.checker = js_checker.JSChecker(input_api, output_api)
32
33   def GetHighlight(self, line, error):
34     """Returns the substring of |line| that is highlighted in |error|."""
35     error_lines = error.split('\n')
36     highlight = error_lines[error_lines.index(line) + 1]
37     return ''.join(ch1 for (ch1, ch2) in zip(line, highlight) if ch2 == '^')
38
39   def ShouldFailConstCheck(self, line):
40     """Checks that the 'const' checker flags |line| as a style error."""
41     error = self.checker.ConstCheck(1, line)
42     self.assertNotEqual('', error,
43         'Should be flagged as style error: ' + line)
44     self.assertEqual(self.GetHighlight(line, error), 'const')
45
46   def ShouldPassConstCheck(self, line):
47     """Checks that the 'const' checker doesn't flag |line| as a style error."""
48     self.assertEqual('', self.checker.ConstCheck(1, line),
49         'Should not be flagged as style error: ' + line)
50
51   def testConstFails(self):
52     lines = [
53         "const foo = 'bar';",
54         "    const bar = 'foo';",
55
56         # Trying to use |const| as a variable name
57         "var const = 0;",
58
59         "var x = 5; const y = 6;",
60         "for (var i=0, const e=10; i<e; i++) {",
61         "for (const x=0; x<foo; i++) {",
62         "while (const x = 7) {",
63     ]
64     for line in lines:
65       self.ShouldFailConstCheck(line)
66
67   def testConstPasses(self):
68     lines = [
69         # sanity check
70         "var foo = 'bar'",
71
72         # @const JsDoc tag
73         "/** @const */ var SEVEN = 7;",
74
75         # @const tag in multi-line comment
76         " * @const",
77         "   * @const",
78
79         # @constructor tag in multi-line comment
80         " * @constructor",
81         "   * @constructor",
82
83         # words containing 'const'
84         "if (foo.constructor) {",
85         "var deconstruction = 'something';",
86         "var madeUpWordconst = 10;",
87
88         # Strings containing the word |const|
89         "var str = 'const at the beginning';",
90         "var str = 'At the end: const';",
91
92         # doing this one with regex is probably not practical
93         #"var str = 'a const in the middle';",
94     ]
95     for line in lines:
96       self.ShouldPassConstCheck(line)
97
98   def ShouldFailChromeSendCheck(self, line):
99     """Checks that the 'chrome.send' checker flags |line| as a style error."""
100     error = self.checker.ChromeSendCheck(1, line)
101     self.assertNotEqual('', error,
102         'Should be flagged as style error: ' + line)
103     self.assertEqual(self.GetHighlight(line, error), ', []')
104
105   def ShouldPassChromeSendCheck(self, line):
106     """Checks that the 'chrome.send' checker doesn't flag |line| as a style
107        error.
108     """
109     self.assertEqual('', self.checker.ChromeSendCheck(1, line),
110         'Should not be flagged as style error: ' + line)
111
112   def testChromeSendFails(self):
113     lines = [
114         "chrome.send('message', []);",
115         "  chrome.send('message', []);",
116     ]
117     for line in lines:
118       self.ShouldFailChromeSendCheck(line)
119
120   def testChromeSendPasses(self):
121     lines = [
122         "chrome.send('message', constructArgs('foo', []));",
123         "  chrome.send('message', constructArgs('foo', []));",
124         "chrome.send('message', constructArgs([]));",
125         "  chrome.send('message', constructArgs([]));",
126     ]
127     for line in lines:
128       self.ShouldPassChromeSendCheck(line)
129
130   def ShouldFailEndJsDocCommentCheck(self, line):
131     """Checks that the **/ checker flags |line| as a style error."""
132     error = self.checker.EndJsDocCommentCheck(1, line)
133     self.assertNotEqual('', error,
134         'Should be flagged as style error: ' + line)
135     self.assertEqual(self.GetHighlight(line, error), '**/')
136
137   def ShouldPassEndJsDocCommentCheck(self, line):
138     """Checks that the **/ checker doesn't flag |line| as a style error."""
139     self.assertEqual('', self.checker.EndJsDocCommentCheck(1, line),
140         'Should not be flagged as style error: ' + line)
141
142   def testEndJsDocCommentFails(self):
143     lines = [
144         "/** @override **/",
145         "/** @type {number} @const **/",
146         "  **/",
147         "**/  ",
148     ]
149     for line in lines:
150       self.ShouldFailEndJsDocCommentCheck(line)
151
152   def testEndJsDocCommentPasses(self):
153     lines = [
154         "/***************/",  # visual separators
155         "  */",  # valid JSDoc comment ends
156         "*/  ",
157         "/**/",  # funky multi-line comment enders
158         "/** @override */",  # legit JSDoc one-liners
159     ]
160     for line in lines:
161       self.ShouldPassEndJsDocCommentCheck(line)
162
163   def ShouldFailGetElementByIdCheck(self, line):
164     """Checks that the 'getElementById' checker flags |line| as a style
165        error.
166     """
167     error = self.checker.GetElementByIdCheck(1, line)
168     self.assertNotEqual('', error,
169         'Should be flagged as style error: ' + line)
170     self.assertEqual(self.GetHighlight(line, error), 'document.getElementById')
171
172   def ShouldPassGetElementByIdCheck(self, line):
173     """Checks that the 'getElementById' checker doesn't flag |line| as a style
174        error.
175     """
176     self.assertEqual('', self.checker.GetElementByIdCheck(1, line),
177         'Should not be flagged as style error: ' + line)
178
179   def testGetElementByIdFails(self):
180     lines = [
181         "document.getElementById('foo');",
182         "  document.getElementById('foo');",
183         "var x = document.getElementById('foo');",
184         "if (document.getElementById('foo').hidden) {",
185     ]
186     for line in lines:
187       self.ShouldFailGetElementByIdCheck(line)
188
189   def testGetElementByIdPasses(self):
190     lines = [
191         "elem.ownerDocument.getElementById('foo');",
192         "  elem.ownerDocument.getElementById('foo');",
193         "var x = elem.ownerDocument.getElementById('foo');",
194         "if (elem.ownerDocument.getElementById('foo').hidden) {",
195         "doc.getElementById('foo');",
196         "  doc.getElementById('foo');",
197         "cr.doc.getElementById('foo');",
198         "  cr.doc.getElementById('foo');",
199         "var x = doc.getElementById('foo');",
200         "if (doc.getElementById('foo').hidden) {",
201     ]
202     for line in lines:
203       self.ShouldPassGetElementByIdCheck(line)
204
205   def ShouldFailInheritDocCheck(self, line):
206     """Checks that the '@inheritDoc' checker flags |line| as a style error."""
207     error = self.checker.InheritDocCheck(1, line)
208     self.assertNotEqual('', error,
209         msg='Should be flagged as style error: ' + line)
210     self.assertEqual(self.GetHighlight(line, error), '@inheritDoc')
211
212   def ShouldPassInheritDocCheck(self, line):
213     """Checks that the '@inheritDoc' checker doesn't flag |line| as a style
214        error.
215     """
216     self.assertEqual('', self.checker.InheritDocCheck(1, line),
217         msg='Should not be flagged as style error: ' + line)
218
219   def testInheritDocFails(self):
220     lines = [
221         " /** @inheritDoc */",
222         "   * @inheritDoc",
223     ]
224     for line in lines:
225       self.ShouldFailInheritDocCheck(line)
226
227   def testInheritDocPasses(self):
228     lines = [
229         "And then I said, but I won't @inheritDoc! Hahaha!",
230         " If your dad's a doctor, do you inheritDoc?",
231         "  What's up, inherit doc?",
232         "   this.inheritDoc(someDoc)",
233     ]
234     for line in lines:
235       self.ShouldPassInheritDocCheck(line)
236
237   def ShouldFailWrapperTypeCheck(self, line):
238     """Checks that the use of wrapper types (i.e. new Number(), @type {Number})
239        is a style error.
240     """
241     error = self.checker.WrapperTypeCheck(1, line)
242     self.assertNotEqual('', error,
243         msg='Should be flagged as style error: ' + line)
244     highlight = self.GetHighlight(line, error)
245     self.assertTrue(highlight in ('Boolean', 'Number', 'String'))
246
247   def ShouldPassWrapperTypeCheck(self, line):
248     """Checks that the wrapper type checker doesn't flag |line| as a style
249        error.
250     """
251     self.assertEqual('', self.checker.WrapperTypeCheck(1, line),
252         msg='Should not be flagged as style error: ' + line)
253
254   def testWrapperTypePasses(self):
255     lines = [
256         "/** @param {!ComplexType} */",
257         "  * @type {Object}",
258         "   * @param {Function=} opt_callback",
259         "    * @param {} num Number of things to add to {blah}.",
260         "   *  @return {!print_preview.PageNumberSet}",
261         " /* @returns {Number} */",  # Should be /** @return {Number} */
262         "* @param {!LocalStrings}"
263         " Your type of Boolean is false!",
264         "  Then I parameterized her Number from her friend!",
265         "   A String of Pearls",
266         "    types.params.aBoolean.typeString(someNumber)",
267     ]
268     for line in lines:
269       self.ShouldPassWrapperTypeCheck(line)
270
271   def testWrapperTypeFails(self):
272     lines = [
273         "  /**@type {String}*/(string)",
274         "   * @param{Number=} opt_blah A number",
275         "/** @private @return {!Boolean} */",
276         " * @param {number|String}",
277     ]
278     for line in lines:
279       self.ShouldFailWrapperTypeCheck(line)
280
281   def ShouldFailVarNameCheck(self, line):
282     """Checks that var unix_hacker, $dollar are style errors."""
283     error = self.checker.VarNameCheck(1, line)
284     self.assertNotEqual('', error,
285         msg='Should be flagged as style error: ' + line)
286     highlight = self.GetHighlight(line, error)
287     self.assertFalse('var ' in highlight);
288
289   def ShouldPassVarNameCheck(self, line):
290     """Checks that variableNamesLikeThis aren't style errors."""
291     self.assertEqual('', self.checker.VarNameCheck(1, line),
292         msg='Should not be flagged as style error: ' + line)
293
294   def testVarNameFails(self):
295     lines = [
296         "var private_;",
297         " var _super_private",
298         "  var unix_hacker = someFunc();",
299     ]
300     for line in lines:
301       self.ShouldFailVarNameCheck(line)
302
303   def testVarNamePasses(self):
304     lines = [
305         "  var namesLikeThis = [];",
306         " for (var i = 0; i < 10; ++i) { ",
307         "for (var i in obj) {",
308         " var one, two, three;",
309         "  var magnumPI = {};",
310         " var g_browser = 'da browzer';",
311         "/** @const */ var Bla = options.Bla;",  # goog.scope() replacement.
312         " var $ = function() {",                 # For legacy reasons.
313         "  var StudlyCaps = cr.define('bla')",   # Classes.
314         " var SCARE_SMALL_CHILDREN = [",         # TODO(dbeam): add @const in
315                                                  # front of all these vars like
316         "/** @const */ CONST_VAR = 1;",          # this line has (<--).
317     ]
318     for line in lines:
319       self.ShouldPassVarNameCheck(line)
320
321
322 class CssStyleGuideTest(SuperMoxTestBase):
323   def setUp(self):
324     SuperMoxTestBase.setUp(self)
325
326     self.fake_file_name = 'fake.css'
327
328     self.fake_file = self.mox.CreateMockAnything()
329     self.mox.StubOutWithMock(self.fake_file, 'LocalPath')
330     self.fake_file.LocalPath().AndReturn(self.fake_file_name)
331     # Actual calls to NewContents() are defined in each test.
332     self.mox.StubOutWithMock(self.fake_file, 'NewContents')
333
334     self.input_api = self.mox.CreateMockAnything()
335     self.input_api.re = re
336     self.mox.StubOutWithMock(self.input_api, 'AffectedSourceFiles')
337     self.input_api.AffectedFiles(
338         include_deletes=False, file_filter=None).AndReturn([self.fake_file])
339
340     # Actual creations of PresubmitPromptWarning are defined in each test.
341     self.output_api = self.mox.CreateMockAnything()
342     self.mox.StubOutWithMock(self.output_api, 'PresubmitPromptWarning',
343                              use_mock_anything=True)
344
345     author_msg = ('Was the CSS checker useful? '
346                   'Send feedback or hate mail to dbeam@chromium.org.')
347     self.output_api = self.mox.CreateMockAnything()
348     self.mox.StubOutWithMock(self.output_api, 'PresubmitNotifyResult',
349                              use_mock_anything=True)
350     self.output_api.PresubmitNotifyResult(author_msg).AndReturn(None)
351
352   def VerifyContentsProducesOutput(self, contents, output):
353     self.fake_file.NewContents().AndReturn(contents.splitlines())
354     self.output_api.PresubmitPromptWarning(
355         self.fake_file_name + ':\n' + output.strip()).AndReturn(None)
356     self.mox.ReplayAll()
357     css_checker.CSSChecker(self.input_api, self.output_api).RunChecks()
358
359   def testCssAlphaWithAtBlock(self):
360     self.VerifyContentsProducesOutput("""
361 <include src="../shared/css/cr/ui/overlay.css">
362 <include src="chrome://resources/totally-cool.css" />
363
364 /* A hopefully safely ignored comment and @media statement. /**/
365 @media print {
366   div {
367     display: block;
368     color: red;
369   }
370 }
371
372 .rule {
373   z-index: 5;
374 <if expr="not is macosx">
375   background-image: url(chrome://resources/BLAH); /* TODO(dbeam): Fix this. */
376   background-color: rgb(235, 239, 249);
377 </if>
378 <if expr="is_macosx">
379   background-color: white;
380   background-image: url(chrome://resources/BLAH2);
381 </if>
382   color: black;
383 }
384
385 <if expr="is_macosx">
386 .language-options-right {
387   visibility: hidden;
388   opacity: 1; /* TODO(dbeam): Fix this. */
389 }
390 </if>""", """
391 - Alphabetize properties and list vendor specific (i.e. -webkit) above standard.
392     display: block;
393     color: red;
394
395     z-index: 5;
396     color: black;""")
397
398   def testCssAlphaWithNonStandard(self):
399     self.VerifyContentsProducesOutput("""
400 div {
401   /* A hopefully safely ignored comment and @media statement. /**/
402   color: red;
403   -webkit-margin-start: 5px;
404 }""", """
405 - Alphabetize properties and list vendor specific (i.e. -webkit) above standard.
406     color: red;
407     -webkit-margin-start: 5px;""")
408
409   def testCssAlphaWithLongerDashedProps(self):
410     self.VerifyContentsProducesOutput("""
411 div {
412   border-left: 5px;  /* A hopefully removed comment. */
413   border: 5px solid red;
414 }""", """
415 - Alphabetize properties and list vendor specific (i.e. -webkit) above standard.
416     border-left: 5px;
417     border: 5px solid red;""")
418
419   def testCssBracesHaveSpaceBeforeAndNothingAfter(self):
420     self.VerifyContentsProducesOutput("""
421 /* Hello! */div/* Comment here*/{
422   display: block;
423 }
424
425 blah /* hey! */
426 {
427   rule: value;
428 }
429
430 .this.is { /* allowed */
431   rule: value;
432 }""", """
433 - Start braces ({) end a selector, have a space before them and no rules after.
434     div{
435     {""")
436
437   def testCssClassesUseDashes(self):
438     self.VerifyContentsProducesOutput("""
439 .className,
440 .ClassName,
441 .class-name /* We should not catch this. */,
442 .class_name {
443   display: block;
444 }""", """
445  - Classes use .dash-form.
446     .className,
447     .ClassName,
448     .class_name {""")
449
450   def testCssCloseBraceOnNewLine(self):
451     self.VerifyContentsProducesOutput("""
452 @media { /* TODO(dbeam) Fix this case. */
453   .rule {
454     display: block;
455   }}
456
457 @-webkit-keyframe blah {
458   100% { height: -500px 0; }
459 }
460
461 #rule {
462   rule: value; }""", """
463 - Always put a rule closing brace (}) on a new line.
464     rule: value; }""")
465
466   def testCssColonsHaveSpaceAfter(self):
467     self.VerifyContentsProducesOutput("""
468 div:not(.class):not([attr=5]), /* We should not catch this. */
469 div:not(.class):not([attr]) /* Nor this. */ {
470   background: url(data:image/jpeg,asdfasdfsadf); /* Ignore this. */
471   background: -webkit-linear-gradient(left, red,
472                                       80% blah blee blar);
473   color: red;
474   display:block;
475 }""", """
476 - Colons (:) should have a space after them.
477     display:block;
478
479 - Don't use data URIs in source files. Use grit instead.
480     background: url(data:image/jpeg,asdfasdfsadf);""")
481
482   def testCssFavorSingleQuotes(self):
483     self.VerifyContentsProducesOutput("""
484 html[dir="rtl"] body,
485 html[dir=ltr] body /* TODO(dbeam): Require '' around rtl in future? */ {
486   background: url("chrome://resources/BLAH");
487   font-family: "Open Sans";
488 <if expr="is_macosx">
489   blah: blee;
490 </if>
491 }""", """
492 - Use single quotes (') instead of double quotes (") in strings.
493     html[dir="rtl"] body,
494     background: url("chrome://resources/BLAH");
495     font-family: "Open Sans";""")
496
497   def testCssHexCouldBeShorter(self):
498     self.VerifyContentsProducesOutput("""
499 #abc,
500 #abc-,
501 #abc-ghij,
502 #abcdef-,
503 #abcdef-ghij,
504 #aaaaaa,
505 #bbaacc {
506   background-color: #336699; /* Ignore short hex rule if not gray. */
507   color: #999999;
508   color: #666;
509 }""", """
510 - Use abbreviated hex (#rgb) when in form #rrggbb.
511     color: #999999; (replace with #999)
512
513 - Use rgb() over #hex when not a shade of gray (like #333).
514     background-color: #336699; (replace with rgb(51, 102, 153))""")
515
516   def testCssUseMillisecondsForSmallTimes(self):
517     self.VerifyContentsProducesOutput("""
518 .transition-0s /* This is gross but may happen. */ {
519   transform: one 0.2s;
520   transform: two .1s;
521   transform: tree 1s;
522   transform: four 300ms;
523 }""", """
524 - Use milliseconds for time measurements under 1 second.
525     transform: one 0.2s; (replace with 200ms)
526     transform: two .1s; (replace with 100ms)""")
527
528   def testCssNoDataUrisInSourceFiles(self):
529     self.VerifyContentsProducesOutput("""
530 img {
531   background: url( data:image/jpeg,4\/\/350|\/|3|2 );
532   background: url('data:image/jpeg,4\/\/350|\/|3|2');
533 }""", """
534 - Don't use data URIs in source files. Use grit instead.
535     background: url( data:image/jpeg,4\/\/350|\/|3|2 );
536     background: url('data:image/jpeg,4\/\/350|\/|3|2');""")
537
538   def testCssOneRulePerLine(self):
539     self.VerifyContentsProducesOutput("""
540 a:not([hidden]):not(.custom-appearance):not([version=1]):first-of-type,
541 a:not([hidden]):not(.custom-appearance):not([version=1]):first-of-type ~
542     input[type='checkbox']:not([hidden]),
543 div {
544   background: url(chrome://resources/BLAH);
545   rule: value; /* rule: value; */
546   rule: value; rule: value;
547 }""", """
548 - One rule per line (what not to do: color: red; margin: 0;).
549     rule: value; rule: value;""")
550
551   def testCssOneSelectorPerLine(self):
552     self.VerifyContentsProducesOutput("""
553 a,
554 div,a,
555 div,/* Hello! */ span,
556 #id.class([dir=rtl):not(.class):any(a, b, d) {
557   rule: value;
558 }
559
560 a,
561 div,a {
562   some-other: rule here;
563 }""", """
564 - One selector per line (what not to do: a, b {}).
565     div,a,
566     div, span,
567     div,a {""")
568
569   def testCssPseudoElementDoubleColon(self):
570     self.VerifyContentsProducesOutput("""
571 a:href,
572 br::after,
573 ::-webkit-scrollbar-thumb,
574 a:not([empty]):hover:focus:active, /* shouldn't catch here and above */
575 abbr:after,
576 .tree-label:empty:after,
577 b:before,
578 :-WebKit-ScrollBar {
579   rule: value;
580 }""", """
581 - Pseudo-elements should use double colon (i.e. ::after).
582     :after (should be ::after)
583     :after (should be ::after)
584     :before (should be ::before)
585     :-WebKit-ScrollBar (should be ::-WebKit-ScrollBar)
586     """)
587
588   def testCssRgbIfNotGray(self):
589     self.VerifyContentsProducesOutput("""
590 #abc,
591 #aaa,
592 #aabbcc {
593   background: -webkit-linear-gradient(left, from(#abc), to(#def));
594   color: #bad;
595   color: #bada55;
596 }""", """
597 - Use rgb() over #hex when not a shade of gray (like #333).
598     background: -webkit-linear-gradient(left, from(#abc), to(#def)); """
599 """(replace with rgb(170, 187, 204), rgb(221, 238, 255))
600     color: #bad; (replace with rgb(187, 170, 221))
601     color: #bada55; (replace with rgb(186, 218, 85))""")
602
603   def testCssZeroLengthTerms(self):
604     self.VerifyContentsProducesOutput("""
605 @-webkit-keyframe anim {
606   0% { /* Ignore key frames */
607     width: 0px;
608   }
609   10% {
610     width: 10px;
611   }
612   100% {
613     width: 100px;
614   }
615 }
616
617 /* http://crbug.com/359682 */
618 #spinner-container #spinner {
619   -webkit-animation-duration: 1.0s;
620 }
621
622 .media-button.play > .state0.active,
623 .media-button[state='0'] > .state0.normal /* blah */, /* blee */
624 .media-button[state='0']:not(.disabled):hover > .state0.hover {
625   -webkit-animation: anim 0s;
626   -webkit-animation-duration: anim 0ms;
627   -webkit-transform: scale(0%),
628                      translateX(0deg),
629                      translateY(0rad),
630                      translateZ(0grad);
631   background-position-x: 0em;
632   background-position-y: 0ex;
633   border-width: 0em;
634   color: hsl(0, 0%, 85%); /* Shouldn't trigger error. */
635   opacity: .0;
636   opacity: 0.0;
637   opacity: 0.;
638 }
639
640 @page {
641   border-width: 0mm;
642   height: 0cm;
643   width: 0in;
644 }""", """
645 - Make all zero length terms (i.e. 0px) 0 unless inside of hsl() or part of"""
646 """ @keyframe.
647     width: 0px;
648     -webkit-animation: anim 0s;
649     -webkit-animation-duration: anim 0ms;
650     -webkit-transform: scale(0%),
651     translateX(0deg),
652     translateY(0rad),
653     translateZ(0grad);
654     background-position-x: 0em;
655     background-position-y: 0ex;
656     border-width: 0em;
657     opacity: .0;
658     opacity: 0.0;
659     opacity: 0.;
660     border-width: 0mm;
661     height: 0cm;
662     width: 0in;
663 """)
664
665 if __name__ == '__main__':
666   unittest.main()