From e2fb497ebae1f3b27bfd6d8abce1b247b5fd03c9 Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Wed, 13 Mar 2013 16:27:14 -0700 Subject: [PATCH] Add operator value extraction tests. --- storage/metric/operation.go | 31 ++- storage/metric/operation_test.go | 401 +++++++++++++++++++++++++++++++ 2 files changed, 422 insertions(+), 10 deletions(-) diff --git a/storage/metric/operation.go b/storage/metric/operation.go index 1ac99fafe..388456ddf 100644 --- a/storage/metric/operation.go +++ b/storage/metric/operation.go @@ -62,23 +62,28 @@ func (g getValuesAtTimeOp) StartsAt() time.Time { } func (g *getValuesAtTimeOp) ExtractSamples(in []model.SamplePair) (out []model.SamplePair) { - extractValuesAroundTime(g.time, in, out) + if len(in) == 0 { + return + } + out = extractValuesAroundTime(g.time, in) g.consumed = true return } -func extractValuesAroundTime(t time.Time, in []model.SamplePair, out []model.SamplePair) { +func extractValuesAroundTime(t time.Time, in []model.SamplePair) (out []model.SamplePair) { i := sort.Search(len(in), func(i int) bool { - return in[i].Timestamp.After(t) + return in[i].Timestamp.After(t) || in[i].Timestamp.Equal(t) }) - if i == len(in) { - panic("Searched past end of input") - } - if i == 0 { + fmt.Printf("I: %d\n", i) + switch i { + case len(in): + out = in[len(in)-1:] + case 0: out = append(out, in[0:1]...) - } else { + default: out = append(out, in[i-1:i+1]...) } + return } func (g getValuesAtTimeOp) CurrentTime() (currentTime *time.Time) { @@ -108,16 +113,19 @@ func (g getValuesAtIntervalOp) Through() time.Time { } func (g *getValuesAtIntervalOp) ExtractSamples(in []model.SamplePair) (out []model.SamplePair) { + if len(in) == 0 { + return + } lastChunkTime := in[len(in)-1].Timestamp for { + out = extractValuesAroundTime(g.from, in) + g.from = g.from.Add(g.interval) if g.from.After(lastChunkTime) { break } if g.from.After(g.through) { break } - extractValuesAroundTime(g.from, in, out) - g.from = g.from.Add(g.interval) } return } @@ -147,6 +155,9 @@ func (g getValuesAlongRangeOp) Through() time.Time { } func (g *getValuesAlongRangeOp) ExtractSamples(in []model.SamplePair) (out []model.SamplePair) { + if len(in) == 0 { + return + } lastChunkTime := in[len(in)-1].Timestamp g.from = lastChunkTime.Add(time.Duration(1)) return in diff --git a/storage/metric/operation_test.go b/storage/metric/operation_test.go index fc80eabfd..a13ba7174 100644 --- a/storage/metric/operation_test.go +++ b/storage/metric/operation_test.go @@ -14,6 +14,7 @@ package metric import ( + "github.com/prometheus/prometheus/model" "github.com/prometheus/prometheus/utility/test" "sort" "testing" @@ -1076,3 +1077,403 @@ func BenchmarkOptimize(b *testing.B) { testOptimize(b) } } + +func TestGetValuesAtTimeOp(t *testing.T) { + var scenarios = []struct { + op getValuesAtTimeOp + in []model.SamplePair + out []model.SamplePair + }{ + // No values. + { + op: getValuesAtTimeOp{ + time: testInstant, + }, + }, + // Operator time before single value. + { + op: getValuesAtTimeOp{ + time: testInstant, + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + }, + // Operator time exactly at single value. + { + op: getValuesAtTimeOp{ + time: testInstant.Add(1 * time.Minute), + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + }, + // Operator time after single value. + { + op: getValuesAtTimeOp{ + time: testInstant.Add(2 * time.Minute), + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + }, + // Operator time before two values. + { + op: getValuesAtTimeOp{ + time: testInstant, + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + }, + // Operator time at first of two values. + { + op: getValuesAtTimeOp{ + time: testInstant.Add(1 * time.Minute), + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + }, + // Operator time between first and second of two values. + { + op: getValuesAtTimeOp{ + time: testInstant.Add(90 * time.Second), + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + }, + // Operator time at second of two values. + { + op: getValuesAtTimeOp{ + time: testInstant.Add(2 * time.Minute), + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + }, + // Operator time after second of two values. + { + op: getValuesAtTimeOp{ + time: testInstant.Add(3 * time.Minute), + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + }, + } + for i, scenario := range scenarios { + actual := scenario.op.ExtractSamples(scenario.in) + if len(actual) != len(scenario.out) { + t.Fatalf("%d. expected length %d, got %d: %v", i, len(scenario.out), len(actual), scenario.op) + t.Fatalf("%d. expected length %d, got %d", i, len(scenario.out), len(actual)) + } + for j, out := range scenario.out { + if out != actual[j] { + t.Fatal("%d. expected output %v, got %v", i, scenario.out, actual) + } + } + } +} + +func TestGetValuesAtIntervalOp(t *testing.T) { + var scenarios = []struct { + op getValuesAtIntervalOp + in []model.SamplePair + out []model.SamplePair + }{ + // No values. + { + op: getValuesAtIntervalOp{ + from: testInstant, + through: testInstant.Add(1 * time.Minute), + interval: 30 * time.Second, + }, + }, + // Entire operator range before first value. + { + op: getValuesAtIntervalOp{ + from: testInstant, + through: testInstant.Add(1 * time.Minute), + interval: 30 * time.Second, + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(3 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + }, + // Operator range starts before first value, ends within available values. + { + op: getValuesAtIntervalOp{ + from: testInstant, + through: testInstant.Add(2 * time.Minute), + interval: 30 * time.Second, + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(3 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(3 * time.Minute), + Value: 1, + }, + }, + }, + // Entire operator range is within available values. + { + op: getValuesAtIntervalOp{ + from: testInstant.Add(1 * time.Minute), + through: testInstant.Add(2 * time.Minute), + interval: 30 * time.Second, + }, + in: []model.SamplePair{ + { + Timestamp: testInstant, + Value: 1, + }, + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(3 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(3 * time.Minute), + Value: 1, + }, + }, + }, + // Operator range begins before first value, ends after last. + { + op: getValuesAtIntervalOp{ + from: testInstant, + through: testInstant.Add(3 * time.Minute), + interval: 30 * time.Second, + }, + in: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + }, + }, + // Operator range begins within available values, ends after the last value. + { + op: getValuesAtIntervalOp{ + from: testInstant.Add(2 * time.Minute), + through: testInstant.Add(4 * time.Minute), + interval: 30 * time.Second, + }, + in: []model.SamplePair{ + { + Timestamp: testInstant, + Value: 1, + }, + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(3 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(2 * time.Minute), + Value: 1, + }, + { + Timestamp: testInstant.Add(3 * time.Minute), + Value: 1, + }, + }, + }, + // Entire operator range after the last available value. + { + op: getValuesAtIntervalOp{ + from: testInstant.Add(2 * time.Minute), + through: testInstant.Add(3 * time.Minute), + interval: 30 * time.Second, + }, + in: []model.SamplePair{ + { + Timestamp: testInstant, + Value: 1, + }, + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + out: []model.SamplePair{ + { + Timestamp: testInstant.Add(1 * time.Minute), + Value: 1, + }, + }, + }, + } + for i, scenario := range scenarios { + actual := scenario.op.ExtractSamples(scenario.in) + if len(actual) != len(scenario.out) { + t.Fatalf("%d. expected length %d, got %d: %v", i, len(scenario.out), len(actual), scenario.op) + t.Fatalf("%d. expected length %d, got %d", i, len(scenario.out), len(actual)) + } + for j, out := range scenario.out { + if out != actual[j] { + t.Fatal("%d. expected output %v, got %v", i, scenario.out, actual) + } + } + } +}