1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2012 The Chromium OS Authors.
3#
4
5import os
6import shutil
7import sys
8import tempfile
9import time
10import unittest
11
12from buildman import board
13from buildman import bsettings
14from buildman import builder
15from buildman import control
16from buildman import toolchain
17from patman import commit
18from patman import command
19from patman import terminal
20from patman import test_util
21from patman import tools
22
23use_network = True
24
25settings_data = '''
26# Buildman settings file
27
28[toolchain]
29main: /usr/sbin
30
31[toolchain-alias]
32x86: i386 x86_64
33'''
34
35migration = '''===================== WARNING ======================
36This board does not use CONFIG_DM. CONFIG_DM will be
37compulsory starting with the v2020.01 release.
38Failure to update may result in board removal.
39See doc/driver-model/migration.rst for more info.
40====================================================
41'''
42
43errors = [
44    '''main.c: In function 'main_loop':
45main.c:260:6: warning: unused variable 'joe' [-Wunused-variable]
46''',
47    '''main.c: In function 'main_loop2':
48main.c:295:2: error: 'fred' undeclared (first use in this function)
49main.c:295:2: note: each undeclared identifier is reported only once for each function it appears in
50make[1]: *** [main.o] Error 1
51make: *** [common/libcommon.o] Error 2
52Make failed
53''',
54    '''arch/arm/dts/socfpga_arria10_socdk_sdmmc.dtb: Warning \
55(avoid_unnecessary_addr_size): /clocks: unnecessary #address-cells/#size-cells \
56without "ranges" or child "reg" property
57''',
58    '''powerpc-linux-ld: warning: dot moved backwards before `.bss'
59powerpc-linux-ld: warning: dot moved backwards before `.bss'
60powerpc-linux-ld: u-boot: section .text lma 0xfffc0000 overlaps previous sections
61powerpc-linux-ld: u-boot: section .rodata lma 0xfffef3ec overlaps previous sections
62powerpc-linux-ld: u-boot: section .reloc lma 0xffffa400 overlaps previous sections
63powerpc-linux-ld: u-boot: section .data lma 0xffffcd38 overlaps previous sections
64powerpc-linux-ld: u-boot: section .u_boot_cmd lma 0xffffeb40 overlaps previous sections
65powerpc-linux-ld: u-boot: section .bootpg lma 0xfffff198 overlaps previous sections
66''',
67   '''In file included from %(basedir)sarch/sandbox/cpu/cpu.c:9:0:
68%(basedir)sarch/sandbox/include/asm/state.h:44:0: warning: "xxxx" redefined [enabled by default]
69%(basedir)sarch/sandbox/include/asm/state.h:43:0: note: this is the location of the previous definition
70%(basedir)sarch/sandbox/cpu/cpu.c: In function 'do_reset':
71%(basedir)sarch/sandbox/cpu/cpu.c:27:1: error: unknown type name 'blah'
72%(basedir)sarch/sandbox/cpu/cpu.c:28:12: error: expected declaration specifiers or '...' before numeric constant
73make[2]: *** [arch/sandbox/cpu/cpu.o] Error 1
74make[1]: *** [arch/sandbox/cpu] Error 2
75make[1]: *** Waiting for unfinished jobs....
76In file included from %(basedir)scommon/board_f.c:55:0:
77%(basedir)sarch/sandbox/include/asm/state.h:44:0: warning: "xxxx" redefined [enabled by default]
78%(basedir)sarch/sandbox/include/asm/state.h:43:0: note: this is the location of the previous definition
79make: *** [sub-make] Error 2
80'''
81]
82
83
84# hash, subject, return code, list of errors/warnings
85commits = [
86    ['1234', 'upstream/master, migration warning', 0, []],
87    ['5678', 'Second commit, a warning', 0, errors[0:1]],
88    ['9012', 'Third commit, error', 1, errors[0:2]],
89    ['3456', 'Fourth commit, warning', 0, [errors[0], errors[2]]],
90    ['7890', 'Fifth commit, link errors', 1, [errors[0], errors[3]]],
91    ['abcd', 'Sixth commit, fixes all errors', 0, []],
92    ['ef01', 'Seventh commit, fix migration, check directory suppression', 1,
93     [errors[4]]],
94]
95
96boards = [
97    ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0',  ''],
98    ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''],
99    ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''],
100    ['Active', 'powerpc', 'mpc83xx', '', 'Tester', 'PowerPC board 2', 'board3', ''],
101    ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
102]
103
104BASE_DIR = 'base'
105
106OUTCOME_OK, OUTCOME_WARN, OUTCOME_ERR = range(3)
107
108class Options:
109    """Class that holds build options"""
110    pass
111
112class TestBuild(unittest.TestCase):
113    """Test buildman
114
115    TODO: Write tests for the rest of the functionality
116    """
117    def setUp(self):
118        # Set up commits to build
119        self.commits = []
120        sequence = 0
121        for commit_info in commits:
122            comm = commit.Commit(commit_info[0])
123            comm.subject = commit_info[1]
124            comm.return_code = commit_info[2]
125            comm.error_list = commit_info[3]
126            if sequence < 6:
127                 comm.error_list += [migration]
128            comm.sequence = sequence
129            sequence += 1
130            self.commits.append(comm)
131
132        # Set up boards to build
133        self.boards = board.Boards()
134        for brd in boards:
135            self.boards.AddBoard(board.Board(*brd))
136        self.boards.SelectBoards([])
137
138        # Add some test settings
139        bsettings.Setup(None)
140        bsettings.AddFile(settings_data)
141
142        # Set up the toolchains
143        self.toolchains = toolchain.Toolchains()
144        self.toolchains.Add('arm-linux-gcc', test=False)
145        self.toolchains.Add('sparc-linux-gcc', test=False)
146        self.toolchains.Add('powerpc-linux-gcc', test=False)
147        self.toolchains.Add('gcc', test=False)
148
149        # Avoid sending any output
150        terminal.SetPrintTestMode()
151        self._col = terminal.Color()
152
153        self.base_dir = tempfile.mkdtemp()
154        if not os.path.isdir(self.base_dir):
155            os.mkdir(self.base_dir)
156
157    def tearDown(self):
158        shutil.rmtree(self.base_dir)
159
160    def Make(self, commit, brd, stage, *args, **kwargs):
161        result = command.CommandResult()
162        boardnum = int(brd.target[-1])
163        result.return_code = 0
164        result.stderr = ''
165        result.stdout = ('This is the test output for board %s, commit %s' %
166                (brd.target, commit.hash))
167        if ((boardnum >= 1 and boardnum >= commit.sequence) or
168                boardnum == 4 and commit.sequence == 6):
169            result.return_code = commit.return_code
170            result.stderr = (''.join(commit.error_list)
171                % {'basedir' : self.base_dir + '/.bm-work/00/'})
172        elif commit.sequence < 6:
173            result.stderr = migration
174
175        result.combined = result.stdout + result.stderr
176        return result
177
178    def assertSummary(self, text, arch, plus, boards, outcome=OUTCOME_ERR):
179        col = self._col
180        expected_colour = (col.GREEN if outcome == OUTCOME_OK else
181                           col.YELLOW if outcome == OUTCOME_WARN else col.RED)
182        expect = '%10s: ' % arch
183        # TODO(sjg@chromium.org): If plus is '', we shouldn't need this
184        expect += ' ' + col.Color(expected_colour, plus)
185        expect += '  '
186        for board in boards:
187            expect += col.Color(expected_colour, ' %s' % board)
188        self.assertEqual(text, expect)
189
190    def _SetupTest(self, echo_lines=False, threads=1, **kwdisplay_args):
191        """Set up the test by running a build and summary
192
193        Args:
194            echo_lines: True to echo lines to the terminal to aid test
195                development
196            kwdisplay_args: Dict of arguemnts to pass to
197                Builder.SetDisplayOptions()
198
199        Returns:
200            Iterator containing the output lines, each a PrintLine() object
201        """
202        build = builder.Builder(self.toolchains, self.base_dir, None, threads,
203                                2, checkout=False, show_unknown=False)
204        build.do_make = self.Make
205        board_selected = self.boards.GetSelectedDict()
206
207        # Build the boards for the pre-defined commits and warnings/errors
208        # associated with each. This calls our Make() to inject the fake output.
209        build.BuildBoards(self.commits, board_selected, keep_outputs=False,
210                          verbose=False)
211        lines = terminal.GetPrintTestLines()
212        count = 0
213        for line in lines:
214            if line.text.strip():
215                count += 1
216
217        # We should get two starting messages, an update for every commit built
218        # and a summary message
219        self.assertEqual(count, len(commits) * len(boards) + 3)
220        build.SetDisplayOptions(**kwdisplay_args);
221        build.ShowSummary(self.commits, board_selected)
222        if echo_lines:
223            terminal.EchoPrintTestLines()
224        return iter(terminal.GetPrintTestLines())
225
226    def _CheckOutput(self, lines, list_error_boards=False,
227                     filter_dtb_warnings=False,
228                     filter_migration_warnings=False):
229        """Check for expected output from the build summary
230
231        Args:
232            lines: Iterator containing the lines returned from the summary
233            list_error_boards: Adjust the check for output produced with the
234               --list-error-boards flag
235            filter_dtb_warnings: Adjust the check for output produced with the
236               --filter-dtb-warnings flag
237        """
238        def add_line_prefix(prefix, boards, error_str, colour):
239            """Add a prefix to each line of a string
240
241            The training \n in error_str is removed before processing
242
243            Args:
244                prefix: String prefix to add
245                error_str: Error string containing the lines
246                colour: Expected colour for the line. Note that the board list,
247                    if present, always appears in magenta
248
249            Returns:
250                New string where each line has the prefix added
251            """
252            lines = error_str.strip().splitlines()
253            new_lines = []
254            for line in lines:
255                if boards:
256                    expect = self._col.Color(colour, prefix + '(')
257                    expect += self._col.Color(self._col.MAGENTA, boards,
258                                              bright=False)
259                    expect += self._col.Color(colour, ') %s' % line)
260                else:
261                    expect = self._col.Color(colour, prefix + line)
262                new_lines.append(expect)
263            return '\n'.join(new_lines)
264
265        col = terminal.Color()
266        boards01234 = ('board0 board1 board2 board3 board4'
267                       if list_error_boards else '')
268        boards1234 = 'board1 board2 board3 board4' if list_error_boards else ''
269        boards234 = 'board2 board3 board4' if list_error_boards else ''
270        boards34 = 'board3 board4' if list_error_boards else ''
271        boards4 = 'board4' if list_error_boards else ''
272
273        # Upstream commit: migration warnings only
274        self.assertEqual(next(lines).text, '01: %s' % commits[0][1])
275
276        if not filter_migration_warnings:
277            self.assertSummary(next(lines).text, 'arm', 'w+',
278                               ['board0', 'board1'], outcome=OUTCOME_WARN)
279            self.assertSummary(next(lines).text, 'powerpc', 'w+',
280                               ['board2', 'board3'], outcome=OUTCOME_WARN)
281            self.assertSummary(next(lines).text, 'sandbox', 'w+', ['board4'],
282                               outcome=OUTCOME_WARN)
283
284            self.assertEqual(next(lines).text,
285                add_line_prefix('+', boards01234, migration, col.RED))
286
287        # Second commit: all archs should fail with warnings
288        self.assertEqual(next(lines).text, '02: %s' % commits[1][1])
289
290        if filter_migration_warnings:
291            self.assertSummary(next(lines).text, 'arm', 'w+',
292                               ['board1'], outcome=OUTCOME_WARN)
293            self.assertSummary(next(lines).text, 'powerpc', 'w+',
294                               ['board2', 'board3'], outcome=OUTCOME_WARN)
295            self.assertSummary(next(lines).text, 'sandbox', 'w+', ['board4'],
296                               outcome=OUTCOME_WARN)
297
298        # Second commit: The warnings should be listed
299        self.assertEqual(next(lines).text,
300            add_line_prefix('w+', boards1234, errors[0], col.YELLOW))
301
302        # Third commit: Still fails
303        self.assertEqual(next(lines).text, '03: %s' % commits[2][1])
304        if filter_migration_warnings:
305            self.assertSummary(next(lines).text, 'arm', '',
306                               ['board1'], outcome=OUTCOME_OK)
307        self.assertSummary(next(lines).text, 'powerpc', '+',
308                           ['board2', 'board3'])
309        self.assertSummary(next(lines).text, 'sandbox', '+', ['board4'])
310
311        # Expect a compiler error
312        self.assertEqual(next(lines).text,
313                         add_line_prefix('+', boards234, errors[1], col.RED))
314
315        # Fourth commit: Compile errors are fixed, just have warning for board3
316        self.assertEqual(next(lines).text, '04: %s' % commits[3][1])
317        if filter_migration_warnings:
318            expect = '%10s: ' % 'powerpc'
319            expect += ' ' + col.Color(col.GREEN, '')
320            expect += '  '
321            expect += col.Color(col.GREEN, ' %s' % 'board2')
322            expect += ' ' + col.Color(col.YELLOW, 'w+')
323            expect += '  '
324            expect += col.Color(col.YELLOW, ' %s' % 'board3')
325            self.assertEqual(next(lines).text, expect)
326        else:
327            self.assertSummary(next(lines).text, 'powerpc', 'w+',
328                               ['board2', 'board3'], outcome=OUTCOME_WARN)
329        self.assertSummary(next(lines).text, 'sandbox', 'w+', ['board4'],
330                               outcome=OUTCOME_WARN)
331
332        # Compile error fixed
333        self.assertEqual(next(lines).text,
334                         add_line_prefix('-', boards234, errors[1], col.GREEN))
335
336        if not filter_dtb_warnings:
337            self.assertEqual(
338                next(lines).text,
339                add_line_prefix('w+', boards34, errors[2], col.YELLOW))
340
341        # Fifth commit
342        self.assertEqual(next(lines).text, '05: %s' % commits[4][1])
343        if filter_migration_warnings:
344            self.assertSummary(next(lines).text, 'powerpc', '', ['board3'],
345                               outcome=OUTCOME_OK)
346        self.assertSummary(next(lines).text, 'sandbox', '+', ['board4'])
347
348        # The second line of errors[3] is a duplicate, so buildman will drop it
349        expect = errors[3].rstrip().split('\n')
350        expect = [expect[0]] + expect[2:]
351        expect = '\n'.join(expect)
352        self.assertEqual(next(lines).text,
353                         add_line_prefix('+', boards4, expect, col.RED))
354
355        if not filter_dtb_warnings:
356            self.assertEqual(
357                next(lines).text,
358                add_line_prefix('w-', boards34, errors[2], col.CYAN))
359
360        # Sixth commit
361        self.assertEqual(next(lines).text, '06: %s' % commits[5][1])
362        if filter_migration_warnings:
363            self.assertSummary(next(lines).text, 'sandbox', '', ['board4'],
364                               outcome=OUTCOME_OK)
365        else:
366            self.assertSummary(next(lines).text, 'sandbox', 'w+', ['board4'],
367                               outcome=OUTCOME_WARN)
368
369        # The second line of errors[3] is a duplicate, so buildman will drop it
370        expect = errors[3].rstrip().split('\n')
371        expect = [expect[0]] + expect[2:]
372        expect = '\n'.join(expect)
373        self.assertEqual(next(lines).text,
374                         add_line_prefix('-', boards4, expect, col.GREEN))
375        self.assertEqual(next(lines).text,
376                         add_line_prefix('w-', boards4, errors[0], col.CYAN))
377
378        # Seventh commit
379        self.assertEqual(next(lines).text, '07: %s' % commits[6][1])
380        if filter_migration_warnings:
381            self.assertSummary(next(lines).text, 'sandbox', '+', ['board4'])
382        else:
383            self.assertSummary(next(lines).text, 'arm', '', ['board0', 'board1'],
384                               outcome=OUTCOME_OK)
385            self.assertSummary(next(lines).text, 'powerpc', '',
386                               ['board2', 'board3'], outcome=OUTCOME_OK)
387            self.assertSummary(next(lines).text, 'sandbox', '+', ['board4'])
388
389        # Pick out the correct error lines
390        expect_str = errors[4].rstrip().replace('%(basedir)s', '').split('\n')
391        expect = expect_str[3:8] + [expect_str[-1]]
392        expect = '\n'.join(expect)
393        if not filter_migration_warnings:
394            self.assertEqual(
395                next(lines).text,
396                add_line_prefix('-', boards01234, migration, col.GREEN))
397
398        self.assertEqual(next(lines).text,
399                         add_line_prefix('+', boards4, expect, col.RED))
400
401        # Now the warnings lines
402        expect = [expect_str[0]] + expect_str[10:12] + [expect_str[9]]
403        expect = '\n'.join(expect)
404        self.assertEqual(next(lines).text,
405                         add_line_prefix('w+', boards4, expect, col.YELLOW))
406
407    def testOutput(self):
408        """Test basic builder operation and output
409
410        This does a line-by-line verification of the summary output.
411        """
412        lines = self._SetupTest(show_errors=True)
413        self._CheckOutput(lines, list_error_boards=False,
414                          filter_dtb_warnings=False)
415
416    def testErrorBoards(self):
417        """Test output with --list-error-boards
418
419        This does a line-by-line verification of the summary output.
420        """
421        lines = self._SetupTest(show_errors=True, list_error_boards=True)
422        self._CheckOutput(lines, list_error_boards=True)
423
424    def testFilterDtb(self):
425        """Test output with --filter-dtb-warnings
426
427        This does a line-by-line verification of the summary output.
428        """
429        lines = self._SetupTest(show_errors=True, filter_dtb_warnings=True)
430        self._CheckOutput(lines, filter_dtb_warnings=True)
431
432    def testFilterMigration(self):
433        """Test output with --filter-migration-warnings
434
435        This does a line-by-line verification of the summary output.
436        """
437        lines = self._SetupTest(show_errors=True,
438                                filter_migration_warnings=True)
439        self._CheckOutput(lines, filter_migration_warnings=True)
440
441    def testSingleThread(self):
442        """Test operation without threading"""
443        lines = self._SetupTest(show_errors=True, threads=0)
444        self._CheckOutput(lines, list_error_boards=False,
445                          filter_dtb_warnings=False)
446
447    def _testGit(self):
448        """Test basic builder operation by building a branch"""
449        options = Options()
450        options.git = os.getcwd()
451        options.summary = False
452        options.jobs = None
453        options.dry_run = False
454        #options.git = os.path.join(self.base_dir, 'repo')
455        options.branch = 'test-buildman'
456        options.force_build = False
457        options.list_tool_chains = False
458        options.count = -1
459        options.git_dir = None
460        options.threads = None
461        options.show_unknown = False
462        options.quick = False
463        options.show_errors = False
464        options.keep_outputs = False
465        args = ['tegra20']
466        control.DoBuildman(options, args)
467
468    def testBoardSingle(self):
469        """Test single board selection"""
470        self.assertEqual(self.boards.SelectBoards(['sandbox']),
471                         ({'all': ['board4'], 'sandbox': ['board4']}, []))
472
473    def testBoardArch(self):
474        """Test single board selection"""
475        self.assertEqual(self.boards.SelectBoards(['arm']),
476                         ({'all': ['board0', 'board1'],
477                          'arm': ['board0', 'board1']}, []))
478
479    def testBoardArchSingle(self):
480        """Test single board selection"""
481        self.assertEqual(self.boards.SelectBoards(['arm sandbox']),
482                         ({'sandbox': ['board4'],
483                          'all': ['board0', 'board1', 'board4'],
484                          'arm': ['board0', 'board1']}, []))
485
486
487    def testBoardArchSingleMultiWord(self):
488        """Test single board selection"""
489        self.assertEqual(self.boards.SelectBoards(['arm', 'sandbox']),
490                         ({'sandbox': ['board4'],
491                          'all': ['board0', 'board1', 'board4'],
492                          'arm': ['board0', 'board1']}, []))
493
494    def testBoardSingleAnd(self):
495        """Test single board selection"""
496        self.assertEqual(self.boards.SelectBoards(['Tester & arm']),
497                         ({'Tester&arm': ['board0', 'board1'],
498                           'all': ['board0', 'board1']}, []))
499
500    def testBoardTwoAnd(self):
501        """Test single board selection"""
502        self.assertEqual(self.boards.SelectBoards(['Tester', '&', 'arm',
503                                                   'Tester' '&', 'powerpc',
504                                                   'sandbox']),
505                         ({'sandbox': ['board4'],
506                          'all': ['board0', 'board1', 'board2', 'board3',
507                                  'board4'],
508                          'Tester&powerpc': ['board2', 'board3'],
509                          'Tester&arm': ['board0', 'board1']}, []))
510
511    def testBoardAll(self):
512        """Test single board selection"""
513        self.assertEqual(self.boards.SelectBoards([]),
514                         ({'all': ['board0', 'board1', 'board2', 'board3',
515                                  'board4']}, []))
516
517    def testBoardRegularExpression(self):
518        """Test single board selection"""
519        self.assertEqual(self.boards.SelectBoards(['T.*r&^Po']),
520                         ({'all': ['board2', 'board3'],
521                          'T.*r&^Po': ['board2', 'board3']}, []))
522
523    def testBoardDuplicate(self):
524        """Test single board selection"""
525        self.assertEqual(self.boards.SelectBoards(['sandbox sandbox',
526                                                   'sandbox']),
527                         ({'all': ['board4'], 'sandbox': ['board4']}, []))
528    def CheckDirs(self, build, dirname):
529        self.assertEqual('base%s' % dirname, build._GetOutputDir(1))
530        self.assertEqual('base%s/fred' % dirname,
531                         build.GetBuildDir(1, 'fred'))
532        self.assertEqual('base%s/fred/done' % dirname,
533                         build.GetDoneFile(1, 'fred'))
534        self.assertEqual('base%s/fred/u-boot.sizes' % dirname,
535                         build.GetFuncSizesFile(1, 'fred', 'u-boot'))
536        self.assertEqual('base%s/fred/u-boot.objdump' % dirname,
537                         build.GetObjdumpFile(1, 'fred', 'u-boot'))
538        self.assertEqual('base%s/fred/err' % dirname,
539                         build.GetErrFile(1, 'fred'))
540
541    def testOutputDir(self):
542        build = builder.Builder(self.toolchains, BASE_DIR, None, 1, 2,
543                                checkout=False, show_unknown=False)
544        build.commits = self.commits
545        build.commit_count = len(self.commits)
546        subject = self.commits[1].subject.translate(builder.trans_valid_chars)
547        dirname ='/%02d_g%s_%s' % (2, commits[1][0], subject[:20])
548        self.CheckDirs(build, dirname)
549
550    def testOutputDirCurrent(self):
551        build = builder.Builder(self.toolchains, BASE_DIR, None, 1, 2,
552                                checkout=False, show_unknown=False)
553        build.commits = None
554        build.commit_count = 0
555        self.CheckDirs(build, '/current')
556
557    def testOutputDirNoSubdirs(self):
558        build = builder.Builder(self.toolchains, BASE_DIR, None, 1, 2,
559                                checkout=False, show_unknown=False,
560                                no_subdirs=True)
561        build.commits = None
562        build.commit_count = 0
563        self.CheckDirs(build, '')
564
565    def testToolchainAliases(self):
566        self.assertTrue(self.toolchains.Select('arm') != None)
567        with self.assertRaises(ValueError):
568            self.toolchains.Select('no-arch')
569        with self.assertRaises(ValueError):
570            self.toolchains.Select('x86')
571
572        self.toolchains = toolchain.Toolchains()
573        self.toolchains.Add('x86_64-linux-gcc', test=False)
574        self.assertTrue(self.toolchains.Select('x86') != None)
575
576        self.toolchains = toolchain.Toolchains()
577        self.toolchains.Add('i386-linux-gcc', test=False)
578        self.assertTrue(self.toolchains.Select('x86') != None)
579
580    def testToolchainDownload(self):
581        """Test that we can download toolchains"""
582        if use_network:
583            with test_util.capture_sys_output() as (stdout, stderr):
584                url = self.toolchains.LocateArchUrl('arm')
585            self.assertRegexpMatches(url, 'https://www.kernel.org/pub/tools/'
586                    'crosstool/files/bin/x86_64/.*/'
587                    'x86_64-gcc-.*-nolibc[-_]arm-.*linux-gnueabi.tar.xz')
588
589    def testGetEnvArgs(self):
590        """Test the GetEnvArgs() function"""
591        tc = self.toolchains.Select('arm')
592        self.assertEqual('arm-linux-',
593                         tc.GetEnvArgs(toolchain.VAR_CROSS_COMPILE))
594        self.assertEqual('', tc.GetEnvArgs(toolchain.VAR_PATH))
595        self.assertEqual('arm',
596                         tc.GetEnvArgs(toolchain.VAR_ARCH))
597        self.assertEqual('', tc.GetEnvArgs(toolchain.VAR_MAKE_ARGS))
598
599        self.toolchains.Add('/path/to/x86_64-linux-gcc', test=False)
600        tc = self.toolchains.Select('x86')
601        self.assertEqual('/path/to',
602                         tc.GetEnvArgs(toolchain.VAR_PATH))
603        tc.override_toolchain = 'clang'
604        self.assertEqual('HOSTCC=clang CC=clang',
605                         tc.GetEnvArgs(toolchain.VAR_MAKE_ARGS))
606
607    def testPrepareOutputSpace(self):
608        def _Touch(fname):
609            tools.WriteFile(os.path.join(base_dir, fname), b'')
610
611        base_dir = tempfile.mkdtemp()
612
613        # Add various files that we want removed and left alone
614        to_remove = ['01_g0982734987_title', '102_g92bf_title',
615                     '01_g2938abd8_title']
616        to_leave = ['something_else', '01-something.patch', '01_another']
617        for name in to_remove + to_leave:
618            _Touch(name)
619
620        build = builder.Builder(self.toolchains, base_dir, None, 1, 2)
621        build.commits = self.commits
622        build.commit_count = len(commits)
623        result = set(build._GetOutputSpaceRemovals())
624        expected = set([os.path.join(base_dir, f) for f in to_remove])
625        self.assertEqual(expected, result)
626
627if __name__ == "__main__":
628    unittest.main()
629