github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/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 "github.com/x04/go/src/bytes" 9 "github.com/x04/go/src/crypto/sha1" 10 "github.com/x04/go/src/encoding/json" 11 "github.com/x04/go/src/fmt" 12 "github.com/x04/go/src/net" 13 "github.com/x04/go/src/net/http/httptest" 14 "github.com/x04/go/src/reflect" 15 "github.com/x04/go/src/runtime" 16 "github.com/x04/go/src/strconv" 17 "github.com/x04/go/src/sync" 18 "github.com/x04/go/src/sync/atomic" 19 "github.com/x04/go/src/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 interface{} 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]interface{}) 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 interface{} = []string{"a", "b"} 431 f := Func(func() interface{} { 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) 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 return 499 } 500 servers[p] = s 501 } 502 done <- true 503 }() 504 for p := 0; p < P; p++ { 505 c, err := net.Dial("tcp", ln.Addr().String()) 506 if err != nil { 507 b.Fatalf("Dial failed: %v", err) 508 } 509 clients[p] = c 510 } 511 <-done 512 513 b.StartTimer() 514 515 var wg sync.WaitGroup 516 wg.Add(4 * P) 517 for p := 0; p < P; p++ { 518 // Client writer. 519 go func(c net.Conn) { 520 defer wg.Done() 521 var buf [1]byte 522 for i := 0; i < N; i++ { 523 v := byte(i) 524 for w := 0; w < W; w++ { 525 v *= v 526 } 527 buf[0] = v 528 n, err := c.Write(buf[:]) 529 if err != nil { 530 b.Errorf("Write failed: %v", err) 531 return 532 } 533 534 bytesSent.Add(int64(n)) 535 } 536 }(clients[p]) 537 538 // Pipe between server reader and server writer. 539 pipe := make(chan byte, 128) 540 541 // Server reader. 542 go func(s net.Conn) { 543 defer wg.Done() 544 var buf [1]byte 545 for i := 0; i < N; i++ { 546 n, err := s.Read(buf[:]) 547 548 if err != nil { 549 b.Errorf("Read failed: %v", err) 550 return 551 } 552 553 bytesRead.Add(int64(n)) 554 pipe <- buf[0] 555 } 556 }(servers[p]) 557 558 // Server writer. 559 go func(s net.Conn) { 560 defer wg.Done() 561 var buf [1]byte 562 for i := 0; i < N; i++ { 563 v := <-pipe 564 for w := 0; w < W; w++ { 565 v *= v 566 } 567 buf[0] = v 568 n, err := s.Write(buf[:]) 569 if err != nil { 570 b.Errorf("Write failed: %v", err) 571 return 572 } 573 574 bytesSent.Add(int64(n)) 575 } 576 s.Close() 577 }(servers[p]) 578 579 // Client reader. 580 go func(c net.Conn) { 581 defer wg.Done() 582 var buf [1]byte 583 for i := 0; i < N; i++ { 584 n, err := c.Read(buf[:]) 585 586 if err != nil { 587 b.Errorf("Read failed: %v", err) 588 return 589 } 590 591 bytesRead.Add(int64(n)) 592 } 593 c.Close() 594 }(clients[p]) 595 } 596 wg.Wait() 597 }