+func (s *genericVolumeSuite) testPutConcurrent(t TB, factory TestableVolumeFactory) {
+ s.setup(t)
+ v := s.newVolume(t, factory)
+ defer v.Teardown()
+
+ blks := []struct {
+ hash string
+ data []byte
+ }{
+ {hash: TestHash, data: TestBlock},
+ {hash: TestHash2, data: TestBlock2},
+ {hash: TestHash3, data: TestBlock3},
+ }
+
+ var wg sync.WaitGroup
+ for _, blk := range blks {
+ blk := blk
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ err := v.BlockWrite(context.Background(), blk.hash, blk.data)
+ if err != nil {
+ t.Errorf("%s: %v", blk.hash, err)
+ }
+ }()
+ }
+ wg.Wait()
+
+ // Check that we actually wrote the blocks.
+ for _, blk := range blks {
+ buf := &brbuffer{}
+ err := v.BlockRead(context.Background(), blk.hash, buf)
+ if err != nil {
+ t.Errorf("get %s: %v", blk.hash, err)
+ } else if buf.String() != string(blk.data) {
+ t.Errorf("get %s: expected %s, got %s", blk.hash, blk.data, buf)
+ }
+ }
+}
+
+// Write and read back a full size block
+func (s *genericVolumeSuite) testPutFullBlock(t TB, factory TestableVolumeFactory) {
+ s.setup(t)
+ v := s.newVolume(t, factory)
+ defer v.Teardown()
+
+ wdata := make([]byte, BlockSize)
+ wdata[0] = 'a'
+ wdata[BlockSize-1] = 'z'
+ hash := fmt.Sprintf("%x", md5.Sum(wdata))
+ err := v.BlockWrite(context.Background(), hash, wdata)
+ if err != nil {
+ t.Error(err)
+ }
+
+ buf := &brbuffer{}
+ err = v.BlockRead(context.Background(), hash, buf)
+ if err != nil {
+ t.Error(err)
+ }
+ if buf.String() != string(wdata) {
+ t.Errorf("buf (len %d) != wdata (len %d)", buf.Len(), len(wdata))
+ }
+}
+
+// With BlobTrashLifetime != 0, perform:
+// Trash an old block - which either raises ErrNotImplemented or succeeds
+// Untrash - which either raises ErrNotImplemented or succeeds
+// Get - which must succeed
+func (s *genericVolumeSuite) testTrashUntrash(t TB, readonly bool, factory TestableVolumeFactory) {
+ s.setup(t)
+ s.cluster.Collections.BlobTrashLifetime.Set("1h")
+ v := s.newVolume(t, factory)