From 9a827e31cf8ee86914b2f5d5210ae2999c7dec26 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Wed, 1 May 2024 10:44:17 -0500 Subject: [PATCH] Add prepare_outputs tests Add regular file output when input is collected but not piped. --- .github/workflows/test.yaml | 22 +----------- src/duct.py | 8 +++-- test/test_prepare_outputs.py | 68 ++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 test/test_prepare_outputs.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6b56035f..6873089d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -39,7 +39,6 @@ jobs: # - 'pypy-3.8' # - 'pypy-3.9' # - 'pypy-3.10' - toxenv: [py] # exclude: # # No older Pythons on arm64 macos-latest # - python-version: '3.7' @@ -77,24 +76,5 @@ jobs: run: pip install . - name: Run tests - # if: matrix.toxenv == 'py' - run: ./run-tests.sh - # run: ./run-tests.sh -e py -- -vv --ci --cov-report=xml - # env: - # GITHUB_TOKEN: ${{ secrets.GH_DOWNLOAD_TOKEN }} - - # TODO - # - name: Run generic tests - # if: matrix.toxenv != 'py' - # run: tox -e ${{ matrix.toxenv }} - # - # TODO - # - name: Upload coverage to Codecov - # if: matrix.toxenv == 'py' - # uses: codecov/codecov-action@v4 - # with: - # fail_ci_if_error: false - # token: ${{ secrets.CODECOV_TOKEN }} - # name: ${{ matrix.python-version }} - + run: tox -e py # vim:set et sts=2: diff --git a/src/duct.py b/src/duct.py index 721f256c..bd02f806 100755 --- a/src/duct.py +++ b/src/duct.py @@ -239,9 +239,9 @@ def monitor_process( ): while True: if process.poll() is not None: # the passthrough command has finished - if isinstance(stdout, TeeStream): + if hasattr(stdout, "close"): stdout.close() - if isinstance(stderr, TeeStream): + if hasattr(stderr, "close"): stderr.close() break @@ -263,6 +263,8 @@ def prepare_outputs(capture_outputs, outputs, output_prefix): if capture_outputs in ["all", "stdout"] and outputs in ["all", "stdout"]: stdout = TeeStream(f"{output_prefix}/stdout.txt") stdout.start() + elif capture_outputs in ["all", "stdout"] and outputs in ["none", "stderr"]: + stdout = open(f"{output_prefix}/stdout.txt") elif capture_outputs in ["none", "stderr"] and outputs in ["all", "stdout"]: stdout = subprocess.PIPE else: @@ -271,6 +273,8 @@ def prepare_outputs(capture_outputs, outputs, output_prefix): if capture_outputs in ["all", "stderr"] and outputs in ["all", "stderr"]: stderr = TeeStream(f"{output_prefix}/stderr.txt") stderr.start() + elif capture_outputs in ["all", "stderr"] and outputs in ["none", "stdout"]: + stderr = open(f"{output_prefix}/stderr.txt") elif capture_outputs in ["none", "stdout"] and outputs in [ "all", "stderr", diff --git a/test/test_prepare_outputs.py b/test/test_prepare_outputs.py new file mode 100644 index 00000000..e9016ced --- /dev/null +++ b/test/test_prepare_outputs.py @@ -0,0 +1,68 @@ +import subprocess +from unittest.mock import MagicMock, call, patch +from duct import prepare_outputs + + +def test_prepare_outputs_all_stdout(): + output_prefix = "test_outputs" + with patch("duct.TeeStream") as mock_tee_stream, patch( + "builtins.open", new_callable=MagicMock + ) as mock_open: + mock_tee_stream.return_value.start = MagicMock() + stdout, stderr = prepare_outputs("all", "stdout", output_prefix) + mock_tee_stream.assert_called_with(f"{output_prefix}/stdout.txt") + assert stdout == mock_tee_stream.return_value + assert stderr == mock_open.return_value + + +def test_prepare_outputs_all_stderr(): + output_prefix = "test_outputs" + with patch("duct.TeeStream") as mock_tee_stream, patch( + "builtins.open", new_callable=MagicMock + ) as mock_open: + mock_tee_stream.return_value.start = MagicMock() + stdout, stderr = prepare_outputs("all", "stderr", output_prefix) + mock_tee_stream.assert_called_with(f"{output_prefix}/stderr.txt") + assert stdout == mock_open.return_value + assert stderr == mock_tee_stream.return_value + + +def test_prepare_outputs_all_none(): + output_prefix = "test_outputs" + with patch("builtins.open", new_callable=MagicMock) as mock_open: + stdout, stderr = prepare_outputs("all", "none", output_prefix) + calls = [ + call(f"{output_prefix}/stdout.txt"), + call(f"{output_prefix}/stderr.txt"), + ] + mock_open.assert_has_calls(calls, any_order=True) + assert stdout == mock_open.return_value + assert stderr == mock_open.return_value + + +def test_prepare_outputs_none_stdout(): + output_prefix = "test_outputs" + stdout, stderr = prepare_outputs("none", "stdout", output_prefix) + assert stdout == subprocess.PIPE + assert stderr == subprocess.DEVNULL + + +def test_prepare_outputs_none_stderr(): + output_prefix = "test_outputs" + stdout, stderr = prepare_outputs("none", "stderr", output_prefix) + assert stderr == subprocess.PIPE + assert stdout == subprocess.DEVNULL + + +def test_prepare_outputs_all_all(): + output_prefix = "test_outputs" + with patch("duct.TeeStream") as mock_tee_stream: + mock_tee_stream.return_value.start = MagicMock() + stdout, stderr = prepare_outputs("all", "all", output_prefix) + assert stdout == mock_tee_stream.return_value + assert stderr == mock_tee_stream.return_value + calls = [ + call(f"{output_prefix}/stdout.txt"), + call(f"{output_prefix}/stderr.txt"), + ] + mock_tee_stream.assert_has_calls(calls, any_order=True)