diff --git a/promql/engine_test.go b/promql/engine_test.go index 54567d154c..0df9693751 100644 --- a/promql/engine_test.go +++ b/promql/engine_test.go @@ -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() + engine := newTestEngine() + storage := LoadedStorage(t, load) + t.Cleanup(func() { storage.Close() }) - err = test.Run() - require.NoError(t, err) - - 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) diff --git a/promql/promql_test.go b/promql/promql_test.go index a07a0f5cb7..05821b1c11 100644 --- a/promql/promql_test.go +++ b/promql/promql_test.go @@ -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 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, + }) +} + 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()) - - test.Close() - }) - } + RunBuiltinTests(t, newTestEngine()) } // Run a lot of queries at the same time, to check for race conditions. diff --git a/promql/test.go b/promql/test.go index 64fa66d5d3..3d88ca0330 100644 --- a/promql/test.go +++ b/promql/test.go @@ -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)) -} +//go:embed testdata +var testsFs embed.FS -// 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 -} - -// 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 ] ") } @@ -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 { diff --git a/rules/alerting_test.go b/rules/alerting_test.go index 8cd0da2815..f980c2a984 100644 --- a/rules/alerting_test.go +++ b/rules/alerting_test.go @@ -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)) } diff --git a/rules/manager_test.go b/rules/manager_test.go index 26a7909644..f301aa010c 100644 --- a/rules/manager_test.go +++ b/rules/manager_test.go @@ -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(), } diff --git a/rules/recording_test.go b/rules/recording_test.go index 35a0b1a0bd..960ff4bdb8 100644 --- a/rules/recording_test.go +++ b/rules/recording_test.go @@ -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 != "": diff --git a/storage/remote/read_handler_test.go b/storage/remote/read_handler_test.go index 261c28e215..3d9182640b 100644 --- a/storage/remote/read_handler_test.go +++ b/storage/remote/read_handler_test.go @@ -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() + defer store.Close() - addNativeHistogramsToTestSuite(t, suite, 1) + addNativeHistogramsToTestSuite(t, store, 1) - err = suite.Run() - require.NoError(t, err) - - 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() + defer store.Close() - addNativeHistogramsToTestSuite(t, suite, 120) + addNativeHistogramsToTestSuite(t, store, 120) - require.NoError(t, suite.Run()) - - 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) diff --git a/web/api/v1/api_test.go b/web/api/v1/api_test.go index 742ca09ba6..c4710c69fb 100644 --- a/web/api/v1/api_test.go +++ b/web/api/v1/api_test.go @@ -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) }, diff --git a/web/federate_test.go b/web/federate_test.go index 4fdcf8daa1..2d3542ddcb 100644 --- a/web/federate_test.go +++ b/web/federate_test.go @@ -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{