diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index 33d72819eb..643b79ae94 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -23,6 +23,7 @@ from hashlib import sha256 from io import BytesIO, StringIO from unittest.mock import patch +from pathlib import Path import pytest @@ -4472,6 +4473,7 @@ def test_basic_functionality(self): self.create_regular_file('file_removed', size=256) self.create_regular_file('file_removed2', size=512) self.create_regular_file('file_replaced', size=1024) + self.create_regular_file('file_touched', size=128) os.mkdir('input/dir_replaced_with_file') os.chmod('input/dir_replaced_with_file', stat.S_IFDIR | 0o755) os.mkdir('input/dir_removed') @@ -4500,6 +4502,7 @@ def test_basic_functionality(self): self.create_regular_file('file_replaced', contents=b'0' * 4096) os.unlink('input/file_removed') os.unlink('input/file_removed2') + Path('input/file_touched').touch() os.rmdir('input/dir_replaced_with_file') self.create_regular_file('dir_replaced_with_file', size=8192) os.chmod('input/dir_replaced_with_file', stat.S_IFREG | 0o755) @@ -4562,6 +4565,13 @@ def do_asserts(output, can_compare_ids, content_only=False): change = '0 B' if can_compare_ids else '{:<19}'.format('modified') self.assert_line_exists(lines, f"{change}.*input/empty") + # Show a 0 byte change for a file whose contents weren't modified + # for text output. + if content_only: + assert "input/file_touched" not in output + else: + self.assert_line_exists(lines, f"{change}.*input/file_touched") + if are_hardlinks_supported(): self.assert_line_exists(lines, f"{change}.*input/hardlink_contents_changed") if are_symlinks_supported(): @@ -4610,6 +4620,16 @@ def get_changes(filename, data): # File unchanged assert not any(get_changes('input/file_unchanged', joutput)) + # Do NOT show a 0 byte change for a file whose contents weren't + # modified for JSON output. + unexpected = {'type': 'modified', 'added': 0, 'removed': 0} + assert unexpected not in get_changes('input/file_touched', joutput) + if not content_only: + assert {"ctime", "mtime"}.issubset({c["type"] for c in get_changes('input/file_touched', joutput)}) + else: + # And if we're doing content-only, don't show the file at all. + assert not any(get_changes('input/file_touched', joutput)) + # Directory replaced with a regular file if 'BORG_TESTS_IGNORE_MODES' not in os.environ and not content_only: assert {'type': 'mode', 'old_mode': 'drwxr-xr-x', 'new_mode': '-rwxr-xr-x'} in \ diff --git a/src/borg/testsuite/conftest.py b/src/borg/testsuite/conftest.py index bc40f33b7e..4c99b822d2 100644 --- a/src/borg/testsuite/conftest.py +++ b/src/borg/testsuite/conftest.py @@ -1,6 +1,7 @@ import os import pytest +from packaging.version import parse as parse_version, Version # IMPORTANT keep this above all other borg imports to avoid inconsistent values # for `from borg.constants import PBKDF2_ITERATIONS` (or star import) usages before @@ -37,8 +38,7 @@ def clean_env(tmpdir_factory, monkeypatch): for key in keys: monkeypatch.delenv(key, raising=False) - -def pytest_report_header(config, start_path): +def _pytest_report_header(config, start_path): tests = { "BSD flags": has_lchflags, "fuse2": has_llfuse, @@ -60,6 +60,15 @@ def pytest_report_header(config, start_path): output += "Tests disabled: " + ", ".join(disabled) return output +# Avoid "Argument(s) {'start_path'} are declared in the hookimpl but can not +# be found in the hookspec" for pytest 6.2.x. +if parse_version(pytest.__version__) < Version("7.0.0"): + def pytest_report_header(config): + _pytest_report_header(config, None) +else: + def pytest_report_header(config, start_path): + _pytest_report_header(config, start_path) + class DefaultPatches: def __init__(self, request):