func_test.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #
  2. # Copyright (c) 2014 Google, Inc
  3. #
  4. # SPDX-License-Identifier: GPL-2.0+
  5. #
  6. import os
  7. import shutil
  8. import sys
  9. import tempfile
  10. import unittest
  11. import board
  12. import bsettings
  13. import cmdline
  14. import command
  15. import control
  16. import gitutil
  17. import terminal
  18. import toolchain
  19. settings_data = '''
  20. # Buildman settings file
  21. [toolchain]
  22. [toolchain-alias]
  23. [make-flags]
  24. src=/home/sjg/c/src
  25. chroot=/home/sjg/c/chroot
  26. vboot=USE_STDINT=1 VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference
  27. chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot}
  28. chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot}
  29. chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
  30. '''
  31. boards = [
  32. ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''],
  33. ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''],
  34. ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''],
  35. ['Active', 'powerpc', 'mpc5xx', '', 'Tester', 'PowerPC board 2', 'board3', ''],
  36. ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
  37. ]
  38. class TestFunctional(unittest.TestCase):
  39. """Functional test for buildman.
  40. This aims to test from just below the invocation of buildman (parsing
  41. of arguments) to 'make' and 'git' invocation. It is not a true
  42. emd-to-end test, as it mocks git, make and the tool chain. But this
  43. makes it easier to detect when the builder is doing the wrong thing,
  44. since in many cases this test code will fail. For example, only a
  45. very limited subset of 'git' arguments is supported - anything
  46. unexpected will fail.
  47. """
  48. def setUp(self):
  49. self._base_dir = tempfile.mkdtemp()
  50. self._git_dir = os.path.join(self._base_dir, 'src')
  51. self._buildman_pathname = sys.argv[0]
  52. self._buildman_dir = os.path.dirname(sys.argv[0])
  53. command.test_result = self._HandleCommand
  54. self._toolchains = toolchain.Toolchains()
  55. self._toolchains.Add('gcc', test=False)
  56. bsettings.Setup(None)
  57. bsettings.AddFile(settings_data)
  58. self._boards = board.Boards()
  59. for brd in boards:
  60. self._boards.AddBoard(board.Board(*brd))
  61. def tearDown(self):
  62. shutil.rmtree(self._base_dir)
  63. def _RunBuildman(self, *args):
  64. return command.RunPipe([[self._buildman_pathname] + list(args)],
  65. capture=True, capture_stderr=True)
  66. def _RunControl(self, *args):
  67. sys.argv = [sys.argv[0]] + list(args)
  68. options, args = cmdline.ParseArgs()
  69. return control.DoBuildman(options, args, toolchains=self._toolchains,
  70. make_func=self._HandleMake, boards=self._boards)
  71. def testFullHelp(self):
  72. command.test_result = None
  73. result = self._RunBuildman('-H')
  74. help_file = os.path.join(self._buildman_dir, 'README')
  75. self.assertEqual(len(result.stdout), os.path.getsize(help_file))
  76. self.assertEqual(0, len(result.stderr))
  77. self.assertEqual(0, result.return_code)
  78. def testHelp(self):
  79. command.test_result = None
  80. result = self._RunBuildman('-h')
  81. help_file = os.path.join(self._buildman_dir, 'README')
  82. self.assertTrue(len(result.stdout) > 1000)
  83. self.assertEqual(0, len(result.stderr))
  84. self.assertEqual(0, result.return_code)
  85. def testGitSetup(self):
  86. """Test gitutils.Setup(), from outside the module itself"""
  87. command.test_result = command.CommandResult(return_code=1)
  88. gitutil.Setup()
  89. self.assertEqual(gitutil.use_no_decorate, False)
  90. command.test_result = command.CommandResult(return_code=0)
  91. gitutil.Setup()
  92. self.assertEqual(gitutil.use_no_decorate, True)
  93. def _HandleCommandGitLog(self, args):
  94. if '-n0' in args:
  95. return command.CommandResult(return_code=0)
  96. # Not handled, so abort
  97. print 'git log', args
  98. sys.exit(1)
  99. def _HandleCommandGit(self, in_args):
  100. """Handle execution of a git command
  101. This uses a hacked-up parser.
  102. Args:
  103. in_args: Arguments after 'git' from the command line
  104. """
  105. git_args = [] # Top-level arguments to git itself
  106. sub_cmd = None # Git sub-command selected
  107. args = [] # Arguments to the git sub-command
  108. for arg in in_args:
  109. if sub_cmd:
  110. args.append(arg)
  111. elif arg[0] == '-':
  112. git_args.append(arg)
  113. else:
  114. sub_cmd = arg
  115. if sub_cmd == 'config':
  116. return command.CommandResult(return_code=0)
  117. elif sub_cmd == 'log':
  118. return self._HandleCommandGitLog(args)
  119. # Not handled, so abort
  120. print 'git', git_args, sub_cmd, args
  121. sys.exit(1)
  122. def _HandleCommandNm(self, args):
  123. return command.CommandResult(return_code=0)
  124. def _HandleCommandObjdump(self, args):
  125. return command.CommandResult(return_code=0)
  126. def _HandleCommandSize(self, args):
  127. return command.CommandResult(return_code=0)
  128. def _HandleCommand(self, **kwargs):
  129. """Handle a command execution.
  130. The command is in kwargs['pipe-list'], as a list of pipes, each a
  131. list of commands. The command should be emulated as required for
  132. testing purposes.
  133. Returns:
  134. A CommandResult object
  135. """
  136. pipe_list = kwargs['pipe_list']
  137. if len(pipe_list) != 1:
  138. print 'invalid pipe', kwargs
  139. sys.exit(1)
  140. cmd = pipe_list[0][0]
  141. args = pipe_list[0][1:]
  142. if cmd == 'git':
  143. return self._HandleCommandGit(args)
  144. elif cmd == './scripts/show-gnu-make':
  145. return command.CommandResult(return_code=0, stdout='make')
  146. elif cmd == 'nm':
  147. return self._HandleCommandNm(args)
  148. elif cmd == 'objdump':
  149. return self._HandleCommandObjdump(args)
  150. elif cmd == 'size':
  151. return self._HandleCommandSize(args)
  152. # Not handled, so abort
  153. print 'unknown command', kwargs
  154. sys.exit(1)
  155. return command.CommandResult(return_code=0)
  156. def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs):
  157. """Handle execution of 'make'
  158. Args:
  159. commit: Commit object that is being built
  160. brd: Board object that is being built
  161. stage: Stage that we are at (mrproper, config, build)
  162. cwd: Directory where make should be run
  163. args: Arguments to pass to make
  164. kwargs: Arguments to pass to command.RunPipe()
  165. """
  166. if stage == 'mrproper':
  167. return command.CommandResult(return_code=0)
  168. elif stage == 'config':
  169. return command.CommandResult(return_code=0,
  170. combined='Test configuration complete')
  171. elif stage == 'build':
  172. return command.CommandResult(return_code=0)
  173. # Not handled, so abort
  174. print 'make', stage
  175. sys.exit(1)
  176. def testNoBoards(self):
  177. """Test that buildman aborts when there are no boards"""
  178. self._boards = board.Boards()
  179. with self.assertRaises(SystemExit):
  180. self._RunControl()
  181. def testCurrentSource(self):
  182. """Very simple test to invoke buildman on the current source"""
  183. self._RunControl()
  184. lines = terminal.GetPrintTestLines()
  185. self.assertTrue(lines[0].text.startswith('Building current source'))