From 130380d6ceb810712b449ed183cd2abe03129bf9 Mon Sep 17 00:00:00 2001 From: Daniel <d.hornung@indiscale.com> Date: Thu, 13 Apr 2023 13:55:27 +0200 Subject: [PATCH] TEST: Added and improved unit tests. --- unittests/test_cached.py | 103 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 8 deletions(-) diff --git a/unittests/test_cached.py b/unittests/test_cached.py index 6e87c12a..ce302d67 100644 --- a/unittests/test_cached.py +++ b/unittests/test_cached.py @@ -21,7 +21,7 @@ """ Test the caosdb.cached module """ -from caosdb.cached import (cached_get_entity_by, cache_clear, cache_info, fill_cache, +from caosdb.cached import (cached_get_entity_by, cache_clear, cache_info, cache_fill, AccessType, cache_initialize, cached_query) from unittest.mock import patch import caosdb as db @@ -60,11 +60,17 @@ def mocked_path_query(path): return deepcopy([el for el in DUMMY_SERVER_CONTENT if el.path == path][0]) -def mocked_gen_query(q): - if q == 'a': - return db.Container().extend([DUMMY_SERVER_CONTENT[0]]) +def mocked_gen_query(q, unique): + if unique: + if q == 'a': + return DUMMY_SERVER_CONTENT[0] + else: + return None else: - return db.Container().extend(DUMMY_SERVER_CONTENT) + if q == 'a': + return db.Container().extend([DUMMY_SERVER_CONTENT[0]]) + else: + return db.Container().extend(DUMMY_SERVER_CONTENT) @patch("caosdb.utils.get_entity.get_entity_by_name") @@ -87,7 +93,7 @@ def test_get_by_name(mocked_get_by_name): cached_get_entity_by(name='a') assert mocked_get_by_name.call_count == 2 # we fill the cache manually and make sure the element is used - fill_cache({'lol': db.Entity(id=10001, name='lol')}, AccessType.NAME, unique=True) + cache_fill({'lol': db.Entity(id=10001, name='lol')}, AccessType.NAME, unique=True) # there are now two elements in the cache: a and lol assert cache_info().currsize == 2 # we can retrieve the inserted element @@ -122,7 +128,7 @@ def test_get_by_id(mocked_get_by_id): cached_get_entity_by(eid=102) assert mocked_get_by_id.call_count == 2 # we fill the cache manually and make sure the element is used - fill_cache({10001: db.Entity(id=10001, name='lol')}, AccessType.EID, unique=True) + cache_fill({10001: db.Entity(id=10001, name='lol')}, AccessType.EID, unique=True) # there are now two elements in the cache: a and lol assert cache_info().currsize == 2 # we can retrieve the inserted element @@ -156,7 +162,7 @@ def test_get_by_path(mocked_get_by_path): cached_get_entity_by(path='p') assert mocked_get_by_path.call_count == 2 # we fill the cache manually and make sure the element is used - fill_cache({'lol': db.File(id=10001, path='lol')}, AccessType.PATH, unique=True) + cache_fill({'lol': db.File(id=10001, path='lol')}, AccessType.PATH, unique=True) # there are now two elements in the cache: a and lol assert cache_info().currsize == 2 # we can retrieve the inserted element @@ -170,6 +176,35 @@ def test_get_by_path(mocked_get_by_path): assert c.id == 105 +@patch("caosdb.cached.execute_query") +def test_get_by_query(mocked_query): + mocked_query.side_effect = mocked_gen_query + # test cache initialization + cache_initialize(maxsize=10) + assert cache_info().currsize == 0 + + # Non-existent entity + res = cached_get_entity_by(query='stuff') + assert res is None + assert cache_info().currsize == 1 + assert cache_info().hits == 0 + assert cache_info().misses == 1 + + res = cached_get_entity_by(query='stuff') + assert res is None + assert cache_info().currsize == 1 + assert cache_info().hits == 1 + assert cache_info().misses == 1 + + # Existent entity + a = cached_get_entity_by(query='a') + assert a is not None + assert a.id == 101 + assert cache_info().currsize == 2 + assert cache_info().hits == 1 + assert cache_info().misses == 2 + + @patch("caosdb.cached.execute_query") def test_cached_query(mocked_query): mocked_query.side_effect = mocked_gen_query @@ -206,3 +241,55 @@ def test_cached_query(mocked_query): c = cached_query('a') assert mocked_query.call_count == 3 assert c[0].id == 101 + + +@patch("caosdb.utils.get_entity.get_entity_by_name") +def test_cache_size(mocked_get_by_name): + mocked_get_by_name.side_effect = lambda x: x + # first call; not in cache -> mocked_execute is touched + maxsize = 5 + cache_initialize(maxsize=maxsize) + assert cache_info().currsize == 0 + + names_first = ("a", "b", "c", "d", "e") + names_later = ("A", "B", "C", "D", "E") + names_fill = {"X": None, "Y": None, "Z": None} + + # Use the first batch of names + for ii, name in enumerate(names_first, start=1): + cached_get_entity_by(name=name) + assert cache_info().currsize == ii + assert cache_info().hits == 0 + assert cache_info().misses == ii + for ii, name in enumerate(names_first, start=1): + cached_get_entity_by(name=name) + assert cache_info().currsize == maxsize + assert cache_info().hits == ii + assert cache_info().misses == maxsize + + # use the second batch of names + for ii, name in enumerate(names_later, start=1): + cached_get_entity_by(name=name) + assert cache_info().currsize == maxsize + assert cache_info().hits == len(names_first) + assert cache_info().misses == len(names_first) + ii + for ii, name in enumerate(names_later, start=1): + cached_get_entity_by(name=name) + assert cache_info().currsize == maxsize + assert cache_info().hits == len(names_first) + ii + assert cache_info().misses == len(names_first) + len(names_later) + + # The cache is now filled with A,B,C,D,E (oldest to least recently used). + # Let's fill it with X,Y,Z. + cache_fill(names_fill, kind=AccessType.NAME) + + # Now, the cache should be: D,E,X,Y,Z + current_misses = cache_info().misses + + for name in ("Z", "Y", "X", "E", "D"): + cached_get_entity_by(name=name) + assert cache_info().misses == current_misses + + for ii, name in enumerate(("A", "B", "C"), start=1): + cached_get_entity_by(name=name) + assert cache_info().misses == current_misses + ii -- GitLab