github.com/flyinox/gosm@v0.0.0-20171117061539-16768cb62077/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 TestMapInit(t *testing.T) { 165 RemoveAll() 166 colors := NewMap("bike-shed-colors") 167 colors.Add("red", 1) 168 colors.Add("blue", 1) 169 colors.Add("chartreuse", 1) 170 171 n := 0 172 colors.Do(func(KeyValue) { n++ }) 173 if n != 3 { 174 t.Errorf("after three Add calls with distinct keys, Do should invoke f 3 times; got %v", n) 175 } 176 177 colors.Init() 178 179 n = 0 180 colors.Do(func(KeyValue) { n++ }) 181 if n != 0 { 182 t.Errorf("after Init, Do should invoke f 0 times; got %v", n) 183 } 184 } 185 186 func TestMapCounter(t *testing.T) { 187 RemoveAll() 188 colors := NewMap("bike-shed-colors") 189 190 colors.Add("red", 1) 191 colors.Add("red", 2) 192 colors.Add("blue", 4) 193 colors.AddFloat(`green "midori"`, 4.125) 194 if x := colors.Get("red").(*Int).Value(); x != 3 { 195 t.Errorf("colors.m[\"red\"] = %v, want 3", x) 196 } 197 if x := colors.Get("blue").(*Int).Value(); x != 4 { 198 t.Errorf("colors.m[\"blue\"] = %v, want 4", x) 199 } 200 if x := colors.Get(`green "midori"`).(*Float).Value(); x != 4.125 { 201 t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x) 202 } 203 204 // colors.String() should be '{"red":3, "blue":4}', 205 // though the order of red and blue could vary. 206 s := colors.String() 207 var j interface{} 208 err := json.Unmarshal([]byte(s), &j) 209 if err != nil { 210 t.Errorf("colors.String() isn't valid JSON: %v", err) 211 } 212 m, ok := j.(map[string]interface{}) 213 if !ok { 214 t.Error("colors.String() didn't produce a map.") 215 } 216 red := m["red"] 217 x, ok := red.(float64) 218 if !ok { 219 t.Error("red.Kind() is not a number.") 220 } 221 if x != 3 { 222 t.Errorf("red = %v, want 3", x) 223 } 224 } 225 226 func BenchmarkMapSet(b *testing.B) { 227 m := new(Map).Init() 228 229 v := new(Int) 230 231 b.RunParallel(func(pb *testing.PB) { 232 for pb.Next() { 233 m.Set("red", v) 234 } 235 }) 236 } 237 238 func BenchmarkMapSetDifferent(b *testing.B) { 239 procKeys := make([][]string, runtime.GOMAXPROCS(0)) 240 for i := range procKeys { 241 keys := make([]string, 4) 242 for j := range keys { 243 keys[j] = fmt.Sprint(i, j) 244 } 245 procKeys[i] = keys 246 } 247 248 m := new(Map).Init() 249 v := new(Int) 250 b.ResetTimer() 251 252 var n int32 253 b.RunParallel(func(pb *testing.PB) { 254 i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys) 255 keys := procKeys[i] 256 257 for pb.Next() { 258 for _, k := range keys { 259 m.Set(k, v) 260 } 261 } 262 }) 263 } 264 265 func BenchmarkMapSetString(b *testing.B) { 266 m := new(Map).Init() 267 268 v := new(String) 269 v.Set("Hello, !") 270 271 b.RunParallel(func(pb *testing.PB) { 272 for pb.Next() { 273 m.Set("red", v) 274 } 275 }) 276 } 277 278 func BenchmarkMapAddSame(b *testing.B) { 279 b.RunParallel(func(pb *testing.PB) { 280 for pb.Next() { 281 m := new(Map).Init() 282 m.Add("red", 1) 283 m.Add("red", 1) 284 m.Add("red", 1) 285 m.Add("red", 1) 286 } 287 }) 288 } 289 290 func BenchmarkMapAddDifferent(b *testing.B) { 291 procKeys := make([][]string, runtime.GOMAXPROCS(0)) 292 for i := range procKeys { 293 keys := make([]string, 4) 294 for j := range keys { 295 keys[j] = fmt.Sprint(i, j) 296 } 297 procKeys[i] = keys 298 } 299 300 b.ResetTimer() 301 302 var n int32 303 b.RunParallel(func(pb *testing.PB) { 304 i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys) 305 keys := procKeys[i] 306 307 for pb.Next() { 308 m := new(Map).Init() 309 for _, k := range keys { 310 m.Add(k, 1) 311 } 312 } 313 }) 314 } 315 316 func BenchmarkMapAddSameSteadyState(b *testing.B) { 317 m := new(Map).Init() 318 b.RunParallel(func(pb *testing.PB) { 319 for pb.Next() { 320 m.Add("red", 1) 321 } 322 }) 323 } 324 325 func BenchmarkMapAddDifferentSteadyState(b *testing.B) { 326 procKeys := make([][]string, runtime.GOMAXPROCS(0)) 327 for i := range procKeys { 328 keys := make([]string, 4) 329 for j := range keys { 330 keys[j] = fmt.Sprint(i, j) 331 } 332 procKeys[i] = keys 333 } 334 335 m := new(Map).Init() 336 b.ResetTimer() 337 338 var n int32 339 b.RunParallel(func(pb *testing.PB) { 340 i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys) 341 keys := procKeys[i] 342 343 for pb.Next() { 344 for _, k := range keys { 345 m.Add(k, 1) 346 } 347 } 348 }) 349 } 350 351 func TestFunc(t *testing.T) { 352 RemoveAll() 353 var x interface{} = []string{"a", "b"} 354 f := Func(func() interface{} { return x }) 355 if s, exp := f.String(), `["a","b"]`; s != exp { 356 t.Errorf(`f.String() = %q, want %q`, s, exp) 357 } 358 if v := f.Value(); !reflect.DeepEqual(v, x) { 359 t.Errorf(`f.Value() = %q, want %q`, v, x) 360 } 361 362 x = 17 363 if s, exp := f.String(), `17`; s != exp { 364 t.Errorf(`f.String() = %q, want %q`, s, exp) 365 } 366 } 367 368 func TestHandler(t *testing.T) { 369 RemoveAll() 370 m := NewMap("map1") 371 m.Add("a", 1) 372 m.Add("z", 2) 373 m2 := NewMap("map2") 374 for i := 0; i < 9; i++ { 375 m2.Add(strconv.Itoa(i), int64(i)) 376 } 377 rr := httptest.NewRecorder() 378 rr.Body = new(bytes.Buffer) 379 expvarHandler(rr, nil) 380 want := `{ 381 "map1": {"a": 1, "z": 2}, 382 "map2": {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8} 383 } 384 ` 385 if got := rr.Body.String(); got != want { 386 t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want) 387 } 388 } 389 390 func BenchmarkRealworldExpvarUsage(b *testing.B) { 391 var ( 392 bytesSent Int 393 bytesRead Int 394 ) 395 396 // The benchmark creates GOMAXPROCS client/server pairs. 397 // Each pair creates 4 goroutines: client reader/writer and server reader/writer. 398 // The benchmark stresses concurrent reading and writing to the same connection. 399 // Such pattern is used in net/http and net/rpc. 400 401 b.StopTimer() 402 403 P := runtime.GOMAXPROCS(0) 404 N := b.N / P 405 W := 1000 406 407 // Setup P client/server connections. 408 clients := make([]net.Conn, P) 409 servers := make([]net.Conn, P) 410 ln, err := net.Listen("tcp", "127.0.0.1:0") 411 if err != nil { 412 b.Fatalf("Listen failed: %v", err) 413 } 414 defer ln.Close() 415 done := make(chan bool) 416 go func() { 417 for p := 0; p < P; p++ { 418 s, err := ln.Accept() 419 if err != nil { 420 b.Errorf("Accept failed: %v", err) 421 return 422 } 423 servers[p] = s 424 } 425 done <- true 426 }() 427 for p := 0; p < P; p++ { 428 c, err := net.Dial("tcp", ln.Addr().String()) 429 if err != nil { 430 b.Fatalf("Dial failed: %v", err) 431 } 432 clients[p] = c 433 } 434 <-done 435 436 b.StartTimer() 437 438 var wg sync.WaitGroup 439 wg.Add(4 * P) 440 for p := 0; p < P; p++ { 441 // Client writer. 442 go func(c net.Conn) { 443 defer wg.Done() 444 var buf [1]byte 445 for i := 0; i < N; i++ { 446 v := byte(i) 447 for w := 0; w < W; w++ { 448 v *= v 449 } 450 buf[0] = v 451 n, err := c.Write(buf[:]) 452 if err != nil { 453 b.Errorf("Write failed: %v", err) 454 return 455 } 456 457 bytesSent.Add(int64(n)) 458 } 459 }(clients[p]) 460 461 // Pipe between server reader and server writer. 462 pipe := make(chan byte, 128) 463 464 // Server reader. 465 go func(s net.Conn) { 466 defer wg.Done() 467 var buf [1]byte 468 for i := 0; i < N; i++ { 469 n, err := s.Read(buf[:]) 470 471 if err != nil { 472 b.Errorf("Read failed: %v", err) 473 return 474 } 475 476 bytesRead.Add(int64(n)) 477 pipe <- buf[0] 478 } 479 }(servers[p]) 480 481 // Server writer. 482 go func(s net.Conn) { 483 defer wg.Done() 484 var buf [1]byte 485 for i := 0; i < N; i++ { 486 v := <-pipe 487 for w := 0; w < W; w++ { 488 v *= v 489 } 490 buf[0] = v 491 n, err := s.Write(buf[:]) 492 if err != nil { 493 b.Errorf("Write failed: %v", err) 494 return 495 } 496 497 bytesSent.Add(int64(n)) 498 } 499 s.Close() 500 }(servers[p]) 501 502 // Client reader. 503 go func(c net.Conn) { 504 defer wg.Done() 505 var buf [1]byte 506 for i := 0; i < N; i++ { 507 n, err := c.Read(buf[:]) 508 509 if err != nil { 510 b.Errorf("Read failed: %v", err) 511 return 512 } 513 514 bytesRead.Add(int64(n)) 515 } 516 c.Close() 517 }(clients[p]) 518 } 519 wg.Wait() 520 }