Repository: cockroachdb/pebble
Pull request #2169 (2022-12-06 16:15:55)

crossversion: don't stream TestMeta output on verbose
Previously, I added functionality to stream metamorphic test output to stdout when verbosity is set to help in debugging recent failures. When tests are run in parallel, streaming makes the output difficult to parse. Put the streaming output behind a new, separate cli flag.

Informs #2144.

cockroach-teamcity: 

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/cockroachdb/pebble/2169)
<!-- Reviewable:end -->


Patch:
diff --git a/go.mod b/go.mod
index 8ccdf262..32098e14 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ require (
 	github.com/DataDog/zstd v1.4.5
 	github.com/HdrHistogram/hdrhistogram-go v1.1.2
 	github.com/cespare/xxhash/v2 v2.1.2
+	github.com/cockroachdb/datadriven v1.0.2
 	github.com/cockroachdb/errors v1.8.1
 	github.com/cockroachdb/redact v1.0.8
 	github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9
diff --git a/go.sum b/go.sum
index 45fa6910..360272d0 100644
--- a/go.sum
+++ b/go.sum
@@ -69,6 +69,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
+github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
+github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
 github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
 github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y=
 github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac=
diff --git a/internal/datadriven/datadriven.go b/internal/datadriven/datadriven.go
deleted file mode 100644
index 926950b4..00000000
--- a/internal/datadriven/datadriven.go
+++ /dev/null
@@ -1,330 +0,0 @@
-// Copyright 2018 The Cockroach Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-// implied. See the License for the specific language governing
-// permissions and limitations under the License.
-
-package datadriven // import "github.com/cockroachdb/pebble/internal/datadriven"
-
-import (
-	"bufio"
-	"flag"
-	"fmt"
-	"io"
-	"os"
-	"path/filepath"
-	"strconv"
-	"strings"
-	"testing"
-
-	"github.com/cockroachdb/errors"
-)
-
-var (
-	rewriteTestFiles = flag.Bool(
-		"rewrite", false,
-		"ignore the expected results and rewrite the test files with the actual results from this "+
-			"run. Used to update tests when a change affects many cases; please verify the testfile "+
-			"diffs carefully!",
-	)
-)
-
-// RunTest invokes a data-driven test. The test cases are contained in a
-// separate test file and are dynamically loaded, parsed, and executed by this
-// testing framework. By convention, test files are typically located in a
-// sub-directory called "testdata". Each test file has the following format:
-//
-//   <command>[,<command>...] [arg | arg=val | arg=(val1, val2, ...)]...
-//   <input to the command>
-//   ----
-//   <expected results>
-//
-// The command input can contain blank lines. However, by default, the expected
-// results cannot contain blank lines. This alternate syntax allows the use of
-// blank lines:
-//
-//   <command>[,<command>...] [arg | arg=val | arg=(val1, val2, ...)]...
-//   <input to the command>
-//   ----
-//   ----
-//   <expected results>
-//
-//   <more expected results>
-//   ----
-//   ----
-//
-// To execute data-driven tests, pass the path of the test file as well as a
-// function which can interpret and execute whatever commands are present in
-// the test file. The framework invokes the function, passing it information
-// about the test case in a TestData struct. The function then returns the
-// actual results of the case, which this function compares with the expected
-// results, and either succeeds or fails the test.
-func RunTest(t *testing.T, path string, f func(d *TestData) string) {
-	t.Helper()
-	file, err := os.OpenFile(path, os.O_RDWR, 0644 /* irrelevant */)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer func() {
-		_ = file.Close()
-	}()
-
-	runTestInternal(t, path, file, f, *rewriteTestFiles)
-}
-
-// RunTestFromString is a version of RunTest which takes the contents of a test
-// directly.
-func RunTestFromString(t *testing.T, input string, f func(d *TestData) string) {
-	t.Helper()
-	runTestInternal(t, "<string>" /* optionalPath */, strings.NewReader(input), f, *rewriteTestFiles)
-}
-
-func runTestInternal(
-	t *testing.T, sourceName string, reader io.Reader, f func(d *TestData) string, rewrite bool,
-) {
-	t.Helper()
-
-	r := newTestDataReader(t, sourceName, reader, rewrite)
-	for r.Next(t) {
-		d := &r.data
-		actual := func() string {
-			defer func() {
-				if r := recover(); r != nil {
-					fmt.Printf("\npanic during %s:\n%s\n", d.Pos, d.Input)
-					panic(r)
-				}
-			}()
-			s := f(d)
-			if n := len(s); n > 0 && s[n-1] != '\n' {
-				s += "\n"
-			}
-			return s
-		}()
-
-		if r.rewrite != nil {
-			r.emit("----")
-			if hasBlankLine(actual) {
-				r.emit("----")
-				r.rewrite.WriteString(actual)
-				r.emit("----")
-				r.emit("----")
-			} else {
-				r.emit(actual)
-			}
-		} else if d.Expected != actual {
-			t.Fatalf("\n%s: %s\nexpected:\n%s\nfound:\n%s", d.Pos, d.Input, d.Expected, actual)
-		} else if testing.Verbose() {
-			input := d.Input
-			if input == "" {
-				input = "<no input to command>"
-			}
-			// TODO(tbg): it's awkward to reproduce the args, but it would be helpful.
-			fmt.Printf("\n%s:\n%s [%d args]\n%s\n----\n%s", d.Pos, d.Cmd, len(d.CmdArgs), input, actual)
-		}
-	}
-
-	if r.rewrite != nil {
-		data := r.rewrite.Bytes()
-		if l := len(data); l > 2 && data[l-1] == '\n' && data[l-2] == '\n' {
-			data = data[:l-1]
-		}
-		if dest, ok := reader.(*os.File); ok {
-			if _, err := dest.WriteAt(data, 0); err != nil {
-				t.Fatal(err)
-			}
-			if err := dest.Truncate(int64(len(data))); err != nil {
-				t.Fatal(err)
-			}
-			if err := dest.Sync(); err != nil {
-				t.Fatal(err)
-			}
-		} else {
-			t.Logf("input is not a file; rewritten output is:\n%s", data)
-		}
-	}
-}
-
-// Walk goes through all the files in a subdirectory, creating subtests to match
-// the file hierarchy; for each "leaf" file, the given function is called.
-//
-// This can be used in conjunction with RunTest. For example:
-//
-//    datadriven.Walk(t, path, func (t *testing.T, path string) {
-//      // initialize per-test state
-//      datadriven.RunTest(t, path, func (d *datadriven.TestData) {
-//       // ...
-//      }
-//    }
-//
-//   Files:
-//     testdata/typing
-//     testdata/logprops/scan
-//     testdata/logprops/select
-//
-//   If path is "testdata/typing", the function is called once and no subtests
-//   care created.
-//
-//   If path is "testdata/logprops", the function is called two times, in
-//   separate subtests /scan, /select.
-//
-//   If path is "testdata", the function is called three times, in subtest
-//   hierarchy /typing, /logprops/scan, /logprops/select.
-//
-func Walk(t *testing.T, path string, f func(t *testing.T, path string)) {
-	finfo, err := os.Stat(path)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !finfo.IsDir() {
-		f(t, path)
-		return
-	}
-	files, err := os.ReadDir(path)
-	if err != nil {
-		t.Fatal(err)
-	}
-	for _, file := range files {
-		t.Run(file.Name(), func(t *testing.T) {
-			Walk(t, filepath.Join(path, file.Name()), f)
-		})
-	}
-}
-
-// TestData contains information about one data-driven test case that was
-// parsed from the test file.
-type TestData struct {
-	Pos string // reader and line number
-
-	// Cmd is the first string on the directive line (up to the first whitespace).
-	Cmd string
-
-	CmdArgs []CmdArg
-
-	Input    string
-	Expected string
-}
-
-// ScanArgs looks up the first CmdArg matching the given key and scans it into
-// the given destinations in order. If the arg does not exist, the number of
-// destinations does not match that of the arguments, or a destination can not
-// be populated from its matching value, a fatal error results.
-//
-// For example, for a TestData originating from
-//
-// cmd arg1=50 arg2=yoruba arg3=(50, 50, 50)
-//
-// the following would be valid:
-//
-// var i1, i2, i3, i4 int
-// var s string
-// td.ScanArgs(t, "arg1", &i1)
-// td.ScanArgs(t, "arg2", &s)
-// td.ScanArgs(t, "arg3", &i2, &i3, &i4)
-func (td *TestData) ScanArgs(t *testing.T, key string, dests ...interface{}) {
-	t.Helper()
-	arg := td.findArg(key)
-	if arg == nil {
-		t.Fatalf("missing argument: %s", key)
-	}
-	err := arg.scan(dests...)
-	if err != nil {
-		t.Fatal(err)
-	}
-}
-
-// HasArg determines if `key` appears in CmdArgs.
-func (td *TestData) HasArg(key string) bool {
-	return td.findArg(key) != nil
-}
-
-func (td *TestData) findArg(key string) *CmdArg {
-	for i := range td.CmdArgs {
-		if td.CmdArgs[i].Key == key {
-			return &td.CmdArgs[i]
-		}
-	}
-	return nil
-}
-
-// CmdArg contains information about an argument on the directive line. An
-// argument is specified in one of the following forms:
-//  - argument
-//  - argument=value
-//  - argument=(values, ...)
-type CmdArg struct {
-	Key  string
-	Vals []string
-}
-
-func (arg CmdArg) String() string {
-	switch len(arg.Vals) {
-	case 0:
-		return arg.Key
-
-	case 1:
-		return fmt.Sprintf("%s=%s", arg.Key, arg.Vals[0])
-
-	default:
-		return fmt.Sprintf("%s=(%s)", arg.Key, strings.Join(arg.Vals, ", "))
-	}
-}
-
-func (arg CmdArg) scan(dests ...interface{}) error {
-	if len(dests) != len(arg.Vals) {
-		return errors.Errorf("%s: got %d destinations, but %d values", arg.Key, len(dests), len(arg.Vals))
-	}
-
-	for i := range dests {
-		val := arg.Vals[i]
-		switch dest := dests[i].(type) {
-		case *string:
-			*dest = val
-		case *int:
-			n, err := strconv.ParseInt(val, 10, 64)
-			if err != nil {
-				return err
-			}
-			*dest = int(n) // assume 64bit ints
-		case *uint64:
-			n, err := strconv.ParseUint(val, 10, 64)
-			if err != nil {
-				return err
-			}
-			*dest = n
-		case *bool:
-			b, err := strconv.ParseBool(val)
-			if err != nil {
-				return err
-			}
-			*dest = b
-		default:
-			return errors.Errorf("unsupported type %T for destination #%d (might be easy to add it)", dest, i+1)
-		}
-	}
-	return nil
-}
-
-// Fatalf wraps a fatal testing error with test file position information, so
-// that it's easy to locate the source of the error.
-func (td TestData) Fatalf(tb testing.TB, format string, args ...interface{}) {
-	tb.Helper()
-	tb.Fatalf("%s: %s", td.Pos, fmt.Sprintf(format, args...))
-}
-
-func hasBlankLine(s string) bool {
-	scanner := bufio.NewScanner(strings.NewReader(s))
-	for scanner.Scan() {
-		if strings.TrimSpace(scanner.Text()) == "" {
-			return true
-		}
-	}
-	return false
-}
diff --git a/internal/datadriven/line_scanner.go b/internal/datadriven/line_scanner.go
deleted file mode 100644
index 67681dcf..00000000
--- a/internal/datadriven/line_scanner.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 The Cockroach Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-// implied. See the License for the specific language governing
-// permissions and limitations under the License.
-
-package datadriven
-
-import (
-	"bufio"
-	"io"
-)
-
-type lineScanner struct {
-	*bufio.Scanner
-	line int
-}
-
-func newLineScanner(r io.Reader) *lineScanner {
-	return &lineScanner{
-		Scanner: bufio.NewScanner(r),
-		line:    0,
-	}
-}
-
-func (l *lineScanner) Scan() bool {
-	ok := l.Scanner.Scan()
-	if ok {
-		l.line++
-	}
-	return ok
-}
diff --git a/merging_iter.go b/merging_iter.go
index 60bd2ea6..f72b7a18 100644
--- a/merging_iter.go
+++ b/merging_iter.go
@@ -659,15 +659,48 @@ func (m *mergingIter) isNextEntryDeleted(item *mergingIterItem) bool {
 				if l.largestUserKey != nil && m.heap.cmp(l.largestUserKey, seekKey) < 0 {
 					seekKey = l.largestUserKey
 				}
-				// This seek is not directly due to a SeekGE call, so we don't
-				// know enough about the underlying iterator positions, and so
-				// we keep the try-seek-using-next optimization disabled.
+				// This seek is not directly due to a SeekGE call, so we don't know
+				// enough about the underlying iterator positions, and so we keep the
+				// try-seek-using-next optimization disabled. Additionally, if we're in
+				// prefix-seek mode and a re-seek would have moved us past the original
+				// prefix, we can remove all merging iter levels below the rangedel
+				// tombstone's level and return immediately instead of re-seeking. This
+				// is correct since those levels cannot provide a key that matches the
+				// prefix, and is also visible. Additionally, this is important to make
+				// subsequent `TrySeekUsingNext` work correctly, as a re-seek on a
+				// different prefix could have resulted in this iterator skipping visible
+				// keys at prefixes in between m.prefix and seekKey, that are currently
+				// not in the heap due to a bloom filter mismatch.
 				//
 				// Additionally, we set the relative-seek flag. This is
 				// important when iterating with lazy combined iteration. If
 				// there's a range key between this level's current file and the
 				// file the seek will land on, we need to detect it in order to
 				// trigger construction of the combined iterator.
+				if m.prefix != nil {
+					if n := m.split(seekKey); !bytes.Equal(m.prefix, seekKey[:n]) {
+						for i := item.index; i < len(m.levels); i++ {
+							// Remove this level from the heap. Setting iterKey and iterValue
+							// to their zero values should be sufficient for initMinHeap to not
+							// re-initialize the heap with them in it. Other fields in
+							// mergingIterLevel can remain as-is; the iter/rangeDelIter needs
+							// to stay intact for future trySeekUsingNexts to work, the level
+							// iter boundary context is owned by the levelIter which is not
+							// being repositioned, and any tombstones in these levels will be
+							// irrelevant for us anyway.
+							m.levels[i].iterKey = nil
+							m.levels[i].iterValue = base.LazyValue{}
+						}
+						// TODO(bilal): Consider a more efficient way of removing levels from
+						// the heap without reinitializing all of it. This would likely
+						// necessitate tracking the heap positions of each mergingIterHeap
+						// item in the mergingIterLevel, and then swapping that item in the
+						// heap with the last-positioned heap item, and shrinking the heap by
+						// one.
+						m.initMinHeap()
+						return true
+					}
+				}
 				m.seekGE(seekKey, item.index, base.SeekGEFlagsNone.EnableRelativeSeek())
 				return true
 			}
@@ -682,45 +715,33 @@ func (m *mergingIter) isNextEntryDeleted(item *mergingIterItem) bool {
 
 // Starting from the current entry, finds the first (next) entry that can be returned.
 func (m *mergingIter) findNextEntry() (*InternalKey, base.LazyValue) {
-	var reseeked bool
 	for m.heap.len() > 0 && m.err == nil {
 		item := &m.heap.items[0]
 		if m.levels[item.index].isSyntheticIterBoundsKey {
 			break
 		}
 		// For prefix iteration, stop if we've already exceeded the iterator's
-		// current prefix. There are two cases where we perform this check.
-		//
-		// 1. We already re-seeked the iterator due to a range tombstone. We
-		//    could amortize the cost of this comparison, by doing it only after
-		//    we have iterated in this for loop a few times. But unless we find
-		//    a performance benefit to that, we do the simple thing and compare
-		//    each time. Note that isNextEntryDeleted already did at least 4 key
-		//    comparisons in order to return true, and additionally at least one
-		//    heap comparison to step to the next entry.
+		// current prefix. There is one case where we perform this check:
 		//
-		//    Note that we cannot move this comparison into the
-		//    isNextEntryDeleted branch. Once isNextEntryDeleted determines a
-		//    key is deleted and seeks the level's iterator, item.key's memory
-		//    is potentially invalid. If the iterator is now exhausted, item.key
-		//    may be garbage.
+		// If the heap root is an ignorable boundary key, Next-ing the iterator will
+		// close the associated file and open the next file in the level. We don't
+		// want to do this unnecessarily because the 'exhausted' file may only appear
+		// exhausted because it failed a bloom filter check. Avoiding Next-ing the
+		// file has a performance benefit of unnecessarily advancing to the next
+		// file, but it's also necessary for correctness of some TrySeekUsingNext
+		// optimizations deeper in the iterator stack:
 		//
-		// 2. If the heap root is an ignorable boundary key, Next-ing the
-		//    iterator will close the associated file and open the next file in
-		//    the level. We don't want to do this unnecessarily because the
-		//    'exhausted' file may only appear exhausted because it failed a
-		//    bloom filter check. Avoiding Next-ing the file has a performance
-		//    benefit of unnecessarily advancing to the next file, but it's also
-		//    necessary for correctness of some TrySeekUsingNext optimizations
-		//    deeper in the iterator stack:
+		// There may exist valid keys between our current prefix (m.prefix) and the
+		// ignorable boundary key (item.key) that were not returned due to the bloom
+		// filter test. A subsequent SeekPrefixGE call with a seek key in the range
+		// (m.prefix - item.key) will have the TrySeekUsingNext flag enabled. The
+		// levelIter relies on having not skipped these keys in order to avoid more
+		// expensive full seeks.
 		//
-		//    There may exist valid keys between our current prefix (m.prefix)
-		//    and the ignorable boundary key (item.key) that were not returned
-		//    due to the bloom filter test. A subsequent SeekPrefixGE call with
-		//    a seek key in the range (m.prefix - item.key) will have the
-		//    TrySeekUsingNext flag enabled. The levelIter relies on having not
-		//    skipped these keys in order to avoid more expensive full seeks.
-		if m.prefix != nil && (reseeked || m.levels[item.index].isIgnorableBoundaryKey) {
+		// While isNextEntryDeleted could re-seek the iterator, that function is
+		// expected to peek at m.prefix and avoid any seeks past the iterator's
+		// prefix if it is set, avoiding the need to do a check here.
+		if m.prefix != nil && m.levels[item.index].isIgnorableBoundaryKey {
 			if n := m.split(item.key.UserKey); !bytes.Equal(m.prefix, item.key.UserKey[:n]) {
 				return nil, base.LazyValue{}
 			}
@@ -729,7 +750,6 @@ func (m *mergingIter) findNextEntry() (*InternalKey, base.LazyValue) {
 		m.addItemStats(item)
 		if m.isNextEntryDeleted(item) {
 			m.stats.PointsCoveredByRangeTombstones++
-			reseeked = true
 			continue
 		}
 		if item.key.Visible(m.snapshot, m.batchSnapshot) &&
diff --git a/sstable/reader.go b/sstable/reader.go
index e5cc3ca5..a556d778 100644
--- a/sstable/reader.go
+++ b/sstable/reader.go
@@ -2897,12 +2897,6 @@ func (r *Reader) readBlock(
 						raState.sequentialFile = f
 						file = f
 					}
-
-					// If we tried to load a table that doesn't exist, panic
-					// immediately.  Something is seriously wrong if a table
-					// doesn't exist.
-					// See cockroachdb/cockroach#56490.
-					base.MustExist(r.fs, r.filename, panicFataler{}, err)
 				}
 			}
 			if raState.sequentialFile == nil {
@@ -3785,9 +3779,3 @@ func (l *Layout) Describe(
 	last := blocks[len(blocks)-1]
 	fmt.Fprintf(w, "%10d  EOF\n", last.Offset+last.Length)
 }
-
-type panicFataler struct{}
-
-func (panicFataler) Fatalf(format string, args ...interface{}) {
-	panic(errors.Errorf(format, args...))
-}
diff --git a/vfs/vfs.go b/vfs/vfs.go
index 70b533aa..5e0571b9 100644
--- a/vfs/vfs.go
+++ b/vfs/vfs.go
@@ -274,7 +274,7 @@ func Copy(fs FS, oldname, newname string) error {
 // CopyAcrossFS copies the contents of oldname on srcFS to newname dstFS. If
 // newname exists, it will be overwritten.
 func CopyAcrossFS(srcFS FS, oldname string, dstFS FS, newname string) error {
-	src, err := srcFS.Open(oldname)
+	src, err := srcFS.Open(oldname, SequentialReadsOption)
 	if err != nil {
 		return err
 	}
@@ -295,7 +295,7 @@ func CopyAcrossFS(srcFS FS, oldname string, dstFS FS, newname string) error {
 // LimitedCopy copies up to maxBytes from oldname to newname. If newname
 // exists, it will be overwritten.
 func LimitedCopy(fs FS, oldname, newname string, maxBytes int64) error {
-	src, err := fs.Open(oldname)
+	src, err := fs.Open(oldname, SequentialReadsOption)
 	if err != nil {
 		return err
 	}


Test patch:
diff --git a/batch_test.go b/batch_test.go
index 303b6809..5c990222 100644
--- a/batch_test.go
+++ b/batch_test.go
@@ -16,10 +16,10 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/internal/batchskl"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/vfs"
@@ -398,7 +398,7 @@ func TestIndexedBatchMutation(t *testing.T) {
 		}
 	}()
 
-	datadriven.RunTest(t, "testdata/indexed_batch_mutation", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/indexed_batch_mutation", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "batch":
 			writeBatch := newBatch(d)
@@ -676,7 +676,7 @@ func TestBatchGet(t *testing.T) {
 			defer d.Close()
 			var b *Batch
 
-			datadriven.RunTest(t, "testdata/batch_get", func(td *datadriven.TestData) string {
+			datadriven.RunTest(t, "testdata/batch_get", func(t *testing.T, td *datadriven.TestData) string {
 				switch td.Cmd {
 				case "define":
 					switch c.method {
@@ -731,7 +731,7 @@ func TestBatchIter(t *testing.T) {
 		for _, testdata := range []string{
 			"testdata/internal_iter_next", "testdata/internal_iter_bounds"} {
 			t.Run(method, func(t *testing.T) {
-				datadriven.RunTest(t, testdata, func(d *datadriven.TestData) string {
+				datadriven.RunTest(t, testdata, func(t *testing.T, d *datadriven.TestData) string {
 					switch d.Cmd {
 					case "define":
 						switch method {
@@ -792,7 +792,7 @@ func TestBatchIter(t *testing.T) {
 func TestBatchRangeOps(t *testing.T) {
 	var b *Batch
 
-	datadriven.RunTest(t, "testdata/batch_range_ops", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/batch_range_ops", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "clear":
 			b = nil
@@ -883,7 +883,7 @@ func TestBatchTooLarge(t *testing.T) {
 
 func TestFlushableBatchIter(t *testing.T) {
 	var b *flushableBatch
-	datadriven.RunTest(t, "testdata/internal_iter_next", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/internal_iter_next", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			batch := newBatch(nil)
@@ -909,7 +909,7 @@ func TestFlushableBatchIter(t *testing.T) {
 
 func TestFlushableBatch(t *testing.T) {
 	var b *flushableBatch
-	datadriven.RunTest(t, "testdata/flushable_batch", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/flushable_batch", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			batch := newBatch(nil)
@@ -995,7 +995,7 @@ func TestFlushableBatchDeleteRange(t *testing.T) {
 	var fb *flushableBatch
 	var input string
 
-	datadriven.RunTest(t, "testdata/delete_range", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/delete_range", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "clear":
 			input = ""
diff --git a/checkpoint_test.go b/checkpoint_test.go
index 54f68c9d..e8c7d88e 100644
--- a/checkpoint_test.go
+++ b/checkpoint_test.go
@@ -12,8 +12,8 @@ import (
 	"sync"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/stretchr/testify/require"
 )
@@ -34,7 +34,7 @@ func TestCheckpoint(t *testing.T) {
 		L0CompactionThreshold: 10,
 	}
 
-	datadriven.RunTest(t, "testdata/checkpoint", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/checkpoint", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "batch":
 			if len(td.CmdArgs) != 1 {
diff --git a/cleaner_test.go b/cleaner_test.go
index c953fc0f..a7784923 100644
--- a/cleaner_test.go
+++ b/cleaner_test.go
@@ -10,7 +10,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/cockroachdb/pebble/internal/datadriven"
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/stretchr/testify/require"
 )
@@ -31,7 +31,7 @@ func TestArchiveCleaner(t *testing.T) {
 		WALDir:  "wal",
 	}
 
-	datadriven.RunTest(t, "testdata/cleaner", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/cleaner", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "batch":
 			if len(td.CmdArgs) != 1 {
diff --git a/compaction_iter_test.go b/compaction_iter_test.go
index ede349bb..e68c672d 100644
--- a/compaction_iter_test.go
+++ b/compaction_iter_test.go
@@ -13,8 +13,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/rangekey"
 )
@@ -135,7 +135,7 @@ func TestCompactionIter(t *testing.T) {
 	}
 
 	runTest := func(t *testing.T, formatVersion FormatMajorVersion) {
-		datadriven.RunTest(t, fileFunc(formatVersion), func(d *datadriven.TestData) string {
+		datadriven.RunTest(t, fileFunc(formatVersion), func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "define":
 				merge = nil
diff --git a/compaction_picker_test.go b/compaction_picker_test.go
index aaa2693b..1ac45224 100644
--- a/compaction_picker_test.go
+++ b/compaction_picker_test.go
@@ -13,9 +13,9 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/manifest"
 	"github.com/stretchr/testify/require"
 )
@@ -92,7 +92,7 @@ func diskAvailBytesInf() uint64 {
 
 func TestCompactionPickerByScoreLevelMaxBytes(t *testing.T) {
 	datadriven.RunTest(t, "testdata/compaction_picker_level_max_bytes",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "init":
 				vers, opts, sizes, errMsg := loadVersion(d)
@@ -154,7 +154,7 @@ func TestCompactionPickerTargetLevel(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_picker_target_level",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "init":
 				var errMsg string
@@ -322,7 +322,7 @@ func TestCompactionPickerTargetLevel(t *testing.T) {
 
 func TestCompactionPickerEstimatedCompactionDebt(t *testing.T) {
 	datadriven.RunTest(t, "testdata/compaction_picker_estimated_debt",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "init":
 				vers, opts, sizes, errMsg := loadVersion(d)
@@ -371,7 +371,7 @@ func TestCompactionPickerL0(t *testing.T) {
 	var inProgressCompactions []compactionInfo
 	var pc *pickedCompaction
 
-	datadriven.RunTest(t, "testdata/compaction_picker_L0", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/compaction_picker_L0", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			fileMetas := [manifest.NumLevels][]*fileMetadata{}
@@ -611,7 +611,7 @@ func TestCompactionPickerConcurrency(t *testing.T) {
 	var picker *compactionPickerByScore
 	var inProgressCompactions []compactionInfo
 
-	datadriven.RunTest(t, "testdata/compaction_picker_concurrency", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/compaction_picker_concurrency", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			fileMetas := [manifest.NumLevels][]*fileMetadata{}
@@ -825,7 +825,7 @@ func TestCompactionPickerPickReadTriggered(t *testing.T) {
 		return m, nil
 	}
 
-	datadriven.RunTest(t, "testdata/compaction_picker_read_triggered", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/compaction_picker_read_triggered", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			rcList = readCompactionQueue{}
@@ -974,7 +974,7 @@ func TestPickedCompactionSetupInputs(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_setup_inputs",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "setup-inputs":
 				var availBytes uint64 = math.MaxUint64
@@ -1083,7 +1083,7 @@ func TestPickedCompactionExpandInputs(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_expand_inputs",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "define":
 				files = nil
@@ -1185,7 +1185,7 @@ func TestCompactionOutputFileSize(t *testing.T) {
 		return m, nil
 	}
 
-	datadriven.RunTest(t, "testdata/compaction_output_file_size", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/compaction_output_file_size", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			fileMetas := [manifest.NumLevels][]*fileMetadata{}
diff --git a/compaction_test.go b/compaction_test.go
index 162c6243..9d668394 100644
--- a/compaction_test.go
+++ b/compaction_test.go
@@ -21,10 +21,10 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors/oserror"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/errorfs"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/manifest"
@@ -830,7 +830,7 @@ func TestElideRangeTombstone(t *testing.T) {
 }
 
 func TestCompactionTransform(t *testing.T) {
-	datadriven.RunTest(t, "testdata/compaction_transform", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/compaction_transform", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "transform":
 			var snapshots []uint64
@@ -1324,7 +1324,7 @@ func TestManualCompaction(t *testing.T) {
 	runTest := func(t *testing.T, testData string, minVersion, maxVersion FormatMajorVersion, verbose bool) {
 		reset(minVersion, maxVersion)
 		var ongoingCompaction *compaction
-		datadriven.RunTest(t, testData, func(td *datadriven.TestData) string {
+		datadriven.RunTest(t, testData, func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "reset":
 				reset(minVersion, maxVersion)
@@ -1597,7 +1597,7 @@ func TestCompactionFindGrandparentLimit(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_find_grandparent_limit",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "define":
 				grandparents = nil
@@ -1702,7 +1702,7 @@ func TestCompactionFindL0Limit(t *testing.T) {
 	flushSplitBytes := int64(0)
 
 	datadriven.RunTest(t, "testdata/compaction_find_l0_limit",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "define":
 				fileMetas := [manifest.NumLevels][]*fileMetadata{}
@@ -1790,7 +1790,7 @@ func TestCompactionOutputLevel(t *testing.T) {
 	version := &version{}
 
 	datadriven.RunTest(t, "testdata/compaction_output_level",
-		func(d *datadriven.TestData) (res string) {
+		func(t *testing.T, d *datadriven.TestData) (res string) {
 			defer func() {
 				if r := recover(); r != nil {
 					res = fmt.Sprintln(r)
@@ -1831,7 +1831,7 @@ func TestCompactionAtomicUnitBounds(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_atomic_unit_bounds",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "define":
 				files = manifest.LevelSlice{}
@@ -1944,7 +1944,7 @@ func TestCompactionDeleteOnlyHints(t *testing.T) {
 	var err error
 	var opts *Options
 	datadriven.RunTest(t, "testdata/compaction_delete_only_hints",
-		func(td *datadriven.TestData) string {
+		func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "define":
 				opts, err = reset()
@@ -2163,7 +2163,7 @@ func TestCompactionTombstones(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_tombstones",
-		func(td *datadriven.TestData) string {
+		func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "define":
 				if d != nil {
@@ -2278,7 +2278,7 @@ func TestCompactionReadTriggeredQueue(t *testing.T) {
 	var queue *readCompactionQueue
 
 	datadriven.RunTest(t, "testdata/read_compaction_queue",
-		func(td *datadriven.TestData) string {
+		func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "create":
 				queue = &readCompactionQueue{}
@@ -2372,7 +2372,7 @@ func TestCompactionReadTriggered(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_read_triggered",
-		func(td *datadriven.TestData) string {
+		func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "define":
 				if d != nil {
@@ -2491,7 +2491,7 @@ func TestCompactionInuseKeyRanges(t *testing.T) {
 	opts := (*Options)(nil).EnsureDefaults()
 
 	var c *compaction
-	datadriven.RunTest(t, "testdata/compaction_inuse_key_ranges", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/compaction_inuse_key_ranges", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			c = &compaction{
@@ -2701,7 +2701,7 @@ func TestCompactionAllowZeroSeqNum(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_allow_zero_seqnum",
-		func(td *datadriven.TestData) string {
+		func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "define":
 				if d != nil {
@@ -2806,7 +2806,7 @@ func TestCompactionErrorOnUserKeyOverlap(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_error_on_user_key_overlap",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "error-on-user-key-overlap":
 				c := &compaction{
@@ -2935,7 +2935,7 @@ func TestCompactionCheckOrdering(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_check_ordering",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "check-ordering":
 				c := &compaction{
@@ -3072,7 +3072,7 @@ func TestCompactionOutputSplitters(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/compaction_output_splitters",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "reset":
 				main = nil
@@ -3679,7 +3679,7 @@ func TestMarkedForCompaction(t *testing.T) {
 		d, err = Open("", opts)
 		require.NoError(t, err)
 	}
-	datadriven.RunTest(t, "testdata/marked_for_compaction", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/marked_for_compaction", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "reset":
 			reset()
diff --git a/data_test.go b/data_test.go
index 97382fa1..a39b4ca8 100644
--- a/data_test.go
+++ b/data_test.go
@@ -14,9 +14,9 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/rangedel"
 	"github.com/cockroachdb/pebble/internal/rangekey"
diff --git a/event_listener_test.go b/event_listener_test.go
index 5a90a39e..72907bbe 100644
--- a/event_listener_test.go
+++ b/event_listener_test.go
@@ -16,9 +16,9 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/sstable"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/cockroachdb/redact"
@@ -138,7 +138,7 @@ func TestEventListener(t *testing.T) {
 	mem := vfs.NewMem()
 	require.NoError(t, mem.MkdirAll("ext", 0755))
 
-	datadriven.RunTest(t, "testdata/event_listener", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/event_listener", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "open":
 			buf.Reset()
diff --git a/external_iterator_test.go b/external_iterator_test.go
index 967abbe4..ab353627 100644
--- a/external_iterator_test.go
+++ b/external_iterator_test.go
@@ -11,10 +11,10 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/internal/cache"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/sstable"
 	"github.com/cockroachdb/pebble/vfs"
@@ -34,7 +34,7 @@ func TestExternalIterator(t *testing.T) {
 	require.NoError(t, err)
 	defer func() { require.NoError(t, d.Close()) }()
 
-	datadriven.RunTest(t, "testdata/external_iterator", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/external_iterator", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "reset":
 			mem = vfs.NewMem()
@@ -87,7 +87,7 @@ func TestSimpleLevelIter(t *testing.T) {
 	require.NoError(t, err)
 	defer func() { require.NoError(t, d.Close()) }()
 
-	datadriven.RunTest(t, "testdata/simple_level_iter", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/simple_level_iter", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "reset":
 			mem = vfs.NewMem()
diff --git a/flush_test.go b/flush_test.go
index 209ce29d..edbf1a45 100644
--- a/flush_test.go
+++ b/flush_test.go
@@ -9,8 +9,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/stretchr/testify/require"
 )
@@ -30,7 +30,7 @@ func TestManualFlush(t *testing.T) {
 		require.NoError(t, d.Close())
 	}()
 
-	datadriven.RunTest(t, "testdata/manual_flush", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/manual_flush", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "batch":
 			b := d.NewBatch()
diff --git a/format_major_version_test.go b/format_major_version_test.go
index dba2a828..24edc847 100644
--- a/format_major_version_test.go
+++ b/format_major_version_test.go
@@ -12,9 +12,9 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/bloom"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/sstable"
 	"github.com/cockroachdb/pebble/vfs"
@@ -246,7 +246,7 @@ func TestSplitUserKeyMigration(t *testing.T) {
 	}()
 
 	datadriven.RunTest(t, "testdata/format_major_version_split_user_key_migration",
-		func(td *datadriven.TestData) string {
+		func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "define":
 				if d != nil {
@@ -347,7 +347,7 @@ func TestPebblev1Migration(t *testing.T) {
 	}()
 
 	datadriven.RunTest(t, "testdata/format_major_version_pebblev1_migration",
-		func(td *datadriven.TestData) string {
+		func(t *testing.T, td *datadriven.TestData) string {
 			switch cmd := td.Cmd; cmd {
 			case "open":
 				var version int
diff --git a/ingest_test.go b/ingest_test.go
index 692fe09a..3f3b4fa0 100644
--- a/ingest_test.go
+++ b/ingest_test.go
@@ -16,10 +16,10 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors/oserror"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/errorfs"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/rangekey"
@@ -33,7 +33,7 @@ import (
 
 func TestSSTableKeyCompare(t *testing.T) {
 	var buf bytes.Buffer
-	datadriven.RunTest(t, "testdata/sstable_key_compare", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/sstable_key_compare", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "cmp":
 			buf.Reset()
@@ -63,7 +63,7 @@ func TestSSTableKeyCompare(t *testing.T) {
 func TestIngestLoad(t *testing.T) {
 	mem := vfs.NewMem()
 
-	datadriven.RunTest(t, "testdata/ingest_load", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/ingest_load", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "load":
 			writerOpts := sstable.WriterOptions{}
@@ -236,7 +236,7 @@ func TestIngestSortAndVerify(t *testing.T) {
 	}
 
 	t.Run("", func(t *testing.T) {
-		datadriven.RunTest(t, "testdata/ingest_sort_and_verify", func(d *datadriven.TestData) string {
+		datadriven.RunTest(t, "testdata/ingest_sort_and_verify", func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "ingest":
 				var buf bytes.Buffer
@@ -432,7 +432,7 @@ func TestIngestMemtableOverlaps(t *testing.T) {
 				return meta
 			}
 
-			datadriven.RunTest(t, "testdata/ingest_memtable_overlaps", func(d *datadriven.TestData) string {
+			datadriven.RunTest(t, "testdata/ingest_memtable_overlaps", func(t *testing.T, d *datadriven.TestData) string {
 				switch d.Cmd {
 				case "define":
 					b := newBatch(nil)
@@ -559,7 +559,7 @@ func TestIngestTargetLevel(t *testing.T) {
 		return m
 	}
 
-	datadriven.RunTest(t, "testdata/ingest_target_level", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/ingest_target_level", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			if d != nil {
@@ -653,7 +653,7 @@ func TestIngest(t *testing.T) {
 	}
 	reset()
 
-	datadriven.RunTest(t, "testdata/ingest", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/ingest", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "reset":
 			reset()
@@ -1365,7 +1365,7 @@ func TestIngest_UpdateSequenceNumber(t *testing.T) {
 		err    error
 		metas  []*fileMetadata
 	)
-	datadriven.RunTest(t, "testdata/ingest_update_seqnums", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/ingest_update_seqnums", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "starting-seqnum":
 			seqnum, err = strconv.ParseUint(td.Input, 10, 64)
diff --git a/internal/datadriven/datadriven_test.go b/internal/datadriven/datadriven_test.go
deleted file mode 100644
index dcbccec0..00000000
--- a/internal/datadriven/datadriven_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2019 The Cockroach Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-// implied. See the License for the specific language governing
-// permissions and limitations under the License.
-
-package datadriven
-
-import (
-	"fmt"
-	"testing"
-)
-
-func TestDataDriven(t *testing.T) {
-	input := `
-# NB: we allow duplicate args. It's unclear at this time whether this is useful,
-# either way, ScanArgs simply picks the first occurrence.
-make argTuple=(1, 🍌) argInt=12 argString=greedily argString=totally_ignored
-sentence
-----
-Did the following: make sentence
-1 hungry monkey eats a 🍌
-while 12 other monkeys watch greedily
-`
-
-	RunTestFromString(t, input, func(d *TestData) string {
-		var one int
-		var twelve int
-		var banana string
-		var greedily string
-		d.ScanArgs(t, "argTuple", &one, &banana)
-		d.ScanArgs(t, "argInt", &twelve)
-		d.ScanArgs(t, "argString", &greedily)
-		return fmt.Sprintf("Did the following: %s %s\n%d hungry monkey eats a %s\nwhile %d other monkeys watch %s\n",
-			d.Cmd, d.Input, one, banana, twelve, greedily,
-		)
-	})
-}
diff --git a/internal/datadriven/test_data_reader.go b/internal/datadriven/test_data_reader.go
deleted file mode 100644
index 39a7ce5d..00000000
--- a/internal/datadriven/test_data_reader.go
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2018 The Cockroach Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-// implied. See the License for the specific language governing
-// permissions and limitations under the License.
-
-package datadriven
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"regexp"
-	"strings"
-	"testing"
-)
-
-type testDataReader struct {
-	sourceName string
-	reader     io.Reader
-	scanner    *lineScanner
-	data       TestData
-	rewrite    *bytes.Buffer
-}
-
-func newTestDataReader(
-	t *testing.T, sourceName string, file io.Reader, record bool,
-) *testDataReader {
-	t.Helper()
-
-	var rewrite *bytes.Buffer
-	if record {
-		rewrite = &bytes.Buffer{}
-	}
-	return &testDataReader{
-		sourceName: sourceName,
-		reader:     file,
-		scanner:    newLineScanner(file),
-		rewrite:    rewrite,
-	}
-}
-
-func (r *testDataReader) Next(t *testing.T) bool {
-	t.Helper()
-
-	r.data = TestData{}
-	for r.scanner.Scan() {
-		line := r.scanner.Text()
-		r.emit(line)
-
-		line = strings.TrimSpace(line)
-		if strings.HasPrefix(line, "#") {
-			// Skip comment lines.
-			continue
-		}
-		// Support wrapping directive lines using \, for example:
-		//   build-scalar \
-		//   vars(int)
-		for strings.HasSuffix(line, `\`) && r.scanner.Scan() {
-			nextLine := r.scanner.Text()
-			r.emit(nextLine)
-			line = strings.TrimSuffix(line, `\`) + " " + strings.TrimSpace(nextLine)
-		}
-
-		fields := splitDirectives(t, line)
-		if len(fields) == 0 {
-			continue
-		}
-		cmd := fields[0]
-		r.data.Pos = fmt.Sprintf("%s:%d", r.sourceName, r.scanner.line)
-		r.data.Cmd = cmd
-
-		for _, arg := range fields[1:] {
-			key := arg
-			var vals []string
-			if pos := strings.IndexByte(key, '='); pos >= 0 {
-				key = arg[:pos]
-				val := arg[pos+1:]
-
-				if len(val) > 2 && val[0] == '(' && val[len(val)-1] == ')' {
-					vals = strings.Split(val[1:len(val)-1], ",")
-					for i := range vals {
-						vals[i] = strings.TrimSpace(vals[i])
-					}
-				} else {
-					vals = []string{val}
-				}
-			}
-			r.data.CmdArgs = append(r.data.CmdArgs, CmdArg{Key: key, Vals: vals})
-		}
-
-		var buf bytes.Buffer
-		var separator bool
-		for r.scanner.Scan() {
-			line := r.scanner.Text()
-			if line == "----" {
-				separator = true
-				break
-			}
-
-			r.emit(line)
-			fmt.Fprintln(&buf, line)
-		}
-
-		r.data.Input = strings.TrimSpace(buf.String())
-
-		if separator {
-			r.readExpected()
-		}
-		return true
-	}
-	return false
-}
-
-func (r *testDataReader) readExpected() {
-	var buf bytes.Buffer
-	var line string
-	var allowBlankLines bool
-
-	if r.scanner.Scan() {
-		line = r.scanner.Text()
-		if line == "----" {
-			allowBlankLines = true
-		}
-	}
-
-	if allowBlankLines {
-		// Look for two successive lines of "----" before terminating.
-		for r.scanner.Scan() {
-			line = r.scanner.Text()
-
-			if line == "----" {
-				if r.scanner.Scan() {
-					line2 := r.scanner.Text()
-					if line2 == "----" {
-						break
-					}
-
-					fmt.Fprintln(&buf, line)
-					fmt.Fprintln(&buf, line2)
-					continue
-				}
-			}
-
-			fmt.Fprintln(&buf, line)
-		}
-	} else {
-		// Terminate on first blank line.
-		for {
-			if strings.TrimSpace(line) == "" {
-				break
-			}
-
-			fmt.Fprintln(&buf, line)
-
-			if !r.scanner.Scan() {
-				break
-			}
-
-			line = r.scanner.Text()
-		}
-	}
-
-	r.data.Expected = buf.String()
-}
-
-func (r *testDataReader) emit(s string) {
-	if r.rewrite != nil {
-		r.rewrite.WriteString(s)
-		r.rewrite.WriteString("\n")
-	}
-}
-
-var splitDirectivesRE = regexp.MustCompile(`^ *[a-zA-Z0-9_\/,-\.]+(|=[-a-zA-Z0-9_@]+|=\([^)]*\))( |$)`)
-
-// splits a directive line into tokens, where each token is
-// either:
-//  - a,list,of,things
-//  - argument
-//  - argument=value
-//  - argument=(values, ...)
-func splitDirectives(t *testing.T, line string) []string {
-	var res []string
-
-	for line != "" {
-		str := splitDirectivesRE.FindString(line)
-		if len(str) == 0 {
-			t.Fatalf("cannot parse directive %s\n", line)
-		}
-		res = append(res, strings.TrimSpace(line[0:len(str)]))
-		line = line[len(str):]
-	}
-	return res
-}
diff --git a/internal/keyspan/bounded_test.go b/internal/keyspan/bounded_test.go
index ba1e3d06..71218828 100644
--- a/internal/keyspan/bounded_test.go
+++ b/internal/keyspan/bounded_test.go
@@ -10,7 +10,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/cockroachdb/pebble/internal/datadriven"
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/stretchr/testify/require"
 )
@@ -38,7 +38,7 @@ func TestBoundedIter(t *testing.T) {
 	var iter BoundedIter
 	var hasPrefix bool
 	var prefix []byte
-	datadriven.RunTest(t, "testdata/bounded_iter", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/bounded_iter", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			var spans []Span
diff --git a/internal/keyspan/defragment_test.go b/internal/keyspan/defragment_test.go
index 3ff3b503..5f985d76 100644
--- a/internal/keyspan/defragment_test.go
+++ b/internal/keyspan/defragment_test.go
@@ -14,8 +14,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/pmezard/go-difflib/difflib"
 )
@@ -34,7 +34,7 @@ func TestDefragmentingIter(t *testing.T) {
 
 	var buf bytes.Buffer
 	var spans []Span
-	datadriven.RunTest(t, "testdata/defragmenting_iter", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/defragmenting_iter", func(t *testing.T, td *datadriven.TestData) string {
 		buf.Reset()
 		switch td.Cmd {
 		case "define":
diff --git a/internal/keyspan/filter_test.go b/internal/keyspan/filter_test.go
index 13ec3c0a..052d3ffc 100644
--- a/internal/keyspan/filter_test.go
+++ b/internal/keyspan/filter_test.go
@@ -9,8 +9,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 )
 
@@ -33,7 +33,7 @@ func TestFilteringIter(t *testing.T) {
 
 	cmp := testkeys.Comparer.Compare
 	var spans []Span
-	datadriven.RunTest(t, "testdata/filtering_iter", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/filtering_iter", func(t *testing.T, td *datadriven.TestData) string {
 		switch cmd := td.Cmd; cmd {
 		case "define":
 			spans = spans[:0]
diff --git a/internal/keyspan/fragmenter_test.go b/internal/keyspan/fragmenter_test.go
index fcc858ba..8458d366 100644
--- a/internal/keyspan/fragmenter_test.go
+++ b/internal/keyspan/fragmenter_test.go
@@ -12,8 +12,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/stretchr/testify/require"
 )
 
@@ -135,7 +135,7 @@ func TestFragmenter(t *testing.T) {
 		return s != nil && s.CoversAt(readSeq, seq)
 	}
 
-	datadriven.RunTest(t, "testdata/fragmenter", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/fragmenter", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "build":
 			return func() (result string) {
@@ -178,7 +178,7 @@ func TestFragmenter(t *testing.T) {
 }
 
 func TestFragmenterDeleted(t *testing.T) {
-	datadriven.RunTest(t, "testdata/fragmenter_deleted", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/fragmenter_deleted", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "build":
 			f := &Fragmenter{
@@ -217,7 +217,7 @@ func TestFragmenterTruncateAndFlushTo(t *testing.T) {
 	cmp := base.DefaultComparer.Compare
 	fmtKey := base.DefaultComparer.FormatKey
 
-	datadriven.RunTest(t, "testdata/fragmenter_truncate_and_flush_to", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/fragmenter_truncate_and_flush_to", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "build":
 			return func() (result string) {
@@ -241,7 +241,7 @@ func TestFragmenter_Values(t *testing.T) {
 	cmp := base.DefaultComparer.Compare
 	fmtKey := base.DefaultComparer.FormatKey
 
-	datadriven.RunTest(t, "testdata/fragmenter_values", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/fragmenter_values", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "build":
 			return func() (result string) {
@@ -264,7 +264,7 @@ func TestFragmenter_Values(t *testing.T) {
 func TestFragmenter_EmitOrder(t *testing.T) {
 	var buf bytes.Buffer
 
-	datadriven.RunTest(t, "testdata/fragmenter_emit_order", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/fragmenter_emit_order", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "build":
 			buf.Reset()
diff --git a/internal/keyspan/interleaving_iter_test.go b/internal/keyspan/interleaving_iter_test.go
index a55a674f..678b82a6 100644
--- a/internal/keyspan/interleaving_iter_test.go
+++ b/internal/keyspan/interleaving_iter_test.go
@@ -12,8 +12,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/stretchr/testify/require"
 )
@@ -93,7 +93,7 @@ func runInterleavingIterTest(t *testing.T, filename string) {
 		}
 	}
 
-	datadriven.RunTest(t, filename, func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, filename, func(t *testing.T, td *datadriven.TestData) string {
 		buf.Reset()
 		switch td.Cmd {
 		case "set-masking-threshold":
diff --git a/internal/keyspan/iter_test.go b/internal/keyspan/iter_test.go
index 71c52bbe..c269f3bf 100644
--- a/internal/keyspan/iter_test.go
+++ b/internal/keyspan/iter_test.go
@@ -10,8 +10,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 )
 
 func runFragmentIteratorCmd(iter FragmentIterator, input string, extraInfo func() string) string {
@@ -61,7 +61,7 @@ func runFragmentIteratorCmd(iter FragmentIterator, input string, extraInfo func(
 
 func TestIter(t *testing.T) {
 	var spans []Span
-	datadriven.RunTest(t, "testdata/iter", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iter", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			spans = nil
diff --git a/internal/keyspan/level_iter_test.go b/internal/keyspan/level_iter_test.go
index d125a7a1..0161122b 100644
--- a/internal/keyspan/level_iter_test.go
+++ b/internal/keyspan/level_iter_test.go
@@ -9,8 +9,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/manifest"
 	"github.com/stretchr/testify/require"
 )
@@ -341,7 +341,7 @@ func TestLevelIter(t *testing.T) {
 	var iter FragmentIterator
 	var extraInfo func() string
 
-	datadriven.RunTest(t, "testdata/level_iter", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/level_iter", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			level = level[:0]
diff --git a/internal/keyspan/merging_iter_test.go b/internal/keyspan/merging_iter_test.go
index 00b36980..8d6aee29 100644
--- a/internal/keyspan/merging_iter_test.go
+++ b/internal/keyspan/merging_iter_test.go
@@ -13,8 +13,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/stretchr/testify/require"
 )
@@ -26,7 +26,7 @@ func TestMergingIter(t *testing.T) {
 
 	formatSpan := func(s *Span) { fmt.Fprintln(&buf, s) }
 
-	datadriven.RunTest(t, "testdata/merging_iter", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/merging_iter", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			snapshot := base.InternalKeySeqNumMax
diff --git a/internal/keyspan/seek_test.go b/internal/keyspan/seek_test.go
index 96f0337d..3acd77e4 100644
--- a/internal/keyspan/seek_test.go
+++ b/internal/keyspan/seek_test.go
@@ -11,8 +11,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 )
 
 func TestSeek(t *testing.T) {
@@ -21,7 +21,7 @@ func TestSeek(t *testing.T) {
 	var iter FragmentIterator
 	var buf bytes.Buffer
 
-	datadriven.RunTest(t, "testdata/seek", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/seek", func(t *testing.T, d *datadriven.TestData) string {
 		buf.Reset()
 		switch d.Cmd {
 		case "build":
diff --git a/internal/keyspan/span_test.go b/internal/keyspan/span_test.go
index c7af9bcb..29651fb4 100644
--- a/internal/keyspan/span_test.go
+++ b/internal/keyspan/span_test.go
@@ -11,7 +11,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/cockroachdb/pebble/internal/datadriven"
+	"github.com/cockroachdb/datadriven"
 	"github.com/stretchr/testify/require"
 )
 
@@ -33,7 +33,7 @@ func TestSpan_ParseRoundtrip(t *testing.T) {
 
 func TestSpan_Visible(t *testing.T) {
 	var s Span
-	datadriven.RunTest(t, "testdata/visible", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/visible", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			s = ParseSpan(d.Input)
@@ -54,7 +54,7 @@ func TestSpan_Visible(t *testing.T) {
 
 func TestSpan_VisibleAt(t *testing.T) {
 	var s Span
-	datadriven.RunTest(t, "testdata/visible_at", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/visible_at", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			s = ParseSpan(d.Input)
@@ -75,7 +75,7 @@ func TestSpan_VisibleAt(t *testing.T) {
 
 func TestSpan_CoversAt(t *testing.T) {
 	var s Span
-	datadriven.RunTest(t, "testdata/covers_at", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/covers_at", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			s = ParseSpan(d.Input)
diff --git a/internal/keyspan/truncate_test.go b/internal/keyspan/truncate_test.go
index 767fab9b..5142f85f 100644
--- a/internal/keyspan/truncate_test.go
+++ b/internal/keyspan/truncate_test.go
@@ -9,8 +9,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 )
 
 func TestTruncate(t *testing.T) {
@@ -18,7 +18,7 @@ func TestTruncate(t *testing.T) {
 	fmtKey := base.DefaultComparer.FormatKey
 	var iter FragmentIterator
 
-	datadriven.RunTest(t, "testdata/truncate", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/truncate", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "build":
 			tombstones := buildSpans(t, cmp, fmtKey, d.Input, base.InternalKeyKindRangeDelete)
diff --git a/internal/manifest/l0_sublevels_test.go b/internal/manifest/l0_sublevels_test.go
index fa405d6e..0c7306c1 100644
--- a/internal/manifest/l0_sublevels_test.go
+++ b/internal/manifest/l0_sublevels_test.go
@@ -16,8 +16,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/record"
 	"github.com/stretchr/testify/require"
@@ -262,7 +262,7 @@ func TestL0Sublevels(t *testing.T) {
 	var sublevels *L0Sublevels
 	baseLevel := NumLevels - 1
 
-	datadriven.RunTest(t, "testdata/l0_sublevels", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/l0_sublevels", func(t *testing.T, td *datadriven.TestData) string {
 		pickBaseCompaction := false
 		level := 0
 		addL0FilesOpt := false
diff --git a/internal/manifest/level_metadata_test.go b/internal/manifest/level_metadata_test.go
index c5a6608b..d1136afc 100644
--- a/internal/manifest/level_metadata_test.go
+++ b/internal/manifest/level_metadata_test.go
@@ -10,14 +10,14 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 )
 
 func TestLevelIterator(t *testing.T) {
 	var level LevelSlice
 	datadriven.RunTest(t, "testdata/level_iterator",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "define":
 				var files []*FileMetadata
diff --git a/internal/manifest/version_edit_test.go b/internal/manifest/version_edit_test.go
index 36685b1c..eb611efc 100644
--- a/internal/manifest/version_edit_test.go
+++ b/internal/manifest/version_edit_test.go
@@ -15,9 +15,9 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/record"
 	"github.com/kr/pretty"
 	"github.com/stretchr/testify/require"
@@ -316,7 +316,7 @@ func TestVersionEditApply(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/version_edit_apply",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "apply":
 				// TODO(sumeer): move this Version parsing code to utils, to
diff --git a/internal/manifest/version_test.go b/internal/manifest/version_test.go
index bc471ada..9d7d8e55 100644
--- a/internal/manifest/version_test.go
+++ b/internal/manifest/version_test.go
@@ -12,9 +12,9 @@ import (
 	"sync"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/cockroachdb/redact"
@@ -95,7 +95,7 @@ func TestOverlaps(t *testing.T) {
 	var v *Version
 	cmp := testkeys.Comparer.Compare
 	fmtKey := testkeys.Comparer.FormatKey
-	datadriven.RunTest(t, "testdata/overlaps", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/overlaps", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			var err error
@@ -287,7 +287,7 @@ func TestCheckOrdering(t *testing.T) {
 	cmp := base.DefaultComparer.Compare
 	fmtKey := base.DefaultComparer.FormatKey
 	datadriven.RunTest(t, "testdata/version_check_ordering",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "check-ordering":
 				v, err := ParseVersionDebug(cmp, fmtKey, 10<<20, d.Input)
@@ -341,7 +341,7 @@ func TestCheckConsistency(t *testing.T) {
 	}
 
 	datadriven.RunTest(t, "testdata/version_check_consistency",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "check-consistency":
 				var filesByLevel [NumLevels][]*FileMetadata
@@ -464,7 +464,7 @@ func TestExtendBounds(t *testing.T) {
 		return b.String()
 	}
 	m := &FileMetadata{}
-	datadriven.RunTest(t, "testdata/file_metadata_bounds", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/file_metadata_bounds", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "reset":
 			m = &FileMetadata{}
diff --git a/internal/metamorphic/crossversion/crossversion_test.go b/internal/metamorphic/crossversion/crossversion_test.go
index 5a9a1211..fd91e88b 100644
--- a/internal/metamorphic/crossversion/crossversion_test.go
+++ b/internal/metamorphic/crossversion/crossversion_test.go
@@ -35,6 +35,7 @@ var (
 	seed         int64
 	versions     pebbleVersions
 	artifactsDir string
+	streamOutput bool
 )
 
 func init() {
@@ -63,6 +64,8 @@ versions.`)
 	flag.StringVar(&artifactsDir, "artifacts", "",
 		`the path to a directory where test artifacts should be
 moved on failure. Defaults to the current working directory.`)
+	flag.BoolVar(&streamOutput, "stream-output", false,
+		`stream TestMeta output to standard output`)
 }
 
 func reproductionCommand() string {
@@ -224,7 +227,7 @@ func runVersion(
 
 				var buf bytes.Buffer
 				var out io.Writer = &buf
-				if testing.Verbose() {
+				if streamOutput {
 					out = io.MultiWriter(out, os.Stderr)
 				}
 				t.Logf("  Running test with version %s with initial state %s.",
diff --git a/internal/metamorphic/history_test.go b/internal/metamorphic/history_test.go
index 4ed862b4..7403bcd6 100644
--- a/internal/metamorphic/history_test.go
+++ b/internal/metamorphic/history_test.go
@@ -10,7 +10,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/cockroachdb/pebble/internal/datadriven"
+	"github.com/cockroachdb/datadriven"
 	"github.com/pmezard/go-difflib/difflib"
 	"github.com/stretchr/testify/require"
 )
@@ -42,7 +42,7 @@ func TestHistoryFail(t *testing.T) {
 }
 
 func TestReorderHistory(t *testing.T) {
-	datadriven.RunTest(t, "testdata/reorder_history", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/reorder_history", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "reorder":
 			lines := difflib.SplitLines(string(d.Input))
diff --git a/internal/metamorphic/parser_test.go b/internal/metamorphic/parser_test.go
index f05f27f9..a6b33cd1 100644
--- a/internal/metamorphic/parser_test.go
+++ b/internal/metamorphic/parser_test.go
@@ -9,14 +9,14 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/cockroachdb/pebble/internal/datadriven"
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/randvar"
 	"github.com/kr/pretty"
 	"github.com/stretchr/testify/require"
 )
 
 func TestParser(t *testing.T) {
-	datadriven.RunTest(t, "testdata/parser", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/parser", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "parse":
 			ops, err := parse([]byte(d.Input))
diff --git a/internal/rangekey/coalesce_test.go b/internal/rangekey/coalesce_test.go
index 94e458e0..fb0768fb 100644
--- a/internal/rangekey/coalesce_test.go
+++ b/internal/rangekey/coalesce_test.go
@@ -14,8 +14,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/pmezard/go-difflib/difflib"
@@ -26,7 +26,7 @@ func TestCoalesce(t *testing.T) {
 	var buf bytes.Buffer
 	cmp := testkeys.Comparer.Compare
 
-	datadriven.RunTest(t, "testdata/coalesce", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/coalesce", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "coalesce":
 			buf.Reset()
@@ -51,7 +51,7 @@ func TestIter(t *testing.T) {
 	var iter keyspan.MergingIter
 	var buf bytes.Buffer
 
-	datadriven.RunTest(t, "testdata/iter", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iter", func(t *testing.T, td *datadriven.TestData) string {
 		buf.Reset()
 		switch td.Cmd {
 		case "define":
@@ -124,7 +124,7 @@ func TestDefragmenting(t *testing.T) {
 	var spans []keyspan.Span
 	var hasPrefix bool
 	var prefix []byte
-	datadriven.RunTest(t, "testdata/defragmenting_iter", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/defragmenting_iter", func(t *testing.T, td *datadriven.TestData) string {
 		buf.Reset()
 		switch td.Cmd {
 		case "define":
diff --git a/internal/testkeys/testkeys_test.go b/internal/testkeys/testkeys_test.go
index 1f805458..076b5c13 100644
--- a/internal/testkeys/testkeys_test.go
+++ b/internal/testkeys/testkeys_test.go
@@ -9,7 +9,7 @@ import (
 	"fmt"
 	"testing"
 
-	"github.com/cockroachdb/pebble/internal/datadriven"
+	"github.com/cockroachdb/datadriven"
 	"github.com/stretchr/testify/require"
 )
 
@@ -179,7 +179,7 @@ func TestSuffixLen(t *testing.T) {
 
 func TestDivvy(t *testing.T) {
 	var buf bytes.Buffer
-	datadriven.RunTest(t, "testdata/divvy", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/divvy", func(t *testing.T, d *datadriven.TestData) string {
 		buf.Reset()
 		switch d.Cmd {
 		case "divvy":
diff --git a/iterator_histories_test.go b/iterator_histories_test.go
index e81538cd..7416a70d 100644
--- a/iterator_histories_test.go
+++ b/iterator_histories_test.go
@@ -10,10 +10,10 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/bloom"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/sstable"
 	"github.com/cockroachdb/pebble/vfs"
@@ -50,7 +50,7 @@ func TestIterHistories(t *testing.T) {
 		}
 		defer cleanup()
 
-		datadriven.RunTest(t, path, func(td *datadriven.TestData) string {
+		datadriven.RunTest(t, path, func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "reset":
 				if err := cleanup(); err != nil {
diff --git a/iterator_test.go b/iterator_test.go
index ee97c2b5..451b1a55 100644
--- a/iterator_test.go
+++ b/iterator_test.go
@@ -17,9 +17,9 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/manifest"
 	"github.com/cockroachdb/pebble/internal/testkeys"
@@ -519,7 +519,7 @@ func TestIterator(t *testing.T) {
 		return it
 	}
 
-	datadriven.RunTest(t, "testdata/iterator", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iterator", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			merge = nil
@@ -606,7 +606,7 @@ func TestReadSampling(t *testing.T) {
 		}
 	}()
 
-	datadriven.RunTest(t, "testdata/iterator_read_sampling", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iterator_read_sampling", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			if iter != nil {
@@ -773,7 +773,7 @@ func TestIteratorTableFilter(t *testing.T) {
 		}
 	}()
 
-	datadriven.RunTest(t, "testdata/iterator_table_filter", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iterator_table_filter", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			if d != nil {
@@ -866,7 +866,7 @@ func TestIteratorNextPrev(t *testing.T) {
 	}
 	reset()
 
-	datadriven.RunTest(t, "testdata/iterator_next_prev", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iterator_next_prev", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "reset":
 			reset()
@@ -938,7 +938,7 @@ func TestIteratorStats(t *testing.T) {
 	}
 	reset()
 
-	datadriven.RunTest(t, "testdata/iterator_stats", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iterator_stats", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "reset":
 			reset()
@@ -1024,7 +1024,7 @@ func TestIteratorSeekOpt(t *testing.T) {
 	}()
 	var seekGEUsingNext, seekPrefixGEUsingNext int
 
-	datadriven.RunTest(t, "testdata/iterator_seek_opt", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iterator_seek_opt", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			if iter != nil {
@@ -1199,7 +1199,7 @@ func TestIteratorSeekOptErrors(t *testing.T) {
 		}
 	}
 
-	datadriven.RunTest(t, "testdata/iterator_seek_opt_errors", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iterator_seek_opt_errors", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			keys = keys[:0]
@@ -1335,7 +1335,7 @@ func TestIteratorBlockIntervalFilter(t *testing.T) {
 	}
 
 	datadriven.RunTest(
-		t, "testdata/iterator_block_interval_filter", func(td *datadriven.TestData) string {
+		t, "testdata/iterator_block_interval_filter", func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "build":
 				var collectors []collector
@@ -1594,7 +1594,7 @@ func TestIteratorBoundsLifetimes(t *testing.T) {
 		}
 	}
 
-	datadriven.RunTest(t, "testdata/iterator_bounds_lifetimes", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/iterator_bounds_lifetimes", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			var err error
diff --git a/level_checker_test.go b/level_checker_test.go
index 347ca36e..a57a17d1 100644
--- a/level_checker_test.go
+++ b/level_checker_test.go
@@ -12,9 +12,9 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/manifest"
 	"github.com/cockroachdb/pebble/internal/private"
@@ -119,7 +119,7 @@ func TestCheckLevelsCornerCases(t *testing.T) {
 		Name: "fail-merger",
 	}
 
-	datadriven.RunTest(t, "testdata/level_checker", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/level_checker", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			lines := strings.Split(d.Input, "\n")
diff --git a/level_iter_test.go b/level_iter_test.go
index 50e1e1b6..90f9e87c 100644
--- a/level_iter_test.go
+++ b/level_iter_test.go
@@ -11,9 +11,9 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/bloom"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/manifest"
 	"github.com/cockroachdb/pebble/internal/rangedel"
@@ -40,7 +40,7 @@ func TestLevelIter(t *testing.T) {
 		return &f, nil, nil
 	}
 
-	datadriven.RunTest(t, "testdata/level_iter", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/level_iter", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			iters = nil
@@ -284,7 +284,7 @@ func TestLevelIterBoundaries(t *testing.T) {
 	defer lt.runClear(nil)
 
 	var iter *levelIter
-	datadriven.RunTest(t, "testdata/level_iter_boundaries", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/level_iter_boundaries", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "clear":
 			return lt.runClear(d)
@@ -407,7 +407,7 @@ func TestLevelIterSeek(t *testing.T) {
 	lt := newLevelIterTest()
 	defer lt.runClear(nil)
 
-	datadriven.RunTest(t, "testdata/level_iter_seek", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/level_iter_seek", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "clear":
 			return lt.runClear(d)
diff --git a/mem_table_test.go b/mem_table_test.go
index 6221b6cb..2172297e 100644
--- a/mem_table_test.go
+++ b/mem_table_test.go
@@ -14,10 +14,10 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/arenaskl"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/rangekey"
 	"github.com/stretchr/testify/require"
 	"golang.org/x/exp/rand"
@@ -253,7 +253,7 @@ func TestMemTableIter(t *testing.T) {
 	var mem *memTable
 	for _, testdata := range []string{
 		"testdata/internal_iter_next", "testdata/internal_iter_bounds"} {
-		datadriven.RunTest(t, testdata, func(d *datadriven.TestData) string {
+		datadriven.RunTest(t, testdata, func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "define":
 				mem = newMemTable(memTableOptions{})
@@ -300,7 +300,7 @@ func TestMemTableDeleteRange(t *testing.T) {
 	var mem *memTable
 	var seqNum uint64
 
-	datadriven.RunTest(t, "testdata/delete_range", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/delete_range", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "clear":
 			mem = nil
diff --git a/merging_iter_test.go b/merging_iter_test.go
index ecf57742..16352f2b 100644
--- a/merging_iter_test.go
+++ b/merging_iter_test.go
@@ -10,9 +10,9 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/bloom"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/manifest"
 	"github.com/cockroachdb/pebble/internal/rangedel"
@@ -43,7 +43,7 @@ func TestMergingIter(t *testing.T) {
 
 func TestMergingIterSeek(t *testing.T) {
 	var def string
-	datadriven.RunTest(t, "testdata/merging_iter_seek", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/merging_iter_seek", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			def = d.Input
@@ -102,7 +102,7 @@ func TestMergingIterNextPrev(t *testing.T) {
 
 	for _, c := range iterCases {
 		t.Run("", func(t *testing.T) {
-			datadriven.RunTest(t, "testdata/internal_iter_next", func(d *datadriven.TestData) string {
+			datadriven.RunTest(t, "testdata/internal_iter_next", func(t *testing.T, d *datadriven.TestData) string {
 				switch d.Cmd {
 				case "define":
 					// Ignore. We've defined the iterator data above.
@@ -166,7 +166,7 @@ func TestMergingIterCornerCases(t *testing.T) {
 			return iter, rangeDelIter, nil
 		}
 
-	datadriven.RunTest(t, "testdata/merging_iter", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/merging_iter", func(t *testing.T, d *datadriven.TestData) string {
 		switch d.Cmd {
 		case "define":
 			lines := strings.Split(d.Input, "\n")
diff --git a/metrics_test.go b/metrics_test.go
index 787cc5fc..b4685b41 100644
--- a/metrics_test.go
+++ b/metrics_test.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"testing"
 
-	"github.com/cockroachdb/pebble/internal/datadriven"
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/humanize"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/cockroachdb/redact"
@@ -125,7 +125,7 @@ func TestMetrics(t *testing.T) {
 		}
 	}()
 
-	datadriven.RunTest(t, "testdata/metrics", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/metrics", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "batch":
 			b := d.NewBatch()
diff --git a/pacer_test.go b/pacer_test.go
index 18c5cdaa..ec6a0ac5 100644
--- a/pacer_test.go
+++ b/pacer_test.go
@@ -12,7 +12,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/cockroachdb/pebble/internal/datadriven"
+	"github.com/cockroachdb/datadriven"
 )
 
 type mockPrintLimiter struct {
@@ -36,7 +36,7 @@ func (m *mockPrintLimiter) Burst() int {
 
 func TestCompactionPacerMaybeThrottle(t *testing.T) {
 	datadriven.RunTest(t, "testdata/compaction_pacer_maybe_throttle",
-		func(d *datadriven.TestData) string {
+		func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "init":
 				if len(d.CmdArgs) != 1 {
diff --git a/range_del_test.go b/range_del_test.go
index aee5521d..84da2fe3 100644
--- a/range_del_test.go
+++ b/range_del_test.go
@@ -14,8 +14,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/sstable"
 	"github.com/cockroachdb/pebble/vfs"
@@ -34,7 +34,7 @@ func TestRangeDel(t *testing.T) {
 	opts := &Options{}
 	opts.DisableAutomaticCompactions = true
 
-	datadriven.RunTest(t, "testdata/range_del", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/range_del", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			if d != nil {
diff --git a/snapshot_test.go b/snapshot_test.go
index 39ccdd1d..a1d19ac5 100644
--- a/snapshot_test.go
+++ b/snapshot_test.go
@@ -14,8 +14,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/stretchr/testify/require"
 )
@@ -60,7 +60,7 @@ func TestSnapshot(t *testing.T) {
 	}
 	defer close()
 
-	datadriven.RunTest(t, "testdata/snapshot", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/snapshot", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			close()
diff --git a/sstable/block_property_test.go b/sstable/block_property_test.go
index 54890b9f..bae88eab 100644
--- a/sstable/block_property_test.go
+++ b/sstable/block_property_test.go
@@ -15,9 +15,9 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/rangekey"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/stretchr/testify/require"
@@ -873,7 +873,7 @@ func TestBlockProperties(t *testing.T) {
 	}()
 
 	var stats base.InternalIteratorStats
-	datadriven.RunTest(t, "testdata/block_properties", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/block_properties", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "build":
 			if r != nil {
@@ -1033,7 +1033,7 @@ func TestBlockProperties_BoundLimited(t *testing.T) {
 	}()
 
 	var stats base.InternalIteratorStats
-	datadriven.RunTest(t, "testdata/block_properties_boundlimited", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/block_properties_boundlimited", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "build":
 			if r != nil {
diff --git a/sstable/block_test.go b/sstable/block_test.go
index e39c4d2a..403fb6eb 100644
--- a/sstable/block_test.go
+++ b/sstable/block_test.go
@@ -13,8 +13,8 @@ import (
 	"time"
 	"unsafe"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/stretchr/testify/require"
 	"golang.org/x/exp/rand"
 )
@@ -224,7 +224,7 @@ func TestBlockIter2(t *testing.T) {
 
 	for _, r := range []int{1, 2, 3, 4} {
 		t.Run(fmt.Sprintf("restart=%d", r), func(t *testing.T) {
-			datadriven.RunTest(t, "testdata/block", func(d *datadriven.TestData) string {
+			datadriven.RunTest(t, "testdata/block", func(t *testing.T, d *datadriven.TestData) string {
 				switch d.Cmd {
 				case "build":
 					w := &blockWriter{restartInterval: r}
diff --git a/sstable/data_test.go b/sstable/data_test.go
index c6c9e4df..ae8e44ea 100644
--- a/sstable/data_test.go
+++ b/sstable/data_test.go
@@ -11,11 +11,11 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/bloom"
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/internal/cache"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/vfs"
 )
diff --git a/sstable/reader_test.go b/sstable/reader_test.go
index c94e1fdf..2128335d 100644
--- a/sstable/reader_test.go
+++ b/sstable/reader_test.go
@@ -18,11 +18,11 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/bloom"
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/internal/cache"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/errorfs"
 	"github.com/cockroachdb/pebble/internal/humanize"
 	"github.com/cockroachdb/pebble/internal/testkeys"
@@ -412,7 +412,7 @@ func runTestReader(
 			}
 		}()
 
-		datadriven.RunTest(t, path, func(d *datadriven.TestData) string {
+		datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string {
 			switch d.Cmd {
 			case "build":
 				if r != nil {
@@ -661,7 +661,7 @@ func TestCompactionIteratorSetupForCompaction(t *testing.T) {
 
 func TestMaybeReadahead(t *testing.T) {
 	var rs readaheadState
-	datadriven.RunTest(t, "testdata/readahead", func(d *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/readahead", func(t *testing.T, d *datadriven.TestData) string {
 		cacheHit := false
 		switch d.Cmd {
 		case "reset":
diff --git a/sstable/writer_rangekey_test.go b/sstable/writer_rangekey_test.go
index 1e7b9432..f80fd583 100644
--- a/sstable/writer_rangekey_test.go
+++ b/sstable/writer_rangekey_test.go
@@ -7,8 +7,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/stretchr/testify/require"
@@ -90,7 +90,7 @@ func TestWriter_RangeKeys(t *testing.T) {
 		return r, nil
 	}
 
-	datadriven.RunTest(t, "testdata/writer_range_keys", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/writer_range_keys", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "build":
 			if r != nil {
diff --git a/sstable/writer_test.go b/sstable/writer_test.go
index 97e811e6..dd71d32a 100644
--- a/sstable/writer_test.go
+++ b/sstable/writer_test.go
@@ -14,11 +14,11 @@ import (
 	"sync"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble/bloom"
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/internal/cache"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/humanize"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/vfs"
@@ -83,7 +83,7 @@ func runDataDriven(t *testing.T, file string, tableFormat TableFormat, paralleli
 		return b.String()
 	}
 
-	datadriven.RunTest(t, file, func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, file, func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "build":
 			if r != nil {
@@ -238,7 +238,7 @@ func TestWriterWithValueBlocks(t *testing.T) {
 		return attribute, nil
 	}
 
-	datadriven.RunTest(t, "testdata/writer_value_blocks", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/writer_value_blocks", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "build":
 			if r != nil {
@@ -487,7 +487,7 @@ func TestParallelWriterErrorProp(t *testing.T) {
 func TestSizeEstimate(t *testing.T) {
 	var sizeEstimate sizeEstimate
 	datadriven.RunTest(t, "testdata/size_estimate",
-		func(td *datadriven.TestData) string {
+		func(t *testing.T, td *datadriven.TestData) string {
 			switch td.Cmd {
 			case "init":
 				if len(td.CmdArgs) != 1 {
diff --git a/table_stats_test.go b/table_stats_test.go
index 4408debb..898ddd94 100644
--- a/table_stats_test.go
+++ b/table_stats_test.go
@@ -10,8 +10,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/rangekey"
 	"github.com/cockroachdb/pebble/internal/testkeys"
@@ -45,7 +45,7 @@ func TestTableStats(t *testing.T) {
 		}
 	}()
 
-	datadriven.RunTest(t, "testdata/table_stats", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/table_stats", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "disable":
 			d.mu.Lock()
@@ -146,7 +146,7 @@ func TestTableRangeDeletionIter(t *testing.T) {
 	var m *fileMetadata
 	cmp := base.DefaultComparer.Compare
 	fs := vfs.NewMem()
-	datadriven.RunTest(t, "testdata/table_stats_deletion_iter", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/table_stats_deletion_iter", func(t *testing.T, td *datadriven.TestData) string {
 		switch cmd := td.Cmd; cmd {
 		case "build":
 			f, err := fs.Create("tmp.sst")
diff --git a/testdata/iter_histories/iter_optimizations b/testdata/iter_histories/iter_optimizations
index 9cb12150..a9300955 100644
--- a/testdata/iter_histories/iter_optimizations
+++ b/testdata/iter_histories/iter_optimizations
@@ -625,3 +625,99 @@ seek-prefix-ge c@2
 ----
 b@2: (g, .)
 c@2: (., [c-"c\x00") @5=bar UPDATED)
+
+# Regression test for Cockroachdb#92205. This test constructs this scenario:
+#
+# A DEL in a middle level (L0.0) that we SeekPrefixGE directly for. Note that
+# this DEL is not deleted by any range deletes; it gets exposed to the
+# Iterator. There's a key after this DEL in the L0.0 levelIter, and there's a
+# level above it (L0.1) that has a rangedel deleting that key, but not the DEL
+# we SeekPrefixGE for. In the lowest level, there's a SET at L6 that is to the
+# right of the DEL in L0.0, but is also not deleted by the RANGEDEL in L0.1.
+# Our second SeekPrefixGE will be for this SET. Visualization, where square
+# brackets are files:
+#
+# L0.1                 [dd-ee#RANGEDEL]
+# L0.0    [b#DEL          e#SET]
+# L6            [d#SET]       [f#SET g#SET]
+#
+# When the Iterator encounters the above DEL internal key in the SeekPrefixGE, it
+# calls Iterator.nextUserKey in the Iterator.findNextEntry call that was part of the
+# SeekPrefixGE call. While Iterator.findNextEntry has a conditional to exit
+# out of the loop if we're in prefix iteration and have gone past the prefix,
+# this break only happens _after_ nextUserKey() has run. As a result we Next()
+# the levelIter in L0.0, land on e#SET, and the mergingIter realizes that it
+# is deleted by the rangedel in a higher level (L0.1). The mergingIter does not
+# see d#SET because that sstable was excluded by the bloom filter. We then do a relative
+# seek of all levels below L0.1 to ee (the end key of the rangedel), and in that
+# process we advance the L6 levelIter to the second file.
+#
+# When we do the second SeekPrefixGE for d, the outer Iterator thinks d > b and
+# so TrySeekUsingNext can work. However, the L6 levelIter has already advanced
+# past the file containing d#SET, so we don't surface it even though we should
+# have.
+
+reset bloom-bits-per-key=100
+----
+
+batch commit
+set d@4 foo
+----
+committed 1 keys
+
+flush
+----
+
+compact a-f
+----
+6:
+  000005:[d@4#1,SET-d@4#1,SET]
+
+batch commit
+set f@5 bar
+set g@5 baz
+----
+committed 2 keys
+
+flush
+----
+
+compact e-k
+----
+6:
+  000005:[d@4#1,SET-d@4#1,SET]
+  000007:[f@5#2,SET-g@5#3,SET]
+
+batch commit
+del b@5
+set e@4 foobar
+----
+committed 2 keys
+
+flush
+----
+
+batch commit
+del-range dd ee
+----
+committed 1 keys
+
+flush
+----
+
+lsm
+----
+0.1:
+  000011:[dd#6,RANGEDEL-ee#72057594037927935,RANGEDEL]
+0.0:
+  000009:[b@5#4,DEL-e@4#5,SET]
+6:
+  000005:[d@4#1,SET-d@4#1,SET]
+  000007:[f@5#2,SET-g@5#3,SET]
+
+combined-iter upper=z@3 use-l6-filter
+seek-prefix-ge b@6
+seek-prefix-ge d@5
+----
+.
+d@4: (foo, .)
diff --git a/testdata/merging_iter b/testdata/merging_iter
index a7cddba4..1f41d4d4 100644
--- a/testdata/merging_iter
+++ b/testdata/merging_iter
@@ -540,8 +540,8 @@ seek-prefix-ge c true
 seek-prefix-ge d true
 ----
 a#10,1:a10
-.
-.
+d#10,1:d10
+d#10,1:d10
 d#10,1:d10
 d#10,1:d10
 
@@ -553,8 +553,8 @@ seek-prefix-ge d true
 next
 ----
 a#10,1:a10
-.
-.
+d#10,1:d10
+d#10,1:d10
 d#10,1:d10
 .
 
diff --git a/tool/data_test.go b/tool/data_test.go
index ed289c09..a83eebf8 100644
--- a/tool/data_test.go
+++ b/tool/data_test.go
@@ -13,8 +13,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/spf13/cobra"
@@ -47,7 +47,7 @@ func runTests(t *testing.T, path string) {
 
 		fs := vfs.NewMem()
 		t.Run(name, func(t *testing.T) {
-			datadriven.RunTest(t, path, func(d *datadriven.TestData) string {
+			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())
diff --git a/tool/logs/compaction_test.go b/tool/logs/compaction_test.go
index 45646298..5c85e99f 100644
--- a/tool/logs/compaction_test.go
+++ b/tool/logs/compaction_test.go
@@ -14,8 +14,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/stretchr/testify/require"
 )
 
@@ -180,7 +180,7 @@ func TestCompactionLogs(t *testing.T) {
 	resetFn()
 
 	dir := t.TempDir()
-	datadriven.RunTest(t, "testdata/compactions", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/compactions", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "log":
 			basename := fmt.Sprintf("%d.log", logNum)
diff --git a/vfs/atomicfs/marker_test.go b/vfs/atomicfs/marker_test.go
index 7ef81fba..e1cb2abc 100644
--- a/vfs/atomicfs/marker_test.go
+++ b/vfs/atomicfs/marker_test.go
@@ -14,8 +14,8 @@ import (
 	"sync/atomic"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/cockroachdb/pebble/internal/errorfs"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/stretchr/testify/require"
@@ -64,7 +64,7 @@ func TestMarker(t *testing.T) {
 	memFS := vfs.NewMem()
 
 	var buf bytes.Buffer
-	datadriven.RunTest(t, "testdata/marker", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/marker", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "list":
 			ls, err := memFS.List(td.CmdArgs[0].String())
diff --git a/vfs/vfs_test.go b/vfs/vfs_test.go
index 81638205..a48127c1 100644
--- a/vfs/vfs_test.go
+++ b/vfs/vfs_test.go
@@ -14,9 +14,9 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/cockroachdb/datadriven"
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors/oserror"
-	"github.com/cockroachdb/pebble/internal/datadriven"
 	"github.com/stretchr/testify/require"
 )
 
@@ -122,7 +122,7 @@ func runTestVFS(t *testing.T, baseFS FS, dir string) {
 	var buf bytes.Buffer
 	fs := loggingFS{FS: baseFS, base: dir, w: &buf}
 
-	datadriven.RunTest(t, "testdata/vfs", func(td *datadriven.TestData) string {
+	datadriven.RunTest(t, "testdata/vfs", func(t *testing.T, td *datadriven.TestData) string {
 		switch td.Cmd {
 		case "define":
 			buf.Reset()