}
}
+// pollDuration is an arbitrary interval to wait between checks when polling for
+// a condition to occur.
+const pollDuration = 5 * time.Millisecond
+
const fakeDBName = "foo"
var chrisBirthday = time.Unix(123456789, 0)
}
var numOpen int
- if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+ if !waitCondition(t, func() bool {
numOpen = db.numOpenConns()
return numOpen == 0
}) {
}
// Dependencies are closed via a goroutine, so this polls waiting for
-// numDeps to fall to want, waiting up to d.
-func (db *DB) numDepsPollUntil(want int, d time.Duration) int {
- deadline := time.Now().Add(d)
- for {
- n := db.numDeps()
- if n <= want || time.Now().After(deadline) {
- return n
- }
- time.Sleep(50 * time.Millisecond)
- }
+// numDeps to fall to want, waiting up to nearly the test's deadline.
+func (db *DB) numDepsPoll(t *testing.T, want int) int {
+ var n int
+ waitCondition(t, func() bool {
+ n = db.numDeps()
+ return n <= want
+ })
+ return n
}
func (db *DB) numFreeConns() int {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(0, time.Second); n > 0 {
+ if n := db.numDepsPoll(t, 0); n > 0 {
t.Errorf("number of dependencies = %d; expected 0", n)
db.dumpDeps(t)
}
for rows.Next() {
if index == 2 {
cancel()
- waitForRowsClose(t, rows, 5*time.Second)
+ waitForRowsClose(t, rows)
}
var r row
err = rows.Scan(&r.age, &r.name)
// 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)
+ waitForRowsClose(t, rows)
+ waitForFree(t, db, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
}
-func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
- deadline := time.Now().Add(waitFor)
- for time.Now().Before(deadline) {
+func waitCondition(t testing.TB, fn func() bool) bool {
+ timeout := 5 * time.Second
+
+ type deadliner interface {
+ Deadline() (time.Time, bool)
+ }
+ if td, ok := t.(deadliner); ok {
+ if deadline, ok := td.Deadline(); ok {
+ timeout = time.Until(deadline)
+ timeout = timeout * 19 / 20 // Give 5% headroom for cleanup and error-reporting.
+ }
+ }
+
+ deadline := time.Now().Add(timeout)
+ for {
if fn() {
return true
}
- time.Sleep(checkEvery)
+ if time.Until(deadline) < pollDuration {
+ return false
+ }
+ time.Sleep(pollDuration)
}
- return false
}
// waitForFree checks db.numFreeConns until either it equals want or
// the maxWait time elapses.
-func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
+func waitForFree(t *testing.T, db *DB, want int) {
var numFree int
- if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
+ if !waitCondition(t, func() bool {
numFree = db.numFreeConns()
return numFree == want
}) {
}
}
-func waitForRowsClose(t *testing.T, rows *Rows, maxWait time.Duration) {
- if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
+func waitForRowsClose(t *testing.T, rows *Rows) {
+ if !waitCondition(t, func() bool {
rows.closemu.RLock()
defer rows.closemu.RUnlock()
return rows.closed
}
// Verify closed rows connection after error condition.
- waitForFree(t, db, 5*time.Second, 1)
+ waitForFree(t, db, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
// TODO(kardianos): if the context timeouts before the db.QueryContext
// executes this check may fail. After adjusting how the context
t.Fatalf("expected QueryContext to error with context canceled but returned %v", err)
}
- waitForFree(t, db, 5*time.Second, 0)
+ waitForFree(t, db, 0)
}
// TestTxContextWaitNoDiscard is the same as TestTxContextWait, but should not discard
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
}
- waitForFree(t, db, 5*time.Second, 1)
+ waitForFree(t, db, 1)
}
// TestUnsupportedOptions checks that the database fails when a driver that
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
- waitForFree(t, db, 5*time.Second, 1)
+ waitForFree(t, db, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
}
}()
}
- // Sleep for twice the expected length of time for the
- // batch of 50 queries above to finish before starting
- // the next round.
- time.Sleep(2 * sleepMillis * time.Millisecond)
+ // Wait for the batch of queries above to finish before starting the next round.
+ wg.Wait()
}
- wg.Wait()
if g, w := db.numFreeConns(), 10; g != w {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(20, time.Second); n > 20 {
+ if n := db.numDepsPoll(t, 20); n > 20 {
t.Errorf("number of dependencies = %d; expected <= 20", n)
db.dumpDeps(t)
}
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(10, time.Second); n > 10 {
+ if n := db.numDepsPoll(t, 10); n > 10 {
t.Errorf("number of dependencies = %d; expected <= 10", n)
db.dumpDeps(t)
}
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(5, time.Second); n > 5 {
+ if n := db.numDepsPoll(t, 5); n > 5 {
t.Errorf("number of dependencies = %d; expected 0", n)
db.dumpDeps(t)
}
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(5, time.Second); n > 5 {
+ if n := db.numDepsPoll(t, 5); n > 5 {
t.Errorf("number of dependencies = %d; expected 0", n)
db.dumpDeps(t)
}
tx2.Commit()
// Give connectionCleaner chance to run.
- for i := 0; i < 100 && closes != 1; i++ {
- time.Sleep(time.Millisecond)
+ waitCondition(t, func() bool {
driver.mu.Lock()
opens = driver.openCount - opens0
closes = driver.closeCount - closes0
driver.mu.Unlock()
- }
+
+ return closes == 1
+ })
if opens != 3 {
t.Errorf("opens = %d; want 3", opens)
}
}()
}
- // Sleep for twice the expected length of time for the
- // batch of 50 queries above to finish before starting
- // the next round.
- time.Sleep(2 * sleepMillis * time.Millisecond)
+ // Wait for the batch of queries above to finish before starting the next round.
+ wg.Wait()
}
- wg.Wait()
if g, w := db.numFreeConns(), 2; g != w {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(4, time.Second); n > 4 {
+ if n := db.numDepsPoll(t, 4); n > 4 {
t.Errorf("number of dependencies = %d; expected <= 4", n)
db.dumpDeps(t)
}
db.dumpDeps(t)
}
- if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+ if !waitCondition(t, func() bool {
return len(stmt.css) <= nquery
}) {
t.Errorf("len(stmt.css) = %d; want <= %d", len(stmt.css), nquery)
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(2, time.Second); n > 2 {
+ if n := db.numDepsPoll(t, 2); n > 2 {
t.Errorf("number of dependencies = %d; expected <= 2", n)
db.dumpDeps(t)
}
if ct > 0 {
return
}
- time.Sleep(10 * time.Millisecond)
+ time.Sleep(pollDuration)
}
}()
// Wait for the context to cancel and tx to rollback.
for tx.isDone() == false {
- time.Sleep(3 * time.Millisecond)
+ time.Sleep(pollDuration)
}
}
defer func() { hookTxGrabConn = nil }()