4823: More tests and fixes for updating and merging from remote api record.
[arvados.git] / sdk / python / tests / test_arvfile.py
1 #!/usr/bin/env python
2
3 import bz2
4 import gzip
5 import io
6 import mock
7 import os
8 import unittest
9 import hashlib
10
11 import arvados
12 from arvados import Range, KeepLocator
13 from arvados.collection import import_manifest, export_manifest, ReadOnlyCollection, WritableCollection
14 from arvados.arvfile import ArvadosFile, ArvadosFileReader, SYNC_READONLY, SYNC_EXPLICIT
15
16 import arvados_testutil as tutil
17 from test_stream import StreamFileReaderTestCase, StreamRetryTestMixin
18
19 class ArvadosFileWriterTestCase(unittest.TestCase):
20     class MockKeep(object):
21         def __init__(self, blocks):
22             self.blocks = blocks
23             self.requests = []
24         def get(self, locator, num_retries=0):
25             self.requests.append(locator)
26             return self.blocks.get(locator)
27         def get_from_cache(self, locator):
28             self.requests.append(locator)
29             return self.blocks.get(locator)
30         def put(self, data, num_retries=None):
31             pdh = "%s+%i" % (hashlib.md5(data).hexdigest(), len(data))
32             self.blocks[pdh] = str(data)
33             return pdh
34
35     class MockApi(object):
36         def __init__(self, b, r):
37             self.b = b
38             self.r = r
39         class MockCollections(object):
40             def __init__(self, b, r):
41                 self.b = b
42                 self.r = r
43             class Execute(object):
44                 def __init__(self, r):
45                     self.r = r
46                 def execute(self, num_retries=None):
47                     return self.r
48             def create(self, ensure_unique_name=False, body=None):
49                 if body != self.b:
50                     raise Exception("Body %s does not match expectation %s" % (body, self.b))
51                 return ArvadosFileWriterTestCase.MockApi.MockCollections.Execute(self.r)
52             def update(self, uuid=None, body=None):
53                 return ArvadosFileWriterTestCase.MockApi.MockCollections.Execute(self.r)
54         def collections(self):
55             return ArvadosFileWriterTestCase.MockApi.MockCollections(self.b, self.r)
56
57
58     def test_truncate(self):
59         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
60         api = ArvadosFileWriterTestCase.MockApi({"name":"test_truncate",
61                                                  "manifest_text":". 781e5e245d69b566979b86e28d23f2c7+10 0:8:count.txt\n"},
62                                                 {"uuid":"zzzzz-4zz18-mockcollection0"})
63         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n',
64                              api_client=api, keep_client=keep) as c:
65             writer = c.open("count.txt", "r+")
66             self.assertEqual(writer.size(), 10)
67             writer.seek(5)
68             self.assertEqual("56789", writer.read(8))
69             writer.truncate(8)
70             writer.seek(5, os.SEEK_SET)
71             self.assertEqual("567", writer.read(8))
72
73             self.assertEqual(None, c._manifest_locator)
74             self.assertEqual(True, c.modified())
75             c.save_new("test_truncate")
76             self.assertEqual("zzzzz-4zz18-mockcollection0", c._manifest_locator)
77             self.assertEqual(False, c.modified())
78
79     def test_append0(self):
80         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
81         api = ArvadosFileWriterTestCase.MockApi({"name":"test_append",
82                                                  "manifest_text": ". 781e5e245d69b566979b86e28d23f2c7+10 acbd18db4cc2f85cedef654fccc4a4d8+3 0:13:count.txt\n"},
83                                                 {"uuid":"zzzzz-4zz18-mockcollection0"})
84         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n',
85                              api_client=api, keep_client=keep) as c:
86             writer = c.open("count.txt", "r+")
87             self.assertEqual(writer.size(), 10)
88
89             writer.seek(5, os.SEEK_SET)
90             self.assertEqual("56789", writer.read(8))
91
92             writer.seek(10, os.SEEK_SET)
93             writer.write("foo")
94             self.assertEqual(writer.size(), 13)
95
96             writer.seek(5, os.SEEK_SET)
97             self.assertEqual("56789foo", writer.read(8))
98
99             self.assertEqual(None, c._manifest_locator)
100             self.assertEqual(True, c.modified())
101             self.assertEqual(None, keep.get("acbd18db4cc2f85cedef654fccc4a4d8+3"))
102
103             c.save_new("test_append")
104             self.assertEqual("zzzzz-4zz18-mockcollection0", c._manifest_locator)
105             self.assertEqual(False, c.modified())
106             self.assertEqual("foo", keep.get("acbd18db4cc2f85cedef654fccc4a4d8+3"))
107
108
109     def test_append1(self):
110         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
111         c = WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n', keep_client=keep)
112         writer = c.open("count.txt", "a+")
113         self.assertEqual(writer.read(20), "0123456789")
114         writer.seek(0, os.SEEK_SET)
115
116         writer.write("hello")
117         self.assertEqual(writer.read(20), "0123456789hello")
118         writer.seek(0, os.SEEK_SET)
119
120         writer.write("world")
121         self.assertEqual(writer.read(20), "0123456789helloworld")
122
123         self.assertEqual(". 781e5e245d69b566979b86e28d23f2c7+10 fc5e038d38a57032085441e7fe7010b0+10 0:20:count.txt\n", export_manifest(c))
124
125     def test_write0(self):
126         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
127         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n',
128                              keep_client=keep) as c:
129             writer = c.open("count.txt", "r+")
130             self.assertEqual("0123456789", writer.readfrom(0, 13))
131             writer.seek(0, os.SEEK_SET)
132             writer.write("foo")
133             self.assertEqual(writer.size(), 10)
134             self.assertEqual("foo3456789", writer.readfrom(0, 13))
135             self.assertEqual(". acbd18db4cc2f85cedef654fccc4a4d8+3 781e5e245d69b566979b86e28d23f2c7+10 0:3:count.txt 6:7:count.txt\n", export_manifest(c))
136
137     def test_write1(self):
138         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
139         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n',
140                              keep_client=keep) as c:
141             writer = c.open("count.txt", "r+")
142             self.assertEqual("0123456789", writer.readfrom(0, 13))
143             writer.seek(3, os.SEEK_SET)
144             writer.write("foo")
145             self.assertEqual(writer.size(), 10)
146             self.assertEqual("012foo6789", writer.readfrom(0, 13))
147             self.assertEqual(". 781e5e245d69b566979b86e28d23f2c7+10 acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:count.txt 10:3:count.txt 6:4:count.txt\n", export_manifest(c))
148
149     def test_write2(self):
150         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
151         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n',
152                              keep_client=keep) as c:
153             writer = c.open("count.txt", "r+")
154             self.assertEqual("0123456789", writer.readfrom(0, 13))
155             writer.seek(7, os.SEEK_SET)
156             writer.write("foo")
157             self.assertEqual(writer.size(), 10)
158             self.assertEqual("0123456foo", writer.readfrom(0, 13))
159             self.assertEqual(". 781e5e245d69b566979b86e28d23f2c7+10 acbd18db4cc2f85cedef654fccc4a4d8+3 0:7:count.txt 10:3:count.txt\n", export_manifest(c))
160
161     def test_write3(self):
162         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
163         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt 0:10:count.txt\n',
164                              keep_client=keep) as c:
165             writer = c.open("count.txt", "r+")
166             self.assertEqual("012345678901234", writer.readfrom(0, 15))
167             writer.seek(7, os.SEEK_SET)
168             writer.write("foobar")
169             self.assertEqual(writer.size(), 20)
170             self.assertEqual("0123456foobar34", writer.readfrom(0, 15))
171             self.assertEqual(". 781e5e245d69b566979b86e28d23f2c7+10 3858f62230ac3c915f300c664312c63f+6 0:7:count.txt 10:6:count.txt 3:7:count.txt\n", export_manifest(c))
172
173     def test_write4(self):
174         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
175         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:4:count.txt 0:4:count.txt 0:4:count.txt',
176                              keep_client=keep) as c:
177             writer = c.open("count.txt", "r+")
178             self.assertEqual("012301230123", writer.readfrom(0, 15))
179             writer.seek(2, os.SEEK_SET)
180             writer.write("abcdefg")
181             self.assertEqual(writer.size(), 12)
182             self.assertEqual("01abcdefg123", writer.readfrom(0, 15))
183             self.assertEqual(". 781e5e245d69b566979b86e28d23f2c7+10 7ac66c0f148de9519b8bd264312c4d64+7 0:2:count.txt 10:7:count.txt 1:3:count.txt\n", export_manifest(c))
184
185     def test_write_large(self):
186         keep = ArvadosFileWriterTestCase.MockKeep({})
187         api = ArvadosFileWriterTestCase.MockApi({"name":"test_write_large",
188                                                  "manifest_text": ". a5de24f4417cfba9d5825eadc2f4ca49+67108000 598cc1a4ccaef8ab6e4724d87e675d78+32892000 0:100000000:count.txt\n"},
189                                                 {"uuid":"zzzzz-4zz18-mockcollection0"})
190         with WritableCollection('. ' + arvados.config.EMPTY_BLOCK_LOCATOR + ' 0:0:count.txt',
191                              api_client=api, keep_client=keep) as c:
192             writer = c.open("count.txt", "r+")
193             text = ''.join(["0123456789" for a in xrange(0, 100)])
194             for b in xrange(0, 100000):
195                 writer.write(text)
196             self.assertEqual(writer.size(), 100000000)
197
198             self.assertEqual(None, c._manifest_locator)
199             self.assertEqual(True, c.modified())
200             c.save_new("test_write_large")
201             self.assertEqual("zzzzz-4zz18-mockcollection0", c._manifest_locator)
202             self.assertEqual(False, c.modified())
203
204     def test_write_rewrite0(self):
205         keep = ArvadosFileWriterTestCase.MockKeep({})
206         with WritableCollection('. ' + arvados.config.EMPTY_BLOCK_LOCATOR + ' 0:0:count.txt',
207                              keep_client=keep) as c:
208             writer = c.open("count.txt", "r+")
209             for b in xrange(0, 10):
210                 writer.seek(0, os.SEEK_SET)
211                 writer.write("0123456789")
212             writer.arvadosfile._repack_writes()
213             self.assertEqual(writer.size(), 10)
214             self.assertEqual("0123456789", writer.readfrom(0, 20))
215             self.assertEqual(". 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n", export_manifest(c))
216
217     def test_write_rewrite1(self):
218         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
219         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt',
220                              keep_client=keep) as c:
221             writer = c.open("count.txt", "r+")
222             for b in xrange(0, 10):
223                 writer.seek(10, os.SEEK_SET)
224                 writer.write("abcdefghij")
225             writer.arvadosfile._repack_writes()
226             self.assertEqual(writer.size(), 20)
227             self.assertEqual("0123456789abcdefghij", writer.readfrom(0, 20))
228             self.assertEqual(". 781e5e245d69b566979b86e28d23f2c7+10 a925576942e94b2ef57a066101b48876+10 0:20:count.txt\n", export_manifest(c))
229
230     def test_write_rewrite2(self):
231         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
232         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt',
233                              keep_client=keep) as c:
234             writer = c.open("count.txt", "r+")
235             for b in xrange(0, 10):
236                 writer.seek(5, os.SEEK_SET)
237                 writer.write("abcdefghij")
238             writer.arvadosfile._repack_writes()
239             self.assertEqual(writer.size(), 15)
240             self.assertEqual("01234abcdefghij", writer.readfrom(0, 20))
241             self.assertEqual(". 781e5e245d69b566979b86e28d23f2c7+10 a925576942e94b2ef57a066101b48876+10 0:5:count.txt 10:10:count.txt\n", export_manifest(c))
242
243     def test_write_large_rewrite0(self):
244         keep = ArvadosFileWriterTestCase.MockKeep({})
245         api = ArvadosFileWriterTestCase.MockApi({"name":"test_write_large",
246                                                  "manifest_text": ". 37400a68af9abdd76ca5bf13e819e42a+32892003 a5de24f4417cfba9d5825eadc2f4ca49+67108000 32892000:3:count.txt 32892006:67107997:count.txt 0:32892000:count.txt\n"},
247                                                 {"uuid":"zzzzz-4zz18-mockcollection0"})
248         with WritableCollection('. ' + arvados.config.EMPTY_BLOCK_LOCATOR + ' 0:0:count.txt',
249                              api_client=api, keep_client=keep) as c:
250             writer = c.open("count.txt", "r+")
251             text = ''.join(["0123456789" for a in xrange(0, 100)])
252             for b in xrange(0, 100000):
253                 writer.write(text)
254             writer.seek(0, os.SEEK_SET)
255             writer.write("foo")
256             self.assertEqual(writer.size(), 100000000)
257
258             self.assertEqual(None, c._manifest_locator)
259             self.assertEqual(True, c.modified())
260             c.save_new("test_write_large")
261             self.assertEqual("zzzzz-4zz18-mockcollection0", c._manifest_locator)
262             self.assertEqual(False, c.modified())
263
264     def test_create(self):
265         keep = ArvadosFileWriterTestCase.MockKeep({})
266         api = ArvadosFileWriterTestCase.MockApi({"name":"test_create",
267                                                  "manifest_text":". 2e9ec317e197819358fbc43afca7d837+8 0:8:count.txt\n"},
268                                                 {"uuid":"zzzzz-4zz18-mockcollection0"})
269         with WritableCollection(api_client=api, keep_client=keep) as c:
270             writer = c.open("count.txt", "w+")
271             self.assertEqual(writer.size(), 0)
272             writer.write("01234567")
273             self.assertEqual(writer.size(), 8)
274
275             self.assertEqual(None, c._manifest_locator)
276             self.assertEqual(True, c.modified())
277             self.assertEqual(None, keep.get("2e9ec317e197819358fbc43afca7d837+8"))
278             c.save_new("test_create")
279             self.assertEqual("zzzzz-4zz18-mockcollection0", c._manifest_locator)
280             self.assertEqual(False, c.modified())
281             self.assertEqual("01234567", keep.get("2e9ec317e197819358fbc43afca7d837+8"))
282
283
284     def test_create_subdir(self):
285         keep = ArvadosFileWriterTestCase.MockKeep({})
286         api = ArvadosFileWriterTestCase.MockApi({"name":"test_create",
287                                                  "manifest_text":"./foo/bar 2e9ec317e197819358fbc43afca7d837+8 0:8:count.txt\n"},
288                                                 {"uuid":"zzzzz-4zz18-mockcollection0"})
289         with WritableCollection(api_client=api, keep_client=keep) as c:
290             writer = c.open("foo/bar/count.txt", "w+")
291             writer.write("01234567")
292             c.save_new("test_create")
293
294     def test_overwrite(self):
295         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
296         api = ArvadosFileWriterTestCase.MockApi({"name":"test_overwrite",
297                                                  "manifest_text":". 2e9ec317e197819358fbc43afca7d837+8 0:8:count.txt\n"},
298                                                 {"uuid":"zzzzz-4zz18-mockcollection0"})
299         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n',
300                              api_client=api, keep_client=keep) as c:
301             writer = c.open("count.txt", "w+")
302             self.assertEqual(writer.size(), 0)
303             writer.write("01234567")
304             self.assertEqual(writer.size(), 8)
305
306             self.assertEqual(None, c._manifest_locator)
307             self.assertEqual(True, c.modified())
308             c.save_new("test_overwrite")
309             self.assertEqual("zzzzz-4zz18-mockcollection0", c._manifest_locator)
310             self.assertEqual(False, c.modified())
311
312     def test_file_not_found(self):
313         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n') as c:
314             with self.assertRaises(IOError):
315                 writer = c.open("nocount.txt", "r")
316
317     def test_cannot_open_directory(self):
318         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count.txt\n') as c:
319             with self.assertRaises(IOError):
320                 writer = c.open(".", "r")
321
322     def test_create_multiple(self):
323         keep = ArvadosFileWriterTestCase.MockKeep({})
324         api = ArvadosFileWriterTestCase.MockApi({"name":"test_create_multiple",
325                                                  "manifest_text":". 2e9ec317e197819358fbc43afca7d837+8 e8dc4081b13434b45189a720b77b6818+8 0:8:count1.txt 8:8:count2.txt\n"},
326                                                 {"uuid":"zzzzz-4zz18-mockcollection0"})
327         with WritableCollection(api_client=api, keep_client=keep) as c:
328             w1 = c.open("count1.txt", "w")
329             w2 = c.open("count2.txt", "w")
330             w1.write("01234567")
331             w2.write("abcdefgh")
332             self.assertEqual(w1.size(), 8)
333             self.assertEqual(w2.size(), 8)
334
335             self.assertEqual(None, c._manifest_locator)
336             self.assertEqual(True, c.modified())
337             self.assertEqual(None, keep.get("2e9ec317e197819358fbc43afca7d837+8"))
338             c.save_new("test_create_multiple")
339             self.assertEqual("zzzzz-4zz18-mockcollection0", c._manifest_locator)
340             self.assertEqual(False, c.modified())
341             self.assertEqual("01234567", keep.get("2e9ec317e197819358fbc43afca7d837+8"))
342
343
344 class ArvadosFileReaderTestCase(StreamFileReaderTestCase):
345     class MockParent(object):
346         class MockBlockMgr(object):
347             def __init__(self, blocks, nocache):
348                 self.blocks = blocks
349                 self.nocache = nocache
350
351             def block_prefetch(self, loc):
352                 pass
353
354             def get_block_contents(self, loc, num_retries=0, cache_only=False):
355                 if self.nocache and cache_only:
356                     return None
357                 return self.blocks[loc]
358
359         def __init__(self, blocks, nocache):
360             self.blocks = blocks
361             self.nocache = nocache
362             self.lock = arvados.arvfile.NoopLock()
363
364         def root_collection(self):
365             return self
366
367         def _my_block_manager(self):
368             return ArvadosFileReaderTestCase.MockParent.MockBlockMgr(self.blocks, self.nocache)
369
370         def sync_mode(self):
371             return SYNC_READONLY
372
373
374     def make_count_reader(self, nocache=False):
375         stream = []
376         n = 0
377         blocks = {}
378         for d in ['01234', '34567', '67890']:
379             loc = '{}+{}'.format(hashlib.md5(d).hexdigest(), len(d))
380             blocks[loc] = d
381             stream.append(Range(loc, n, len(d)))
382             n += len(d)
383         af = ArvadosFile(ArvadosFileReaderTestCase.MockParent(blocks, nocache), stream=stream, segments=[Range(1, 0, 3), Range(6, 3, 3), Range(11, 6, 3)])
384         return ArvadosFileReader(af, "count.txt")
385
386     def test_read_returns_first_block(self):
387         # read() calls will be aligned on block boundaries - see #3663.
388         sfile = self.make_count_reader(nocache=True)
389         self.assertEqual('123', sfile.read(10))
390
391     def test_successive_reads(self):
392         sfile = self.make_count_reader(nocache=True)
393         for expect in ['123', '456', '789', '']:
394             self.assertEqual(expect, sfile.read(10))
395
396     def test_tell_after_block_read(self):
397         sfile = self.make_count_reader(nocache=True)
398         sfile.read(5)
399         self.assertEqual(3, sfile.tell())
400
401     def test_prefetch(self):
402         keep = ArvadosFileWriterTestCase.MockKeep({"2e9ec317e197819358fbc43afca7d837+8": "01234567", "e8dc4081b13434b45189a720b77b6818+8": "abcdefgh"})
403         with WritableCollection(". 2e9ec317e197819358fbc43afca7d837+8 e8dc4081b13434b45189a720b77b6818+8 0:16:count.txt\n", keep_client=keep) as c:
404             r = c.open("count.txt", "r")
405             self.assertEqual("0123", r.read(4))
406         self.assertIn("2e9ec317e197819358fbc43afca7d837+8", keep.requests)
407         self.assertIn("e8dc4081b13434b45189a720b77b6818+8", keep.requests)
408
409     def test__eq__1(self):
410         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count1.txt') as c1:
411             with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count1.txt') as c2:
412                 self.assertTrue(c1["count1.txt"] == c2["count1.txt"])
413                 self.assertFalse(c1["count1.txt"] != c2["count1.txt"])
414
415     def test__eq__2(self):
416         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count1.txt') as c1:
417             with WritableCollection() as c2:
418                 with c2.open("count1.txt", "w") as f:
419                     f.write("0123456789")
420
421                 self.assertTrue(c1["count1.txt"] == c2["count1.txt"])
422                 self.assertFalse(c1["count1.txt"] != c2["count1.txt"])
423
424     def test__ne__(self):
425         with WritableCollection('. 781e5e245d69b566979b86e28d23f2c7+10 0:10:count1.txt') as c1:
426             with WritableCollection() as c2:
427                 with c2.open("count1.txt", "w") as f:
428                     f.write("1234567890")
429
430                 self.assertTrue(c1["count1.txt"] != c2["count1.txt"])
431                 self.assertFalse(c1["count1.txt"] == c2["count1.txt"])
432
433
434 class ArvadosFileReadTestCase(unittest.TestCase, StreamRetryTestMixin):
435     def reader_for(self, coll_name, **kwargs):
436         stream = []
437         segments = []
438         n = 0
439         for d in self.manifest_for(coll_name).split():
440             try:
441                 k = KeepLocator(d)
442                 segments.append(Range(n, n, k.size))
443                 stream.append(Range(d, n, k.size))
444                 n += k.size
445             except ValueError:
446                 pass
447         col = ReadOnlyCollection(keep_client=self.keep_client())
448         col._my_block_manager().prefetch_enabled = False
449         af = ArvadosFile(col,
450                          stream=stream,
451                          segments=segments)
452         return ArvadosFileReader(af, "test", **kwargs)
453
454     def read_for_test(self, reader, byte_count, **kwargs):
455         return reader.read(byte_count, **kwargs)
456
457
458 class ArvadosFileReadFromTestCase(ArvadosFileReadTestCase):
459     def read_for_test(self, reader, byte_count, **kwargs):
460         return reader.readfrom(0, byte_count, **kwargs)
461
462
463 class ArvadosFileReadAllTestCase(ArvadosFileReadTestCase):
464     def read_for_test(self, reader, byte_count, **kwargs):
465         return ''.join(reader.readall(**kwargs))
466
467
468 class ArvadosFileReadAllDecompressedTestCase(ArvadosFileReadTestCase):
469     def read_for_test(self, reader, byte_count, **kwargs):
470         return ''.join(reader.readall_decompressed(**kwargs))
471
472
473 class ArvadosFileReadlinesTestCase(ArvadosFileReadTestCase):
474     def read_for_test(self, reader, byte_count, **kwargs):
475         return ''.join(reader.readlines(**kwargs))
476
477 class BlockManagerTest(unittest.TestCase):
478     def test_bufferblock_append(self):
479         keep = ArvadosFileWriterTestCase.MockKeep({})
480         blockmanager = arvados.arvfile.BlockManager(keep)
481         bufferblock = blockmanager.alloc_bufferblock()
482         bufferblock.append("foo")
483
484         self.assertEqual(bufferblock.size(), 3)
485         self.assertEqual(bufferblock.buffer_view[0:3], "foo")
486         self.assertEqual(bufferblock.locator(), "acbd18db4cc2f85cedef654fccc4a4d8+3")
487
488         bufferblock.append("bar")
489
490         self.assertEqual(bufferblock.size(), 6)
491         self.assertEqual(bufferblock.buffer_view[0:6], "foobar")
492         self.assertEqual(bufferblock.locator(), "3858f62230ac3c915f300c664312c63f+6")
493
494         bufferblock.set_state(arvados.arvfile.BufferBlock.PENDING)
495         with self.assertRaises(arvados.errors.AssertionError):
496             bufferblock.append("bar")
497
498     def test_bufferblock_dup(self):
499         keep = ArvadosFileWriterTestCase.MockKeep({})
500         blockmanager = arvados.arvfile.BlockManager(keep)
501         bufferblock = blockmanager.alloc_bufferblock()
502         bufferblock.append("foo")
503
504         self.assertEqual(bufferblock.size(), 3)
505         self.assertEqual(bufferblock.buffer_view[0:3], "foo")
506         self.assertEqual(bufferblock.locator(), "acbd18db4cc2f85cedef654fccc4a4d8+3")
507         bufferblock.set_state(arvados.arvfile.BufferBlock.PENDING)
508
509         bufferblock2 = blockmanager.dup_block(bufferblock, None)
510         self.assertNotEqual(bufferblock.blockid, bufferblock2.blockid)
511
512         bufferblock2.append("bar")
513
514         self.assertEqual(bufferblock2.size(), 6)
515         self.assertEqual(bufferblock2.buffer_view[0:6], "foobar")
516         self.assertEqual(bufferblock2.locator(), "3858f62230ac3c915f300c664312c63f+6")
517
518         self.assertEqual(bufferblock.size(), 3)
519         self.assertEqual(bufferblock.buffer_view[0:3], "foo")
520         self.assertEqual(bufferblock.locator(), "acbd18db4cc2f85cedef654fccc4a4d8+3")
521
522     def test_bufferblock_get(self):
523         keep = ArvadosFileWriterTestCase.MockKeep({"781e5e245d69b566979b86e28d23f2c7+10": "0123456789"})
524         blockmanager = arvados.arvfile.BlockManager(keep)
525         bufferblock = blockmanager.alloc_bufferblock()
526         bufferblock.append("foo")
527
528         self.assertEqual(blockmanager.get_block_contents("781e5e245d69b566979b86e28d23f2c7+10", 1), "0123456789")
529         self.assertEqual(blockmanager.get_block_contents(bufferblock.blockid, 1), "foo")
530
531     def test_bufferblock_commit(self):
532         mockkeep = mock.MagicMock()
533         blockmanager = arvados.arvfile.BlockManager(mockkeep)
534         bufferblock = blockmanager.alloc_bufferblock()
535         bufferblock.append("foo")
536         blockmanager.commit_all()
537         self.assertTrue(mockkeep.put.called)
538         self.assertEqual(bufferblock.state(), arvados.arvfile.BufferBlock.COMMITTED)
539         self.assertIsNone(bufferblock.buffer_view)
540
541
542     def test_bufferblock_commit_with_error(self):
543         mockkeep = mock.MagicMock()
544         mockkeep.put.side_effect = arvados.errors.KeepWriteError("fail")
545         blockmanager = arvados.arvfile.BlockManager(mockkeep)
546         bufferblock = blockmanager.alloc_bufferblock()
547         bufferblock.append("foo")
548         with self.assertRaises(arvados.errors.KeepWriteError) as err:
549             blockmanager.commit_all()
550         self.assertEquals(str(err.exception), "Error writing some blocks: acbd18db4cc2f85cedef654fccc4a4d8+3 raised KeepWriteError (fail)")
551         self.assertEqual(bufferblock.state(), arvados.arvfile.BufferBlock.PENDING)