diff --git a/fs/fs.go b/fs/fs.go index a25eb1039..f4552a589 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -370,6 +370,35 @@ type GetTierer interface { GetTier() string } +// ObjectOptionalInterfaces returns the names of supported and +// unsupported optional interfaces for an Object +func ObjectOptionalInterfaces(o Object) (supported, unsupported []string) { + store := func(ok bool, name string) { + if ok { + supported = append(supported, name) + } else { + unsupported = append(unsupported, name) + } + } + + _, ok := o.(MimeTyper) + store(ok, "MimeType") + + _, ok = o.(IDer) + store(ok, "ID") + + _, ok = o.(ObjectUnWrapper) + store(ok, "UnWrap") + + _, ok = o.(SetTierer) + store(ok, "SetTier") + + _, ok = o.(GetTierer) + store(ok, "GetTier") + + return supported, unsupported +} + // ListRCallback defines a callback function for ListR to use // // It is called for each tranche of entries read from the listing and diff --git a/fstest/fstests/fstests.go b/fstest/fstests/fstests.go index bdf20d40f..0923d29ac 100644 --- a/fstest/fstests/fstests.go +++ b/fstest/fstests/fstests.go @@ -15,6 +15,7 @@ import ( "os" "path" "path/filepath" + "reflect" "sort" "strings" "testing" @@ -241,13 +242,27 @@ type ExtraConfigItem struct{ Name, Key, Value string } // Opt is options for Run type Opt struct { - RemoteName string - NilObject fs.Object - ExtraConfig []ExtraConfigItem - SkipBadWindowsCharacters bool // skips unusable characters for windows if set - SkipFsMatch bool // if set skip exact matching of Fs value - TiersToTest []string // List of tiers which can be tested in setTier test - ChunkedUpload ChunkedUploadConfig + RemoteName string + NilObject fs.Object + ExtraConfig []ExtraConfigItem + SkipBadWindowsCharacters bool // skips unusable characters for windows if set + SkipFsMatch bool // if set skip exact matching of Fs value + TiersToTest []string // List of tiers which can be tested in setTier test + ChunkedUpload ChunkedUploadConfig + UnimplementableFsMethods []string // List of methods which can't be implemented in this wrapping Fs + UnimplementableObjectMethods []string // List of methods which can't be implemented in this wrapping Fs + SkipFsCheckWrap bool // if set skip FsCheckWrap + SkipObjectCheckWrap bool // if set skip ObjectCheckWrap +} + +// returns true if x is found in ss +func stringsContains(x string, ss []string) bool { + for _, s := range ss { + if x == s { + return true + } + } + return false } // Run runs the basic integration tests for a remote using the options passed in. @@ -360,6 +375,34 @@ func Run(t *testing.T, opt *Opt) { // Skip the rest if it failed skipIfNotOk(t) + // Check to see if Fs that wrap other Fs implement all the optional methods + t.Run("FsCheckWrap", func(t *testing.T) { + skipIfNotOk(t) + if opt.SkipFsCheckWrap { + t.Skip("Skipping FsCheckWrap on this Fs") + } + ft := new(fs.Features).Fill(remote) + if ft.UnWrap == nil { + t.Skip("Not a wrapping Fs") + } + v := reflect.ValueOf(ft).Elem() + vType := v.Type() + for i := 0; i < v.NumField(); i++ { + vName := vType.Field(i).Name + if stringsContains(vName, opt.UnimplementableFsMethods) { + continue + } + field := v.Field(i) + // skip the bools + if field.Type().Kind() == reflect.Bool { + continue + } + if field.IsNil() { + t.Errorf("Missing Fs wrapper for %s", vName) + } + } + }) + // TestFsRmdirNotFound tests deleting a non existent directory t.Run("FsRmdirNotFound", func(t *testing.T) { skipIfNotOk(t) @@ -1367,6 +1410,25 @@ func Run(t *testing.T, opt *Opt) { } }) + // Check to see if Fs that wrap other Objects implement all the optional methods + t.Run("ObjectCheckWrap", func(t *testing.T) { + skipIfNotOk(t) + if opt.SkipObjectCheckWrap { + t.Skip("Skipping FsCheckWrap on this Fs") + } + ft := new(fs.Features).Fill(remote) + if ft.UnWrap == nil { + t.Skip("Not a wrapping Fs") + } + obj := findObject(t, remote, file1.Path) + _, unsupported := fs.ObjectOptionalInterfaces(obj) + for _, name := range unsupported { + if !stringsContains(name, opt.UnimplementableObjectMethods) { + t.Errorf("Missing Object wrapper for %s", name) + } + } + }) + // TestObjectRemove tests Remove t.Run("ObjectRemove", func(t *testing.T) { skipIfNotOk(t)