From 6642a176e3591f3ef8ed0c04dbcbc4a257c5b127 Mon Sep 17 00:00:00 2001 From: "Jason M. Gates" Date: Wed, 20 Sep 2023 11:48:21 -0600 Subject: [PATCH] test: Run examples in CI Create `example/test_examples.py` so `pytest` runs all the examples and checks their output. --- .github/workflows/continuous-integration.yml | 8 +- example/basic.py | 0 example/default_values.py | 0 example/post_processing.py | 0 example/pretty_printing.py | 0 example/relative_references.py | 0 example/subparsers.py | 0 example/test_examples.py | 129 +++++++++++++++++++ test/requirements.txt | 1 + 9 files changed, 134 insertions(+), 4 deletions(-) mode change 100644 => 100755 example/basic.py mode change 100644 => 100755 example/default_values.py mode change 100644 => 100755 example/post_processing.py mode change 100644 => 100755 example/pretty_printing.py mode change 100644 => 100755 example/relative_references.py mode change 100644 => 100755 example/subparsers.py create mode 100755 example/test_examples.py diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 2e4ecd1..4bdc318 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -35,17 +35,17 @@ jobs: -r example/requirements.txt \ -r test/requirements.txt + - name: Test install + run: python3 -m pip install . + - name: Test with pytest - run: python3 -m pytest --cov=reverse_argparse test/ + run: python3 -m pytest --cov=reverse_argparse example/ test/ - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - name: Test install - run: python3 -m pip install . - - name: Check documentation spelling run: make spelling working-directory: ./doc diff --git a/example/basic.py b/example/basic.py old mode 100644 new mode 100755 diff --git a/example/default_values.py b/example/default_values.py old mode 100644 new mode 100755 diff --git a/example/post_processing.py b/example/post_processing.py old mode 100644 new mode 100755 diff --git a/example/pretty_printing.py b/example/pretty_printing.py old mode 100644 new mode 100755 diff --git a/example/relative_references.py b/example/relative_references.py old mode 100644 new mode 100755 diff --git a/example/subparsers.py b/example/subparsers.py old mode 100644 new mode 100755 diff --git a/example/test_examples.py b/example/test_examples.py new file mode 100755 index 0000000..719ed55 --- /dev/null +++ b/example/test_examples.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +"""Run all the examples and ensure their output is correct.""" +import re +import shlex +import subprocess # nosec B404 +from datetime import datetime, timedelta +from pathlib import Path + + +def test_basic() -> None: + example = Path(__file__).parent / "basic.py" + result = subprocess.run( + [example, "--foo", "bar"], + capture_output=True, + check=True, + text=True, + ) # nosec B603 + assert ( + result.stdout + == """ +The effective command line invocation was: +basic.py --foo bar +""".lstrip() + ) + + +def test_default_values() -> None: + example = Path(__file__).parent / "default_values.py" + result = subprocess.run( + [example, "--foo", "bar"], + capture_output=True, + check=True, + text=True, + ) # nosec B603 + assert ( + result.stdout + == """ +The effective command line invocation was: +default_values.py --foo bar --bar spam --baz 42 +""".lstrip() + ) + + +def test_relative_references() -> None: + example = Path(__file__).parent / "relative_references.py" + result = subprocess.run( + [example, "--src", "bar.txt"], + capture_output=True, + check=True, + text=True, + ) # nosec B603 + assert ( + """ +The effective command line invocation was: +relative_references.py --bar spam --baz 42 --src +""".strip() + in result.stdout + ) + assert re.search(r"--src /\S+/bar\.txt", result.stdout) + + +def test_post_processing() -> None: + example = Path(__file__).parent / "post_processing.py" + result = subprocess.run( + [example, "--before", "'30 minutes ago'"], + capture_output=True, + check=True, + text=True, + ) # nosec B603 + assert ( + """ +The effective command line invocation was: +post_processing.py --bar spam --baz 42 --before +""".strip() + in result.stdout + ) + thirty_miutes_ago = datetime.now() - timedelta(minutes=30) + time_from_example = datetime.strptime( + shlex.split(result.stdout)[-1], "%Y-%m-%d %H:%M:%S.%f" + ) + assert thirty_miutes_ago - time_from_example < timedelta(seconds=1) + + +def test_pretty_printing() -> None: + example = Path(__file__).parent / "pretty_printing.py" + result = subprocess.run( + [example, "--foo", "eggs", "--src", "file.txt", "--before", "'today'"], + capture_output=True, + check=True, + text=True, + ) # nosec B603 + assert ( + """ +The effective command line invocation was: +pretty_printing.py \\ + --foo eggs \\ + --bar spam \\ + --baz 42 \\ +""".strip() + in result.stdout + ) + assert re.search(r"--src /\S+/file\.txt", result.stdout) + today = datetime.now() + time_from_example = datetime.strptime( + shlex.split(result.stdout.splitlines()[-1])[-1], + "%Y-%m-%d %H:%M:%S.%f", + ) + assert today - time_from_example < timedelta(seconds=1) + + +def test_subparsers() -> None: + example = Path(__file__).parent / "subparsers.py" + result = subprocess.run( + [example, "foo", "--one", "eggs"], + capture_output=True, + check=True, + text=True, + ) # nosec B603 + assert ( + result.stdout + == """ +The effective command line invocation was: +subparsers.py \\ + foo \\ + --one eggs \\ + --two spam \\ + --three 42 +""".lstrip() + ) diff --git a/test/requirements.txt b/test/requirements.txt index c92d67f..4585c8b 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -2,6 +2,7 @@ bandit black +dateparser flake8 flake8-bugbear mypy