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