libgo: update to Go1.10beta2 release
Reviewed-on: https://go-review.googlesource.com/87897 From-SVN: r256794
This commit is contained in:
parent
9bff008691
commit
c6d6367f84
139 changed files with 6574 additions and 5042 deletions
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
go1.10beta1
|
||||
go1.10beta2
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
|
|
|
@ -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
|
||||
`
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
6
libgo/go/cmd/go/internal/cache/cache.go
vendored
6
libgo/go/cmd/go/internal/cache/cache.go
vendored
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
3
libgo/go/cmd/go/internal/cache/default.go
vendored
3
libgo/go/cmd/go/internal/cache/default.go
vendored
|
@ -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")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 "", ""
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
32
libgo/go/cmd/go/internal/str/path.go
Normal file
32
libgo/go/cmd/go/internal/str/path.go
Normal 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
|
||||
}
|
||||
}
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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--
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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/") {
|
||||
|
|
3
libgo/go/cmd/go/testdata/src/coverdep2/p1/p.go
vendored
Normal file
3
libgo/go/cmd/go/testdata/src/coverdep2/p1/p.go
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
package p1
|
||||
|
||||
func F() int { return 1 }
|
10
libgo/go/cmd/go/testdata/src/coverdep2/p1/p_test.go
vendored
Normal file
10
libgo/go/cmd/go/testdata/src/coverdep2/p1/p_test.go
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
package p1_test
|
||||
|
||||
import (
|
||||
"coverdep2/p2"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
p2.F()
|
||||
}
|
7
libgo/go/cmd/go/testdata/src/coverdep2/p2/p2.go
vendored
Normal file
7
libgo/go/cmd/go/testdata/src/coverdep2/p2/p2.go
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package p2
|
||||
|
||||
import "coverdep2/p1"
|
||||
|
||||
func F() {
|
||||
p1.F()
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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{},
|
||||
|
|
|
@ -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"},
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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('.')
|
||||
|
|
4
libgo/go/go/internal/gccgoimporter/testdata/alias.gox
vendored
Normal file
4
libgo/go/go/internal/gccgoimporter/testdata/alias.gox
vendored
Normal 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>; }>>;
|
13
libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.go
vendored
Normal file
13
libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.go
vendored
Normal 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) {}
|
BIN
libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox
vendored
Normal file
BIN
libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox
vendored
Normal file
Binary file not shown.
|
@ -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")
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
10
libgo/go/go/types/testdata/decls2b.src
vendored
10
libgo/go/go/types/testdata/decls2b.src
vendored
|
@ -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
|
||||
)
|
4
libgo/go/go/types/testdata/expr3.src
vendored
4
libgo/go/go/types/testdata/expr3.src
vendored
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"},
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ var attrTypeMap = map[string]contentType{
|
|||
"src": contentTypeURL,
|
||||
"srcdoc": contentTypeHTML,
|
||||
"srclang": contentTypePlain,
|
||||
"srcset": contentTypeSrcset,
|
||||
"start": contentTypePlain,
|
||||
"step": contentTypePlain,
|
||||
"style": contentTypeCSS,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&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) {
|
|||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -104,7 +116,9 @@ func TestTypedContent(t *testing.T) {
|
|||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -117,7 +131,9 @@ func TestTypedContent(t *testing.T) {
|
|||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&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 && 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/,"`,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -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="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&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&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/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -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&addressee=%28World%29`,
|
||||
// Metadata is not escaped.
|
||||
`greeting=H%69,&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
|
||||
` dir=%22ltr%22`,
|
||||
`#ZgotmplZ, World!%22%29;`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
`greeting=H%69%2c&addressee=%28World%29`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 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&addressee=%28World%29`,
|
||||
`greeting=H%69,&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&addressee=%28World%29`,
|
||||
`greeting=H%69,&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&addressee=%28World%29`,
|
||||
`greeting=H%69,&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&addressee=%28World%29`,
|
||||
`greeting=H%69,&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&addressee=%28World%29`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`%2cfoo/%2c`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -650,6 +650,12 @@ func TestEscape(t *testing.T) {
|
|||
`<{{"script"}}>{{"doEvil()"}}</{{"script"}}>`,
|
||||
`<script>doEvil()</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;".
|
||||
|
@ -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 = `<baz>`
|
||||
)
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -18,3 +18,7 @@ func hasSymlink() (ok bool, reason string) {
|
|||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func IsWindowsXP() bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
69
libgo/go/internal/testlog/log.go
Normal file
69
libgo/go/internal/testlog/log.go
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{},
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue