|
@@ -2,6 +2,7 @@ import pytest
|
|
|
from flexmock import flexmock
|
|
|
|
|
|
from borgmatic.actions import create as module
|
|
|
+from borgmatic.borg.pattern import Pattern, Pattern_type
|
|
|
|
|
|
|
|
|
def test_expand_directory_with_basic_path_passes_it_through():
|
|
@@ -53,7 +54,7 @@ def test_expand_directory_with_slashdot_hack_globs_working_directory_and_strips_
|
|
|
assert paths == ['./foo', './food']
|
|
|
|
|
|
|
|
|
-def test_expand_directories_flattens_expanded_directories():
|
|
|
+def test_expand_patterns_flattens_expanded_directories():
|
|
|
flexmock(module).should_receive('expand_directory').with_args('~/foo', None).and_return(
|
|
|
['/root/foo']
|
|
|
)
|
|
@@ -61,167 +62,205 @@ def test_expand_directories_flattens_expanded_directories():
|
|
|
['bar', 'barf']
|
|
|
)
|
|
|
|
|
|
- paths = module.expand_directories(('~/foo', 'bar*'))
|
|
|
+ paths = module.expand_patterns((Pattern('~/foo'), Pattern('bar*')))
|
|
|
|
|
|
- assert paths == ('/root/foo', 'bar', 'barf')
|
|
|
+ assert paths == (Pattern('/root/foo'), Pattern('bar'), Pattern('barf'))
|
|
|
|
|
|
|
|
|
-def test_expand_directories_with_working_directory_passes_it_through():
|
|
|
+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']
|
|
|
)
|
|
|
|
|
|
- paths = module.expand_directories(('foo',), working_directory='/working/dir')
|
|
|
+ patterns = module.expand_patterns((Pattern('foo'),), working_directory='/working/dir')
|
|
|
|
|
|
- assert paths == ('/working/dir/foo',)
|
|
|
+ assert patterns == (Pattern('/working/dir/foo'),)
|
|
|
|
|
|
|
|
|
-def test_expand_directories_considers_none_as_no_directories():
|
|
|
- paths = module.expand_directories(None, None)
|
|
|
+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()
|
|
|
|
|
|
- assert paths == ()
|
|
|
+ patterns = module.expand_patterns((Pattern('/foo'), Pattern('/bar*')), skip_paths=('/bar*',))
|
|
|
|
|
|
+ assert patterns == (Pattern('/foo'), Pattern('/bar*'))
|
|
|
|
|
|
-def test_map_directories_to_devices_gives_device_id_per_path():
|
|
|
+
|
|
|
+def test_expand_patterns_considers_none_as_no_patterns():
|
|
|
+ assert module.expand_patterns(None) == ()
|
|
|
+
|
|
|
+
|
|
|
+def test_expand_patterns_only_considers_root_patterns():
|
|
|
+ flexmock(module).should_receive('expand_directory').with_args('~/foo', None).and_return(
|
|
|
+ ['/root/foo']
|
|
|
+ )
|
|
|
+ flexmock(module).should_receive('expand_directory').with_args('bar*', None).never()
|
|
|
+
|
|
|
+ paths = module.expand_patterns((Pattern('~/foo'), Pattern('bar*', Pattern_type.INCLUDE)))
|
|
|
+
|
|
|
+ assert paths == (Pattern('/root/foo'), Pattern('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.map_directories_to_devices(('/foo', '/bar'))
|
|
|
+ device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar')))
|
|
|
|
|
|
- assert device_map == {
|
|
|
- '/foo': 55,
|
|
|
- '/bar': 66,
|
|
|
- }
|
|
|
+ assert device_map == (
|
|
|
+ Pattern('/foo', device=55),
|
|
|
+ Pattern('/bar', device=66),
|
|
|
+ )
|
|
|
|
|
|
|
|
|
-def test_map_directories_to_devices_with_missing_path_does_not_error():
|
|
|
+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.map_directories_to_devices(('/foo', '/bar'))
|
|
|
+ device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar')))
|
|
|
|
|
|
- assert device_map == {
|
|
|
- '/foo': 55,
|
|
|
- '/bar': None,
|
|
|
- }
|
|
|
+ assert device_map == (
|
|
|
+ Pattern('/foo', device=55),
|
|
|
+ Pattern('/bar'),
|
|
|
+ )
|
|
|
|
|
|
|
|
|
-def test_map_directories_to_devices_uses_working_directory_to_construct_path():
|
|
|
+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.map_directories_to_devices(
|
|
|
- ('/foo', 'bar'), working_directory='/working/dir'
|
|
|
+ device_map = module.device_map_patterns(
|
|
|
+ (Pattern('/foo'), Pattern('bar')), working_directory='/working/dir'
|
|
|
)
|
|
|
|
|
|
- assert device_map == {
|
|
|
- '/foo': 55,
|
|
|
- 'bar': 66,
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-@pytest.mark.parametrize(
|
|
|
- 'directories,additional_directories,expected_directories',
|
|
|
- (
|
|
|
- ({'/': 1, '/root': 1}, {}, ['/']),
|
|
|
- ({'/': 1, '/root/': 1}, {}, ['/']),
|
|
|
- ({'/': 1, '/root': 2}, {}, ['/', '/root']),
|
|
|
- ({'/root': 1, '/': 1}, {}, ['/']),
|
|
|
- ({'/root': 1, '/root/foo': 1}, {}, ['/root']),
|
|
|
- ({'/root/': 1, '/root/foo': 1}, {}, ['/root/']),
|
|
|
- ({'/root': 1, '/root/foo/': 1}, {}, ['/root']),
|
|
|
- ({'/root': 1, '/root/foo': 2}, {}, ['/root', '/root/foo']),
|
|
|
- ({'/root/foo': 1, '/root': 1}, {}, ['/root']),
|
|
|
- ({'/root': None, '/root/foo': None}, {}, ['/root', '/root/foo']),
|
|
|
- ({'/root': 1, '/etc': 1, '/root/foo/bar': 1}, {}, ['/etc', '/root']),
|
|
|
- ({'/root': 1, '/root/foo': 1, '/root/foo/bar': 1}, {}, ['/root']),
|
|
|
- ({'/dup': 1, '/dup': 1}, {}, ['/dup']),
|
|
|
- ({'/foo': 1, '/bar': 1}, {}, ['/bar', '/foo']),
|
|
|
- ({'/foo': 1, '/bar': 2}, {}, ['/bar', '/foo']),
|
|
|
- ({'/root/foo': 1}, {'/root': 1}, []),
|
|
|
- ({'/root/foo': 1}, {'/root': 2}, ['/root/foo']),
|
|
|
- ({'/root/foo': 1}, {}, ['/root/foo']),
|
|
|
- ),
|
|
|
-)
|
|
|
-def test_deduplicate_directories_removes_child_paths_on_the_same_filesystem(
|
|
|
- directories, additional_directories, expected_directories
|
|
|
-):
|
|
|
- assert (
|
|
|
- module.deduplicate_directories(directories, additional_directories) == expected_directories
|
|
|
+ assert device_map == (
|
|
|
+ Pattern('/foo', device=55),
|
|
|
+ Pattern('bar', device=66),
|
|
|
)
|
|
|
|
|
|
|
|
|
-def test_pattern_root_directories_deals_with_none_patterns():
|
|
|
- assert module.pattern_root_directories(patterns=None) == []
|
|
|
-
|
|
|
-
|
|
|
-def test_pattern_root_directories_parses_roots_and_ignores_others():
|
|
|
- assert module.pattern_root_directories(
|
|
|
- ['R /root', '+ /root/foo', '- /root/foo/bar', 'R /baz']
|
|
|
- ) == ['/root', '/baz']
|
|
|
+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)))
|
|
|
|
|
|
-def test_process_source_directories_includes_source_directories():
|
|
|
- flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
|
|
- '/working'
|
|
|
+ assert device_map == (
|
|
|
+ Pattern('/foo', device=55),
|
|
|
+ Pattern('/bar', device=66),
|
|
|
)
|
|
|
- flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
|
|
|
- flexmock(module).should_receive('map_directories_to_devices').and_return({})
|
|
|
- flexmock(module).should_receive('expand_directories').with_args(
|
|
|
- ('foo', 'bar'), working_directory='/working'
|
|
|
- ).and_return(()).once()
|
|
|
- flexmock(module).should_receive('pattern_root_directories').and_return(())
|
|
|
- flexmock(module).should_receive('expand_directories').with_args(
|
|
|
- (), working_directory='/working'
|
|
|
- ).and_return(())
|
|
|
|
|
|
- assert module.process_source_directories(
|
|
|
- config={'source_directories': ['foo', 'bar']},
|
|
|
- ) == ('foo', 'bar')
|
|
|
+
|
|
|
+@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)),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+)
|
|
|
+def test_deduplicate_patterns_omits_child_paths_on_the_same_filesystem(patterns, expected_patterns):
|
|
|
+ assert module.deduplicate_patterns(patterns) == expected_patterns
|
|
|
|
|
|
|
|
|
-def test_process_source_directories_prefers_source_directory_argument_to_config():
|
|
|
- flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
|
|
- '/working'
|
|
|
+def test_process_patterns_includes_patterns():
|
|
|
+ flexmock(module).should_receive('deduplicate_patterns').and_return(
|
|
|
+ (Pattern('foo'), Pattern('bar'))
|
|
|
)
|
|
|
- flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
|
|
|
- flexmock(module).should_receive('map_directories_to_devices').and_return({})
|
|
|
- flexmock(module).should_receive('expand_directories').with_args(
|
|
|
- ('foo', 'bar'), working_directory='/working'
|
|
|
+ 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()
|
|
|
- flexmock(module).should_receive('pattern_root_directories').and_return(())
|
|
|
- flexmock(module).should_receive('expand_directories').with_args(
|
|
|
- (), working_directory='/working'
|
|
|
- ).and_return(())
|
|
|
|
|
|
- assert module.process_source_directories(
|
|
|
- config={'source_directories': ['nope']},
|
|
|
- source_directories=['foo', 'bar'],
|
|
|
- ) == ('foo', 'bar')
|
|
|
+ assert module.process_patterns(
|
|
|
+ (Pattern('foo'), Pattern('bar')),
|
|
|
+ working_directory='/working',
|
|
|
+ ) == [Pattern('foo'), Pattern('bar')]
|
|
|
|
|
|
|
|
|
-def test_process_source_directories_skips_expand_for_requested_paths():
|
|
|
- flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
|
|
- '/working'
|
|
|
+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('deduplicate_directories').and_return(('foo', 'bar'))
|
|
|
- flexmock(module).should_receive('map_directories_to_devices').and_return({})
|
|
|
- flexmock(module).should_receive('expand_directories').with_args(
|
|
|
- ('bar',), working_directory='/working'
|
|
|
+ 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()
|
|
|
- flexmock(module).should_receive('pattern_root_directories').and_return(())
|
|
|
- flexmock(module).should_receive('expand_directories').with_args(
|
|
|
- (), working_directory='/working'
|
|
|
- ).and_return(())
|
|
|
|
|
|
- assert module.process_source_directories(
|
|
|
- config={'source_directories': ['foo', 'bar']}, skip_expand_paths=('foo',)
|
|
|
- ) == ('foo', 'bar')
|
|
|
+ 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():
|
|
@@ -236,7 +275,7 @@ 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('process_source_directories').and_return([])
|
|
|
+ flexmock(module).should_receive('process_patterns').and_return([])
|
|
|
flexmock(module.os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
|
|
create_arguments = flexmock(
|
|
|
repository=None,
|
|
@@ -278,7 +317,7 @@ 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('process_source_directories').and_return([])
|
|
|
+ flexmock(module).should_receive('process_patterns').and_return([])
|
|
|
flexmock(module.os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
|
|
create_arguments = flexmock(
|
|
|
repository=flexmock(),
|
|
@@ -357,7 +396,7 @@ def test_run_create_produces_json():
|
|
|
flexmock(module.borgmatic.hooks.dispatch).should_receive(
|
|
|
'call_hooks_even_if_unconfigured'
|
|
|
).and_return({})
|
|
|
- flexmock(module).should_receive('process_source_directories').and_return([])
|
|
|
+ flexmock(module).should_receive('process_patterns').and_return([])
|
|
|
flexmock(module.os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
|
|
create_arguments = flexmock(
|
|
|
repository=flexmock(),
|