Source file
src/expvar/expvar_test.go
1
2
3
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
23
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
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
243
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 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
304
305
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
375
376
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 any = []string{"a", "b"}
431 f := Func(func() any { 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
474
475
476
477
478 b.StopTimer()
479
480 P := runtime.GOMAXPROCS(0)
481 N := b.N / P
482 W := 1000
483
484
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, 1)
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 done <- false
499 return
500 }
501 servers[p] = s
502 }
503 done <- true
504 }()
505 for p := 0; p < P; p++ {
506 c, err := net.Dial("tcp", ln.Addr().String())
507 if err != nil {
508 <-done
509 b.Fatalf("Dial failed: %v", err)
510 }
511 clients[p] = c
512 }
513 if !<-done {
514 b.FailNow()
515 }
516
517 b.StartTimer()
518
519 var wg sync.WaitGroup
520 wg.Add(4 * P)
521 for p := 0; p < P; p++ {
522
523 go func(c net.Conn) {
524 defer wg.Done()
525 var buf [1]byte
526 for i := 0; i < N; i++ {
527 v := byte(i)
528 for w := 0; w < W; w++ {
529 v *= v
530 }
531 buf[0] = v
532 n, err := c.Write(buf[:])
533 if err != nil {
534 b.Errorf("Write failed: %v", err)
535 return
536 }
537
538 bytesSent.Add(int64(n))
539 }
540 }(clients[p])
541
542
543 pipe := make(chan byte, 128)
544
545
546 go func(s net.Conn) {
547 defer wg.Done()
548 var buf [1]byte
549 for i := 0; i < N; i++ {
550 n, err := s.Read(buf[:])
551
552 if err != nil {
553 b.Errorf("Read failed: %v", err)
554 return
555 }
556
557 bytesRead.Add(int64(n))
558 pipe <- buf[0]
559 }
560 }(servers[p])
561
562
563 go func(s net.Conn) {
564 defer wg.Done()
565 var buf [1]byte
566 for i := 0; i < N; i++ {
567 v := <-pipe
568 for w := 0; w < W; w++ {
569 v *= v
570 }
571 buf[0] = v
572 n, err := s.Write(buf[:])
573 if err != nil {
574 b.Errorf("Write failed: %v", err)
575 return
576 }
577
578 bytesSent.Add(int64(n))
579 }
580 s.Close()
581 }(servers[p])
582
583
584 go func(c net.Conn) {
585 defer wg.Done()
586 var buf [1]byte
587 for i := 0; i < N; i++ {
588 n, err := c.Read(buf[:])
589
590 if err != nil {
591 b.Errorf("Read failed: %v", err)
592 return
593 }
594
595 bytesRead.Add(int64(n))
596 }
597 c.Close()
598 }(clients[p])
599 }
600 wg.Wait()
601 }
602
View as plain text