github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/metric/metric_test.go (about)

     1  // Copyright 2018 The gVisor 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  	"testing"
    19  
    20  	"google.golang.org/protobuf/proto"
    21  	"github.com/SagerNet/gvisor/pkg/eventchannel"
    22  	pb "github.com/SagerNet/gvisor/pkg/metric/metric_go_proto"
    23  )
    24  
    25  // sliceEmitter implements eventchannel.Emitter by appending all messages to a
    26  // slice.
    27  type sliceEmitter []proto.Message
    28  
    29  // Emit implements eventchannel.Emitter.Emit.
    30  func (s *sliceEmitter) Emit(msg proto.Message) (bool, error) {
    31  	*s = append(*s, msg)
    32  	return false, nil
    33  }
    34  
    35  // Emit implements eventchannel.Emitter.Close.
    36  func (s *sliceEmitter) Close() error {
    37  	return nil
    38  }
    39  
    40  // Reset clears all events in s.
    41  func (s *sliceEmitter) Reset() {
    42  	*s = nil
    43  }
    44  
    45  // emitter is the eventchannel.Emitter used for all tests. Package eventchannel
    46  // doesn't allow removing Emitters, so we must use one global emitter for all
    47  // test cases.
    48  var emitter sliceEmitter
    49  
    50  func init() {
    51  	reset()
    52  
    53  	eventchannel.AddEmitter(&emitter)
    54  }
    55  
    56  // reset clears all global state in the metric package.
    57  func reset() {
    58  	initialized = false
    59  	allMetrics = makeMetricSet()
    60  	emitter.Reset()
    61  }
    62  
    63  const (
    64  	fooDescription     = "Foo!"
    65  	barDescription     = "Bar Baz"
    66  	counterDescription = "Counter"
    67  )
    68  
    69  func TestInitialize(t *testing.T) {
    70  	defer reset()
    71  
    72  	_, err := NewUint64Metric("/foo", false, pb.MetricMetadata_UNITS_NONE, fooDescription)
    73  	if err != nil {
    74  		t.Fatalf("NewUint64Metric got err %v want nil", err)
    75  	}
    76  
    77  	_, err = NewUint64Metric("/bar", true, pb.MetricMetadata_UNITS_NANOSECONDS, barDescription)
    78  	if err != nil {
    79  		t.Fatalf("NewUint64Metric got err %v want nil", err)
    80  	}
    81  
    82  	if err := Initialize(); err != nil {
    83  		t.Fatalf("Initialize(): %s", err)
    84  	}
    85  
    86  	if len(emitter) != 1 {
    87  		t.Fatalf("Initialize emitted %d events want 1", len(emitter))
    88  	}
    89  
    90  	mr, ok := emitter[0].(*pb.MetricRegistration)
    91  	if !ok {
    92  		t.Fatalf("emitter %v got %T want pb.MetricRegistration", emitter[0], emitter[0])
    93  	}
    94  
    95  	if len(mr.Metrics) != 2 {
    96  		t.Errorf("MetricRegistration got %d metrics want 2", len(mr.Metrics))
    97  	}
    98  
    99  	foundFoo := false
   100  	foundBar := false
   101  	for _, m := range mr.Metrics {
   102  		if m.Type != pb.MetricMetadata_TYPE_UINT64 {
   103  			t.Errorf("Metadata %+v Type got %v want pb.MetricMetadata_TYPE_UINT64", m, m.Type)
   104  		}
   105  		if !m.Cumulative {
   106  			t.Errorf("Metadata %+v Cumulative got false want true", m)
   107  		}
   108  
   109  		switch m.Name {
   110  		case "/foo":
   111  			foundFoo = true
   112  			if m.Description != fooDescription {
   113  				t.Errorf("/foo %+v Description got %q want %q", m, m.Description, fooDescription)
   114  			}
   115  			if m.Sync {
   116  				t.Errorf("/foo %+v Sync got true want false", m)
   117  			}
   118  			if m.Units != pb.MetricMetadata_UNITS_NONE {
   119  				t.Errorf("/foo %+v Units got %v want %v", m, m.Units, pb.MetricMetadata_UNITS_NONE)
   120  			}
   121  		case "/bar":
   122  			foundBar = true
   123  			if m.Description != barDescription {
   124  				t.Errorf("/bar %+v Description got %q want %q", m, m.Description, barDescription)
   125  			}
   126  			if !m.Sync {
   127  				t.Errorf("/bar %+v Sync got true want false", m)
   128  			}
   129  			if m.Units != pb.MetricMetadata_UNITS_NANOSECONDS {
   130  				t.Errorf("/bar %+v Units got %v want %v", m, m.Units, pb.MetricMetadata_UNITS_NANOSECONDS)
   131  			}
   132  		}
   133  	}
   134  
   135  	if !foundFoo {
   136  		t.Errorf("/foo not found: %+v", emitter)
   137  	}
   138  	if !foundBar {
   139  		t.Errorf("/bar not found: %+v", emitter)
   140  	}
   141  }
   142  
   143  func TestDisable(t *testing.T) {
   144  	defer reset()
   145  
   146  	_, err := NewUint64Metric("/foo", false, pb.MetricMetadata_UNITS_NONE, fooDescription)
   147  	if err != nil {
   148  		t.Fatalf("NewUint64Metric got err %v want nil", err)
   149  	}
   150  
   151  	_, err = NewUint64Metric("/bar", true, pb.MetricMetadata_UNITS_NONE, barDescription)
   152  	if err != nil {
   153  		t.Fatalf("NewUint64Metric got err %v want nil", err)
   154  	}
   155  
   156  	if err := Disable(); err != nil {
   157  		t.Fatalf("Disable(): %s", err)
   158  	}
   159  
   160  	if len(emitter) != 1 {
   161  		t.Fatalf("Initialize emitted %d events want 1", len(emitter))
   162  	}
   163  
   164  	mr, ok := emitter[0].(*pb.MetricRegistration)
   165  	if !ok {
   166  		t.Fatalf("emitter %v got %T want pb.MetricRegistration", emitter[0], emitter[0])
   167  	}
   168  
   169  	if len(mr.Metrics) != 0 {
   170  		t.Errorf("MetricRegistration got %d metrics want 0", len(mr.Metrics))
   171  	}
   172  }
   173  
   174  func TestEmitMetricUpdate(t *testing.T) {
   175  	defer reset()
   176  
   177  	foo, err := NewUint64Metric("/foo", false, pb.MetricMetadata_UNITS_NONE, fooDescription)
   178  	if err != nil {
   179  		t.Fatalf("NewUint64Metric got err %v want nil", err)
   180  	}
   181  
   182  	_, err = NewUint64Metric("/bar", true, pb.MetricMetadata_UNITS_NONE, barDescription)
   183  	if err != nil {
   184  		t.Fatalf("NewUint64Metric got err %v want nil", err)
   185  	}
   186  
   187  	if err := Initialize(); err != nil {
   188  		t.Fatalf("Initialize(): %s", err)
   189  	}
   190  
   191  	// Don't care about the registration metrics.
   192  	emitter.Reset()
   193  	EmitMetricUpdate()
   194  
   195  	if len(emitter) != 1 {
   196  		t.Fatalf("EmitMetricUpdate emitted %d events want 1", len(emitter))
   197  	}
   198  
   199  	update, ok := emitter[0].(*pb.MetricUpdate)
   200  	if !ok {
   201  		t.Fatalf("emitter %v got %T want pb.MetricUpdate", emitter[0], emitter[0])
   202  	}
   203  
   204  	if len(update.Metrics) != 2 {
   205  		t.Errorf("MetricUpdate got %d metrics want 2", len(update.Metrics))
   206  	}
   207  
   208  	// Both are included for their initial values.
   209  	foundFoo := false
   210  	foundBar := false
   211  	for _, m := range update.Metrics {
   212  		switch m.Name {
   213  		case "/foo":
   214  			foundFoo = true
   215  		case "/bar":
   216  			foundBar = true
   217  		}
   218  		uv, ok := m.Value.(*pb.MetricValue_Uint64Value)
   219  		if !ok {
   220  			t.Errorf("%+v: value %v got %T want pb.MetricValue_Uint64Value", m, m.Value, m.Value)
   221  			continue
   222  		}
   223  		if uv.Uint64Value != 0 {
   224  			t.Errorf("%v: Value got %v want 0", m, uv.Uint64Value)
   225  		}
   226  	}
   227  
   228  	if !foundFoo {
   229  		t.Errorf("/foo not found: %+v", emitter)
   230  	}
   231  	if !foundBar {
   232  		t.Errorf("/bar not found: %+v", emitter)
   233  	}
   234  
   235  	// Increment foo. Only it is included in the next update.
   236  	foo.Increment()
   237  
   238  	emitter.Reset()
   239  	EmitMetricUpdate()
   240  
   241  	if len(emitter) != 1 {
   242  		t.Fatalf("EmitMetricUpdate emitted %d events want 1", len(emitter))
   243  	}
   244  
   245  	update, ok = emitter[0].(*pb.MetricUpdate)
   246  	if !ok {
   247  		t.Fatalf("emitter %v got %T want pb.MetricUpdate", emitter[0], emitter[0])
   248  	}
   249  
   250  	if len(update.Metrics) != 1 {
   251  		t.Errorf("MetricUpdate got %d metrics want 1", len(update.Metrics))
   252  	}
   253  
   254  	m := update.Metrics[0]
   255  
   256  	if m.Name != "/foo" {
   257  		t.Errorf("Metric %+v name got %q want '/foo'", m, m.Name)
   258  	}
   259  
   260  	uv, ok := m.Value.(*pb.MetricValue_Uint64Value)
   261  	if !ok {
   262  		t.Errorf("%+v: value %v got %T want pb.MetricValue_Uint64Value", m, m.Value, m.Value)
   263  	}
   264  	if uv.Uint64Value != 1 {
   265  		t.Errorf("%v: Value got %v want 1", m, uv.Uint64Value)
   266  	}
   267  }
   268  
   269  func TestEmitMetricUpdateWithFields(t *testing.T) {
   270  	defer reset()
   271  
   272  	field := Field{
   273  		name:          "weirdness_type",
   274  		allowedValues: []string{"weird1", "weird2"}}
   275  
   276  	counter, err := NewUint64Metric("/weirdness", false, pb.MetricMetadata_UNITS_NONE, counterDescription, field)
   277  	if err != nil {
   278  		t.Fatalf("NewUint64Metric got err %v want nil", err)
   279  	}
   280  
   281  	if err := Initialize(); err != nil {
   282  		t.Fatalf("Initialize(): %s", err)
   283  	}
   284  
   285  	// Don't care about the registration metrics.
   286  	emitter.Reset()
   287  	EmitMetricUpdate()
   288  
   289  	// For metrics with fields, we do not emit data unless the value is
   290  	// incremented.
   291  	if len(emitter) != 0 {
   292  		t.Fatalf("EmitMetricUpdate emitted %d events want 0", len(emitter))
   293  	}
   294  
   295  	counter.IncrementBy(4, "weird1")
   296  	counter.Increment("weird2")
   297  
   298  	emitter.Reset()
   299  	EmitMetricUpdate()
   300  
   301  	if len(emitter) != 1 {
   302  		t.Fatalf("EmitMetricUpdate emitted %d events want 1", len(emitter))
   303  	}
   304  
   305  	update, ok := emitter[0].(*pb.MetricUpdate)
   306  	if !ok {
   307  		t.Fatalf("emitter %v got %T want pb.MetricUpdate", emitter[0], emitter[0])
   308  	}
   309  
   310  	if len(update.Metrics) != 2 {
   311  		t.Errorf("MetricUpdate got %d metrics want 2", len(update.Metrics))
   312  	}
   313  
   314  	foundWeird1 := false
   315  	foundWeird2 := false
   316  	for i := 0; i < len(update.Metrics); i++ {
   317  		m := update.Metrics[i]
   318  
   319  		if m.Name != "/weirdness" {
   320  			t.Errorf("Metric %+v name got %q want '/weirdness'", m, m.Name)
   321  		}
   322  		if len(m.FieldValues) != 1 {
   323  			t.Errorf("MetricUpdate got %d fields want 1", len(m.FieldValues))
   324  		}
   325  
   326  		switch m.FieldValues[0] {
   327  		case "weird1":
   328  			uv, ok := m.Value.(*pb.MetricValue_Uint64Value)
   329  			if !ok {
   330  				t.Errorf("%+v: value %v got %T want pb.MetricValue_Uint64Value", m, m.Value, m.Value)
   331  			}
   332  			if uv.Uint64Value != 4 {
   333  				t.Errorf("%v: Value got %v want 4", m, uv.Uint64Value)
   334  			}
   335  			foundWeird1 = true
   336  		case "weird2":
   337  			uv, ok := m.Value.(*pb.MetricValue_Uint64Value)
   338  			if !ok {
   339  				t.Errorf("%+v: value %v got %T want pb.MetricValue_Uint64Value", m, m.Value, m.Value)
   340  			}
   341  			if uv.Uint64Value != 1 {
   342  				t.Errorf("%v: Value got %v want 1", m, uv.Uint64Value)
   343  			}
   344  			foundWeird2 = true
   345  		}
   346  	}
   347  
   348  	if !foundWeird1 {
   349  		t.Errorf("Field value weird1 not found: %+v", emitter)
   350  	}
   351  	if !foundWeird2 {
   352  		t.Errorf("Field value weird2 not found: %+v", emitter)
   353  	}
   354  }