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
6 :
7 : import (
8 : "fmt"
9 : "go/token"
10 : "strings"
11 :
12 : "github.com/cockroachdb/pebble/internal/base"
13 : "github.com/cockroachdb/pebble/internal/dsl"
14 : "github.com/cockroachdb/pebble/vfs/errorfs"
15 : )
16 :
17 : // Predicate encodes conditional logic that yields a boolean.
18 : type Predicate = dsl.Predicate[*ProbeContext]
19 :
20 : // NewParser constructs a Probe parser.
21 1 : func NewParser() *dsl.Parser[Probe] {
22 1 : predicateParser := dsl.NewPredicateParser[*ProbeContext]()
23 1 : for i, name := range opNames {
24 1 : opKind := OpKind(i)
25 1 : predicateParser.DefineConstant(name, func() dsl.Predicate[*ProbeContext] {
26 1 : // An OpKind implements dsl.Predicate[*ProbeContext].
27 1 : return opKind
28 1 : })
29 : }
30 1 : predicateParser.DefineFunc("UserKey",
31 1 : func(p *dsl.Parser[Predicate], s *dsl.Scanner) dsl.Predicate[*ProbeContext] {
32 0 : userKey := s.ConsumeString()
33 0 : s.Consume(token.RPAREN)
34 0 : return UserKey(userKey)
35 0 : })
36 :
37 1 : probeParser := dsl.NewParser[Probe]()
38 1 : probeParser.DefineConstant("ErrInjected", func() Probe { return ErrInjected })
39 1 : probeParser.DefineConstant("noop", Noop)
40 1 : probeParser.DefineConstant("Nil", Nil)
41 1 : probeParser.DefineFunc("If",
42 1 : func(p *dsl.Parser[Probe], s *dsl.Scanner) Probe {
43 1 : pred := If(
44 1 : predicateParser.ParseFromPos(s, s.Scan()),
45 1 : probeParser.ParseFromPos(s, s.Scan()),
46 1 : probeParser.ParseFromPos(s, s.Scan()),
47 1 : )
48 1 : s.Consume(token.RPAREN)
49 1 : return pred
50 1 : })
51 1 : probeParser.DefineFunc("ReturnKV",
52 1 : func(p *dsl.Parser[Probe], s *dsl.Scanner) Probe {
53 1 : ik := base.ParseInternalKey(s.ConsumeString())
54 1 : val := []byte(s.ConsumeString())
55 1 : s.Consume(token.RPAREN)
56 1 : return ReturnKV(&ik, val)
57 1 : })
58 1 : probeParser.DefineFunc("Log",
59 1 : func(p *dsl.Parser[Probe], s *dsl.Scanner) (ret Probe) {
60 1 : ret = loggingProbe{prefix: s.ConsumeString()}
61 1 : s.Consume(token.RPAREN)
62 1 : return ret
63 1 : })
64 1 : return probeParser
65 : }
66 :
67 : // ErrInjected is an error artificially injected for testing.
68 : var ErrInjected = Error("ErrInjected", errorfs.ErrInjected)
69 :
70 : // Error returns a Probe that returns the provided error. The name is Name
71 : // returned by String().
72 1 : func Error(name string, err error) *ErrorProbe {
73 1 : return &ErrorProbe{name: name, err: err}
74 1 : }
75 :
76 : // ErrorProbe is a Probe that injects an error.
77 : type ErrorProbe struct {
78 : name string
79 : err error
80 : }
81 :
82 : // String implements fmt.Stringer.
83 0 : func (p *ErrorProbe) String() string {
84 0 : return p.name
85 0 : }
86 :
87 : // Error implements error, so that injected error values may be used as probes
88 : // that inject themselves.
89 0 : func (p *ErrorProbe) Error() error {
90 0 : return p.err
91 0 : }
92 :
93 : // Probe implements the Probe interface, replacing the iterator return value
94 : // with an error.
95 1 : func (p *ErrorProbe) Probe(pctx *ProbeContext) {
96 1 : pctx.Op.Return.Err = p.err
97 1 : pctx.Op.Return.Key = nil
98 1 : pctx.Op.Return.Value = base.LazyValue{}
99 1 : }
100 :
101 : // If a conditional Probe. If its predicate evaluates to true, it probes using
102 : // its Then probe. If its predicate evalutes to false, it probes using its Else
103 : // probe.
104 1 : func If(pred Predicate, thenProbe, elseProbe Probe) Probe {
105 1 : return ifProbe{pred, thenProbe, elseProbe}
106 1 : }
107 :
108 : type ifProbe struct {
109 : Predicate Predicate
110 : Then Probe
111 : Else Probe
112 : }
113 :
114 : // String implements fmt.Stringer.
115 0 : func (p ifProbe) String() string { return fmt.Sprintf("(If %s %s %s)", p.Predicate, p.Then, p.Else) }
116 :
117 : // Probe implements Probe.
118 1 : func (p ifProbe) Probe(pctx *ProbeContext) {
119 1 : if p.Predicate.Evaluate(pctx) {
120 1 : p.Then.Probe(pctx)
121 1 : } else {
122 1 : p.Else.Probe(pctx)
123 1 : }
124 : }
125 :
126 : type loggingProbe struct {
127 : prefix string
128 : }
129 :
130 0 : func (lp loggingProbe) String() string { return fmt.Sprintf("(Log %q)", lp.prefix) }
131 1 : func (lp loggingProbe) Probe(pctx *ProbeContext) {
132 1 : opStr := strings.TrimPrefix(pctx.Kind.String(), "Op")
133 1 : fmt.Fprintf(pctx.Log, "%s%s(", lp.prefix, opStr)
134 1 : if pctx.SeekKey != nil {
135 1 : fmt.Fprintf(pctx.Log, "%q", pctx.SeekKey)
136 1 : }
137 1 : fmt.Fprint(pctx.Log, ") = ")
138 1 : if pctx.Return.Key == nil {
139 1 : fmt.Fprint(pctx.Log, "nil")
140 1 : if pctx.Return.Err != nil {
141 1 : fmt.Fprintf(pctx.Log, " <err=%q>", pctx.Return.Err)
142 1 : }
143 1 : } else {
144 1 : v, _, err := pctx.Return.Value.Value(nil)
145 1 : if err != nil {
146 0 : panic(err)
147 : }
148 1 : fmt.Fprintf(pctx.Log, "(%s,%q)", pctx.Return.Key, v)
149 : }
150 1 : fmt.Fprintln(pctx.Log)
151 : }
152 :
153 : // UserKey implements a predicate that evaluates to true if the returned
154 : // InternalKey holds a specific user key.
155 : type UserKey []byte
156 :
157 : // String implements fmt.Stringer.
158 0 : func (p UserKey) String() string { return fmt.Sprintf("(UserKey %q)", string(p)) }
159 :
160 : // Evaluate implements Predicate.
161 0 : func (p UserKey) Evaluate(pctx *ProbeContext) bool {
162 0 : return pctx.Op.Return.Key != nil && pctx.Comparer.Equal(pctx.Op.Return.Key.UserKey, p)
163 0 : }
164 :
165 : // ReturnKV returns a Probe that modifies an operation's return value to the
166 : // provided KV pair.
167 1 : func ReturnKV(k *base.InternalKey, v []byte) Probe {
168 1 : return &returnKV{k, v}
169 1 : }
170 :
171 : type returnKV struct {
172 : *base.InternalKey
173 : Value []byte
174 : }
175 :
176 : // Probe implements Probe.
177 1 : func (kv *returnKV) Probe(pctx *ProbeContext) {
178 1 : pctx.Op.Return.Key = kv.InternalKey
179 1 : pctx.Op.Return.Value = base.MakeInPlaceValue(kv.Value)
180 1 : }
181 :
182 : // Noop returns a Probe that does nothing.
183 1 : func Noop() Probe { return noop{} }
184 :
185 : type noop struct{}
186 :
187 0 : func (noop) String() string { return "noop" }
188 1 : func (noop) Probe(pctx *ProbeContext) {}
189 :
190 : // Nil returns a Probe that always returns nil.
191 1 : func Nil() Probe { return returnNil{} }
192 :
193 : type returnNil struct{}
194 :
195 0 : func (returnNil) String() string { return "Nil" }
196 1 : func (returnNil) Probe(pctx *ProbeContext) {
197 1 : pctx.Op.Return.Key = nil
198 1 : pctx.Op.Return.Value = base.LazyValue{}
199 1 : }
|