LCOV - code coverage report
Current view: top level - pebble/internal/itertest - datadriven.go (source / functions) Hit Total Coverage
Test: 2024-08-16 08:16Z 91a64c7d - tests only.lcov Lines: 126 153 82.4 %
Date: 2024-08-16 08:17:21 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/datadriven"
      17             :         "github.com/cockroachdb/pebble/internal/base"
      18             :         "github.com/cockroachdb/pebble/internal/keyspan"
      19             :         "github.com/cockroachdb/pebble/internal/testkeys"
      20             :         "github.com/stretchr/testify/require"
      21             : )
      22             : 
      23             : type iterCmdOpts struct {
      24             :         fmtKV           formatKV
      25             :         withoutNewlines bool
      26             :         stats           *base.InternalIteratorStats
      27             : }
      28             : 
      29             : // An IterOpt configures the behavior of RunInternalIterCmd.
      30             : type IterOpt func(*iterCmdOpts)
      31             : 
      32             : // A formatKV configures the formatting to use when presenting key-value pairs.
      33             : type formatKV func(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator)
      34             : 
      35             : // Condensed configures RunInternalIterCmd to output condensed results without
      36             : // values, collapsed onto a single line.
      37           1 : func Condensed(opts *iterCmdOpts) {
      38           1 :         opts.fmtKV = condensedFormatKV
      39           1 :         opts.withoutNewlines = true
      40           1 : }
      41             : 
      42             : // Verbose configures RunInternalIterCmd to output verbose results.
      43           1 : func Verbose(opts *iterCmdOpts) { opts.fmtKV = verboseFormatKV }
      44             : 
      45             : // WithSpan configures RunInternalIterCmd to print the span returned by spanFunc
      46             : // after each iteration operation.
      47           1 : func WithSpan(spanFunc func() *keyspan.Span) IterOpt {
      48           1 :         return func(opts *iterCmdOpts) {
      49           1 :                 prevFmtKV := opts.fmtKV
      50           1 :                 opts.fmtKV = func(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator) {
      51           1 :                         prevFmtKV(w, key, v, iter)
      52           1 :                         if s := spanFunc(); s != nil {
      53           1 :                                 fmt.Fprintf(w, " / %s", s)
      54           1 :                         }
      55             :                 }
      56             :         }
      57             : }
      58             : 
      59             : // WithStats configures RunInternalIterCmd to collect iterator stats in the
      60             : // struct pointed to by stats.
      61           1 : func WithStats(stats *base.InternalIteratorStats) IterOpt {
      62           1 :         return func(opts *iterCmdOpts) { opts.stats = stats }
      63             : }
      64             : 
      65           1 : func defaultFormatKV(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator) {
      66           1 :         if key != nil {
      67           1 :                 fmt.Fprintf(w, "%s:%s", key.UserKey, v)
      68           1 :         } else if err := iter.Error(); err != nil {
      69           1 :                 fmt.Fprintf(w, "err=%v", err)
      70           1 :         } else {
      71           1 :                 fmt.Fprintf(w, ".")
      72           1 :         }
      73             : }
      74             : 
      75             : // condensedFormatKV is a FormatKV that outputs condensed results.
      76           1 : func condensedFormatKV(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator) {
      77           1 :         if key != nil {
      78           1 :                 fmt.Fprintf(w, "<%s:%d>", key.UserKey, key.SeqNum())
      79           1 :         } else if err := iter.Error(); err != nil {
      80           0 :                 fmt.Fprintf(w, "err=%v", err)
      81           1 :         } else {
      82           1 :                 fmt.Fprint(w, ".")
      83           1 :         }
      84             : }
      85             : 
      86             : // verboseFormatKV is a FormatKV that outputs verbose results.
      87           1 : func verboseFormatKV(w io.Writer, key *base.InternalKey, v []byte, iter base.InternalIterator) {
      88           1 :         if key != nil {
      89           1 :                 fmt.Fprintf(w, "%s:%s", key, v)
      90           1 :                 return
      91           1 :         }
      92           1 :         defaultFormatKV(w, key, v, iter)
      93             : }
      94             : 
      95             : // RunInternalIterCmd evaluates a datadriven command controlling an internal
      96             : // iterator, returning a string with the results of the iterator operations.
      97             : func RunInternalIterCmd(
      98             :         t *testing.T, d *datadriven.TestData, iter base.InternalIterator, opts ...IterOpt,
      99           1 : ) string {
     100           1 :         var buf bytes.Buffer
     101           1 :         RunInternalIterCmdWriter(t, &buf, d, iter, opts...)
     102           1 :         return buf.String()
     103           1 : }
     104             : 
     105             : // RunInternalIterCmdWriter evaluates a datadriven command controlling an
     106             : // internal iterator, writing the results of the iterator operations to the
     107             : // provided Writer.
     108             : func RunInternalIterCmdWriter(
     109             :         t *testing.T, w io.Writer, d *datadriven.TestData, iter base.InternalIterator, opts ...IterOpt,
     110           1 : ) {
     111           1 :         o := iterCmdOpts{fmtKV: defaultFormatKV}
     112           1 :         for _, opt := range opts {
     113           1 :                 opt(&o)
     114           1 :         }
     115             : 
     116           1 :         var prefix []byte
     117           1 :         var prevKey []byte
     118           1 :         getKV := func(kv *base.InternalKV) (*base.InternalKey, []byte) {
     119           1 :                 if kv == nil {
     120           1 :                         prevKey = nil
     121           1 :                         return nil, nil
     122           1 :                 }
     123           1 :                 prevKey = kv.K.UserKey
     124           1 :                 v, _, err := kv.Value(nil)
     125           1 :                 require.NoError(t, err)
     126           1 :                 return &kv.K, v
     127             :         }
     128           1 :         for _, line := range strings.Split(d.Input, "\n") {
     129           1 :                 parts := strings.Fields(line)
     130           1 :                 if len(parts) == 0 {
     131           1 :                         continue
     132             :                 }
     133           1 :                 var key *base.InternalKey
     134           1 :                 var value []byte
     135           1 :                 switch parts[0] {
     136           1 :                 case "seek-ge":
     137           1 :                         if len(parts) < 2 || len(parts) > 3 {
     138           0 :                                 fmt.Fprint(w, "seek-ge <key> [<try-seek-using-next>]\n")
     139           0 :                                 return
     140           0 :                         }
     141           1 :                         prefix = nil
     142           1 :                         var flags base.SeekGEFlags
     143           1 :                         if len(parts) == 3 {
     144           0 :                                 if trySeekUsingNext, err := strconv.ParseBool(parts[2]); err != nil {
     145           0 :                                         fmt.Fprintf(w, "%s", err.Error())
     146           0 :                                         return
     147           0 :                                 } else if trySeekUsingNext {
     148           0 :                                         flags = flags.EnableTrySeekUsingNext()
     149           0 :                                 }
     150             :                         }
     151           1 :                         key, value = getKV(iter.SeekGE([]byte(strings.TrimSpace(parts[1])), flags))
     152           1 :                 case "seek-prefix-ge":
     153           1 :                         if len(parts) != 2 && len(parts) != 3 {
     154           0 :                                 fmt.Fprint(w, "seek-prefix-ge <key> [<try-seek-using-next>]\n")
     155           0 :                                 return
     156           0 :                         }
     157           1 :                         prefix = []byte(strings.TrimSpace(parts[1]))
     158           1 :                         var flags base.SeekGEFlags
     159           1 :                         if len(parts) == 3 {
     160           1 :                                 if trySeekUsingNext, err := strconv.ParseBool(parts[2]); err != nil {
     161           0 :                                         fmt.Fprintf(w, "%s", err.Error())
     162           0 :                                         return
     163           1 :                                 } else if trySeekUsingNext {
     164           1 :                                         flags = flags.EnableTrySeekUsingNext()
     165           1 :                                 }
     166             :                         }
     167           1 :                         key, value = getKV(iter.SeekPrefixGE(prefix, prefix /* key */, flags))
     168           1 :                 case "seek-lt":
     169           1 :                         if len(parts) != 2 {
     170           0 :                                 fmt.Fprint(w, "seek-lt <key>\n")
     171           0 :                                 return
     172           0 :                         }
     173           1 :                         prefix = nil
     174           1 :                         key, value = getKV(iter.SeekLT([]byte(strings.TrimSpace(parts[1])), base.SeekLTFlagsNone))
     175           1 :                 case "first":
     176           1 :                         prefix = nil
     177           1 :                         key, value = getKV(iter.First())
     178           1 :                 case "last":
     179           1 :                         prefix = nil
     180           1 :                         key, value = getKV(iter.Last())
     181           1 :                 case "next":
     182           1 :                         key, value = getKV(iter.Next())
     183           1 :                 case "next-prefix":
     184           1 :                         succKey := testkeys.Comparer.ImmediateSuccessor(prevKey[:testkeys.Comparer.Split(prevKey)], nil)
     185           1 :                         key, value = getKV(iter.NextPrefix(succKey))
     186           1 :                 case "prev":
     187           1 :                         key, value = getKV(iter.Prev())
     188           1 :                 case "set-bounds":
     189           1 :                         if len(parts) <= 1 || len(parts) > 3 {
     190           0 :                                 fmt.Fprint(w, "set-bounds lower=<lower> upper=<upper>\n")
     191           0 :                                 return
     192           0 :                         }
     193           1 :                         var lower []byte
     194           1 :                         var upper []byte
     195           1 :                         for _, part := range parts[1:] {
     196           1 :                                 arg := strings.Split(strings.TrimSpace(part), "=")
     197           1 :                                 switch arg[0] {
     198           1 :                                 case "lower":
     199           1 :                                         lower = []byte(arg[1])
     200           1 :                                 case "upper":
     201           1 :                                         upper = []byte(arg[1])
     202           0 :                                 default:
     203           0 :                                         fmt.Fprintf(w, "set-bounds: unknown arg: %s", arg)
     204           0 :                                         return
     205             :                                 }
     206             :                         }
     207           1 :                         iter.SetBounds(lower, upper)
     208           1 :                         continue
     209           1 :                 case "stats":
     210           1 :                         if o.stats != nil {
     211           1 :                                 // The timing is non-deterministic, so set to 0.
     212           1 :                                 o.stats.BlockReadDuration = 0
     213           1 :                                 fmt.Fprintf(w, "%+v\n", *o.stats)
     214           1 :                         }
     215           1 :                         continue
     216           1 :                 case "reset-stats":
     217           1 :                         if o.stats != nil {
     218           1 :                                 *o.stats = base.InternalIteratorStats{}
     219           1 :                         }
     220           1 :                         continue
     221           0 :                 default:
     222           0 :                         fmt.Fprintf(w, "unknown op: %s", parts[0])
     223           0 :                         return
     224             :                 }
     225           1 :                 o.fmtKV(w, key, value, iter)
     226           1 :                 if !o.withoutNewlines {
     227           1 :                         fmt.Fprintln(w)
     228           1 :                 }
     229             :         }
     230             : }

Generated by: LCOV version 1.14