libgo: update to Go1.10beta2 release

Reviewed-on: https://go-review.googlesource.com/87897

From-SVN: r256794
This commit is contained in:
Ian Lance Taylor 2018-01-17 14:20:29 +00:00
parent 9bff008691
commit c6d6367f84
139 changed files with 6574 additions and 5042 deletions

View File

@ -1,4 +1,4 @@
ff851e1190923f8612004c6c214a7c202471b0ba
ca805b704fc141d7ad61f8fcd3badbaa04b7e363
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View File

@ -1,4 +1,4 @@
9ce6b5c2ed5d3d5251b9a6a0c548d5fb2c8567e8
594668a5a96267a46282ce3007a584ec07adf705
The first line of this file holds the git revision number of the
last merge done from the master library sources.

View File

@ -479,7 +479,7 @@ version.go: s-version; @true
s-version: Makefile
rm -f version.go.tmp
echo "package sys" > version.go.tmp
echo 'const DefaultGoroot = "$(prefix)"' >> version.go.tmp
echo 'func init() { DefaultGoroot = "$(prefix)" }' >> version.go.tmp
echo 'const TheVersion = "'`cat $(srcdir)/VERSION | sed 1q`' '`$(GOC) --version | sed 1q`'"' >> version.go.tmp
echo 'const Goexperiment = ``' >> version.go.tmp
echo 'const GOARCH = "'$(GOARCH)'"' >> version.go.tmp
@ -535,7 +535,7 @@ s-objabi: Makefile
rm -f objabi.go.tmp
echo "package objabi" > objabi.go.tmp
echo "import \"runtime\"" >> objabi.go.tmp
echo 'const defaultGOROOT = `$(prefix)`' >> objabi.go.tmp
echo 'func init() { defaultGOROOT = `$(prefix)` }' >> objabi.go.tmp
echo 'const defaultGO386 = `sse2`' >> objabi.go.tmp
echo 'const defaultGOARM = `5`' >> objabi.go.tmp
echo 'const defaultGOMIPS = `hardfloat`' >> objabi.go.tmp
@ -824,6 +824,7 @@ PACKAGES = \
internal/singleflight \
internal/syscall/unix \
internal/testenv \
internal/testlog \
internal/trace \
io \
io/ioutil \

View File

@ -931,6 +931,7 @@ PACKAGES = \
internal/singleflight \
internal/syscall/unix \
internal/testenv \
internal/testlog \
internal/trace \
io \
io/ioutil \
@ -3061,7 +3062,7 @@ version.go: s-version; @true
s-version: Makefile
rm -f version.go.tmp
echo "package sys" > version.go.tmp
echo 'const DefaultGoroot = "$(prefix)"' >> version.go.tmp
echo 'func init() { DefaultGoroot = "$(prefix)" }' >> version.go.tmp
echo 'const TheVersion = "'`cat $(srcdir)/VERSION | sed 1q`' '`$(GOC) --version | sed 1q`'"' >> version.go.tmp
echo 'const Goexperiment = ``' >> version.go.tmp
echo 'const GOARCH = "'$(GOARCH)'"' >> version.go.tmp
@ -3117,7 +3118,7 @@ s-objabi: Makefile
rm -f objabi.go.tmp
echo "package objabi" > objabi.go.tmp
echo "import \"runtime\"" >> objabi.go.tmp
echo 'const defaultGOROOT = `$(prefix)`' >> objabi.go.tmp
echo 'func init() { defaultGOROOT = `$(prefix)` }' >> objabi.go.tmp
echo 'const defaultGO386 = `sse2`' >> objabi.go.tmp
echo 'const defaultGOARM = `5`' >> objabi.go.tmp
echo 'const defaultGOMIPS = `hardfloat`' >> objabi.go.tmp

View File

@ -1 +1 @@
go1.10beta1
go1.10beta2

View File

@ -403,16 +403,35 @@ the CF*Ref types from the CoreFoundation library on Darwin, including:
CFXMLParserRef
CFXMLTreeRef
Also the object types from Java's JNI interface:
jobject
jclass
jthrowable
jstring
jarray
jbooleanArray
jbyteArray
jcharArray
jshortArray
jintArray
jlongArray
jfloatArray
jdoubleArray
jobjectArray
jweak
These types are uintptr on the Go side because they would otherwise
confuse the Go garbage collector; they are sometimes not really
pointers but data structures encoded in a pointer type. All operations
on these types must happen in C. The proper constant to initialize an
empty such reference is 0, not nil.
This special case was introduced in Go 1.10. For auto-updating code
from Go 1.9 and earlier, use the cftype rewrite in the Go fix tool:
These special cases were introduced in Go 1.10. For auto-updating code
from Go 1.9 and earlier, use the cftype or jni rewrites in the Go fix tool:
go tool fix -r cftype <pkg>
go tool fix -r jni <pkg>
It will replace nil with 0 in the appropriate places.

View File

@ -2152,7 +2152,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
name := c.Ident("_Ctype_" + dt.Name)
goIdent[name.Name] = name
sub := c.Type(dt.Type, pos)
if badPointerTypedef(dt.Name) {
if badPointerTypedef(dt) {
// Treat this typedef as a uintptr.
s := *sub
s.Go = c.uintptr
@ -2318,7 +2318,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
}
// ...or the typedef is one in which we expect bad pointers.
// It will be a uintptr instead of *X.
if badPointerTypedef(dt.Name) {
if badPointerTypedef(dt) {
break
}
@ -2666,13 +2666,23 @@ func fieldPrefix(fld []*ast.Field) string {
// A typedef is bad if C code sometimes stores non-pointers in this type.
// TODO: Currently our best solution is to find these manually and list them as
// they come up. A better solution is desired.
func badPointerTypedef(t string) bool {
// The real bad types are CFNumberRef and CFTypeRef.
func badPointerTypedef(dt *dwarf.TypedefType) bool {
if badCFType(dt) {
return true
}
if badJNI(dt) {
return true
}
return false
}
func badCFType(dt *dwarf.TypedefType) bool {
// The real bad types are CFNumberRef and CFDateRef.
// Sometimes non-pointers are stored in these types.
// CFTypeRef is a supertype of those, so it can have bad pointers in it as well.
// We return true for the other CF*Ref types just so casting between them is easier.
// See comment below for details about the bad pointers.
return goos == "darwin" && strings.HasPrefix(t, "CF") && strings.HasSuffix(t, "Ref")
return goos == "darwin" && strings.HasPrefix(dt.Name, "CF") && strings.HasSuffix(dt.Name, "Ref")
}
// Comment from Darwin's CFInternal.h
@ -2709,3 +2719,61 @@ enum {
kCFTaggedObjectID_Undefined7 = (7 << 1) + 1,
};
*/
func badJNI(dt *dwarf.TypedefType) bool {
// In Dalvik and ART, the jobject type in the JNI interface of the JVM has the
// property that it is sometimes (always?) a small integer instead of a real pointer.
// Note: although only the android JVMs are bad in this respect, we declare the JNI types
// bad regardless of platform, so the same Go code compiles on both android and non-android.
if parent, ok := jniTypes[dt.Name]; ok {
// Try to make sure we're talking about a JNI type, not just some random user's
// type that happens to use the same name.
// C doesn't have the notion of a package, so it's hard to be certain.
// Walk up to jobject, checking each typedef on the way.
w := dt
for parent != "" {
t, ok := w.Type.(*dwarf.TypedefType)
if !ok || t.Name != parent {
return false
}
w = t
parent, ok = jniTypes[w.Name]
if !ok {
return false
}
}
// Check that the typedef is:
// struct _jobject;
// typedef struct _jobject *jobject;
if ptr, ok := w.Type.(*dwarf.PtrType); ok {
if str, ok := ptr.Type.(*dwarf.StructType); ok {
if str.StructName == "_jobject" && str.Kind == "struct" && len(str.Field) == 0 && str.Incomplete {
return true
}
}
}
}
return false
}
// jniTypes maps from JNI types that we want to be uintptrs, to the underlying type to which
// they are mapped. The base "jobject" maps to the empty string.
var jniTypes = map[string]string{
"jobject": "",
"jclass": "jobject",
"jthrowable": "jobject",
"jstring": "jobject",
"jarray": "jobject",
"jbooleanArray": "jarray",
"jbyteArray": "jarray",
"jcharArray": "jarray",
"jshortArray": "jarray",
"jintArray": "jarray",
"jlongArray": "jarray",
"jfloatArray": "jarray",
"jdoubleArray": "jarray",
"jobjectArray": "jarray",
"jweak": "jobject",
}

View File

@ -1680,9 +1680,7 @@ const builtinExportProlog = `
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
typedef ptrdiff_t intgo;
typedef struct { const char *p; intgo n; } _GoString_;
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif
`

View File

@ -505,7 +505,7 @@
//
// Usage:
//
// go get [-d] [-f] [-fix] [-insecure] [-t] [-u] [build flags] [packages]
// go get [-d] [-f] [-fix] [-insecure] [-t] [-u] [-v] [build flags] [packages]
//
// Get downloads the packages named by the import paths, along with their
// dependencies. It then installs the named packages, like 'go install'.
@ -1710,14 +1710,14 @@
// The 'go test' command expects to find test, benchmark, and example functions
// in the "*_test.go" files corresponding to the package under test.
//
// A test function is one named TestXXX (where XXX is any alphanumeric string
// not starting with a lower case letter) and should have the signature,
// A test function is one named TestXxx (where Xxx does not start with a
// lower case letter) and should have the signature,
//
// func TestXXX(t *testing.T) { ... }
// func TestXxx(t *testing.T) { ... }
//
// A benchmark function is one named BenchmarkXXX and should have the signature,
// A benchmark function is one named BenchmarkXxx and should have the signature,
//
// func BenchmarkXXX(b *testing.B) { ... }
// func BenchmarkXxx(b *testing.B) { ... }
//
// An example function is similar to a test function but, instead of using
// *testing.T to report success or failure, prints output to os.Stdout.
@ -1728,8 +1728,8 @@
// comment is compiled but not executed. An example with no text after
// "Output:" is compiled, executed, and expected to produce no output.
//
// Godoc displays the body of ExampleXXX to demonstrate the use
// of the function, constant, or variable XXX. An example of a method M with
// Godoc displays the body of ExampleXxx to demonstrate the use
// of the function, constant, or variable Xxx. An example of a method M with
// receiver type T or *T is named ExampleT_M. There may be multiple examples
// for a given function, constant, or variable, distinguished by a trailing _xxx,
// where xxx is a suffix not beginning with an upper case letter.

View File

@ -36,6 +36,16 @@ var (
skipExternal = false // skip external tests
)
func tooSlow(t *testing.T) {
if testing.Short() {
// In -short mode; skip test, except run it on the {darwin,linux,windows}/amd64 builders.
if testenv.Builder() != "" && runtime.GOARCH == "amd64" && (runtime.GOOS == "linux" || runtime.GOOS == "darwin" || runtime.GOOS == "windows") {
return
}
t.Skip("skipping test in -short mode")
}
}
func init() {
switch runtime.GOOS {
case "android", "nacl":
@ -92,6 +102,7 @@ func TestMain(m *testing.M) {
fmt.Printf("SKIP\n")
return
}
os.Unsetenv("GOROOT_FINAL")
if canRun {
args := []string{"build", "-tags", "testgo", "-o", "testgo" + exeSuffix}
@ -916,6 +927,7 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
func TestGoListStandard(t *testing.T) {
skipIfGccgo(t, "gccgo does not GOROOT")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
// TODO: tg.parallel()
@ -941,6 +953,7 @@ func TestGoListStandard(t *testing.T) {
}
func TestGoInstallCleansUpAfterGoBuild(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
// TODO: tg.parallel()
@ -983,6 +996,7 @@ func TestGoInstallCleansUpAfterGoBuild(t *testing.T) {
}
func TestGoInstallRebuildsStalePackagesInOtherGOPATH(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -1084,6 +1098,7 @@ func TestGoInstallErrorOnCrossCompileToBin(t *testing.T) {
func TestGoInstallDetectsRemovedFilesInPackageMain(t *testing.T) {
skipIfGccgo(t, "gccgo does not yet support package build IDs")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -1586,6 +1601,7 @@ func TestBuildOutputToDevNull(t *testing.T) {
}
func TestPackageMainTestImportsArchiveNotBinary(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -1601,6 +1617,19 @@ func TestPackageMainTestImportsArchiveNotBinary(t *testing.T) {
tg.run("test", "main_test")
}
func TestPackageMainTestCompilerFlags(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.makeTempdir()
tg.setenv("GOPATH", tg.path("."))
tg.tempFile("src/p1/p1.go", "package main\n")
tg.tempFile("src/p1/p1_test.go", "package main\nimport \"testing\"\nfunc Test(t *testing.T){}\n")
tg.run("test", "-c", "-n", "p1")
tg.grepBothNot(`([\\/]compile|gccgo).* (-p main|-fgo-pkgpath=main).*p1\.go`, "should not have run compile -p main p1.go")
tg.grepStderr(`([\\/]compile|gccgo).* (-p p1|-fgo-pkgpath=p1).*p1\.go`, "should have run compile -p p1 p1.go")
}
// The runtime version string takes one of two forms:
// "go1.X[.Y]" for Go releases, and "devel +hash" at tip.
// Determine whether we are in a released copy by
@ -1788,6 +1817,7 @@ func TestIgnoreEmptyPathsInGOPATH(t *testing.T) {
// Issue 4104.
func TestGoTestWithPackageListedMultipleTimes(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -1798,6 +1828,7 @@ func TestGoTestWithPackageListedMultipleTimes(t *testing.T) {
}
func TestGoListHasAConsistentOrder(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -1810,6 +1841,7 @@ func TestGoListHasAConsistentOrder(t *testing.T) {
}
func TestGoListStdDoesNotIncludeCommands(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -1819,6 +1851,7 @@ func TestGoListStdDoesNotIncludeCommands(t *testing.T) {
func TestGoListCmdOnlyShowsCommands(t *testing.T) {
skipIfGccgo(t, "gccgo has no GOROOT")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2038,6 +2071,7 @@ func TestGoGetIntoGOROOT(t *testing.T) {
func TestLdflagsArgumentsWithSpacesIssue3941(t *testing.T) {
skipIfGccgo(t, "gccgo does not support -ldflags -X")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2052,6 +2086,7 @@ func TestLdflagsArgumentsWithSpacesIssue3941(t *testing.T) {
func TestGoTestCpuprofileLeavesBinaryBehind(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
// TODO: tg.parallel()
@ -2063,6 +2098,7 @@ func TestGoTestCpuprofileLeavesBinaryBehind(t *testing.T) {
func TestGoTestCpuprofileDashOControlsBinaryLocation(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
// TODO: tg.parallel()
@ -2074,6 +2110,7 @@ func TestGoTestCpuprofileDashOControlsBinaryLocation(t *testing.T) {
func TestGoTestMutexprofileLeavesBinaryBehind(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
// TODO: tg.parallel()
@ -2085,6 +2122,7 @@ func TestGoTestMutexprofileLeavesBinaryBehind(t *testing.T) {
func TestGoTestMutexprofileDashOControlsBinaryLocation(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
// TODO: tg.parallel()
@ -2106,6 +2144,7 @@ func TestGoBuildNonMain(t *testing.T) {
func TestGoTestDashCDashOControlsBinaryLocation(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2116,6 +2155,7 @@ func TestGoTestDashCDashOControlsBinaryLocation(t *testing.T) {
func TestGoTestDashOWritesBinary(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2126,6 +2166,7 @@ func TestGoTestDashOWritesBinary(t *testing.T) {
func TestGoTestDashIDashOWritesBinary(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2223,6 +2264,7 @@ func TestSymlinksInternal(t *testing.T) {
// Issue 4515.
func TestInstallWithTags(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2408,10 +2450,8 @@ func checkCoverage(tg *testgoData, data string) {
}
func TestCoverageRuns(t *testing.T) {
if testing.Short() {
t.Skip("don't build libraries for coverage in short mode")
}
skipIfGccgo(t, "gccgo has no cover tool")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.run("test", "-short", "-coverpkg=strings", "strings", "regexp")
@ -2424,10 +2464,8 @@ func TestCoverageRuns(t *testing.T) {
// Check that coverage analysis uses set mode.
// Also check that coverage profiles merge correctly.
func TestCoverageUsesSetMode(t *testing.T) {
if testing.Short() {
t.Skip("don't build libraries for coverage in short mode")
}
skipIfGccgo(t, "gccgo has no cover tool")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.creatingTemp("testdata/cover.out")
@ -2453,9 +2491,7 @@ func TestCoverageUsesSetMode(t *testing.T) {
}
func TestCoverageUsesAtomicModeForRace(t *testing.T) {
if testing.Short() {
t.Skip("don't build libraries for coverage in short mode")
}
tooSlow(t)
if !canRace {
t.Skip("skipping because race detector not supported")
}
@ -2478,6 +2514,7 @@ func TestCoverageUsesAtomicModeForRace(t *testing.T) {
func TestCoverageSyncAtomicImport(t *testing.T) {
skipIfGccgo(t, "gccgo has no cover tool")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2485,6 +2522,18 @@ func TestCoverageSyncAtomicImport(t *testing.T) {
tg.run("test", "-short", "-cover", "-covermode=atomic", "-coverpkg=coverdep/p1", "coverdep")
}
func TestCoverageDepLoop(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
// coverdep2/p1's xtest imports coverdep2/p2 which imports coverdep2/p1.
// Make sure that coverage on coverdep2/p2 recompiles coverdep2/p2.
tg.run("test", "-short", "-cover", "coverdep2/p1")
tg.grepStdout("coverage: 100.0% of statements", "expected 100.0% coverage")
}
func TestCoverageImportMainLoop(t *testing.T) {
skipIfGccgo(t, "gccgo has no cover tool")
tg := testgo(t)
@ -2498,6 +2547,7 @@ func TestCoverageImportMainLoop(t *testing.T) {
func TestCoveragePattern(t *testing.T) {
skipIfGccgo(t, "gccgo has no cover tool")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2508,11 +2558,12 @@ func TestCoveragePattern(t *testing.T) {
// (as opposed to pattern matching on deps)
// then it will try to load sleepybad, which does not compile,
// and the test command will fail.
tg.run("test", "-coverprofile="+filepath.Join(tg.tempdir, "cover.out"), "-coverpkg=sleepy...", "-run=^$", "sleepy1")
tg.run("test", "-coverprofile="+tg.path("cover.out"), "-coverpkg=sleepy...", "-run=^$", "sleepy1")
}
func TestCoverageErrorLine(t *testing.T) {
skipIfGccgo(t, "gccgo has no cover tool")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2542,6 +2593,8 @@ func TestCoverageErrorLine(t *testing.T) {
}
func TestTestBuildFailureOutput(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2554,6 +2607,7 @@ func TestTestBuildFailureOutput(t *testing.T) {
func TestCoverageFunc(t *testing.T) {
skipIfGccgo(t, "gccgo has no cover tool")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -2561,7 +2615,7 @@ func TestCoverageFunc(t *testing.T) {
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.run("test", "-outputdir="+tg.tempdir, "-coverprofile=cover.out", "coverasm")
tg.run("tool", "cover", "-func="+filepath.Join(tg.tempdir, "cover.out"))
tg.run("tool", "cover", "-func="+tg.path("cover.out"))
tg.grepStdout(`\tg\t*100.0%`, "did not find g 100% covered")
tg.grepStdoutNot(`\tf\t*[0-9]`, "reported coverage for assembly function f")
}
@ -2628,9 +2682,7 @@ func TestTestRaceInstall(t *testing.T) {
if !canRace {
t.Skip("no race detector")
}
if testing.Short() && testenv.Builder() == "" {
t.Skip("don't rebuild the standard library in short mode")
}
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
@ -2668,6 +2720,7 @@ func main() {
func TestCoverageWithCgo(t *testing.T) {
skipIfGccgo(t, "gccgo has no cover tool")
tooSlow(t)
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
@ -2742,6 +2795,7 @@ func TestCgoShowsFullPathNames(t *testing.T) {
}
func TestCgoHandlesWlORIGIN(t *testing.T) {
tooSlow(t)
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
@ -2759,6 +2813,7 @@ func TestCgoHandlesWlORIGIN(t *testing.T) {
}
func TestCgoPkgConfig(t *testing.T) {
tooSlow(t)
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
@ -2810,6 +2865,7 @@ func main() {
// even if it's really just updating the mtime on an existing up-to-date binary.
func TestIssue6480(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
// TODO: tg.parallel()
@ -2945,6 +3001,8 @@ func TestIssue6844(t *testing.T) {
}
func TestBuildDashIInstallsDependencies(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -3015,7 +3073,20 @@ func TestGoTestMainAsNormalTest(t *testing.T) {
tg.grepBoth(okPattern, "go test did not say ok")
}
func TestGoTestMainTwice(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
tg.setenv("GOCACHE", tg.tempdir)
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.run("test", "-v", "multimain")
if strings.Count(tg.getStdout(), "notwithstanding") != 2 {
t.Fatal("tests did not run twice")
}
}
func TestGoTestFlagsAfterPackage(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.run("test", "testdata/flag_test.go", "-v", "-args", "-v=7") // Two distinct -v flags.
@ -3203,6 +3274,8 @@ func TestGoGetHTTPS404(t *testing.T) {
// Test that you cannot import a main package.
// See golang.org/issue/4210 and golang.org/issue/17475.
func TestImportMain(t *testing.T) {
tooSlow(t)
tg := testgo(t)
tg.parallel()
defer tg.cleanup()
@ -3291,6 +3364,8 @@ func TestImportMain(t *testing.T) {
// accessed by a non-local import (found in a GOPATH/GOROOT).
// See golang.org/issue/17475.
func TestImportLocal(t *testing.T) {
tooSlow(t)
tg := testgo(t)
tg.parallel()
defer tg.cleanup()
@ -3509,6 +3584,8 @@ func TestGoRunDirs(t *testing.T) {
func TestGoInstallPkgdir(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
tg.parallel()
defer tg.cleanup()
@ -3544,6 +3621,8 @@ func TestGoTestRaceInstallCgo(t *testing.T) {
}
func TestGoTestRaceFailures(t *testing.T) {
tooSlow(t)
if !canRace {
t.Skip("skipping because race detector not supported")
}
@ -3753,6 +3832,7 @@ func TestIssue12096(t *testing.T) {
func TestGoBuildOutput(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
@ -3833,6 +3913,7 @@ func TestGoBuildARM(t *testing.T) {
// For issue 14337.
func TestParallelTest(t *testing.T) {
tooSlow(t)
tg := testgo(t)
tg.parallel()
defer tg.cleanup()
@ -3852,6 +3933,7 @@ func TestParallelTest(t *testing.T) {
}
func TestCgoConsistentResults(t *testing.T) {
tooSlow(t)
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
@ -3918,6 +4000,8 @@ func TestFatalInBenchmarkCauseNonZeroExitStatus(t *testing.T) {
}
func TestBinaryOnlyPackages(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -4228,6 +4312,7 @@ func TestMatchesOnlySubtestParallelIsOK(t *testing.T) {
// Issue 18845
func TestBenchTimeout(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.run("test", "-bench", ".", "-timeout", "750ms", "testdata/timeoutbench_test.go")
@ -4235,6 +4320,7 @@ func TestBenchTimeout(t *testing.T) {
// Issue 19394
func TestWriteProfilesOnTimeout(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.tempDir("profiling")
@ -4350,6 +4436,7 @@ func TestFFLAGS(t *testing.T) {
// This is really a cmd/link issue but this is a convenient place to test it.
func TestDuplicateGlobalAsmSymbols(t *testing.T) {
skipIfGccgo(t, "gccgo does not use cmd/asm")
tooSlow(t)
if runtime.GOARCH != "386" && runtime.GOARCH != "amd64" {
t.Skipf("skipping test on %s", runtime.GOARCH)
}
@ -4386,7 +4473,7 @@ func main() {
}
`)
tg.setenv("GOPATH", tg.path("go"))
exe := filepath.Join(tg.tempdir, "p.exe")
exe := tg.path("p.exe")
tg.creatingTemp(exe)
tg.run("build", "-o", exe, "p")
}
@ -4469,19 +4556,9 @@ func TestExecutableGOROOT(t *testing.T) {
newRoot := tg.path("new")
t.Run("RelocatedExe", func(t *testing.T) {
t.Skip("TODO: skipping known broken test; see golang.org/issue/20284")
// Should fall back to default location in binary.
// No way to dig out other than look at source code.
data, err := ioutil.ReadFile("../../runtime/internal/sys/zversion.go")
if err != nil {
t.Fatal(err)
}
m := regexp.MustCompile("var DefaultGoroot = `([^`]+)`").FindStringSubmatch(string(data))
if m == nil {
t.Fatal("cannot find DefaultGoroot in ../../runtime/internal/sys/zversion.go")
}
check(t, newGoTool, m[1])
// Should fall back to default location in binary,
// which is the GOROOT we used when building testgo.exe.
check(t, newGoTool, testGOROOT)
})
// If the binary is sitting in a bin dir next to ../pkg/tool, that counts as a GOROOT,
@ -4506,9 +4583,7 @@ func TestExecutableGOROOT(t *testing.T) {
tg.must(os.RemoveAll(tg.path("new/pkg")))
// Binaries built in the new tree should report the
// new tree when they call runtime.GOROOT().
// This is implemented by having the go tool pass a -X option
// to the linker setting runtime/internal/sys.DefaultGoroot.
// new tree when they call runtime.GOROOT.
t.Run("RuntimeGoroot", func(t *testing.T) {
// Build a working GOROOT the easy way, with symlinks.
testenv.MustHaveSymlink(t)
@ -4584,6 +4659,7 @@ func main() {}`)
}
func TestCgoFlagContainsSpace(t *testing.T) {
tooSlow(t)
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
@ -4607,6 +4683,7 @@ func TestCgoFlagContainsSpace(t *testing.T) {
// Issue #20435.
func TestGoTestRaceCoverModeFailures(t *testing.T) {
tooSlow(t)
if !canRace {
t.Skip("skipping because race detector not supported")
}
@ -4721,6 +4798,7 @@ func TestTestRegexps(t *testing.T) {
}
func TestListTests(t *testing.T) {
tooSlow(t)
var tg *testgoData
testWith := func(listName, expected string) func(*testing.T) {
return func(t *testing.T) {
@ -4738,6 +4816,10 @@ func TestListTests(t *testing.T) {
}
func TestBuildmodePIE(t *testing.T) {
if testing.Short() && testenv.Builder() == "" {
t.Skipf("skipping in -short mode on non-builder")
}
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
switch platform {
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x",
@ -4792,6 +4874,7 @@ func TestBuildmodePIE(t *testing.T) {
}
func TestExecBuildX(t *testing.T) {
tooSlow(t)
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
@ -4803,12 +4886,14 @@ func TestExecBuildX(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOCACHE", "off")
tg.tempFile("main.go", `package main; import "C"; func main() { print("hello") }`)
src := tg.path("main.go")
obj := tg.path("main")
tg.run("build", "-x", "-o", obj, src)
sh := tg.path("test.sh")
err := ioutil.WriteFile(sh, []byte(tg.getStderr()), 0666)
err := ioutil.WriteFile(sh, []byte("set -e\n"+tg.getStderr()), 0666)
if err != nil {
t.Fatal(err)
}
@ -4842,6 +4927,7 @@ func TestExecBuildX(t *testing.T) {
}
func TestParallelNumber(t *testing.T) {
tooSlow(t)
for _, n := range [...]string{"-1", "0"} {
t.Run(n, func(t *testing.T) {
tg := testgo(t)
@ -4931,6 +5017,7 @@ func TestGOTMPDIR(t *testing.T) {
}
func TestBuildCache(t *testing.T) {
tooSlow(t)
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
t.Skip("GODEBUG gocacheverify")
}
@ -4991,6 +5078,8 @@ func TestCacheOutput(t *testing.T) {
}
func TestCacheCoverage(t *testing.T) {
tooSlow(t)
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
t.Skip("GODEBUG gocacheverify")
}
@ -5001,9 +5090,9 @@ func TestCacheCoverage(t *testing.T) {
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.makeTempdir()
tg.setenv("GOCACHE", filepath.Join(tg.tempdir, "c1"))
tg.run("test", "-cover", "strings")
tg.run("test", "-cover", "math", "strings")
tg.setenv("GOCACHE", tg.path("c1"))
tg.run("test", "-cover", "-short", "strings")
tg.run("test", "-cover", "-short", "math", "strings")
}
func TestIssue22588(t *testing.T) {
@ -5022,6 +5111,7 @@ func TestIssue22588(t *testing.T) {
}
func TestIssue22531(t *testing.T) {
tooSlow(t)
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
t.Skip("GODEBUG gocacheverify")
}
@ -5030,12 +5120,12 @@ func TestIssue22531(t *testing.T) {
tg.parallel()
tg.makeTempdir()
tg.setenv("GOPATH", tg.tempdir)
tg.setenv("GOCACHE", filepath.Join(tg.tempdir, "cache"))
tg.setenv("GOCACHE", tg.path("cache"))
tg.tempFile("src/m/main.go", "package main /* c1 */; func main() {}\n")
tg.run("install", "-x", "m")
tg.run("list", "-f", "{{.Stale}}", "m")
tg.grepStdout("false", "reported m as stale after install")
tg.run("tool", "buildid", filepath.Join(tg.tempdir, "bin/m"+exeSuffix))
tg.run("tool", "buildid", tg.path("bin/m"+exeSuffix))
// The link action ID did not include the full main build ID,
// even though the full main build ID is written into the
@ -5046,10 +5136,11 @@ func TestIssue22531(t *testing.T) {
tg.run("install", "-x", "m")
tg.run("list", "-f", "{{.Stale}}", "m")
tg.grepStdout("false", "reported m as stale after reinstall")
tg.run("tool", "buildid", filepath.Join(tg.tempdir, "bin/m"+exeSuffix))
tg.run("tool", "buildid", tg.path("bin/m"+exeSuffix))
}
func TestIssue22596(t *testing.T) {
tooSlow(t)
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
t.Skip("GODEBUG gocacheverify")
}
@ -5057,17 +5148,17 @@ func TestIssue22596(t *testing.T) {
defer tg.cleanup()
tg.parallel()
tg.makeTempdir()
tg.setenv("GOCACHE", filepath.Join(tg.tempdir, "cache"))
tg.setenv("GOCACHE", tg.path("cache"))
tg.tempFile("gopath1/src/p/p.go", "package p; func F(){}\n")
tg.tempFile("gopath2/src/p/p.go", "package p; func F(){}\n")
tg.setenv("GOPATH", filepath.Join(tg.tempdir, "gopath1"))
tg.setenv("GOPATH", tg.path("gopath1"))
tg.run("list", "-f={{.Target}}", "p")
target1 := strings.TrimSpace(tg.getStdout())
tg.run("install", "p")
tg.wantNotStale("p", "", "p stale after install")
tg.setenv("GOPATH", filepath.Join(tg.tempdir, "gopath2"))
tg.setenv("GOPATH", tg.path("gopath2"))
tg.run("list", "-f={{.Target}}", "p")
target2 := strings.TrimSpace(tg.getStdout())
tg.must(os.MkdirAll(filepath.Dir(target2), 0777))
@ -5078,6 +5169,8 @@ func TestIssue22596(t *testing.T) {
}
func TestTestCache(t *testing.T) {
tooSlow(t)
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
t.Skip("GODEBUG gocacheverify")
}
@ -5086,7 +5179,7 @@ func TestTestCache(t *testing.T) {
tg.parallel()
tg.makeTempdir()
tg.setenv("GOPATH", tg.tempdir)
tg.setenv("GOCACHE", filepath.Join(tg.tempdir, "cache"))
tg.setenv("GOCACHE", tg.path("cache"))
if runtime.Compiler != "gccgo" {
// timeout here should not affect result being cached
@ -5191,7 +5284,124 @@ func TestTestCache(t *testing.T) {
}
}
func TestTestCacheInputs(t *testing.T) {
tooSlow(t)
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
t.Skip("GODEBUG gocacheverify")
}
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.makeTempdir()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.setenv("GOCACHE", tg.path("cache"))
defer os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"))
defer os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"))
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("x"), 0644))
old := time.Now().Add(-1 * time.Minute)
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), old, old))
info, err := os.Stat(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"))
if err != nil {
t.Fatal(err)
}
t.Logf("file.txt: old=%v, info.ModTime=%v", old, info.ModTime()) // help debug when Chtimes lies about succeeding
tg.setenv("TESTKEY", "x")
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"), []byte("#!/bin/sh\nexit 0\n"), 0755))
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"), old, old))
tg.run("test", "testcache")
tg.run("test", "testcache")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.setenv("TESTKEY", "y")
tg.run("test", "testcache")
tg.grepStdoutNot(`\(cached\)`, "did not notice env var change")
tg.run("test", "testcache")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.run("test", "testcache", "-run=FileSize")
tg.run("test", "testcache", "-run=FileSize")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("xxx"), 0644))
tg.run("test", "testcache", "-run=FileSize")
tg.grepStdoutNot(`\(cached\)`, "did not notice file size change")
tg.run("test", "testcache", "-run=FileSize")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.run("test", "testcache", "-run=Chdir")
tg.run("test", "testcache", "-run=Chdir")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("xxxxx"), 0644))
tg.run("test", "testcache", "-run=Chdir")
tg.grepStdoutNot(`\(cached\)`, "did not notice file size change")
tg.run("test", "testcache", "-run=Chdir")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), old, old))
tg.run("test", "testcache", "-run=FileContent")
tg.run("test", "testcache", "-run=FileContent")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("yyy"), 0644))
old2 := old.Add(10 * time.Second)
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), old2, old2))
tg.run("test", "testcache", "-run=FileContent")
tg.grepStdoutNot(`\(cached\)`, "did not notice file content change")
tg.run("test", "testcache", "-run=FileContent")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.run("test", "testcache", "-run=DirList")
tg.run("test", "testcache", "-run=DirList")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.must(os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt")))
tg.run("test", "testcache", "-run=DirList")
tg.grepStdoutNot(`\(cached\)`, "did not notice directory change")
tg.run("test", "testcache", "-run=DirList")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.tempFile("file.txt", "")
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/testcachetmp_test.go"), []byte(`package testcache
import (
"os"
"testing"
)
func TestExternalFile(t *testing.T) {
os.Open(`+fmt.Sprintf("%q", tg.path("file.txt"))+`)
_, err := os.Stat(`+fmt.Sprintf("%q", tg.path("file.txt"))+`)
if err != nil {
t.Fatal(err)
}
}
`), 0666))
defer os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/testcachetmp_test.go"))
tg.run("test", "testcache", "-run=ExternalFile")
tg.run("test", "testcache", "-run=ExternalFile")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.must(os.Remove(filepath.Join(tg.tempdir, "file.txt")))
tg.run("test", "testcache", "-run=ExternalFile")
tg.grepStdout(`\(cached\)`, "did not cache")
switch runtime.GOOS {
case "nacl", "plan9", "windows":
// no shell scripts
default:
tg.run("test", "testcache", "-run=Exec")
tg.run("test", "testcache", "-run=Exec")
tg.grepStdout(`\(cached\)`, "did not cache")
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"), old2, old2))
tg.run("test", "testcache", "-run=Exec")
tg.grepStdoutNot(`\(cached\)`, "did not notice script change")
tg.run("test", "testcache", "-run=Exec")
tg.grepStdout(`\(cached\)`, "did not cache")
}
}
func TestTestVet(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -5204,9 +5414,9 @@ func TestTestVet(t *testing.T) {
}
`)
tg.runFail("test", filepath.Join(tg.tempdir, "p1_test.go"))
tg.runFail("test", tg.path("p1_test.go"))
tg.grepStderr(`Logf format %d`, "did not diagnose bad Logf")
tg.run("test", "-vet=off", filepath.Join(tg.tempdir, "p1_test.go"))
tg.run("test", "-vet=off", tg.path("p1_test.go"))
tg.grepStdout(`^ok`, "did not print test summary")
tg.tempFile("p1.go", `
@ -5216,19 +5426,24 @@ func TestTestVet(t *testing.T) {
fmt.Printf("%d") // oops
}
`)
tg.runFail("test", filepath.Join(tg.tempdir, "p1.go"))
tg.runFail("test", tg.path("p1.go"))
tg.grepStderr(`Printf format %d`, "did not diagnose bad Printf")
tg.run("test", "-x", "-vet=shift", filepath.Join(tg.tempdir, "p1.go"))
tg.run("test", "-x", "-vet=shift", tg.path("p1.go"))
tg.grepStderr(`[\\/]vet.*-shift`, "did not run vet with -shift")
tg.grepStdout(`\[no test files\]`, "did not print test summary")
tg.run("test", "-vet=off", filepath.Join(tg.tempdir, "p1.go"))
tg.run("test", "-vet=off", tg.path("p1.go"))
tg.grepStdout(`\[no test files\]`, "did not print test summary")
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.run("test", "vetcycle") // must not fail; #22890
tg.runFail("test", "vetfail/...")
tg.grepStderr(`Printf format %d`, "did not diagnose bad Printf")
tg.grepStdout(`ok\s+vetfail/p2`, "did not run vetfail/p2")
}
func TestInstallDeps(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -5276,6 +5491,7 @@ func TestFmtLoadErrors(t *testing.T) {
}
func TestRelativePkgdir(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
@ -5292,26 +5508,28 @@ func TestGcflagsPatterns(t *testing.T) {
tg.setenv("GOPATH", "")
tg.setenv("GOCACHE", "off")
tg.run("build", "-v", "-gcflags= \t\r\n -e", "fmt")
tg.grepStderr("fmt", "did not rebuild fmt")
tg.grepStderrNot("reflect", "incorrectly rebuilt reflect")
tg.run("build", "-n", "-v", "-gcflags= \t\r\n -e", "fmt")
tg.grepStderr("^# fmt", "did not rebuild fmt")
tg.grepStderrNot("^# reflect", "incorrectly rebuilt reflect")
tg.run("build", "-v", "-gcflags=-e", "fmt", "reflect")
tg.grepStderr("fmt", "did not rebuild fmt")
tg.grepStderr("reflect", "did not rebuild reflect")
tg.grepStderrNot("runtime", "incorrectly rebuilt runtime")
tg.run("build", "-n", "-v", "-gcflags=-e", "fmt", "reflect")
tg.grepStderr("^# fmt", "did not rebuild fmt")
tg.grepStderr("^# reflect", "did not rebuild reflect")
tg.grepStderrNot("^# runtime", "incorrectly rebuilt runtime")
tg.run("build", "-x", "-v", "-gcflags= \t\r\n reflect \t\r\n = \t\r\n -N", "fmt")
tg.grepStderr("fmt", "did not rebuild fmt")
tg.grepStderr("reflect", "did not rebuild reflect")
tg.run("build", "-n", "-x", "-v", "-gcflags= \t\r\n reflect \t\r\n = \t\r\n -N", "fmt")
tg.grepStderr("^# fmt", "did not rebuild fmt")
tg.grepStderr("^# reflect", "did not rebuild reflect")
tg.grepStderr("compile.* -N .*-p reflect", "did not build reflect with -N flag")
tg.grepStderrNot("compile.* -N .*-p fmt", "incorrectly built fmt with -N flag")
tg.run("test", "-c", "-n", "-gcflags=-N", "strings")
tg.grepStderr("compile.* -N .*compare_test.go", "did not build strings_test package with -N flag")
tg.run("test", "-c", "-n", "-gcflags=-N", "-ldflags=-X=x.y=z", "strings")
tg.grepStderr("compile.* -N .*compare_test.go", "did not compile strings_test package with -N flag")
tg.grepStderr("link.* -X=x.y=z", "did not link strings.test binary with -X flag")
tg.run("test", "-c", "-n", "-gcflags=strings=-N", "strings")
tg.grepStderr("compile.* -N .*compare_test.go", "did not build strings_test package with -N flag")
tg.run("test", "-c", "-n", "-gcflags=strings=-N", "-ldflags=strings=-X=x.y=z", "strings")
tg.grepStderr("compile.* -N .*compare_test.go", "did not compile strings_test package with -N flag")
tg.grepStderr("link.* -X=x.y=z", "did not link strings.test binary with -X flag")
}
func TestGoTestMinusN(t *testing.T) {
@ -5324,6 +5542,8 @@ func TestGoTestMinusN(t *testing.T) {
func TestGoTestJSON(t *testing.T) {
skipIfGccgo(t, "gccgo does not have standard packages")
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
@ -5344,18 +5564,22 @@ func TestGoTestJSON(t *testing.T) {
tg.grepStdout(`"Action":"output","Package":"skipper","Test":"Test","Output":"--- SKIP:`, "did not see SKIP output")
tg.grepStdout(`"Action":"skip","Package":"skipper","Test":"Test"`, "did not see skip result for Test")
tg.run("test", "-json", "-short", "-v", "errors")
tg.grepStdout(`"Action":"output","Package":"errors","Output":".*\(cached\)`, "did not see no cached output")
tg.run("test", "-json", "-bench=NONE", "-short", "-v", "errors")
tg.grepStdout(`"Package":"errors"`, "did not see JSON output")
tg.grepStdout(`"Action":"run"`, "did not see JSON output")
tg.run("test", "-o", filepath.Join(tg.tempdir, "errors.test.exe"), "-c", "errors")
tg.run("tool", "test2json", "-p", "errors", filepath.Join(tg.tempdir, "errors.test.exe"), "-test.v", "-test.short")
tg.run("test", "-o", tg.path("errors.test.exe"), "-c", "errors")
tg.run("tool", "test2json", "-p", "errors", tg.path("errors.test.exe"), "-test.v", "-test.short")
tg.grepStdout(`"Package":"errors"`, "did not see JSON output")
tg.grepStdout(`"Action":"run"`, "did not see JSON output")
tg.grepStdout(`\{"Action":"pass","Package":"errors"\}`, "did not see final pass")
}
func TestFailFast(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
@ -5398,3 +5622,72 @@ func TestFailFast(t *testing.T) {
})
}
}
// Issue 22986.
func TestImportPath(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempFile("src/a/a.go", `
package main
import (
"log"
p "a/p-1.0"
)
func main() {
if !p.V {
log.Fatal("false")
}
}`)
tg.tempFile("src/a/a_test.go", `
package main_test
import (
p "a/p-1.0"
"testing"
)
func TestV(t *testing.T) {
if !p.V {
t.Fatal("false")
}
}`)
tg.tempFile("src/a/p-1.0/p.go", `
package p
var V = true
func init() {}
`)
tg.setenv("GOPATH", tg.path("."))
tg.run("build", "-o", tg.path("a.exe"), "a")
tg.run("test", "a")
}
// Issue 23150.
func TestCpuprofileTwice(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempFile("prof/src/x/x_test.go", `
package x_test
import (
"testing"
"time"
)
func TestSleep(t *testing.T) { time.Sleep(10 * time.Millisecond) }`)
tg.setenv("GOPATH", tg.path("prof"))
bin := tg.path("x.test")
out := tg.path("cpu.out")
tg.run("test", "-o="+bin, "-cpuprofile="+out, "x")
tg.must(os.Remove(out))
tg.run("test", "-o="+bin, "-cpuprofile="+out, "x")
tg.mustExist(out)
}

View File

@ -97,6 +97,9 @@ const (
// GODEBUG=gocacheverify=1.
var verify = false
// DebugTest is set when GODEBUG=gocachetest=1 is in the environment.
var DebugTest = false
func init() { initEnv() }
func initEnv() {
@ -110,6 +113,9 @@ func initEnv() {
if f == "gocachehash=1" {
debugHash = true
}
if f == "gocachetest=1" {
DebugTest = true
}
}
}

View File

@ -68,6 +68,9 @@ func DefaultDir() string {
switch runtime.GOOS {
case "windows":
dir = os.Getenv("LocalAppData")
if dir == "" {
return "off"
}
case "darwin":
dir = os.Getenv("HOME")

View File

@ -76,11 +76,12 @@ func init() {
}
var (
GOROOT = findGOROOT()
GOBIN = os.Getenv("GOBIN")
GOROOTbin = filepath.Join(GOROOT, "bin")
GOROOTpkg = filepath.Join(GOROOT, "pkg")
GOROOTsrc = filepath.Join(GOROOT, "src")
GOROOT = findGOROOT()
GOBIN = os.Getenv("GOBIN")
GOROOTbin = filepath.Join(GOROOT, "bin")
GOROOTpkg = filepath.Join(GOROOT, "pkg")
GOROOTsrc = filepath.Join(GOROOT, "src")
GOROOT_FINAL = findGOROOT_FINAL()
// Used in envcmd.MkEnv and build ID computations.
GOARM = fmt.Sprint(objabi.GOARM)
@ -134,6 +135,14 @@ func findGOROOT() string {
return def
}
func findGOROOT_FINAL() string {
def := GOROOT
if env := os.Getenv("GOROOT_FINAL"); env != "" {
def = filepath.Clean(env)
}
return def
}
// isSameDir reports whether dir1 and dir2 are the same directory.
func isSameDir(dir1, dir2 string) bool {
if dir1 == dir2 {

View File

@ -22,7 +22,7 @@ import (
)
var CmdGet = &base.Command{
UsageLine: "get [-d] [-f] [-fix] [-insecure] [-t] [-u] [build flags] [packages]",
UsageLine: "get [-d] [-f] [-fix] [-insecure] [-t] [-u] [-v] [build flags] [packages]",
Short: "download and install packages and dependencies",
Long: `
Get downloads the packages named by the import paths, along with their

View File

@ -19,7 +19,8 @@ var DebugDeprecatedImportcfg debugDeprecatedImportcfgFlag
type debugDeprecatedImportcfgFlag struct {
enabled bool
pkgs map[string]*debugDeprecatedImportcfgPkg
Import map[string]string
Pkg map[string]*debugDeprecatedImportcfgPkg
}
type debugDeprecatedImportcfgPkg struct {
@ -49,8 +50,9 @@ func (f *debugDeprecatedImportcfgFlag) Set(x string) error {
}
data = data[len(debugDeprecatedImportcfgMagic):]
f.pkgs = nil
if err := json.Unmarshal(data, &f.pkgs); err != nil {
f.Import = nil
f.Pkg = nil
if err := json.Unmarshal(data, &f); err != nil {
return errImportcfgSyntax
}
f.enabled = true
@ -58,18 +60,19 @@ func (f *debugDeprecatedImportcfgFlag) Set(x string) error {
}
func (f *debugDeprecatedImportcfgFlag) lookup(parent *Package, path string) (dir, newPath string) {
if parent == nil {
if p1 := f.pkgs[path]; p1 != nil {
return p1.Dir, path
}
return "", ""
newPath = path
if p := f.Import[path]; p != "" {
newPath = p
}
if p1 := f.pkgs[parent.ImportPath]; p1 != nil {
if newPath := p1.Import[path]; newPath != "" {
if p2 := f.pkgs[newPath]; p2 != nil {
return p2.Dir, newPath
if parent != nil {
if p1 := f.Pkg[parent.ImportPath]; p1 != nil {
if p := p1.Import[path]; p != "" {
newPath = p
}
}
}
if p2 := f.Pkg[newPath]; p2 != nil {
return p2.Dir, newPath
}
return "", ""
}

View File

@ -56,25 +56,3 @@ func expandPath(p string) string {
}
return p
}
// hasFilePathPrefix reports whether the filesystem path s begins with the
// elements in prefix.
func hasFilePathPrefix(s, prefix string) bool {
sv := strings.ToUpper(filepath.VolumeName(s))
pv := strings.ToUpper(filepath.VolumeName(prefix))
s = s[len(sv):]
prefix = prefix[len(pv):]
switch {
default:
return false
case sv != pv:
return false
case len(s) == len(prefix):
return s == prefix
case len(s) > len(prefix):
if prefix != "" && prefix[len(prefix)-1] == filepath.Separator {
return strings.HasPrefix(s, prefix)
}
return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
}
}

View File

@ -524,13 +524,13 @@ func VendoredImportPath(parent *Package, path string) (found string) {
dir := filepath.Clean(parent.Dir)
root := filepath.Join(parent.Root, "src")
if !hasFilePathPrefix(dir, root) || parent.ImportPath != "command-line-arguments" && filepath.Join(root, parent.ImportPath) != dir {
if !str.HasFilePathPrefix(dir, root) || parent.ImportPath != "command-line-arguments" && filepath.Join(root, parent.ImportPath) != dir {
// Look for symlinks before reporting error.
dir = expandPath(dir)
root = expandPath(root)
}
if !hasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || parent.ImportPath != "command-line-arguments" && !parent.Internal.Local && filepath.Join(root, parent.ImportPath) != dir {
if !str.HasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || parent.ImportPath != "command-line-arguments" && !parent.Internal.Local && filepath.Join(root, parent.ImportPath) != dir {
base.Fatalf("unexpected directory layout:\n"+
" import path: %s\n"+
" root: %s\n"+
@ -674,14 +674,14 @@ func disallowInternal(srcDir string, p *Package, stk *ImportStack) *Package {
i-- // rewind over slash in ".../internal"
}
parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)]
if hasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
return p
}
// Look for symlinks before reporting error.
srcDir = expandPath(srcDir)
parent = expandPath(parent)
if hasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
return p
}
@ -774,14 +774,14 @@ func disallowVendorVisibility(srcDir string, p *Package, stk *ImportStack) *Pack
return p
}
parent := p.Dir[:truncateTo]
if hasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
return p
}
// Look for symlinks before reporting error.
srcDir = expandPath(srcDir)
parent = expandPath(parent)
if hasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
if str.HasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
return p
}

View File

@ -0,0 +1,32 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package str
import (
"path/filepath"
"strings"
)
// HasFilePathPrefix reports whether the filesystem path s begins with the
// elements in prefix.
func HasFilePathPrefix(s, prefix string) bool {
sv := strings.ToUpper(filepath.VolumeName(s))
pv := strings.ToUpper(filepath.VolumeName(prefix))
s = s[len(sv):]
prefix = prefix[len(pv):]
switch {
default:
return false
case sv != pv:
return false
case len(s) == len(prefix):
return s == prefix
case len(s) > len(prefix):
if prefix != "" && prefix[len(prefix)-1] == filepath.Separator {
return strings.HasPrefix(s, prefix)
}
return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
}
}

View File

@ -406,14 +406,14 @@ var HelpTestfunc = &base.Command{
The 'go test' command expects to find test, benchmark, and example functions
in the "*_test.go" files corresponding to the package under test.
A test function is one named TestXXX (where XXX is any alphanumeric string
not starting with a lower case letter) and should have the signature,
A test function is one named TestXxx (where Xxx does not start with a
lower case letter) and should have the signature,
func TestXXX(t *testing.T) { ... }
func TestXxx(t *testing.T) { ... }
A benchmark function is one named BenchmarkXXX and should have the signature,
A benchmark function is one named BenchmarkXxx and should have the signature,
func BenchmarkXXX(b *testing.B) { ... }
func BenchmarkXxx(b *testing.B) { ... }
An example function is similar to a test function but, instead of using
*testing.T to report success or failure, prints output to os.Stdout.
@ -424,8 +424,8 @@ comment, however the order of the lines is ignored. An example with no such
comment is compiled but not executed. An example with no text after
"Output:" is compiled, executed, and expected to produce no output.
Godoc displays the body of ExampleXXX to demonstrate the use
of the function, constant, or variable XXX. An example of a method M with
Godoc displays the body of ExampleXxx to demonstrate the use
of the function, constant, or variable Xxx. An example of a method M with
receiver type T or *T is named ExampleT_M. There may be multiple examples
for a given function, constant, or variable, distinguished by a trailing _xxx,
where xxx is a suffix not beginning with an upper case letter.
@ -938,6 +938,11 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
Internal: load.PackageInternal{
Build: &build.Package{Name: "main"},
OmitDebug: !testC && !testNeedBinary,
Asmflags: p.Internal.Asmflags,
Gcflags: p.Internal.Gcflags,
Ldflags: p.Internal.Ldflags,
Gccgoflags: p.Internal.Gccgoflags,
},
}
@ -1010,7 +1015,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
// This will cause extra compilation, so for now we only do it
// when testCover is set. The conditions are more general, though,
// and we may find that we need to do it always in the future.
recompileForTest(pmain, p, ptest)
recompileForTest(pmain, p, ptest, pxtest)
}
for _, cp := range pmain.Internal.Imports {
@ -1064,8 +1069,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
}
}
buildAction = a
var installAction *work.Action
var installAction, cleanAction *work.Action
if testC || testNeedBinary {
// -c or profiling flag: create action to copy binary to ./test.out.
target := filepath.Join(base.Cwd, testBinary+cfg.ExeSuffix)
@ -1083,7 +1087,6 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
Package: pmain,
Target: target,
}
buildAction = installAction
runAction = installAction // make sure runAction != nil even if not running test
}
if testC {
@ -1096,7 +1099,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
Func: c.builderRunTest,
Deps: []*work.Action{buildAction},
Package: p,
IgnoreFail: true,
IgnoreFail: true, // run (prepare output) even if build failed
TryCache: c.tryCache,
Objdir: testDir,
}
@ -1106,18 +1109,28 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
if pxtest != nil {
addTestVet(b, pxtest, runAction, installAction)
}
cleanAction := &work.Action{
Mode: "test clean",
Func: builderCleanTest,
Deps: []*work.Action{runAction},
Package: p,
Objdir: testDir,
cleanAction = &work.Action{
Mode: "test clean",
Func: builderCleanTest,
Deps: []*work.Action{runAction},
Package: p,
IgnoreFail: true, // clean even if test failed
Objdir: testDir,
}
printAction = &work.Action{
Mode: "test print",
Func: builderPrintTest,
Deps: []*work.Action{cleanAction},
Package: p,
Mode: "test print",
Func: builderPrintTest,
Deps: []*work.Action{cleanAction},
Package: p,
IgnoreFail: true, // print even if test failed
}
}
if installAction != nil {
if runAction != installAction {
installAction.Deps = append(installAction.Deps, runAction)
}
if cleanAction != nil {
cleanAction.Deps = append(cleanAction.Deps, installAction)
}
}
@ -1158,14 +1171,17 @@ Search:
return stk
}
func recompileForTest(pmain, preal, ptest *load.Package) {
func recompileForTest(pmain, preal, ptest, pxtest *load.Package) {
// The "test copy" of preal is ptest.
// For each package that depends on preal, make a "test copy"
// that depends on ptest. And so on, up the dependency tree.
testCopy := map[*load.Package]*load.Package{preal: ptest}
for _, p := range load.PackageList([]*load.Package{pmain}) {
if p == preal {
continue
}
// Copy on write.
didSplit := false
didSplit := p == pmain || p == pxtest
split := func() {
if didSplit {
return
@ -1245,6 +1261,52 @@ func (lockedStdout) Write(b []byte) (int, error) {
// builderRunTest is the action for running a test binary.
func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
if a.Failed {
// We were unable to build the binary.
a.Failed = false
a.TestOutput = new(bytes.Buffer)
fmt.Fprintf(a.TestOutput, "FAIL\t%s [build failed]\n", a.Package.ImportPath)
base.SetExitStatus(1)
return nil
}
var stdout io.Writer = os.Stdout
if testJSON {
json := test2json.NewConverter(lockedStdout{}, a.Package.ImportPath, test2json.Timestamp)
defer json.Close()
stdout = json
}
var buf bytes.Buffer
if len(pkgArgs) == 0 || testBench {
// Stream test output (no buffering) when no package has
// been given on the command line (implicit current directory)
// or when benchmarking.
// No change to stdout.
} else {
// If we're only running a single package under test or if parallelism is
// set to 1, and if we're displaying all output (testShowPass), we can
// hurry the output along, echoing it as soon as it comes in.
// We still have to copy to &buf for caching the result. This special
// case was introduced in Go 1.5 and is intentionally undocumented:
// the exact details of output buffering are up to the go command and
// subject to change. It would be nice to remove this special case
// entirely, but it is surely very helpful to see progress being made
// when tests are run on slow single-CPU ARM systems.
//
// If we're showing JSON output, then display output as soon as
// possible even when multiple tests are being run: the JSON output
// events are attributed to specific package tests, so interlacing them
// is OK.
if testShowPass && (len(pkgs) == 1 || cfg.BuildP == 1) || testJSON {
// Write both to stdout and buf, for possible saving
// to cache, and for looking for the "no tests to run" message.
stdout = io.MultiWriter(stdout, &buf)
} else {
stdout = &buf
}
}
if c.buf == nil {
// We did not find a cached result using the link step action ID,
// so we ran the link step. Try again now with the link output
@ -1258,20 +1320,20 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
c.tryCacheWithID(b, a, a.Deps[0].BuildContentID())
}
if c.buf != nil {
if stdout != &buf {
stdout.Write(c.buf.Bytes())
c.buf.Reset()
}
a.TestOutput = c.buf
return nil
}
if a.Failed {
// We were unable to build the binary.
a.Failed = false
a.TestOutput = new(bytes.Buffer)
fmt.Fprintf(a.TestOutput, "FAIL\t%s [build failed]\n", a.Package.ImportPath)
base.SetExitStatus(1)
return nil
execCmd := work.FindExecCmd()
testlogArg := []string{}
if !c.disableCache && len(execCmd) == 0 {
testlogArg = []string{"-test.testlogfile=" + a.Objdir + "testlog.txt"}
}
args := str.StringList(work.FindExecCmd(), a.Deps[0].Target, testArgs)
args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, testArgs)
if testCoverProfile != "" {
// Write coverage to temporary profile, for merging later.
@ -1292,42 +1354,8 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = a.Package.Dir
cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv)
var buf bytes.Buffer
var stdout io.Writer = os.Stdout
if testJSON {
json := test2json.NewConverter(lockedStdout{}, a.Package.ImportPath, test2json.Timestamp)
defer json.Close()
stdout = json
}
if len(pkgArgs) == 0 || testBench {
// Stream test output (no buffering) when no package has
// been given on the command line (implicit current directory)
// or when benchmarking.
cmd.Stdout = stdout
} else {
// If we're only running a single package under test or if parallelism is
// set to 1, and if we're displaying all output (testShowPass), we can
// hurry the output along, echoing it as soon as it comes in.
// We still have to copy to &buf for caching the result. This special
// case was introduced in Go 1.5 and is intentionally undocumented:
// the exact details of output buffering are up to the go command and
// subject to change. It would be nice to remove this special case
// entirely, but it is surely very helpful to see progress being made
// when tests are run on slow single-CPU ARM systems.
//
// If we're showing JSON output, then display output as soon as
// possible even when multiple tests are being run: the JSON output
// events are attributed to specific package tests, so interlacing them
// is OK.
if testShowPass && (len(pkgs) == 1 || cfg.BuildP == 1) || testJSON {
// Write both to stdout and buf, for possible saving
// to cache, and for looking for the "no tests to run" message.
cmd.Stdout = io.MultiWriter(stdout, &buf)
} else {
cmd.Stdout = &buf
}
}
cmd.Stderr = cmd.Stdout
cmd.Stdout = stdout
cmd.Stderr = stdout
// If there are any local SWIG dependencies, we want to load
// the shared library from the build directory.
@ -1392,7 +1420,7 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
if err == nil {
norun := ""
if !testShowPass {
if !testShowPass && !testJSON {
buf.Reset()
}
if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
@ -1427,6 +1455,9 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
if len(pkgArgs) == 0 {
// Caching does not apply to "go test",
// only to "go test foo" (including "go test .").
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: caching disabled in local directory mode\n")
}
c.disableCache = true
return false
}
@ -1435,6 +1466,9 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
for _, arg := range testArgs {
i := strings.Index(arg, "=")
if i < 0 || !strings.HasPrefix(arg, "-test.") {
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: caching disabled for test argument: %s\n", arg)
}
c.disableCache = true
return false
}
@ -1456,49 +1490,111 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
default:
// nothing else is cacheable
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: caching disabled for test argument: %s\n", arg)
}
c.disableCache = true
return false
}
}
if cache.Default() == nil {
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: GOCACHE=off\n")
}
c.disableCache = true
return false
}
// The test cache result fetch is a two-level lookup.
//
// First, we use the content hash of the test binary
// and its command-line arguments to find the
// list of environment variables and files consulted
// the last time the test was run with those arguments.
// (To avoid unnecessary links, we store this entry
// under two hashes: id1 uses the linker inputs as a
// proxy for the test binary, and id2 uses the actual
// test binary. If the linker inputs are unchanged,
// this way we avoid the link step, even though we
// do not cache link outputs.)
//
// Second, we compute a hash of the values of the
// environment variables and the content of the files
// listed in the log from the previous run.
// Then we look up test output using a combination of
// the hash from the first part (testID) and the hash of the
// test inputs (testInputsID).
//
// In order to store a new test result, we must redo the
// testInputsID computation using the log from the run
// we want to cache, and then we store that new log and
// the new outputs.
h := cache.NewHash("testResult")
fmt.Fprintf(h, "test binary %s args %q execcmd %q", id, cacheArgs, work.ExecCmd)
// TODO(rsc): How to handle other test dependencies like environment variables or input files?
// We could potentially add new API like testing.UsedEnv(envName string)
// or testing.UsedFile(inputFile string) to let tests declare what external inputs
// they consulted. These could be recorded and rechecked.
// The lookup here would become a two-step lookup: first use the binary+args
// to fetch the list of other inputs, then add the other inputs to produce a
// second key for fetching the results.
// For now, we'll assume that users will use -count=1 (or "go test") to bypass the test result
// cache when modifying those things.
testID := h.Sum()
if c.id1 == (cache.ActionID{}) {
c.id1 = testID
} else {
c.id2 = testID
}
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: test ID %x => %x\n", a.Package.ImportPath, id, testID)
}
// Load list of referenced environment variables and files
// from last run of testID, and compute hash of that content.
data, entry, err := cache.Default().GetBytes(testID)
if !bytes.HasPrefix(data, testlogMagic) || data[len(data)-1] != '\n' {
if cache.DebugTest {
if err != nil {
fmt.Fprintf(os.Stderr, "testcache: %s: input list not found: %v\n", a.Package.ImportPath, err)
} else {
fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed\n", a.Package.ImportPath)
}
}
return false
}
testInputsID, err := computeTestInputsID(a, data)
if err != nil {
return false
}
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: test ID %x => input ID %x => %x\n", a.Package.ImportPath, testID, testInputsID, testAndInputKey(testID, testInputsID))
}
// Parse cached result in preparation for changing run time to "(cached)".
// If we can't parse the cached result, don't use it.
data, entry, _ := cache.Default().GetBytes(testID)
data, entry, err = cache.Default().GetBytes(testAndInputKey(testID, testInputsID))
if len(data) == 0 || data[len(data)-1] != '\n' {
if cache.DebugTest {
if err != nil {
fmt.Fprintf(os.Stderr, "testcache: %s: test output not found: %v\n", a.Package.ImportPath, err)
} else {
fmt.Fprintf(os.Stderr, "testcache: %s: test output malformed\n", a.Package.ImportPath)
}
}
return false
}
if entry.Time.Before(testCacheExpire) {
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: test output expired due to go clean -testcache\n", a.Package.ImportPath)
}
return false
}
i := bytes.LastIndexByte(data[:len(data)-1], '\n') + 1
if !bytes.HasPrefix(data[i:], []byte("ok \t")) {
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: test output malformed\n", a.Package.ImportPath)
}
return false
}
j := bytes.IndexByte(data[i+len("ok \t"):], '\t')
if j < 0 {
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: test output malformed\n", a.Package.ImportPath)
}
return false
}
j += i + len("ok \t") + 1
@ -1514,12 +1610,192 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
return true
}
var errBadTestInputs = errors.New("error parsing test inputs")
var testlogMagic = []byte("# test log\n") // known to testing/internal/testdeps/deps.go
// computeTestInputsID computes the "test inputs ID"
// (see comment in tryCacheWithID above) for the
// test log.
func computeTestInputsID(a *work.Action, testlog []byte) (cache.ActionID, error) {
testlog = bytes.TrimPrefix(testlog, testlogMagic)
h := cache.NewHash("testInputs")
pwd := a.Package.Dir
for _, line := range bytes.Split(testlog, []byte("\n")) {
if len(line) == 0 {
continue
}
s := string(line)
i := strings.Index(s, " ")
if i < 0 {
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed (%q)\n", a.Package.ImportPath, line)
}
return cache.ActionID{}, errBadTestInputs
}
op := s[:i]
name := s[i+1:]
switch op {
default:
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed (%q)\n", a.Package.ImportPath, line)
}
return cache.ActionID{}, errBadTestInputs
case "getenv":
fmt.Fprintf(h, "env %s %x\n", name, hashGetenv(name))
case "chdir":
pwd = name // always absolute
fmt.Fprintf(h, "cbdir %s %x\n", name, hashStat(name))
case "stat":
if !filepath.IsAbs(name) {
name = filepath.Join(pwd, name)
}
if !inDir(name, a.Package.Root) {
// Do not recheck files outside the GOPATH or GOROOT root.
break
}
fmt.Fprintf(h, "stat %s %x\n", name, hashStat(name))
case "open":
if !filepath.IsAbs(name) {
name = filepath.Join(pwd, name)
}
if !inDir(name, a.Package.Root) {
// Do not recheck files outside the GOPATH or GOROOT root.
break
}
fh, err := hashOpen(name)
if err != nil {
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: input file %s: %s\n", a.Package.ImportPath, name, err)
}
return cache.ActionID{}, err
}
fmt.Fprintf(h, "open %s %x\n", name, fh)
}
}
sum := h.Sum()
return sum, nil
}
func inDir(path, dir string) bool {
if str.HasFilePathPrefix(path, dir) {
return true
}
xpath, err1 := filepath.EvalSymlinks(path)
xdir, err2 := filepath.EvalSymlinks(dir)
if err1 == nil && err2 == nil && str.HasFilePathPrefix(xpath, xdir) {
return true
}
return false
}
func hashGetenv(name string) cache.ActionID {
h := cache.NewHash("getenv")
v, ok := os.LookupEnv(name)
if !ok {
h.Write([]byte{0})
} else {
h.Write([]byte{1})
h.Write([]byte(v))
}
return h.Sum()
}
const modTimeCutoff = 2 * time.Second
var errFileTooNew = errors.New("file used as input is too new")
func hashOpen(name string) (cache.ActionID, error) {
h := cache.NewHash("open")
info, err := os.Stat(name)
if err != nil {
fmt.Fprintf(h, "err %v\n", err)
return h.Sum(), nil
}
hashWriteStat(h, info)
if info.IsDir() {
names, err := ioutil.ReadDir(name)
if err != nil {
fmt.Fprintf(h, "err %v\n", err)
}
for _, f := range names {
fmt.Fprintf(h, "file %s ", f.Name())
hashWriteStat(h, f)
}
} else if info.Mode().IsRegular() {
// Because files might be very large, do not attempt
// to hash the entirety of their content. Instead assume
// the mtime and size recorded in hashWriteStat above
// are good enough.
//
// To avoid problems for very recent files where a new
// write might not change the mtime due to file system
// mtime precision, reject caching if a file was read that
// is less than modTimeCutoff old.
if time.Since(info.ModTime()) < modTimeCutoff {
return cache.ActionID{}, errFileTooNew
}
}
return h.Sum(), nil
}
func hashStat(name string) cache.ActionID {
h := cache.NewHash("stat")
if info, err := os.Stat(name); err != nil {
fmt.Fprintf(h, "err %v\n", err)
} else {
hashWriteStat(h, info)
}
if info, err := os.Lstat(name); err != nil {
fmt.Fprintf(h, "err %v\n", err)
} else {
hashWriteStat(h, info)
}
return h.Sum()
}
func hashWriteStat(h io.Writer, info os.FileInfo) {
fmt.Fprintf(h, "stat %d %x %v %v\n", info.Size(), uint64(info.Mode()), info.ModTime(), info.IsDir())
}
// testAndInputKey returns the actual cache key for the pair (testID, testInputsID).
func testAndInputKey(testID, testInputsID cache.ActionID) cache.ActionID {
return cache.Subkey(testID, fmt.Sprintf("inputs:%x", testInputsID))
}
func (c *runCache) saveOutput(a *work.Action) {
if c.id1 == (cache.ActionID{}) && c.id2 == (cache.ActionID{}) {
return
}
// See comment about two-level lookup in tryCacheWithID above.
testlog, err := ioutil.ReadFile(a.Objdir + "testlog.txt")
if err != nil || !bytes.HasPrefix(testlog, testlogMagic) || testlog[len(testlog)-1] != '\n' {
if cache.DebugTest {
if err != nil {
fmt.Fprintf(os.Stderr, "testcache: %s: reading testlog: %v\n", a.Package.ImportPath, err)
} else {
fmt.Fprintf(os.Stderr, "testcache: %s: reading testlog: malformed\n", a.Package.ImportPath)
}
}
return
}
testInputsID, err := computeTestInputsID(a, testlog)
if err != nil {
return
}
if c.id1 != (cache.ActionID{}) {
cache.Default().PutNoVerify(c.id1, bytes.NewReader(a.TestOutput.Bytes()))
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: save test ID %x => input ID %x => %x\n", a.Package.ImportPath, c.id1, testInputsID, testAndInputKey(c.id1, testInputsID))
}
cache.Default().PutNoVerify(c.id1, bytes.NewReader(testlog))
cache.Default().PutNoVerify(testAndInputKey(c.id1, testInputsID), bytes.NewReader(a.TestOutput.Bytes()))
}
if c.id2 != (cache.ActionID{}) {
cache.Default().PutNoVerify(c.id2, bytes.NewReader(a.TestOutput.Bytes()))
if cache.DebugTest {
fmt.Fprintf(os.Stderr, "testcache: %s: save test ID %x => input ID %x => %x\n", a.Package.ImportPath, c.id2, testInputsID, testAndInputKey(c.id2, testInputsID))
}
cache.Default().PutNoVerify(c.id2, bytes.NewReader(testlog))
cache.Default().PutNoVerify(testAndInputKey(c.id2, testInputsID), bytes.NewReader(a.TestOutput.Bytes()))
}
}

View File

@ -137,7 +137,7 @@ func testFlags(args []string) (packageNames, passToTest []string) {
case "c", "i", "v", "cover", "json":
cmdflag.SetBool(cmd, f.BoolVar, value)
if f.Name == "json" && testJSON {
passToTest = append(passToTest, "-test.v")
passToTest = append(passToTest, "-test.v=true")
}
case "o":
testO = value

View File

@ -9,6 +9,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/load"
"cmd/go/internal/work"
"path/filepath"
)
var CmdVet = &base.Command{
@ -38,6 +39,13 @@ func runVet(cmd *base.Command, args []string) {
work.BuildInit()
work.VetFlags = vetFlags
if vetTool != "" {
var err error
work.VetTool, err = filepath.Abs(vetTool)
if err != nil {
base.Fatalf("%v", err)
}
}
pkgs := load.PackagesForBuild(pkgArgs)
if len(pkgs) == 0 {

View File

@ -55,10 +55,13 @@ var vetFlagDefn = []*cmdflag.Defn{
{Name: "unusedstringmethods"},
}
var vetTool string
// add build flags to vetFlagDefn.
func init() {
var cmd base.Command
work.AddBuildFlags(&cmd)
cmd.Flag.StringVar(&vetTool, "vettool", "", "path to vet tool binary") // for cmd/vet tests; undocumented for now
cmd.Flag.VisitAll(func(f *flag.Flag) {
vetFlagDefn = append(vetFlagDefn, &cmdflag.Defn{
Name: f.Name,
@ -87,8 +90,13 @@ func vetFlags(args []string) (passToVet, packageNames []string) {
}
switch f.Name {
// Flags known to the build but not to vet, so must be dropped.
case "x", "n":
args = append(args[:i], args[i+1:]...)
case "x", "n", "vettool":
if extraWord {
args = append(args[:i], args[i+2:]...)
extraWord = false
} else {
args = append(args[:i], args[i+1:]...)
}
i--
}
}

View File

@ -99,6 +99,10 @@ func (a *Action) BuildContentID() string { return contentID(a.buildID) }
// BuildID returns a's build ID.
func (a *Action) BuildID() string { return a.buildID }
// BuiltTarget returns the actual file that was built. This differs
// from Target when the result was cached.
func (a *Action) BuiltTarget() string { return a.built }
// An actionQueue is a priority queue of actions.
type actionQueue []*Action

View File

@ -535,6 +535,8 @@ func (b *Builder) flushOutput(a *Action) {
// a.buildID to record as the build ID in the resulting package or binary.
// updateBuildID computes the final content ID and updates the build IDs
// in the binary.
//
// Keep in sync with src/cmd/buildid/buildid.go
func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error {
if cfg.BuildX || cfg.BuildN {
if rewrite {

View File

@ -509,6 +509,7 @@ func (b *Builder) build(a *Action) (err error) {
Compiler: cfg.BuildToolchainName,
Dir: a.Package.Dir,
GoFiles: mkAbsFiles(a.Package.Dir, gofiles),
ImportPath: a.Package.ImportPath,
ImportMap: make(map[string]string),
PackageFile: make(map[string]string),
}
@ -675,10 +676,15 @@ type vetConfig struct {
GoFiles []string
ImportMap map[string]string
PackageFile map[string]string
ImportPath string
SucceedOnTypecheckFailure bool
}
// VetTool is the path to an alternate vet tool binary.
// The caller is expected to set it (if needed) before executing any vet actions.
var VetTool string
// VetFlags are the flags to pass to vet.
// The caller is expected to set them before executing any vet actions.
var VetFlags []string
@ -724,7 +730,11 @@ func (b *Builder) vet(a *Action) error {
}
p := a.Package
return b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, base.Tool("vet"), VetFlags, a.Objdir+"vet.cfg")
tool := VetTool
if tool == "" {
tool = base.Tool("vet")
}
return b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, VetFlags, a.Objdir+"vet.cfg")
}
// linkActionID computes the action ID for a link action.
@ -780,15 +790,8 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
}
fmt.Fprintf(h, "GO$GOARCH=%s\n", os.Getenv("GO"+strings.ToUpper(cfg.BuildContext.GOARCH))) // GO386, GOARM, etc
/*
// TODO(rsc): Enable this code.
// golang.org/issue/22475.
goroot := cfg.BuildContext.GOROOT
if final := os.Getenv("GOROOT_FINAL"); final != "" {
goroot = final
}
fmt.Fprintf(h, "GOROOT=%s\n", goroot)
*/
// The linker writes source file paths that say GOROOT_FINAL.
fmt.Fprintf(h, "GOROOT=%s\n", cfg.GOROOT_FINAL)
// TODO(rsc): Convince linker team not to add more magic environment variables,
// or perhaps restrict the environment variables passed to subprocesses.
@ -1845,7 +1848,7 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
// GCC and clang.
cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-")
if cfg.BuildN || cfg.BuildX {
b.Showcmd(b.WorkDir, "%s", joinUnambiguously(cmdArgs))
b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs))
if cfg.BuildN {
return false
}

View File

@ -49,7 +49,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, a
pkgpath := p.ImportPath
if cfg.BuildBuildmode == "plugin" {
pkgpath = pluginPath(a)
} else if p.Name == "main" {
} else if p.Name == "main" && !p.Internal.ForceLibrary {
pkgpath = "main"
}
gcargs := []string{"-p", pkgpath}
@ -418,11 +418,6 @@ func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string)
ldflags = append(ldflags, "-pluginpath", pluginPath(root))
}
// TODO(rsc): This is probably wrong - see golang.org/issue/22155.
if cfg.GOROOT != runtime.GOROOT() {
ldflags = append(ldflags, "-X=runtime/internal/sys.DefaultGoroot="+cfg.GOROOT)
}
// Store BuildID inside toolchain binaries as a unique identifier of the
// tool being run, for use by content-based staleness determination.
if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "cmd/") {

View File

@ -0,0 +1,3 @@
package p1
func F() int { return 1 }

View File

@ -0,0 +1,10 @@
package p1_test
import (
"coverdep2/p2"
"testing"
)
func Test(t *testing.T) {
p2.F()
}

View File

@ -0,0 +1,7 @@
package p2
import "coverdep2/p1"
func F() {
p1.F()
}

View File

@ -148,6 +148,7 @@ func splitLines(s string) []string {
}
func TestVendorGet(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("src/v/m.go", `
@ -173,8 +174,8 @@ func TestVendorGet(t *testing.T) {
tg.grepStdout("v/vendor/vendor.org/p", "import not in vendor directory")
tg.run("list", "-f", "{{.TestImports}}")
tg.grepStdout("v/vendor/vendor.org/p", "test import not in vendor directory")
tg.run("get")
tg.run("get", "-t")
tg.run("get", "-d")
tg.run("get", "-t", "-d")
}
func TestVendorGetUpdate(t *testing.T) {

View File

@ -19,6 +19,8 @@ func envOr(key, value string) string {
}
var (
defaultGOROOT string // set by linker
GOROOT = envOr("GOROOT", defaultGOROOT)
GOARCH = envOr("GOARCH", defaultGOARCH)
GOOS = envOr("GOOS", defaultGOOS)

View File

@ -65,7 +65,7 @@ func New() *List { return new(List).Init() }
// The complexity is O(1).
func (l *List) Len() int { return l.len }
// Front returns the first element of list l or nil.
// Front returns the first element of list l or nil if the list is empty.
func (l *List) Front() *Element {
if l.len == 0 {
return nil
@ -73,7 +73,7 @@ func (l *List) Front() *Element {
return l.root.next
}
// Back returns the last element of list l or nil.
// Back returns the last element of list l or nil if the list is empty.
func (l *List) Back() *Element {
if l.len == 0 {
return nil
@ -118,6 +118,7 @@ func (l *List) remove(e *Element) *Element {
// Remove removes e from l if e is an element of list l.
// It returns the element value e.Value.
// The element must not be nil.
func (l *List) Remove(e *Element) interface{} {
if e.list == l {
// if e.list == l, l must have been initialized when e was inserted
@ -141,6 +142,7 @@ func (l *List) PushBack(v interface{}) *Element {
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
if mark.list != l {
return nil
@ -151,6 +153,7 @@ func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *List) InsertAfter(v interface{}, mark *Element) *Element {
if mark.list != l {
return nil
@ -161,6 +164,7 @@ func (l *List) InsertAfter(v interface{}, mark *Element) *Element {
// MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *List) MoveToFront(e *Element) {
if e.list != l || l.root.next == e {
return
@ -171,6 +175,7 @@ func (l *List) MoveToFront(e *Element) {
// MoveToBack moves element e to the back of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *List) MoveToBack(e *Element) {
if e.list != l || l.root.prev == e {
return
@ -181,6 +186,7 @@ func (l *List) MoveToBack(e *Element) {
// MoveBefore moves element e to its new position before mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *List) MoveBefore(e, mark *Element) {
if e.list != l || e == mark || mark.list != l {
return
@ -190,6 +196,7 @@ func (l *List) MoveBefore(e, mark *Element) {
// MoveAfter moves element e to its new position after mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *List) MoveAfter(e, mark *Element) {
if e.list != l || e == mark || mark.list != l {
return
@ -198,7 +205,7 @@ func (l *List) MoveAfter(e, mark *Element) {
}
// PushBackList inserts a copy of an other list at the back of list l.
// The lists l and other may be the same.
// The lists l and other may be the same. They must not be nil.
func (l *List) PushBackList(other *List) {
l.lazyInit()
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
@ -207,7 +214,7 @@ func (l *List) PushBackList(other *List) {
}
// PushFrontList inserts a copy of an other list at the front of list l.
// The lists l and other may be the same.
// The lists l and other may be the same. They must not be nil.
func (l *List) PushFrontList(other *List) {
l.lazyInit()
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {

View File

@ -406,8 +406,9 @@ type Config struct {
//
// If normal verification fails then the handshake will abort before
// considering this callback. If normal verification is disabled by
// setting InsecureSkipVerify then this callback will be considered but
// the verifiedChains argument will always be nil.
// setting InsecureSkipVerify, or (for a server) when ClientAuth is
// RequestClientCert or RequireAnyClientCert, then this callback will
// be considered but the verifiedChains argument will always be nil.
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
// RootCAs defines the set of root certificate authorities

View File

@ -372,26 +372,34 @@ func (hs *clientHandshakeState) doFullHandshake() error {
}
}
if hs.serverHello.ocspStapling {
msg, err = c.readHandshake()
if err != nil {
return err
}
cs, ok := msg.(*certificateStatusMsg)
if !ok {
msg, err = c.readHandshake()
if err != nil {
return err
}
cs, ok := msg.(*certificateStatusMsg)
if ok {
// RFC4366 on Certificate Status Request:
// The server MAY return a "certificate_status" message.
if !hs.serverHello.ocspStapling {
// If a server returns a "CertificateStatus" message, then the
// server MUST have included an extension of type "status_request"
// with empty "extension_data" in the extended server hello.
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(cs, msg)
return errors.New("tls: received unexpected CertificateStatus message")
}
hs.finishedHash.Write(cs.marshal())
if cs.statusType == statusTypeOCSP {
c.ocspResponse = cs.response
}
}
msg, err = c.readHandshake()
if err != nil {
return err
msg, err = c.readHandshake()
if err != nil {
return err
}
}
keyAgreement := hs.suite.ka(c.vers)

View File

@ -119,7 +119,11 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
}
// We only want trusted certs.
int untrusted = 0;
if (i != 0) {
int trustAsRoot = 0;
int trustRoot = 0;
if (i == 0) {
trustAsRoot = 1;
} else {
// Certs found in the system domain are always trusted. If the user
// configures "Never Trust" on such a cert, it will also be found in the
// admin or user domain, causing it to be added to untrustedPemRoots. The
@ -129,7 +133,7 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
// SecTrustServer.c, "user trust settings overrule admin trust settings",
// so take the last trust settings array we find.
// Skip the system domain since it is always trusted.
for (int k = 1; k < numDomains; k++) {
for (int k = i; k < numDomains; k++) {
CFArrayRef domainTrustSettings = NULL;
err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings);
if (err == errSecSuccess && domainTrustSettings != NULL) {
@ -152,28 +156,35 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
// TODO: The rest of the dictionary specifies conditions for evaluation.
if (result == kSecTrustSettingsResultDeny) {
untrusted = 1;
} else if (result == kSecTrustSettingsResultTrustAsRoot) {
trustAsRoot = 1;
} else if (result == kSecTrustSettingsResultTrustRoot) {
trustRoot = 1;
}
}
}
CFRelease(trustSettings);
}
// We only want to add Root CAs, so make sure Subject and Issuer Name match
CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
if (errRef != NULL) {
CFRelease(errRef);
continue;
}
CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef);
if (errRef != NULL) {
if (trustRoot) {
// We only want to add Root CAs, so make sure Subject and Issuer Name match
CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
if (errRef != NULL) {
CFRelease(errRef);
continue;
}
CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef);
if (errRef != NULL) {
CFRelease(subjectName);
CFRelease(errRef);
continue;
}
Boolean equal = CFEqual(subjectName, issuerName);
CFRelease(subjectName);
CFRelease(errRef);
continue;
}
Boolean equal = CFEqual(subjectName, issuerName);
CFRelease(subjectName);
CFRelease(issuerName);
if (!equal) {
continue;
CFRelease(issuerName);
if (!equal) {
continue;
}
}
// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
@ -185,6 +196,9 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
}
if (data != NULL) {
if (!trustRoot && !trustAsRoot) {
untrusted = 1;
}
CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
CFRelease(data);

View File

@ -18,16 +18,18 @@ package main
import (
"bytes"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"flag"
"fmt"
"go/format"
"io/ioutil"
"log"
"math/big"
"net/http"
"os/exec"
"regexp"
"strings"
)
@ -41,7 +43,7 @@ func main() {
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "// Created by root_darwin_arm_gen --output %s; DO NOT EDIT\n", *output)
fmt.Fprintf(buf, "// Code generated by root_darwin_arm_gen --output %s; DO NOT EDIT.\n", *output)
fmt.Fprintf(buf, "%s", header)
fmt.Fprintf(buf, "const systemRootsPEM = `\n")
@ -78,36 +80,22 @@ func selectCerts() ([]*x509.Certificate, error) {
var certs []*x509.Certificate
for _, id := range ids {
sn, ok := big.NewInt(0).SetString(id.serialNumber, 0) // 0x prefix selects hex
if !ok {
return nil, fmt.Errorf("invalid serial number: %q", id.serialNumber)
}
ski, ok := big.NewInt(0).SetString(id.subjectKeyID, 0)
if !ok {
return nil, fmt.Errorf("invalid Subject Key ID: %q", id.subjectKeyID)
}
for _, cert := range scerts {
if sn.Cmp(cert.SerialNumber) != 0 {
continue
}
cski := big.NewInt(0).SetBytes(cert.SubjectKeyId)
if ski.Cmp(cski) != 0 {
continue
}
certs = append(certs, cert)
break
if c, ok := scerts[id.fingerprint]; ok {
certs = append(certs, c)
} else {
fmt.Printf("WARNING: cannot find certificate: %s (fingerprint: %s)\n", id.name, id.fingerprint)
}
}
return certs, nil
}
func sysCerts() (certs []*x509.Certificate, err error) {
func sysCerts() (certs map[string]*x509.Certificate, err error) {
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
data, err := cmd.Output()
if err != nil {
return nil, err
}
certs = make(map[string]*x509.Certificate)
for len(data) > 0 {
var block *pem.Block
block, data = pem.Decode(data)
@ -122,19 +110,23 @@ func sysCerts() (certs []*x509.Certificate, err error) {
if err != nil {
continue
}
certs = append(certs, cert)
fingerprint := sha256.Sum256(cert.Raw)
certs[hex.EncodeToString(fingerprint[:])] = cert
}
return certs, nil
}
type certID struct {
serialNumber string
subjectKeyID string
name string
fingerprint string
}
// fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
func fetchCertIDs() ([]certID, error) {
resp, err := http.Get("https://support.apple.com/en-us/HT204132")
// Download the iOS 11 support page. The index for all iOS versions is here:
// https://support.apple.com/en-us/HT204132
resp, err := http.Get("https://support.apple.com/en-us/HT208125")
if err != nil {
return nil, err
}
@ -144,31 +136,33 @@ func fetchCertIDs() ([]certID, error) {
return nil, err
}
text := string(body)
text = text[strings.Index(text, "<section id=trusted"):]
text = text[:strings.Index(text, "</section>")]
text = text[strings.Index(text, "<div id=trusted"):]
text = text[:strings.Index(text, "</div>")]
lines := strings.Split(text, "\n")
var ids []certID
var id certID
for i, ln := range lines {
if i == len(lines)-1 {
break
}
const sn = "Serial Number:"
if ln == sn {
id.serialNumber = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
cols := make(map[string]int)
for i, rowmatch := range regexp.MustCompile("(?s)<tr>(.*?)</tr>").FindAllStringSubmatch(text, -1) {
row := rowmatch[1]
if i == 0 {
// Parse table header row to extract column names
for i, match := range regexp.MustCompile("(?s)<th>(.*?)</th>").FindAllStringSubmatch(row, -1) {
cols[match[1]] = i
}
continue
}
if strings.HasPrefix(ln, sn) {
// extract hex value from parentheses.
id.serialNumber = ln[strings.Index(ln, "(")+1 : len(ln)-1]
continue
}
if strings.TrimSpace(ln) == "X509v3 Subject Key Identifier:" {
id.subjectKeyID = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
ids = append(ids, id)
id = certID{}
}
values := regexp.MustCompile("(?s)<td>(.*?)</td>").FindAllStringSubmatch(row, -1)
name := values[cols["Certificate name"]][1]
fingerprint := values[cols["Fingerprint (SHA-256)"]][1]
fingerprint = strings.Replace(fingerprint, "<br>", "", -1)
fingerprint = strings.Replace(fingerprint, "\n", "", -1)
fingerprint = strings.Replace(fingerprint, " ", "", -1)
fingerprint = strings.ToLower(fingerprint)
ids = append(ids, certID{
name: name,
fingerprint: fingerprint,
})
}
return ids, nil
}
@ -180,7 +174,7 @@ const header = `
// +build cgo
// +build darwin
// +build arm arm64
// +build arm arm64 ios
package x509

File diff suppressed because it is too large Load Diff

View File

@ -984,7 +984,7 @@ type distributionPoint struct {
}
type distributionPointName struct {
FullName asn1.RawValue `asn1:"optional,tag:0"`
FullName []asn1.RawValue `asn1:"optional,tag:0"`
RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
}
@ -1466,20 +1466,14 @@ func parseCertificate(in *certificate) (*Certificate, error) {
for _, dp := range cdp {
// Per RFC 5280, 4.2.1.13, one of distributionPoint or cRLIssuer may be empty.
if len(dp.DistributionPoint.FullName.Bytes) == 0 {
if len(dp.DistributionPoint.FullName) == 0 {
continue
}
var n asn1.RawValue
if _, err := asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n); err != nil {
return nil, err
}
// Trailing data after the fullName is
// allowed because other elements of
// the SEQUENCE can appear.
if n.Tag == 6 {
out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(n.Bytes))
for _, fullName := range dp.DistributionPoint.FullName {
if fullName.Tag == 6 {
out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(fullName.Bytes))
}
}
}
@ -1946,11 +1940,11 @@ func buildExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId
var crlDp []distributionPoint
for _, name := range template.CRLDistributionPoints {
rawFullName, _ := asn1.Marshal(asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)})
dp := distributionPoint{
DistributionPoint: distributionPointName{
FullName: asn1.RawValue{Tag: 0, Class: 2, IsCompound: true, Bytes: rawFullName},
FullName: []asn1.RawValue{
asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)},
},
},
}
crlDp = append(crlDp, dp)

View File

@ -1891,3 +1891,58 @@ func TestEmptySubject(t *testing.T) {
t.Fatal("SAN extension is missing")
}
// multipleURLsInCRLDPPEM contains two URLs in a single CRL DistributionPoint
// structure. It is taken from https://crt.sh/?id=12721534.
const multipleURLsInCRLDPPEM = `
-----BEGIN CERTIFICATE-----
MIIF4TCCBMmgAwIBAgIQc+6uFePfrahUGpXs8lhiTzANBgkqhkiG9w0BAQsFADCB
8zELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2Vy
dGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1
YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3
dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UECxMsSmVyYXJxdWlh
IEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMTBkVD
LUFDQzAeFw0xNDA5MTgwODIxMDBaFw0zMDA5MTgwODIxMDBaMIGGMQswCQYDVQQG
EwJFUzEzMDEGA1UECgwqQ09OU09SQ0kgQURNSU5JU1RSQUNJTyBPQkVSVEEgREUg
Q0FUQUxVTllBMSowKAYDVQQLDCFTZXJ2ZWlzIFDDumJsaWNzIGRlIENlcnRpZmlj
YWNpw7MxFjAUBgNVBAMMDUVDLUNpdXRhZGFuaWEwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQDFkHPRZPZlXTWZ5psJhbS/Gx+bxcTpGrlVQHHtIkgGz77y
TA7UZUFb2EQMncfbOhR0OkvQQn1aMvhObFJSR6nI+caf2D+h/m/InMl1MyH3S0Ak
YGZZsthnyC6KxqK2A/NApncrOreh70ULkQs45aOKsi1kR1W0zE+iFN+/P19P7AkL
Rl3bXBCVd8w+DLhcwRrkf1FCDw6cEqaFm3cGgf5cbBDMaVYAweWTxwBZAq2RbQAW
jE7mledcYghcZa4U6bUmCBPuLOnO8KMFAvH+aRzaf3ws5/ZoOVmryyLLJVZ54peZ
OwnP9EL4OuWzmXCjBifXR2IAblxs5JYj57tls45nAgMBAAGjggHaMIIB1jASBgNV
HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUC2hZPofI
oxUa4ECCIl+fHbLFNxUwHwYDVR0jBBgwFoAUoMOLRKo3pUW/l4Ba0fF4opvpXY0w
gdYGA1UdIASBzjCByzCByAYEVR0gADCBvzAxBggrBgEFBQcCARYlaHR0cHM6Ly93
d3cuYW9jLmNhdC9DQVRDZXJ0L1JlZ3VsYWNpbzCBiQYIKwYBBQUHAgIwfQx7QXF1
ZXN0IGNlcnRpZmljYXQgw6lzIGVtw6hzIMO6bmljYSBpIGV4Y2x1c2l2YW1lbnQg
YSBFbnRpdGF0cyBkZSBDZXJ0aWZpY2FjacOzLiBWZWdldSBodHRwczovL3d3dy5h
b2MuY2F0L0NBVENlcnQvUmVndWxhY2lvMDMGCCsGAQUFBwEBBCcwJTAjBggrBgEF
BQcwAYYXaHR0cDovL29jc3AuY2F0Y2VydC5jYXQwYgYDVR0fBFswWTBXoFWgU4Yn
aHR0cDovL2Vwc2NkLmNhdGNlcnQubmV0L2NybC9lYy1hY2MuY3JshihodHRwOi8v
ZXBzY2QyLmNhdGNlcnQubmV0L2NybC9lYy1hY2MuY3JsMA0GCSqGSIb3DQEBCwUA
A4IBAQChqFTjlAH5PyIhLjLgEs68CyNNC1+vDuZXRhy22TI83JcvGmQrZosPvVIL
PsUXx+C06Pfqmh48Q9S89X9K8w1SdJxP/rZeGEoRiKpwvQzM4ArD9QxyC8jirxex
3Umg9Ai/sXQ+1lBf6xw4HfUUr1WIp7pNHj0ZWLo106urqktcdeAFWme+/klis5fu
labCSVPuT/QpwakPrtqOhRms8vgpKiXa/eLtL9ZiA28X/Mker0zlAeTA7Z7uAnp6
oPJTlZu1Gg1ZDJueTWWsLlO+P+Wzm3MRRIbcgdRzm4mdO7ubu26SzX/aQXDhuih+
eVxXDTCfs7GUlxnjOp5j559X/N0A
-----END CERTIFICATE-----
`
func TestMultipleURLsInCRLDP(t *testing.T) {
block, _ := pem.Decode([]byte(multipleURLsInCRLDPPEM))
cert, err := ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("failed to parse certificate: %s", err)
}
want := []string{
"http://epscd.catcert.net/crl/ec-acc.crl",
"http://epscd2.catcert.net/crl/ec-acc.crl",
}
if got := cert.CRLDistributionPoints; !reflect.DeepEqual(got, want) {
t.Errorf("CRL distribution points = %#v, want #%v", got, want)
}
}

View File

@ -15,7 +15,8 @@ import (
)
// Value is a value that drivers must be able to handle.
// It is either nil or an instance of one of these types:
// It is either nil, a type handled by a database driver's NamedValueChecker
// interface, or an instance of one of these types:
//
// int64
// float64

View File

@ -2055,14 +2055,14 @@ func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt {
stmt.mu.Unlock()
if si == nil {
var ds *driverStmt
withLock(dc, func() {
var ds *driverStmt
ds, err = stmt.prepareOnConnLocked(ctx, dc)
si = ds.si
})
if err != nil {
return &Stmt{stickyErr: err}
}
si = ds.si
}
parentStmt = stmt
}

View File

@ -1038,6 +1038,8 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[re
// typeString returns a human-readable description of the type identified by remoteId.
func (dec *Decoder) typeString(remoteId typeId) string {
typeLock.Lock()
defer typeLock.Unlock()
if t := idToType[remoteId]; t != nil {
// globally known type.
return t.string()

View File

@ -7,6 +7,7 @@ package gob
import (
"bytes"
"reflect"
"sync"
"testing"
)
@ -218,3 +219,44 @@ func TestStressParallel(t *testing.T) {
<-c
}
}
// Issue 23328. Note that this test name is known to cmd/dist/test.go.
func TestTypeRace(t *testing.T) {
c := make(chan bool)
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
var buf bytes.Buffer
enc := NewEncoder(&buf)
dec := NewDecoder(&buf)
var x interface{}
switch i {
case 0:
x = &N1{}
case 1:
x = &N2{}
default:
t.Errorf("bad i %d", i)
return
}
m := make(map[string]string)
<-c
if err := enc.Encode(x); err != nil {
t.Error(err)
return
}
if err := enc.Encode(x); err != nil {
t.Error(err)
return
}
if err := dec.Decode(&m); err == nil {
t.Error("decode unexpectedly succeeded")
return
}
}(i)
}
close(c)
wg.Wait()
}

View File

@ -583,16 +583,6 @@ var marshalTests = []struct {
ExpectXML: `<PresenceTest></PresenceTest>`,
},
// A pointer to struct{} may be used to test for an element's presence.
{
Value: &PresenceTest{new(struct{})},
ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
},
{
Value: &PresenceTest{},
ExpectXML: `<PresenceTest></PresenceTest>`,
},
// A []byte field is only nil if the element was not found.
{
Value: &Data{},

View File

@ -154,12 +154,13 @@ var pkgDeps = map[string][]string{
"syscall",
},
"internal/poll": {"L0", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8", "internal/syscall/windows"},
"os": {"L1", "os", "syscall", "time", "internal/poll", "internal/syscall/windows"},
"path/filepath": {"L2", "os", "syscall", "internal/syscall/windows"},
"io/ioutil": {"L2", "os", "path/filepath", "time"},
"os/exec": {"L2", "os", "context", "path/filepath", "syscall"},
"os/signal": {"L2", "os", "syscall"},
"internal/poll": {"L0", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8", "internal/syscall/windows"},
"internal/testlog": {"L0"},
"os": {"L1", "os", "syscall", "time", "internal/poll", "internal/syscall/windows", "internal/testlog"},
"path/filepath": {"L2", "os", "syscall", "internal/syscall/windows"},
"io/ioutil": {"L2", "os", "path/filepath", "time"},
"os/exec": {"L2", "os", "context", "path/filepath", "syscall"},
"os/signal": {"L2", "os", "syscall"},
// OS enables basic operating system functionality,
// but not direct use of package syscall, nor os/signal.
@ -271,7 +272,7 @@ var pkgDeps = map[string][]string{
"net/url": {"L4"},
"plugin": {"L0", "OS", "CGO"},
"runtime/pprof/internal/profile": {"L4", "OS", "compress/gzip", "regexp"},
"testing/internal/testdeps": {"L4", "runtime/pprof", "regexp"},
"testing/internal/testdeps": {"L4", "internal/testlog", "runtime/pprof", "regexp"},
"text/scanner": {"L4", "OS"},
"text/template/parse": {"L4"},

View File

@ -18,6 +18,8 @@ import (
"math"
"math/big"
"strconv"
"strings"
"sync"
"unicode/utf8"
)
@ -67,7 +69,12 @@ const prec = 512
type (
unknownVal struct{}
boolVal bool
stringVal string
stringVal struct {
// Lazy value: either a string (l,r==nil) or an addition (l,r!=nil).
mu sync.Mutex
s string
l, r *stringVal
}
int64Val int64 // Int values representable as an int64
intVal struct{ val *big.Int } // Int values not representable as an int64
ratVal struct{ val *big.Rat } // Float values representable as a fraction
@ -77,7 +84,7 @@ type (
func (unknownVal) Kind() Kind { return Unknown }
func (boolVal) Kind() Kind { return Bool }
func (stringVal) Kind() Kind { return String }
func (*stringVal) Kind() Kind { return String }
func (int64Val) Kind() Kind { return Int }
func (intVal) Kind() Kind { return Int }
func (ratVal) Kind() Kind { return Float }
@ -88,9 +95,9 @@ func (unknownVal) String() string { return "unknown" }
func (x boolVal) String() string { return strconv.FormatBool(bool(x)) }
// String returns a possibly shortened quoted form of the String value.
func (x stringVal) String() string {
func (x *stringVal) String() string {
const maxLen = 72 // a reasonable length
s := strconv.Quote(string(x))
s := strconv.Quote(x.string())
if utf8.RuneCountInString(s) > maxLen {
// The string without the enclosing quotes is greater than maxLen-2 runes
// long. Remove the last 3 runes (including the closing '"') by keeping
@ -105,6 +112,60 @@ func (x stringVal) String() string {
return s
}
// string constructs and returns the actual string literal value.
// If x represents an addition, then it rewrites x to be a single
// string, to speed future calls. This lazy construction avoids
// building different string values for all subpieces of a large
// concatenation. See golang.org/issue/23348.
func (x *stringVal) string() string {
x.mu.Lock()
if x.l != nil {
x.s = strings.Join(reverse(x.appendReverse(nil)), "")
x.l = nil
x.r = nil
}
s := x.s
x.mu.Unlock()
return s
}
// reverse reverses x in place and returns it.
func reverse(x []string) []string {
n := len(x)
for i := 0; i+i < n; i++ {
x[i], x[n-1-i] = x[n-1-i], x[i]
}
return x
}
// appendReverse appends to list all of x's subpieces, but in reverse,
// and returns the result. Appending the reversal allows processing
// the right side in a recursive call and the left side in a loop.
// Because a chain like a + b + c + d + e is actually represented
// as ((((a + b) + c) + d) + e), the left-side loop avoids deep recursion.
// x must be locked.
func (x *stringVal) appendReverse(list []string) []string {
y := x
for y.r != nil {
y.r.mu.Lock()
list = y.r.appendReverse(list)
y.r.mu.Unlock()
l := y.l
if y != x {
y.mu.Unlock()
}
l.mu.Lock()
y = l
}
s := y.s
if y != x {
y.mu.Unlock()
}
return append(list, s)
}
func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) }
func (x intVal) String() string { return x.val.String() }
func (x ratVal) String() string { return rtof(x).String() }
@ -160,7 +221,7 @@ func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.i
func (x unknownVal) ExactString() string { return x.String() }
func (x boolVal) ExactString() string { return x.String() }
func (x stringVal) ExactString() string { return strconv.Quote(string(x)) }
func (x *stringVal) ExactString() string { return strconv.Quote(x.string()) }
func (x int64Val) ExactString() string { return x.String() }
func (x intVal) ExactString() string { return x.String() }
@ -180,7 +241,7 @@ func (x complexVal) ExactString() string {
func (unknownVal) implementsValue() {}
func (boolVal) implementsValue() {}
func (stringVal) implementsValue() {}
func (*stringVal) implementsValue() {}
func (int64Val) implementsValue() {}
func (ratVal) implementsValue() {}
func (intVal) implementsValue() {}
@ -283,7 +344,7 @@ func MakeUnknown() Value { return unknownVal{} }
func MakeBool(b bool) Value { return boolVal(b) }
// MakeString returns the String value for s.
func MakeString(s string) Value { return stringVal(s) }
func MakeString(s string) Value { return &stringVal{s: s} }
// MakeInt64 returns the Int value for x.
func MakeInt64(x int64) Value { return int64Val(x) }
@ -382,8 +443,8 @@ func BoolVal(x Value) bool {
// If x is Unknown, the result is "".
func StringVal(x Value) string {
switch x := x.(type) {
case stringVal:
return string(x)
case *stringVal:
return x.string()
case unknownVal:
return ""
default:
@ -856,7 +917,7 @@ func ord(x Value) int {
return -1
case unknownVal:
return 0
case boolVal, stringVal:
case boolVal, *stringVal:
return 1
case int64Val:
return 2
@ -884,7 +945,7 @@ func match(x, y Value) (_, _ Value) {
// ord(x) <= ord(y)
switch x := x.(type) {
case boolVal, stringVal, complexVal:
case boolVal, *stringVal, complexVal:
return x, y
case int64Val:
@ -1108,9 +1169,9 @@ func BinaryOp(x_ Value, op token.Token, y_ Value) Value {
}
return makeComplex(re, im)
case stringVal:
case *stringVal:
if op == token.ADD {
return x + y.(stringVal)
return &stringVal{l: x, r: y.(*stringVal)}
}
}
@ -1236,21 +1297,22 @@ func Compare(x_ Value, op token.Token, y_ Value) bool {
return !re || !im
}
case stringVal:
y := y.(stringVal)
case *stringVal:
xs := x.string()
ys := y.(*stringVal).string()
switch op {
case token.EQL:
return x == y
return xs == ys
case token.NEQ:
return x != y
return xs != ys
case token.LSS:
return x < y
return xs < ys
case token.LEQ:
return x <= y
return xs <= ys
case token.GTR:
return x > y
return xs > ys
case token.GEQ:
return x >= y
return xs >= ys
}
}

View File

@ -5,6 +5,7 @@
package constant
import (
"fmt"
"go/token"
"strings"
"testing"
@ -449,3 +450,23 @@ func TestUnknown(t *testing.T) {
}
}
}
func BenchmarkStringAdd(b *testing.B) {
for size := 1; size <= 65536; size *= 4 {
b.Run(fmt.Sprint(size), func(b *testing.B) {
b.ReportAllocs()
n := int64(0)
for i := 0; i < b.N; i++ {
x := MakeString(strings.Repeat("x", 100))
y := x
for j := 0; j < size-1; j++ {
y = BinaryOp(y, token.ADD, x)
}
n += int64(len(StringVal(y)))
}
if n != int64(b.N)*int64(size)*100 {
b.Fatalf("bad string %d != %d", n, int64(b.N)*int64(size)*100)
}
})
}
}

View File

@ -17,7 +17,7 @@ func TestFor(t *testing.T) {
testenv.MustHaveGoBuild(t)
const thePackage = "math/big"
out, err := exec.Command("go", "list", "-f={{context.Compiler}}:{{.Target}}", thePackage).CombinedOutput()
out, err := exec.Command(testenv.GoToolPath(t), "list", "-f={{context.Compiler}}:{{.Target}}", thePackage).CombinedOutput()
if err != nil {
t.Fatalf("go list %s: %v\n%s", thePackage, err, out)
}

View File

@ -102,6 +102,7 @@ var importerTests = [...]importerTest{
{pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"},
{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
{pkgpath: "alias", name: "IntAlias2", want: "type IntAlias2 = Int"},
{pkgpath: "escapeinfo", name: "NewT", want: "func NewT(data []byte) *T"},
}
func TestGoxImporter(t *testing.T) {

View File

@ -226,6 +226,14 @@ func (p *parser) parseField(pkg *types.Package) (field *types.Var, tag string) {
// Param = Name ["..."] Type .
func (p *parser) parseParam(pkg *types.Package) (param *types.Var, isVariadic bool) {
name := p.parseName()
if p.tok == '<' && p.scanner.Peek() == 'e' {
// EscInfo = "<esc:" int ">" . (optional and ignored)
p.next()
p.expectKeyword("esc")
p.expect(':')
p.expect(scanner.Int)
p.expect('>')
}
if p.tok == '.' {
p.next()
p.expect('.')

View File

@ -0,0 +1,4 @@
v1;
package alias;
pkgpath alias;
type <type 115 "I1" <type 116 interface { M1 (? <type 117 "IntAlias2" = <type 118 "IntAlias" = <type 119 "Int" <type -11>>>>) < type 114>; M2 () <type 1>; }>>;

View File

@ -0,0 +1,13 @@
// Test case for escape info in export data. To compile and extract .gox file:
// gccgo -fgo-optimize-allocs -c escapeinfo.go
// objcopy -j .go_export escapeinfo.o escapeinfo.gox
package escapeinfo
type T struct{ data []byte }
func NewT(data []byte) *T {
return &T{data}
}
func (*T) Read(p []byte) {}

Binary file not shown.

View File

@ -42,7 +42,7 @@ import (
)
var (
listErrors = flag.Bool("list", false, "list errors")
listErrors = flag.Bool("errlist", false, "list errors")
testFiles = flag.String("files", "", "space-separated list of test files")
)

View File

@ -1194,17 +1194,18 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
if x.mode == constant_ {
duplicate := false
// if the key is of interface type, the type is also significant when checking for duplicates
xkey := keyVal(x.val)
if _, ok := utyp.key.Underlying().(*Interface); ok {
for _, vtyp := range visited[x.val] {
for _, vtyp := range visited[xkey] {
if Identical(vtyp, x.typ) {
duplicate = true
break
}
}
visited[x.val] = append(visited[x.val], x.typ)
visited[xkey] = append(visited[xkey], x.typ)
} else {
_, duplicate = visited[x.val]
visited[x.val] = nil
_, duplicate = visited[xkey]
visited[xkey] = nil
}
if duplicate {
check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
@ -1508,6 +1509,30 @@ Error:
return statement // avoid follow-up errors
}
func keyVal(x constant.Value) interface{} {
switch x.Kind() {
case constant.Bool:
return constant.BoolVal(x)
case constant.String:
return constant.StringVal(x)
case constant.Int:
if v, ok := constant.Int64Val(x); ok {
return v
}
if v, ok := constant.Uint64Val(x); ok {
return v
}
case constant.Float:
v, _ := constant.Float64Val(x)
return v
case constant.Complex:
r, _ := constant.Float64Val(constant.Real(x))
i, _ := constant.Float64Val(constant.Imag(x))
return complex(r, i)
}
return x
}
// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type) {
method, wrongType := assertableTo(xtyp, T)

View File

@ -417,9 +417,9 @@ func (check *Checker) collectObjects() {
// receiver name. They will be type-checked later, with regular
// functions.
if list := d.Recv.List; len(list) > 0 {
typ := list[0].Type
typ := unparen(list[0].Type)
if ptr, _ := typ.(*ast.StarExpr); ptr != nil {
typ = ptr.X
typ = unparen(ptr.X)
}
if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" {
check.assocMethod(base.Name, obj)

View File

@ -63,3 +63,13 @@ func ((*T7)) m3() {}
func (x *(T7),) m4() {}
func (x (*(T7)),) m5() {}
func (x ((*((T7)))),) m6() {}
// Check that methods with parenthesized receiver are actually present (issue #23130).
var (
_ = T7.m1
_ = T7.m2
_ = (*T7).m3
_ = (*T7).m4
_ = (*T7).m5
_ = (*T7).m6
)

View File

@ -367,6 +367,10 @@ func map_literals() {
_ = map[interface{}]int{"a": 1, "a" /* ERROR "duplicate key" */ : 1}
_ = map[interface{}]int{"a": 1, S("a"): 1}
_ = map[interface{}]int{S("a"): 1, S /* ERROR "duplicate key" */ ("a"): 1}
_ = map[interface{}]int{1.0: 1, 1.0 /* ERROR "duplicate key" */: 1}
_ = map[interface{}]int{int64(-1): 1, int64 /* ERROR "duplicate key" */ (-1) : 1}
_ = map[interface{}]int{^uint64(0): 1, ^ /* ERROR "duplicate key" */ uint64(0): 1}
_ = map[interface{}]int{complex(1,2): 1, complex /* ERROR "duplicate key" */ (1,2) : 1}
type I interface {
f()

View File

@ -418,7 +418,6 @@ func (t *Named) NumMethods() int { return len(t.methods) }
func (t *Named) Method(i int) *Func { return t.methods[i] }
// SetUnderlying sets the underlying type and marks t as complete.
// TODO(gri) determine if there's a better solution rather than providing this function
func (t *Named) SetUnderlying(underlying Type) {
if underlying == nil {
panic("types.Named.SetUnderlying: underlying type must not be nil")
@ -430,7 +429,6 @@ func (t *Named) SetUnderlying(underlying Type) {
}
// AddMethod adds method m unless it is already in the method list.
// TODO(gri) find a better solution instead of providing this function
func (t *Named) AddMethod(m *Func) {
if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
t.methods = append(t.methods, m)

View File

@ -20,6 +20,12 @@ var (
universeRune *Basic // int32 alias, but has name "rune"
)
// Typ contains the predeclared *Basic types indexed by their
// corresponding BasicKind.
//
// The *Basic type for Typ[Byte] will have the name "uint8".
// Use Universe.Lookup("byte").Type() to obtain the specific
// alias basic type named "byte" (and analogous for "rune").
var Typ = []*Basic{
Invalid: {Invalid, 0, "invalid type"},

View File

@ -120,6 +120,7 @@ var attrTypeMap = map[string]contentType{
"src": contentTypeURL,
"srcdoc": contentTypeHTML,
"srclang": contentTypePlain,
"srcset": contentTypeSrcset,
"start": contentTypePlain,
"step": contentTypePlain,
"style": contentTypeCSS,

View File

@ -83,6 +83,14 @@ type (
// the encapsulated content should come from a trusted source,
// as it will be included verbatim in the template output.
URL string
// Srcset encapsulates a known safe srcset attribute
// (see http://w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset).
//
// Use of this type presents a security risk:
// the encapsulated content should come from a trusted source,
// as it will be included verbatim in the template output.
Srcset string
)
type contentType uint8
@ -95,6 +103,7 @@ const (
contentTypeJS
contentTypeJSStr
contentTypeURL
contentTypeSrcset
// contentTypeUnsafe is used in attr.go for values that affect how
// embedded content and network messages are formed, vetted,
// or interpreted; or which credentials network messages carry.
@ -156,6 +165,8 @@ func stringify(args ...interface{}) (string, contentType) {
return string(s), contentTypeJSStr
case URL:
return string(s), contentTypeURL
case Srcset:
return string(s), contentTypeSrcset
}
}
for i, arg := range args {

View File

@ -19,7 +19,9 @@ func TestTypedContent(t *testing.T) {
HTMLAttr(` dir="ltr"`),
JS(`c && alert("Hello, World!");`),
JSStr(`Hello, World & O'Reilly\x21`),
URL(`greeting=H%69&addressee=(World)`),
URL(`greeting=H%69,&addressee=(World)`),
Srcset(`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`),
URL(`,foo/,`),
}
// For each content sensitive escaper, see how it does on
@ -40,6 +42,8 @@ func TestTypedContent(t *testing.T) {
`ZgotmplZ`,
`ZgotmplZ`,
`ZgotmplZ`,
`ZgotmplZ`,
`ZgotmplZ`,
},
},
{
@ -53,6 +57,8 @@ func TestTypedContent(t *testing.T) {
`ZgotmplZ`,
`ZgotmplZ`,
`ZgotmplZ`,
`ZgotmplZ`,
`ZgotmplZ`,
},
},
{
@ -65,7 +71,9 @@ func TestTypedContent(t *testing.T) {
` dir=&#34;ltr&#34;`,
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
`Hello, World &amp; O&#39;Reilly\x21`,
`greeting=H%69&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
},
},
{
@ -79,6 +87,8 @@ func TestTypedContent(t *testing.T) {
`ZgotmplZ`,
`ZgotmplZ`,
`ZgotmplZ`,
`ZgotmplZ`,
`ZgotmplZ`,
},
},
{
@ -91,7 +101,9 @@ func TestTypedContent(t *testing.T) {
`&#32;dir&#61;&#34;ltr&#34;`,
`c&#32;&amp;&amp;&#32;alert(&#34;Hello,&#32;World!&#34;);`,
`Hello,&#32;World&#32;&amp;&#32;O&#39;Reilly\x21`,
`greeting&#61;H%69&amp;addressee&#61;(World)`,
`greeting&#61;H%69,&amp;addressee&#61;(World)`,
`greeting&#61;H%69,&amp;addressee&#61;(World)&#32;2x,&#32;https://golang.org/favicon.ico&#32;500.5w`,
`,foo/,`,
},
},
{
@ -104,7 +116,9 @@ func TestTypedContent(t *testing.T) {
` dir=&#34;ltr&#34;`,
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
`Hello, World &amp; O&#39;Reilly\x21`,
`greeting=H%69&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
},
},
{
@ -117,7 +131,9 @@ func TestTypedContent(t *testing.T) {
` dir=&#34;ltr&#34;`,
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
`Hello, World &amp; O&#39;Reilly\x21`,
`greeting=H%69&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
},
},
{
@ -131,7 +147,9 @@ func TestTypedContent(t *testing.T) {
`c && alert("Hello, World!");`,
// Escape sequence not over-escaped.
`"Hello, World & O'Reilly\x21"`,
`"greeting=H%69\u0026addressee=(World)"`,
`"greeting=H%69,\u0026addressee=(World)"`,
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
`",foo/,"`,
},
},
{
@ -145,7 +163,9 @@ func TestTypedContent(t *testing.T) {
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
// Escape sequence not over-escaped.
`&#34;Hello, World &amp; O&#39;Reilly\x21&#34;`,
`&#34;greeting=H%69\u0026addressee=(World)&#34;`,
`&#34;greeting=H%69,\u0026addressee=(World)&#34;`,
`&#34;greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w&#34;`,
`&#34;,foo/,&#34;`,
},
},
{
@ -158,7 +178,9 @@ func TestTypedContent(t *testing.T) {
`c \x26\x26 alert(\x22Hello, World!\x22);`,
// Escape sequence not over-escaped.
`Hello, World \x26 O\x27Reilly\x21`,
`greeting=H%69\x26addressee=(World)`,
`greeting=H%69,\x26addressee=(World)`,
`greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
`,foo\/,`,
},
},
{
@ -171,7 +193,9 @@ func TestTypedContent(t *testing.T) {
`c \x26\x26 alert(\x22Hello, World!\x22);`,
// Escape sequence not over-escaped.
`Hello, World \x26 O\x27Reilly\x21`,
`greeting=H%69\x26addressee=(World)`,
`greeting=H%69,\x26addressee=(World)`,
`greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
`,foo\/,`,
},
},
{
@ -185,7 +209,9 @@ func TestTypedContent(t *testing.T) {
`c && alert("Hello, World!");`,
// Escape sequence not over-escaped.
`"Hello, World & O'Reilly\x21"`,
`"greeting=H%69\u0026addressee=(World)"`,
`"greeting=H%69,\u0026addressee=(World)"`,
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
`",foo/,"`,
},
},
{
@ -199,7 +225,9 @@ func TestTypedContent(t *testing.T) {
` dir=&#34;ltr&#34;`,
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
`Hello, World &amp; O&#39;Reilly\x21`,
`greeting=H%69&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
},
},
{
@ -212,7 +240,9 @@ func TestTypedContent(t *testing.T) {
`c \x26\x26 alert(\x22Hello, World!\x22);`,
// Escape sequence not over-escaped.
`Hello, World \x26 O\x27Reilly\x21`,
`greeting=H%69\x26addressee=(World)`,
`greeting=H%69,\x26addressee=(World)`,
`greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
`,foo\/,`,
},
},
{
@ -225,7 +255,9 @@ func TestTypedContent(t *testing.T) {
`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
`Hello%2c%20World%20%26%20O%27Reilly%5cx21`,
// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done.
`greeting=H%69&amp;addressee=%28World%29`,
`greeting=H%69,&amp;addressee=%28World%29`,
`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
`,foo/,`,
},
},
{
@ -238,7 +270,113 @@ func TestTypedContent(t *testing.T) {
`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
`Hello%2c%20World%20%26%20O%27Reilly%5cx21`,
// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done.
`greeting=H%69&addressee=%28World%29`,
`greeting=H%69,&addressee=%28World%29`,
`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
`,foo/,`,
},
},
{
`<img srcset="{{.}}">`,
[]string{
`#ZgotmplZ`,
`#ZgotmplZ`,
// Commas are not esacped
`Hello,#ZgotmplZ`,
// Leading spaces are not percent escapes.
` dir=%22ltr%22`,
// Spaces after commas are not percent escaped.
`#ZgotmplZ, World!%22%29;`,
`Hello,#ZgotmplZ`,
`greeting=H%69%2c&amp;addressee=%28World%29`,
// Metadata is not escaped.
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`%2cfoo/%2c`,
},
},
{
`<img srcset={{.}}>`,
[]string{
`#ZgotmplZ`,
`#ZgotmplZ`,
`Hello,#ZgotmplZ`,
// Spaces are HTML escaped not %-escaped
`&#32;dir&#61;%22ltr%22`,
`#ZgotmplZ,&#32;World!%22%29;`,
`Hello,#ZgotmplZ`,
`greeting&#61;H%69%2c&amp;addressee&#61;%28World%29`,
`greeting&#61;H%69,&amp;addressee&#61;(World)&#32;2x,&#32;https://golang.org/favicon.ico&#32;500.5w`,
// Commas are escaped.
`%2cfoo/%2c`,
},
},
{
`<img srcset="{{.}} 2x, https://golang.org/ 500.5w">`,
[]string{
`#ZgotmplZ`,
`#ZgotmplZ`,
`Hello,#ZgotmplZ`,
` dir=%22ltr%22`,
`#ZgotmplZ, World!%22%29;`,
`Hello,#ZgotmplZ`,
`greeting=H%69%2c&amp;addressee=%28World%29`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`%2cfoo/%2c`,
},
},
{
`<img srcset="http://godoc.org/ {{.}}, https://golang.org/ 500.5w">`,
[]string{
`#ZgotmplZ`,
`#ZgotmplZ`,
`Hello,#ZgotmplZ`,
` dir=%22ltr%22`,
`#ZgotmplZ, World!%22%29;`,
`Hello,#ZgotmplZ`,
`greeting=H%69%2c&amp;addressee=%28World%29`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`%2cfoo/%2c`,
},
},
{
`<img srcset="http://godoc.org/?q={{.}} 2x, https://golang.org/ 500.5w">`,
[]string{
`#ZgotmplZ`,
`#ZgotmplZ`,
`Hello,#ZgotmplZ`,
` dir=%22ltr%22`,
`#ZgotmplZ, World!%22%29;`,
`Hello,#ZgotmplZ`,
`greeting=H%69%2c&amp;addressee=%28World%29`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`%2cfoo/%2c`,
},
},
{
`<img srcset="http://godoc.org/ 2x, {{.}} 500.5w">`,
[]string{
`#ZgotmplZ`,
`#ZgotmplZ`,
`Hello,#ZgotmplZ`,
` dir=%22ltr%22`,
`#ZgotmplZ, World!%22%29;`,
`Hello,#ZgotmplZ`,
`greeting=H%69%2c&amp;addressee=%28World%29`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`%2cfoo/%2c`,
},
},
{
`<img srcset="http://godoc.org/ 2x, https://golang.org/ {{.}}">`,
[]string{
`#ZgotmplZ`,
`#ZgotmplZ`,
`Hello,#ZgotmplZ`,
` dir=%22ltr%22`,
`#ZgotmplZ, World!%22%29;`,
`Hello,#ZgotmplZ`,
`greeting=H%69%2c&amp;addressee=%28World%29`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`%2cfoo/%2c`,
},
},
}

View File

@ -102,6 +102,8 @@ const (
stateAttr
// stateURL occurs inside an HTML attribute whose content is a URL.
stateURL
// stateSrcset occurs inside an HTML srcset attribute.
stateSrcset
// stateJS occurs inside an event handler or script element.
stateJS
// stateJSDqStr occurs inside a JavaScript double quoted string.
@ -145,6 +147,7 @@ var stateNames = [...]string{
stateRCDATA: "stateRCDATA",
stateAttr: "stateAttr",
stateURL: "stateURL",
stateSrcset: "stateSrcset",
stateJS: "stateJS",
stateJSDqStr: "stateJSDqStr",
stateJSSqStr: "stateJSSqStr",
@ -326,6 +329,8 @@ const (
attrStyle
// attrURL corresponds to an attribute whose value is a URL.
attrURL
// attrSrcset corresponds to a srcset attribute.
attrSrcset
)
var attrNames = [...]string{
@ -334,6 +339,7 @@ var attrNames = [...]string{
attrScriptType: "attrScriptType",
attrStyle: "attrStyle",
attrURL: "attrURL",
attrSrcset: "attrSrcset",
}
func (a attr) String() string {

View File

@ -71,6 +71,7 @@ var funcMap = template.FuncMap{
"_html_template_jsvalescaper": jsValEscaper,
"_html_template_nospaceescaper": htmlNospaceEscaper,
"_html_template_rcdataescaper": rcdataEscaper,
"_html_template_srcsetescaper": srcsetFilterAndEscaper,
"_html_template_urlescaper": urlEscaper,
"_html_template_urlfilter": urlFilter,
"_html_template_urlnormalizer": urlNormalizer,
@ -215,6 +216,8 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
case stateAttrName, stateTag:
c.state = stateAttrName
s = append(s, "_html_template_htmlnamefilter")
case stateSrcset:
s = append(s, "_html_template_srcsetescaper")
default:
if isComment(c.state) {
s = append(s, "_html_template_commentescaper")
@ -280,9 +283,22 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) {
}
// Rewrite the pipeline, creating the escapers in s at the end of the pipeline.
newCmds := make([]*parse.CommandNode, pipelineLen, pipelineLen+len(s))
copy(newCmds, p.Cmds)
insertedIdents := make(map[string]bool)
for i := 0; i < pipelineLen; i++ {
cmd := p.Cmds[i]
newCmds[i] = cmd
if idNode, ok := cmd.Args[0].(*parse.IdentifierNode); ok {
insertedIdents[normalizeEscFn(idNode.Ident)] = true
}
}
for _, name := range s {
newCmds = appendCmd(newCmds, newIdentCmd(name, p.Position()))
if !insertedIdents[normalizeEscFn(name)] {
// When two templates share an underlying parse tree via the use of
// AddParseTree and one template is executed after the other, this check
// ensures that escapers that were already inserted into the pipeline on
// the first escaping pass do not get inserted again.
newCmds = appendCmd(newCmds, newIdentCmd(name, p.Position()))
}
}
p.Cmds = newCmds
}
@ -317,13 +333,16 @@ var equivEscapers = map[string]string{
// escFnsEq reports whether the two escaping functions are equivalent.
func escFnsEq(a, b string) bool {
if e := equivEscapers[a]; e != "" {
a = e
return normalizeEscFn(a) == normalizeEscFn(b)
}
// normalizeEscFn(a) is equal to normalizeEscFn(b) for any pair of names of
// escaper functions a and b that are equivalent.
func normalizeEscFn(e string) string {
if norm := equivEscapers[e]; norm != "" {
return norm
}
if e := equivEscapers[b]; e != "" {
b = e
}
return a == b
return e
}
// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x)

View File

@ -650,6 +650,12 @@ func TestEscape(t *testing.T) {
`<{{"script"}}>{{"doEvil()"}}</{{"script"}}>`,
`&lt;script>doEvil()&lt;/script>`,
},
{
"srcset bad URL in second position",
`<img srcset="{{"/not-an-image#,javascript:alert(1)"}}">`,
// The second URL is also filtered.
`<img srcset="/not-an-image#,#ZgotmplZ">`,
},
}
for _, test := range tests {
@ -1840,7 +1846,7 @@ func TestErrorOnUndefined(t *testing.T) {
err := tmpl.Execute(nil, nil)
if err == nil {
t.Fatal("expected error")
t.Error("expected error")
}
if !strings.Contains(err.Error(), "incomplete") {
t.Errorf("expected error about incomplete template; got %s", err)
@ -1860,10 +1866,10 @@ func TestIdempotentExecute(t *testing.T) {
for i := 0; i < 2; i++ {
err = tmpl.ExecuteTemplate(got, "hello", nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
t.Errorf("unexpected error: %s", err)
}
if got.String() != want {
t.Fatalf("after executing template \"hello\", got:\n\t%q\nwant:\n\t%q\n", got.String(), want)
t.Errorf("after executing template \"hello\", got:\n\t%q\nwant:\n\t%q\n", got.String(), want)
}
got.Reset()
}
@ -1871,7 +1877,7 @@ func TestIdempotentExecute(t *testing.T) {
// "main" does not cause the output of "hello" to change.
err = tmpl.ExecuteTemplate(got, "main", nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
t.Errorf("unexpected error: %s", err)
}
// If the HTML escaper is added again to the action {{"Ladies & Gentlemen!"}},
// we would expected to see the ampersand overescaped to "&amp;amp;".
@ -1881,19 +1887,6 @@ func TestIdempotentExecute(t *testing.T) {
}
}
// This covers issue #21844.
func TestAddExistingTreeError(t *testing.T) {
tmpl := Must(New("foo").Parse(`<p>{{.}}</p>`))
tmpl, err := tmpl.AddParseTree("bar", tmpl.Tree)
if err == nil {
t.Fatalf("expected error after AddParseTree")
}
const want = `html/template: cannot add parse tree that template "foo" already references`
if got := err.Error(); got != want {
t.Errorf("got error:\n\t%q\nwant:\n\t%q\n", got, want)
}
}
func BenchmarkEscapedExecute(b *testing.B) {
tmpl := Must(New("t").Parse(`<a onclick="alert('{{.}}')">{{.}}</a>`))
var buf bytes.Buffer
@ -1903,3 +1896,53 @@ func BenchmarkEscapedExecute(b *testing.B) {
buf.Reset()
}
}
// Covers issue 22780.
func TestOrphanedTemplate(t *testing.T) {
t1 := Must(New("foo").Parse(`<a href="{{.}}">link1</a>`))
t2 := Must(t1.New("foo").Parse(`bar`))
var b bytes.Buffer
const wantError = `template: "foo" is an incomplete or empty template`
if err := t1.Execute(&b, "javascript:alert(1)"); err == nil {
t.Fatal("expected error executing t1")
} else if gotError := err.Error(); gotError != wantError {
t.Fatalf("got t1 execution error:\n\t%s\nwant:\n\t%s", gotError, wantError)
}
b.Reset()
if err := t2.Execute(&b, nil); err != nil {
t.Fatalf("error executing t2: %s", err)
}
const want = "bar"
if got := b.String(); got != want {
t.Fatalf("t2 rendered %q, want %q", got, want)
}
}
// Covers issue 21844.
func TestAliasedParseTreeDoesNotOverescape(t *testing.T) {
const (
tmplText = `{{.}}`
data = `<baz>`
want = `&lt;baz&gt;`
)
// Templates "foo" and "bar" both alias the same underlying parse tree.
tpl := Must(New("foo").Parse(tmplText))
if _, err := tpl.AddParseTree("bar", tpl.Tree); err != nil {
t.Fatalf("AddParseTree error: %v", err)
}
var b1, b2 bytes.Buffer
if err := tpl.ExecuteTemplate(&b1, "foo", data); err != nil {
t.Fatalf(`ExecuteTemplate failed for "foo": %v`, err)
}
if err := tpl.ExecuteTemplate(&b2, "bar", data); err != nil {
t.Fatalf(`ExecuteTemplate failed for "foo": %v`, err)
}
got1, got2 := b1.String(), b2.String()
if got1 != want {
t.Fatalf(`Template "foo" rendered %q, want %q`, got1, want)
}
if got1 != got2 {
t.Fatalf(`Template "foo" and "bar" rendered %q and %q respectively, expected equal values`, got1, got2)
}
}

View File

@ -219,11 +219,6 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
for _, tmpl := range t.set {
if tmpl.Tree == tree {
return nil, fmt.Errorf("html/template: cannot add parse tree that template %q already references", tmpl.Name())
}
}
text, err := t.text.AddParseTree(name, tree)
if err != nil {
return nil, err
@ -300,6 +295,10 @@ func New(name string) *Template {
// New allocates a new HTML template associated with the given one
// and with the same delimiters. The association, which is transitive,
// allows one template to invoke another with a {{template}} action.
//
// If a template with the given name already exists, the new HTML template
// will replace it. The existing template will be reset and disassociated with
// t.
func (t *Template) New(name string) *Template {
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
@ -314,6 +313,10 @@ func (t *Template) new(name string) *Template {
nil,
t.nameSpace,
}
if existing, ok := tmpl.set[name]; ok {
emptyTmpl := New(existing.Name())
*existing = *emptyTmpl
}
tmpl.set[name] = tmpl
return tmpl
}

View File

@ -23,6 +23,7 @@ var transitionFunc = [...]func(context, []byte) (context, int){
stateRCDATA: tSpecialTagEnd,
stateAttr: tAttr,
stateURL: tURL,
stateSrcset: tURL,
stateJS: tJS,
stateJSDqStr: tJSDelimited,
stateJSSqStr: tJSDelimited,
@ -117,6 +118,8 @@ func tTag(c context, s []byte) (context, int) {
attr = attrStyle
case contentTypeJS:
attr = attrScript
case contentTypeSrcset:
attr = attrSrcset
}
}
@ -161,6 +164,7 @@ var attrStartStates = [...]state{
attrScriptType: stateAttr,
attrStyle: stateCSS,
attrURL: stateURL,
attrSrcset: stateSrcset,
}
// tBeforeValue is the context transition function for stateBeforeValue.

View File

@ -37,15 +37,25 @@ func urlFilter(args ...interface{}) string {
if t == contentTypeURL {
return s
}
if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') {
protocol := strings.ToLower(s[:i])
if protocol != "http" && protocol != "https" && protocol != "mailto" {
return "#" + filterFailsafe
}
if !isSafeUrl(s) {
return "#" + filterFailsafe
}
return s
}
// isSafeUrl is true if s is a relative URL or if URL has a protocol in
// (http, https, mailto).
func isSafeUrl(s string) bool {
if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') {
protocol := s[:i]
if !strings.EqualFold(protocol, "http") && !strings.EqualFold(protocol, "https") && !strings.EqualFold(protocol, "mailto") {
return false
}
}
return true
}
// urlEscaper produces an output that can be embedded in a URL query.
// The output can be embedded in an HTML attribute without further escaping.
func urlEscaper(args ...interface{}) string {
@ -69,6 +79,16 @@ func urlProcessor(norm bool, args ...interface{}) string {
norm = true
}
var b bytes.Buffer
if processUrlOnto(s, norm, &b) {
return b.String()
}
return s
}
// processUrlOnto appends a normalized URL corresponding to its input to b
// and returns true if the appended content differs from s.
func processUrlOnto(s string, norm bool, b *bytes.Buffer) bool {
b.Grow(b.Cap() + len(s) + 16)
written := 0
// The byte loop below assumes that all URLs use UTF-8 as the
// content-encoding. This is similar to the URI to IRI encoding scheme
@ -114,12 +134,86 @@ func urlProcessor(norm bool, args ...interface{}) string {
}
}
b.WriteString(s[written:i])
fmt.Fprintf(&b, "%%%02x", c)
fmt.Fprintf(b, "%%%02x", c)
written = i + 1
}
if written == 0 {
return s
}
b.WriteString(s[written:])
return written != 0
}
// Filters and normalizes srcset values which are comma separated
// URLs followed by metadata.
func srcsetFilterAndEscaper(args ...interface{}) string {
s, t := stringify(args...)
switch t {
case contentTypeSrcset:
return s
case contentTypeURL:
// Normalizing gets rid of all HTML whitespace
// which separate the image URL from its metadata.
var b bytes.Buffer
if processUrlOnto(s, true, &b) {
s = b.String()
}
// Additionally, commas separate one source from another.
return strings.Replace(s, ",", "%2c", -1)
}
var b bytes.Buffer
written := 0
for i := 0; i < len(s); i++ {
if s[i] == ',' {
filterSrcsetElement(s, written, i, &b)
b.WriteString(",")
written = i + 1
}
}
filterSrcsetElement(s, written, len(s), &b)
return b.String()
}
// Derived from https://play.golang.org/p/Dhmj7FORT5
const htmlSpaceAndAsciiAlnumBytes = "\x00\x36\x00\x00\x01\x00\xff\x03\xfe\xff\xff\x07\xfe\xff\xff\x07"
// isHtmlSpace is true iff c is a whitespace character per
// https://infra.spec.whatwg.org/#ascii-whitespace
func isHtmlSpace(c byte) bool {
return (c <= 0x20) && 0 != (htmlSpaceAndAsciiAlnumBytes[c>>3]&(1<<uint(c&0x7)))
}
func isHtmlSpaceOrAsciiAlnum(c byte) bool {
return (c < 0x80) && 0 != (htmlSpaceAndAsciiAlnumBytes[c>>3]&(1<<uint(c&0x7)))
}
func filterSrcsetElement(s string, left int, right int, b *bytes.Buffer) {
start := left
for start < right && isHtmlSpace(s[start]) {
start += 1
}
end := right
for i := start; i < right; i++ {
if isHtmlSpace(s[i]) {
end = i
break
}
}
if url := s[start:end]; isSafeUrl(url) {
// If image metadata is only spaces or alnums then
// we don't need to URL normalize it.
metadataOk := true
for i := end; i < right; i++ {
if !isHtmlSpaceOrAsciiAlnum(s[i]) {
metadataOk = false
break
}
}
if metadataOk {
b.WriteString(s[left:start])
processUrlOnto(url, true, b)
b.WriteString(s[end:right])
return
}
}
b.WriteString("#")
b.WriteString(filterFailsafe)
}

View File

@ -87,6 +87,51 @@ func TestURLFilters(t *testing.T) {
}
}
func TestSrcsetFilter(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
"one ok",
"http://example.com/img.png",
"http://example.com/img.png",
},
{
"one ok with metadata",
" /img.png 200w",
" /img.png 200w",
},
{
"one bad",
"javascript:alert(1) 200w",
"#ZgotmplZ",
},
{
"two ok",
"foo.png, bar.png",
"foo.png, bar.png",
},
{
"left bad",
"javascript:alert(1), /foo.png",
"#ZgotmplZ, /foo.png",
},
{
"right bad",
"/bogus#, javascript:alert(1)",
"/bogus#,#ZgotmplZ",
},
}
for _, test := range tests {
if got := srcsetFilterAndEscaper(test.input); got != test.want {
t.Errorf("%s: srcsetFilterAndEscaper(%q) want %q != %q", test.name, test.input, test.want, got)
}
}
}
func BenchmarkURLEscaper(b *testing.B) {
for i := 0; i < b.N; i++ {
urlEscaper("http://example.com:80/foo?q=bar%20&baz=x+y#frag")
@ -110,3 +155,15 @@ func BenchmarkURLNormalizerNoSpecials(b *testing.B) {
urlNormalizer("http://example.com:80/foo?q=bar%20&baz=x+y#frag")
}
}
func BenchmarkSrcsetFilter(b *testing.B) {
for i := 0; i < b.N; i++ {
srcsetFilterAndEscaper(" /foo/bar.png 200w, /baz/boo(1).png")
}
}
func BenchmarkSrcsetFilterNoSpecials(b *testing.B) {
for i := 0; i < b.N; i++ {
srcsetFilterAndEscaper("http://example.com:80/foo?q=bar%20&baz=x+y#frag")
}
}

View File

@ -65,7 +65,7 @@ func (mu *fdMutex) incref() bool {
}
// increfAndClose sets the state of mu to closed.
// It reports whether there is no remaining reference.
// It returns false if the file was already closed.
func (mu *fdMutex) increfAndClose() bool {
for {
old := atomic.LoadUint64(&mu.state)

View File

@ -40,6 +40,9 @@ type FD struct {
// Whether this is a file rather than a network socket.
isFile bool
// Whether this file has been set to blocking mode.
isBlocking bool
}
// Init initializes the FD. The Sysfd field should already be set.
@ -53,6 +56,7 @@ func (fd *FD) Init(net string, pollable bool) error {
fd.isFile = true
}
if !pollable {
fd.isBlocking = true
return nil
}
return fd.pd.init(fd)
@ -76,18 +80,26 @@ func (fd *FD) Close() error {
if !fd.fdmu.increfAndClose() {
return errClosing(fd.isFile)
}
// Unblock any I/O. Once it all unblocks and returns,
// so that it cannot be referring to fd.sysfd anymore,
// the final decref will close fd.sysfd. This should happen
// fairly quickly, since all the I/O is non-blocking, and any
// attempts to block in the pollDesc will return errClosing(fd.isFile).
fd.pd.evict()
// The call to decref will call destroy if there are no other
// references.
err := fd.decref()
// Wait until the descriptor is closed. If this was the only
// reference, it is already closed.
runtime_Semacquire(&fd.csema)
// reference, it is already closed. Only wait if the file has
// not been set to blocking mode, as otherwise any current I/O
// may be blocking, and that would block the Close.
if !fd.isBlocking {
runtime_Semacquire(&fd.csema)
}
return err
}
@ -100,6 +112,16 @@ func (fd *FD) Shutdown(how int) error {
return syscall.Shutdown(fd.Sysfd, how)
}
// SetBlocking puts the file into blocking mode.
func (fd *FD) SetBlocking() error {
if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
fd.isBlocking = true
return syscall.SetNonblock(fd.Sysfd, false)
}
// Darwin and FreeBSD can't read or write 2GB+ files at a time,
// even on 64-bit systems.
// The same is true of socket implementations on many systems.

View File

@ -65,8 +65,10 @@ func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, e
}
// DoChan is like Do but returns a channel that will receive the
// results when they are ready.
func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result {
// results when they are ready. The second result is true if the function
// will eventually be called, false if it will not (because there is
// a pending request with this key).
func (g *Group) DoChan(key string, fn func() (interface{}, error)) (<-chan Result, bool) {
ch := make(chan Result, 1)
g.mu.Lock()
if g.m == nil {
@ -76,7 +78,7 @@ func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result
c.dups++
c.chans = append(c.chans, ch)
g.mu.Unlock()
return ch
return ch, false
}
c := &call{chans: []chan<- Result{ch}}
c.wg.Add(1)
@ -85,7 +87,7 @@ func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result
go g.doCall(c, key, fn)
return ch
return ch, true
}
// doCall handles the single call for a key.

View File

@ -18,3 +18,7 @@ func hasSymlink() (ok bool, reason string) {
return true, ""
}
func IsWindowsXP() bool {
return false
}

View File

@ -46,3 +46,12 @@ func hasSymlink() (ok bool, reason string) {
return false, ""
}
func IsWindowsXP() bool {
v, err := syscall.GetVersion()
if err != nil {
panic("GetVersion failed: " + err.Error())
}
major := byte(v)
return major < 6
}

View File

@ -0,0 +1,69 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package testlog provides a back-channel communication path
// between tests and package os, so that cmd/go can see which
// environment variables and files a test consults.
package testlog
import "sync/atomic"
// Interface is the interface required of test loggers.
// The os package will invoke the interface's methods to indicate that
// it is inspecting the given environment variables or files.
// Multiple goroutines may call these methods simultaneously.
type Interface interface {
Getenv(key string)
Stat(file string)
Open(file string)
Chdir(dir string)
}
// logger is the current logger Interface.
// We use an atomic.Value in case test startup
// is racing with goroutines started during init.
// That must not cause a race detector failure,
// although it will still result in limited visibility
// into exactly what those goroutines do.
var logger atomic.Value
// SetLogger sets the test logger implementation for the current process.
// It must be called only once, at process startup.
func SetLogger(impl Interface) {
if logger.Load() != nil {
panic("testlog: SetLogger must be called only once")
}
logger.Store(&impl)
}
// Logger returns the current test logger implementation.
// It returns nil if there is no logger.
func Logger() Interface {
impl := logger.Load()
if impl == nil {
return nil
}
return *impl.(*Interface)
}
// Getenv calls Logger().Getenv, if a logger has been set.
func Getenv(name string) {
if log := Logger(); log != nil {
log.Getenv(name)
}
}
// Open calls Logger().Open, if a logger has been set.
func Open(name string) {
if log := Logger(); log != nil {
log.Open(name)
}
}
// Stat calls Logger().Stat, if a logger has been set.
func Stat(name string) {
if log := Logger(); log != nil {
log.Stat(name)
}
}

View File

@ -75,36 +75,44 @@ const (
GCP // depicts GC state
)
// ParseResult is the result of Parse.
type ParseResult struct {
// Events is the sorted list of Events in the trace.
Events []*Event
// Stacks is the stack traces keyed by stack IDs from the trace.
Stacks map[uint64][]*Frame
}
// Parse parses, post-processes and verifies the trace.
func Parse(r io.Reader, bin string) ([]*Event, error) {
ver, events, err := parse(r, bin)
func Parse(r io.Reader, bin string) (ParseResult, error) {
ver, res, err := parse(r, bin)
if err != nil {
return nil, err
return ParseResult{}, err
}
if ver < 1007 && bin == "" {
return nil, fmt.Errorf("for traces produced by go 1.6 or below, the binary argument must be provided")
return ParseResult{}, fmt.Errorf("for traces produced by go 1.6 or below, the binary argument must be provided")
}
return events, nil
return res, nil
}
// parse parses, post-processes and verifies the trace. It returns the
// trace version and the list of events.
func parse(r io.Reader, bin string) (int, []*Event, error) {
func parse(r io.Reader, bin string) (int, ParseResult, error) {
ver, rawEvents, strings, err := readTrace(r)
if err != nil {
return 0, nil, err
return 0, ParseResult{}, err
}
events, stacks, err := parseEvents(ver, rawEvents, strings)
if err != nil {
return 0, nil, err
return 0, ParseResult{}, err
}
events, err = removeFutile(events)
if err != nil {
return 0, nil, err
return 0, ParseResult{}, err
}
err = postProcessTrace(ver, events)
if err != nil {
return 0, nil, err
return 0, ParseResult{}, err
}
// Attach stack traces.
for _, ev := range events {
@ -114,10 +122,10 @@ func parse(r io.Reader, bin string) (int, []*Event, error) {
}
if ver < 1007 && bin != "" {
if err := symbolize(events, bin); err != nil {
return 0, nil, err
return 0, ParseResult{}, err
}
}
return ver, events, nil
return ver, ParseResult{Events: events, Stacks: stacks}, nil
}
// rawEvent is a helper type used during parsing.

View File

@ -25,8 +25,8 @@ func TestCorruptedInputs(t *testing.T) {
"go 1.5 trace\x00\x00\x00\x00\xc3\x0200",
}
for _, data := range tests {
events, err := Parse(strings.NewReader(data), "")
if err == nil || events != nil {
res, err := Parse(strings.NewReader(data), "")
if err == nil || res.Events != nil || res.Stacks != nil {
t.Fatalf("no error on input: %q", data)
}
}

View File

@ -1589,6 +1589,7 @@ var vfpowSC = [][2]float64{
{Inf(-1), 1},
{Inf(-1), 3},
{Inf(-1), Pi},
{Inf(-1), 0.5},
{Inf(-1), NaN()},
{-Pi, Inf(-1)},
@ -1607,9 +1608,11 @@ var vfpowSC = [][2]float64{
{-1 / 2, Inf(1)},
{Copysign(0, -1), Inf(-1)},
{Copysign(0, -1), -Pi},
{Copysign(0, -1), -0.5},
{Copysign(0, -1), -3},
{Copysign(0, -1), 3},
{Copysign(0, -1), Pi},
{Copysign(0, -1), 0.5},
{Copysign(0, -1), Inf(1)},
{0, Inf(-1)},
@ -1666,6 +1669,7 @@ var powSC = []float64{
Inf(-1), // pow(-Inf, 1)
Inf(-1), // pow(-Inf, 3)
Inf(1), // pow(-Inf, Pi)
Inf(1), // pow(-Inf, 0.5)
NaN(), // pow(-Inf, NaN)
0, // pow(-Pi, -Inf)
NaN(), // pow(-Pi, -Pi)
@ -1682,9 +1686,11 @@ var powSC = []float64{
0, // pow(-1/2, +Inf)
Inf(1), // pow(-0, -Inf)
Inf(1), // pow(-0, -Pi)
Inf(1), // pow(-0, -0.5)
Inf(-1), // pow(-0, -3) IEEE 754-2008
Copysign(0, -1), // pow(-0, 3) IEEE 754-2008
0, // pow(-0, +Pi)
0, // pow(-0, 0.5)
0, // pow(-0, +Inf)
Inf(1), // pow(+0, -Inf)
Inf(1), // pow(+0, -Pi)

View File

@ -48,10 +48,6 @@ func pow(x, y float64) float64 {
return 1
case y == 1:
return x
case y == 0.5:
return Sqrt(x)
case y == -0.5:
return 1 / Sqrt(x)
case IsNaN(x) || IsNaN(y):
return NaN()
case x == 0:
@ -86,6 +82,10 @@ func pow(x, y float64) float64 {
case y > 0:
return Inf(1)
}
case y == 0.5:
return Sqrt(x)
case y == -0.5:
return 1 / Sqrt(x)
}
absy := y

View File

@ -393,7 +393,7 @@ func (r *lockedSource) Seed(seed int64) {
r.lk.Unlock()
}
// seedPos implements Seed for a lockedSource without a race condiiton.
// seedPos implements Seed for a lockedSource without a race condition.
func (r *lockedSource) seedPos(seed int64, readPos *int8) {
r.lk.Lock()
r.src.Seed(seed)

View File

@ -13,6 +13,7 @@ import (
)
func TestCgoLookupIP(t *testing.T) {
defer dnsWaitGroup.Wait()
ctx := context.Background()
_, err, ok := cgoLookupIP(ctx, "localhost")
if !ok {
@ -24,6 +25,7 @@ func TestCgoLookupIP(t *testing.T) {
}
func TestCgoLookupIPWithCancel(t *testing.T) {
defer dnsWaitGroup.Wait()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, err, ok := cgoLookupIP(ctx, "localhost")
@ -36,6 +38,7 @@ func TestCgoLookupIPWithCancel(t *testing.T) {
}
func TestCgoLookupPort(t *testing.T) {
defer dnsWaitGroup.Wait()
ctx := context.Background()
_, err, ok := cgoLookupPort(ctx, "tcp", "smtp")
if !ok {
@ -47,6 +50,7 @@ func TestCgoLookupPort(t *testing.T) {
}
func TestCgoLookupPortWithCancel(t *testing.T) {
defer dnsWaitGroup.Wait()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, err, ok := cgoLookupPort(ctx, "tcp", "smtp")
@ -59,6 +63,7 @@ func TestCgoLookupPortWithCancel(t *testing.T) {
}
func TestCgoLookupPTR(t *testing.T) {
defer dnsWaitGroup.Wait()
ctx := context.Background()
_, err, ok := cgoLookupPTR(ctx, "127.0.0.1")
if !ok {
@ -70,6 +75,7 @@ func TestCgoLookupPTR(t *testing.T) {
}
func TestCgoLookupPTRWithCancel(t *testing.T) {
defer dnsWaitGroup.Wait()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, err, ok := cgoLookupPTR(ctx, "127.0.0.1")

View File

@ -10,6 +10,7 @@ import (
"internal/poll"
"internal/testenv"
"io"
"os"
"runtime"
"sync"
"testing"
@ -85,11 +86,6 @@ func TestDialerDualStackFDLeak(t *testing.T) {
t.Skip("both IPv4 and IPv6 are required")
}
closedPortDelay, expectClosedPortDelay := dialClosedPort()
if closedPortDelay > expectClosedPortDelay {
t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
}
before := sw.Sockets()
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
@ -115,7 +111,7 @@ func TestDialerDualStackFDLeak(t *testing.T) {
const N = 10
var wg sync.WaitGroup
wg.Add(N)
d := &Dialer{DualStack: true, Timeout: 100*time.Millisecond + closedPortDelay}
d := &Dialer{DualStack: true, Timeout: 5 * time.Second}
for i := 0; i < N; i++ {
go func() {
defer wg.Done()
@ -639,7 +635,13 @@ func TestDialerLocalAddr(t *testing.T) {
}
c, err := d.Dial(tt.network, addr)
if err == nil && tt.error != nil || err != nil && tt.error == nil {
t.Errorf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error)
// On Darwin this occasionally times out.
// We don't know why. Issue #22019.
if runtime.GOOS == "darwin" && tt.error == nil && os.IsTimeout(err) {
t.Logf("ignoring timeout error on Darwin; see https://golang.org/issue/22019")
} else {
t.Errorf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error)
}
}
if err != nil {
if perr := parseDialError(err); perr != nil {

View File

@ -479,7 +479,9 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order
var lastErr error
for _, fqdn := range conf.nameList(name) {
for _, qtype := range qtypes {
dnsWaitGroup.Add(1)
go func(qtype uint16) {
defer dnsWaitGroup.Done()
cname, rrs, err := r.tryOneName(ctx, conf, fqdn, qtype)
lane <- racer{cname, rrs, err}
}(qtype)

View File

@ -203,6 +203,7 @@ var fakeDNSServerSuccessful = fakeDNSServer{func(_, _ string, q *dnsMsg, _ time.
// Issue 13705: don't try to resolve onion addresses, etc
func TestLookupTorOnion(t *testing.T) {
defer dnsWaitGroup.Wait()
r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
addrs, err := r.LookupIPAddr(context.Background(), "foo.onion")
if err != nil {
@ -300,6 +301,8 @@ var updateResolvConfTests = []struct {
}
func TestUpdateResolvConf(t *testing.T) {
defer dnsWaitGroup.Wait()
r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
conf, err := newResolvConfTest()
@ -455,6 +458,8 @@ var goLookupIPWithResolverConfigTests = []struct {
}
func TestGoLookupIPWithResolverConfig(t *testing.T) {
defer dnsWaitGroup.Wait()
fake := fakeDNSServer{func(n, s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
switch s {
case "[2001:4860:4860::8888]:53", "8.8.8.8:53":
@ -547,6 +552,8 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) {
// Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
defer dnsWaitGroup.Wait()
fake := fakeDNSServer{func(n, s string, q *dnsMsg, tm time.Time) (*dnsMsg, error) {
r := &dnsMsg{
dnsMsgHdr: dnsMsgHdr{
@ -603,6 +610,8 @@ func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
// querying the original name instead of an error encountered
// querying a generated name.
func TestErrorForOriginalNameWhenSearching(t *testing.T) {
defer dnsWaitGroup.Wait()
const fqdn = "doesnotexist.domain"
conf, err := newResolvConfTest()
@ -657,6 +666,8 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
// Issue 15434. If a name server gives a lame referral, continue to the next.
func TestIgnoreLameReferrals(t *testing.T) {
defer dnsWaitGroup.Wait()
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@ -889,6 +900,8 @@ func TestIgnoreDNSForgeries(t *testing.T) {
// Issue 16865. If a name server times out, continue to the next.
func TestRetryTimeout(t *testing.T) {
defer dnsWaitGroup.Wait()
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@ -945,6 +958,8 @@ func TestRotate(t *testing.T) {
}
func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) {
defer dnsWaitGroup.Wait()
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@ -1008,6 +1023,8 @@ func mockTXTResponse(q *dnsMsg) *dnsMsg {
// Issue 17448. With StrictErrors enabled, temporary errors should make
// LookupIP fail rather than return a partial result.
func TestStrictErrorsLookupIP(t *testing.T) {
defer dnsWaitGroup.Wait()
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@ -1256,6 +1273,8 @@ func TestStrictErrorsLookupIP(t *testing.T) {
// Issue 17448. With StrictErrors enabled, temporary errors should make
// LookupTXT stop walking the search list.
func TestStrictErrorsLookupTXT(t *testing.T) {
defer dnsWaitGroup.Wait()
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@ -1312,3 +1331,25 @@ func TestStrictErrorsLookupTXT(t *testing.T) {
}
}
}
// Test for a race between uninstalling the test hooks and closing a
// socket connection. This used to fail when testing with -race.
func TestDNSGoroutineRace(t *testing.T) {
defer dnsWaitGroup.Wait()
fake := fakeDNSServer{func(n, s string, q *dnsMsg, t time.Time) (*dnsMsg, error) {
time.Sleep(10 * time.Microsecond)
return nil, poll.ErrTimeout
}}
r := Resolver{PreferGo: true, Dial: fake.DialContext}
// The timeout here is less than the timeout used by the server,
// so the goroutine started to query the (fake) server will hang
// around after this test is done if we don't call dnsWaitGroup.Wait.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Microsecond)
defer cancel()
_, err := r.LookupIPAddr(ctx, "where.are.they.now")
if err == nil {
t.Fatal("fake DNS lookup unexpectedly succeeded")
}
}

View File

@ -173,7 +173,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa sysc
return rsa, nil
}
default:
return nil, os.NewSyscallError("getsockopt", err)
return nil, os.NewSyscallError("connect", err)
}
runtime.KeepAlive(fd)
}
@ -321,7 +321,7 @@ func (fd *netFD) dup() (f *os.File, err error) {
// This also puts the old fd into blocking mode, meaning that
// I/O will block the thread instead of letting us use the epoll server.
// Everything will still work, just with more threads.
if err = syscall.SetNonblock(ns, false); err != nil {
if err = fd.pfd.SetBlocking(); err != nil {
return nil, os.NewSyscallError("setnonblock", err)
}

View File

@ -1428,3 +1428,59 @@ func testWriteHeader0(t *testing.T, h2 bool) {
t.Error("expected panic in handler")
}
}
// Issue 23010: don't be super strict checking WriteHeader's code if
// it's not even valid to call WriteHeader then anyway.
func TestWriteHeaderNoCodeCheck_h1(t *testing.T) { testWriteHeaderAfterWrite(t, h1Mode, false) }
func TestWriteHeaderNoCodeCheck_h1hijack(t *testing.T) { testWriteHeaderAfterWrite(t, h1Mode, true) }
func TestWriteHeaderNoCodeCheck_h2(t *testing.T) { testWriteHeaderAfterWrite(t, h2Mode, false) }
func testWriteHeaderAfterWrite(t *testing.T, h2, hijack bool) {
setParallel(t)
defer afterTest(t)
var errorLog lockedBytesBuffer
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
if hijack {
conn, _, _ := w.(Hijacker).Hijack()
defer conn.Close()
conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nfoo"))
w.WriteHeader(0) // verify this doesn't panic if there's already output; Issue 23010
conn.Write([]byte("bar"))
return
}
io.WriteString(w, "foo")
w.(Flusher).Flush()
w.WriteHeader(0) // verify this doesn't panic if there's already output; Issue 23010
io.WriteString(w, "bar")
}), func(ts *httptest.Server) {
ts.Config.ErrorLog = log.New(&errorLog, "", 0)
})
defer cst.close()
res, err := cst.c.Get(cst.ts.URL)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
}
if got, want := string(body), "foobar"; got != want {
t.Errorf("got = %q; want %q", got, want)
}
// Also check the stderr output:
if h2 {
// TODO: also emit this log message for HTTP/2?
// We historically haven't, so don't check.
return
}
gotLog := strings.TrimSpace(errorLog.String())
wantLog := "http: multiple response.WriteHeader calls"
if hijack {
wantLog = "http: response.WriteHeader on hijacked connection"
}
if gotLog != wantLog {
t.Errorf("stderr output = %q; want %q", gotLog, wantLog)
}
}

View File

@ -1140,7 +1140,7 @@ func TestLinuxSendfile(t *testing.T) {
Post(fmt.Sprintf("http://%s/quit", ln.Addr()), "", nil)
child.Wait()
rx := regexp.MustCompile(`sendfile(64)?\(\d+,\s*\d+,\s*NULL,\s*\d+`)
rx := regexp.MustCompile(`sendfile(64)?\(`)
out := buf.String()
if !rx.MatchString(out) {
t.Errorf("no sendfile system call found in:\n%s", out)

View File

@ -989,7 +989,7 @@ type http2noDialH2RoundTripper struct{ t *http2Transport }
func (rt http2noDialH2RoundTripper) RoundTrip(req *Request) (*Response, error) {
res, err := rt.t.RoundTrip(req)
if err == http2ErrNoCachedConn {
if http2isNoCachedConnError(err) {
return nil, ErrSkipAltProtocol
}
return res, err
@ -6204,7 +6204,6 @@ func http2checkWriteHeaderCode(code int) {
}
func (w *http2responseWriter) WriteHeader(code int) {
http2checkWriteHeaderCode(code)
rws := w.rws
if rws == nil {
panic("WriteHeader called after Handler finished")
@ -6214,6 +6213,7 @@ func (w *http2responseWriter) WriteHeader(code int) {
func (rws *http2responseWriterState) writeHeader(code int) {
if !rws.wroteHeader {
http2checkWriteHeaderCode(code)
rws.wroteHeader = true
rws.status = code
if len(rws.handlerHeader) > 0 {
@ -6856,7 +6856,27 @@ func (sew http2stickyErrWriter) Write(p []byte) (n int, err error) {
return
}
var http2ErrNoCachedConn = errors.New("http2: no cached connection was available")
// noCachedConnError is the concrete type of ErrNoCachedConn, needs to be detected
// by net/http regardless of whether it's its bundled version (in h2_bundle.go with a rewritten type name)
// or from a user's x/net/http2. As such, as it has a unique method name (IsHTTP2NoCachedConnError) that
// net/http sniffs for via func isNoCachedConnError.
type http2noCachedConnError struct{}
func (http2noCachedConnError) IsHTTP2NoCachedConnError() {}
func (http2noCachedConnError) Error() string { return "http2: no cached connection was available" }
// isNoCachedConnError reports whether err is of type noCachedConnError
// or its equivalent renamed type in net/http2's h2_bundle.go. Both types
// may coexist in the same running program.
func http2isNoCachedConnError(err error) bool {
_, ok := err.(interface {
IsHTTP2NoCachedConnError()
})
return ok
}
var http2ErrNoCachedConn error = http2noCachedConnError{}
// RoundTripOpt are options for the Transport.RoundTripOpt method.
type http2RoundTripOpt struct {

View File

@ -191,11 +191,10 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
res, err := transport.RoundTrip(outreq)
if res == nil {
res = &http.Response{
StatusCode: http.StatusBadGateway,
Body: http.NoBody,
}
if err != nil {
p.logf("http: proxy error: %v", err)
rw.WriteHeader(http.StatusBadGateway)
return
}
removeConnectionHeaders(res.Header)
@ -205,16 +204,12 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
if p.ModifyResponse != nil {
if err != nil {
if err := p.ModifyResponse(res); err != nil {
p.logf("http: proxy error: %v", err)
rw.WriteHeader(http.StatusBadGateway)
res.Body.Close()
return
}
err = p.ModifyResponse(res)
}
if err != nil {
p.logf("http: proxy error: %v", err)
rw.WriteHeader(http.StatusBadGateway)
res.Body.Close()
return
}
copyHeader(rw.Header(), res.Header)

View File

@ -631,35 +631,6 @@ func TestReverseProxyModifyResponse(t *testing.T) {
}
}
// Issue 21255. Test ModifyResponse when an error from transport.RoundTrip
// occurs, and that the proxy returns StatusOK.
func TestReverseProxyModifyResponse_OnError(t *testing.T) {
// Always returns an error
errBackend := httptest.NewUnstartedServer(nil)
errBackend.Config.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests
defer errBackend.Close()
rpURL, _ := url.Parse(errBackend.URL)
rproxy := NewSingleHostReverseProxy(rpURL)
rproxy.ModifyResponse = func(resp *http.Response) error {
// Will be set for a non-nil error
resp.StatusCode = http.StatusOK
return nil
}
frontend := httptest.NewServer(rproxy)
defer frontend.Close()
resp, err := http.Get(frontend.URL)
if err != nil {
t.Fatalf("failed to reach proxy: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("err != nil: got res.StatusCode %d; expected %d", resp.StatusCode, http.StatusOK)
}
resp.Body.Close()
}
// Issue 16659: log errors from short read
func TestReverseProxy_CopyBuffer(t *testing.T) {
backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

View File

@ -98,6 +98,10 @@ var reqWriteExcludeHeader = map[string]bool{
type Request struct {
// Method specifies the HTTP method (GET, POST, PUT, etc.).
// For client requests an empty string means GET.
//
// Go's HTTP client does not support sending a request with
// the CONNECT method. See the documentation on Transport for
// details.
Method string
// URL specifies either the URI being requested (for server

View File

@ -57,10 +57,9 @@ type Response struct {
// The http Client and Transport guarantee that Body is always
// non-nil, even on responses without a body or responses with
// a zero-length body. It is the caller's responsibility to
// close Body. The default HTTP client's Transport does not
// attempt to reuse HTTP/1.0 or HTTP/1.1 TCP connections
// ("keep-alive") unless the Body is read to completion and is
// closed.
// close Body. The default HTTP client's Transport may not
// reuse HTTP/1.x "keep-alive" TCP connections if the Body is
// not read to completion and closed.
//
// The Body is automatically dechunked if the server replied
// with a "chunked" Transfer-Encoding.

View File

@ -523,6 +523,64 @@ func TestServeWithSlashRedirectKeepsQueryString(t *testing.T) {
}
}
func TestServeWithSlashRedirectForHostPatterns(t *testing.T) {
setParallel(t)
defer afterTest(t)
mux := NewServeMux()
mux.Handle("example.com/pkg/foo/", stringHandler("example.com/pkg/foo/"))
mux.Handle("example.com/pkg/bar", stringHandler("example.com/pkg/bar"))
mux.Handle("example.com/pkg/bar/", stringHandler("example.com/pkg/bar/"))
mux.Handle("example.com:3000/pkg/connect/", stringHandler("example.com:3000/pkg/connect/"))
mux.Handle("example.com:9000/", stringHandler("example.com:9000/"))
mux.Handle("/pkg/baz/", stringHandler("/pkg/baz/"))
tests := []struct {
method string
url string
code int
loc string
want string
}{
{"GET", "http://example.com/", 404, "", ""},
{"GET", "http://example.com/pkg/foo", 301, "/pkg/foo/", ""},
{"GET", "http://example.com/pkg/bar", 200, "", "example.com/pkg/bar"},
{"GET", "http://example.com/pkg/bar/", 200, "", "example.com/pkg/bar/"},
{"GET", "http://example.com/pkg/baz", 301, "/pkg/baz/", ""},
{"GET", "http://example.com:3000/pkg/foo", 301, "/pkg/foo/", ""},
{"CONNECT", "http://example.com/", 404, "", ""},
{"CONNECT", "http://example.com:3000/", 404, "", ""},
{"CONNECT", "http://example.com:9000/", 200, "", "example.com:9000/"},
{"CONNECT", "http://example.com/pkg/foo", 301, "/pkg/foo/", ""},
{"CONNECT", "http://example.com:3000/pkg/foo", 404, "", ""},
{"CONNECT", "http://example.com:3000/pkg/baz", 301, "/pkg/baz/", ""},
{"CONNECT", "http://example.com:3000/pkg/connect", 301, "/pkg/connect/", ""},
}
ts := httptest.NewServer(mux)
defer ts.Close()
for i, tt := range tests {
req, _ := NewRequest(tt.method, tt.url, nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if got, want := w.Code, tt.code; got != want {
t.Errorf("#%d: Status = %d; want = %d", i, got, want)
}
if tt.code == 301 {
if got, want := w.HeaderMap.Get("Location"), tt.loc; got != want {
t.Errorf("#%d: Location = %q; want = %q", i, got, want)
}
} else {
if got, want := w.HeaderMap.Get("Result"), tt.want; got != want {
t.Errorf("#%d: Result = %q; want = %q", i, got, want)
}
}
}
}
func BenchmarkServeMux(b *testing.B) {
type test struct {
@ -5478,32 +5536,54 @@ func testServerKeepAlivesEnabled(t *testing.T, h2 bool) {
func TestServerCancelsReadTimeoutWhenIdle(t *testing.T) {
setParallel(t)
defer afterTest(t)
const timeout = 250 * time.Millisecond
ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
select {
case <-time.After(2 * timeout):
fmt.Fprint(w, "ok")
case <-r.Context().Done():
fmt.Fprint(w, r.Context().Err())
runTimeSensitiveTest(t, []time.Duration{
10 * time.Millisecond,
50 * time.Millisecond,
250 * time.Millisecond,
time.Second,
2 * time.Second,
}, func(t *testing.T, timeout time.Duration) error {
ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
select {
case <-time.After(2 * timeout):
fmt.Fprint(w, "ok")
case <-r.Context().Done():
fmt.Fprint(w, r.Context().Err())
}
}))
ts.Config.ReadTimeout = timeout
ts.Start()
defer ts.Close()
c := ts.Client()
res, err := c.Get(ts.URL)
if err != nil {
return fmt.Errorf("Get: %v", err)
}
}))
ts.Config.ReadTimeout = timeout
ts.Start()
defer ts.Close()
slurp, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
return fmt.Errorf("Body ReadAll: %v", err)
}
if string(slurp) != "ok" {
return fmt.Errorf("got: %q, want ok", slurp)
}
return nil
})
}
c := ts.Client()
res, err := c.Get(ts.URL)
if err != nil {
t.Fatal(err)
}
slurp, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Fatal(err)
}
if string(slurp) != "ok" {
t.Fatalf("Got: %q, want ok", slurp)
// runTimeSensitiveTest runs test with the provided durations until one passes.
// If they all fail, t.Fatal is called with the last one's duration and error value.
func runTimeSensitiveTest(t *testing.T, durations []time.Duration, test func(t *testing.T, d time.Duration) error) {
for i, d := range durations {
err := test(t, d)
if err == nil {
return
}
if i == len(durations)-1 {
t.Fatalf("failed with duration %v: %v", d, err)
}
}
}

View File

@ -132,12 +132,20 @@ type ResponseWriter interface {
// possible to maximize compatibility.
Write([]byte) (int, error)
// WriteHeader sends an HTTP response header with status code.
// WriteHeader sends an HTTP response header with the provided
// status code.
//
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
WriteHeader(int)
//
// The provided code must be a valid HTTP 1xx-5xx status code.
// Only one header may be written. Go does not currently
// support sending user-defined 1xx informational headers,
// with the exception of 100-continue response header that the
// Server sends automatically when the Request.Body is read.
WriteHeader(statusCode int)
}
// The Flusher interface is implemented by ResponseWriters that allow
@ -1064,7 +1072,6 @@ func checkWriteHeaderCode(code int) {
}
func (w *response) WriteHeader(code int) {
checkWriteHeaderCode(code)
if w.conn.hijacked() {
w.conn.server.logf("http: response.WriteHeader on hijacked connection")
return
@ -1073,6 +1080,7 @@ func (w *response) WriteHeader(code int) {
w.conn.server.logf("http: multiple response.WriteHeader calls")
return
}
checkWriteHeaderCode(code)
w.wroteHeader = true
w.status = code
@ -2212,8 +2220,8 @@ func (mux *ServeMux) match(path string) (h Handler, pattern string) {
// This occurs when a handler for path + "/" was already registered, but
// not for path itself. If the path needs appending to, it creates a new
// URL, setting the path to u.Path + "/" and returning true to indicate so.
func (mux *ServeMux) redirectToPathSlash(path string, u *url.URL) (*url.URL, bool) {
if !mux.shouldRedirect(path) {
func (mux *ServeMux) redirectToPathSlash(host, path string, u *url.URL) (*url.URL, bool) {
if !mux.shouldRedirect(host, path) {
return u, false
}
path = path + "/"
@ -2221,16 +2229,29 @@ func (mux *ServeMux) redirectToPathSlash(path string, u *url.URL) (*url.URL, boo
return u, true
}
// shouldRedirect reports whether the given path should be redirected to
// shouldRedirect reports whether the given path and host should be redirected to
// path+"/". This should happen if a handler is registered for path+"/" but
// not path -- see comments at ServeMux.
func (mux *ServeMux) shouldRedirect(path string) bool {
if _, exist := mux.m[path]; exist {
func (mux *ServeMux) shouldRedirect(host, path string) bool {
p := []string{path, host + path}
for _, c := range p {
if _, exist := mux.m[c]; exist {
return false
}
}
n := len(path)
if n == 0 {
return false
}
n := len(path)
_, exist := mux.m[path+"/"]
return n > 0 && path[n-1] != '/' && exist
for _, c := range p {
if _, exist := mux.m[c+"/"]; exist {
return path[n-1] != '/'
}
}
return false
}
// Handler returns the handler to use for the given request,
@ -2255,7 +2276,7 @@ func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// If r.URL.Path is /tree and its handler is not registered,
// the /tree -> /tree/ redirect applies to CONNECT requests
// but the path canonicalization does not.
if u, ok := mux.redirectToPathSlash(r.URL.Path, r.URL); ok {
if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
@ -2269,7 +2290,7 @@ func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// If the given path is /tree and its handler is not registered,
// redirect for /tree/.
if u, ok := mux.redirectToPathSlash(path, r.URL); ok {
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
@ -2386,9 +2407,17 @@ func ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error {
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
Addr string // TCP address to listen on, ":http" if empty
Handler Handler // handler to invoke, http.DefaultServeMux if nil
TLSConfig *tls.Config // optional TLS config, used by ServeTLS and ListenAndServeTLS
Addr string // TCP address to listen on, ":http" if empty
Handler Handler // handler to invoke, http.DefaultServeMux if nil
// TLSConfig optionally provides a TLS configuration for use
// by ServeTLS and ListenAndServeTLS. Note that this value is
// cloned by ServeTLS and ListenAndServeTLS, so it's not
// possible to modify the configuration with methods like
// tls.Config.SetSessionTicketKeys. To use
// SetSessionTicketKeys, use Server.Serve with a TLS Listener
// instead.
TLSConfig *tls.Config
// ReadTimeout is the maximum duration for reading the entire
// request, including the body.

View File

@ -73,6 +73,15 @@ const DefaultMaxIdleConnsPerHost = 2
// and how the Transport is configured. The DefaultTransport supports HTTP/2.
// To explicitly enable HTTP/2 on a transport, use golang.org/x/net/http2
// and call ConfigureTransport. See the package docs for more about HTTP/2.
//
// The Transport will send CONNECT requests to a proxy for its own use
// when processing HTTPS requests, but Transport should generally not
// be used to send a CONNECT request. That is, the Request passed to
// the RoundTrip method should not have a Method of "CONNECT", as Go's
// HTTP/1.x implementation does not support full-duplex request bodies
// being written while the response body is streamed. Go's HTTP/2
// implementation does support full duplex, but many CONNECT proxies speak
// HTTP/1.x.
type Transport struct {
idleMu sync.Mutex
wantIdle bool // user has requested to close all idle conns
@ -443,7 +452,7 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
// HTTP request on a new connection. The non-nil input error is the
// error from roundTrip.
func (pc *persistConn) shouldRetryRequest(req *Request, err error) bool {
if err == http2ErrNoCachedConn {
if http2isNoCachedConnError(err) {
// Issue 16582: if the user started a bunch of
// requests at once, they can all pick the same conn
// and violate the server's max concurrent streams.
@ -646,9 +655,14 @@ var (
errTooManyIdleHost = errors.New("http: putIdleConn: too many idle connections for host")
errCloseIdleConns = errors.New("http: CloseIdleConnections called")
errReadLoopExiting = errors.New("http: persistConn.readLoop exiting")
errServerClosedIdle = errors.New("http: server closed idle connection")
errIdleConnTimeout = errors.New("http: idle connection timeout")
errNotCachingH2Conn = errors.New("http: not caching alternate protocol's connections")
// errServerClosedIdle is not seen by users for idempotent requests, but may be
// seen by a user if the server shuts down an idle connection and sends its FIN
// in flight with already-written POST body bytes from the client.
// See https://github.com/golang/go/issues/19943#issuecomment-355607646
errServerClosedIdle = errors.New("http: server closed idle connection")
)
// transportReadFromServerError is used by Transport.readLoop when the

View File

@ -96,6 +96,12 @@ func dummyRequestWithBodyNoGetBody(method string) *Request {
return req
}
// issue22091Error acts like a golang.org/x/net/http2.ErrNoCachedConn.
type issue22091Error struct{}
func (issue22091Error) IsHTTP2NoCachedConnError() {}
func (issue22091Error) Error() string { return "issue22091Error" }
func TestTransportShouldRetryRequest(t *testing.T) {
tests := []struct {
pc *persistConn
@ -123,36 +129,42 @@ func TestTransportShouldRetryRequest(t *testing.T) {
want: true,
},
3: {
pc: nil,
req: nil,
err: issue22091Error{}, // like an external http2ErrNoCachedConn
want: true,
},
4: {
pc: &persistConn{reused: true},
req: dummyRequest("POST"),
err: errMissingHost,
want: false,
},
4: {
5: {
pc: &persistConn{reused: true},
req: dummyRequest("POST"),
err: transportReadFromServerError{},
want: false,
},
5: {
6: {
pc: &persistConn{reused: true},
req: dummyRequest("GET"),
err: transportReadFromServerError{},
want: true,
},
6: {
7: {
pc: &persistConn{reused: true},
req: dummyRequest("GET"),
err: errServerClosedIdle,
want: true,
},
7: {
8: {
pc: &persistConn{reused: true},
req: dummyRequestWithBody("POST"),
err: nothingWrittenError{},
want: true,
},
8: {
9: {
pc: &persistConn{reused: true},
req: dummyRequestWithBodyNoGetBody("POST"),
err: nothingWrittenError{},

View File

@ -24,7 +24,7 @@ import (
// general. Unfortunately, we need to run on kernels built without
// IPv6 support too. So probe the kernel to figure it out.
func (p *ipStackCapabilities) probe() {
s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
s, err := sysSocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
switch err {
case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
case nil:
@ -48,7 +48,7 @@ func (p *ipStackCapabilities) probe() {
probes = probes[:1]
}
for i := range probes {
s, err := socketFunc(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
s, err := sysSocket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if err != nil {
continue
}

View File

@ -8,6 +8,7 @@ import (
"context"
"internal/nettrace"
"internal/singleflight"
"sync"
)
// protocols contains minimal mappings between internet protocol
@ -53,6 +54,10 @@ var services = map[string]map[string]int{
},
}
// dnsWaitGroup can be used by tests to wait for all DNS goroutines to
// complete. This avoids races on the test hooks.
var dnsWaitGroup sync.WaitGroup
const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow
func lookupProtocolMap(name string) (int, error) {
@ -189,9 +194,14 @@ func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, err
resolverFunc = alt
}
ch := lookupGroup.DoChan(host, func() (interface{}, error) {
dnsWaitGroup.Add(1)
ch, called := lookupGroup.DoChan(host, func() (interface{}, error) {
defer dnsWaitGroup.Done()
return testHookLookupIP(ctx, resolverFunc, host)
})
if !called {
dnsWaitGroup.Done()
}
select {
case <-ctx.Done():

View File

@ -105,6 +105,8 @@ func TestLookupGmailMX(t *testing.T) {
t.Skip("IPv4 is required")
}
defer dnsWaitGroup.Wait()
for _, tt := range lookupGmailMXTests {
mxs, err := LookupMX(tt.name)
if err != nil {
@ -137,6 +139,8 @@ func TestLookupGmailNS(t *testing.T) {
t.Skip("IPv4 is required")
}
defer dnsWaitGroup.Wait()
for _, tt := range lookupGmailNSTests {
nss, err := LookupNS(tt.name)
if err != nil {
@ -170,6 +174,8 @@ func TestLookupGmailTXT(t *testing.T) {
t.Skip("IPv4 is required")
}
defer dnsWaitGroup.Wait()
for _, tt := range lookupGmailTXTTests {
txts, err := LookupTXT(tt.name)
if err != nil {
@ -205,6 +211,8 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
t.Skip("both IPv4 and IPv6 are required")
}
defer dnsWaitGroup.Wait()
for _, tt := range lookupGooglePublicDNSAddrTests {
names, err := LookupAddr(tt.addr)
if err != nil {
@ -226,6 +234,8 @@ func TestLookupIPv6LinkLocalAddr(t *testing.T) {
t.Skip("IPv6 is required")
}
defer dnsWaitGroup.Wait()
addrs, err := LookupHost("localhost")
if err != nil {
t.Fatal(err)
@ -262,6 +272,8 @@ func TestLookupCNAME(t *testing.T) {
t.Skip("IPv4 is required")
}
defer dnsWaitGroup.Wait()
for _, tt := range lookupCNAMETests {
cname, err := LookupCNAME(tt.name)
if err != nil {
@ -289,6 +301,8 @@ func TestLookupGoogleHost(t *testing.T) {
t.Skip("IPv4 is required")
}
defer dnsWaitGroup.Wait()
for _, tt := range lookupGoogleHostTests {
addrs, err := LookupHost(tt.name)
if err != nil {
@ -313,6 +327,8 @@ func TestLookupLongTXT(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
}
defer dnsWaitGroup.Wait()
txts, err := LookupTXT("golang.rsc.io")
if err != nil {
t.Fatal(err)
@ -343,6 +359,8 @@ func TestLookupGoogleIP(t *testing.T) {
t.Skip("IPv4 is required")
}
defer dnsWaitGroup.Wait()
for _, tt := range lookupGoogleIPTests {
ips, err := LookupIP(tt.name)
if err != nil {
@ -378,6 +396,7 @@ var revAddrTests = []struct {
}
func TestReverseAddress(t *testing.T) {
defer dnsWaitGroup.Wait()
for i, tt := range revAddrTests {
a, err := reverseaddr(tt.Addr)
if len(tt.ErrPrefix) > 0 && err == nil {
@ -401,6 +420,8 @@ func TestDNSFlood(t *testing.T) {
t.Skip("test disabled; use -dnsflood to enable")
}
defer dnsWaitGroup.Wait()
var N = 5000
if runtime.GOOS == "darwin" {
// On Darwin this test consumes kernel threads much
@ -482,6 +503,8 @@ func TestLookupDotsWithLocalSource(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
}
defer dnsWaitGroup.Wait()
for i, fn := range []func() func(){forceGoDNS, forceCgoDNS} {
fixup := fn()
if fixup == nil {
@ -527,6 +550,8 @@ func TestLookupDotsWithRemoteSource(t *testing.T) {
t.Skip("IPv4 is required")
}
defer dnsWaitGroup.Wait()
if fixup := forceGoDNS(); fixup != nil {
testDots(t, "go")
fixup()
@ -747,6 +772,9 @@ func TestLookupNonLDH(t *testing.T) {
if runtime.GOOS == "nacl" {
t.Skip("skip on nacl")
}
defer dnsWaitGroup.Wait()
if fixup := forceGoDNS(); fixup != nil {
defer fixup()
}

Some files were not shown because too many files have changed in this diff Show More