LCOV - code coverage report
Current view: top level - pebble/internal/itertest - datadriven.go (source / functions) Hit Total Coverage
Test: 2024-10-25 08:17Z f498510f - tests only.lcov Lines: 141 171 82.5 %
Date: 2024-10-25 08:17:52 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use
       2             : // of this source code is governed by a BSD-style license that can be found in
       3             : // the LICENSE file.
       4             : 
       5             : // Package itertest provides facilities for testing internal iterators.
       6             : package itertest
       7             : 
       8             : import (
       9             :         "bytes"
      10             :         "fmt"
      11             :         "io"
      12             :         "strconv"
      13             :         "strings"
      14             :         "testing"
      15             : 
      16             :         "github.com/cockroachdb/crlib/crstrings"
      17             :         "github.com/cockroachdb/datadriven"
      18             :         "github.com/cockroachdb/pebble/internal/base"
      19             :         "github.com/cockroachdb/pebble/internal/keyspan"
      20             :         "github.com/cockroachdb/pebble/internal/testkeys"
      21             :         "github.com/stretchr/testify/require"
      22             : )
      23             : 
      24             : type iterCmdOpts struct {
      25             :         fmtKV           formatKV
      26             :         showCommands    bool
      27             :         withoutNewlines bool
      28             :         stats           *base.InternalIteratorStats
      29             : }
      30             : 
      31             : // An IterOpt configures the behavior of RunInternalIterCmd.
      32             : type IterOpt func(*iterCmdOpts)
      33             : 
      34             : // A formatKV configures the formatting to use when presenting key-value pairs.
      35             : type formatKV func(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator)
      36             : 
      37             : // Condensed configures RunInternalIterCmd to output condensed results without
      38             : // values, collapsed onto a single line.
      39           1 : func Condensed(opts *iterCmdOpts) {
      40           1 :         opts.fmtKV = condensedFormatKV
      41           1 :         opts.withoutNewlines = true
      42           1 : }
      43             : 
      44             : // ShowCommands configures RunInternalIterCmd to show the command in each output
      45             : // line (so you don't have to visually match the line to the command).
      46           1 : func ShowCommands(opts *iterCmdOpts) {
      47           1 :         opts.showCommands = true
      48           1 : }
      49             : 
      50             : // Verbose configures RunInternalIterCmd to output verbose results.
      51           1 : func Verbose(opts *iterCmdOpts) { opts.fmtKV = verboseFormatKV }
      52             : 
      53             : // WithSpan configures RunInternalIterCmd to print the span returned by spanFunc
      54             : // after each iteration operation.
      55           1 : func WithSpan(spanFunc func() *keyspan.Span) IterOpt {
      56           1 :         return func(opts *iterCmdOpts) {
      57           1 :                 prevFmtKV := opts.fmtKV
      58           1 :                 opts.fmtKV = func(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator) {
      59           1 :                         prevFmtKV(w, key, v, iter)
      60           1 :                         if s := spanFunc(); s != nil {
      61           1 :                                 fmt.Fprintf(w, " / %s", s)
      62           1 :                         }
      63             :                 }
      64             :         }
      65             : }
      66             : 
      67             : // WithStats configures RunInternalIterCmd to collect iterator stats in the
      68             : // struct pointed to by stats.
      69           1 : func WithStats(stats *base.InternalIteratorStats) IterOpt {
      70           1 :         return func(opts *iterCmdOpts) { opts.stats = stats }
      71             : }
      72             : 
      73           1 : func defaultFormatKV(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator) {
      74           1 :         if key != nil {
      75           1 :                 fmt.Fprintf(w, "%s:%s", key.UserKey, v)
      76           1 :         } else if err := iter.Error(); err != nil {
      77           1 :                 fmt.Fprintf(w, "err=%v", err)
      78           1 :         } else {
      79           1 :                 fmt.Fprintf(w, ".")
      80           1 :         }
      81             : }
      82             : 
      83             : // condensedFormatKV is a FormatKV that outputs condensed results.
      84           1 : func condensedFormatKV(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator) {
      85           1 :         if key != nil {
      86           1 :                 fmt.Fprintf(w, "<%s:%d>", key.UserKey, key.SeqNum())
      87           1 :         } else if err := iter.Error(); err != nil {
      88           0 :                 fmt.Fprintf(w, "err=%v", err)
      89           1 :         } else {
      90           1 :                 fmt.Fprint(w, ".")
      91           1 :         }
      92             : }
      93             : 
      94             : // verboseFormatKV is a FormatKV that outputs verbose results.
      95           1 : func verboseFormatKV(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator) {
      96           1 :         if key != nil {
      97           1 :                 fmt.Fprintf(w, "%s:%s", key, v)
      98           1 :                 return
      99           1 :         }
     100           1 :         defaultFormatKV(w, key, v, iter)
     101             : }
     102             : 
     103             : // RunInternalIterCmd evaluates a datadriven command controlling an internal
     104             : // iterator, returning a string with the results of the iterator operations.
     105             : func RunInternalIterCmd(
     106             :         t *testing.T, d *datadriven.TestData, iter base.InternalIterator, opts ...IterOpt,
     107           1 : ) string {
     108           1 :         var buf bytes.Buffer
     109           1 :         RunInternalIterCmdWriter(t, &buf, d, iter, opts...)
     110           1 :         return buf.String()
     111           1 : }
     112             : 
     113             : // RunInternalIterCmdWriter evaluates a datadriven command controlling an
     114             : // internal iterator, writing the results of the iterator operations to the
     115             : // provided Writer.
     116             : func RunInternalIterCmdWriter(
     117             :         t *testing.T, w io.Writer, d *datadriven.TestData, iter base.InternalIterator, opts ...IterOpt,
     118           1 : ) {
     119           1 :         o := iterCmdOpts{fmtKV: defaultFormatKV}
     120           1 :         for _, opt := range opts {
     121           1 :                 opt(&o)
     122           1 :         }
     123             : 
     124           1 :         var prefix []byte
     125           1 :         var prevKey []byte
     126           1 :         getKV := func(kv *base.InternalKV) (*base.InternalKey, []byte) {
     127           1 :                 if kv == nil {
     128           1 :                         prevKey = nil
     129           1 :                         return nil, nil
     130           1 :                 }
     131           1 :                 prevKey = kv.K.UserKey
     132           1 :                 v, _, err := kv.Value(nil)
     133           1 :                 require.NoError(t, err)
     134           1 :                 return &kv.K, v
     135             :         }
     136           1 :         lines := crstrings.Lines(d.Input)
     137           1 :         maxCmdLen := 1
     138           1 :         for _, line := range lines {
     139           1 :                 maxCmdLen = max(maxCmdLen, len(line))
     140           1 :         }
     141           1 :         for _, line := range lines {
     142           1 :                 parts := strings.Fields(line)
     143           1 :                 var key *base.InternalKey
     144           1 :                 var value []byte
     145           1 :                 if o.showCommands {
     146           1 :                         fmt.Fprintf(w, "%*s: ", min(maxCmdLen, 40), line)
     147           1 :                 }
     148           1 :                 switch parts[0] {
     149           1 :                 case "seek-ge":
     150           1 :                         if len(parts) < 2 || len(parts) > 3 {
     151           0 :                                 fmt.Fprint(w, "seek-ge <key> [<try-seek-using-next>]\n")
     152           0 :                                 return
     153           0 :                         }
     154           1 :                         prefix = nil
     155           1 :                         var flags base.SeekGEFlags
     156           1 :                         if len(parts) == 3 {
     157           0 :                                 if trySeekUsingNext, err := strconv.ParseBool(parts[2]); err != nil {
     158           0 :                                         fmt.Fprintf(w, "%s", err.Error())
     159           0 :                                         return
     160           0 :                                 } else if trySeekUsingNext {
     161           0 :                                         flags = flags.EnableTrySeekUsingNext()
     162           0 :                                 }
     163             :                         }
     164           1 :                         key, value = getKV(iter.SeekGE([]byte(strings.TrimSpace(parts[1])), flags))
     165           1 :                 case "seek-prefix-ge":
     166           1 :                         if len(parts) != 2 && len(parts) != 3 {
     167           0 :                                 fmt.Fprint(w, "seek-prefix-ge <key> [<try-seek-using-next>]\n")
     168           0 :                                 return
     169           0 :                         }
     170           1 :                         prefix = []byte(strings.TrimSpace(parts[1]))
     171           1 :                         var flags base.SeekGEFlags
     172           1 :                         if len(parts) == 3 {
     173           1 :                                 if trySeekUsingNext, err := strconv.ParseBool(parts[2]); err != nil {
     174           0 :                                         fmt.Fprintf(w, "%s", err.Error())
     175           0 :                                         return
     176           1 :                                 } else if trySeekUsingNext {
     177           1 :                                         flags = flags.EnableTrySeekUsingNext()
     178           1 :                                 }
     179             :                         }
     180           1 :                         key, value = getKV(iter.SeekPrefixGE(prefix, prefix /* key */, flags))
     181           1 :                 case "seek-lt":
     182           1 :                         if len(parts) != 2 {
     183           0 :                                 fmt.Fprint(w, "seek-lt <key>\n")
     184           0 :                                 return
     185           0 :                         }
     186           1 :                         prefix = nil
     187           1 :                         key, value = getKV(iter.SeekLT([]byte(strings.TrimSpace(parts[1])), base.SeekLTFlagsNone))
     188           1 :                 case "first":
     189           1 :                         prefix = nil
     190           1 :                         key, value = getKV(iter.First())
     191           1 :                 case "last":
     192           1 :                         prefix = nil
     193           1 :                         key, value = getKV(iter.Last())
     194           1 :                 case "next":
     195           1 :                         key, value = getKV(iter.Next())
     196           1 :                 case "next-prefix":
     197           1 :                         succKey := testkeys.Comparer.ImmediateSuccessor(prevKey[:testkeys.Comparer.Split(prevKey)], nil)
     198           1 :                         key, value = getKV(iter.NextPrefix(succKey))
     199           1 :                 case "prev":
     200           1 :                         key, value = getKV(iter.Prev())
     201           1 :                 case "set-bounds":
     202           1 :                         if len(parts) <= 1 || len(parts) > 3 {
     203           0 :                                 fmt.Fprint(w, "set-bounds lower=<lower> upper=<upper>\n")
     204           0 :                                 return
     205           0 :                         }
     206           1 :                         var lower []byte
     207           1 :                         var upper []byte
     208           1 :                         for _, part := range parts[1:] {
     209           1 :                                 arg := strings.Split(strings.TrimSpace(part), "=")
     210           1 :                                 switch arg[0] {
     211           1 :                                 case "lower":
     212           1 :                                         lower = []byte(arg[1])
     213           1 :                                 case "upper":
     214           1 :                                         upper = []byte(arg[1])
     215           0 :                                 default:
     216           0 :                                         fmt.Fprintf(w, "set-bounds: unknown arg: %s", arg)
     217           0 :                                         return
     218             :                                 }
     219             :                         }
     220           1 :                         iter.SetBounds(lower, upper)
     221           1 :                         continue
     222           1 :                 case "stats":
     223           1 :                         if o.stats != nil {
     224           1 :                                 // The timing is non-deterministic, so set to 0.
     225           1 :                                 o.stats.BlockReadDuration = 0
     226           1 :                                 fmt.Fprintf(w, "%+v\n", *o.stats)
     227           1 :                         }
     228           1 :                         continue
     229           1 :                 case "reset-stats":
     230           1 :                         if o.stats != nil {
     231           1 :                                 *o.stats = base.InternalIteratorStats{}
     232           1 :                         }
     233           1 :                         continue
     234           1 :                 case "is-lower-bound":
     235           1 :                         // This command is specific to colblk.DataBlockIter.
     236           1 :                         if len(parts) != 2 {
     237           0 :                                 fmt.Fprint(w, "is-lower-bound <key>\n")
     238           0 :                                 return
     239           0 :                         }
     240           1 :                         i := iter.(interface{ IsLowerBound(key []byte) bool })
     241           1 :                         fmt.Fprintf(w, "%v\n", i.IsLowerBound([]byte(parts[1])))
     242           1 :                         continue
     243           0 :                 default:
     244           0 :                         fmt.Fprintf(w, "unknown op: %s", parts[0])
     245           0 :                         return
     246             :                 }
     247           1 :                 o.fmtKV(w, key, value, iter)
     248           1 :                 if !o.withoutNewlines {
     249           1 :                         fmt.Fprintln(w)
     250           1 :                 }
     251             :         }
     252             : }

Generated by: LCOV version 1.14