...

Source file src/go.opencensus.io/metric/gauge_test.go

Documentation: go.opencensus.io/metric

     1  // Copyright 2018, 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  package metric
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  
    25  	"go.opencensus.io/metric/metricdata"
    26  )
    27  
    28  func TestGauge(t *testing.T) {
    29  	r := NewRegistry()
    30  
    31  	f, _ := r.AddFloat64Gauge("TestGauge",
    32  		WithLabelKeys("k1", "k2"))
    33  	e, _ := f.GetEntry(metricdata.LabelValue{}, metricdata.LabelValue{})
    34  	e.Set(5)
    35  	e, _ = f.GetEntry(metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
    36  	e.Add(1)
    37  	e, _ = f.GetEntry(metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
    38  	e.Add(1)
    39  	e, _ = f.GetEntry(metricdata.NewLabelValue("k1v2"), metricdata.NewLabelValue("k2v2"))
    40  	e.Add(1)
    41  	m := r.Read()
    42  	want := []*metricdata.Metric{
    43  		{
    44  			Descriptor: metricdata.Descriptor{
    45  				Name: "TestGauge",
    46  				LabelKeys: []metricdata.LabelKey{
    47  					{Key: "k1"},
    48  					{Key: "k2"},
    49  				},
    50  				Type: metricdata.TypeGaugeFloat64,
    51  			},
    52  			TimeSeries: []*metricdata.TimeSeries{
    53  				{
    54  					LabelValues: []metricdata.LabelValue{
    55  						{}, {},
    56  					},
    57  					Points: []metricdata.Point{
    58  						metricdata.NewFloat64Point(time.Time{}, 5),
    59  					},
    60  				},
    61  				{
    62  					LabelValues: []metricdata.LabelValue{
    63  						metricdata.NewLabelValue("k1v1"),
    64  						{},
    65  					},
    66  					Points: []metricdata.Point{
    67  						metricdata.NewFloat64Point(time.Time{}, 2),
    68  					},
    69  				},
    70  				{
    71  					LabelValues: []metricdata.LabelValue{
    72  						metricdata.NewLabelValue("k1v2"),
    73  						metricdata.NewLabelValue("k2v2"),
    74  					},
    75  					Points: []metricdata.Point{
    76  						metricdata.NewFloat64Point(time.Time{}, 1),
    77  					},
    78  				},
    79  			},
    80  		},
    81  	}
    82  	canonicalize(m)
    83  	canonicalize(want)
    84  	if diff := cmp.Diff(m, want, cmp.Comparer(ignoreTimes)); diff != "" {
    85  		t.Errorf("-got +want: %s", diff)
    86  	}
    87  }
    88  
    89  func TestGaugeConstLabel(t *testing.T) {
    90  	r := NewRegistry()
    91  
    92  	f, _ := r.AddFloat64Gauge("TestGaugeWithConstLabel",
    93  		WithLabelKeys("k1"),
    94  		WithConstLabel(map[metricdata.LabelKey]metricdata.LabelValue{
    95  			{Key: "const"}:  metricdata.NewLabelValue("same"),
    96  			{Key: "const2"}: metricdata.NewLabelValue("same2"),
    97  		}))
    98  
    99  	e, _ := f.GetEntry(metricdata.LabelValue{})
   100  	e.Set(5)
   101  	e, _ = f.GetEntry(metricdata.NewLabelValue("k1v1"))
   102  	e.Add(1)
   103  	m := r.Read()
   104  	want := []*metricdata.Metric{
   105  		{
   106  			Descriptor: metricdata.Descriptor{
   107  				Name: "TestGaugeWithConstLabel",
   108  				LabelKeys: []metricdata.LabelKey{
   109  					{Key: "const"},
   110  					{Key: "const2"},
   111  					{Key: "k1"}},
   112  				Type: metricdata.TypeGaugeFloat64,
   113  			},
   114  			TimeSeries: []*metricdata.TimeSeries{
   115  				{
   116  					LabelValues: []metricdata.LabelValue{
   117  						metricdata.NewLabelValue("same"),
   118  						metricdata.NewLabelValue("same2"),
   119  						{},
   120  					},
   121  					Points: []metricdata.Point{
   122  						metricdata.NewFloat64Point(time.Time{}, 5),
   123  					},
   124  				},
   125  				{
   126  					LabelValues: []metricdata.LabelValue{
   127  						metricdata.NewLabelValue("same"),
   128  						metricdata.NewLabelValue("same2"),
   129  						metricdata.NewLabelValue("k1v1"),
   130  					},
   131  					Points: []metricdata.Point{
   132  						metricdata.NewFloat64Point(time.Time{}, 1),
   133  					},
   134  				},
   135  			},
   136  		},
   137  	}
   138  	canonicalize(m)
   139  	canonicalize(want)
   140  	if diff := cmp.Diff(m, want, cmp.Comparer(ignoreTimes)); diff != "" {
   141  		t.Errorf("-got +want: %s", diff)
   142  	}
   143  }
   144  
   145  func TestGaugeMetricDescriptor(t *testing.T) {
   146  	r := NewRegistry()
   147  
   148  	gf, _ := r.AddFloat64Gauge("float64_gauge")
   149  	compareType(gf.bm.desc.Type, metricdata.TypeGaugeFloat64, t)
   150  	gi, _ := r.AddInt64Gauge("int64_gauge")
   151  	compareType(gi.bm.desc.Type, metricdata.TypeGaugeInt64, t)
   152  	dgf, _ := r.AddFloat64DerivedGauge("derived_float64_gauge")
   153  	compareType(dgf.bm.desc.Type, metricdata.TypeGaugeFloat64, t)
   154  	dgi, _ := r.AddInt64DerivedGauge("derived_int64_gauge")
   155  	compareType(dgi.bm.desc.Type, metricdata.TypeGaugeInt64, t)
   156  }
   157  
   158  func compareType(got, want metricdata.Type, t *testing.T) {
   159  	if got != want {
   160  		t.Errorf("metricdata type: got %v, want %v\n", got, want)
   161  	}
   162  }
   163  
   164  func TestGaugeMetricOptionDesc(t *testing.T) {
   165  	r := NewRegistry()
   166  	name := "testOptDesc"
   167  	gf, _ := r.AddFloat64Gauge(name, WithDescription("test"))
   168  	want := metricdata.Descriptor{
   169  		Name:        name,
   170  		Description: "test",
   171  		Type:        metricdata.TypeGaugeFloat64,
   172  	}
   173  	got := gf.bm.desc
   174  	if !cmp.Equal(got, want) {
   175  		t.Errorf("metric option description: got %v, want %v\n", got, want)
   176  	}
   177  }
   178  
   179  func TestGaugeMetricOptionUnit(t *testing.T) {
   180  	r := NewRegistry()
   181  	name := "testOptUnit"
   182  	gf, _ := r.AddFloat64Gauge(name, WithUnit(metricdata.UnitMilliseconds))
   183  	want := metricdata.Descriptor{
   184  		Name: name,
   185  		Unit: metricdata.UnitMilliseconds,
   186  		Type: metricdata.TypeGaugeFloat64,
   187  	}
   188  	got := gf.bm.desc
   189  	if !cmp.Equal(got, want) {
   190  		t.Errorf("metric descriptor: got %v, want %v\n", got, want)
   191  	}
   192  }
   193  
   194  func TestGaugeMetricOptionLabelKeys(t *testing.T) {
   195  	r := NewRegistry()
   196  	name := "testOptUnit"
   197  	gf, _ := r.AddFloat64Gauge(name, WithLabelKeys("k1", "k3"))
   198  	want := metricdata.Descriptor{
   199  		Name: name,
   200  		LabelKeys: []metricdata.LabelKey{
   201  			{Key: "k1"},
   202  			{Key: "k3"},
   203  		},
   204  		Type: metricdata.TypeGaugeFloat64,
   205  	}
   206  	got := gf.bm.desc
   207  	if !cmp.Equal(got, want) {
   208  		t.Errorf("metric descriptor: got %v, want %v\n", got, want)
   209  	}
   210  }
   211  
   212  func TestGaugeMetricOptionLabelKeysAndDesc(t *testing.T) {
   213  	r := NewRegistry()
   214  	name := "testOptUnit"
   215  	lks := []metricdata.LabelKey{}
   216  	lks = append(lks, metricdata.LabelKey{Key: "k1", Description: "desc k1"},
   217  		metricdata.LabelKey{Key: "k3", Description: "desc k3"})
   218  	gf, _ := r.AddFloat64Gauge(name, WithLabelKeysAndDescription(lks...))
   219  	want := metricdata.Descriptor{
   220  		Name: name,
   221  		LabelKeys: []metricdata.LabelKey{
   222  			{Key: "k1", Description: "desc k1"},
   223  			{Key: "k3", Description: "desc k3"},
   224  		},
   225  		Type: metricdata.TypeGaugeFloat64,
   226  	}
   227  	got := gf.bm.desc
   228  	if !cmp.Equal(got, want) {
   229  		t.Errorf("metric descriptor: got %v, want %v\n", got, want)
   230  	}
   231  }
   232  
   233  func TestGaugeMetricOptionDefault(t *testing.T) {
   234  	r := NewRegistry()
   235  	name := "testOptUnit"
   236  	gf, _ := r.AddFloat64Gauge(name)
   237  	want := metricdata.Descriptor{
   238  		Name: name,
   239  		Type: metricdata.TypeGaugeFloat64,
   240  	}
   241  	got := gf.bm.desc
   242  	if !cmp.Equal(got, want) {
   243  		t.Errorf("metric descriptor: got %v, want %v\n", got, want)
   244  	}
   245  }
   246  
   247  func TestFloat64Entry_Add(t *testing.T) {
   248  	r := NewRegistry()
   249  	g, _ := r.AddFloat64Gauge("g")
   250  	e, _ := g.GetEntry()
   251  	e.Add(0)
   252  	ms := r.Read()
   253  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), 0.0; got != want {
   254  		t.Errorf("value = %v, want %v", got, want)
   255  	}
   256  	e, _ = g.GetEntry()
   257  	e.Add(1)
   258  	ms = r.Read()
   259  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), 1.0; got != want {
   260  		t.Errorf("value = %v, want %v", got, want)
   261  	}
   262  	e, _ = g.GetEntry()
   263  	e.Add(-1)
   264  	ms = r.Read()
   265  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), 0.0; got != want {
   266  		t.Errorf("value = %v, want %v", got, want)
   267  	}
   268  }
   269  
   270  func TestFloat64Gauge_Add_NegativeTotals(t *testing.T) {
   271  	r := NewRegistry()
   272  	g, _ := r.AddFloat64Gauge("g")
   273  	e, _ := g.GetEntry()
   274  	e.Add(-1.0)
   275  	ms := r.Read()
   276  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), float64(0); got != want {
   277  		t.Errorf("value = %v, want %v", got, want)
   278  	}
   279  }
   280  
   281  func TestInt64GaugeEntry_Add(t *testing.T) {
   282  	r := NewRegistry()
   283  	g, _ := r.AddInt64Gauge("g")
   284  	e, _ := g.GetEntry()
   285  	e.Add(0)
   286  	ms := r.Read()
   287  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(0); got != want {
   288  		t.Errorf("value = %v, want %v", got, want)
   289  	}
   290  	e, _ = g.GetEntry()
   291  	e.Add(1)
   292  	ms = r.Read()
   293  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(1); got != want {
   294  		t.Errorf("value = %v, want %v", got, want)
   295  	}
   296  }
   297  
   298  func TestInt64Gauge_Add_NegativeTotals(t *testing.T) {
   299  	r := NewRegistry()
   300  	g, _ := r.AddInt64Gauge("g")
   301  	e, _ := g.GetEntry()
   302  	e.Add(-1)
   303  	ms := r.Read()
   304  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(0); got != want {
   305  		t.Errorf("value = %v, want %v", got, want)
   306  	}
   307  }
   308  
   309  func TestGaugeWithSameNameDiffType(t *testing.T) {
   310  	r := NewRegistry()
   311  	r.AddInt64Gauge("g")
   312  	_, gotErr := r.AddFloat64Gauge("g")
   313  	if gotErr == nil {
   314  		t.Errorf("got: nil, want error: %v", errMetricExistsWithDiffType)
   315  	}
   316  	_, gotErr = r.AddInt64DerivedGauge("g")
   317  	if gotErr == nil {
   318  		t.Errorf("got: nil, want error: %v", errMetricExistsWithDiffType)
   319  	}
   320  	_, gotErr = r.AddFloat64DerivedGauge("g")
   321  	if gotErr == nil {
   322  		t.Errorf("got: nil, want error: %v", errMetricExistsWithDiffType)
   323  	}
   324  }
   325  
   326  func TestGaugeWithLabelMismatch(t *testing.T) {
   327  	r := NewRegistry()
   328  	g, _ := r.AddInt64Gauge("g", WithLabelKeys("k1"))
   329  	_, gotErr := g.GetEntry(metricdata.NewLabelValue("k1v2"), metricdata.NewLabelValue("k2v2"))
   330  	if gotErr == nil {
   331  		t.Errorf("got: nil, want error: %v", errKeyValueMismatch)
   332  	}
   333  }
   334  
   335  func TestMapKey(t *testing.T) {
   336  	cases := [][]metricdata.LabelValue{
   337  		{},
   338  		{metricdata.LabelValue{}},
   339  		{metricdata.NewLabelValue("")},
   340  		{metricdata.NewLabelValue("-")},
   341  		{metricdata.NewLabelValue(",")},
   342  		{metricdata.NewLabelValue("v1"), metricdata.NewLabelValue("v2")},
   343  		{metricdata.NewLabelValue("v1"), metricdata.LabelValue{}},
   344  		{metricdata.NewLabelValue("v1"), metricdata.LabelValue{}, metricdata.NewLabelValue(string([]byte{0}))},
   345  		{metricdata.LabelValue{}, metricdata.LabelValue{}},
   346  	}
   347  	for i, tc := range cases {
   348  		t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
   349  			g := &baseMetric{
   350  				keys: make([]metricdata.LabelKey, len(tc)),
   351  			}
   352  			mk := g.encodeLabelVals(tc)
   353  			vals := g.decodeLabelVals(mk)
   354  			if diff := cmp.Diff(vals, tc); diff != "" {
   355  				t.Errorf("values differ after serialization -got +want: %s", diff)
   356  			}
   357  		})
   358  	}
   359  }
   360  
   361  func TestRaceCondition(t *testing.T) {
   362  	r := NewRegistry()
   363  
   364  	// start reader before adding Gauge metric.
   365  	var ms = []*metricdata.Metric{}
   366  	for i := 0; i < 5; i++ {
   367  		go func(k int) {
   368  			for j := 0; j < 5; j++ {
   369  				g, _ := r.AddInt64Gauge(fmt.Sprintf("g%d%d", k, j))
   370  				e, _ := g.GetEntry()
   371  				e.Add(1)
   372  			}
   373  		}(i)
   374  	}
   375  	time.Sleep(1 * time.Second)
   376  	ms = r.Read()
   377  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(1); got != want {
   378  		t.Errorf("value = %v, want %v", got, want)
   379  	}
   380  }
   381  
   382  func ignoreTimes(_, _ time.Time) bool {
   383  	return true
   384  }
   385  
   386  func canonicalize(ms []*metricdata.Metric) {
   387  	for _, m := range ms {
   388  		sort.Slice(m.TimeSeries, func(i, j int) bool {
   389  			// sort time series by their label values
   390  			iStr := ""
   391  
   392  			for _, label := range m.TimeSeries[i].LabelValues {
   393  				iStr += fmt.Sprintf("%+v", label)
   394  			}
   395  
   396  			jStr := ""
   397  			for _, label := range m.TimeSeries[j].LabelValues {
   398  				jStr += fmt.Sprintf("%+v", label)
   399  			}
   400  
   401  			return iStr < jStr
   402  		})
   403  	}
   404  }
   405  
   406  type queueInt64 struct {
   407  	size int64
   408  }
   409  
   410  func (q *queueInt64) ToInt64() int64 {
   411  	return q.size
   412  }
   413  
   414  func TestInt64DerivedGaugeEntry_Add(t *testing.T) {
   415  	r := NewRegistry()
   416  	q := &queueInt64{3}
   417  	g, _ := r.AddInt64DerivedGauge("g", WithLabelKeys("k1", "k2"))
   418  	err := g.UpsertEntry(q.ToInt64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
   419  	if err != nil {
   420  		t.Errorf("want: nil, got: %v", err)
   421  	}
   422  	ms := r.Read()
   423  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(3); got != want {
   424  		t.Errorf("value = %v, want %v", got, want)
   425  	}
   426  	q.size = 5
   427  	ms = r.Read()
   428  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(5); got != want {
   429  		t.Errorf("value = %v, want %v", got, want)
   430  	}
   431  }
   432  
   433  func TestInt64DerivedGaugeEntry_AddWithNilObj(t *testing.T) {
   434  	r := NewRegistry()
   435  	g, _ := r.AddInt64DerivedGauge("g", WithLabelKeys("k1", "k2"))
   436  	gotErr := g.UpsertEntry(nil, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
   437  	if gotErr == nil {
   438  		t.Errorf("expected error but got nil")
   439  	}
   440  }
   441  
   442  func TestInt64DerivedGaugeEntry_AddWithInvalidLabels(t *testing.T) {
   443  	r := NewRegistry()
   444  	q := &queueInt64{3}
   445  	g, _ := r.AddInt64DerivedGauge("g", WithLabelKeys("k1", "k2"))
   446  	gotErr := g.UpsertEntry(q.ToInt64, metricdata.NewLabelValue("k1v1"))
   447  	if gotErr == nil {
   448  		t.Errorf("expected error but got nil")
   449  	}
   450  }
   451  
   452  func TestInt64DerivedGaugeEntry_Update(t *testing.T) {
   453  	r := NewRegistry()
   454  	q := &queueInt64{3}
   455  	q2 := &queueInt64{5}
   456  	g, _ := r.AddInt64DerivedGauge("g", WithLabelKeys("k1", "k2"))
   457  	g.UpsertEntry(q.ToInt64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
   458  	gotErr := g.UpsertEntry(q2.ToInt64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
   459  	if gotErr != nil {
   460  		t.Errorf("got: %v, want: nil", gotErr)
   461  	}
   462  	ms := r.Read()
   463  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(5); got != want {
   464  		t.Errorf("value = %v, want %v", got, want)
   465  	}
   466  }
   467  
   468  func TestInt64DerivedGaugeEntry_UpsertConstLabels(t *testing.T) {
   469  	r := NewRegistry()
   470  	q := &queueInt64{3}
   471  	g, _ := r.AddInt64DerivedGauge("g",
   472  		WithConstLabel(map[metricdata.LabelKey]metricdata.LabelValue{
   473  			{Key: "const"}: metricdata.NewLabelValue("same"),
   474  		}))
   475  	err := g.UpsertEntry(q.ToInt64)
   476  	if err != nil {
   477  		t.Errorf("want: nil, got: %v", err)
   478  	}
   479  	ms := r.Read()
   480  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(3); got != want {
   481  		t.Errorf("value = %v, want %v", got, want)
   482  	}
   483  	if got, want := ms[0].Descriptor.LabelKeys[0].Key, "const"; got != want {
   484  		t.Errorf("label key = %v, want %v", got, want)
   485  	}
   486  	if got, want := ms[0].TimeSeries[0].LabelValues[0].Value, "same"; got != want {
   487  		t.Errorf("label value = %v, want %v", got, want)
   488  	}
   489  }
   490  
   491  type queueFloat64 struct {
   492  	size float64
   493  }
   494  
   495  func (q *queueFloat64) ToFloat64() float64 {
   496  	return q.size
   497  }
   498  
   499  func TestFloat64DerivedGaugeEntry_Add(t *testing.T) {
   500  	r := NewRegistry()
   501  	q := &queueFloat64{5.0}
   502  	g, _ := r.AddFloat64DerivedGauge("g", WithLabelKeys("k1", "k2"))
   503  	err := g.UpsertEntry(q.ToFloat64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
   504  	if err != nil {
   505  		t.Errorf("want: nil, got: %v", err)
   506  	}
   507  	ms := r.Read()
   508  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), float64(5.0); got != want {
   509  		t.Errorf("value = %v, want %v", got, want)
   510  	}
   511  	q.size = 5
   512  	ms = r.Read()
   513  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), float64(5.0); got != want {
   514  		t.Errorf("value = %v, want %v", got, want)
   515  	}
   516  }
   517  
   518  func TestFloat64DerivedGaugeEntry_AddWithNilObj(t *testing.T) {
   519  	r := NewRegistry()
   520  	g, _ := r.AddFloat64DerivedGauge("g", WithLabelKeys("k1", "k2"))
   521  	gotErr := g.UpsertEntry(nil, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
   522  	if gotErr == nil {
   523  		t.Errorf("expected error but got nil")
   524  	}
   525  }
   526  
   527  func TestFloat64DerivedGaugeEntry_AddWithInvalidLabels(t *testing.T) {
   528  	r := NewRegistry()
   529  	q := &queueFloat64{3}
   530  	g, _ := r.AddFloat64DerivedGauge("g", WithLabelKeys("k1", "k2"))
   531  	gotErr := g.UpsertEntry(q.ToFloat64, metricdata.NewLabelValue("k1v1"))
   532  	if gotErr == nil {
   533  		t.Errorf("expected error but got nil")
   534  	}
   535  }
   536  
   537  func TestFloat64DerivedGaugeEntry_Update(t *testing.T) {
   538  	r := NewRegistry()
   539  	q := &queueFloat64{3.0}
   540  	q2 := &queueFloat64{5.0}
   541  	g, _ := r.AddFloat64DerivedGauge("g", WithLabelKeys("k1", "k2"))
   542  	g.UpsertEntry(q.ToFloat64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
   543  	gotErr := g.UpsertEntry(q2.ToFloat64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
   544  	if gotErr != nil {
   545  		t.Errorf("got: %v, want: nil", gotErr)
   546  	}
   547  	ms := r.Read()
   548  	if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), float64(5.0); got != want {
   549  		t.Errorf("value = %v, want %v", got, want)
   550  	}
   551  }
   552  

View as plain text