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 "encoding/json"
24
25 "github.com/google/go-cmp/cmp"
26 "go.opencensus.io/metric/metricdata"
27 "go.opencensus.io/metric/metricexport"
28 "go.opencensus.io/stats"
29 "go.opencensus.io/tag"
30 )
31
32 type recordValWithTag struct {
33 tags []tag.Tag
34 value interface{}
35 }
36 type testToMetrics struct {
37 vi *viewInternal
38 view *View
39 recordValue []recordValWithTag
40 wantMetric *metricdata.Metric
41 }
42
43 var (
44
45 tk1 tag.Key
46 tk2 tag.Key
47 tk3 tag.Key
48 tk1v1 tag.Tag
49 tk2v2 tag.Tag
50 tags []tag.Tag
51
52 labelValues []metricdata.LabelValue
53 emptyLabelValues []metricdata.LabelValue
54
55 labelKeys []metricdata.LabelKey
56
57 recordsInt64 []recordValWithTag
58 recordsFloat64 []recordValWithTag
59 recordsFloat64WoTag []recordValWithTag
60
61
62 aggDist *Aggregation
63 aggCnt *Aggregation
64 aggS *Aggregation
65 aggL *Aggregation
66 buckOpt *metricdata.BucketOptions
67
68
69 attachments metricdata.Attachments
70
71
72 viewTypeFloat64Distribution *View
73 viewTypeInt64Distribution *View
74 viewTypeInt64Count *View
75 viewTypeFloat64Count *View
76 viewTypeFloat64Sum *View
77 viewTypeInt64Sum *View
78 viewTypeFloat64LastValue *View
79 viewTypeInt64LastValue *View
80 viewRecordWithoutLabel *View
81 mdTypeFloat64CumulativeDistribution metricdata.Descriptor
82 mdTypeInt64CumulativeDistribution metricdata.Descriptor
83 mdTypeInt64CumulativeCount metricdata.Descriptor
84 mdTypeFloat64CumulativeCount metricdata.Descriptor
85 mdTypeInt64CumulativeSum metricdata.Descriptor
86 mdTypeFloat64CumulativeSum metricdata.Descriptor
87 mdTypeInt64CumulativeLastValue metricdata.Descriptor
88 mdTypeFloat64CumulativeLastValue metricdata.Descriptor
89 mdTypeRecordWithoutLabel metricdata.Descriptor
90 )
91
92 const (
93 nameInt64DistM1 = "viewToMetricTest_Int64_Distribution/m1"
94 nameFloat64DistM1 = "viewToMetricTest_Float64_Distribution/m1"
95 nameInt64CountM1 = "viewToMetricTest_Int64_Count/m1"
96 nameFloat64CountM1 = "viewToMetricTest_Float64_Count/m1"
97 nameInt64SumM1 = "viewToMetricTest_Int64_Sum/m1"
98 nameFloat64SumM1 = "viewToMetricTest_Float64_Sum/m1"
99 nameInt64LastValueM1 = "viewToMetricTest_Int64_LastValue/m1"
100 nameFloat64LastValueM1 = "viewToMetricTest_Float64_LastValue/m1"
101 nameRecordWithoutLabel = "viewToMetricTest_RecordWithoutLabel/m1"
102 v1 = "v1"
103 v2 = "v2"
104 )
105
106 func init() {
107 initTags()
108 initAgg()
109 initViews()
110 initMetricDescriptors()
111
112 }
113
114 func initTags() {
115 tk1 = tag.MustNewKey("k1")
116 tk2 = tag.MustNewKey("k2")
117 tk3 = tag.MustNewKey("k3")
118 tk1v1 = tag.Tag{Key: tk1, Value: v1}
119 tk2v2 = tag.Tag{Key: tk2, Value: v2}
120
121 tags = []tag.Tag{tk1v1, tk2v2}
122 labelValues = []metricdata.LabelValue{
123 {Value: v1, Present: true},
124 {Value: v2, Present: true},
125 }
126 emptyLabelValues = []metricdata.LabelValue{
127 {Value: "", Present: false},
128 {Value: "", Present: false},
129 }
130 labelKeys = []metricdata.LabelKey{
131 {Key: tk1.Name()},
132 {Key: tk2.Name()},
133 }
134
135 recordsInt64 = []recordValWithTag{
136 {tags: tags, value: int64(2)},
137 {tags: tags, value: int64(4)},
138 }
139 recordsFloat64 = []recordValWithTag{
140 {tags: tags, value: float64(1.5)},
141 {tags: tags, value: float64(5.4)},
142 }
143 recordsFloat64WoTag = []recordValWithTag{
144 {value: float64(1.5)},
145 {value: float64(5.4)},
146 }
147 }
148
149 func initAgg() {
150 aggDist = Distribution(2.0)
151 aggCnt = Count()
152 aggS = Sum()
153 aggL = LastValue()
154 buckOpt = &metricdata.BucketOptions{Bounds: []float64{2.0}}
155 }
156
157 func initViews() {
158
159 viewTypeInt64Distribution = &View{
160 Name: nameInt64DistM1,
161 TagKeys: []tag.Key{tk1, tk2},
162 Measure: stats.Int64(nameInt64DistM1, "", stats.UnitDimensionless),
163 Aggregation: aggDist,
164 }
165 viewTypeFloat64Distribution = &View{
166 Name: nameFloat64DistM1,
167 TagKeys: []tag.Key{tk1, tk2},
168 Measure: stats.Float64(nameFloat64DistM1, "", stats.UnitDimensionless),
169 Aggregation: aggDist,
170 }
171 viewTypeInt64Count = &View{
172 Name: nameInt64CountM1,
173 TagKeys: []tag.Key{tk1, tk2},
174 Measure: stats.Int64(nameInt64CountM1, "", stats.UnitDimensionless),
175 Aggregation: aggCnt,
176 }
177 viewTypeFloat64Count = &View{
178 Name: nameFloat64CountM1,
179 TagKeys: []tag.Key{tk1, tk2},
180 Measure: stats.Float64(nameFloat64CountM1, "", stats.UnitDimensionless),
181 Aggregation: aggCnt,
182 }
183 viewTypeInt64Sum = &View{
184 Name: nameInt64SumM1,
185 TagKeys: []tag.Key{tk1, tk2},
186 Measure: stats.Int64(nameInt64SumM1, "", stats.UnitBytes),
187 Aggregation: aggS,
188 }
189 viewTypeFloat64Sum = &View{
190 Name: nameFloat64SumM1,
191 TagKeys: []tag.Key{tk1, tk2},
192 Measure: stats.Float64(nameFloat64SumM1, "", stats.UnitMilliseconds),
193 Aggregation: aggS,
194 }
195 viewTypeInt64LastValue = &View{
196 Name: nameInt64LastValueM1,
197 TagKeys: []tag.Key{tk1, tk2},
198 Measure: stats.Int64(nameInt64LastValueM1, "", stats.UnitDimensionless),
199 Aggregation: aggL,
200 }
201 viewTypeFloat64LastValue = &View{
202 Name: nameFloat64LastValueM1,
203 TagKeys: []tag.Key{tk1, tk2},
204 Measure: stats.Float64(nameFloat64LastValueM1, "", stats.UnitDimensionless),
205 Aggregation: aggL,
206 }
207 viewRecordWithoutLabel = &View{
208 Name: nameRecordWithoutLabel,
209 TagKeys: []tag.Key{tk1, tk2},
210 Measure: stats.Float64(nameRecordWithoutLabel, "", stats.UnitDimensionless),
211 Aggregation: aggL,
212 }
213 }
214
215 func initMetricDescriptors() {
216
217 mdTypeFloat64CumulativeDistribution = metricdata.Descriptor{
218 Name: nameFloat64DistM1, Description: "", Unit: metricdata.UnitDimensionless,
219 Type: metricdata.TypeCumulativeDistribution, LabelKeys: labelKeys,
220 }
221 mdTypeInt64CumulativeDistribution = metricdata.Descriptor{
222 Name: nameInt64DistM1, Description: "", Unit: metricdata.UnitDimensionless,
223 Type: metricdata.TypeCumulativeDistribution, LabelKeys: labelKeys,
224 }
225 mdTypeInt64CumulativeCount = metricdata.Descriptor{
226 Name: nameInt64CountM1, Description: "", Unit: metricdata.UnitDimensionless,
227 Type: metricdata.TypeCumulativeInt64, LabelKeys: labelKeys,
228 }
229 mdTypeFloat64CumulativeCount = metricdata.Descriptor{
230 Name: nameFloat64CountM1, Description: "", Unit: metricdata.UnitDimensionless,
231 Type: metricdata.TypeCumulativeInt64, LabelKeys: labelKeys,
232 }
233 mdTypeInt64CumulativeSum = metricdata.Descriptor{
234 Name: nameInt64SumM1, Description: "", Unit: metricdata.UnitBytes,
235 Type: metricdata.TypeCumulativeInt64, LabelKeys: labelKeys,
236 }
237 mdTypeFloat64CumulativeSum = metricdata.Descriptor{
238 Name: nameFloat64SumM1, Description: "", Unit: metricdata.UnitMilliseconds,
239 Type: metricdata.TypeCumulativeFloat64, LabelKeys: labelKeys,
240 }
241 mdTypeInt64CumulativeLastValue = metricdata.Descriptor{
242 Name: nameInt64LastValueM1, Description: "", Unit: metricdata.UnitDimensionless,
243 Type: metricdata.TypeGaugeInt64, LabelKeys: labelKeys,
244 }
245 mdTypeFloat64CumulativeLastValue = metricdata.Descriptor{
246 Name: nameFloat64LastValueM1, Description: "", Unit: metricdata.UnitDimensionless,
247 Type: metricdata.TypeGaugeFloat64, LabelKeys: labelKeys,
248 }
249 mdTypeRecordWithoutLabel = metricdata.Descriptor{
250 Name: nameRecordWithoutLabel, Description: "", Unit: metricdata.UnitDimensionless,
251 Type: metricdata.TypeGaugeFloat64, LabelKeys: labelKeys,
252 }
253 }
254
255 func Test_ViewToMetric(t *testing.T) {
256 now := time.Now()
257 tests := []*testToMetrics{
258 {
259 view: viewTypeInt64Distribution,
260 recordValue: recordsInt64,
261 wantMetric: &metricdata.Metric{
262 Descriptor: mdTypeInt64CumulativeDistribution,
263 TimeSeries: []*metricdata.TimeSeries{
264 {Points: []metricdata.Point{
265 {Value: &metricdata.Distribution{
266 Count: 2,
267 Sum: 6.0,
268 SumOfSquaredDeviation: 2,
269 BucketOptions: buckOpt,
270 Buckets: []metricdata.Bucket{
271 {Count: 0, Exemplar: nil},
272 {Count: 2, Exemplar: nil},
273 },
274 },
275 Time: now,
276 },
277 },
278 LabelValues: labelValues,
279 StartTime: now,
280 },
281 },
282 },
283 },
284 {
285 view: viewTypeFloat64Distribution,
286 recordValue: recordsFloat64,
287 wantMetric: &metricdata.Metric{
288 Descriptor: mdTypeFloat64CumulativeDistribution,
289 TimeSeries: []*metricdata.TimeSeries{
290 {
291 Points: []metricdata.Point{
292 {
293 Value: &metricdata.Distribution{
294 Count: 2,
295 Sum: 6.9,
296 SumOfSquaredDeviation: 7.605000000000001,
297 BucketOptions: buckOpt,
298 Buckets: []metricdata.Bucket{
299 {Count: 1, Exemplar: nil},
300 {Count: 1, Exemplar: nil},
301 },
302 },
303 Time: now,
304 },
305 },
306 LabelValues: labelValues,
307 StartTime: now,
308 },
309 },
310 },
311 },
312 {
313 view: viewTypeInt64Count,
314 recordValue: recordsInt64,
315 wantMetric: &metricdata.Metric{
316 Descriptor: mdTypeInt64CumulativeCount,
317 TimeSeries: []*metricdata.TimeSeries{
318 {Points: []metricdata.Point{
319 metricdata.NewInt64Point(now, 2),
320 },
321 LabelValues: labelValues,
322 StartTime: now,
323 },
324 },
325 },
326 },
327 {
328 view: viewTypeFloat64Count,
329 recordValue: recordsFloat64,
330 wantMetric: &metricdata.Metric{
331 Descriptor: mdTypeFloat64CumulativeCount,
332 TimeSeries: []*metricdata.TimeSeries{
333 {Points: []metricdata.Point{
334 metricdata.NewInt64Point(now, 2),
335 },
336 LabelValues: labelValues,
337 StartTime: now,
338 },
339 },
340 },
341 },
342 {
343 view: viewTypeInt64Sum,
344 recordValue: recordsInt64,
345 wantMetric: &metricdata.Metric{
346 Descriptor: mdTypeInt64CumulativeSum,
347 TimeSeries: []*metricdata.TimeSeries{
348 {Points: []metricdata.Point{
349 metricdata.NewInt64Point(now, 6),
350 },
351 LabelValues: labelValues,
352 StartTime: now,
353 },
354 },
355 },
356 },
357 {
358 view: viewTypeFloat64Sum,
359 recordValue: recordsFloat64,
360 wantMetric: &metricdata.Metric{
361 Descriptor: mdTypeFloat64CumulativeSum,
362 TimeSeries: []*metricdata.TimeSeries{
363 {Points: []metricdata.Point{
364 metricdata.NewFloat64Point(now, 6.9),
365 },
366 LabelValues: labelValues,
367 StartTime: now,
368 },
369 },
370 },
371 },
372 {
373 view: viewTypeInt64LastValue,
374 recordValue: recordsInt64,
375 wantMetric: &metricdata.Metric{
376 Descriptor: mdTypeInt64CumulativeLastValue,
377 TimeSeries: []*metricdata.TimeSeries{
378 {Points: []metricdata.Point{
379 metricdata.NewInt64Point(now, 4),
380 },
381 LabelValues: labelValues,
382 StartTime: time.Time{},
383 },
384 },
385 },
386 },
387 {
388 view: viewTypeFloat64LastValue,
389 recordValue: recordsFloat64,
390 wantMetric: &metricdata.Metric{
391 Descriptor: mdTypeFloat64CumulativeLastValue,
392 TimeSeries: []*metricdata.TimeSeries{
393 {Points: []metricdata.Point{
394 metricdata.NewFloat64Point(now, 5.4),
395 },
396 LabelValues: labelValues,
397 StartTime: time.Time{},
398 },
399 },
400 },
401 },
402 {
403 view: viewRecordWithoutLabel,
404 recordValue: recordsFloat64WoTag,
405 wantMetric: &metricdata.Metric{
406 Descriptor: mdTypeRecordWithoutLabel,
407 TimeSeries: []*metricdata.TimeSeries{
408 {Points: []metricdata.Point{
409 metricdata.NewFloat64Point(now, 5.4),
410 },
411 LabelValues: emptyLabelValues,
412 StartTime: time.Time{},
413 },
414 },
415 },
416 },
417 }
418
419 for _, tc := range tests {
420 tc.vi, _ = defaultWorker.tryRegisterView(tc.view)
421 tc.vi.clearRows()
422 tc.vi.subscribe()
423 }
424
425 for i, tc := range tests {
426 for _, r := range tc.recordValue {
427 mods := []tag.Mutator{}
428 for _, tg := range r.tags {
429 mods = append(mods, tag.Insert(tg.Key, tg.Value))
430 }
431 ctx, err := tag.New(context.Background(), mods...)
432 if err != nil {
433 t.Errorf("%v: New = %v", tc.view.Name, err)
434 }
435 var v float64
436 switch i := r.value.(type) {
437 case float64:
438 v = float64(i)
439 case int64:
440 v = float64(i)
441 default:
442 t.Errorf("unexpected value type %v", r.tags)
443 }
444 tc.vi.addSample(tag.FromContext(ctx), v, nil, now)
445 }
446
447 gotMetric := viewToMetric(tc.vi, nil, now)
448 if !cmp.Equal(gotMetric, tc.wantMetric) {
449
450
451
452 t.Errorf("#%d: Unmatched \nGot:\n\t%v\nWant:\n\t%v\nGot Serialized:%s\nWant Serialized:%s\n",
453 i, gotMetric, tc.wantMetric, serializeAsJSON(gotMetric), serializeAsJSON(tc.wantMetric))
454 }
455 }
456 }
457
458
459
460 func TestUnitConversionForAggCount(t *testing.T) {
461 now := time.Now()
462 tests := []*struct {
463 name string
464 vi *viewInternal
465 v *View
466 wantUnit metricdata.Unit
467 }{
468 {
469 name: "View with Count Aggregation on Latency measurement",
470 v: &View{
471 Name: "request_count1",
472 Measure: stats.Int64("request_latency", "", stats.UnitMilliseconds),
473 Aggregation: aggCnt,
474 },
475 wantUnit: metricdata.UnitDimensionless,
476 },
477 {
478 name: "View with Count Aggregation on bytes measurement",
479 v: &View{
480 Name: "request_count2",
481 Measure: stats.Int64("request_bytes", "", stats.UnitBytes),
482 Aggregation: aggCnt,
483 },
484 wantUnit: metricdata.UnitDimensionless,
485 },
486 {
487 name: "View with aggregation other than Count Aggregation on Latency measurement",
488 v: &View{
489 Name: "request_latency",
490 Measure: stats.Int64("request_latency", "", stats.UnitMilliseconds),
491 Aggregation: aggSum,
492 },
493 wantUnit: metricdata.UnitMilliseconds,
494 },
495 }
496 var err error
497 for _, tc := range tests {
498 tc.vi, err = defaultWorker.tryRegisterView(tc.v)
499 if err != nil {
500 t.Fatalf("error registering view: %v, err: %v\n", tc.v, err)
501 }
502 tc.vi.clearRows()
503 tc.vi.subscribe()
504 }
505
506 for _, tc := range tests {
507 tc.vi.addSample(tag.FromContext(context.Background()), 5.0, nil, now)
508 gotMetric := viewToMetric(tc.vi, nil, now)
509 gotUnit := gotMetric.Descriptor.Unit
510 if !cmp.Equal(gotUnit, tc.wantUnit) {
511 t.Errorf("Verify Unit: %s: Got:%v Want:%v", tc.name, gotUnit, tc.wantUnit)
512 }
513 }
514 }
515
516 type mockExp struct {
517 metrics []*metricdata.Metric
518 }
519
520 func (me *mockExp) ExportMetrics(ctx context.Context, metrics []*metricdata.Metric) error {
521 me.metrics = append(me.metrics, metrics...)
522 return nil
523 }
524
525 var _ metricexport.Exporter = (*mockExp)(nil)
526
527 func TestViewToMetric_OutOfOrderWithZeroBuckets(t *testing.T) {
528 m := stats.Int64("OutOfOrderWithZeroBuckets", "", "")
529 now := time.Now()
530 tts := []struct {
531 v *View
532 m *metricdata.Metric
533 }{
534 {
535 v: &View{
536 Name: m.Name() + "_order1",
537 Measure: m,
538 Aggregation: Distribution(10, 0, 2),
539 },
540 m: &metricdata.Metric{
541 Descriptor: metricdata.Descriptor{
542 Name: "OutOfOrderWithZeroBuckets_order1",
543 Unit: metricdata.UnitDimensionless,
544 Type: metricdata.TypeCumulativeDistribution,
545 LabelKeys: []metricdata.LabelKey{},
546 },
547 TimeSeries: []*metricdata.TimeSeries{
548 {Points: []metricdata.Point{
549 {Value: &metricdata.Distribution{
550 Count: 3,
551 Sum: 9.0,
552 SumOfSquaredDeviation: 8,
553 BucketOptions: &metricdata.BucketOptions{
554 Bounds: []float64{2, 10},
555 },
556 Buckets: []metricdata.Bucket{
557 {Count: 1, Exemplar: nil},
558 {Count: 2, Exemplar: nil},
559 {Count: 0, Exemplar: nil},
560 },
561 },
562 Time: now,
563 },
564 },
565 StartTime: now,
566 LabelValues: []metricdata.LabelValue{},
567 },
568 },
569 },
570 },
571 {
572 v: &View{
573 Name: m.Name() + "_order2",
574 Measure: m,
575 Aggregation: Distribution(0, 5, 10),
576 },
577 m: &metricdata.Metric{
578 Descriptor: metricdata.Descriptor{
579 Name: "OutOfOrderWithZeroBuckets_order2",
580 Unit: metricdata.UnitDimensionless,
581 Type: metricdata.TypeCumulativeDistribution,
582 LabelKeys: []metricdata.LabelKey{},
583 },
584 TimeSeries: []*metricdata.TimeSeries{
585 {Points: []metricdata.Point{
586 {Value: &metricdata.Distribution{
587 Count: 3,
588 Sum: 9.0,
589 SumOfSquaredDeviation: 8,
590 BucketOptions: &metricdata.BucketOptions{
591 Bounds: []float64{5, 10},
592 },
593 Buckets: []metricdata.Bucket{
594 {Count: 2, Exemplar: nil},
595 {Count: 1, Exemplar: nil},
596 {Count: 0, Exemplar: nil},
597 },
598 },
599 Time: now,
600 },
601 },
602 StartTime: now,
603 LabelValues: []metricdata.LabelValue{},
604 },
605 },
606 },
607 },
608 }
609 for _, tt := range tts {
610 err := Register(tt.v)
611 if err != nil {
612 t.Fatalf("error registering view %v, err: %v", tt.v, err)
613 }
614
615 }
616
617 stats.Record(context.Background(), m.M(5), m.M(1), m.M(3))
618 time.Sleep(1 * time.Second)
619
620 me := &mockExp{}
621 reader := metricexport.NewReader()
622 reader.ReadAndExport(me)
623
624 var got *metricdata.Metric
625 lookup := func(vname string, metrics []*metricdata.Metric) *metricdata.Metric {
626 for _, m := range metrics {
627 if m.Descriptor.Name == vname {
628 return m
629 }
630 }
631 return nil
632 }
633
634 for _, tt := range tts {
635 got = lookup(tt.v.Name, me.metrics)
636 if got == nil {
637 t.Fatalf("metric %s not found in %v\n", tt.v.Name, me.metrics)
638 }
639 got.TimeSeries[0].Points[0].Time = now
640 got.TimeSeries[0].StartTime = now
641
642 want := tt.m
643 if diff := cmp.Diff(got, want); diff != "" {
644 t.Errorf("buckets differ -got +want: %s \n Serialized got %v\n, Serialized want %v\n", diff, serializeAsJSON(got), serializeAsJSON(want))
645 }
646 }
647 }
648
649 func serializeAsJSON(v interface{}) string {
650 blob, _ := json.MarshalIndent(v, "", " ")
651 return string(blob)
652 }
653
View as plain text