def test_get_absolute_piece_indexes(piece_size, files, file,
                                    relative_piece_indexes,
                                    exp_absolute_indexes, tmp_path):
    torrent = Torrent(piece_size=piece_size, files=files)
    tfs = TorrentFileStream(torrent, location=tmp_path)
    file = [f for f in files if f == file][0]
    assert tfs.get_absolute_piece_indexes(
        file, relative_piece_indexes) == exp_absolute_indexes
def test_get_open_file_gets_nonexisting_file(mocker):
    exists_mock = mocker.patch('os.path.exists', return_value=False)
    open_mock = mocker.patch('__main__.open')
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='foo/path')
    assert tfs._get_open_file('b') is None
    assert exists_mock.call_args_list == [call('foo/path/b')]
    assert open_mock.call_args_list == []
def test_get_file_size_from_fs_gets_nonexisting_file(mocker):
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='path/to')
    exists_mock = mocker.patch('os.path.exists', return_value=False)
    getsize_mock = mocker.patch('os.path.getsize', return_value=123456)
    assert tfs._get_file_size_from_fs('b') is None
    assert exists_mock.call_args_list == [call('path/to/b')]
    assert getsize_mock.call_args_list == []
def test_get_file_size_from_fs_gets_private_file(mocker):
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='path/to')
    exists_mock = mocker.patch('os.path.exists', return_value=True)
    getsize_mock = mocker.patch('os.path.getsize',
                                side_effect=PermissionError('Size is secret'))
    assert tfs._get_file_size_from_fs('b') is None
    assert exists_mock.call_args_list == [call('path/to/b')]
    assert getsize_mock.call_args_list == [call('path/to/b')]
def test_get_open_file_opens_file_only_once(mocker):
    exists_mock = mocker.patch('os.path.exists', return_value=True)
    fh1, fh2 = (Mock(), Mock())
    open_mock = mocker.patch('builtins.open', side_effect=(fh1, fh2))
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='foo/path')
    for _ in range(5):
        assert tfs._get_open_file('b') == fh1
    assert exists_mock.call_args_list == [call('foo/path/b')]
    assert open_mock.call_args_list == [call('foo/path/b', 'rb')]
def test_get_piece_raises_if_piece_index_is_out_of_bounds(
        piece_size, files, piece_index, exp_min_piece_index,
        exp_max_piece_index, tmp_path):
    torrent = Torrent(piece_size=piece_size, files=files)
    tfs = TorrentFileStream(torrent, location=tmp_path)
    with pytest.raises(
            ValueError,
            match=
            rf'^piece_index must be in range 0 - {exp_max_piece_index}: {piece_index}$'
    ):
        tfs.get_piece(piece_index)
def test_get_piece_hash_from_unreadable_piece(mocker):
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='foo/path')
    get_piece_mock = mocker.patch.object(tfs, 'get_piece', return_value=None)
    sha1_mock = mocker.patch(
        'hashlib.sha1',
        return_value=Mock(digest=Mock(return_value=b'mock hash')))
    assert tfs.get_piece_hash(123) is None
    assert get_piece_mock.call_args_list == [call(123)]
    assert sha1_mock.call_args_list == []
def test_get_file_at_position(files, position, exp_filename, exp_error,
                              tmp_path):
    torrent = Torrent(piece_size=4, files=files)
    tfs = TorrentFileStream(torrent, location=tmp_path)

    if exp_error:
        with pytest.raises(ValueError, match=rf'^{re.escape(exp_error)}$'):
            tfs.get_file_at_position(position)
    else:
        file = [f for f in files if f == exp_filename][0]
        assert tfs.get_file_at_position(position) == file
def test_get_open_file_fails_to_open_file(mocker):
    exists_mock = mocker.patch('os.path.exists', return_value=True)
    open_mock = mocker.patch('builtins.open',
                             side_effect=PermissionError('nope'))
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='foo/path')
    with pytest.raises(errors.ContentError,
                       match=r'^Failed to open foo/path/b: nope$'):
        tfs._get_open_file('b')
    assert exists_mock.call_args_list == [call('foo/path/b')]
    assert open_mock.call_args_list == [call('foo/path/b', 'rb')]
示例#10
0
def test_close(mocker):
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='foo/path')
    mocked_open_files = [Mock(), Mock(), Mock()]
    tfs._open_files = {
        f'path/to/{i}': mof
        for i, mof in enumerate(mocked_open_files)
    }
    tfs.close()
    for mof in mocked_open_files:
        assert mof.close.call_args_list == [call()]
    assert tfs._open_files == {}
示例#11
0
def test_get_files_for_byte_range(first_byte_indexes, last_byte_indexes, files,
                                  exp_files, tmp_path):
    torrent = Torrent(piece_size=12, files=files)
    tfs = TorrentFileStream(torrent, location=tmp_path)
    first_byte_indexes = tuple(first_byte_indexes)
    last_byte_indexes = tuple(last_byte_indexes)
    assert first_byte_indexes, first_byte_indexes
    assert last_byte_indexes, last_byte_indexes
    for first_byte_index in first_byte_indexes:
        for last_byte_index in last_byte_indexes:
            if first_byte_index <= last_byte_index:
                assert tfs.get_files_for_byte_range(
                    first_byte_index, last_byte_index) == exp_files
示例#12
0
def test_get_file_position(tmp_path):
    torrent = Torrent(
        piece_size=12,
        files=(
            File('foo', size=12),
            File('bar', size=15),
            File('baz', size=24),
            File('bam', size=5),
        ),
    )
    tfs = TorrentFileStream(torrent, location=tmp_path)
    assert tfs.get_file_position(torrent.files[0]) == 0
    assert tfs.get_file_position(torrent.files[1]) == 12
    assert tfs.get_file_position(torrent.files[2]) == 27
    assert tfs.get_file_position(torrent.files[3]) == 51
示例#13
0
def test_get_piece_returns_None_if_file_is_missing(files, missing_files,
                                                   piece_index, exp_piece,
                                                   tmp_path):
    for f in files:
        if f not in missing_files:
            print(f'writing {f}: {f.size} bytes: {f.content}')
            f.write_at(tmp_path)
        else:
            print(f'not writing {f}: {f.size} bytes: {f.content}')

    torrent = Torrent(piece_size=12, files=files)
    tfs = TorrentFileStream(torrent, location=tmp_path)
    if exp_piece is None:
        assert tfs.get_piece(piece_index) is None
    else:
        assert isinstance(tfs.get_piece(piece_index), bytes)
示例#14
0
def test_get_piece_resets_seek_position_when_reusing_file_handle(
        piece_size, tmp_path):
    files = (
        File('a', size=12),
        File('b', size=13),
        File('c', size=7),
        File('d', size=16),
    )
    for f in files:
        print(f'{f}: {f.size} bytes: {f.content}')
        f.write_at(tmp_path)
    stream = b''.join(f.content for f in files)
    print('concatenated stream:', stream)

    total_size = sum(f.size for f in files)
    max_piece_index = math.floor((total_size - 1) // piece_size)
    for piece_index in range(max_piece_index + 1):
        print('testing piece:', piece_index)
        start = piece_index * piece_size
        stop = min(start + piece_size, len(stream))
        exp_piece = stream[start:stop]
        print('exp_piece:', f'[{start}:{stop}]:', exp_piece)
        exp_piece_length = stop - start
        assert len(exp_piece) == exp_piece_length

        torrent = Torrent(piece_size=piece_size, files=files)
        with TorrentFileStream(torrent, location=tmp_path) as tfs:
            for i in range(10):
                piece = tfs.get_piece(piece_index)
                assert piece == exp_piece
示例#15
0
def test_behaviour_as_context_manager(mocker):
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='foo/path')
    mocker.patch.object(tfs, 'close')
    assert tfs.close.call_args_list == []
    with tfs as x:
        assert x is tfs
        assert tfs.close.call_args_list == []
    assert tfs.close.call_args_list == [call()]
示例#16
0
def test_verify_piece_returns_None_for_nonexisting_files(mocker):
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='foo/path')
    mocker.patch.object(type(tfs), 'torrent_piece_hashes',
                        PropertyMock(return_value=(b'foo', b'bar', b'baz'), ))
    mocker.patch.object(tfs, 'get_piece_hash', return_value=None)
    assert tfs.verify_piece(0) is None
    assert tfs.verify_piece(1) is None
    assert tfs.verify_piece(2) is None
    with pytest.raises(ValueError,
                       match=r'^piece_index must be in range 0 - 2: 3$'):
        tfs.verify_piece(3)
示例#17
0
def test_get_piece_returns_piece_from_file(piece_size, files, piece_index,
                                           tmp_path):
    for f in files:
        print(f'{f}: {f.size} bytes: {f.content}')
        f.write_at(tmp_path)

    stream = b''.join(f.content for f in files)
    print('concatenated stream:', stream)
    start = piece_index * piece_size
    stop = min(start + piece_size, len(stream))
    exp_piece = stream[start:stop]
    print('exp_piece:', f'[{start}:{stop}]:', exp_piece)
    exp_piece_length = stop - start
    assert len(exp_piece) == exp_piece_length

    torrent = Torrent(piece_size=piece_size, files=files)
    with TorrentFileStream(torrent, location=tmp_path) as tfs:
        piece = tfs.get_piece(piece_index)
        assert piece == exp_piece
示例#18
0
def test_torrent_piece_hashes(mocker):
    torrent = Torrent(piece_size=123,
                      files=(File('a', 1), File('b', 2), File('c', 3)))
    tfs = TorrentFileStream(torrent, location='foo/path')
    mocker.patch.object(
        tfs, '_torrent',
        Mock(
            metainfo={
                'info': {
                    'pieces': (b'01234567890123456789'
                               b'abcdefghijklmnopqrst'
                               b'ABCDEFGHIJKLMNOPQRST')
                }
            }))
    assert tfs.torrent_piece_hashes == (
        b'01234567890123456789',
        b'abcdefghijklmnopqrst',
        b'ABCDEFGHIJKLMNOPQRST',
    )
示例#19
0
def test_get_piece_returns_None_if_file_size_is_wrong(piece_size, files,
                                                      contents, piece_index,
                                                      exp_piece, tmp_path):
    for f in files:
        content = contents.get(f, f.content)
        print(
            f'{f}: {f.size} bytes: {content}, real size: {len(content)} bytes')
        f.write_at(tmp_path, content)

    if exp_piece is not None:
        stream = b''.join(f.content for f in files)
        print('concatenated stream:', stream)
        start = piece_index * piece_size
        stop = min(start + piece_size, len(stream))
        exp_piece = stream[start:stop]
        print('exp_piece:', f'[{start}:{stop}]:', exp_piece)
        exp_piece_length = stop - start
        assert len(exp_piece) == exp_piece_length

    torrent = Torrent(piece_size=piece_size, files=files)
    with TorrentFileStream(torrent, location=tmp_path) as tfs:
        assert tfs.get_piece(piece_index) == exp_piece
示例#20
0
def test_get_piece_indexes_for_file(files, exp_piece_indexes, tmp_path):
    torrent = Torrent(piece_size=12, files=files)
    tfs = TorrentFileStream(torrent, location=tmp_path)
    for filename, exp_indexes in exp_piece_indexes.items():
        file = [f for f in torrent.files if f == filename][0]
        assert tfs.get_piece_indexes_for_file(file) == exp_indexes