Add regression tests for metrics mutations in AST.

It turned out in the end, that only drop_common_metrics() produced any
erroneous output in the old system. The second expression in the test
("sum(testmetric) keeping_extra") already worked in the old code, but
why not keep it in...

The way to test ranged evaluations is a bit clumsy so far, so I want to
build a nicer test framework in the end, where all the test cases can be
specified as text files which specify desired inputs, outputs, query
step widths, etc.

Change-Id: I821859789e69b8232bededf670a1b76e9e8c8ca4
This commit is contained in:
Julius Volz 2014-12-12 20:01:32 +01:00
parent c9618d11e8
commit 00a2a93a05
3 changed files with 191 additions and 6 deletions

View File

@ -40,6 +40,7 @@ type SampleStream struct {
Values metric.Values Values metric.Values
} }
// Sample is a single sample belonging to a COWMetric.
type Sample struct { type Sample struct {
Metric clientmodel.COWMetric Metric clientmodel.COWMetric
Value clientmodel.SampleValue Value clientmodel.SampleValue
@ -413,7 +414,7 @@ func EvalVectorRange(node VectorNode, start clientmodel.Timestamp, end clientmod
// TODO implement watchdog timer for long-running queries. // TODO implement watchdog timer for long-running queries.
evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start() evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start()
sampleStreams := map[uint64]*SampleStream{} sampleStreams := map[uint64]*SampleStream{}
for t := start; t.Before(end); t = t.Add(interval) { for t := start; !t.After(end); t = t.Add(interval) {
vector := node.Eval(t) vector := node.Eval(t)
for _, sample := range vector { for _, sample := range vector {
samplePair := metric.SamplePair{ samplePair := metric.SamplePair{

View File

@ -91,11 +91,12 @@ func (vector Vector) String() string {
func (matrix Matrix) String() string { func (matrix Matrix) String() string {
metricStrings := make([]string, 0, len(matrix)) metricStrings := make([]string, 0, len(matrix))
for _, sampleStream := range matrix { for _, sampleStream := range matrix {
metricName, ok := sampleStream.Metric.Metric[clientmodel.MetricNameLabel] metricName, hasName := sampleStream.Metric.Metric[clientmodel.MetricNameLabel]
if !ok { numLabels := len(sampleStream.Metric.Metric)
panic("Tried to print matrix without metric name") if hasName {
numLabels--
} }
labelStrings := make([]string, 0, len(sampleStream.Metric.Metric)-1) labelStrings := make([]string, 0, numLabels)
for label, value := range sampleStream.Metric.Metric { for label, value := range sampleStream.Metric.Metric {
if label != clientmodel.MetricNameLabel { if label != clientmodel.MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value)) labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))

View File

@ -25,6 +25,7 @@ import (
"github.com/prometheus/prometheus/rules/ast" "github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/stats" "github.com/prometheus/prometheus/stats"
"github.com/prometheus/prometheus/storage/local" "github.com/prometheus/prometheus/storage/local"
"github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/utility/test" "github.com/prometheus/prometheus/utility/test"
) )
@ -60,7 +61,7 @@ func newTestStorage(t testing.TB) (storage local.Storage, closer test.Closer) {
func TestExpressions(t *testing.T) { func TestExpressions(t *testing.T) {
// Labels in expected output need to be alphabetically sorted. // Labels in expected output need to be alphabetically sorted.
var expressionTests = []struct { expressionTests := []struct {
expr string expr string
output []string output []string
shouldFail bool shouldFail bool
@ -705,6 +706,188 @@ func TestExpressions(t *testing.T) {
} }
} }
func TestRangedEvaluationRegressions(t *testing.T) {
scenarios := []struct {
in ast.Matrix
out ast.Matrix
expr string
}{
{
// Testing COWMetric behavior in drop_common_labels.
in: ast.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: ast.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: ast.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: ast.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",
},
}
for i, s := range scenarios {
storage, closer := local.NewTestStorage(t)
storeMatrix(storage, s.in)
expr, err := LoadExprFromString(s.expr)
if err != nil {
t.Fatalf("%d. Error parsing expression: %v", i, err)
}
got, err := ast.EvalVectorRange(
expr.(ast.VectorNode),
testStartTime,
testStartTime.Add(time.Hour),
time.Hour,
storage,
stats.NewTimerGroup(),
)
if err != nil {
t.Fatalf("%d. Error evaluating expression: %v", i, err)
}
if got.String() != s.out.String() {
t.Fatalf("%d. Expression: %s\n\ngot:\n=====\n%v\n====\n\nwant:\n=====\n%v\n=====\n", i, s.expr, got.String(), s.out.String())
}
closer.Close()
}
}
var ruleTests = []struct { var ruleTests = []struct {
inputFile string inputFile string
shouldFail bool shouldFail bool