blob: 7b1c9dfc4f872476b5ee7dfb76d909dc26172c53 [file] [log] [blame]
David Symonds3cc702b2009-04-19 21:17:27 -07001// 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
Rob Pike0632bb42009-09-30 13:11:33 -07005package expvar
David Symonds3cc702b2009-04-19 21:17:27 -07006
7import (
Brad Fitzpatrickcfb9cf02014-01-20 09:59:23 -08008 "bytes"
Rob Pike30aa7012011-11-08 15:40:58 -08009 "encoding/json"
Matt T. Proud926616d2015-02-02 00:33:44 -080010 "math"
Evan Phoenixfc22fb82015-01-30 15:31:47 -080011 "net"
Brad Fitzpatrickcfb9cf02014-01-20 09:59:23 -080012 "net/http/httptest"
Evan Phoenixfc22fb82015-01-30 15:31:47 -080013 "runtime"
Brad Fitzpatrickcfb9cf02014-01-20 09:59:23 -080014 "strconv"
Evan Phoenixfc22fb82015-01-30 15:31:47 -080015 "sync"
Matt T. Proud926616d2015-02-02 00:33:44 -080016 "sync/atomic"
Robert Griesemer1c729592009-12-15 15:27:16 -080017 "testing"
David Symonds3cc702b2009-04-19 21:17:27 -070018)
19
David Symonds715588f2012-02-04 14:32:05 +110020// RemoveAll removes all exported variables.
21// This is for tests only.
22func RemoveAll() {
23 mutex.Lock()
24 defer mutex.Unlock()
25 vars = make(map[string]Var)
Brad Fitzpatrickcfb9cf02014-01-20 09:59:23 -080026 varKeys = nil
David Symonds715588f2012-02-04 14:32:05 +110027}
28
Rick Arnold5abd3272016-02-24 19:25:38 -050029func TestNil(t *testing.T) {
30 RemoveAll()
31 val := Get("missing")
32 if val != nil {
33 t.Errorf("got %v, want nil", val)
34 }
35}
36
David Symonds2f284942009-05-04 15:14:22 -070037func TestInt(t *testing.T) {
Rémy Oudompheng75104232013-01-27 00:24:09 +010038 RemoveAll()
Robert Griesemer1c729592009-12-15 15:27:16 -080039 reqs := NewInt("requests")
David Symonds2f284942009-05-04 15:14:22 -070040 if reqs.i != 0 {
Russ Coxc6341892010-01-22 14:24:17 -080041 t.Errorf("reqs.i = %v, want 0", reqs.i)
David Symonds2f284942009-05-04 15:14:22 -070042 }
43 if reqs != Get("requests").(*Int) {
Robert Griesemer40621d52009-11-09 12:07:39 -080044 t.Errorf("Get() failed.")
David Symonds3cc702b2009-04-19 21:17:27 -070045 }
46
Robert Griesemer1c729592009-12-15 15:27:16 -080047 reqs.Add(1)
48 reqs.Add(3)
David Symonds2f284942009-05-04 15:14:22 -070049 if reqs.i != 4 {
Robert Griesemer40621d52009-11-09 12:07:39 -080050 t.Errorf("reqs.i = %v, want 4", reqs.i)
David Symonds3cc702b2009-04-19 21:17:27 -070051 }
52
David Symonds2f284942009-05-04 15:14:22 -070053 if s := reqs.String(); s != "4" {
Robert Griesemer40621d52009-11-09 12:07:39 -080054 t.Errorf("reqs.String() = %q, want \"4\"", s)
David Symonds3cc702b2009-04-19 21:17:27 -070055 }
Sam Thorogood3b9c9d22010-10-11 13:14:07 -040056
57 reqs.Set(-2)
58 if reqs.i != -2 {
59 t.Errorf("reqs.i = %v, want -2", reqs.i)
60 }
David Symonds3cc702b2009-04-19 21:17:27 -070061}
62
Evan Phoenixfc22fb82015-01-30 15:31:47 -080063func BenchmarkIntAdd(b *testing.B) {
64 var v Int
65
66 b.RunParallel(func(pb *testing.PB) {
67 for pb.Next() {
68 v.Add(1)
69 }
70 })
71}
72
73func BenchmarkIntSet(b *testing.B) {
74 var v Int
75
76 b.RunParallel(func(pb *testing.PB) {
77 for pb.Next() {
78 v.Set(1)
79 }
80 })
81}
82
Matt T. Proud926616d2015-02-02 00:33:44 -080083func (v *Float) val() float64 {
84 return math.Float64frombits(atomic.LoadUint64(&v.f))
85}
86
Jos Visser25f762c2011-01-18 11:24:43 -050087func TestFloat(t *testing.T) {
Rémy Oudompheng75104232013-01-27 00:24:09 +010088 RemoveAll()
Jos Visser25f762c2011-01-18 11:24:43 -050089 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)
Matt T. Proud926616d2015-02-02 00:33:44 -080099 if v := reqs.val(); v != 2.75 {
100 t.Errorf("reqs.val() = %v, want 2.75", v)
Jos Visser25f762c2011-01-18 11:24:43 -0500101 }
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)
Matt T. Proud926616d2015-02-02 00:33:44 -0800108 if v := reqs.val(); v != 0.75 {
109 t.Errorf("reqs.val() = %v, want 0.75", v)
Jos Visser25f762c2011-01-18 11:24:43 -0500110 }
111}
112
Evan Phoenixfc22fb82015-01-30 15:31:47 -0800113func 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
123func 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
David Symonds2f284942009-05-04 15:14:22 -0700133func TestString(t *testing.T) {
Rémy Oudompheng75104232013-01-27 00:24:09 +0100134 RemoveAll()
Robert Griesemer1c729592009-12-15 15:27:16 -0800135 name := NewString("my-name")
David Symonds2f284942009-05-04 15:14:22 -0700136 if name.s != "" {
Robert Griesemer40621d52009-11-09 12:07:39 -0800137 t.Errorf("name.s = %q, want \"\"", name.s)
David Symondsf4b92c82009-04-26 20:57:01 -0700138 }
139
Robert Griesemer1c729592009-12-15 15:27:16 -0800140 name.Set("Mike")
David Symonds2f284942009-05-04 15:14:22 -0700141 if name.s != "Mike" {
Robert Griesemer40621d52009-11-09 12:07:39 -0800142 t.Errorf("name.s = %q, want \"Mike\"", name.s)
David Symondsde489fb2009-04-20 22:38:14 -0700143 }
144
David Symonds6f2a8812016-04-06 11:53:26 +1000145 if s, want := name.String(), `"Mike"`; s != want {
146 t.Errorf("from %q, name.String() = %q, want %q", name.s, s, want)
147 }
148
149 // Make sure we produce safe JSON output.
150 name.Set(`<`)
151 if s, want := name.String(), "\"\\u003c\""; s != want {
152 t.Errorf("from %q, name.String() = %q, want %q", name.s, s, want)
David Symondsde489fb2009-04-20 22:38:14 -0700153 }
154}
155
Evan Phoenixfc22fb82015-01-30 15:31:47 -0800156func BenchmarkStringSet(b *testing.B) {
157 var s String
158
159 b.RunParallel(func(pb *testing.PB) {
160 for pb.Next() {
161 s.Set("red")
162 }
163 })
164}
165
David Symondsde489fb2009-04-20 22:38:14 -0700166func TestMapCounter(t *testing.T) {
Rémy Oudompheng75104232013-01-27 00:24:09 +0100167 RemoveAll()
Robert Henckec8727c82011-05-18 13:14:56 -0400168 colors := NewMap("bike-shed-colors")
David Symonds2f284942009-05-04 15:14:22 -0700169
Robert Henckec8727c82011-05-18 13:14:56 -0400170 colors.Add("red", 1)
171 colors.Add("red", 2)
172 colors.Add("blue", 4)
Rui Ueyama74c6b842014-04-10 21:14:04 -0700173 colors.AddFloat(`green "midori"`, 4.125)
Robert Henckec8727c82011-05-18 13:14:56 -0400174 if x := colors.m["red"].(*Int).i; x != 3 {
175 t.Errorf("colors.m[\"red\"] = %v, want 3", x)
David Symonds2f284942009-05-04 15:14:22 -0700176 }
Robert Henckec8727c82011-05-18 13:14:56 -0400177 if x := colors.m["blue"].(*Int).i; x != 4 {
178 t.Errorf("colors.m[\"blue\"] = %v, want 4", x)
David Symondsde489fb2009-04-20 22:38:14 -0700179 }
Matt T. Proud926616d2015-02-02 00:33:44 -0800180 if x := colors.m[`green "midori"`].(*Float).val(); x != 4.125 {
181 t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x)
Jos Visser25f762c2011-01-18 11:24:43 -0500182 }
David Symondsde489fb2009-04-20 22:38:14 -0700183
Robert Henckec8727c82011-05-18 13:14:56 -0400184 // colors.String() should be '{"red":3, "blue":4}',
David Symonds2f284942009-05-04 15:14:22 -0700185 // though the order of red and blue could vary.
Robert Henckec8727c82011-05-18 13:14:56 -0400186 s := colors.String()
Russ Coxdba9d622010-04-21 16:40:53 -0700187 var j interface{}
188 err := json.Unmarshal([]byte(s), &j)
Sergey 'SnakE' Gromov9d50b462009-11-30 13:55:09 -0800189 if err != nil {
Robert Henckec8727c82011-05-18 13:14:56 -0400190 t.Errorf("colors.String() isn't valid JSON: %v", err)
David Symondsde489fb2009-04-20 22:38:14 -0700191 }
Robert Griesemer1c729592009-12-15 15:27:16 -0800192 m, ok := j.(map[string]interface{})
Sergey 'SnakE' Gromov9d50b462009-11-30 13:55:09 -0800193 if !ok {
Robert Henckec8727c82011-05-18 13:14:56 -0400194 t.Error("colors.String() didn't produce a map.")
David Symondsde489fb2009-04-20 22:38:14 -0700195 }
Robert Griesemer1c729592009-12-15 15:27:16 -0800196 red := m["red"]
197 x, ok := red.(float64)
Sergey 'SnakE' Gromov9d50b462009-11-30 13:55:09 -0800198 if !ok {
199 t.Error("red.Kind() is not a number.")
David Symonds3cc702b2009-04-19 21:17:27 -0700200 }
Sergey 'SnakE' Gromov9d50b462009-11-30 13:55:09 -0800201 if x != 3 {
Rob Pike1959c3a2010-09-23 13:48:56 +1000202 t.Errorf("red = %v, want 3", x)
David Symonds3cc702b2009-04-19 21:17:27 -0700203 }
204}
David Symonds5a12b182009-05-24 15:04:43 -0700205
Evan Phoenixfc22fb82015-01-30 15:31:47 -0800206func BenchmarkMapSet(b *testing.B) {
207 m := new(Map).Init()
208
209 v := new(Int)
210
211 b.RunParallel(func(pb *testing.PB) {
212 for pb.Next() {
213 m.Set("red", v)
214 }
215 })
216}
217
218func BenchmarkMapAddSame(b *testing.B) {
219 for i := 0; i < b.N; i++ {
220 m := new(Map).Init()
221 m.Add("red", 1)
222 m.Add("red", 1)
223 m.Add("red", 1)
224 m.Add("red", 1)
225 }
226}
227
228func BenchmarkMapAddDifferent(b *testing.B) {
229 for i := 0; i < b.N; i++ {
230 m := new(Map).Init()
231 m.Add("red", 1)
232 m.Add("blue", 1)
233 m.Add("green", 1)
234 m.Add("yellow", 1)
235 }
236}
237
David Symonds57d0c262011-04-15 01:21:18 -0700238func TestFunc(t *testing.T) {
Rémy Oudompheng75104232013-01-27 00:24:09 +0100239 RemoveAll()
David Symonds57d0c262011-04-15 01:21:18 -0700240 var x interface{} = []string{"a", "b"}
241 f := Func(func() interface{} { return x })
242 if s, exp := f.String(), `["a","b"]`; s != exp {
243 t.Errorf(`f.String() = %q, want %q`, s, exp)
David Symonds5a12b182009-05-24 15:04:43 -0700244 }
245
David Symonds57d0c262011-04-15 01:21:18 -0700246 x = 17
247 if s, exp := f.String(), `17`; s != exp {
248 t.Errorf(`f.String() = %q, want %q`, s, exp)
David Symonds8bfa9172010-12-20 17:35:21 +1100249 }
250}
Brad Fitzpatrickcfb9cf02014-01-20 09:59:23 -0800251
252func TestHandler(t *testing.T) {
253 RemoveAll()
254 m := NewMap("map1")
255 m.Add("a", 1)
256 m.Add("z", 2)
257 m2 := NewMap("map2")
258 for i := 0; i < 9; i++ {
259 m2.Add(strconv.Itoa(i), int64(i))
260 }
261 rr := httptest.NewRecorder()
262 rr.Body = new(bytes.Buffer)
263 expvarHandler(rr, nil)
264 want := `{
265"map1": {"a": 1, "z": 2},
266"map2": {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8}
267}
268`
269 if got := rr.Body.String(); got != want {
270 t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want)
271 }
272}
Evan Phoenixfc22fb82015-01-30 15:31:47 -0800273
274func BenchmarkRealworldExpvarUsage(b *testing.B) {
275 var (
276 bytesSent Int
277 bytesRead Int
278 )
279
280 // The benchmark creates GOMAXPROCS client/server pairs.
281 // Each pair creates 4 goroutines: client reader/writer and server reader/writer.
282 // The benchmark stresses concurrent reading and writing to the same connection.
283 // Such pattern is used in net/http and net/rpc.
284
285 b.StopTimer()
286
287 P := runtime.GOMAXPROCS(0)
288 N := b.N / P
289 W := 1000
290
291 // Setup P client/server connections.
292 clients := make([]net.Conn, P)
293 servers := make([]net.Conn, P)
Dmitry Vyukov08ca4012015-02-05 16:50:11 +0300294 ln, err := net.Listen("tcp", "127.0.0.1:0")
Evan Phoenixfc22fb82015-01-30 15:31:47 -0800295 if err != nil {
296 b.Fatalf("Listen failed: %v", err)
297 }
298 defer ln.Close()
299 done := make(chan bool)
300 go func() {
301 for p := 0; p < P; p++ {
302 s, err := ln.Accept()
303 if err != nil {
304 b.Errorf("Accept failed: %v", err)
305 return
306 }
307 servers[p] = s
308 }
309 done <- true
310 }()
311 for p := 0; p < P; p++ {
312 c, err := net.Dial("tcp", ln.Addr().String())
313 if err != nil {
314 b.Fatalf("Dial failed: %v", err)
315 }
316 clients[p] = c
317 }
318 <-done
319
320 b.StartTimer()
321
322 var wg sync.WaitGroup
323 wg.Add(4 * P)
324 for p := 0; p < P; p++ {
325 // Client writer.
326 go func(c net.Conn) {
327 defer wg.Done()
328 var buf [1]byte
329 for i := 0; i < N; i++ {
330 v := byte(i)
331 for w := 0; w < W; w++ {
332 v *= v
333 }
334 buf[0] = v
335 n, err := c.Write(buf[:])
336 if err != nil {
337 b.Errorf("Write failed: %v", err)
338 return
339 }
340
341 bytesSent.Add(int64(n))
342 }
343 }(clients[p])
344
345 // Pipe between server reader and server writer.
346 pipe := make(chan byte, 128)
347
348 // Server reader.
349 go func(s net.Conn) {
350 defer wg.Done()
351 var buf [1]byte
352 for i := 0; i < N; i++ {
353 n, err := s.Read(buf[:])
354
355 if err != nil {
356 b.Errorf("Read failed: %v", err)
357 return
358 }
359
360 bytesRead.Add(int64(n))
361 pipe <- buf[0]
362 }
363 }(servers[p])
364
365 // Server writer.
366 go func(s net.Conn) {
367 defer wg.Done()
368 var buf [1]byte
369 for i := 0; i < N; i++ {
370 v := <-pipe
371 for w := 0; w < W; w++ {
372 v *= v
373 }
374 buf[0] = v
375 n, err := s.Write(buf[:])
376 if err != nil {
377 b.Errorf("Write failed: %v", err)
378 return
379 }
380
381 bytesSent.Add(int64(n))
382 }
383 s.Close()
384 }(servers[p])
385
386 // Client reader.
387 go func(c net.Conn) {
388 defer wg.Done()
389 var buf [1]byte
390 for i := 0; i < N; i++ {
391 n, err := c.Read(buf[:])
392
393 if err != nil {
394 b.Errorf("Read failed: %v", err)
395 return
396 }
397
398 bytesRead.Add(int64(n))
399 }
400 c.Close()
401 }(clients[p])
402 }
403 wg.Wait()
404}