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