mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-08-01 22:23:01 +02:00
Move pattern and flag functions from create.py into their own module (#1056).
Reviewed-on: https://projects.torsion.org/borgmatic-collective/borgmatic/pulls/1056 Reviewed-by: Dan Helfman <witten@torsion.org>
This commit is contained in:
@@ -8,7 +8,7 @@ import pathlib
|
||||
import random
|
||||
import shutil
|
||||
|
||||
import borgmatic.actions.create
|
||||
import borgmatic.actions.pattern
|
||||
import borgmatic.borg.check
|
||||
import borgmatic.borg.create
|
||||
import borgmatic.borg.environment
|
||||
@@ -373,8 +373,8 @@ def collect_spot_check_source_paths(
|
||||
dry_run=True,
|
||||
repository_path=repository['path'],
|
||||
config=dict(config, list_details=True),
|
||||
patterns=borgmatic.actions.create.process_patterns(
|
||||
borgmatic.actions.create.collect_patterns(config),
|
||||
patterns=borgmatic.actions.pattern.process_patterns(
|
||||
borgmatic.actions.pattern.collect_patterns(config),
|
||||
working_directory,
|
||||
),
|
||||
local_borg_version=local_borg_version,
|
||||
|
||||
+5
-260
@@ -1,272 +1,15 @@
|
||||
import glob
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
import borgmatic.actions.json
|
||||
import borgmatic.borg.create
|
||||
import borgmatic.borg.pattern
|
||||
import borgmatic.config.paths
|
||||
import borgmatic.config.validate
|
||||
import borgmatic.hooks.command
|
||||
import borgmatic.hooks.dispatch
|
||||
from borgmatic.actions import pattern
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def parse_pattern(pattern_line, default_style=borgmatic.borg.pattern.Pattern_style.NONE):
|
||||
'''
|
||||
Given a Borg pattern as a string, parse it into a borgmatic.borg.pattern.Pattern instance and
|
||||
return it.
|
||||
'''
|
||||
try:
|
||||
(pattern_type, remainder) = pattern_line.split(' ', maxsplit=1)
|
||||
except ValueError:
|
||||
raise ValueError(f'Invalid pattern: {pattern_line}')
|
||||
|
||||
try:
|
||||
(parsed_pattern_style, path) = remainder.split(':', maxsplit=1)
|
||||
pattern_style = borgmatic.borg.pattern.Pattern_style(parsed_pattern_style)
|
||||
except ValueError:
|
||||
pattern_style = default_style
|
||||
path = remainder
|
||||
|
||||
return borgmatic.borg.pattern.Pattern(
|
||||
path,
|
||||
borgmatic.borg.pattern.Pattern_type(pattern_type),
|
||||
borgmatic.borg.pattern.Pattern_style(pattern_style),
|
||||
source=borgmatic.borg.pattern.Pattern_source.CONFIG,
|
||||
)
|
||||
|
||||
|
||||
def collect_patterns(config):
|
||||
'''
|
||||
Given a configuration dict, produce a single sequence of patterns comprised of the configured
|
||||
source directories, patterns, excludes, pattern files, and exclude files.
|
||||
|
||||
The idea is that Borg has all these different ways of specifying includes, excludes, source
|
||||
directories, etc., but we'd like to collapse them all down to one common format (patterns) for
|
||||
ease of manipulation within borgmatic.
|
||||
'''
|
||||
try:
|
||||
return (
|
||||
tuple(
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
source_directory, source=borgmatic.borg.pattern.Pattern_source.CONFIG
|
||||
)
|
||||
for source_directory in config.get('source_directories', ())
|
||||
)
|
||||
+ tuple(
|
||||
parse_pattern(pattern_line.strip())
|
||||
for pattern_line in config.get('patterns', ())
|
||||
if not pattern_line.lstrip().startswith('#')
|
||||
if pattern_line.strip()
|
||||
)
|
||||
+ tuple(
|
||||
parse_pattern(
|
||||
f'{borgmatic.borg.pattern.Pattern_type.NO_RECURSE.value} {exclude_line.strip()}',
|
||||
borgmatic.borg.pattern.Pattern_style.FNMATCH,
|
||||
)
|
||||
for exclude_line in config.get('exclude_patterns', ())
|
||||
)
|
||||
+ tuple(
|
||||
parse_pattern(pattern_line.strip())
|
||||
for filename in config.get('patterns_from', ())
|
||||
for pattern_line in open(filename).readlines()
|
||||
if not pattern_line.lstrip().startswith('#')
|
||||
if pattern_line.strip()
|
||||
)
|
||||
+ tuple(
|
||||
parse_pattern(
|
||||
f'{borgmatic.borg.pattern.Pattern_type.NO_RECURSE.value} {exclude_line.strip()}',
|
||||
borgmatic.borg.pattern.Pattern_style.FNMATCH,
|
||||
)
|
||||
for filename in config.get('exclude_from', ())
|
||||
for exclude_line in open(filename).readlines()
|
||||
if not exclude_line.lstrip().startswith('#')
|
||||
if exclude_line.strip()
|
||||
)
|
||||
)
|
||||
except (FileNotFoundError, OSError) as error:
|
||||
logger.debug(error)
|
||||
|
||||
raise ValueError(f'Cannot read patterns_from/exclude_from file: {error.filename}')
|
||||
|
||||
|
||||
def expand_directory(directory, working_directory):
|
||||
'''
|
||||
Given a directory path, expand any tilde (representing a user's home directory) and any globs
|
||||
therein. Return a list of one or more resulting paths.
|
||||
|
||||
Take into account the given working directory so that relative paths are supported.
|
||||
'''
|
||||
expanded_directory = os.path.expanduser(directory)
|
||||
|
||||
# This would be a lot easier to do with glob(..., root_dir=working_directory), but root_dir is
|
||||
# only available in Python 3.10+.
|
||||
normalized_directory = os.path.join(working_directory or '', expanded_directory)
|
||||
glob_paths = glob.glob(normalized_directory)
|
||||
|
||||
if not glob_paths:
|
||||
return [expanded_directory]
|
||||
|
||||
working_directory_prefix = os.path.join(working_directory or '', '')
|
||||
|
||||
return [
|
||||
(
|
||||
glob_path
|
||||
# If these are equal, that means we didn't add any working directory prefix above.
|
||||
if normalized_directory == expanded_directory
|
||||
# Remove the working directory prefix that we added above in order to make glob() work.
|
||||
# We can't use os.path.relpath() here because it collapses any use of Borg's slashdot
|
||||
# hack.
|
||||
else glob_path.removeprefix(working_directory_prefix)
|
||||
)
|
||||
for glob_path in glob_paths
|
||||
]
|
||||
|
||||
|
||||
def expand_patterns(patterns, working_directory=None, skip_paths=None):
|
||||
'''
|
||||
Given a sequence of borgmatic.borg.pattern.Pattern instances and an optional working directory,
|
||||
expand tildes and globs in each root pattern and expand just tildes in each non-root pattern.
|
||||
The idea is that non-root patterns may be regular expressions or other pattern styles containing
|
||||
"*" that borgmatic should not expand as a shell glob.
|
||||
|
||||
Return all the resulting patterns as a tuple.
|
||||
|
||||
If a set of paths are given to skip, then don't expand any patterns matching them.
|
||||
'''
|
||||
if patterns is None:
|
||||
return ()
|
||||
|
||||
return tuple(
|
||||
itertools.chain.from_iterable(
|
||||
(
|
||||
(
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
expanded_path,
|
||||
pattern.type,
|
||||
pattern.style,
|
||||
pattern.device,
|
||||
pattern.source,
|
||||
)
|
||||
for expanded_path in expand_directory(pattern.path, working_directory)
|
||||
)
|
||||
if pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
||||
and pattern.path not in (skip_paths or ())
|
||||
else (
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
os.path.expanduser(pattern.path),
|
||||
pattern.type,
|
||||
pattern.style,
|
||||
pattern.device,
|
||||
pattern.source,
|
||||
),
|
||||
)
|
||||
)
|
||||
for pattern in patterns
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def device_map_patterns(patterns, working_directory=None):
|
||||
'''
|
||||
Given a sequence of borgmatic.borg.pattern.Pattern instances and an optional working directory,
|
||||
determine the identifier for the device on which the pattern's path resides—or None if the path
|
||||
doesn't exist or is from a non-root pattern. Return an updated sequence of patterns with the
|
||||
device field populated. But if the device field is already set, don't bother setting it again.
|
||||
|
||||
This is handy for determining whether two different pattern paths are on the same filesystem
|
||||
(have the same device identifier).
|
||||
'''
|
||||
return tuple(
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
pattern.path,
|
||||
pattern.type,
|
||||
pattern.style,
|
||||
device=pattern.device
|
||||
or (
|
||||
os.stat(full_path).st_dev
|
||||
if pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
||||
and os.path.exists(full_path)
|
||||
else None
|
||||
),
|
||||
source=pattern.source,
|
||||
)
|
||||
for pattern in patterns
|
||||
for full_path in (os.path.join(working_directory or '', pattern.path),)
|
||||
)
|
||||
|
||||
|
||||
def deduplicate_patterns(patterns):
|
||||
'''
|
||||
Given a sequence of borgmatic.borg.pattern.Pattern instances, return them with all duplicate
|
||||
root child patterns removed. For instance, if two root patterns are given with paths "/foo" and
|
||||
"/foo/bar", return just the one with "/foo". Non-root patterns are passed through without
|
||||
modification.
|
||||
|
||||
The one exception to deduplication is two paths are on different filesystems (devices). In that
|
||||
case, they won't get deduplicated, in case they both need to be passed to Borg (e.g. the
|
||||
one_file_system option is true).
|
||||
|
||||
The idea is that if Borg is given a root parent pattern, then it doesn't also need to be given
|
||||
child patterns, because it will naturally spider the contents of the parent pattern's path. And
|
||||
there are cases where Borg coming across the same file twice will result in duplicate reads and
|
||||
even hangs, e.g. when a database hook is using a named pipe for streaming database dumps to
|
||||
Borg.
|
||||
'''
|
||||
deduplicated = {} # Use just the keys as an ordered set.
|
||||
|
||||
for pattern in patterns:
|
||||
if pattern.type != borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||
deduplicated[pattern] = True
|
||||
continue
|
||||
|
||||
parents = pathlib.PurePath(pattern.path).parents
|
||||
|
||||
# If another directory in the given list is a parent of current directory (even n levels up)
|
||||
# and both are on the same filesystem, then the current directory is a duplicate.
|
||||
for other_pattern in patterns:
|
||||
if other_pattern.type != borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||
continue
|
||||
|
||||
if any(
|
||||
pathlib.PurePath(other_pattern.path) == parent
|
||||
and pattern.device is not None
|
||||
and other_pattern.device == pattern.device
|
||||
for parent in parents
|
||||
):
|
||||
break
|
||||
else:
|
||||
deduplicated[pattern] = True
|
||||
|
||||
return tuple(deduplicated.keys())
|
||||
|
||||
|
||||
def process_patterns(patterns, working_directory, skip_expand_paths=None):
|
||||
'''
|
||||
Given a sequence of Borg patterns and a configured working directory, expand and deduplicate any
|
||||
"root" patterns, returning the resulting root and non-root patterns as a list.
|
||||
|
||||
If any paths are given to skip, don't expand them.
|
||||
'''
|
||||
skip_paths = set(skip_expand_paths or ())
|
||||
|
||||
return list(
|
||||
deduplicate_patterns(
|
||||
device_map_patterns(
|
||||
expand_patterns(
|
||||
patterns,
|
||||
working_directory=working_directory,
|
||||
skip_paths=skip_paths,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def run_create(
|
||||
config_filename,
|
||||
repository,
|
||||
@@ -310,7 +53,7 @@ def run_create(
|
||||
borgmatic_runtime_directory,
|
||||
global_arguments.dry_run,
|
||||
)
|
||||
patterns = process_patterns(collect_patterns(config), working_directory)
|
||||
patterns = pattern.process_patterns(pattern.collect_patterns(config), working_directory)
|
||||
active_dumps = borgmatic.hooks.dispatch.call_hooks(
|
||||
'dump_data_sources',
|
||||
config,
|
||||
@@ -324,7 +67,9 @@ def run_create(
|
||||
# Process the patterns again in case any data source hooks updated them. Without this step,
|
||||
# we could end up with duplicate paths that cause Borg to hang when it tries to read from
|
||||
# the same named pipe twice.
|
||||
patterns = process_patterns(patterns, working_directory, skip_expand_paths=config_paths)
|
||||
patterns = pattern.process_patterns(
|
||||
patterns, working_directory, skip_expand_paths=config_paths
|
||||
)
|
||||
stream_processes = [process for processes in active_dumps.values() for process in processes]
|
||||
|
||||
json_output = borgmatic.borg.create.create_archive(
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
import glob
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
import borgmatic.borg.pattern
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def parse_pattern(pattern_line, default_style=borgmatic.borg.pattern.Pattern_style.NONE):
|
||||
'''
|
||||
Given a Borg pattern as a string, parse it into a borgmatic.borg.pattern.Pattern instance and
|
||||
return it.
|
||||
'''
|
||||
try:
|
||||
(pattern_type, remainder) = pattern_line.split(' ', maxsplit=1)
|
||||
except ValueError:
|
||||
raise ValueError(f'Invalid pattern: {pattern_line}')
|
||||
|
||||
try:
|
||||
(parsed_pattern_style, path) = remainder.split(':', maxsplit=1)
|
||||
pattern_style = borgmatic.borg.pattern.Pattern_style(parsed_pattern_style)
|
||||
except ValueError:
|
||||
pattern_style = default_style
|
||||
path = remainder
|
||||
|
||||
return borgmatic.borg.pattern.Pattern(
|
||||
path,
|
||||
borgmatic.borg.pattern.Pattern_type(pattern_type),
|
||||
borgmatic.borg.pattern.Pattern_style(pattern_style),
|
||||
source=borgmatic.borg.pattern.Pattern_source.CONFIG,
|
||||
)
|
||||
|
||||
|
||||
def collect_patterns(config):
|
||||
'''
|
||||
Given a configuration dict, produce a single sequence of patterns comprised of the configured
|
||||
source directories, patterns, excludes, pattern files, and exclude files.
|
||||
|
||||
The idea is that Borg has all these different ways of specifying includes, excludes, source
|
||||
directories, etc., but we'd like to collapse them all down to one common format (patterns) for
|
||||
ease of manipulation within borgmatic.
|
||||
'''
|
||||
try:
|
||||
return (
|
||||
tuple(
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
source_directory, source=borgmatic.borg.pattern.Pattern_source.CONFIG
|
||||
)
|
||||
for source_directory in config.get('source_directories', ())
|
||||
)
|
||||
+ tuple(
|
||||
parse_pattern(pattern_line.strip())
|
||||
for pattern_line in config.get('patterns', ())
|
||||
if not pattern_line.lstrip().startswith('#')
|
||||
if pattern_line.strip()
|
||||
)
|
||||
+ tuple(
|
||||
parse_pattern(
|
||||
f'{borgmatic.borg.pattern.Pattern_type.NO_RECURSE.value} {exclude_line.strip()}',
|
||||
borgmatic.borg.pattern.Pattern_style.FNMATCH,
|
||||
)
|
||||
for exclude_line in config.get('exclude_patterns', ())
|
||||
)
|
||||
+ tuple(
|
||||
parse_pattern(pattern_line.strip())
|
||||
for filename in config.get('patterns_from', ())
|
||||
for pattern_line in open(filename).readlines()
|
||||
if not pattern_line.lstrip().startswith('#')
|
||||
if pattern_line.strip()
|
||||
)
|
||||
+ tuple(
|
||||
parse_pattern(
|
||||
f'{borgmatic.borg.pattern.Pattern_type.NO_RECURSE.value} {exclude_line.strip()}',
|
||||
borgmatic.borg.pattern.Pattern_style.FNMATCH,
|
||||
)
|
||||
for filename in config.get('exclude_from', ())
|
||||
for exclude_line in open(filename).readlines()
|
||||
if not exclude_line.lstrip().startswith('#')
|
||||
if exclude_line.strip()
|
||||
)
|
||||
)
|
||||
except (FileNotFoundError, OSError) as error:
|
||||
logger.debug(error)
|
||||
|
||||
raise ValueError(f'Cannot read patterns_from/exclude_from file: {error.filename}')
|
||||
|
||||
|
||||
def expand_directory(directory, working_directory):
|
||||
'''
|
||||
Given a directory path, expand any tilde (representing a user's home directory) and any globs
|
||||
therein. Return a list of one or more resulting paths.
|
||||
|
||||
Take into account the given working directory so that relative paths are supported.
|
||||
'''
|
||||
expanded_directory = os.path.expanduser(directory)
|
||||
|
||||
# This would be a lot easier to do with glob(..., root_dir=working_directory), but root_dir is
|
||||
# only available in Python 3.10+.
|
||||
normalized_directory = os.path.join(working_directory or '', expanded_directory)
|
||||
glob_paths = glob.glob(normalized_directory)
|
||||
|
||||
if not glob_paths:
|
||||
return [expanded_directory]
|
||||
|
||||
working_directory_prefix = os.path.join(working_directory or '', '')
|
||||
|
||||
return [
|
||||
(
|
||||
glob_path
|
||||
# If these are equal, that means we didn't add any working directory prefix above.
|
||||
if normalized_directory == expanded_directory
|
||||
# Remove the working directory prefix that we added above in order to make glob() work.
|
||||
# We can't use os.path.relpath() here because it collapses any use of Borg's slashdot
|
||||
# hack.
|
||||
else glob_path.removeprefix(working_directory_prefix)
|
||||
)
|
||||
for glob_path in glob_paths
|
||||
]
|
||||
|
||||
|
||||
def expand_patterns(patterns, working_directory=None, skip_paths=None):
|
||||
'''
|
||||
Given a sequence of borgmatic.borg.pattern.Pattern instances and an optional working directory,
|
||||
expand tildes and globs in each root pattern and expand just tildes in each non-root pattern.
|
||||
The idea is that non-root patterns may be regular expressions or other pattern styles containing
|
||||
"*" that borgmatic should not expand as a shell glob.
|
||||
|
||||
Return all the resulting patterns as a tuple.
|
||||
|
||||
If a set of paths are given to skip, then don't expand any patterns matching them.
|
||||
'''
|
||||
if patterns is None:
|
||||
return ()
|
||||
|
||||
return tuple(
|
||||
itertools.chain.from_iterable(
|
||||
(
|
||||
(
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
expanded_path,
|
||||
pattern.type,
|
||||
pattern.style,
|
||||
pattern.device,
|
||||
pattern.source,
|
||||
)
|
||||
for expanded_path in expand_directory(pattern.path, working_directory)
|
||||
)
|
||||
if pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
||||
and pattern.path not in (skip_paths or ())
|
||||
else (
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
os.path.expanduser(pattern.path),
|
||||
pattern.type,
|
||||
pattern.style,
|
||||
pattern.device,
|
||||
pattern.source,
|
||||
),
|
||||
)
|
||||
)
|
||||
for pattern in patterns
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def device_map_patterns(patterns, working_directory=None):
|
||||
'''
|
||||
Given a sequence of borgmatic.borg.pattern.Pattern instances and an optional working directory,
|
||||
determine the identifier for the device on which the pattern's path resides—or None if the path
|
||||
doesn't exist or is from a non-root pattern. Return an updated sequence of patterns with the
|
||||
device field populated. But if the device field is already set, don't bother setting it again.
|
||||
|
||||
This is handy for determining whether two different pattern paths are on the same filesystem
|
||||
(have the same device identifier).
|
||||
'''
|
||||
return tuple(
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
pattern.path,
|
||||
pattern.type,
|
||||
pattern.style,
|
||||
device=pattern.device
|
||||
or (
|
||||
os.stat(full_path).st_dev
|
||||
if pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
||||
and os.path.exists(full_path)
|
||||
else None
|
||||
),
|
||||
source=pattern.source,
|
||||
)
|
||||
for pattern in patterns
|
||||
for full_path in (os.path.join(working_directory or '', pattern.path),)
|
||||
)
|
||||
|
||||
|
||||
def deduplicate_patterns(patterns):
|
||||
'''
|
||||
Given a sequence of borgmatic.borg.pattern.Pattern instances, return them with all duplicate
|
||||
root child patterns removed. For instance, if two root patterns are given with paths "/foo" and
|
||||
"/foo/bar", return just the one with "/foo". Non-root patterns are passed through without
|
||||
modification.
|
||||
|
||||
The one exception to deduplication is two paths are on different filesystems (devices). In that
|
||||
case, they won't get deduplicated, in case they both need to be passed to Borg (e.g. the
|
||||
one_file_system option is true).
|
||||
|
||||
The idea is that if Borg is given a root parent pattern, then it doesn't also need to be given
|
||||
child patterns, because it will naturally spider the contents of the parent pattern's path. And
|
||||
there are cases where Borg coming across the same file twice will result in duplicate reads and
|
||||
even hangs, e.g. when a database hook is using a named pipe for streaming database dumps to
|
||||
Borg.
|
||||
'''
|
||||
deduplicated = {} # Use just the keys as an ordered set.
|
||||
|
||||
for pattern in patterns:
|
||||
if pattern.type != borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||
deduplicated[pattern] = True
|
||||
continue
|
||||
|
||||
parents = pathlib.PurePath(pattern.path).parents
|
||||
|
||||
# If another directory in the given list is a parent of current directory (even n levels up)
|
||||
# and both are on the same filesystem, then the current directory is a duplicate.
|
||||
for other_pattern in patterns:
|
||||
if other_pattern.type != borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||
continue
|
||||
|
||||
if any(
|
||||
pathlib.PurePath(other_pattern.path) == parent
|
||||
and pattern.device is not None
|
||||
and other_pattern.device == pattern.device
|
||||
for parent in parents
|
||||
):
|
||||
break
|
||||
else:
|
||||
deduplicated[pattern] = True
|
||||
|
||||
return tuple(deduplicated.keys())
|
||||
|
||||
|
||||
def process_patterns(patterns, working_directory, skip_expand_paths=None):
|
||||
'''
|
||||
Given a sequence of Borg patterns and a configured working directory, expand and deduplicate any
|
||||
"root" patterns, returning the resulting root and non-root patterns as a list.
|
||||
|
||||
If any paths are given to skip, don't expand them.
|
||||
'''
|
||||
skip_paths = set(skip_expand_paths or ())
|
||||
|
||||
return list(
|
||||
deduplicate_patterns(
|
||||
device_map_patterns(
|
||||
expand_patterns(
|
||||
patterns,
|
||||
working_directory=working_directory,
|
||||
skip_paths=skip_paths,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -2,7 +2,7 @@ import logging
|
||||
|
||||
import borgmatic.borg.recreate
|
||||
import borgmatic.config.validate
|
||||
from borgmatic.actions.create import collect_patterns, process_patterns
|
||||
from borgmatic.actions.pattern import collect_patterns, process_patterns
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import stat
|
||||
import tempfile
|
||||
import textwrap
|
||||
|
||||
import borgmatic.borg.pattern
|
||||
@@ -20,76 +18,6 @@ from borgmatic.execute import (
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def write_patterns_file(patterns, borgmatic_runtime_directory, patterns_file=None):
|
||||
'''
|
||||
Given a sequence of patterns as borgmatic.borg.pattern.Pattern instances, write them to a named
|
||||
temporary file in the given borgmatic runtime directory and return the file object so it can
|
||||
continue to exist on disk as long as the caller needs it.
|
||||
|
||||
If an optional open pattern file is given, append to it instead of making a new temporary file.
|
||||
Return None if no patterns are provided.
|
||||
'''
|
||||
if not patterns:
|
||||
return None
|
||||
|
||||
if patterns_file is None:
|
||||
patterns_file = tempfile.NamedTemporaryFile('w', dir=borgmatic_runtime_directory)
|
||||
operation_name = 'Writing'
|
||||
else:
|
||||
patterns_file.write('\n')
|
||||
operation_name = 'Appending'
|
||||
|
||||
patterns_output = '\n'.join(
|
||||
f'{pattern.type.value} {pattern.style.value}{":" if pattern.style.value else ""}{pattern.path}'
|
||||
for pattern in patterns
|
||||
)
|
||||
logger.debug(f'{operation_name} patterns to {patterns_file.name}:\n{patterns_output}')
|
||||
|
||||
patterns_file.write(patterns_output)
|
||||
patterns_file.flush()
|
||||
|
||||
return patterns_file
|
||||
|
||||
|
||||
def make_exclude_flags(config):
|
||||
'''
|
||||
Given a configuration dict with various exclude options, return the corresponding Borg flags as
|
||||
a tuple.
|
||||
'''
|
||||
caches_flag = ('--exclude-caches',) if config.get('exclude_caches') else ()
|
||||
if_present_flags = tuple(
|
||||
itertools.chain.from_iterable(
|
||||
('--exclude-if-present', if_present)
|
||||
for if_present in config.get('exclude_if_present', ())
|
||||
)
|
||||
)
|
||||
keep_exclude_tags_flags = ('--keep-exclude-tags',) if config.get('keep_exclude_tags') else ()
|
||||
exclude_nodump_flags = ('--exclude-nodump',) if config.get('exclude_nodump') else ()
|
||||
|
||||
return caches_flag + if_present_flags + keep_exclude_tags_flags + exclude_nodump_flags
|
||||
|
||||
|
||||
def make_list_filter_flags(local_borg_version, dry_run):
|
||||
'''
|
||||
Given the local Borg version and whether this is a dry run, return the corresponding flags for
|
||||
passing to "--list --filter". The general idea is that excludes are shown for a dry run or when
|
||||
the verbosity is debug.
|
||||
'''
|
||||
base_flags = 'AME'
|
||||
show_excludes = logger.isEnabledFor(logging.DEBUG)
|
||||
|
||||
if feature.available(feature.Feature.EXCLUDED_FILES_MINUS, local_borg_version):
|
||||
if show_excludes or dry_run:
|
||||
return f'{base_flags}+-'
|
||||
else:
|
||||
return base_flags
|
||||
|
||||
if show_excludes:
|
||||
return f'{base_flags}x-'
|
||||
else:
|
||||
return f'{base_flags}-'
|
||||
|
||||
|
||||
def special_file(path, working_directory=None):
|
||||
'''
|
||||
Return whether the given path is a special file (character device, block device, or named pipe
|
||||
@@ -182,24 +110,6 @@ def collect_special_file_paths(
|
||||
)
|
||||
|
||||
|
||||
def check_all_root_patterns_exist(patterns):
|
||||
'''
|
||||
Given a sequence of borgmatic.borg.pattern.Pattern instances, check that all root pattern
|
||||
paths exist. If any don't, raise an exception.
|
||||
'''
|
||||
missing_paths = [
|
||||
pattern.path
|
||||
for pattern in patterns
|
||||
if pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
||||
if not os.path.exists(pattern.path)
|
||||
]
|
||||
|
||||
if missing_paths:
|
||||
raise ValueError(
|
||||
f"Source directories or root pattern paths do not exist: {', '.join(missing_paths)}"
|
||||
)
|
||||
|
||||
|
||||
MAX_SPECIAL_FILE_PATHS_LENGTH = 1000
|
||||
|
||||
|
||||
@@ -224,9 +134,11 @@ def make_base_create_command(
|
||||
arguments, open pattern file handle).
|
||||
'''
|
||||
if config.get('source_directories_must_exist', False):
|
||||
check_all_root_patterns_exist(patterns)
|
||||
borgmatic.borg.pattern.check_all_root_patterns_exist(patterns)
|
||||
|
||||
patterns_file = write_patterns_file(patterns, borgmatic_runtime_directory)
|
||||
patterns_file = borgmatic.borg.pattern.write_patterns_file(
|
||||
patterns, borgmatic_runtime_directory
|
||||
)
|
||||
checkpoint_interval = config.get('checkpoint_interval', None)
|
||||
checkpoint_volume = config.get('checkpoint_volume', None)
|
||||
chunker_params = config.get('chunker_params', None)
|
||||
@@ -235,7 +147,7 @@ def make_base_create_command(
|
||||
upload_buffer_size = config.get('upload_buffer_size', None)
|
||||
umask = config.get('umask', None)
|
||||
lock_wait = config.get('lock_wait', None)
|
||||
list_filter_flags = make_list_filter_flags(local_borg_version, dry_run)
|
||||
list_filter_flags = flags.make_list_filter_flags(local_borg_version, dry_run)
|
||||
files_cache = config.get('files_cache')
|
||||
archive_name_format = config.get(
|
||||
'archive_name_format', flags.get_default_archive_name_format(local_borg_version)
|
||||
@@ -270,7 +182,7 @@ def make_base_create_command(
|
||||
tuple(local_path.split(' '))
|
||||
+ ('create',)
|
||||
+ (('--patterns-from', patterns_file.name) if patterns_file else ())
|
||||
+ make_exclude_flags(config)
|
||||
+ flags.make_exclude_flags(config)
|
||||
+ (('--checkpoint-interval', str(checkpoint_interval)) if checkpoint_interval else ())
|
||||
+ (('--checkpoint-volume', str(checkpoint_volume)) if checkpoint_volume else ())
|
||||
+ (('--chunker-params', chunker_params) if chunker_params else ())
|
||||
@@ -329,7 +241,7 @@ def make_base_create_command(
|
||||
logger.warning(
|
||||
f'Excluding special files to prevent Borg from hanging: {truncated_special_file_paths}'
|
||||
)
|
||||
patterns_file = write_patterns_file(
|
||||
patterns_file = borgmatic.borg.pattern.write_patterns_file(
|
||||
tuple(
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
special_file_path,
|
||||
|
||||
@@ -197,3 +197,42 @@ def omit_flag_and_value(arguments, flag):
|
||||
if flag not in (previous_argument, argument)
|
||||
if not argument.startswith(f'{flag}=')
|
||||
)
|
||||
|
||||
|
||||
def make_exclude_flags(config):
|
||||
'''
|
||||
Given a configuration dict with various exclude options, return the corresponding Borg flags as
|
||||
a tuple.
|
||||
'''
|
||||
caches_flag = ('--exclude-caches',) if config.get('exclude_caches') else ()
|
||||
if_present_flags = tuple(
|
||||
itertools.chain.from_iterable(
|
||||
('--exclude-if-present', if_present)
|
||||
for if_present in config.get('exclude_if_present', ())
|
||||
)
|
||||
)
|
||||
keep_exclude_tags_flags = ('--keep-exclude-tags',) if config.get('keep_exclude_tags') else ()
|
||||
exclude_nodump_flags = ('--exclude-nodump',) if config.get('exclude_nodump') else ()
|
||||
|
||||
return caches_flag + if_present_flags + keep_exclude_tags_flags + exclude_nodump_flags
|
||||
|
||||
|
||||
def make_list_filter_flags(local_borg_version, dry_run):
|
||||
'''
|
||||
Given the local Borg version and whether this is a dry run, return the corresponding flags for
|
||||
passing to "--list --filter". The general idea is that excludes are shown for a dry run or when
|
||||
the verbosity is debug.
|
||||
'''
|
||||
base_flags = 'AME'
|
||||
show_excludes = logger.isEnabledFor(logging.DEBUG)
|
||||
|
||||
if feature.available(feature.Feature.EXCLUDED_FILES_MINUS, local_borg_version):
|
||||
if show_excludes or dry_run:
|
||||
return f'{base_flags}+-'
|
||||
else:
|
||||
return base_flags
|
||||
|
||||
if show_excludes:
|
||||
return f'{base_flags}x-'
|
||||
else:
|
||||
return f'{base_flags}-'
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import collections
|
||||
import enum
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import borgmatic.borg.pattern
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# See https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-help-patterns
|
||||
@@ -48,3 +55,52 @@ Pattern = collections.namedtuple(
|
||||
Pattern_source.HOOK,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def write_patterns_file(patterns, borgmatic_runtime_directory, patterns_file=None):
|
||||
'''
|
||||
Given a sequence of patterns as borgmatic.borg.pattern.Pattern instances, write them to a named
|
||||
temporary file in the given borgmatic runtime directory and return the file object so it can
|
||||
continue to exist on disk as long as the caller needs it.
|
||||
|
||||
If an optional open pattern file is given, append to it instead of making a new temporary file.
|
||||
Return None if no patterns are provided.
|
||||
'''
|
||||
if not patterns:
|
||||
return None
|
||||
|
||||
if patterns_file is None:
|
||||
patterns_file = tempfile.NamedTemporaryFile('w', dir=borgmatic_runtime_directory)
|
||||
operation_name = 'Writing'
|
||||
else:
|
||||
patterns_file.write('\n')
|
||||
operation_name = 'Appending'
|
||||
|
||||
patterns_output = '\n'.join(
|
||||
f'{pattern.type.value} {pattern.style.value}{":" if pattern.style.value else ""}{pattern.path}'
|
||||
for pattern in patterns
|
||||
)
|
||||
logger.debug(f'{operation_name} patterns to {patterns_file.name}:\n{patterns_output}')
|
||||
|
||||
patterns_file.write(patterns_output)
|
||||
patterns_file.flush()
|
||||
|
||||
return patterns_file
|
||||
|
||||
|
||||
def check_all_root_patterns_exist(patterns):
|
||||
'''
|
||||
Given a sequence of borgmatic.borg.pattern.Pattern instances, check that all root pattern
|
||||
paths exist. If any don't, raise an exception.
|
||||
'''
|
||||
missing_paths = [
|
||||
pattern.path
|
||||
for pattern in patterns
|
||||
if pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
||||
if not os.path.exists(pattern.path)
|
||||
]
|
||||
|
||||
if missing_paths:
|
||||
raise ValueError(
|
||||
f"Source directories or root pattern paths do not exist: {', '.join(missing_paths)}"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ import borgmatic.borg.feature
|
||||
import borgmatic.config.paths
|
||||
import borgmatic.execute
|
||||
from borgmatic.borg import flags
|
||||
from borgmatic.borg.create import make_exclude_flags, make_list_filter_flags, write_patterns_file
|
||||
from borgmatic.borg.pattern import write_patterns_file
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -29,7 +29,7 @@ def recreate_archive(
|
||||
arguments.
|
||||
'''
|
||||
lock_wait = config.get('lock_wait', None)
|
||||
exclude_flags = make_exclude_flags(config)
|
||||
exclude_flags = flags.make_exclude_flags(config)
|
||||
compression = config.get('compression', None)
|
||||
chunker_params = config.get('chunker_params', None)
|
||||
# Available recompress MODES: "if-different", "always", "never" (default)
|
||||
@@ -52,7 +52,7 @@ def recreate_archive(
|
||||
(
|
||||
'--list',
|
||||
'--filter',
|
||||
make_list_filter_flags(local_borg_version, global_arguments.dry_run),
|
||||
flags.make_list_filter_flags(local_borg_version, global_arguments.dry_run),
|
||||
)
|
||||
if config.get('list_details')
|
||||
else ()
|
||||
|
||||
@@ -923,6 +923,7 @@ properties:
|
||||
- compact
|
||||
- create
|
||||
- recreate
|
||||
- pattern
|
||||
- check
|
||||
- delete
|
||||
- extract
|
||||
|
||||
@@ -561,10 +561,10 @@ def test_collect_spot_check_source_paths_parses_borg_output():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('collect_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('process_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(
|
||||
[Pattern('foo'), Pattern('bar')]
|
||||
)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_base_create_command').with_args(
|
||||
@@ -608,10 +608,10 @@ def test_collect_spot_check_source_paths_passes_through_stream_processes_false()
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('collect_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('process_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(
|
||||
[Pattern('foo'), Pattern('bar')]
|
||||
)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_base_create_command').with_args(
|
||||
@@ -655,10 +655,10 @@ def test_collect_spot_check_source_paths_without_working_directory_parses_borg_o
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('collect_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('process_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(
|
||||
[Pattern('foo'), Pattern('bar')]
|
||||
)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_base_create_command').with_args(
|
||||
@@ -702,10 +702,10 @@ def test_collect_spot_check_source_paths_skips_directories():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('collect_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('process_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(
|
||||
[Pattern('foo'), Pattern('bar')]
|
||||
)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_base_create_command').with_args(
|
||||
@@ -847,10 +847,10 @@ def test_collect_spot_check_source_paths_uses_working_directory():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('collect_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(
|
||||
flexmock()
|
||||
)
|
||||
flexmock(module.borgmatic.actions.create).should_receive('process_patterns').and_return(
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(
|
||||
[Pattern('foo'), Pattern('bar')]
|
||||
)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_base_create_command').with_args(
|
||||
|
||||
@@ -1,429 +1,9 @@
|
||||
import io
|
||||
import sys
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.actions import create as module
|
||||
from borgmatic.borg.pattern import Pattern, Pattern_source, Pattern_style, Pattern_type
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'pattern_line,expected_pattern',
|
||||
(
|
||||
('R /foo', Pattern('/foo', source=Pattern_source.CONFIG)),
|
||||
('P sh', Pattern('sh', Pattern_type.PATTERN_STYLE, source=Pattern_source.CONFIG)),
|
||||
('+ /foo*', Pattern('/foo*', Pattern_type.INCLUDE, source=Pattern_source.CONFIG)),
|
||||
(
|
||||
'+ sh:/foo*',
|
||||
Pattern(
|
||||
'/foo*', Pattern_type.INCLUDE, Pattern_style.SHELL, source=Pattern_source.CONFIG
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_parse_pattern_transforms_pattern_line_to_instance(pattern_line, expected_pattern):
|
||||
module.parse_pattern(pattern_line) == expected_pattern
|
||||
|
||||
|
||||
def test_parse_pattern_with_invalid_pattern_line_errors():
|
||||
with pytest.raises(ValueError):
|
||||
module.parse_pattern('/foo')
|
||||
|
||||
|
||||
def test_collect_patterns_converts_source_directories():
|
||||
assert module.collect_patterns({'source_directories': ['/foo', '/bar']}) == (
|
||||
Pattern('/foo', source=Pattern_source.CONFIG),
|
||||
Pattern('/bar', source=Pattern_source.CONFIG),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_parses_config_patterns():
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /foo').and_return(Pattern('/foo'))
|
||||
flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args(' ').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /bar').and_return(Pattern('/bar'))
|
||||
|
||||
assert module.collect_patterns({'patterns': ['R /foo', '# comment', '', ' ', 'R /bar']}) == (
|
||||
Pattern('/foo'),
|
||||
Pattern('/bar'),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_converts_exclude_patterns():
|
||||
assert module.collect_patterns({'exclude_patterns': ['/foo', '/bar', 'sh:**/baz']}) == (
|
||||
Pattern(
|
||||
'/foo', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH, source=Pattern_source.CONFIG
|
||||
),
|
||||
Pattern(
|
||||
'/bar', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH, source=Pattern_source.CONFIG
|
||||
),
|
||||
Pattern(
|
||||
'**/baz', Pattern_type.NO_RECURSE, Pattern_style.SHELL, source=Pattern_source.CONFIG
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_reads_config_patterns_from_file():
|
||||
builtins = flexmock(sys.modules['builtins'])
|
||||
builtins.should_receive('open').with_args('file1.txt').and_return(io.StringIO('R /foo'))
|
||||
builtins.should_receive('open').with_args('file2.txt').and_return(
|
||||
io.StringIO('R /bar\n# comment\n\n \nR /baz')
|
||||
)
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /foo').and_return(Pattern('/foo'))
|
||||
flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args(' ').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /bar').and_return(Pattern('/bar'))
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /baz').and_return(Pattern('/baz'))
|
||||
|
||||
assert module.collect_patterns({'patterns_from': ['file1.txt', 'file2.txt']}) == (
|
||||
Pattern('/foo'),
|
||||
Pattern('/bar'),
|
||||
Pattern('/baz'),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_errors_on_missing_config_patterns_from_file():
|
||||
builtins = flexmock(sys.modules['builtins'])
|
||||
builtins.should_receive('open').with_args('file1.txt').and_raise(FileNotFoundError)
|
||||
flexmock(module).should_receive('parse_pattern').never()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.collect_patterns({'patterns_from': ['file1.txt', 'file2.txt']})
|
||||
|
||||
|
||||
def test_collect_patterns_reads_config_exclude_from_file():
|
||||
builtins = flexmock(sys.modules['builtins'])
|
||||
builtins.should_receive('open').with_args('file1.txt').and_return(io.StringIO('/foo'))
|
||||
builtins.should_receive('open').with_args('file2.txt').and_return(
|
||||
io.StringIO('/bar\n# comment\n\n \n/baz')
|
||||
)
|
||||
flexmock(module).should_receive('parse_pattern').with_args(
|
||||
'! /foo', default_style=Pattern_style.FNMATCH
|
||||
).and_return(Pattern('/foo', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH))
|
||||
flexmock(module).should_receive('parse_pattern').with_args(
|
||||
'! /bar', default_style=Pattern_style.FNMATCH
|
||||
).and_return(Pattern('/bar', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH))
|
||||
flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args(' ').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args(
|
||||
'! /baz', default_style=Pattern_style.FNMATCH
|
||||
).and_return(Pattern('/baz', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH))
|
||||
|
||||
assert module.collect_patterns({'exclude_from': ['file1.txt', 'file2.txt']}) == (
|
||||
Pattern('/foo', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH),
|
||||
Pattern('/bar', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH),
|
||||
Pattern('/baz', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_errors_on_missing_config_exclude_from_file():
|
||||
builtins = flexmock(sys.modules['builtins'])
|
||||
builtins.should_receive('open').with_args('file1.txt').and_raise(OSError)
|
||||
flexmock(module).should_receive('parse_pattern').never()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.collect_patterns({'exclude_from': ['file1.txt', 'file2.txt']})
|
||||
|
||||
|
||||
def test_expand_directory_with_basic_path_passes_it_through():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('foo')
|
||||
flexmock(module.glob).should_receive('glob').and_return([])
|
||||
|
||||
paths = module.expand_directory('foo', None)
|
||||
|
||||
assert paths == ['foo']
|
||||
|
||||
|
||||
def test_expand_directory_with_glob_expands():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('foo*')
|
||||
flexmock(module.glob).should_receive('glob').and_return(['foo', 'food'])
|
||||
|
||||
paths = module.expand_directory('foo*', None)
|
||||
|
||||
assert paths == ['foo', 'food']
|
||||
|
||||
|
||||
def test_expand_directory_strips_off_working_directory():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('foo')
|
||||
flexmock(module.glob).should_receive('glob').with_args('/working/dir/foo').and_return([]).once()
|
||||
|
||||
paths = module.expand_directory('foo', working_directory='/working/dir')
|
||||
|
||||
assert paths == ['foo']
|
||||
|
||||
|
||||
def test_expand_directory_globs_working_directory_and_strips_it_off():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('foo*')
|
||||
flexmock(module.glob).should_receive('glob').with_args('/working/dir/foo*').and_return(
|
||||
['/working/dir/foo', '/working/dir/food']
|
||||
).once()
|
||||
|
||||
paths = module.expand_directory('foo*', working_directory='/working/dir')
|
||||
|
||||
assert paths == ['foo', 'food']
|
||||
|
||||
|
||||
def test_expand_directory_with_slashdot_hack_globs_working_directory_and_strips_it_off():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('./foo*')
|
||||
flexmock(module.glob).should_receive('glob').with_args('/working/dir/./foo*').and_return(
|
||||
['/working/dir/./foo', '/working/dir/./food']
|
||||
).once()
|
||||
|
||||
paths = module.expand_directory('./foo*', working_directory='/working/dir')
|
||||
|
||||
assert paths == ['./foo', './food']
|
||||
|
||||
|
||||
def test_expand_directory_with_working_directory_matching_start_of_directory_does_not_strip_it_off():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('/working/dir/foo')
|
||||
flexmock(module.glob).should_receive('glob').with_args('/working/dir/foo').and_return(
|
||||
['/working/dir/foo']
|
||||
).once()
|
||||
|
||||
paths = module.expand_directory('/working/dir/foo', working_directory='/working/dir')
|
||||
|
||||
assert paths == ['/working/dir/foo']
|
||||
|
||||
|
||||
def test_expand_patterns_flattens_expanded_directories():
|
||||
flexmock(module).should_receive('expand_directory').with_args('~/foo', None).and_return(
|
||||
['/root/foo']
|
||||
)
|
||||
flexmock(module).should_receive('expand_directory').with_args('bar*', None).and_return(
|
||||
['bar', 'barf']
|
||||
)
|
||||
|
||||
paths = module.expand_patterns((Pattern('~/foo'), Pattern('bar*')))
|
||||
|
||||
assert paths == (Pattern('/root/foo'), Pattern('bar'), Pattern('barf'))
|
||||
|
||||
|
||||
def test_expand_patterns_with_working_directory_passes_it_through():
|
||||
flexmock(module).should_receive('expand_directory').with_args('foo', '/working/dir').and_return(
|
||||
['/working/dir/foo']
|
||||
)
|
||||
|
||||
patterns = module.expand_patterns((Pattern('foo'),), working_directory='/working/dir')
|
||||
|
||||
assert patterns == (Pattern('/working/dir/foo'),)
|
||||
|
||||
|
||||
def test_expand_patterns_does_not_expand_skip_paths():
|
||||
flexmock(module).should_receive('expand_directory').with_args('/foo', None).and_return(['/foo'])
|
||||
flexmock(module).should_receive('expand_directory').with_args('/bar*', None).never()
|
||||
|
||||
patterns = module.expand_patterns((Pattern('/foo'), Pattern('/bar*')), skip_paths=('/bar*',))
|
||||
|
||||
assert patterns == (Pattern('/foo'), Pattern('/bar*'))
|
||||
|
||||
|
||||
def test_expand_patterns_considers_none_as_no_patterns():
|
||||
assert module.expand_patterns(None) == ()
|
||||
|
||||
|
||||
def test_expand_patterns_expands_tildes_and_globs_in_root_patterns():
|
||||
flexmock(module.os.path).should_receive('expanduser').never()
|
||||
flexmock(module).should_receive('expand_directory').and_return(
|
||||
['/root/foo/one', '/root/foo/two']
|
||||
)
|
||||
|
||||
paths = module.expand_patterns((Pattern('~/foo/*'),))
|
||||
|
||||
assert paths == (Pattern('/root/foo/one'), Pattern('/root/foo/two'))
|
||||
|
||||
|
||||
def test_expand_patterns_expands_only_tildes_in_non_root_patterns():
|
||||
flexmock(module).should_receive('expand_directory').never()
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('/root/bar/*')
|
||||
|
||||
paths = module.expand_patterns((Pattern('~/bar/*', Pattern_type.INCLUDE),))
|
||||
|
||||
assert paths == (Pattern('/root/bar/*', Pattern_type.INCLUDE),)
|
||||
|
||||
|
||||
def test_device_map_patterns_gives_device_id_per_path():
|
||||
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').and_return(flexmock(st_dev=66))
|
||||
|
||||
device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar')))
|
||||
|
||||
assert device_map == (
|
||||
Pattern('/foo', device=55),
|
||||
Pattern('/bar', device=66),
|
||||
)
|
||||
|
||||
|
||||
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))
|
||||
flexmock(module.os).should_receive('stat').with_args('/bar').never()
|
||||
|
||||
device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar')))
|
||||
|
||||
assert device_map == (
|
||||
Pattern('/foo', device=55),
|
||||
Pattern('/bar'),
|
||||
)
|
||||
|
||||
|
||||
def test_device_map_patterns_uses_working_directory_to_construct_path():
|
||||
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('/working/dir/bar').and_return(
|
||||
flexmock(st_dev=66)
|
||||
)
|
||||
|
||||
device_map = module.device_map_patterns(
|
||||
(Pattern('/foo'), Pattern('bar')), working_directory='/working/dir'
|
||||
)
|
||||
|
||||
assert device_map == (
|
||||
Pattern('/foo', device=55),
|
||||
Pattern('bar', device=66),
|
||||
)
|
||||
|
||||
|
||||
def test_device_map_patterns_with_existing_device_id_does_not_overwrite_it():
|
||||
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').and_return(flexmock(st_dev=100))
|
||||
|
||||
device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar', device=66)))
|
||||
|
||||
assert device_map == (
|
||||
Pattern('/foo', device=55),
|
||||
Pattern('/bar', device=66),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'patterns,expected_patterns',
|
||||
(
|
||||
((Pattern('/', device=1), Pattern('/root', device=1)), (Pattern('/', device=1),)),
|
||||
((Pattern('/', device=1), Pattern('/root/', device=1)), (Pattern('/', device=1),)),
|
||||
(
|
||||
(Pattern('/', device=1), Pattern('/root', device=2)),
|
||||
(Pattern('/', device=1), Pattern('/root', device=2)),
|
||||
),
|
||||
((Pattern('/root', device=1), Pattern('/', device=1)), (Pattern('/', device=1),)),
|
||||
(
|
||||
(Pattern('/root', device=1), Pattern('/root/foo', device=1)),
|
||||
(Pattern('/root', device=1),),
|
||||
),
|
||||
(
|
||||
(Pattern('/root/', device=1), Pattern('/root/foo', device=1)),
|
||||
(Pattern('/root/', device=1),),
|
||||
),
|
||||
(
|
||||
(Pattern('/root', device=1), Pattern('/root/foo/', device=1)),
|
||||
(Pattern('/root', device=1),),
|
||||
),
|
||||
(
|
||||
(Pattern('/root', device=1), Pattern('/root/foo', device=2)),
|
||||
(Pattern('/root', device=1), Pattern('/root/foo', device=2)),
|
||||
),
|
||||
(
|
||||
(Pattern('/root/foo', device=1), Pattern('/root', device=1)),
|
||||
(Pattern('/root', device=1),),
|
||||
),
|
||||
(
|
||||
(Pattern('/root', device=None), Pattern('/root/foo', device=None)),
|
||||
(Pattern('/root'), Pattern('/root/foo')),
|
||||
),
|
||||
(
|
||||
(
|
||||
Pattern('/root', device=1),
|
||||
Pattern('/etc', device=1),
|
||||
Pattern('/root/foo/bar', device=1),
|
||||
),
|
||||
(Pattern('/root', device=1), Pattern('/etc', device=1)),
|
||||
),
|
||||
(
|
||||
(
|
||||
Pattern('/root', device=1),
|
||||
Pattern('/root/foo', device=1),
|
||||
Pattern('/root/foo/bar', device=1),
|
||||
),
|
||||
(Pattern('/root', device=1),),
|
||||
),
|
||||
((Pattern('/dup', device=1), Pattern('/dup', device=1)), (Pattern('/dup', device=1),)),
|
||||
(
|
||||
(Pattern('/foo', device=1), Pattern('/bar', device=1)),
|
||||
(Pattern('/foo', device=1), Pattern('/bar', device=1)),
|
||||
),
|
||||
(
|
||||
(Pattern('/foo', device=1), Pattern('/bar', device=2)),
|
||||
(Pattern('/foo', device=1), Pattern('/bar', device=2)),
|
||||
),
|
||||
((Pattern('/root/foo', device=1),), (Pattern('/root/foo', device=1),)),
|
||||
(
|
||||
(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):
|
||||
assert module.deduplicate_patterns(patterns) == expected_patterns
|
||||
|
||||
|
||||
def test_process_patterns_includes_patterns():
|
||||
flexmock(module).should_receive('deduplicate_patterns').and_return(
|
||||
(Pattern('foo'), Pattern('bar'))
|
||||
)
|
||||
flexmock(module).should_receive('device_map_patterns').and_return({})
|
||||
flexmock(module).should_receive('expand_patterns').with_args(
|
||||
(Pattern('foo'), Pattern('bar')),
|
||||
working_directory='/working',
|
||||
skip_paths=set(),
|
||||
).and_return(()).once()
|
||||
|
||||
assert module.process_patterns(
|
||||
(Pattern('foo'), Pattern('bar')),
|
||||
working_directory='/working',
|
||||
) == [Pattern('foo'), Pattern('bar')]
|
||||
|
||||
|
||||
def test_process_patterns_skips_expand_for_requested_paths():
|
||||
skip_paths = {flexmock()}
|
||||
flexmock(module).should_receive('deduplicate_patterns').and_return(
|
||||
(Pattern('foo'), Pattern('bar'))
|
||||
)
|
||||
flexmock(module).should_receive('device_map_patterns').and_return({})
|
||||
flexmock(module).should_receive('expand_patterns').with_args(
|
||||
(Pattern('foo'), Pattern('bar')),
|
||||
working_directory='/working',
|
||||
skip_paths=skip_paths,
|
||||
).and_return(()).once()
|
||||
|
||||
assert module.process_patterns(
|
||||
(Pattern('foo'), Pattern('bar')),
|
||||
working_directory='/working',
|
||||
skip_expand_paths=skip_paths,
|
||||
) == [Pattern('foo'), Pattern('bar')]
|
||||
|
||||
|
||||
def test_run_create_executes_and_calls_hooks_for_configured_repository():
|
||||
@@ -437,9 +17,9 @@ def test_run_create_executes_and_calls_hooks_for_configured_repository():
|
||||
flexmock(module.borgmatic.hooks.dispatch).should_receive(
|
||||
'call_hooks_even_if_unconfigured'
|
||||
).and_return({})
|
||||
flexmock(module).should_receive('collect_patterns').and_return(())
|
||||
flexmock(module).should_receive('process_patterns').and_return([])
|
||||
flexmock(module.os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
|
||||
flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
||||
create_arguments = flexmock(
|
||||
repository=None,
|
||||
progress=flexmock(),
|
||||
@@ -478,9 +58,9 @@ def test_run_create_runs_with_selected_repository():
|
||||
flexmock(module.borgmatic.hooks.dispatch).should_receive(
|
||||
'call_hooks_even_if_unconfigured'
|
||||
).and_return({})
|
||||
flexmock(module).should_receive('collect_patterns').and_return(())
|
||||
flexmock(module).should_receive('process_patterns').and_return([])
|
||||
flexmock(module.os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
|
||||
flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
||||
create_arguments = flexmock(
|
||||
repository=flexmock(),
|
||||
progress=flexmock(),
|
||||
@@ -621,9 +201,9 @@ def test_run_create_produces_json():
|
||||
flexmock(module.borgmatic.hooks.dispatch).should_receive(
|
||||
'call_hooks_even_if_unconfigured'
|
||||
).and_return({})
|
||||
flexmock(module).should_receive('collect_patterns').and_return(())
|
||||
flexmock(module).should_receive('process_patterns').and_return([])
|
||||
flexmock(module.os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
|
||||
flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
||||
create_arguments = flexmock(
|
||||
repository=flexmock(),
|
||||
progress=flexmock(),
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
import io
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.actions import pattern as module
|
||||
from borgmatic.borg.pattern import Pattern, Pattern_source, Pattern_style, Pattern_type
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'pattern_line,expected_pattern',
|
||||
(
|
||||
('R /foo', Pattern('/foo', source=Pattern_source.CONFIG)),
|
||||
('P sh', Pattern('sh', Pattern_type.PATTERN_STYLE, source=Pattern_source.CONFIG)),
|
||||
('+ /foo*', Pattern('/foo*', Pattern_type.INCLUDE, source=Pattern_source.CONFIG)),
|
||||
(
|
||||
'+ sh:/foo*',
|
||||
Pattern(
|
||||
'/foo*', Pattern_type.INCLUDE, Pattern_style.SHELL, source=Pattern_source.CONFIG
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_parse_pattern_transforms_pattern_line_to_instance(pattern_line, expected_pattern):
|
||||
module.parse_pattern(pattern_line) == expected_pattern
|
||||
|
||||
|
||||
def test_parse_pattern_with_invalid_pattern_line_errors():
|
||||
with pytest.raises(ValueError):
|
||||
module.parse_pattern('/foo')
|
||||
|
||||
|
||||
def test_collect_patterns_converts_source_directories():
|
||||
assert module.collect_patterns({'source_directories': ['/foo', '/bar']}) == (
|
||||
Pattern('/foo', source=Pattern_source.CONFIG),
|
||||
Pattern('/bar', source=Pattern_source.CONFIG),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_parses_config_patterns():
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /foo').and_return(Pattern('/foo'))
|
||||
flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args(' ').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /bar').and_return(Pattern('/bar'))
|
||||
|
||||
assert module.collect_patterns({'patterns': ['R /foo', '# comment', '', ' ', 'R /bar']}) == (
|
||||
Pattern('/foo'),
|
||||
Pattern('/bar'),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_converts_exclude_patterns():
|
||||
assert module.collect_patterns({'exclude_patterns': ['/foo', '/bar', 'sh:**/baz']}) == (
|
||||
Pattern(
|
||||
'/foo', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH, source=Pattern_source.CONFIG
|
||||
),
|
||||
Pattern(
|
||||
'/bar', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH, source=Pattern_source.CONFIG
|
||||
),
|
||||
Pattern(
|
||||
'**/baz', Pattern_type.NO_RECURSE, Pattern_style.SHELL, source=Pattern_source.CONFIG
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_reads_config_patterns_from_file():
|
||||
builtins = flexmock(sys.modules['builtins'])
|
||||
builtins.should_receive('open').with_args('file1.txt').and_return(io.StringIO('R /foo'))
|
||||
builtins.should_receive('open').with_args('file2.txt').and_return(
|
||||
io.StringIO('R /bar\n# comment\n\n \nR /baz')
|
||||
)
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /foo').and_return(Pattern('/foo'))
|
||||
flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args(' ').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /bar').and_return(Pattern('/bar'))
|
||||
flexmock(module).should_receive('parse_pattern').with_args('R /baz').and_return(Pattern('/baz'))
|
||||
|
||||
assert module.collect_patterns({'patterns_from': ['file1.txt', 'file2.txt']}) == (
|
||||
Pattern('/foo'),
|
||||
Pattern('/bar'),
|
||||
Pattern('/baz'),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_errors_on_missing_config_patterns_from_file():
|
||||
builtins = flexmock(sys.modules['builtins'])
|
||||
builtins.should_receive('open').with_args('file1.txt').and_raise(FileNotFoundError)
|
||||
flexmock(module).should_receive('parse_pattern').never()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.collect_patterns({'patterns_from': ['file1.txt', 'file2.txt']})
|
||||
|
||||
|
||||
def test_collect_patterns_reads_config_exclude_from_file():
|
||||
builtins = flexmock(sys.modules['builtins'])
|
||||
builtins.should_receive('open').with_args('file1.txt').and_return(io.StringIO('/foo'))
|
||||
builtins.should_receive('open').with_args('file2.txt').and_return(
|
||||
io.StringIO('/bar\n# comment\n\n \n/baz')
|
||||
)
|
||||
flexmock(module).should_receive('parse_pattern').with_args(
|
||||
'! /foo', default_style=Pattern_style.FNMATCH
|
||||
).and_return(Pattern('/foo', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH))
|
||||
flexmock(module).should_receive('parse_pattern').with_args(
|
||||
'! /bar', default_style=Pattern_style.FNMATCH
|
||||
).and_return(Pattern('/bar', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH))
|
||||
flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args('').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args(' ').never()
|
||||
flexmock(module).should_receive('parse_pattern').with_args(
|
||||
'! /baz', default_style=Pattern_style.FNMATCH
|
||||
).and_return(Pattern('/baz', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH))
|
||||
|
||||
assert module.collect_patterns({'exclude_from': ['file1.txt', 'file2.txt']}) == (
|
||||
Pattern('/foo', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH),
|
||||
Pattern('/bar', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH),
|
||||
Pattern('/baz', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH),
|
||||
)
|
||||
|
||||
|
||||
def test_collect_patterns_errors_on_missing_config_exclude_from_file():
|
||||
builtins = flexmock(sys.modules['builtins'])
|
||||
builtins.should_receive('open').with_args('file1.txt').and_raise(OSError)
|
||||
flexmock(module).should_receive('parse_pattern').never()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.collect_patterns({'exclude_from': ['file1.txt', 'file2.txt']})
|
||||
|
||||
|
||||
def test_expand_directory_with_basic_path_passes_it_through():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('foo')
|
||||
flexmock(module.glob).should_receive('glob').and_return([])
|
||||
|
||||
paths = module.expand_directory('foo', None)
|
||||
|
||||
assert paths == ['foo']
|
||||
|
||||
|
||||
def test_expand_directory_with_glob_expands():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('foo*')
|
||||
flexmock(module.glob).should_receive('glob').and_return(['foo', 'food'])
|
||||
|
||||
paths = module.expand_directory('foo*', None)
|
||||
|
||||
assert paths == ['foo', 'food']
|
||||
|
||||
|
||||
def test_expand_directory_strips_off_working_directory():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('foo')
|
||||
flexmock(module.glob).should_receive('glob').with_args('/working/dir/foo').and_return([]).once()
|
||||
|
||||
paths = module.expand_directory('foo', working_directory='/working/dir')
|
||||
|
||||
assert paths == ['foo']
|
||||
|
||||
|
||||
def test_expand_directory_globs_working_directory_and_strips_it_off():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('foo*')
|
||||
flexmock(module.glob).should_receive('glob').with_args('/working/dir/foo*').and_return(
|
||||
['/working/dir/foo', '/working/dir/food']
|
||||
).once()
|
||||
|
||||
paths = module.expand_directory('foo*', working_directory='/working/dir')
|
||||
|
||||
assert paths == ['foo', 'food']
|
||||
|
||||
|
||||
def test_expand_directory_with_slashdot_hack_globs_working_directory_and_strips_it_off():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('./foo*')
|
||||
flexmock(module.glob).should_receive('glob').with_args('/working/dir/./foo*').and_return(
|
||||
['/working/dir/./foo', '/working/dir/./food']
|
||||
).once()
|
||||
|
||||
paths = module.expand_directory('./foo*', working_directory='/working/dir')
|
||||
|
||||
assert paths == ['./foo', './food']
|
||||
|
||||
|
||||
def test_expand_directory_with_working_directory_matching_start_of_directory_does_not_strip_it_off():
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('/working/dir/foo')
|
||||
flexmock(module.glob).should_receive('glob').with_args('/working/dir/foo').and_return(
|
||||
['/working/dir/foo']
|
||||
).once()
|
||||
|
||||
paths = module.expand_directory('/working/dir/foo', working_directory='/working/dir')
|
||||
|
||||
assert paths == ['/working/dir/foo']
|
||||
|
||||
|
||||
def test_expand_patterns_flattens_expanded_directories():
|
||||
flexmock(module).should_receive('expand_directory').with_args('~/foo', None).and_return(
|
||||
['/root/foo']
|
||||
)
|
||||
flexmock(module).should_receive('expand_directory').with_args('bar*', None).and_return(
|
||||
['bar', 'barf']
|
||||
)
|
||||
|
||||
paths = module.expand_patterns((Pattern('~/foo'), Pattern('bar*')))
|
||||
|
||||
assert paths == (Pattern('/root/foo'), Pattern('bar'), Pattern('barf'))
|
||||
|
||||
|
||||
def test_expand_patterns_with_working_directory_passes_it_through():
|
||||
flexmock(module).should_receive('expand_directory').with_args('foo', '/working/dir').and_return(
|
||||
['/working/dir/foo']
|
||||
)
|
||||
|
||||
patterns = module.expand_patterns((Pattern('foo'),), working_directory='/working/dir')
|
||||
|
||||
assert patterns == (Pattern('/working/dir/foo'),)
|
||||
|
||||
|
||||
def test_expand_patterns_does_not_expand_skip_paths():
|
||||
flexmock(module).should_receive('expand_directory').with_args('/foo', None).and_return(['/foo'])
|
||||
flexmock(module).should_receive('expand_directory').with_args('/bar*', None).never()
|
||||
|
||||
patterns = module.expand_patterns((Pattern('/foo'), Pattern('/bar*')), skip_paths=('/bar*',))
|
||||
|
||||
assert patterns == (Pattern('/foo'), Pattern('/bar*'))
|
||||
|
||||
|
||||
def test_expand_patterns_considers_none_as_no_patterns():
|
||||
assert module.expand_patterns(None) == ()
|
||||
|
||||
|
||||
def test_expand_patterns_expands_tildes_and_globs_in_root_patterns():
|
||||
flexmock(module.os.path).should_receive('expanduser').never()
|
||||
flexmock(module).should_receive('expand_directory').and_return(
|
||||
['/root/foo/one', '/root/foo/two']
|
||||
)
|
||||
|
||||
paths = module.expand_patterns((Pattern('~/foo/*'),))
|
||||
|
||||
assert paths == (Pattern('/root/foo/one'), Pattern('/root/foo/two'))
|
||||
|
||||
|
||||
def test_expand_patterns_expands_only_tildes_in_non_root_patterns():
|
||||
flexmock(module).should_receive('expand_directory').never()
|
||||
flexmock(module.os.path).should_receive('expanduser').and_return('/root/bar/*')
|
||||
|
||||
paths = module.expand_patterns((Pattern('~/bar/*', Pattern_type.INCLUDE),))
|
||||
|
||||
assert paths == (Pattern('/root/bar/*', Pattern_type.INCLUDE),)
|
||||
|
||||
|
||||
def test_device_map_patterns_gives_device_id_per_path():
|
||||
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').and_return(flexmock(st_dev=66))
|
||||
|
||||
device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar')))
|
||||
|
||||
assert device_map == (
|
||||
Pattern('/foo', device=55),
|
||||
Pattern('/bar', device=66),
|
||||
)
|
||||
|
||||
|
||||
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))
|
||||
flexmock(module.os).should_receive('stat').with_args('/bar').never()
|
||||
|
||||
device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar')))
|
||||
|
||||
assert device_map == (
|
||||
Pattern('/foo', device=55),
|
||||
Pattern('/bar'),
|
||||
)
|
||||
|
||||
|
||||
def test_device_map_patterns_uses_working_directory_to_construct_path():
|
||||
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('/working/dir/bar').and_return(
|
||||
flexmock(st_dev=66)
|
||||
)
|
||||
|
||||
device_map = module.device_map_patterns(
|
||||
(Pattern('/foo'), Pattern('bar')), working_directory='/working/dir'
|
||||
)
|
||||
|
||||
assert device_map == (
|
||||
Pattern('/foo', device=55),
|
||||
Pattern('bar', device=66),
|
||||
)
|
||||
|
||||
|
||||
def test_device_map_patterns_with_existing_device_id_does_not_overwrite_it():
|
||||
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').and_return(flexmock(st_dev=100))
|
||||
|
||||
device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar', device=66)))
|
||||
|
||||
assert device_map == (
|
||||
Pattern('/foo', device=55),
|
||||
Pattern('/bar', device=66),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'patterns,expected_patterns',
|
||||
(
|
||||
((Pattern('/', device=1), Pattern('/root', device=1)), (Pattern('/', device=1),)),
|
||||
((Pattern('/', device=1), Pattern('/root/', device=1)), (Pattern('/', device=1),)),
|
||||
(
|
||||
(Pattern('/', device=1), Pattern('/root', device=2)),
|
||||
(Pattern('/', device=1), Pattern('/root', device=2)),
|
||||
),
|
||||
((Pattern('/root', device=1), Pattern('/', device=1)), (Pattern('/', device=1),)),
|
||||
(
|
||||
(Pattern('/root', device=1), Pattern('/root/foo', device=1)),
|
||||
(Pattern('/root', device=1),),
|
||||
),
|
||||
(
|
||||
(Pattern('/root/', device=1), Pattern('/root/foo', device=1)),
|
||||
(Pattern('/root/', device=1),),
|
||||
),
|
||||
(
|
||||
(Pattern('/root', device=1), Pattern('/root/foo/', device=1)),
|
||||
(Pattern('/root', device=1),),
|
||||
),
|
||||
(
|
||||
(Pattern('/root', device=1), Pattern('/root/foo', device=2)),
|
||||
(Pattern('/root', device=1), Pattern('/root/foo', device=2)),
|
||||
),
|
||||
(
|
||||
(Pattern('/root/foo', device=1), Pattern('/root', device=1)),
|
||||
(Pattern('/root', device=1),),
|
||||
),
|
||||
(
|
||||
(Pattern('/root', device=None), Pattern('/root/foo', device=None)),
|
||||
(Pattern('/root'), Pattern('/root/foo')),
|
||||
),
|
||||
(
|
||||
(
|
||||
Pattern('/root', device=1),
|
||||
Pattern('/etc', device=1),
|
||||
Pattern('/root/foo/bar', device=1),
|
||||
),
|
||||
(Pattern('/root', device=1), Pattern('/etc', device=1)),
|
||||
),
|
||||
(
|
||||
(
|
||||
Pattern('/root', device=1),
|
||||
Pattern('/root/foo', device=1),
|
||||
Pattern('/root/foo/bar', device=1),
|
||||
),
|
||||
(Pattern('/root', device=1),),
|
||||
),
|
||||
((Pattern('/dup', device=1), Pattern('/dup', device=1)), (Pattern('/dup', device=1),)),
|
||||
(
|
||||
(Pattern('/foo', device=1), Pattern('/bar', device=1)),
|
||||
(Pattern('/foo', device=1), Pattern('/bar', device=1)),
|
||||
),
|
||||
(
|
||||
(Pattern('/foo', device=1), Pattern('/bar', device=2)),
|
||||
(Pattern('/foo', device=1), Pattern('/bar', device=2)),
|
||||
),
|
||||
((Pattern('/root/foo', device=1),), (Pattern('/root/foo', device=1),)),
|
||||
(
|
||||
(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):
|
||||
assert module.deduplicate_patterns(patterns) == expected_patterns
|
||||
|
||||
|
||||
def test_process_patterns_includes_patterns():
|
||||
flexmock(module).should_receive('deduplicate_patterns').and_return(
|
||||
(Pattern('foo'), Pattern('bar'))
|
||||
)
|
||||
flexmock(module).should_receive('device_map_patterns').and_return({})
|
||||
flexmock(module).should_receive('expand_patterns').with_args(
|
||||
(Pattern('foo'), Pattern('bar')),
|
||||
working_directory='/working',
|
||||
skip_paths=set(),
|
||||
).and_return(()).once()
|
||||
|
||||
assert module.process_patterns(
|
||||
(Pattern('foo'), Pattern('bar')),
|
||||
working_directory='/working',
|
||||
) == [Pattern('foo'), Pattern('bar')]
|
||||
|
||||
|
||||
def test_process_patterns_skips_expand_for_requested_paths():
|
||||
skip_paths = {flexmock()}
|
||||
flexmock(module).should_receive('deduplicate_patterns').and_return(
|
||||
(Pattern('foo'), Pattern('bar'))
|
||||
)
|
||||
flexmock(module).should_receive('device_map_patterns').and_return({})
|
||||
flexmock(module).should_receive('expand_patterns').with_args(
|
||||
(Pattern('foo'), Pattern('bar')),
|
||||
working_directory='/working',
|
||||
skip_paths=skip_paths,
|
||||
).and_return(()).once()
|
||||
|
||||
assert module.process_patterns(
|
||||
(Pattern('foo'), Pattern('bar')),
|
||||
working_directory='/working',
|
||||
skip_expand_paths=skip_paths,
|
||||
) == [Pattern('foo'), Pattern('bar')]
|
||||
+60
-202
@@ -9,131 +9,6 @@ from borgmatic.borg.pattern import Pattern, Pattern_source, Pattern_style, Patte
|
||||
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+ 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_style.SHELL)],
|
||||
borgmatic_runtime_directory='/run/user/0',
|
||||
)
|
||||
|
||||
|
||||
def test_write_patterns_file_with_empty_exclude_patterns_does_not_raise():
|
||||
module.write_patterns_file([], borgmatic_runtime_directory='/run/user/0')
|
||||
|
||||
|
||||
def test_write_patterns_file_appends_to_existing():
|
||||
patterns_file = flexmock(name='filename', flush=lambda: None)
|
||||
patterns_file.should_receive('write').with_args('\n')
|
||||
patterns_file.should_receive('write').with_args('R /foo\n+ /foo/bar')
|
||||
flexmock(module.tempfile).should_receive('NamedTemporaryFile').never()
|
||||
|
||||
module.write_patterns_file(
|
||||
[Pattern('/foo'), Pattern('/foo/bar', Pattern_type.INCLUDE)],
|
||||
borgmatic_runtime_directory='/run/user/0',
|
||||
patterns_file=patterns_file,
|
||||
)
|
||||
|
||||
|
||||
def test_make_exclude_flags_includes_exclude_caches_when_true_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'exclude_caches': True})
|
||||
|
||||
assert exclude_flags == ('--exclude-caches',)
|
||||
|
||||
|
||||
def test_make_exclude_flags_does_not_include_exclude_caches_when_false_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'exclude_caches': False})
|
||||
|
||||
assert exclude_flags == ()
|
||||
|
||||
|
||||
def test_make_exclude_flags_includes_exclude_if_present_when_in_config():
|
||||
exclude_flags = module.make_exclude_flags(
|
||||
config={'exclude_if_present': ['exclude_me', 'also_me']}
|
||||
)
|
||||
|
||||
assert exclude_flags == (
|
||||
'--exclude-if-present',
|
||||
'exclude_me',
|
||||
'--exclude-if-present',
|
||||
'also_me',
|
||||
)
|
||||
|
||||
|
||||
def test_make_exclude_flags_includes_keep_exclude_tags_when_true_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'keep_exclude_tags': True})
|
||||
|
||||
assert exclude_flags == ('--keep-exclude-tags',)
|
||||
|
||||
|
||||
def test_make_exclude_flags_does_not_include_keep_exclude_tags_when_false_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'keep_exclude_tags': False})
|
||||
|
||||
assert exclude_flags == ()
|
||||
|
||||
|
||||
def test_make_exclude_flags_includes_exclude_nodump_when_true_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'exclude_nodump': True})
|
||||
|
||||
assert exclude_flags == ('--exclude-nodump',)
|
||||
|
||||
|
||||
def test_make_exclude_flags_does_not_include_exclude_nodump_when_false_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'exclude_nodump': False})
|
||||
|
||||
assert exclude_flags == ()
|
||||
|
||||
|
||||
def test_make_exclude_flags_is_empty_when_config_has_no_excludes():
|
||||
exclude_flags = module.make_exclude_flags(config={})
|
||||
|
||||
assert exclude_flags == ()
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_debug_and_feature_available_includes_plus_and_minus():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME+-'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_info_and_feature_available_omits_plus_and_minus():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_debug_and_feature_available_and_dry_run_includes_plus_and_minus():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=True) == 'AME+-'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_info_and_feature_available_and_dry_run_includes_plus_and_minus():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=True) == 'AME+-'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_debug_and_feature_not_available_includes_x():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AMEx-'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_info_and_feature_not_available_omits_x():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME-'
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'character_device,block_device,fifo,expected_result',
|
||||
(
|
||||
@@ -326,10 +201,10 @@ REPO_ARCHIVE = (f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
|
||||
def test_make_base_create_produces_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -355,16 +230,16 @@ def test_make_base_create_produces_borg_command():
|
||||
def test_make_base_create_command_includes_patterns_file_in_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
mock_pattern_file = flexmock(name='/tmp/patterns')
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(mock_pattern_file).and_return(
|
||||
None
|
||||
)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(
|
||||
mock_pattern_file
|
||||
).and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
pattern_flags = ('--patterns-from', mock_pattern_file.name)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -390,13 +265,13 @@ def test_make_base_create_command_includes_patterns_file_in_borg_command():
|
||||
|
||||
def test_make_base_create_command_with_store_config_false_omits_config_files():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -455,13 +330,13 @@ def test_make_base_create_command_includes_configuration_option_as_command_flag(
|
||||
option_name, option_value, feature_available, option_flags
|
||||
):
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(feature_available)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -487,13 +362,13 @@ def test_make_base_create_command_includes_configuration_option_as_command_flag(
|
||||
|
||||
def test_make_base_create_command_includes_dry_run_in_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -519,13 +394,13 @@ def test_make_base_create_command_includes_dry_run_in_borg_command():
|
||||
|
||||
def test_make_base_create_command_includes_local_path_in_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -551,13 +426,13 @@ def test_make_base_create_command_includes_local_path_in_borg_command():
|
||||
|
||||
def test_make_base_create_command_includes_remote_path_in_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -583,13 +458,13 @@ def test_make_base_create_command_includes_remote_path_in_borg_command():
|
||||
|
||||
def test_make_base_create_command_includes_log_json_in_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -615,13 +490,13 @@ def test_make_base_create_command_includes_log_json_in_borg_command():
|
||||
|
||||
def test_make_base_create_command_includes_list_flags_in_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -649,22 +524,22 @@ def test_make_base_create_command_with_stream_processes_ignores_read_special_fal
|
||||
patterns = [Pattern('foo'), Pattern('bar')]
|
||||
patterns_file = flexmock(name='patterns')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').with_args(
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').with_args(
|
||||
patterns, '/run/borgmatic'
|
||||
).and_return(patterns_file)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
flexmock(module.logger).should_receive('warning').twice()
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module).should_receive('collect_special_file_paths').and_return(('/dev/null',)).once()
|
||||
flexmock(module).should_receive('write_patterns_file').with_args(
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').with_args(
|
||||
(
|
||||
Pattern(
|
||||
'/dev/null',
|
||||
@@ -676,7 +551,7 @@ def test_make_base_create_command_with_stream_processes_ignores_read_special_fal
|
||||
'/run/borgmatic',
|
||||
patterns_file=patterns_file,
|
||||
).and_return(patterns_file).once()
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
|
||||
(create_flags, create_positional_arguments, pattern_file) = module.make_base_create_command(
|
||||
dry_run=False,
|
||||
@@ -700,22 +575,22 @@ def test_make_base_create_command_with_stream_processes_ignores_read_special_fal
|
||||
|
||||
def test_make_base_create_command_without_patterns_and_with_stream_processes_ignores_read_special_false_and_excludes_special_files():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').with_args(
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').with_args(
|
||||
[], '/run/borgmatic'
|
||||
).and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
flexmock(module.logger).should_receive('warning').twice()
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module).should_receive('collect_special_file_paths').and_return(('/dev/null',)).once()
|
||||
flexmock(module).should_receive('write_patterns_file').with_args(
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').with_args(
|
||||
(
|
||||
Pattern(
|
||||
'/dev/null',
|
||||
@@ -727,7 +602,7 @@ def test_make_base_create_command_without_patterns_and_with_stream_processes_ign
|
||||
'/run/borgmatic',
|
||||
patterns_file=None,
|
||||
).and_return(flexmock(name='patterns')).once()
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
|
||||
(create_flags, create_positional_arguments, pattern_file) = module.make_base_create_command(
|
||||
dry_run=False,
|
||||
@@ -751,13 +626,13 @@ def test_make_base_create_command_without_patterns_and_with_stream_processes_ign
|
||||
|
||||
def test_make_base_create_command_with_stream_processes_and_read_special_true_skips_special_files_excludes():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -786,13 +661,13 @@ def test_make_base_create_command_with_stream_processes_and_read_special_true_sk
|
||||
|
||||
def test_make_base_create_command_includes_archive_name_format_in_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
('repo::ARCHIVE_NAME',)
|
||||
)
|
||||
@@ -818,13 +693,13 @@ def test_make_base_create_command_includes_archive_name_format_in_borg_command()
|
||||
|
||||
def test_make_base_create_command_includes_default_archive_name_format_in_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
('repo::{hostname}',)
|
||||
)
|
||||
@@ -849,13 +724,13 @@ def test_make_base_create_command_includes_default_archive_name_format_in_borg_c
|
||||
|
||||
def test_make_base_create_command_includes_archive_name_format_with_placeholders_in_borg_command():
|
||||
repository_archive_pattern = 'repo::Documents_{hostname}-{now}' # noqa: FS003
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(repository_archive_pattern,)
|
||||
)
|
||||
@@ -881,13 +756,13 @@ def test_make_base_create_command_includes_archive_name_format_with_placeholders
|
||||
|
||||
def test_make_base_create_command_includes_repository_and_archive_name_format_with_placeholders_in_borg_command():
|
||||
repository_archive_pattern = '{fqdn}::Documents_{hostname}-{now}' # noqa: FS003
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(repository_archive_pattern,)
|
||||
)
|
||||
@@ -913,13 +788,13 @@ def test_make_base_create_command_includes_repository_and_archive_name_format_wi
|
||||
|
||||
def test_make_base_create_command_includes_extra_borg_options_in_borg_command():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('FOO')
|
||||
flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
|
||||
'{hostname}'
|
||||
)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
|
||||
(f'repo::{DEFAULT_ARCHIVE_NAME}',)
|
||||
)
|
||||
@@ -945,7 +820,9 @@ def test_make_base_create_command_includes_extra_borg_options_in_borg_command():
|
||||
|
||||
def test_make_base_create_command_with_non_existent_directory_and_source_directories_must_exist_raises():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('check_all_root_patterns_exist').and_raise(ValueError)
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive(
|
||||
'check_all_root_patterns_exist'
|
||||
).and_raise(ValueError)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.make_base_create_command(
|
||||
@@ -1571,22 +1448,3 @@ def test_create_archive_calls_borg_with_working_directory():
|
||||
global_arguments=flexmock(),
|
||||
borgmatic_runtime_directory='/borgmatic/run',
|
||||
)
|
||||
|
||||
|
||||
def test_check_all_root_patterns_exist_with_existent_pattern_path_does_not_raise():
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
|
||||
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)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.check_all_root_patterns_exist([Pattern('foo')])
|
||||
|
||||
@@ -327,3 +327,100 @@ def test_omit_flag_and_value_without_flag_present_passes_through_arguments():
|
||||
'create',
|
||||
'--other',
|
||||
)
|
||||
|
||||
|
||||
def test_make_exclude_flags_includes_exclude_caches_when_true_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'exclude_caches': True})
|
||||
|
||||
assert exclude_flags == ('--exclude-caches',)
|
||||
|
||||
|
||||
def test_make_exclude_flags_does_not_include_exclude_caches_when_false_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'exclude_caches': False})
|
||||
|
||||
assert exclude_flags == ()
|
||||
|
||||
|
||||
def test_make_exclude_flags_includes_exclude_if_present_when_in_config():
|
||||
exclude_flags = module.make_exclude_flags(
|
||||
config={'exclude_if_present': ['exclude_me', 'also_me']}
|
||||
)
|
||||
|
||||
assert exclude_flags == (
|
||||
'--exclude-if-present',
|
||||
'exclude_me',
|
||||
'--exclude-if-present',
|
||||
'also_me',
|
||||
)
|
||||
|
||||
|
||||
def test_make_exclude_flags_includes_keep_exclude_tags_when_true_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'keep_exclude_tags': True})
|
||||
|
||||
assert exclude_flags == ('--keep-exclude-tags',)
|
||||
|
||||
|
||||
def test_make_exclude_flags_does_not_include_keep_exclude_tags_when_false_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'keep_exclude_tags': False})
|
||||
|
||||
assert exclude_flags == ()
|
||||
|
||||
|
||||
def test_make_exclude_flags_includes_exclude_nodump_when_true_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'exclude_nodump': True})
|
||||
|
||||
assert exclude_flags == ('--exclude-nodump',)
|
||||
|
||||
|
||||
def test_make_exclude_flags_does_not_include_exclude_nodump_when_false_in_config():
|
||||
exclude_flags = module.make_exclude_flags(config={'exclude_nodump': False})
|
||||
|
||||
assert exclude_flags == ()
|
||||
|
||||
|
||||
def test_make_exclude_flags_is_empty_when_config_has_no_excludes():
|
||||
exclude_flags = module.make_exclude_flags(config={})
|
||||
|
||||
assert exclude_flags == ()
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_debug_and_feature_available_includes_plus_and_minus():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME+-'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_info_and_feature_available_omits_plus_and_minus():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_debug_and_feature_available_and_dry_run_includes_plus_and_minus():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=True) == 'AME+-'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_info_and_feature_available_and_dry_run_includes_plus_and_minus():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
|
||||
flexmock(module.feature).should_receive('available').and_return(True)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=True) == 'AME+-'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_debug_and_feature_not_available_includes_x():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AMEx-'
|
||||
|
||||
|
||||
def test_make_list_filter_flags_with_info_and_feature_not_available_omits_x():
|
||||
flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
|
||||
assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME-'
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import pytest
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.borg import pattern as module
|
||||
from borgmatic.borg.pattern import Pattern, Pattern_style, Pattern_type
|
||||
|
||||
|
||||
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+ 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_style.SHELL)],
|
||||
borgmatic_runtime_directory='/run/user/0',
|
||||
)
|
||||
|
||||
|
||||
def test_write_patterns_file_with_empty_exclude_patterns_does_not_raise():
|
||||
module.write_patterns_file([], borgmatic_runtime_directory='/run/user/0')
|
||||
|
||||
|
||||
def test_write_patterns_file_appends_to_existing():
|
||||
patterns_file = flexmock(name='filename', flush=lambda: None)
|
||||
patterns_file.should_receive('write').with_args('\n')
|
||||
patterns_file.should_receive('write').with_args('R /foo\n+ /foo/bar')
|
||||
flexmock(module.tempfile).should_receive('NamedTemporaryFile').never()
|
||||
|
||||
module.write_patterns_file(
|
||||
[Pattern('/foo'), Pattern('/foo/bar', Pattern_type.INCLUDE)],
|
||||
borgmatic_runtime_directory='/run/user/0',
|
||||
patterns_file=patterns_file,
|
||||
)
|
||||
|
||||
|
||||
def test_check_all_root_patterns_exist_with_existent_pattern_path_does_not_raise():
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
|
||||
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)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.check_all_root_patterns_exist([Pattern('foo')])
|
||||
@@ -21,9 +21,9 @@ def insert_execute_command_mock(command, working_directory=None, borg_exit_codes
|
||||
|
||||
|
||||
def test_recreate_archive_dry_run_skips_execution():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -59,9 +59,9 @@ def test_recreate_archive_dry_run_skips_execution():
|
||||
|
||||
|
||||
def test_recreate_calls_borg_with_required_flags():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -94,9 +94,9 @@ def test_recreate_calls_borg_with_required_flags():
|
||||
|
||||
|
||||
def test_recreate_with_remote_path():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -129,9 +129,9 @@ def test_recreate_with_remote_path():
|
||||
|
||||
|
||||
def test_recreate_with_lock_wait():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -163,9 +163,9 @@ def test_recreate_with_lock_wait():
|
||||
|
||||
|
||||
def test_recreate_with_log_info():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -199,9 +199,9 @@ def test_recreate_with_log_info():
|
||||
|
||||
|
||||
def test_recreate_with_log_debug():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -234,9 +234,9 @@ def test_recreate_with_log_debug():
|
||||
|
||||
|
||||
def test_recreate_with_log_json():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -268,8 +268,8 @@ def test_recreate_with_log_json():
|
||||
|
||||
|
||||
def test_recreate_with_list_config_calls_borg_with_list_flag():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -280,7 +280,9 @@ def test_recreate_with_list_config_calls_borg_with_list_flag():
|
||||
'repo',
|
||||
)
|
||||
)
|
||||
flexmock(module).should_receive('make_list_filter_flags').and_return('AME+-')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return(
|
||||
'AME+-'
|
||||
)
|
||||
insert_execute_command_mock(
|
||||
('borg', 'recreate', '--list', '--filter', 'AME+-', '--repo', 'repo')
|
||||
)
|
||||
@@ -304,8 +306,8 @@ def test_recreate_with_list_config_calls_borg_with_list_flag():
|
||||
|
||||
|
||||
def test_recreate_with_patterns_from_flag():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -341,8 +343,8 @@ def test_recreate_with_patterns_from_flag():
|
||||
|
||||
|
||||
def test_recreate_with_exclude_flags():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -353,7 +355,9 @@ def test_recreate_with_exclude_flags():
|
||||
'repo',
|
||||
)
|
||||
)
|
||||
flexmock(module).should_receive('make_exclude_flags').and_return(('--exclude', 'pattern'))
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(
|
||||
('--exclude', 'pattern')
|
||||
)
|
||||
insert_execute_command_mock(('borg', 'recreate', '--exclude', 'pattern', '--repo', 'repo'))
|
||||
|
||||
module.recreate_archive(
|
||||
@@ -375,9 +379,9 @@ def test_recreate_with_exclude_flags():
|
||||
|
||||
|
||||
def test_recreate_with_target_flag():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -409,9 +413,9 @@ def test_recreate_with_target_flag():
|
||||
|
||||
|
||||
def test_recreate_with_comment_flag():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -445,9 +449,9 @@ def test_recreate_with_comment_flag():
|
||||
|
||||
|
||||
def test_recreate_with_timestamp_flag():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -481,9 +485,9 @@ def test_recreate_with_timestamp_flag():
|
||||
|
||||
|
||||
def test_recreate_with_compression_flag():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -515,9 +519,9 @@ def test_recreate_with_compression_flag():
|
||||
|
||||
|
||||
def test_recreate_with_chunker_params_flag():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -551,9 +555,9 @@ def test_recreate_with_chunker_params_flag():
|
||||
|
||||
|
||||
def test_recreate_with_recompress_flag():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -585,9 +589,9 @@ def test_recreate_with_recompress_flag():
|
||||
|
||||
|
||||
def test_recreate_with_match_archives_star():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -619,9 +623,9 @@ def test_recreate_with_match_archives_star():
|
||||
|
||||
|
||||
def test_recreate_with_match_archives_regex():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -653,9 +657,9 @@ def test_recreate_with_match_archives_regex():
|
||||
|
||||
|
||||
def test_recreate_with_match_archives_shell():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
@@ -687,9 +691,9 @@ def test_recreate_with_match_archives_shell():
|
||||
|
||||
|
||||
def test_recreate_with_match_archives_and_feature_available_calls_borg_with_match_archives():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').with_args(
|
||||
'foo-*', None, '1.2.3'
|
||||
).and_return(('--match-archives', 'foo-*'))
|
||||
@@ -719,9 +723,9 @@ def test_recreate_with_match_archives_and_feature_available_calls_borg_with_matc
|
||||
|
||||
|
||||
def test_recreate_with_archives_flag_and_feature_available_calls_borg_with_match_archives():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').with_args(
|
||||
'archive', None, '1.2.3'
|
||||
).and_return(('--match-archives', 'archive'))
|
||||
@@ -753,9 +757,9 @@ def test_recreate_with_archives_flag_and_feature_available_calls_borg_with_match
|
||||
|
||||
|
||||
def test_recreate_with_match_archives_and_feature_not_available_calls_borg_without_match_archives():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').never()
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(False)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return(
|
||||
@@ -783,9 +787,9 @@ def test_recreate_with_match_archives_and_feature_not_available_calls_borg_witho
|
||||
|
||||
|
||||
def test_recreate_with_archives_flags_and_feature_not_available_calls_borg_with_combined_repo_and_archive():
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(())
|
||||
flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('')
|
||||
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').never()
|
||||
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(False)
|
||||
flexmock(module.borgmatic.borg.flags).should_receive(
|
||||
|
||||
Reference in New Issue
Block a user