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