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