Ejemplo n.º 1
0
 def setUp(self):
     """Set up a BuildContext test."""
     self.context = ScriptContext(['sourcery.selftests'])
     self.tempdir_td = tempfile.TemporaryDirectory()
     self.tempdir = self.tempdir_td.name
     parser = argparse.ArgumentParser()
     add_common_options(parser, self.tempdir)
     add_parallelism_option(parser)
     self.args = parser.parse_args([])
     self.args.build_source_packages = False
     self.relcfg = None
     self.build_context = None
Ejemplo n.º 2
0
 def setUp(self):
     """Set up a version control test."""
     self.context = ScriptContext()
     self.context.silent = True
     self.tempdir_td = tempfile.TemporaryDirectory()
     self.tempdir = self.tempdir_td.name
     self.svndir = os.path.join(self.tempdir, 'svn')
     self.codir = os.path.join(self.tempdir, 'co')
     self.srcdir = os.path.join(self.tempdir, 'src')
     self.srcdir_copy = os.path.join(self.tempdir, 'src-copy')
Ejemplo n.º 3
0
 def setUp(self):
     """Set up a Multilib test."""
     self.context = ScriptContext(['sourcery.selftests'])
     parser = argparse.ArgumentParser()
     add_common_options(parser, os.getcwd())
     self.args = parser.parse_args([])
     self.tempdir_td = tempfile.TemporaryDirectory()
     self.tempdir = self.tempdir_td.name
     self.indir = os.path.join(self.tempdir, 'in')
     self.outdir = os.path.join(self.tempdir, 'out')
Ejemplo n.º 4
0
 def setUp(self):
     """Set up a version control test."""
     self.context = ScriptContext(['sourcery.selftests'])
     self.context.silent = True
     self.tempdir_td = tempfile.TemporaryDirectory()
     self.tempdir = self.tempdir_td.name
     self.svndir = os.path.join(self.tempdir, 'svn')
     self.codir = os.path.join(self.tempdir, 'co')
     self.srcdir = os.path.join(self.tempdir, 'src')
     parser = argparse.ArgumentParser()
     add_common_options(parser, self.tempdir)
     self.args = parser.parse_args([])
 def test_main(self):
     """Test ScriptContext.main."""
     context = ScriptContext(['sourcery.selftests'])
     context.setlocale = unittest.mock.MagicMock()
     context.umask = unittest.mock.MagicMock()
     context.flags = unittest.mock.MagicMock()
     context.flags.no_user_site = False
     context.flags.ignore_environment = False
     context.execve = unittest.mock.MagicMock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.bootstrap_command = True
     test_env = {'HOME': 'test-home',
                 'LOGNAME': 'test-logname',
                 'SSH_AUTH_SOCK': 'test-sock',
                 'TERM': 'test-term',
                 'USER': '******',
                 'LANG': 'test-lang',
                 'PATH': 'test-path',
                 'PYTHONTEST': 'test-python',
                 'OTHER': 'test-other',
                 'OTHER2': 'test-other2'}
     context.environ = dict(test_env)
     # Test generic command execution, no release config.
     context.message_file = io.StringIO()
     context.main(None, ['-i', 'instarg', 'generic', 'arg1'])
     self.assertEqual(context.argv, ['-i', 'instarg', 'generic', 'arg1'])
     self.assertFalse(context.bootstrap_command)
     self.assertFalse(context.silent)
     self.assertFalse(context.verbose_messages)
     self.assertEqual(context.script, '%s generic' % context.script_only)
     self.assertIsNone(context.called_with_relcfg)
     self.assertEqual(context.called_with_args.extra, 'arg1')
     self.assertEqual(context.called_with_args.toplevelprefix,
                      os.path.join(self.cwd, 'instarg'))
     self.assertEqual(context.called_with_args.logdir,
                      os.path.join(self.cwd, 'logs'))
     self.assertEqual(context.called_with_args.objdir,
                      os.path.join(self.cwd, 'obj'))
     self.assertEqual(context.called_with_args.pkgdir,
                      os.path.join(self.cwd, 'pkg'))
     self.assertEqual(context.called_with_args.srcdir,
                      os.path.join(self.cwd, 'src'))
     self.assertEqual(context.called_with_args.testlogdir,
                      os.path.join(self.cwd, 'testlogs'))
     self.assertFalse(context.called_with_args.verbose)
     self.assertFalse(context.called_with_args.silent)
     output = context.message_file.getvalue()
     self.assertIn('%s -i instarg generic arg1 starting...'
                   % context.script_only,
                   output)
     self.assertIn('... %s complete.' % context.script, output)
     self.assertRegex(output,
                      r'^\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] '
                      r'.* -i instarg generic arg1 starting\.\.\.\n'
                      r'\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] '
                      r'\.\.\..*complete\.\n\Z')
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_not_called()
     self.assertEqual(context.environ,
                      {'HOME': 'test-home',
                       'LOGNAME': 'test-logname',
                       'SSH_AUTH_SOCK': 'test-sock',
                       'TERM': 'test-term',
                       'USER': '******',
                       'LANG': 'C',
                       'LC_ALL': 'C',
                       'PATH': 'test-path'})
     # Test --silent.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     context.message_file = io.StringIO()
     context.main(None, ['--silent', 'generic', 'arg1'])
     self.assertTrue(context.silent)
     self.assertFalse(context.verbose_messages)
     self.assertEqual(context.script, '%s generic' % context.script_only)
     self.assertIsNone(context.called_with_relcfg)
     self.assertEqual(context.called_with_args.extra, 'arg1')
     self.assertFalse(context.called_with_args.verbose)
     self.assertTrue(context.called_with_args.silent)
     output = context.message_file.getvalue()
     self.assertEqual(output, '')
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_not_called()
     self.assertEqual(context.environ,
                      {'HOME': 'test-home',
                       'LOGNAME': 'test-logname',
                       'SSH_AUTH_SOCK': 'test-sock',
                       'TERM': 'test-term',
                       'USER': '******',
                       'LANG': 'C',
                       'LC_ALL': 'C',
                       'PATH': 'test-path'})
     # Test -v.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     context.main(None, ['-v', 'generic', 'arg1'])
     self.assertFalse(context.silent)
     self.assertTrue(context.verbose_messages)
     self.assertEqual(context.script, '%s generic' % context.script_only)
     self.assertIsNone(context.called_with_relcfg)
     self.assertEqual(context.called_with_args.extra, 'arg1')
     self.assertTrue(context.called_with_args.verbose)
     self.assertFalse(context.called_with_args.silent)
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_not_called()
     self.assertEqual(context.environ,
                      {'HOME': 'test-home',
                       'LOGNAME': 'test-logname',
                       'SSH_AUTH_SOCK': 'test-sock',
                       'TERM': 'test-term',
                       'USER': '******',
                       'LANG': 'C',
                       'LC_ALL': 'C',
                       'PATH': 'test-path'})
     # Test description used for top-level --help.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     help_out = io.StringIO()
     with contextlib.redirect_stdout(help_out):
         self.assertRaises(SystemExit, context.main, None, ['--help'])
     help_text = help_out.getvalue()
     self.assertRegex(help_text, r'generic *Save argument information\.')
     # Test descriptions used for sub-command --help.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     help_out = io.StringIO()
     with contextlib.redirect_stdout(help_out):
         self.assertRaises(SystemExit, context.main, None,
                           ['generic', '--help'])
     help_text = help_out.getvalue()
     self.assertIn('Save argument information.', help_text)
     self.assertTrue(help_text.endswith('Additional description.\n'))
     # Test re-exec when requested by check_script.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     context.main(None, ['reexec', 'arg1'])
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_called_once_with(
         context.interp,
         context.script_command() + ['reexec', 'arg1'],
         context.environ)
     # Test re-exec not done when not needed.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.flags.no_user_site = True
     context.flags.ignore_environment = True
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     context.main(None, ['reexec', 'arg1'])
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_not_called()
     # Test commands using release configs.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     relcfg_text = ('cfg.add_component("generic")\n'
                    'cfg.generic.source_type.set("none")\n'
                    'cfg.build.set("x86_64-linux-gnu")\n'
                    'cfg.target.set("aarch64-linux-gnu")\n')
     context.main(ReleaseConfigTextLoader(), ['reexec-relcfg', relcfg_text])
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_not_called()
     self.assertEqual(context.called_with_relcfg.build.get().name,
                      'x86_64-linux-gnu')
     self.assertEqual(context.environ,
                      {'HOME': 'test-home',
                       'LOGNAME': 'test-logname',
                       'SSH_AUTH_SOCK': 'test-sock',
                       'TERM': 'test-term',
                       'USER': '******',
                       'LANG': 'C',
                       'LC_ALL': 'C',
                       'PATH': 'test-path',
                       'SOURCE_DATE_EPOCH': str(
                           context.called_with_relcfg.source_date_epoch.get(
                           ))})
     self.assertEqual(context.script_full, context.orig_script_full)
     self.assertEqual(context.interp, sys.executable)
     # Test release config setting environment variables.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     relcfg_text = ('cfg.add_component("generic")\n'
                    'cfg.generic.source_type.set("none")\n'
                    'cfg.build.set("x86_64-linux-gnu")\n'
                    'cfg.target.set("aarch64-linux-gnu")\n'
                    'cfg.env_set.set({"OTHER": "rc-other"})\n')
     context.main(ReleaseConfigTextLoader(), ['reexec-relcfg', relcfg_text])
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_not_called()
     self.assertEqual(context.called_with_relcfg.build.get().name,
                      'x86_64-linux-gnu')
     self.assertEqual(context.environ,
                      {'HOME': 'test-home',
                       'LOGNAME': 'test-logname',
                       'SSH_AUTH_SOCK': 'test-sock',
                       'TERM': 'test-term',
                       'USER': '******',
                       'LANG': 'C',
                       'LC_ALL': 'C',
                       'PATH': 'test-path',
                       'OTHER': 'rc-other',
                       'SOURCE_DATE_EPOCH': str(
                           context.called_with_relcfg.source_date_epoch.get(
                           ))})
     self.assertEqual(context.script_full, context.orig_script_full)
     self.assertEqual(context.interp, sys.executable)
     # Test release config forcing re-exec by setting script_full.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     relcfg_text = ('cfg.add_component("generic")\n'
                    'cfg.generic.source_type.set("none")\n'
                    'cfg.build.set("x86_64-linux-gnu")\n'
                    'cfg.target.set("aarch64-linux-gnu")\n'
                    'cfg.script_full.set("%s-other")\n'
                    % context.script_full)
     context.main(ReleaseConfigTextLoader(), ['reexec-relcfg', relcfg_text])
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_called_once_with(
         context.interp,
         context.script_command() + ['reexec-relcfg', relcfg_text],
         context.environ)
     self.assertEqual(context.called_with_relcfg.build.get().name,
                      'x86_64-linux-gnu')
     self.assertEqual(context.environ,
                      {'HOME': 'test-home',
                       'LOGNAME': 'test-logname',
                       'SSH_AUTH_SOCK': 'test-sock',
                       'TERM': 'test-term',
                       'USER': '******',
                       'LANG': 'C',
                       'LC_ALL': 'C',
                       'PATH': 'test-path',
                       'SOURCE_DATE_EPOCH': str(
                           context.called_with_relcfg.source_date_epoch.get(
                           ))})
     self.assertEqual(context.script_full,
                      '%s-other' % context.orig_script_full)
     self.assertEqual(context.interp, sys.executable)
     context.script_full = context.orig_script_full
     # Test release config forcing re-exec by setting interp.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     relcfg_text = ('cfg.add_component("generic")\n'
                    'cfg.generic.source_type.set("none")\n'
                    'cfg.build.set("x86_64-linux-gnu")\n'
                    'cfg.target.set("aarch64-linux-gnu")\n'
                    'cfg.interp.set("%s-other")\n'
                    % context.interp)
     context.main(ReleaseConfigTextLoader(), ['reexec-relcfg', relcfg_text])
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_called_once_with(
         context.interp,
         context.script_command() + ['reexec-relcfg', relcfg_text],
         context.environ)
     self.assertEqual(context.called_with_relcfg.build.get().name,
                      'x86_64-linux-gnu')
     self.assertEqual(context.environ,
                      {'HOME': 'test-home',
                       'LOGNAME': 'test-logname',
                       'SSH_AUTH_SOCK': 'test-sock',
                       'TERM': 'test-term',
                       'USER': '******',
                       'LANG': 'C',
                       'LC_ALL': 'C',
                       'PATH': 'test-path',
                       'SOURCE_DATE_EPOCH': str(
                           context.called_with_relcfg.source_date_epoch.get(
                           ))})
     self.assertEqual(context.script_full, context.orig_script_full)
     self.assertEqual(context.interp, '%s-other' % sys.executable)
     # Test bootstrap_command.
     context.setlocale.reset_mock()
     context.umask.reset_mock()
     context.execve.reset_mock()
     context.called_with_relcfg = unittest.mock.MagicMock()
     context.environ = dict(test_env)
     context.bootstrap_command = False
     context.main(None, ['bootstrap-command'])
     context.setlocale.assert_called_with(locale.LC_ALL, 'C')
     context.umask.assert_called_with(0o022)
     context.execve.assert_not_called()
     self.assertTrue(context.bootstrap_command)
 def setUp(self):
     """Set up a context test."""
     self.context = ScriptContext()
     self.cwd = os.getcwd()
     self.tempdir_td = tempfile.TemporaryDirectory()
     self.tempdir = self.tempdir_td.name
class ContextTestCase(unittest.TestCase):

    """Test the ScriptContext class and associated functions."""

    def setUp(self):
        """Set up a context test."""
        self.context = ScriptContext()
        self.cwd = os.getcwd()
        self.tempdir_td = tempfile.TemporaryDirectory()
        self.tempdir = self.tempdir_td.name

    def tearDown(self):
        """Tear down a context test."""
        self.tempdir_td.cleanup()

    def temp_file(self, name):
        """Return the name of a temporary file for a test."""
        return os.path.join(self.tempdir, name)

    @contextlib.contextmanager
    def redirect_stdout_stderr(self):
        """Redirect stdout and stderr for code in a 'with' statement."""
        with redirect_file(sys.stdout, self.temp_file('stdout')):
            with redirect_file(sys.stderr, self.temp_file('stderr')):
                yield

    def temp_file_read(self, name):
        """Read a file in tempdir for this test."""
        with open(self.temp_file(name), 'r', encoding='utf-8') as file:
            return file.read()

    def msg_stdout_stderr_read(self):
        """Read the redirected messages, stdout and stderr for this test."""
        return (self.context.message_file.getvalue(),
                self.temp_file_read('stdout'), self.temp_file_read('stderr'))

    def test_add_common_options(self):
        """Test add_common_options."""
        parser = argparse.ArgumentParser()
        add_common_options(parser, os.getcwd())
        args = parser.parse_args([])
        self.assertEqual(args.toplevelprefix,
                         os.path.join(self.cwd, 'install'))
        self.assertEqual(args.logdir,
                         os.path.join(self.cwd, 'logs'))
        self.assertEqual(args.objdir,
                         os.path.join(self.cwd, 'obj'))
        self.assertEqual(args.pkgdir,
                         os.path.join(self.cwd, 'pkg'))
        self.assertEqual(args.srcdir,
                         os.path.join(self.cwd, 'src'))
        self.assertEqual(args.testlogdir,
                         os.path.join(self.cwd, 'testlogs'))
        self.assertFalse(args.verbose)
        self.assertFalse(args.silent)
        args = parser.parse_args(['-i', 'arg1', '-l', 'arg2', '-o', 'arg3',
                                  '-p', 'arg4', '-s', 'arg5', '-T', 'arg6',
                                  '-v'])
        self.assertEqual(args.toplevelprefix,
                         os.path.join(self.cwd, 'arg1'))
        self.assertEqual(args.logdir,
                         os.path.join(self.cwd, 'arg2'))
        self.assertEqual(args.objdir,
                         os.path.join(self.cwd, 'arg3'))
        self.assertEqual(args.pkgdir,
                         os.path.join(self.cwd, 'arg4'))
        self.assertEqual(args.srcdir,
                         os.path.join(self.cwd, 'arg5'))
        self.assertEqual(args.testlogdir,
                         os.path.join(self.cwd, 'arg6'))
        self.assertTrue(args.verbose)
        self.assertFalse(args.silent)
        args = parser.parse_args(['-i', '/arg1', '-l', '/arg2', '-o', '/arg3',
                                  '-p', '/arg4', '-s', '/arg5', '-T', '/arg6',
                                  '--silent'])
        self.assertEqual(args.toplevelprefix, '/arg1')
        self.assertEqual(args.logdir, '/arg2')
        self.assertEqual(args.objdir, '/arg3')
        self.assertEqual(args.pkgdir, '/arg4')
        self.assertEqual(args.srcdir, '/arg5')
        self.assertEqual(args.testlogdir, '/arg6')
        self.assertFalse(args.verbose)
        self.assertTrue(args.silent)

    def test_add_parallelism_option(self):
        """Test add_parallelism_option."""
        parser = argparse.ArgumentParser()
        add_parallelism_option(parser)
        args = parser.parse_args([])
        self.assertEqual(args.parallelism, os.cpu_count())
        args = parser.parse_args(['-j', '1'])
        self.assertEqual(args.parallelism, 1)
        args = parser.parse_args(['-j', '123'])
        self.assertEqual(args.parallelism, 123)

    def test_init(self):
        """Test ScriptContext.__init__."""
        # pylint: disable=import-outside-toplevel
        # We can't test more for most attributes than just repeating
        # the assignments in __init__ as assertions.
        self.assertEqual(self.context.orig_script_full,
                         os.path.abspath(sys.argv[0]))
        self.assertEqual(self.context.script_full,
                         os.path.abspath(sys.argv[0]))
        self.assertIsNone(self.context.argv)
        self.assertFalse(self.context.bootstrap_command)
        self.assertEqual(self.context.interp, sys.executable)
        self.assertEqual(self.context.script_only,
                         os.path.basename(sys.argv[0]))
        self.assertTrue(os.path.isabs(self.context.sourcery_builder_dir))
        self.assertTrue(os.path.isdir(self.context.sourcery_builder_dir))
        sb_script = os.path.join(self.context.sourcery_builder_dir,
                                 'sourcery-builder')
        sb_readme = os.path.join(self.context.sourcery_builder_dir, 'README')
        sb_run_command = os.path.join(self.context.sourcery_builder_dir,
                                      'build-wrappers', 'run-command')
        self.assertTrue(os.access(sb_script, os.F_OK))
        self.assertTrue(os.access(sb_readme, os.F_OK))
        self.assertTrue(os.access(sb_run_command, os.F_OK))
        self.assertEqual(self.context.script, os.path.basename(sys.argv[0]))
        self.assertFalse(self.context.silent)
        self.assertFalse(self.context.verbose_messages)
        self.assertEqual(self.context.message_file, sys.stderr)
        self.assertFalse(self.context.execute_silent)
        self.assertIs(self.context.environ, os.environ)
        self.assertEqual(self.context.environ_orig, os.environ)
        self.assertIsNot(self.context.environ_orig, os.environ)
        self.assertIs(self.context.flags, sys.flags)
        self.assertIs(self.context.execve, os.execve)
        self.assertIs(self.context.setlocale, locale.setlocale)
        self.assertIs(self.context.umask, os.umask)
        self.assertEqual(self.context.package_list, ('sourcery',))
        self.assertIn('self-test', self.context.commands)
        import sourcery.commands.self_test
        self.assertIs(self.context.commands['self-test'],
                      sourcery.commands.self_test.Command)
        self.assertIn('gcc', self.context.components)
        import sourcery.components.gcc
        self.assertIs(self.context.components['gcc'],
                      sourcery.components.gcc.Component)
        # Test loading extra commands and components.
        test_context = ScriptContext(['sourcery.selftests'])
        self.assertEqual(test_context.package_list,
                         ('sourcery', 'sourcery.selftests'))
        self.assertIn('self-test', test_context.commands)
        self.assertIsNot(test_context.commands['self-test'],
                         sourcery.commands.self_test.Command)
        self.assertTrue(issubclass(test_context.commands['self-test'],
                                   sourcery.commands.self_test.Command))
        import sourcery.selftests.command
        self.assertTrue(issubclass(test_context.commands['self-test'],
                                   sourcery.selftests.command.Command))
        self.assertIn('null', test_context.commands)
        import sourcery.selftests.commands.null
        self.assertIs(test_context.commands['null'],
                      sourcery.selftests.commands.null.Command)
        self.assertIn('gcc', test_context.components)
        self.assertIsNot(test_context.components['gcc'],
                         sourcery.components.gcc.Component)
        self.assertTrue(issubclass(test_context.components['gcc'],
                                   sourcery.components.gcc.Component))
        import sourcery.selftests.component
        self.assertTrue(issubclass(test_context.components['gcc'],
                                   sourcery.selftests.component.Component))
        self.assertIn('generic', test_context.components)
        import sourcery.selftests.components.generic
        self.assertIs(test_context.components['generic'],
                      sourcery.selftests.components.generic.Component)
        # Test case with multiple levels of extra commands and components.
        test_context = ScriptContext(['sourcery.selftests',
                                      'sourcery.selftests.extra'])
        self.assertEqual(test_context.package_list,
                         ('sourcery', 'sourcery.selftests',
                          'sourcery.selftests.extra'))
        self.assertEqual(test_context.commands['null'].short_desc,
                         'Do nothing, overridden.')
        import sourcery.selftests.extra.component
        self.assertTrue(issubclass(
            test_context.components['generic'],
            sourcery.selftests.extra.component.Component))
        self.assertTrue(issubclass(test_context.components['generic'],
                                   sourcery.selftests.component.Component))
        import sourcery.selftests.extra.components.generic
        self.assertTrue(issubclass(
            test_context.components['generic'],
            sourcery.selftests.extra.components.generic.Component))
        self.assertTrue(issubclass(
            test_context.components['generic'],
            sourcery.selftests.components.generic.Component))

    def test_build_wrapper_path(self):
        """Test ScriptContext.build_wrapper_path."""
        path = self.context.build_wrapper_path('start-task')
        self.assertEqual(path, os.path.join(self.context.sourcery_builder_dir,
                                            'build-wrappers', 'start-task'))
        self.assertTrue(os.access(path, os.F_OK))

    def test_inform(self):
        """Test ScriptContext.inform."""
        self.context.message_file = io.StringIO()
        self.context.inform('test message')
        output = self.context.message_file.getvalue()
        self.assertIn('test message', output)
        self.assertRegex(output, r'^\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] ')
        self.context.silent = True
        self.context.message_file = io.StringIO()
        self.context.inform('test message')
        output = self.context.message_file.getvalue()
        self.assertEqual(output, '')

    def test_inform_start(self):
        """Test ScriptContext.inform_start."""
        self.context.message_file = io.StringIO()
        self.context.inform_start(['arg1', 'arg2'])
        output = self.context.message_file.getvalue()
        self.assertIn('%s arg1 arg2 starting...' % self.context.script_only,
                      output)
        self.assertRegex(output, r'^\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] ')
        self.context.silent = True
        self.context.message_file = io.StringIO()
        self.context.inform_start(['arg1', 'arg2'])
        output = self.context.message_file.getvalue()
        self.assertEqual(output, '')

    def test_inform_end(self):
        """Test ScriptContext.inform_end."""
        self.context.message_file = io.StringIO()
        self.context.inform_end()
        output = self.context.message_file.getvalue()
        self.assertIn('... %s complete.' % self.context.script, output)
        self.assertRegex(output, r'^\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] ')
        self.context.silent = True
        self.context.message_file = io.StringIO()
        self.context.inform_end()
        output = self.context.message_file.getvalue()
        self.assertEqual(output, '')

    def test_verbose(self):
        """Test ScriptContext.verbose."""
        self.context.message_file = io.StringIO()
        self.context.verbose('test verbose message')
        output = self.context.message_file.getvalue()
        self.assertEqual(output, '')
        self.context.verbose_messages = True
        self.context.message_file = io.StringIO()
        self.context.verbose('test verbose message')
        output = self.context.message_file.getvalue()
        self.assertEqual(output,
                         '%s: test verbose message\n' % self.context.script)

    def test_warning(self):
        """Test ScriptContext.warning."""
        self.context.message_file = io.StringIO()
        self.context.warning('test warning message')
        output = self.context.message_file.getvalue()
        self.assertEqual(output,
                         ('%s: warning: test warning message\n'
                          % self.context.script))

    def test_error(self):
        """Test ScriptContext.error."""
        self.assertRaisesRegex(ScriptError, 'test error message',
                               self.context.error, 'test error message')

    def test_execute(self):
        """Test ScriptContext.execute."""
        # Ordinary execution.
        self.context.message_file = io.StringIO()
        with self.redirect_stdout_stderr():
            self.context.execute(['sh', '-c', 'echo a; echo b >&2'])
        output, stdout, stderr = self.msg_stdout_stderr_read()
        self.assertEqual(output, "sh -c 'echo a; echo b >&2'\n")
        self.assertEqual(stdout, 'a\n')
        self.assertEqual(stderr, 'b\n')
        # Making the subprocess silent.
        self.context.message_file = io.StringIO()
        self.context.execute_silent = True
        with self.redirect_stdout_stderr():
            self.context.execute(['sh', '-c', 'echo a; echo b >&2'])
        output, stdout, stderr = self.msg_stdout_stderr_read()
        self.assertEqual(output, "sh -c 'echo a; echo b >&2'\n")
        self.assertEqual(stdout, '')
        self.assertEqual(stderr, '')
        # Specifying a working directory.
        self.context.message_file = io.StringIO()
        self.context.execute_silent = False
        with self.redirect_stdout_stderr():
            self.context.execute(['sh', '-c', 'echo *'], cwd=self.tempdir)
        output, stdout, stderr = self.msg_stdout_stderr_read()
        self.assertEqual(output, ("pushd %s; sh -c 'echo *'; popd\n"
                                  % self.tempdir))
        self.assertEqual(stdout, 'stderr stdout\n')
        self.assertEqual(stderr, '')
        # Not printing the command run.
        self.context.message_file = io.StringIO()
        self.context.silent = True
        with self.redirect_stdout_stderr():
            self.context.execute(['sh', '-c', 'echo *'], cwd=self.tempdir)
        output, stdout, stderr = self.msg_stdout_stderr_read()
        self.assertEqual(output, '')
        self.assertEqual(stdout, 'stderr stdout\n')
        self.assertEqual(stderr, '')
        # Using environment variables.
        self.context.message_file = io.StringIO()
        self.context.environ = dict(self.context.environ)
        self.context.environ['TEST'] = self.tempdir
        with self.redirect_stdout_stderr():
            self.context.execute(['sh', '-c', 'echo $TEST'])
        output, stdout, stderr = self.msg_stdout_stderr_read()
        self.assertEqual(output, '')
        self.assertEqual(stdout, '%s\n' % self.tempdir)
        self.assertEqual(stderr, '')
        # Errors from execute.
        self.assertRaises(subprocess.CalledProcessError, self.context.execute,
                          ['false'])
        self.assertRaises(OSError, self.context.execute, ['true'],
                          cwd=self.temp_file('no-such-directory'))

    def test_script_command(self):
        """Test ScriptContext.script_command."""
        # We can't test much more than repeating the function's logic.
        self.assertEqual(self.context.script_command(),
                         [self.context.interp, '-s', '-E',
                          self.context.script_full])

    def test_exec_self(self):
        """Test ScriptContext.exec_self."""
        # We can't test much more than repeating the function's logic.
        self.context.execve = unittest.mock.MagicMock()
        argv = ['arg1', 'arg2']
        self.context.argv = argv
        self.context.exec_self()
        self.context.execve.assert_called_once_with(
            self.context.interp, self.context.script_command() + argv,
            self.context.environ)

    def test_clean_environment(self):
        """Test ScriptContext.clean_environment."""
        self.context.setlocale = unittest.mock.MagicMock()
        self.context.umask = unittest.mock.MagicMock()
        self.context.flags = unittest.mock.MagicMock()
        self.context.flags.no_user_site = False
        self.context.flags.ignore_environment = False
        self.context.execve = unittest.mock.MagicMock()
        # Basic test.
        test_env = {'HOME': 'test-home',
                    'LOGNAME': 'test-logname',
                    'SSH_AUTH_SOCK': 'test-sock',
                    'TERM': 'test-term',
                    'USER': '******',
                    'LANG': 'test-lang',
                    'PATH': 'test-path',
                    'PYTHONTEST': 'test-python',
                    'OTHER': 'test-other',
                    'OTHER2': 'test-other2'}
        argv = ['arg1']
        self.context.argv = argv
        self.context.environ = dict(test_env)
        self.context.clean_environment()
        self.context.setlocale.assert_called_once_with(locale.LC_ALL, 'C')
        self.context.umask.assert_called_once_with(0o022)
        self.context.execve.assert_not_called()
        self.assertEqual(self.context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Test with re-execution enabled.
        self.context.setlocale.reset_mock()
        self.context.umask.reset_mock()
        self.context.execve.reset_mock()
        self.context.environ = dict(test_env)
        self.context.clean_environment(reexec=True)
        self.context.setlocale.assert_called_once_with(locale.LC_ALL, 'C')
        self.context.umask.assert_called_once_with(0o022)
        self.context.execve.assert_called_once_with(
            self.context.interp, self.context.script_command() + argv,
            self.context.environ)
        self.assertEqual(self.context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Test with extra variables specified.
        self.context.setlocale.reset_mock()
        self.context.umask.reset_mock()
        self.context.execve.reset_mock()
        self.context.environ = dict(test_env)
        self.context.clean_environment(
            extra_vars={'OTHER': 'mod-other',
                        'EXTRA': 'mod-extra',
                        'PATH': 'mod-path',
                        'LD_LIBRARY_PATH': 'mod-ld-library-path'},
            reexec=True)
        self.context.setlocale.assert_called_once_with(locale.LC_ALL, 'C')
        self.context.umask.assert_called_once_with(0o022)
        self.context.execve.assert_called_once_with(
            self.context.interp, self.context.script_command() + argv,
            self.context.environ)
        self.assertEqual(self.context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'mod-path',
                          'LD_LIBRARY_PATH': 'mod-ld-library-path',
                          'OTHER': 'mod-other',
                          'EXTRA': 'mod-extra'})
        # Test case not needing re-execution.
        self.context.setlocale.reset_mock()
        self.context.umask.reset_mock()
        self.context.execve.reset_mock()
        self.context.environ = dict(test_env)
        self.context.flags.no_user_site = True
        self.context.flags.ignore_environment = True
        self.context.clean_environment(reexec=True)
        self.context.setlocale.assert_called_once_with(locale.LC_ALL, 'C')
        self.context.umask.assert_called_once_with(0o022)
        self.context.execve.assert_not_called()
        self.assertEqual(self.context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Re-execution needed if no_user_site is False.
        self.context.setlocale.reset_mock()
        self.context.umask.reset_mock()
        self.context.execve.reset_mock()
        self.context.environ = dict(test_env)
        self.context.flags.no_user_site = False
        self.context.clean_environment(reexec=True)
        self.context.setlocale.assert_called_once_with(locale.LC_ALL, 'C')
        self.context.umask.assert_called_once_with(0o022)
        self.context.execve.assert_called_once_with(
            self.context.interp, self.context.script_command() + argv,
            self.context.environ)
        self.assertEqual(self.context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Re-execution needed if ignore_environment is False.
        self.context.setlocale.reset_mock()
        self.context.umask.reset_mock()
        self.context.execve.reset_mock()
        self.context.environ = dict(test_env)
        self.context.flags.no_user_site = True
        self.context.flags.ignore_environment = False
        self.context.clean_environment(reexec=True)
        self.context.setlocale.assert_called_once_with(locale.LC_ALL, 'C')
        self.context.umask.assert_called_once_with(0o022)
        self.context.execve.assert_called_once_with(
            self.context.interp, self.context.script_command() + argv,
            self.context.environ)
        self.assertEqual(self.context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Re-execution not needed if ignore_environment is False but
        # there are no PYTHON* variables.
        self.context.setlocale.reset_mock()
        self.context.umask.reset_mock()
        self.context.execve.reset_mock()
        self.context.environ = dict(test_env)
        del self.context.environ['PYTHONTEST']
        self.context.clean_environment(reexec=True)
        self.context.setlocale.assert_called_once_with(locale.LC_ALL, 'C')
        self.context.umask.assert_called_once_with(0o022)
        self.context.execve.assert_not_called()
        self.assertEqual(self.context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Re-execution needed if different interpreter wanted.
        self.context.setlocale.reset_mock()
        self.context.umask.reset_mock()
        self.context.execve.reset_mock()
        self.context.environ = dict(test_env)
        self.context.flags.no_user_site = True
        self.context.flags.ignore_environment = True
        self.context.interp = sys.executable + '-other'
        self.context.clean_environment(reexec=True)
        self.context.setlocale.assert_called_once_with(locale.LC_ALL, 'C')
        self.context.umask.assert_called_once_with(0o022)
        self.context.execve.assert_called_once_with(
            self.context.interp, self.context.script_command() + argv,
            self.context.environ)
        self.assertEqual(self.context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Re-execution needed if different script wanted.
        self.context.setlocale.reset_mock()
        self.context.umask.reset_mock()
        self.context.execve.reset_mock()
        self.context.environ = dict(test_env)
        self.context.interp = sys.executable
        self.context.script_full = self.context.orig_script_full + '-other'
        self.context.clean_environment(reexec=True)
        self.context.setlocale.assert_called_once_with(locale.LC_ALL, 'C')
        self.context.umask.assert_called_once_with(0o022)
        self.context.execve.assert_called_once_with(
            self.context.interp, self.context.script_command() + argv,
            self.context.environ)
        self.assertEqual(self.context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})

    def test_main(self):
        """Test ScriptContext.main."""
        context = ScriptContext(['sourcery.selftests'])
        context.setlocale = unittest.mock.MagicMock()
        context.umask = unittest.mock.MagicMock()
        context.flags = unittest.mock.MagicMock()
        context.flags.no_user_site = False
        context.flags.ignore_environment = False
        context.execve = unittest.mock.MagicMock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.bootstrap_command = True
        test_env = {'HOME': 'test-home',
                    'LOGNAME': 'test-logname',
                    'SSH_AUTH_SOCK': 'test-sock',
                    'TERM': 'test-term',
                    'USER': '******',
                    'LANG': 'test-lang',
                    'PATH': 'test-path',
                    'PYTHONTEST': 'test-python',
                    'OTHER': 'test-other',
                    'OTHER2': 'test-other2'}
        context.environ = dict(test_env)
        # Test generic command execution, no release config.
        context.message_file = io.StringIO()
        context.main(None, ['-i', 'instarg', 'generic', 'arg1'])
        self.assertEqual(context.argv, ['-i', 'instarg', 'generic', 'arg1'])
        self.assertFalse(context.bootstrap_command)
        self.assertFalse(context.silent)
        self.assertFalse(context.verbose_messages)
        self.assertEqual(context.script, '%s generic' % context.script_only)
        self.assertIsNone(context.called_with_relcfg)
        self.assertEqual(context.called_with_args.extra, 'arg1')
        self.assertEqual(context.called_with_args.toplevelprefix,
                         os.path.join(self.cwd, 'instarg'))
        self.assertEqual(context.called_with_args.logdir,
                         os.path.join(self.cwd, 'logs'))
        self.assertEqual(context.called_with_args.objdir,
                         os.path.join(self.cwd, 'obj'))
        self.assertEqual(context.called_with_args.pkgdir,
                         os.path.join(self.cwd, 'pkg'))
        self.assertEqual(context.called_with_args.srcdir,
                         os.path.join(self.cwd, 'src'))
        self.assertEqual(context.called_with_args.testlogdir,
                         os.path.join(self.cwd, 'testlogs'))
        self.assertFalse(context.called_with_args.verbose)
        self.assertFalse(context.called_with_args.silent)
        output = context.message_file.getvalue()
        self.assertIn('%s -i instarg generic arg1 starting...'
                      % context.script_only,
                      output)
        self.assertIn('... %s complete.' % context.script, output)
        self.assertRegex(output,
                         r'^\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] '
                         r'.* -i instarg generic arg1 starting\.\.\.\n'
                         r'\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] '
                         r'\.\.\..*complete\.\n\Z')
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_not_called()
        self.assertEqual(context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Test --silent.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        context.message_file = io.StringIO()
        context.main(None, ['--silent', 'generic', 'arg1'])
        self.assertTrue(context.silent)
        self.assertFalse(context.verbose_messages)
        self.assertEqual(context.script, '%s generic' % context.script_only)
        self.assertIsNone(context.called_with_relcfg)
        self.assertEqual(context.called_with_args.extra, 'arg1')
        self.assertFalse(context.called_with_args.verbose)
        self.assertTrue(context.called_with_args.silent)
        output = context.message_file.getvalue()
        self.assertEqual(output, '')
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_not_called()
        self.assertEqual(context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Test -v.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        context.main(None, ['-v', 'generic', 'arg1'])
        self.assertFalse(context.silent)
        self.assertTrue(context.verbose_messages)
        self.assertEqual(context.script, '%s generic' % context.script_only)
        self.assertIsNone(context.called_with_relcfg)
        self.assertEqual(context.called_with_args.extra, 'arg1')
        self.assertTrue(context.called_with_args.verbose)
        self.assertFalse(context.called_with_args.silent)
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_not_called()
        self.assertEqual(context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path'})
        # Test description used for top-level --help.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        help_out = io.StringIO()
        with contextlib.redirect_stdout(help_out):
            self.assertRaises(SystemExit, context.main, None, ['--help'])
        help_text = help_out.getvalue()
        self.assertRegex(help_text, r'generic *Save argument information\.')
        # Test descriptions used for sub-command --help.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        help_out = io.StringIO()
        with contextlib.redirect_stdout(help_out):
            self.assertRaises(SystemExit, context.main, None,
                              ['generic', '--help'])
        help_text = help_out.getvalue()
        self.assertIn('Save argument information.', help_text)
        self.assertTrue(help_text.endswith('Additional description.\n'))
        # Test re-exec when requested by check_script.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        context.main(None, ['reexec', 'arg1'])
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_called_once_with(
            context.interp,
            context.script_command() + ['reexec', 'arg1'],
            context.environ)
        # Test re-exec not done when not needed.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.flags.no_user_site = True
        context.flags.ignore_environment = True
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        context.main(None, ['reexec', 'arg1'])
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_not_called()
        # Test commands using release configs.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        relcfg_text = ('cfg.add_component("generic")\n'
                       'cfg.generic.source_type.set("none")\n'
                       'cfg.build.set("x86_64-linux-gnu")\n'
                       'cfg.target.set("aarch64-linux-gnu")\n')
        context.main(ReleaseConfigTextLoader(), ['reexec-relcfg', relcfg_text])
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_not_called()
        self.assertEqual(context.called_with_relcfg.build.get().name,
                         'x86_64-linux-gnu')
        self.assertEqual(context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path',
                          'SOURCE_DATE_EPOCH': str(
                              context.called_with_relcfg.source_date_epoch.get(
                              ))})
        self.assertEqual(context.script_full, context.orig_script_full)
        self.assertEqual(context.interp, sys.executable)
        # Test release config setting environment variables.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        relcfg_text = ('cfg.add_component("generic")\n'
                       'cfg.generic.source_type.set("none")\n'
                       'cfg.build.set("x86_64-linux-gnu")\n'
                       'cfg.target.set("aarch64-linux-gnu")\n'
                       'cfg.env_set.set({"OTHER": "rc-other"})\n')
        context.main(ReleaseConfigTextLoader(), ['reexec-relcfg', relcfg_text])
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_not_called()
        self.assertEqual(context.called_with_relcfg.build.get().name,
                         'x86_64-linux-gnu')
        self.assertEqual(context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path',
                          'OTHER': 'rc-other',
                          'SOURCE_DATE_EPOCH': str(
                              context.called_with_relcfg.source_date_epoch.get(
                              ))})
        self.assertEqual(context.script_full, context.orig_script_full)
        self.assertEqual(context.interp, sys.executable)
        # Test release config forcing re-exec by setting script_full.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        relcfg_text = ('cfg.add_component("generic")\n'
                       'cfg.generic.source_type.set("none")\n'
                       'cfg.build.set("x86_64-linux-gnu")\n'
                       'cfg.target.set("aarch64-linux-gnu")\n'
                       'cfg.script_full.set("%s-other")\n'
                       % context.script_full)
        context.main(ReleaseConfigTextLoader(), ['reexec-relcfg', relcfg_text])
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_called_once_with(
            context.interp,
            context.script_command() + ['reexec-relcfg', relcfg_text],
            context.environ)
        self.assertEqual(context.called_with_relcfg.build.get().name,
                         'x86_64-linux-gnu')
        self.assertEqual(context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path',
                          'SOURCE_DATE_EPOCH': str(
                              context.called_with_relcfg.source_date_epoch.get(
                              ))})
        self.assertEqual(context.script_full,
                         '%s-other' % context.orig_script_full)
        self.assertEqual(context.interp, sys.executable)
        context.script_full = context.orig_script_full
        # Test release config forcing re-exec by setting interp.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        relcfg_text = ('cfg.add_component("generic")\n'
                       'cfg.generic.source_type.set("none")\n'
                       'cfg.build.set("x86_64-linux-gnu")\n'
                       'cfg.target.set("aarch64-linux-gnu")\n'
                       'cfg.interp.set("%s-other")\n'
                       % context.interp)
        context.main(ReleaseConfigTextLoader(), ['reexec-relcfg', relcfg_text])
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_called_once_with(
            context.interp,
            context.script_command() + ['reexec-relcfg', relcfg_text],
            context.environ)
        self.assertEqual(context.called_with_relcfg.build.get().name,
                         'x86_64-linux-gnu')
        self.assertEqual(context.environ,
                         {'HOME': 'test-home',
                          'LOGNAME': 'test-logname',
                          'SSH_AUTH_SOCK': 'test-sock',
                          'TERM': 'test-term',
                          'USER': '******',
                          'LANG': 'C',
                          'LC_ALL': 'C',
                          'PATH': 'test-path',
                          'SOURCE_DATE_EPOCH': str(
                              context.called_with_relcfg.source_date_epoch.get(
                              ))})
        self.assertEqual(context.script_full, context.orig_script_full)
        self.assertEqual(context.interp, '%s-other' % sys.executable)
        # Test bootstrap_command.
        context.setlocale.reset_mock()
        context.umask.reset_mock()
        context.execve.reset_mock()
        context.called_with_relcfg = unittest.mock.MagicMock()
        context.environ = dict(test_env)
        context.bootstrap_command = False
        context.main(None, ['bootstrap-command'])
        context.setlocale.assert_called_with(locale.LC_ALL, 'C')
        context.umask.assert_called_with(0o022)
        context.execve.assert_not_called()
        self.assertTrue(context.bootstrap_command)
 def test_init(self):
     """Test ScriptContext.__init__."""
     # pylint: disable=import-outside-toplevel
     # We can't test more for most attributes than just repeating
     # the assignments in __init__ as assertions.
     self.assertEqual(self.context.orig_script_full,
                      os.path.abspath(sys.argv[0]))
     self.assertEqual(self.context.script_full,
                      os.path.abspath(sys.argv[0]))
     self.assertIsNone(self.context.argv)
     self.assertFalse(self.context.bootstrap_command)
     self.assertEqual(self.context.interp, sys.executable)
     self.assertEqual(self.context.script_only,
                      os.path.basename(sys.argv[0]))
     self.assertTrue(os.path.isabs(self.context.sourcery_builder_dir))
     self.assertTrue(os.path.isdir(self.context.sourcery_builder_dir))
     sb_script = os.path.join(self.context.sourcery_builder_dir,
                              'sourcery-builder')
     sb_readme = os.path.join(self.context.sourcery_builder_dir, 'README')
     sb_run_command = os.path.join(self.context.sourcery_builder_dir,
                                   'build-wrappers', 'run-command')
     self.assertTrue(os.access(sb_script, os.F_OK))
     self.assertTrue(os.access(sb_readme, os.F_OK))
     self.assertTrue(os.access(sb_run_command, os.F_OK))
     self.assertEqual(self.context.script, os.path.basename(sys.argv[0]))
     self.assertFalse(self.context.silent)
     self.assertFalse(self.context.verbose_messages)
     self.assertEqual(self.context.message_file, sys.stderr)
     self.assertFalse(self.context.execute_silent)
     self.assertIs(self.context.environ, os.environ)
     self.assertEqual(self.context.environ_orig, os.environ)
     self.assertIsNot(self.context.environ_orig, os.environ)
     self.assertIs(self.context.flags, sys.flags)
     self.assertIs(self.context.execve, os.execve)
     self.assertIs(self.context.setlocale, locale.setlocale)
     self.assertIs(self.context.umask, os.umask)
     self.assertEqual(self.context.package_list, ('sourcery',))
     self.assertIn('self-test', self.context.commands)
     import sourcery.commands.self_test
     self.assertIs(self.context.commands['self-test'],
                   sourcery.commands.self_test.Command)
     self.assertIn('gcc', self.context.components)
     import sourcery.components.gcc
     self.assertIs(self.context.components['gcc'],
                   sourcery.components.gcc.Component)
     # Test loading extra commands and components.
     test_context = ScriptContext(['sourcery.selftests'])
     self.assertEqual(test_context.package_list,
                      ('sourcery', 'sourcery.selftests'))
     self.assertIn('self-test', test_context.commands)
     self.assertIsNot(test_context.commands['self-test'],
                      sourcery.commands.self_test.Command)
     self.assertTrue(issubclass(test_context.commands['self-test'],
                                sourcery.commands.self_test.Command))
     import sourcery.selftests.command
     self.assertTrue(issubclass(test_context.commands['self-test'],
                                sourcery.selftests.command.Command))
     self.assertIn('null', test_context.commands)
     import sourcery.selftests.commands.null
     self.assertIs(test_context.commands['null'],
                   sourcery.selftests.commands.null.Command)
     self.assertIn('gcc', test_context.components)
     self.assertIsNot(test_context.components['gcc'],
                      sourcery.components.gcc.Component)
     self.assertTrue(issubclass(test_context.components['gcc'],
                                sourcery.components.gcc.Component))
     import sourcery.selftests.component
     self.assertTrue(issubclass(test_context.components['gcc'],
                                sourcery.selftests.component.Component))
     self.assertIn('generic', test_context.components)
     import sourcery.selftests.components.generic
     self.assertIs(test_context.components['generic'],
                   sourcery.selftests.components.generic.Component)
     # Test case with multiple levels of extra commands and components.
     test_context = ScriptContext(['sourcery.selftests',
                                   'sourcery.selftests.extra'])
     self.assertEqual(test_context.package_list,
                      ('sourcery', 'sourcery.selftests',
                       'sourcery.selftests.extra'))
     self.assertEqual(test_context.commands['null'].short_desc,
                      'Do nothing, overridden.')
     import sourcery.selftests.extra.component
     self.assertTrue(issubclass(
         test_context.components['generic'],
         sourcery.selftests.extra.component.Component))
     self.assertTrue(issubclass(test_context.components['generic'],
                                sourcery.selftests.component.Component))
     import sourcery.selftests.extra.components.generic
     self.assertTrue(issubclass(
         test_context.components['generic'],
         sourcery.selftests.extra.components.generic.Component))
     self.assertTrue(issubclass(
         test_context.components['generic'],
         sourcery.selftests.components.generic.Component))
Ejemplo n.º 9
0
 def setUp(self):
     """Set up an info_text test."""
     self.context = ScriptContext(['sourcery.selftests'])
     self.parser = argparse.ArgumentParser()
     add_common_options(self.parser, os.getcwd())
     self.args = self.parser.parse_args([])
Ejemplo n.º 10
0
 def setUp(self):
     """Set up a PkgHost test."""
     self.context = ScriptContext()
Ejemplo n.º 11
0
class BuildContextTestCase(unittest.TestCase):
    """Test the BuildContext class."""
    def setUp(self):
        """Set up a BuildContext test."""
        self.context = ScriptContext(['sourcery.selftests'])
        self.tempdir_td = tempfile.TemporaryDirectory()
        self.tempdir = self.tempdir_td.name
        parser = argparse.ArgumentParser()
        add_common_options(parser, self.tempdir)
        add_parallelism_option(parser)
        self.args = parser.parse_args([])
        self.args.build_source_packages = False
        self.relcfg = None
        self.build_context = None

    def setup_rc(self, rc_text_extra=''):
        """Complete test setup.

        Tests require different release configurations using different
        test components, so this part of the setup needs to be
        deferred to this function, called from the individual test
        methods.

        """
        relcfg_text = ('cfg.build.set("x86_64-linux-gnu")\n'
                       'cfg.hosts.set(("x86_64-linux-gnu", '
                       '"x86_64-w64-mingw32"))\n'
                       'cfg.target.set("aarch64-linux-gnu")\n' + rc_text_extra)
        self.relcfg = ReleaseConfig(self.context, relcfg_text,
                                    ReleaseConfigTextLoader(), self.args)
        self.build_context = BuildContext(self.context, self.relcfg, self.args)

    def tearDown(self):
        """Tear down a BuildContext test."""
        self.tempdir_td.cleanup()

    def temp_file(self, name):
        """Return the name of a temporary file for a test."""
        return os.path.join(self.tempdir, name)

    @contextlib.contextmanager
    def redirect_stdout_stderr(self):
        """Redirect stdout and stderr for code in a 'with' statement."""
        with redirect_file(sys.stdout, self.temp_file('stdout')):
            with redirect_file(sys.stderr, self.temp_file('stderr')):
                yield

    def temp_file_read(self, name):
        """Read a file in tempdir for this test."""
        with open(self.temp_file(name), 'r', encoding='utf-8') as file:
            return file.read()

    def temp_file_write(self, name, contents):
        """Write a file in tempdir for this test."""
        with open(self.temp_file(name), 'w', encoding='utf-8') as file:
            file.write(contents)

    def stdout_stderr_read(self):
        """Read the stdout and stderr for this test."""
        return (self.temp_file_read('stdout'), self.temp_file_read('stderr'))

    def test_init(self):
        """Test BuildContext.__init__."""
        self.setup_rc()
        bcontext = self.build_context
        self.assertIs(bcontext.context, self.context)
        self.assertIs(bcontext.relcfg, self.relcfg)
        self.assertEqual(
            bcontext.logdir,
            os.path.join(self.args.logdir,
                         'toolchain-1.0-1-aarch64-linux-gnu'))
        self.assertEqual(bcontext.parallelism, self.args.parallelism)
        self.assertEqual(bcontext.build_objdir,
                         self.relcfg.objdir_path(None, 'build'))
        self.assertTrue(
            stat.S_ISDIR(
                os.stat(bcontext.sockdir, follow_symlinks=False).st_mode))
        self.assertIsInstance(bcontext.server, RPCServer)

    def test_setup_build_dir(self):
        """Test setup_build_dir."""
        self.setup_rc('cfg.add_component("build_test")\n')
        self.build_context.setup_build_dir()
        makefile_name = os.path.join(self.build_context.build_objdir,
                                     'GNUmakefile')
        with open(makefile_name, 'r', encoding='utf-8') as file:
            makefile_text = file.read()
        deps, commands = parse_makefile(makefile_text)
        # Commands are tested in tests of run_build, via running them;
        # mainly verify dependencies here.
        self.assertIn('task-end/x86_64-linux-gnu/all-hosts', deps)
        self.assertIn('task-end/x86_64-w64-mingw32/all-hosts', deps)
        self.assertIn('task-end/x86_64-linux-gnu/first-host', deps)
        self.assertNotIn('task-end/x86_64-w64-mingw32/first-host', deps)
        self.assertNotIn('task-end/x86_64-linux-gnu/other-hosts', deps)
        self.assertIn('task-end/x86_64-w64-mingw32/other-hosts', deps)
        self.assertIn('task-end/init', deps['task-start/x86_64-linux-gnu'])
        self.assertIn('task-end/init', deps['task-start/x86_64-w64-mingw32'])
        self.assertIn('task-end/init', deps['task-start/host-indep'])
        self.assertIn('task-end/x86_64-linux-gnu', deps['task-start/fini'])
        self.assertIn('task-end/x86_64-w64-mingw32', deps['task-start/fini'])
        self.assertIn('task-end/host-indep', deps['task-start/fini'])
        self.assertIn('rm -rf',
                      commands['task-end/x86_64-linux-gnu/all-hosts'][1])

    def test_setup_build_dir_remove(self):
        """Test setup_build_dir removal of existing directory."""
        self.setup_rc('cfg.add_component("build_test")\n')
        os.makedirs(os.path.join(self.build_context.build_objdir, 'x'))
        self.build_context.setup_build_dir()
        dir_contents = sorted(os.listdir(self.build_context.build_objdir))
        self.assertEqual(dir_contents, ['GNUmakefile'])

    def test_wrapper_run_command(self):
        """Test wrapper_run_command."""
        # We can't test much more than repeating the function's logic.
        # The main testing that the generated command does what is
        # intended is in tests of run_build.
        self.setup_rc()
        command = self.build_context.wrapper_run_command(
            '/some/log', 123, '/some/dir')
        self.assertEqual(command, [
            self.context.build_wrapper_path('run-command'),
            self.context.interp, self.context.script_full,
            self.build_context.build_objdir, '/some/log',
            self.build_context.sockdir, '123', '/some/dir'
        ])

    def test_wrapper_start_task(self):
        """Test wrapper_start_task."""
        # We can't test much more than repeating the function's logic.
        # The main testing that the generated command does what is
        # intended is in tests of run_build.
        self.setup_rc()
        command = self.build_context.wrapper_start_task('/some/log', 123)
        self.assertEqual(command, [
            self.context.build_wrapper_path('start-task'), self.context.interp,
            self.context.script_full, self.build_context.build_objdir,
            '/some/log', self.build_context.sockdir, '123'
        ])

    def test_wrapper_end_task(self):
        """Test wrapper_end_task."""
        # We can't test much more than repeating the function's logic.
        # The main testing that the generated command does what is
        # intended is in tests of run_build.
        self.setup_rc()
        command = self.build_context.wrapper_end_task('/some/log', 123)
        self.assertEqual(command, [
            self.context.build_wrapper_path('end-task'), self.context.interp,
            self.context.script_full, self.build_context.build_objdir,
            '/some/log', self.build_context.sockdir, '123'
        ])

    def test_rpc_client_command(self):
        """Test rpc_client_command."""
        # We can't test much more than repeating the function's logic.
        # The main testing that the generated command does what is
        # intended is in tests of run_build.
        self.setup_rc()
        command = self.build_context.rpc_client_command(123)
        self.assertEqual(command,
                         (self.context.script_command() +
                          ['rpc-client', self.build_context.sockdir, '123']))

    def test_task_start(self):
        """Test task_start."""
        self.setup_rc()
        self.context.message_file = io.StringIO()
        self.build_context.task_start('some start text')
        output = self.context.message_file.getvalue()
        self.assertTrue(output.endswith(' some start text start\n'))
        self.assertRegex(output, r'^\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] ')
        self.context.silent = True
        self.context.message_file = io.StringIO()
        self.build_context.task_start('more start text')
        output = self.context.message_file.getvalue()
        self.assertEqual(output, '')

    def test_task_fail_command(self):
        """Test task_fail_command."""
        self.setup_rc()
        self.context.message_file = io.StringIO()
        test_desc = 'test description'
        # Want to test that str() is called for non-string command
        # passed, so don't just pass a string here.
        test_cmd = ('test', 1)
        test_log = self.temp_file('log')
        exp_start = ('%s: warning: test description FAILED\n'
                     "%s: warning: failed command was: ('test', 1)\n"
                     '%s: warning: current log file is: %s '
                     '(last 25 lines shown)\n'
                     '------------------------------------ start '
                     '------------------------------------\n' %
                     (self.context.script, self.context.script,
                      self.context.script, test_log))
        exp_end = ('------------------------------------- end '
                   '-------------------------------------\n')
        num_text = '\n'.join(str(n) for n in range(100))
        num_text_25 = '\n'.join(str(n) for n in range(75, 100))
        # Last 25 lines of log.
        self.context.message_file = io.StringIO()
        self.temp_file_write('log', num_text + '\n')
        self.build_context.task_fail_command(test_desc, test_cmd, test_log)
        output = self.context.message_file.getvalue()
        self.assertEqual(output,
                         '%s%s\n%s' % (exp_start, num_text_25, exp_end))
        # Last 25 lines of log, no newline.
        self.context.message_file = io.StringIO()
        self.temp_file_write('log', num_text)
        self.build_context.task_fail_command(test_desc, test_cmd, test_log)
        output = self.context.message_file.getvalue()
        self.assertEqual(output,
                         '%s%s\n%s' % (exp_start, num_text_25, exp_end))
        # Empty log.
        self.context.message_file = io.StringIO()
        self.temp_file_write('log', '')
        self.build_context.task_fail_command(test_desc, test_cmd, test_log)
        output = self.context.message_file.getvalue()
        self.assertEqual(output, '%s\n%s' % (exp_start, exp_end))
        # Non-ASCII text in log.
        self.context.message_file = io.StringIO()
        self.temp_file_write('log', '\u00ff')
        self.build_context.task_fail_command(test_desc, test_cmd, test_log)
        output = self.context.message_file.getvalue()
        self.assertEqual(output, '%s\\xc3\\xbf\n%s' % (exp_start, exp_end))

    def test_task_end(self):
        """Test task_end."""
        self.setup_rc()
        self.context.message_file = io.StringIO()
        self.build_context.task_end('some end text')
        output = self.context.message_file.getvalue()
        self.assertTrue(output.endswith(' some end text end\n'))
        self.assertRegex(output, r'^\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] ')
        self.context.silent = True
        self.context.message_file = io.StringIO()
        self.build_context.task_end('more end text')
        output = self.context.message_file.getvalue()
        self.assertEqual(output, '')

    def test_run_build(self):
        """Test run_build, simple successful build."""
        self.setup_rc('cfg.add_component("build_test")\n'
                      'cfg.multilibs.set((Multilib("build_test", '
                      '"build_test", ()), Multilib("build_test", '
                      '"build_test", ("-mtest",), osdir="test")))\n')
        with self.redirect_stdout_stderr():
            self.build_context.run_build()
        stdout, stderr = self.stdout_stderr_read()
        hosts = self.relcfg.hosts.get()
        host_b0 = hosts[0].build_cfg
        host_b1 = hosts[1].build_cfg
        h0_all_objdir = self.relcfg.objdir_path(host_b0, 'build_test-all')
        h0_all_objdir2 = self.relcfg.objdir_path(host_b0, 'build_test-all2')
        h1_all_objdir = self.relcfg.objdir_path(host_b1, 'build_test-all')
        h1_all_objdir2 = self.relcfg.objdir_path(host_b1, 'build_test-all2')
        self.assertEqual(read_files(h0_all_objdir), (set(), {
            'out1': 'all-hosts\n',
            'out2': 'all-hosts-2\n'
        }, {}))
        self.assertEqual(read_files(h0_all_objdir2), (set(), {}, {}))
        self.assertEqual(read_files(h1_all_objdir), (set(), {
            'out1': 'all-hosts\n',
            'out2': 'all-hosts-2\n'
        }, {}))
        self.assertEqual(read_files(h1_all_objdir2), (set(), {}, {}))
        first_objdir = self.relcfg.objdir_path(host_b0, 'build_test-first')
        first_objdir2 = self.relcfg.objdir_path(host_b0, 'build_test-first2')
        self.assertEqual(read_files(first_objdir), ({'x', 'x/y'}, {
            'GNUmakefile': 'all:; echo first-host $(X) > out\n',
            'out': 'first-host Y\n'
        }, {}))
        self.assertEqual(read_files(first_objdir2), ({'x', 'x/y'}, {}, {}))
        other_objdir = self.relcfg.objdir_path(host_b1, 'build_test-other')
        self.assertEqual(read_files(other_objdir), (set(), {
            'out': 'test python\n'
        }, {}))
        first_objdir_m0 = self.relcfg.objdir_path(
            host_b0, 'build_test-first-aarch64-linux-gnu')
        first_objdir_m1 = self.relcfg.objdir_path(
            host_b0, 'build_test-first-aarch64-linux-gnu-mtest')
        other_objdir_m0 = self.relcfg.objdir_path(
            host_b1, 'build_test-other-aarch64-linux-gnu')
        other_objdir_m1 = self.relcfg.objdir_path(
            host_b1, 'build_test-other-aarch64-linux-gnu-mtest')
        self.assertEqual(read_files(first_objdir_m0),
                         (set(), {
                             'out': 'aarch64-linux-gnu\n'
                         }, {}))
        self.assertEqual(read_files(first_objdir_m1),
                         (set(), {
                             'out': 'aarch64-linux-gnu-mtest\n'
                         }, {}))
        self.assertEqual(read_files(other_objdir_m0),
                         (set(), {
                             'out': 'test aarch64-linux-gnu\n'
                         }, {}))
        self.assertEqual(read_files(other_objdir_m1),
                         (set(), {
                             'out': 'test aarch64-linux-gnu-mtest\n'
                         }, {}))
        init_objdir = self.relcfg.objdir_path(None, 'build_test-init')
        host_indep_objdir = self.relcfg.objdir_path(None,
                                                    'build_test-host-indep')
        fini_objdir = self.relcfg.objdir_path(None, 'build_test-fini')
        self.assertEqual(read_files(init_objdir), (set(), {
            'out': 'init\n'
        }, {}))
        self.assertEqual(read_files(host_indep_objdir), (set(), {
            'out': 'host-indep\n'
        }, {}))
        self.assertEqual(read_files(fini_objdir), (set(), {
            'out': 'fini\n'
        }, {}))
        self.assertEqual(stdout, '')
        lines = stderr.splitlines()
        self.assertEqual(len(lines), 36)
        for line in lines:
            self.assertRegex(
                line, r'^\[[0-2][0-9]:[0-5][0-9]:[0-6][0-9]\] '
                r'\[00[0-1][0-9]/0018\] /.*(start|end)\Z')
        self.assertIn('[0001/0018]', stderr)
        self.assertIn('[0018/0018]', stderr)
        self.assertIn('/x86_64-linux-gnu/all-hosts start', stderr)
        self.assertIn('/x86_64-w64-mingw32/other-hosts end', stderr)
        self.assertIn('/install-trees-x86_64-linux-gnu/package-input start',
                      stderr)
        self.assertIn('/install-trees-x86_64-w64-mingw32/package-input end',
                      stderr)
        self.assertIn('/x86_64-linux-gnu/package-output start', stderr)
        self.assertIn('/x86_64-w64-mingw32/package-output end', stderr)
        self.assertIn('/x86_64-linux-gnu/package-tar-xz end', stderr)
        self.assertIn('/x86_64-w64-mingw32/package-tar-xz start', stderr)
        self.assertIn('/init/init start', stderr)
        self.assertIn('/init/pkgdir end', stderr)
        self.assertIn('/host-indep/host-indep start', stderr)
        self.assertIn('/fini/fini end', stderr)
        # In this case, the created packages are empty.
        pkg_0 = self.relcfg.pkgdir_path(hosts[0], '.tar.xz')
        pkg_1 = self.relcfg.pkgdir_path(hosts[1], '.tar.xz')
        dir_out = os.path.join(self.tempdir, 'toolchain-1.0')
        subprocess.run(['tar', '-x', '-f', pkg_0],
                       cwd=self.tempdir,
                       check=True)
        self.assertEqual(read_files(dir_out), (set(), {}, {}))
        shutil.rmtree(dir_out)
        subprocess.run(['tar', '-x', '-f', pkg_1],
                       cwd=self.tempdir,
                       check=True)
        self.assertEqual(read_files(dir_out), (set(), {}, {}))

    def test_run_build_silent(self):
        """Test run_build, simple successful build, silent."""
        self.context.silent = True
        self.setup_rc('cfg.add_component("build_test")\n')
        with self.redirect_stdout_stderr():
            self.build_context.run_build()
        stdout, stderr = self.stdout_stderr_read()
        self.assertEqual(stdout, '')
        self.assertEqual(stderr, '')

    def test_run_build_log(self):
        """Test run_build, simple successful build, command output to log."""
        self.setup_rc('cfg.add_component("build_log")\n')
        with self.redirect_stdout_stderr():
            self.build_context.run_build()
        log = glob.glob(
            os.path.join(self.build_context.logdir,
                         '00*-x86_64-linux-gnu-first-host-log.txt'))[0]
        with open(log, 'r', encoding='utf-8') as file:
            log_text = file.read()
        num_text_1 = '\n'.join(str(n) for n in range(10))
        num_text_2 = '\n'.join(str(n) for n in range(10, 20))
        self.assertIn(num_text_1, log_text)
        self.assertIn(num_text_2, log_text)

    def test_run_build_fail_command(self):
        """Test run_build, simple failed build."""
        self.setup_rc('cfg.add_component("build_fail_command")\n')
        with self.redirect_stdout_stderr():
            self.assertRaisesRegex(ScriptError, 'build failed',
                                   self.build_context.run_build)
        stdout, stderr = self.stdout_stderr_read()
        self.assertEqual(stdout, '')
        log = glob.glob(
            os.path.join(self.build_context.logdir,
                         '00*-x86_64-linux-gnu-first-host-log.txt'))[0]
        with open(log, 'r', encoding='utf-8') as file:
            log_text = file.read()
        self.assertIn('1\n2\n3\n4\n', log_text)
        self.assertIn('1\n2\n3\n4\n', stderr)

    def test_run_build_fail_command_silent(self):
        """Test run_build, simple failed build, silent."""
        self.context.silent = True
        self.setup_rc('cfg.add_component("build_fail_command")\n')
        with self.redirect_stdout_stderr():
            self.assertRaisesRegex(ScriptError, 'build failed',
                                   self.build_context.run_build)
        stdout, stderr = self.stdout_stderr_read()
        self.assertEqual(stdout, '')
        log = glob.glob(
            os.path.join(self.build_context.logdir,
                         '00*-x86_64-linux-gnu-first-host-log.txt'))[0]
        with open(log, 'r', encoding='utf-8') as file:
            log_text = file.read()
        self.assertIn('1\n2\n3\n4\n', log_text)
        # Errors should still appear on stderr even with --silent.
        self.assertIn('1\n2\n3\n4\n', stderr)

    def test_run_build_fail_cd(self):
        """Test run_build, bad cwd for command."""
        self.setup_rc('cfg.add_component("build_fail_cd")\n')
        with self.redirect_stdout_stderr():
            self.assertRaisesRegex(ScriptError, 'build failed',
                                   self.build_context.run_build)

    def test_run_build_fail_python(self):
        """Test run_build, failed Python step."""
        self.setup_rc('cfg.add_component("build_fail_python")\n')
        with self.redirect_stdout_stderr():
            self.assertRaisesRegex(ScriptError, 'build failed',
                                   self.build_context.run_build)
        stdout, stderr = self.stdout_stderr_read()
        self.assertEqual(stdout, '')
        log = glob.glob(
            os.path.join(self.build_context.logdir,
                         '00*-x86_64-linux-gnu-first-host-log.txt'))[0]
        with open(log, 'r', encoding='utf-8') as file:
            log_text = file.read()
        self.assertRegex(log_text, 'ValueError.*test failure')
        self.assertRegex(stderr, 'ValueError.*test failure')

    def test_run_build_install_tree(self):
        """Test run_build, implicit install tree creation."""
        self.setup_rc('cfg.add_component("build_install_tree")\n')
        with self.redirect_stdout_stderr():
            self.build_context.run_build()
        hosts = self.relcfg.hosts.get()
        host_b0 = hosts[0].build_cfg
        instdir_def = self.relcfg.install_tree_path(host_b0, 'impl-def')
        instdir_empty = self.relcfg.install_tree_path(host_b0, 'impl-empty')
        instdir_one = self.relcfg.install_tree_path(host_b0, 'impl-one')
        instdir_two = self.relcfg.install_tree_path(host_b0, 'impl-two')
        self.assertEqual(read_files(instdir_def), ({'q'}, {'q/a': 'a\n'}, {}))
        self.assertEqual(read_files(instdir_empty), (set(), {}, {}))
        self.assertEqual(read_files(instdir_one), (set(), {'b': 'b\n'}, {}))
        self.assertEqual(read_files(instdir_two), (set(), {
            'b': 'b\n',
            'c': 'c\n'
        }, {}))

    def test_run_build_package(self):
        """Test run_build, nonempty packages built."""
        self.setup_rc('cfg.add_component("build_package")\n'
                      'cfg.source_date_epoch.set(1111199990)\n')
        with self.redirect_stdout_stderr():
            self.build_context.run_build()
        hosts = self.relcfg.hosts.get()
        pkg_0 = self.relcfg.pkgdir_path(hosts[0], '.tar.xz')
        pkg_1 = self.relcfg.pkgdir_path(hosts[1], '.tar.xz')
        dir_out = os.path.join(self.tempdir, 'toolchain-1.0')
        subprocess.run(['tar', '-x', '-f', pkg_0],
                       cwd=self.tempdir,
                       check=True)
        self.assertEqual(read_files(dir_out), (set(), {
            'a1': 'a\n',
            'a2': 'a\n',
            'a3': 'a\n',
            'b': 'b\n'
        }, {
            'c': 'b'
        }))
        stat_a1 = os.stat(os.path.join(dir_out, 'a1'))
        self.assertEqual(stat_a1.st_nlink, 3)
        self.assertEqual(stat_a1.st_mtime, 1111199990)
        stat_b = os.stat(os.path.join(dir_out, 'b'))
        self.assertEqual(stat_b.st_nlink, 1)
        self.assertEqual(stat_b.st_mtime, 1111199990)
        shutil.rmtree(dir_out)
        subprocess.run(['tar', '-x', '-f', pkg_1],
                       cwd=self.tempdir,
                       check=True)
        self.assertEqual(read_files(dir_out), (set(), {
            'a1': 'a\n',
            'a2': 'a\n',
            'a3': 'a\n',
            'b': 'b\n',
            'c': 'b\n'
        }, {}))
        stat_a1 = os.stat(os.path.join(dir_out, 'a1'))
        self.assertEqual(stat_a1.st_nlink, 3)
        self.assertEqual(stat_a1.st_mtime, 1111199990)
        stat_b = os.stat(os.path.join(dir_out, 'b'))
        self.assertEqual(stat_b.st_nlink, 2)
        self.assertEqual(stat_b.st_mtime, 1111199990)

    def test_run_build_src_package(self):
        """Test run_build, source and backup packages built."""
        self.args.build_source_packages = True
        # We need to create dummy source trees, but they do not
        # actually need to come from checking out the given version
        # control locations.
        srcdir = os.path.join(self.tempdir, 'src')
        create_files(
            srcdir, ['build_src_open-123', 'build_src_closed-456'], {
                'build_src_open-123/x': 'x',
                'build_src_closed-456/y': 'y',
                'build_src_closed-456/.git': 'ignore'
            }, {})
        self.setup_rc('cfg.add_component("build_src_open")\n'
                      'cfg.build_src_open.version.set("123")\n'
                      'cfg.build_src_open.vc.set(TarVC("/dummy"))\n'
                      'cfg.add_component("build_src_closed")\n'
                      'cfg.build_src_closed.version.set("456")\n'
                      'cfg.build_src_closed.vc.set(GitVC("/dummy"))\n'
                      'cfg.source_date_epoch.set(1111199990)\n')
        with self.redirect_stdout_stderr():
            self.build_context.run_build()
        pkg_src = self.relcfg.pkgdir_path(None, '.src.tar.xz')
        pkg_backup = self.relcfg.pkgdir_path(None, '.backup.tar.xz')
        dir_src = os.path.join(self.tempdir,
                               'toolchain-1.0-1-aarch64-linux-gnu')
        dir_backup = os.path.join(self.tempdir,
                                  'toolchain-1.0-1-aarch64-linux-gnu.backup')
        subprocess.run(['tar', '-x', '-f', pkg_src],
                       cwd=self.tempdir,
                       check=True)
        self.assertEqual(os.listdir(dir_src), ['build_src_open-1.0-1.tar.xz'])
        subprocess.run(['tar', '-x', '-f', pkg_backup],
                       cwd=self.tempdir,
                       check=True)
        self.assertEqual(os.listdir(dir_backup),
                         ['build_src_closed-1.0-1.tar.xz'])
        tar_open = os.path.join(dir_src, 'build_src_open-1.0-1.tar.xz')
        tar_closed = os.path.join(dir_backup, 'build_src_closed-1.0-1.tar.xz')
        self.assertEqual(os.stat(tar_open).st_mtime, 1111199990)
        self.assertEqual(os.stat(tar_closed).st_mtime, 1111199990)
        subprocess.run(['tar', '-x', '-f', tar_open],
                       cwd=self.tempdir,
                       check=True)
        subprocess.run(['tar', '-x', '-f', tar_closed],
                       cwd=self.tempdir,
                       check=True)
        dir_open = os.path.join(self.tempdir, 'build_src_open-1.0-1')
        dir_closed = os.path.join(self.tempdir, 'build_src_closed-1.0-1')
        self.assertEqual(read_files(dir_open), (set(), {'x': 'x'}, {}))
        self.assertEqual(read_files(dir_closed), (set(), {'y': 'y'}, {}))
        self.assertEqual(
            os.stat(os.path.join(dir_open, 'x')).st_mtime, 1111199990)
        self.assertEqual(
            os.stat(os.path.join(dir_closed, 'y')).st_mtime, 1111199990)
Ejemplo n.º 12
0
 def setUp(self):
     """Set up a tsort test."""
     self.context = ScriptContext()
Ejemplo n.º 13
0
 def setUp(self):
     """Set up a sourcery.package test."""
     self.context = ScriptContext()
     self.tempdir_td = tempfile.TemporaryDirectory()
     self.tempdir = self.tempdir_td.name
     self.indir = os.path.join(self.tempdir, 'in')
 def setUp(self):
     """Set up a BuildCfg test."""
     self.context = ScriptContext()
     self.tempdir_td = tempfile.TemporaryDirectory()
     self.tempdir = self.tempdir_td.name
     self.bindir = os.path.join(self.tempdir, 'bin')
Ejemplo n.º 15
0
 def setUp(self):
     """Set up a makefile test."""
     self.context = ScriptContext()