From 54fd6e21b72c5450c6542877c0c5f553bfcb98f4 Mon Sep 17 00:00:00 2001 From: "Jason M. Gates" Date: Thu, 27 Jun 2024 08:56:49 -0600 Subject: [PATCH] test: Run the examples and check their output --- .github/workflows/continuous-integration.yml | 2 +- example/ex_0_the_basics.py | 1 + example/ex_1_removing_the_retry_arguments.py | 1 + .../ex_2_running_certain_stages_by_default.py | 1 + example/ex_3_adding_arguments.py | 1 + example/ex_4_customizing_stage_behavior.py | 1 + example/ex_5_customizing_individual_stages.py | 1 + example/ex_6_creating_retryable_stages.py | 1 + example/ex_7_customizing_the_summary.py | 1 + example/test_examples.py | 173 ++++++++++++++++++ pyproject.toml | 4 +- 11 files changed, 185 insertions(+), 2 deletions(-) mode change 100644 => 100755 example/ex_0_the_basics.py mode change 100644 => 100755 example/ex_1_removing_the_retry_arguments.py mode change 100644 => 100755 example/ex_2_running_certain_stages_by_default.py mode change 100644 => 100755 example/ex_3_adding_arguments.py mode change 100644 => 100755 example/ex_4_customizing_stage_behavior.py mode change 100644 => 100755 example/ex_5_customizing_individual_stages.py mode change 100644 => 100755 example/ex_6_creating_retryable_stages.py mode change 100644 => 100755 example/ex_7_customizing_the_summary.py create mode 100755 example/test_examples.py diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 691ec04..674ab77 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -40,7 +40,7 @@ jobs: run: python3 -m pip install . - name: Test with pytest - run: python3 -m pytest --cov=staged_script test/ + run: python3 -m pytest --cov=staged_script example/ test/ - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 diff --git a/example/ex_0_the_basics.py b/example/ex_0_the_basics.py old mode 100644 new mode 100755 index e9ff502..7faafa4 --- a/example/ex_0_the_basics.py +++ b/example/ex_0_the_basics.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """A very basic staged script.""" # © 2024 National Technology & Engineering Solutions of Sandia, LLC diff --git a/example/ex_1_removing_the_retry_arguments.py b/example/ex_1_removing_the_retry_arguments.py old mode 100644 new mode 100755 index fd713a0..d63124a --- a/example/ex_1_removing_the_retry_arguments.py +++ b/example/ex_1_removing_the_retry_arguments.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """A staged script without retry arguments.""" # © 2024 National Technology & Engineering Solutions of Sandia, LLC diff --git a/example/ex_2_running_certain_stages_by_default.py b/example/ex_2_running_certain_stages_by_default.py old mode 100644 new mode 100755 index 978df8f..4f70ed5 --- a/example/ex_2_running_certain_stages_by_default.py +++ b/example/ex_2_running_certain_stages_by_default.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """A staged script that runs certain stages by default.""" # © 2024 National Technology & Engineering Solutions of Sandia, LLC diff --git a/example/ex_3_adding_arguments.py b/example/ex_3_adding_arguments.py old mode 100644 new mode 100755 index d5685e3..222dd15 --- a/example/ex_3_adding_arguments.py +++ b/example/ex_3_adding_arguments.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """A staged script with additional arguments.""" # © 2024 National Technology & Engineering Solutions of Sandia, LLC diff --git a/example/ex_4_customizing_stage_behavior.py b/example/ex_4_customizing_stage_behavior.py old mode 100644 new mode 100755 index ce8d8f8..2ac7422 --- a/example/ex_4_customizing_stage_behavior.py +++ b/example/ex_4_customizing_stage_behavior.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """A staged script with custom stage behavior.""" # © 2024 National Technology & Engineering Solutions of Sandia, LLC diff --git a/example/ex_5_customizing_individual_stages.py b/example/ex_5_customizing_individual_stages.py old mode 100644 new mode 100755 index da01bcc..b10f61a --- a/example/ex_5_customizing_individual_stages.py +++ b/example/ex_5_customizing_individual_stages.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """A staged script with phases customized per stage.""" # © 2024 National Technology & Engineering Solutions of Sandia, LLC diff --git a/example/ex_6_creating_retryable_stages.py b/example/ex_6_creating_retryable_stages.py old mode 100644 new mode 100755 index 49208f8..bf0eff8 --- a/example/ex_6_creating_retryable_stages.py +++ b/example/ex_6_creating_retryable_stages.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """A staged script with a retryable stage.""" # © 2024 National Technology & Engineering Solutions of Sandia, LLC diff --git a/example/ex_7_customizing_the_summary.py b/example/ex_7_customizing_the_summary.py old mode 100644 new mode 100755 index 91cd2a7..4a0b173 --- a/example/ex_7_customizing_the_summary.py +++ b/example/ex_7_customizing_the_summary.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """A staged script with a retryable stage.""" # © 2024 National Technology & Engineering Solutions of Sandia, LLC diff --git a/example/test_examples.py b/example/test_examples.py new file mode 100755 index 0000000..2527fd1 --- /dev/null +++ b/example/test_examples.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""Run all the examples and ensure their output is correct.""" + +# © 2024 National Technology & Engineering Solutions of Sandia, LLC +# (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the +# U.S. Government retains certain rights in this software. + +# SPDX-License-Identifier: BSD-3-Clause + +import re +import shlex +import subprocess +from datetime import datetime, timedelta, timezone +from pathlib import Path +from typing import List + + +def assert_output_in_order(stdout: str, output: List[str]) -> None: + """ + Ensure the output appears in the correct order. + + Args: + stdout: The ``stdout`` of the command that was run. + output: The list of terms that should appear in the output in + the given order. + + Raises: + ValueError: If any of the terms in the output list cannot be + found. + """ + index = 0 + for term in output: + index = stdout.find(term, index) + len(term) + + +def test_ex_0_the_basics_help() -> None: + example = Path(__file__).parent / "ex_0_the_basics.py" + result = subprocess.run( + [example, "--help"], + capture_output=True, + check=True, + text=True, + ) + assert_output_in_order( + result.stdout, + [ + "the ArgumentParser for the StagedScript base class.", + "--stage {goodbye,hello}", + "--goodbye-retry-attempts", + "--goodbye-retry-delay", + "--goodbye-retry-timeout", + "--hello-retry-attempts", + "--hello-retry-delay", + "--hello-retry-timeout", + ], + ) + + +def test_ex_0_the_basics() -> None: + example = Path(__file__).parent / "ex_0_the_basics.py" + result = subprocess.run( + [example, "--stage", "hello"], + capture_output=True, + check=True, + text=True, + ) + assert_output_in_order( + result.stdout, + [ + "Greeting the user", + "Executing: echo 'Hello World'", + "`hello` stage duration", + "Bidding farewell", + "Skipping this stage", + "`goodbye` stage duration", + "Ran the following", + "--hello-retry-attempts 0", + "--hello-retry-delay 0", + "--hello-retry-timeout 60", + "--goodbye-retry-attempts 0", + "--goodbye-retry-delay 0", + "--goodbye-retry-timeout 60", + "Commands executed", + "Timing results", + "Script result", + ], + ) + + +def test_ex_1_removing_the_retry_arguments_help() -> None: + example = Path(__file__).parent / "ex_1_removing_the_retry_arguments.py" + result = subprocess.run( + [example, "--help"], + capture_output=True, + check=True, + text=True, + ) + assert "Demonstrate removing the retry arguments" in result.stdout + for _ in [ + "--goodbye-retry-attempts", + "--goodbye-retry-delay", + "--goodbye-retry-timeout", + "--hello-retry-attempts", + "--hello-retry-delay", + "--hello-retry-timeout", + ]: + assert _ not in result.stdout + + +def test_ex_1_removing_the_retry_arguments() -> None: + example = Path(__file__).parent / "ex_1_removing_the_retry_arguments.py" + result = subprocess.run( + [example, "--stage", "hello"], + capture_output=True, + check=True, + text=True, + ) + for _ in [ + "--goodbye-retry-attempts", + "--goodbye-retry-delay", + "--goodbye-retry-timeout", + "--hello-retry-attempts", + "--hello-retry-delay", + "--hello-retry-timeout", + ]: + assert _ not in result.stdout + + +def test_ex_2_running_certain_stages_by_default() -> None: + example = ( + Path(__file__).parent / "ex_2_running_certain_stages_by_default.py" + ) + result = subprocess.run( + [example], + capture_output=True, + check=True, + text=True, + ) + assert_output_in_order( + result.stdout, + [ + "Greeting the user", + "Executing: echo 'Hello World'", + "`hello` stage duration", + "Bidding farewell", + "Executing: echo 'Goodbye World'", + "`goodbye` stage duration", + ], + ) + + +def test_ex_3_adding_arguments() -> None: + example = Path(__file__).parent / "ex_3_adding_arguments.py" + result = subprocess.run( + [example, "--some-file", "foo.txt"], + capture_output=True, + check=True, + text=True, + ) + assert_output_in_order( + result.stdout, + [ + "Greeting the user", + "Executing: echo 'Hello World'", + "Processing file", + "`hello` stage duration", + "Bidding farewell", + "Executing: echo 'Goodbye World'", + "Some flag as not set!", + "`goodbye` stage duration", + ], + ) + assert re.match(r"/.+/foo\.txt", result.stdout) diff --git a/pyproject.toml b/pyproject.toml index 1fdd635..bfabc27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,10 +104,12 @@ ignore = [ [tool.ruff.lint.per-file-ignores] "**/test_*.py" = ["S101"] -"example/ex_*.py" = [ +"example/*.py" = [ "D101", "D102", + "D103", "D107", + "S603", "S604", ]