|
@@ -12,6 +12,7 @@ from ..crypto.key import PlaintextKey
|
|
from ..helpers import IntegrityError
|
|
from ..helpers import IntegrityError
|
|
from ..repoobj import RepoObj
|
|
from ..repoobj import RepoObj
|
|
from .hashindex import H
|
|
from .hashindex import H
|
|
|
|
+from .repository import fchunk, pdchunk
|
|
from .key import TestKey
|
|
from .key import TestKey
|
|
|
|
|
|
|
|
|
|
@@ -74,9 +75,9 @@ class TestRepositoryCache:
|
|
def repository(self, tmpdir):
|
|
def repository(self, tmpdir):
|
|
self.repository_location = os.path.join(str(tmpdir), "repository")
|
|
self.repository_location = os.path.join(str(tmpdir), "repository")
|
|
with Repository(self.repository_location, exclusive=True, create=True) as repository:
|
|
with Repository(self.repository_location, exclusive=True, create=True) as repository:
|
|
- repository.put(H(1), b"1234")
|
|
|
|
- repository.put(H(2), b"5678")
|
|
|
|
- repository.put(H(3), bytes(100))
|
|
|
|
|
|
+ repository.put(H(1), fchunk(b"1234"))
|
|
|
|
+ repository.put(H(2), fchunk(b"5678"))
|
|
|
|
+ repository.put(H(3), fchunk(bytes(100)))
|
|
yield repository
|
|
yield repository
|
|
|
|
|
|
@pytest.fixture
|
|
@pytest.fixture
|
|
@@ -85,19 +86,55 @@ class TestRepositoryCache:
|
|
|
|
|
|
def test_simple(self, cache: RepositoryCache):
|
|
def test_simple(self, cache: RepositoryCache):
|
|
# Single get()s are not cached, since they are used for unique objects like archives.
|
|
# Single get()s are not cached, since they are used for unique objects like archives.
|
|
- assert cache.get(H(1)) == b"1234"
|
|
|
|
|
|
+ assert pdchunk(cache.get(H(1))) == b"1234"
|
|
assert cache.misses == 1
|
|
assert cache.misses == 1
|
|
assert cache.hits == 0
|
|
assert cache.hits == 0
|
|
|
|
|
|
- assert list(cache.get_many([H(1)])) == [b"1234"]
|
|
|
|
|
|
+ assert [pdchunk(ch) for ch in cache.get_many([H(1)])] == [b"1234"]
|
|
assert cache.misses == 2
|
|
assert cache.misses == 2
|
|
assert cache.hits == 0
|
|
assert cache.hits == 0
|
|
|
|
|
|
- assert list(cache.get_many([H(1)])) == [b"1234"]
|
|
|
|
|
|
+ assert [pdchunk(ch) for ch in cache.get_many([H(1)])] == [b"1234"]
|
|
assert cache.misses == 2
|
|
assert cache.misses == 2
|
|
assert cache.hits == 1
|
|
assert cache.hits == 1
|
|
|
|
|
|
- assert cache.get(H(1)) == b"1234"
|
|
|
|
|
|
+ assert pdchunk(cache.get(H(1))) == b"1234"
|
|
|
|
+ assert cache.misses == 2
|
|
|
|
+ assert cache.hits == 2
|
|
|
|
+
|
|
|
|
+ def test_meta(self, cache: RepositoryCache):
|
|
|
|
+ # same as test_simple, but not reading the chunk data (metadata only).
|
|
|
|
+ # Single get()s are not cached, since they are used for unique objects like archives.
|
|
|
|
+ assert pdchunk(cache.get(H(1), read_data=False)) == b""
|
|
|
|
+ assert cache.misses == 1
|
|
|
|
+ assert cache.hits == 0
|
|
|
|
+
|
|
|
|
+ assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=False)] == [b""]
|
|
|
|
+ assert cache.misses == 2
|
|
|
|
+ assert cache.hits == 0
|
|
|
|
+
|
|
|
|
+ assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=False)] == [b""]
|
|
|
|
+ assert cache.misses == 2
|
|
|
|
+ assert cache.hits == 1
|
|
|
|
+
|
|
|
|
+ assert pdchunk(cache.get(H(1), read_data=False)) == b""
|
|
|
|
+ assert cache.misses == 2
|
|
|
|
+ assert cache.hits == 2
|
|
|
|
+
|
|
|
|
+ def test_mixed(self, cache: RepositoryCache):
|
|
|
|
+ assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=False)] == [b""]
|
|
|
|
+ assert cache.misses == 1
|
|
|
|
+ assert cache.hits == 0
|
|
|
|
+
|
|
|
|
+ assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=True)] == [b"1234"]
|
|
|
|
+ assert cache.misses == 2
|
|
|
|
+ assert cache.hits == 0
|
|
|
|
+
|
|
|
|
+ assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=False)] == [b""]
|
|
|
|
+ assert cache.misses == 2
|
|
|
|
+ assert cache.hits == 1
|
|
|
|
+
|
|
|
|
+ assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=True)] == [b"1234"]
|
|
assert cache.misses == 2
|
|
assert cache.misses == 2
|
|
assert cache.hits == 2
|
|
assert cache.hits == 2
|
|
|
|
|
|
@@ -105,11 +142,11 @@ class TestRepositoryCache:
|
|
def query_size_limit():
|
|
def query_size_limit():
|
|
cache.size_limit = 0
|
|
cache.size_limit = 0
|
|
|
|
|
|
- assert list(cache.get_many([H(1), H(2)])) == [b"1234", b"5678"]
|
|
|
|
|
|
+ assert [pdchunk(ch) for ch in cache.get_many([H(1), H(2)])] == [b"1234", b"5678"]
|
|
assert cache.misses == 2
|
|
assert cache.misses == 2
|
|
assert cache.evictions == 0
|
|
assert cache.evictions == 0
|
|
iterator = cache.get_many([H(1), H(3), H(2)])
|
|
iterator = cache.get_many([H(1), H(3), H(2)])
|
|
- assert next(iterator) == b"1234"
|
|
|
|
|
|
+ assert pdchunk(next(iterator)) == b"1234"
|
|
|
|
|
|
# Force cache to back off
|
|
# Force cache to back off
|
|
qsl = cache.query_size_limit
|
|
qsl = cache.query_size_limit
|
|
@@ -120,11 +157,11 @@ class TestRepositoryCache:
|
|
assert cache.evictions == 2
|
|
assert cache.evictions == 2
|
|
assert H(1) not in cache.cache
|
|
assert H(1) not in cache.cache
|
|
assert H(2) not in cache.cache
|
|
assert H(2) not in cache.cache
|
|
- assert next(iterator) == bytes(100)
|
|
|
|
|
|
+ assert pdchunk(next(iterator)) == bytes(100)
|
|
assert cache.slow_misses == 0
|
|
assert cache.slow_misses == 0
|
|
# Since H(2) was in the cache when we called get_many(), but has
|
|
# Since H(2) was in the cache when we called get_many(), but has
|
|
# been evicted during iterating the generator, it will be a slow miss.
|
|
# been evicted during iterating the generator, it will be a slow miss.
|
|
- assert next(iterator) == b"5678"
|
|
|
|
|
|
+ assert pdchunk(next(iterator)) == b"5678"
|
|
assert cache.slow_misses == 1
|
|
assert cache.slow_misses == 1
|
|
|
|
|
|
def test_enospc(self, cache: RepositoryCache):
|
|
def test_enospc(self, cache: RepositoryCache):
|
|
@@ -145,16 +182,16 @@ class TestRepositoryCache:
|
|
pass
|
|
pass
|
|
|
|
|
|
iterator = cache.get_many([H(1), H(2), H(3)])
|
|
iterator = cache.get_many([H(1), H(2), H(3)])
|
|
- assert next(iterator) == b"1234"
|
|
|
|
|
|
+ assert pdchunk(next(iterator)) == b"1234"
|
|
|
|
|
|
with patch("builtins.open", enospc_open):
|
|
with patch("builtins.open", enospc_open):
|
|
- assert next(iterator) == b"5678"
|
|
|
|
|
|
+ assert pdchunk(next(iterator)) == b"5678"
|
|
assert cache.enospc == 1
|
|
assert cache.enospc == 1
|
|
# We didn't patch query_size_limit which would set size_limit to some low
|
|
# We didn't patch query_size_limit which would set size_limit to some low
|
|
# value, so nothing was actually evicted.
|
|
# value, so nothing was actually evicted.
|
|
assert cache.evictions == 0
|
|
assert cache.evictions == 0
|
|
|
|
|
|
- assert next(iterator) == bytes(100)
|
|
|
|
|
|
+ assert pdchunk(next(iterator)) == bytes(100)
|
|
|
|
|
|
@pytest.fixture
|
|
@pytest.fixture
|
|
def key(self, repository, monkeypatch):
|
|
def key(self, repository, monkeypatch):
|
|
@@ -193,7 +230,8 @@ class TestRepositoryCache:
|
|
iterator = decrypted_cache.get_many([H1, H2, H3])
|
|
iterator = decrypted_cache.get_many([H1, H2, H3])
|
|
assert next(iterator) == (4, b"1234")
|
|
assert next(iterator) == (4, b"1234")
|
|
|
|
|
|
- with open(decrypted_cache.key_filename(H2), "a+b") as fd:
|
|
|
|
|
|
+ pkey = decrypted_cache.prefixed_key(H2, complete=True)
|
|
|
|
+ with open(decrypted_cache.key_filename(pkey), "a+b") as fd:
|
|
fd.seek(-1, io.SEEK_END)
|
|
fd.seek(-1, io.SEEK_END)
|
|
corrupted = (int.from_bytes(fd.read(), "little") ^ 2).to_bytes(1, "little")
|
|
corrupted = (int.from_bytes(fd.read(), "little") ^ 2).to_bytes(1, "little")
|
|
fd.seek(-1, io.SEEK_END)
|
|
fd.seek(-1, io.SEEK_END)
|