Home | History | Annotate | Download | only in expvar
      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 	"encoding/json"
     10 	"math"
     11 	"net"
     12 	"net/http/httptest"
     13 	"runtime"
     14 	"strconv"
     15 	"sync"
     16 	"sync/atomic"
     17 	"testing"
     18 )
     19 
     20 // RemoveAll removes all exported variables.
     21 // This is for tests only.
     22 func RemoveAll() {
     23 	mutex.Lock()
     24 	defer mutex.Unlock()
     25 	vars = make(map[string]Var)
     26 	varKeys = nil
     27 }
     28 
     29 func TestInt(t *testing.T) {
     30 	RemoveAll()
     31 	reqs := NewInt("requests")
     32 	if reqs.i != 0 {
     33 		t.Errorf("reqs.i = %v, want 0", reqs.i)
     34 	}
     35 	if reqs != Get("requests").(*Int) {
     36 		t.Errorf("Get() failed.")
     37 	}
     38 
     39 	reqs.Add(1)
     40 	reqs.Add(3)
     41 	if reqs.i != 4 {
     42 		t.Errorf("reqs.i = %v, want 4", reqs.i)
     43 	}
     44 
     45 	if s := reqs.String(); s != "4" {
     46 		t.Errorf("reqs.String() = %q, want \"4\"", s)
     47 	}
     48 
     49 	reqs.Set(-2)
     50 	if reqs.i != -2 {
     51 		t.Errorf("reqs.i = %v, want -2", reqs.i)
     52 	}
     53 }
     54 
     55 func BenchmarkIntAdd(b *testing.B) {
     56 	var v Int
     57 
     58 	b.RunParallel(func(pb *testing.PB) {
     59 		for pb.Next() {
     60 			v.Add(1)
     61 		}
     62 	})
     63 }
     64 
     65 func BenchmarkIntSet(b *testing.B) {
     66 	var v Int
     67 
     68 	b.RunParallel(func(pb *testing.PB) {
     69 		for pb.Next() {
     70 			v.Set(1)
     71 		}
     72 	})
     73 }
     74 
     75 func (v *Float) val() float64 {
     76 	return math.Float64frombits(atomic.LoadUint64(&v.f))
     77 }
     78 
     79 func TestFloat(t *testing.T) {
     80 	RemoveAll()
     81 	reqs := NewFloat("requests-float")
     82 	if reqs.f != 0.0 {
     83 		t.Errorf("reqs.f = %v, want 0", reqs.f)
     84 	}
     85 	if reqs != Get("requests-float").(*Float) {
     86 		t.Errorf("Get() failed.")
     87 	}
     88 
     89 	reqs.Add(1.5)
     90 	reqs.Add(1.25)
     91 	if v := reqs.val(); v != 2.75 {
     92 		t.Errorf("reqs.val() = %v, want 2.75", v)
     93 	}
     94 
     95 	if s := reqs.String(); s != "2.75" {
     96 		t.Errorf("reqs.String() = %q, want \"4.64\"", s)
     97 	}
     98 
     99 	reqs.Add(-2)
    100 	if v := reqs.val(); v != 0.75 {
    101 		t.Errorf("reqs.val() = %v, want 0.75", v)
    102 	}
    103 }
    104 
    105 func BenchmarkFloatAdd(b *testing.B) {
    106 	var f Float
    107 
    108 	b.RunParallel(func(pb *testing.PB) {
    109 		for pb.Next() {
    110 			f.Add(1.0)
    111 		}
    112 	})
    113 }
    114 
    115 func BenchmarkFloatSet(b *testing.B) {
    116 	var f Float
    117 
    118 	b.RunParallel(func(pb *testing.PB) {
    119 		for pb.Next() {
    120 			f.Set(1.0)
    121 		}
    122 	})
    123 }
    124 
    125 func TestString(t *testing.T) {
    126 	RemoveAll()
    127 	name := NewString("my-name")
    128 	if name.s != "" {
    129 		t.Errorf("name.s = %q, want \"\"", name.s)
    130 	}
    131 
    132 	name.Set("Mike")
    133 	if name.s != "Mike" {
    134 		t.Errorf("name.s = %q, want \"Mike\"", name.s)
    135 	}
    136 
    137 	if s := name.String(); s != "\"Mike\"" {
    138 		t.Errorf("reqs.String() = %q, want \"\"Mike\"\"", s)
    139 	}
    140 }
    141 
    142 func BenchmarkStringSet(b *testing.B) {
    143 	var s String
    144 
    145 	b.RunParallel(func(pb *testing.PB) {
    146 		for pb.Next() {
    147 			s.Set("red")
    148 		}
    149 	})
    150 }
    151 
    152 func TestMapCounter(t *testing.T) {
    153 	RemoveAll()
    154 	colors := NewMap("bike-shed-colors")
    155 
    156 	colors.Add("red", 1)
    157 	colors.Add("red", 2)
    158 	colors.Add("blue", 4)
    159 	colors.AddFloat(`green "midori"`, 4.125)
    160 	if x := colors.m["red"].(*Int).i; x != 3 {
    161 		t.Errorf("colors.m[\"red\"] = %v, want 3", x)
    162 	}
    163 	if x := colors.m["blue"].(*Int).i; x != 4 {
    164 		t.Errorf("colors.m[\"blue\"] = %v, want 4", x)
    165 	}
    166 	if x := colors.m[`green "midori"`].(*Float).val(); x != 4.125 {
    167 		t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x)
    168 	}
    169 
    170 	// colors.String() should be '{"red":3, "blue":4}',
    171 	// though the order of red and blue could vary.
    172 	s := colors.String()
    173 	var j interface{}
    174 	err := json.Unmarshal([]byte(s), &j)
    175 	if err != nil {
    176 		t.Errorf("colors.String() isn't valid JSON: %v", err)
    177 	}
    178 	m, ok := j.(map[string]interface{})
    179 	if !ok {
    180 		t.Error("colors.String() didn't produce a map.")
    181 	}
    182 	red := m["red"]
    183 	x, ok := red.(float64)
    184 	if !ok {
    185 		t.Error("red.Kind() is not a number.")
    186 	}
    187 	if x != 3 {
    188 		t.Errorf("red = %v, want 3", x)
    189 	}
    190 }
    191 
    192 func BenchmarkMapSet(b *testing.B) {
    193 	m := new(Map).Init()
    194 
    195 	v := new(Int)
    196 
    197 	b.RunParallel(func(pb *testing.PB) {
    198 		for pb.Next() {
    199 			m.Set("red", v)
    200 		}
    201 	})
    202 }
    203 
    204 func BenchmarkMapAddSame(b *testing.B) {
    205 	for i := 0; i < b.N; i++ {
    206 		m := new(Map).Init()
    207 		m.Add("red", 1)
    208 		m.Add("red", 1)
    209 		m.Add("red", 1)
    210 		m.Add("red", 1)
    211 	}
    212 }
    213 
    214 func BenchmarkMapAddDifferent(b *testing.B) {
    215 	for i := 0; i < b.N; i++ {
    216 		m := new(Map).Init()
    217 		m.Add("red", 1)
    218 		m.Add("blue", 1)
    219 		m.Add("green", 1)
    220 		m.Add("yellow", 1)
    221 	}
    222 }
    223 
    224 func TestFunc(t *testing.T) {
    225 	RemoveAll()
    226 	var x interface{} = []string{"a", "b"}
    227 	f := Func(func() interface{} { return x })
    228 	if s, exp := f.String(), `["a","b"]`; s != exp {
    229 		t.Errorf(`f.String() = %q, want %q`, s, exp)
    230 	}
    231 
    232 	x = 17
    233 	if s, exp := f.String(), `17`; s != exp {
    234 		t.Errorf(`f.String() = %q, want %q`, s, exp)
    235 	}
    236 }
    237 
    238 func TestHandler(t *testing.T) {
    239 	RemoveAll()
    240 	m := NewMap("map1")
    241 	m.Add("a", 1)
    242 	m.Add("z", 2)
    243 	m2 := NewMap("map2")
    244 	for i := 0; i < 9; i++ {
    245 		m2.Add(strconv.Itoa(i), int64(i))
    246 	}
    247 	rr := httptest.NewRecorder()
    248 	rr.Body = new(bytes.Buffer)
    249 	expvarHandler(rr, nil)
    250 	want := `{
    251 "map1": {"a": 1, "z": 2},
    252 "map2": {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8}
    253 }
    254 `
    255 	if got := rr.Body.String(); got != want {
    256 		t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want)
    257 	}
    258 }
    259 
    260 func BenchmarkRealworldExpvarUsage(b *testing.B) {
    261 	var (
    262 		bytesSent Int
    263 		bytesRead Int
    264 	)
    265 
    266 	// The benchmark creates GOMAXPROCS client/server pairs.
    267 	// Each pair creates 4 goroutines: client reader/writer and server reader/writer.
    268 	// The benchmark stresses concurrent reading and writing to the same connection.
    269 	// Such pattern is used in net/http and net/rpc.
    270 
    271 	b.StopTimer()
    272 
    273 	P := runtime.GOMAXPROCS(0)
    274 	N := b.N / P
    275 	W := 1000
    276 
    277 	// Setup P client/server connections.
    278 	clients := make([]net.Conn, P)
    279 	servers := make([]net.Conn, P)
    280 	ln, err := net.Listen("tcp", "127.0.0.1:0")
    281 	if err != nil {
    282 		b.Fatalf("Listen failed: %v", err)
    283 	}
    284 	defer ln.Close()
    285 	done := make(chan bool)
    286 	go func() {
    287 		for p := 0; p < P; p++ {
    288 			s, err := ln.Accept()
    289 			if err != nil {
    290 				b.Errorf("Accept failed: %v", err)
    291 				return
    292 			}
    293 			servers[p] = s
    294 		}
    295 		done <- true
    296 	}()
    297 	for p := 0; p < P; p++ {
    298 		c, err := net.Dial("tcp", ln.Addr().String())
    299 		if err != nil {
    300 			b.Fatalf("Dial failed: %v", err)
    301 		}
    302 		clients[p] = c
    303 	}
    304 	<-done
    305 
    306 	b.StartTimer()
    307 
    308 	var wg sync.WaitGroup
    309 	wg.Add(4 * P)
    310 	for p := 0; p < P; p++ {
    311 		// Client writer.
    312 		go func(c net.Conn) {
    313 			defer wg.Done()
    314 			var buf [1]byte
    315 			for i := 0; i < N; i++ {
    316 				v := byte(i)
    317 				for w := 0; w < W; w++ {
    318 					v *= v
    319 				}
    320 				buf[0] = v
    321 				n, err := c.Write(buf[:])
    322 				if err != nil {
    323 					b.Errorf("Write failed: %v", err)
    324 					return
    325 				}
    326 
    327 				bytesSent.Add(int64(n))
    328 			}
    329 		}(clients[p])
    330 
    331 		// Pipe between server reader and server writer.
    332 		pipe := make(chan byte, 128)
    333 
    334 		// Server reader.
    335 		go func(s net.Conn) {
    336 			defer wg.Done()
    337 			var buf [1]byte
    338 			for i := 0; i < N; i++ {
    339 				n, err := s.Read(buf[:])
    340 
    341 				if err != nil {
    342 					b.Errorf("Read failed: %v", err)
    343 					return
    344 				}
    345 
    346 				bytesRead.Add(int64(n))
    347 				pipe <- buf[0]
    348 			}
    349 		}(servers[p])
    350 
    351 		// Server writer.
    352 		go func(s net.Conn) {
    353 			defer wg.Done()
    354 			var buf [1]byte
    355 			for i := 0; i < N; i++ {
    356 				v := <-pipe
    357 				for w := 0; w < W; w++ {
    358 					v *= v
    359 				}
    360 				buf[0] = v
    361 				n, err := s.Write(buf[:])
    362 				if err != nil {
    363 					b.Errorf("Write failed: %v", err)
    364 					return
    365 				}
    366 
    367 				bytesSent.Add(int64(n))
    368 			}
    369 			s.Close()
    370 		}(servers[p])
    371 
    372 		// Client reader.
    373 		go func(c net.Conn) {
    374 			defer wg.Done()
    375 			var buf [1]byte
    376 			for i := 0; i < N; i++ {
    377 				n, err := c.Read(buf[:])
    378 
    379 				if err != nil {
    380 					b.Errorf("Read failed: %v", err)
    381 					return
    382 				}
    383 
    384 				bytesRead.Add(int64(n))
    385 			}
    386 			c.Close()
    387 		}(clients[p])
    388 	}
    389 	wg.Wait()
    390 }
    391