]> arthur.barton.de Git - bup.git/commitdiff
Add pytest collector for all test/cmd/test-* files
authorRob Browning <rlb@defaultvalue.org>
Sun, 8 Nov 2020 18:13:55 +0000 (12:13 -0600)
committerRob Browning <rlb@defaultvalue.org>
Thu, 26 Nov 2020 21:53:09 +0000 (15:53 -0600)
Add a collector for all the test/cmd tests that runs them as
individual subprocesses.  Add support for a pytest "release" mark and
apply it to the test-release-archive test.  Disable the collector
until the upcoming changes that rely on it are in place.

Signed-off-by: Rob Browning <rlb@defaultvalue.org>
pytest.ini
test/ext/conftest.py [new file with mode: 0644]

index 792be34664f4ea6df1b10e5d96cae023430e0d45..1ba300b6753d07ad055ec50f653ec5f0766241c3 100644 (file)
@@ -1,2 +1,7 @@
+
 [pytest]
+
 testpaths = test/int test/ext
+
+markers =
+    release: tests to check that the tree is ready for a release
diff --git a/test/ext/conftest.py b/test/ext/conftest.py
new file mode 100644 (file)
index 0000000..e93d6b0
--- /dev/null
@@ -0,0 +1,78 @@
+
+from subprocess import CalledProcessError
+import pytest, subprocess, sys
+
+from bup.compat import fsdecode
+from bup.io import byte_stream
+
+# Handle all test-* files as wvtest protocol subprocesses
+# cf. https://docs.pytest.org/en/latest/example/nonpython.html
+
+class BupSubprocFailure(Exception):
+    def __init__(self, msg, cmd, status, failures):
+        super(BupSubprocFailure, self).__init__(msg)
+        self.cmd = cmd
+        self.status = status
+        self.failures = failures
+
+class BupSubprocTestRunner(pytest.Item):
+
+    def __init__(self, name, parent):
+        super(BupSubprocTestRunner, self).__init__(name, parent)
+
+    def runtest(self):
+        cmd = str(self.fspath)
+        p = subprocess.Popen(cmd,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        out = p.communicate()[0]
+        sys.stdout.flush()
+        byte_stream(sys.stdout).write(out)
+        failures = [line for line in out.splitlines()
+                    if (line.startswith(b'!')
+                        and line.lower().endswith(b' failed'))]
+        if failures or p.returncode != 0:
+            raise BupSubprocFailure('%s failed (exit %d, %d failures)'
+                                    % (cmd, p.returncode, len(failures)),
+                                    cmd, p.returncode, failures)
+
+    def repr_failure(self, excinfo):
+        ex = excinfo.value
+        if isinstance(ex, BupSubprocFailure):
+            msg = ['Exit status: %d' % ex.status,
+                   'Failures:']
+            msg.extend(fsdecode(s) for s in ex.failures)
+            return '\n'.join(msg)
+
+    def reportinfo(self):
+        # This does not appear to be documented, but is in the
+        # example, and sets the final report header line (at least)
+        # for failures.
+        test_name = str(self.fspath)
+        linenum = None
+        return self.fspath, linenum, test_name
+
+class BupSubprocTestFile(pytest.File):
+    def collect(self):
+        name = self.fspath.basename
+        # name='' because there's only one test: running the command.
+        # i.e there are no sub-tests.  Otherwise the status messages
+        # duplicate the test name like this:
+        #   test/ext/test-cat-file.sh::test-cat-file.sh PASSED ...
+        try:
+            yield BupSubprocTestRunner.from_parent(self, name='')
+        except AttributeError:
+            yield BupSubprocTestRunner('', self)
+
+def pytest_collect_file(parent, path):
+    # Disabled until the upcoming changes that rely on it are in place.
+    return None
+    base = path.basename
+    if base.startswith('test-') and not base.endswith('~'):
+        try:
+            item = BupSubprocTestFile.from_parent(parent, fspath=path)
+        except AttributeError:
+            item = BupSubprocTestFile(path, parent)
+        if base == 'test-release-archive.sh':
+            item.add_marker(pytest.mark.release)
+        return item