1package metrics
2
3import (
4	"testing"
5	"time"
6
7	"github.com/pascaldekloe/goe/verify"
8)
9
10func TestDisplayMetrics(t *testing.T) {
11	interval := 10 * time.Millisecond
12	inm := NewInmemSink(interval, 50*time.Millisecond)
13
14	// Add data points
15	inm.SetGauge([]string{"foo", "bar"}, 42)
16	inm.SetGaugeWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}})
17	inm.EmitKey([]string{"foo", "bar"}, 42)
18	inm.IncrCounter([]string{"foo", "bar"}, 20)
19	inm.IncrCounter([]string{"foo", "bar"}, 22)
20	inm.IncrCounterWithLabels([]string{"foo", "bar"}, 20, []Label{{"a", "b"}})
21	inm.IncrCounterWithLabels([]string{"foo", "bar"}, 40, []Label{{"a", "b"}})
22	inm.AddSample([]string{"foo", "bar"}, 20)
23	inm.AddSample([]string{"foo", "bar"}, 24)
24	inm.AddSampleWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}})
25	inm.AddSampleWithLabels([]string{"foo", "bar"}, 33, []Label{{"a", "b"}})
26
27	data := inm.Data()
28	if len(data) != 1 {
29		t.Fatalf("bad: %v", data)
30	}
31
32	expected := MetricsSummary{
33		Timestamp: data[0].Interval.Round(time.Second).UTC().String(),
34		Gauges: []GaugeValue{
35			{
36				Name:          "foo.bar",
37				Hash:          "foo.bar",
38				Value:         float32(42),
39				DisplayLabels: map[string]string{},
40			},
41			{
42				Name:          "foo.bar",
43				Hash:          "foo.bar;a=b",
44				Value:         float32(23),
45				DisplayLabels: map[string]string{"a": "b"},
46			},
47		},
48		Points: []PointValue{
49			{
50				Name:   "foo.bar",
51				Points: []float32{42},
52			},
53		},
54		Counters: []SampledValue{
55			{
56				Name: "foo.bar",
57				Hash: "foo.bar",
58				AggregateSample: &AggregateSample{
59					Count: 2,
60					Min:   20,
61					Max:   22,
62					Sum:   42,
63					SumSq: 884,
64					Rate:  4200,
65				},
66				Mean:   21,
67				Stddev: 1.4142135623730951,
68			},
69			{
70				Name: "foo.bar",
71				Hash: "foo.bar;a=b",
72				AggregateSample: &AggregateSample{
73					Count: 2,
74					Min:   20,
75					Max:   40,
76					Sum:   60,
77					SumSq: 2000,
78					Rate:  6000,
79				},
80				Mean:          30,
81				Stddev:        14.142135623730951,
82				DisplayLabels: map[string]string{"a": "b"},
83			},
84		},
85		Samples: []SampledValue{
86			{
87				Name: "foo.bar",
88				Hash: "foo.bar",
89				AggregateSample: &AggregateSample{
90					Count: 2,
91					Min:   20,
92					Max:   24,
93					Sum:   44,
94					SumSq: 976,
95					Rate:  4400,
96				},
97				Mean:   22,
98				Stddev: 2.8284271247461903,
99			},
100			{
101				Name: "foo.bar",
102				Hash: "foo.bar;a=b",
103				AggregateSample: &AggregateSample{
104					Count: 2,
105					Min:   23,
106					Max:   33,
107					Sum:   56,
108					SumSq: 1618,
109					Rate:  5600,
110				},
111				Mean:          28,
112				Stddev:        7.0710678118654755,
113				DisplayLabels: map[string]string{"a": "b"},
114			},
115		},
116	}
117
118	raw, err := inm.DisplayMetrics(nil, nil)
119	if err != nil {
120		t.Fatalf("err: %v", err)
121	}
122	result := raw.(MetricsSummary)
123
124	// Ignore the LastUpdated field, we don't export that anyway
125	for i, got := range result.Counters {
126		expected.Counters[i].LastUpdated = got.LastUpdated
127	}
128	for i, got := range result.Samples {
129		expected.Samples[i].LastUpdated = got.LastUpdated
130	}
131
132	verify.Values(t, "all", result, expected)
133}
134
135func TestDisplayMetrics_RaceSetGauge(t *testing.T) {
136	interval := 200 * time.Millisecond
137	inm := NewInmemSink(interval, 10*interval)
138	result := make(chan float32)
139
140	go func() {
141		for {
142			time.Sleep(150 * time.Millisecond)
143			inm.SetGauge([]string{"foo", "bar"}, float32(42))
144		}
145	}()
146
147	go func() {
148		start := time.Now()
149		var summary MetricsSummary
150		// test for twenty intervals
151		for time.Now().Sub(start) < 20*interval {
152			time.Sleep(100 * time.Millisecond)
153			raw, _ := inm.DisplayMetrics(nil, nil)
154			summary = raw.(MetricsSummary)
155		}
156		// save result
157		for _, g := range summary.Gauges {
158			if g.Name == "foo.bar" {
159				result <- g.Value
160			}
161		}
162		close(result)
163	}()
164
165	got := <-result
166	verify.Values(t, "all", got, float32(42))
167}
168
169func TestDisplayMetrics_RaceAddSample(t *testing.T) {
170	interval := 200 * time.Millisecond
171	inm := NewInmemSink(interval, 10*interval)
172	result := make(chan float32)
173
174	go func() {
175		for {
176			time.Sleep(75 * time.Millisecond)
177			inm.AddSample([]string{"foo", "bar"}, float32(0.0))
178		}
179	}()
180
181	go func() {
182		start := time.Now()
183		var summary MetricsSummary
184		// test for twenty intervals
185		for time.Now().Sub(start) < 20*interval {
186			time.Sleep(100 * time.Millisecond)
187			raw, _ := inm.DisplayMetrics(nil, nil)
188			summary = raw.(MetricsSummary)
189		}
190		// save result
191		for _, g := range summary.Gauges {
192			if g.Name == "foo.bar" {
193				result <- g.Value
194			}
195		}
196		close(result)
197	}()
198
199	got := <-result
200	verify.Values(t, "all", got, float32(0.0))
201}
202
203func TestDisplayMetrics_RaceIncrCounter(t *testing.T) {
204	interval := 200 * time.Millisecond
205	inm := NewInmemSink(interval, 10*interval)
206	result := make(chan float32)
207
208	go func() {
209		for {
210			time.Sleep(75 * time.Millisecond)
211			inm.IncrCounter([]string{"foo", "bar"}, float32(0.0))
212		}
213	}()
214
215	go func() {
216		start := time.Now()
217		var summary MetricsSummary
218		// test for twenty intervals
219		for time.Now().Sub(start) < 20*interval {
220			time.Sleep(30 * time.Millisecond)
221			raw, _ := inm.DisplayMetrics(nil, nil)
222			summary = raw.(MetricsSummary)
223		}
224		// save result for testing
225		for _, g := range summary.Gauges {
226			if g.Name == "foo.bar" {
227				result <- g.Value
228			}
229		}
230		close(result)
231	}()
232
233	got := <-result
234	verify.Values(t, "all", got, float32(0.0))
235}
236
237func TestDisplayMetrics_RaceMetricsSetGauge(t *testing.T) {
238	interval := 200 * time.Millisecond
239	inm := NewInmemSink(interval, 10*interval)
240	met := &Metrics{Config: Config{FilterDefault: true}, sink: inm}
241	result := make(chan float32)
242	labels := []Label{
243		{"name1", "value1"},
244		{"name2", "value2"},
245	}
246
247	go func() {
248		for {
249			time.Sleep(75 * time.Millisecond)
250			met.SetGaugeWithLabels([]string{"foo", "bar"}, float32(42), labels)
251		}
252	}()
253
254	go func() {
255		start := time.Now()
256		var summary MetricsSummary
257		// test for twenty intervals
258		for time.Now().Sub(start) < 40*interval {
259			time.Sleep(150 * time.Millisecond)
260			raw, _ := inm.DisplayMetrics(nil, nil)
261			summary = raw.(MetricsSummary)
262		}
263		// save result
264		for _, g := range summary.Gauges {
265			if g.Name == "foo.bar" {
266				result <- g.Value
267			}
268		}
269		close(result)
270	}()
271
272	got := <-result
273	verify.Values(t, "all", got, float32(42))
274}
275
276