LCOV - code coverage report
Current view: top level - pebble/internal/keyspan - test_utils.go (source / functions) Hit Total Coverage
Test: 2024-07-11 08:15Z e727ab74 - meta test only.lcov Lines: 72 345 20.9 %
Date: 2024-07-11 08:16:42 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 keyspan
       6             : 
       7             : import (
       8             :         "bytes"
       9             :         "fmt"
      10             :         "go/token"
      11             :         "io"
      12             :         "reflect"
      13             :         "strconv"
      14             :         "strings"
      15             : 
      16             :         "github.com/cockroachdb/errors"
      17             :         "github.com/cockroachdb/pebble/internal/dsl"
      18             :         "github.com/cockroachdb/pebble/internal/treeprinter"
      19             : )
      20             : 
      21             : // This file contains testing facilities for Spans and FragmentIterators. It's
      22             : // defined here so that it may be used by the keyspan package to test its
      23             : // various FragmentIterator implementations.
      24             : //
      25             : // TODO(jackson): Move keyspan.{Span,Key,FragmentIterator} into internal/base,
      26             : // and then move the testing facilities to an independent package, eg
      27             : // internal/itertest. Alternatively, make all tests that use it use the
      28             : // keyspan_test package, which can then import a separate itertest package.
      29             : 
      30             : // probe defines an interface for probes that may inspect or mutate internal
      31             : // span iterator behavior.
      32             : type probe interface {
      33             :         // probe inspects, and possibly manipulates, iterator operations' results.
      34             :         probe(*probeContext)
      35             : }
      36             : 
      37           0 : func parseProbes(probeDSLs ...string) []probe {
      38           0 :         probes := make([]probe, len(probeDSLs))
      39           0 :         var err error
      40           0 :         for i := range probeDSLs {
      41           0 :                 probes[i], err = probeParser.Parse(probeDSLs[i])
      42           0 :                 if err != nil {
      43           0 :                         panic(err)
      44             :                 }
      45             :         }
      46           0 :         return probes
      47             : }
      48             : 
      49           0 : func attachProbes(iter FragmentIterator, pctx probeContext, probes ...probe) FragmentIterator {
      50           0 :         if pctx.log == nil {
      51           0 :                 pctx.log = io.Discard
      52           0 :         }
      53           0 :         for i := range probes {
      54           0 :                 iter = &probeIterator{
      55           0 :                         iter:     iter,
      56           0 :                         probe:    probes[i],
      57           0 :                         probeCtx: pctx,
      58           0 :                 }
      59           0 :         }
      60           0 :         return iter
      61             : }
      62             : 
      63             : // ParseAndAttachProbes parses DSL probes and attaches them to an iterator.
      64             : func ParseAndAttachProbes(
      65             :         iter FragmentIterator, log io.Writer, probeDSLs ...string,
      66           0 : ) FragmentIterator {
      67           0 :         pctx := probeContext{log: log}
      68           0 :         return attachProbes(iter, pctx, parseProbes(probeDSLs...)...)
      69           0 : }
      70             : 
      71             : // probeContext provides the context within which a probe is run. It includes
      72             : // information about the iterator operation in progress.
      73             : type probeContext struct {
      74             :         op
      75             :         log io.Writer
      76             : }
      77             : 
      78             : type op struct {
      79             :         Kind    OpKind
      80             :         SeekKey []byte
      81             :         Span    *Span
      82             :         Err     error
      83             : }
      84             : 
      85             : // ErrInjected is an error artificially injected for testing.
      86             : var ErrInjected = &errorProbe{name: "ErrInjected", err: errors.New("injected error")}
      87             : 
      88           1 : var probeParser = func() *dsl.Parser[probe] {
      89           1 :         valuerParser := dsl.NewParser[valuer]()
      90           1 :         valuerParser.DefineConstant("StartKey", func() valuer { return startKey{} })
      91           1 :         valuerParser.DefineFunc("Bytes",
      92           1 :                 func(p *dsl.Parser[valuer], s *dsl.Scanner) valuer {
      93           0 :                         v := bytesConstant{bytes: []byte(s.ConsumeString())}
      94           0 :                         s.Consume(token.RPAREN)
      95           0 :                         return v
      96           0 :                 })
      97             : 
      98           1 :         predicateParser := dsl.NewPredicateParser[*probeContext]()
      99           1 :         predicateParser.DefineFunc("Equal",
     100           1 :                 func(p *dsl.Parser[dsl.Predicate[*probeContext]], s *dsl.Scanner) dsl.Predicate[*probeContext] {
     101           0 :                         eq := equal{
     102           0 :                                 valuerParser.ParseFromPos(s, s.Scan()),
     103           0 :                                 valuerParser.ParseFromPos(s, s.Scan()),
     104           0 :                         }
     105           0 :                         s.Consume(token.RPAREN)
     106           0 :                         return eq
     107           0 :                 })
     108           1 :         for i, name := range opNames {
     109           1 :                 opKind := OpKind(i)
     110           1 :                 predicateParser.DefineConstant(name, func() dsl.Predicate[*probeContext] {
     111           0 :                         // An OpKind implements dsl.Predicate[*probeContext].
     112           0 :                         return opKind
     113           0 :                 })
     114             :         }
     115           1 :         probeParser := dsl.NewParser[probe]()
     116           1 :         probeParser.DefineConstant("ErrInjected", func() probe { return ErrInjected })
     117           1 :         probeParser.DefineConstant("noop", func() probe { return noop{} })
     118           1 :         probeParser.DefineFunc("If",
     119           1 :                 func(p *dsl.Parser[probe], s *dsl.Scanner) probe {
     120           0 :                         probe := ifProbe{
     121           0 :                                 predicateParser.ParseFromPos(s, s.Scan()),
     122           0 :                                 probeParser.ParseFromPos(s, s.Scan()),
     123           0 :                                 probeParser.ParseFromPos(s, s.Scan()),
     124           0 :                         }
     125           0 :                         s.Consume(token.RPAREN)
     126           0 :                         return probe
     127           0 :                 })
     128           1 :         probeParser.DefineFunc("Return",
     129           1 :                 func(p *dsl.Parser[probe], s *dsl.Scanner) (ret probe) {
     130           0 :                         switch tok := s.Scan(); tok.Kind {
     131           0 :                         case token.STRING:
     132           0 :                                 str, err := strconv.Unquote(tok.Lit)
     133           0 :                                 if err != nil {
     134           0 :                                         panic(err)
     135             :                                 }
     136           0 :                                 span := ParseSpan(str)
     137           0 :                                 ret = returnSpan{s: &span}
     138           0 :                         case token.IDENT:
     139           0 :                                 switch tok.Lit {
     140           0 :                                 case "nil":
     141           0 :                                         ret = returnSpan{s: nil}
     142           0 :                                 default:
     143           0 :                                         panic(errors.Newf("unrecognized return value %q", tok.Lit))
     144             :                                 }
     145             :                         }
     146           0 :                         s.Consume(token.RPAREN)
     147           0 :                         return ret
     148             :                 })
     149           1 :         probeParser.DefineFunc("Log",
     150           1 :                 func(p *dsl.Parser[probe], s *dsl.Scanner) (ret probe) {
     151           0 :                         ret = loggingProbe{prefix: s.ConsumeString()}
     152           0 :                         s.Consume(token.RPAREN)
     153           0 :                         return ret
     154           0 :                 })
     155           1 :         return probeParser
     156             : }()
     157             : 
     158             : // probe implementations
     159             : 
     160             : type errorProbe struct {
     161             :         name string
     162             :         err  error
     163             : }
     164             : 
     165           0 : func (p *errorProbe) String() string { return p.name }
     166           0 : func (p *errorProbe) Error() error   { return p.err }
     167           0 : func (p *errorProbe) probe(pctx *probeContext) {
     168           0 :         pctx.op.Err = p.err
     169           0 :         pctx.op.Span = nil
     170           0 : }
     171             : 
     172             : // ifProbe is a conditional probe. If its predicate evaluates to true, it probes
     173             : // using its Then probe. If its predicate evalutes to false, it probes using its
     174             : // Else probe.
     175             : type ifProbe struct {
     176             :         Predicate dsl.Predicate[*probeContext]
     177             :         Then      probe
     178             :         Else      probe
     179             : }
     180             : 
     181           0 : func (p ifProbe) String() string { return fmt.Sprintf("(If %s %s %s)", p.Predicate, p.Then, p.Else) }
     182           0 : func (p ifProbe) probe(pctx *probeContext) {
     183           0 :         if p.Predicate.Evaluate(pctx) {
     184           0 :                 p.Then.probe(pctx)
     185           0 :         } else {
     186           0 :                 p.Else.probe(pctx)
     187           0 :         }
     188             : }
     189             : 
     190             : type returnSpan struct {
     191             :         s *Span
     192             : }
     193             : 
     194           0 : func (p returnSpan) String() string {
     195           0 :         if p.s == nil {
     196           0 :                 return "(Return nil)"
     197           0 :         }
     198           0 :         return fmt.Sprintf("(Return %q)", p.s.String())
     199             : }
     200             : 
     201           0 : func (p returnSpan) probe(pctx *probeContext) {
     202           0 :         pctx.op.Span = p.s
     203           0 :         pctx.op.Err = nil
     204           0 : }
     205             : 
     206             : type noop struct{}
     207             : 
     208           0 : func (noop) String() string           { return "Noop" }
     209           0 : func (noop) probe(pctx *probeContext) {}
     210             : 
     211             : type loggingProbe struct {
     212             :         prefix string
     213             : }
     214             : 
     215           0 : func (lp loggingProbe) String() string { return fmt.Sprintf("(Log %q)", lp.prefix) }
     216           0 : func (lp loggingProbe) probe(pctx *probeContext) {
     217           0 :         opStr := strings.TrimPrefix(pctx.op.Kind.String(), "Op")
     218           0 :         fmt.Fprintf(pctx.log, "%s%s(", lp.prefix, opStr)
     219           0 :         if pctx.op.SeekKey != nil {
     220           0 :                 fmt.Fprintf(pctx.log, "%q", pctx.op.SeekKey)
     221           0 :         }
     222           0 :         fmt.Fprint(pctx.log, ") = ")
     223           0 :         if pctx.op.Span == nil {
     224           0 :                 fmt.Fprint(pctx.log, "nil")
     225           0 :                 if pctx.op.Err != nil {
     226           0 :                         fmt.Fprintf(pctx.log, " <err=%q>", pctx.op.Err)
     227           0 :                 }
     228           0 :         } else {
     229           0 :                 fmt.Fprint(pctx.log, pctx.op.Span.String())
     230           0 :         }
     231           0 :         fmt.Fprintln(pctx.log)
     232             : }
     233             : 
     234             : // dsl.Predicate[*probeContext] implementations.
     235             : 
     236             : type equal struct {
     237             :         a, b valuer
     238             : }
     239             : 
     240           0 : func (e equal) String() string { return fmt.Sprintf("(Equal %s %s)", e.a, e.b) }
     241           0 : func (e equal) Evaluate(pctx *probeContext) bool {
     242           0 :         return reflect.DeepEqual(e.a.value(pctx), e.b.value(pctx))
     243           0 : }
     244             : 
     245             : // OpKind indicates the type of iterator operation being performed.
     246             : type OpKind int8
     247             : 
     248             : // OpKind values.
     249             : const (
     250             :         OpSeekGE OpKind = iota
     251             :         OpSeekLT
     252             :         OpFirst
     253             :         OpLast
     254             :         OpNext
     255             :         OpPrev
     256             :         OpClose
     257             :         numOpKinds
     258             : )
     259             : 
     260           0 : func (o OpKind) String() string { return opNames[o] }
     261             : 
     262             : // Evaluate implements dsl.Predicate.
     263           0 : func (o OpKind) Evaluate(pctx *probeContext) bool { return pctx.op.Kind == o }
     264             : 
     265             : var opNames = [numOpKinds]string{
     266             :         OpSeekGE: "OpSeekGE",
     267             :         OpSeekLT: "OpSeekLT",
     268             :         OpFirst:  "OpFirst",
     269             :         OpLast:   "OpLast",
     270             :         OpNext:   "OpNext",
     271             :         OpPrev:   "OpPrev",
     272             :         OpClose:  "OpClose",
     273             : }
     274             : 
     275             : // valuer implementations
     276             : 
     277             : type valuer interface {
     278             :         fmt.Stringer
     279             :         value(pctx *probeContext) any
     280             : }
     281             : 
     282             : type bytesConstant struct {
     283             :         bytes []byte
     284             : }
     285             : 
     286           0 : func (b bytesConstant) String() string               { return fmt.Sprintf("%q", string(b.bytes)) }
     287           0 : func (b bytesConstant) value(pctx *probeContext) any { return b.bytes }
     288             : 
     289             : type startKey struct{}
     290             : 
     291           0 : func (s startKey) String() string { return "StartKey" }
     292           0 : func (s startKey) value(pctx *probeContext) any {
     293           0 :         if pctx.op.Span == nil {
     294           0 :                 return nil
     295           0 :         }
     296           0 :         return pctx.op.Span.Start
     297             : }
     298             : 
     299             : type probeIterator struct {
     300             :         iter     FragmentIterator
     301             :         probe    probe
     302             :         probeCtx probeContext
     303             : }
     304             : 
     305             : // Assert that probeIterator implements the fragment iterator interface.
     306             : var _ FragmentIterator = (*probeIterator)(nil)
     307             : 
     308           0 : func (p *probeIterator) handleOp(preProbeOp op) (*Span, error) {
     309           0 :         p.probeCtx.op = preProbeOp
     310           0 :         p.probe.probe(&p.probeCtx)
     311           0 :         return p.probeCtx.op.Span, p.probeCtx.op.Err
     312           0 : }
     313             : 
     314           0 : func (p *probeIterator) SeekGE(key []byte) (*Span, error) {
     315           0 :         op := op{
     316           0 :                 Kind:    OpSeekGE,
     317           0 :                 SeekKey: key,
     318           0 :         }
     319           0 :         if p.iter != nil {
     320           0 :                 op.Span, op.Err = p.iter.SeekGE(key)
     321           0 :         }
     322           0 :         return p.handleOp(op)
     323             : }
     324             : 
     325           0 : func (p *probeIterator) SeekLT(key []byte) (*Span, error) {
     326           0 :         op := op{
     327           0 :                 Kind:    OpSeekLT,
     328           0 :                 SeekKey: key,
     329           0 :         }
     330           0 :         if p.iter != nil {
     331           0 :                 op.Span, op.Err = p.iter.SeekLT(key)
     332           0 :         }
     333           0 :         return p.handleOp(op)
     334             : }
     335             : 
     336           0 : func (p *probeIterator) First() (*Span, error) {
     337           0 :         op := op{Kind: OpFirst}
     338           0 :         if p.iter != nil {
     339           0 :                 op.Span, op.Err = p.iter.First()
     340           0 :         }
     341           0 :         return p.handleOp(op)
     342             : }
     343             : 
     344           0 : func (p *probeIterator) Last() (*Span, error) {
     345           0 :         op := op{Kind: OpLast}
     346           0 :         if p.iter != nil {
     347           0 :                 op.Span, op.Err = p.iter.Last()
     348           0 :         }
     349           0 :         return p.handleOp(op)
     350             : }
     351             : 
     352           0 : func (p *probeIterator) Next() (*Span, error) {
     353           0 :         op := op{Kind: OpNext}
     354           0 :         if p.iter != nil {
     355           0 :                 op.Span, op.Err = p.iter.Next()
     356           0 :         }
     357           0 :         return p.handleOp(op)
     358             : }
     359             : 
     360           0 : func (p *probeIterator) Prev() (*Span, error) {
     361           0 :         op := op{Kind: OpPrev}
     362           0 :         if p.iter != nil {
     363           0 :                 op.Span, op.Err = p.iter.Prev()
     364           0 :         }
     365           0 :         return p.handleOp(op)
     366             : }
     367             : 
     368           0 : func (p *probeIterator) Close() {
     369           0 :         op := op{Kind: OpClose}
     370           0 :         if p.iter != nil {
     371           0 :                 p.iter.Close()
     372           0 :         }
     373           0 :         _, _ = p.handleOp(op)
     374             : }
     375             : 
     376           0 : func (p *probeIterator) WrapChildren(wrap WrapFn) {
     377           0 :         p.iter = wrap(p.iter)
     378           0 : }
     379             : 
     380             : // DebugTree is part of the FragmentIterator interface.
     381           0 : func (p *probeIterator) DebugTree(tp treeprinter.Node) {
     382           0 :         n := tp.Childf("%T(%p)", p, p)
     383           0 :         if p.iter != nil {
     384           0 :                 p.iter.DebugTree(n)
     385           0 :         }
     386             : }
     387             : 
     388             : // RunIterCmd evaluates a datadriven command controlling an internal
     389             : // keyspan.FragmentIterator, writing the results of the iterator operations to
     390             : // the provided writer.
     391           0 : func RunIterCmd(tdInput string, iter FragmentIterator, w io.Writer) {
     392           0 :         lines := strings.Split(strings.TrimSpace(tdInput), "\n")
     393           0 :         for i, line := range lines {
     394           0 :                 if i > 0 {
     395           0 :                         fmt.Fprintln(w)
     396           0 :                 }
     397           0 :                 line = strings.TrimSpace(line)
     398           0 :                 i := strings.IndexByte(line, '#')
     399           0 :                 iterCmd := line
     400           0 :                 if i > 0 {
     401           0 :                         iterCmd = string(line[:i])
     402           0 :                 }
     403           0 :                 runIterOp(w, iter, iterCmd)
     404             :         }
     405             : }
     406             : 
     407             : var iterDelim = map[rune]bool{',': true, ' ': true, '(': true, ')': true, '"': true}
     408             : 
     409           0 : func runIterOp(w io.Writer, it FragmentIterator, op string) {
     410           0 :         fields := strings.FieldsFunc(op, func(r rune) bool { return iterDelim[r] })
     411           0 :         var s *Span
     412           0 :         var err error
     413           0 :         switch strings.ToLower(fields[0]) {
     414           0 :         case "first":
     415           0 :                 s, err = it.First()
     416           0 :         case "last":
     417           0 :                 s, err = it.Last()
     418           0 :         case "seekge", "seek-ge":
     419           0 :                 if len(fields) == 1 {
     420           0 :                         panic(fmt.Sprintf("unable to parse iter op %q", op))
     421             :                 }
     422           0 :                 s, err = it.SeekGE([]byte(fields[1]))
     423           0 :         case "seeklt", "seek-lt":
     424           0 :                 if len(fields) == 1 {
     425           0 :                         panic(fmt.Sprintf("unable to parse iter op %q", op))
     426             :                 }
     427           0 :                 s, err = it.SeekLT([]byte(fields[1]))
     428           0 :         case "next":
     429           0 :                 s, err = it.Next()
     430           0 :         case "prev":
     431           0 :                 s, err = it.Prev()
     432           0 :         default:
     433           0 :                 panic(fmt.Sprintf("unrecognized iter op %q", fields[0]))
     434             :         }
     435           0 :         switch {
     436           0 :         case err != nil:
     437           0 :                 fmt.Fprintf(w, "<nil> err=<%s>", err)
     438           0 :         case s == nil:
     439           0 :                 fmt.Fprint(w, "<nil>")
     440           0 :         default:
     441           0 :                 fmt.Fprint(w, s)
     442             :         }
     443             : }
     444             : 
     445             : // RunFragmentIteratorCmd runs a command on an iterator; intended for testing.
     446           0 : func RunFragmentIteratorCmd(iter FragmentIterator, input string, extraInfo func() string) string {
     447           0 :         var b bytes.Buffer
     448           0 :         for _, line := range strings.Split(input, "\n") {
     449           0 :                 parts := strings.Fields(line)
     450           0 :                 if len(parts) == 0 {
     451           0 :                         continue
     452             :                 }
     453           0 :                 var span *Span
     454           0 :                 var err error
     455           0 :                 switch parts[0] {
     456           0 :                 case "seek-ge":
     457           0 :                         if len(parts) != 2 {
     458           0 :                                 return "seek-ge <key>\n"
     459           0 :                         }
     460           0 :                         span, err = iter.SeekGE([]byte(strings.TrimSpace(parts[1])))
     461           0 :                 case "seek-lt":
     462           0 :                         if len(parts) != 2 {
     463           0 :                                 return "seek-lt <key>\n"
     464           0 :                         }
     465           0 :                         span, err = iter.SeekLT([]byte(strings.TrimSpace(parts[1])))
     466           0 :                 case "first":
     467           0 :                         span, err = iter.First()
     468           0 :                 case "last":
     469           0 :                         span, err = iter.Last()
     470           0 :                 case "next":
     471           0 :                         span, err = iter.Next()
     472           0 :                 case "prev":
     473           0 :                         span, err = iter.Prev()
     474           0 :                 default:
     475           0 :                         return fmt.Sprintf("unknown op: %s", parts[0])
     476             :                 }
     477           0 :                 switch {
     478           0 :                 case err != nil:
     479           0 :                         fmt.Fprintf(&b, "err=%v\n", err)
     480           0 :                 case span == nil:
     481           0 :                         fmt.Fprintf(&b, ".\n")
     482           0 :                 default:
     483           0 :                         fmt.Fprintf(&b, "%s", span)
     484           0 :                         if extraInfo != nil {
     485           0 :                                 fmt.Fprintf(&b, " (%s)", extraInfo())
     486           0 :                         }
     487           0 :                         b.WriteByte('\n')
     488             :                 }
     489             :         }
     490           0 :         return b.String()
     491             : }
     492             : 
     493             : // NewInvalidatingIter wraps a FragmentIterator; spans surfaced by the inner
     494             : // iterator are copied to buffers that are zeroed by subsequent iterator
     495             : // positioning calls. This is intended to help surface bugs in improper lifetime
     496             : // expectations of Spans.
     497           1 : func NewInvalidatingIter(iter FragmentIterator) FragmentIterator {
     498           1 :         return &invalidatingIter{
     499           1 :                 iter: iter,
     500           1 :         }
     501           1 : }
     502             : 
     503             : type invalidatingIter struct {
     504             :         iter FragmentIterator
     505             :         bufs [][]byte
     506             :         keys []Key
     507             :         span Span
     508             : }
     509             : 
     510             : // invalidatingIter implements FragmentIterator.
     511             : var _ FragmentIterator = (*invalidatingIter)(nil)
     512             : 
     513           1 : func (i *invalidatingIter) invalidate(s *Span, err error) (*Span, error) {
     514           1 :         // Mangle the entirety of the byte bufs and the keys slice.
     515           1 :         for j := range i.bufs {
     516           1 :                 for k := range i.bufs[j] {
     517           1 :                         i.bufs[j][k] = 0xff
     518           1 :                 }
     519           1 :                 i.bufs[j] = nil
     520             :         }
     521           1 :         for j := range i.keys {
     522           1 :                 i.keys[j] = Key{}
     523           1 :         }
     524           1 :         if s == nil {
     525           1 :                 return nil, err
     526           1 :         }
     527             : 
     528             :         // Copy all of the span's slices into slices owned by the invalidating iter
     529             :         // that we can invalidate on a subsequent positioning method.
     530           1 :         i.bufs = i.bufs[:0]
     531           1 :         i.keys = i.keys[:0]
     532           1 :         i.span = Span{
     533           1 :                 Start:     i.saveBytes(s.Start),
     534           1 :                 End:       i.saveBytes(s.End),
     535           1 :                 KeysOrder: s.KeysOrder,
     536           1 :         }
     537           1 :         for j := range s.Keys {
     538           1 :                 i.keys = append(i.keys, Key{
     539           1 :                         Trailer: s.Keys[j].Trailer,
     540           1 :                         Suffix:  i.saveBytes(s.Keys[j].Suffix),
     541           1 :                         Value:   i.saveBytes(s.Keys[j].Value),
     542           1 :                 })
     543           1 :         }
     544           1 :         i.span.Keys = i.keys
     545           1 :         return &i.span, err
     546             : }
     547             : 
     548           1 : func (i *invalidatingIter) saveBytes(b []byte) []byte {
     549           1 :         if b == nil {
     550           1 :                 return nil
     551           1 :         }
     552           1 :         saved := append([]byte(nil), b...)
     553           1 :         i.bufs = append(i.bufs, saved)
     554           1 :         return saved
     555             : }
     556             : 
     557           1 : func (i *invalidatingIter) SeekGE(key []byte) (*Span, error) { return i.invalidate(i.iter.SeekGE(key)) }
     558           1 : func (i *invalidatingIter) SeekLT(key []byte) (*Span, error) { return i.invalidate(i.iter.SeekLT(key)) }
     559           1 : func (i *invalidatingIter) First() (*Span, error)            { return i.invalidate(i.iter.First()) }
     560           1 : func (i *invalidatingIter) Last() (*Span, error)             { return i.invalidate(i.iter.Last()) }
     561           1 : func (i *invalidatingIter) Next() (*Span, error)             { return i.invalidate(i.iter.Next()) }
     562           1 : func (i *invalidatingIter) Prev() (*Span, error)             { return i.invalidate(i.iter.Prev()) }
     563             : 
     564           1 : func (i *invalidatingIter) Close() {
     565           1 :         _, _ = i.invalidate(nil, nil)
     566           1 :         i.iter.Close()
     567           1 : }
     568             : 
     569           0 : func (i *invalidatingIter) WrapChildren(wrap WrapFn) {
     570           0 :         i.iter = wrap(i.iter)
     571           0 : }
     572             : 
     573             : // DebugTree is part of the FragmentIterator interface.
     574           0 : func (i *invalidatingIter) DebugTree(tp treeprinter.Node) {
     575           0 :         n := tp.Childf("%T(%p)", i, i)
     576           0 :         if i.iter != nil {
     577           0 :                 i.iter.DebugTree(n)
     578           0 :         }
     579             : }

Generated by: LCOV version 1.14