mirror of
https://github.com/golang/go
synced 2024-11-18 14:04:45 -07:00
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>
This commit is contained in:
parent
3792db5183
commit
50ab37d008
@ -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,26 @@ 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, cancel := context.WithTimeout(context.Background(), 15*time.Millisecond)
|
||||
// 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
|
||||
@ -379,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)
|
||||
@ -392,6 +418,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)
|
||||
}
|
||||
|
||||
@ -404,12 +434,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) {
|
||||
@ -2740,7 +2764,6 @@ func TestIssue18429(t *testing.T) {
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
time.Sleep(milliWait * 3 * time.Millisecond)
|
||||
}
|
||||
|
||||
// TestIssue18719 closes the context right before use. The sql.driverConn
|
||||
@ -2783,14 +2806,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) {
|
||||
|
Loading…
Reference in New Issue
Block a user