910e62b5创建于 1月15日历史提交
#!/usr/bin/env python3
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for subprocess_utils."""

import os
import pathlib
import subprocess
import unittest

import git_metadata_utils, subprocess_utils

_COMMAND_PROCESS_ERROR_LOG_REGEX = (r'Command ".*" failed with code \d+\.'
                                    r'(\nSTDERR: .*)?(\nSTDOUT: .*)?')


class TestResolveCommands(unittest.TestCase):
    """Tests for util commands like resolve_{ninja,autoninja}."""

    def setUp(self):
        self.original_path = os.environ.get('PATH')

    def tearDown(self):
        if self.original_path is not None:
            os.environ['PATH'] = self.original_path
        else:
            del os.environ['PATH']

    def test_resolve_ninja_returns_command_if_in_path(self):
        ninja_dir = str(git_metadata_utils.get_chromium_src_path() /
                        'third_party/ninja')
        os.environ['PATH'] = ninja_dir
        self.assertEqual('ninja', subprocess_utils.resolve_ninja())

    def test_resolve_ninja_returns_depot_path_if_not_in_path(self):
        if 'PATH' in os.environ:
            del os.environ['PATH']
        ninja_path = str(git_metadata_utils.get_chromium_src_path() /
                         'third_party/ninja/ninja')
        self.assertEqual(ninja_path, subprocess_utils.resolve_ninja())

    def test_resolve_autoninja_returns_command_if_in_path(self):
        autoninja_dir = str(git_metadata_utils.get_chromium_src_path() /
                            'third_party/depot_tools')
        os.environ['PATH'] = autoninja_dir
        self.assertEqual('autoninja', subprocess_utils.resolve_autoninja())

    def test_resolve_autoninja_returns_depot_path_if_not_in_path(self):
        if 'PATH' in os.environ:
            del os.environ['PATH']
        autoninja_path = str(git_metadata_utils.get_chromium_src_path() /
                             'third_party/depot_tools/autoninja')
        self.assertEqual(autoninja_path, subprocess_utils.resolve_autoninja())


class TestRunCommand(unittest.TestCase):
    """Tests for the run_command function."""

    def test_run_command_no_args(self):
        expected_cwd: str = os.getcwd()

        pwd_output = subprocess_utils.run_command(['pwd'])

        self.assertEqual(expected_cwd, pwd_output)

    def test_run_command_custom_cwd(self):
        expected_cwd = '/usr/local/bin'

        pwd_output = subprocess_utils.run_command(['pwd'], cwd=expected_cwd)

        self.assertEqual(expected_cwd, pwd_output)

    def test_run_command_custom_cwd_path(self):
        expected_cwd = '/usr/local/bin'

        pwd_output = subprocess_utils.run_command(
            ['pwd'], cwd=pathlib.Path(expected_cwd).resolve(strict=True))

        self.assertEqual(expected_cwd, pwd_output)

    def test_run_command_multiple_args(self):
        expected_output = 'some sample output'

        command_output = subprocess_utils.run_command(
            ['echo', 'some', 'sample', 'output'])

        self.assertEqual(expected_output, command_output)

    def test_run_command_strips_output_whitespace(self):
        expected_output = 'Hello, world!'

        command_output = subprocess_utils.run_command(
            ['echo', '\t Hello, world! \n'])

        self.assertEqual(expected_output, command_output)

    def test_run_command_exitcode_only_returns_zero_on_success(self):
        expected_output = 0

        command_output = subprocess_utils.run_command(
            ['echo', '\t Hello, world! \n'], exitcode_only=True)

        self.assertEqual(expected_output, command_output)

    def test_run_command_exitcode_only_returns_exitcode_on_failure(self):
        expected_output = 1

        command_output = subprocess_utils.run_command(['git', 'foo'],
                                                      exitcode_only=True)

        self.assertEqual(expected_output, command_output)

    def test_run_command_process_error_no_output(self):
        with self.assertRaises(
                subprocess.CalledProcessError) as error_cm, self.assertLogs(
                    level='ERROR') as logging_cm:
            subprocess_utils.run_command(['false'])

        self.assertEqual(1, error_cm.exception.returncode)
        self.assertEqual(1, len(logging_cm.output))
        self.assertRegex(logging_cm.output[0],
                         _COMMAND_PROCESS_ERROR_LOG_REGEX)

    def test_run_command_process_error_with_stderr_output(self):
        expected_stderr_regex = r"git: 'foo' is not a git command.+"

        with self.assertRaises(
                subprocess.CalledProcessError) as error_cm, self.assertLogs(
                    level='ERROR') as logging_cm:
            subprocess_utils.run_command(['git', 'foo'])

        self.assertEqual(1, error_cm.exception.returncode)
        self.assertRegex(error_cm.exception.stderr, expected_stderr_regex)
        self.assertEqual('', error_cm.exception.stdout)
        self.assertEqual(1, len(logging_cm.output))
        self.assertRegex(logging_cm.output[0],
                         _COMMAND_PROCESS_ERROR_LOG_REGEX)

    def test_run_command_process_error_with_stdout_output(self):
        expected_stdout_regex = r'usage: git.+'

        with self.assertRaises(
                subprocess.CalledProcessError) as error_cm, self.assertLogs(
                    level='ERROR') as logging_cm:
            subprocess_utils.run_command(['git'])

        self.assertEqual(1, error_cm.exception.returncode)
        self.assertEqual('', error_cm.exception.stderr)
        self.assertRegex(error_cm.exception.stdout, expected_stdout_regex)
        self.assertEqual(1, len(logging_cm.output))
        self.assertRegex(logging_cm.output[0],
                         _COMMAND_PROCESS_ERROR_LOG_REGEX)

    def test_run_command_not_found(self):
        with self.assertRaises(FileNotFoundError) as error_cm:
            subprocess_utils.run_command(['foo_cmd'])

        self.assertEqual(2, error_cm.exception.errno)
        self.assertEqual('foo_cmd', error_cm.exception.filename)


if __name__ == '__main__':
    unittest.main()