...

Source file src/expvar/expvar_test.go

Documentation: expvar

		 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  	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  	// 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 interface{}
	 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]interface{})
	 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  // BenchmarkMapSetDifferentRandom simulates such a case where the concerned
	 304  // keys of Map.Set are generated dynamically and as a result insertion is
	 305  // out of order and the number of the keys may be large.
	 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  // BenchmarkMapAddDifferentRandom simulates such a case where that the concerned
	 375  // keys of Map.Add are generated dynamically and as a result insertion is out of
	 376  // order and the number of the keys may be large.
	 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 interface{} = []string{"a", "b"}
	 431  	f := Func(func() interface{} { 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  	// The benchmark creates GOMAXPROCS client/server pairs.
	 474  	// Each pair creates 4 goroutines: client reader/writer and server reader/writer.
	 475  	// The benchmark stresses concurrent reading and writing to the same connection.
	 476  	// Such pattern is used in net/http and net/rpc.
	 477  
	 478  	b.StopTimer()
	 479  
	 480  	P := runtime.GOMAXPROCS(0)
	 481  	N := b.N / P
	 482  	W := 1000
	 483  
	 484  	// Setup P client/server connections.
	 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  		// Client writer.
	 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  		// Pipe between server reader and server writer.
	 543  		pipe := make(chan byte, 128)
	 544  
	 545  		// Server reader.
	 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  		// Server writer.
	 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  		// Client reader.
	 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