Skip to content

Commit 2e3b898

Browse files
committed
refactor chunk tests
1 parent 36ef8d9 commit 2e3b898

File tree

4 files changed

+224
-248
lines changed

4 files changed

+224
-248
lines changed

zarr/ext.pyx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,13 @@ cdef class PersistentChunk(BaseChunk):
459459
self._basename = os.path.basename(path)
460460
self._dirname = os.path.dirname(path)
461461

462+
# check consistency
463+
if os.path.exists(path):
464+
header = self.read_header()
465+
if self._nbytes != header.nbytes:
466+
raise ValueError('expected nbytes %s, found %s' %
467+
(self._nbytes, header.nbytes))
468+
462469
property is_initialized:
463470
def __get__(self):
464471
return os.path.exists(self._path)
@@ -481,11 +488,6 @@ cdef class PersistentChunk(BaseChunk):
481488
with open(self._path, 'rb') as f:
482489
header_raw = f.read(BLOSC_HEADER_LENGTH)
483490
header = decode_blosc_header(header_raw)
484-
# check nbytes consistency
485-
if self._nbytes != header.nbytes:
486-
# should never happen
487-
raise RuntimeError('expected nbytes %s, found %s' %
488-
(self._nbytes, header.nbytes))
489491
# seek back BLOSC_HEADER_LENGTH bytes relative to current position
490492
f.seek(-BLOSC_HEADER_LENGTH, 1)
491493
data = f.read(header.cbytes)

zarr/tests/test_chunk.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import, print_function, division
3+
import tempfile
4+
from unittest import TestCase
5+
import atexit
6+
import os
7+
8+
9+
from nose.tools import eq_ as eq, assert_false, assert_true, assert_raises
10+
import numpy as np
11+
from numpy.testing import assert_array_equal
12+
from zarr.ext import Chunk, PersistentChunk
13+
from zarr import defaults
14+
15+
16+
class ChunkTests(object):
17+
18+
def create_chunk(self, **kwargs):
19+
# override in sub-classes
20+
raise NotImplementedError()
21+
22+
def _test_create_chunk_cparams(self, a, cname, clevel, shuffle):
23+
24+
# setup
25+
c = self.create_chunk(shape=a.shape, dtype=a.dtype, cname=cname,
26+
clevel=clevel, shuffle=shuffle)
27+
28+
# check basic properties
29+
eq(a.shape, c.shape)
30+
eq(a.dtype, c.dtype)
31+
eq(a.size, c.size)
32+
eq(a.itemsize, c.itemsize)
33+
eq(a.nbytes, c.nbytes)
34+
35+
# check compression properties
36+
if cname is None:
37+
eq(defaults.cname, c.cname)
38+
else:
39+
eq(cname, c.cname)
40+
if clevel is None:
41+
eq(defaults.clevel, c.clevel)
42+
else:
43+
eq(clevel, c.clevel)
44+
if shuffle is None:
45+
eq(defaults.shuffle, c.shuffle)
46+
else:
47+
eq(shuffle, c.shuffle)
48+
eq(0, c.cbytes)
49+
assert_false(c.is_initialized)
50+
51+
# store data
52+
c[:] = a
53+
54+
# check properties after storing data
55+
eq(a.shape, c.shape)
56+
eq(a.dtype, c.dtype)
57+
eq(a.size, c.size)
58+
eq(a.itemsize, c.itemsize)
59+
eq(a.nbytes, c.nbytes)
60+
if c.clevel > 0 and c.shuffle > 0:
61+
# N.B., for some arrays, shuffle is required to achieve any
62+
# compression
63+
assert c.cbytes < c.nbytes, (c.nbytes, c.cbytes)
64+
assert_true(c.is_initialized)
65+
66+
# check round-trip
67+
assert_array_equal(a, c[:])
68+
assert_array_equal(a, c[...])
69+
70+
def _test_create_chunk(self, a):
71+
for cname in None, b'blosclz', b'lz4', b'snappy', b'zlib':
72+
for clevel in None, 0, 1, 5:
73+
for shuffle in None, 0, 1, 2:
74+
print(cname, clevel, shuffle)
75+
self._test_create_chunk_cparams(a, cname, clevel, shuffle)
76+
77+
def test_create_chunk(self):
78+
79+
# arange
80+
for dtype in 'u1', 'u4', 'u8', 'i1', 'i4', 'i8':
81+
print(dtype)
82+
print('1-dimensional')
83+
self._test_create_chunk(np.arange(1e5, dtype=dtype))
84+
print('2-dimensional')
85+
self._test_create_chunk(np.arange(1e5, dtype=dtype)
86+
.reshape(100, -1))
87+
88+
# linspace
89+
for dtype in 'f2', 'f4', 'f8':
90+
print(dtype)
91+
print('1-dimensional')
92+
self._test_create_chunk(np.linspace(-1, 1, 1e5, dtype=dtype))
93+
print('2-dimensional')
94+
self._test_create_chunk(np.linspace(-1, 1, 1e5, dtype=dtype)
95+
.reshape(100, -1))
96+
97+
def test_create_chunk_fill_value(self):
98+
99+
for shape in 100, (100, 100):
100+
101+
# default dtype and fill_value
102+
c = self.create_chunk(shape=shape)
103+
a = c[:]
104+
e = np.empty(shape)
105+
eq(e.shape, a.shape)
106+
eq(e.dtype, a.dtype)
107+
108+
# specified dtype and fill_value
109+
for dtype in 'i4', 'f8':
110+
for fill_value in 1, -1:
111+
c = self.create_chunk(shape=shape, dtype=dtype,
112+
fill_value=fill_value)
113+
e = np.empty(shape, dtype=dtype)
114+
e.fill(fill_value)
115+
assert_array_equal(e, c[:])
116+
117+
118+
class TestMemoryChunks(TestCase, ChunkTests):
119+
120+
def create_chunk(self, **kwargs):
121+
return Chunk(**kwargs)
122+
123+
124+
class TestPersistentChunks(TestCase, ChunkTests):
125+
126+
def create_chunk(self, **kwargs):
127+
path = kwargs.get('path', tempfile.mktemp())
128+
kwargs['path'] = path
129+
# tidy up
130+
atexit.register(
131+
lambda: os.remove(path) if os.path.exists(path) else None
132+
)
133+
return PersistentChunk(**kwargs)
134+
135+
def _test_persistence_cparams(self, a, cname, clevel, shuffle):
136+
137+
# setup file
138+
path = tempfile.mktemp()
139+
assert_false(os.path.exists(path))
140+
141+
# create chunk
142+
c = self.create_chunk(path=path, shape=a.shape, dtype=a.dtype,
143+
cname=cname, clevel=clevel, shuffle=shuffle)
144+
145+
# check state
146+
assert_false(os.path.exists(path))
147+
assert_false(c.is_initialized)
148+
149+
# store some data
150+
c[:] = a
151+
152+
# check state
153+
assert_true(os.path.exists(path))
154+
assert_true(c.is_initialized)
155+
156+
# check persistence
157+
c2 = self.create_chunk(path=path, shape=a.shape, dtype=a.dtype,
158+
cname=cname, clevel=clevel, shuffle=shuffle)
159+
160+
# check state
161+
eq(a.shape, c2.shape)
162+
eq(a.dtype, c2.dtype)
163+
if cname is None:
164+
eq(defaults.cname, c2.cname)
165+
else:
166+
eq(cname, c2.cname)
167+
if clevel is None:
168+
eq(defaults.clevel, c2.clevel)
169+
else:
170+
eq(clevel, c2.clevel)
171+
if shuffle is None:
172+
eq(defaults.shuffle, c2.shuffle)
173+
else:
174+
eq(shuffle, c2.shuffle)
175+
eq(a.nbytes, c2.nbytes)
176+
assert_true(c.nbytes, c2.nbytes)
177+
assert_true(c2.is_initialized)
178+
179+
# check data
180+
assert_array_equal(a, c2[:])
181+
assert_array_equal(a, c2[...])
182+
183+
# check what happens if you do something stupid
184+
with assert_raises(ValueError):
185+
self.create_chunk(path=path, shape=[2*i for i in a.shape],
186+
dtype=a.dtype, cname=cname, clevel=clevel,
187+
shuffle=shuffle)
188+
with assert_raises(ValueError):
189+
self.create_chunk(path=path, shape=a.shape, dtype='S7',
190+
cname=cname, clevel=clevel, shuffle=shuffle)
191+
192+
def _test_persistence(self, a):
193+
for cname in None, b'blosclz', b'lz4', b'snappy', b'zlib':
194+
for clevel in None, 0, 1, 5:
195+
for shuffle in None, 0, 1, 2:
196+
print(cname, clevel, shuffle)
197+
self._test_persistence_cparams(a, cname, clevel, shuffle)
198+
199+
def test_persistence(self):
200+
201+
# arange
202+
for dtype in 'u1', 'u4', 'u8', 'i1', 'i4', 'i8':
203+
print(dtype)
204+
print('1-dimensional')
205+
self._test_persistence(np.arange(1e5, dtype=dtype))
206+
print('2-dimensional')
207+
self._test_persistence(np.arange(1e5, dtype=dtype)
208+
.reshape(100, -1))
209+
210+
# linspace
211+
for dtype in 'f2', 'f4', 'f8':
212+
print(dtype)
213+
print('1-dimensional')
214+
self._test_persistence(np.linspace(-1, 1, 1e5, dtype=dtype))
215+
print('2-dimensional')
216+
self._test_persistence(np.linspace(-1, 1, 1e5, dtype=dtype)
217+
.reshape(100, -1))

zarr/tests/test_ext_chunk.py

Lines changed: 0 additions & 112 deletions
This file was deleted.

0 commit comments

Comments
 (0)