示例#1
0
def test_DownloadLocationJob_remove_link_ignores_errors_from_removing_empty_parent_directories(
        mocker):
    exists_mock = mocker.patch('os.path.exists', return_value=True)
    unlink_mock = mocker.patch('os.unlink')
    removedirs_mock = mocker.patch('os.removedirs',
                                   side_effect=OSError('nope'))
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    mocker.patch.object(
        dlj, '_links_created', {
            'path/to/a': 'path/to/a1',
            'path/to/b': 'path/to/b1',
            'path/to/c': 'path/to/c1',
        })
    dlj._remove_link('path/to/c')
    assert exists_mock.call_args_list == [call('path/to/c')]
    assert unlink_mock.call_args_list == [call('path/to/c')]
    assert removedirs_mock.call_args_list == [call('path/to')]
    assert dlj.output == ()
    assert dlj.errors == ()
    assert dlj.raised is None
    assert dlj._links_created == {
        'path/to/a': 'path/to/a1',
        'path/to/b': 'path/to/b1',
    }
示例#2
0
def test_DownloadLocationJob_get_target_location(file_candidates,
                                                 exp_target_location, mocker,
                                                 tmp_path):
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    mocker.patch.object(type(dlj), 'cache_directory',
                        PropertyMock(return_value=tmp_path))
    mocker.patch.object(dlj, '_create_hardlink')

    for file, candidates in tuple(file_candidates.items()):
        for i, candidate in enumerate(candidates):
            candidates[i]['filepath'] = tmp_path / candidate['filepath']
            candidates[i]['location'] = tmp_path / candidate['location']
            candidate['filepath'].parent.mkdir(parents=True, exist_ok=True)
            if 'corrupt' in candidate['filepath'].name:
                candidate['filepath'].write_text('corrupt')
            else:
                candidate['filepath'].write_text('good')

    mocker.patch.object(dlj,
                        '_get_file_candidates',
                        return_value=file_candidates)

    def verify_file_mock(file):
        filepath = os.path.join(dlj._check_location, file)
        with open(filepath, 'r') as f:
            return f.read() == 'good'

    mocker.patch.object(dlj, '_verify_file', side_effect=verify_file_mock)

    assert dlj._get_target_location() == tmp_path / exp_target_location
示例#3
0
def test_DownloadLocationJob_remove_link_that_cannot_be_removed(mocker):
    exists_mock = mocker.patch('os.path.exists', return_value=True)
    unlink_mock = mocker.patch('os.unlink', side_effect=OSError('nope'))
    removedirs_mock = mocker.patch('os.removedirs')
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    mocker.patch.object(
        dlj, '_links_created', {
            'path/to/a': 'path/to/a1',
            'path/to/b': 'path/to/b1',
            'path/to/c': 'path/to/c1',
        })
    dlj._remove_link('path/to/b')
    assert exists_mock.call_args_list == [call('path/to/b')]
    assert unlink_mock.call_args_list == [call('path/to/b')]
    assert removedirs_mock.call_args_list == []
    assert dlj.output == ()
    assert dlj.errors == ()
    assert type(dlj.raised) is OSError
    assert str(dlj.raised) == 'nope'
    assert dlj._links_created == {
        'path/to/a': 'path/to/a1',
        'path/to/b': 'path/to/b1',
        'path/to/c': 'path/to/c1',
    }
示例#4
0
def test_DownloadLocationJob_execute(locations, default, torf_error,
                                     target_location,
                                     exp_get_target_location_calls, exp_errors,
                                     exp_output, mocker, tmp_path):
    dlj = download_location.DownloadLocationJob(
        torrent='mock.torrent',
        locations=locations,
        default=default,
    )
    mocker.patch.object(dlj,
                        '_get_target_location',
                        return_value=target_location)
    if torf_error:
        mocker.patch('torf.Torrent.read', side_effect=torf_error)
    else:
        mocker.patch('torf.Torrent.read', return_value='mock torrent object')

    dlj.execute()

    assert dlj._get_target_location.call_args_list == exp_get_target_location_calls

    if locations and not torf_error:
        assert dlj._torrent == 'mock torrent object'
    else:
        assert dlj._torrent is None

    assert [type(e)
            for e in dlj.errors] == [type(exp_e) for exp_e in exp_errors]
    assert [str(e) for e in dlj.errors] == [str(exp_e) for exp_e in exp_errors]
    assert dlj.output == exp_output
    assert dlj.raised is None
    assert dlj.is_finished
示例#5
0
def test_DownloadLocationJob_check_location(mocker):
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    mocker.patch.object(type(dlj), 'cache_directory',
                        PropertyMock(return_value='mock/cache/directory'))
    mocker.patch.object(type(dlj), 'name',
                        PropertyMock(return_value='mock_name'))
    assert dlj._check_location == 'mock/cache/directory/mock_name'
示例#6
0
def test_DownloadLocationJob_create_symlink(mocker):
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    mocker.patch.object(dlj, '_create_link', Mock(return_value='foo'))
    assert dlj._create_symlink('path/to/source', 'path/to/target') == 'foo'
    import os
    assert dlj._create_link.call_args_list == [
        call(os.symlink, 'path/to/source', 'path/to/target')
    ]
示例#7
0
def test_DownloadLocationJob_create_link_that_already_exists(mocker):
    exists_mock = mocker.patch('os.path.exists', return_value=True)
    makedirs_mock = mocker.patch('os.makedirs')
    create_link_function = Mock(__qualname__='mylink')
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    dlj._create_link(create_link_function, 'path/to/source', 'path/to/target')
    assert exists_mock.call_args_list == [call('path/to/target')]
    assert makedirs_mock.call_args_list == []
    assert create_link_function.call_args_list == []
    assert dlj.output == ()
    assert dlj.errors == ()
    assert dlj.raised is None
    assert dlj._links_created == {}
示例#8
0
def test_DownloadLocationJob_create_link_fails_to_create_parent_directories(
        mocker):
    exists_mock = mocker.patch('os.path.exists', return_value=False)
    makedirs_mock = mocker.patch('os.makedirs', side_effect=OSError('nope'))
    create_link_function = Mock(__qualname__='mylink')
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    dlj._create_link(create_link_function, 'path/to/source', 'path/to/target')
    assert exists_mock.call_args_list == [call('path/to/target')]
    assert makedirs_mock.call_args_list == [call('path/to', exist_ok=True)]
    assert create_link_function.call_args_list == []
    assert dlj.output == ()
    assert dlj.errors == ('Failed to create directory: path/to: nope', )
    assert dlj.raised is None
    assert dlj._links_created == {}
示例#9
0
def test_DownloadLocationJob_each_file(tmp_path):
    files = (
        tmp_path / '1',
        tmp_path / 'a' / '2',
        tmp_path / 'a' / '3',
        tmp_path / 'a' / 'b' / '4',
        tmp_path / 'a' / 'b' / '5',
        tmp_path / 'a' / 'b' / '6',
        tmp_path / 'c' / '7',
        tmp_path / 'c' / '8',
    )
    for f in files:
        f.parent.mkdir(parents=True, exist_ok=True)
        f.write_bytes(b'mock data')

    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    assert sorted(dlj._each_file(tmp_path / 'a')) == [
        (str(tmp_path / 'a' / '2'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / '3'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / 'b' / '4'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / 'b' / '5'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / 'b' / '6'), str(tmp_path / 'a')),
    ]
    assert sorted(dlj._each_file(tmp_path / 'a', tmp_path / 'c' / '8')) == [
        (str(tmp_path / 'a' / '2'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / '3'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / 'b' / '4'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / 'b' / '5'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / 'b' / '6'), str(tmp_path / 'a')),
        (str(tmp_path / 'c' / '8'), str(tmp_path / 'c' / '8')),
    ]
    assert sorted(dlj._each_file(tmp_path / 'a', tmp_path / 'c')) == [
        (str(tmp_path / 'a' / '2'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / '3'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / 'b' / '4'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / 'b' / '5'), str(tmp_path / 'a')),
        (str(tmp_path / 'a' / 'b' / '6'), str(tmp_path / 'a')),
        (str(tmp_path / 'c' / '7'), str(tmp_path / 'c')),
        (str(tmp_path / 'c' / '8'), str(tmp_path / 'c')),
    ]
    assert sorted(dlj._each_file(tmp_path / 'a' / 'b', tmp_path / 'c')) == [
        (str(tmp_path / 'a' / 'b' / '4'), str(tmp_path / 'a' / 'b')),
        (str(tmp_path / 'a' / 'b' / '5'), str(tmp_path / 'a' / 'b')),
        (str(tmp_path / 'a' / 'b' / '6'), str(tmp_path / 'a' / 'b')),
        (str(tmp_path / 'c' / '7'), str(tmp_path / 'c')),
        (str(tmp_path / 'c' / '8'), str(tmp_path / 'c')),
    ]
示例#10
0
def test_DownloadLocationJob_remove_link_that_was_not_created(mocker):
    exists_mock = mocker.patch('os.path.exists')
    unlink_mock = mocker.patch('os.unlink')
    removedirs_mock = mocker.patch('os.removedirs')
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    mocker.patch.object(dlj, '_links_created',
                        {'path/to/target': 'path/to/source'})
    dlj._remove_link('path/to/different/target')
    assert exists_mock.call_args_list == []
    assert unlink_mock.call_args_list == []
    assert removedirs_mock.call_args_list == []
    assert dlj.output == ()
    assert dlj.errors == ()
    assert dlj.raised is None
    assert dlj._links_created == {'path/to/target': 'path/to/source'}
示例#11
0
def test_DownloadLocationJob_is_size_match(mocker):
    file_size_mock = mocker.patch('upsies.utils.fs.file_size',
                                  return_value=456)
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    files = (Mock(size=123), Mock(size=456), Mock(size=789))
    assert dlj._is_size_match(files[0], 'path/to/foo') is False
    assert file_size_mock.call_args_list == [call('path/to/foo')]
    assert dlj._is_size_match(files[1], 'path/to/bar') is True
    assert file_size_mock.call_args_list == [
        call('path/to/foo'), call('path/to/bar')
    ]
    assert dlj._is_size_match(files[2], 'path/to/baz') is False
    assert file_size_mock.call_args_list == [
        call('path/to/foo'),
        call('path/to/bar'),
        call('path/to/baz')
    ]
示例#12
0
def test_DownloadLocationJob_verify_file(piece_indexes,
                                         verify_piece_return_values,
                                         exp_verify_piece_calls,
                                         exp_return_value, mocker, tmp_path):
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    mocker.patch.object(dlj, '_torrent')
    TorrentFileStream_mock = mocker.patch(
        'upsies.utils.torrent.TorrentFileStream')
    tfs_mock = TorrentFileStream_mock.return_value.__enter__.return_value
    tfs_mock.get_absolute_piece_indexes.return_value = piece_indexes
    tfs_mock.verify_piece.side_effect = verify_piece_return_values

    assert dlj._verify_file('mock/file/path') is exp_return_value

    assert TorrentFileStream_mock.call_args_list == [
        call(dlj._torrent, dlj._check_location)
    ]
    assert tfs_mock.get_absolute_piece_indexes.call_args_list == [
        call('mock/file/path', (1, -2))
    ]
    assert tfs_mock.verify_piece.call_args_list == exp_verify_piece_calls
示例#13
0
def test_DownloadLocationJob_remove_link_that_does_not_exist(mocker):
    exists_mock = mocker.patch('os.path.exists', return_value=False)
    unlink_mock = mocker.patch('os.unlink')
    removedirs_mock = mocker.patch('os.removedirs')
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    mocker.patch.object(
        dlj, '_links_created', {
            'path/to/a': 'path/to/a1',
            'path/to/b': 'path/to/b1',
            'path/to/c': 'path/to/c1',
        })
    dlj._remove_link('path/to/b')
    assert exists_mock.call_args_list == [call('path/to/b')]
    assert unlink_mock.call_args_list == []
    assert removedirs_mock.call_args_list == []
    assert dlj.output == ()
    assert dlj.errors == ()
    assert dlj.raised is None
    assert dlj._links_created == {
        'path/to/a': 'path/to/a1',
        'path/to/b': 'path/to/b1',
        'path/to/c': 'path/to/c1',
    }
示例#14
0
def test_DownloadLocationJob_get_file_candidates(mocker, tmp_path):
    files = (
        tmp_path / '.1',
        tmp_path / '.2',
        tmp_path / '.3',
        tmp_path / 'a' / 'a1',
        tmp_path / 'a' / 'a2',
        tmp_path / 'a' / 'a3',
        tmp_path / 'a' / 'b' / 'ab1',
        tmp_path / 'a' / 'b' / 'ab2',
        tmp_path / 'a' / 'b' / 'ab3',
        tmp_path / 'c' / 'c1',
        tmp_path / 'c' / 'c2',
        tmp_path / 'c' / 'c3',
    )
    for f in files:
        f.parent.mkdir(parents=True, exist_ok=True)
        filesize = int(f.name[-1])
        f.write_bytes(b'x' * filesize)

    dlj = download_location.DownloadLocationJob(
        torrent='mock.torrent',
        locations=(
            tmp_path / 'a',
            tmp_path / 'c',
        ),
    )
    mocker.patch.object(
        dlj,
        '_torrent',
        Mock(files=(
            # Same path
            MockFile('a/a1', size=1),
            # Same path, different file name
            MockFile('a/b/ab.2', size=2),
            # Different path, different file name
            MockFile('c/d/c3', size=3),
            # No matches
            MockFile('foo/bar0', size=0),
            MockFile('foo/baz4', size=4),
        )))

    exp_file_candidates = {
        str('a/a1'): [
            {
                'location': str(tmp_path / 'a'),
                'filepath': str(tmp_path / 'a/a1'),
                'filepath_rel': 'a1',
                'similarity': 0.6666666666666666
            },
        ],
        str('a/b/ab.2'): [
            {
                'location': str(tmp_path / 'a'),
                'filepath': str(tmp_path / 'a/b/ab2'),
                'filepath_rel': 'b/ab2',
                'similarity': 0.7692307692307693
            },
        ],
        str('c/d/c3'): [
            {
                'location': str(tmp_path / 'c'),
                'filepath': str(tmp_path / 'c/c3'),
                'filepath_rel': 'c3',
                'similarity': 0.5
            },
        ],
    }
    file_candidates = dlj._get_file_candidates()

    # for file, cands in file_candidates.items():
    #     print(file)
    #     for c in cands:
    #         print('  ', c)
    # print('--------------')
    # for file, cands in exp_file_candidates.items():
    #     print(file)
    #     for c in cands:
    #         print('  ', c)

    assert file_candidates == exp_file_candidates
示例#15
0
def test_DownloadLocationJob_each_set_of_linked_candidates(mocker, tmp_path):
    dlj = download_location.DownloadLocationJob(torrent='mock.torrent',
                                                locations=('a', 'b', 'c'))
    mocker.patch.object(dlj, '_create_symlink')
    mocker.patch.object(dlj, '_remove_link')

    location = tmp_path / 'links'
    file_candidates = {
        'a': [{
            'filepath': 'a1'
        }],
        'b': [{
            'filepath': 'b1'
        }, {
            'filepath': 'b2'
        }],
        'c': [{
            'filepath': 'c1'
        }, {
            'filepath': 'c2'
        }, {
            'filepath': 'c3'
        }],
    }
    exp = [
        (
            {
                'a': {
                    'filepath': 'a1'
                },
                'b': {
                    'filepath': 'b1'
                },
                'c': {
                    'filepath': 'c1'
                }
            },
            [
                call(os.path.abspath('a1'), os.path.join(location, 'a')),
                call(os.path.abspath('b1'), os.path.join(location, 'b')),
                call(os.path.abspath('c1'), os.path.join(location, 'c')),
            ],
            [],
        ),
        (
            {
                'a': {
                    'filepath': 'a1'
                },
                'b': {
                    'filepath': 'b1'
                },
                'c': {
                    'filepath': 'c2'
                }
            },
            [
                call(os.path.abspath('a1'), os.path.join(location, 'a')),
                call(os.path.abspath('b1'), os.path.join(location, 'b')),
                call(os.path.abspath('c2'), os.path.join(location, 'c')),
            ],
            [
                call(os.path.join(location, 'a')),
                call(os.path.join(location, 'b')),
                call(os.path.join(location, 'c'))
            ],
        ),
        (
            {
                'a': {
                    'filepath': 'a1'
                },
                'b': {
                    'filepath': 'b1'
                },
                'c': {
                    'filepath': 'c3'
                }
            },
            [
                call(os.path.abspath('a1'), os.path.join(location, 'a')),
                call(os.path.abspath('b1'), os.path.join(location, 'b')),
                call(os.path.abspath('c3'), os.path.join(location, 'c')),
            ],
            [
                call(os.path.join(location, 'a')),
                call(os.path.join(location, 'b')),
                call(os.path.join(location, 'c'))
            ],
        ),
        (
            {
                'a': {
                    'filepath': 'a1'
                },
                'b': {
                    'filepath': 'b2'
                },
                'c': {
                    'filepath': 'c1'
                }
            },
            [
                call(os.path.abspath('a1'), os.path.join(location, 'a')),
                call(os.path.abspath('b2'), os.path.join(location, 'b')),
                call(os.path.abspath('c1'), os.path.join(location, 'c')),
            ],
            [
                call(os.path.join(location, 'a')),
                call(os.path.join(location, 'b')),
                call(os.path.join(location, 'c'))
            ],
        ),
        (
            {
                'a': {
                    'filepath': 'a1'
                },
                'b': {
                    'filepath': 'b2'
                },
                'c': {
                    'filepath': 'c2'
                }
            },
            [
                call(os.path.abspath('a1'), os.path.join(location, 'a')),
                call(os.path.abspath('b2'), os.path.join(location, 'b')),
                call(os.path.abspath('c2'), os.path.join(location, 'c')),
            ],
            [
                call(os.path.join(location, 'a')),
                call(os.path.join(location, 'b')),
                call(os.path.join(location, 'c'))
            ],
        ),
        (
            {
                'a': {
                    'filepath': 'a1'
                },
                'b': {
                    'filepath': 'b2'
                },
                'c': {
                    'filepath': 'c3'
                }
            },
            [
                call(os.path.abspath('a1'), os.path.join(location, 'a')),
                call(os.path.abspath('b2'), os.path.join(location, 'b')),
                call(os.path.abspath('c3'), os.path.join(location, 'c')),
            ],
            [
                call(os.path.join(location, 'a')),
                call(os.path.join(location, 'b')),
                call(os.path.join(location, 'c'))
            ],
        ),
    ]

    for paths in dlj._each_set_of_linked_candidates(location, file_candidates):
        paths, create_symlink_calls, remove_link_calls = exp.pop(0)
        assert paths == paths
        assert dlj._create_symlink.call_args_list == create_symlink_calls
        assert dlj._remove_link.call_args_list == remove_link_calls
        dlj._create_symlink.reset_mock()
        dlj._remove_link.reset_mock()