...

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

Documentation: go.opencensus.io/stats/view

     1  // Copyright 2017, 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  	"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  	// Register normalizes the view by sorting the tag keys, retrieve the normalized view
   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  	// Tests that we can register views after measurements are created and
   399  	// they still take effect.
   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