|
@@ -1,11 +1,19 @@
|
|
|
import io
|
|
|
+import os.path
|
|
|
|
|
|
from msgpack import packb
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
-from ..hashindex import ChunkIndex, CacheSynchronizer
|
|
|
from .hashindex import H
|
|
|
+from .key import TestKey
|
|
|
+from ..archive import Statistics
|
|
|
+from ..cache import AdHocCache
|
|
|
+from ..compress import CompressionSpec
|
|
|
+from ..crypto.key import RepoKey
|
|
|
+from ..hashindex import ChunkIndex, CacheSynchronizer
|
|
|
+from ..helpers import Manifest
|
|
|
+from ..repository import Repository
|
|
|
|
|
|
|
|
|
class TestCacheSynchronizer:
|
|
@@ -196,3 +204,76 @@ class TestCacheSynchronizer:
|
|
|
assert index[H(0)] == (ChunkIndex.MAX_VALUE, 1234, 5678)
|
|
|
sync.feed(data)
|
|
|
assert index[H(0)] == (ChunkIndex.MAX_VALUE, 1234, 5678)
|
|
|
+
|
|
|
+
|
|
|
+class TestAdHocCache:
|
|
|
+ @pytest.yield_fixture
|
|
|
+ def repository(self, tmpdir):
|
|
|
+ self.repository_location = os.path.join(str(tmpdir), 'repository')
|
|
|
+ with Repository(self.repository_location, exclusive=True, create=True) as repository:
|
|
|
+ repository.put(H(1), b'1234')
|
|
|
+ repository.put(Manifest.MANIFEST_ID, b'5678')
|
|
|
+ yield repository
|
|
|
+
|
|
|
+ @pytest.fixture
|
|
|
+ def key(self, repository, monkeypatch):
|
|
|
+ monkeypatch.setenv('BORG_PASSPHRASE', 'test')
|
|
|
+ key = RepoKey.create(repository, TestKey.MockArgs())
|
|
|
+ key.compressor = CompressionSpec('none').compressor
|
|
|
+ return key
|
|
|
+
|
|
|
+ @pytest.fixture
|
|
|
+ def manifest(self, repository, key):
|
|
|
+ Manifest(key, repository).write()
|
|
|
+ return Manifest.load(repository, key=key, operations=Manifest.NO_OPERATION_CHECK)[0]
|
|
|
+
|
|
|
+ @pytest.fixture
|
|
|
+ def cache(self, repository, key, manifest):
|
|
|
+ return AdHocCache(repository, key, manifest)
|
|
|
+
|
|
|
+ def test_does_not_contain_manifest(self, cache):
|
|
|
+ assert not cache.seen_chunk(Manifest.MANIFEST_ID)
|
|
|
+
|
|
|
+ def test_does_not_delete_existing_chunks(self, repository, cache):
|
|
|
+ assert cache.seen_chunk(H(1)) == ChunkIndex.MAX_VALUE
|
|
|
+ cache.chunk_decref(H(1), Statistics())
|
|
|
+ assert repository.get(H(1)) == b'1234'
|
|
|
+
|
|
|
+ def test_does_not_overwrite(self, cache):
|
|
|
+ with pytest.raises(AssertionError):
|
|
|
+ cache.add_chunk(H(1), b'5678', Statistics(), overwrite=True)
|
|
|
+
|
|
|
+ def test_seen_chunk_add_chunk_size(self, cache):
|
|
|
+ assert cache.add_chunk(H(1), b'5678', Statistics()) == (H(1), 4, 0)
|
|
|
+
|
|
|
+ def test_deletes_chunks_during_lifetime(self, cache, repository):
|
|
|
+ """E.g. checkpoint archives"""
|
|
|
+ cache.add_chunk(H(5), b'1010', Statistics())
|
|
|
+ assert cache.seen_chunk(H(5)) == 1
|
|
|
+ cache.chunk_decref(H(5), Statistics())
|
|
|
+ assert not cache.seen_chunk(H(5))
|
|
|
+ with pytest.raises(Repository.ObjectNotFound):
|
|
|
+ repository.get(H(5))
|
|
|
+
|
|
|
+ def test_files_cache(self, cache):
|
|
|
+ assert cache.file_known_and_unchanged(bytes(32), None) is None
|
|
|
+ assert not cache.do_files
|
|
|
+ assert cache.files is None
|
|
|
+
|
|
|
+ def test_txn(self, cache):
|
|
|
+ assert not cache._txn_active
|
|
|
+ cache.seen_chunk(H(5))
|
|
|
+ assert cache._txn_active
|
|
|
+ assert cache.chunks
|
|
|
+ cache.rollback()
|
|
|
+ assert not cache._txn_active
|
|
|
+ assert not hasattr(cache, 'chunks')
|
|
|
+
|
|
|
+ def test_incref_after_add_chunk(self, cache):
|
|
|
+ assert cache.add_chunk(H(3), b'5678', Statistics()) == (H(3), 4, 47)
|
|
|
+ assert cache.chunk_incref(H(3), Statistics()) == (H(3), 4, 47)
|
|
|
+
|
|
|
+ def test_existing_incref_after_add_chunk(self, cache):
|
|
|
+ """This case occurs with part files, see Archive.chunk_file."""
|
|
|
+ assert cache.add_chunk(H(1), b'5678', Statistics()) == (H(1), 4, 0)
|
|
|
+ assert cache.chunk_incref(H(1), Statistics()) == (H(1), 4, 0)
|