...

Source file src/github.com/go-kit/kit/metrics/generic/generic_test.go

Documentation: github.com/go-kit/kit/metrics/generic

     1  package generic_test
     2  
     3  // This is package generic_test in order to get around an import cycle: this
     4  // package imports teststat to do its testing, but package teststat imports
     5  // generic to use its Histogram in the Quantiles helper function.
     6  
     7  import (
     8  	"go/ast"
     9  	"go/importer"
    10  	"go/parser"
    11  	"go/token"
    12  	"go/types"
    13  	"io/ioutil"
    14  	"math"
    15  	"math/rand"
    16  	"sync"
    17  	"testing"
    18  
    19  	"github.com/go-kit/kit/metrics/generic"
    20  	"github.com/go-kit/kit/metrics/teststat"
    21  )
    22  
    23  func TestCounter(t *testing.T) {
    24  	name := "my_counter"
    25  	counter := generic.NewCounter(name).With("label", "counter").(*generic.Counter)
    26  	if want, have := name, counter.Name; want != have {
    27  		t.Errorf("Name: want %q, have %q", want, have)
    28  	}
    29  	value := counter.Value
    30  	if err := teststat.TestCounter(counter, value); err != nil {
    31  		t.Fatal(err)
    32  	}
    33  }
    34  
    35  func TestValueReset(t *testing.T) {
    36  	counter := generic.NewCounter("test_value_reset")
    37  	counter.Add(123)
    38  	counter.Add(456)
    39  	counter.Add(789)
    40  	if want, have := float64(123+456+789), counter.ValueReset(); want != have {
    41  		t.Errorf("want %f, have %f", want, have)
    42  	}
    43  	if want, have := float64(0), counter.Value(); want != have {
    44  		t.Errorf("want %f, have %f", want, have)
    45  	}
    46  }
    47  
    48  func TestGauge(t *testing.T) {
    49  	name := "my_gauge"
    50  	gauge := generic.NewGauge(name).With("label", "gauge").(*generic.Gauge)
    51  	if want, have := name, gauge.Name; want != have {
    52  		t.Errorf("Name: want %q, have %q", want, have)
    53  	}
    54  	value := func() []float64 { return []float64{gauge.Value()} }
    55  	if err := teststat.TestGauge(gauge, value); err != nil {
    56  		t.Fatal(err)
    57  	}
    58  }
    59  
    60  func TestHistogram(t *testing.T) {
    61  	name := "my_histogram"
    62  	histogram := generic.NewHistogram(name, 50).With("label", "histogram").(*generic.Histogram)
    63  	if want, have := name, histogram.Name; want != have {
    64  		t.Errorf("Name: want %q, have %q", want, have)
    65  	}
    66  	quantiles := func() (float64, float64, float64, float64) {
    67  		return histogram.Quantile(0.50), histogram.Quantile(0.90), histogram.Quantile(0.95), histogram.Quantile(0.99)
    68  	}
    69  	if err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {
    70  		t.Fatal(err)
    71  	}
    72  }
    73  
    74  func TestIssue424(t *testing.T) {
    75  	var (
    76  		histogram   = generic.NewHistogram("dont_panic", 50)
    77  		concurrency = 100
    78  		operations  = 1000
    79  		wg          sync.WaitGroup
    80  	)
    81  
    82  	wg.Add(concurrency)
    83  	for i := 0; i < concurrency; i++ {
    84  		go func() {
    85  			defer wg.Done()
    86  			for j := 0; j < operations; j++ {
    87  				histogram.Observe(float64(j))
    88  				histogram.Observe(histogram.Quantile(0.5))
    89  			}
    90  		}()
    91  	}
    92  	wg.Wait()
    93  }
    94  
    95  func TestSimpleHistogram(t *testing.T) {
    96  	histogram := generic.NewSimpleHistogram().With("label", "simple_histogram").(*generic.SimpleHistogram)
    97  	var (
    98  		sum   int
    99  		count = 1234 // not too big
   100  	)
   101  	for i := 0; i < count; i++ {
   102  		value := rand.Intn(1000)
   103  		sum += value
   104  		histogram.Observe(float64(value))
   105  	}
   106  
   107  	var (
   108  		want      = float64(sum) / float64(count)
   109  		have      = histogram.ApproximateMovingAverage()
   110  		tolerance = 0.001 // real real slim
   111  	)
   112  	if math.Abs(want-have)/want > tolerance {
   113  		t.Errorf("want %f, have %f", want, have)
   114  	}
   115  }
   116  
   117  // Naive atomic alignment test.
   118  // The problem is related to the use of `atomic.*` and not directly to a structure.
   119  // But currently works for Counter and Gauge.
   120  // To have a more solid test, this test should be removed and the other tests should be run on a 32-bit arch.
   121  func TestAtomicAlignment(t *testing.T) {
   122  	content, err := ioutil.ReadFile("./generic.go")
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  
   127  	fset := token.NewFileSet()
   128  
   129  	file, err := parser.ParseFile(fset, "generic.go", content, parser.ParseComments)
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  
   134  	conf := types.Config{Importer: importer.ForCompiler(fset, "source", nil)}
   135  
   136  	pkg, err := conf.Check(".", fset, []*ast.File{file}, nil)
   137  	if err != nil {
   138  		t.Fatal(err)
   139  	}
   140  
   141  	// uses ARM as reference for 32-bit arch
   142  	sizes := types.SizesFor("gc", "arm")
   143  
   144  	names := []string{"Counter", "Gauge"}
   145  
   146  	for _, name := range names {
   147  		t.Run(name, func(t *testing.T) {
   148  			checkAtomicAlignment(t, sizes, pkg.Scope().Lookup(name), pkg)
   149  		})
   150  	}
   151  }
   152  
   153  func checkAtomicAlignment(t *testing.T, sizes types.Sizes, obj types.Object, pkg *types.Package) {
   154  	t.Helper()
   155  
   156  	st := obj.Type().Underlying().(*types.Struct)
   157  
   158  	posToCheck := make(map[int]types.Type)
   159  
   160  	var vars []*types.Var
   161  	for i := 0; i < st.NumFields(); i++ {
   162  		field := st.Field(i)
   163  
   164  		if v, ok := field.Type().(*types.Basic); ok {
   165  			switch v.Kind() {
   166  			case types.Uint64, types.Float64, types.Int64:
   167  				posToCheck[i] = v
   168  			}
   169  		}
   170  
   171  		vars = append(vars, types.NewVar(field.Pos(), pkg, field.Name(), field.Type()))
   172  	}
   173  
   174  	offsets := sizes.Offsetsof(vars)
   175  	for i, offset := range offsets {
   176  		if _, ok := posToCheck[i]; !ok {
   177  			continue
   178  		}
   179  
   180  		if offset%8 != 0 {
   181  			t.Errorf("misalignment detected in %s for the type %s, offset %d", obj.Name(), posToCheck[i], offset)
   182  		}
   183  	}
   184  }
   185  

View as plain text