Skip to content

Commit d10d78a

Browse files
committed
db: don't inject GetDiskUsage errors in TestIteratorErrors
Add a more general `OpKindIn` errorfs predicate and use it to disable errors in `GetDiskUsage` (which causes a background error to be reported, which causes a test failure).
1 parent fa478f8 commit d10d78a

File tree

4 files changed

+67
-43
lines changed

4 files changed

+67
-43
lines changed

error_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ func TestDBWALRotationCrash(t *testing.T) {
360360
var crashFS *vfs.MemFS
361361
var index atomic.Int32
362362
inj := errorfs.InjectorFunc(func(op errorfs.Op) error {
363-
if op.Kind.ReadOrWrite() == errorfs.OpIsWrite && index.Add(-1) == -1 {
363+
if op.Kind.IsWrite() && index.Add(-1) == -1 {
364364
crashFS = memfs.CrashClone(vfs.CrashCloneCfg{UnsyncedDataPercent: 0})
365365
}
366366
return nil
@@ -435,7 +435,7 @@ func TestDBCompactionCrash(t *testing.T) {
435435
mkFS := func() vfs.FS {
436436
memfs := vfs.NewCrashableMem()
437437
inj := errorfs.InjectorFunc(func(op errorfs.Op) error {
438-
if op.Kind.ReadOrWrite() == errorfs.OpIsWrite && crashIndex.Add(-1) == -1 {
438+
if op.Kind.IsWrite() && crashIndex.Add(-1) == -1 {
439439
// Allow an arbitrary subset of non-synced state to survive beyond the
440440
// crash point.
441441
crashFS = memfs.CrashClone(vfs.CrashCloneCfg{UnsyncedDataPercent: 10, RNG: crashRNG})

external_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ func TestIteratorErrors(t *testing.T) {
7373
// Wrap it in both a counter and a toggle so that we a) know whether an
7474
// error was injected over the course of an operation, and b) so that we
7575
// can disable error injection during Open.
76-
predicate := errorfs.And(errorfs.Reads, errorfs.Randomly(0.50, seed))
76+
predicate := errorfs.And(
77+
errorfs.OpKindIn("ReadsExceptGetDiskUsage", errorfs.ReadOps.Minus(errorfs.OpGetDiskUsage)),
78+
errorfs.Randomly(0.50, seed),
79+
)
7780
counter := errorfs.Counter{Injector: errorfs.ErrInjected.If(predicate)}
7881
toggle := errorfs.Toggle{Injector: &counter}
7982
testOpts.Opts.FS = errorfs.Wrap(testOpts.Opts.FS, &toggle)
@@ -82,6 +85,8 @@ func TestIteratorErrors(t *testing.T) {
8285
var logBuf bytes.Buffer
8386
defer func() {
8487
if t.Failed() {
88+
// TODO(radu): we don't close the db, which could be emitting a log
89+
// right now, causing a race here.
8590
t.Log(logBuf.String())
8691
}
8792
}()

vfs/errorfs/dsl.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ func And(operands ...Predicate) Predicate {
2525
return dsl.And[Op](operands...)
2626
}
2727

28+
// Not returns a predicate that evaluates to true if the operand evaluates to false.
29+
func Not(operand Predicate) Predicate {
30+
return dsl.Not[Op](operand)
31+
}
32+
2833
// PathMatch returns a predicate that returns true if an operation's file path
2934
// matches the provided pattern according to filepath.Match.
3035
func PathMatch(pattern string) Predicate {
@@ -52,16 +57,25 @@ func (pm *pathMatch) Evaluate(op Op) bool {
5257
var (
5358
// Reads is a predicate that returns true iff an operation is a read
5459
// operation.
55-
Reads Predicate = opKindPred{kind: OpIsRead}
60+
Reads Predicate = OpKindIn("Reads", ReadOps)
5661
// Writes is a predicate that returns true iff an operation is a write
5762
// operation.
58-
Writes Predicate = opKindPred{kind: OpIsWrite}
63+
Writes Predicate = OpKindIn("Writes", WriteOps)
5964
)
6065

61-
type opFileWrite struct{}
66+
// OpKindIn returns a predicate that evaluates to true if an operation's kind is
67+
// in the given set.
68+
func OpKindIn(desc string, kinds OpKinds) Predicate {
69+
return opKindPred{desc: desc, kinds: kinds}
70+
}
71+
72+
type opKindPred struct {
73+
desc string
74+
kinds OpKinds
75+
}
6276

63-
func (o opFileWrite) String() string { return "OpFileWrite" }
64-
func (o opFileWrite) Evaluate(op Op) bool { return op.Kind == OpFileWrite }
77+
func (p opKindPred) String() string { return p.desc }
78+
func (p opKindPred) Evaluate(op Op) bool { return p.kinds.Contains(op.Kind) }
6579

6680
type opFileReadAt struct {
6781
// offset configures the predicate to evaluate to true only if the
@@ -77,13 +91,6 @@ func (o *opFileReadAt) Evaluate(op Op) bool {
7791
return op.Kind == OpFileReadAt && o.offset == op.Offset
7892
}
7993

80-
type opKindPred struct {
81-
kind OpReadWrite
82-
}
83-
84-
func (p opKindPred) String() string { return p.kind.String() }
85-
func (p opKindPred) Evaluate(op Op) bool { return p.kind == op.Kind.ReadOrWrite() }
86-
8794
// Randomly constructs a new predicate that pseudorandomly evaluates to true
8895
// with probability p using randomness determinstically derived from seed.
8996
//
@@ -172,7 +179,7 @@ func NewParser() *Parser {
172179
}
173180
p.predicates.DefineConstant("Reads", func() dsl.Predicate[Op] { return Reads })
174181
p.predicates.DefineConstant("Writes", func() dsl.Predicate[Op] { return Writes })
175-
p.predicates.DefineConstant("OpFileWrite", func() dsl.Predicate[Op] { return opFileWrite{} })
182+
p.predicates.DefineConstant("OpFileWrite", func() dsl.Predicate[Op] { return OpKindIn("OpFileWrite", MakeOpKinds(OpFileWrite)) })
176183
p.predicates.DefineFunc("PathMatch",
177184
func(p *dsl.Parser[dsl.Predicate[Op]], s *dsl.Scanner) dsl.Predicate[Op] {
178185
pattern := s.ConsumeString()

vfs/errorfs/errorfs.go

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -87,40 +87,52 @@ const (
8787
OpFileSyncTo
8888
// OpFileFlush describes a file flush operation.
8989
OpFileFlush
90+
91+
numOpKinds
9092
)
9193

92-
// ReadOrWrite returns the operation's kind.
93-
func (o OpKind) ReadOrWrite() OpReadWrite {
94-
switch o {
95-
case OpOpen, OpOpenDir, OpList, OpStat, OpGetDiskUsage, OpFileRead, OpFileReadAt, OpFileStat:
96-
return OpIsRead
97-
case OpCreate, OpLink, OpRemove, OpRemoveAll, OpRename, OpReuseForWrite, OpMkdirAll, OpLock, OpFileClose, OpFileWrite, OpFileWriteAt, OpFileSync, OpFileSyncData, OpFileSyncTo, OpFileFlush, OpFilePreallocate:
98-
return OpIsWrite
99-
default:
100-
panic(fmt.Sprintf("unrecognized op %v\n", o))
94+
func (o OpKind) IsRead() bool {
95+
if o < 0 || o >= numOpKinds {
96+
panic(fmt.Sprintf("invalid op kind: %d", o))
10197
}
98+
return ReadOps.Contains(o)
10299
}
103100

104-
// OpReadWrite is an enum describing whether an operation is a read or write
105-
// operation.
106-
type OpReadWrite int
101+
func (o OpKind) IsWrite() bool {
102+
if o < 0 || o >= numOpKinds {
103+
panic(fmt.Sprintf("invalid op kind: %d", o))
104+
}
105+
return WriteOps.Contains(o)
106+
}
107107

108-
const (
109-
// OpIsRead describes read operations.
110-
OpIsRead OpReadWrite = iota
111-
// OpIsWrite describes write operations.
112-
OpIsWrite
113-
)
108+
// OpKinds represents a set of OpKind values.
109+
type OpKinds uint64
114110

115-
// String implements fmt.Stringer.
116-
func (kind OpReadWrite) String() string {
117-
switch kind {
118-
case OpIsRead:
119-
return "Reads"
120-
case OpIsWrite:
121-
return "Writes"
122-
default:
123-
panic(fmt.Sprintf("unrecognized OpKind %d", kind))
111+
func MakeOpKinds(kinds ...OpKind) OpKinds {
112+
var res OpKinds
113+
for _, kind := range kinds {
114+
res |= OpKinds(1) << kind
115+
}
116+
return res
117+
}
118+
119+
func (k OpKinds) Minus(kind ...OpKind) OpKinds {
120+
return k &^ MakeOpKinds(kind...)
121+
}
122+
123+
func (k OpKinds) Contains(kind OpKind) bool {
124+
return k&(OpKinds(1)<<kind) != 0
125+
}
126+
127+
var ReadOps = MakeOpKinds(OpOpen, OpOpenDir, OpList, OpStat, OpGetDiskUsage, OpFileRead, OpFileReadAt, OpFileStat)
128+
var WriteOps = MakeOpKinds(OpCreate, OpLink, OpRemove, OpRemoveAll, OpRename, OpReuseForWrite, OpMkdirAll, OpLock, OpFileClose, OpFileWrite, OpFileWriteAt, OpFileSync, OpFileSyncData, OpFileSyncTo, OpFileFlush, OpFilePreallocate)
129+
130+
func init() {
131+
if ReadOps&WriteOps != 0 {
132+
panic("some op is both read and write")
133+
}
134+
if ReadOps|WriteOps != (OpKinds(1)<<numOpKinds - 1) {
135+
panic("some op is neither read nor write")
124136
}
125137
}
126138

0 commit comments

Comments
 (0)