Skip to content

Commit de88dd4

Browse files
committed
Generalize stateful store test
1 parent 8c5038a commit de88dd4

File tree

3 files changed

+35
-13
lines changed

3 files changed

+35
-13
lines changed

src/zarr/testing/strategies.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
)
3333
array_names = node_names
3434
attrs = st.none() | st.dictionaries(_attr_keys, _attr_values)
35-
paths = st.lists(node_names, min_size=1).map(lambda x: "/".join(x)) | st.just("/")
35+
keys = st.lists(node_names, min_size=1).map(lambda x: "/".join(x))
36+
paths = st.just("/") | keys
3637
np_arrays = npst.arrays(
3738
# TODO: re-enable timedeltas once they are supported
3839
dtype=npst.scalar_dtypes().filter(lambda x: x.kind != "m"),

tests/v3/conftest.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from hypothesis import HealthCheck, Verbosity, settings
1111

1212
from zarr import AsyncGroup, config
13+
from zarr.abc.store import Store
14+
from zarr.core.sync import sync
1315
from zarr.store import LocalStore, MemoryStore, StorePath, ZipStore
1416
from zarr.store.remote import RemoteStore
1517

@@ -20,7 +22,6 @@
2022

2123
from _pytest.compat import LEGACY_PATH
2224

23-
from zarr.abc.store import Store
2425
from zarr.core.common import ChunkCoords, MemoryOrder, ZarrFormat
2526

2627

@@ -77,6 +78,14 @@ async def store(request: pytest.FixtureRequest, tmpdir: LEGACY_PATH) -> Store:
7778
return await parse_store(param, str(tmpdir))
7879

7980

81+
@pytest.fixture(params=["local", "memory", "zip"])
82+
def sync_store(request: pytest.FixtureRequest, tmpdir: LEGACY_PATH) -> Store:
83+
result = sync(parse_store(request.param, str(tmpdir)))
84+
if not isinstance(result, Store):
85+
raise TypeError("Wrong store class returned by test fixture! got " + result + " instead")
86+
return result
87+
88+
8089
@dataclass
8190
class AsyncGroupRequest:
8291
zarr_format: ZarrFormat

tests/v3/test_store/test_stateful_store.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
# Stateful tests for arbitrary Zarr stores.
2-
3-
42
import hypothesis.strategies as st
3+
import pytest
54
from hypothesis import assume, note
65
from hypothesis.stateful import (
76
RuleBasedStateMachine,
7+
Settings,
8+
initialize,
89
invariant,
910
precondition,
1011
rule,
12+
run_state_machine_as_test,
1113
)
1214
from hypothesis.strategies import DataObject
1315

1416
import zarr
1517
from zarr.abc.store import AccessMode, Store
1618
from zarr.core.buffer import BufferPrototype, cpu, default_buffer_prototype
17-
from zarr.store import MemoryStore
18-
from zarr.testing.strategies import key_ranges, paths
19+
from zarr.store import ZipStore
20+
from zarr.testing.strategies import key_ranges, keys
1921

2022

2123
class SyncStoreWrapper(zarr.core.sync.SyncMixin):
@@ -103,13 +105,17 @@ class ZarrStoreStateMachine(RuleBasedStateMachine):
103105
https://hypothesis.readthedocs.io/en/latest/stateful.html
104106
"""
105107

106-
def __init__(self) -> None:
108+
def __init__(self, store) -> None:
107109
super().__init__()
108110
self.model: dict[str, bytes] = {}
109-
self.store = SyncStoreWrapper(MemoryStore(mode="w"))
111+
self.store = SyncStoreWrapper(store)
110112
self.prototype = default_buffer_prototype()
111113

112-
@rule(key=paths, data=st.binary(min_size=0, max_size=100))
114+
@initialize()
115+
def init_store(self):
116+
self.store.clear()
117+
118+
@rule(key=keys, data=st.binary(min_size=0, max_size=100))
113119
def set(self, key: str, data: DataObject) -> None:
114120
note(f"(set) Setting {key!r} with {data}")
115121
assert not self.store.mode.readonly
@@ -118,7 +124,7 @@ def set(self, key: str, data: DataObject) -> None:
118124
self.model[key] = data_buf
119125

120126
@precondition(lambda self: len(self.model.keys()) > 0)
121-
@rule(key=paths, data=st.data())
127+
@rule(key=keys, data=st.data())
122128
def get(self, key: str, data: DataObject) -> None:
123129
key = data.draw(
124130
st.sampled_from(sorted(self.model.keys()))
@@ -128,7 +134,7 @@ def get(self, key: str, data: DataObject) -> None:
128134
# to bytes here necessary because data_buf set to model in set()
129135
assert self.model[key].to_bytes() == (store_value.to_bytes())
130136

131-
@rule(key=paths, data=st.data())
137+
@rule(key=keys, data=st.data())
132138
def get_invalid_keys(self, key: str, data: DataObject) -> None:
133139
note("(get_invalid)")
134140
assume(key not in self.model.keys())
@@ -186,7 +192,7 @@ def empty(self) -> None:
186192
# make sure they either both are or both aren't empty (same state)
187193
assert self.store.empty() == (not self.model)
188194

189-
@rule(key=paths)
195+
@rule(key=keys)
190196
def exists(self, key: str) -> None:
191197
note("(exists)")
192198

@@ -227,4 +233,10 @@ def check_keys(self) -> None:
227233
note("checking keys / exists / empty")
228234

229235

230-
StatefulStoreTest = ZarrStoreStateMachine.TestCase
236+
def test_zarr_hierarchy(sync_store: Store):
237+
def mk_test_instance_sync():
238+
return ZarrStoreStateMachine(sync_store)
239+
240+
if isinstance(sync_store, ZipStore):
241+
pytest.skip(reason="ZipStore does not support delete")
242+
run_state_machine_as_test(mk_test_instance_sync, settings=Settings(report_multiple_bugs=False))

0 commit comments

Comments
 (0)