mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-29 21:03:02 +02:00
Add even more missing test coverage (#962).
This commit is contained in:
@@ -634,7 +634,7 @@ def spot_check(
|
||||
f'{log_prefix}: Paths in latest archive but not source paths: {", ".join(set(archive_paths)) or "none"}'
|
||||
)
|
||||
raise ValueError(
|
||||
f'Spot check failed: There are no source paths to compare against the archive'
|
||||
'Spot check failed: There are no source paths to compare against the archive'
|
||||
)
|
||||
|
||||
# Calculate the percentage delta between the source paths count and the archive paths count, and
|
||||
|
||||
@@ -203,7 +203,7 @@ def deduplicate_patterns(patterns):
|
||||
|
||||
for pattern in patterns:
|
||||
if pattern.type != borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||
deduplicated[pattern] = None
|
||||
deduplicated[pattern] = True
|
||||
continue
|
||||
|
||||
parents = pathlib.PurePath(pattern.path).parents
|
||||
@@ -222,7 +222,7 @@ def deduplicate_patterns(patterns):
|
||||
):
|
||||
break
|
||||
else:
|
||||
deduplicated[pattern] = None
|
||||
deduplicated[pattern] = True
|
||||
|
||||
return tuple(deduplicated.keys())
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ def make_base_create_command(
|
||||
stream_processes=None,
|
||||
):
|
||||
'''
|
||||
Given vebosity/dry-run flags, a local or remote repository path, a configuration dict, a
|
||||
Given verbosity/dry-run flags, a local or remote repository path, a configuration dict, a
|
||||
sequence of patterns as borgmatic.borg.pattern.Pattern instances, the local Borg version,
|
||||
global arguments as an argparse.Namespace instance, and a sequence of borgmatic source
|
||||
directories, return a tuple of (base Borg create command flags, Borg create command positional
|
||||
@@ -361,7 +361,7 @@ def create_archive(
|
||||
stream_processes=None,
|
||||
):
|
||||
'''
|
||||
Given vebosity/dry-run flags, a local or remote repository path, a configuration dict, a
|
||||
Given verbosity/dry-run flags, a local or remote repository path, a configuration dict, a
|
||||
sequence of loaded configuration paths, the local Borg version, and global arguments as an
|
||||
argparse.Namespace instance, create a Borg archive and return Borg's JSON output (if any).
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import enum
|
||||
|
||||
# See https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-help-patterns
|
||||
class Pattern_type(enum.Enum):
|
||||
ROOT = 'R' # A ROOT pattern type always has a NONE pattern style.
|
||||
ROOT = 'R' # A ROOT pattern always has a NONE pattern style.
|
||||
PATTERN_STYLE = 'P'
|
||||
EXCLUDE = '-'
|
||||
NO_RECURSE = '!'
|
||||
|
||||
@@ -221,6 +221,21 @@ def test_device_map_patterns_gives_device_id_per_path():
|
||||
)
|
||||
|
||||
|
||||
def test_device_map_patterns_only_considers_root_patterns():
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
|
||||
flexmock(module.os).should_receive('stat').with_args('/bar*').never()
|
||||
|
||||
device_map = module.device_map_patterns(
|
||||
(Pattern('/foo'), Pattern('/bar*', Pattern_type.INCLUDE))
|
||||
)
|
||||
|
||||
assert device_map == (
|
||||
Pattern('/foo', device=55),
|
||||
Pattern('/bar*', Pattern_type.INCLUDE),
|
||||
)
|
||||
|
||||
|
||||
def test_device_map_patterns_with_missing_path_does_not_error():
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True).and_return(False)
|
||||
flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
|
||||
@@ -328,6 +343,10 @@ def test_device_map_patterns_with_existing_device_id_does_not_overwrite_it():
|
||||
(Pattern('/', device=1), Pattern('/root', Pattern_type.INCLUDE, device=1)),
|
||||
(Pattern('/', device=1), Pattern('/root', Pattern_type.INCLUDE, device=1)),
|
||||
),
|
||||
(
|
||||
(Pattern('/root', Pattern_type.INCLUDE, device=1), Pattern('/', device=1)),
|
||||
(Pattern('/root', Pattern_type.INCLUDE, device=1), Pattern('/', device=1)),
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_deduplicate_patterns_omits_child_paths_on_the_same_filesystem(patterns, expected_patterns):
|
||||
|
||||
@@ -11,11 +11,11 @@ from ..test_verbosity import insert_logging_mock
|
||||
|
||||
def test_write_patterns_file_writes_pattern_lines():
|
||||
temporary_file = flexmock(name='filename', flush=lambda: None)
|
||||
temporary_file.should_receive('write').with_args('R /foo\n+ /foo/bar')
|
||||
temporary_file.should_receive('write').with_args('R /foo\n+ sh:/foo/bar')
|
||||
flexmock(module.tempfile).should_receive('NamedTemporaryFile').and_return(temporary_file)
|
||||
|
||||
module.write_patterns_file(
|
||||
[Pattern('/foo'), Pattern('/foo/bar', Pattern_type.INCLUDE)],
|
||||
[Pattern('/foo'), Pattern('/foo/bar', Pattern_type.INCLUDE, Pattern_style.SHELL)],
|
||||
borgmatic_runtime_directory='/run/user/0',
|
||||
log_prefix='test.yaml',
|
||||
)
|
||||
@@ -1578,6 +1578,12 @@ def test_check_all_root_patterns_exist_with_existent_pattern_path_does_not_raise
|
||||
module.check_all_root_patterns_exist([Pattern('foo')])
|
||||
|
||||
|
||||
def test_check_all_root_patterns_exist_with_non_root_pattern_skips_existence_check():
|
||||
flexmock(module.os.path).should_receive('exists').never()
|
||||
|
||||
module.check_all_root_patterns_exist([Pattern('foo', Pattern_type.INCLUDE)])
|
||||
|
||||
|
||||
def test_check_all_root_patterns_exist_with_non_existent_pattern_path_raises():
|
||||
flexmock(module.os.path).should_receive('exists').and_return(False)
|
||||
|
||||
|
||||
@@ -154,22 +154,42 @@ def test_make_snapshot_path_includes_stripped_subvolume_path(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'subvolume_path,pattern_path,expected_path',
|
||||
'subvolume_path,pattern,expected_pattern',
|
||||
(
|
||||
('/foo/bar', '/foo/bar/baz', '/foo/bar/.borgmatic-snapshot-1234/./foo/bar/baz'),
|
||||
('/foo/bar', '/foo/bar', '/foo/bar/.borgmatic-snapshot-1234/./foo/bar'),
|
||||
('/', '/foo', '/.borgmatic-snapshot-1234/./foo'),
|
||||
('/', '/', '/.borgmatic-snapshot-1234/./'),
|
||||
(
|
||||
'/foo/bar',
|
||||
Pattern('/foo/bar/baz'),
|
||||
Pattern('/foo/bar/.borgmatic-snapshot-1234/./foo/bar/baz'),
|
||||
),
|
||||
('/foo/bar', Pattern('/foo/bar'), Pattern('/foo/bar/.borgmatic-snapshot-1234/./foo/bar')),
|
||||
(
|
||||
'/foo/bar',
|
||||
Pattern('^/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
|
||||
Pattern(
|
||||
'^/foo/bar/.borgmatic-snapshot-1234/./foo/bar',
|
||||
Pattern_type.INCLUDE,
|
||||
Pattern_style.REGULAR_EXPRESSION,
|
||||
),
|
||||
),
|
||||
(
|
||||
'/foo/bar',
|
||||
Pattern('/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
|
||||
Pattern(
|
||||
'/foo/bar/.borgmatic-snapshot-1234/./foo/bar',
|
||||
Pattern_type.INCLUDE,
|
||||
Pattern_style.REGULAR_EXPRESSION,
|
||||
),
|
||||
),
|
||||
('/', Pattern('/foo'), Pattern('/.borgmatic-snapshot-1234/./foo')),
|
||||
('/', Pattern('/'), Pattern('/.borgmatic-snapshot-1234/./')),
|
||||
),
|
||||
)
|
||||
def test_make_borg_snapshot_pattern_includes_slashdot_hack_and_stripped_pattern_path(
|
||||
subvolume_path, pattern_path, expected_path
|
||||
subvolume_path, pattern, expected_pattern
|
||||
):
|
||||
flexmock(module.os).should_receive('getpid').and_return(1234)
|
||||
|
||||
assert module.make_borg_snapshot_pattern(subvolume_path, Pattern(pattern_path)) == Pattern(
|
||||
expected_path
|
||||
)
|
||||
assert module.make_borg_snapshot_pattern(subvolume_path, pattern) == expected_pattern
|
||||
|
||||
|
||||
def test_dump_data_sources_snapshots_each_subvolume_and_updates_patterns():
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.borg.pattern import Pattern
|
||||
from borgmatic.borg.pattern import Pattern, Pattern_style, Pattern_type
|
||||
from borgmatic.hooks.data_source import lvm as module
|
||||
|
||||
|
||||
@@ -133,6 +133,40 @@ def test_snapshot_logical_volume_with_non_percentage_snapshot_name_uses_lvcreate
|
||||
module.snapshot_logical_volume('lvcreate', 'snap', '/dev/snap', '10TB')
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'pattern,expected_pattern',
|
||||
(
|
||||
(
|
||||
Pattern('/foo/bar/baz'),
|
||||
Pattern('/run/borgmatic/lvm_snapshots/./foo/bar/baz'),
|
||||
),
|
||||
(Pattern('/foo/bar'), Pattern('/run/borgmatic/lvm_snapshots/./foo/bar')),
|
||||
(
|
||||
Pattern('^/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
|
||||
Pattern(
|
||||
'^/run/borgmatic/lvm_snapshots/./foo/bar',
|
||||
Pattern_type.INCLUDE,
|
||||
Pattern_style.REGULAR_EXPRESSION,
|
||||
),
|
||||
),
|
||||
(
|
||||
Pattern('/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
|
||||
Pattern(
|
||||
'/run/borgmatic/lvm_snapshots/./foo/bar',
|
||||
Pattern_type.INCLUDE,
|
||||
Pattern_style.REGULAR_EXPRESSION,
|
||||
),
|
||||
),
|
||||
(Pattern('/foo'), Pattern('/run/borgmatic/lvm_snapshots/./foo')),
|
||||
(Pattern('/'), Pattern('/run/borgmatic/lvm_snapshots/./')),
|
||||
),
|
||||
)
|
||||
def test_make_borg_snapshot_pattern_includes_slashdot_hack_and_stripped_pattern_path(
|
||||
pattern, expected_pattern
|
||||
):
|
||||
assert module.make_borg_snapshot_pattern(pattern, '/run/borgmatic') == expected_pattern
|
||||
|
||||
|
||||
def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
|
||||
config = {'lvm': {}}
|
||||
patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
|
||||
@@ -175,6 +209,12 @@ def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
|
||||
flexmock(module).should_receive('mount_snapshot').with_args(
|
||||
'mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
|
||||
).once()
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/lvolume2'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
|
||||
|
||||
assert (
|
||||
module.dump_data_sources(
|
||||
@@ -266,6 +306,12 @@ def test_dump_data_sources_uses_snapshot_size_for_snapshot():
|
||||
flexmock(module).should_receive('mount_snapshot').with_args(
|
||||
'mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
|
||||
).once()
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/lvolume2'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
|
||||
|
||||
assert (
|
||||
module.dump_data_sources(
|
||||
@@ -341,6 +387,12 @@ def test_dump_data_sources_uses_custom_commands():
|
||||
flexmock(module).should_receive('mount_snapshot').with_args(
|
||||
'/usr/local/bin/mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
|
||||
).once()
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/lvolume2'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
|
||||
|
||||
assert (
|
||||
module.dump_data_sources(
|
||||
@@ -455,6 +507,12 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
|
||||
flexmock(module).should_receive('mount_snapshot').with_args(
|
||||
'mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
|
||||
).once()
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/lvolume2'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
|
||||
|
||||
assert (
|
||||
module.dump_data_sources(
|
||||
|
||||
@@ -13,6 +13,13 @@ def test_get_contained_patterns_with_self_candidate_returns_self():
|
||||
assert candidates == {Pattern('/foo'), Pattern('/bar')}
|
||||
|
||||
|
||||
def test_get_contained_patterns_with_self_candidate_and_caret_prefix_returns_self():
|
||||
candidates = {Pattern('^/foo'), Pattern('^/mnt'), Pattern('^/bar')}
|
||||
|
||||
assert module.get_contained_patterns('/mnt', candidates) == (Pattern('^/mnt'),)
|
||||
assert candidates == {Pattern('^/foo'), Pattern('^/bar')}
|
||||
|
||||
|
||||
def test_get_contained_patterns_with_child_candidate_returns_child():
|
||||
candidates = {Pattern('/foo'), Pattern('/mnt/subdir'), Pattern('/bar')}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import os
|
||||
import pytest
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.borg.pattern import Pattern
|
||||
from borgmatic.borg.pattern import Pattern, Pattern_style, Pattern_type
|
||||
from borgmatic.hooks.data_source import zfs as module
|
||||
|
||||
|
||||
@@ -89,6 +89,40 @@ def test_get_all_dataset_mount_points_does_not_filter_datasets():
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'pattern,expected_pattern',
|
||||
(
|
||||
(
|
||||
Pattern('/foo/bar/baz'),
|
||||
Pattern('/run/borgmatic/zfs_snapshots/./foo/bar/baz'),
|
||||
),
|
||||
(Pattern('/foo/bar'), Pattern('/run/borgmatic/zfs_snapshots/./foo/bar')),
|
||||
(
|
||||
Pattern('^/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
|
||||
Pattern(
|
||||
'^/run/borgmatic/zfs_snapshots/./foo/bar',
|
||||
Pattern_type.INCLUDE,
|
||||
Pattern_style.REGULAR_EXPRESSION,
|
||||
),
|
||||
),
|
||||
(
|
||||
Pattern('/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
|
||||
Pattern(
|
||||
'/run/borgmatic/zfs_snapshots/./foo/bar',
|
||||
Pattern_type.INCLUDE,
|
||||
Pattern_style.REGULAR_EXPRESSION,
|
||||
),
|
||||
),
|
||||
(Pattern('/foo'), Pattern('/run/borgmatic/zfs_snapshots/./foo')),
|
||||
(Pattern('/'), Pattern('/run/borgmatic/zfs_snapshots/./')),
|
||||
),
|
||||
)
|
||||
def test_make_borg_snapshot_pattern_includes_slashdot_hack_and_stripped_pattern_path(
|
||||
pattern, expected_pattern
|
||||
):
|
||||
assert module.make_borg_snapshot_pattern(pattern, '/run/borgmatic') == expected_pattern
|
||||
|
||||
|
||||
def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
|
||||
flexmock(module).should_receive('get_datasets_to_backup').and_return(
|
||||
(
|
||||
@@ -111,6 +145,9 @@ def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
|
||||
full_snapshot_name,
|
||||
module.os.path.normpath(snapshot_mount_path),
|
||||
).once()
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/dataset/subdir'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/zfs_snapshots/./mnt/dataset/subdir'))
|
||||
patterns = [Pattern('/mnt/dataset/subdir')]
|
||||
|
||||
assert (
|
||||
@@ -174,6 +211,9 @@ def test_dump_data_sources_uses_custom_commands():
|
||||
full_snapshot_name,
|
||||
module.os.path.normpath(snapshot_mount_path),
|
||||
).once()
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/dataset/subdir'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/zfs_snapshots/./mnt/dataset/subdir'))
|
||||
patterns = [Pattern('/mnt/dataset/subdir')]
|
||||
hook_config = {
|
||||
'zfs_command': '/usr/local/bin/zfs',
|
||||
@@ -246,9 +286,10 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
|
||||
full_snapshot_name,
|
||||
module.os.path.normpath(snapshot_mount_path),
|
||||
).once()
|
||||
patterns = [
|
||||
Pattern('/hmm'),
|
||||
]
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
Pattern('/mnt/dataset/subdir'), '/run/borgmatic'
|
||||
).and_return(Pattern('/run/borgmatic/zfs_snapshots/./mnt/dataset/subdir'))
|
||||
patterns = [Pattern('/hmm')]
|
||||
|
||||
assert (
|
||||
module.dump_data_sources(
|
||||
|
||||
Reference in New Issue
Block a user