Beispiel #1
0
func TestSumAverage(t *testing.T) {
	aggregator := aggregators.New(
		1000.0,
		2000.0,
		aggregators.Sum,
		200.0,
		aggregators.Avg,
		aggregators.NaN,
		nil)
	aggregator.Add(tsdb.TimeSeries{
		{1000.0, 42.0}, {1030.0, 54.0}, {1199.999, 49.5},
		{1401.0, 20.0}, {1599.0, 30.0},
		{1836.0, 98.0}})
	aggregator.Add(tsdb.TimeSeries{
		{1500.0, 1025.0},
		{1600.0, 99.0},
		{1800.0, 198.0}, {1801.0, 202.0}, {1999.1, 200.0}})
	aggregator.Add(tsdb.TimeSeries{
		{1400.0, 1975.0},
		{1450.0, 2000.0},
		{1599.0, 2025.0},
		{1599.1, 2050.0},
		{1599.2, 2075.0}})
	aggregator.Add(nil)
	aggregated := aggregator.Aggregate()
	expected := tsdb.TimeSeries{
		{1000.0, 48.5},
		{1400.0, 3075.0},
		{1600.0, 99.0},
		{1800.0, 298.0}}
	assertValueDeepEqual(t, expected, aggregated)
}
Beispiel #2
0
func TestLinearInterpolation(t *testing.T) {
	aggregator := aggregators.New(
		1000.0,
		2000.0,
		aggregators.Avg,
		200.0,
		aggregators.Avg,
		aggregators.None,
		nil)
	aggregator.Add(tsdb.TimeSeries{
		{1000.0, 31.0}, {1030.0, 31.5}, {1199.999, 31.25},
		{1401.0, 20.0}, {1599.0, 30.0},
		{1836.0, 75.0}})
	aggregator.Add(nil)
	aggregated := aggregator.Aggregate()
	expected := tsdb.TimeSeries{
		{1000.0, 31.25},
		{1200.0, 28.125},
		{1400.0, 25.0},
		{1600.0, 50.0},
		{1800.0, 75.0}}
	assertValueDeepEqual(t, expected, aggregated)
	aggregator.Add(tsdb.TimeSeries{
		{1200.0, 40.0},
		{1600.0, 46.0}})
	aggregated = aggregator.Aggregate()
	expected = tsdb.TimeSeries{
		{1000.0, 31.25},
		{1200.0, 34.0625},
		{1400.0, 34.0},
		{1600.0, 48.0},
		{1800.0, 75.0}}
	assertValueDeepEqual(t, expected, aggregated)
}
Beispiel #3
0
func TestAverageZero(t *testing.T) {
	aggregator := aggregators.New(
		1000.0,
		2000.0,
		aggregators.Avg,
		200.0,
		aggregators.Avg,
		aggregators.Zero,
		nil)
	aggregator.Add(tsdb.TimeSeries{
		{1000.0, 42.0}, {1030.0, 54.0}, {1199.999, 49.5},
		{1401.0, 20.0}, {1599.0, 30.0},
		{1836.0, 98.0}})
	aggregator.Add(tsdb.TimeSeries{
		{1500.0, 1025.0},
		{1600.0, 99.0},
		{1800.0, 198.0}, {1801.0, 202.0}, {1999.1, 200.0}})
	aggregator.Add(tsdb.TimeSeries{
		{1400.0, 1975.0},
		{1450.0, 2000.0},
		{1599.0, 2025.0},
		{1599.1, 2050.0},
		{1599.2, 2075.0}})
	// Counts as all zeros
	aggregator.Add(nil)
	aggregated := aggregator.Aggregate()
	expected := tsdb.TimeSeries{
		{1000.0, 12.125},
		{1200.0, 0.0},
		{1400.0, 768.75},
		{1600.0, 24.75},
		{1800.0, 74.5}}
	assertValueDeepEqual(t, expected, aggregated)
}
Beispiel #4
0
func TestCount(t *testing.T) {
	aggregator := aggregators.New(
		1043.0,
		2021.0,
		aggregators.Count,
		200.0,
		aggregators.Avg,
		aggregators.NaN,
		nil)
	aggregator.Add(tsdb.TimeSeries{
		{1043.0, 42.0}, {1044.0, 54.0}, {1199.999, 49.5},
		{1200.0, 35.0},
		{1401.0, 20.0}, {1599.0, 30.0},
		{1836.0, 98.0},
		{2019.0, 1.3}})
	aggregator.Add(nil)
	aggregator.Add(tsdb.TimeSeries{
		{1043.0, 42.0}, {1044.0, 54.0}, {1199.999, 49.5},
		{1200.0, 53.0},
		{1401.0, 20.0}, {1599.0, 30.0},
		{1836.0, 98.0},
		{2019.0, 1.3}})
	aggregated := aggregator.Aggregate()
	expected := tsdb.TimeSeries{
		{1200.0, 2.0},
		{1400.0, 2.0},
		{1600.0, 0.0},
		{1800.0, 2.0}}
	assertValueDeepEqual(t, expected, aggregated)
}
Beispiel #5
0
func TestStartEqualsEnd(t *testing.T) {
	aggregators.New(
		1057.0,
		1057.0,
		aggregators.Avg,
		200.0,
		aggregators.Avg,
		aggregators.NaN,
		nil)
	aggregators.New(
		1000.0,
		1000.0,
		aggregators.Avg,
		200.0,
		aggregators.Avg,
		aggregators.NaN,
		nil)
}
Beispiel #6
0
func TestAverageNone(t *testing.T) {
	aggregator := aggregators.New(
		1000.0,
		2000.0,
		aggregators.Avg,
		200.0,
		aggregators.Avg,
		aggregators.NaN,
		nil)
	aggregated := aggregator.Aggregate()
	if len(aggregated) != 0 {
		t.Error("Expected no aggregation")
	}
}
Beispiel #7
0
func TestRateMissingValues(t *testing.T) {
	aggregator := aggregators.New(
		1000.0,
		2000.0,
		aggregators.Avg,
		200.0,
		aggregators.Avg,
		aggregators.NaN,
		&aggregators.RateSpec{})
	aggregator.Add(tsdb.TimeSeries{
		{1000.0, 10000.0},
		{1600.0, 49000.0},
		{1800.0, 52000.0}})
	aggregated := aggregator.Aggregate()
	expected := tsdb.TimeSeries{
		{1000.0, 65.0},
		{1200.0, 65.0},
		{1400.0, 65.0},
		{1600.0, 15.0}}
	assertValueDeepEqual(t, expected, aggregated)

	aggregator = aggregators.New(
		1000.0,
		2000.0,
		aggregators.Avg,
		200.0,
		aggregators.Avg,
		aggregators.NaN,
		&aggregators.RateSpec{})
	aggregator.Add(tsdb.TimeSeries{
		{1400.0, 10000.0},
		{1600.0, 49000.0}})
	aggregated = aggregator.Aggregate()
	expected = tsdb.TimeSeries{
		{1400.0, 195.0}}
	assertValueDeepEqual(t, expected, aggregated)
}
Beispiel #8
0
func TestAverageStrangeStartAndEnd(t *testing.T) {
	aggregator := aggregators.New(
		1057.0,
		1938.0,
		aggregators.Avg,
		200.0,
		aggregators.Avg,
		aggregators.NaN,
		nil)
	aggregator.Add(tsdb.TimeSeries{
		{1057.0, 30.0}, {1199.0, 40.0},
		{1401.0, 20.0}, {1599.0, 30.0},
		{1836.0, 98.0}})
	aggregated := aggregator.Aggregate()
	expected := tsdb.TimeSeries{{1400.0, 25.0}}
	assertValueDeepEqual(t, expected, aggregated)
}
Beispiel #9
0
func TestNegativeStart(t *testing.T) {
	aggregator := aggregators.New(
		-29028.0,
		29028.0,
		aggregators.Avg,
		10000.0,
		aggregators.Avg,
		aggregators.NaN,
		nil)
	aggregator.Add(tsdb.TimeSeries{
		{-29028.0, -60.0}, {-21000.0, -50.0},
		{-3000.0, -5.0}, {3000.0, 5.0},
		{29027.0, 43.0}})
	aggregated := aggregator.Aggregate()
	expected := tsdb.TimeSeries{
		{-10000.0, -5.0},
		{0, 5.0}}
	assertValueDeepEqual(t, expected, aggregated)
}
func (a *aggregatorTesterType) Verify(t *testing.T) {
	length := len(a.expected)
	agg := aggregators.New(
		0, float64(length)*kMaxSampleSize,
		aggregators.Sum,
		kMaxSampleSize,
		a.aggregator,
		a.fillPolicy,
		nil)
	var timeSeries tsdb.TimeSeries
	for i := range a.expected {
		for j, val := range a.expected[i].Values {
			timeSeries = append(
				timeSeries,
				tsdb.TsValue{
					float64(i)*kMaxSampleSize + float64(j),
					val})
		}
	}
	agg.Add(timeSeries)
	aggregatedTimeSeries := agg.Aggregate()
	aggIdx := 0
	for i := range a.expected {
		if !a.expected[i].Valid {
			continue
		}
		if aggIdx >= len(aggregatedTimeSeries) || aggregatedTimeSeries[aggIdx].Ts != float64(i)*kMaxSampleSize {
			t.Error(
				"Timestamps don't match. No value was emitted when one was expected or the other way around. Or maybe the Len() function is wrong.")
			return
		}
		if a.expected[i].Answer != aggregatedTimeSeries[aggIdx].Value {
			t.Errorf(
				"Expected %g for %v, got %g",
				a.expected[i].Answer,
				a.expected[i].Values,
				aggregatedTimeSeries[aggIdx].Value)
		}
		aggIdx++
	}
}
Beispiel #11
0
func newAggregatorGenerator(
	aggregatorStr string,
	downSample *DownSampleSpec,
	rateOptions *RateSpec) (
	tsdb.AggregatorGenerator, error) {
	if downSample == nil {
		return nil, ErrUnsupportedAggregator
	}
	aggregator, ok := aggregators.ByName(aggregatorStr)
	if !ok {
		return nil, ErrUnsupportedAggregator
	}
	downSampleAggregator, ok := aggregators.ByName(downSample.Type)
	if !ok {
		return nil, ErrUnsupportedAggregator
	}
	fill, _ := aggregators.ByFillPolicyName(downSample.Fill)
	duration := downSample.DurationInSeconds
	var rateSpec *aggregators.RateSpec
	if rateOptions != nil {
		rateSpec = &aggregators.RateSpec{
			Counter:    rateOptions.Counter,
			CounterMax: rateOptions.CounterMax,
			ResetValue: rateOptions.ResetValue,
		}
	}
	return func(start, end float64) (tsdb.Aggregator, error) {
		if (end-start)/duration > kMaxDownSampleBuckets {
			return nil, kErrTimeRangeTooBig
		}
		return aggregators.New(
			start,
			end,
			aggregator,
			duration,
			downSampleAggregator,
			fill,
			rateSpec), nil
	}, nil
}
Beispiel #12
0
func TestRateNoResetOrMax(t *testing.T) {
	aggregator := aggregators.New(
		1000.0,
		2000.0,
		aggregators.Avg,
		200.0,
		aggregators.Avg,
		aggregators.NaN,
		&aggregators.RateSpec{Counter: true})
	aggregator.Add(tsdb.TimeSeries{
		{1000.0, 10000.0},
		{1200.0, 49000.0},
		{1400.0, 3000.0},
		{1600.0, 53000.0},
		{1800.0, 52000.0}})
	aggregated := aggregator.Aggregate()
	expected := tsdb.TimeSeries{
		{1000.0, 195.0},
		{1200.0, 0.0},
		{1400.0, 250.0},
		{1600.0, 0.0}}
	assertValueDeepEqual(t, expected, aggregated)
}
Beispiel #13
0
func TestAPI(t *testing.T) {
	alBuilder := datastructs.NewApplicationListBuilder()
	alBuilder.Add(
		37, "AnApp", sources.ConnectorList{trisource.GetConnector()})
	alBuilder.Add(
		97, "AnotherApp", sources.ConnectorList{trisource.GetConnector()})
	appList := alBuilder.Build()
	appStatus := datastructs.NewApplicationStatuses(
		appList,
		newStore(
			t, "TestAPI", 2, 100, 1.0, 10))
	appStatus.MarkHostsActiveExclusively(
		100.0,
		[]string{"host1", "host2", "host3", "host4", "host5"})
	endpointId, aStore := appStatus.EndpointIdByHostAndName(
		"host1", "AnApp")
	addValues(t, aStore, endpointId, "/foo",
		500.0, 30.0, 510.0, 31.0, 520.0, 32.0, 530.0, 33.0, 540.0, 34.0)
	endpointId, aStore = appStatus.EndpointIdByHostAndName(
		"host1", "AnotherApp")
	addValues(t, aStore, endpointId, "/foo",
		500.0, 40.0, 510.0, 41.0, 520.0, 42.0, 530.0, 43.0, 540.0, 44.0)
	endpointId, aStore = appStatus.EndpointIdByHostAndName(
		"host2", "AnApp")
	addValues(t, aStore, endpointId, "/foo",
		500.0, 50.0, 510.0, 51.0, 520.0, 52.0, 530.0, 53.0, 540.0, 54.0)
	endpointId, aStore = appStatus.EndpointIdByHostAndName(
		"host2", "AnotherApp")
	addValues(t, aStore, endpointId, "/foo",
		500.0, 60.0, 510.0, 61.0, 520.0, 62.0, 530.0, 63.0, 540.0, 64.0)
	endpointId, aStore = appStatus.EndpointIdByHostAndName(
		"host3", "AnApp")
	addValues(t, aStore, endpointId, "/foo",
		500.0, 70.0, 510.0, 71.0, 520.0, 72.0, 530.0, 73.0, 540.0, 74.0)
	endpointId, aStore = appStatus.EndpointIdByHostAndName(
		"host3", "AnotherApp")
	addValues(t, aStore, endpointId, "/foo",
		500.0, 80.0, 510.0, 81.0, 520.0, 82.0, 530.0, 83.0, 540.0, 84.0)
	endpointId, aStore = appStatus.EndpointIdByHostAndName(
		"host5", "AnApp")
	addValues(t, aStore, endpointId, "/bar",
		700.0, 80.0, 800.0, 100.0)
	var taggedTimeSeriesSet *tsdb.TaggedTimeSeriesSet
	var err error
	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		500.0, 600.0,
		nil); err != nil {
		t.Fatal(err)
	}
	expected := &tsdb.TaggedTimeSeriesSet{
		MetricName: "/foo",
		Data: []tsdb.TaggedTimeSeries{
			{
				Values: tsdb.TimeSeries{
					{500.0, 55.5}, {520.0, 57.5}, {540.0, 59.0},
				},
			},
		},
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	if _, err = tsdbimpl.Query(
		appStatus,
		"/not there",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		500.0, 600.0,
		nil); err != tsdbimpl.ErrNoSuchMetric {
		t.Error("Expected ErrNoSuchName")
	}

	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/bar",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		500.0, 1000.0,
		nil); err != nil {
		t.Fatal(err)
	}
	expected = &tsdb.TaggedTimeSeriesSet{
		MetricName: "/bar",
		Data: []tsdb.TaggedTimeSeries{
			{
				Values: tsdb.TimeSeries{
					{700.0, 80.0}, {800.0, 100.0},
				},
			},
		},
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		501.0, 539.0,
		nil); err != nil {
		t.Fatal(err)
	}
	expected = &tsdb.TaggedTimeSeriesSet{
		MetricName: "/foo",
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	options := &tsdbimpl.QueryOptions{
		GroupByHostName: true,
	}
	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		400.0, 700.0,
		options); err != nil {
		t.Fatal(err)
	}
	expected = &tsdb.TaggedTimeSeriesSet{
		MetricName: "/foo",
		Data: []tsdb.TaggedTimeSeries{
			{
				Tags: tsdb.TagSet{
					HostName: "host1",
				},
				Values: tsdb.TimeSeries{
					{500.0, 35.5}, {520.0, 37.5}, {540.0, 39.0},
				},
			},
			{
				Tags: tsdb.TagSet{
					HostName: "host2",
				},
				Values: tsdb.TimeSeries{
					{500.0, 55.5}, {520.0, 57.5}, {540.0, 59.0},
				},
			},
			{
				Tags: tsdb.TagSet{
					HostName: "host3",
				},
				Values: tsdb.TimeSeries{
					{500.0, 75.5}, {520.0, 77.5}, {540.0, 79.0},
				},
			},
		},
		GroupedByHostName: true,
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	options = &tsdbimpl.QueryOptions{
		GroupByHostName: true,
	}
	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		700.0, 900.0,
		options); err != nil {
		t.Fatal(err)
	}
	expected = &tsdb.TaggedTimeSeriesSet{
		MetricName:        "/foo",
		GroupedByHostName: true,
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	options = &tsdbimpl.QueryOptions{
		GroupByAppName: true,
	}
	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		400.0, 700.0,
		options); err != nil {
		t.Fatal(err)
	}
	expected = &tsdb.TaggedTimeSeriesSet{
		MetricName: "/foo",
		Data: []tsdb.TaggedTimeSeries{
			{
				Tags: tsdb.TagSet{
					AppName: "AnApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 50.5}, {520.0, 52.5}, {540.0, 54.0},
				},
			},
			{
				Tags: tsdb.TagSet{
					AppName: "AnotherApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 60.5}, {520.0, 62.5}, {540.0, 64},
				},
			},
		},
		GroupedByAppName: true,
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	options = &tsdbimpl.QueryOptions{
		GroupByHostName: true,
		GroupByAppName:  true,
	}
	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		400.0, 700.0,
		options); err != nil {
		t.Fatal(err)
	}
	expected = &tsdb.TaggedTimeSeriesSet{
		MetricName: "/foo",
		Data: []tsdb.TaggedTimeSeries{
			{
				Tags: tsdb.TagSet{
					HostName: "host1",
					AppName:  "AnApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 30.5}, {520.0, 32.5}, {540.0, 34.0},
				},
			},
			{
				Tags: tsdb.TagSet{
					HostName: "host1",
					AppName:  "AnotherApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 40.5}, {520.0, 42.5}, {540.0, 44.0},
				},
			},
			{
				Tags: tsdb.TagSet{
					HostName: "host2",
					AppName:  "AnApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 50.5}, {520.0, 52.5}, {540.0, 54.0},
				},
			},
			{
				Tags: tsdb.TagSet{
					HostName: "host2",
					AppName:  "AnotherApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 60.5}, {520.0, 62.5}, {540.0, 64.0},
				},
			},
			{
				Tags: tsdb.TagSet{
					HostName: "host3",
					AppName:  "AnApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 70.5}, {520.0, 72.5}, {540.0, 74.0},
				},
			},
			{
				Tags: tsdb.TagSet{
					HostName: "host3",
					AppName:  "AnotherApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 80.5}, {520.0, 82.5}, {540.0, 84.0},
				},
			},
		},
		GroupedByHostName: true,
		GroupedByAppName:  true,
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	options = &tsdbimpl.QueryOptions{
		GroupByHostName: true,
		GroupByAppName:  true,
	}
	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		700.0, 900.0,
		options); err != nil {
		t.Fatal(err)
	}
	expected = &tsdb.TaggedTimeSeriesSet{
		MetricName:        "/foo",
		GroupedByHostName: true,
		GroupedByAppName:  true,
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	options = &tsdbimpl.QueryOptions{
		HostNameFilter: tagFilter(func(s string) bool {
			return s == "host2" || s == "host3"
		}),
		AppNameFilter: tagFilter(func(s string) bool {
			return s == "AnApp"
		}),
		GroupByHostName: true,
		GroupByAppName:  true,
	}
	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		400.0, 700.0,
		options); err != nil {
		t.Fatal(err)
	}
	expected = &tsdb.TaggedTimeSeriesSet{
		MetricName: "/foo",
		Data: []tsdb.TaggedTimeSeries{
			{
				Tags: tsdb.TagSet{
					HostName: "host2",
					AppName:  "AnApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 50.5}, {520.0, 52.5}, {540.0, 54.0},
				},
			},
			{
				Tags: tsdb.TagSet{
					HostName: "host3",
					AppName:  "AnApp",
				},
				Values: tsdb.TimeSeries{
					{500.0, 70.5}, {520.0, 72.5}, {540.0, 74.0},
				},
			},
		},
		GroupedByHostName: true,
		GroupedByAppName:  true,
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	options = &tsdbimpl.QueryOptions{
		HostNameFilter: tagFilter(func(s string) bool {
			return s == "host2" || s == "host3"
		}),
		AppNameFilter: tagFilter(func(s string) bool {
			return s == "AnApp"
		}),
	}
	if taggedTimeSeriesSet, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		400.0, 700.0,
		options); err != nil {
		t.Fatal(err)
	}
	expected = &tsdb.TaggedTimeSeriesSet{
		MetricName: "/foo",
		Data: []tsdb.TaggedTimeSeries{
			{
				Values: tsdb.TimeSeries{
					{500.0, 60.5}, {520.0, 62.5}, {540.0, 64.0},
				},
			},
		},
	}
	assertTaggedTimeSeriesSetEquals(
		t,
		expected,
		taggedTimeSeriesSet,
	)

	options = &tsdbimpl.QueryOptions{
		HostNameFilter: tagFilter(func(s string) bool {
			return s == "host2" || s == "host3"
		}),
		AppNameFilter: tagFilter(func(s string) bool {
			return s == "No app"
		}),
	}
	if _, err = tsdbimpl.Query(
		appStatus,
		"/foo",
		func(start, end float64) (tsdb.Aggregator, error) {
			return aggregators.New(
				start,
				end,
				aggregators.Avg,
				20.0,
				aggregators.Avg,
				aggregators.NaN,
				nil), nil
		},
		400.0, 700.0,
		options); err != tsdbimpl.ErrNoSuchMetric {
		t.Error("Expected ErrNoSuchMetric")
	}
}