...

Source file src/go.opencensus.io/stats/view/view_to_metric_test.go

Documentation: go.opencensus.io/stats/view

     1  // Copyright 2019, OpenCensus Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    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  	// tag objects.
    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  	// distribution objects.
    62  	aggDist *Aggregation
    63  	aggCnt  *Aggregation
    64  	aggS    *Aggregation
    65  	aggL    *Aggregation
    66  	buckOpt *metricdata.BucketOptions
    67  
    68  	// exemplar objects.
    69  	attachments metricdata.Attachments
    70  
    71  	// views and descriptors
    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  	// View objects
   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  	// Metric objects
   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}, // TODO: [rghetia] add exemplar test.
   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  			// JSON format is strictly for checking the content when test fails. Do not use JSON
   450  			// format to determine if the two values are same as it doesn't differentiate between
   451  			// int64(2) and float64(2.0)
   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  // Test to verify that a metric converted from a view with Aggregation Count should always
   459  // have Dimensionless unit.
   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