Browse Source

Special case fetching for driver tests (#129)

Use a more regular mechanism to name sources during driver tests and
recognize the naming during fetching to avoid saving the test profiles
into $HOME/pprof.

Also move the https+insecure testing into fetch_test, to focus it on
fetching the profile.

This fixes #107
Raul Silvera 8 years ago
parent
commit
402739d38b
3 changed files with 141 additions and 134 deletions
  1. 47
    134
      internal/driver/driver_test.go
  2. 7
    0
      internal/driver/fetch.go
  3. 87
    0
      internal/driver/fetch_test.go

+ 47
- 134
internal/driver/driver_test.go View File

@@ -16,17 +16,10 @@ package driver
16 16
 
17 17
 import (
18 18
 	"bytes"
19
-	"crypto/ecdsa"
20
-	"crypto/elliptic"
21
-	"crypto/rand"
22
-	"crypto/tls"
23
-	"crypto/x509"
24
-	"encoding/pem"
25 19
 	"flag"
26 20
 	"fmt"
27 21
 	"io/ioutil"
28
-	"math/big"
29
-	"net/http"
22
+	"net"
30 23
 	_ "net/http/pprof"
31 24
 	"os"
32 25
 	"regexp"
@@ -55,7 +48,6 @@ func TestParse(t *testing.T) {
55 48
 	savePath := os.Getenv("PPROF_BINARY_PATH")
56 49
 	os.Setenv("PPROF_BINARY_PATH", "/path/to")
57 50
 	defer os.Setenv("PPROF_BINARY_PATH", savePath)
58
-
59 51
 	testcase := []struct {
60 52
 		flags, source string
61 53
 	}{
@@ -236,9 +228,13 @@ func addFlags(f *testFlags, flags []string) {
236 228
 	}
237 229
 }
238 230
 
231
+func testSourceURL(port int) string {
232
+	return fmt.Sprintf("http://%s/", net.JoinHostPort(testSourceAddress, strconv.Itoa(port)))
233
+}
234
+
239 235
 // solutionFilename returns the name of the solution file for the test
240 236
 func solutionFilename(source string, f *testFlags) string {
241
-	name := []string{"pprof", strings.TrimPrefix(source, "http://host:8000/")}
237
+	name := []string{"pprof", strings.TrimPrefix(source, testSourceURL(8000))}
242 238
 	name = addString(name, f, []string{"flat", "cum"})
243 239
 	name = addString(name, f, []string{"functions", "files", "lines", "addresses"})
244 240
 	name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"})
@@ -347,15 +343,6 @@ func (f testFlags) Parse(func()) []string {
347 343
 	return f.args
348 344
 }
349 345
 
350
-func emptyFlags() testFlags {
351
-	return testFlags{
352
-		bools:   map[string]bool{},
353
-		ints:    map[string]int{},
354
-		floats:  map[string]float64{},
355
-		strings: map[string]string{},
356
-	}
357
-}
358
-
359 346
 func baseFlags() testFlags {
360 347
 	return testFlags{
361 348
 		bools: map[string]bool{
@@ -384,7 +371,6 @@ type testFetcher struct{}
384 371
 
385 372
 func (testFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string, error) {
386 373
 	var p *profile.Profile
387
-	s = strings.TrimPrefix(s, "http://host:8000/")
388 374
 	switch s {
389 375
 	case "cpu", "unknown":
390 376
 		p = cpuProfile()
@@ -402,17 +388,10 @@ func (testFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string
402 388
 		p = contentionProfile()
403 389
 	case "symbolz":
404 390
 		p = symzProfile()
405
-	case "http://host2/symbolz":
406
-		p = symzProfile()
407
-		p.Mapping[0].Start += testOffset
408
-		p.Mapping[0].Limit += testOffset
409
-		for i := range p.Location {
410
-			p.Location[i].Address += testOffset
411
-		}
412 391
 	default:
413 392
 		return nil, "", fmt.Errorf("unexpected source: %s", s)
414 393
 	}
415
-	return p, s, nil
394
+	return p, testSourceURL(8000) + s, nil
416 395
 }
417 396
 
418 397
 type testSymbolizer struct{}
@@ -435,7 +414,19 @@ func (testSymbolizeDemangler) Symbolize(_ string, _ plugin.MappingSources, p *pr
435 414
 func testFetchSymbols(source, post string) ([]byte, error) {
436 415
 	var buf bytes.Buffer
437 416
 
438
-	if source == "http://host2/symbolz" {
417
+	switch source {
418
+	case testSourceURL(8000) + "symbolz":
419
+		for _, address := range strings.Split(post, "+") {
420
+			a, _ := strconv.ParseInt(address, 0, 64)
421
+			fmt.Fprintf(&buf, "%v\t", address)
422
+			if a-testStart > testOffset {
423
+				fmt.Fprintf(&buf, "wrong_source_%v_", address)
424
+				continue
425
+			}
426
+			fmt.Fprintf(&buf, "%#x\n", a-testStart)
427
+		}
428
+		return buf.Bytes(), nil
429
+	case testSourceURL(8001) + "symbolz":
439 430
 		for _, address := range strings.Split(post, "+") {
440 431
 			a, _ := strconv.ParseInt(address, 0, 64)
441 432
 			fmt.Fprintf(&buf, "%v\t", address)
@@ -446,17 +437,9 @@ func testFetchSymbols(source, post string) ([]byte, error) {
446 437
 			fmt.Fprintf(&buf, "%#x\n", a-testStart-testOffset)
447 438
 		}
448 439
 		return buf.Bytes(), nil
440
+	default:
441
+		return nil, fmt.Errorf("unexpected source: %s", source)
449 442
 	}
450
-	for _, address := range strings.Split(post, "+") {
451
-		a, _ := strconv.ParseInt(address, 0, 64)
452
-		fmt.Fprintf(&buf, "%v\t", address)
453
-		if a-testStart > testOffset {
454
-			fmt.Fprintf(&buf, "wrong_source_%v_", address)
455
-			continue
456
-		}
457
-		fmt.Fprintf(&buf, "%#x\n", a-testStart)
458
-	}
459
-	return buf.Bytes(), nil
460 443
 }
461 444
 
462 445
 type testSymbolzSymbolizer struct{}
@@ -1006,13 +989,36 @@ func TestTagFilter(t *testing.T) {
1006 989
 	}
1007 990
 }
1008 991
 
992
+type testSymbolzMergeFetcher struct{}
993
+
994
+func (testSymbolzMergeFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string, error) {
995
+	var p *profile.Profile
996
+	switch s {
997
+	case testSourceURL(8000) + "symbolz":
998
+		p = symzProfile()
999
+	case testSourceURL(8001) + "symbolz":
1000
+		p = symzProfile()
1001
+		p.Mapping[0].Start += testOffset
1002
+		p.Mapping[0].Limit += testOffset
1003
+		for i := range p.Location {
1004
+			p.Location[i].Address += testOffset
1005
+		}
1006
+	default:
1007
+		return nil, "", fmt.Errorf("unexpected source: %s", s)
1008
+	}
1009
+	return p, s, nil
1010
+}
1011
+
1009 1012
 func TestSymbolzAfterMerge(t *testing.T) {
1010 1013
 	baseVars := pprofVariables
1011 1014
 	pprofVariables = baseVars.makeCopy()
1012 1015
 	defer func() { pprofVariables = baseVars }()
1013 1016
 
1014 1017
 	f := baseFlags()
1015
-	f.args = []string{"symbolz", "http://host2/symbolz"}
1018
+	f.args = []string{
1019
+		testSourceURL(8000) + "symbolz",
1020
+		testSourceURL(8001) + "symbolz",
1021
+	}
1016 1022
 
1017 1023
 	o := setDefaults(nil)
1018 1024
 	o.Flagset = f
@@ -1026,7 +1032,7 @@ func TestSymbolzAfterMerge(t *testing.T) {
1026 1032
 		t.Fatalf("parseFlags returned command %v, want [proto]", cmd)
1027 1033
 	}
1028 1034
 
1029
-	o.Fetch = testFetcher{}
1035
+	o.Fetch = testSymbolzMergeFetcher{}
1030 1036
 	o.Sym = testSymbolzSymbolizer{}
1031 1037
 	p, err := fetchProfiles(src, o)
1032 1038
 	if err != nil {
@@ -1047,99 +1053,6 @@ func TestSymbolzAfterMerge(t *testing.T) {
1047 1053
 	}
1048 1054
 }
1049 1055
 
1050
-func TestHttpsInsecure(t *testing.T) {
1051
-	baseVars := pprofVariables
1052
-	pprofVariables = baseVars.makeCopy()
1053
-	defer func() { pprofVariables = baseVars }()
1054
-
1055
-	tlsConfig := &tls.Config{Certificates: []tls.Certificate{selfSignedCert(t)}}
1056
-
1057
-	l, err := tls.Listen("tcp", "localhost:0", tlsConfig)
1058
-	if err != nil {
1059
-		t.Fatalf("net.Listen: got error %v, want no error", err)
1060
-	}
1061
-
1062
-	donec := make(chan error, 1)
1063
-	go func(donec chan<- error) {
1064
-		donec <- http.Serve(l, nil)
1065
-	}(donec)
1066
-	defer func() {
1067
-		if got, want := <-donec, "use of closed"; !strings.Contains(got.Error(), want) {
1068
-			t.Fatalf("Serve got error %v, want %q", got, want)
1069
-		}
1070
-	}()
1071
-	defer l.Close()
1072
-
1073
-	go func() {
1074
-		deadline := time.Now().Add(5 * time.Second)
1075
-		for time.Now().Before(deadline) {
1076
-			// Simulate a hotspot function.
1077
-		}
1078
-	}()
1079
-
1080
-	outputTempFile, err := ioutil.TempFile("", "profile_output")
1081
-	if err != nil {
1082
-		t.Fatalf("Failed to create tempfile: %v", err)
1083
-	}
1084
-	defer os.Remove(outputTempFile.Name())
1085
-	defer outputTempFile.Close()
1086
-
1087
-	f := emptyFlags()
1088
-	o := setDefaults(nil)
1089
-	o.Flagset = &f
1090
-
1091
-	f.args = []string{"https+insecure://" + l.Addr().String() + "/debug/pprof/profile?seconds=5"}
1092
-	addFlags(&f, []string{
1093
-		"top",
1094
-		"symbolize=remote",
1095
-		"output=" + outputTempFile.Name(),
1096
-	})
1097
-
1098
-	if err := PProf(o); err != nil {
1099
-		t.Fatalf("PProf(%v): got error %v, want no error", o, err)
1100
-	}
1101
-
1102
-	b, err := ioutil.ReadFile(outputTempFile.Name())
1103
-	if err != nil {
1104
-		t.Fatalf("ReadFile(%s) got error %v, want no error", outputTempFile.Name(), err)
1105
-	}
1106
-	// TODO(aalexand): Fix to reqiure "TestHttpsInsecure" in the output once #120
1107
-	// is fixed which causes symbolization issues on OSX with Go 1.8.
1108
-	if got, want := string(b), "Showing nodes accounting"; !strings.Contains(got, want) {
1109
-		t.Fatalf("Pprof(%v): got %v, want %q substring", o, got, want)
1110
-	}
1111
-}
1112
-
1113
-func selfSignedCert(t *testing.T) tls.Certificate {
1114
-	privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
1115
-	if err != nil {
1116
-		t.Fatalf("failed to generate private key: %v", err)
1117
-	}
1118
-	b, err := x509.MarshalECPrivateKey(privKey)
1119
-	if err != nil {
1120
-		t.Fatalf("failed to marshal private key: %v", err)
1121
-	}
1122
-	bk := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
1123
-
1124
-	tmpl := x509.Certificate{
1125
-		SerialNumber: big.NewInt(1),
1126
-		NotBefore:    time.Now(),
1127
-		NotAfter:     time.Now().Add(10 * time.Minute),
1128
-	}
1129
-
1130
-	b, err = x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, privKey.Public(), privKey)
1131
-	if err != nil {
1132
-		t.Fatalf("failed to create cert: %v", err)
1133
-	}
1134
-	bc := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: b})
1135
-
1136
-	cert, err := tls.X509KeyPair(bc, bk)
1137
-	if err != nil {
1138
-		t.Fatalf("failed to create TLS key pair: %v", err)
1139
-	}
1140
-	return cert
1141
-}
1142
-
1143 1056
 type mockObjTool struct{}
1144 1057
 
1145 1058
 func (*mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {

+ 7
- 0
internal/driver/fetch.go View File

@@ -242,6 +242,8 @@ func setTmpDir(ui plugin.UI) (string, error) {
242 242
 	return "", fmt.Errorf("failed to identify temp dir")
243 243
 }
244 244
 
245
+const testSourceAddress = "pproftest.local"
246
+
245 247
 // grabProfile fetches a profile. Returns the profile, sources for the
246 248
 // profile mappings, a bool indicating if the profile was fetched
247 249
 // remotely, and an error.
@@ -276,6 +278,11 @@ func grabProfile(s *source, source string, scale float64, fetcher plugin.Fetcher
276 278
 	if src != "" {
277 279
 		msrc = collectMappingSources(p, src)
278 280
 		remote = true
281
+		if strings.HasPrefix(src, "http://"+testSourceAddress) {
282
+			// Treat test inputs as local to avoid saving
283
+			// testcase profiles during driver testing.
284
+			remote = false
285
+		}
279 286
 	}
280 287
 	return
281 288
 }

+ 87
- 0
internal/driver/fetch_test.go View File

@@ -15,8 +15,15 @@
15 15
 package driver
16 16
 
17 17
 import (
18
+	"crypto/ecdsa"
19
+	"crypto/elliptic"
20
+	"crypto/rand"
21
+	"crypto/tls"
22
+	"crypto/x509"
23
+	"encoding/pem"
18 24
 	"fmt"
19 25
 	"io/ioutil"
26
+	"math/big"
20 27
 	"net/http"
21 28
 	"net/url"
22 29
 	"os"
@@ -24,6 +31,7 @@ import (
24 31
 	"reflect"
25 32
 	"regexp"
26 33
 	"runtime"
34
+	"strings"
27 35
 	"testing"
28 36
 	"time"
29 37
 
@@ -165,6 +173,8 @@ func TestFetch(t *testing.T) {
165 173
 	const path = "testdata/"
166 174
 
167 175
 	// Intercept http.Get calls from HTTPFetcher.
176
+	savedHTTPGet := httpGet
177
+	defer func() { httpGet = savedHTTPGet }()
168 178
 	httpGet = stubHTTPGet
169 179
 
170 180
 	type testcase struct {
@@ -227,3 +237,80 @@ func stubHTTPGet(source string, _ time.Duration) (*http.Response, error) {
227 237
 	c := &http.Client{Transport: t}
228 238
 	return c.Get("file:///" + file)
229 239
 }
240
+
241
+func TestHttpsInsecure(t *testing.T) {
242
+	baseVars := pprofVariables
243
+	pprofVariables = baseVars.makeCopy()
244
+	defer func() { pprofVariables = baseVars }()
245
+
246
+	tlsConfig := &tls.Config{Certificates: []tls.Certificate{selfSignedCert(t)}}
247
+
248
+	l, err := tls.Listen("tcp", "localhost:0", tlsConfig)
249
+	if err != nil {
250
+		t.Fatalf("net.Listen: got error %v, want no error", err)
251
+	}
252
+
253
+	donec := make(chan error, 1)
254
+	go func(donec chan<- error) {
255
+		donec <- http.Serve(l, nil)
256
+	}(donec)
257
+	defer func() {
258
+		if got, want := <-donec, "use of closed"; !strings.Contains(got.Error(), want) {
259
+			t.Fatalf("Serve got error %v, want %q", got, want)
260
+		}
261
+	}()
262
+	defer l.Close()
263
+
264
+	go func() {
265
+		deadline := time.Now().Add(5 * time.Second)
266
+		for time.Now().Before(deadline) {
267
+			// Simulate a hotspot function.
268
+		}
269
+	}()
270
+
271
+	outputTempFile, err := ioutil.TempFile("", "profile_output")
272
+	if err != nil {
273
+		t.Fatalf("Failed to create tempfile: %v", err)
274
+	}
275
+	defer os.Remove(outputTempFile.Name())
276
+	defer outputTempFile.Close()
277
+
278
+	address := "https+insecure://" + l.Addr().String() + "/debug/pprof/profile?seconds=5"
279
+	p, _, _, err := grabProfile(&source{}, address, 0, nil, testObj{}, &proftest.TestUI{T: t})
280
+	if err != nil {
281
+		t.Fatal(err)
282
+	}
283
+	if len(p.SampleType) == 0 {
284
+		t.Fatalf("grabProfile(%s) got empty profile: len(p.SampleType)==0", address)
285
+	}
286
+}
287
+
288
+func selfSignedCert(t *testing.T) tls.Certificate {
289
+	privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
290
+	if err != nil {
291
+		t.Fatalf("failed to generate private key: %v", err)
292
+	}
293
+	b, err := x509.MarshalECPrivateKey(privKey)
294
+	if err != nil {
295
+		t.Fatalf("failed to marshal private key: %v", err)
296
+	}
297
+	bk := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
298
+
299
+	tmpl := x509.Certificate{
300
+		SerialNumber: big.NewInt(1),
301
+		NotBefore:    time.Now(),
302
+		NotAfter:     time.Now().Add(10 * time.Minute),
303
+	}
304
+
305
+	b, err = x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, privKey.Public(), privKey)
306
+	if err != nil {
307
+		t.Fatalf("failed to create cert: %v", err)
308
+	}
309
+	bc := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: b})
310
+
311
+	cert, err := tls.X509KeyPair(bc, bk)
312
+	if err != nil {
313
+		t.Fatalf("failed to create TLS key pair: %v", err)
314
+	}
315
+	return cert
316
+}