]> Cypherpunks.ru repositories - gostls13.git/blob - src/expvar/expvar_test.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / expvar / expvar_test.go
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         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         // 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 BenchmarkMapString(b *testing.B) {
491         var m, m1, m2 Map
492         m.Set("map1", &m1)
493         m1.Add("a", 1)
494         m1.Add("z", 2)
495         m.Set("map2", &m2)
496         for i := 0; i < 9; i++ {
497                 m2.Add(strconv.Itoa(i), int64(i))
498         }
499         var s1, s2 String
500         m.Set("str1", &s1)
501         s1.Set("hello, world!")
502         m.Set("str2", &s2)
503         s2.Set("fizz buzz")
504         b.ResetTimer()
505
506         b.ReportAllocs()
507         for i := 0; i < b.N; i++ {
508                 _ = m.String()
509         }
510 }
511
512 func BenchmarkRealworldExpvarUsage(b *testing.B) {
513         var (
514                 bytesSent Int
515                 bytesRead Int
516         )
517
518         // The benchmark creates GOMAXPROCS client/server pairs.
519         // Each pair creates 4 goroutines: client reader/writer and server reader/writer.
520         // The benchmark stresses concurrent reading and writing to the same connection.
521         // Such pattern is used in net/http and net/rpc.
522
523         b.StopTimer()
524
525         P := runtime.GOMAXPROCS(0)
526         N := b.N / P
527         W := 1000
528
529         // Setup P client/server connections.
530         clients := make([]net.Conn, P)
531         servers := make([]net.Conn, P)
532         ln, err := net.Listen("tcp", "127.0.0.1:0")
533         if err != nil {
534                 b.Fatalf("Listen failed: %v", err)
535         }
536         defer ln.Close()
537         done := make(chan bool, 1)
538         go func() {
539                 for p := 0; p < P; p++ {
540                         s, err := ln.Accept()
541                         if err != nil {
542                                 b.Errorf("Accept failed: %v", err)
543                                 done <- false
544                                 return
545                         }
546                         servers[p] = s
547                 }
548                 done <- true
549         }()
550         for p := 0; p < P; p++ {
551                 c, err := net.Dial("tcp", ln.Addr().String())
552                 if err != nil {
553                         <-done
554                         b.Fatalf("Dial failed: %v", err)
555                 }
556                 clients[p] = c
557         }
558         if !<-done {
559                 b.FailNow()
560         }
561
562         b.StartTimer()
563
564         var wg sync.WaitGroup
565         wg.Add(4 * P)
566         for p := 0; p < P; p++ {
567                 // Client writer.
568                 go func(c net.Conn) {
569                         defer wg.Done()
570                         var buf [1]byte
571                         for i := 0; i < N; i++ {
572                                 v := byte(i)
573                                 for w := 0; w < W; w++ {
574                                         v *= v
575                                 }
576                                 buf[0] = v
577                                 n, err := c.Write(buf[:])
578                                 if err != nil {
579                                         b.Errorf("Write failed: %v", err)
580                                         return
581                                 }
582
583                                 bytesSent.Add(int64(n))
584                         }
585                 }(clients[p])
586
587                 // Pipe between server reader and server writer.
588                 pipe := make(chan byte, 128)
589
590                 // Server reader.
591                 go func(s net.Conn) {
592                         defer wg.Done()
593                         var buf [1]byte
594                         for i := 0; i < N; i++ {
595                                 n, err := s.Read(buf[:])
596
597                                 if err != nil {
598                                         b.Errorf("Read failed: %v", err)
599                                         return
600                                 }
601
602                                 bytesRead.Add(int64(n))
603                                 pipe <- buf[0]
604                         }
605                 }(servers[p])
606
607                 // Server writer.
608                 go func(s net.Conn) {
609                         defer wg.Done()
610                         var buf [1]byte
611                         for i := 0; i < N; i++ {
612                                 v := <-pipe
613                                 for w := 0; w < W; w++ {
614                                         v *= v
615                                 }
616                                 buf[0] = v
617                                 n, err := s.Write(buf[:])
618                                 if err != nil {
619                                         b.Errorf("Write failed: %v", err)
620                                         return
621                                 }
622
623                                 bytesSent.Add(int64(n))
624                         }
625                         s.Close()
626                 }(servers[p])
627
628                 // Client reader.
629                 go func(c net.Conn) {
630                         defer wg.Done()
631                         var buf [1]byte
632                         for i := 0; i < N; i++ {
633                                 n, err := c.Read(buf[:])
634
635                                 if err != nil {
636                                         b.Errorf("Read failed: %v", err)
637                                         return
638                                 }
639
640                                 bytesRead.Add(int64(n))
641                         }
642                         c.Close()
643                 }(clients[p])
644         }
645         wg.Wait()
646 }
647
648 func TestAppendJSONQuote(t *testing.T) {
649         var b []byte
650         for i := 0; i < 128; i++ {
651                 b = append(b, byte(i))
652         }
653         b = append(b, "\u2028\u2029"...)
654         got := string(appendJSONQuote(nil, string(b[:])))
655         want := `"` +
656                 `\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\t\n\u000b\u000c\r\u000e\u000f` +
657                 `\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f` +
658                 ` !\"#$%\u0026'()*+,-./0123456789:;\u003c=\u003e?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_` +
659                 "`" + `abcdefghijklmnopqrstuvwxyz{|}~` + "\x7f" + `\u2028\u2029"`
660         if got != want {
661                 t.Errorf("appendJSONQuote mismatch:\ngot  %v\nwant %v", got, want)
662         }
663 }