Skip to content

Commit 954a373

Browse files
committed
Exclude known edge cases from fuzz tests
Signed-off-by: 🌲 Harry 🌊 John 🏔 <[email protected]>
1 parent ad0ee3d commit 954a373

File tree

2 files changed

+107
-19
lines changed

2 files changed

+107
-19
lines changed

engine/engine_test.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/thanos-io/promql-engine/engine"
2525
"github.com/thanos-io/promql-engine/execution/model"
2626
"github.com/thanos-io/promql-engine/execution/warnings"
27+
"github.com/thanos-io/promql-engine/extlabels"
2728
"github.com/thanos-io/promql-engine/logicalplan"
2829
"github.com/thanos-io/promql-engine/query"
2930
"github.com/thanos-io/promql-engine/storage/prometheus"
@@ -6049,10 +6050,23 @@ var (
60496050
return l.Hash() == r.Hash()
60506051
}
60516052

6052-
if x.Err != nil && y.Err != nil {
6053-
return cmp.Equal(x.Err.Error(), y.Err.Error())
6054-
} else if x.Err != nil || y.Err != nil {
6055-
return false
6053+
compareErrors := func(l, r error) (stop bool, result bool) {
6054+
if l == nil && r == nil {
6055+
return false, true
6056+
}
6057+
if l != nil && r != nil {
6058+
return true, l.Error() == r.Error()
6059+
}
6060+
err := l
6061+
if err == nil {
6062+
err = r
6063+
}
6064+
// Thanos engine handles duplicate label check differently than Prometheus engine.
6065+
return true, err.Error() == extlabels.ErrDuplicateLabelSet.Error()
6066+
}
6067+
6068+
if stop, result := compareErrors(x.Err, y.Err); stop {
6069+
return result
60566070
}
60576071

60586072
if !compareAnnotations(x.Warnings, y.Warnings) {

engine/enginefuzz_test.go

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,28 @@ type testCase struct {
3939
validateSamples bool
4040
}
4141

42+
type testType int
43+
44+
const (
45+
testTypeFloat testType = iota // 0
46+
testTypeNativeHistogram // 1
47+
)
48+
4249
// shouldValidateSamples checks if the samples can be compared for the expr.
43-
// For certain known cases, prometheus engine and thanos engine returns different samples.
50+
// For certain known cases, Thanos engine returns less samples than Prometheus engine due to optimizations.
4451
func shouldValidateSamples(expr parser.Expr) bool {
4552
valid := true
4653

4754
parser.Inspect(expr, func(node parser.Node, path []parser.Node) error {
4855
switch n := node.(type) {
4956
case *parser.Call:
50-
if n.Func.Name == "scalar" {
57+
switch n.Func.Name {
58+
case "scalar":
59+
// Optimized to step_invariant in Thanos engine.
60+
valid = false
61+
return errors.New("error")
62+
case "histogram_count", "histogram_sum", "histogram_avg":
63+
// Optimized using DetectHistogramStatsOptimizer and will return smaller samples than Prometheus engine.
5164
valid = false
5265
return errors.New("error")
5366
}
@@ -57,6 +70,55 @@ func shouldValidateSamples(expr parser.Expr) bool {
5770
return valid
5871
}
5972

73+
// validateExpr checks if the given expression is valid for fuzz tests.
74+
// For certain known cases Thanos engine results do not match with Prometheus engine.
75+
func validateExpr(expr parser.Expr, testType testType) bool {
76+
expr = promql.PreprocessExpr(expr, time.Unix(0, 0), time.Unix(0, 0))
77+
valid := true
78+
79+
parser.Inspect(expr, func(node parser.Node, path []parser.Node) error {
80+
switch n := node.(type) {
81+
case *parser.Call:
82+
switch n.Func.Name {
83+
case "sort", "sort_desc", "sort_by_label", "sort_by_label_desc":
84+
if testType == testTypeNativeHistogram {
85+
// Prometheus engine filters out native histograms in nested sort().
86+
// Thanos engine implements sorting only at the presentation time and ignores nested sort().
87+
// See: https://github.com/thanos-io/promql-engine/pull/595
88+
valid = false
89+
return errors.New("error")
90+
}
91+
case "predict_linear":
92+
switch t := n.Args[0].(type) {
93+
case *parser.StepInvariantExpr:
94+
// Thanos engine cannot correctly handle a MatrixSelector wrapped by StepInvariant.
95+
// eg: predict_linear({__name__="http_request_duration_seconds"}[5m] @ end(), 0.5)
96+
// See: https://github.com/thanos-io/promql-engine/pull/527
97+
if _, ok := t.Expr.(*parser.MatrixSelector); ok {
98+
valid = false
99+
return errors.New("error")
100+
}
101+
}
102+
case "timestamp":
103+
if testType == testTypeNativeHistogram {
104+
// TODO(johrry): Remove after merging https://github.com/thanos-io/promql-engine/pull/598
105+
valid = false
106+
return errors.New("error")
107+
}
108+
case "last_over_time":
109+
if testType == testTypeNativeHistogram {
110+
// Upstream prometheus bug: https://github.com/prometheus/prometheus/pull/16744
111+
// TODO(johrry): Remove after pulling in latest Prometheus
112+
valid = false
113+
return errors.New("error")
114+
}
115+
}
116+
}
117+
return nil
118+
})
119+
return valid
120+
}
121+
60122
func FuzzEnginePromQLSmithRangeQuery(f *testing.F) {
61123
f.Add(int64(0), uint32(0), uint32(120), uint32(30), 1.0, 1.0, 1.0, 2.0, 30)
62124

@@ -114,6 +176,9 @@ func FuzzEnginePromQLSmithRangeQuery(f *testing.F) {
114176
for i := range testRuns {
115177
for {
116178
expr := ps.WalkRangeQuery()
179+
if !validateExpr(expr, testTypeFloat) {
180+
continue
181+
}
117182
validateSamples = shouldValidateSamples(expr)
118183

119184
query = expr.Pretty(0)
@@ -200,8 +265,9 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
200265
ps := promqlsmith.New(rnd, seriesSet, psOpts...)
201266

202267
var (
203-
q1 promql.Query
204-
query string
268+
q1 promql.Query
269+
query string
270+
validateSamples bool
205271
)
206272
cases := make([]*testCase, testRuns)
207273
for i := range testRuns {
@@ -210,9 +276,10 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
210276
// Parsing experimental function, like mad_over_time, will lead to a parser.ParseErrors, so we also ignore those.
211277
for {
212278
expr := ps.WalkInstantQuery()
213-
if !shouldValidateSamples(expr) {
279+
if !validateExpr(expr, testTypeFloat) {
214280
continue
215281
}
282+
validateSamples = shouldValidateSamples(expr)
216283
query = expr.Pretty(0)
217284
q1, err = newEngine.NewInstantQuery(context.Background(), storage, qOpts, query, queryTime)
218285
if engine.IsUnimplemented(err) || errors.As(err, &parser.ParseErrors{}) {
@@ -243,7 +310,7 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
243310
loads: []string{load},
244311
start: queryTime,
245312
end: queryTime,
246-
validateSamples: true,
313+
validateSamples: validateSamples,
247314
}
248315
}
249316
validateTestCases(t, cases)
@@ -308,7 +375,6 @@ func getMaxNativeHistogramSumLimit(schema int8, noOfBuckets int) float64 {
308375
}
309376

310377
func FuzzNativeHistogramQuery(f *testing.F) {
311-
f.Skip()
312378
f.Add(int64(0), uint32(0), uint32(60), uint32(120), int8(0), int8(0), uint64(1), uint64(2), uint64(1))
313379

314380
f.Fuzz(func(t *testing.T, seed int64, startTS, endTS, intervalSeconds uint32, schema1 int8, schema2 int8, bucketValue1, bucketValue2, bucketValue3 uint64) {
@@ -396,17 +462,21 @@ func FuzzNativeHistogramQuery(f *testing.F) {
396462

397463
for range testRuns / 2 {
398464
var (
399-
qInstant promql.Query
400-
qRange promql.Query
401-
instantQuery string
402-
rangeQuery string
465+
qInstant promql.Query
466+
qRange promql.Query
467+
instantQuery string
468+
rangeQuery string
469+
validateSamplesForInstantQuery bool
470+
validateSamplesForRangeQuery bool
403471
)
404472

405473
for {
406474
expr := ps.WalkInstantQuery()
407-
if !shouldValidateSamples(expr) {
475+
if !validateExpr(expr, testTypeNativeHistogram) {
408476
continue
409477
}
478+
479+
validateSamplesForInstantQuery = shouldValidateSamples(expr)
410480
instantQuery = expr.Pretty(0)
411481

412482
qInstant, err = newEngine.NewInstantQuery(context.Background(), storage, qOpts, instantQuery, startTime)
@@ -420,9 +490,11 @@ func FuzzNativeHistogramQuery(f *testing.F) {
420490

421491
for {
422492
expr := ps.WalkRangeQuery()
423-
if !shouldValidateSamples(expr) {
493+
if !validateExpr(expr, testTypeNativeHistogram) {
424494
continue
425495
}
496+
497+
validateSamplesForRangeQuery = shouldValidateSamples(expr)
426498
rangeQuery = expr.Pretty(0)
427499

428500
qRange, err = newEngine.NewRangeQuery(context.Background(), storage, qOpts, rangeQuery, startTime, endTime, interval)
@@ -463,7 +535,8 @@ func FuzzNativeHistogramQuery(f *testing.F) {
463535
loads: []string{load},
464536
start: startTime,
465537
end: startTime,
466-
validateSamples: true,
538+
interval: 0,
539+
validateSamples: validateSamplesForInstantQuery,
467540
})
468541

469542
rangeCases = append(rangeCases, &testCase{
@@ -475,7 +548,8 @@ func FuzzNativeHistogramQuery(f *testing.F) {
475548
loads: []string{load},
476549
start: startTime,
477550
end: endTime,
478-
validateSamples: true,
551+
interval: interval,
552+
validateSamples: validateSamplesForRangeQuery,
479553
})
480554
}
481555

0 commit comments

Comments
 (0)