// Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use // of this source code is governed by a BSD-style license that can be found in // the LICENSE file. package tool import ( "bytes" "fmt" "os" "path/filepath" "strings" "testing" "time" "github.com/cockroachdb/datadriven" "github.com/cockroachdb/errors" "github.com/cockroachdb/pebble/internal/base" "github.com/cockroachdb/pebble/internal/testkeys" "github.com/cockroachdb/pebble/vfs" "github.com/spf13/cobra" "github.com/stretchr/testify/require" ) func runTests(t *testing.T, path string) { paths, err := filepath.Glob(path) require.NoError(t, err) root := filepath.Dir(path) for { next := filepath.Dir(root) if next == "." { break } root = next } normalize := func(name string) string { if os.PathSeparator == '/' { return name } return strings.Replace(name, "/", string(os.PathSeparator), -1) } for _, path := range paths { name, err := filepath.Rel(root, path) require.NoError(t, err) fs := vfs.NewMem() t.Run(name, func(t *testing.T) { datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { args := []string{d.Cmd} for _, arg := range d.CmdArgs { args = append(args, arg.String()) } args = append(args, strings.Fields(d.Input)...) // The testdata files contain paths with "/" path separators, but we // might be running on a system with a different path separator // (e.g. Windows). Copy the input data into a mem filesystem which // always uses "/" for the path separator. for i := range args { src := normalize(args[i]) dest := vfs.Default.PathBase(src) if ok, err := vfs.Clone(vfs.Default, fs, src, dest); err != nil { return err.Error() } else if ok { args[i] = fs.PathBase(args[i]) } } var buf bytes.Buffer var secs int64 timeNow = func() time.Time { secs++; return time.Unix(secs, 0) } defer func() { timeNow = time.Now }() // Register a test comparer and merger so that we can check the // behavior of tools when the comparer and merger do not match. comparer := func() *Comparer { c := *base.DefaultComparer c.Name = "test-comparer" c.FormatKey = func(key []byte) fmt.Formatter { return fmtFormatter{ fmt: "test formatter: %s", v: key, } } c.FormatValue = func(_, value []byte) fmt.Formatter { return fmtFormatter{ fmt: "test value formatter: %s", v: value, } } return &c }() altComparer := func() *Comparer { c := *base.DefaultComparer c.Name = "alt-comparer" return &c }() merger := func() *Merger { m := *base.DefaultMerger m.Name = "test-merger" return &m }() openErrEnhancer := func(err error) error { if errors.Is(err, base.ErrCorruption) { return base.CorruptionErrorf("%v\nCustom message in case of corruption error.", err) } return err } tool := New( DefaultComparer(comparer), Comparers(altComparer, testkeys.Comparer), Mergers(merger), FS(fs), OpenErrEnhancer(openErrEnhancer), ) c := &cobra.Command{} c.AddCommand(tool.Commands...) c.SetArgs(args) c.SetOut(&buf) c.SetErr(&buf) if err := c.Execute(); err != nil { return err.Error() } return buf.String() }) }) } }