2 # Copyright 2020 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
19 os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')))
23 # Call has argument input to match subprocess.run
24 # pylint: disable=redefined-builtin
27 class FakeMBW(mb.MetaBuildWrapper):
28 def __init__(self, win32=False):
31 # Override vars for test portability.
33 self.chromium_src_dir = 'c:\\fake_src'
34 self.default_config = 'c:\\fake_src\\tools\\mb\\mb_config.pyl'
35 self.default_isolate_map = ('c:\\fake_src\\testing\\buildbot\\'
37 self.temp = 'c:\\temp'
38 self.platform = 'win32'
39 self.executable = 'c:\\python\\python.exe'
41 self.cwd = 'c:\\fake_src\\out\\Default'
43 self.chromium_src_dir = '/fake_src'
44 self.default_config = '/fake_src/tools/mb/mb_config.pyl'
45 self.default_isolate_map = '/fake_src/testing/buildbot/gn_isolate_map.pyl'
47 self.platform = 'linux'
48 self.executable = '/usr/bin/python'
50 self.cwd = '/fake_src/out/Default'
56 self.cross_compile = None
61 def Exists(self, path):
62 abs_path = self._AbsPath(path)
63 return (self.files.get(abs_path) is not None or abs_path in self.dirs)
65 def ListDir(self, path):
67 for f in list(self.files.keys()) + list(self.dirs):
68 head, _ = os.path.split(f)
70 dir_contents.append(f)
73 def MaybeMakeDirectory(self, path):
74 abpath = self._AbsPath(path)
77 def PathJoin(self, *comps):
78 return self.sep.join(comps)
80 def ReadFile(self, path):
82 return self.files[self._AbsPath(path)]
84 raise IOError('%s not found' % path) from e
86 def WriteFile(self, path, contents, force_verbose=False):
87 if self.args.dryrun or self.args.verbose or force_verbose:
88 self.Print('\nWriting """\\\n%s""" to %s.\n' % (contents, path))
89 abpath = self._AbsPath(path)
90 self.files[abpath] = contents
92 def Call(self, cmd, env=None, capture_output=True, input=None):
93 # Avoid unused-argument warnings from Pylint
97 self.calls.append(cmd)
99 return self.cmds.pop(0)
102 def Print(self, *args, **kwargs):
103 sep = kwargs.get('sep', ' ')
104 end = kwargs.get('end', '\n')
105 f = kwargs.get('file', sys.stdout)
107 self.err += sep.join(args) + end
109 self.out += sep.join(args) + end
112 tmp_dir = self.temp + self.sep + 'mb_test'
113 self.dirs.add(tmp_dir)
116 def TempFile(self, mode='w'):
117 # Avoid unused-argument warnings from Pylint
119 return FakeFile(self.files)
121 def RemoveFile(self, path):
122 abpath = self._AbsPath(path)
123 self.files[abpath] = None
125 def RemoveDirectory(self, abs_path):
126 # Normalize the passed-in path to handle different working directories
127 # used during unit testing.
128 abs_path = self._AbsPath(abs_path)
129 self.rmdirs.append(abs_path)
130 files_to_delete = [f for f in self.files if f.startswith(abs_path)]
131 for f in files_to_delete:
134 def _AbsPath(self, path):
135 if not ((self.platform == 'win32' and path.startswith('c:')) or
136 (self.platform != 'win32' and path.startswith('/'))):
137 path = self.PathJoin(self.cwd, path)
139 return re.sub(r'\\+', r'\\', path)
140 return re.sub('/+', '/', path)
144 def __init__(self, files):
145 self.name = '/tmp/file'
149 def write(self, contents):
153 self.files[self.name] = self.buf
160 'fake_builder_group': {
161 'fake_args_bot': 'fake_args_bot',
162 'fake_args_file': 'args_file_goma',
163 'fake_builder': 'rel_bot',
164 'fake_debug_builder': 'debug_goma',
165 'fake_ios_error': 'ios_error',
166 'fake_multi_phase': { 'phase_1': 'phase_1', 'phase_2': 'phase_2'},
170 'args_file_goma': ['fake_args_bot', 'goma'],
171 'debug_goma': ['debug', 'goma'],
172 'fake_args_bot': ['fake_args_bot'],
173 'ios_error': ['error'],
174 'phase_1': ['rel', 'phase_1'],
175 'phase_2': ['rel', 'phase_2'],
176 'rel_bot': ['rel', 'goma', 'fake_feature1'],
180 'gn_args': 'is_debug=true',
186 'args_file': '//build/args/bots/fake_builder_group/fake_args_bot.gn',
189 'gn_args': 'enable_doom_melon=true',
192 'gn_args': 'use_goma=true',
195 'gn_args': 'phase=1',
198 'gn_args': 'phase=2',
201 'gn_args': 'is_debug=false dcheck_always_on=false',
207 CONFIG_STARLARK_GN_ARGS = """\
209 'gn_args_locations_files': [
210 '../../infra/config/generated/builders/gn_args_locations.json',
221 TEST_GN_ARGS_LOCATIONS_JSON = """\
224 "linux-official": "ci/linux-official/gn-args.json"
226 "tryserver.chromium": {
227 "linux-official": "try/linux-official/gn-args.json"
232 TEST_GN_ARGS_JSON = """\
235 "string_arg": "has double quotes",
236 "bool_arg_lower_case": true
241 TEST_PHASED_GN_ARGS_JSON = """\
246 "string_arg": "has double quotes",
247 "bool_arg_lower_case": true
252 "string_arg": "second phase",
253 "bool_arg_lower_case": false
260 TEST_BAD_CONFIG = """\
263 'rel_bot_1': ['rel', 'chrome_with_codecs'],
264 'rel_bot_2': ['rel', 'bad_nested_config'],
273 'chrome_with_codecs': {
274 'gn_args': 'proprietary_codecs=true',
276 'bad_nested_config': {
277 'mixins': ['chrome_with_codecs'],
280 'gn_args': 'is_debug=false',
288 TEST_ARGS_FILE_TWICE_CONFIG = """\
292 'fake_builder_group': {
293 'fake_args_file_twice': 'args_file_twice',
297 'args_file_twice': ['args_file', 'args_file'],
301 'args_file': '//build/args/fake.gn',
308 TEST_DUP_CONFIG = """\
312 'fake_builder_group': {
313 'fake_builder': 'some_config',
314 'other_builder': 'some_other_config',
318 'some_config': ['args_file'],
319 'some_other_config': ['args_file'],
323 'args_file': '//build/args/fake.gn',
329 TRYSERVER_CONFIG = """\
333 'fake_builder': 'fake_config',
335 'tryserver.chromium.linux': {
336 'try_builder': 'fake_config',
338 'tryserver.chromium.mac': {
339 'try_builder2': 'fake_config',
349 return sys.platform == 'win32'
352 class UnitTest(unittest.TestCase):
355 def fake_mbw(self, files=None, win32=False):
356 mbw = FakeMBW(win32=win32)
357 mbw.files.setdefault(mbw.default_config, TEST_CONFIG)
358 mbw.files.setdefault(
359 mbw.ToAbsPath('//testing/buildbot/gn_isolate_map.pyl'),
362 "label": "//foo:foo_unittests",
363 "type": "console_test_launcher",
367 mbw.files.setdefault(
368 mbw.ToAbsPath('//build/args/bots/fake_builder_group/fake_args_bot.gn'),
369 'is_debug = false\ndcheck_always_on=false\n')
370 mbw.files.setdefault(mbw.ToAbsPath('//tools/mb/rts_banned_suites.json'),
373 for path, contents in files.items():
374 mbw.files[path] = contents
377 def check(self, args, mbw=None, files=None, out=None, err=None, ret=None,
380 mbw = self.fake_mbw(files)
383 prev_env = os.environ.copy()
384 os.environ = env if env else prev_env
385 actual_ret = mbw.Main(args)
387 os.environ = prev_env
390 "ret: %s, out: %s, err: %s" % (actual_ret, mbw.out, mbw.err))
392 self.assertEqual(mbw.out, out)
394 self.assertEqual(mbw.err, err)
399 return 'c:' + p.replace('/', '\\')
402 def test_analyze(self):
403 files = {'/tmp/in.json': '''{\
404 "files": ["foo/foo_unittest.cc"],
405 "test_targets": ["foo_unittests"],
406 "additional_compile_targets": ["all"]
408 '/tmp/out.json.gn': '''{\
409 "status": "Found dependency",
410 "compile_targets": ["//foo:foo_unittests"],
411 "test_targets": ["//foo:foo_unittests"]
414 mbw = self.fake_mbw(files)
415 mbw.Call = lambda cmd, env=None, capture_output=True, input='': (0, '', '')
417 self.check(['analyze', '-c', 'debug_goma', '//out/Default',
418 '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
419 out = json.loads(mbw.files['/tmp/out.json'])
420 self.assertEqual(out, {
421 'status': 'Found dependency',
422 'compile_targets': ['foo:foo_unittests'],
423 'test_targets': ['foo_unittests']
426 def test_analyze_optimizes_compile_for_all(self):
427 files = {'/tmp/in.json': '''{\
428 "files": ["foo/foo_unittest.cc"],
429 "test_targets": ["foo_unittests"],
430 "additional_compile_targets": ["all"]
432 '/tmp/out.json.gn': '''{\
433 "status": "Found dependency",
434 "compile_targets": ["//foo:foo_unittests", "all"],
435 "test_targets": ["//foo:foo_unittests"]
438 mbw = self.fake_mbw(files)
439 mbw.Call = lambda cmd, env=None, capture_output=True, input='': (0, '', '')
441 self.check(['analyze', '-c', 'debug_goma', '//out/Default',
442 '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
443 out = json.loads(mbw.files['/tmp/out.json'])
445 # check that 'foo_unittests' is not in the compile_targets
446 self.assertEqual(['all'], out['compile_targets'])
448 def test_analyze_handles_other_toolchains(self):
449 files = {'/tmp/in.json': '''{\
450 "files": ["foo/foo_unittest.cc"],
451 "test_targets": ["foo_unittests"],
452 "additional_compile_targets": ["all"]
454 '/tmp/out.json.gn': '''{\
455 "status": "Found dependency",
456 "compile_targets": ["//foo:foo_unittests",
457 "//foo:foo_unittests(bar)"],
458 "test_targets": ["//foo:foo_unittests"]
461 mbw = self.fake_mbw(files)
462 mbw.Call = lambda cmd, env=None, capture_output=True, input='': (0, '', '')
464 self.check(['analyze', '-c', 'debug_goma', '//out/Default',
465 '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
466 out = json.loads(mbw.files['/tmp/out.json'])
468 # crbug.com/736215: If GN returns a label containing a toolchain,
469 # MB (and Ninja) don't know how to handle it; to work around this,
470 # we give up and just build everything we were asked to build. The
471 # output compile_targets should include all of the input test_targets and
472 # additional_compile_targets.
473 self.assertEqual(['all', 'foo_unittests'], out['compile_targets'])
475 def test_analyze_handles_way_too_many_results(self):
476 too_many_files = ', '.join(['"//foo:foo%d"' % i for i in range(40 * 1024)])
477 files = {'/tmp/in.json': '''{\
478 "files": ["foo/foo_unittest.cc"],
479 "test_targets": ["foo_unittests"],
480 "additional_compile_targets": ["all"]
482 '/tmp/out.json.gn': '''{\
483 "status": "Found dependency",
484 "compile_targets": [''' + too_many_files + '''],
485 "test_targets": ["//foo:foo_unittests"]
488 mbw = self.fake_mbw(files)
489 mbw.Call = lambda cmd, env=None, capture_output=True, input='': (0, '', '')
491 self.check(['analyze', '-c', 'debug_goma', '//out/Default',
492 '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
493 out = json.loads(mbw.files['/tmp/out.json'])
495 # If GN returns so many compile targets that we might have command-line
496 # issues, we should give up and just build everything we were asked to
497 # build. The output compile_targets should include all of the input
498 # test_targets and additional_compile_targets.
499 self.assertEqual(['all', 'foo_unittests'], out['compile_targets'])
502 mbw = self.fake_mbw()
503 self.check(['gen', '-c', 'debug_goma', '//out/Default', '-g', '/goma'],
505 self.assertMultiLineEqual(mbw.files['/fake_src/out/Default/args.gn'],
506 ('goma_dir = "/goma"\n'
508 'use_goma = true\n'))
510 # Make sure we log both what is written to args.gn and the command line.
511 self.assertIn('Writing """', mbw.out)
512 self.assertIn('/fake_src/buildtools/linux64/gn gen //out/Default --check',
515 mbw = self.fake_mbw(win32=True)
516 self.check(['gen', '-c', 'debug_goma', '-g', 'c:\\goma', '//out/Debug'],
518 self.assertMultiLineEqual(mbw.files['c:\\fake_src\\out\\Debug\\args.gn'],
519 ('goma_dir = "c:\\\\goma"\n'
521 'use_goma = true\n'))
523 'c:\\fake_src\\buildtools\\win\\gn.exe gen //out/Debug '
526 mbw = self.fake_mbw()
527 self.check(['gen', '-m', 'fake_builder_group', '-b', 'fake_args_bot',
530 # TODO(https://crbug.com/1093038): This assert is inappropriately failing.
532 # mbw.files['/fake_src/out/Debug/args.gn'],
533 # 'import("//build/args/bots/fake_builder_group/fake_args_bot.gn")\n')
535 def test_gen_args_file_mixins(self):
536 mbw = self.fake_mbw()
537 self.check(['gen', '-m', 'fake_builder_group', '-b', 'fake_args_file',
538 '//out/Debug'], mbw=mbw, ret=0)
541 mbw.files['/fake_src/out/Debug/args.gn'],
542 ('import("//build/args/bots/fake_builder_group/fake_args_bot.gn")\n'
543 'use_goma = true\n'))
545 def test_gen_args_file_twice(self):
546 mbw = self.fake_mbw()
547 mbw.files[mbw.default_config] = TEST_ARGS_FILE_TWICE_CONFIG
548 self.check(['gen', '-m', 'fake_builder_group', '-b', 'fake_args_file_twice',
549 '//out/Debug'], mbw=mbw, ret=1)
551 def test_gen_fails(self):
552 mbw = self.fake_mbw()
553 mbw.Call = lambda cmd, env=None, capture_output=True, input='': (1, '', '')
554 self.check(['gen', '-c', 'debug_goma', '//out/Default'], mbw=mbw, ret=1)
556 def test_gen_swarming(self):
558 '/tmp/swarming_targets':
560 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
561 ("{'base_unittests': {"
562 " 'label': '//base:base_unittests',"
563 " 'type': 'console_test_launcher',"
567 mbw = self.fake_mbw(files)
569 def fake_call(cmd, env=None, capture_output=True, input=''):
574 mbw.files['/fake_src/out/Default/base_unittests.runtime_deps'] = (
582 '--swarming-targets-file', '/tmp/swarming_targets',
583 '//out/Default'], mbw=mbw, ret=0)
584 self.assertIn('/fake_src/out/Default/base_unittests.isolate',
586 self.assertIn('/fake_src/out/Default/base_unittests.isolated.gen.json',
589 def test_gen_swarming_script(self):
591 '/tmp/swarming_targets': 'cc_perftests\n',
592 '/fake_src/testing/buildbot/gn_isolate_map.pyl': (
594 " 'label': '//cc:cc_perftests',"
596 " 'script': '/fake_src/out/Default/test_script.py',"
600 mbw = self.fake_mbw(files=files)
602 def fake_call(cmd, env=None, capture_output=True, input=''):
607 mbw.files['/fake_src/out/Default/cc_perftests.runtime_deps'] = (
615 '--swarming-targets-file', '/tmp/swarming_targets',
616 '--isolate-map-file',
617 '/fake_src/testing/buildbot/gn_isolate_map.pyl',
618 '//out/Default'], mbw=mbw, ret=0)
619 self.assertIn('/fake_src/out/Default/cc_perftests.isolate',
621 self.assertIn('/fake_src/out/Default/cc_perftests.isolated.gen.json',
624 def test_multiple_isolate_maps(self):
626 '/tmp/swarming_targets':
628 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
629 ("{'cc_perftests': {"
630 " 'label': '//cc:cc_perftests',"
631 " 'type': 'console_test_launcher',"
633 '/fake_src/testing/buildbot/gn_isolate_map2.pyl':
634 ("{'cc_perftests2': {"
635 " 'label': '//cc:cc_perftests',"
636 " 'type': 'console_test_launcher',"
639 mbw = self.fake_mbw(files=files)
641 def fake_call(cmd, env=None, capture_output=True, input=''):
646 mbw.files['/fake_src/out/Default/cc_perftests.runtime_deps'] = (
647 'cc_perftests_fuzzer\n')
654 '--swarming-targets-file', '/tmp/swarming_targets',
655 '--isolate-map-file',
656 '/fake_src/testing/buildbot/gn_isolate_map.pyl',
657 '--isolate-map-file',
658 '/fake_src/testing/buildbot/gn_isolate_map2.pyl',
659 '//out/Default'], mbw=mbw, ret=0)
660 self.assertIn('/fake_src/out/Default/cc_perftests.isolate',
662 self.assertIn('/fake_src/out/Default/cc_perftests.isolated.gen.json',
666 def test_duplicate_isolate_maps(self):
668 '/tmp/swarming_targets':
670 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
671 ("{'cc_perftests': {"
672 " 'label': '//cc:cc_perftests',"
673 " 'type': 'console_test_launcher',"
675 '/fake_src/testing/buildbot/gn_isolate_map2.pyl':
676 ("{'cc_perftests': {"
677 " 'label': '//cc:cc_perftests',"
678 " 'type': 'console_test_launcher',"
680 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps':
683 mbw = self.fake_mbw(files=files, win32=True)
684 # Check that passing duplicate targets into mb fails.
687 '--swarming-targets-file', '/tmp/swarming_targets',
688 '--isolate-map-file',
689 '/fake_src/testing/buildbot/gn_isolate_map.pyl',
690 '--isolate-map-file',
691 '/fake_src/testing/buildbot/gn_isolate_map2.pyl',
692 '//out/Default'], mbw=mbw, ret=1)
695 def test_isolate(self):
697 '/fake_src/out/Default/toolchain.ninja':
699 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
700 ("{'base_unittests': {"
701 " 'label': '//base:base_unittests',"
702 " 'type': 'console_test_launcher',"
704 '/fake_src/out/Default/base_unittests.runtime_deps':
705 ("base_unittests\n"),
707 self.check(['isolate', '-c', 'debug_goma', '//out/Default',
708 'base_unittests'], files=files, ret=0)
710 # test running isolate on an existing build_dir
711 files['/fake_src/out/Default/args.gn'] = 'is_debug = true\n'
712 self.check(['isolate', '//out/Default', 'base_unittests'],
715 self.check(['isolate', '//out/Default', 'base_unittests'],
718 # Existing build dir that uses a .gni import.
719 files['/fake_src/out/Default/args.gn'] = 'import("//import/args.gni")\n'
720 files['/fake_src/import/args.gni'] = 'is_debug = true\n'
721 self.check(['isolate', '//out/Default', 'base_unittests'],
725 def test_dedup_runtime_deps(self):
727 '/tmp/swarming_targets':
729 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
730 ("{'base_unittests': {"
731 " 'label': '//base:base_unittests',"
732 " 'type': 'console_test_launcher',"
736 mbw = self.fake_mbw(files)
738 def fake_call(cmd, env=None, capture_output=True, input=''):
743 mbw.files['/fake_src/out/Default/base_unittests.runtime_deps'] = (
745 '../../filters/some_filter/\n'
746 '../../filters/some_filter/foo\n'
747 '../../filters/another_filter/hoo\n')
753 'gen', '-c', 'debug_goma', '--swarming-targets-file',
754 '/tmp/swarming_targets', '//out/Default'
758 self.assertIn('/fake_src/out/Default/base_unittests.isolate', mbw.files)
759 files = mbw.files.get('/fake_src/out/Default/base_unittests.isolate')
760 self.assertIn('../../filters/some_filter', files)
761 self.assertNotIn('../../filters/some_filter/foo', files)
762 self.assertIn('../../filters/another_filter/hoo', files)
764 def test_isolate_dir(self):
766 '/fake_src/out/Default/toolchain.ninja':
768 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
769 ("{'base_unittests': {"
770 " 'label': '//base:base_unittests',"
771 " 'type': 'console_test_launcher',"
774 mbw = self.fake_mbw(files=files)
775 mbw.cmds.append((0, '', '')) # Result of `gn gen`
776 mbw.cmds.append((0, '', '')) # Result of `autoninja`
778 # Result of `gn desc runtime_deps`
779 mbw.cmds.append((0, 'base_unitests\n../../test_data/\n', ''))
780 self.check(['isolate', '-c', 'debug_goma', '//out/Default',
781 'base_unittests'], mbw=mbw, ret=0, err='')
783 def test_isolate_generated_dir(self):
785 '/fake_src/out/Default/toolchain.ninja':
787 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
788 ("{'base_unittests': {"
789 " 'label': '//base:base_unittests',"
790 " 'type': 'console_test_launcher',"
793 mbw = self.fake_mbw(files=files)
794 mbw.cmds.append((0, '', '')) # Result of `gn gen`
795 mbw.cmds.append((0, '', '')) # Result of `autoninja`
797 # Result of `gn desc runtime_deps`
798 mbw.cmds.append((0, 'base_unitests\ntest_data/\n', ''))
799 expected_err = ('error: gn `data` items may not list generated directories;'
800 ' list files in directory instead for:\n'
801 '//out/Default/test_data/\n')
802 self.check(['isolate', '-c', 'debug_goma', '//out/Default',
803 'base_unittests'], mbw=mbw, ret=1)
804 self.assertEqual(mbw.out[-len(expected_err):], expected_err)
809 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
810 ("{'base_unittests': {"
811 " 'label': '//base:base_unittests',"
812 " 'type': 'console_test_launcher',"
814 '/fake_src/out/Default/base_unittests.runtime_deps':
815 ("base_unittests\n"),
817 mbw = self.check(['run', '-c', 'debug_goma', '//out/Default',
818 'base_unittests'], files=files, ret=0)
819 # pylint: disable=line-too-long
821 mbw.files['/fake_src/out/Default/base_unittests.isolate'],
822 '{"variables": {"command": ["vpython3", "../../testing/test_env.py", "./base_unittests", "--test-launcher-bot-mode", "--asan=0", "--lsan=0", "--msan=0", "--tsan=0", "--cfi-diag=0"], "files": ["../../.vpython3", "../../testing/test_env.py"]}}\n')
823 # pylint: enable=line-too-long
825 def test_run_swarmed(self):
827 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
828 ("{'base_unittests': {"
829 " 'label': '//base:base_unittests',"
830 " 'type': 'console_test_launcher',"
832 '/fake_src/out/Default/base_unittests.runtime_deps':
833 ("base_unittests\n"),
834 '/fake_src/out/Default/base_unittests.archive.json':
835 ("{\"base_unittests\":\"fake_hash\"}"),
836 '/fake_src/third_party/depot_tools/cipd_manifest.txt':
838 "/some/vpython/pkg git_revision:deadbeef\n"),
841 task_json = json.dumps({'tasks': [{'task_id': '00000'}]})
842 collect_json = json.dumps({'00000': {'results': {}}})
844 mbw = self.fake_mbw(files=files)
845 mbw.files[mbw.PathJoin(mbw.TempDir(), 'task.json')] = task_json
846 mbw.files[mbw.PathJoin(mbw.TempDir(), 'collect_output.json')] = collect_json
847 original_impl = mbw.ToSrcRelPath
849 def to_src_rel_path_stub(path):
850 if path.endswith('base_unittests.archive.json'):
851 return 'base_unittests.archive.json'
852 return original_impl(path)
854 mbw.ToSrcRelPath = to_src_rel_path_stub
856 self.check(['run', '-s', '-c', 'debug_goma', '//out/Default',
857 'base_unittests'], mbw=mbw, ret=0)
859 # Specify a custom dimension via '-d'.
860 mbw = self.fake_mbw(files=files)
861 mbw.files[mbw.PathJoin(mbw.TempDir(), 'task.json')] = task_json
862 mbw.files[mbw.PathJoin(mbw.TempDir(), 'collect_output.json')] = collect_json
863 mbw.ToSrcRelPath = to_src_rel_path_stub
864 self.check(['run', '-s', '-c', 'debug_goma', '-d', 'os', 'Win7',
865 '//out/Default', 'base_unittests'], mbw=mbw, ret=0)
867 # Use the internal swarming server via '--internal'.
868 mbw = self.fake_mbw(files=files)
869 mbw.files[mbw.PathJoin(mbw.TempDir(), 'task.json')] = task_json
870 mbw.files[mbw.PathJoin(mbw.TempDir(), 'collect_output.json')] = collect_json
871 mbw.ToSrcRelPath = to_src_rel_path_stub
873 'run', '-s', '--internal', '-c', 'debug_goma', '//out/Default',
879 def test_run_swarmed_task_failure(self):
881 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
882 ("{'base_unittests': {"
883 " 'label': '//base:base_unittests',"
884 " 'type': 'console_test_launcher',"
886 '/fake_src/out/Default/base_unittests.runtime_deps':
887 ("base_unittests\n"),
888 '/fake_src/out/Default/base_unittests.archive.json':
889 ("{\"base_unittests\":\"fake_hash\"}"),
890 '/fake_src/third_party/depot_tools/cipd_manifest.txt':
892 "/some/vpython/pkg git_revision:deadbeef\n"),
895 task_json = json.dumps({'tasks': [{'task_id': '00000'}]})
896 collect_json = json.dumps({'00000': {'results': {'exit_code': 1}}})
898 mbw = self.fake_mbw(files=files)
899 mbw.files[mbw.PathJoin(mbw.TempDir(), 'task.json')] = task_json
900 mbw.files[mbw.PathJoin(mbw.TempDir(), 'collect_output.json')] = collect_json
901 original_impl = mbw.ToSrcRelPath
903 def to_src_rel_path_stub(path):
904 if path.endswith('base_unittests.archive.json'):
905 return 'base_unittests.archive.json'
906 return original_impl(path)
908 mbw.ToSrcRelPath = to_src_rel_path_stub
911 ['run', '-s', '-c', 'debug_goma', '//out/Default', 'base_unittests'],
914 mbw = self.fake_mbw(files=files)
915 mbw.files[mbw.PathJoin(mbw.TempDir(), 'task.json')] = task_json
916 mbw.files[mbw.PathJoin(mbw.TempDir(), 'collect_output.json')] = collect_json
917 mbw.ToSrcRelPath = to_src_rel_path_stub
919 'run', '-s', '-c', 'debug_goma', '-d', 'os', 'Win7', '//out/Default',
925 def test_lookup(self):
926 self.check(['lookup', '-c', 'debug_goma'], ret=0,
931 '""" to _path_/args.gn.\n\n'
932 '/fake_src/buildtools/linux64/gn gen _path_\n'))
934 def gen_starlark_gn_args_mbw(self, gn_args_json):
936 self.path('/fake_src/tools/mb/mb_config.pyl'):
937 CONFIG_STARLARK_GN_ARGS,
938 self.path('/fake_src/tools/mb/../../infra/config/generated/builders/'
939 'gn_args_locations.json'):
940 TEST_GN_ARGS_LOCATIONS_JSON,
941 self.path('/fake_src/tools/mb/../../infra/config/generated/builders/'
942 'ci/linux-official/gn-args.json'):
945 return self.fake_mbw(files=files, win32=is_win())
947 def test_lookup_starlark_gn_args(self):
948 mbw = self.gen_starlark_gn_args_mbw(TEST_GN_ARGS_JSON)
951 'bool_arg_lower_case = true\n'
952 'string_arg = "has double quotes"\n'
953 '""" to _path_/args.gn.\n\n')
954 if sys.platform == 'win32':
955 expected_out += 'c:\\fake_src\\buildtools\\win\\gn.exe gen _path_\n'
957 expected_out += '/fake_src/buildtools/linux64/gn gen _path_\n'
958 self.check(['lookup', '-m', 'chromium', '-b', 'linux-official'],
963 def test_lookup_starlark_gn_args_specified_phase(self):
964 mbw = self.gen_starlark_gn_args_mbw(TEST_GN_ARGS_JSON)
966 'lookup', '-m', 'chromium', '-b', 'linux-official', '--phase', 'phase_1'
971 'MBErr: Must not specify a build --phase '
972 'for linux-official on chromium', mbw.out)
974 def test_lookup_starlark_phased_gn_args(self):
975 mbw = self.gen_starlark_gn_args_mbw(TEST_PHASED_GN_ARGS_JSON)
978 'bool_arg_lower_case = false\n'
979 'string_arg = "second phase"\n'
980 '""" to _path_/args.gn.\n\n')
981 if sys.platform == 'win32':
982 expected_out += 'c:\\fake_src\\buildtools\\win\\gn.exe gen _path_\n'
984 expected_out += '/fake_src/buildtools/linux64/gn gen _path_\n'
986 'lookup', '-m', 'chromium', '-b', 'linux-official', '--phase', 'phase_2'
992 def test_lookup_starlark_phased_gn_args_no_phase(self):
993 mbw = self.gen_starlark_gn_args_mbw(TEST_PHASED_GN_ARGS_JSON)
994 self.check(['lookup', '-m', 'chromium', '-b', 'linux-official'],
998 'MBErr: Must specify a build --phase for linux-official on chromium',
1001 def test_lookup_starlark_phased_gn_args_wrong_phase(self):
1002 mbw = self.gen_starlark_gn_args_mbw(TEST_PHASED_GN_ARGS_JSON)
1004 'lookup', '-m', 'chromium', '-b', 'linux-official', '--phase', 'phase_3'
1009 'MBErr: Phase phase_3 doesn\'t exist for linux-official on chromium',
1012 def test_lookup_gn_args_with_non_existent_gn_args_location_file(self):
1014 self.path('/fake_src/tools/mb/mb_config.pyl'):
1015 textwrap.dedent("""\
1017 'gn_args_locations_files': [
1018 '../../infra/config/generated/builders/gn_args_locations.json',
1022 'fake-builder': 'fake-config',
1032 mbw = self.fake_mbw(files=files, win32=is_win())
1033 self.check(['lookup', '-m', 'fake-group', '-b', 'fake-builder'],
1037 def test_quiet_lookup(self):
1038 self.check(['lookup', '-c', 'debug_goma', '--quiet'], ret=0,
1039 out=('is_debug = true\n'
1040 'use_goma = true\n'))
1042 def test_lookup_goma_dir_expansion(self):
1043 self.check(['lookup', '-c', 'rel_bot', '-g', '/foo'],
1047 'dcheck_always_on = false\n'
1048 'enable_doom_melon = true\n'
1049 'goma_dir = "/foo"\n'
1050 'is_debug = false\n'
1052 '""" to _path_/args.gn.\n\n'
1053 '/fake_src/buildtools/linux64/gn gen _path_\n'))
1055 def test_help(self):
1056 orig_stdout = sys.stdout
1058 sys.stdout = io.StringIO()
1059 self.assertRaises(SystemExit, self.check, ['-h'])
1060 self.assertRaises(SystemExit, self.check, ['help'])
1061 self.assertRaises(SystemExit, self.check, ['help', 'gen'])
1063 sys.stdout = orig_stdout
1065 def test_multiple_phases(self):
1066 # Check that not passing a --phase to a multi-phase builder fails.
1067 mbw = self.check(['lookup', '-m', 'fake_builder_group', '-b',
1068 'fake_multi_phase'], ret=1)
1069 self.assertIn('Must specify a build --phase', mbw.out)
1071 # Check that passing a --phase to a single-phase builder fails.
1072 mbw = self.check(['lookup', '-m', 'fake_builder_group', '-b',
1073 'fake_builder', '--phase', 'phase_1'], ret=1)
1074 self.assertIn('Must not specify a build --phase', mbw.out)
1076 # Check that passing a wrong phase key to a multi-phase builder fails.
1077 mbw = self.check(['lookup', '-m', 'fake_builder_group', '-b',
1078 'fake_multi_phase', '--phase', 'wrong_phase'], ret=1)
1079 self.assertIn('Phase wrong_phase doesn\'t exist', mbw.out)
1081 # Check that passing a correct phase key to a multi-phase builder passes.
1082 mbw = self.check(['lookup', '-m', 'fake_builder_group', '-b',
1083 'fake_multi_phase', '--phase', 'phase_1'], ret=0)
1084 self.assertIn('phase = 1', mbw.out)
1086 mbw = self.check(['lookup', '-m', 'fake_builder_group', '-b',
1087 'fake_multi_phase', '--phase', 'phase_2'], ret=0)
1088 self.assertIn('phase = 2', mbw.out)
1090 def test_recursive_lookup(self):
1092 '/fake_src/build/args/fake.gn': (
1093 'enable_doom_melon = true\n'
1094 'enable_antidoom_banana = true\n'
1098 'lookup', '-m', 'fake_builder_group', '-b', 'fake_args_file',
1103 out=('dcheck_always_on = false\n'
1104 'is_debug = false\n'
1105 'use_goma = true\n'))
1107 def test_train(self):
1108 mbw = self.fake_mbw()
1109 temp_dir = mbw.TempDir()
1110 self.check(['train', '--expectations-dir', temp_dir], mbw=mbw, ret=0)
1111 self.assertIn(os.path.join(temp_dir, 'fake_builder_group.json'), mbw.files)
1113 def test_validate(self):
1114 mbw = self.fake_mbw()
1115 self.check(['validate'], mbw=mbw, ret=0)
1117 def test_bad_validate(self):
1118 mbw = self.fake_mbw()
1119 mbw.files[mbw.default_config] = TEST_BAD_CONFIG
1120 self.check(['validate', '-f', mbw.default_config], mbw=mbw, ret=1)
1122 def test_duplicate_validate(self):
1123 mbw = self.fake_mbw()
1124 mbw.files[mbw.default_config] = TEST_DUP_CONFIG
1125 self.check(['validate'], mbw=mbw, ret=1)
1127 'Duplicate configs detected. When evaluated fully, the '
1128 'following configs are all equivalent: \'some_config\', '
1129 '\'some_other_config\'.', mbw.out)
1131 def test_good_expectations_validate(self):
1132 mbw = self.fake_mbw()
1133 # Train the expectations normally.
1134 temp_dir = mbw.TempDir()
1135 self.check(['train', '--expectations-dir', temp_dir], mbw=mbw, ret=0)
1136 # Immediately validating them should pass.
1137 self.check(['validate', '--expectations-dir', temp_dir], mbw=mbw, ret=0)
1139 def test_bad_expectations_validate(self):
1140 mbw = self.fake_mbw()
1141 # Train the expectations normally.
1142 temp_dir = mbw.TempDir()
1143 self.check(['train', '--expectations-dir', temp_dir], mbw=mbw, ret=0)
1144 # Remove one of the expectation files.
1145 mbw.files.pop(os.path.join(temp_dir, 'fake_builder_group.json'))
1146 # Now validating should fail.
1147 self.check(['validate', '--expectations-dir', temp_dir], mbw=mbw, ret=1)
1148 self.assertIn('Expectations out of date', mbw.out)
1150 def test_build_command_unix(self):
1152 '/fake_src/out/Default/toolchain.ninja':
1154 '/fake_src/testing/buildbot/gn_isolate_map.pyl':
1155 ('{"base_unittests": {'
1156 ' "label": "//base:base_unittests",'
1157 ' "type": "console_test_launcher",'
1162 mbw = self.fake_mbw(files)
1163 self.check(['run', '//out/Default', 'base_unittests'], mbw=mbw, ret=0)
1164 self.assertIn(['autoninja', '-C', 'out/Default', 'base_unittests'],
1167 def test_build_command_windows(self):
1169 'c:\\fake_src\\out\\Default\\toolchain.ninja':
1171 'c:\\fake_src\\testing\\buildbot\\gn_isolate_map.pyl':
1172 ('{"base_unittests": {'
1173 ' "label": "//base:base_unittests",'
1174 ' "type": "console_test_launcher",'
1179 mbw = self.fake_mbw(files, True)
1180 self.check(['run', '//out/Default', 'base_unittests'], mbw=mbw, ret=0)
1181 self.assertIn(['autoninja.bat', '-C', 'out\\Default', 'base_unittests'],
1184 def test_ios_error_config_with_ios_json(self):
1185 """Ensures that ios_error config finds the correct iOS JSON file for args"""
1187 '/fake_src/ios/build/bots/fake_builder_group/fake_ios_error.json':
1188 ('{"gn_args": ["is_debug=true"]}\n')
1190 mbw = self.fake_mbw(files)
1191 self.check(['lookup', '-m', 'fake_builder_group', '-b', 'fake_ios_error'],
1197 '""" to _path_/args.gn.\n\n'
1198 '/fake_src/buildtools/linux64/gn gen _path_\n'))
1200 def test_bot_definition_in_ios_json_only(self):
1201 """Ensures that logic checks iOS JSON file for args
1203 When builder definition is not present, ensure that ios/build/bots/ is
1207 '/fake_src/ios/build/bots/fake_builder_group/fake_ios_bot.json':
1208 ('{"gn_args": ["is_debug=true"]}\n')
1210 mbw = self.fake_mbw(files)
1211 self.check(['lookup', '-m', 'fake_builder_group', '-b', 'fake_ios_bot'],
1217 '""" to _path_/args.gn.\n\n'
1218 '/fake_src/buildtools/linux64/gn gen _path_\n'))
1220 def test_ios_error_config_missing_json_definition(self):
1221 """Ensures MBErr is thrown
1223 Expect MBErr with 'No iOS definition ...' for iOS bots when the bot config
1224 is ios_error, but there is no iOS JSON definition for it.
1226 mbw = self.fake_mbw()
1227 self.check(['lookup', '-m', 'fake_builder_group', '-b', 'fake_ios_error'],
1230 self.assertIn('MBErr: No iOS definition was found.', mbw.out)
1232 def test_bot_missing_definition(self):
1233 """Ensures builder missing MBErr is thrown
1235 Expect the original MBErr to be thrown for iOS bots when the bot definition
1236 doesn't exist at all.
1238 mbw = self.fake_mbw()
1239 self.check(['lookup', '-m', 'fake_builder_group', '-b', 'random_bot'],
1242 self.assertIn('MBErr: Builder name "random_bot" not found under groups',
1246 if __name__ == '__main__':