package testsuites import ( "bytes" "io/ioutil" "math/rand" "path" "sort" "testing" "github.com/docker/docker-registry/storagedriver" "github.com/docker/docker-registry/storagedriver/ipc" . "gopkg.in/check.v1" ) // Hook up gocheck into the "go test" runner func Test(t *testing.T) { TestingT(t) } // RegisterInProcessSuite registers an in-process storage driver test suite with the go test runner func RegisterInProcessSuite(driverConstructor DriverConstructor, skipCheck SkipCheck) { Suite(&DriverSuite{ Constructor: driverConstructor, SkipCheck: skipCheck, }) } // RegisterIPCSuite registers a storage driver test suite which runs the named driver as a child // process with the given parameters func RegisterIPCSuite(driverName string, ipcParams map[string]string, skipCheck SkipCheck) { suite := &DriverSuite{ Constructor: func() (storagedriver.StorageDriver, error) { d, err := ipc.NewDriverClient(driverName, ipcParams) if err != nil { return nil, err } err = d.Start() if err != nil { return nil, err } return d, nil }, SkipCheck: skipCheck, } suite.Teardown = func() error { if suite.StorageDriver == nil { return nil } driverClient := suite.StorageDriver.(*ipc.StorageDriverClient) return driverClient.Stop() } Suite(suite) } // SkipCheck is a function used to determine if a test suite should be skipped // If a SkipCheck returns a non-empty skip reason, the suite is skipped with the given reason type SkipCheck func() (reason string) // NeverSkip is a default SkipCheck which never skips the suite var NeverSkip SkipCheck = func() string { return "" } // DriverConstructor is a function which returns a new storagedriver.StorageDriver type DriverConstructor func() (storagedriver.StorageDriver, error) // DriverTeardown is a function which cleans up a suite's storagedriver.StorageDriver type DriverTeardown func() error // DriverSuite is a gocheck test suite designed to test a storagedriver.StorageDriver // The intended way to create a DriverSuite is with RegisterInProcessSuite or RegisterIPCSuite type DriverSuite struct { Constructor DriverConstructor Teardown DriverTeardown SkipCheck storagedriver.StorageDriver } func (suite *DriverSuite) SetUpSuite(c *C) { if reason := suite.SkipCheck(); reason != "" { c.Skip(reason) } d, err := suite.Constructor() c.Assert(err, IsNil) suite.StorageDriver = d } func (suite *DriverSuite) TearDownSuite(c *C) { if suite.Teardown != nil { err := suite.Teardown() c.Assert(err, IsNil) } } func (suite *DriverSuite) TestWriteRead1(c *C) { filename := randomString(32) contents := []byte("a") suite.writeReadCompare(c, filename, contents, contents) } func (suite *DriverSuite) TestWriteRead2(c *C) { filename := randomString(32) contents := []byte("\xc3\x9f") suite.writeReadCompare(c, filename, contents, contents) } func (suite *DriverSuite) TestWriteRead3(c *C) { filename := randomString(32) contents := []byte(randomString(32)) suite.writeReadCompare(c, filename, contents, contents) } func (suite *DriverSuite) TestWriteRead4(c *C) { filename := randomString(32) contents := []byte(randomString(1024 * 1024)) suite.writeReadCompare(c, filename, contents, contents) } func (suite *DriverSuite) TestReadNonexistent(c *C) { filename := randomString(32) _, err := suite.StorageDriver.GetContent(filename) c.Assert(err, NotNil) } func (suite *DriverSuite) TestWriteReadStreams1(c *C) { filename := randomString(32) contents := []byte("a") suite.writeReadCompareStreams(c, filename, contents, contents) } func (suite *DriverSuite) TestWriteReadStreams2(c *C) { filename := randomString(32) contents := []byte("\xc3\x9f") suite.writeReadCompareStreams(c, filename, contents, contents) } func (suite *DriverSuite) TestWriteReadStreams3(c *C) { filename := randomString(32) contents := []byte(randomString(32)) suite.writeReadCompareStreams(c, filename, contents, contents) } func (suite *DriverSuite) TestWriteReadStreams4(c *C) { filename := randomString(32) contents := []byte(randomString(1024 * 1024)) suite.writeReadCompareStreams(c, filename, contents, contents) } func (suite *DriverSuite) TestContinueStreamAppend(c *C) { filename := randomString(32) defer suite.StorageDriver.Delete(filename) chunkSize := uint64(10 * 1024 * 1024) contentsChunk1 := []byte(randomString(chunkSize)) contentsChunk2 := []byte(randomString(chunkSize)) contentsChunk3 := []byte(randomString(chunkSize)) fullContents := append(append(contentsChunk1, contentsChunk2...), contentsChunk3...) err := suite.StorageDriver.WriteStream(filename, 0, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(contentsChunk1))) c.Assert(err, IsNil) offset, err := suite.StorageDriver.CurrentSize(filename) c.Assert(err, IsNil) if offset > chunkSize { c.Fatalf("Offset too large, %d > %d", offset, chunkSize) } err = suite.StorageDriver.WriteStream(filename, offset, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(fullContents[offset:2*chunkSize]))) c.Assert(err, IsNil) offset, err = suite.StorageDriver.CurrentSize(filename) c.Assert(err, IsNil) if offset > 2*chunkSize { c.Fatalf("Offset too large, %d > %d", offset, 2*chunkSize) } err = suite.StorageDriver.WriteStream(filename, offset, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(fullContents[offset:]))) c.Assert(err, IsNil) received, err := suite.StorageDriver.GetContent(filename) c.Assert(err, IsNil) c.Assert(received, DeepEquals, fullContents) } func (suite *DriverSuite) TestReadStreamWithOffset(c *C) { filename := randomString(32) defer suite.StorageDriver.Delete(filename) chunkSize := uint64(32) contentsChunk1 := []byte(randomString(chunkSize)) contentsChunk2 := []byte(randomString(chunkSize)) contentsChunk3 := []byte(randomString(chunkSize)) err := suite.StorageDriver.PutContent(filename, append(append(contentsChunk1, contentsChunk2...), contentsChunk3...)) c.Assert(err, IsNil) reader, err := suite.StorageDriver.ReadStream(filename, 0) c.Assert(err, IsNil) defer reader.Close() readContents, err := ioutil.ReadAll(reader) c.Assert(err, IsNil) c.Assert(readContents, DeepEquals, append(append(contentsChunk1, contentsChunk2...), contentsChunk3...)) reader, err = suite.StorageDriver.ReadStream(filename, chunkSize) c.Assert(err, IsNil) defer reader.Close() readContents, err = ioutil.ReadAll(reader) c.Assert(err, IsNil) c.Assert(readContents, DeepEquals, append(contentsChunk2, contentsChunk3...)) reader, err = suite.StorageDriver.ReadStream(filename, chunkSize*2) c.Assert(err, IsNil) defer reader.Close() readContents, err = ioutil.ReadAll(reader) c.Assert(err, IsNil) c.Assert(readContents, DeepEquals, contentsChunk3) } func (suite *DriverSuite) TestReadNonexistentStream(c *C) { filename := randomString(32) _, err := suite.StorageDriver.ReadStream(filename, 0) c.Assert(err, NotNil) } func (suite *DriverSuite) TestList(c *C) { rootDirectory := randomString(uint64(8 + rand.Intn(8))) defer suite.StorageDriver.Delete(rootDirectory) parentDirectory := rootDirectory + "/" + randomString(uint64(8+rand.Intn(8))) childFiles := make([]string, 50) for i := 0; i < len(childFiles); i++ { childFile := parentDirectory + "/" + randomString(uint64(8+rand.Intn(8))) childFiles[i] = childFile err := suite.StorageDriver.PutContent(childFile, []byte(randomString(32))) c.Assert(err, IsNil) } sort.Strings(childFiles) keys, err := suite.StorageDriver.List(rootDirectory) c.Assert(err, IsNil) c.Assert(keys, DeepEquals, []string{parentDirectory}) keys, err = suite.StorageDriver.List(parentDirectory) c.Assert(err, IsNil) sort.Strings(keys) c.Assert(keys, DeepEquals, childFiles) } func (suite *DriverSuite) TestMove(c *C) { contents := []byte(randomString(32)) sourcePath := randomString(32) destPath := randomString(32) defer suite.StorageDriver.Delete(sourcePath) defer suite.StorageDriver.Delete(destPath) err := suite.StorageDriver.PutContent(sourcePath, contents) c.Assert(err, IsNil) err = suite.StorageDriver.Move(sourcePath, destPath) c.Assert(err, IsNil) received, err := suite.StorageDriver.GetContent(destPath) c.Assert(err, IsNil) c.Assert(received, DeepEquals, contents) _, err = suite.StorageDriver.GetContent(sourcePath) c.Assert(err, NotNil) } func (suite *DriverSuite) TestMoveNonexistent(c *C) { sourcePath := randomString(32) destPath := randomString(32) err := suite.StorageDriver.Move(sourcePath, destPath) c.Assert(err, NotNil) } func (suite *DriverSuite) TestRemove(c *C) { filename := randomString(32) contents := []byte(randomString(32)) defer suite.StorageDriver.Delete(filename) err := suite.StorageDriver.PutContent(filename, contents) c.Assert(err, IsNil) err = suite.StorageDriver.Delete(filename) c.Assert(err, IsNil) _, err = suite.StorageDriver.GetContent(filename) c.Assert(err, NotNil) } func (suite *DriverSuite) TestRemoveNonexistent(c *C) { filename := randomString(32) err := suite.StorageDriver.Delete(filename) c.Assert(err, NotNil) } func (suite *DriverSuite) TestRemoveFolder(c *C) { dirname := randomString(32) filename1 := randomString(32) filename2 := randomString(32) contents := []byte(randomString(32)) defer suite.StorageDriver.Delete(path.Join(dirname, filename1)) defer suite.StorageDriver.Delete(path.Join(dirname, filename2)) err := suite.StorageDriver.PutContent(path.Join(dirname, filename1), contents) c.Assert(err, IsNil) err = suite.StorageDriver.PutContent(path.Join(dirname, filename2), contents) c.Assert(err, IsNil) err = suite.StorageDriver.Delete(dirname) c.Assert(err, IsNil) _, err = suite.StorageDriver.GetContent(path.Join(dirname, filename1)) c.Assert(err, NotNil) _, err = suite.StorageDriver.GetContent(path.Join(dirname, filename2)) c.Assert(err, NotNil) } func (suite *DriverSuite) writeReadCompare(c *C, filename string, contents, expected []byte) { defer suite.StorageDriver.Delete(filename) err := suite.StorageDriver.PutContent(filename, contents) c.Assert(err, IsNil) readContents, err := suite.StorageDriver.GetContent(filename) c.Assert(err, IsNil) c.Assert(readContents, DeepEquals, contents) } func (suite *DriverSuite) writeReadCompareStreams(c *C, filename string, contents, expected []byte) { defer suite.StorageDriver.Delete(filename) err := suite.StorageDriver.WriteStream(filename, 0, uint64(len(contents)), ioutil.NopCloser(bytes.NewReader(contents))) c.Assert(err, IsNil) reader, err := suite.StorageDriver.ReadStream(filename, 0) c.Assert(err, IsNil) defer reader.Close() readContents, err := ioutil.ReadAll(reader) c.Assert(err, IsNil) c.Assert(readContents, DeepEquals, contents) } var pathChars = []byte("abcdefghijklmnopqrstuvwxyz") func randomString(length uint64) string { b := make([]byte, length) for i := range b { b[i] = pathChars[rand.Intn(len(pathChars))] } return string(b) }