Skip to content

Commit 6ad763e

Browse files
authored
Merge pull request #192 from LUMC/release_2.1.0
Release 2.1.0
2 parents 18eb79c + 77251bb commit 6ad763e

File tree

15 files changed

+129
-30
lines changed

15 files changed

+129
-30
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
strategy:
1515
matrix:
1616
python-version:
17-
- "3.7"
17+
- "3.8"
1818
steps:
1919
- uses: actions/checkout@v2.3.4
2020
- name: Set up Python ${{ matrix.python-version }}
@@ -42,11 +42,11 @@ jobs:
4242
strategy:
4343
matrix:
4444
python-version:
45-
- "3.7"
4645
- "3.8"
4746
- "3.9"
4847
- "3.10"
4948
- "3.11"
49+
- "3.12"
5050
steps:
5151
- uses: actions/checkout@v2.3.4
5252
- name: Set up Python ${{ matrix.python-version }}
@@ -64,7 +64,7 @@ jobs:
6464
runs-on: ubuntu-latest
6565
strategy:
6666
matrix:
67-
python-version: ["3.7"]
67+
python-version: ["3.11"]
6868
test-program: [snakemake, miniwdl]
6969
steps:
7070
- uses: actions/checkout@v2.3.4

.readthedocs.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: 2
2+
3+
build:
4+
os: ubuntu-22.04
5+
tools:
6+
python: "3.11"
7+
8+
sphinx:
9+
configuration: docs/conf.py
10+
11+
python:
12+
install:
13+
- requirements: requirements-docs.txt

HISTORY.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,24 @@
22
Changelog
33
==========
44

5+
56
.. Newest changes should be on top.
67
78
.. This document is user facing. Please word the changes in such a way
89
.. that users understand how the changes affect the new version.
910
11+
version 2.1.0
12+
---------------------------
13+
+ Python version 3.7 support is dropped because it is deprecated. Python
14+
version 3.12 was added.
15+
+ Fixed a bug where pytest 8.1+ would raise a ``PluginValidationError`` because
16+
the hook ``pytest_collect_file()`` has finally dropped the deprecated
17+
argument ``path`` from its specification.
18+
+ Add extract_md5sum check on uncompressed contents of compressed output files.
19+
Gzipped files contain a timestamp which makes it hard to directly compare the
20+
md5sums of gzipped files.
21+
+ Document naming conventions for Python test discovery
22+
1023
version 2.0.1
1124
---------------------------
1225
+ Fixed a bug where pytest-workflow would crash on logs that used non-ASCII

README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ predefined tests as well as custom tests are possible.
127127
- path: "TomCruise.txt.gz" # Gzipped files can also be searched, provided their extension is '.gz'
128128
contains:
129129
- "starring"
130+
extract_md5sum: e27c52f6b5f8152aa3ef58be7bdacc4d # Md5sum of the uncompressed file (optional)
130131
stderr: # Options for testing stderr (optional)
131132
contains: # A list of strings which should be in stderr (optional)
132133
- "BSOD error, please contact the IT crowd"

docs/writing_tests.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Test options
6464
- path: "TomCruise.txt.gz" # Gzipped files can also be searched, provided their extension is '.gz'
6565
contains:
6666
- "starring"
67+
extract_md5sum: e27c52f6b5f8152aa3ef58be7bdacc4d # Md5sum of the uncompressed file (optional)
6768
stderr: # Options for testing stderr (optional)
6869
contains: # A list of strings which should be in stderr (optional)
6970
- "BSOD error, please contact the IT crowd"
@@ -89,6 +90,12 @@ Please see the `Python documentation on regular expressions
8990
<https://docs.python.org/3/library/re.html>`_ to see how Python handles escape
9091
sequences.
9192

93+
The ``extract_md5sum`` option is used to uncompress a file and then compare
94+
the md5sum of the uncompressed file with the supplied md5sum. This option is
95+
particularly useful when testing gzipped files, which may contain a file
96+
creation timestamp in the gzip header. The supported compressed file
97+
formats for this option are gzip, bzip2, xz and Zstandard.
98+
9299
.. note::
93100
Workflow names must be unique. Pytest workflow will crash when multiple
94101
workflows have the same name, even if they are in different files.
@@ -160,6 +167,10 @@ Multiple workflows can use the same custom test like this:
160167
points to the folder where the named workflow was executed. This allows writing
161168
of advanced python tests for each file produced by the workflow.
162169

170+
Custom tests must follow the `conventions for Python test discovery
171+
<https://docs.pytest.org/en/latest/explanation/goodpractices.html#conventions-for-python-test-discovery>`_,
172+
which constrains the names of files and functions containing custom tests.
173+
163174
.. note::
164175

165176
stdout and stderr are available as files in the root of the

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
pyyaml
22
pytest>=7.0.0
3-
jsonschema
3+
jsonschema
4+
xopen>=1.7.0
5+
zstandard

setup.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
setup(
2222
name="pytest-workflow",
23-
version="2.0.1",
23+
version="2.1.0",
2424
description="A pytest plugin for configuring workflow/pipeline tests "
2525
"using YAML files",
2626
author="Leiden University Medical Center",
@@ -39,22 +39,24 @@
3939
classifiers=[
4040
"Programming Language :: Python :: 3 :: Only",
4141
"Programming Language :: Python :: 3",
42-
"Programming Language :: Python :: 3.7",
4342
"Programming Language :: Python :: 3.8",
4443
"Programming Language :: Python :: 3.9",
4544
"Programming Language :: Python :: 3.10",
4645
"Programming Language :: Python :: 3.11",
46+
"Programming Language :: Python :: 3.12",
4747
"Development Status :: 5 - Production/Stable",
4848
"License :: OSI Approved :: "
4949
"GNU Affero General Public License v3 or later (AGPLv3+)",
5050
"Framework :: Pytest",
5151
],
52-
# Because we cannot test anymore on Python 3.6.
53-
python_requires=">=3.7",
52+
# Because we cannot test anymore on Python 3.8.
53+
python_requires=">=3.8",
5454
install_requires=[
5555
"pytest>=7.0.0", # To use pathlib Path's in pytest
5656
"pyyaml",
57-
"jsonschema"
57+
"jsonschema",
58+
"xopen>=1.4.0",
59+
"zstandard",
5860
],
5961
# This line makes sure the plugin is automatically loaded when it is
6062
# installed in the same environment as pytest. No need to configure

src/pytest_workflow/file_tests.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from .content_tests import ContentTestCollector
2424
from .schema import FileTest
25-
from .util import file_md5sum
25+
from .util import extract_md5sum, file_md5sum
2626
from .workflow import Workflow
2727

2828

@@ -76,7 +76,16 @@ def collect(self):
7676
parent=self,
7777
filepath=filepath,
7878
md5sum=self.filetest.md5sum,
79-
workflow=self.workflow)]
79+
workflow=self.workflow,
80+
extract=False)]
81+
82+
if self.filetest.extract_md5sum:
83+
tests += [FileMd5.from_parent(
84+
parent=self,
85+
filepath=filepath,
86+
md5sum=self.filetest.extract_md5sum,
87+
workflow=self.workflow,
88+
extract=True)]
8089

8190
return tests
8291

@@ -119,32 +128,37 @@ def repr_failure(self, excinfo, style=None):
119128

120129
class FileMd5(pytest.Item):
121130
def __init__(self, parent: pytest.Collector, filepath: Path,
122-
md5sum: str, workflow: Workflow):
131+
md5sum: str, workflow: Workflow, extract: bool):
123132
"""
124133
Create a tests for the file md5sum.
125134
:param parent: The collector that started this item
126135
:param filepath: The path to the file
127136
:param md5sum: The expected md5sum
128137
:param workflow: The workflow running to generate the file
138+
:param extract: Whether the file should be extracted before calculating
129139
"""
130-
name = "md5sum"
140+
name = "extract_md5sum" if extract else "md5sum"
131141
super().__init__(name, parent)
132142
self.filepath = filepath
133143
self.expected_md5sum = md5sum
134144
self.observed_md5sum = None
135145
self.workflow = workflow
146+
self.extract = extract
136147

137148
def runtest(self):
138149
# Wait for the workflow to finish before we check the md5sum of a file.
139150
self.workflow.wait()
140151
if not self.workflow.matching_exitcode():
141152
pytest.skip(f"'{self.parent.workflow.name}' did not exit with"
142153
f"desired exit code.")
143-
self.observed_md5sum = file_md5sum(self.filepath)
154+
sum_func = extract_md5sum if self.extract else file_md5sum
155+
self.observed_md5sum = sum_func(self.filepath)
144156
assert self.observed_md5sum == self.expected_md5sum
145157

146158
def repr_failure(self, excinfo, style=None):
159+
metric = "extract_md5sum" if self.extract else "md5sum"
147160
return (
148-
f"Observed md5sum '{self.observed_md5sum}' not equal to expected "
149-
f"md5sum '{self.expected_md5sum}' for file '{self.filepath}'"
150-
)
161+
f"Observed {metric} '{self.observed_md5sum}' not equal to "
162+
f"expected {metric} '{self.expected_md5sum}' for file "
163+
f"'{self.filepath}'"
164+
)

src/pytest_workflow/plugin.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,12 @@ def addoption(self, *args, **kwargs):
117117
return parser
118118

119119

120-
def pytest_collect_file(file_path, path, parent):
120+
def pytest_collect_file(file_path, parent):
121121
"""Collection hook
122122
This collects the yaml files that start with "test" and end with
123123
.yaml or .yml"""
124-
if path.ext in [".yml", ".yaml"] and path.basename.startswith("test"):
124+
if (file_path.suffix in [".yml", ".yaml"] and
125+
file_path.name.startswith("test")):
125126
return YamlFile.from_parent(parent, path=file_path)
126127
return None
127128

src/pytest_workflow/schema.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def __init__(self, contains: Optional[List[str]] = None,
125125
class FileTest(ContentTest):
126126
"""A class that contains all the properties of a to be tested file."""
127127
def __init__(self, path: str, md5sum: Optional[str] = None,
128+
extract_md5sum: Optional[str] = None,
128129
should_exist: bool = DEFAULT_FILE_SHOULD_EXIST,
129130
contains: Optional[List[str]] = None,
130131
must_not_contain: Optional[List[str]] = None,
@@ -135,6 +136,7 @@ def __init__(self, path: str, md5sum: Optional[str] = None,
135136
A container object
136137
:param path: the path to the file
137138
:param md5sum: md5sum of the file contents
139+
:param extract_md5sum: md5sum of the extracted file contents
138140
:param should_exist: whether the file should exist or not
139141
:param contains: a list of strings that should be present in the file
140142
:param must_not_contain: a list of strings that should not be present
@@ -150,6 +152,7 @@ def __init__(self, path: str, md5sum: Optional[str] = None,
150152
encoding=encoding)
151153
self.path = Path(path)
152154
self.md5sum = md5sum
155+
self.extract_md5sum = extract_md5sum
153156
self.should_exist = should_exist
154157

155158

0 commit comments

Comments
 (0)