Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(201)

Side by Side Diff: tools/mb/mb_unittest.py

Issue 2306163002: MB: Copy MB from Chromium repo (Closed)
Patch Set: updated-to-f1e2718a3ff89c80691a50f8ea2503cbb9aa97ee Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/mb/mb.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 #
4 # Use of this source code is governed by a BSD-style license
5 # that can be found in the LICENSE file in the root of the source
6 # tree. An additional intellectual property rights grant can be found
7 # in the file PATENTS. All contributing project authors may
8 # be found in the AUTHORS file in the root of the source tree.
9
10 """Tests for mb.py."""
11
12 import json
13 import StringIO
14 import os
15 import sys
16 import unittest
17
18 import mb
19
20
21 class FakeMBW(mb.MetaBuildWrapper):
22 def __init__(self, win32=False):
23 super(FakeMBW, self).__init__()
24
25 # Override vars for test portability.
26 if win32:
27 self.chromium_src_dir = 'c:\\fake_src'
28 self.default_config = 'c:\\fake_src\\tools\\mb\\mb_config.pyl'
29 self.default_isolate_map = ('c:\\fake_src\\testing\\buildbot\\'
30 'gn_isolate_map.pyl')
31 self.platform = 'win32'
32 self.executable = 'c:\\python\\python.exe'
33 self.sep = '\\'
34 else:
35 self.chromium_src_dir = '/fake_src'
36 self.default_config = '/fake_src/tools/mb/mb_config.pyl'
37 self.default_isolate_map = '/fake_src/testing/buildbot/gn_isolate_map.pyl'
38 self.executable = '/usr/bin/python'
39 self.platform = 'linux2'
40 self.sep = '/'
41
42 self.files = {}
43 self.calls = []
44 self.cmds = []
45 self.cross_compile = None
46 self.out = ''
47 self.err = ''
48 self.rmdirs = []
49
50 def ExpandUser(self, path):
51 return '$HOME/%s' % path
52
53 def Exists(self, path):
54 return self.files.get(path) is not None
55
56 def MaybeMakeDirectory(self, path):
57 self.files[path] = True
58
59 def PathJoin(self, *comps):
60 return self.sep.join(comps)
61
62 def ReadFile(self, path):
63 return self.files[path]
64
65 def WriteFile(self, path, contents, force_verbose=False):
66 if self.args.dryrun or self.args.verbose or force_verbose:
67 self.Print('\nWriting """\\\n%s""" to %s.\n' % (contents, path))
68 self.files[path] = contents
69
70 def Call(self, cmd, env=None, buffer_output=True):
71 if env:
72 self.cross_compile = env.get('GYP_CROSSCOMPILE')
73 self.calls.append(cmd)
74 if self.cmds:
75 return self.cmds.pop(0)
76 return 0, '', ''
77
78 def Print(self, *args, **kwargs):
79 sep = kwargs.get('sep', ' ')
80 end = kwargs.get('end', '\n')
81 f = kwargs.get('file', sys.stdout)
82 if f == sys.stderr:
83 self.err += sep.join(args) + end
84 else:
85 self.out += sep.join(args) + end
86
87 def TempFile(self, mode='w'):
88 return FakeFile(self.files)
89
90 def RemoveFile(self, path):
91 del self.files[path]
92
93 def RemoveDirectory(self, path):
94 self.rmdirs.append(path)
95 files_to_delete = [f for f in self.files if f.startswith(path)]
96 for f in files_to_delete:
97 self.files[f] = None
98
99
100 class FakeFile(object):
101 def __init__(self, files):
102 self.name = '/tmp/file'
103 self.buf = ''
104 self.files = files
105
106 def write(self, contents):
107 self.buf += contents
108
109 def close(self):
110 self.files[self.name] = self.buf
111
112
113 TEST_CONFIG = """\
114 {
115 'masters': {
116 'chromium': {},
117 'fake_master': {
118 'fake_builder': 'gyp_rel_bot',
119 'fake_gn_builder': 'gn_rel_bot',
120 'fake_gyp_crosscompile_builder': 'gyp_crosscompile',
121 'fake_gn_debug_builder': 'gn_debug_goma',
122 'fake_gyp_builder': 'gyp_debug',
123 'fake_gn_args_bot': '//build/args/bots/fake_master/fake_gn_args_bot.gn',
124 'fake_multi_phase': { 'phase_1': 'gn_phase_1', 'phase_2': 'gn_phase_2'},
125 },
126 },
127 'configs': {
128 'gyp_rel_bot': ['gyp', 'rel', 'goma'],
129 'gn_debug_goma': ['gn', 'debug', 'goma'],
130 'gyp_debug': ['gyp', 'debug', 'fake_feature1'],
131 'gn_rel_bot': ['gn', 'rel', 'goma'],
132 'gyp_crosscompile': ['gyp', 'crosscompile'],
133 'gn_phase_1': ['gn', 'phase_1'],
134 'gn_phase_2': ['gn', 'phase_2'],
135 },
136 'mixins': {
137 'crosscompile': {
138 'gyp_crosscompile': True,
139 },
140 'fake_feature1': {
141 'gn_args': 'enable_doom_melon=true',
142 'gyp_defines': 'doom_melon=1',
143 },
144 'gyp': {'type': 'gyp'},
145 'gn': {'type': 'gn'},
146 'goma': {
147 'gn_args': 'use_goma=true',
148 'gyp_defines': 'goma=1',
149 },
150 'phase_1': {
151 'gn_args': 'phase=1',
152 'gyp_args': 'phase=1',
153 },
154 'phase_2': {
155 'gn_args': 'phase=2',
156 'gyp_args': 'phase=2',
157 },
158 'rel': {
159 'gn_args': 'is_debug=false',
160 },
161 'debug': {
162 'gn_args': 'is_debug=true',
163 },
164 },
165 }
166 """
167
168
169 TEST_BAD_CONFIG = """\
170 {
171 'configs': {
172 'gn_rel_bot_1': ['gn', 'rel', 'chrome_with_codecs'],
173 'gn_rel_bot_2': ['gn', 'rel', 'bad_nested_config'],
174 },
175 'masters': {
176 'chromium': {
177 'a': 'gn_rel_bot_1',
178 'b': 'gn_rel_bot_2',
179 },
180 },
181 'mixins': {
182 'gn': {'type': 'gn'},
183 'chrome_with_codecs': {
184 'gn_args': 'proprietary_codecs=true',
185 },
186 'bad_nested_config': {
187 'mixins': ['chrome_with_codecs'],
188 },
189 'rel': {
190 'gn_args': 'is_debug=false',
191 },
192 },
193 }
194 """
195
196
197 GYP_HACKS_CONFIG = """\
198 {
199 'masters': {
200 'chromium': {},
201 'fake_master': {
202 'fake_builder': 'fake_config',
203 },
204 },
205 'configs': {
206 'fake_config': ['fake_mixin'],
207 },
208 'mixins': {
209 'fake_mixin': {
210 'type': 'gyp',
211 'gn_args': '',
212 'gyp_defines':
213 ('foo=bar llvm_force_head_revision=1 '
214 'gyp_link_concurrency=1 baz=1'),
215 },
216 },
217 }
218 """
219
220
221 class UnitTest(unittest.TestCase):
222 def fake_mbw(self, files=None, win32=False):
223 mbw = FakeMBW(win32=win32)
224 mbw.files.setdefault(mbw.default_config, TEST_CONFIG)
225 mbw.files.setdefault(
226 mbw.ToAbsPath('//testing/buildbot/gn_isolate_map.pyl'),
227 '''{
228 "foo_unittests": {
229 "label": "//foo:foo_unittests",
230 "type": "console_test_launcher",
231 "args": [],
232 },
233 }''')
234 mbw.files.setdefault(
235 mbw.ToAbsPath('//build/args/bots/fake_master/fake_gn_args_bot.gn'),
236 'is_debug = false\n')
237 if files:
238 for path, contents in files.items():
239 mbw.files[path] = contents
240 return mbw
241
242 def check(self, args, mbw=None, files=None, out=None, err=None, ret=None):
243 if not mbw:
244 mbw = self.fake_mbw(files)
245
246 actual_ret = mbw.Main(args)
247
248 self.assertEqual(actual_ret, ret)
249 if out is not None:
250 self.assertEqual(mbw.out, out)
251 if err is not None:
252 self.assertEqual(mbw.err, err)
253 return mbw
254
255 def test_clobber(self):
256 files = {
257 '/fake_src/out/Debug': None,
258 '/fake_src/out/Debug/mb_type': None,
259 }
260 mbw = self.fake_mbw(files)
261
262 # The first time we run this, the build dir doesn't exist, so no clobber.
263 self.check(['gen', '-c', 'gn_debug_goma', '//out/Debug'], mbw=mbw, ret=0)
264 self.assertEqual(mbw.rmdirs, [])
265 self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gn')
266
267 # The second time we run this, the build dir exists and matches, so no
268 # clobber.
269 self.check(['gen', '-c', 'gn_debug_goma', '//out/Debug'], mbw=mbw, ret=0)
270 self.assertEqual(mbw.rmdirs, [])
271 self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gn')
272
273 # Now we switch build types; this should result in a clobber.
274 self.check(['gen', '-c', 'gyp_debug', '//out/Debug'], mbw=mbw, ret=0)
275 self.assertEqual(mbw.rmdirs, ['/fake_src/out/Debug'])
276 self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gyp')
277
278 # Now we delete mb_type; this checks the case where the build dir
279 # exists but wasn't populated by mb; this should also result in a clobber.
280 del mbw.files['/fake_src/out/Debug/mb_type']
281 self.check(['gen', '-c', 'gyp_debug', '//out/Debug'], mbw=mbw, ret=0)
282 self.assertEqual(mbw.rmdirs,
283 ['/fake_src/out/Debug', '/fake_src/out/Debug'])
284 self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gyp')
285
286 def test_gn_analyze(self):
287 files = {'/tmp/in.json': '''{\
288 "files": ["foo/foo_unittest.cc"],
289 "test_targets": ["foo_unittests"],
290 "additional_compile_targets": ["all"]
291 }''',
292 '/tmp/out.json.gn': '''{\
293 "status": "Found dependency",
294 "compile_targets": ["//foo:foo_unittests"],
295 "test_targets": ["//foo:foo_unittests"]
296 }'''}
297
298 mbw = self.fake_mbw(files)
299 mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '')
300
301 self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default',
302 '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
303 out = json.loads(mbw.files['/tmp/out.json'])
304 self.assertEqual(out, {
305 'status': 'Found dependency',
306 'compile_targets': ['foo:foo_unittests'],
307 'test_targets': ['foo_unittests']
308 })
309
310 def test_gn_gen(self):
311 mbw = self.fake_mbw()
312 self.check(['gen', '-c', 'gn_debug_goma', '//out/Default', '-g', '/goma'],
313 mbw=mbw, ret=0)
314 self.assertMultiLineEqual(mbw.files['/fake_src/out/Default/args.gn'],
315 ('goma_dir = "/goma"\n'
316 'is_debug = true\n'
317 'use_goma = true\n'))
318
319 # Make sure we log both what is written to args.gn and the command line.
320 self.assertIn('Writing """', mbw.out)
321 self.assertIn('/fake_src/buildtools/linux64/gn gen //out/Default --check',
322 mbw.out)
323
324 mbw = self.fake_mbw(win32=True)
325 self.check(['gen', '-c', 'gn_debug_goma', '-g', 'c:\\goma', '//out/Debug'],
326 mbw=mbw, ret=0)
327 self.assertMultiLineEqual(mbw.files['c:\\fake_src\\out\\Debug\\args.gn'],
328 ('goma_dir = "c:\\\\goma"\n'
329 'is_debug = true\n'
330 'use_goma = true\n'))
331 self.assertIn('c:\\fake_src\\buildtools\\win\\gn.exe gen //out/Debug '
332 '--check\n', mbw.out)
333
334 mbw = self.fake_mbw()
335 self.check(['gen', '-m', 'fake_master', '-b', 'fake_gn_args_bot',
336 '//out/Debug'],
337 mbw=mbw, ret=0)
338 self.assertEqual(
339 mbw.files['/fake_src/out/Debug/args.gn'],
340 'import("//build/args/bots/fake_master/fake_gn_args_bot.gn")\n')
341
342
343 def test_gn_gen_fails(self):
344 mbw = self.fake_mbw()
345 mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '')
346 self.check(['gen', '-c', 'gn_debug_goma', '//out/Default'], mbw=mbw, ret=1)
347
348 def test_gn_gen_swarming(self):
349 files = {
350 '/tmp/swarming_targets': 'base_unittests\n',
351 '/fake_src/testing/buildbot/gn_isolate_map.pyl': (
352 "{'base_unittests': {"
353 " 'label': '//base:base_unittests',"
354 " 'type': 'raw',"
355 " 'args': [],"
356 "}}\n"
357 ),
358 '/fake_src/out/Default/base_unittests.runtime_deps': (
359 "base_unittests\n"
360 ),
361 }
362 mbw = self.fake_mbw(files)
363 self.check(['gen',
364 '-c', 'gn_debug_goma',
365 '--swarming-targets-file', '/tmp/swarming_targets',
366 '//out/Default'], mbw=mbw, ret=0)
367 self.assertIn('/fake_src/out/Default/base_unittests.isolate',
368 mbw.files)
369 self.assertIn('/fake_src/out/Default/base_unittests.isolated.gen.json',
370 mbw.files)
371
372 def test_gn_gen_swarming_script(self):
373 files = {
374 '/tmp/swarming_targets': 'cc_perftests\n',
375 '/fake_src/testing/buildbot/gn_isolate_map.pyl': (
376 "{'cc_perftests': {"
377 " 'label': '//cc:cc_perftests',"
378 " 'type': 'script',"
379 " 'script': '/fake_src/out/Default/test_script.py',"
380 " 'args': [],"
381 "}}\n"
382 ),
383 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps': (
384 "cc_perftests\n"
385 ),
386 }
387 mbw = self.fake_mbw(files=files, win32=True)
388 self.check(['gen',
389 '-c', 'gn_debug_goma',
390 '--swarming-targets-file', '/tmp/swarming_targets',
391 '--isolate-map-file',
392 '/fake_src/testing/buildbot/gn_isolate_map.pyl',
393 '//out/Default'], mbw=mbw, ret=0)
394 self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolate',
395 mbw.files)
396 self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolated.gen.json',
397 mbw.files)
398
399
400 def test_gn_isolate(self):
401 files = {
402 '/fake_src/out/Default/toolchain.ninja': "",
403 '/fake_src/testing/buildbot/gn_isolate_map.pyl': (
404 "{'base_unittests': {"
405 " 'label': '//base:base_unittests',"
406 " 'type': 'raw',"
407 " 'args': [],"
408 "}}\n"
409 ),
410 '/fake_src/out/Default/base_unittests.runtime_deps': (
411 "base_unittests\n"
412 ),
413 }
414 self.check(['isolate', '-c', 'gn_debug_goma', '//out/Default',
415 'base_unittests'], files=files, ret=0)
416
417 # test running isolate on an existing build_dir
418 files['/fake_src/out/Default/args.gn'] = 'is_debug = True\n'
419 self.check(['isolate', '//out/Default', 'base_unittests'],
420 files=files, ret=0)
421
422 files['/fake_src/out/Default/mb_type'] = 'gn\n'
423 self.check(['isolate', '//out/Default', 'base_unittests'],
424 files=files, ret=0)
425
426 def test_gn_run(self):
427 files = {
428 '/fake_src/testing/buildbot/gn_isolate_map.pyl': (
429 "{'base_unittests': {"
430 " 'label': '//base:base_unittests',"
431 " 'type': 'raw',"
432 " 'args': [],"
433 "}}\n"
434 ),
435 '/fake_src/out/Default/base_unittests.runtime_deps': (
436 "base_unittests\n"
437 ),
438 }
439 self.check(['run', '-c', 'gn_debug_goma', '//out/Default',
440 'base_unittests'], files=files, ret=0)
441
442 def test_gn_lookup(self):
443 self.check(['lookup', '-c', 'gn_debug_goma'], ret=0)
444
445 def test_gn_lookup_goma_dir_expansion(self):
446 self.check(['lookup', '-c', 'gn_rel_bot', '-g', '/foo'], ret=0,
447 out=('\n'
448 'Writing """\\\n'
449 'goma_dir = "/foo"\n'
450 'is_debug = false\n'
451 'use_goma = true\n'
452 '""" to _path_/args.gn.\n\n'
453 '/fake_src/buildtools/linux64/gn gen _path_\n'))
454
455 def test_gyp_analyze(self):
456 mbw = self.check(['analyze', '-c', 'gyp_rel_bot', '//out/Release',
457 '/tmp/in.json', '/tmp/out.json'], ret=0)
458 self.assertIn('analyzer', mbw.calls[0])
459
460 def test_gyp_crosscompile(self):
461 mbw = self.fake_mbw()
462 self.check(['gen', '-c', 'gyp_crosscompile', '//out/Release'],
463 mbw=mbw, ret=0)
464 self.assertTrue(mbw.cross_compile)
465
466 def test_gyp_gen(self):
467 self.check(['gen', '-c', 'gyp_rel_bot', '-g', '/goma', '//out/Release'],
468 ret=0,
469 out=("GYP_DEFINES='goma=1 gomadir=/goma'\n"
470 "python build/gyp_chromium -G output_dir=out\n"))
471
472 mbw = self.fake_mbw(win32=True)
473 self.check(['gen', '-c', 'gyp_rel_bot', '-g', 'c:\\goma', '//out/Release'],
474 mbw=mbw, ret=0,
475 out=("set GYP_DEFINES=goma=1 gomadir='c:\\goma'\n"
476 "python build\\gyp_chromium -G output_dir=out\n"))
477
478 def test_gyp_gen_fails(self):
479 mbw = self.fake_mbw()
480 mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '')
481 self.check(['gen', '-c', 'gyp_rel_bot', '//out/Release'], mbw=mbw, ret=1)
482
483 def test_gyp_lookup_goma_dir_expansion(self):
484 self.check(['lookup', '-c', 'gyp_rel_bot', '-g', '/foo'], ret=0,
485 out=("GYP_DEFINES='goma=1 gomadir=/foo'\n"
486 "python build/gyp_chromium -G output_dir=_path_\n"))
487
488 def test_help(self):
489 orig_stdout = sys.stdout
490 try:
491 sys.stdout = StringIO.StringIO()
492 self.assertRaises(SystemExit, self.check, ['-h'])
493 self.assertRaises(SystemExit, self.check, ['help'])
494 self.assertRaises(SystemExit, self.check, ['help', 'gen'])
495 finally:
496 sys.stdout = orig_stdout
497
498 def test_multiple_phases(self):
499 # Check that not passing a --phase to a multi-phase builder fails.
500 mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase'],
501 ret=1)
502 self.assertIn('Must specify a build --phase', mbw.out)
503
504 # Check that passing a --phase to a single-phase builder fails.
505 mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_gn_builder',
506 '--phase', 'phase_1'], ret=1)
507 self.assertIn('Must not specify a build --phase', mbw.out)
508
509 # Check that passing a wrong phase key to a multi-phase builder fails.
510 mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase',
511 '--phase', 'wrong_phase'], ret=1)
512 self.assertIn('Phase wrong_phase doesn\'t exist', mbw.out)
513
514 # Check that passing a correct phase key to a multi-phase builder passes.
515 mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase',
516 '--phase', 'phase_1'], ret=0)
517 self.assertIn('phase = 1', mbw.out)
518
519 mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase',
520 '--phase', 'phase_2'], ret=0)
521 self.assertIn('phase = 2', mbw.out)
522
523 def test_validate(self):
524 mbw = self.fake_mbw()
525 self.check(['validate'], mbw=mbw, ret=0)
526
527 def test_bad_validate(self):
528 mbw = self.fake_mbw()
529 mbw.files[mbw.default_config] = TEST_BAD_CONFIG
530 self.check(['validate'], mbw=mbw, ret=1)
531
532 def test_gyp_env_hacks(self):
533 mbw = self.fake_mbw()
534 mbw.files[mbw.default_config] = GYP_HACKS_CONFIG
535 self.check(['lookup', '-c', 'fake_config'], mbw=mbw,
536 ret=0,
537 out=("GYP_DEFINES='foo=bar baz=1'\n"
538 "GYP_LINK_CONCURRENCY=1\n"
539 "LLVM_FORCE_HEAD_REVISION=1\n"
540 "python build/gyp_chromium -G output_dir=_path_\n"))
541
542
543 if __name__ == '__main__':
544 unittest.main()
545
546 def test_validate(self):
547 mbw = self.fake_mbw()
548 self.check(['validate'], mbw=mbw, ret=0)
549
550 def test_bad_validate(self):
551 mbw = self.fake_mbw()
552 mbw.files[mbw.default_config] = TEST_BAD_CONFIG
553 self.check(['validate'], mbw=mbw, ret=1)
554
555 def test_gyp_env_hacks(self):
556 mbw = self.fake_mbw()
557 mbw.files[mbw.default_config] = GYP_HACKS_CONFIG
558 self.check(['lookup', '-c', 'fake_config'], mbw=mbw,
559 ret=0,
560 out=("GYP_DEFINES='foo=bar baz=1'\n"
561 "GYP_LINK_CONCURRENCY=1\n"
562 "LLVM_FORCE_HEAD_REVISION=1\n"
563 "python build/gyp_chromium -G output_dir=_path_\n"))
564
565
566 if __name__ == '__main__':
567 unittest.main()
OLDNEW
« no previous file with comments | « tools/mb/mb.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698