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
 
16
 
17
 import (
17
 import (
18
 	"bytes"
18
 	"bytes"
19
-	"crypto/ecdsa"
20
-	"crypto/elliptic"
21
-	"crypto/rand"
22
-	"crypto/tls"
23
-	"crypto/x509"
24
-	"encoding/pem"
25
 	"flag"
19
 	"flag"
26
 	"fmt"
20
 	"fmt"
27
 	"io/ioutil"
21
 	"io/ioutil"
28
-	"math/big"
29
-	"net/http"
22
+	"net"
30
 	_ "net/http/pprof"
23
 	_ "net/http/pprof"
31
 	"os"
24
 	"os"
32
 	"regexp"
25
 	"regexp"
55
 	savePath := os.Getenv("PPROF_BINARY_PATH")
48
 	savePath := os.Getenv("PPROF_BINARY_PATH")
56
 	os.Setenv("PPROF_BINARY_PATH", "/path/to")
49
 	os.Setenv("PPROF_BINARY_PATH", "/path/to")
57
 	defer os.Setenv("PPROF_BINARY_PATH", savePath)
50
 	defer os.Setenv("PPROF_BINARY_PATH", savePath)
58
-
59
 	testcase := []struct {
51
 	testcase := []struct {
60
 		flags, source string
52
 		flags, source string
61
 	}{
53
 	}{
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
 // solutionFilename returns the name of the solution file for the test
235
 // solutionFilename returns the name of the solution file for the test
240
 func solutionFilename(source string, f *testFlags) string {
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
 	name = addString(name, f, []string{"flat", "cum"})
238
 	name = addString(name, f, []string{"flat", "cum"})
243
 	name = addString(name, f, []string{"functions", "files", "lines", "addresses"})
239
 	name = addString(name, f, []string{"functions", "files", "lines", "addresses"})
244
 	name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"})
240
 	name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"})
347
 	return f.args
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
 func baseFlags() testFlags {
346
 func baseFlags() testFlags {
360
 	return testFlags{
347
 	return testFlags{
361
 		bools: map[string]bool{
348
 		bools: map[string]bool{
384
 
371
 
385
 func (testFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string, error) {
372
 func (testFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string, error) {
386
 	var p *profile.Profile
373
 	var p *profile.Profile
387
-	s = strings.TrimPrefix(s, "http://host:8000/")
388
 	switch s {
374
 	switch s {
389
 	case "cpu", "unknown":
375
 	case "cpu", "unknown":
390
 		p = cpuProfile()
376
 		p = cpuProfile()
402
 		p = contentionProfile()
388
 		p = contentionProfile()
403
 	case "symbolz":
389
 	case "symbolz":
404
 		p = symzProfile()
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
 	default:
391
 	default:
413
 		return nil, "", fmt.Errorf("unexpected source: %s", s)
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
 type testSymbolizer struct{}
397
 type testSymbolizer struct{}
435
 func testFetchSymbols(source, post string) ([]byte, error) {
414
 func testFetchSymbols(source, post string) ([]byte, error) {
436
 	var buf bytes.Buffer
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
 		for _, address := range strings.Split(post, "+") {
430
 		for _, address := range strings.Split(post, "+") {
440
 			a, _ := strconv.ParseInt(address, 0, 64)
431
 			a, _ := strconv.ParseInt(address, 0, 64)
441
 			fmt.Fprintf(&buf, "%v\t", address)
432
 			fmt.Fprintf(&buf, "%v\t", address)
446
 			fmt.Fprintf(&buf, "%#x\n", a-testStart-testOffset)
437
 			fmt.Fprintf(&buf, "%#x\n", a-testStart-testOffset)
447
 		}
438
 		}
448
 		return buf.Bytes(), nil
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
 type testSymbolzSymbolizer struct{}
445
 type testSymbolzSymbolizer struct{}
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
 func TestSymbolzAfterMerge(t *testing.T) {
1012
 func TestSymbolzAfterMerge(t *testing.T) {
1010
 	baseVars := pprofVariables
1013
 	baseVars := pprofVariables
1011
 	pprofVariables = baseVars.makeCopy()
1014
 	pprofVariables = baseVars.makeCopy()
1012
 	defer func() { pprofVariables = baseVars }()
1015
 	defer func() { pprofVariables = baseVars }()
1013
 
1016
 
1014
 	f := baseFlags()
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
 	o := setDefaults(nil)
1023
 	o := setDefaults(nil)
1018
 	o.Flagset = f
1024
 	o.Flagset = f
1026
 		t.Fatalf("parseFlags returned command %v, want [proto]", cmd)
1032
 		t.Fatalf("parseFlags returned command %v, want [proto]", cmd)
1027
 	}
1033
 	}
1028
 
1034
 
1029
-	o.Fetch = testFetcher{}
1035
+	o.Fetch = testSymbolzMergeFetcher{}
1030
 	o.Sym = testSymbolzSymbolizer{}
1036
 	o.Sym = testSymbolzSymbolizer{}
1031
 	p, err := fetchProfiles(src, o)
1037
 	p, err := fetchProfiles(src, o)
1032
 	if err != nil {
1038
 	if err != nil {
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
 type mockObjTool struct{}
1056
 type mockObjTool struct{}
1144
 
1057
 
1145
 func (*mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
1058
 func (*mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {

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

242
 	return "", fmt.Errorf("failed to identify temp dir")
242
 	return "", fmt.Errorf("failed to identify temp dir")
243
 }
243
 }
244
 
244
 
245
+const testSourceAddress = "pproftest.local"
246
+
245
 // grabProfile fetches a profile. Returns the profile, sources for the
247
 // grabProfile fetches a profile. Returns the profile, sources for the
246
 // profile mappings, a bool indicating if the profile was fetched
248
 // profile mappings, a bool indicating if the profile was fetched
247
 // remotely, and an error.
249
 // remotely, and an error.
276
 	if src != "" {
278
 	if src != "" {
277
 		msrc = collectMappingSources(p, src)
279
 		msrc = collectMappingSources(p, src)
278
 		remote = true
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
 	return
287
 	return
281
 }
288
 }

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

15
 package driver
15
 package driver
16
 
16
 
17
 import (
17
 import (
18
+	"crypto/ecdsa"
19
+	"crypto/elliptic"
20
+	"crypto/rand"
21
+	"crypto/tls"
22
+	"crypto/x509"
23
+	"encoding/pem"
18
 	"fmt"
24
 	"fmt"
19
 	"io/ioutil"
25
 	"io/ioutil"
26
+	"math/big"
20
 	"net/http"
27
 	"net/http"
21
 	"net/url"
28
 	"net/url"
22
 	"os"
29
 	"os"
24
 	"reflect"
31
 	"reflect"
25
 	"regexp"
32
 	"regexp"
26
 	"runtime"
33
 	"runtime"
34
+	"strings"
27
 	"testing"
35
 	"testing"
28
 	"time"
36
 	"time"
29
 
37
 
165
 	const path = "testdata/"
173
 	const path = "testdata/"
166
 
174
 
167
 	// Intercept http.Get calls from HTTPFetcher.
175
 	// Intercept http.Get calls from HTTPFetcher.
176
+	savedHTTPGet := httpGet
177
+	defer func() { httpGet = savedHTTPGet }()
168
 	httpGet = stubHTTPGet
178
 	httpGet = stubHTTPGet
169
 
179
 
170
 	type testcase struct {
180
 	type testcase struct {
227
 	c := &http.Client{Transport: t}
237
 	c := &http.Client{Transport: t}
228
 	return c.Get("file:///" + file)
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
+}