Source file src/expvar/expvar_test.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package expvar
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/sha1"
    10  	"encoding/json"
    11  	"fmt"
    12  	"net"
    13  	"net/http/httptest"
    14  	"reflect"
    15  	"runtime"
    16  	"strconv"
    17  	"sync"
    18  	"sync/atomic"
    19  	"testing"
    20  )
    21  
    22  // RemoveAll removes all exported variables.
    23  // This is for tests only.
    24  func RemoveAll() {
    25  	varKeysMu.Lock()
    26  	defer varKeysMu.Unlock()
    27  	for _, k := range varKeys {
    28  		vars.Delete(k)
    29  	}
    30  	varKeys = nil
    31  }
    32  
    33  func TestNil(t *testing.T) {
    34  	RemoveAll()
    35  	val := Get("missing")
    36  	if val != nil {
    37  		t.Errorf("got %v, want nil", val)
    38  	}
    39  }
    40  
    41  func TestInt(t *testing.T) {
    42  	RemoveAll()
    43  	reqs := NewInt("requests")
    44  	if i := reqs.Value(); i != 0 {
    45  		t.Errorf("reqs.Value() = %v, want 0", i)
    46  	}
    47  	if reqs != Get("requests").(*Int) {
    48  		t.Errorf("Get() failed.")
    49  	}
    50  
    51  	reqs.Add(1)
    52  	reqs.Add(3)
    53  	if i := reqs.Value(); i != 4 {
    54  		t.Errorf("reqs.Value() = %v, want 4", i)
    55  	}
    56  
    57  	if s := reqs.String(); s != "4" {
    58  		t.Errorf("reqs.String() = %q, want \"4\"", s)
    59  	}
    60  
    61  	reqs.Set(-2)
    62  	if i := reqs.Value(); i != -2 {
    63  		t.Errorf("reqs.Value() = %v, want -2", i)
    64  	}
    65  }
    66  
    67  func BenchmarkIntAdd(b *testing.B) {
    68  	var v Int
    69  
    70  	b.RunParallel(func(pb *testing.PB) {
    71  		for pb.Next() {
    72  			v.Add(1)
    73  		}
    74  	})
    75  }
    76  
    77  func BenchmarkIntSet(b *testing.B) {
    78  	var v Int
    79  
    80  	b.RunParallel(func(pb *testing.PB) {
    81  		for pb.Next() {
    82  			v.Set(1)
    83  		}
    84  	})
    85  }
    86  
    87  func TestFloat(t *testing.T) {
    88  	RemoveAll()
    89  	reqs := NewFloat("requests-float")
    90  	if reqs.f != 0.0 {
    91  		t.Errorf("reqs.f = %v, want 0", reqs.f)
    92  	}
    93  	if reqs != Get("requests-float").(*Float) {
    94  		t.Errorf("Get() failed.")
    95  	}
    96  
    97  	reqs.Add(1.5)
    98  	reqs.Add(1.25)
    99  	if v := reqs.Value(); v != 2.75 {
   100  		t.Errorf("reqs.Value() = %v, want 2.75", v)
   101  	}
   102  
   103  	if s := reqs.String(); s != "2.75" {
   104  		t.Errorf("reqs.String() = %q, want \"4.64\"", s)
   105  	}
   106  
   107  	reqs.Add(-2)
   108  	if v := reqs.Value(); v != 0.75 {
   109  		t.Errorf("reqs.Value() = %v, want 0.75", v)
   110  	}
   111  }
   112  
   113  func BenchmarkFloatAdd(b *testing.B) {
   114  	var f Float
   115  
   116  	b.RunParallel(func(pb *testing.PB) {
   117  		for pb.Next() {
   118  			f.Add(1.0)
   119  		}
   120  	})
   121  }
   122  
   123  func BenchmarkFloatSet(b *testing.B) {
   124  	var f Float
   125  
   126  	b.RunParallel(func(pb *testing.PB) {
   127  		for pb.Next() {
   128  			f.Set(1.0)
   129  		}
   130  	})
   131  }
   132  
   133  func TestString(t *testing.T) {
   134  	RemoveAll()
   135  	name := NewString("my-name")
   136  	if s := name.Value(); s != "" {
   137  		t.Errorf(`NewString("my-name").Value() = %q, want ""`, s)
   138  	}
   139  
   140  	name.Set("Mike")
   141  	if s, want := name.String(), `"Mike"`; s != want {
   142  		t.Errorf(`after name.Set("Mike"), name.String() = %q, want %q`, s, want)
   143  	}
   144  	if s, want := name.Value(), "Mike"; s != want {
   145  		t.Errorf(`after name.Set("Mike"), name.Value() = %q, want %q`, s, want)
   146  	}
   147  
   148  	// Make sure we produce safe JSON output.
   149  	name.Set("<")
   150  	if s, want := name.String(), "\"\\u003c\""; s != want {
   151  		t.Errorf(`after name.Set("<"), name.String() = %q, want %q`, s, want)
   152  	}
   153  }
   154  
   155  func BenchmarkStringSet(b *testing.B) {
   156  	var s String
   157  
   158  	b.RunParallel(func(pb *testing.PB) {
   159  		for pb.Next() {
   160  			s.Set("red")
   161  		}
   162  	})
   163  }
   164  
   165  func TestMapInit(t *testing.T) {
   166  	RemoveAll()
   167  	colors := NewMap("bike-shed-colors")
   168  	colors.Add("red", 1)
   169  	colors.Add("blue", 1)
   170  	colors.Add("chartreuse", 1)
   171  
   172  	n := 0
   173  	colors.Do(func(KeyValue) { n++ })
   174  	if n != 3 {
   175  		t.Errorf("after three Add calls with distinct keys, Do should invoke f 3 times; got %v", n)
   176  	}
   177  
   178  	colors.Init()
   179  
   180  	n = 0
   181  	colors.Do(func(KeyValue) { n++ })
   182  	if n != 0 {
   183  		t.Errorf("after Init, Do should invoke f 0 times; got %v", n)
   184  	}
   185  }
   186  
   187  func TestMapDelete(t *testing.T) {
   188  	RemoveAll()
   189  	colors := NewMap("bike-shed-colors")
   190  
   191  	colors.Add("red", 1)
   192  	colors.Add("red", 2)
   193  	colors.Add("blue", 4)
   194  
   195  	n := 0
   196  	colors.Do(func(KeyValue) { n++ })
   197  	if n != 2 {
   198  		t.Errorf("after two Add calls with distinct keys, Do should invoke f 2 times; got %v", n)
   199  	}
   200  
   201  	colors.Delete("red")
   202  	n = 0
   203  	colors.Do(func(KeyValue) { n++ })
   204  	if n != 1 {
   205  		t.Errorf("removed red, Do should invoke f 1 times; got %v", n)
   206  	}
   207  
   208  	colors.Delete("notfound")
   209  	n = 0
   210  	colors.Do(func(KeyValue) { n++ })
   211  	if n != 1 {
   212  		t.Errorf("attempted to remove notfound, Do should invoke f 1 times; got %v", n)
   213  	}
   214  
   215  	colors.Delete("blue")
   216  	colors.Delete("blue")
   217  	n = 0
   218  	colors.Do(func(KeyValue) { n++ })
   219  	if n != 0 {
   220  		t.Errorf("all keys removed, Do should invoke f 0 times; got %v", n)
   221  	}
   222  }
   223  
   224  func TestMapCounter(t *testing.T) {
   225  	RemoveAll()
   226  	colors := NewMap("bike-shed-colors")
   227  
   228  	colors.Add("red", 1)
   229  	colors.Add("red", 2)
   230  	colors.Add("blue", 4)
   231  	colors.AddFloat(`green "midori"`, 4.125)
   232  	if x := colors.Get("red").(*Int).Value(); x != 3 {
   233  		t.Errorf("colors.m[\"red\"] = %v, want 3", x)
   234  	}
   235  	if x := colors.Get("blue").(*Int).Value(); x != 4 {
   236  		t.Errorf("colors.m[\"blue\"] = %v, want 4", x)
   237  	}
   238  	if x := colors.Get(`green "midori"`).(*Float).Value(); x != 4.125 {
   239  		t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x)
   240  	}
   241  
   242  	// colors.String() should be '{"red":3, "blue":4}',
   243  	// though the order of red and blue could vary.
   244  	s := colors.String()
   245  	var j any
   246  	err := json.Unmarshal([]byte(s), &j)
   247  	if err != nil {
   248  		t.Errorf("colors.String() isn't valid JSON: %v", err)
   249  	}
   250  	m, ok := j.(map[string]any)
   251  	if !ok {
   252  		t.Error("colors.String() didn't produce a map.")
   253  	}
   254  	red := m["red"]
   255  	x, ok := red.(float64)
   256  	if !ok {
   257  		t.Error("red.Kind() is not a number.")
   258  	}
   259  	if x != 3 {
   260  		t.Errorf("red = %v, want 3", x)
   261  	}
   262  }
   263  
   264  func BenchmarkMapSet(b *testing.B) {
   265  	m := new(Map).Init()
   266  
   267  	v := new(Int)
   268  
   269  	b.RunParallel(func(pb *testing.PB) {
   270  		for pb.Next() {
   271  			m.Set("red", v)
   272  		}
   273  	})
   274  }
   275  
   276  func BenchmarkMapSetDifferent(b *testing.B) {
   277  	procKeys := make([][]string, runtime.GOMAXPROCS(0))
   278  	for i := range procKeys {
   279  		keys := make([]string, 4)
   280  		for j := range keys {
   281  			keys[j] = fmt.Sprint(i, j)
   282  		}
   283  		procKeys[i] = keys
   284  	}
   285  
   286  	m := new(Map).Init()
   287  	v := new(Int)
   288  	b.ResetTimer()
   289  
   290  	var n int32
   291  	b.RunParallel(func(pb *testing.PB) {
   292  		i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys)
   293  		keys := procKeys[i]
   294  
   295  		for pb.Next() {
   296  			for _, k := range keys {
   297  				m.Set(k, v)
   298  			}
   299  		}
   300  	})
   301  }
   302  
   303  // BenchmarkMapSetDifferentRandom simulates such a case where the concerned
   304  // keys of Map.Set are generated dynamically and as a result insertion is
   305  // out of order and the number of the keys may be large.
   306  func BenchmarkMapSetDifferentRandom(b *testing.B) {
   307  	keys := make([]string, 100)
   308  	for i := range keys {
   309  		keys[i] = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprint(i))))
   310  	}
   311  
   312  	v := new(Int)
   313  	b.ResetTimer()
   314  
   315  	for i := 0; i < b.N; i++ {
   316  		m := new(Map).Init()
   317  		for _, k := range keys {
   318  			m.Set(k, v)
   319  		}
   320  	}
   321  }
   322  
   323  func BenchmarkMapSetString(b *testing.B) {
   324  	m := new(Map).Init()
   325  
   326  	v := new(String)
   327  	v.Set("Hello, !")
   328  
   329  	b.RunParallel(func(pb *testing.PB) {
   330  		for pb.Next() {
   331  			m.Set("red", v)
   332  		}
   333  	})
   334  }
   335  
   336  func BenchmarkMapAddSame(b *testing.B) {
   337  	b.RunParallel(func(pb *testing.PB) {
   338  		for pb.Next() {
   339  			m := new(Map).Init()
   340  			m.Add("red", 1)
   341  			m.Add("red", 1)
   342  			m.Add("red", 1)
   343  			m.Add("red", 1)
   344  		}
   345  	})
   346  }
   347  
   348  func BenchmarkMapAddDifferent(b *testing.B) {
   349  	procKeys := make([][]string, runtime.GOMAXPROCS(0))
   350  	for i := range procKeys {
   351  		keys := make([]string, 4)
   352  		for j := range keys {
   353  			keys[j] = fmt.Sprint(i, j)
   354  		}
   355  		procKeys[i] = keys
   356  	}
   357  
   358  	b.ResetTimer()
   359  
   360  	var n int32
   361  	b.RunParallel(func(pb *testing.PB) {
   362  		i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys)
   363  		keys := procKeys[i]
   364  
   365  		for pb.Next() {
   366  			m := new(Map).Init()
   367  			for _, k := range keys {
   368  				m.Add(k, 1)
   369  			}
   370  		}
   371  	})
   372  }
   373  
   374  // BenchmarkMapAddDifferentRandom simulates such a case where that the concerned
   375  // keys of Map.Add are generated dynamically and as a result insertion is out of
   376  // order and the number of the keys may be large.
   377  func BenchmarkMapAddDifferentRandom(b *testing.B) {
   378  	keys := make([]string, 100)
   379  	for i := range keys {
   380  		keys[i] = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprint(i))))
   381  	}
   382  
   383  	b.ResetTimer()
   384  
   385  	for i := 0; i < b.N; i++ {
   386  		m := new(Map).Init()
   387  		for _, k := range keys {
   388  			m.Add(k, 1)
   389  		}
   390  	}
   391  }
   392  
   393  func BenchmarkMapAddSameSteadyState(b *testing.B) {
   394  	m := new(Map).Init()
   395  	b.RunParallel(func(pb *testing.PB) {
   396  		for pb.Next() {
   397  			m.Add("red", 1)
   398  		}
   399  	})
   400  }
   401  
   402  func BenchmarkMapAddDifferentSteadyState(b *testing.B) {
   403  	procKeys := make([][]string, runtime.GOMAXPROCS(0))
   404  	for i := range procKeys {
   405  		keys := make([]string, 4)
   406  		for j := range keys {
   407  			keys[j] = fmt.Sprint(i, j)
   408  		}
   409  		procKeys[i] = keys
   410  	}
   411  
   412  	m := new(Map).Init()
   413  	b.ResetTimer()
   414  
   415  	var n int32
   416  	b.RunParallel(func(pb *testing.PB) {
   417  		i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys)
   418  		keys := procKeys[i]
   419  
   420  		for pb.Next() {
   421  			for _, k := range keys {
   422  				m.Add(k, 1)
   423  			}
   424  		}
   425  	})
   426  }
   427  
   428  func TestFunc(t *testing.T) {
   429  	RemoveAll()
   430  	var x any = []string{"a", "b"}
   431  	f := Func(func() any { return x })
   432  	if s, exp := f.String(), `["a","b"]`; s != exp {
   433  		t.Errorf(`f.String() = %q, want %q`, s, exp)
   434  	}
   435  	if v := f.Value(); !reflect.DeepEqual(v, x) {
   436  		t.Errorf(`f.Value() = %q, want %q`, v, x)
   437  	}
   438  
   439  	x = 17
   440  	if s, exp := f.String(), `17`; s != exp {
   441  		t.Errorf(`f.String() = %q, want %q`, s, exp)
   442  	}
   443  }
   444  
   445  func TestHandler(t *testing.T) {
   446  	RemoveAll()
   447  	m := NewMap("map1")
   448  	m.Add("a", 1)
   449  	m.Add("z", 2)
   450  	m2 := NewMap("map2")
   451  	for i := 0; i < 9; i++ {
   452  		m2.Add(strconv.Itoa(i), int64(i))
   453  	}
   454  	rr := httptest.NewRecorder()
   455  	rr.Body = new(bytes.Buffer)
   456  	expvarHandler(rr, nil)
   457  	want := `{
   458  "map1": {"a": 1, "z": 2},
   459  "map2": {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8}
   460  }
   461  `
   462  	if got := rr.Body.String(); got != want {
   463  		t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want)
   464  	}
   465  }
   466  
   467  func BenchmarkRealworldExpvarUsage(b *testing.B) {
   468  	var (
   469  		bytesSent Int
   470  		bytesRead Int
   471  	)
   472  
   473  	// The benchmark creates GOMAXPROCS client/server pairs.
   474  	// Each pair creates 4 goroutines: client reader/writer and server reader/writer.
   475  	// The benchmark stresses concurrent reading and writing to the same connection.
   476  	// Such pattern is used in net/http and net/rpc.
   477  
   478  	b.StopTimer()
   479  
   480  	P := runtime.GOMAXPROCS(0)
   481  	N := b.N / P
   482  	W := 1000
   483  
   484  	// Setup P client/server connections.
   485  	clients := make([]net.Conn, P)
   486  	servers := make([]net.Conn, P)
   487  	ln, err := net.Listen("tcp", "127.0.0.1:0")
   488  	if err != nil {
   489  		b.Fatalf("Listen failed: %v", err)
   490  	}
   491  	defer ln.Close()
   492  	done := make(chan bool, 1)
   493  	go func() {
   494  		for p := 0; p < P; p++ {
   495  			s, err := ln.Accept()
   496  			if err != nil {
   497  				b.Errorf("Accept failed: %v", err)
   498  				done <- false
   499  				return
   500  			}
   501  			servers[p] = s
   502  		}
   503  		done <- true
   504  	}()
   505  	for p := 0; p < P; p++ {
   506  		c, err := net.Dial("tcp", ln.Addr().String())
   507  		if err != nil {
   508  			<-done
   509  			b.Fatalf("Dial failed: %v", err)
   510  		}
   511  		clients[p] = c
   512  	}
   513  	if !<-done {
   514  		b.FailNow()
   515  	}
   516  
   517  	b.StartTimer()
   518  
   519  	var wg sync.WaitGroup
   520  	wg.Add(4 * P)
   521  	for p := 0; p < P; p++ {
   522  		// Client writer.
   523  		go func(c net.Conn) {
   524  			defer wg.Done()
   525  			var buf [1]byte
   526  			for i := 0; i < N; i++ {
   527  				v := byte(i)
   528  				for w := 0; w < W; w++ {
   529  					v *= v
   530  				}
   531  				buf[0] = v
   532  				n, err := c.Write(buf[:])
   533  				if err != nil {
   534  					b.Errorf("Write failed: %v", err)
   535  					return
   536  				}
   537  
   538  				bytesSent.Add(int64(n))
   539  			}
   540  		}(clients[p])
   541  
   542  		// Pipe between server reader and server writer.
   543  		pipe := make(chan byte, 128)
   544  
   545  		// Server reader.
   546  		go func(s net.Conn) {
   547  			defer wg.Done()
   548  			var buf [1]byte
   549  			for i := 0; i < N; i++ {
   550  				n, err := s.Read(buf[:])
   551  
   552  				if err != nil {
   553  					b.Errorf("Read failed: %v", err)
   554  					return
   555  				}
   556  
   557  				bytesRead.Add(int64(n))
   558  				pipe <- buf[0]
   559  			}
   560  		}(servers[p])
   561  
   562  		// Server writer.
   563  		go func(s net.Conn) {
   564  			defer wg.Done()
   565  			var buf [1]byte
   566  			for i := 0; i < N; i++ {
   567  				v := <-pipe
   568  				for w := 0; w < W; w++ {
   569  					v *= v
   570  				}
   571  				buf[0] = v
   572  				n, err := s.Write(buf[:])
   573  				if err != nil {
   574  					b.Errorf("Write failed: %v", err)
   575  					return
   576  				}
   577  
   578  				bytesSent.Add(int64(n))
   579  			}
   580  			s.Close()
   581  		}(servers[p])
   582  
   583  		// Client reader.
   584  		go func(c net.Conn) {
   585  			defer wg.Done()
   586  			var buf [1]byte
   587  			for i := 0; i < N; i++ {
   588  				n, err := c.Read(buf[:])
   589  
   590  				if err != nil {
   591  					b.Errorf("Read failed: %v", err)
   592  					return
   593  				}
   594  
   595  				bytesRead.Add(int64(n))
   596  			}
   597  			c.Close()
   598  		}(clients[p])
   599  	}
   600  	wg.Wait()
   601  }
   602  

View as plain text