Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / PRESUBMIT.py
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Top-level presubmit script for Chromium.
6
7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8 for more details about the presubmit API built into gcl.
9 """
10
11
12 import re
13 import sys
14
15
16 _EXCLUDED_PATHS = (
17     r"^breakpad[\\\/].*",
18     r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_rules.py",
19     r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_simple.py",
20     r"^native_client_sdk[\\\/]src[\\\/]tools[\\\/].*.mk",
21     r"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
22     r"^skia[\\\/].*",
23     r"^v8[\\\/].*",
24     r".*MakeFile$",
25     r".+_autogen\.h$",
26     r".+[\\\/]pnacl_shim\.c$",
27     r"^gpu[\\\/]config[\\\/].*_list_json\.cc$",
28     r"^chrome[\\\/]browser[\\\/]resources[\\\/]pdf[\\\/]index.js"
29 )
30
31 # TestRunner and NetscapePlugIn library is temporarily excluded from pan-project
32 # checks until it's transitioned to chromium coding style.
33 _TESTRUNNER_PATHS = (
34     r"^content[\\\/]shell[\\\/]renderer[\\\/]test_runner[\\\/].*",
35     r"^content[\\\/]shell[\\\/]common[\\\/]test_runner[\\\/].*",
36     r"^content[\\\/]shell[\\\/]tools[\\\/]plugin[\\\/].*",
37 )
38
39 # Fragment of a regular expression that matches C++ and Objective-C++
40 # implementation files.
41 _IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
42
43 # Regular expression that matches code only used for test binaries
44 # (best effort).
45 _TEST_CODE_EXCLUDED_PATHS = (
46     r'.*[/\\](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS,
47     r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS,
48     r'.+_(api|browser|kif|perf|pixel|unit|ui)?test(_[a-z]+)?%s' %
49         _IMPLEMENTATION_EXTENSIONS,
50     r'.+profile_sync_service_harness%s' % _IMPLEMENTATION_EXTENSIONS,
51     r'.*[/\\](test|tool(s)?)[/\\].*',
52     # content_shell is used for running layout tests.
53     r'content[/\\]shell[/\\].*',
54     # At request of folks maintaining this folder.
55     r'chrome[/\\]browser[/\\]automation[/\\].*',
56     # Non-production example code.
57     r'mojo[/\\]examples[/\\].*',
58 )
59
60 _TEST_ONLY_WARNING = (
61     'You might be calling functions intended only for testing from\n'
62     'production code.  It is OK to ignore this warning if you know what\n'
63     'you are doing, as the heuristics used to detect the situation are\n'
64     'not perfect.  The commit queue will not block on this warning.\n'
65     'Email joi@chromium.org if you have questions.')
66
67
68 _INCLUDE_ORDER_WARNING = (
69     'Your #include order seems to be broken. Send mail to\n'
70     'marja@chromium.org if this is not the case.')
71
72
73 _BANNED_OBJC_FUNCTIONS = (
74     (
75       'addTrackingRect:',
76       (
77        'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
78        'prohibited. Please use CrTrackingArea instead.',
79        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
80       ),
81       False,
82     ),
83     (
84       'NSTrackingArea',
85       (
86        'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
87        'instead.',
88        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
89       ),
90       False,
91     ),
92     (
93       'convertPointFromBase:',
94       (
95        'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
96        'Please use |convertPoint:(point) fromView:nil| instead.',
97        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
98       ),
99       True,
100     ),
101     (
102       'convertPointToBase:',
103       (
104        'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
105        'Please use |convertPoint:(point) toView:nil| instead.',
106        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
107       ),
108       True,
109     ),
110     (
111       'convertRectFromBase:',
112       (
113        'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
114        'Please use |convertRect:(point) fromView:nil| instead.',
115        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
116       ),
117       True,
118     ),
119     (
120       'convertRectToBase:',
121       (
122        'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
123        'Please use |convertRect:(point) toView:nil| instead.',
124        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
125       ),
126       True,
127     ),
128     (
129       'convertSizeFromBase:',
130       (
131        'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
132        'Please use |convertSize:(point) fromView:nil| instead.',
133        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
134       ),
135       True,
136     ),
137     (
138       'convertSizeToBase:',
139       (
140        'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
141        'Please use |convertSize:(point) toView:nil| instead.',
142        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
143       ),
144       True,
145     ),
146 )
147
148
149 _BANNED_CPP_FUNCTIONS = (
150     # Make sure that gtest's FRIEND_TEST() macro is not used; the
151     # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
152     # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
153     (
154       'FRIEND_TEST(',
155       (
156        'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
157        'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
158       ),
159       False,
160       (),
161     ),
162     (
163       'ScopedAllowIO',
164       (
165        'New code should not use ScopedAllowIO. Post a task to the blocking',
166        'pool or the FILE thread instead.',
167       ),
168       True,
169       (
170         r"^components[\\\/]breakpad[\\\/]app[\\\/]breakpad_mac\.mm$",
171         r"^content[\\\/]shell[\\\/]browser[\\\/]shell_browser_main\.cc$",
172         r"^content[\\\/]shell[\\\/]browser[\\\/]shell_message_filter\.cc$",
173         r"^net[\\\/]disk_cache[\\\/]cache_util\.cc$",
174       ),
175     ),
176     (
177       'SkRefPtr',
178       (
179         'The use of SkRefPtr is prohibited. ',
180         'Please use skia::RefPtr instead.'
181       ),
182       True,
183       (),
184     ),
185     (
186       'SkAutoRef',
187       (
188         'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
189         'Please use skia::RefPtr instead.'
190       ),
191       True,
192       (),
193     ),
194     (
195       'SkAutoTUnref',
196       (
197         'The use of SkAutoTUnref is dangerous because it implicitly ',
198         'converts to a raw pointer. Please use skia::RefPtr instead.'
199       ),
200       True,
201       (),
202     ),
203     (
204       'SkAutoUnref',
205       (
206         'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
207         'because it implicitly converts to a raw pointer. ',
208         'Please use skia::RefPtr instead.'
209       ),
210       True,
211       (),
212     ),
213     (
214       r'/HANDLE_EINTR\(.*close',
215       (
216        'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
217        'descriptor will be closed, and it is incorrect to retry the close.',
218        'Either call close directly and ignore its return value, or wrap close',
219        'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
220       ),
221       True,
222       (),
223     ),
224     (
225       r'/IGNORE_EINTR\((?!.*close)',
226       (
227        'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
228        'calls, use HANDLE_EINTR. See http://crbug.com/269623',
229       ),
230       True,
231       (
232         # Files that #define IGNORE_EINTR.
233         r'^base[\\\/]posix[\\\/]eintr_wrapper\.h$',
234         r'^ppapi[\\\/]tests[\\\/]test_broker\.cc$',
235       ),
236     ),
237 )
238
239
240 _VALID_OS_MACROS = (
241     # Please keep sorted.
242     'OS_ANDROID',
243     'OS_BSD',
244     'OS_CAT',       # For testing.
245     'OS_CHROMEOS',
246     'OS_FREEBSD',
247     'OS_IOS',
248     'OS_LINUX',
249     'OS_MACOSX',
250     'OS_NACL',
251     'OS_OPENBSD',
252     'OS_POSIX',
253     'OS_QNX',
254     'OS_SOLARIS',
255     'OS_WIN',
256 )
257
258
259 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
260   """Attempts to prevent use of functions intended only for testing in
261   non-testing code. For now this is just a best-effort implementation
262   that ignores header files and may have some false positives. A
263   better implementation would probably need a proper C++ parser.
264   """
265   # We only scan .cc files and the like, as the declaration of
266   # for-testing functions in header files are hard to distinguish from
267   # calls to such functions without a proper C++ parser.
268   file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
269
270   base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
271   inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
272   comment_pattern = input_api.re.compile(r'//.*%s' % base_function_pattern)
273   exclusion_pattern = input_api.re.compile(
274     r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
275       base_function_pattern, base_function_pattern))
276
277   def FilterFile(affected_file):
278     black_list = (_EXCLUDED_PATHS +
279                   _TEST_CODE_EXCLUDED_PATHS +
280                   input_api.DEFAULT_BLACK_LIST)
281     return input_api.FilterSourceFile(
282       affected_file,
283       white_list=(file_inclusion_pattern, ),
284       black_list=black_list)
285
286   problems = []
287   for f in input_api.AffectedSourceFiles(FilterFile):
288     local_path = f.LocalPath()
289     for line_number, line in f.ChangedContents():
290       if (inclusion_pattern.search(line) and
291           not comment_pattern.search(line) and
292           not exclusion_pattern.search(line)):
293         problems.append(
294           '%s:%d\n    %s' % (local_path, line_number, line.strip()))
295
296   if problems:
297     return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
298   else:
299     return []
300
301
302 def _CheckNoIOStreamInHeaders(input_api, output_api):
303   """Checks to make sure no .h files include <iostream>."""
304   files = []
305   pattern = input_api.re.compile(r'^#include\s*<iostream>',
306                                  input_api.re.MULTILINE)
307   for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
308     if not f.LocalPath().endswith('.h'):
309       continue
310     contents = input_api.ReadFile(f)
311     if pattern.search(contents):
312       files.append(f)
313
314   if len(files):
315     return [ output_api.PresubmitError(
316         'Do not #include <iostream> in header files, since it inserts static '
317         'initialization into every file including the header. Instead, '
318         '#include <ostream>. See http://crbug.com/94794',
319         files) ]
320   return []
321
322
323 def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
324   """Checks to make sure no source files use UNIT_TEST"""
325   problems = []
326   for f in input_api.AffectedFiles():
327     if (not f.LocalPath().endswith(('.cc', '.mm'))):
328       continue
329
330     for line_num, line in f.ChangedContents():
331       if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
332         problems.append('    %s:%d' % (f.LocalPath(), line_num))
333
334   if not problems:
335     return []
336   return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
337       '\n'.join(problems))]
338
339
340 def _CheckNoNewWStrings(input_api, output_api):
341   """Checks to make sure we don't introduce use of wstrings."""
342   problems = []
343   for f in input_api.AffectedFiles():
344     if (not f.LocalPath().endswith(('.cc', '.h')) or
345         f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h'))):
346       continue
347
348     allowWString = False
349     for line_num, line in f.ChangedContents():
350       if 'presubmit: allow wstring' in line:
351         allowWString = True
352       elif not allowWString and 'wstring' in line:
353         problems.append('    %s:%d' % (f.LocalPath(), line_num))
354         allowWString = False
355       else:
356         allowWString = False
357
358   if not problems:
359     return []
360   return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
361       '  If you are calling a cross-platform API that accepts a wstring, '
362       'fix the API.\n' +
363       '\n'.join(problems))]
364
365
366 def _CheckNoDEPSGIT(input_api, output_api):
367   """Make sure .DEPS.git is never modified manually."""
368   if any(f.LocalPath().endswith('.DEPS.git') for f in
369       input_api.AffectedFiles()):
370     return [output_api.PresubmitError(
371       'Never commit changes to .DEPS.git. This file is maintained by an\n'
372       'automated system based on what\'s in DEPS and your changes will be\n'
373       'overwritten.\n'
374       'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
375       'for more information')]
376   return []
377
378
379 def _CheckNoBannedFunctions(input_api, output_api):
380   """Make sure that banned functions are not used."""
381   warnings = []
382   errors = []
383
384   file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
385   for f in input_api.AffectedFiles(file_filter=file_filter):
386     for line_num, line in f.ChangedContents():
387       for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
388         if func_name in line:
389           problems = warnings;
390           if error:
391             problems = errors;
392           problems.append('    %s:%d:' % (f.LocalPath(), line_num))
393           for message_line in message:
394             problems.append('      %s' % message_line)
395
396   file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
397   for f in input_api.AffectedFiles(file_filter=file_filter):
398     for line_num, line in f.ChangedContents():
399       for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
400         def IsBlacklisted(affected_file, blacklist):
401           local_path = affected_file.LocalPath()
402           for item in blacklist:
403             if input_api.re.match(item, local_path):
404               return True
405           return False
406         if IsBlacklisted(f, excluded_paths):
407           continue
408         matched = False
409         if func_name[0:1] == '/':
410           regex = func_name[1:]
411           if input_api.re.search(regex, line):
412             matched = True
413         elif func_name in line:
414             matched = True
415         if matched:
416           problems = warnings;
417           if error:
418             problems = errors;
419           problems.append('    %s:%d:' % (f.LocalPath(), line_num))
420           for message_line in message:
421             problems.append('      %s' % message_line)
422
423   result = []
424   if (warnings):
425     result.append(output_api.PresubmitPromptWarning(
426         'Banned functions were used.\n' + '\n'.join(warnings)))
427   if (errors):
428     result.append(output_api.PresubmitError(
429         'Banned functions were used.\n' + '\n'.join(errors)))
430   return result
431
432
433 def _CheckNoPragmaOnce(input_api, output_api):
434   """Make sure that banned functions are not used."""
435   files = []
436   pattern = input_api.re.compile(r'^#pragma\s+once',
437                                  input_api.re.MULTILINE)
438   for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
439     if not f.LocalPath().endswith('.h'):
440       continue
441     contents = input_api.ReadFile(f)
442     if pattern.search(contents):
443       files.append(f)
444
445   if files:
446     return [output_api.PresubmitError(
447         'Do not use #pragma once in header files.\n'
448         'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
449         files)]
450   return []
451
452
453 def _CheckNoTrinaryTrueFalse(input_api, output_api):
454   """Checks to make sure we don't introduce use of foo ? true : false."""
455   problems = []
456   pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
457   for f in input_api.AffectedFiles():
458     if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
459       continue
460
461     for line_num, line in f.ChangedContents():
462       if pattern.match(line):
463         problems.append('    %s:%d' % (f.LocalPath(), line_num))
464
465   if not problems:
466     return []
467   return [output_api.PresubmitPromptWarning(
468       'Please consider avoiding the "? true : false" pattern if possible.\n' +
469       '\n'.join(problems))]
470
471
472 def _CheckUnwantedDependencies(input_api, output_api):
473   """Runs checkdeps on #include statements added in this
474   change. Breaking - rules is an error, breaking ! rules is a
475   warning.
476   """
477   # We need to wait until we have an input_api object and use this
478   # roundabout construct to import checkdeps because this file is
479   # eval-ed and thus doesn't have __file__.
480   original_sys_path = sys.path
481   try:
482     sys.path = sys.path + [input_api.os_path.join(
483         input_api.PresubmitLocalPath(), 'tools', 'checkdeps')]
484     import checkdeps
485     from cpp_checker import CppChecker
486     from rules import Rule
487   finally:
488     # Restore sys.path to what it was before.
489     sys.path = original_sys_path
490
491   added_includes = []
492   for f in input_api.AffectedFiles():
493     if not CppChecker.IsCppFile(f.LocalPath()):
494       continue
495
496     changed_lines = [line for line_num, line in f.ChangedContents()]
497     added_includes.append([f.LocalPath(), changed_lines])
498
499   deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
500
501   error_descriptions = []
502   warning_descriptions = []
503   for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
504       added_includes):
505     description_with_path = '%s\n    %s' % (path, rule_description)
506     if rule_type == Rule.DISALLOW:
507       error_descriptions.append(description_with_path)
508     else:
509       warning_descriptions.append(description_with_path)
510
511   results = []
512   if error_descriptions:
513     results.append(output_api.PresubmitError(
514         'You added one or more #includes that violate checkdeps rules.',
515         error_descriptions))
516   if warning_descriptions:
517     results.append(output_api.PresubmitPromptOrNotify(
518         'You added one or more #includes of files that are temporarily\n'
519         'allowed but being removed. Can you avoid introducing the\n'
520         '#include? See relevant DEPS file(s) for details and contacts.',
521         warning_descriptions))
522   return results
523
524
525 def _CheckFilePermissions(input_api, output_api):
526   """Check that all files have their permissions properly set."""
527   if input_api.platform == 'win32':
528     return []
529   args = [sys.executable, 'tools/checkperms/checkperms.py', '--root',
530           input_api.change.RepositoryRoot()]
531   for f in input_api.AffectedFiles():
532     args += ['--file', f.LocalPath()]
533   checkperms = input_api.subprocess.Popen(args,
534                                           stdout=input_api.subprocess.PIPE)
535   errors = checkperms.communicate()[0].strip()
536   if errors:
537     return [output_api.PresubmitError('checkperms.py failed.',
538                                       errors.splitlines())]
539   return []
540
541
542 def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
543   """Makes sure we don't include ui/aura/window_property.h
544   in header files.
545   """
546   pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
547   errors = []
548   for f in input_api.AffectedFiles():
549     if not f.LocalPath().endswith('.h'):
550       continue
551     for line_num, line in f.ChangedContents():
552       if pattern.match(line):
553         errors.append('    %s:%d' % (f.LocalPath(), line_num))
554
555   results = []
556   if errors:
557     results.append(output_api.PresubmitError(
558       'Header files should not include ui/aura/window_property.h', errors))
559   return results
560
561
562 def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
563   """Checks that the lines in scope occur in the right order.
564
565   1. C system files in alphabetical order
566   2. C++ system files in alphabetical order
567   3. Project's .h files
568   """
569
570   c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
571   cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
572   custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
573
574   C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
575
576   state = C_SYSTEM_INCLUDES
577
578   previous_line = ''
579   previous_line_num = 0
580   problem_linenums = []
581   for line_num, line in scope:
582     if c_system_include_pattern.match(line):
583       if state != C_SYSTEM_INCLUDES:
584         problem_linenums.append((line_num, previous_line_num))
585       elif previous_line and previous_line > line:
586         problem_linenums.append((line_num, previous_line_num))
587     elif cpp_system_include_pattern.match(line):
588       if state == C_SYSTEM_INCLUDES:
589         state = CPP_SYSTEM_INCLUDES
590       elif state == CUSTOM_INCLUDES:
591         problem_linenums.append((line_num, previous_line_num))
592       elif previous_line and previous_line > line:
593         problem_linenums.append((line_num, previous_line_num))
594     elif custom_include_pattern.match(line):
595       if state != CUSTOM_INCLUDES:
596         state = CUSTOM_INCLUDES
597       elif previous_line and previous_line > line:
598         problem_linenums.append((line_num, previous_line_num))
599     else:
600       problem_linenums.append(line_num)
601     previous_line = line
602     previous_line_num = line_num
603
604   warnings = []
605   for (line_num, previous_line_num) in problem_linenums:
606     if line_num in changed_linenums or previous_line_num in changed_linenums:
607       warnings.append('    %s:%d' % (file_path, line_num))
608   return warnings
609
610
611 def _CheckIncludeOrderInFile(input_api, f, changed_linenums):
612   """Checks the #include order for the given file f."""
613
614   system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
615   # Exclude the following includes from the check:
616   # 1) #include <.../...>, e.g., <sys/...> includes often need to appear in a
617   # specific order.
618   # 2) <atlbase.h>, "build/build_config.h"
619   excluded_include_pattern = input_api.re.compile(
620       r'\s*#include (\<.*/.*|\<atlbase\.h\>|"build/build_config.h")')
621   custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
622   # Match the final or penultimate token if it is xxxtest so we can ignore it
623   # when considering the special first include.
624   test_file_tag_pattern = input_api.re.compile(
625     r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
626   if_pattern = input_api.re.compile(
627       r'\s*#\s*(if|elif|else|endif|define|undef).*')
628   # Some files need specialized order of includes; exclude such files from this
629   # check.
630   uncheckable_includes_pattern = input_api.re.compile(
631       r'\s*#include '
632       '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
633
634   contents = f.NewContents()
635   warnings = []
636   line_num = 0
637
638   # Handle the special first include. If the first include file is
639   # some/path/file.h, the corresponding including file can be some/path/file.cc,
640   # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
641   # etc. It's also possible that no special first include exists.
642   # If the included file is some/path/file_platform.h the including file could
643   # also be some/path/file_xxxtest_platform.h.
644   including_file_base_name = test_file_tag_pattern.sub(
645     '', input_api.os_path.basename(f.LocalPath()))
646
647   for line in contents:
648     line_num += 1
649     if system_include_pattern.match(line):
650       # No special first include -> process the line again along with normal
651       # includes.
652       line_num -= 1
653       break
654     match = custom_include_pattern.match(line)
655     if match:
656       match_dict = match.groupdict()
657       header_basename = test_file_tag_pattern.sub(
658         '', input_api.os_path.basename(match_dict['FILE'])).replace('.h', '')
659
660       if header_basename not in including_file_base_name:
661         # No special first include -> process the line again along with normal
662         # includes.
663         line_num -= 1
664       break
665
666   # Split into scopes: Each region between #if and #endif is its own scope.
667   scopes = []
668   current_scope = []
669   for line in contents[line_num:]:
670     line_num += 1
671     if uncheckable_includes_pattern.match(line):
672       continue
673     if if_pattern.match(line):
674       scopes.append(current_scope)
675       current_scope = []
676     elif ((system_include_pattern.match(line) or
677            custom_include_pattern.match(line)) and
678           not excluded_include_pattern.match(line)):
679       current_scope.append((line_num, line))
680   scopes.append(current_scope)
681
682   for scope in scopes:
683     warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
684                                                changed_linenums))
685   return warnings
686
687
688 def _CheckIncludeOrder(input_api, output_api):
689   """Checks that the #include order is correct.
690
691   1. The corresponding header for source files.
692   2. C system files in alphabetical order
693   3. C++ system files in alphabetical order
694   4. Project's .h files in alphabetical order
695
696   Each region separated by #if, #elif, #else, #endif, #define and #undef follows
697   these rules separately.
698   """
699
700   warnings = []
701   for f in input_api.AffectedFiles():
702     if f.LocalPath().endswith(('.cc', '.h')):
703       changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
704       warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
705
706   results = []
707   if warnings:
708     results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
709                                                       warnings))
710   return results
711
712
713 def _CheckForVersionControlConflictsInFile(input_api, f):
714   pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
715   errors = []
716   for line_num, line in f.ChangedContents():
717     if pattern.match(line):
718       errors.append('    %s:%d %s' % (f.LocalPath(), line_num, line))
719   return errors
720
721
722 def _CheckForVersionControlConflicts(input_api, output_api):
723   """Usually this is not intentional and will cause a compile failure."""
724   errors = []
725   for f in input_api.AffectedFiles():
726     errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
727
728   results = []
729   if errors:
730     results.append(output_api.PresubmitError(
731       'Version control conflict markers found, please resolve.', errors))
732   return results
733
734
735 def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
736   def FilterFile(affected_file):
737     """Filter function for use with input_api.AffectedSourceFiles,
738     below.  This filters out everything except non-test files from
739     top-level directories that generally speaking should not hard-code
740     service URLs (e.g. src/android_webview/, src/content/ and others).
741     """
742     return input_api.FilterSourceFile(
743       affected_file,
744       white_list=(r'^(android_webview|base|content|net)[\\\/].*', ),
745       black_list=(_EXCLUDED_PATHS +
746                   _TEST_CODE_EXCLUDED_PATHS +
747                   input_api.DEFAULT_BLACK_LIST))
748
749   base_pattern = '"[^"]*google\.com[^"]*"'
750   comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
751   pattern = input_api.re.compile(base_pattern)
752   problems = []  # items are (filename, line_number, line)
753   for f in input_api.AffectedSourceFiles(FilterFile):
754     for line_num, line in f.ChangedContents():
755       if not comment_pattern.search(line) and pattern.search(line):
756         problems.append((f.LocalPath(), line_num, line))
757
758   if problems:
759     return [output_api.PresubmitPromptOrNotify(
760         'Most layers below src/chrome/ should not hardcode service URLs.\n'
761         'Are you sure this is correct? (Contact: joi@chromium.org)',
762         ['  %s:%d:  %s' % (
763             problem[0], problem[1], problem[2]) for problem in problems])]
764   else:
765     return []
766
767
768 def _CheckNoAbbreviationInPngFileName(input_api, output_api):
769   """Makes sure there are no abbreviations in the name of PNG files.
770   """
771   pattern = input_api.re.compile(r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$')
772   errors = []
773   for f in input_api.AffectedFiles(include_deletes=False):
774     if pattern.match(f.LocalPath()):
775       errors.append('    %s' % f.LocalPath())
776
777   results = []
778   if errors:
779     results.append(output_api.PresubmitError(
780         'The name of PNG files should not have abbreviations. \n'
781         'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
782         'Contact oshima@chromium.org if you have questions.', errors))
783   return results
784
785
786 def _FilesToCheckForIncomingDeps(re, changed_lines):
787   """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
788   a set of DEPS entries that we should look up.
789
790   For a directory (rather than a specific filename) we fake a path to
791   a specific filename by adding /DEPS. This is chosen as a file that
792   will seldom or never be subject to per-file include_rules.
793   """
794   # We ignore deps entries on auto-generated directories.
795   AUTO_GENERATED_DIRS = ['grit', 'jni']
796
797   # This pattern grabs the path without basename in the first
798   # parentheses, and the basename (if present) in the second. It
799   # relies on the simple heuristic that if there is a basename it will
800   # be a header file ending in ".h".
801   pattern = re.compile(
802       r"""['"]\+([^'"]+?)(/[a-zA-Z0-9_]+\.h)?['"].*""")
803   results = set()
804   for changed_line in changed_lines:
805     m = pattern.match(changed_line)
806     if m:
807       path = m.group(1)
808       if path.split('/')[0] not in AUTO_GENERATED_DIRS:
809         if m.group(2):
810           results.add('%s%s' % (path, m.group(2)))
811         else:
812           results.add('%s/DEPS' % path)
813   return results
814
815
816 def _CheckAddedDepsHaveTargetApprovals(input_api, output_api):
817   """When a dependency prefixed with + is added to a DEPS file, we
818   want to make sure that the change is reviewed by an OWNER of the
819   target file or directory, to avoid layering violations from being
820   introduced. This check verifies that this happens.
821   """
822   changed_lines = set()
823   for f in input_api.AffectedFiles():
824     filename = input_api.os_path.basename(f.LocalPath())
825     if filename == 'DEPS':
826       changed_lines |= set(line.strip()
827                            for line_num, line
828                            in f.ChangedContents())
829   if not changed_lines:
830     return []
831
832   virtual_depended_on_files = _FilesToCheckForIncomingDeps(input_api.re,
833                                                            changed_lines)
834   if not virtual_depended_on_files:
835     return []
836
837   if input_api.is_committing:
838     if input_api.tbr:
839       return [output_api.PresubmitNotifyResult(
840           '--tbr was specified, skipping OWNERS check for DEPS additions')]
841     if not input_api.change.issue:
842       return [output_api.PresubmitError(
843           "DEPS approval by OWNERS check failed: this change has "
844           "no Rietveld issue number, so we can't check it for approvals.")]
845     output = output_api.PresubmitError
846   else:
847     output = output_api.PresubmitNotifyResult
848
849   owners_db = input_api.owners_db
850   owner_email, reviewers = input_api.canned_checks._RietveldOwnerAndReviewers(
851       input_api,
852       owners_db.email_regexp,
853       approval_needed=input_api.is_committing)
854
855   owner_email = owner_email or input_api.change.author_email
856
857   reviewers_plus_owner = set(reviewers)
858   if owner_email:
859     reviewers_plus_owner.add(owner_email)
860   missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
861                                                  reviewers_plus_owner)
862
863   # We strip the /DEPS part that was added by
864   # _FilesToCheckForIncomingDeps to fake a path to a file in a
865   # directory.
866   def StripDeps(path):
867     start_deps = path.rfind('/DEPS')
868     if start_deps != -1:
869       return path[:start_deps]
870     else:
871       return path
872   unapproved_dependencies = ["'+%s'," % StripDeps(path)
873                              for path in missing_files]
874
875   if unapproved_dependencies:
876     output_list = [
877       output('Missing LGTM from OWNERS of dependencies added to DEPS:\n    %s' %
878              '\n    '.join(sorted(unapproved_dependencies)))]
879     if not input_api.is_committing:
880       suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
881       output_list.append(output(
882           'Suggested missing target path OWNERS:\n    %s' %
883           '\n    '.join(suggested_owners or [])))
884     return output_list
885
886   return []
887
888
889 def _CheckSpamLogging(input_api, output_api):
890   file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
891   black_list = (_EXCLUDED_PATHS +
892                 _TEST_CODE_EXCLUDED_PATHS +
893                 input_api.DEFAULT_BLACK_LIST +
894                 (r"^base[\\\/]logging\.h$",
895                  r"^base[\\\/]logging\.cc$",
896                  r"^cloud_print[\\\/]",
897                  r"^chrome[\\\/]app[\\\/]chrome_main_delegate\.cc$",
898                  r"^chrome[\\\/]browser[\\\/]chrome_browser_main\.cc$",
899                  r"^chrome[\\\/]browser[\\\/]ui[\\\/]startup[\\\/]"
900                      r"startup_browser_creator\.cc$",
901                  r"^chrome[\\\/]installer[\\\/]setup[\\\/].*",
902                  r"^chrome[\\\/]renderer[\\\/]extensions[\\\/]"
903                      r"logging_native_handler\.cc$",
904                  r"^content[\\\/]common[\\\/]gpu[\\\/]client[\\\/]"
905                      r"gl_helper_benchmark\.cc$",
906                  r"^remoting[\\\/]base[\\\/]logging\.h$",
907                  r"^remoting[\\\/]host[\\\/].*",
908                  r"^sandbox[\\\/]linux[\\\/].*",
909                  r"^tools[\\\/]",
910                  r"^ui[\\\/]aura[\\\/]bench[\\\/]bench_main\.cc$",))
911   source_file_filter = lambda x: input_api.FilterSourceFile(
912       x, white_list=(file_inclusion_pattern,), black_list=black_list)
913
914   log_info = []
915   printf = []
916
917   for f in input_api.AffectedSourceFiles(source_file_filter):
918     contents = input_api.ReadFile(f, 'rb')
919     if re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", contents):
920       log_info.append(f.LocalPath())
921     elif re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", contents):
922       log_info.append(f.LocalPath())
923
924     if re.search(r"\bprintf\(", contents):
925       printf.append(f.LocalPath())
926     elif re.search(r"\bfprintf\((stdout|stderr)", contents):
927       printf.append(f.LocalPath())
928
929   if log_info:
930     return [output_api.PresubmitError(
931       'These files spam the console log with LOG(INFO):',
932       items=log_info)]
933   if printf:
934     return [output_api.PresubmitError(
935       'These files spam the console log with printf/fprintf:',
936       items=printf)]
937   return []
938
939
940 def _CheckForAnonymousVariables(input_api, output_api):
941   """These types are all expected to hold locks while in scope and
942      so should never be anonymous (which causes them to be immediately
943      destroyed)."""
944   they_who_must_be_named = [
945     'base::AutoLock',
946     'base::AutoReset',
947     'base::AutoUnlock',
948     'SkAutoAlphaRestore',
949     'SkAutoBitmapShaderInstall',
950     'SkAutoBlitterChoose',
951     'SkAutoBounderCommit',
952     'SkAutoCallProc',
953     'SkAutoCanvasRestore',
954     'SkAutoCommentBlock',
955     'SkAutoDescriptor',
956     'SkAutoDisableDirectionCheck',
957     'SkAutoDisableOvalCheck',
958     'SkAutoFree',
959     'SkAutoGlyphCache',
960     'SkAutoHDC',
961     'SkAutoLockColors',
962     'SkAutoLockPixels',
963     'SkAutoMalloc',
964     'SkAutoMaskFreeImage',
965     'SkAutoMutexAcquire',
966     'SkAutoPathBoundsUpdate',
967     'SkAutoPDFRelease',
968     'SkAutoRasterClipValidate',
969     'SkAutoRef',
970     'SkAutoTime',
971     'SkAutoTrace',
972     'SkAutoUnref',
973   ]
974   anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
975   # bad: base::AutoLock(lock.get());
976   # not bad: base::AutoLock lock(lock.get());
977   bad_pattern = input_api.re.compile(anonymous)
978   # good: new base::AutoLock(lock.get())
979   good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
980   errors = []
981
982   for f in input_api.AffectedFiles():
983     if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
984       continue
985     for linenum, line in f.ChangedContents():
986       if bad_pattern.search(line) and not good_pattern.search(line):
987         errors.append('%s:%d' % (f.LocalPath(), linenum))
988
989   if errors:
990     return [output_api.PresubmitError(
991       'These lines create anonymous variables that need to be named:',
992       items=errors)]
993   return []
994
995
996 def _CheckCygwinShell(input_api, output_api):
997   source_file_filter = lambda x: input_api.FilterSourceFile(
998       x, white_list=(r'.+\.(gyp|gypi)$',))
999   cygwin_shell = []
1000
1001   for f in input_api.AffectedSourceFiles(source_file_filter):
1002     for linenum, line in f.ChangedContents():
1003       if 'msvs_cygwin_shell' in line:
1004         cygwin_shell.append(f.LocalPath())
1005         break
1006
1007   if cygwin_shell:
1008     return [output_api.PresubmitError(
1009       'These files should not use msvs_cygwin_shell (the default is 0):',
1010       items=cygwin_shell)]
1011   return []
1012
1013
1014 def _CheckJavaStyle(input_api, output_api):
1015   """Runs checkstyle on changed java files and returns errors if any exist."""
1016   original_sys_path = sys.path
1017   try:
1018     sys.path = sys.path + [input_api.os_path.join(
1019         input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
1020     import checkstyle
1021   finally:
1022     # Restore sys.path to what it was before.
1023     sys.path = original_sys_path
1024
1025   return checkstyle.RunCheckstyle(
1026       input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml')
1027
1028
1029 def _CommonChecks(input_api, output_api):
1030   """Checks common to both upload and commit."""
1031   results = []
1032   results.extend(input_api.canned_checks.PanProjectChecks(
1033       input_api, output_api,
1034       excluded_paths=_EXCLUDED_PATHS + _TESTRUNNER_PATHS))
1035   results.extend(_CheckAuthorizedAuthor(input_api, output_api))
1036   results.extend(
1037       _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
1038   results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
1039   results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
1040   results.extend(_CheckNoNewWStrings(input_api, output_api))
1041   results.extend(_CheckNoDEPSGIT(input_api, output_api))
1042   results.extend(_CheckNoBannedFunctions(input_api, output_api))
1043   results.extend(_CheckNoPragmaOnce(input_api, output_api))
1044   results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
1045   results.extend(_CheckUnwantedDependencies(input_api, output_api))
1046   results.extend(_CheckFilePermissions(input_api, output_api))
1047   results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
1048   results.extend(_CheckIncludeOrder(input_api, output_api))
1049   results.extend(_CheckForVersionControlConflicts(input_api, output_api))
1050   results.extend(_CheckPatchFiles(input_api, output_api))
1051   results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
1052   results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api))
1053   results.extend(_CheckForInvalidOSMacros(input_api, output_api))
1054   results.extend(_CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1055   results.extend(
1056       input_api.canned_checks.CheckChangeHasNoTabs(
1057           input_api,
1058           output_api,
1059           source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
1060   results.extend(_CheckSpamLogging(input_api, output_api))
1061   results.extend(_CheckForAnonymousVariables(input_api, output_api))
1062   results.extend(_CheckCygwinShell(input_api, output_api))
1063   results.extend(_CheckJavaStyle(input_api, output_api))
1064
1065   if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()):
1066     results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
1067         input_api, output_api,
1068         input_api.PresubmitLocalPath(),
1069         whitelist=[r'^PRESUBMIT_test\.py$']))
1070   return results
1071
1072
1073 def _CheckSubversionConfig(input_api, output_api):
1074   """Verifies the subversion config file is correctly setup.
1075
1076   Checks that autoprops are enabled, returns an error otherwise.
1077   """
1078   join = input_api.os_path.join
1079   if input_api.platform == 'win32':
1080     appdata = input_api.environ.get('APPDATA', '')
1081     if not appdata:
1082       return [output_api.PresubmitError('%APPDATA% is not configured.')]
1083     path = join(appdata, 'Subversion', 'config')
1084   else:
1085     home = input_api.environ.get('HOME', '')
1086     if not home:
1087       return [output_api.PresubmitError('$HOME is not configured.')]
1088     path = join(home, '.subversion', 'config')
1089
1090   error_msg = (
1091       'Please look at http://dev.chromium.org/developers/coding-style to\n'
1092       'configure your subversion configuration file. This enables automatic\n'
1093       'properties to simplify the project maintenance.\n'
1094       'Pro-tip: just download and install\n'
1095       'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
1096
1097   try:
1098     lines = open(path, 'r').read().splitlines()
1099     # Make sure auto-props is enabled and check for 2 Chromium standard
1100     # auto-prop.
1101     if (not '*.cc = svn:eol-style=LF' in lines or
1102         not '*.pdf = svn:mime-type=application/pdf' in lines or
1103         not 'enable-auto-props = yes' in lines):
1104       return [
1105           output_api.PresubmitNotifyResult(
1106               'It looks like you have not configured your subversion config '
1107               'file or it is not up-to-date.\n' + error_msg)
1108       ]
1109   except (OSError, IOError):
1110     return [
1111         output_api.PresubmitNotifyResult(
1112             'Can\'t find your subversion config file.\n' + error_msg)
1113     ]
1114   return []
1115
1116
1117 def _CheckAuthorizedAuthor(input_api, output_api):
1118   """For non-googler/chromites committers, verify the author's email address is
1119   in AUTHORS.
1120   """
1121   # TODO(maruel): Add it to input_api?
1122   import fnmatch
1123
1124   author = input_api.change.author_email
1125   if not author:
1126     input_api.logging.info('No author, skipping AUTHOR check')
1127     return []
1128   authors_path = input_api.os_path.join(
1129       input_api.PresubmitLocalPath(), 'AUTHORS')
1130   valid_authors = (
1131       input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
1132       for line in open(authors_path))
1133   valid_authors = [item.group(1).lower() for item in valid_authors if item]
1134   if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
1135     input_api.logging.info('Valid authors are %s', ', '.join(valid_authors))
1136     return [output_api.PresubmitPromptWarning(
1137         ('%s is not in AUTHORS file. If you are a new contributor, please visit'
1138         '\n'
1139         'http://www.chromium.org/developers/contributing-code and read the '
1140         '"Legal" section\n'
1141         'If you are a chromite, verify the contributor signed the CLA.') %
1142         author)]
1143   return []
1144
1145
1146 def _CheckPatchFiles(input_api, output_api):
1147   problems = [f.LocalPath() for f in input_api.AffectedFiles()
1148       if f.LocalPath().endswith(('.orig', '.rej'))]
1149   if problems:
1150     return [output_api.PresubmitError(
1151         "Don't commit .rej and .orig files.", problems)]
1152   else:
1153     return []
1154
1155
1156 def _DidYouMeanOSMacro(bad_macro):
1157   try:
1158     return {'A': 'OS_ANDROID',
1159             'B': 'OS_BSD',
1160             'C': 'OS_CHROMEOS',
1161             'F': 'OS_FREEBSD',
1162             'L': 'OS_LINUX',
1163             'M': 'OS_MACOSX',
1164             'N': 'OS_NACL',
1165             'O': 'OS_OPENBSD',
1166             'P': 'OS_POSIX',
1167             'S': 'OS_SOLARIS',
1168             'W': 'OS_WIN'}[bad_macro[3].upper()]
1169   except KeyError:
1170     return ''
1171
1172
1173 def _CheckForInvalidOSMacrosInFile(input_api, f):
1174   """Check for sensible looking, totally invalid OS macros."""
1175   preprocessor_statement = input_api.re.compile(r'^\s*#')
1176   os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)')
1177   results = []
1178   for lnum, line in f.ChangedContents():
1179     if preprocessor_statement.search(line):
1180       for match in os_macro.finditer(line):
1181         if not match.group(1) in _VALID_OS_MACROS:
1182           good = _DidYouMeanOSMacro(match.group(1))
1183           did_you_mean = ' (did you mean %s?)' % good if good else ''
1184           results.append('    %s:%d %s%s' % (f.LocalPath(),
1185                                              lnum,
1186                                              match.group(1),
1187                                              did_you_mean))
1188   return results
1189
1190
1191 def _CheckForInvalidOSMacros(input_api, output_api):
1192   """Check all affected files for invalid OS macros."""
1193   bad_macros = []
1194   for f in input_api.AffectedFiles():
1195     if not f.LocalPath().endswith(('.py', '.js', '.html', '.css')):
1196       bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f))
1197
1198   if not bad_macros:
1199     return []
1200
1201   return [output_api.PresubmitError(
1202       'Possibly invalid OS macro[s] found. Please fix your code\n'
1203       'or add your macro to src/PRESUBMIT.py.', bad_macros)]
1204
1205
1206 def CheckChangeOnUpload(input_api, output_api):
1207   results = []
1208   results.extend(_CommonChecks(input_api, output_api))
1209   return results
1210
1211
1212 def GetDefaultTryConfigs(bots=None):
1213   """Returns a list of ('bot', set(['tests']), optionally filtered by [bots].
1214
1215   To add tests to this list, they MUST be in the the corresponding master's
1216   gatekeeper config. For example, anything on master.chromium would be closed by
1217   tools/build/masters/master.chromium/master_gatekeeper_cfg.py.
1218
1219   If 'bots' is specified, will only return configurations for bots in that list.
1220   """
1221
1222   standard_tests = [
1223       'base_unittests',
1224       'browser_tests',
1225       'cacheinvalidation_unittests',
1226       'check_deps',
1227       'check_deps2git',
1228       'content_browsertests',
1229       'content_unittests',
1230       'crypto_unittests',
1231       #'gfx_unittests',
1232       'gpu_unittests',
1233       'interactive_ui_tests',
1234       'ipc_tests',
1235       'jingle_unittests',
1236       'media_unittests',
1237       'net_unittests',
1238       'ppapi_unittests',
1239       'printing_unittests',
1240       'sql_unittests',
1241       'sync_unit_tests',
1242       'unit_tests',
1243       # Broken in release.
1244       #'url_unittests',
1245       #'webkit_unit_tests',
1246   ]
1247
1248   builders_and_tests = {
1249       # TODO(maruel): Figure out a way to run 'sizes' where people can
1250       # effectively update the perf expectation correctly.  This requires a
1251       # clobber=True build running 'sizes'. 'sizes' is not accurate with
1252       # incremental build. Reference:
1253       # http://chromium.org/developers/tree-sheriffs/perf-sheriffs.
1254       # TODO(maruel): An option would be to run 'sizes' but not count a failure
1255       # of this step as a try job failure.
1256       'android_aosp': ['compile'],
1257       'android_clang_dbg': ['slave_steps'],
1258       'android_dbg': ['slave_steps'],
1259       'cros_x86': ['defaulttests'],
1260       'ios_dbg_simulator': [
1261           'compile',
1262           'base_unittests',
1263           'content_unittests',
1264           'crypto_unittests',
1265           'url_unittests',
1266           'net_unittests',
1267           'sql_unittests',
1268           'ui_unittests',
1269       ],
1270       'ios_rel_device': ['compile'],
1271       'linux_asan': ['defaulttests'],
1272       #TODO(stip): Change the name of this builder to reflect that it's release.
1273       'linux_gtk': standard_tests,
1274       'linux_chromeos_asan': ['defaulttests'],
1275       'linux_chromeos_clang': ['compile'],
1276       # Note: It is a Release builder even if its name convey otherwise.
1277       'linux_chromeos': standard_tests + [
1278           'app_list_unittests',
1279           'aura_unittests',
1280           'ash_unittests',
1281           'chromeos_unittests',
1282           'components_unittests',
1283           'dbus_unittests',
1284           'device_unittests',
1285           'events_unittests',
1286           'google_apis_unittests',
1287           'sandbox_linux_unittests',
1288       ],
1289       'linux_chromium_dbg': ['defaulttests'],
1290       'linux_chromium_rel': ['defaulttests'],
1291       'linux_clang': ['compile'],
1292       'linux_nacl_sdk_build': ['compile'],
1293       'linux_rel': standard_tests + [
1294           'app_list_unittests',
1295           'aura_unittests',
1296           'cc_unittests',
1297           'chromedriver_unittests',
1298           'components_unittests',
1299           'compositor_unittests',
1300           'events_unittests',
1301           'google_apis_unittests',
1302           'nacl_integration',
1303           'remoting_unittests',
1304           'sandbox_linux_unittests',
1305           'sync_integration_tests',
1306           'telemetry_perf_unittests',
1307           'telemetry_unittests',
1308       ],
1309       'mac': ['compile'],
1310       'mac_chromium_dbg': ['defaulttests'],
1311       'mac_chromium_rel': ['defaulttests'],
1312       'mac_nacl_sdk_build': ['compile'],
1313       'mac_rel': standard_tests + [
1314           'app_list_unittests',
1315           'cc_unittests',
1316           'chromedriver_unittests',
1317           'components_unittests',
1318           'google_apis_unittests',
1319           'message_center_unittests',
1320           'nacl_integration',
1321           'remoting_unittests',
1322           'sync_integration_tests',
1323           'telemetry_perf_unittests',
1324           'telemetry_unittests',
1325       ],
1326       'win': ['compile'],
1327       'win_nacl_sdk_build': ['compile'],
1328       'win_rel': standard_tests + [
1329           'app_list_unittests',
1330           'ash_unittests',
1331           'aura_unittests',
1332           'cc_unittests',
1333           'chrome_elf_unittests',
1334           'chromedriver_unittests',
1335           'components_unittests',
1336           'compositor_unittests',
1337           'events_unittests',
1338           'google_apis_unittests',
1339           'installer_util_unittests',
1340           'mini_installer_test',
1341           'nacl_integration',
1342           'remoting_unittests',
1343           'sync_integration_tests',
1344           'telemetry_perf_unittests',
1345           'telemetry_unittests',
1346           'views_unittests',
1347       ],
1348       'win_x64_rel': [
1349           'base_unittests',
1350       ],
1351   }
1352
1353   swarm_enabled_builders = (
1354       'linux_rel',
1355       'mac_rel',
1356       'win_rel',
1357   )
1358
1359   swarm_enabled_tests = (
1360       'base_unittests',
1361       'browser_tests',
1362       'interactive_ui_tests',
1363       'net_unittests',
1364       'unit_tests',
1365   )
1366
1367   for bot in builders_and_tests:
1368     if bot in swarm_enabled_builders:
1369       builders_and_tests[bot] = [x + '_swarm' if x in swarm_enabled_tests else x
1370                                  for x in builders_and_tests[bot]]
1371
1372   if bots:
1373     return [(bot, set(builders_and_tests[bot])) for bot in bots]
1374   else:
1375     return [(bot, set(tests)) for bot, tests in builders_and_tests.iteritems()]
1376
1377
1378 def CheckChangeOnCommit(input_api, output_api):
1379   results = []
1380   results.extend(_CommonChecks(input_api, output_api))
1381   # TODO(thestig) temporarily disabled, doesn't work in third_party/
1382   #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
1383   #    input_api, output_api, sources))
1384   # Make sure the tree is 'open'.
1385   results.extend(input_api.canned_checks.CheckTreeIsOpen(
1386       input_api,
1387       output_api,
1388       json_url='http://chromium-status.appspot.com/current?format=json'))
1389   results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
1390       output_api, 'http://codereview.chromium.org',
1391       ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
1392       'tryserver@chromium.org'))
1393
1394   results.extend(input_api.canned_checks.CheckChangeHasBugField(
1395       input_api, output_api))
1396   results.extend(input_api.canned_checks.CheckChangeHasDescription(
1397       input_api, output_api))
1398   results.extend(_CheckSubversionConfig(input_api, output_api))
1399   return results
1400
1401
1402 def GetPreferredTrySlaves(project, change):
1403   files = change.LocalPaths()
1404
1405   if not files or all(re.search(r'[\\/]OWNERS$', f) for f in files):
1406     return []
1407
1408   if all(re.search('\.(m|mm)$|(^|[/_])mac[/_.]', f) for f in files):
1409     return GetDefaultTryConfigs(['mac', 'mac_rel'])
1410   if all(re.search('(^|[/_])win[/_.]', f) for f in files):
1411     return GetDefaultTryConfigs(['win', 'win_rel'])
1412   if all(re.search('(^|[/_])android[/_.]', f) for f in files):
1413     return GetDefaultTryConfigs([
1414         'android_aosp',
1415         'android_clang_dbg',
1416         'android_dbg',
1417     ])
1418   if all(re.search('[/_]ios[/_.]', f) for f in files):
1419     return GetDefaultTryConfigs(['ios_rel_device', 'ios_dbg_simulator'])
1420
1421   trybots = GetDefaultTryConfigs([
1422       'android_clang_dbg',
1423       'android_dbg',
1424       'ios_dbg_simulator',
1425       'ios_rel_device',
1426       'linux_gtk',
1427       'linux_asan',
1428       'linux_chromeos',
1429       'linux_clang',
1430       'linux_nacl_sdk_build',
1431       'linux_rel',
1432       'mac',
1433       'mac_nacl_sdk_build',
1434       'mac_rel',
1435       'win',
1436       'win_nacl_sdk_build',
1437       'win_rel',
1438       'win_x64_rel',
1439   ])
1440
1441   # Match things like path/aura/file.cc and path/file_aura.cc.
1442   # Same for chromeos.
1443   if any(re.search('[/_](aura|chromeos)', f) for f in files):
1444     trybots.extend(GetDefaultTryConfigs([
1445         'linux_chromeos_asan', 'linux_chromeos_clang']))
1446
1447   # If there are gyp changes to base, build, or chromeos, run a full cros build
1448   # in addition to the shorter linux_chromeos build. Changes to high level gyp
1449   # files have a much higher chance of breaking the cros build, which is
1450   # differnt from the linux_chromeos build that most chrome developers test
1451   # with.
1452   if any(re.search('^(base|build|chromeos).*\.gypi?$', f) for f in files):
1453     trybots.extend(GetDefaultTryConfigs(['cros_x86']))
1454
1455   # The AOSP bot doesn't build the chrome/ layer, so ignore any changes to it
1456   # unless they're .gyp(i) files as changes to those files can break the gyp
1457   # step on that bot.
1458   if (not all(re.search('^chrome', f) for f in files) or
1459       any(re.search('\.gypi?$', f) for f in files)):
1460     trybots.extend(GetDefaultTryConfigs(['android_aosp']))
1461
1462   return trybots