Skip to content

Commit

Permalink
precommit
Browse files Browse the repository at this point in the history
  • Loading branch information
e-marshall committed Aug 9, 2024
1 parent 8902981 commit a28f4b3
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 76 deletions.
13 changes: 7 additions & 6 deletions src/zarr/strategies.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import string
from typing import Any

import hypothesis.extra.numpy as npst
import hypothesis.strategies as st
from hypothesis.strategies import SearchStrategy

import numpy as np
from hypothesis import given, settings # noqa
from hypothesis.strategies import SearchStrategy

from .array import Array
from .group import Group
Expand Down Expand Up @@ -146,11 +146,12 @@ def basic_indices(draw: st.DrawFn, *, shape: tuple[int], **kwargs): # type: ign
)
)

def key_ranges(keys: SearchStrategy | None = None):
'''fn to generate key_ranges strategy for get_partial_values()

def key_ranges(keys: SearchStrategy | None = None) -> st.lists[:]:
"""fn to generate key_ranges strategy for get_partial_values()
returns list strategy w/ form: [(key, (range_start, range_step)),
(key, (range_start, range_step)),...]
'''
"""
if keys is None:
group_st = st.text(alphabet=string.ascii_letters + string.digits, min_size=1, max_size=10)
keys = st.lists(group_st, min_size=1, max_size=5).map("/".join)
Expand All @@ -159,4 +160,4 @@ def key_ranges(keys: SearchStrategy | None = None):
)
key_tuple = st.tuples(keys, byte_ranges)
key_range_st = st.lists(key_tuple, min_size=1, max_size=10)
return key_range_st
return key_range_st
131 changes: 61 additions & 70 deletions tests/v3/test_store/test_stateful_store.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
# Stateful tests for arbitrary Zarr stores.

import asyncio
import string


import hypothesis.strategies as st
from hypothesis import note
from hypothesis.stateful import (
Bundle,
RuleBasedStateMachine,
invariant,
precondition,
rule,
)
from hypothesis.strategies import SearchStrategy

from zarr.buffer import Buffer, default_buffer_prototype
from zarr.store import MemoryStore, RemoteStore, LocalStore
from zarr.store import MemoryStore

#from strategies_store import StoreStatefulStrategies, key_ranges
from zarr.strategies import paths, key_ranges
# from strategies_store import StoreStatefulStrategies, key_ranges
from zarr.strategies import key_ranges, paths

# zarr spec: https://zarr-specs.readthedocs.io/en/latest/v3/core/v3.0.html

#zarr spec: https://zarr-specs.readthedocs.io/en/latest/v3/core/v3.0.html

# NOTE: all methods should be called on self.store, assertions should compare self.model and self.store._store_dict
class ZarrStoreStateMachine(RuleBasedStateMachine):
def __init__(self): # look into using run_machine_as_test()
super().__init__()
def __init__(self): # look into using run_machine_as_test()
super().__init__()
self.model = {}
self.store = MemoryStore(mode="w")
self.store = MemoryStore(mode="w")

# Unfortunately, hypothesis' stateful testing infra does not support asyncio
# So we redefine sync versions of the Store API.
Expand All @@ -39,7 +35,7 @@ async def store_set(self, key, data_buffer):

async def store_list(self):
paths = [path async for path in self.store.list()]
#note(f'(store set) store {paths=}, type {type(paths)=}, {len(paths)=}')
# note(f'(store set) store {paths=}, type {type(paths)=}, {len(paths)=}')
return paths

async def get_key(self, key):
Expand All @@ -53,17 +49,15 @@ async def get_partial(self, key_ranges):
return obs_maybe

async def delete_key(self, path):

await self.store.delete(path)

async def store_empty(self):

await self.store.empty()

async def store_clear(self):

await self.store.clear()
#async def listdir(self, group): #does listdir take group?

# async def listdir(self, group): #does listdir take group?

# dir_ls = await self.store.list_dir(group)

Expand All @@ -76,32 +70,30 @@ def set(self, key: str, data: bytes) -> None:
assert not self.store.mode.readonly
data_buf = Buffer.from_bytes(data)
asyncio.run(self.store_set(key, data_buf))
self.model[key] = data_buf #this was data
self.model[key] = data_buf # this was data

@invariant()
def check_paths_equal(self) -> None:
note("Checking that paths are equal")
paths = asyncio.run(self.store_list())
#note(f'(check paths equal) {self.model=}, {self.store._store_dict=}')
# note(f'(check paths equal) {self.model=}, {self.store._store_dict=}')
assert list(self.model.keys()) == paths
assert len(self.model.keys()) == len(self.store._store_dict)
assert self.model == self.store._store_dict

@precondition(lambda self: len(self.model.keys()) > 0)
@rule(data=st.data())
#@rule(key=keys_bundle)
# @rule(key=keys_bundle)
def get(self, data) -> None:

key = data.draw(st.sampled_from(sorted(self.model.keys())))
store_value = asyncio.run(self.get_key(key))
#note(f'(get) {self.model[key]=}, {store_value}')
#to bytes here necessary (on model and store) because data_buf set to model in set()
# note(f'(get) {self.model[key]=}, {store_value}')
# to bytes here necessary (on model and store) because data_buf set to model in set()
assert self.model[key].to_bytes() == store_value.to_bytes()

@precondition(lambda self: len(self.model.keys()) > 0)
@rule(data=st.data())
def get_partial_values(self, data) -> None:

key_st = st.sampled_from(sorted(self.model.keys()))
key_range = data.draw(key_ranges(keys=key_st))

Expand All @@ -123,93 +115,92 @@ def get_partial_values(self, data) -> None:
model_vals_partial = model_vals[start:stop]
model_vals_ls.append(model_vals_partial)

assert all(obs == exp.to_bytes() for obs, exp in zip(observed, model_vals_ls, strict=True)), (
assert all(
obs == exp.to_bytes() for obs, exp in zip(observed, model_vals_ls, strict=True)
), (
observed,
model_vals_ls,
)

@precondition(lambda self: len(self.model.keys()) > 0)
@rule(data=st.data())
def delete(self,data) -> None:

def delete(self, data) -> None:
path_st = data.draw(st.sampled_from(sorted(self.model.keys())))
note(f'(delete) Deleting {path_st=}')
note(f"(delete) Deleting {path_st=}")

asyncio.run(self.delete_key(path_st))

del self.model[path_st]

#property tests
# property tests
assert self.model.keys() == self.store._store_dict.keys()
assert path_st not in list(self.model.keys())

@rule(key=paths, data=st.binary(min_size=0, max_size=100))
def clear(self, key:str, data:bytes):
''' clear() is in zarr/store/memory.py
def clear(self, key: str, data: bytes):
"""clear() is in zarr/store/memory.py
it calls clear on self._store_dict
clear() is dict method that removes all key-val pairs from dict
'''
"""
assert not self.store.mode.readonly
note('(clear)')
note("(clear)")
asyncio.run(self.store_clear())
self.model.clear()

assert self.model == self.store._store_dict
assert len(self.model.keys()) == 0

def empty(self, data) -> None:
'''empty checks if a store is empty or not
"""empty checks if a store is empty or not
return true if self._store_dict doesn't exist
return false if self._store_dict exists '''
note('(empty)')
return false if self._store_dict exists"""
note("(empty)")

asyncio.run(self.store_clear())
assert self.store.empty()

#@precondition(lambda self: len(self.model.keys()) > 0)
#@rule(key = paths, data=st.binary(min_size=0, max_size=100))
#def listdir(self, key, data) -> None:
# '''list_dir - Retrieve all keys and prefixes with a given prefix
# @precondition(lambda self: len(self.model.keys()) > 0)
# @rule(key = paths, data=st.binary(min_size=0, max_size=100))
# def listdir(self, key, data) -> None:
# '''list_dir - Retrieve all keys and prefixes with a given prefix
# and which do not contain the character “/” after the given prefix.
# '''

#assert not self.store.mode.readonly
#data_buf = Buffer.from_bytes(data)
#set keys on store
#asyncio.run(self.store_set(key, data_buf))
#set same keys on model
#self.model[key] = data
#list keys on store
#asyncio.run(self.listdir(key))
# assert not self.store.mode.readonly
# data_buf = Buffer.from_bytes(data)
# set keys on store
# asyncio.run(self.store_set(key, data_buf))
# set same keys on model
# self.model[key] = data
# list keys on store
# asyncio.run(self.listdir(key))

#self.model.listed_keys = list(self.model.keys())
#for store, set random keys
# self.model.listed_keys = list(self.model.keys())
# for store, set random keys

#for store, list all keys
#for model, list all keys
# for store, list all keys
# for model, list all keys

StatefulStoreTest = ZarrStoreStateMachine.TestCase


#@invariant()
#def check_delete(self) -> None:
# '''this doesn't actually do anything'''
# note('(check_delete)')
#can/should this be the same as the invariant for set?
# paths = asyncio.run(self.store_list())
# note(f"After delete, checking that paths are equal, {paths=}, model={list(self.model.keys())}")
# assert list(self.model.keys()) == paths
# assert len(list(self.model.keys())) == len(paths)
#maybe add assertion that path_st not in keys? but this would req paths_st from delete()..
StatefulStoreTest = ZarrStoreStateMachine.TestCase


#@invariant()
#listdir not working
#def check_listdir(self):
# @invariant()
# def check_delete(self) -> None:
# '''this doesn't actually do anything'''
# note('(check_delete)')
# can/should this be the same as the invariant for set?
# paths = asyncio.run(self.store_list())
# note(f"After delete, checking that paths are equal, {paths=}, model={list(self.model.keys())}")
# assert list(self.model.keys()) == paths
# assert len(list(self.model.keys())) == len(paths)
# maybe add assertion that path_st not in keys? but this would req paths_st from delete()..

# store_keys = asyncio.run(self.list_dir())#need to pass a gruop to this
# model_keys = self.model.listed_keys
# assert store_keys == model_keys

# @invariant()
# listdir not working
# def check_listdir(self):


# store_keys = asyncio.run(self.list_dir())#need to pass a group to this
# model_keys = self.model.listed_keys
# assert store_keys == model_keys

0 comments on commit a28f4b3

Please sign in to comment.