-c3935e1f20ad5b1d4c41150f11fb266913c04df7
+893f0e4a707c6f10eb14842b18954486042f0fb3
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
-2a5f65a98ca483aad2dd74dc2636a7baecc59cf2
+cd6b6202dd1559b3ac63179b45f1833fcfbe7eca
The first line of this file holds the git revision number of the
last merge done from the master library sources.
// clean remove object files
// doc show documentation for package or symbol
// env print Go environment information
-// bug print information for bug reports
+// bug start a bug report
// fix run go tool fix on packages
// fmt run gofmt on package sources
// generate generate Go files by processing source
// each named variable on its own line.
//
//
-// Print information for bug reports
+// Start a bug report
//
// Usage:
//
// go bug
//
-// Bug prints information that helps file effective bug reports.
-//
-// Bugs may be reported at https://golang.org/issue/new.
+// Bug opens the default browser and starts a new bug report.
+// The report includes useful system information.
//
//
// Run go tool fix on packages
return fmt.Errorf("cannot download, $GOPATH not set. For more details see: 'go help gopath'")
}
// Guard against people setting GOPATH=$GOROOT.
- if list[0] == goroot {
+ if filepath.Clean(list[0]) == filepath.Clean(goroot) {
return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'")
}
if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil {
}
}
-// Test go env missing GOPATH shows default.
-func TestMissingGOPATHEnvShowsDefault(t *testing.T) {
+func TestDefaultGOPATH(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
- tg.setenv("GOPATH", "")
- tg.run("env", "GOPATH")
+ tg.tempDir("home/go")
+ tg.setenv(homeEnvName(), tg.path("home"))
- want := filepath.Join(os.Getenv(homeEnvName()), "go")
- got := strings.TrimSpace(tg.getStdout())
- if got != want {
- t.Errorf("got %q; want %q", got, want)
- }
-}
-
-// Test go get missing GOPATH causes go get to warn if directory doesn't exist.
-func TestMissingGOPATHGetWarnsIfNotExists(t *testing.T) {
- testenv.MustHaveExternalNetwork(t)
-
- if _, err := exec.LookPath("git"); err != nil {
- t.Skip("skipping because git binary not found")
- }
-
- tg := testgo(t)
- defer tg.cleanup()
-
- // setenv variables for test and defer deleting temporary home directory.
- tg.setenv("GOPATH", "")
- tmp, err := ioutil.TempDir("", "")
- if err != nil {
- t.Fatalf("could not create tmp home: %v", err)
- }
- defer os.RemoveAll(tmp)
- tg.setenv(homeEnvName(), tmp)
+ tg.run("env", "GOPATH")
+ tg.grepStdout(regexp.QuoteMeta(tg.path("home/go")), "want GOPATH=$HOME/go")
- tg.run("get", "-v", "github.com/golang/example/hello")
+ tg.setenv("GOROOT", tg.path("home/go"))
+ tg.run("env", "GOPATH")
+ tg.grepStdoutNot(".", "want unset GOPATH because GOROOT=$HOME/go")
- want := fmt.Sprintf("created GOPATH=%s; see 'go help gopath'", filepath.Join(tmp, "go"))
- got := strings.TrimSpace(tg.getStderr())
- if !strings.Contains(got, want) {
- t.Errorf("got %q; want %q", got, want)
- }
+ tg.setenv("GOROOT", tg.path("home/go")+"/")
+ tg.run("env", "GOPATH")
+ tg.grepStdoutNot(".", "want unset GOPATH because GOROOT=$HOME/go/")
}
-// Test go get missing GOPATH causes no warning if directory exists.
-func TestMissingGOPATHGetDoesntWarnIfExists(t *testing.T) {
+func TestDefaultGOPATHGet(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
- if _, err := exec.LookPath("git"); err != nil {
- t.Skip("skipping because git binary not found")
- }
-
tg := testgo(t)
defer tg.cleanup()
-
- // setenv variables for test and defer resetting them.
tg.setenv("GOPATH", "")
- tmp, err := ioutil.TempDir("", "")
- if err != nil {
- t.Fatalf("could not create tmp home: %v", err)
- }
- defer os.RemoveAll(tmp)
- if err := os.Mkdir(filepath.Join(tmp, "go"), 0777); err != nil {
- t.Fatalf("could not create $HOME/go: %v", err)
- }
+ tg.tempDir("home")
+ tg.setenv(homeEnvName(), tg.path("home"))
- tg.setenv(homeEnvName(), tmp)
+ // warn for creating directory
+ tg.run("get", "-v", "github.com/golang/example/hello")
+ tg.grepStderr("created GOPATH="+regexp.QuoteMeta(tg.path("home/go"))+"; see 'go help gopath'", "did not create GOPATH")
+ // no warning if directory already exists
+ tg.must(os.RemoveAll(tg.path("home/go")))
+ tg.tempDir("home/go")
tg.run("get", "github.com/golang/example/hello")
+ tg.grepStderrNot(".", "expected no output on standard error")
- got := strings.TrimSpace(tg.getStderr())
- if got != "" {
- t.Errorf("got %q; wants empty", got)
- }
-}
-
-// Test go get missing GOPATH fails if pointed file is not a directory.
-func TestMissingGOPATHGetFailsIfItsNotDirectory(t *testing.T) {
- testenv.MustHaveExternalNetwork(t)
-
- tg := testgo(t)
- defer tg.cleanup()
-
- // setenv variables for test and defer resetting them.
- tg.setenv("GOPATH", "")
- tmp, err := ioutil.TempDir("", "")
- if err != nil {
- t.Fatalf("could not create tmp home: %v", err)
- }
- defer os.RemoveAll(tmp)
-
- path := filepath.Join(tmp, "go")
- if err := ioutil.WriteFile(path, nil, 0777); err != nil {
- t.Fatalf("could not create GOPATH at %s: %v", path, err)
- }
- tg.setenv(homeEnvName(), tmp)
-
- const pkg = "github.com/golang/example/hello"
- tg.runFail("get", pkg)
-
- msg := "not a directory"
- if runtime.GOOS == "windows" {
- msg = "The system cannot find the path specified."
- }
- want := fmt.Sprintf("package %s: mkdir %s: %s", pkg, filepath.Join(tmp, "go"), msg)
- got := strings.TrimSpace(tg.getStderr())
- if got != want {
- t.Errorf("got %q; wants %q", got, want)
- }
+ // error if $HOME/go is a file
+ tg.must(os.RemoveAll(tg.path("home/go")))
+ tg.tempFile("home/go", "")
+ tg.runFail("get", "github.com/golang/example/hello")
+ tg.grepStderr(`mkdir .*[/\\]go: .*(not a directory|cannot find the path)`, "expected error because $HOME/go is a file")
}
-// Test go install of missing package when missing GOPATH fails and shows default GOPATH.
-func TestMissingGOPATHInstallMissingPackageFailsAndShowsDefault(t *testing.T) {
+func TestDefaultGOPATHPrintedSearchList(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
-
- // setenv variables for test and defer resetting them.
tg.setenv("GOPATH", "")
- tmp, err := ioutil.TempDir("", "")
- if err != nil {
- t.Fatalf("could not create tmp home: %v", err)
- }
- defer os.RemoveAll(tmp)
- if err := os.Mkdir(filepath.Join(tmp, "go"), 0777); err != nil {
- t.Fatalf("could not create $HOME/go: %v", err)
- }
- tg.setenv(homeEnvName(), tmp)
-
- const pkg = "github.com/golang/example/hello"
- tg.runFail("install", pkg)
-
- pkgPath := filepath.Join(strings.Split(pkg, "/")...)
- want := fmt.Sprintf("can't load package: package %s: cannot find package \"%s\" in any of:", pkg, pkg) +
- fmt.Sprintf("\n\t%s (from $GOROOT)", filepath.Join(runtime.GOROOT(), "src", pkgPath)) +
- fmt.Sprintf("\n\t%s (from $GOPATH)", filepath.Join(tmp, "go", "src", pkgPath))
+ tg.tempDir("home")
+ tg.setenv(homeEnvName(), tg.path("home"))
- got := strings.TrimSpace(tg.getStderr())
- if got != want {
- t.Errorf("got %q; wants %q", got, want)
- }
+ tg.runFail("install", "github.com/golang/example/hello")
+ tg.grepStderr(regexp.QuoteMeta(tg.path("home/go/src/github.com/golang/example/hello"))+`.*from \$GOPATH`, "expected default GOPATH")
}
// Issue 4186. go get cannot be used to download packages to $GOROOT.
// Test that without GOPATH set, go get should fail.
-func TestWithoutGOPATHGoGetFails(t *testing.T) {
+func TestGoGetIntoGOROOT(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempDir("src")
- tg.setenv("GOPATH", "")
+
+ // Fails because GOROOT=GOPATH
+ tg.setenv("GOPATH", tg.path("."))
tg.setenv("GOROOT", tg.path("."))
- tg.runFail("get", "-d", "golang.org/x/codereview/cmd/hgpatch")
-}
+ tg.runFail("get", "-d", "github.com/golang/example/hello")
+ tg.grepStderr("warning: GOPATH set to GOROOT", "go should detect GOPATH=GOROOT")
+ tg.grepStderr(`\$GOPATH must not be set to \$GOROOT`, "go should detect GOPATH=GOROOT")
-// Test that with GOPATH=$GOROOT, go get should fail.
-func TestWithGOPATHEqualsGOROOTGoGetFails(t *testing.T) {
- testenv.MustHaveExternalNetwork(t)
+ // Fails because GOROOT=GOPATH after cleaning.
+ tg.setenv("GOPATH", tg.path(".")+"/")
+ tg.setenv("GOROOT", tg.path("."))
+ tg.runFail("get", "-d", "github.com/golang/example/hello")
+ tg.grepStderr("warning: GOPATH set to GOROOT", "go should detect GOPATH=GOROOT")
+ tg.grepStderr(`\$GOPATH must not be set to \$GOROOT`, "go should detect GOPATH=GOROOT")
- tg := testgo(t)
- defer tg.cleanup()
- tg.parallel()
- tg.tempDir("src")
tg.setenv("GOPATH", tg.path("."))
- tg.setenv("GOROOT", tg.path("."))
- tg.runFail("get", "-d", "golang.org/x/codereview/cmd/hgpatch")
+ tg.setenv("GOROOT", tg.path(".")+"/")
+ tg.runFail("get", "-d", "github.com/golang/example/hello")
+ tg.grepStderr("warning: GOPATH set to GOROOT", "go should detect GOPATH=GOROOT")
+ tg.grepStderr(`\$GOPATH must not be set to \$GOROOT`, "go should detect GOPATH=GOROOT")
+
+ // Fails because GOROOT=$HOME/go so default GOPATH unset.
+ tg.tempDir("home/go")
+ tg.setenv(homeEnvName(), tg.path("home"))
+ tg.setenv("GOPATH", "")
+ tg.setenv("GOROOT", tg.path("home/go"))
+ tg.runFail("get", "-d", "github.com/golang/example/hello")
+ tg.grepStderr(`\$GOPATH not set`, "expected GOPATH not set")
+
+ tg.setenv(homeEnvName(), tg.path("home")+"/")
+ tg.setenv("GOPATH", "")
+ tg.setenv("GOROOT", tg.path("home/go"))
+ tg.runFail("get", "-d", "github.com/golang/example/hello")
+ tg.grepStderr(`\$GOPATH not set`, "expected GOPATH not set")
+
+ tg.setenv(homeEnvName(), tg.path("home"))
+ tg.setenv("GOPATH", "")
+ tg.setenv("GOROOT", tg.path("home/go")+"/")
+ tg.runFail("get", "-d", "github.com/golang/example/hello")
+ tg.grepStderr(`\$GOPATH not set`, "expected GOPATH not set")
}
func TestLdflagsArgumentsWithSpacesIssue3941(t *testing.T) {
tg.grepBoth(okPattern, "go test did not say ok")
}
+// Issue 18845
+func TestBenchTimeout(t *testing.T) {
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.run("test", "-bench", ".", "-timeout", "750ms", "testdata/timeoutbench_test.go")
+}
+
func TestLinkXImportPathEscape(t *testing.T) {
// golang.org/issue/16710
tg := testgo(t)
// Diagnose common mistake: GOPATH==GOROOT.
// This setting is equivalent to not setting GOPATH at all,
// which is not what most people want when they do it.
- if gopath := buildContext.GOPATH; gopath == runtime.GOROOT() {
+ if gopath := buildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(runtime.GOROOT()) {
fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
} else {
for _, p := range filepath.SplitList(gopath) {
// Possible certificate files; stop after finding one.
var certFiles = []string{
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
- "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
"/etc/ssl/ca-bundle.pem", // OpenSUSE
"/etc/pki/tls/cacert.pem", // OpenELEC
+ "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
}
return nil, err
}
- resi, err := execer.Exec(query, dargs)
- if err == nil {
- select {
- default:
- case <-ctx.Done():
- return resi, ctx.Err()
- }
+ select {
+ default:
+ case <-ctx.Done():
+ return nil, ctx.Err()
}
- return resi, err
+ return execer.Exec(query, dargs)
}
func ctxDriverQuery(ctx context.Context, queryer driver.Queryer, query string, nvdargs []driver.NamedValue) (driver.Rows, error) {
return nil, err
}
- rowsi, err := queryer.Query(query, dargs)
- if err == nil {
- select {
- default:
- case <-ctx.Done():
- rowsi.Close()
- return nil, ctx.Err()
- }
+ select {
+ default:
+ case <-ctx.Done():
+ return nil, ctx.Err()
}
- return rowsi, err
+ return queryer.Query(query, dargs)
}
func ctxDriverStmtExec(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Result, error) {
return nil, err
}
- resi, err := si.Exec(dargs)
- if err == nil {
- select {
- default:
- case <-ctx.Done():
- return resi, ctx.Err()
- }
+ select {
+ default:
+ case <-ctx.Done():
+ return nil, ctx.Err()
}
- return resi, err
+ return si.Exec(dargs)
}
func ctxDriverStmtQuery(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Rows, error) {
return nil, err
}
- rowsi, err := si.Query(dargs)
- if err == nil {
- select {
- default:
- case <-ctx.Done():
- rowsi.Close()
- return nil, ctx.Err()
- }
+ select {
+ default:
+ case <-ctx.Done():
+ return nil, ctx.Err()
}
- return rowsi, err
+ return si.Query(dargs)
}
var errLevelNotSupported = errors.New("sql: selected isolation level is not supported")
mu sync.Mutex // protects following fields
freeConn []*driverConn
- connRequests []chan connRequest
- numOpen int // number of opened and pending open connections
+ connRequests map[uint64]chan connRequest
+ nextRequest uint64 // Next key to use in connRequests.
+ numOpen int // number of opened and pending open connections
// Used to signal the need for new connections
// a goroutine running connectionOpener() reads on this chan and
// maybeOpenNewConnections sends on the chan (one send per needed connection)
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
db := &DB{
- driver: driveri,
- dsn: dataSourceName,
- openerCh: make(chan struct{}, connectionRequestQueueSize),
- lastPut: make(map[*driverConn]string),
+ driver: driveri,
+ dsn: dataSourceName,
+ openerCh: make(chan struct{}, connectionRequestQueueSize),
+ lastPut: make(map[*driverConn]string),
+ connRequests: make(map[uint64]chan connRequest),
}
go db.connectionOpener()
return db, nil
var errDBClosed = errors.New("sql: database is closed")
+// nextRequestKeyLocked returns the next connection request key.
+// It is assumed that nextRequest will not overflow.
+func (db *DB) nextRequestKeyLocked() uint64 {
+ next := db.nextRequest
+ db.nextRequest++
+ return next
+}
+
// conn returns a newly-opened or cached *driverConn.
func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn, error) {
db.mu.Lock()
// Make the connRequest channel. It's buffered so that the
// connectionOpener doesn't block while waiting for the req to be read.
req := make(chan connRequest, 1)
- db.connRequests = append(db.connRequests, req)
+ reqKey := db.nextRequestKeyLocked()
+ db.connRequests[reqKey] = req
db.mu.Unlock()
// Timeout the connection request with the context.
select {
case <-ctx.Done():
+ // Remove the connection request and ensure no value has been sent
+ // on it after removing.
+ db.mu.Lock()
+ delete(db.connRequests, reqKey)
+ db.mu.Unlock()
+ select {
+ default:
+ case ret, ok := <-req:
+ if ok {
+ db.putConn(ret.conn, ret.err)
+ }
+ }
return nil, ctx.Err()
case ret, ok := <-req:
if !ok {
return false
}
if c := len(db.connRequests); c > 0 {
- req := db.connRequests[0]
- // This copy is O(n) but in practice faster than a linked list.
- // TODO: consider compacting it down less often and
- // moving the base instead?
- copy(db.connRequests, db.connRequests[1:])
- db.connRequests = db.connRequests[:c-1]
+ var req chan connRequest
+ var reqKey uint64
+ for reqKey, req = range db.connRequests {
+ break
+ }
+ delete(db.connRequests, reqKey) // Remove from pending requests.
if err == nil {
dc.inUse = true
}
dc *driverConn // owned; must call releaseConn when closed to release
releaseConn func(error)
rowsi driver.Rows
+ cancel func() // called when Rows is closed, may be nil.
+ closeStmt *driverStmt // if non-nil, statement to Close on close
- // closed value is 1 when the Rows is closed.
- // Use atomic operations on value when checking value.
- closed int32
- cancel func() // called when Rows is closed, may be nil.
- lastcols []driver.Value
- lasterr error // non-nil only if closed is true
- closeStmt *driverStmt // if non-nil, statement to Close on close
+ // closemu prevents Rows from closing while there
+ // is an active streaming result. It is held for read during non-close operations
+ // and exclusively during close.
+ //
+ // closemu guards lasterr and closed.
+ closemu sync.RWMutex
+ closed bool
+ lasterr error // non-nil only if closed is true
+
+ // lastcols is only used in Scan, Next, and NextResultSet which are expected
+ // not not be called concurrently.
+ lastcols []driver.Value
}
func (rs *Rows) initContextClose(ctx context.Context) {
// awaitDone blocks until the rows are closed or the context canceled.
func (rs *Rows) awaitDone(ctx context.Context) {
<-ctx.Done()
- rs.Close()
+ rs.close(ctx.Err())
}
// Next prepares the next result row for reading with the Scan method. It
//
// Every call to Scan, even the first one, must be preceded by a call to Next.
func (rs *Rows) Next() bool {
- if rs.isClosed() {
- return false
+ var doClose, ok bool
+ withLock(rs.closemu.RLocker(), func() {
+ doClose, ok = rs.nextLocked()
+ })
+ if doClose {
+ rs.Close()
+ }
+ return ok
+}
+
+func (rs *Rows) nextLocked() (doClose, ok bool) {
+ if rs.closed {
+ return false, false
}
if rs.lastcols == nil {
rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns()))
if rs.lasterr != nil {
// Close the connection if there is a driver error.
if rs.lasterr != io.EOF {
- rs.Close()
- return false
+ return true, false
}
nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet)
if !ok {
- rs.Close()
- return false
+ return true, false
}
// The driver is at the end of the current result set.
// Test to see if there is another result set after the current one.
// Only close Rows if there is no further result sets to read.
if !nextResultSet.HasNextResultSet() {
- rs.Close()
+ doClose = true
}
- return false
+ return doClose, false
}
- return true
+ return false, true
}
// NextResultSet prepares the next result set for reading. It returns true if
// scanning. If there are further result sets they may not have rows in the result
// set.
func (rs *Rows) NextResultSet() bool {
- if rs.isClosed() {
+ var doClose bool
+ defer func() {
+ if doClose {
+ rs.Close()
+ }
+ }()
+ rs.closemu.RLock()
+ defer rs.closemu.RUnlock()
+
+ if rs.closed {
return false
}
+
rs.lastcols = nil
nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet)
if !ok {
- rs.Close()
+ doClose = true
return false
}
rs.lasterr = nextResultSet.NextResultSet()
if rs.lasterr != nil {
- rs.Close()
+ doClose = true
return false
}
return true
// Err returns the error, if any, that was encountered during iteration.
// Err may be called after an explicit or implicit Close.
func (rs *Rows) Err() error {
+ rs.closemu.RLock()
+ defer rs.closemu.RUnlock()
if rs.lasterr == io.EOF {
return nil
}
// Columns returns an error if the rows are closed, or if the rows
// are from QueryRow and there was a deferred error.
func (rs *Rows) Columns() ([]string, error) {
- if rs.isClosed() {
+ rs.closemu.RLock()
+ defer rs.closemu.RUnlock()
+ if rs.closed {
return nil, errors.New("sql: Rows are closed")
}
if rs.rowsi == nil {
// ColumnTypes returns column information such as column type, length,
// and nullable. Some information may not be available from some drivers.
func (rs *Rows) ColumnTypes() ([]*ColumnType, error) {
- if rs.isClosed() {
+ rs.closemu.RLock()
+ defer rs.closemu.RUnlock()
+ if rs.closed {
return nil, errors.New("sql: Rows are closed")
}
if rs.rowsi == nil {
// For scanning into *bool, the source may be true, false, 1, 0, or
// string inputs parseable by strconv.ParseBool.
func (rs *Rows) Scan(dest ...interface{}) error {
- if rs.isClosed() {
+ rs.closemu.RLock()
+ if rs.closed {
+ rs.closemu.RUnlock()
return errors.New("sql: Rows are closed")
}
+ rs.closemu.RUnlock()
+
if rs.lastcols == nil {
return errors.New("sql: Scan called without calling Next")
}
// hook throug a test only mutex.
var rowsCloseHook = func() func(*Rows, *error) { return nil }
-func (rs *Rows) isClosed() bool {
- return atomic.LoadInt32(&rs.closed) != 0
-}
-
// Close closes the Rows, preventing further enumeration. If Next is called
// and returns false and there are no further result sets,
// the Rows are closed automatically and it will suffice to check the
// result of Err. Close is idempotent and does not affect the result of Err.
func (rs *Rows) Close() error {
- if !atomic.CompareAndSwapInt32(&rs.closed, 0, 1) {
+ return rs.close(nil)
+}
+
+func (rs *Rows) close(err error) error {
+ rs.closemu.Lock()
+ defer rs.closemu.Unlock()
+
+ if rs.closed {
return nil
}
+ rs.closed = true
+
+ if rs.lasterr == nil {
+ rs.lasterr = err
+ }
- err := rs.rowsi.Close()
+ err = rs.rowsi.Close()
if fn := rowsCloseHook(); fn != nil {
fn(rs, &err)
}
if err != nil {
t.Fatalf("error closing DB: %v", err)
}
- if count := db.numOpenConns(); count != 0 {
- t.Fatalf("%d connections still open after closing DB", count)
+
+ var numOpen int
+ if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+ numOpen = db.numOpenConns()
+ return numOpen == 0
+ }) {
+ t.Fatalf("%d connections still open after closing DB", numOpen)
}
}
}
}
+// TestQueryContext tests canceling the context while scanning the rows.
func TestQueryContext(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
for rows.Next() {
if index == 2 {
cancel()
- time.Sleep(10 * time.Millisecond)
+ waitForRowsClose(t, rows, 5*time.Second)
}
var r row
err = rows.Scan(&r.age, &r.name)
got = append(got, r)
index++
}
- err = rows.Err()
- if err != nil {
- t.Fatalf("Err: %v", err)
+ select {
+ case <-ctx.Done():
+ if err := ctx.Err(); err != context.Canceled {
+ t.Fatalf("context err = %v; want context.Canceled")
+ }
+ default:
+ t.Fatalf("context err = nil; want context.Canceled")
}
want := []row{
{age: 1, name: "Alice"},
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
+ waitForRowsClose(t, rows, 5*time.Second)
waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
}
+func waitForRowsClose(t *testing.T, rows *Rows, maxWait time.Duration) {
+ if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
+ rows.closemu.RLock()
+ defer rows.closemu.RUnlock()
+ return rows.closed
+ }) {
+ t.Fatal("failed to close rows")
+ }
+}
+
+// TestQueryContextWait ensures that rows and all internal statements are closed when
+// a query context is closed during execution.
func TestQueryContextWait(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
prepares0 := numPrepares(t, db)
- ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*15)
+ // TODO(kardianos): convert this from using a timeout to using an explicit
+ // cancel when the query signals that is is "executing" the query.
+ ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
+ defer cancel()
// This will trigger the *fakeConn.Prepare method which will take time
// performing the query. The ctxDriverPrepare func will check the context
// Verify closed rows connection after error condition.
waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
- t.Errorf("executed %d Prepare statements; want 1", prepares)
+ // TODO(kardianos): if the context timeouts before the db.QueryContext
+ // executes this check may fail. After adjusting how the context
+ // is canceled above revert this back to a Fatal error.
+ t.Logf("executed %d Prepare statements; want 1", prepares)
}
}
+// TestTxContextWait tests the transaction behavior when the tx context is canceled
+// during execution of the query.
func TestTxContextWait(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
tx, err := db.BeginTx(ctx, nil)
if err != nil {
+ // Guard against the context being canceled before BeginTx completes.
+ if err == context.DeadlineExceeded {
+ t.Skip("tx context canceled prior to first use")
+ }
t.Fatal(err)
}
}
waitForFree(t, db, 5*time.Second, 0)
-
- // Ensure the dropped connection allows more connections to be made.
- // Checked on DB Close.
- waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
- return db.numOpenConns() == 0
- })
}
func TestMultiResultSetQuery(t *testing.T) {
}
}
+func TestPoolExhaustOnCancel(t *testing.T) {
+ if testing.Short() {
+ t.Skip("long test")
+ }
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ max := 3
+
+ db.SetMaxOpenConns(max)
+
+ // First saturate the connection pool.
+ // Then start new requests for a connection that is cancelled after it is requested.
+
+ var saturate, saturateDone sync.WaitGroup
+ saturate.Add(max)
+ saturateDone.Add(max)
+
+ for i := 0; i < max; i++ {
+ go func() {
+ saturate.Done()
+ rows, err := db.Query("WAIT|500ms|SELECT|people|name,photo|")
+ if err != nil {
+ t.Fatalf("Query: %v", err)
+ }
+ rows.Close()
+ saturateDone.Done()
+ }()
+ }
+
+ saturate.Wait()
+
+ // Now cancel the request while it is waiting.
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
+ defer cancel()
+
+ for i := 0; i < max; i++ {
+ ctxReq, cancelReq := context.WithCancel(ctx)
+ go func() {
+ time.Sleep(time.Millisecond * 100)
+ cancelReq()
+ }()
+ err := db.PingContext(ctxReq)
+ if err != context.Canceled {
+ t.Fatalf("PingContext (Exhaust): %v", err)
+ }
+ }
+
+ saturateDone.Wait()
+
+ // Now try to open a normal connection.
+ err := db.PingContext(ctx)
+ if err != nil {
+ t.Fatalf("PingContext (Normal): %v", err)
+ }
+}
+
func TestByteOwnership(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
}()
}
wg.Wait()
- time.Sleep(milliWait * 3 * time.Millisecond)
}
// TestIssue18719 closes the context right before use. The sql.driverConn
// Do not explicitly rollback. The rollback will happen from the
// canceled context.
- // Wait for connections to return to pool.
- var numOpen int
- if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
- numOpen = db.numOpenConns()
- return numOpen == 0
- }) {
- t.Fatalf("open conns after hitting EOF = %d; want 0", numOpen)
- }
+ cancel()
+ waitForRowsClose(t, rows, 5*time.Second)
}
func TestConcurrency(t *testing.T) {
var ddBytes = []byte("--")
+// indirect drills into interfaces and pointers, returning the pointed-at value.
+// If it encounters a nil interface or pointer, indirect returns that nil value.
+// This can turn into an infinite loop given a cyclic chain,
+// but it matches the Go 1 behavior.
+func indirect(vf reflect.Value) reflect.Value {
+ for vf.Kind() == reflect.Interface || vf.Kind() == reflect.Ptr {
+ if vf.IsNil() {
+ return vf
+ }
+ vf = vf.Elem()
+ }
+ return vf
+}
+
func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
s := parentStack{p: p}
for i := range tinfo.fields {
continue
}
}
- // Drill into interfaces and pointers.
- // This can turn into an infinite loop given a cyclic chain,
- // but it matches the Go 1 behavior.
- for vf.Kind() == reflect.Interface || vf.Kind() == reflect.Ptr {
- if vf.IsNil() {
- return nil
- }
- vf = vf.Elem()
- }
var scratch [64]byte
+ vf = indirect(vf)
switch vf.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if err := emit(p, strconv.AppendInt(scratch[:0], vf.Int(), 10)); err != nil {
if err := s.trim(finfo.parents); err != nil {
return err
}
+ vf = indirect(vf)
k := vf.Kind()
if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) {
return fmt.Errorf("xml: bad type for comment field of %s", val.Type())
continue
case fInnerXml:
+ vf = indirect(vf)
iface := vf.Interface()
switch raw := iface.(type) {
case []byte:
return &x
}
+func stringptr(x string) *string {
+ return &x
+}
+
+type T1 struct{}
+type T2 struct{}
+type T3 struct{}
+
+type IndirComment struct {
+ T1 T1
+ Comment *string `xml:",comment"`
+ T2 T2
+}
+
+type DirectComment struct {
+ T1 T1
+ Comment string `xml:",comment"`
+ T2 T2
+}
+
+type IfaceComment struct {
+ T1 T1
+ Comment interface{} `xml:",comment"`
+ T2 T2
+}
+
+type IndirChardata struct {
+ T1 T1
+ Chardata *string `xml:",chardata"`
+ T2 T2
+}
+
+type DirectChardata struct {
+ T1 T1
+ Chardata string `xml:",chardata"`
+ T2 T2
+}
+
+type IfaceChardata struct {
+ T1 T1
+ Chardata interface{} `xml:",chardata"`
+ T2 T2
+}
+
+type IndirCDATA struct {
+ T1 T1
+ CDATA *string `xml:",cdata"`
+ T2 T2
+}
+
+type DirectCDATA struct {
+ T1 T1
+ CDATA string `xml:",cdata"`
+ T2 T2
+}
+
+type IfaceCDATA struct {
+ T1 T1
+ CDATA interface{} `xml:",cdata"`
+ T2 T2
+}
+
+type IndirInnerXML struct {
+ T1 T1
+ InnerXML *string `xml:",innerxml"`
+ T2 T2
+}
+
+type DirectInnerXML struct {
+ T1 T1
+ InnerXML string `xml:",innerxml"`
+ T2 T2
+}
+
+type IfaceInnerXML struct {
+ T1 T1
+ InnerXML interface{} `xml:",innerxml"`
+ T2 T2
+}
+
+type IndirElement struct {
+ T1 T1
+ Element *string
+ T2 T2
+}
+
+type DirectElement struct {
+ T1 T1
+ Element string
+ T2 T2
+}
+
+type IfaceElement struct {
+ T1 T1
+ Element interface{}
+ T2 T2
+}
+
+type IndirOmitEmpty struct {
+ T1 T1
+ OmitEmpty *string `xml:",omitempty"`
+ T2 T2
+}
+
+type DirectOmitEmpty struct {
+ T1 T1
+ OmitEmpty string `xml:",omitempty"`
+ T2 T2
+}
+
+type IfaceOmitEmpty struct {
+ T1 T1
+ OmitEmpty interface{} `xml:",omitempty"`
+ T2 T2
+}
+
+type IndirAny struct {
+ T1 T1
+ Any *string `xml:",any"`
+ T2 T2
+}
+
+type DirectAny struct {
+ T1 T1
+ Any string `xml:",any"`
+ T2 T2
+}
+
+type IfaceAny struct {
+ T1 T1
+ Any interface{} `xml:",any"`
+ T2 T2
+}
+
var (
nameAttr = "Sarah"
ageAttr = uint(12)
// please try to make them two-way as well to ensure that
// marshaling and unmarshaling are as symmetrical as feasible.
var marshalTests = []struct {
- Value interface{}
- ExpectXML string
- MarshalOnly bool
- UnmarshalOnly bool
+ Value interface{}
+ ExpectXML string
+ MarshalOnly bool
+ MarshalError string
+ UnmarshalOnly bool
+ UnmarshalError string
}{
// Test nil marshals to nothing
{Value: nil, ExpectXML: ``, MarshalOnly: true},
ExpectXML: `<NestedAndCData><A><B></B><B></B></A><![CDATA[test]]></NestedAndCData>`,
Value: &NestedAndCData{AB: make([]string, 2), CDATA: "test"},
},
+ // Test pointer indirection in various kinds of fields.
+ // https://golang.org/issue/19063
+ {
+ ExpectXML: `<IndirComment><T1></T1><!--hi--><T2></T2></IndirComment>`,
+ Value: &IndirComment{Comment: stringptr("hi")},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IndirComment><T1></T1><T2></T2></IndirComment>`,
+ Value: &IndirComment{Comment: stringptr("")},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IndirComment><T1></T1><T2></T2></IndirComment>`,
+ Value: &IndirComment{Comment: nil},
+ MarshalError: "xml: bad type for comment field of xml.IndirComment",
+ },
+ {
+ ExpectXML: `<IndirComment><T1></T1><!--hi--><T2></T2></IndirComment>`,
+ Value: &IndirComment{Comment: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceComment><T1></T1><!--hi--><T2></T2></IfaceComment>`,
+ Value: &IfaceComment{Comment: "hi"},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceComment><T1></T1><!--hi--><T2></T2></IfaceComment>`,
+ Value: &IfaceComment{Comment: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceComment><T1></T1><T2></T2></IfaceComment>`,
+ Value: &IfaceComment{Comment: nil},
+ MarshalError: "xml: bad type for comment field of xml.IfaceComment",
+ },
+ {
+ ExpectXML: `<IfaceComment><T1></T1><T2></T2></IfaceComment>`,
+ Value: &IfaceComment{Comment: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectComment><T1></T1><!--hi--><T2></T2></DirectComment>`,
+ Value: &DirectComment{Comment: string("hi")},
+ },
+ {
+ ExpectXML: `<DirectComment><T1></T1><T2></T2></DirectComment>`,
+ Value: &DirectComment{Comment: string("")},
+ },
+ {
+ ExpectXML: `<IndirChardata><T1></T1>hi<T2></T2></IndirChardata>`,
+ Value: &IndirChardata{Chardata: stringptr("hi")},
+ },
+ {
+ ExpectXML: `<IndirChardata><T1></T1><![CDATA[hi]]><T2></T2></IndirChardata>`,
+ Value: &IndirChardata{Chardata: stringptr("hi")},
+ UnmarshalOnly: true, // marshals without CDATA
+ },
+ {
+ ExpectXML: `<IndirChardata><T1></T1><T2></T2></IndirChardata>`,
+ Value: &IndirChardata{Chardata: stringptr("")},
+ },
+ {
+ ExpectXML: `<IndirChardata><T1></T1><T2></T2></IndirChardata>`,
+ Value: &IndirChardata{Chardata: nil},
+ MarshalOnly: true, // unmarshal leaves Chardata=stringptr("")
+ },
+ {
+ ExpectXML: `<IfaceChardata><T1></T1>hi<T2></T2></IfaceChardata>`,
+ Value: &IfaceChardata{Chardata: string("hi")},
+ UnmarshalError: "cannot unmarshal into interface {}",
+ },
+ {
+ ExpectXML: `<IfaceChardata><T1></T1><![CDATA[hi]]><T2></T2></IfaceChardata>`,
+ Value: &IfaceChardata{Chardata: string("hi")},
+ UnmarshalOnly: true, // marshals without CDATA
+ UnmarshalError: "cannot unmarshal into interface {}",
+ },
+ {
+ ExpectXML: `<IfaceChardata><T1></T1><T2></T2></IfaceChardata>`,
+ Value: &IfaceChardata{Chardata: string("")},
+ UnmarshalError: "cannot unmarshal into interface {}",
+ },
+ {
+ ExpectXML: `<IfaceChardata><T1></T1><T2></T2></IfaceChardata>`,
+ Value: &IfaceChardata{Chardata: nil},
+ UnmarshalError: "cannot unmarshal into interface {}",
+ },
+ {
+ ExpectXML: `<DirectChardata><T1></T1>hi<T2></T2></DirectChardata>`,
+ Value: &DirectChardata{Chardata: string("hi")},
+ },
+ {
+ ExpectXML: `<DirectChardata><T1></T1><![CDATA[hi]]><T2></T2></DirectChardata>`,
+ Value: &DirectChardata{Chardata: string("hi")},
+ UnmarshalOnly: true, // marshals without CDATA
+ },
+ {
+ ExpectXML: `<DirectChardata><T1></T1><T2></T2></DirectChardata>`,
+ Value: &DirectChardata{Chardata: string("")},
+ },
+ {
+ ExpectXML: `<IndirCDATA><T1></T1><![CDATA[hi]]><T2></T2></IndirCDATA>`,
+ Value: &IndirCDATA{CDATA: stringptr("hi")},
+ },
+ {
+ ExpectXML: `<IndirCDATA><T1></T1>hi<T2></T2></IndirCDATA>`,
+ Value: &IndirCDATA{CDATA: stringptr("hi")},
+ UnmarshalOnly: true, // marshals with CDATA
+ },
+ {
+ ExpectXML: `<IndirCDATA><T1></T1><T2></T2></IndirCDATA>`,
+ Value: &IndirCDATA{CDATA: stringptr("")},
+ },
+ {
+ ExpectXML: `<IndirCDATA><T1></T1><T2></T2></IndirCDATA>`,
+ Value: &IndirCDATA{CDATA: nil},
+ MarshalOnly: true, // unmarshal leaves CDATA=stringptr("")
+ },
+ {
+ ExpectXML: `<IfaceCDATA><T1></T1><![CDATA[hi]]><T2></T2></IfaceCDATA>`,
+ Value: &IfaceCDATA{CDATA: string("hi")},
+ UnmarshalError: "cannot unmarshal into interface {}",
+ },
+ {
+ ExpectXML: `<IfaceCDATA><T1></T1>hi<T2></T2></IfaceCDATA>`,
+ Value: &IfaceCDATA{CDATA: string("hi")},
+ UnmarshalOnly: true, // marshals with CDATA
+ UnmarshalError: "cannot unmarshal into interface {}",
+ },
+ {
+ ExpectXML: `<IfaceCDATA><T1></T1><T2></T2></IfaceCDATA>`,
+ Value: &IfaceCDATA{CDATA: string("")},
+ UnmarshalError: "cannot unmarshal into interface {}",
+ },
+ {
+ ExpectXML: `<IfaceCDATA><T1></T1><T2></T2></IfaceCDATA>`,
+ Value: &IfaceCDATA{CDATA: nil},
+ UnmarshalError: "cannot unmarshal into interface {}",
+ },
+ {
+ ExpectXML: `<DirectCDATA><T1></T1><![CDATA[hi]]><T2></T2></DirectCDATA>`,
+ Value: &DirectCDATA{CDATA: string("hi")},
+ },
+ {
+ ExpectXML: `<DirectCDATA><T1></T1>hi<T2></T2></DirectCDATA>`,
+ Value: &DirectCDATA{CDATA: string("hi")},
+ UnmarshalOnly: true, // marshals with CDATA
+ },
+ {
+ ExpectXML: `<DirectCDATA><T1></T1><T2></T2></DirectCDATA>`,
+ Value: &DirectCDATA{CDATA: string("")},
+ },
+ {
+ ExpectXML: `<IndirInnerXML><T1></T1><hi/><T2></T2></IndirInnerXML>`,
+ Value: &IndirInnerXML{InnerXML: stringptr("<hi/>")},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IndirInnerXML><T1></T1><T2></T2></IndirInnerXML>`,
+ Value: &IndirInnerXML{InnerXML: stringptr("")},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IndirInnerXML><T1></T1><T2></T2></IndirInnerXML>`,
+ Value: &IndirInnerXML{InnerXML: nil},
+ },
+ {
+ ExpectXML: `<IndirInnerXML><T1></T1><hi/><T2></T2></IndirInnerXML>`,
+ Value: &IndirInnerXML{InnerXML: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceInnerXML><T1></T1><hi/><T2></T2></IfaceInnerXML>`,
+ Value: &IfaceInnerXML{InnerXML: "<hi/>"},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceInnerXML><T1></T1><hi/><T2></T2></IfaceInnerXML>`,
+ Value: &IfaceInnerXML{InnerXML: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceInnerXML><T1></T1><T2></T2></IfaceInnerXML>`,
+ Value: &IfaceInnerXML{InnerXML: nil},
+ },
+ {
+ ExpectXML: `<IfaceInnerXML><T1></T1><T2></T2></IfaceInnerXML>`,
+ Value: &IfaceInnerXML{InnerXML: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectInnerXML><T1></T1><hi/><T2></T2></DirectInnerXML>`,
+ Value: &DirectInnerXML{InnerXML: string("<hi/>")},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectInnerXML><T1></T1><hi/><T2></T2></DirectInnerXML>`,
+ Value: &DirectInnerXML{InnerXML: string("<T1></T1><hi/><T2></T2>")},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectInnerXML><T1></T1><T2></T2></DirectInnerXML>`,
+ Value: &DirectInnerXML{InnerXML: string("")},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectInnerXML><T1></T1><T2></T2></DirectInnerXML>`,
+ Value: &DirectInnerXML{InnerXML: string("<T1></T1><T2></T2>")},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IndirElement><T1></T1><Element>hi</Element><T2></T2></IndirElement>`,
+ Value: &IndirElement{Element: stringptr("hi")},
+ },
+ {
+ ExpectXML: `<IndirElement><T1></T1><Element></Element><T2></T2></IndirElement>`,
+ Value: &IndirElement{Element: stringptr("")},
+ },
+ {
+ ExpectXML: `<IndirElement><T1></T1><T2></T2></IndirElement>`,
+ Value: &IndirElement{Element: nil},
+ },
+ {
+ ExpectXML: `<IfaceElement><T1></T1><Element>hi</Element><T2></T2></IfaceElement>`,
+ Value: &IfaceElement{Element: "hi"},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceElement><T1></T1><Element>hi</Element><T2></T2></IfaceElement>`,
+ Value: &IfaceElement{Element: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceElement><T1></T1><T2></T2></IfaceElement>`,
+ Value: &IfaceElement{Element: nil},
+ },
+ {
+ ExpectXML: `<IfaceElement><T1></T1><T2></T2></IfaceElement>`,
+ Value: &IfaceElement{Element: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectElement><T1></T1><Element>hi</Element><T2></T2></DirectElement>`,
+ Value: &DirectElement{Element: string("hi")},
+ },
+ {
+ ExpectXML: `<DirectElement><T1></T1><Element></Element><T2></T2></DirectElement>`,
+ Value: &DirectElement{Element: string("")},
+ },
+ {
+ ExpectXML: `<IndirOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></IndirOmitEmpty>`,
+ Value: &IndirOmitEmpty{OmitEmpty: stringptr("hi")},
+ },
+ {
+ // Note: Changed in Go 1.8 to include <OmitEmpty> element (because x.OmitEmpty != nil).
+ ExpectXML: `<IndirOmitEmpty><T1></T1><OmitEmpty></OmitEmpty><T2></T2></IndirOmitEmpty>`,
+ Value: &IndirOmitEmpty{OmitEmpty: stringptr("")},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IndirOmitEmpty><T1></T1><OmitEmpty></OmitEmpty><T2></T2></IndirOmitEmpty>`,
+ Value: &IndirOmitEmpty{OmitEmpty: stringptr("")},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IndirOmitEmpty><T1></T1><T2></T2></IndirOmitEmpty>`,
+ Value: &IndirOmitEmpty{OmitEmpty: nil},
+ },
+ {
+ ExpectXML: `<IfaceOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></IfaceOmitEmpty>`,
+ Value: &IfaceOmitEmpty{OmitEmpty: "hi"},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></IfaceOmitEmpty>`,
+ Value: &IfaceOmitEmpty{OmitEmpty: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceOmitEmpty><T1></T1><T2></T2></IfaceOmitEmpty>`,
+ Value: &IfaceOmitEmpty{OmitEmpty: nil},
+ },
+ {
+ ExpectXML: `<IfaceOmitEmpty><T1></T1><T2></T2></IfaceOmitEmpty>`,
+ Value: &IfaceOmitEmpty{OmitEmpty: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></DirectOmitEmpty>`,
+ Value: &DirectOmitEmpty{OmitEmpty: string("hi")},
+ },
+ {
+ ExpectXML: `<DirectOmitEmpty><T1></T1><T2></T2></DirectOmitEmpty>`,
+ Value: &DirectOmitEmpty{OmitEmpty: string("")},
+ },
+ {
+ ExpectXML: `<IndirAny><T1></T1><Any>hi</Any><T2></T2></IndirAny>`,
+ Value: &IndirAny{Any: stringptr("hi")},
+ },
+ {
+ ExpectXML: `<IndirAny><T1></T1><Any></Any><T2></T2></IndirAny>`,
+ Value: &IndirAny{Any: stringptr("")},
+ },
+ {
+ ExpectXML: `<IndirAny><T1></T1><T2></T2></IndirAny>`,
+ Value: &IndirAny{Any: nil},
+ },
+ {
+ ExpectXML: `<IfaceAny><T1></T1><Any>hi</Any><T2></T2></IfaceAny>`,
+ Value: &IfaceAny{Any: "hi"},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceAny><T1></T1><Any>hi</Any><T2></T2></IfaceAny>`,
+ Value: &IfaceAny{Any: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceAny><T1></T1><T2></T2></IfaceAny>`,
+ Value: &IfaceAny{Any: nil},
+ },
+ {
+ ExpectXML: `<IfaceAny><T1></T1><T2></T2></IfaceAny>`,
+ Value: &IfaceAny{Any: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectAny><T1></T1><Any>hi</Any><T2></T2></DirectAny>`,
+ Value: &DirectAny{Any: string("hi")},
+ },
+ {
+ ExpectXML: `<DirectAny><T1></T1><Any></Any><T2></T2></DirectAny>`,
+ Value: &DirectAny{Any: string("")},
+ },
+ {
+ ExpectXML: `<IndirFoo><T1></T1><Foo>hi</Foo><T2></T2></IndirFoo>`,
+ Value: &IndirAny{Any: stringptr("hi")},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IndirFoo><T1></T1><Foo></Foo><T2></T2></IndirFoo>`,
+ Value: &IndirAny{Any: stringptr("")},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IndirFoo><T1></T1><T2></T2></IndirFoo>`,
+ Value: &IndirAny{Any: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceFoo><T1></T1><Foo>hi</Foo><T2></T2></IfaceFoo>`,
+ Value: &IfaceAny{Any: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceFoo><T1></T1><T2></T2></IfaceFoo>`,
+ Value: &IfaceAny{Any: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IfaceFoo><T1></T1><T2></T2></IfaceFoo>`,
+ Value: &IfaceAny{Any: nil},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectFoo><T1></T1><Foo>hi</Foo><T2></T2></DirectFoo>`,
+ Value: &DirectAny{Any: string("hi")},
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<DirectFoo><T1></T1><Foo></Foo><T2></T2></DirectFoo>`,
+ Value: &DirectAny{Any: string("")},
+ UnmarshalOnly: true,
+ },
}
func TestMarshal(t *testing.T) {
}
data, err := Marshal(test.Value)
if err != nil {
- t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
+ if test.MarshalError == "" {
+ t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
+ continue
+ }
+ if !strings.Contains(err.Error(), test.MarshalError) {
+ t.Errorf("#%d: marshal(%#v): %s, want %q", idx, test.Value, err, test.MarshalError)
+ }
+ continue
+ }
+ if test.MarshalError != "" {
+ t.Errorf("#%d: Marshal succeeded, want error %q", idx, test.MarshalError)
continue
}
if got, want := string(data), test.ExpectXML; got != want {
}
if err != nil {
- t.Errorf("#%d: unexpected error: %#v", i, err)
- } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
+ if test.UnmarshalError == "" {
+ t.Errorf("#%d: unmarshal(%#v): %s", i, test.ExpectXML, err)
+ continue
+ }
+ if !strings.Contains(err.Error(), test.UnmarshalError) {
+ t.Errorf("#%d: unmarshal(%#v): %s, want %q", i, test.ExpectXML, err, test.UnmarshalError)
+ }
+ continue
+ }
+ if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
}
}
}
if home := os.Getenv(env); home != "" {
def := filepath.Join(home, "go")
- if def == runtime.GOROOT() {
+ if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
// Don't set the default GOPATH to GOROOT,
// as that will trigger warnings from the go tool.
return ""
}
func TestPtrTo(t *testing.T) {
+ // This block of code means that the ptrToThis field of the
+ // reflect data for *unsafe.Pointer is non zero, see
+ // https://golang.org/issue/19003
+ var x unsafe.Pointer
+ var y = &x
+ var z = &y
+
var i int
- typ := TypeOf(i)
+ typ := TypeOf(z)
for i = 0; i < 100; i++ {
typ = PtrTo(typ)
}
for i = 0; i < 100; i++ {
typ = typ.Elem()
}
- if typ != TypeOf(i) {
- t.Errorf("after 100 PtrTo and Elem, have %s, want %s", typ, TypeOf(i))
+ if typ != TypeOf(z) {
+ t.Errorf("after 100 PtrTo and Elem, have %s, want %s", typ, TypeOf(z))
}
}
pp := *prototype
pp.string = &s
+ pp.ptrToThis = nil
// For the type structures linked into the binary, the
// compiler provides a good hash of the string.
return 0;
}
-void runCPUHogThread() {
+void runCPUHogThread(void) {
pthread_t tid;
pthread_create(&tid, 0, cpuHogDriver, 0);
}
char *p;
-static int f3() {
+static int f3(void) {
*p = 0;
return 0;
}
-static int f2() {
+static int f2(void) {
return f3();
}
-static int f1() {
+static int f1(void) {
return f2();
}
haveExamples = len(m.examples) > 0
testRan, testOk := runTests(m.deps.MatchString, m.tests)
exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
+ stopAlarm()
if !testRan && !exampleRan && *matchBenchmarks == "" {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
}
sizeof PREFLECTION - 1,
};
+extern const uintptr pointer_unsafe_Pointer_gc[]
+ __asm__ (GOSYM_PREFIX "__go_td_pN14_unsafe.Pointer$gc");
+
+const uintptr pointer_unsafe_Pointer_gc[] __attribute__((aligned(4))) =
+ {sizeof(void*), GC_APTR, 0, GC_END};
+
const struct __go_ptr_type pointer_unsafe_Pointer =
{
/* __common */
/* __equalfn */
&runtime_pointerequal_descriptor,
/* __gc */
- unsafe_Pointer_gc,
+ pointer_unsafe_Pointer_gc,
/* __reflection */
&preflection_string,
/* __uncommon */