promql: allow tests to be imported (#12050)

Signed-off-by: Michael Hoffmann <mhoffm@posteo.de>
pull/12586/merge
Michael Hoffmann 1 year ago committed by GitHub
parent 214d6434c9
commit 4d8e380269
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -25,8 +25,6 @@ import (
"github.com/go-kit/log"
"github.com/prometheus/prometheus/tsdb/tsdbutil"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
@ -35,7 +33,9 @@ import (
"github.com/prometheus/prometheus/model/timestamp"
"github.com/prometheus/prometheus/promql/parser"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/tsdb/tsdbutil"
"github.com/prometheus/prometheus/util/stats"
"github.com/prometheus/prometheus/util/teststorage"
)
func TestMain(m *testing.M) {
@ -566,6 +566,7 @@ func TestSelectHintsSetCorrectly(t *testing.T) {
err error
)
ctx := context.Background()
if tc.end == 0 {
query, err = engine.NewInstantQuery(ctx, hintsRecorder, nil, tc.query, timestamp.Time(tc.start))
} else {
@ -573,7 +574,7 @@ func TestSelectHintsSetCorrectly(t *testing.T) {
}
require.NoError(t, err)
res := query.Exec(ctx)
res := query.Exec(context.Background())
require.NoError(t, res.Err)
require.Equal(t, tc.expected, hintsRecorder.hints)
@ -636,15 +637,11 @@ func TestEngineShutdown(t *testing.T) {
}
func TestEngineEvalStmtTimestamps(t *testing.T) {
test, err := NewTest(t, `
storage := LoadedStorage(t, `
load 10s
metric 1 2
`)
require.NoError(t, err)
defer test.Close()
err = test.Run()
require.NoError(t, err)
t.Cleanup(func() { storage.Close() })
cases := []struct {
Query string
@ -728,14 +725,15 @@ load 10s
t.Run(fmt.Sprintf("%d query=%s", i, c.Query), func(t *testing.T) {
var err error
var qry Query
engine := newTestEngine()
if c.Interval == 0 {
qry, err = test.QueryEngine().NewInstantQuery(test.context, test.Queryable(), nil, c.Query, c.Start)
qry, err = engine.NewInstantQuery(context.Background(), storage, nil, c.Query, c.Start)
} else {
qry, err = test.QueryEngine().NewRangeQuery(test.context, test.Queryable(), nil, c.Query, c.Start, c.End, c.Interval)
qry, err = engine.NewRangeQuery(context.Background(), storage, nil, c.Query, c.Start, c.End, c.Interval)
}
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
if c.ShouldError {
require.Error(t, res.Err, "expected error for the query %q", c.Query)
return
@ -748,18 +746,14 @@ load 10s
}
func TestQueryStatistics(t *testing.T) {
test, err := NewTest(t, `
storage := LoadedStorage(t, `
load 10s
metricWith1SampleEvery10Seconds 1+1x100
metricWith3SampleEvery10Seconds{a="1",b="1"} 1+1x100
metricWith3SampleEvery10Seconds{a="2",b="2"} 1+1x100
metricWith3SampleEvery10Seconds{a="3",b="2"} 1+1x100
`)
require.NoError(t, err)
defer test.Close()
err = test.Run()
require.NoError(t, err)
t.Cleanup(func() { storage.Close() })
cases := []struct {
Query string
@ -1194,7 +1188,7 @@ load 10s
},
}
engine := test.QueryEngine()
engine := newTestEngine()
engine.enablePerStepStats = true
origMaxSamples := engine.maxSamplesPerQuery
for _, c := range cases {
@ -1206,13 +1200,13 @@ load 10s
var err error
var qry Query
if c.Interval == 0 {
qry, err = engine.NewInstantQuery(test.context, test.Queryable(), opts, c.Query, c.Start)
qry, err = engine.NewInstantQuery(context.Background(), storage, opts, c.Query, c.Start)
} else {
qry, err = engine.NewRangeQuery(test.context, test.Queryable(), opts, c.Query, c.Start, c.End, c.Interval)
qry, err = engine.NewRangeQuery(context.Background(), storage, opts, c.Query, c.Start, c.End, c.Interval)
}
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.Equal(t, expErr, res.Err)
return qry.Stats()
@ -1234,17 +1228,13 @@ load 10s
}
func TestMaxQuerySamples(t *testing.T) {
test, err := NewTest(t, `
storage := LoadedStorage(t, `
load 10s
metric 1+1x100
bigmetric{a="1"} 1+1x100
bigmetric{a="2"} 1+1x100
`)
require.NoError(t, err)
defer test.Close()
err = test.Run()
require.NoError(t, err)
t.Cleanup(func() { storage.Close() })
// These test cases should be touching the limit exactly (hence no exceeding).
// Exceeding the limit will be tested by doing -1 to the MaxSamples.
@ -1382,20 +1372,20 @@ load 10s
},
}
engine := test.QueryEngine()
for _, c := range cases {
t.Run(c.Query, func(t *testing.T) {
engine := newTestEngine()
testFunc := func(expError error) {
var err error
var qry Query
if c.Interval == 0 {
qry, err = engine.NewInstantQuery(test.context, test.Queryable(), nil, c.Query, c.Start)
qry, err = engine.NewInstantQuery(context.Background(), storage, nil, c.Query, c.Start)
} else {
qry, err = engine.NewRangeQuery(test.context, test.Queryable(), nil, c.Query, c.Start, c.End, c.Interval)
qry, err = engine.NewRangeQuery(context.Background(), storage, nil, c.Query, c.Start, c.End, c.Interval)
}
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
stats := qry.Stats()
require.Equal(t, expError, res.Err)
require.NotNil(t, stats)
@ -1416,7 +1406,8 @@ load 10s
}
func TestAtModifier(t *testing.T) {
test, err := NewTest(t, `
engine := newTestEngine()
storage := LoadedStorage(t, `
load 10s
metric{job="1"} 0+1x1000
metric{job="2"} 0+2x1000
@ -1427,11 +1418,7 @@ load 10s
load 1ms
metric_ms 0+1x10000
`)
require.NoError(t, err)
defer test.Close()
err = test.Run()
require.NoError(t, err)
t.Cleanup(func() { storage.Close() })
lbls1 := labels.FromStrings("__name__", "metric", "job", "1")
lbls2 := labels.FromStrings("__name__", "metric", "job", "2")
@ -1441,7 +1428,7 @@ load 1ms
lblsneg := labels.FromStrings("__name__", "metric_neg")
// Add some samples with negative timestamp.
db := test.TSDB()
db := storage.DB
app := db.Appender(context.Background())
ref, err := app.Append(0, lblsneg, -1000000, 1000)
require.NoError(t, err)
@ -1630,13 +1617,13 @@ load 1ms
var err error
var qry Query
if c.end == 0 {
qry, err = test.QueryEngine().NewInstantQuery(test.context, test.Queryable(), nil, c.query, start)
qry, err = engine.NewInstantQuery(context.Background(), storage, nil, c.query, start)
} else {
qry, err = test.QueryEngine().NewRangeQuery(test.context, test.Queryable(), nil, c.query, start, end, interval)
qry, err = engine.NewRangeQuery(context.Background(), storage, nil, c.query, start, end, interval)
}
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
if expMat, ok := c.result.(Matrix); ok {
sort.Sort(expMat)
@ -1955,18 +1942,16 @@ func TestSubquerySelector(t *testing.T) {
},
} {
t.Run("", func(t *testing.T) {
test, err := NewTest(t, tst.loadString)
require.NoError(t, err)
defer test.Close()
engine := newTestEngine()
storage := LoadedStorage(t, tst.loadString)
t.Cleanup(func() { storage.Close() })
require.NoError(t, test.Run())
engine := test.QueryEngine()
for _, c := range tst.cases {
t.Run(c.Query, func(t *testing.T) {
qry, err := engine.NewInstantQuery(test.context, test.Queryable(), nil, c.Query, c.Start)
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, c.Query, c.Start)
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.Equal(t, c.Result.Err, res.Err)
mat := res.Value.(Matrix)
sort.Sort(mat)
@ -1978,15 +1963,12 @@ func TestSubquerySelector(t *testing.T) {
}
func TestTimestampFunction_StepsMoreOftenThanSamples(t *testing.T) {
test, err := NewTest(t, `
engine := newTestEngine()
storage := LoadedStorage(t, `
load 1m
metric 0+1x1000
`)
require.NoError(t, err)
defer test.Close()
err = test.Run()
require.NoError(t, err)
t.Cleanup(func() { storage.Close() })
query := "timestamp(metric)"
start := time.Unix(0, 0)
@ -2013,10 +1995,10 @@ load 1m
},
}
qry, err := test.QueryEngine().NewRangeQuery(test.context, test.Queryable(), nil, query, start, end, interval)
qry, err := engine.NewRangeQuery(context.Background(), storage, nil, query, start, end, interval)
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
require.Equal(t, expectedResult, res.Value)
}
@ -2955,7 +2937,6 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) {
}
func TestEngineOptsValidation(t *testing.T) {
ctx := context.Background()
cases := []struct {
opts EngineOpts
query string
@ -3015,8 +2996,8 @@ func TestEngineOptsValidation(t *testing.T) {
for _, c := range cases {
eng := NewEngine(c.opts)
_, err1 := eng.NewInstantQuery(ctx, nil, nil, c.query, time.Unix(10, 0))
_, err2 := eng.NewRangeQuery(ctx, nil, nil, c.query, time.Unix(0, 0), time.Unix(10, 0), time.Second)
_, err1 := eng.NewInstantQuery(context.Background(), nil, nil, c.query, time.Unix(10, 0))
_, err2 := eng.NewRangeQuery(context.Background(), nil, nil, c.query, time.Unix(0, 0), time.Unix(10, 0), time.Second)
if c.fail {
require.Equal(t, c.expError, err1)
require.Equal(t, c.expError, err2)
@ -3156,17 +3137,14 @@ func TestRangeQuery(t *testing.T) {
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
test, err := NewTest(t, c.Load)
require.NoError(t, err)
defer test.Close()
engine := newTestEngine()
storage := LoadedStorage(t, c.Load)
t.Cleanup(func() { storage.Close() })
err = test.Run()
qry, err := engine.NewRangeQuery(context.Background(), storage, nil, c.Query, c.Start, c.End, c.Interval)
require.NoError(t, err)
qry, err := test.QueryEngine().NewRangeQuery(test.context, test.Queryable(), nil, c.Query, c.Start, c.End, c.Interval)
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
require.Equal(t, c.Result, res.Value)
})
@ -3176,27 +3154,24 @@ func TestRangeQuery(t *testing.T) {
func TestNativeHistogramRate(t *testing.T) {
// TODO(beorn7): Integrate histograms into the PromQL testing framework
// and write more tests there.
test, err := NewTest(t, "")
require.NoError(t, err)
defer test.Close()
engine := newTestEngine()
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
seriesName := "sparse_histogram_series"
lbls := labels.FromStrings("__name__", seriesName)
app := test.Storage().Appender(context.TODO())
app := storage.Appender(context.Background())
for i, h := range tsdbutil.GenerateTestHistograms(100) {
_, err := app.AppendHistogram(0, lbls, int64(i)*int64(15*time.Second/time.Millisecond), h, nil)
require.NoError(t, err)
}
require.NoError(t, app.Commit())
require.NoError(t, test.Run())
engine := test.QueryEngine()
queryString := fmt.Sprintf("rate(%s[1m])", seriesName)
qry, err := engine.NewInstantQuery(test.context, test.Queryable(), nil, queryString, timestamp.Time(int64(5*time.Minute/time.Millisecond)))
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(int64(5*time.Minute/time.Millisecond)))
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
vector, err := res.Vector()
require.NoError(t, err)
@ -3220,27 +3195,24 @@ func TestNativeHistogramRate(t *testing.T) {
func TestNativeFloatHistogramRate(t *testing.T) {
// TODO(beorn7): Integrate histograms into the PromQL testing framework
// and write more tests there.
test, err := NewTest(t, "")
require.NoError(t, err)
defer test.Close()
engine := newTestEngine()
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
seriesName := "sparse_histogram_series"
lbls := labels.FromStrings("__name__", seriesName)
app := test.Storage().Appender(context.TODO())
app := storage.Appender(context.Background())
for i, fh := range tsdbutil.GenerateTestFloatHistograms(100) {
_, err := app.AppendHistogram(0, lbls, int64(i)*int64(15*time.Second/time.Millisecond), nil, fh)
require.NoError(t, err)
}
require.NoError(t, app.Commit())
require.NoError(t, test.Run())
engine := test.QueryEngine()
queryString := fmt.Sprintf("rate(%s[1m])", seriesName)
qry, err := engine.NewInstantQuery(test.context, test.Queryable(), nil, queryString, timestamp.Time(int64(5*time.Minute/time.Millisecond)))
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(int64(5*time.Minute/time.Millisecond)))
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
vector, err := res.Vector()
require.NoError(t, err)
@ -3283,16 +3255,16 @@ func TestNativeHistogram_HistogramCountAndSum(t *testing.T) {
}
for _, floatHisto := range []bool{true, false} {
t.Run(fmt.Sprintf("floatHistogram=%t", floatHisto), func(t *testing.T) {
test, err := NewTest(t, "")
require.NoError(t, err)
t.Cleanup(test.Close)
engine := newTestEngine()
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
seriesName := "sparse_histogram_series"
lbls := labels.FromStrings("__name__", seriesName)
engine := test.QueryEngine()
ts := int64(10 * time.Minute / time.Millisecond)
app := test.Storage().Appender(context.TODO())
app := storage.Appender(context.Background())
var err error
if floatHisto {
_, err = app.AppendHistogram(0, lbls, ts, nil, h.ToFloat())
} else {
@ -3302,10 +3274,10 @@ func TestNativeHistogram_HistogramCountAndSum(t *testing.T) {
require.NoError(t, app.Commit())
queryString := fmt.Sprintf("histogram_count(%s)", seriesName)
qry, err := engine.NewInstantQuery(test.context, test.Queryable(), nil, queryString, timestamp.Time(ts))
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(ts))
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
vector, err := res.Vector()
@ -3320,10 +3292,10 @@ func TestNativeHistogram_HistogramCountAndSum(t *testing.T) {
}
queryString = fmt.Sprintf("histogram_sum(%s)", seriesName)
qry, err = engine.NewInstantQuery(test.context, test.Queryable(), nil, queryString, timestamp.Time(ts))
qry, err = engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(ts))
require.NoError(t, err)
res = qry.Exec(test.Context())
res = qry.Exec(context.Background())
require.NoError(t, res.Err)
vector, err = res.Vector()
@ -3533,18 +3505,18 @@ func TestNativeHistogram_HistogramQuantile(t *testing.T) {
},
}
test, err := NewTest(t, "")
require.NoError(t, err)
t.Cleanup(test.Close)
engine := newTestEngine()
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
idx := int64(0)
for _, floatHisto := range []bool{true, false} {
for _, c := range cases {
t.Run(fmt.Sprintf("%s floatHistogram=%t", c.text, floatHisto), func(t *testing.T) {
seriesName := "sparse_histogram_series"
lbls := labels.FromStrings("__name__", seriesName)
engine := test.QueryEngine()
ts := idx * int64(10*time.Minute/time.Millisecond)
app := test.Storage().Appender(context.TODO())
app := storage.Appender(context.Background())
var err error
if floatHisto {
_, err = app.AppendHistogram(0, lbls, ts, nil, c.h.ToFloat())
} else {
@ -3556,10 +3528,10 @@ func TestNativeHistogram_HistogramQuantile(t *testing.T) {
for j, sc := range c.subCases {
t.Run(fmt.Sprintf("%d %s", j, sc.quantile), func(t *testing.T) {
queryString := fmt.Sprintf("histogram_quantile(%s, %s)", sc.quantile, seriesName)
qry, err := engine.NewInstantQuery(test.context, test.Queryable(), nil, queryString, timestamp.Time(ts))
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(ts))
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
vector, err := res.Vector()
@ -3966,16 +3938,16 @@ func TestNativeHistogram_HistogramFraction(t *testing.T) {
for _, floatHisto := range []bool{true, false} {
for _, c := range cases {
t.Run(fmt.Sprintf("%s floatHistogram=%t", c.text, floatHisto), func(t *testing.T) {
test, err := NewTest(t, "")
require.NoError(t, err)
t.Cleanup(test.Close)
engine := newTestEngine()
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
seriesName := "sparse_histogram_series"
lbls := labels.FromStrings("__name__", seriesName)
engine := test.QueryEngine()
ts := idx * int64(10*time.Minute/time.Millisecond)
app := test.Storage().Appender(context.TODO())
app := storage.Appender(context.Background())
var err error
if floatHisto {
_, err = app.AppendHistogram(0, lbls, ts, nil, c.h.ToFloat())
} else {
@ -3987,10 +3959,10 @@ func TestNativeHistogram_HistogramFraction(t *testing.T) {
for j, sc := range c.subCases {
t.Run(fmt.Sprintf("%d %s %s", j, sc.lower, sc.upper), func(t *testing.T) {
queryString := fmt.Sprintf("histogram_fraction(%s, %s, %s)", sc.lower, sc.upper, seriesName)
qry, err := engine.NewInstantQuery(test.context, test.Queryable(), nil, queryString, timestamp.Time(ts))
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(ts))
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
vector, err := res.Vector()
@ -4125,20 +4097,20 @@ func TestNativeHistogram_Sum_Count_Add_AvgOperator(t *testing.T) {
for _, c := range cases {
for _, floatHisto := range []bool{true, false} {
t.Run(fmt.Sprintf("floatHistogram=%t %d", floatHisto, idx0), func(t *testing.T) {
test, err := NewTest(t, "")
require.NoError(t, err)
t.Cleanup(test.Close)
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
seriesName := "sparse_histogram_series"
seriesNameOverTime := "sparse_histogram_series_over_time"
engine := test.QueryEngine()
engine := newTestEngine()
ts := idx0 * int64(10*time.Minute/time.Millisecond)
app := test.Storage().Appender(context.TODO())
app := storage.Appender(context.Background())
for idx1, h := range c.histograms {
lbls := labels.FromStrings("__name__", seriesName, "idx", fmt.Sprintf("%d", idx1))
// Since we mutate h later, we need to create a copy here.
var err error
if floatHisto {
_, err = app.AppendHistogram(0, lbls, ts, nil, h.Copy().ToFloat())
} else {
@ -4159,10 +4131,10 @@ func TestNativeHistogram_Sum_Count_Add_AvgOperator(t *testing.T) {
require.NoError(t, app.Commit())
queryAndCheck := func(queryString string, ts int64, exp Vector) {
qry, err := engine.NewInstantQuery(test.context, test.Queryable(), nil, queryString, timestamp.Time(ts))
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(ts))
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
vector, err := res.Vector()
@ -4385,19 +4357,18 @@ func TestNativeHistogram_SubOperator(t *testing.T) {
for _, c := range cases {
for _, floatHisto := range []bool{true, false} {
t.Run(fmt.Sprintf("floatHistogram=%t %d", floatHisto, idx0), func(t *testing.T) {
test, err := NewTest(t, "")
require.NoError(t, err)
t.Cleanup(test.Close)
engine := newTestEngine()
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
seriesName := "sparse_histogram_series"
engine := test.QueryEngine()
ts := idx0 * int64(10*time.Minute/time.Millisecond)
app := test.Storage().Appender(context.TODO())
app := storage.Appender(context.Background())
for idx1, h := range c.histograms {
lbls := labels.FromStrings("__name__", seriesName, "idx", fmt.Sprintf("%d", idx1))
// Since we mutate h later, we need to create a copy here.
var err error
if floatHisto {
_, err = app.AppendHistogram(0, lbls, ts, nil, h.Copy().ToFloat())
} else {
@ -4408,10 +4379,10 @@ func TestNativeHistogram_SubOperator(t *testing.T) {
require.NoError(t, app.Commit())
queryAndCheck := func(queryString string, exp Vector) {
qry, err := engine.NewInstantQuery(test.context, test.Queryable(), nil, queryString, timestamp.Time(ts))
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(ts))
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
vector, err := res.Vector()
@ -4531,20 +4502,20 @@ func TestNativeHistogram_MulDivOperator(t *testing.T) {
for _, c := range cases {
for _, floatHisto := range []bool{true, false} {
t.Run(fmt.Sprintf("floatHistogram=%t %d", floatHisto, idx0), func(t *testing.T) {
test, err := NewTest(t, "")
require.NoError(t, err)
t.Cleanup(test.Close)
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
seriesName := "sparse_histogram_series"
floatSeriesName := "float_series"
engine := test.QueryEngine()
engine := newTestEngine()
ts := idx0 * int64(10*time.Minute/time.Millisecond)
app := test.Storage().Appender(context.TODO())
app := storage.Appender(context.Background())
h := c.histogram
lbls := labels.FromStrings("__name__", seriesName)
// Since we mutate h later, we need to create a copy here.
var err error
if floatHisto {
_, err = app.AppendHistogram(0, lbls, ts, nil, h.Copy().ToFloat())
} else {
@ -4556,10 +4527,10 @@ func TestNativeHistogram_MulDivOperator(t *testing.T) {
require.NoError(t, app.Commit())
queryAndCheck := func(queryString string, exp Vector) {
qry, err := engine.NewInstantQuery(test.context, test.Queryable(), nil, queryString, timestamp.Time(ts))
qry, err := engine.NewInstantQuery(context.Background(), storage, nil, queryString, timestamp.Time(ts))
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
vector, err := res.Vector()
@ -4660,22 +4631,18 @@ metric 0 1 2
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
test, err := NewTest(t, load)
require.NoError(t, err)
defer test.Close()
err = test.Run()
require.NoError(t, err)
engine := newTestEngine()
storage := LoadedStorage(t, load)
t.Cleanup(func() { storage.Close() })
eng := test.QueryEngine()
if c.engineLookback != 0 {
eng.lookbackDelta = c.engineLookback
engine.lookbackDelta = c.engineLookback
}
opts := NewPrometheusQueryOpts(false, c.queryLookback)
qry, err := eng.NewInstantQuery(test.context, test.Queryable(), opts, query, c.ts)
qry, err := engine.NewInstantQuery(context.Background(), storage, opts, query, c.ts)
require.NoError(t, err)
res := qry.Exec(test.Context())
res := qry.Exec(context.Background())
require.NoError(t, res.Err)
vec, ok := res.Value.(Vector)
require.True(t, ok)

@ -15,7 +15,6 @@ package promql
import (
"context"
"path/filepath"
"strings"
"testing"
"time"
@ -26,19 +25,21 @@ import (
"github.com/prometheus/prometheus/util/teststorage"
)
func TestEvaluations(t *testing.T) {
files, err := filepath.Glob("testdata/*.test")
require.NoError(t, err)
for _, fn := range files {
t.Run(fn, func(t *testing.T) {
test, err := newTestFromFile(t, fn)
require.NoError(t, err)
require.NoError(t, test.Run())
func newTestEngine() *Engine {
return NewEngine(EngineOpts{
Logger: nil,
Reg: nil,
MaxSamples: 10000,
Timeout: 100 * time.Second,
NoStepSubqueryIntervalFn: func(int64) int64 { return durationMilliseconds(1 * time.Minute) },
EnableAtModifier: true,
EnableNegativeOffset: true,
EnablePerStepStats: true,
})
}
test.Close()
})
}
func TestEvaluations(t *testing.T) {
RunBuiltinTests(t, newTestEngine())
}
// Run a lot of queries at the same time, to check for race conditions.

@ -15,12 +15,14 @@ package promql
import (
"context"
"embed"
"errors"
"fmt"
"io/fs"
"math"
"os"
"strconv"
"strings"
"testing"
"time"
"github.com/grafana/regexp"
@ -32,7 +34,6 @@ import (
"github.com/prometheus/prometheus/model/timestamp"
"github.com/prometheus/prometheus/promql/parser"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/tsdb"
"github.com/prometheus/prometheus/util/teststorage"
"github.com/prometheus/prometheus/util/testutil"
)
@ -51,23 +52,74 @@ const (
var testStartTime = time.Unix(0, 0).UTC()
// Test is a sequence of read and write commands that are run
// LoadedStorage returns storage with generated data using the provided load statements.
// Non-load statements will cause test errors.
func LoadedStorage(t testutil.T, input string) *teststorage.TestStorage {
test, err := newTest(t, input)
require.NoError(t, err)
for _, cmd := range test.cmds {
switch cmd.(type) {
case *loadCmd:
require.NoError(t, test.exec(cmd, nil))
default:
t.Errorf("only 'load' commands accepted, got '%s'", cmd)
}
}
return test.storage
}
// RunBuiltinTests runs an acceptance test suite against the provided engine.
func RunBuiltinTests(t *testing.T, engine engineQuerier) {
files, err := fs.Glob(testsFs, "*/*.test")
require.NoError(t, err)
for _, fn := range files {
t.Run(fn, func(t *testing.T) {
content, err := fs.ReadFile(testsFs, fn)
require.NoError(t, err)
RunTest(t, string(content), engine)
})
}
}
// RunTest parses and runs the test against the provided engine.
func RunTest(t testutil.T, input string, engine engineQuerier) {
test, err := newTest(t, input)
require.NoError(t, err)
defer func() {
if test.storage != nil {
test.storage.Close()
}
if test.cancelCtx != nil {
test.cancelCtx()
}
}()
for _, cmd := range test.cmds {
// TODO(fabxc): aggregate command errors, yield diffs for result
// comparison errors.
require.NoError(t, test.exec(cmd, engine))
}
}
// test is a sequence of read and write commands that are run
// against a test storage.
type Test struct {
type test struct {
testutil.T
cmds []testCommand
storage *teststorage.TestStorage
queryEngine *Engine
context context.Context
cancelCtx context.CancelFunc
context context.Context
cancelCtx context.CancelFunc
}
// NewTest returns an initialized empty Test.
func NewTest(t testutil.T, input string) (*Test, error) {
test := &Test{
// newTest returns an initialized empty Test.
func newTest(t testutil.T, input string) (*test, error) {
test := &test{
T: t,
cmds: []testCommand{},
}
@ -77,46 +129,12 @@ func NewTest(t testutil.T, input string) (*Test, error) {
return test, err
}
func newTestFromFile(t testutil.T, filename string) (*Test, error) {
content, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
return NewTest(t, string(content))
}
// QueryEngine returns the test's query engine.
func (t *Test) QueryEngine() *Engine {
return t.queryEngine
}
// Queryable allows querying the test data.
func (t *Test) Queryable() storage.Queryable {
return t.storage
}
// Context returns the test's context.
func (t *Test) Context() context.Context {
return t.context
}
// Storage returns the test's storage.
func (t *Test) Storage() storage.Storage {
return t.storage
}
// TSDB returns test's TSDB.
func (t *Test) TSDB() *tsdb.DB {
return t.storage.DB
}
//go:embed testdata
var testsFs embed.FS
// ExemplarStorage returns the test's exemplar storage.
func (t *Test) ExemplarStorage() storage.ExemplarStorage {
return t.storage
}
func (t *Test) ExemplarQueryable() storage.ExemplarQueryable {
return t.storage.ExemplarQueryable()
type engineQuerier interface {
NewRangeQuery(ctx context.Context, q storage.Queryable, opts QueryOpts, qs string, start, end time.Time, interval time.Duration) (Query, error)
NewInstantQuery(ctx context.Context, q storage.Queryable, opts QueryOpts, qs string, ts time.Time) (Query, error)
}
func raise(line int, format string, v ...interface{}) error {
@ -157,7 +175,7 @@ func parseLoad(lines []string, i int) (int, *loadCmd, error) {
return i, cmd, nil
}
func (t *Test) parseEval(lines []string, i int) (int, *evalCmd, error) {
func (t *test) parseEval(lines []string, i int) (int, *evalCmd, error) {
if !patEvalInstant.MatchString(lines[i]) {
return i, nil, raise(i, "invalid evaluation command. (eval[_fail|_ordered] instant [at <offset:duration>] <query>")
}
@ -237,7 +255,7 @@ func getLines(input string) []string {
}
// parse the given command sequence and appends it to the test.
func (t *Test) parse(input string) error {
func (t *test) parse(input string) error {
lines := getLines(input)
var err error
// Scan for steps line by line.
@ -433,19 +451,6 @@ func (cmd clearCmd) String() string {
return "clear"
}
// Run executes the command sequence of the test. Until the maximum error number
// is reached, evaluation errors do not terminate execution.
func (t *Test) Run() error {
for _, cmd := range t.cmds {
// TODO(fabxc): aggregate command errors, yield diffs for result
// comparison errors.
if err := t.exec(cmd); err != nil {
return err
}
}
return nil
}
type atModifierTestCase struct {
expr string
evalTime time.Time
@ -515,7 +520,7 @@ func atModifierTestCases(exprStr string, evalTime time.Time) ([]atModifierTestCa
}
// exec processes a single step of the test.
func (t *Test) exec(tc testCommand) error {
func (t *test) exec(tc testCommand, engine engineQuerier) error {
switch cmd := tc.(type) {
case *clearCmd:
t.clear()
@ -538,7 +543,7 @@ func (t *Test) exec(tc testCommand) error {
}
queries = append([]atModifierTestCase{{expr: cmd.expr, evalTime: cmd.start}}, queries...)
for _, iq := range queries {
q, err := t.QueryEngine().NewInstantQuery(t.context, t.storage, nil, iq.expr, iq.evalTime)
q, err := engine.NewInstantQuery(t.context, t.storage, nil, iq.expr, iq.evalTime)
if err != nil {
return err
}
@ -560,7 +565,7 @@ func (t *Test) exec(tc testCommand) error {
// Check query returns same result in range mode,
// by checking against the middle step.
q, err = t.queryEngine.NewRangeQuery(t.context, t.storage, nil, iq.expr, iq.evalTime.Add(-time.Minute), iq.evalTime.Add(time.Minute), time.Minute)
q, err = engine.NewRangeQuery(t.context, t.storage, nil, iq.expr, iq.evalTime.Add(-time.Minute), iq.evalTime.Add(time.Minute), time.Minute)
if err != nil {
return err
}
@ -601,7 +606,7 @@ func (t *Test) exec(tc testCommand) error {
}
// clear the current test storage of all inserted samples.
func (t *Test) clear() {
func (t *test) clear() {
if t.storage != nil {
err := t.storage.Close()
require.NoError(t.T, err, "Unexpected error while closing test storage.")
@ -610,30 +615,9 @@ func (t *Test) clear() {
t.cancelCtx()
}
t.storage = teststorage.New(t)
opts := EngineOpts{
Logger: nil,
Reg: nil,
MaxSamples: 10000,
Timeout: 100 * time.Second,
NoStepSubqueryIntervalFn: func(int64) int64 { return durationMilliseconds(1 * time.Minute) },
EnableAtModifier: true,
EnableNegativeOffset: true,
EnablePerStepStats: true,
}
t.queryEngine = NewEngine(opts)
t.context, t.cancelCtx = context.WithCancel(context.Background())
}
// Close closes resources associated with the Test.
func (t *Test) Close() {
t.cancelCtx()
err := t.storage.Close()
require.NoError(t.T, err, "Unexpected error while closing test storage.")
}
// samplesAlmostEqual returns true if the two sample lines only differ by a
// small relative error in their sample value.
func almostEqual(a, b float64) bool {

@ -33,6 +33,17 @@ import (
"github.com/prometheus/prometheus/util/teststorage"
)
var testEngine = promql.NewEngine(promql.EngineOpts{
Logger: nil,
Reg: nil,
MaxSamples: 10000,
Timeout: 100 * time.Second,
NoStepSubqueryIntervalFn: func(int64) int64 { return 60 * 1000 },
EnableAtModifier: true,
EnableNegativeOffset: true,
EnablePerStepStats: true,
})
func TestAlertingRuleState(t *testing.T) {
tests := []struct {
name string
@ -74,14 +85,11 @@ func TestAlertingRuleState(t *testing.T) {
}
func TestAlertingRuleLabelsUpdate(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
http_requests{job="app-server", instance="0"} 75 85 70 70 stale
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests < 100`)
require.NoError(t, err)
@ -158,7 +166,7 @@ func TestAlertingRuleLabelsUpdate(t *testing.T) {
t.Logf("case %d", i)
evalTime := baseTime.Add(time.Duration(i) * time.Minute)
result[0].T = timestamp.FromTime(evalTime)
res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
res, err := rule.Eval(context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
require.NoError(t, err)
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
@ -175,20 +183,17 @@ func TestAlertingRuleLabelsUpdate(t *testing.T) {
require.Equal(t, result, filteredRes)
}
evalTime := baseTime.Add(time.Duration(len(results)) * time.Minute)
res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
res, err := rule.Eval(context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
require.NoError(t, err)
require.Equal(t, 0, len(res))
}
func TestAlertingRuleExternalLabelsInTemplate(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
http_requests{job="app-server", instance="0"} 75 85 70 70
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests < 100`)
require.NoError(t, err)
@ -246,7 +251,7 @@ func TestAlertingRuleExternalLabelsInTemplate(t *testing.T) {
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
res, err := ruleWithoutExternalLabels.Eval(
suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0,
context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
)
require.NoError(t, err)
for _, smpl := range res {
@ -260,7 +265,7 @@ func TestAlertingRuleExternalLabelsInTemplate(t *testing.T) {
}
res, err = ruleWithExternalLabels.Eval(
suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0,
context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
)
require.NoError(t, err)
for _, smpl := range res {
@ -277,14 +282,11 @@ func TestAlertingRuleExternalLabelsInTemplate(t *testing.T) {
}
func TestAlertingRuleExternalURLInTemplate(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
http_requests{job="app-server", instance="0"} 75 85 70 70
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests < 100`)
require.NoError(t, err)
@ -342,7 +344,7 @@ func TestAlertingRuleExternalURLInTemplate(t *testing.T) {
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
res, err := ruleWithoutExternalURL.Eval(
suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0,
context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
)
require.NoError(t, err)
for _, smpl := range res {
@ -356,7 +358,7 @@ func TestAlertingRuleExternalURLInTemplate(t *testing.T) {
}
res, err = ruleWithExternalURL.Eval(
suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0,
context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
)
require.NoError(t, err)
for _, smpl := range res {
@ -373,14 +375,11 @@ func TestAlertingRuleExternalURLInTemplate(t *testing.T) {
}
func TestAlertingRuleEmptyLabelFromTemplate(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
http_requests{job="app-server", instance="0"} 75 85 70 70
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests < 100`)
require.NoError(t, err)
@ -414,7 +413,7 @@ func TestAlertingRuleEmptyLabelFromTemplate(t *testing.T) {
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
res, err := rule.Eval(
suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0,
context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0,
)
require.NoError(t, err)
for _, smpl := range res {
@ -430,14 +429,11 @@ func TestAlertingRuleEmptyLabelFromTemplate(t *testing.T) {
}
func TestAlertingRuleQueryInTemplate(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
http_requests{job="app-server", instance="0"} 70 85 70 70
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`sum(http_requests) < 100`)
require.NoError(t, err)
@ -473,7 +469,7 @@ instance: {{ $v.Labels.instance }}, value: {{ printf "%.0f" $v.Value }};
require.Fail(t, "unexpected blocking when template expanding.")
}
}
return EngineQueryFunc(suite.QueryEngine(), suite.Storage())(ctx, q, ts)
return EngineQueryFunc(testEngine, storage)(ctx, q, ts)
}
go func() {
<-startQueryCh
@ -484,7 +480,7 @@ instance: {{ $v.Labels.instance }}, value: {{ printf "%.0f" $v.Value }};
close(getDoneCh)
}()
_, err = ruleWithQueryInTemplate.Eval(
suite.Context(), evalTime, slowQueryFunc, nil, 0,
context.TODO(), evalTime, slowQueryFunc, nil, 0,
)
require.NoError(t, err)
}
@ -542,15 +538,12 @@ func TestAlertingRuleDuplicate(t *testing.T) {
}
func TestAlertingRuleLimit(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
metric{label="1"} 1
metric{label="2"} 1
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
tests := []struct {
limit int
@ -587,7 +580,7 @@ func TestAlertingRuleLimit(t *testing.T) {
evalTime := time.Unix(0, 0)
for _, test := range tests {
switch _, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, test.limit); {
switch _, err := rule.Eval(context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, test.limit); {
case err != nil:
require.EqualError(t, err, test.err)
case test.err != "":
@ -729,14 +722,11 @@ func TestSendAlertsDontAffectActiveAlerts(t *testing.T) {
}
func TestKeepFiringFor(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
http_requests{job="app-server", instance="0"} 75 85 70 70 10x5
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests > 50`)
require.NoError(t, err)
@ -819,7 +809,7 @@ func TestKeepFiringFor(t *testing.T) {
t.Logf("case %d", i)
evalTime := baseTime.Add(time.Duration(i) * time.Minute)
result[0].T = timestamp.FromTime(evalTime)
res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
res, err := rule.Eval(context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
require.NoError(t, err)
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
@ -836,20 +826,17 @@ func TestKeepFiringFor(t *testing.T) {
require.Equal(t, result, filteredRes)
}
evalTime := baseTime.Add(time.Duration(len(results)) * time.Minute)
res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
res, err := rule.Eval(context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
require.NoError(t, err)
require.Equal(t, 0, len(res))
}
func TestPendingAndKeepFiringFor(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
http_requests{job="app-server", instance="0"} 75 10x10
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests > 50`)
require.NoError(t, err)
@ -876,7 +863,7 @@ func TestPendingAndKeepFiringFor(t *testing.T) {
baseTime := time.Unix(0, 0)
result.T = timestamp.FromTime(baseTime)
res, err := rule.Eval(suite.Context(), baseTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
res, err := rule.Eval(context.TODO(), baseTime, EngineQueryFunc(testEngine, storage), nil, 0)
require.NoError(t, err)
require.Len(t, res, 2)
@ -891,7 +878,7 @@ func TestPendingAndKeepFiringFor(t *testing.T) {
}
evalTime := baseTime.Add(time.Minute)
res, err = rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
res, err = rule.Eval(context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
require.NoError(t, err)
require.Equal(t, 0, len(res))
}

@ -47,16 +47,12 @@ func TestMain(m *testing.M) {
}
func TestAlertingRule(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 5m
http_requests{job="app-server", instance="0", group="canary", severity="overwrite-me"} 75 85 95 105 105 95 85
http_requests{job="app-server", instance="1", group="canary", severity="overwrite-me"} 80 90 100 110 120 130 140
`)
require.NoError(t, err)
defer suite.Close()
err = suite.Run()
require.NoError(t, err)
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests{group="canary", job="app-server"} < 100`)
require.NoError(t, err)
@ -161,7 +157,7 @@ func TestAlertingRule(t *testing.T) {
evalTime := baseTime.Add(test.time)
res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
res, err := rule.Eval(context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
require.NoError(t, err)
var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
@ -191,16 +187,12 @@ func TestAlertingRule(t *testing.T) {
}
func TestForStateAddSamples(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 5m
http_requests{job="app-server", instance="0", group="canary", severity="overwrite-me"} 75 85 95 105 105 95 85
http_requests{job="app-server", instance="1", group="canary", severity="overwrite-me"} 80 90 100 110 120 130 140
`)
require.NoError(t, err)
defer suite.Close()
err = suite.Run()
require.NoError(t, err)
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests{group="canary", job="app-server"} < 100`)
require.NoError(t, err)
@ -311,7 +303,7 @@ func TestForStateAddSamples(t *testing.T) {
forState = float64(value.StaleNaN)
}
res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
res, err := rule.Eval(context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, 0)
require.NoError(t, err)
var filteredRes promql.Vector // After removing 'ALERTS' samples.
@ -353,24 +345,20 @@ func sortAlerts(items []*Alert) {
}
func TestForStateRestore(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 5m
http_requests{job="app-server", instance="0", group="canary", severity="overwrite-me"} 75 85 50 0 0 25 0 0 40 0 120
http_requests{job="app-server", instance="1", group="canary", severity="overwrite-me"} 125 90 60 0 0 25 0 0 40 0 130
`)
require.NoError(t, err)
defer suite.Close()
err = suite.Run()
require.NoError(t, err)
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests{group="canary", job="app-server"} < 100`)
require.NoError(t, err)
opts := &ManagerOptions{
QueryFunc: EngineQueryFunc(suite.QueryEngine(), suite.Storage()),
Appendable: suite.Storage(),
Queryable: suite.Storage(),
QueryFunc: EngineQueryFunc(testEngine, storage),
Appendable: storage,
Queryable: storage,
Context: context.Background(),
Logger: log.NewNopLogger(),
NotifyFunc: func(ctx context.Context, expr string, alerts ...*Alert) {},
@ -404,7 +392,7 @@ func TestForStateRestore(t *testing.T) {
baseTime := time.Unix(0, 0)
for _, duration := range initialRuns {
evalTime := baseTime.Add(duration)
group.Eval(suite.Context(), evalTime)
group.Eval(context.TODO(), evalTime)
}
exp := rule.ActiveAlerts()
@ -468,7 +456,7 @@ func TestForStateRestore(t *testing.T) {
restoreTime := baseTime.Add(tst.restoreDuration)
// First eval before restoration.
newGroup.Eval(suite.Context(), restoreTime)
newGroup.Eval(context.TODO(), restoreTime)
// Restore happens here.
newGroup.RestoreForState(restoreTime)
@ -515,7 +503,7 @@ func TestForStateRestore(t *testing.T) {
// Testing the grace period.
for _, duration := range []time.Duration{10 * time.Minute, 15 * time.Minute, 20 * time.Minute} {
evalTime := baseTime.Add(duration)
group.Eval(suite.Context(), evalTime)
group.Eval(context.TODO(), evalTime)
}
testFunc(testInput{
restoreDuration: 25 * time.Minute,
@ -1239,16 +1227,11 @@ func TestRuleHealthUpdates(t *testing.T) {
}
func TestRuleGroupEvalIterationFunc(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 5m
http_requests{instance="0"} 75 85 50 0 0 25 0 0 40 0 120
`)
require.NoError(t, err)
defer suite.Close()
err = suite.Run()
require.NoError(t, err)
t.Cleanup(func() { storage.Close() })
expr, err := parser.ParseExpr(`http_requests{group="canary", job="app-server"} < 100`)
require.NoError(t, err)
@ -1294,9 +1277,9 @@ func TestRuleGroupEvalIterationFunc(t *testing.T) {
testFunc := func(tst testInput) {
opts := &ManagerOptions{
QueryFunc: EngineQueryFunc(suite.QueryEngine(), suite.Storage()),
Appendable: suite.Storage(),
Queryable: suite.Storage(),
QueryFunc: EngineQueryFunc(testEngine, storage),
Appendable: storage,
Queryable: storage,
Context: context.Background(),
Logger: log.NewNopLogger(),
NotifyFunc: func(ctx context.Context, expr string, alerts ...*Alert) {},
@ -1361,15 +1344,11 @@ func TestRuleGroupEvalIterationFunc(t *testing.T) {
}
func TestNativeHistogramsInRecordingRules(t *testing.T) {
suite, err := promql.NewTest(t, "")
require.NoError(t, err)
t.Cleanup(suite.Close)
err = suite.Run()
require.NoError(t, err)
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
// Add some histograms.
db := suite.TSDB()
db := storage.DB
hists := tsdbutil.GenerateTestHistograms(5)
ts := time.Now()
app := db.Appender(context.Background())
@ -1381,9 +1360,9 @@ func TestNativeHistogramsInRecordingRules(t *testing.T) {
require.NoError(t, app.Commit())
opts := &ManagerOptions{
QueryFunc: EngineQueryFunc(suite.QueryEngine(), suite.Storage()),
Appendable: suite.Storage(),
Queryable: suite.Storage(),
QueryFunc: EngineQueryFunc(testEngine, storage),
Appendable: storage,
Queryable: storage,
Context: context.Background(),
Logger: log.NewNopLogger(),
}

@ -109,27 +109,22 @@ var ruleEvalTestScenarios = []struct {
},
}
func setUpRuleEvalTest(t require.TestingT) *promql.Test {
suite, err := promql.NewTest(t, `
func setUpRuleEvalTest(t require.TestingT) *teststorage.TestStorage {
return promql.LoadedStorage(t, `
load 1m
metric{label_a="1",label_b="3"} 1
metric{label_a="2",label_b="4"} 10
`)
require.NoError(t, err)
return suite
}
func TestRuleEval(t *testing.T) {
suite := setUpRuleEvalTest(t)
defer suite.Close()
require.NoError(t, suite.Run())
storage := setUpRuleEvalTest(t)
t.Cleanup(func() { storage.Close() })
for _, scenario := range ruleEvalTestScenarios {
t.Run(scenario.name, func(t *testing.T) {
rule := NewRecordingRule("test_rule", scenario.expr, scenario.ruleLabels)
result, err := rule.Eval(suite.Context(), ruleEvaluationTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
result, err := rule.Eval(context.TODO(), ruleEvaluationTime, EngineQueryFunc(testEngine, storage), nil, 0)
require.NoError(t, err)
require.Equal(t, scenario.expected, result)
})
@ -137,10 +132,8 @@ func TestRuleEval(t *testing.T) {
}
func BenchmarkRuleEval(b *testing.B) {
suite := setUpRuleEvalTest(b)
defer suite.Close()
require.NoError(b, suite.Run())
storage := setUpRuleEvalTest(b)
b.Cleanup(func() { storage.Close() })
for _, scenario := range ruleEvalTestScenarios {
b.Run(scenario.name, func(b *testing.B) {
@ -149,7 +142,7 @@ func BenchmarkRuleEval(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := rule.Eval(suite.Context(), ruleEvaluationTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0)
_, err := rule.Eval(context.TODO(), ruleEvaluationTime, EngineQueryFunc(testEngine, storage), nil, 0)
if err != nil {
require.NoError(b, err)
}
@ -184,15 +177,12 @@ func TestRuleEvalDuplicate(t *testing.T) {
}
func TestRecordingRuleLimit(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
metric{label="1"} 1
metric{label="2"} 1
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
tests := []struct {
limit int
@ -223,7 +213,7 @@ func TestRecordingRuleLimit(t *testing.T) {
evalTime := time.Unix(0, 0)
for _, test := range tests {
switch _, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, test.limit); {
switch _, err := rule.Eval(context.TODO(), evalTime, EngineQueryFunc(testEngine, storage), nil, test.limit); {
case err != nil:
require.EqualError(t, err, test.err)
case test.err != "":

@ -33,22 +33,19 @@ import (
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/tsdb/tsdbutil"
"github.com/prometheus/prometheus/util/teststorage"
)
func TestSampledReadEndpoint(t *testing.T) {
suite, err := promql.NewTest(t, `
store := promql.LoadedStorage(t, `
load 1m
test_metric1{foo="bar",baz="qux"} 1
`)
require.NoError(t, err)
defer suite.Close()
addNativeHistogramsToTestSuite(t, suite, 1)
defer store.Close()
err = suite.Run()
require.NoError(t, err)
addNativeHistogramsToTestSuite(t, store, 1)
h := NewReadHandler(nil, nil, suite.Storage(), func() config.Config {
h := NewReadHandler(nil, nil, store, func() config.Config {
return config.Config{
GlobalConfig: config.GlobalConfig{
// We expect external labels to be added, with the source labels honored.
@ -135,19 +132,16 @@ func TestSampledReadEndpoint(t *testing.T) {
}
func BenchmarkStreamReadEndpoint(b *testing.B) {
suite, err := promql.NewTest(b, `
store := promql.LoadedStorage(b, `
load 1m
test_metric1{foo="bar1",baz="qux"} 0+100x119
test_metric1{foo="bar2",baz="qux"} 0+100x120
test_metric1{foo="bar3",baz="qux"} 0+100x240
`)
require.NoError(b, err)
defer suite.Close()
b.Cleanup(func() { store.Close() })
require.NoError(b, suite.Run())
api := NewReadHandler(nil, nil, suite.Storage(), func() config.Config {
api := NewReadHandler(nil, nil, store, func() config.Config {
return config.Config{}
},
0, 1, 0,
@ -206,20 +200,17 @@ func TestStreamReadEndpoint(t *testing.T) {
// Second with 121 samples, We expect 1 frame with 2 chunks.
// Third with 241 samples. We expect 1 frame with 2 chunks, and 1 frame with 1 chunk for the same series due to bytes limit.
// Fourth with 120 histogram samples. We expect 1 frame with 1 chunk.
suite, err := promql.NewTest(t, `
store := promql.LoadedStorage(t, `
load 1m
test_metric1{foo="bar1",baz="qux"} 0+100x119
test_metric1{foo="bar2",baz="qux"} 0+100x120
test_metric1{foo="bar3",baz="qux"} 0+100x240
`)
require.NoError(t, err)
defer suite.Close()
addNativeHistogramsToTestSuite(t, suite, 120)
defer store.Close()
require.NoError(t, suite.Run())
addNativeHistogramsToTestSuite(t, store, 120)
api := NewReadHandler(nil, nil, suite.Storage(), func() config.Config {
api := NewReadHandler(nil, nil, store, func() config.Config {
return config.Config{
GlobalConfig: config.GlobalConfig{
// We expect external labels to be added, with the source labels honored.
@ -440,10 +431,10 @@ func TestStreamReadEndpoint(t *testing.T) {
}, results)
}
func addNativeHistogramsToTestSuite(t *testing.T, pqlTest *promql.Test, n int) {
func addNativeHistogramsToTestSuite(t *testing.T, storage *teststorage.TestStorage, n int) {
lbls := labels.FromStrings("__name__", "test_histogram_metric1", "baz", "qux")
app := pqlTest.Storage().Appender(context.TODO())
app := storage.Appender(context.TODO())
for i, fh := range tsdbutil.GenerateTestFloatHistograms(n) {
_, err := app.AppendHistogram(0, lbls, int64(i)*int64(60*time.Second/time.Millisecond), nil, fh)
require.NoError(t, err)

@ -56,6 +56,17 @@ import (
"github.com/prometheus/prometheus/util/teststorage"
)
var testEngine = promql.NewEngine(promql.EngineOpts{
Logger: nil,
Reg: nil,
MaxSamples: 10000,
Timeout: 100 * time.Second,
NoStepSubqueryIntervalFn: func(int64) int64 { return 60 * 1000 },
EnableAtModifier: true,
EnableNegativeOffset: true,
EnablePerStepStats: true,
})
// testMetaStore satisfies the scrape.MetricMetadataStore interface.
// It is used to inject specific metadata as part of a test case.
type testMetaStore struct {
@ -305,7 +316,7 @@ var sampleFlagMap = map[string]string{
}
func TestEndpoints(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
test_metric1{foo="bar"} 0+100x100
test_metric1{foo="boo"} 1+0x100
@ -316,6 +327,7 @@ func TestEndpoints(t *testing.T) {
test_metric4{foo="boo", dup="1"} 1+0x100
test_metric4{foo="boo"} 1+0x100
`)
t.Cleanup(func() { storage.Close() })
start := time.Unix(0, 0)
exemplars := []exemplar.QueryResult{
@ -361,15 +373,10 @@ func TestEndpoints(t *testing.T) {
},
}
for _, ed := range exemplars {
suite.ExemplarStorage().AppendExemplar(0, ed.SeriesLabels, ed.Exemplars[0])
_, err := storage.AppendExemplar(0, ed.SeriesLabels, ed.Exemplars[0])
require.NoError(t, err, "failed to add exemplar: %+v", ed.Exemplars[0])
}
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
now := time.Now()
t.Run("local", func(t *testing.T) {
@ -383,9 +390,9 @@ func TestEndpoints(t *testing.T) {
testTargetRetriever := setupTestTargetRetriever(t)
api := &API{
Queryable: suite.Storage(),
QueryEngine: suite.QueryEngine(),
ExemplarQueryable: suite.ExemplarQueryable(),
Queryable: storage,
QueryEngine: testEngine,
ExemplarQueryable: storage.ExemplarQueryable(),
targetRetriever: testTargetRetriever.toFactory(),
alertmanagerRetriever: testAlertmanagerRetriever{}.toFactory(),
flagsMap: sampleFlagMap,
@ -394,14 +401,14 @@ func TestEndpoints(t *testing.T) {
ready: func(f http.HandlerFunc) http.HandlerFunc { return f },
rulesRetriever: algr.toFactory(),
}
testEndpoints(t, api, testTargetRetriever, suite.ExemplarStorage(), true)
testEndpoints(t, api, testTargetRetriever, storage, true)
})
// Run all the API tests against a API that is wired to forward queries via
// the remote read client to a test server, which in turn sends them to the
// data from the test suite.
// data from the test storage.
t.Run("remote", func(t *testing.T) {
server := setupRemote(suite.Storage())
server := setupRemote(storage)
defer server.Close()
u, err := url.Parse(server.URL)
@ -446,8 +453,8 @@ func TestEndpoints(t *testing.T) {
api := &API{
Queryable: remote,
QueryEngine: suite.QueryEngine(),
ExemplarQueryable: suite.ExemplarQueryable(),
QueryEngine: testEngine,
ExemplarQueryable: storage.ExemplarQueryable(),
targetRetriever: testTargetRetriever.toFactory(),
alertmanagerRetriever: testAlertmanagerRetriever{}.toFactory(),
flagsMap: sampleFlagMap,
@ -456,8 +463,7 @@ func TestEndpoints(t *testing.T) {
ready: func(f http.HandlerFunc) http.HandlerFunc { return f },
rulesRetriever: algr.toFactory(),
}
testEndpoints(t, api, testTargetRetriever, suite.ExemplarStorage(), false)
testEndpoints(t, api, testTargetRetriever, storage, false)
})
}
@ -470,7 +476,7 @@ func (b byLabels) Less(i, j int) bool { return labels.Compare(b[i], b[j]) < 0 }
func TestGetSeries(t *testing.T) {
// TestEndpoints doesn't have enough label names to test api.labelNames
// endpoint properly. Hence we test it separately.
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
test_metric1{foo1="bar", baz="abc"} 0+100x100
test_metric1{foo2="boo"} 1+0x100
@ -478,11 +484,9 @@ func TestGetSeries(t *testing.T) {
test_metric2{foo="boo", xyz="qwerty"} 1+0x100
test_metric2{foo="baz", abc="qwerty"} 1+0x100
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
api := &API{
Queryable: suite.Storage(),
Queryable: storage,
}
request := func(method string, matchers ...string) (*http.Request, error) {
u, err := url.Parse("http://example.com")
@ -576,7 +580,7 @@ func TestGetSeries(t *testing.T) {
func TestQueryExemplars(t *testing.T) {
start := time.Unix(0, 0)
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
test_metric1{foo="bar"} 0+100x100
test_metric1{foo="boo"} 1+0x100
@ -587,15 +591,12 @@ func TestQueryExemplars(t *testing.T) {
test_metric4{foo="boo", dup="1"} 1+0x100
test_metric4{foo="boo"} 1+0x100
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
api := &API{
Queryable: suite.Storage(),
QueryEngine: suite.QueryEngine(),
ExemplarQueryable: suite.ExemplarQueryable(),
Queryable: storage,
QueryEngine: testEngine,
ExemplarQueryable: storage.ExemplarQueryable(),
}
request := func(method string, qs url.Values) (*http.Request, error) {
@ -673,7 +674,7 @@ func TestQueryExemplars(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
es := suite.ExemplarStorage()
es := storage
ctx := context.Background()
for _, te := range tc.exemplars {
@ -700,7 +701,7 @@ func TestQueryExemplars(t *testing.T) {
func TestLabelNames(t *testing.T) {
// TestEndpoints doesn't have enough label names to test api.labelNames
// endpoint properly. Hence we test it separately.
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
test_metric1{foo1="bar", baz="abc"} 0+100x100
test_metric1{foo2="boo"} 1+0x100
@ -708,11 +709,9 @@ func TestLabelNames(t *testing.T) {
test_metric2{foo="boo", xyz="qwerty"} 1+0x100
test_metric2{foo="baz", abc="qwerty"} 1+0x100
`)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
t.Cleanup(func() { storage.Close() })
api := &API{
Queryable: suite.Storage(),
Queryable: storage,
}
request := func(method string, matchers ...string) (*http.Request, error) {
u, err := url.Parse("http://example.com")
@ -801,14 +800,12 @@ func (testStats) Builtin() (_ stats.BuiltinStats) {
}
func TestStats(t *testing.T) {
suite, err := promql.NewTest(t, ``)
require.NoError(t, err)
defer suite.Close()
require.NoError(t, suite.Run())
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
api := &API{
Queryable: suite.Storage(),
QueryEngine: suite.QueryEngine(),
Queryable: storage,
QueryEngine: testEngine,
now: func() time.Time {
return time.Unix(123, 0)
},

@ -36,6 +36,7 @@ import (
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/tsdb"
"github.com/prometheus/prometheus/util/teststorage"
)
var scenarios = map[string]struct {
@ -199,7 +200,7 @@ test_metric_without_labels{instance="baz"} 1001 6000000
}
func TestFederation(t *testing.T) {
suite, err := promql.NewTest(t, `
storage := promql.LoadedStorage(t, `
load 1m
test_metric1{foo="bar",instance="i"} 0+100x100
test_metric1{foo="boo",instance="i"} 1+0x100
@ -208,17 +209,10 @@ func TestFederation(t *testing.T) {
test_metric_stale 1+10x99 stale
test_metric_old 1+10x98
`)
if err != nil {
t.Fatal(err)
}
defer suite.Close()
if err := suite.Run(); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { storage.Close() })
h := &Handler{
localStorage: &dbAdapter{suite.TSDB()},
localStorage: &dbAdapter{storage.DB},
lookbackDelta: 5 * time.Minute,
now: func() model.Time { return 101 * 60 * 1000 }, // 101min after epoch.
config: &config.Config{
@ -305,19 +299,12 @@ func normalizeBody(body *bytes.Buffer) string {
}
func TestFederationWithNativeHistograms(t *testing.T) {
suite, err := promql.NewTest(t, "")
if err != nil {
t.Fatal(err)
}
defer suite.Close()
if err := suite.Run(); err != nil {
t.Fatal(err)
}
storage := teststorage.New(t)
t.Cleanup(func() { storage.Close() })
var expVec promql.Vector
db := suite.TSDB()
db := storage.DB
hist := &histogram.Histogram{
Count: 10,
ZeroCount: 2,
@ -354,6 +341,7 @@ func TestFederationWithNativeHistograms(t *testing.T) {
for i := 0; i < 6; i++ {
l := labels.FromStrings("__name__", "test_metric", "foo", fmt.Sprintf("%d", i))
expL := labels.FromStrings("__name__", "test_metric", "instance", "", "foo", fmt.Sprintf("%d", i))
var err error
switch i {
case 0, 3:
_, err = app.Append(0, l, 100*60*1000, float64(i*100))
@ -383,7 +371,7 @@ func TestFederationWithNativeHistograms(t *testing.T) {
require.NoError(t, app.Commit())
h := &Handler{
localStorage: &dbAdapter{suite.TSDB()},
localStorage: &dbAdapter{db},
lookbackDelta: 5 * time.Minute,
now: func() model.Time { return 101 * 60 * 1000 }, // 101min after epoch.
config: &config.Config{

Loading…
Cancel
Save