github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/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 "crypto/sha1" 10 "encoding/json" 11 "fmt" 12 "net" 13 "net/http/httptest" 14 "reflect" 15 "runtime" 16 "strconv" 17 "sync" 18 "sync/atomic" 19 "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 any 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]any) 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 TestMapNil(t *testing.T) { 265 RemoveAll() 266 const key = "key" 267 m := NewMap("issue527719") 268 m.Set(key, nil) 269 s := m.String() 270 var j any 271 if err := json.Unmarshal([]byte(s), &j); err != nil { 272 t.Fatalf("m.String() == %q isn't valid JSON: %v", s, err) 273 } 274 m2, ok := j.(map[string]any) 275 if !ok { 276 t.Fatalf("m.String() produced %T, wanted a map", j) 277 } 278 v, ok := m2[key] 279 if !ok { 280 t.Fatalf("missing %q in %v", key, m2) 281 } 282 if v != nil { 283 t.Fatalf("m[%q] = %v, want nil", key, v) 284 } 285 } 286 287 func BenchmarkMapSet(b *testing.B) { 288 m := new(Map).Init() 289 290 v := new(Int) 291 292 b.RunParallel(func(pb *testing.PB) { 293 for pb.Next() { 294 m.Set("red", v) 295 } 296 }) 297 } 298 299 func BenchmarkMapSetDifferent(b *testing.B) { 300 procKeys := make([][]string, runtime.GOMAXPROCS(0)) 301 for i := range procKeys { 302 keys := make([]string, 4) 303 for j := range keys { 304 keys[j] = fmt.Sprint(i, j) 305 } 306 procKeys[i] = keys 307 } 308 309 m := new(Map).Init() 310 v := new(Int) 311 b.ResetTimer() 312 313 var n int32 314 b.RunParallel(func(pb *testing.PB) { 315 i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys) 316 keys := procKeys[i] 317 318 for pb.Next() { 319 for _, k := range keys { 320 m.Set(k, v) 321 } 322 } 323 }) 324 } 325 326 // BenchmarkMapSetDifferentRandom simulates such a case where the concerned 327 // keys of Map.Set are generated dynamically and as a result insertion is 328 // out of order and the number of the keys may be large. 329 func BenchmarkMapSetDifferentRandom(b *testing.B) { 330 keys := make([]string, 100) 331 for i := range keys { 332 keys[i] = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprint(i)))) 333 } 334 335 v := new(Int) 336 b.ResetTimer() 337 338 for i := 0; i < b.N; i++ { 339 m := new(Map).Init() 340 for _, k := range keys { 341 m.Set(k, v) 342 } 343 } 344 } 345 346 func BenchmarkMapSetString(b *testing.B) { 347 m := new(Map).Init() 348 349 v := new(String) 350 v.Set("Hello, !") 351 352 b.RunParallel(func(pb *testing.PB) { 353 for pb.Next() { 354 m.Set("red", v) 355 } 356 }) 357 } 358 359 func BenchmarkMapAddSame(b *testing.B) { 360 b.RunParallel(func(pb *testing.PB) { 361 for pb.Next() { 362 m := new(Map).Init() 363 m.Add("red", 1) 364 m.Add("red", 1) 365 m.Add("red", 1) 366 m.Add("red", 1) 367 } 368 }) 369 } 370 371 func BenchmarkMapAddDifferent(b *testing.B) { 372 procKeys := make([][]string, runtime.GOMAXPROCS(0)) 373 for i := range procKeys { 374 keys := make([]string, 4) 375 for j := range keys { 376 keys[j] = fmt.Sprint(i, j) 377 } 378 procKeys[i] = keys 379 } 380 381 b.ResetTimer() 382 383 var n int32 384 b.RunParallel(func(pb *testing.PB) { 385 i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys) 386 keys := procKeys[i] 387 388 for pb.Next() { 389 m := new(Map).Init() 390 for _, k := range keys { 391 m.Add(k, 1) 392 } 393 } 394 }) 395 } 396 397 // BenchmarkMapAddDifferentRandom simulates such a case where that the concerned 398 // keys of Map.Add are generated dynamically and as a result insertion is out of 399 // order and the number of the keys may be large. 400 func BenchmarkMapAddDifferentRandom(b *testing.B) { 401 keys := make([]string, 100) 402 for i := range keys { 403 keys[i] = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprint(i)))) 404 } 405 406 b.ResetTimer() 407 408 for i := 0; i < b.N; i++ { 409 m := new(Map).Init() 410 for _, k := range keys { 411 m.Add(k, 1) 412 } 413 } 414 } 415 416 func BenchmarkMapAddSameSteadyState(b *testing.B) { 417 m := new(Map).Init() 418 b.RunParallel(func(pb *testing.PB) { 419 for pb.Next() { 420 m.Add("red", 1) 421 } 422 }) 423 } 424 425 func BenchmarkMapAddDifferentSteadyState(b *testing.B) { 426 procKeys := make([][]string, runtime.GOMAXPROCS(0)) 427 for i := range procKeys { 428 keys := make([]string, 4) 429 for j := range keys { 430 keys[j] = fmt.Sprint(i, j) 431 } 432 procKeys[i] = keys 433 } 434 435 m := new(Map).Init() 436 b.ResetTimer() 437 438 var n int32 439 b.RunParallel(func(pb *testing.PB) { 440 i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys) 441 keys := procKeys[i] 442 443 for pb.Next() { 444 for _, k := range keys { 445 m.Add(k, 1) 446 } 447 } 448 }) 449 } 450 451 func TestFunc(t *testing.T) { 452 RemoveAll() 453 var x any = []string{"a", "b"} 454 f := Func(func() any { return x }) 455 if s, exp := f.String(), `["a","b"]`; s != exp { 456 t.Errorf(`f.String() = %q, want %q`, s, exp) 457 } 458 if v := f.Value(); !reflect.DeepEqual(v, x) { 459 t.Errorf(`f.Value() = %q, want %q`, v, x) 460 } 461 462 x = 17 463 if s, exp := f.String(), `17`; s != exp { 464 t.Errorf(`f.String() = %q, want %q`, s, exp) 465 } 466 } 467 468 func TestHandler(t *testing.T) { 469 RemoveAll() 470 m := NewMap("map1") 471 m.Add("a", 1) 472 m.Add("z", 2) 473 m2 := NewMap("map2") 474 for i := 0; i < 9; i++ { 475 m2.Add(strconv.Itoa(i), int64(i)) 476 } 477 rr := httptest.NewRecorder() 478 rr.Body = new(bytes.Buffer) 479 expvarHandler(rr, nil) 480 want := `{ 481 "map1": {"a": 1, "z": 2}, 482 "map2": {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8} 483 } 484 ` 485 if got := rr.Body.String(); got != want { 486 t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want) 487 } 488 } 489 490 func BenchmarkRealworldExpvarUsage(b *testing.B) { 491 var ( 492 bytesSent Int 493 bytesRead Int 494 ) 495 496 // The benchmark creates GOMAXPROCS client/server pairs. 497 // Each pair creates 4 goroutines: client reader/writer and server reader/writer. 498 // The benchmark stresses concurrent reading and writing to the same connection. 499 // Such pattern is used in net/http and net/rpc. 500 501 b.StopTimer() 502 503 P := runtime.GOMAXPROCS(0) 504 N := b.N / P 505 W := 1000 506 507 // Setup P client/server connections. 508 clients := make([]net.Conn, P) 509 servers := make([]net.Conn, P) 510 ln, err := net.Listen("tcp", "127.0.0.1:0") 511 if err != nil { 512 b.Fatalf("Listen failed: %v", err) 513 } 514 defer ln.Close() 515 done := make(chan bool, 1) 516 go func() { 517 for p := 0; p < P; p++ { 518 s, err := ln.Accept() 519 if err != nil { 520 b.Errorf("Accept failed: %v", err) 521 done <- false 522 return 523 } 524 servers[p] = s 525 } 526 done <- true 527 }() 528 for p := 0; p < P; p++ { 529 c, err := net.Dial("tcp", ln.Addr().String()) 530 if err != nil { 531 <-done 532 b.Fatalf("Dial failed: %v", err) 533 } 534 clients[p] = c 535 } 536 if !<-done { 537 b.FailNow() 538 } 539 540 b.StartTimer() 541 542 var wg sync.WaitGroup 543 wg.Add(4 * P) 544 for p := 0; p < P; p++ { 545 // Client writer. 546 go func(c net.Conn) { 547 defer wg.Done() 548 var buf [1]byte 549 for i := 0; i < N; i++ { 550 v := byte(i) 551 for w := 0; w < W; w++ { 552 v *= v 553 } 554 buf[0] = v 555 n, err := c.Write(buf[:]) 556 if err != nil { 557 b.Errorf("Write failed: %v", err) 558 return 559 } 560 561 bytesSent.Add(int64(n)) 562 } 563 }(clients[p]) 564 565 // Pipe between server reader and server writer. 566 pipe := make(chan byte, 128) 567 568 // Server reader. 569 go func(s net.Conn) { 570 defer wg.Done() 571 var buf [1]byte 572 for i := 0; i < N; i++ { 573 n, err := s.Read(buf[:]) 574 575 if err != nil { 576 b.Errorf("Read failed: %v", err) 577 return 578 } 579 580 bytesRead.Add(int64(n)) 581 pipe <- buf[0] 582 } 583 }(servers[p]) 584 585 // Server writer. 586 go func(s net.Conn) { 587 defer wg.Done() 588 var buf [1]byte 589 for i := 0; i < N; i++ { 590 v := <-pipe 591 for w := 0; w < W; w++ { 592 v *= v 593 } 594 buf[0] = v 595 n, err := s.Write(buf[:]) 596 if err != nil { 597 b.Errorf("Write failed: %v", err) 598 return 599 } 600 601 bytesSent.Add(int64(n)) 602 } 603 s.Close() 604 }(servers[p]) 605 606 // Client reader. 607 go func(c net.Conn) { 608 defer wg.Done() 609 var buf [1]byte 610 for i := 0; i < N; i++ { 611 n, err := c.Read(buf[:]) 612 613 if err != nil { 614 b.Errorf("Read failed: %v", err) 615 return 616 } 617 618 bytesRead.Add(int64(n)) 619 } 620 c.Close() 621 }(clients[p]) 622 } 623 wg.Wait() 624 }