Add a clock package for better time mocking (#1136)
* Add a clock package for better time mocking * Make Clock a struct so it doesn't need initialization * Test clock package * Use atomic for live time tests * Refer to same clock.Mock throughout methods
This commit is contained in:
		
							parent
							
								
									42475c28f7
								
							
						
					
					
						commit
						d3423408c7
					
				|  | @ -8,6 +8,7 @@ | ||||||
| 
 | 
 | ||||||
| ## Changes since v7.1.2 | ## Changes since v7.1.2 | ||||||
| 
 | 
 | ||||||
|  | - [#1136](https://github.com/oauth2-proxy/oauth2-proxy/pull/1136) Add clock package for better time mocking in tests (@NickMeves) | ||||||
| - [#947](https://github.com/oauth2-proxy/oauth2-proxy/pull/947) Multiple provider ingestion and validation in alpha options (first stage: [#926](https://github.com/oauth2-proxy/oauth2-proxy/issues/926)) (@yanasega) | - [#947](https://github.com/oauth2-proxy/oauth2-proxy/pull/947) Multiple provider ingestion and validation in alpha options (first stage: [#926](https://github.com/oauth2-proxy/oauth2-proxy/issues/926)) (@yanasega) | ||||||
| 
 | 
 | ||||||
| # V7.1.2 | # V7.1.2 | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										1
									
								
								go.mod
								
								
								
								
							|  | @ -5,6 +5,7 @@ go 1.16 | ||||||
| require ( | require ( | ||||||
| 	github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb | 	github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb | ||||||
| 	github.com/alicebob/miniredis/v2 v2.13.0 | 	github.com/alicebob/miniredis/v2 v2.13.0 | ||||||
|  | 	github.com/benbjohnson/clock v1.1.1-0.20210213131748-c97fc7b6bee0 | ||||||
| 	github.com/bitly/go-simplejson v0.5.0 | 	github.com/bitly/go-simplejson v0.5.0 | ||||||
| 	github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect | 	github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect | ||||||
| 	github.com/coreos/go-oidc v2.2.1+incompatible | 	github.com/coreos/go-oidc v2.2.1+incompatible | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										2
									
								
								go.sum
								
								
								
								
							|  | @ -38,6 +38,8 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l | ||||||
| github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= | ||||||
| github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= | github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= | ||||||
| github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= | github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= | ||||||
|  | github.com/benbjohnson/clock v1.1.1-0.20210213131748-c97fc7b6bee0 h1:ROGOOFsMU1fh3kR94itIWlWiPLtgd4TA/qWi4+lL0GM= | ||||||
|  | github.com/benbjohnson/clock v1.1.1-0.20210213131748-c97fc7b6bee0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= | ||||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||||
| github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= | ||||||
|  |  | ||||||
|  | @ -0,0 +1,164 @@ | ||||||
|  | package clock | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	clockapi "github.com/benbjohnson/clock" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	globalClock = clockapi.New() | ||||||
|  | 	mu          sync.Mutex | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Set the global clock to a clockapi.Mock with the given time.Time
 | ||||||
|  | func Set(t time.Time) { | ||||||
|  | 	mu.Lock() | ||||||
|  | 	defer mu.Unlock() | ||||||
|  | 	mock, ok := globalClock.(*clockapi.Mock) | ||||||
|  | 	if !ok { | ||||||
|  | 		mock = clockapi.NewMock() | ||||||
|  | 	} | ||||||
|  | 	mock.Set(t) | ||||||
|  | 	globalClock = mock | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Add moves the mocked global clock forward the given duration. It will error
 | ||||||
|  | // if the global clock is not mocked.
 | ||||||
|  | func Add(d time.Duration) error { | ||||||
|  | 	mu.Lock() | ||||||
|  | 	defer mu.Unlock() | ||||||
|  | 	mock, ok := globalClock.(*clockapi.Mock) | ||||||
|  | 	if !ok { | ||||||
|  | 		return errors.New("time not mocked") | ||||||
|  | 	} | ||||||
|  | 	mock.Add(d) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Reset sets the global clock to a pure time implementation. Returns any
 | ||||||
|  | // existing Mock if set in case lingering time operations are attached to it.
 | ||||||
|  | func Reset() *clockapi.Mock { | ||||||
|  | 	mu.Lock() | ||||||
|  | 	defer mu.Unlock() | ||||||
|  | 	existing := globalClock | ||||||
|  | 	globalClock = clockapi.New() | ||||||
|  | 
 | ||||||
|  | 	mock, ok := existing.(*clockapi.Mock) | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return mock | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Clock is a non-package level wrapper around time that supports stubbing.
 | ||||||
|  | // It will use its localized stubs (allowing for parallelized unit tests
 | ||||||
|  | // where package level stubbing would cause issues). It falls back to any
 | ||||||
|  | // package level time stubs for non-parallel, cross-package integration
 | ||||||
|  | // testing scenarios.
 | ||||||
|  | //
 | ||||||
|  | // If nothing is stubbed, it defaults to default time behavior in the time
 | ||||||
|  | // package.
 | ||||||
|  | type Clock struct { | ||||||
|  | 	mock *clockapi.Mock | ||||||
|  | 	sync.Mutex | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets the Clock to a clock.Mock at the given time.Time
 | ||||||
|  | func (c *Clock) Set(t time.Time) { | ||||||
|  | 	c.Lock() | ||||||
|  | 	defer c.Unlock() | ||||||
|  | 	if c.mock == nil { | ||||||
|  | 		c.mock = clockapi.NewMock() | ||||||
|  | 	} | ||||||
|  | 	c.mock.Set(t) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Add moves clock forward time.Duration if it is mocked. It will error
 | ||||||
|  | // if the clock is not mocked.
 | ||||||
|  | func (c *Clock) Add(d time.Duration) error { | ||||||
|  | 	c.Lock() | ||||||
|  | 	defer c.Unlock() | ||||||
|  | 	if c.mock == nil { | ||||||
|  | 		return errors.New("clock not mocked") | ||||||
|  | 	} | ||||||
|  | 	c.mock.Add(d) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Reset removes local clock.Mock.  Returns any existing Mock if set in case
 | ||||||
|  | // lingering time operations are attached to it.
 | ||||||
|  | func (c *Clock) Reset() *clockapi.Mock { | ||||||
|  | 	c.Lock() | ||||||
|  | 	defer c.Unlock() | ||||||
|  | 	existing := c.mock | ||||||
|  | 	c.mock = nil | ||||||
|  | 	return existing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) After(d time.Duration) <-chan time.Time { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.After(d) | ||||||
|  | 	} | ||||||
|  | 	return m.After(d) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) AfterFunc(d time.Duration, f func()) *clockapi.Timer { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.AfterFunc(d, f) | ||||||
|  | 	} | ||||||
|  | 	return m.AfterFunc(d, f) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Now() time.Time { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Now() | ||||||
|  | 	} | ||||||
|  | 	return m.Now() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Since(t time.Time) time.Duration { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Since(t) | ||||||
|  | 	} | ||||||
|  | 	return m.Since(t) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Sleep(d time.Duration) { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		globalClock.Sleep(d) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	m.Sleep(d) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Tick(d time.Duration) <-chan time.Time { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Tick(d) | ||||||
|  | 	} | ||||||
|  | 	return m.Tick(d) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Ticker(d time.Duration) *clockapi.Ticker { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Ticker(d) | ||||||
|  | 	} | ||||||
|  | 	return m.Ticker(d) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Timer(d time.Duration) *clockapi.Timer { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Timer(d) | ||||||
|  | 	} | ||||||
|  | 	return m.Timer(d) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,17 @@ | ||||||
|  | package clock_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" | ||||||
|  | 	. "github.com/onsi/ginkgo" | ||||||
|  | 	. "github.com/onsi/gomega" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestClockSuite(t *testing.T) { | ||||||
|  | 	logger.SetOutput(GinkgoWriter) | ||||||
|  | 	logger.SetErrOutput(GinkgoWriter) | ||||||
|  | 
 | ||||||
|  | 	RegisterFailHandler(Fail) | ||||||
|  | 	RunSpecs(t, "Clock") | ||||||
|  | } | ||||||
|  | @ -0,0 +1,380 @@ | ||||||
|  | package clock_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"sync" | ||||||
|  | 	"sync/atomic" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/clock" | ||||||
|  | 	. "github.com/onsi/ginkgo" | ||||||
|  | 	. "github.com/onsi/gomega" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	testGlobalEpoch = 1000000000 | ||||||
|  | 	testLocalEpoch  = 1234567890 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var _ = Describe("Clock suite", func() { | ||||||
|  | 	var testClock = clock.Clock{} | ||||||
|  | 
 | ||||||
|  | 	AfterEach(func() { | ||||||
|  | 		clock.Reset() | ||||||
|  | 		testClock.Reset() | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("Global time not overridden", func() { | ||||||
|  | 		It("errors when trying to Add", func() { | ||||||
|  | 			err := clock.Add(123 * time.Hour) | ||||||
|  | 			Expect(err).To(HaveOccurred()) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("Clock not mocked via Set", func() { | ||||||
|  | 			const ( | ||||||
|  | 				outsideTolerance = int32(0) | ||||||
|  | 				withinTolerance  = int32(1) | ||||||
|  | 			) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.After for After", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(30 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 
 | ||||||
|  | 				<-testClock.After(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 
 | ||||||
|  | 				<-testClock.After(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.AfterFunc for AfterFunc", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(30 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 
 | ||||||
|  | 				var wg sync.WaitGroup | ||||||
|  | 				wg.Add(1) | ||||||
|  | 				testClock.AfterFunc(20*time.Millisecond, func() { | ||||||
|  | 					wg.Done() | ||||||
|  | 				}) | ||||||
|  | 				wg.Wait() | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 
 | ||||||
|  | 				wg.Add(1) | ||||||
|  | 				testClock.AfterFunc(20*time.Millisecond, func() { | ||||||
|  | 					wg.Done() | ||||||
|  | 				}) | ||||||
|  | 				wg.Wait() | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Now for Now", func() { | ||||||
|  | 				a := time.Now() | ||||||
|  | 				b := testClock.Now() | ||||||
|  | 				Expect(b.Sub(a).Round(10 * time.Millisecond)).To(Equal(0 * time.Millisecond)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Since for Since", func() { | ||||||
|  | 				past := time.Now().Add(-60 * time.Second) | ||||||
|  | 				Expect(time.Since(past).Round(10 * time.Millisecond)). | ||||||
|  | 					To(Equal(60 * time.Second)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Sleep for Sleep", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(30 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 
 | ||||||
|  | 				testClock.Sleep(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 
 | ||||||
|  | 				testClock.Sleep(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Tick for Tick", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(50 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				ch := testClock.Tick(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 				<-ch | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 				<-ch | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 				<-ch | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Ticker for Ticker", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(50 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				ticker := testClock.Ticker(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 				<-ticker.C | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 				<-ticker.C | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 				<-ticker.C | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("errors if Add is used", func() { | ||||||
|  | 				err := testClock.Add(100 * time.Second) | ||||||
|  | 				Expect(err).To(HaveOccurred()) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("Clock mocked via Set", func() { | ||||||
|  | 			var now = time.Unix(testLocalEpoch, 0) | ||||||
|  | 
 | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				testClock.Set(now) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks After", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				ch := testClock.After(10 * time.Second) | ||||||
|  | 				go func(ch <-chan time.Time) { | ||||||
|  | 					close(ready) | ||||||
|  | 					<-ch | ||||||
|  | 					atomic.StoreInt32(&after, 1) | ||||||
|  | 				}(ch) | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(1))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks AfterFunc", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				testClock.AfterFunc(10*time.Second, func() { | ||||||
|  | 					atomic.StoreInt32(&after, 1) | ||||||
|  | 				}) | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(1))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks AfterFunc with a stopped timer", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				timer := testClock.AfterFunc(10*time.Second, func() { | ||||||
|  | 					atomic.StoreInt32(&after, 1) | ||||||
|  | 				}) | ||||||
|  | 				timer.Stop() | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(11 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Now", func() { | ||||||
|  | 				Expect(testClock.Now()).To(Equal(now)) | ||||||
|  | 				err := testClock.Add(123 * time.Hour) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(testClock.Now()).To(Equal(now.Add(123 * time.Hour))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Since", func() { | ||||||
|  | 				Expect(testClock.Since(time.Unix(testLocalEpoch-100, 0))). | ||||||
|  | 					To(Equal(100 * time.Second)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Sleep", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				go func() { | ||||||
|  | 					close(ready) | ||||||
|  | 					testClock.Sleep(10 * time.Second) | ||||||
|  | 					atomic.StoreInt32(&after, 1) | ||||||
|  | 				}() | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(1))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Tick", func() { | ||||||
|  | 				var ticks int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				go func() { | ||||||
|  | 					close(ready) | ||||||
|  | 					tick := testClock.Tick(10 * time.Second) | ||||||
|  | 					for ticks < 5 { | ||||||
|  | 						<-tick | ||||||
|  | 						atomic.AddInt32(&ticks, 1) | ||||||
|  | 					} | ||||||
|  | 				}() | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(1))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(30 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(4))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(10 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(5))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Ticker", func() { | ||||||
|  | 				var ticks int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				go func() { | ||||||
|  | 					ticker := testClock.Ticker(10 * time.Second) | ||||||
|  | 					close(ready) | ||||||
|  | 					for ticks < 5 { | ||||||
|  | 						<-ticker.C | ||||||
|  | 						atomic.AddInt32(&ticks, 1) | ||||||
|  | 					} | ||||||
|  | 				}() | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(1))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(30 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(4))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(10 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(5))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Timer", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				go func() { | ||||||
|  | 					timer := testClock.Timer(10 * time.Second) | ||||||
|  | 					close(ready) | ||||||
|  | 					<-timer.C | ||||||
|  | 					atomic.AddInt32(&after, 1) | ||||||
|  | 				}() | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(1))) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("Global time overridden", func() { | ||||||
|  | 		var ( | ||||||
|  | 			globalNow = time.Unix(testGlobalEpoch, 0) | ||||||
|  | 			localNow  = time.Unix(testLocalEpoch, 0) | ||||||
|  | 		) | ||||||
|  | 
 | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			clock.Set(globalNow) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("Clock not mocked via Set", func() { | ||||||
|  | 			It("uses globally mocked Now", func() { | ||||||
|  | 				Expect(testClock.Now()).To(Equal(globalNow)) | ||||||
|  | 				err := clock.Add(123 * time.Hour) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(testClock.Now()).To(Equal(globalNow.Add(123 * time.Hour))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("errors when Add is called on the local Clock", func() { | ||||||
|  | 				err := testClock.Add(100 * time.Hour) | ||||||
|  | 				Expect(err).To(HaveOccurred()) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("Clock is mocked via Set", func() { | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				testClock.Set(localNow) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses the local mock and ignores the global", func() { | ||||||
|  | 				Expect(testClock.Now()).To(Equal(localNow)) | ||||||
|  | 
 | ||||||
|  | 				err := clock.Add(456 * time.Hour) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(123 * time.Hour) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				Expect(testClock.Now()).To(Equal(localNow.Add(123 * time.Hour))) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | }) | ||||||
		Loading…
	
		Reference in New Issue