mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 00:01:40 +01:00 
			
		
		
		
	See https://tip.golang.org/wiki/LoopvarExperiment#does-this-mean-i-dont-have-to-write-x--x-in-my-loops-anymore Updates https://github.com/tailscale/tailscale/issues/11058 Signed-off-by: Alex Chan <alexc@tailscale.com>
		
			
				
	
	
		
			2475 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			2475 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| package tstest
 | |
| 
 | |
| import (
 | |
| 	"slices"
 | |
| 	"sync/atomic"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"tailscale.com/tstime"
 | |
| )
 | |
| 
 | |
| func TestClockWithDefinedStartTime(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name  string
 | |
| 		start time.Time
 | |
| 		step  time.Duration
 | |
| 		wants []time.Time // The return values of sequential calls to Now().
 | |
| 	}{
 | |
| 		{
 | |
| 			name:  "increment ms",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  1000,
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 2000),
 | |
| 				time.Unix(12345, 3000),
 | |
| 				time.Unix(12345, 4000),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "increment second",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  time.Second,
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12346, 1000),
 | |
| 				time.Unix(12347, 1000),
 | |
| 				time.Unix(12348, 1000),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "no increment",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 1000),
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			clock := NewClock(ClockOpts{
 | |
| 				Start: tt.start,
 | |
| 				Step:  tt.step,
 | |
| 			})
 | |
| 
 | |
| 			if start := clock.GetStart(); !start.Equal(tt.start) {
 | |
| 				t.Errorf("clock has start %v, want %v", start, tt.start)
 | |
| 			}
 | |
| 			if step := clock.GetStep(); step != tt.step {
 | |
| 				t.Errorf("clock has step %v, want %v", step, tt.step)
 | |
| 			}
 | |
| 
 | |
| 			for i := range tt.wants {
 | |
| 				if got := clock.Now(); !got.Equal(tt.wants[i]) {
 | |
| 					t.Errorf("step %v: clock.Now() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 				if got := clock.PeekNow(); !got.Equal(tt.wants[i]) {
 | |
| 					t.Errorf("step %v: clock.PeekNow() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestClockWithDefaultStartTime(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name  string
 | |
| 		step  time.Duration
 | |
| 		wants []time.Duration // The return values of sequential calls to Now() after added to Start()
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "increment ms",
 | |
| 			step: 1000,
 | |
| 			wants: []time.Duration{
 | |
| 				0,
 | |
| 				1000,
 | |
| 				2000,
 | |
| 				3000,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "increment second",
 | |
| 			step: time.Second,
 | |
| 			wants: []time.Duration{
 | |
| 				0 * time.Second,
 | |
| 				1 * time.Second,
 | |
| 				2 * time.Second,
 | |
| 				3 * time.Second,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "no increment",
 | |
| 			wants: []time.Duration{0, 0, 0, 0},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			clock := NewClock(ClockOpts{
 | |
| 				Step: tt.step,
 | |
| 			})
 | |
| 			start := clock.GetStart()
 | |
| 
 | |
| 			if step := clock.GetStep(); step != tt.step {
 | |
| 				t.Errorf("clock has step %v, want %v", step, tt.step)
 | |
| 			}
 | |
| 
 | |
| 			for i := range tt.wants {
 | |
| 				want := start.Add(tt.wants[i])
 | |
| 				if got := clock.Now(); !got.Equal(want) {
 | |
| 					t.Errorf("step %v: clock.Now() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 				if got := clock.PeekNow(); !got.Equal(want) {
 | |
| 					t.Errorf("step %v: clock.PeekNow() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestZeroInitClock(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	var clock Clock
 | |
| 	start := clock.GetStart()
 | |
| 
 | |
| 	if step := clock.GetStep(); step != 0 {
 | |
| 		t.Errorf("clock has step %v, want 0", step)
 | |
| 	}
 | |
| 
 | |
| 	for i := range 10 {
 | |
| 		if got := clock.Now(); !got.Equal(start) {
 | |
| 			t.Errorf("step %v: clock.Now() = %v, want %v", i, got, start)
 | |
| 		}
 | |
| 		if got := clock.PeekNow(); !got.Equal(start) {
 | |
| 			t.Errorf("step %v: clock.PeekNow() = %v, want %v", i, got, start)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestClockSetStep(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	type stepInfo struct {
 | |
| 		when int
 | |
| 		step time.Duration
 | |
| 	}
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name        string
 | |
| 		start       time.Time
 | |
| 		step        time.Duration
 | |
| 		stepChanges []stepInfo
 | |
| 		wants       []time.Time // The return values of sequential calls to Now().
 | |
| 	}{
 | |
| 		{
 | |
| 			name:  "increment ms then s",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  1000,
 | |
| 			stepChanges: []stepInfo{
 | |
| 				{
 | |
| 					when: 4,
 | |
| 					step: time.Second,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 2000),
 | |
| 				time.Unix(12345, 3000),
 | |
| 				time.Unix(12345, 4000),
 | |
| 				time.Unix(12346, 4000),
 | |
| 				time.Unix(12347, 4000),
 | |
| 				time.Unix(12348, 4000),
 | |
| 				time.Unix(12349, 4000),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "multiple changes over time",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  1,
 | |
| 			stepChanges: []stepInfo{
 | |
| 				{
 | |
| 					when: 2,
 | |
| 					step: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when: 4,
 | |
| 					step: 0,
 | |
| 				},
 | |
| 				{
 | |
| 					when: 6,
 | |
| 					step: 1000,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 1001),
 | |
| 				time.Unix(12346, 1001),
 | |
| 				time.Unix(12347, 1001),
 | |
| 				time.Unix(12347, 1001),
 | |
| 				time.Unix(12347, 1001),
 | |
| 				time.Unix(12347, 2001),
 | |
| 				time.Unix(12347, 3001),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "multiple changes at once",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  1,
 | |
| 			stepChanges: []stepInfo{
 | |
| 				{
 | |
| 					when: 2,
 | |
| 					step: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when: 2,
 | |
| 					step: 0,
 | |
| 				},
 | |
| 				{
 | |
| 					when: 2,
 | |
| 					step: 1000,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 1001),
 | |
| 				time.Unix(12345, 2001),
 | |
| 				time.Unix(12345, 3001),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "changes at start",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  0,
 | |
| 			stepChanges: []stepInfo{
 | |
| 				{
 | |
| 					when: 0,
 | |
| 					step: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when: 0,
 | |
| 					step: 1000,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 2000),
 | |
| 				time.Unix(12345, 3000),
 | |
| 				time.Unix(12345, 4000),
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			clock := NewClock(ClockOpts{
 | |
| 				Start: tt.start,
 | |
| 				Step:  tt.step,
 | |
| 			})
 | |
| 			wantStep := tt.step
 | |
| 			changeIndex := 0
 | |
| 
 | |
| 			for i := range tt.wants {
 | |
| 				for len(tt.stepChanges) > changeIndex && tt.stepChanges[changeIndex].when == i {
 | |
| 					wantStep = tt.stepChanges[changeIndex].step
 | |
| 					clock.SetStep(wantStep)
 | |
| 					changeIndex++
 | |
| 				}
 | |
| 
 | |
| 				if start := clock.GetStart(); !start.Equal(tt.start) {
 | |
| 					t.Errorf("clock has start %v, want %v", start, tt.start)
 | |
| 				}
 | |
| 				if step := clock.GetStep(); step != wantStep {
 | |
| 					t.Errorf("clock has step %v, want %v", step, tt.step)
 | |
| 				}
 | |
| 
 | |
| 				if got := clock.Now(); !got.Equal(tt.wants[i]) {
 | |
| 					t.Errorf("step %v: clock.Now() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 				if got := clock.PeekNow(); !got.Equal(tt.wants[i]) {
 | |
| 					t.Errorf("step %v: clock.PeekNow() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestClockAdvance(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	type advanceInfo struct {
 | |
| 		when    int
 | |
| 		advance time.Duration
 | |
| 	}
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name     string
 | |
| 		start    time.Time
 | |
| 		step     time.Duration
 | |
| 		advances []advanceInfo
 | |
| 		wants    []time.Time // The return values of sequential calls to Now().
 | |
| 	}{
 | |
| 		{
 | |
| 			name:  "increment ms then advance 1s",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  1000,
 | |
| 			advances: []advanceInfo{
 | |
| 				{
 | |
| 					when:    4,
 | |
| 					advance: time.Second,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 2000),
 | |
| 				time.Unix(12345, 3000),
 | |
| 				time.Unix(12345, 4000),
 | |
| 				time.Unix(12346, 4000),
 | |
| 				time.Unix(12346, 5000),
 | |
| 				time.Unix(12346, 6000),
 | |
| 				time.Unix(12346, 7000),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "multiple advances over time",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  1,
 | |
| 			advances: []advanceInfo{
 | |
| 				{
 | |
| 					when:    2,
 | |
| 					advance: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when:    4,
 | |
| 					advance: 0,
 | |
| 				},
 | |
| 				{
 | |
| 					when:    6,
 | |
| 					advance: 1000,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 1001),
 | |
| 				time.Unix(12346, 1001),
 | |
| 				time.Unix(12346, 1002),
 | |
| 				time.Unix(12346, 1002),
 | |
| 				time.Unix(12346, 1003),
 | |
| 				time.Unix(12346, 2003),
 | |
| 				time.Unix(12346, 2004),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "multiple advances at once",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  1,
 | |
| 			advances: []advanceInfo{
 | |
| 				{
 | |
| 					when:    2,
 | |
| 					advance: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when:    2,
 | |
| 					advance: 0,
 | |
| 				},
 | |
| 				{
 | |
| 					when:    2,
 | |
| 					advance: 1000,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 1001),
 | |
| 				time.Unix(12346, 2001),
 | |
| 				time.Unix(12346, 2002),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "changes at start",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			step:  5,
 | |
| 			advances: []advanceInfo{
 | |
| 				{
 | |
| 					when:    0,
 | |
| 					advance: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when:    0,
 | |
| 					advance: 1000,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12346, 2000),
 | |
| 				time.Unix(12346, 2005),
 | |
| 				time.Unix(12346, 2010),
 | |
| 				time.Unix(12346, 2015),
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			clock := NewClock(ClockOpts{
 | |
| 				Start: tt.start,
 | |
| 				Step:  tt.step,
 | |
| 			})
 | |
| 			wantStep := tt.step
 | |
| 			changeIndex := 0
 | |
| 
 | |
| 			for i := range tt.wants {
 | |
| 				for len(tt.advances) > changeIndex && tt.advances[changeIndex].when == i {
 | |
| 					clock.Advance(tt.advances[changeIndex].advance)
 | |
| 					changeIndex++
 | |
| 				}
 | |
| 
 | |
| 				if start := clock.GetStart(); !start.Equal(tt.start) {
 | |
| 					t.Errorf("clock has start %v, want %v", start, tt.start)
 | |
| 				}
 | |
| 				if step := clock.GetStep(); step != wantStep {
 | |
| 					t.Errorf("clock has step %v, want %v", step, tt.step)
 | |
| 				}
 | |
| 
 | |
| 				if got := clock.Now(); !got.Equal(tt.wants[i]) {
 | |
| 					t.Errorf("step %v: clock.Now() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 				if got := clock.PeekNow(); !got.Equal(tt.wants[i]) {
 | |
| 					t.Errorf("step %v: clock.PeekNow() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func expectNoTicks(t *testing.T, tickC <-chan time.Time) {
 | |
| 	t.Helper()
 | |
| 	select {
 | |
| 	case tick := <-tickC:
 | |
| 		t.Errorf("wanted no ticks, got %v", tick)
 | |
| 	default:
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSingleTicker(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	type testStep struct {
 | |
| 		stop            bool
 | |
| 		reset           time.Duration
 | |
| 		resetAbsolute   time.Time
 | |
| 		setStep         time.Duration
 | |
| 		advance         time.Duration
 | |
| 		advanceRealTime time.Duration
 | |
| 		wantTime        time.Time
 | |
| 		wantTicks       []time.Time
 | |
| 	}
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name         string
 | |
| 		realTimeOpts *ClockOpts
 | |
| 		start        time.Time
 | |
| 		step         time.Duration
 | |
| 		period       time.Duration
 | |
| 		channelSize  int
 | |
| 		steps        []testStep
 | |
| 	}{
 | |
| 		{
 | |
| 			name:   "no tick advance",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			period: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					advance:  time.Second - 1,
 | |
| 					wantTime: time.Unix(12345, 999_999_999),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "no tick step",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			step:   time.Second - 1,
 | |
| 			period: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 999_999_999),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "single tick advance exact",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			period: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					advance:   time.Second,
 | |
| 					wantTime:  time.Unix(12346, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "single tick advance extra",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			period: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					advance:   time.Second + 1,
 | |
| 					wantTime:  time.Unix(12346, 1),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "single tick step exact",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			step:   time.Second,
 | |
| 			period: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12346, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "single tick step extra",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			step:   time.Second + 1,
 | |
| 			period: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12346, 1),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "single tick per advance",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			period: 3 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:   4 * time.Second,
 | |
| 					wantTime:  time.Unix(12349, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12348, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					advance:   2 * time.Second,
 | |
| 					wantTime:  time.Unix(12351, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12351, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  2 * time.Second,
 | |
| 					wantTime: time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:   2 * time.Second,
 | |
| 					wantTime:  time.Unix(12355, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12354, 0)},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "single tick per step",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			step:   2 * time.Second,
 | |
| 			period: 3 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12349, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12348, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12351, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12351, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12355, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12354, 0)},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "multiple tick per advance",
 | |
| 			start:       time.Unix(12345, 0),
 | |
| 			period:      time.Second,
 | |
| 			channelSize: 3,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  2 * time.Second,
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12346, 0),
 | |
| 						time.Unix(12347, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  4 * time.Second,
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12348, 0),
 | |
| 						time.Unix(12349, 0),
 | |
| 						time.Unix(12350, 0),
 | |
| 						// fourth tick dropped due to channel size
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "multiple tick per step",
 | |
| 			start:       time.Unix(12345, 0),
 | |
| 			step:        3 * time.Second,
 | |
| 			period:      2 * time.Second,
 | |
| 			channelSize: 3,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12348, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12347, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12349, 0),
 | |
| 						time.Unix(12351, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12354, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12353, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12357, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12355, 0),
 | |
| 						time.Unix(12357, 0),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "stop",
 | |
| 			start:       time.Unix(12345, 0),
 | |
| 			step:        2 * time.Second,
 | |
| 			period:      time.Second,
 | |
| 			channelSize: 3,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12346, 0),
 | |
| 						time.Unix(12347, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					stop:     true,
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  10 * time.Second,
 | |
| 					wantTime: time.Unix(12361, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "reset while running",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			period: 2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12347, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					reset:    time.Second,
 | |
| 					wantTime: time.Unix(12348, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12348, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					setStep:  5 * time.Second,
 | |
| 					reset:    10 * time.Second,
 | |
| 					wantTime: time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12358, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12358, 0),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "reset while stopped",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			step:   time.Second,
 | |
| 			period: 2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12347, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					stop:     true,
 | |
| 					wantTime: time.Unix(12348, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    time.Second,
 | |
| 					wantTime: time.Unix(12350, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12350, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12351, 0),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "reset absolute",
 | |
| 			start:  time.Unix(12345, 0),
 | |
| 			step:   time.Second,
 | |
| 			period: 2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12347, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					reset:         time.Second,
 | |
| 					resetAbsolute: time.Unix(12354, 50),
 | |
| 					advance:       7 * time.Second,
 | |
| 					wantTime:      time.Unix(12354, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12355, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12354, 50),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12356, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12355, 50),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:         "follow real time",
 | |
| 			realTimeOpts: new(ClockOpts),
 | |
| 			start:        time.Unix(12345, 0),
 | |
| 			period:       2 * time.Second,
 | |
| 			channelSize:  3,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advanceRealTime: 5 * time.Second,
 | |
| 					wantTime:        time.Unix(12350, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12347, 0),
 | |
| 						time.Unix(12349, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  5 * time.Second,
 | |
| 					wantTime: time.Unix(12355, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12351, 0),
 | |
| 						time.Unix(12353, 0),
 | |
| 						time.Unix(12355, 0),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			var realTimeClockForTestClock tstime.Clock
 | |
| 			var realTimeClock *Clock
 | |
| 			if tt.realTimeOpts != nil {
 | |
| 				realTimeClock = NewClock(*tt.realTimeOpts)
 | |
| 				// Passing realTimeClock into newClockInternal results in a
 | |
| 				// non-nil interface with a nil pointer, so this is necessary.
 | |
| 				realTimeClockForTestClock = realTimeClock
 | |
| 			}
 | |
| 
 | |
| 			clock := newClockInternal(ClockOpts{
 | |
| 				Start:            tt.start,
 | |
| 				Step:             tt.step,
 | |
| 				TimerChannelSize: tt.channelSize,
 | |
| 				FollowRealTime:   realTimeClock != nil,
 | |
| 			}, realTimeClockForTestClock)
 | |
| 			tc, tickC := clock.NewTicker(tt.period)
 | |
| 			tickControl := tc.(*Ticker)
 | |
| 
 | |
| 			t.Cleanup(tickControl.Stop)
 | |
| 
 | |
| 			expectNoTicks(t, tickC)
 | |
| 
 | |
| 			for i, step := range tt.steps {
 | |
| 				if step.stop {
 | |
| 					tickControl.Stop()
 | |
| 				}
 | |
| 
 | |
| 				if !step.resetAbsolute.IsZero() {
 | |
| 					tickControl.ResetAbsolute(step.resetAbsolute, step.reset)
 | |
| 				} else if step.reset > 0 {
 | |
| 					tickControl.Reset(step.reset)
 | |
| 				}
 | |
| 
 | |
| 				if step.setStep > 0 {
 | |
| 					clock.SetStep(step.setStep)
 | |
| 				}
 | |
| 
 | |
| 				if step.advance > 0 {
 | |
| 					clock.Advance(step.advance)
 | |
| 				}
 | |
| 				if step.advanceRealTime > 0 {
 | |
| 					realTimeClock.Advance(step.advanceRealTime)
 | |
| 				}
 | |
| 
 | |
| 				if now := clock.Now(); !step.wantTime.IsZero() && !now.Equal(step.wantTime) {
 | |
| 					t.Errorf("step %v now = %v, want %v", i, now, step.wantTime)
 | |
| 				}
 | |
| 
 | |
| 				for j, want := range step.wantTicks {
 | |
| 					select {
 | |
| 					case tick := <-tickC:
 | |
| 						if tick.Equal(want) {
 | |
| 							continue
 | |
| 						}
 | |
| 						t.Errorf("step %v tick %v = %v, want %v", i, j, tick, want)
 | |
| 					default:
 | |
| 						t.Errorf("step %v tick %v missing", i, j)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				expectNoTicks(t, tickC)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSingleTimer(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	type testStep struct {
 | |
| 		stop            bool
 | |
| 		stopReturn      bool // The expected return value for Stop() if stop is true.
 | |
| 		reset           time.Duration
 | |
| 		resetAbsolute   time.Time
 | |
| 		resetReturn     bool // The expected return value for Reset() or ResetAbsolute().
 | |
| 		setStep         time.Duration
 | |
| 		advance         time.Duration
 | |
| 		advanceRealTime time.Duration
 | |
| 		wantTime        time.Time
 | |
| 		wantTicks       []time.Time
 | |
| 	}
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name         string
 | |
| 		realTimeOpts *ClockOpts
 | |
| 		start        time.Time
 | |
| 		step         time.Duration
 | |
| 		delay        time.Duration
 | |
| 		steps        []testStep
 | |
| 	}{
 | |
| 		{
 | |
| 			name:  "no tick advance",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					advance:  time.Second - 1,
 | |
| 					wantTime: time.Unix(12345, 999_999_999),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "no tick step",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second - 1,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 999_999_999),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "single tick advance exact",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					advance:   time.Second,
 | |
| 					wantTime:  time.Unix(12346, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "single tick advance extra",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					advance:   time.Second + 1,
 | |
| 					wantTime:  time.Unix(12346, 1),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12347, 1),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "single tick step exact",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12346, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "single tick step extra",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second + 1,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12346, 1),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 2),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset for single tick per advance",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: 3 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:   4 * time.Second,
 | |
| 					wantTime:  time.Unix(12349, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12348, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					resetAbsolute: time.Unix(12351, 0),
 | |
| 					advance:       2 * time.Second,
 | |
| 					wantTime:      time.Unix(12351, 0),
 | |
| 					wantTicks:     []time.Time{time.Unix(12351, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    3 * time.Second,
 | |
| 					advance:  2 * time.Second,
 | |
| 					wantTime: time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:   2 * time.Second,
 | |
| 					wantTime:  time.Unix(12355, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12354, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  10 * time.Second,
 | |
| 					wantTime: time.Unix(12365, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset for single tick per step",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: 3 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12349, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12348, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					reset:     time.Second,
 | |
| 					wantTime:  time.Unix(12351, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12350, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					resetAbsolute: time.Unix(12354, 0),
 | |
| 					wantTime:      time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12355, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12354, 0)},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset while active",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: 3 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					reset:       3 * time.Second,
 | |
| 					resetReturn: true,
 | |
| 					wantTime:    time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					resetAbsolute: time.Unix(12354, 0),
 | |
| 					resetReturn:   true,
 | |
| 					wantTime:      time.Unix(12351, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12355, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12354, 0)},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "stop after fire",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12347, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					stop:     true,
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  10 * time.Second,
 | |
| 					wantTime: time.Unix(12361, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "stop before fire",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					stop:       true,
 | |
| 					stopReturn: true,
 | |
| 					wantTime:   time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  10 * time.Second,
 | |
| 					wantTime: time.Unix(12361, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "stop after reset",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime:  time.Unix(12347, 0),
 | |
| 					wantTicks: []time.Time{time.Unix(12346, 0)},
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    10 * time.Second,
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					stop:       true,
 | |
| 					stopReturn: true,
 | |
| 					wantTime:   time.Unix(12351, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  10 * time.Second,
 | |
| 					wantTime: time.Unix(12361, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset while running",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: 2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12347, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					reset:    time.Second,
 | |
| 					wantTime: time.Unix(12348, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12348, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					setStep:  5 * time.Second,
 | |
| 					reset:    10 * time.Second,
 | |
| 					wantTime: time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12358, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12358, 0),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset while stopped",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second,
 | |
| 			delay: 2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					stop:       true,
 | |
| 					stopReturn: true,
 | |
| 					wantTime:   time.Unix(12347, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12348, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    time.Second,
 | |
| 					wantTime: time.Unix(12350, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12350, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset absolute",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second,
 | |
| 			delay: 2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12347, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					resetAbsolute: time.Unix(12354, 50),
 | |
| 					advance:       7 * time.Second,
 | |
| 					wantTime:      time.Unix(12354, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12355, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12354, 50),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12356, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:         "follow real time",
 | |
| 			realTimeOpts: new(ClockOpts),
 | |
| 			start:        time.Unix(12345, 0),
 | |
| 			delay:        2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advanceRealTime: 5 * time.Second,
 | |
| 					wantTime:        time.Unix(12350, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12347, 0),
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    2 * time.Second,
 | |
| 					advance:  5 * time.Second,
 | |
| 					wantTime: time.Unix(12355, 0),
 | |
| 					wantTicks: []time.Time{
 | |
| 						time.Unix(12352, 0),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			var realTimeClockForTestClock tstime.Clock
 | |
| 			var realTimeClock *Clock
 | |
| 			if tt.realTimeOpts != nil {
 | |
| 				realTimeClock = NewClock(*tt.realTimeOpts)
 | |
| 				// Passing realTimeClock into newClockInternal results in a
 | |
| 				// non-nil interface with a nil pointer, so this is necessary.
 | |
| 				realTimeClockForTestClock = realTimeClock
 | |
| 			}
 | |
| 
 | |
| 			clock := newClockInternal(ClockOpts{
 | |
| 				Start:          tt.start,
 | |
| 				Step:           tt.step,
 | |
| 				FollowRealTime: realTimeClock != nil,
 | |
| 			}, realTimeClockForTestClock)
 | |
| 			tc, tickC := clock.NewTimer(tt.delay)
 | |
| 			timerControl := tc.(*Timer)
 | |
| 
 | |
| 			t.Cleanup(func() { timerControl.Stop() })
 | |
| 
 | |
| 			expectNoTicks(t, tickC)
 | |
| 
 | |
| 			for i, step := range tt.steps {
 | |
| 				if step.stop {
 | |
| 					if got := timerControl.Stop(); got != step.stopReturn {
 | |
| 						t.Errorf("step %v Stop returned %v, want %v", i, got, step.stopReturn)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if !step.resetAbsolute.IsZero() {
 | |
| 					if got := timerControl.ResetAbsolute(step.resetAbsolute); got != step.resetReturn {
 | |
| 						t.Errorf("step %v Reset returned %v, want %v", i, got, step.resetReturn)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if step.reset > 0 {
 | |
| 					if got := timerControl.Reset(step.reset); got != step.resetReturn {
 | |
| 						t.Errorf("step %v Reset returned %v, want %v", i, got, step.resetReturn)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if step.setStep > 0 {
 | |
| 					clock.SetStep(step.setStep)
 | |
| 				}
 | |
| 
 | |
| 				if step.advance > 0 {
 | |
| 					clock.Advance(step.advance)
 | |
| 				}
 | |
| 				if step.advanceRealTime > 0 {
 | |
| 					realTimeClock.Advance(step.advanceRealTime)
 | |
| 				}
 | |
| 
 | |
| 				if now := clock.Now(); !step.wantTime.IsZero() && !now.Equal(step.wantTime) {
 | |
| 					t.Errorf("step %v now = %v, want %v", i, now, step.wantTime)
 | |
| 				}
 | |
| 
 | |
| 				for j, want := range step.wantTicks {
 | |
| 					select {
 | |
| 					case tick := <-tickC:
 | |
| 						if tick.Equal(want) {
 | |
| 							continue
 | |
| 						}
 | |
| 						t.Errorf("step %v tick %v = %v, want %v", i, j, tick, want)
 | |
| 					default:
 | |
| 						t.Errorf("step %v tick %v missing", i, j)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				expectNoTicks(t, tickC)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type testEvent struct {
 | |
| 	fireTimes     []time.Time
 | |
| 	scheduleTimes []time.Time
 | |
| }
 | |
| 
 | |
| func (te *testEvent) Fire(t time.Time) time.Time {
 | |
| 	var ret time.Time
 | |
| 
 | |
| 	te.fireTimes = append(te.fireTimes, t)
 | |
| 	if len(te.scheduleTimes) > 0 {
 | |
| 		ret = te.scheduleTimes[0]
 | |
| 		te.scheduleTimes = te.scheduleTimes[1:]
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| func TestEventManager(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	var em eventManager
 | |
| 
 | |
| 	testEvents := []testEvent{
 | |
| 		{
 | |
| 			scheduleTimes: []time.Time{
 | |
| 				time.Unix(12300, 0), // step 1
 | |
| 				time.Unix(12340, 0), // step 1
 | |
| 				time.Unix(12345, 0), // step 1
 | |
| 				time.Unix(12346, 0), // step 1
 | |
| 				time.Unix(12347, 0), // step 3
 | |
| 				time.Unix(12348, 0), // step 4
 | |
| 				time.Unix(12349, 0), // step 4
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			scheduleTimes: []time.Time{
 | |
| 				time.Unix(12350, 0), // step 4
 | |
| 				time.Unix(12360, 0), // step 5
 | |
| 				time.Unix(12370, 0), // rescheduled
 | |
| 				time.Unix(12380, 0), // step 6
 | |
| 				time.Unix(12381, 0), // step 6
 | |
| 				time.Unix(12382, 0), // step 6
 | |
| 				time.Unix(12393, 0), // stopped
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			scheduleTimes: []time.Time{
 | |
| 				time.Unix(12350, 1), // step 4
 | |
| 				time.Unix(12360, 1), // rescheduled
 | |
| 				time.Unix(12370, 1), // step 6
 | |
| 				time.Unix(12380, 1), // step 6
 | |
| 				time.Unix(12381, 1), // step 6
 | |
| 				time.Unix(12382, 1), // step 6
 | |
| 				time.Unix(12383, 1), // step 6
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			scheduleTimes: []time.Time{
 | |
| 				time.Unix(12355, 0), // step 5
 | |
| 				time.Unix(12365, 0), // step 5
 | |
| 				time.Unix(12370, 0), // step 6
 | |
| 				time.Unix(12390, 0), // step 6
 | |
| 				time.Unix(12391, 0), // step 7
 | |
| 				time.Unix(12392, 0), // step 7
 | |
| 				time.Unix(12393, 0), // step 7
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			scheduleTimes: []time.Time{
 | |
| 				time.Unix(100000, 0), // step 7
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			scheduleTimes: []time.Time{
 | |
| 				time.Unix(12346, 0), // step 1
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			scheduleTimes: []time.Time{
 | |
| 				time.Unix(12305, 0), // step 5
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			scheduleTimes: []time.Time{
 | |
| 				time.Unix(12372, 0), // step 6
 | |
| 				time.Unix(12374, 0), // step 6
 | |
| 				time.Unix(12376, 0), // step 6
 | |
| 				time.Unix(12386, 0), // step 6
 | |
| 				time.Unix(12396, 0), // step 7
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	steps := []struct {
 | |
| 		reschedule    []int
 | |
| 		stop          []int
 | |
| 		advanceTo     time.Time
 | |
| 		want          map[int][]time.Time
 | |
| 		waitingEvents int
 | |
| 	}{
 | |
| 		{
 | |
| 			advanceTo: time.Unix(12345, 0),
 | |
| 		},
 | |
| 		{
 | |
| 			reschedule: []int{0, 1, 2, 3, 4, 5}, // add 0, 1, 2, 3, 4, 5
 | |
| 			advanceTo:  time.Unix(12346, 0),
 | |
| 			want: map[int][]time.Time{
 | |
| 				0: {
 | |
| 					time.Unix(12300, 0),
 | |
| 					time.Unix(12340, 0),
 | |
| 					time.Unix(12345, 0),
 | |
| 					time.Unix(12346, 0),
 | |
| 				},
 | |
| 				5: {
 | |
| 					time.Unix(12346, 0),
 | |
| 				},
 | |
| 			},
 | |
| 			waitingEvents: 5, // scheduled 0, 1, 2, 3, 4, 5; retired 5
 | |
| 		},
 | |
| 		{
 | |
| 			advanceTo:     time.Unix(12346, 50),
 | |
| 			waitingEvents: 5, // no change
 | |
| 		},
 | |
| 		{
 | |
| 			advanceTo: time.Unix(12347, 50),
 | |
| 			want: map[int][]time.Time{
 | |
| 				0: {
 | |
| 					time.Unix(12347, 0),
 | |
| 				},
 | |
| 			},
 | |
| 			waitingEvents: 5, // no change
 | |
| 		},
 | |
| 		{
 | |
| 			advanceTo: time.Unix(12350, 50),
 | |
| 			want: map[int][]time.Time{
 | |
| 				0: {
 | |
| 					time.Unix(12348, 0),
 | |
| 					time.Unix(12349, 0),
 | |
| 				},
 | |
| 				1: {
 | |
| 					time.Unix(12350, 0),
 | |
| 				},
 | |
| 				2: {
 | |
| 					time.Unix(12350, 1),
 | |
| 				},
 | |
| 			},
 | |
| 			waitingEvents: 4, // retired 0
 | |
| 		},
 | |
| 		{
 | |
| 			reschedule: []int{6, 7}, // add 6, 7
 | |
| 			stop:       []int{2},
 | |
| 			advanceTo:  time.Unix(12365, 0),
 | |
| 			want: map[int][]time.Time{
 | |
| 				1: {
 | |
| 					time.Unix(12360, 0),
 | |
| 				},
 | |
| 				3: {
 | |
| 					time.Unix(12355, 0),
 | |
| 					time.Unix(12365, 0),
 | |
| 				},
 | |
| 				6: {
 | |
| 					time.Unix(12305, 0),
 | |
| 				},
 | |
| 			},
 | |
| 			waitingEvents: 4, // scheduled 6, 7; retired 2, 5
 | |
| 		},
 | |
| 		{
 | |
| 			reschedule: []int{1, 2}, // update 1; add 2
 | |
| 			stop:       []int{6},
 | |
| 			advanceTo:  time.Unix(12390, 0),
 | |
| 			want: map[int][]time.Time{
 | |
| 				1: {
 | |
| 					time.Unix(12380, 0),
 | |
| 					time.Unix(12381, 0),
 | |
| 					time.Unix(12382, 0),
 | |
| 				},
 | |
| 				2: {
 | |
| 					time.Unix(12370, 1),
 | |
| 					time.Unix(12380, 1),
 | |
| 					time.Unix(12381, 1),
 | |
| 					time.Unix(12382, 1),
 | |
| 					time.Unix(12383, 1),
 | |
| 				},
 | |
| 				3: {
 | |
| 					time.Unix(12370, 0),
 | |
| 					time.Unix(12390, 0),
 | |
| 				},
 | |
| 				7: {
 | |
| 					time.Unix(12372, 0),
 | |
| 					time.Unix(12374, 0),
 | |
| 					time.Unix(12376, 0),
 | |
| 					time.Unix(12386, 0),
 | |
| 				},
 | |
| 			},
 | |
| 			waitingEvents: 3, // scheduled 2, retired 2, stopped 6
 | |
| 		},
 | |
| 		{
 | |
| 			stop:      []int{1}, // no-op: already stopped
 | |
| 			advanceTo: time.Unix(200000, 0),
 | |
| 			want: map[int][]time.Time{
 | |
| 				3: {
 | |
| 					time.Unix(12391, 0),
 | |
| 					time.Unix(12392, 0),
 | |
| 					time.Unix(12393, 0),
 | |
| 				},
 | |
| 				4: {
 | |
| 					time.Unix(100000, 0),
 | |
| 				},
 | |
| 				7: {
 | |
| 					time.Unix(12396, 0),
 | |
| 				},
 | |
| 			},
 | |
| 			waitingEvents: 0, // retired 3, 4, 7
 | |
| 		},
 | |
| 		{
 | |
| 			advanceTo: time.Unix(300000, 0),
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for i, step := range steps {
 | |
| 		for _, idx := range step.reschedule {
 | |
| 			ev := &testEvents[idx]
 | |
| 			t := ev.scheduleTimes[0]
 | |
| 			ev.scheduleTimes = ev.scheduleTimes[1:]
 | |
| 			em.Reschedule(ev, t)
 | |
| 		}
 | |
| 		for _, idx := range step.stop {
 | |
| 			ev := &testEvents[idx]
 | |
| 			em.Reschedule(ev, time.Time{})
 | |
| 		}
 | |
| 		em.AdvanceTo(step.advanceTo)
 | |
| 		for j := range testEvents {
 | |
| 			if !slices.Equal(testEvents[j].fireTimes, step.want[j]) {
 | |
| 				t.Errorf("step %v event %v fire times = %v, want %v", i, j, testEvents[j].fireTimes, step.want[j])
 | |
| 			}
 | |
| 			testEvents[j].fireTimes = nil
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestClockFollowRealTime(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	type advanceInfo struct {
 | |
| 		when                 int
 | |
| 		advanceTestClock     time.Duration
 | |
| 		advanceTestClockTo   time.Time
 | |
| 		advanceRealTimeClock time.Duration
 | |
| 	}
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name              string
 | |
| 		start             time.Time
 | |
| 		wantStart         time.Time // This may differ from start when start.IsZero().
 | |
| 		realTimeClockOpts ClockOpts
 | |
| 		advances          []advanceInfo
 | |
| 		wants             []time.Time // The return values of sequential calls to Now().
 | |
| 	}{
 | |
| 		{
 | |
| 			name:      "increment ms then advance 1s",
 | |
| 			start:     time.Unix(12345, 1000),
 | |
| 			wantStart: time.Unix(12345, 1000),
 | |
| 			advances: []advanceInfo{
 | |
| 				{
 | |
| 					when:                 1,
 | |
| 					advanceRealTimeClock: 1000,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 2,
 | |
| 					advanceRealTimeClock: 1000,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 3,
 | |
| 					advanceRealTimeClock: 1000,
 | |
| 				},
 | |
| 				{
 | |
| 					when:             4,
 | |
| 					advanceTestClock: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 5,
 | |
| 					advanceRealTimeClock: 1000,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 6,
 | |
| 					advanceRealTimeClock: 1000,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 7,
 | |
| 					advanceRealTimeClock: 1000,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 2000),
 | |
| 				time.Unix(12345, 3000),
 | |
| 				time.Unix(12345, 4000),
 | |
| 				time.Unix(12346, 4000),
 | |
| 				time.Unix(12346, 5000),
 | |
| 				time.Unix(12346, 6000),
 | |
| 				time.Unix(12346, 7000),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "multiple advances over time",
 | |
| 			start:     time.Unix(12345, 1000),
 | |
| 			wantStart: time.Unix(12345, 1000),
 | |
| 			advances: []advanceInfo{
 | |
| 				{
 | |
| 					when:                 1,
 | |
| 					advanceRealTimeClock: 1,
 | |
| 				},
 | |
| 				{
 | |
| 					when:             2,
 | |
| 					advanceTestClock: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 3,
 | |
| 					advanceRealTimeClock: 1,
 | |
| 				},
 | |
| 				{
 | |
| 					when:             4,
 | |
| 					advanceTestClock: 0,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 5,
 | |
| 					advanceRealTimeClock: 1,
 | |
| 				},
 | |
| 				{
 | |
| 					when:             6,
 | |
| 					advanceTestClock: 1000,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 7,
 | |
| 					advanceRealTimeClock: 1,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 1001),
 | |
| 				time.Unix(12346, 1001),
 | |
| 				time.Unix(12346, 1002),
 | |
| 				time.Unix(12346, 1002),
 | |
| 				time.Unix(12346, 1003),
 | |
| 				time.Unix(12346, 2003),
 | |
| 				time.Unix(12346, 2004),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "multiple advances at once",
 | |
| 			start:     time.Unix(12345, 1000),
 | |
| 			wantStart: time.Unix(12345, 1000),
 | |
| 			advances: []advanceInfo{
 | |
| 				{
 | |
| 					when:                 1,
 | |
| 					advanceRealTimeClock: 1,
 | |
| 				},
 | |
| 				{
 | |
| 					when:             2,
 | |
| 					advanceTestClock: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when:             2,
 | |
| 					advanceTestClock: 0,
 | |
| 				},
 | |
| 				{
 | |
| 					when:             2,
 | |
| 					advanceTestClock: 1000,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 3,
 | |
| 					advanceRealTimeClock: 1,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 1000),
 | |
| 				time.Unix(12345, 1001),
 | |
| 				time.Unix(12346, 2001),
 | |
| 				time.Unix(12346, 2002),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "changes at start",
 | |
| 			start:     time.Unix(12345, 1000),
 | |
| 			wantStart: time.Unix(12345, 1000),
 | |
| 			advances: []advanceInfo{
 | |
| 				{
 | |
| 					when:             0,
 | |
| 					advanceTestClock: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when:             0,
 | |
| 					advanceTestClock: 1000,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 1,
 | |
| 					advanceRealTimeClock: 5,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 2,
 | |
| 					advanceRealTimeClock: 5,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 3,
 | |
| 					advanceRealTimeClock: 5,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12346, 2000),
 | |
| 				time.Unix(12346, 2005),
 | |
| 				time.Unix(12346, 2010),
 | |
| 				time.Unix(12346, 2015),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "start from current time",
 | |
| 			realTimeClockOpts: ClockOpts{
 | |
| 				Start: time.Unix(12345, 0),
 | |
| 			},
 | |
| 			wantStart: time.Unix(12345, 0),
 | |
| 			advances: []advanceInfo{
 | |
| 				{
 | |
| 					when:             1,
 | |
| 					advanceTestClock: time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 2,
 | |
| 					advanceRealTimeClock: 10 * time.Second,
 | |
| 				},
 | |
| 				{
 | |
| 					when:             3,
 | |
| 					advanceTestClock: time.Minute,
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 4,
 | |
| 					advanceRealTimeClock: time.Hour,
 | |
| 				},
 | |
| 				{
 | |
| 					when:               5,
 | |
| 					advanceTestClockTo: time.Unix(100, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					when:                 6,
 | |
| 					advanceRealTimeClock: time.Hour,
 | |
| 				},
 | |
| 			},
 | |
| 			wants: []time.Time{
 | |
| 				time.Unix(12345, 0),
 | |
| 				time.Unix(12346, 0),
 | |
| 				time.Unix(12356, 0),
 | |
| 				time.Unix(12416, 0),
 | |
| 				time.Unix(16016, 0),
 | |
| 				time.Unix(100, 0),
 | |
| 				time.Unix(3700, 0),
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			realTimeClock := NewClock(tt.realTimeClockOpts)
 | |
| 			clock := newClockInternal(ClockOpts{
 | |
| 				Start:          tt.start,
 | |
| 				FollowRealTime: true,
 | |
| 			}, realTimeClock)
 | |
| 			changeIndex := 0
 | |
| 
 | |
| 			for i := range tt.wants {
 | |
| 				for len(tt.advances) > changeIndex && tt.advances[changeIndex].when == i {
 | |
| 					advance := tt.advances[changeIndex]
 | |
| 					if advance.advanceTestClockTo.IsZero() {
 | |
| 						clock.Advance(advance.advanceTestClock)
 | |
| 					} else {
 | |
| 						clock.AdvanceTo(advance.advanceTestClockTo)
 | |
| 					}
 | |
| 					realTimeClock.Advance(advance.advanceRealTimeClock)
 | |
| 					changeIndex++
 | |
| 				}
 | |
| 
 | |
| 				if start := clock.GetStart(); !start.Equal(tt.wantStart) {
 | |
| 					t.Errorf("clock has start %v, want %v", start, tt.wantStart)
 | |
| 				}
 | |
| 
 | |
| 				if got := clock.Now(); !got.Equal(tt.wants[i]) {
 | |
| 					t.Errorf("step %v: clock.Now() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 				if got := clock.PeekNow(); !got.Equal(tt.wants[i]) {
 | |
| 					t.Errorf("step %v: clock.PeekNow() = %v, want %v", i, got, tt.wants[i])
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestAfterFunc(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	type testStep struct {
 | |
| 		stop            bool
 | |
| 		stopReturn      bool // The expected return value for Stop() if stop is true.
 | |
| 		reset           time.Duration
 | |
| 		resetAbsolute   time.Time
 | |
| 		resetReturn     bool // The expected return value for Reset() or ResetAbsolute().
 | |
| 		setStep         time.Duration
 | |
| 		advance         time.Duration
 | |
| 		advanceRealTime time.Duration
 | |
| 		wantTime        time.Time
 | |
| 		wantTick        bool
 | |
| 	}
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name         string
 | |
| 		realTimeOpts *ClockOpts
 | |
| 		start        time.Time
 | |
| 		step         time.Duration
 | |
| 		delay        time.Duration
 | |
| 		steps        []testStep
 | |
| 	}{
 | |
| 		{
 | |
| 			name:  "no tick advance",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					advance:  time.Second - 1,
 | |
| 					wantTime: time.Unix(12345, 999_999_999),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "no tick step",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second - 1,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 999_999_999),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "single tick advance exact",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "single tick advance extra",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					advance:  time.Second + 1,
 | |
| 					wantTime: time.Unix(12346, 1),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12347, 1),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "single tick step exact",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "single tick step extra",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second + 1,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12346, 1),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 2),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset for single tick per advance",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: 3 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  4 * time.Second,
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					resetAbsolute: time.Unix(12351, 0),
 | |
| 					advance:       2 * time.Second,
 | |
| 					wantTime:      time.Unix(12351, 0),
 | |
| 					wantTick:      true,
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    3 * time.Second,
 | |
| 					advance:  2 * time.Second,
 | |
| 					wantTime: time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  2 * time.Second,
 | |
| 					wantTime: time.Unix(12355, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  10 * time.Second,
 | |
| 					wantTime: time.Unix(12365, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset for single tick per step",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: 3 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    time.Second,
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					resetAbsolute: time.Unix(12354, 0),
 | |
| 					wantTime:      time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12355, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset while active",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: 3 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					reset:       3 * time.Second,
 | |
| 					resetReturn: true,
 | |
| 					wantTime:    time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					resetAbsolute: time.Unix(12354, 0),
 | |
| 					resetReturn:   true,
 | |
| 					wantTime:      time.Unix(12351, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12355, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "stop after fire",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					stop:     true,
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  10 * time.Second,
 | |
| 					wantTime: time.Unix(12361, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "stop before fire",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					stop:       true,
 | |
| 					stopReturn: true,
 | |
| 					wantTime:   time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  10 * time.Second,
 | |
| 					wantTime: time.Unix(12361, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "stop after reset",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  2 * time.Second,
 | |
| 			delay: time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    10 * time.Second,
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					stop:       true,
 | |
| 					stopReturn: true,
 | |
| 					wantTime:   time.Unix(12351, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  10 * time.Second,
 | |
| 					wantTime: time.Unix(12361, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset while running",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			delay: 2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					advance:  time.Second,
 | |
| 					reset:    time.Second,
 | |
| 					wantTime: time.Unix(12348, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					setStep:  5 * time.Second,
 | |
| 					reset:    10 * time.Second,
 | |
| 					wantTime: time.Unix(12353, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12358, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset while stopped",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second,
 | |
| 			delay: 2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					stop:       true,
 | |
| 					stopReturn: true,
 | |
| 					wantTime:   time.Unix(12347, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12348, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12349, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    time.Second,
 | |
| 					wantTime: time.Unix(12350, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12351, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "reset absolute",
 | |
| 			start: time.Unix(12345, 0),
 | |
| 			step:  time.Second,
 | |
| 			delay: 2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12346, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12347, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					resetAbsolute: time.Unix(12354, 50),
 | |
| 					advance:       7 * time.Second,
 | |
| 					wantTime:      time.Unix(12354, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12355, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 				{
 | |
| 					wantTime: time.Unix(12356, 0),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name:         "follow real time",
 | |
| 			realTimeOpts: new(ClockOpts),
 | |
| 			start:        time.Unix(12345, 0),
 | |
| 			delay:        2 * time.Second,
 | |
| 			steps: []testStep{
 | |
| 				{
 | |
| 					wantTime: time.Unix(12345, 0),
 | |
| 				},
 | |
| 				{
 | |
| 					advanceRealTime: 5 * time.Second,
 | |
| 					wantTime:        time.Unix(12350, 0),
 | |
| 					wantTick:        true,
 | |
| 				},
 | |
| 				{
 | |
| 					reset:    2 * time.Second,
 | |
| 					advance:  5 * time.Second,
 | |
| 					wantTime: time.Unix(12355, 0),
 | |
| 					wantTick: true,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			var realTimeClockForTestClock tstime.Clock
 | |
| 			var realTimeClock *Clock
 | |
| 			if tt.realTimeOpts != nil {
 | |
| 				realTimeClock = NewClock(*tt.realTimeOpts)
 | |
| 				// Passing realTimeClock into newClockInternal results in a
 | |
| 				// non-nil interface with a nil pointer, so this is necessary.
 | |
| 				realTimeClockForTestClock = realTimeClock
 | |
| 			}
 | |
| 
 | |
| 			var gotTick atomic.Bool
 | |
| 
 | |
| 			clock := newClockInternal(ClockOpts{
 | |
| 				Start:          tt.start,
 | |
| 				Step:           tt.step,
 | |
| 				FollowRealTime: realTimeClock != nil,
 | |
| 			}, realTimeClockForTestClock)
 | |
| 			tc := clock.AfterFunc(tt.delay, func() {
 | |
| 				if gotTick.Swap(true) == true {
 | |
| 					t.Error("multiple ticks detected")
 | |
| 				}
 | |
| 			})
 | |
| 			timerControl := tc.(*Timer)
 | |
| 
 | |
| 			t.Cleanup(func() { timerControl.Stop() })
 | |
| 
 | |
| 			if gotTick.Load() {
 | |
| 				t.Error("initial tick detected, want none")
 | |
| 			}
 | |
| 
 | |
| 			for i, step := range tt.steps {
 | |
| 				if step.stop {
 | |
| 					if got := timerControl.Stop(); got != step.stopReturn {
 | |
| 						t.Errorf("step %v Stop returned %v, want %v", i, got, step.stopReturn)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if !step.resetAbsolute.IsZero() {
 | |
| 					if got := timerControl.ResetAbsolute(step.resetAbsolute); got != step.resetReturn {
 | |
| 						t.Errorf("step %v Reset returned %v, want %v", i, got, step.resetReturn)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if step.reset > 0 {
 | |
| 					if got := timerControl.Reset(step.reset); got != step.resetReturn {
 | |
| 						t.Errorf("step %v Reset returned %v, want %v", i, got, step.resetReturn)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if step.setStep > 0 {
 | |
| 					clock.SetStep(step.setStep)
 | |
| 				}
 | |
| 
 | |
| 				if step.advance > 0 {
 | |
| 					clock.Advance(step.advance)
 | |
| 				}
 | |
| 				if step.advanceRealTime > 0 {
 | |
| 					realTimeClock.Advance(step.advanceRealTime)
 | |
| 				}
 | |
| 
 | |
| 				if now := clock.Now(); !step.wantTime.IsZero() && !now.Equal(step.wantTime) {
 | |
| 					t.Errorf("step %v now = %v, want %v", i, now, step.wantTime)
 | |
| 				}
 | |
| 
 | |
| 				if got := gotTick.Swap(false); got != step.wantTick {
 | |
| 					t.Errorf("step %v tick %v, want %v", i, got, step.wantTick)
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSince(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name  string
 | |
| 		start time.Time
 | |
| 		since time.Time
 | |
| 		want  time.Duration
 | |
| 	}{
 | |
| 		{
 | |
| 			name:  "positive",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			since: time.Unix(11111, 1000),
 | |
| 			want:  1234 * time.Second,
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "negative",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			since: time.Unix(15436, 1000),
 | |
| 			want:  -3091 * time.Second,
 | |
| 		},
 | |
| 		{
 | |
| 			name:  "zero",
 | |
| 			start: time.Unix(12345, 1000),
 | |
| 			since: time.Unix(12345, 1000),
 | |
| 			want:  0,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 			clock := NewClock(ClockOpts{
 | |
| 				Start: tt.start,
 | |
| 			})
 | |
| 			got := clock.Since(tt.since)
 | |
| 			if got != tt.want {
 | |
| 				t.Errorf("Since duration %v, want %v", got, tt.want)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 |