+// is expected to pass. It calls factory to create a new TestableVolume
+// for each test case, to avoid leaking state between tests.
+func DoGenericVolumeTests(t TB, readonly bool, factory TestableVolumeFactory) {
+ var s genericVolumeSuite
+ s.volume.ReadOnly = readonly
+
+ s.testGet(t, factory)
+ s.testGetNoSuchBlock(t, factory)
+
+ if !readonly {
+ s.testPutBlockWithSameContent(t, factory, TestHash, TestBlock)
+ s.testPutBlockWithSameContent(t, factory, EmptyHash, EmptyBlock)
+ s.testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, arvadostest.MD5CollisionData[0], arvadostest.MD5CollisionData[1])
+ s.testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, EmptyBlock, arvadostest.MD5CollisionData[0])
+ s.testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, arvadostest.MD5CollisionData[0], EmptyBlock)
+ s.testPutBlockWithDifferentContent(t, factory, EmptyHash, EmptyBlock, arvadostest.MD5CollisionData[0])
+ s.testPutMultipleBlocks(t, factory)
+
+ s.testPutAndTouch(t, factory)
+ }
+ s.testTouchNoSuchBlock(t, factory)
+
+ s.testMtimeNoSuchBlock(t, factory)
+
+ s.testIndex(t, factory)
+
+ if !readonly {
+ s.testDeleteNewBlock(t, factory)
+ s.testDeleteOldBlock(t, factory)
+ }
+ s.testDeleteNoSuchBlock(t, factory)
+
+ s.testMetrics(t, readonly, factory)
+
+ s.testGetConcurrent(t, factory)
+ if !readonly {
+ s.testPutConcurrent(t, factory)
+ s.testPutFullBlock(t, factory)
+ s.testTrashUntrash(t, readonly, factory)
+ s.testTrashEmptyTrashUntrash(t, factory)
+ }
+}
+
+type genericVolumeSuite struct {
+ cluster *arvados.Cluster
+ volume arvados.Volume
+ logger logrus.FieldLogger
+ metrics *volumeMetricsVecs
+ registry *prometheus.Registry
+ bufferPool *bufferPool
+}
+
+func (s *genericVolumeSuite) setup(t TB) {
+ s.cluster = testCluster(t)
+ s.logger = ctxlog.TestLogger(t)
+ s.registry = prometheus.NewRegistry()
+ s.metrics = newVolumeMetricsVecs(s.registry)
+ s.bufferPool = newBufferPool(s.logger, 8, s.registry)
+}
+
+func (s *genericVolumeSuite) newVolume(t TB, factory TestableVolumeFactory) TestableVolume {
+ return factory(t, newVolumeParams{
+ UUID: "zzzzz-nyw5e-999999999999999",
+ Cluster: s.cluster,
+ ConfigVolume: s.volume,
+ Logger: s.logger,
+ MetricsVecs: s.metrics,
+ BufferPool: s.bufferPool,
+ })
+}
+
+// Put a test block, get it and verify content
+// Test should pass for both writable and read-only volumes
+func (s *genericVolumeSuite) testGet(t TB, factory TestableVolumeFactory) {
+ s.setup(t)
+ v := s.newVolume(t, factory)
+ defer v.Teardown()
+
+ err := v.BlockWrite(context.Background(), TestHash, TestBlock)
+ if err != nil {
+ t.Error(err)
+ }
+
+ buf := &brbuffer{}
+ err = v.BlockRead(context.Background(), TestHash, buf)
+ if err != nil {
+ t.Error(err)
+ }
+ if bytes.Compare(buf.Bytes(), TestBlock) != 0 {
+ t.Errorf("expected %s, got %s", "foo", buf.String())
+ }
+}
+
+// Invoke get on a block that does not exist in volume; should result in error
+// Test should pass for both writable and read-only volumes
+func (s *genericVolumeSuite) testGetNoSuchBlock(t TB, factory TestableVolumeFactory) {
+ s.setup(t)
+ v := s.newVolume(t, factory)
+ defer v.Teardown()
+
+ if err := v.BlockRead(context.Background(), barHash, brdiscard); err == nil {
+ t.Errorf("Expected error while getting non-existing block %v", barHash)
+ }
+}
+
+// Put a block and put again with same content
+// Test is intended for only writable volumes
+func (s *genericVolumeSuite) testPutBlockWithSameContent(t TB, factory TestableVolumeFactory, testHash string, testData []byte) {
+ s.setup(t)
+ v := s.newVolume(t, factory)
+ defer v.Teardown()
+
+ err := v.BlockWrite(context.Background(), testHash, testData)
+ if err != nil {
+ t.Errorf("Got err putting block %q: %q, expected nil", TestBlock, err)
+ }
+
+ err = v.BlockWrite(context.Background(), testHash, testData)
+ if err != nil {
+ t.Errorf("Got err putting block second time %q: %q, expected nil", TestBlock, err)
+ }
+}
+
+// Put a block and put again with different content
+// Test is intended for only writable volumes
+func (s *genericVolumeSuite) testPutBlockWithDifferentContent(t TB, factory TestableVolumeFactory, testHash string, testDataA, testDataB []byte) {
+ s.setup(t)
+ v := s.newVolume(t, factory)
+ defer v.Teardown()
+
+ v.BlockWrite(context.Background(), testHash, testDataA)
+
+ putErr := v.BlockWrite(context.Background(), testHash, testDataB)
+ buf := &brbuffer{}
+ getErr := v.BlockRead(context.Background(), testHash, buf)
+ if putErr == nil {
+ // Put must not return a nil error unless it has
+ // overwritten the existing data.
+ if buf.String() != string(testDataB) {
+ t.Errorf("Put succeeded but Get returned %+q, expected %+q", buf, testDataB)
+ }
+ } else {
+ // It is permissible for Put to fail, but it must
+ // leave us with either the original data, the new
+ // data, or nothing at all.
+ if getErr == nil && buf.String() != string(testDataA) && buf.String() != string(testDataB) {
+ t.Errorf("Put failed but Get returned %+q, which is neither %+q nor %+q", buf, testDataA, testDataB)
+ }
+ }
+}
+
+// Put and get multiple blocks
+// Test is intended for only writable volumes
+func (s *genericVolumeSuite) testPutMultipleBlocks(t TB, factory TestableVolumeFactory) {
+ s.setup(t)
+ v := s.newVolume(t, factory)
+ defer v.Teardown()
+
+ err := v.BlockWrite(context.Background(), TestHash, TestBlock)
+ if err != nil {
+ t.Errorf("Got err putting block %q: %q, expected nil", TestBlock, err)
+ }
+
+ err = v.BlockWrite(context.Background(), TestHash2, TestBlock2)
+ if err != nil {
+ t.Errorf("Got err putting block %q: %q, expected nil", TestBlock2, err)
+ }
+
+ err = v.BlockWrite(context.Background(), TestHash3, TestBlock3)
+ if err != nil {
+ t.Errorf("Got err putting block %q: %q, expected nil", TestBlock3, err)
+ }
+
+ buf := &brbuffer{}
+ err = v.BlockRead(context.Background(), TestHash, buf)
+ if err != nil {
+ t.Error(err)
+ } else {
+ if bytes.Compare(buf.Bytes(), TestBlock) != 0 {
+ t.Errorf("Block present, but got %+q, expected %+q", buf, TestBlock)
+ }
+ }
+
+ buf.Reset()
+ err = v.BlockRead(context.Background(), TestHash2, buf)
+ if err != nil {
+ t.Error(err)
+ } else {
+ if bytes.Compare(buf.Bytes(), TestBlock2) != 0 {
+ t.Errorf("Block present, but got %+q, expected %+q", buf, TestBlock2)
+ }
+ }
+
+ buf.Reset()
+ err = v.BlockRead(context.Background(), TestHash3, buf)
+ if err != nil {
+ t.Error(err)
+ } else {
+ if bytes.Compare(buf.Bytes(), TestBlock3) != 0 {
+ t.Errorf("Block present, but to %+q, expected %+q", buf, TestBlock3)
+ }
+ }
+}