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 | ||||
| 
 | ||||
| - [#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) | ||||
| 
 | ||||
| # V7.1.2 | ||||
|  |  | |||
							
								
								
									
										1
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										1
									
								
								go.mod
								
								
								
								
							|  | @ -5,6 +5,7 @@ go 1.16 | |||
| require ( | ||||
| 	github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb | ||||
| 	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/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect | ||||
| 	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-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/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 v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| 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