diff --git a/promql/parse.go b/promql/parse.go index 65f94a430e..653535162b 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -120,7 +120,7 @@ func (p *parser) parseExpr() (expr Expr, err error) { return } -// sequenceValue is a omittable value in a sequence of time series values. +// sequenceValue is an omittable value in a sequence of time series values. type sequenceValue struct { value clientmodel.SampleValue omitted bool diff --git a/promql/promql_test.go b/promql/promql_test.go index 0a7f452716..14d1634abd 100644 --- a/promql/promql_test.go +++ b/promql/promql_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -14,341 +14,24 @@ package promql import ( - "fmt" - "math" - "regexp" - "strconv" - "strings" + "path/filepath" "testing" - "time" - - clientmodel "github.com/prometheus/client_golang/model" - - "github.com/prometheus/prometheus/storage/local" - "github.com/prometheus/prometheus/storage/metric" - "github.com/prometheus/prometheus/utility/test" ) -var ( - testEvalTime = testStartTime.Add(testSampleInterval * 10) - fixturesPath = "fixtures" - - reSample = regexp.MustCompile(`^(.*)(?: \=\>|:) (\-?\d+\.?\d*(?:e-?\d+)?|[+-]Inf|NaN) \@\[(\d+)\]$`) - // minNormal = math.Float64frombits(0x0010000000000000) // The smallest positive normal value of type float64. -) - -// const ( -// epsilon = 0.000001 // Relative error allowed for sample values. -// ) - -func annotateWithTime(lines []string, timestamp clientmodel.Timestamp) []string { - annotatedLines := []string{} - for _, line := range lines { - annotatedLines = append(annotatedLines, fmt.Sprintf(line, timestamp)) - } - return annotatedLines -} - -func vectorComparisonString(expected []string, actual []string) string { - separator := "\n--------------\n" - return fmt.Sprintf("Expected:%v%v%v\nActual:%v%v%v ", - separator, - strings.Join(expected, "\n"), - separator, - separator, - strings.Join(actual, "\n"), - separator) -} - -// samplesAlmostEqual returns true if the two sample lines only differ by a -// small relative error in their sample value. -func samplesAlmostEqual(a, b string) bool { - if a == b { - // Fast path if strings are equal. - return true - } - aMatches := reSample.FindStringSubmatch(a) - if aMatches == nil { - panic(fmt.Errorf("sample %q did not match regular expression", a)) - } - bMatches := reSample.FindStringSubmatch(b) - if bMatches == nil { - panic(fmt.Errorf("sample %q did not match regular expression", b)) - } - if aMatches[1] != bMatches[1] { - return false // Labels don't match. - } - if aMatches[3] != bMatches[3] { - return false // Timestamps don't match. - } - // If we are here, we have the diff in the floats. - // We have to check if they are almost equal. - aVal, err := strconv.ParseFloat(aMatches[2], 64) +func TestEvaluations(t *testing.T) { + files, err := filepath.Glob("testdata/*.test") if err != nil { - panic(err) + t.Fatal(err) } - bVal, err := strconv.ParseFloat(bMatches[2], 64) - if err != nil { - panic(err) - } - - // Cf. http://floating-point-gui.de/errors/comparison/ - if aVal == bVal { - return true - } - - diff := math.Abs(aVal - bVal) - - if aVal == 0 || bVal == 0 || diff < minNormal { - return diff < epsilon*minNormal - } - return diff/(math.Abs(aVal)+math.Abs(bVal)) < epsilon -} - -func newTestStorage(t testing.TB) (storage local.Storage, closer test.Closer) { - storage, closer = local.NewTestStorage(t, 1) - storeMatrix(storage, testMatrix) - return storage, closer -} - -func TestRangedEvaluationRegressions(t *testing.T) { - scenarios := []struct { - in Matrix - out Matrix - expr string - }{ - { - // Testing COWMetric behavior in drop_common_labels. - in: Matrix{ - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "testlabel": "1", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime, - Value: 1, - }, - { - Timestamp: testStartTime.Add(time.Hour), - Value: 1, - }, - }, - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "testlabel": "2", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime.Add(time.Hour), - Value: 2, - }, - }, - }, - }, - out: Matrix{ - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime, - Value: 1, - }, - }, - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "testlabel": "1", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime.Add(time.Hour), - Value: 1, - }, - }, - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "testlabel": "2", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime.Add(time.Hour), - Value: 2, - }, - }, - }, - }, - expr: "drop_common_labels(testmetric)", - }, - { - // Testing COWMetric behavior in vector aggregation. - in: Matrix{ - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "testlabel": "1", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime, - Value: 1, - }, - { - Timestamp: testStartTime.Add(time.Hour), - Value: 1, - }, - }, - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "testlabel": "2", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime, - Value: 2, - }, - }, - }, - }, - out: Matrix{ - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{}, - }, - Values: metric.Values{ - { - Timestamp: testStartTime, - Value: 3, - }, - }, - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - "testlabel": "1", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime.Add(time.Hour), - Value: 1, - }, - }, - }, - }, - expr: "sum(testmetric) keeping_extra", - }, - { - // Testing metric fingerprint grouping behavior. - in: Matrix{ - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "aa": "bb", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime, - Value: 1, - }, - }, - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "a": "abb", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime, - Value: 2, - }, - }, - }, - }, - out: Matrix{ - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "aa": "bb", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime, - Value: 1, - }, - }, - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testmetric", - "a": "abb", - }, - }, - Values: metric.Values{ - { - Timestamp: testStartTime, - Value: 2, - }, - }, - }, - }, - expr: "testmetric", - }, - } - - for i, s := range scenarios { - storage, closer := local.NewTestStorage(t, 1) - storeMatrix(storage, s.in) - - engine := NewEngine(storage) - query, err := engine.NewRangeQuery(s.expr, testStartTime, testStartTime.Add(time.Hour), time.Hour) + for _, fn := range files { + test, err := NewTestFromFile(t, fn) if err != nil { - t.Errorf("%d. Error in expression %q", i, s.expr) - t.Fatalf("%d. Error parsing expression: %v", i, err) + t.Errorf("error creating test for %s: %s", fn, err) } - res := query.Exec() - if res.Err != nil { - t.Errorf("%d. Error in expression %q", i, s.expr) - t.Fatalf("%d. Error evaluating expression: %v", i, err) + err = test.Run() + if err != nil { + t.Errorf("error running test %s: %s", fn, err) } - - if res.String() != s.out.String() { - t.Errorf("%d. Error in expression %q", i, s.expr) - t.Fatalf("%d. Expression: %s\n\ngot:\n=====\n%v\n====\n\nwant:\n=====\n%v\n=====\n", i, s.expr, res.String(), s.out.String()) - } - - closer.Close() + test.Close() } } diff --git a/promql/setup_test.go b/promql/setup_test.go deleted file mode 100644 index a02ee5a662..0000000000 --- a/promql/setup_test.go +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2013 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package promql - -import ( - "time" - - clientmodel "github.com/prometheus/client_golang/model" - - "github.com/prometheus/prometheus/storage/local" - "github.com/prometheus/prometheus/storage/metric" -) - -var testSampleInterval = time.Duration(5) * time.Minute - -// var testStartTime = clientmodel.Timestamp(0) - -func getTestValueStream(startVal, endVal, stepVal clientmodel.SampleValue, startTime clientmodel.Timestamp) (resultValues metric.Values) { - currentTime := startTime - for currentVal := startVal; currentVal <= endVal; currentVal += stepVal { - sample := metric.SamplePair{ - Value: currentVal, - Timestamp: currentTime, - } - resultValues = append(resultValues, sample) - currentTime = currentTime.Add(testSampleInterval) - } - return resultValues -} - -func getTestVectorFromTestMatrix(matrix Matrix) Vector { - vector := Vector{} - for _, sampleStream := range matrix { - lastSample := sampleStream.Values[len(sampleStream.Values)-1] - vector = append(vector, &Sample{ - Metric: sampleStream.Metric, - Value: lastSample.Value, - Timestamp: lastSample.Timestamp, - }) - } - return vector -} - -func storeMatrix(storage local.Storage, matrix Matrix) { - pendingSamples := clientmodel.Samples{} - for _, sampleStream := range matrix { - for _, sample := range sampleStream.Values { - pendingSamples = append(pendingSamples, &clientmodel.Sample{ - Metric: sampleStream.Metric.Metric, - Value: sample.Value, - Timestamp: sample.Timestamp, - }) - } - } - for _, s := range pendingSamples { - storage.Append(s) - } - storage.WaitForIndexing() -} - -var testVector = getTestVectorFromTestMatrix(testMatrix) - -var testMatrix = Matrix{ - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "http_requests", - clientmodel.JobLabel: "api-server", - "instance": "0", - "group": "production", - }, - }, - Values: getTestValueStream(0, 100, 10, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "http_requests", - clientmodel.JobLabel: "api-server", - "instance": "1", - "group": "production", - }, - }, - Values: getTestValueStream(0, 200, 20, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "http_requests", - clientmodel.JobLabel: "api-server", - "instance": "0", - "group": "canary", - }, - }, - Values: getTestValueStream(0, 300, 30, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "http_requests", - clientmodel.JobLabel: "api-server", - "instance": "1", - "group": "canary", - }, - }, - Values: getTestValueStream(0, 400, 40, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "http_requests", - clientmodel.JobLabel: "app-server", - "instance": "0", - "group": "production", - }, - }, - Values: getTestValueStream(0, 500, 50, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "http_requests", - clientmodel.JobLabel: "app-server", - "instance": "1", - "group": "production", - }, - }, - Values: getTestValueStream(0, 600, 60, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "http_requests", - clientmodel.JobLabel: "app-server", - "instance": "0", - "group": "canary", - }, - }, - Values: getTestValueStream(0, 700, 70, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "http_requests", - clientmodel.JobLabel: "app-server", - "instance": "1", - "group": "canary", - }, - }, - Values: getTestValueStream(0, 800, 80, testStartTime), - }, - // Single-letter metric and label names. - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "x", - "y": "testvalue", - }, - }, - Values: getTestValueStream(0, 100, 10, testStartTime), - }, - // Counter reset in the middle of range. - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testcounter_reset_middle", - }, - }, - Values: append(getTestValueStream(0, 40, 10, testStartTime), getTestValueStream(0, 50, 10, testStartTime.Add(testSampleInterval*5))...), - }, - // Counter reset at the end of range. - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "testcounter_reset_end", - }, - }, - Values: append(getTestValueStream(0, 90, 10, testStartTime), getTestValueStream(0, 0, 10, testStartTime.Add(testSampleInterval*10))...), - }, - // For label-key grouping regression test. - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "label_grouping_test", - "a": "aa", - "b": "bb", - }, - }, - Values: getTestValueStream(0, 100, 10, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "label_grouping_test", - "a": "a", - "b": "abb", - }, - }, - Values: getTestValueStream(0, 200, 20, testStartTime), - }, - // Now a more realistic histogram per job and instance to test aggregation. - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job1", - "instance": "ins1", - "le": "0.1", - }, - }, - Values: getTestValueStream(0, 10, 1, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job1", - "instance": "ins1", - "le": "0.2", - }, - }, - Values: getTestValueStream(0, 30, 3, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job1", - "instance": "ins1", - "le": "+Inf", - }, - }, - Values: getTestValueStream(0, 40, 4, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job1", - "instance": "ins2", - "le": "0.1", - }, - }, - Values: getTestValueStream(0, 20, 2, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job1", - "instance": "ins2", - "le": "0.2", - }, - }, - Values: getTestValueStream(0, 50, 5, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job1", - "instance": "ins2", - "le": "+Inf", - }, - }, - Values: getTestValueStream(0, 60, 6, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job2", - "instance": "ins1", - "le": "0.1", - }, - }, - Values: getTestValueStream(0, 30, 3, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job2", - "instance": "ins1", - "le": "0.2", - }, - }, - Values: getTestValueStream(0, 40, 4, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job2", - "instance": "ins1", - "le": "+Inf", - }, - }, - Values: getTestValueStream(0, 60, 6, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job2", - "instance": "ins2", - "le": "0.1", - }, - }, - Values: getTestValueStream(0, 40, 4, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job2", - "instance": "ins2", - "le": "0.2", - }, - }, - Values: getTestValueStream(0, 70, 7, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "request_duration_seconds_bucket", - clientmodel.JobLabel: "job2", - "instance": "ins2", - "le": "+Inf", - }, - }, - Values: getTestValueStream(0, 90, 9, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "vector_matching_a", - "l": "x", - }, - }, - Values: getTestValueStream(0, 100, 1, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "vector_matching_a", - "l": "y", - }, - }, - Values: getTestValueStream(0, 100, 2, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "vector_matching_b", - "l": "x", - }, - }, - Values: getTestValueStream(0, 100, 4, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "cpu_count", - "instance": "0", - "type": "numa", - }, - }, - Values: getTestValueStream(0, 500, 30, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "cpu_count", - "instance": "0", - "type": "smp", - }, - }, - Values: getTestValueStream(0, 200, 10, testStartTime), - }, - { - Metric: clientmodel.COWMetric{ - Metric: clientmodel.Metric{ - clientmodel.MetricNameLabel: "cpu_count", - "instance": "1", - "type": "smp", - }, - }, - Values: getTestValueStream(0, 200, 20, testStartTime), - }, -} diff --git a/promql/testdata/histograms.test b/promql/testdata/histograms.test index 6d1f16dfad..2478c34846 100644 --- a/promql/testdata/histograms.test +++ b/promql/testdata/histograms.test @@ -138,4 +138,4 @@ eval instant at 50m histogram_quantile(0.5, rate(request_duration_seconds_bucket {instance="ins1", job="job1"} 0.15 {instance="ins2", job="job1"} 0.13333333333333333 {instance="ins1", job="job2"} 0.1 - {instance="ins2", job="job2"} 0.11666666666666667 \ No newline at end of file + {instance="ins2", job="job2"} 0.11666666666666667 diff --git a/promql/testdata/legacy.test b/promql/testdata/legacy.test index d444090317..62b1415652 100644 --- a/promql/testdata/legacy.test +++ b/promql/testdata/legacy.test @@ -636,3 +636,38 @@ eval instant at 50m stdvar(http_requests) eval instant at 50m stdvar by (instance)(http_requests) {instance="0"} 50000 {instance="1"} 50000 + + +# Matrix tests. + +clear +load 1h + testmetric{testlabel="1"} 1 1 + testmetric{testlabel="2"} _ 2 + +eval instant at 0h drop_common_labels(testmetric) + testmetric 1 + +eval instant at 1h drop_common_labels(testmetric) + testmetric{testlabel="1"} 1 + testmetric{testlabel="2"} 2 + +clear +load 1h + testmetric{testlabel="1"} 1 1 + testmetric{testlabel="2"} 2 _ + +eval instant at 0h sum(testmetric) keeping_extra + {} 3 + +eval instant at 1h sum(testmetric) keeping_extra + {testlabel="1"} 1 + +clear +load 1h + testmetric{aa="bb"} 1 + testmetric{a="abb"} 2 + +eval instant at 0h testmetric + testmetric{aa="bb"} 1 + testmetric{a="abb"} 2 diff --git a/promql/testdata/literals.test b/promql/testdata/literals.test index ffca00e1cf..cc86f0c9ba 100644 --- a/promql/testdata/literals.test +++ b/promql/testdata/literals.test @@ -53,4 +53,4 @@ eval instant at 50m 0 / 0 NaN eval instant at 50m 1 % 0 - NaN \ No newline at end of file + NaN