Expose Sleep and explicitly stop timer (#37)

This commit replaces timeSleeper with Sleep.

Previously, if the context finishes early, timeSleeper returns the
context's error and ignore the timer.
This is correct, but the GC will only be able to collect
the timer after the timer expires.
This commit explicitly stop the timer, so that the GC can claim it
immediately.
diff --git a/invoke.go b/invoke.go
index 644c677..d2134e1 100644
--- a/invoke.go
+++ b/invoke.go
@@ -45,15 +45,25 @@
 	for _, opt := range opts {
 		opt.Resolve(&settings)
 	}
-	return invoke(ctx, call, settings, timeSleeper{})
+	return invoke(ctx, call, settings, Sleep)
 }
 
-type sleeper interface {
-	// Sleep sleeps for duration d or until ctx.Done() closes, whichever happens first.
-	// If ctx.Done() closes, Sleep returns ctx.Err(), otherwise it returns nil.
-	Sleep(ctx context.Context, d time.Duration) error
+// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing.
+// If interrupted, Sleep returns ctx.Err().
+func Sleep(ctx context.Context, d time.Duration) error {
+	t := time.NewTimer(d)
+	select {
+	case <-ctx.Done():
+		t.Stop()
+		return ctx.Err()
+	case <-t.C:
+		return nil
+	}
 }
 
+type sleeper func(ctx context.Context, d time.Duration) error
+
+// invoke implements Invoke, taking an additional sleeper argument for testing.
 func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
 	var retryer Retryer
 	for {
@@ -73,19 +83,8 @@
 		}
 		if d, ok := retryer.Retry(err); !ok {
 			return err
-		} else if err = sp.Sleep(ctx, d); err != nil {
+		} else if err = sp(ctx, d); err != nil {
 			return err
 		}
 	}
 }
-
-type timeSleeper struct{}
-
-func (s timeSleeper) Sleep(ctx context.Context, d time.Duration) error {
-	select {
-	case <-ctx.Done():
-		return ctx.Err()
-	case <-time.After(d):
-		return nil
-	}
-}
diff --git a/invoke_test.go b/invoke_test.go
index 1243ef7..f181849 100644
--- a/invoke_test.go
+++ b/invoke_test.go
@@ -48,7 +48,7 @@
 // recordSleeper is a test implementation of sleeper.
 type recordSleeper int
 
-func (s *recordSleeper) Sleep(ctx context.Context, _ time.Duration) error {
+func (s *recordSleeper) sleep(ctx context.Context, _ time.Duration) error {
 	*s++
 	return ctx.Err()
 }
@@ -60,7 +60,7 @@
 func TestInvokeSuccess(t *testing.T) {
 	apiCall := func(_ context.Context) error { return nil }
 	var sp recordSleeper
-	err := invoke(context.Background(), apiCall, CallSettings{}, &sp)
+	err := invoke(context.Background(), apiCall, CallSettings{}, sp.sleep)
 
 	if err != nil {
 		t.Errorf("found error %s, want nil", err)
@@ -74,7 +74,7 @@
 	apiErr := errors.New("foo error")
 	apiCall := func(_ context.Context) error { return apiErr }
 	var sp recordSleeper
-	err := invoke(context.Background(), apiCall, CallSettings{}, &sp)
+	err := invoke(context.Background(), apiCall, CallSettings{}, sp.sleep)
 
 	if err != apiErr {
 		t.Errorf("found error %s, want %s", err, apiErr)
@@ -90,7 +90,7 @@
 	var settings CallSettings
 	WithRetry(func() Retryer { return nil }).Resolve(&settings)
 	var sp recordSleeper
-	err := invoke(context.Background(), apiCall, settings, &sp)
+	err := invoke(context.Background(), apiCall, settings, sp.sleep)
 
 	if err != apiErr {
 		t.Errorf("found error %s, want %s", err, apiErr)
@@ -106,7 +106,7 @@
 	var settings CallSettings
 	WithRetry(func() Retryer { return boolRetryer(false) }).Resolve(&settings)
 	var sp recordSleeper
-	err := invoke(context.Background(), apiCall, settings, &sp)
+	err := invoke(context.Background(), apiCall, settings, sp.sleep)
 
 	if err != apiErr {
 		t.Errorf("found error %s, want %s", err, apiErr)
@@ -131,7 +131,7 @@
 	var settings CallSettings
 	WithRetry(func() Retryer { return boolRetryer(true) }).Resolve(&settings)
 	var sp recordSleeper
-	err := invoke(context.Background(), apiCall, settings, &sp)
+	err := invoke(context.Background(), apiCall, settings, sp.sleep)
 
 	if err != nil {
 		t.Errorf("found error %s, want nil, call should have succeeded after %d tries", err, target)
@@ -148,26 +148,9 @@
 	WithRetry(func() Retryer { return boolRetryer(true) }).Resolve(&settings)
 	var sp recordSleeper
 
-	err := invoke(canceledContext, apiCall, settings, &sp)
+	err := invoke(canceledContext, apiCall, settings, sp.sleep)
 
 	if err != context.Canceled {
 		t.Errorf("found error %s, want %s", err, context.Canceled)
 	}
 }
-
-func TestTimeSleeper(t *testing.T) {
-	tests := []struct {
-		name string
-		ctx  context.Context
-		d    time.Duration
-		err  error
-	}{
-		{"background", context.Background(), 1, nil},
-		{"canceled", canceledContext, time.Hour, context.Canceled},
-	}
-	for _, tst := range tests {
-		if err := (timeSleeper{}).Sleep(tst.ctx, tst.d); err != tst.err {
-			t.Errorf("%s: got error %s, want %s", tst.name, err, tst.err)
-		}
-	}
-}