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
6import glob
7import json
8import os
9import re
10import subprocess
11import sys
12import unittest
13
14import PRESUBMIT
15
16
17_TEST_DATA_DIR = 'base/test/data/presubmit'
18
19
20class MockInputApi(object):
21  def __init__(self):
22    self.json = json
23    self.re = re
24    self.os_path = os.path
25    self.python_executable = sys.executable
26    self.subprocess = subprocess
27    self.files = []
28    self.is_committing = False
29
30  def AffectedFiles(self, file_filter=None):
31    return self.files
32
33  def PresubmitLocalPath(self):
34    return os.path.dirname(__file__)
35
36  def ReadFile(self, filename, mode='rU'):
37    for file_ in self.files:
38      if file_.LocalPath() == filename:
39        return '\n'.join(file_.NewContents())
40    # Otherwise, file is not in our mock API.
41    raise IOError, "No such file or directory: '%s'" % filename
42
43
44class MockOutputApi(object):
45  class PresubmitResult(object):
46    def __init__(self, message, items=None, long_text=''):
47      self.message = message
48      self.items = items
49      self.long_text = long_text
50
51  class PresubmitError(PresubmitResult):
52    def __init__(self, message, items, long_text=''):
53      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
54      self.type = 'error'
55
56  class PresubmitPromptWarning(PresubmitResult):
57    def __init__(self, message, items, long_text=''):
58      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
59      self.type = 'warning'
60
61  class PresubmitNotifyResult(PresubmitResult):
62    def __init__(self, message, items, long_text=''):
63      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
64      self.type = 'notify'
65
66  class PresubmitPromptOrNotify(PresubmitResult):
67    def __init__(self, message, items, long_text=''):
68      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
69      self.type = 'promptOrNotify'
70
71
72class MockFile(object):
73  def __init__(self, local_path, new_contents):
74    self._local_path = local_path
75    self._new_contents = new_contents
76    self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)]
77
78  def ChangedContents(self):
79    return self._changed_contents
80
81  def NewContents(self):
82    return self._new_contents
83
84  def LocalPath(self):
85    return self._local_path
86
87
88class MockChange(object):
89  def __init__(self, changed_files):
90    self._changed_files = changed_files
91
92  def LocalPaths(self):
93    return self._changed_files
94
95
96class IncludeOrderTest(unittest.TestCase):
97  def testSystemHeaderOrder(self):
98    scope = [(1, '#include <csystem.h>'),
99             (2, '#include <cppsystem>'),
100             (3, '#include "acustom.h"')]
101    all_linenums = [linenum for (linenum, _) in scope]
102    mock_input_api = MockInputApi()
103    warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api,
104                                                    '', all_linenums)
105    self.assertEqual(0, len(warnings))
106
107  def testSystemHeaderOrderMismatch1(self):
108    scope = [(10, '#include <cppsystem>'),
109             (20, '#include <csystem.h>'),
110             (30, '#include "acustom.h"')]
111    all_linenums = [linenum for (linenum, _) in scope]
112    mock_input_api = MockInputApi()
113    warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api,
114                                                    '', all_linenums)
115    self.assertEqual(1, len(warnings))
116    self.assertTrue('20' in warnings[0])
117
118  def testSystemHeaderOrderMismatch2(self):
119    scope = [(10, '#include <cppsystem>'),
120             (20, '#include "acustom.h"'),
121             (30, '#include <csystem.h>')]
122    all_linenums = [linenum for (linenum, _) in scope]
123    mock_input_api = MockInputApi()
124    warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api,
125                                                    '', all_linenums)
126    self.assertEqual(1, len(warnings))
127    self.assertTrue('30' in warnings[0])
128
129  def testSystemHeaderOrderMismatch3(self):
130    scope = [(10, '#include "acustom.h"'),
131             (20, '#include <csystem.h>'),
132             (30, '#include <cppsystem>')]
133    all_linenums = [linenum for (linenum, _) in scope]
134    mock_input_api = MockInputApi()
135    warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api,
136                                                    '', all_linenums)
137    self.assertEqual(2, len(warnings))
138    self.assertTrue('20' in warnings[0])
139    self.assertTrue('30' in warnings[1])
140
141  def testAlphabeticalOrderMismatch(self):
142    scope = [(10, '#include <csystem.h>'),
143             (15, '#include <bsystem.h>'),
144             (20, '#include <cppsystem>'),
145             (25, '#include <bppsystem>'),
146             (30, '#include "bcustom.h"'),
147             (35, '#include "acustom.h"')]
148    all_linenums = [linenum for (linenum, _) in scope]
149    mock_input_api = MockInputApi()
150    warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api,
151                                                    '', all_linenums)
152    self.assertEqual(3, len(warnings))
153    self.assertTrue('15' in warnings[0])
154    self.assertTrue('25' in warnings[1])
155    self.assertTrue('35' in warnings[2])
156
157  def testSpecialFirstInclude1(self):
158    mock_input_api = MockInputApi()
159    contents = ['#include "some/path/foo.h"',
160                '#include "a/header.h"']
161    mock_file = MockFile('some/path/foo.cc', contents)
162    warnings = PRESUBMIT._CheckIncludeOrderInFile(
163        mock_input_api, mock_file, range(1, len(contents) + 1))
164    self.assertEqual(0, len(warnings))
165
166  def testSpecialFirstInclude2(self):
167    mock_input_api = MockInputApi()
168    contents = ['#include "some/other/path/foo.h"',
169                '#include "a/header.h"']
170    mock_file = MockFile('some/path/foo.cc', contents)
171    warnings = PRESUBMIT._CheckIncludeOrderInFile(
172        mock_input_api, mock_file, range(1, len(contents) + 1))
173    self.assertEqual(0, len(warnings))
174
175  def testSpecialFirstInclude3(self):
176    mock_input_api = MockInputApi()
177    contents = ['#include "some/path/foo.h"',
178                '#include "a/header.h"']
179    mock_file = MockFile('some/path/foo_platform.cc', contents)
180    warnings = PRESUBMIT._CheckIncludeOrderInFile(
181        mock_input_api, mock_file, range(1, len(contents) + 1))
182    self.assertEqual(0, len(warnings))
183
184  def testSpecialFirstInclude4(self):
185    mock_input_api = MockInputApi()
186    contents = ['#include "some/path/bar.h"',
187                '#include "a/header.h"']
188    mock_file = MockFile('some/path/foo_platform.cc', contents)
189    warnings = PRESUBMIT._CheckIncludeOrderInFile(
190        mock_input_api, mock_file, range(1, len(contents) + 1))
191    self.assertEqual(1, len(warnings))
192    self.assertTrue('2' in warnings[0])
193
194  def testSpecialFirstInclude5(self):
195    mock_input_api = MockInputApi()
196    contents = ['#include "some/other/path/foo.h"',
197                '#include "a/header.h"']
198    mock_file = MockFile('some/path/foo-suffix.h', contents)
199    warnings = PRESUBMIT._CheckIncludeOrderInFile(
200        mock_input_api, mock_file, range(1, len(contents) + 1))
201    self.assertEqual(0, len(warnings))
202
203  def testSpecialFirstInclude6(self):
204    mock_input_api = MockInputApi()
205    contents = ['#include "some/other/path/foo_win.h"',
206                '#include <set>',
207                '#include "a/header.h"']
208    mock_file = MockFile('some/path/foo_unittest_win.h', contents)
209    warnings = PRESUBMIT._CheckIncludeOrderInFile(
210        mock_input_api, mock_file, range(1, len(contents) + 1))
211    self.assertEqual(0, len(warnings))
212
213  def testOrderAlreadyWrong(self):
214    scope = [(1, '#include "b.h"'),
215             (2, '#include "a.h"'),
216             (3, '#include "c.h"')]
217    mock_input_api = MockInputApi()
218    warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api,
219                                                    '', [3])
220    self.assertEqual(0, len(warnings))
221
222  def testConflictAdded1(self):
223    scope = [(1, '#include "a.h"'),
224             (2, '#include "c.h"'),
225             (3, '#include "b.h"')]
226    mock_input_api = MockInputApi()
227    warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api,
228                                                    '', [2])
229    self.assertEqual(1, len(warnings))
230    self.assertTrue('3' in warnings[0])
231
232  def testConflictAdded2(self):
233    scope = [(1, '#include "c.h"'),
234             (2, '#include "b.h"'),
235             (3, '#include "d.h"')]
236    mock_input_api = MockInputApi()
237    warnings = PRESUBMIT._CheckIncludeOrderForScope(scope, mock_input_api,
238                                                    '', [2])
239    self.assertEqual(1, len(warnings))
240    self.assertTrue('2' in warnings[0])
241
242  def testIfElifElseEndif(self):
243    mock_input_api = MockInputApi()
244    contents = ['#include "e.h"',
245                '#define foo',
246                '#include "f.h"',
247                '#undef foo',
248                '#include "e.h"',
249                '#if foo',
250                '#include "d.h"',
251                '#elif bar',
252                '#include "c.h"',
253                '#else',
254                '#include "b.h"',
255                '#endif',
256                '#include "a.h"']
257    mock_file = MockFile('', contents)
258    warnings = PRESUBMIT._CheckIncludeOrderInFile(
259        mock_input_api, mock_file, range(1, len(contents) + 1))
260    self.assertEqual(0, len(warnings))
261
262  def testExcludedIncludes(self):
263    # #include <sys/...>'s can appear in any order.
264    mock_input_api = MockInputApi()
265    contents = ['#include <sys/b.h>',
266                '#include <sys/a.h>']
267    mock_file = MockFile('', contents)
268    warnings = PRESUBMIT._CheckIncludeOrderInFile(
269        mock_input_api, mock_file, range(1, len(contents) + 1))
270    self.assertEqual(0, len(warnings))
271
272    contents = ['#include <atlbase.h>',
273                '#include <aaa.h>']
274    mock_file = MockFile('', contents)
275    warnings = PRESUBMIT._CheckIncludeOrderInFile(
276        mock_input_api, mock_file, range(1, len(contents) + 1))
277    self.assertEqual(0, len(warnings))
278
279    contents = ['#include "build/build_config.h"',
280                '#include "aaa.h"']
281    mock_file = MockFile('', contents)
282    warnings = PRESUBMIT._CheckIncludeOrderInFile(
283        mock_input_api, mock_file, range(1, len(contents) + 1))
284    self.assertEqual(0, len(warnings))
285
286  def testCheckOnlyCFiles(self):
287    mock_input_api = MockInputApi()
288    mock_output_api = MockOutputApi()
289    contents = ['#include <b.h>',
290                '#include <a.h>']
291    mock_file_cc = MockFile('something.cc', contents)
292    mock_file_h = MockFile('something.h', contents)
293    mock_file_other = MockFile('something.py', contents)
294    mock_input_api.files = [mock_file_cc, mock_file_h, mock_file_other]
295    warnings = PRESUBMIT._CheckIncludeOrder(mock_input_api, mock_output_api)
296    self.assertEqual(1, len(warnings))
297    self.assertEqual(2, len(warnings[0].items))
298    self.assertEqual('promptOrNotify', warnings[0].type)
299
300  def testUncheckableIncludes(self):
301    mock_input_api = MockInputApi()
302    contents = ['#include <windows.h>',
303                '#include "b.h"',
304                '#include "a.h"']
305    mock_file = MockFile('', contents)
306    warnings = PRESUBMIT._CheckIncludeOrderInFile(
307        mock_input_api, mock_file, range(1, len(contents) + 1))
308    self.assertEqual(1, len(warnings))
309
310    contents = ['#include "gpu/command_buffer/gles_autogen.h"',
311                '#include "b.h"',
312                '#include "a.h"']
313    mock_file = MockFile('', contents)
314    warnings = PRESUBMIT._CheckIncludeOrderInFile(
315        mock_input_api, mock_file, range(1, len(contents) + 1))
316    self.assertEqual(1, len(warnings))
317
318    contents = ['#include "gl_mock_autogen.h"',
319                '#include "b.h"',
320                '#include "a.h"']
321    mock_file = MockFile('', contents)
322    warnings = PRESUBMIT._CheckIncludeOrderInFile(
323        mock_input_api, mock_file, range(1, len(contents) + 1))
324    self.assertEqual(1, len(warnings))
325
326    contents = ['#include "ipc/some_macros.h"',
327                '#include "b.h"',
328                '#include "a.h"']
329    mock_file = MockFile('', contents)
330    warnings = PRESUBMIT._CheckIncludeOrderInFile(
331        mock_input_api, mock_file, range(1, len(contents) + 1))
332    self.assertEqual(1, len(warnings))
333
334
335class VersionControlConflictsTest(unittest.TestCase):
336  def testTypicalConflict(self):
337    lines = ['<<<<<<< HEAD',
338             '  base::ScopedTempDir temp_dir_;',
339             '=======',
340             '  ScopedTempDir temp_dir_;',
341             '>>>>>>> master']
342    errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
343        MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
344    self.assertEqual(3, len(errors))
345    self.assertTrue('1' in errors[0])
346    self.assertTrue('3' in errors[1])
347    self.assertTrue('5' in errors[2])
348
349
350class BadExtensionsTest(unittest.TestCase):
351  def testBadRejFile(self):
352    mock_input_api = MockInputApi()
353    mock_input_api.files = [
354      MockFile('some/path/foo.cc', ''),
355      MockFile('some/path/foo.cc.rej', ''),
356      MockFile('some/path2/bar.h.rej', ''),
357    ]
358
359    results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi())
360    self.assertEqual(1, len(results))
361    self.assertEqual(2, len(results[0].items))
362    self.assertTrue('foo.cc.rej' in results[0].items[0])
363    self.assertTrue('bar.h.rej' in results[0].items[1])
364
365  def testBadOrigFile(self):
366    mock_input_api = MockInputApi()
367    mock_input_api.files = [
368      MockFile('other/path/qux.h.orig', ''),
369      MockFile('other/path/qux.h', ''),
370      MockFile('other/path/qux.cc', ''),
371    ]
372
373    results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi())
374    self.assertEqual(1, len(results))
375    self.assertEqual(1, len(results[0].items))
376    self.assertTrue('qux.h.orig' in results[0].items[0])
377
378  def testGoodFiles(self):
379    mock_input_api = MockInputApi()
380    mock_input_api.files = [
381      MockFile('other/path/qux.h', ''),
382      MockFile('other/path/qux.cc', ''),
383    ]
384    results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi())
385    self.assertEqual(0, len(results))
386
387  def testOnlyOwnersFiles(self):
388    mock_change = MockChange([
389      'some/path/OWNERS',
390      'A\Windows\Path\OWNERS',
391    ])
392    results = PRESUBMIT.GetPreferredTryMasters(None, mock_change)
393    self.assertEqual({}, results)
394
395
396class InvalidOSMacroNamesTest(unittest.TestCase):
397  def testInvalidOSMacroNames(self):
398    lines = ['#if defined(OS_WINDOWS)',
399             ' #elif defined(OS_WINDOW)',
400             ' # if defined(OS_MACOSX) || defined(OS_CHROME)',
401             '# else  // defined(OS_MAC)',
402             '#endif  // defined(OS_MACOS)']
403    errors = PRESUBMIT._CheckForInvalidOSMacrosInFile(
404        MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
405    self.assertEqual(len(lines), len(errors))
406    self.assertTrue(':1 OS_WINDOWS' in errors[0])
407    self.assertTrue('(did you mean OS_WIN?)' in errors[0])
408
409  def testValidOSMacroNames(self):
410    lines = ['#if defined(%s)' % m for m in PRESUBMIT._VALID_OS_MACROS]
411    errors = PRESUBMIT._CheckForInvalidOSMacrosInFile(
412        MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
413    self.assertEqual(0, len(errors))
414
415
416class CheckAddedDepsHaveTetsApprovalsTest(unittest.TestCase):
417  def testFilesToCheckForIncomingDeps(self):
418    changed_lines = [
419      '"+breakpad",',
420      '"+chrome/installer",',
421      '"+chrome/plugin/chrome_content_plugin_client.h",',
422      '"+chrome/utility/chrome_content_utility_client.h",',
423      '"+chromeos/chromeos_paths.h",',
424      '"+components/crash",',
425      '"+components/nacl/common",',
426      '"+content/public/browser/render_process_host.h",',
427      '"+jni/fooblat.h",',
428      '"+grit",  # For generated headers',
429      '"+grit/generated_resources.h",',
430      '"+grit/",',
431      '"+policy",  # For generated headers and source',
432      '"+sandbox",',
433      '"+tools/memory_watcher",',
434      '"+third_party/lss/linux_syscall_support.h",',
435    ]
436    files_to_check = PRESUBMIT._FilesToCheckForIncomingDeps(re, changed_lines)
437    expected = set([
438      'breakpad/DEPS',
439      'chrome/installer/DEPS',
440      'chrome/plugin/chrome_content_plugin_client.h',
441      'chrome/utility/chrome_content_utility_client.h',
442      'chromeos/chromeos_paths.h',
443      'components/crash/DEPS',
444      'components/nacl/common/DEPS',
445      'content/public/browser/render_process_host.h',
446      'policy/DEPS',
447      'sandbox/DEPS',
448      'tools/memory_watcher/DEPS',
449      'third_party/lss/linux_syscall_support.h',
450    ])
451    self.assertEqual(expected, files_to_check);
452
453
454class JSONParsingTest(unittest.TestCase):
455  def testSuccess(self):
456    input_api = MockInputApi()
457    filename = 'valid_json.json'
458    contents = ['// This is a comment.',
459                '{',
460                '  "key1": ["value1", "value2"],',
461                '  "key2": 3  // This is an inline comment.',
462                '}'
463                ]
464    input_api.files = [MockFile(filename, contents)]
465    self.assertEqual(None,
466                     PRESUBMIT._GetJSONParseError(input_api, filename))
467
468  def testFailure(self):
469    input_api = MockInputApi()
470    test_data = [
471      ('invalid_json_1.json',
472       ['{ x }'],
473       'Expecting property name:'),
474      ('invalid_json_2.json',
475       ['// Hello world!',
476        '{ "hello": "world }'],
477       'Unterminated string starting at:'),
478      ('invalid_json_3.json',
479       ['{ "a": "b", "c": "d", }'],
480       'Expecting property name:'),
481      ('invalid_json_4.json',
482       ['{ "a": "b" "c": "d" }'],
483       'Expecting , delimiter:'),
484      ]
485
486    input_api.files = [MockFile(filename, contents)
487                       for (filename, contents, _) in test_data]
488
489    for (filename, _, expected_error) in test_data:
490      actual_error = PRESUBMIT._GetJSONParseError(input_api, filename)
491      self.assertTrue(expected_error in str(actual_error),
492                      "'%s' not found in '%s'" % (expected_error, actual_error))
493
494  def testNoEatComments(self):
495    input_api = MockInputApi()
496    file_with_comments = 'file_with_comments.json'
497    contents_with_comments = ['// This is a comment.',
498                              '{',
499                              '  "key1": ["value1", "value2"],',
500                              '  "key2": 3  // This is an inline comment.',
501                              '}'
502                              ]
503    file_without_comments = 'file_without_comments.json'
504    contents_without_comments = ['{',
505                                 '  "key1": ["value1", "value2"],',
506                                 '  "key2": 3',
507                                 '}'
508                                 ]
509    input_api.files = [MockFile(file_with_comments, contents_with_comments),
510                       MockFile(file_without_comments,
511                                contents_without_comments)]
512
513    self.assertEqual('No JSON object could be decoded',
514                     str(PRESUBMIT._GetJSONParseError(input_api,
515                                                      file_with_comments,
516                                                      eat_comments=False)))
517    self.assertEqual(None,
518                     PRESUBMIT._GetJSONParseError(input_api,
519                                                  file_without_comments,
520                                                  eat_comments=False))
521
522
523class IDLParsingTest(unittest.TestCase):
524  def testSuccess(self):
525    input_api = MockInputApi()
526    filename = 'valid_idl_basics.idl'
527    contents = ['// Tests a valid IDL file.',
528                'namespace idl_basics {',
529                '  enum EnumType {',
530                '    name1,',
531                '    name2',
532                '  };',
533                '',
534                '  dictionary MyType1 {',
535                '    DOMString a;',
536                '  };',
537                '',
538                '  callback Callback1 = void();',
539                '  callback Callback2 = void(long x);',
540                '  callback Callback3 = void(MyType1 arg);',
541                '  callback Callback4 = void(EnumType type);',
542                '',
543                '  interface Functions {',
544                '    static void function1();',
545                '    static void function2(long x);',
546                '    static void function3(MyType1 arg);',
547                '    static void function4(Callback1 cb);',
548                '    static void function5(Callback2 cb);',
549                '    static void function6(Callback3 cb);',
550                '    static void function7(Callback4 cb);',
551                '  };',
552                '',
553                '  interface Events {',
554                '    static void onFoo1();',
555                '    static void onFoo2(long x);',
556                '    static void onFoo2(MyType1 arg);',
557                '    static void onFoo3(EnumType type);',
558                '  };',
559                '};'
560                ]
561    input_api.files = [MockFile(filename, contents)]
562    self.assertEqual(None,
563                     PRESUBMIT._GetIDLParseError(input_api, filename))
564
565  def testFailure(self):
566    input_api = MockInputApi()
567    test_data = [
568      ('invalid_idl_1.idl',
569       ['//',
570        'namespace test {',
571        '  dictionary {',
572        '    DOMString s;',
573        '  };',
574        '};'],
575       'Unexpected "{" after keyword "dictionary".\n'),
576      # TODO(yoz): Disabled because it causes the IDL parser to hang.
577      # See crbug.com/363830.
578      # ('invalid_idl_2.idl',
579      #  (['namespace test {',
580      #    '  dictionary MissingSemicolon {',
581      #    '    DOMString a',
582      #    '    DOMString b;',
583      #    '  };',
584      #    '};'],
585      #   'Unexpected symbol DOMString after symbol a.'),
586      ('invalid_idl_3.idl',
587       ['//',
588        'namespace test {',
589        '  enum MissingComma {',
590        '    name1',
591        '    name2',
592        '  };',
593        '};'],
594       'Unexpected symbol name2 after symbol name1.'),
595      ('invalid_idl_4.idl',
596       ['//',
597        'namespace test {',
598        '  enum TrailingComma {',
599        '    name1,',
600        '    name2,',
601        '  };',
602        '};'],
603       'Trailing comma in block.'),
604      ('invalid_idl_5.idl',
605       ['//',
606        'namespace test {',
607        '  callback Callback1 = void(;',
608        '};'],
609       'Unexpected ";" after "(".'),
610      ('invalid_idl_6.idl',
611       ['//',
612        'namespace test {',
613        '  callback Callback1 = void(long );',
614        '};'],
615       'Unexpected ")" after symbol long.'),
616      ('invalid_idl_7.idl',
617       ['//',
618        'namespace test {',
619        '  interace Events {',
620        '    static void onFoo1();',
621        '  };',
622        '};'],
623       'Unexpected symbol Events after symbol interace.'),
624      ('invalid_idl_8.idl',
625       ['//',
626        'namespace test {',
627        '  interface NotEvent {',
628        '    static void onFoo1();',
629        '  };',
630        '};'],
631       'Did not process Interface Interface(NotEvent)'),
632      ('invalid_idl_9.idl',
633       ['//',
634        'namespace test {',
635        '  interface {',
636        '    static void function1();',
637        '  };',
638        '};'],
639       'Interface missing name.'),
640      ]
641
642    input_api.files = [MockFile(filename, contents)
643                       for (filename, contents, _) in test_data]
644
645    for (filename, _, expected_error) in test_data:
646      actual_error = PRESUBMIT._GetIDLParseError(input_api, filename)
647      self.assertTrue(expected_error in str(actual_error),
648                      "'%s' not found in '%s'" % (expected_error, actual_error))
649
650
651class TryServerMasterTest(unittest.TestCase):
652  def testTryServerMasters(self):
653    bots = {
654        'tryserver.chromium.gpu': [
655            'mac_gpu',
656            'mac_gpu_triggered_tests',
657            'linux_gpu',
658            'linux_gpu_triggered_tests',
659            'win_gpu',
660            'win_gpu_triggered_tests',
661        ],
662        'tryserver.chromium.mac': [
663            'ios_dbg_simulator',
664            'ios_rel_device',
665            'ios_rel_device_ninja',
666            'mac_asan',
667            'mac_asan_64',
668            'mac_chromium_compile_dbg',
669            'mac_chromium_compile_rel',
670            'mac_chromium_dbg',
671            'mac_chromium_rel',
672            'mac_chromium_rel_swarming',
673            'mac_nacl_sdk',
674            'mac_nacl_sdk_build',
675            'mac_rel_naclmore',
676            'mac_valgrind',
677            'mac_x64_rel',
678            'mac_xcodebuild',
679        ],
680        'tryserver.chromium.linux': [
681            'android_aosp',
682            'android_chromium_gn_compile_dbg',
683            'android_chromium_gn_compile_rel',
684            'android_clang_dbg',
685            'android_dbg',
686            'android_dbg_recipe',
687            'android_dbg_triggered_tests',
688            'android_dbg_triggered_tests_recipe',
689            'android_fyi_dbg',
690            'android_fyi_dbg_triggered_tests',
691            'android_rel',
692            'android_rel_triggered_tests',
693            'android_x86_dbg',
694            'blink_android_compile_dbg',
695            'blink_android_compile_rel',
696            'blink_presubmit',
697            'chromium_presubmit',
698            'linux_arm_cross_compile',
699            'linux_arm_tester',
700            'linux_asan',
701            'linux_browser_asan',
702            'linux_chromeos_asan',
703            'linux_chromeos_browser_asan',
704            'linux_chromeos_valgrind',
705            'linux_chromium_chromeos_clang_dbg',
706            'linux_chromium_chromeos_clang_rel',
707            'linux_chromium_chromeos_dbg',
708            'linux_chromium_chromeos_rel',
709            'linux_chromium_clang_dbg',
710            'linux_chromium_clang_rel',
711            'linux_chromium_compile_dbg',
712            'linux_chromium_compile_rel',
713            'linux_chromium_dbg',
714            'linux_chromium_gn_dbg',
715            'linux_chromium_gn_rel',
716            'linux_chromium_rel',
717            'linux_chromium_rel_swarming',
718            'linux_chromium_trusty32_dbg',
719            'linux_chromium_trusty32_rel',
720            'linux_chromium_trusty_dbg',
721            'linux_chromium_trusty_rel',
722            'linux_clang_tsan',
723            'linux_ecs_ozone',
724            'linux_layout',
725            'linux_layout_asan',
726            'linux_layout_rel',
727            'linux_layout_rel_32',
728            'linux_nacl_sdk',
729            'linux_nacl_sdk_bionic',
730            'linux_nacl_sdk_bionic_build',
731            'linux_nacl_sdk_build',
732            'linux_redux',
733            'linux_rel_naclmore',
734            'linux_rel_precise32',
735            'linux_valgrind',
736            'tools_build_presubmit',
737        ],
738        'tryserver.chromium.win': [
739            'win8_aura',
740            'win8_chromium_dbg',
741            'win8_chromium_rel',
742            'win_chromium_compile_dbg',
743            'win_chromium_compile_rel',
744            'win_chromium_dbg',
745            'win_chromium_rel',
746            'win_chromium_rel',
747            'win_chromium_rel_swarming',
748            'win_chromium_rel_swarming',
749            'win_chromium_x64_dbg',
750            'win_chromium_x64_rel',
751            'win_drmemory',
752            'win_nacl_sdk',
753            'win_nacl_sdk_build',
754            'win_rel_naclmore',
755         ],
756    }
757    for master, bots in bots.iteritems():
758      for bot in bots:
759        self.assertEqual(master, PRESUBMIT.GetTryServerMasterForBot(bot),
760                         'bot=%s: expected %s, computed %s' % (
761            bot, master, PRESUBMIT.GetTryServerMasterForBot(bot)))
762
763
764if __name__ == '__main__':
765  unittest.main()
766