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 vars.keysMu.Lock()
26 defer vars.keysMu.Unlock()
27 for _, k := range vars.keys {
28 vars.m.Delete(k)
29 }
30 vars.keys = 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.Load() != 0.0 {
91 t.Errorf("reqs.f = %v, want 0", reqs.f.Load())
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 if v := colors.Get("red"); v != nil {
203 t.Errorf("removed red, Get should return nil; got %v", v)
204 }
205 n = 0
206 colors.Do(func(KeyValue) { n++ })
207 if n != 1 {
208 t.Errorf("removed red, Do should invoke f 1 times; got %v", n)
209 }
210
211 colors.Delete("notfound")
212 n = 0
213 colors.Do(func(KeyValue) { n++ })
214 if n != 1 {
215 t.Errorf("attempted to remove notfound, Do should invoke f 1 times; got %v", n)
216 }
217
218 colors.Delete("blue")
219 colors.Delete("blue")
220 if v := colors.Get("blue"); v != nil {
221 t.Errorf("removed blue, Get should return nil; got %v", v)
222 }
223 n = 0
224 colors.Do(func(KeyValue) { n++ })
225 if n != 0 {
226 t.Errorf("all keys removed, Do should invoke f 0 times; got %v", n)
227 }
228 }
229
230 func TestMapCounter(t *testing.T) {
231 RemoveAll()
232 colors := NewMap("bike-shed-colors")
233
234 colors.Add("red", 1)
235 colors.Add("red", 2)
236 colors.Add("blue", 4)
237 colors.AddFloat(`green "midori"`, 4.125)
238 if x := colors.Get("red").(*Int).Value(); x != 3 {
239 t.Errorf("colors.m[\"red\"] = %v, want 3", x)
240 }
241 if x := colors.Get("blue").(*Int).Value(); x != 4 {
242 t.Errorf("colors.m[\"blue\"] = %v, want 4", x)
243 }
244 if x := colors.Get(`green "midori"`).(*Float).Value(); x != 4.125 {
245 t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x)
246 }
247
248
249
250 s := colors.String()
251 var j any
252 err := json.Unmarshal([]byte(s), &j)
253 if err != nil {
254 t.Errorf("colors.String() isn't valid JSON: %v", err)
255 }
256 m, ok := j.(map[string]any)
257 if !ok {
258 t.Error("colors.String() didn't produce a map.")
259 }
260 red := m["red"]
261 x, ok := red.(float64)
262 if !ok {
263 t.Error("red.Kind() is not a number.")
264 }
265 if x != 3 {
266 t.Errorf("red = %v, want 3", x)
267 }
268 }
269
270 func TestMapNil(t *testing.T) {
271 RemoveAll()
272 const key = "key"
273 m := NewMap("issue527719")
274 m.Set(key, nil)
275 s := m.String()
276 var j any
277 if err := json.Unmarshal([]byte(s), &j); err != nil {
278 t.Fatalf("m.String() == %q isn't valid JSON: %v", s, err)
279 }
280 m2, ok := j.(map[string]any)
281 if !ok {
282 t.Fatalf("m.String() produced %T, wanted a map", j)
283 }
284 v, ok := m2[key]
285 if !ok {
286 t.Fatalf("missing %q in %v", key, m2)
287 }
288 if v != nil {
289 t.Fatalf("m[%q] = %v, want nil", key, v)
290 }
291 }
292
293 func BenchmarkMapSet(b *testing.B) {
294 m := new(Map).Init()
295
296 v := new(Int)
297
298 b.RunParallel(func(pb *testing.PB) {
299 for pb.Next() {
300 m.Set("red", v)
301 }
302 })
303 }
304
305 func BenchmarkMapSetDifferent(b *testing.B) {
306 procKeys := make([][]string, runtime.GOMAXPROCS(0))
307 for i := range procKeys {
308 keys := make([]string, 4)
309 for j := range keys {
310 keys[j] = fmt.Sprint(i, j)
311 }
312 procKeys[i] = keys
313 }
314
315 m := new(Map).Init()
316 v := new(Int)
317 b.ResetTimer()
318
319 var n int32
320 b.RunParallel(func(pb *testing.PB) {
321 i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys)
322 keys := procKeys[i]
323
324 for pb.Next() {
325 for _, k := range keys {
326 m.Set(k, v)
327 }
328 }
329 })
330 }
331
332
333
334
335 func BenchmarkMapSetDifferentRandom(b *testing.B) {
336 keys := make([]string, 100)
337 for i := range keys {
338 keys[i] = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprint(i))))
339 }
340
341 v := new(Int)
342 b.ResetTimer()
343
344 for i := 0; i < b.N; i++ {
345 m := new(Map).Init()
346 for _, k := range keys {
347 m.Set(k, v)
348 }
349 }
350 }
351
352 func BenchmarkMapSetString(b *testing.B) {
353 m := new(Map).Init()
354
355 v := new(String)
356 v.Set("Hello, !")
357
358 b.RunParallel(func(pb *testing.PB) {
359 for pb.Next() {
360 m.Set("red", v)
361 }
362 })
363 }
364
365 func BenchmarkMapAddSame(b *testing.B) {
366 b.RunParallel(func(pb *testing.PB) {
367 for pb.Next() {
368 m := new(Map).Init()
369 m.Add("red", 1)
370 m.Add("red", 1)
371 m.Add("red", 1)
372 m.Add("red", 1)
373 }
374 })
375 }
376
377 func BenchmarkMapAddDifferent(b *testing.B) {
378 procKeys := make([][]string, runtime.GOMAXPROCS(0))
379 for i := range procKeys {
380 keys := make([]string, 4)
381 for j := range keys {
382 keys[j] = fmt.Sprint(i, j)
383 }
384 procKeys[i] = keys
385 }
386
387 b.ResetTimer()
388
389 var n int32
390 b.RunParallel(func(pb *testing.PB) {
391 i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys)
392 keys := procKeys[i]
393
394 for pb.Next() {
395 m := new(Map).Init()
396 for _, k := range keys {
397 m.Add(k, 1)
398 }
399 }
400 })
401 }
402
403
404
405
406 func BenchmarkMapAddDifferentRandom(b *testing.B) {
407 keys := make([]string, 100)
408 for i := range keys {
409 keys[i] = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprint(i))))
410 }
411
412 b.ResetTimer()
413
414 for i := 0; i < b.N; i++ {
415 m := new(Map).Init()
416 for _, k := range keys {
417 m.Add(k, 1)
418 }
419 }
420 }
421
422 func BenchmarkMapAddSameSteadyState(b *testing.B) {
423 m := new(Map).Init()
424 b.RunParallel(func(pb *testing.PB) {
425 for pb.Next() {
426 m.Add("red", 1)
427 }
428 })
429 }
430
431 func BenchmarkMapAddDifferentSteadyState(b *testing.B) {
432 procKeys := make([][]string, runtime.GOMAXPROCS(0))
433 for i := range procKeys {
434 keys := make([]string, 4)
435 for j := range keys {
436 keys[j] = fmt.Sprint(i, j)
437 }
438 procKeys[i] = keys
439 }
440
441 m := new(Map).Init()
442 b.ResetTimer()
443
444 var n int32
445 b.RunParallel(func(pb *testing.PB) {
446 i := int(atomic.AddInt32(&n, 1)-1) % len(procKeys)
447 keys := procKeys[i]
448
449 for pb.Next() {
450 for _, k := range keys {
451 m.Add(k, 1)
452 }
453 }
454 })
455 }
456
457 func TestFunc(t *testing.T) {
458 RemoveAll()
459 var x any = []string{"a", "b"}
460 f := Func(func() any { return x })
461 if s, exp := f.String(), `["a","b"]`; s != exp {
462 t.Errorf(`f.String() = %q, want %q`, s, exp)
463 }
464 if v := f.Value(); !reflect.DeepEqual(v, x) {
465 t.Errorf(`f.Value() = %q, want %q`, v, x)
466 }
467
468 x = 17
469 if s, exp := f.String(), `17`; s != exp {
470 t.Errorf(`f.String() = %q, want %q`, s, exp)
471 }
472 }
473
474 func TestHandler(t *testing.T) {
475 RemoveAll()
476 m := NewMap("map1")
477 m.Add("a", 1)
478 m.Add("z", 2)
479 m2 := NewMap("map2")
480 for i := 0; i < 9; i++ {
481 m2.Add(strconv.Itoa(i), int64(i))
482 }
483 rr := httptest.NewRecorder()
484 rr.Body = new(bytes.Buffer)
485 expvarHandler(rr, nil)
486 want := `{
487 "map1": {"a": 1, "z": 2},
488 "map2": {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8}
489 }
490 `
491 if got := rr.Body.String(); got != want {
492 t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want)
493 }
494 }
495
496 func BenchmarkMapString(b *testing.B) {
497 var m, m1, m2 Map
498 m.Set("map1", &m1)
499 m1.Add("a", 1)
500 m1.Add("z", 2)
501 m.Set("map2", &m2)
502 for i := 0; i < 9; i++ {
503 m2.Add(strconv.Itoa(i), int64(i))
504 }
505 var s1, s2 String
506 m.Set("str1", &s1)
507 s1.Set("hello, world!")
508 m.Set("str2", &s2)
509 s2.Set("fizz buzz")
510 b.ResetTimer()
511
512 b.ReportAllocs()
513 for i := 0; i < b.N; i++ {
514 _ = m.String()
515 }
516 }
517
518 func BenchmarkRealworldExpvarUsage(b *testing.B) {
519 var (
520 bytesSent Int
521 bytesRead Int
522 )
523
524
525
526
527
528
529 b.StopTimer()
530
531 P := runtime.GOMAXPROCS(0)
532 N := b.N / P
533 W := 1000
534
535
536 clients := make([]net.Conn, P)
537 servers := make([]net.Conn, P)
538 ln, err := net.Listen("tcp", "127.0.0.1:0")
539 if err != nil {
540 b.Fatalf("Listen failed: %v", err)
541 }
542 defer ln.Close()
543 done := make(chan bool, 1)
544 go func() {
545 for p := 0; p < P; p++ {
546 s, err := ln.Accept()
547 if err != nil {
548 b.Errorf("Accept failed: %v", err)
549 done <- false
550 return
551 }
552 servers[p] = s
553 }
554 done <- true
555 }()
556 for p := 0; p < P; p++ {
557 c, err := net.Dial("tcp", ln.Addr().String())
558 if err != nil {
559 <-done
560 b.Fatalf("Dial failed: %v", err)
561 }
562 clients[p] = c
563 }
564 if !<-done {
565 b.FailNow()
566 }
567
568 b.StartTimer()
569
570 var wg sync.WaitGroup
571 wg.Add(4 * P)
572 for p := 0; p < P; p++ {
573
574 go func(c net.Conn) {
575 defer wg.Done()
576 var buf [1]byte
577 for i := 0; i < N; i++ {
578 v := byte(i)
579 for w := 0; w < W; w++ {
580 v *= v
581 }
582 buf[0] = v
583 n, err := c.Write(buf[:])
584 if err != nil {
585 b.Errorf("Write failed: %v", err)
586 return
587 }
588
589 bytesSent.Add(int64(n))
590 }
591 }(clients[p])
592
593
594 pipe := make(chan byte, 128)
595
596
597 go func(s net.Conn) {
598 defer wg.Done()
599 var buf [1]byte
600 for i := 0; i < N; i++ {
601 n, err := s.Read(buf[:])
602
603 if err != nil {
604 b.Errorf("Read failed: %v", err)
605 return
606 }
607
608 bytesRead.Add(int64(n))
609 pipe <- buf[0]
610 }
611 }(servers[p])
612
613
614 go func(s net.Conn) {
615 defer wg.Done()
616 var buf [1]byte
617 for i := 0; i < N; i++ {
618 v := <-pipe
619 for w := 0; w < W; w++ {
620 v *= v
621 }
622 buf[0] = v
623 n, err := s.Write(buf[:])
624 if err != nil {
625 b.Errorf("Write failed: %v", err)
626 return
627 }
628
629 bytesSent.Add(int64(n))
630 }
631 s.Close()
632 }(servers[p])
633
634
635 go func(c net.Conn) {
636 defer wg.Done()
637 var buf [1]byte
638 for i := 0; i < N; i++ {
639 n, err := c.Read(buf[:])
640
641 if err != nil {
642 b.Errorf("Read failed: %v", err)
643 return
644 }
645
646 bytesRead.Add(int64(n))
647 }
648 c.Close()
649 }(clients[p])
650 }
651 wg.Wait()
652 }
653
654 func TestAppendJSONQuote(t *testing.T) {
655 var b []byte
656 for i := 0; i < 128; i++ {
657 b = append(b, byte(i))
658 }
659 b = append(b, "\u2028\u2029"...)
660 got := string(appendJSONQuote(nil, string(b[:])))
661 want := `"` +
662 `\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\t\n\u000b\u000c\r\u000e\u000f` +
663 `\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f` +
664 ` !\"#$%\u0026'()*+,-./0123456789:;\u003c=\u003e?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_` +
665 "`" + `abcdefghijklmnopqrstuvwxyz{|}~` + "\x7f" + `\u2028\u2029"`
666 if got != want {
667 t.Errorf("appendJSONQuote mismatch:\ngot %v\nwant %v", got, want)
668 }
669 }
670
View as plain text