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