1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package view
17
18 import (
19 "context"
20 "testing"
21 "time"
22
23 "github.com/google/go-cmp/cmp"
24 "github.com/google/go-cmp/cmp/cmpopts"
25
26 "go.opencensus.io/metric/metricdata"
27
28 "go.opencensus.io/stats"
29 "go.opencensus.io/tag"
30 )
31
32 func Test_View_MeasureFloat64_AggregationDistribution(t *testing.T) {
33 k1 := tag.MustNewKey("k1")
34 k2 := tag.MustNewKey("k2")
35 k3 := tag.MustNewKey("k3")
36 agg1 := Distribution(2)
37 m := stats.Int64("Test_View_MeasureFloat64_AggregationDistribution/m1", "", stats.UnitDimensionless)
38 view1 := &View{
39 TagKeys: []tag.Key{k1, k2},
40 Measure: m,
41 Aggregation: agg1,
42 }
43 view, err := newViewInternal(view1)
44 if err != nil {
45 t.Fatal(err)
46 }
47
48 type tagString struct {
49 k tag.Key
50 v string
51 }
52 type record struct {
53 f float64
54 tags []tagString
55 t time.Time
56 }
57
58 type testCase struct {
59 label string
60 records []record
61 wantRows []*Row
62 }
63
64 now := time.Now()
65 ts := make([]time.Time, 7)
66 for i := range ts {
67 ts[i] = now.Add(time.Duration(i) * time.Second)
68 }
69 tcs := []testCase{
70 {
71 "1",
72 []record{
73 {1, []tagString{{k1, "v1"}}, ts[0]},
74 {5, []tagString{{k1, "v1"}}, ts[1]},
75 },
76 []*Row{
77 {
78 []tag.Tag{{Key: k1, Value: "v1"}},
79 &DistributionData{
80 Count: 2, Min: 1, Max: 5, Mean: 3, SumOfSquaredDev: 8, CountPerBucket: []int64{1, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[0],
81 },
82 },
83 },
84 },
85 {
86 "2",
87 []record{
88 {1, []tagString{{k1, "v1"}}, ts[0]},
89 {5, []tagString{{k2, "v2"}}, ts[1]},
90 },
91 []*Row{
92 {
93 []tag.Tag{{Key: k1, Value: "v1"}},
94 &DistributionData{
95 Count: 1, Min: 1, Max: 1, Mean: 1, CountPerBucket: []int64{1, 0}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[0],
96 },
97 },
98 {
99 []tag.Tag{{Key: k2, Value: "v2"}},
100 &DistributionData{
101 Count: 1, Min: 5, Max: 5, Mean: 5, CountPerBucket: []int64{0, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[1],
102 },
103 },
104 },
105 },
106 {
107 "3",
108 []record{
109 {1, []tagString{{k1, "v1"}}, ts[0]},
110 {5, []tagString{{k1, "v1"}, {k3, "v3"}}, ts[1]},
111 {1, []tagString{{k1, "v1 other"}}, ts[2]},
112 {5, []tagString{{k2, "v2"}}, ts[3]},
113 {5, []tagString{{k1, "v1"}, {k2, "v2"}}, ts[4]},
114 },
115 []*Row{
116 {
117 []tag.Tag{{Key: k1, Value: "v1"}},
118 &DistributionData{
119 Count: 2, Min: 1, Max: 5, Mean: 3, SumOfSquaredDev: 8, CountPerBucket: []int64{1, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[0],
120 },
121 },
122 {
123 []tag.Tag{{Key: k1, Value: "v1 other"}},
124 &DistributionData{
125 Count: 1, Min: 1, Max: 1, Mean: 1, CountPerBucket: []int64{1, 0}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[2],
126 },
127 },
128 {
129 []tag.Tag{{Key: k2, Value: "v2"}},
130 &DistributionData{
131 Count: 1, Min: 5, Max: 5, Mean: 5, CountPerBucket: []int64{0, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[3],
132 },
133 },
134 {
135 []tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
136 &DistributionData{
137 Count: 1, Min: 5, Max: 5, Mean: 5, CountPerBucket: []int64{0, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[4],
138 },
139 },
140 },
141 },
142 {
143 "4",
144 []record{
145 {1, []tagString{{k1, "v1 is a very long value key"}}, ts[0]},
146 {5, []tagString{{k1, "v1 is a very long value key"}, {k3, "v3"}}, ts[1]},
147 {1, []tagString{{k1, "v1 is another very long value key"}}, ts[2]},
148 {1, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}, ts[3]},
149 {5, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}, ts[4]},
150 {3, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}, ts[5]},
151 {3, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}, ts[6]},
152 },
153 []*Row{
154 {
155 []tag.Tag{{Key: k1, Value: "v1 is a very long value key"}},
156 &DistributionData{
157 Count: 2, Min: 1, Max: 5, Mean: 3, SumOfSquaredDev: 8, CountPerBucket: []int64{1, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[0],
158 },
159 },
160 {
161 []tag.Tag{{Key: k1, Value: "v1 is another very long value key"}},
162 &DistributionData{
163 Count: 1, Min: 1, Max: 1, Mean: 1, CountPerBucket: []int64{1, 0}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[2],
164 },
165 },
166 {
167 []tag.Tag{{Key: k1, Value: "v1 is a very long value key"}, {Key: k2, Value: "v2 is a very long value key"}},
168 &DistributionData{
169 Count: 4, Min: 1, Max: 5, Mean: 3, SumOfSquaredDev: 2.66666666666667 * 3, CountPerBucket: []int64{1, 3}, bounds: []float64{2}, ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil}, Start: ts[3],
170 },
171 },
172 },
173 },
174 }
175
176 for _, tc := range tcs {
177 view.clearRows()
178 view.subscribe()
179 for _, r := range tc.records {
180 mods := []tag.Mutator{}
181 for _, t := range r.tags {
182 mods = append(mods, tag.Insert(t.k, t.v))
183 }
184 ctx, err := tag.New(context.Background(), mods...)
185 if err != nil {
186 t.Errorf("%v: New = %v", tc.label, err)
187 }
188 view.addSample(tag.FromContext(ctx), r.f, nil, r.t)
189 }
190
191 gotRows := view.collectedRows()
192 if diff := cmp.Diff(gotRows, tc.wantRows, cmpopts.SortSlices(cmpRow)); diff != "" {
193 t.Errorf("%v: unexpected row (got-, want+): %s", tc.label, diff)
194 break
195 }
196 }
197 }
198
199 func Test_View_MeasureFloat64_AggregationSum(t *testing.T) {
200 k1 := tag.MustNewKey("k1")
201 k2 := tag.MustNewKey("k2")
202 k3 := tag.MustNewKey("k3")
203 m := stats.Int64("Test_View_MeasureFloat64_AggregationSum/m1", "", stats.UnitDimensionless)
204 view, err := newViewInternal(&View{TagKeys: []tag.Key{k1, k2}, Measure: m, Aggregation: Sum()})
205 if err != nil {
206 t.Fatal(err)
207 }
208
209 type tagString struct {
210 k tag.Key
211 v string
212 }
213 type record struct {
214 f float64
215 tags []tagString
216 t time.Time
217 }
218
219 now := time.Now()
220 ts := make([]time.Time, 5)
221 for i := range ts {
222 ts[i] = now.Add(time.Duration(i) * time.Second)
223 }
224 tcs := []struct {
225 label string
226 records []record
227 wantRows []*Row
228 }{
229 {
230 "1",
231 []record{
232 {1, []tagString{{k1, "v1"}}, ts[0]},
233 {5, []tagString{{k1, "v1"}}, ts[1]},
234 },
235 []*Row{
236 {
237 []tag.Tag{{Key: k1, Value: "v1"}},
238 &SumData{Value: 6, Start: ts[0]},
239 },
240 },
241 },
242 {
243 "2",
244 []record{
245 {1, []tagString{{k1, "v1"}}, ts[0]},
246 {5, []tagString{{k2, "v2"}}, ts[1]},
247 },
248 []*Row{
249 {
250 []tag.Tag{{Key: k1, Value: "v1"}},
251 &SumData{Value: 1, Start: ts[0]},
252 },
253 {
254 []tag.Tag{{Key: k2, Value: "v2"}},
255 &SumData{Value: 5, Start: ts[1]},
256 },
257 },
258 },
259 {
260 "3",
261 []record{
262 {1, []tagString{{k1, "v1"}}, ts[0]},
263 {5, []tagString{{k1, "v1"}, {k3, "v3"}}, ts[1]},
264 {1, []tagString{{k1, "v1 other"}}, ts[2]},
265 {5, []tagString{{k2, "v2"}}, ts[3]},
266 {5, []tagString{{k1, "v1"}, {k2, "v2"}}, ts[4]},
267 },
268 []*Row{
269 {
270 []tag.Tag{{Key: k1, Value: "v1"}},
271 &SumData{Value: 6, Start: ts[0]},
272 },
273 {
274 []tag.Tag{{Key: k1, Value: "v1 other"}},
275 &SumData{Value: 1, Start: ts[2]},
276 },
277 {
278 []tag.Tag{{Key: k2, Value: "v2"}},
279 &SumData{Value: 5, Start: ts[3]},
280 },
281 {
282 []tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
283 &SumData{Value: 5, Start: ts[4]},
284 },
285 },
286 },
287 }
288
289 for _, tt := range tcs {
290 view.clearRows()
291 view.subscribe()
292 for _, r := range tt.records {
293 mods := []tag.Mutator{}
294 for _, t := range r.tags {
295 mods = append(mods, tag.Insert(t.k, t.v))
296 }
297 ctx, err := tag.New(context.Background(), mods...)
298 if err != nil {
299 t.Errorf("%v: New = %v", tt.label, err)
300 }
301 view.addSample(tag.FromContext(ctx), r.f, nil, r.t)
302 }
303
304 gotRows := view.collectedRows()
305 if diff := cmp.Diff(gotRows, tt.wantRows, cmpopts.SortSlices(cmpRow)); diff != "" {
306 t.Errorf("%v: unexpected row (got-, want+): %s", tt.label, diff)
307 break
308 }
309 }
310 }
311
312 func TestCanonicalize(t *testing.T) {
313 k1 := tag.MustNewKey("k1")
314 k2 := tag.MustNewKey("k2")
315 m := stats.Int64("TestCanonicalize/m1", "desc desc", stats.UnitDimensionless)
316 v := &View{TagKeys: []tag.Key{k2, k1}, Measure: m, Aggregation: Sum()}
317 err := v.canonicalize()
318 if err != nil {
319 t.Fatal(err)
320 }
321 if got, want := v.Name, "TestCanonicalize/m1"; got != want {
322 t.Errorf("vc.Name = %q; want %q", got, want)
323 }
324 if got, want := v.Description, "desc desc"; got != want {
325 t.Errorf("vc.Description = %q; want %q", got, want)
326 }
327 if got, want := len(v.TagKeys), 2; got != want {
328 t.Errorf("len(vc.TagKeys) = %d; want %d", got, want)
329 }
330 if got, want := v.TagKeys[0].Name(), "k1"; got != want {
331 t.Errorf("vc.TagKeys[0].Name() = %q; want %q", got, want)
332 }
333 }
334
335 func TestViewSortedKeys(t *testing.T) {
336 k1 := tag.MustNewKey("a")
337 k2 := tag.MustNewKey("b")
338 k3 := tag.MustNewKey("c")
339 ks := []tag.Key{k1, k3, k2}
340
341 m := stats.Int64("TestViewSortedKeys/m1", "", stats.UnitDimensionless)
342 Register(&View{
343 Name: "sort_keys",
344 Description: "desc sort_keys",
345 TagKeys: ks,
346 Measure: m,
347 Aggregation: Sum(),
348 })
349
350 v := Find("sort_keys")
351
352 want := []string{"a", "b", "c"}
353 vks := v.TagKeys
354 if len(vks) != len(want) {
355 t.Errorf("Keys = %+v; want %+v", vks, want)
356 }
357
358 for i, v := range want {
359 if got, want := v, vks[i].Name(); got != want {
360 t.Errorf("View name = %q; want %q", got, want)
361 }
362 }
363 }
364
365 func cmpRow(r1 *Row, r2 *Row) bool {
366 return r1.Data.StartTime().Before(r2.Data.StartTime())
367 }
368
369 func TestRegisterUnregisterParity(t *testing.T) {
370 measures := []stats.Measure{
371 stats.Int64("ifoo", "iFOO", "iBar"),
372 stats.Float64("ffoo", "fFOO", "fBar"),
373 }
374 aggregations := []*Aggregation{
375 Count(),
376 Sum(),
377 Distribution(1, 2.0, 4.0, 8.0, 16.0),
378 }
379
380 for i := 0; i < 10; i++ {
381 for _, m := range measures {
382 for _, agg := range aggregations {
383 v := &View{
384 Aggregation: agg,
385 Name: "Lookup here",
386 Measure: m,
387 }
388 if err := Register(v); err != nil {
389 t.Errorf("Iteration #%d:\nMeasure: (%#v)\nAggregation (%#v)\nError: %v", i, m, agg, err)
390 }
391 Unregister(v)
392 }
393 }
394 }
395 }
396
397 func TestRegisterAfterMeasurement(t *testing.T) {
398
399
400
401 m := stats.Int64(t.Name(), "", stats.UnitDimensionless)
402 mm := m.M(1)
403 ctx := context.Background()
404
405 stats.Record(ctx, mm)
406 v := &View{
407 Measure: m,
408 Aggregation: Count(),
409 }
410 if err := Register(v); err != nil {
411 t.Fatal(err)
412 }
413
414 rows, err := RetrieveData(v.Name)
415 if err != nil {
416 t.Fatal(err)
417 }
418 if len(rows) > 0 {
419 t.Error("View should not have data")
420 }
421
422 stats.Record(ctx, mm)
423
424 rows, err = RetrieveData(v.Name)
425 if err != nil {
426 t.Fatal(err)
427 }
428 if len(rows) == 0 {
429 t.Error("View should have data")
430 }
431 }
432
433 func TestViewRegister_negativeBucketBounds(t *testing.T) {
434 m := stats.Int64("TestViewRegister_negativeBucketBounds", "", "")
435 v := &View{
436 Measure: m,
437 Aggregation: Distribution(-1, 2),
438 }
439 err := Register(v)
440 if err != ErrNegativeBucketBounds {
441 t.Errorf("Expected ErrNegativeBucketBounds, got %v", err)
442 }
443 }
444
445 func TestViewRegister_sortBuckets(t *testing.T) {
446 m := stats.Int64("TestViewRegister_sortBuckets", "", "")
447 v := &View{
448 Measure: m,
449 Aggregation: Distribution(2, 1),
450 }
451 err := Register(v)
452 if err != nil {
453 t.Fatalf("Unexpected err %s", err)
454 }
455 want := []float64{1, 2}
456 if diff := cmp.Diff(v.Aggregation.Buckets, want); diff != "" {
457 t.Errorf("buckets differ -got +want: %s", diff)
458 }
459 }
460
461 func TestViewRegister_dropZeroBuckets(t *testing.T) {
462 m := stats.Int64("TestViewRegister_dropZeroBuckets", "", "")
463 v := &View{
464 Measure: m,
465 Aggregation: Distribution(2, 0, 1),
466 }
467 err := Register(v)
468 if err != nil {
469 t.Fatalf("Unexpected err %s", err)
470 }
471 want := []float64{1, 2}
472 if diff := cmp.Diff(v.Aggregation.Buckets, want); diff != "" {
473 t.Errorf("buckets differ -got +want: %s", diff)
474 }
475 }
476
View as plain text