From 29580046c8edcc93c1bc802fbd48ec8572d7c05c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 3 Nov 2021 19:15:57 -0500 Subject: [PATCH] Cover the `wwvb.gen` cli program --- .github/workflows/test.yml | 9 ++- .gitignore | 2 +- .pylintrc | 3 + Makefile | 6 +- wwvb/gen.py | 15 ++--- wwvb/testcli.py | 120 +++++++++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 17 deletions(-) create mode 100644 wwvb/testcli.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5fc42a8..bc31edd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,9 +24,6 @@ jobs: - 'pypy-3.7' - 'pypy-3.8' - env: - PYTHON: ${{ matrix.python-version }} - runs-on: ubuntu-20.04 steps: - name: Dump GitHub context @@ -46,14 +43,16 @@ jobs: - name: Check stubs if: (! startsWith(matrix.python-version, 'pypy-')) - run: mypy --strict wwvb uwwvb.py + run: make mypy - name: Test - run: python -mcoverage run --branch -m unittest && python -mcoverage xml && python -mcoverage report --fail-under=100 + run: make coverage - name: Upload Coverage to Codecov if: always() uses: codecov/codecov-action@v2 + env: + PYTHON: ${{ matrix.python-version }} with: env_vars: PYTHON fail_ci_if_error: true diff --git a/.gitignore b/.gitignore index f7bd0e7..f1388be 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ # SPDX-License-Identifier: CC0-1.0 /build -/.coverage +/.coverage* /coverage.xml /dist /*.egg-info diff --git a/.pylintrc b/.pylintrc index fbdb003..c4b10f4 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,6 +1,9 @@ #SPDX-FileCopyrightText: 2021 Jeff Epler # #SPDX-License-Identifier: GPL-3.0-only +[MASTER] +py-version=3.7 + [MESSAGES CONTROL] disable=duplicate-code,line-too-long diff --git a/Makefile b/Makefile index f3a02ba..324a384 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,11 @@ PYTHON ?= python3 COVERAGE_INCLUDE=--omit '/usr/**/*.py' .PHONY: coverage coverage: - $(PYTHON) -mcoverage run --branch -m unittest + $(PYTHON) -mcoverage erase + $(PYTHON) -mcoverage run --branch -p -m unittest + $(PYTHON) -mcoverage combine $(PYTHON) -mcoverage html $(COVERAGE_INCLUDE) - $(PYTHON) -mcoverage annotate $(COVERAGE_INCLUDE) + $(PYTHON) -mcoverage xml $(COVERAGE_INCLUDE) $(PYTHON) -mcoverage report $(COVERAGE_INCLUDE) --fail-under=100 .PHONY: mypy diff --git a/wwvb/gen.py b/wwvb/gen.py index 39156c8..a7822be 100755 --- a/wwvb/gen.py +++ b/wwvb/gen.py @@ -32,11 +32,11 @@ def parse_timespec( # pylint: disable=unused-argument if len(value) == 4: year, yday, hour, minute = map(int, value) return datetime.datetime(year, 1, 1, hour, minute) + datetime.timedelta( - days=yday + days=yday - 1 ) if len(value) == 1: return dateutil.parser.parse(value[0]) - if len(value) == 0: + if len(value) == 0: # pragma no cover return datetime.datetime.utcnow() raise ValueError("Unexpected number of arguments") except ValueError as e: @@ -120,13 +120,10 @@ def main( else: Constructor = WWVBMinute if dut1 is None: - extra_args["ut1"] = -500 * (leap_second or 0) + extra_args["newut1"] = -500 * (leap_second or 0) else: - extra_args["ut1"] = dut1 - extra_args["ls"] = bool(leap_second) - - if timespec is None: - timespec = datetime.datetime.utcnow() + extra_args["newut1"] = dut1 + extra_args["newls"] = bool(leap_second) w = Constructor.from_datetime(timespec, **extra_args) # type: ignore print_timecodes( @@ -134,5 +131,5 @@ def main( ) -if __name__ == "__main__": +if __name__ == "__main__": # pragma no branch main() # pylint: disable=no-value-for-parameter diff --git a/wwvb/testcli.py b/wwvb/testcli.py new file mode 100644 index 0000000..97225b3 --- /dev/null +++ b/wwvb/testcli.py @@ -0,0 +1,120 @@ +#!/usr/bin/python3 +"""Test most wwvblib commandline programs""" + +# Copyright (C) 2011-2020 Jeff Epler +# SPDX-FileCopyrightText: 2021 Jeff Epler +# +# SPDX-License-Identifier: GPL-3.0-only + +# pylint: disable=invalid-name + +import os +import sys +import subprocess +import unittest + +coverage_add = ( + ("-m", "coverage", "run", "--branch", "-p") if "COVERAGE_RUN" in os.environ else () +) + + +class CLITestCase(unittest.TestCase): + """Test various CLI commands within wwvbpy""" + + def assertProgramOutput(self, expected: str, *args: str) -> None: + """Check the output from invoking a program matches the expected""" + actual = subprocess.check_output( + args, stdin=subprocess.DEVNULL, encoding="utf-8" + ) + self.assertMultiLineEqual(expected, actual, "args={args}") + + def assertModuleOutput(self, expected: str, *args: str) -> None: + """Check the output from invoking a `python -m modulename` program matches the expected""" + return self.assertProgramOutput( + expected, sys.executable, *coverage_add, "-m", *args + ) + + def assertProgramError(self, *args: str) -> None: + """Check the output from invoking a program fails""" + with self.assertRaises(subprocess.SubprocessError): + subprocess.check_output( + args, + stdin=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + encoding="utf-8", + ) + + def assertModuleError(self, *args: str) -> None: + """Check the output from invoking a `python -m modulename` program fails""" + return self.assertProgramError(sys.executable, *coverage_add, "-m", *args) + + def test_gen(self) -> None: + """test wwvb.gen""" + self.assertModuleOutput( + """\ +WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-200 ly=1 ls=0 +2020-001 12:30 201100000200010001020000000002000100010200100001020000010002 +""", + "wwvb.gen", + "-m", + "1", + "2020-1-1 12:30", + ) + + self.assertModuleOutput( + """\ +WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-200 ly=1 ls=0 +2020-001 12:30 201100000200010001020000000002000100010200100001020000010002 +""", + "wwvb.gen", + "-m", + "1", + "2020", + "1", + "12", + "30", + ) + + self.assertModuleOutput( + """\ +WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-200 ly=1 ls=0 +2020-001 12:30 201100000200010001020000000002000100010200100001020000010002 +""", + "wwvb.gen", + "-m", + "1", + "2020", + "1", + "1", + "12", + "30", + ) + + self.assertModuleError("wwvb.gen", "-m", "1", "2021", "7") + + # Asserting a leap second + self.assertModuleOutput( + """\ +WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-500 ly=1 ls=1 +2020-001 12:30 201100000200010001020000000002000100010201010001020000011002 +""", + "wwvb.gen", + "-m", + "1", + "-s", + "2020-1-1 12:30", + ) + + # Asserting a different ut1 value + self.assertModuleOutput( + """\ +WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-300 ly=1 ls=0 +2020-001 12:30 201100000200010001020000000002000100010200110001020000010002 +""", + "wwvb.gen", + "-m", + "1", + "-d", + "-300", + "2020-1-1 12:30", + )