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