1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
20 "git.curoverse.com/arvados.git/sdk/go/arvadostest"
21 check "gopkg.in/check.v1"
24 var _ = check.Suite(&CollectionFSSuite{})
26 type keepClientStub struct {
27 blocks map[string][]byte
31 var errStub404 = errors.New("404 block not found")
33 func (kcs *keepClientStub) ReadAt(locator string, p []byte, off int) (int, error) {
36 buf := kcs.blocks[locator[:32]]
40 return copy(p, buf[off:]), nil
43 func (kcs *keepClientStub) PutB(p []byte) (string, int, error) {
44 locator := fmt.Sprintf("%x+%d+A12345@abcde", md5.Sum(p), len(p))
45 buf := make([]byte, len(p))
49 kcs.blocks[locator[:32]] = buf
50 return locator, 1, nil
53 type CollectionFSSuite struct {
56 fs CollectionFileSystem
60 func (s *CollectionFSSuite) SetUpTest(c *check.C) {
61 s.client = NewClientFromEnv()
62 err := s.client.RequestAndDecode(&s.coll, "GET", "arvados/v1/collections/"+arvadostest.FooAndBarFilesInDirUUID, nil, nil)
63 c.Assert(err, check.IsNil)
64 s.kc = &keepClientStub{
65 blocks: map[string][]byte{
66 "3858f62230ac3c915f300c664312c63f": []byte("foobar"),
68 s.fs, err = s.coll.FileSystem(s.client, s.kc)
69 c.Assert(err, check.IsNil)
72 func (s *CollectionFSSuite) TestHttpFileSystemInterface(c *check.C) {
73 _, ok := s.fs.(http.FileSystem)
74 c.Check(ok, check.Equals, true)
77 func (s *CollectionFSSuite) TestReaddirFull(c *check.C) {
78 f, err := s.fs.Open("/dir1")
79 c.Assert(err, check.IsNil)
82 c.Assert(err, check.IsNil)
83 c.Check(st.Size(), check.Equals, int64(2))
84 c.Check(st.IsDir(), check.Equals, true)
86 fis, err := f.Readdir(0)
87 c.Check(err, check.IsNil)
88 c.Check(len(fis), check.Equals, 2)
90 c.Check(fis[0].Size(), check.Equals, int64(3))
94 func (s *CollectionFSSuite) TestReaddirLimited(c *check.C) {
95 f, err := s.fs.Open("./dir1")
96 c.Assert(err, check.IsNil)
98 fis, err := f.Readdir(1)
99 c.Check(err, check.IsNil)
100 c.Check(len(fis), check.Equals, 1)
102 c.Check(fis[0].Size(), check.Equals, int64(3))
105 fis, err = f.Readdir(1)
106 c.Check(err, check.IsNil)
107 c.Check(len(fis), check.Equals, 1)
109 c.Check(fis[0].Size(), check.Equals, int64(3))
112 fis, err = f.Readdir(1)
113 c.Check(len(fis), check.Equals, 0)
114 c.Check(err, check.NotNil)
115 c.Check(err, check.Equals, io.EOF)
117 f, err = s.fs.Open("dir1")
118 c.Assert(err, check.IsNil)
119 fis, err = f.Readdir(1)
120 c.Check(len(fis), check.Equals, 1)
121 c.Assert(err, check.IsNil)
122 fis, err = f.Readdir(2)
123 c.Check(len(fis), check.Equals, 1)
124 c.Assert(err, check.IsNil)
125 fis, err = f.Readdir(2)
126 c.Check(len(fis), check.Equals, 0)
127 c.Assert(err, check.Equals, io.EOF)
130 func (s *CollectionFSSuite) TestPathMunge(c *check.C) {
131 for _, path := range []string{".", "/", "./", "///", "/../", "/./.."} {
132 f, err := s.fs.Open(path)
133 c.Assert(err, check.IsNil)
136 c.Assert(err, check.IsNil)
137 c.Check(st.Size(), check.Equals, int64(1))
138 c.Check(st.IsDir(), check.Equals, true)
140 for _, path := range []string{"/dir1", "dir1", "./dir1", "///dir1//.//", "../dir1/../dir1/"} {
142 f, err := s.fs.Open(path)
143 c.Assert(err, check.IsNil)
146 c.Assert(err, check.IsNil)
147 c.Check(st.Size(), check.Equals, int64(2))
148 c.Check(st.IsDir(), check.Equals, true)
152 func (s *CollectionFSSuite) TestNotExist(c *check.C) {
153 for _, path := range []string{"/no", "no", "./no", "n/o", "/n/o"} {
154 f, err := s.fs.Open(path)
155 c.Assert(f, check.IsNil)
156 c.Assert(err, check.NotNil)
157 c.Assert(os.IsNotExist(err), check.Equals, true)
161 func (s *CollectionFSSuite) TestReadOnlyFile(c *check.C) {
162 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDONLY, 0)
163 c.Assert(err, check.IsNil)
165 c.Assert(err, check.IsNil)
166 c.Check(st.Size(), check.Equals, int64(3))
167 n, err := f.Write([]byte("bar"))
168 c.Check(n, check.Equals, 0)
169 c.Check(err, check.Equals, ErrReadOnlyFile)
172 func (s *CollectionFSSuite) TestCreateFile(c *check.C) {
173 f, err := s.fs.OpenFile("/newfile", os.O_RDWR|os.O_CREATE, 0)
174 c.Assert(err, check.IsNil)
176 c.Assert(err, check.IsNil)
177 c.Check(st.Size(), check.Equals, int64(0))
179 n, err := f.Write([]byte("bar"))
180 c.Check(n, check.Equals, 3)
181 c.Check(err, check.IsNil)
183 c.Check(f.Close(), check.IsNil)
185 f, err = s.fs.OpenFile("/newfile", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0)
186 c.Check(f, check.IsNil)
187 c.Assert(err, check.NotNil)
189 f, err = s.fs.OpenFile("/newfile", os.O_RDWR, 0)
190 c.Assert(err, check.IsNil)
192 c.Assert(err, check.IsNil)
193 c.Check(st.Size(), check.Equals, int64(3))
195 c.Check(f.Close(), check.IsNil)
197 // TODO: serialize to Collection, confirm manifest contents,
198 // make new FileSystem, confirm file contents.
201 func (s *CollectionFSSuite) TestReadWriteFile(c *check.C) {
203 defer func() { maxBlockSize = 2 << 26 }()
205 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
206 c.Assert(err, check.IsNil)
209 c.Assert(err, check.IsNil)
210 c.Check(st.Size(), check.Equals, int64(3))
212 f2, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
213 c.Assert(err, check.IsNil)
216 buf := make([]byte, 64)
217 n, err := f.Read(buf)
218 c.Check(n, check.Equals, 3)
219 c.Check(err, check.Equals, io.EOF)
220 c.Check(string(buf[:3]), check.DeepEquals, "foo")
222 pos, err := f.Seek(-2, os.SEEK_CUR)
223 c.Check(pos, check.Equals, int64(1))
224 c.Check(err, check.IsNil)
226 // Split a storedExtent in two, and insert a memExtent
227 n, err = f.Write([]byte("*"))
228 c.Check(n, check.Equals, 1)
229 c.Check(err, check.IsNil)
231 pos, err = f.Seek(0, os.SEEK_CUR)
232 c.Check(pos, check.Equals, int64(2))
233 c.Check(err, check.IsNil)
235 pos, err = f.Seek(0, os.SEEK_SET)
236 c.Check(pos, check.Equals, int64(0))
237 c.Check(err, check.IsNil)
239 rbuf, err := ioutil.ReadAll(f)
240 c.Check(len(rbuf), check.Equals, 3)
241 c.Check(err, check.IsNil)
242 c.Check(string(rbuf), check.Equals, "f*o")
244 // Write multiple blocks in one call
245 f.Seek(1, os.SEEK_SET)
246 n, err = f.Write([]byte("0123456789abcdefg"))
247 c.Check(n, check.Equals, 17)
248 c.Check(err, check.IsNil)
249 pos, err = f.Seek(0, os.SEEK_CUR)
250 c.Check(pos, check.Equals, int64(18))
251 pos, err = f.Seek(-18, os.SEEK_CUR)
252 c.Check(err, check.IsNil)
253 n, err = io.ReadFull(f, buf)
254 c.Check(n, check.Equals, 18)
255 c.Check(err, check.Equals, io.ErrUnexpectedEOF)
256 c.Check(string(buf[:n]), check.Equals, "f0123456789abcdefg")
258 buf2, err := ioutil.ReadAll(f2)
259 c.Check(err, check.IsNil)
260 c.Check(string(buf2), check.Equals, "f0123456789abcdefg")
262 // truncate to current size
264 f2.Seek(0, os.SEEK_SET)
265 buf2, err = ioutil.ReadAll(f2)
266 c.Check(err, check.IsNil)
267 c.Check(string(buf2), check.Equals, "f0123456789abcdefg")
269 // shrink to zero some data
271 f2.Seek(0, os.SEEK_SET)
272 buf2, err = ioutil.ReadAll(f2)
273 c.Check(err, check.IsNil)
274 c.Check(string(buf2), check.Equals, "f0123456789abcd")
276 // grow to partial block/extent
278 f2.Seek(0, os.SEEK_SET)
279 buf2, err = ioutil.ReadAll(f2)
280 c.Check(err, check.IsNil)
281 c.Check(string(buf2), check.Equals, "f0123456789abcd\x00\x00\x00\x00\x00")
284 f2.Write([]byte("12345678abcdefghijkl"))
286 // grow to block/extent boundary
288 f2.Seek(0, os.SEEK_SET)
289 buf2, err = ioutil.ReadAll(f2)
290 c.Check(err, check.IsNil)
291 c.Check(len(buf2), check.Equals, 64)
292 c.Check(len(f.(*file).inode.(*filenode).extents), check.Equals, 8)
294 // shrink to block/extent boundary
296 f2.Seek(0, os.SEEK_SET)
297 buf2, err = ioutil.ReadAll(f2)
298 c.Check(err, check.IsNil)
299 c.Check(len(buf2), check.Equals, 32)
300 c.Check(len(f.(*file).inode.(*filenode).extents), check.Equals, 4)
302 // shrink to partial block/extent
304 f2.Seek(0, os.SEEK_SET)
305 buf2, err = ioutil.ReadAll(f2)
306 c.Check(err, check.IsNil)
307 c.Check(string(buf2), check.Equals, "12345678abcdefg")
308 c.Check(len(f.(*file).inode.(*filenode).extents), check.Equals, 2)
310 // Force flush to ensure the block "12345678" gets stored, so
311 // we know what to expect in the final manifest below.
312 _, err = s.fs.MarshalManifest(".")
313 c.Check(err, check.IsNil)
315 // Truncate to size=3 while f2's ptr is at 15
317 c.Check(err, check.IsNil)
318 buf2, err = ioutil.ReadAll(f2)
319 c.Check(err, check.IsNil)
320 c.Check(string(buf2), check.Equals, "")
321 f2.Seek(0, os.SEEK_SET)
322 buf2, err = ioutil.ReadAll(f2)
323 c.Check(err, check.IsNil)
324 c.Check(string(buf2), check.Equals, "123")
325 c.Check(len(f.(*file).inode.(*filenode).extents), check.Equals, 1)
327 m, err := s.fs.MarshalManifest(".")
328 c.Check(err, check.IsNil)
329 m = regexp.MustCompile(`\+A[^\+ ]+`).ReplaceAllLiteralString(m, "")
330 c.Check(m, check.Equals, "./dir1 3858f62230ac3c915f300c664312c63f+6 25d55ad283aa400af464c76d713c07ad+8 3:3:bar 6:3:foo\n")
333 func (s *CollectionFSSuite) TestMarshalSmallBlocks(c *check.C) {
335 defer func() { maxBlockSize = 2 << 26 }()
338 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
339 c.Assert(err, check.IsNil)
340 for _, name := range []string{"foo", "bar", "baz"} {
341 f, err := s.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0)
342 c.Assert(err, check.IsNil)
343 f.Write([]byte(name))
347 m, err := s.fs.MarshalManifest(".")
348 c.Check(err, check.IsNil)
349 m = regexp.MustCompile(`\+A[^\+ ]+`).ReplaceAllLiteralString(m, "")
350 c.Check(m, check.Equals, ". c3c23db5285662ef7172373df0003206+6 acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:bar 3:3:baz 6:3:foo\n")
353 func (s *CollectionFSSuite) TestMkdir(c *check.C) {
354 err := s.fs.Mkdir("foo/bar", 0755)
355 c.Check(err, check.Equals, os.ErrNotExist)
357 f, err := s.fs.OpenFile("foo/bar", os.O_CREATE, 0)
358 c.Check(err, check.Equals, os.ErrNotExist)
360 err = s.fs.Mkdir("foo", 0755)
361 c.Check(err, check.IsNil)
363 f, err = s.fs.OpenFile("foo/bar", os.O_CREATE|os.O_WRONLY, 0)
364 c.Check(err, check.IsNil)
367 f.Write([]byte("foo"))
370 // mkdir fails if a file already exists with that name
371 err = s.fs.Mkdir("foo/bar", 0755)
372 c.Check(err, check.NotNil)
374 err = s.fs.Remove("foo/bar")
375 c.Check(err, check.IsNil)
377 // mkdir succeds after the file is deleted
378 err = s.fs.Mkdir("foo/bar", 0755)
379 c.Check(err, check.IsNil)
381 // creating a file in a nonexistent subdir should still fail
382 f, err = s.fs.OpenFile("foo/bar/baz/foo.txt", os.O_CREATE|os.O_WRONLY, 0)
383 c.Check(err, check.Equals, os.ErrNotExist)
385 f, err = s.fs.OpenFile("foo/bar/foo.txt", os.O_CREATE|os.O_WRONLY, 0)
386 c.Check(err, check.IsNil)
389 f.Write([]byte("foo"))
392 // creating foo/bar as a regular file should fail
393 f, err = s.fs.OpenFile("foo/bar", os.O_CREATE|os.O_EXCL, 0)
394 c.Check(err, check.NotNil)
396 // creating foo/bar as a directory should fail
397 f, err = s.fs.OpenFile("foo/bar", os.O_CREATE|os.O_EXCL, os.ModeDir)
398 c.Check(err, check.NotNil)
399 err = s.fs.Mkdir("foo/bar", 0755)
400 c.Check(err, check.NotNil)
402 m, err := s.fs.MarshalManifest(".")
403 c.Check(err, check.IsNil)
404 m = regexp.MustCompile(`\+A[^\+ ]+`).ReplaceAllLiteralString(m, "")
405 c.Check(m, check.Equals, "./dir1 3858f62230ac3c915f300c664312c63f+6 3:3:bar 0:3:foo\n./foo/bar acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:foo.txt\n")
408 func (s *CollectionFSSuite) TestConcurrentWriters(c *check.C) {
410 defer func() { maxBlockSize = 2 << 26 }()
412 var wg sync.WaitGroup
413 for n := 0; n < 128; n++ {
417 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
418 c.Assert(err, check.IsNil)
420 for i := 0; i < 6502; i++ {
421 switch rand.Int() & 3 {
423 f.Truncate(int64(rand.Intn(64)))
425 f.Seek(int64(rand.Intn(64)), os.SEEK_SET)
427 _, err := f.Write([]byte("beep boop"))
428 c.Check(err, check.IsNil)
430 _, err := ioutil.ReadAll(f)
431 c.Check(err, check.IsNil)
438 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
439 c.Assert(err, check.IsNil)
441 buf, err := ioutil.ReadAll(f)
442 c.Check(err, check.IsNil)
443 c.Logf("after lots of random r/w/seek/trunc, buf is %q", buf)
446 func (s *CollectionFSSuite) TestRandomWrites(c *check.C) {
448 defer func() { maxBlockSize = 2 << 26 }()
451 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
452 c.Assert(err, check.IsNil)
455 const ngoroutines = 256
457 var wg sync.WaitGroup
458 for n := 0; n < nfiles; n++ {
462 expect := make([]byte, 0, 64)
463 wbytes := []byte("there's no simple explanation for anything important that any of us do")
464 f, err := s.fs.OpenFile(fmt.Sprintf("random-%d", n), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0)
465 c.Assert(err, check.IsNil)
467 for i := 0; i < ngoroutines; i++ {
468 trunc := rand.Intn(65)
469 woff := rand.Intn(trunc + 1)
470 wbytes = wbytes[:rand.Intn(64-woff+1)]
471 for buf, i := expect[:cap(expect)], len(expect); i < trunc; i++ {
474 expect = expect[:trunc]
475 if trunc < woff+len(wbytes) {
476 expect = expect[:woff+len(wbytes)]
478 copy(expect[woff:], wbytes)
479 f.Truncate(int64(trunc))
480 pos, err := f.Seek(int64(woff), os.SEEK_SET)
481 c.Check(pos, check.Equals, int64(woff))
482 c.Check(err, check.IsNil)
483 n, err := f.Write(wbytes)
484 c.Check(n, check.Equals, len(wbytes))
485 c.Check(err, check.IsNil)
486 pos, err = f.Seek(0, os.SEEK_SET)
487 c.Check(pos, check.Equals, int64(0))
488 c.Check(err, check.IsNil)
489 buf, err := ioutil.ReadAll(f)
490 c.Check(string(buf), check.Equals, string(expect))
491 c.Check(err, check.IsNil)
498 root, err := s.fs.Open("/")
499 c.Assert(err, check.IsNil)
501 fi, err := root.Readdir(-1)
502 c.Check(err, check.IsNil)
503 c.Check(len(fi), check.Equals, nfiles)
505 _, err = s.fs.MarshalManifest(".")
506 c.Check(err, check.IsNil)
507 // TODO: check manifest content
510 func (s *CollectionFSSuite) TestPersist(c *check.C) {
512 defer func() { maxBlockSize = 2 << 26 }()
515 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
516 c.Assert(err, check.IsNil)
517 err = s.fs.Mkdir("d:r", 0755)
518 c.Assert(err, check.IsNil)
520 expect := map[string][]byte{}
522 var wg sync.WaitGroup
523 for _, name := range []string{"random 1", "random:2", "random\\3", "d:r/random4"} {
524 buf := make([]byte, 500)
528 f, err := s.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0)
529 c.Assert(err, check.IsNil)
530 // Note: we don't close the file until after the test
531 // is done. Writes to unclosed files should persist.
537 for i := 0; i < len(buf); i += 5 {
538 _, err := f.Write(buf[i : i+5])
539 c.Assert(err, check.IsNil)
545 m, err := s.fs.MarshalManifest(".")
546 c.Check(err, check.IsNil)
549 root, err := s.fs.Open("/")
550 c.Assert(err, check.IsNil)
552 fi, err := root.Readdir(-1)
553 c.Check(err, check.IsNil)
554 c.Check(len(fi), check.Equals, 4)
556 persisted, err := (&Collection{ManifestText: m}).FileSystem(s.client, s.kc)
557 c.Assert(err, check.IsNil)
559 root, err = persisted.Open("/")
560 c.Assert(err, check.IsNil)
562 fi, err = root.Readdir(-1)
563 c.Check(err, check.IsNil)
564 c.Check(len(fi), check.Equals, 4)
566 for name, content := range expect {
567 c.Logf("read %q", name)
568 f, err := persisted.Open(name)
569 c.Assert(err, check.IsNil)
571 buf, err := ioutil.ReadAll(f)
572 c.Check(err, check.IsNil)
573 c.Check(buf, check.DeepEquals, content)
577 func (s *CollectionFSSuite) TestPersistEmptyFiles(c *check.C) {
579 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
580 c.Assert(err, check.IsNil)
581 for _, name := range []string{"dir", "zero", "zero/zero"} {
582 err = s.fs.Mkdir(name, 0755)
583 c.Assert(err, check.IsNil)
586 expect := map[string][]byte{
591 "dir/two": []byte{1, 2},
593 "zero/zero/zero": nil,
595 for name, data := range expect {
596 f, err := s.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0)
597 c.Assert(err, check.IsNil)
599 _, err := f.Write(data)
600 c.Assert(err, check.IsNil)
605 m, err := s.fs.MarshalManifest(".")
606 c.Check(err, check.IsNil)
609 persisted, err := (&Collection{ManifestText: m}).FileSystem(s.client, s.kc)
610 c.Assert(err, check.IsNil)
612 for name, data := range expect {
613 f, err := persisted.Open("bogus-" + name)
614 c.Check(err, check.NotNil)
616 f, err = persisted.Open(name)
617 c.Assert(err, check.IsNil)
622 buf, err := ioutil.ReadAll(f)
623 c.Check(err, check.IsNil)
624 c.Check(buf, check.DeepEquals, data)
628 func (s *CollectionFSSuite) TestFlushFullBlocks(c *check.C) {
630 defer func() { maxBlockSize = 2 << 26 }()
632 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
633 c.Assert(err, check.IsNil)
634 f, err := fs.OpenFile("50K", os.O_WRONLY|os.O_CREATE, 0)
635 c.Assert(err, check.IsNil)
638 data := make([]byte, 500)
641 for i := 0; i < 100; i++ {
642 n, err := f.Write(data)
643 c.Assert(n, check.Equals, len(data))
644 c.Assert(err, check.IsNil)
647 currentMemExtents := func() (memExtents []int) {
648 for idx, e := range f.(*file).inode.(*filenode).extents {
651 memExtents = append(memExtents, idx)
656 c.Check(currentMemExtents(), check.HasLen, 1)
658 m, err := fs.MarshalManifest(".")
659 c.Check(m, check.Not(check.Equals), "")
660 c.Check(err, check.IsNil)
661 c.Check(currentMemExtents(), check.HasLen, 0)
664 func (s *CollectionFSSuite) TestBrokenManifests(c *check.C) {
665 for _, txt := range []string{
669 ". d41d8cd98f00b204e9800998ecf8427e+0\n",
670 ". d41d8cd98f00b204e9800998ecf8427e+0 \n",
673 ". 0:0:foo 0:0:bar\n",
674 ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo\n",
675 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo:bar\n",
676 ". d41d8cd98f00b204e9800998ecf8427e+0 foo:0:foo\n",
677 ". d41d8cd98f00b204e9800998ecf8427e+0 0:foo:foo\n",
678 ". d41d8cd98f00b204e9800998ecf8427e+1 0:1:foo 1:1:bar\n",
679 ". d41d8cd98f00b204e9800998ecf8427e+1 0:0:foo\n./foo d41d8cd98f00b204e9800998ecf8427e+1 0:0:bar\n",
680 "./foo d41d8cd98f00b204e9800998ecf8427e+1 0:0:bar\n. d41d8cd98f00b204e9800998ecf8427e+1 0:0:foo\n",
683 fs, err := (&Collection{ManifestText: txt}).FileSystem(s.client, s.kc)
684 c.Check(fs, check.IsNil)
686 c.Check(err, check.NotNil)
690 func (s *CollectionFSSuite) TestEdgeCaseManifests(c *check.C) {
691 for _, txt := range []string{
693 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo\n",
694 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo 0:0:foo 0:0:bar\n",
695 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo 0:0:foo 0:0:bar\n",
696 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo/bar\n./foo d41d8cd98f00b204e9800998ecf8427e+0 0:0:bar\n",
699 fs, err := (&Collection{ManifestText: txt}).FileSystem(s.client, s.kc)
700 c.Check(err, check.IsNil)
701 c.Check(fs, check.NotNil)
705 func (s *CollectionFSSuite) checkMemSize(c *check.C, f File) {
706 fn := f.(*file).inode.(*filenode)
708 for _, ext := range fn.extents {
709 if e, ok := ext.(*memExtent); ok {
710 memsize += int64(len(e.buf))
713 c.Check(fn.memsize, check.Equals, memsize)
716 // Gocheck boilerplate
717 func Test(t *testing.T) {