aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Theophanes <kardianos@gmail.com>2017-02-12 15:12:52 -0800
committerBrad Fitzpatrick <bradfitz@golang.org>2017-02-13 19:22:34 +0000
commitae13ccfd6dee7113d11aba0c1c287bfa2897488d (patch)
tree5b2149c78d0fc4d2fd921e3b4091c755ab371e1a
parent7cec9a583d81a3b518795cc2a49cd6296e45c69b (diff)
downloadgo-ae13ccfd6dee7113d11aba0c1c287bfa2897488d.tar.gz
go-ae13ccfd6dee7113d11aba0c1c287bfa2897488d.zip
[release-branch.go1.8] database/sql: convert test timeouts to explicit waits with checks
When testing context cancelation behavior do not rely on context timeouts. Use explicit checks in all such tests. In closeDB convert the simple check for zero open conns with a wait loop for zero open conns. Fixes #19024 Fixes #19041 Change-Id: Iecfcc4467e91249fceb21ffd1f7c62c58140d8e9 Reviewed-on: https://go-review.googlesource.com/36902 Run-TryBot: Daniel Theophanes <kardianos@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Russ Cox <rsc@golang.org> Reviewed-on: https://go-review.googlesource.com/36917 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Daniel Theophanes <kardianos@gmail.com>
-rw-r--r--src/database/sql/sql_test.go58
1 files changed, 38 insertions, 20 deletions
diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go
index 9a1fac8c83..450e5f1f8c 100644
--- a/src/database/sql/sql_test.go
+++ b/src/database/sql/sql_test.go
@@ -153,8 +153,13 @@ func closeDB(t testing.TB, db *DB) {
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)
}
}
@@ -276,6 +281,7 @@ func TestQuery(t *testing.T) {
}
}
+// TestQueryContext tests canceling the context while scanning the rows.
func TestQueryContext(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
@@ -297,7 +303,7 @@ func TestQueryContext(t *testing.T) {
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)
@@ -331,6 +337,7 @@ func TestQueryContext(t *testing.T) {
// 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)
@@ -360,12 +367,27 @@ func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
}
}
+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
@@ -378,10 +400,15 @@ func TestQueryContextWait(t *testing.T) {
// 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)
@@ -390,6 +417,10 @@ func TestTxContextWait(t *testing.T) {
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)
}
@@ -402,12 +433,6 @@ func TestTxContextWait(t *testing.T) {
}
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) {
@@ -2738,7 +2763,6 @@ func TestIssue18429(t *testing.T) {
}()
}
wg.Wait()
- time.Sleep(milliWait * 3 * time.Millisecond)
}
// TestIssue18719 closes the context right before use. The sql.driverConn
@@ -2781,14 +2805,8 @@ func TestIssue18719(t *testing.T) {
// 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) {