Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / tools / mb / mb_unittest.py
blobfad5e76d910a8a79f2360d25715be83c3d86ccfb
1 #!/usr/bin/python
2 # Copyright 2015 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.
6 """Tests for mb.py."""
8 import json
9 import StringIO
10 import os
11 import sys
12 import unittest
14 import mb
17 class FakeMBW(mb.MetaBuildWrapper):
18 def __init__(self):
19 super(FakeMBW, self).__init__()
21 # Override vars for test portability.
22 self.chromium_src_dir = '/fake_src'
23 self.default_config = '/fake_src/tools/mb/mb_config.pyl'
24 self.executable = 'python'
25 self.platform = 'linux2'
26 self.sep = '/'
28 self.files = {}
29 self.calls = []
30 self.cmds = []
31 self.cross_compile = None
32 self.out = ''
33 self.err = ''
34 self.rmdirs = []
36 def ExpandUser(self, path):
37 return '$HOME/%s' % path
39 def Exists(self, path):
40 return self.files.get(path) is not None
42 def MaybeMakeDirectory(self, path):
43 self.files[path] = True
45 def PathJoin(self, *comps):
46 return self.sep.join(comps)
48 def ReadFile(self, path):
49 return self.files[path]
51 def WriteFile(self, path, contents, force_verbose=False):
52 self.files[path] = contents
54 def Call(self, cmd, env=None):
55 if env:
56 self.cross_compile = env.get('GYP_CROSSCOMPILE')
57 self.calls.append(cmd)
58 if self.cmds:
59 return self.cmds.pop(0)
60 return 0, '', ''
62 def Print(self, *args, **kwargs):
63 sep = kwargs.get('sep', ' ')
64 end = kwargs.get('end', '\n')
65 f = kwargs.get('file', sys.stdout)
66 if f == sys.stderr:
67 self.err += sep.join(args) + end
68 else:
69 self.out += sep.join(args) + end
71 def TempFile(self, mode='w'):
72 return FakeFile(self.files)
74 def RemoveFile(self, path):
75 del self.files[path]
77 def RemoveDirectory(self, path):
78 self.rmdirs.append(path)
79 files_to_delete = [f for f in self.files if f.startswith(path)]
80 for f in files_to_delete:
81 self.files[f] = None
84 class FakeFile(object):
85 def __init__(self, files):
86 self.name = '/tmp/file'
87 self.buf = ''
88 self.files = files
90 def write(self, contents):
91 self.buf += contents
93 def close(self):
94 self.files[self.name] = self.buf
97 TEST_CONFIG = """\
99 'common_dev_configs': ['gn_debug'],
100 'configs': {
101 'gyp_rel_bot': ['gyp', 'rel', 'goma'],
102 'gn_debug': ['gn', 'debug'],
103 'gyp_debug': ['gyp', 'debug'],
104 'gn_rel_bot': ['gn', 'rel', 'goma'],
105 'private': ['gyp', 'rel', 'fake_feature1'],
106 'unsupported': ['gn', 'fake_feature2'],
108 'masters': {
109 'fake_master': {
110 'fake_builder': 'gyp_rel_bot',
111 'fake_gn_builder': 'gn_rel_bot',
112 'fake_gyp_builder': 'gyp_debug',
115 'mixins': {
116 'fake_feature1': {
117 'gn_args': 'enable_doom_melon=true',
118 'gyp_crosscompile': True,
119 'gyp_defines': 'doom_melon=1',
121 'fake_feature2': {
122 'gn_args': 'enable_doom_melon=false',
123 'gyp_defaults': 'doom_melon=0',
125 'gyp': {'type': 'gyp'},
126 'gn': {'type': 'gn'},
127 'goma': {
128 'gn_args': 'use_goma=true goma_dir="$(goma_dir)"',
129 'gyp_defines': 'goma=1 gomadir="$(goma_dir)"',
131 'rel': {
132 'gn_args': 'is_debug=false',
134 'debug': {
135 'gn_args': 'is_debug=true',
138 'private_configs': ['private'],
139 'unsupported_configs': ['unsupported'],
144 class UnitTest(unittest.TestCase):
145 def fake_mbw(self, files=None):
146 mbw = FakeMBW()
147 mbw.files.setdefault(mbw.default_config, TEST_CONFIG)
148 if files:
149 for path, contents in files.items():
150 mbw.files[path] = contents
151 return mbw
153 def check(self, args, mbw=None, files=None, out=None, err=None, ret=None):
154 if not mbw:
155 mbw = self.fake_mbw(files)
156 mbw.ParseArgs(args)
157 actual_ret = mbw.args.func()
158 if ret is not None:
159 self.assertEqual(actual_ret, ret)
160 if out is not None:
161 self.assertEqual(mbw.out, out)
162 if err is not None:
163 self.assertEqual(mbw.err, err)
164 return mbw
166 def test_clobber(self):
167 files = {
168 '/fake_src/out/Debug': None,
169 '/fake_src/out/Debug/mb_type': None,
171 mbw = self.fake_mbw(files)
173 # The first time we run this, the build dir doesn't exist, so no clobber.
174 self.check(['gen', '-c', 'gn_debug', '//out/Debug'], mbw=mbw, ret=0)
175 self.assertEqual(mbw.rmdirs, [])
176 self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gn')
178 # The second time we run this, the build dir exists and matches, so no
179 # clobber.
180 self.check(['gen', '-c', 'gn_debug', '//out/Debug'], mbw=mbw, ret=0)
181 self.assertEqual(mbw.rmdirs, [])
182 self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gn')
184 # Now we switch build types; this should result in a clobber.
185 self.check(['gen', '-c', 'gyp_debug', '//out/Debug'], mbw=mbw, ret=0)
186 self.assertEqual(mbw.rmdirs, ['/fake_src/out/Debug'])
187 self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gyp')
189 # Now we delete mb_type; this checks the case where the build dir
190 # exists but wasn't populated by mb; this should also result in a clobber.
191 del mbw.files['/fake_src/out/Debug/mb_type']
192 self.check(['gen', '-c', 'gyp_debug', '//out/Debug'], mbw=mbw, ret=0)
193 self.assertEqual(mbw.rmdirs,
194 ['/fake_src/out/Debug', '/fake_src/out/Debug'])
195 self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gyp')
197 def test_gn_analyze(self):
198 files = {'/tmp/in.json': """{\
199 "files": ["foo/foo_unittest.cc"],
200 "targets": ["foo_unittests", "bar_unittests"]
201 }"""}
203 mbw = self.fake_mbw(files)
204 mbw.Call = lambda cmd, env=None: (0, 'out/Default/foo_unittests\n', '')
206 self.check(['analyze', '-c', 'gn_debug', '//out/Default',
207 '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
208 out = json.loads(mbw.files['/tmp/out.json'])
209 self.assertEqual(out, {
210 'status': 'Found dependency',
211 'targets': ['foo_unittests'],
212 'build_targets': ['foo_unittests']
215 def test_gn_analyze_all(self):
216 files = {'/tmp/in.json': """{\
217 "files": ["foo/foo_unittest.cc"],
218 "targets": ["all", "bar_unittests"]
219 }"""}
220 mbw = self.fake_mbw(files)
221 mbw.Call = lambda cmd, env=None: (0, 'out/Default/foo_unittests\n', '')
222 self.check(['analyze', '-c', 'gn_debug', '//out/Default',
223 '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
224 out = json.loads(mbw.files['/tmp/out.json'])
225 self.assertEqual(out, {
226 'status': 'Found dependency (all)',
229 def test_gn_analyze_missing_file(self):
230 files = {'/tmp/in.json': """{\
231 "files": ["foo/foo_unittest.cc"],
232 "targets": ["bar_unittests"]
233 }"""}
234 mbw = self.fake_mbw(files)
235 mbw.cmds = [
236 (0, '', ''),
237 (1, 'The input matches no targets, configs, or files\n', ''),
238 (1, 'The input matches no targets, configs, or files\n', ''),
241 self.check(['analyze', '-c', 'gn_debug', '//out/Default',
242 '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
243 out = json.loads(mbw.files['/tmp/out.json'])
244 self.assertEqual(out, {
245 'build_targets': [],
246 'targets': [],
247 'status': 'No dependency',
250 def test_gn_gen(self):
251 self.check(['gen', '-c', 'gn_debug', '//out/Default'], ret=0)
252 self.check(['gen', '-c', 'gyp_rel_bot', '//out/Release'], ret=0)
254 def test_gn_gen_fails(self):
255 mbw = self.fake_mbw()
256 mbw.Call = lambda cmd, env=None: (1, '', '')
257 self.check(['gen', '-c', 'gn_debug', '//out/Default'], mbw=mbw, ret=1)
259 def test_gn_gen_swarming(self):
260 files = {
261 '/tmp/swarming_targets': 'base_unittests\n',
262 '/fake_src/testing/buildbot/gn_isolate_map.pyl': (
263 "{'base_unittests': {"
264 " 'label': '//base:base_unittests',"
265 " 'type': 'raw',"
266 " 'args': [],"
267 "}}\n"
269 '/fake_src/out/Default/base_unittests.runtime_deps': (
270 "base_unittests\n"
273 mbw = self.fake_mbw(files)
274 self.check(['gen',
275 '-c', 'gn_debug',
276 '--swarming-targets-file', '/tmp/swarming_targets',
277 '//out/Default'], mbw=mbw, ret=0)
278 self.assertIn('/fake_src/out/Default/base_unittests.isolate',
279 mbw.files)
280 self.assertIn('/fake_src/out/Default/base_unittests.isolated.gen.json',
281 mbw.files)
283 def test_gn_lookup(self):
284 self.check(['lookup', '-c', 'gn_debug'], ret=0)
286 def test_gn_lookup_goma_dir_expansion(self):
287 self.check(['lookup', '-c', 'gn_rel_bot', '-g', '/foo'], ret=0,
288 out=("/fake_src/buildtools/linux64/gn gen '<path>' "
289 "'--args=is_debug=false use_goma=true "
290 "goma_dir=\"/foo\"'\n" ))
292 def test_gyp_analyze(self):
293 mbw = self.check(['analyze', '-c', 'gyp_rel_bot', '//out/Release',
294 '/tmp/in.json', '/tmp/out.json'],
295 ret=0)
296 self.assertIn('analyzer', mbw.calls[0])
298 def test_gyp_crosscompile(self):
299 mbw = self.fake_mbw()
300 self.check(['gen', '-c', 'private', '//out/Release'], mbw=mbw)
301 self.assertTrue(mbw.cross_compile)
303 def test_gyp_gen(self):
304 self.check(['gen', '-c', 'gyp_rel_bot', '-g', '/goma', '//out/Release'],
305 ret=0,
306 out=("python build/gyp_chromium -G output_dir=out "
307 "-D goma=1 -D gomadir=/goma\n"))
309 # simulate win32
310 mbw = self.fake_mbw()
311 mbw.sep = '\\'
312 self.check(['gen', '-c', 'gyp_rel_bot', '-g', 'c:\\goma', '//out/Release'],
313 mbw=mbw, ret=0,
314 out=("python 'build\\gyp_chromium' -G output_dir=out "
315 "-D goma=1 -D 'gomadir=c:\\goma'\n"))
317 def test_gyp_gen_fails(self):
318 mbw = self.fake_mbw()
319 mbw.Call = lambda cmd, env=None: (1, '', '')
320 self.check(['gen', '-c', 'gyp_rel_bot', '//out/Release'], mbw=mbw, ret=1)
322 def test_gyp_lookup_goma_dir_expansion(self):
323 self.check(['lookup', '-c', 'gyp_rel_bot', '-g', '/foo'], ret=0,
324 out=("python build/gyp_chromium -G 'output_dir=<path>' "
325 "-D goma=1 -D gomadir=/foo\n"))
327 def test_help(self):
328 orig_stdout = sys.stdout
329 try:
330 sys.stdout = StringIO.StringIO()
331 self.assertRaises(SystemExit, self.check, ['-h'])
332 self.assertRaises(SystemExit, self.check, ['help'])
333 self.assertRaises(SystemExit, self.check, ['help', 'gen'])
334 finally:
335 sys.stdout = orig_stdout
338 def test_validate(self):
339 self.check(['validate'], ret=0)
342 if __name__ == '__main__':
343 unittest.main()