clock

package
v0.151.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 11, 2024 License: Apache-2.0 Imports: 2 Imported by: 8

README

Clock and Timecop

DESCRIPTION

Package providing "time travel" and "time scaling" capabilities, making it simple to test time-dependent code.

INSTALL

go get -u go.llib.dev/testcase

FEATURES

  • Freeze time to a specific point.
  • Travel back to a specific time, but allow time to continue moving forward.
  • Scale time by a given scaling factor will cause the time to move at an accelerated pace.
  • No dependencies other than the stdlib
  • Nested calls to timecop.Travel is supported
  • Works with any regular Go projects

USAGE

package main

import (
   "go.llib.dev/testcase/assert"
   "go.llib.dev/testcase/clock"
   "go.llib.dev/testcase/clock/timecop"
   "testing"
   "time"
)

func Test(t *testing.T) {
   type Entity struct {
      CreatedAt time.Time
   }

   MyFunc := func() Entity {
      return Entity{
         CreatedAt: clock.TimeNow(),
      }
   }

   expected := Entity{
      CreatedAt: clock.TimeNow(),
   }

   timecop.Travel(t, expected.CreatedAt, timecop.Freeze())

   assert.Equal(t, expected, MyFunc())
}

Time travelling is undone as part of the test's teardown.

timecop.Travel + timecop.Freeze

The Freeze option causes the observed time to stop until the first time reading event.

timecop.SetSpeed

Let's say you want to test a "live" integration wherein entire days could pass by in minutes while you're able to simulate "real" activity. For example, one such use case is being able to test reports and invoices that run in 30-day cycles in very little time while also simulating activity via subsequent calls to your application.

timecop.SetSpeed(t, 1000) // accelerate speed by 1000x times from now on. 
<-clock.After(time.Hour) // takes only 1/1000 time to finish, not an hour.
clock.Sleep(time.Hour) // same

Design

The package uses a singleton pattern. The original design had a Clock and a Timecop type to do dependency injection, but upon doing spiking with it, it felt foreign to how we currently use time.Now() or time.After(duration). Also, it made it possible that different components reside in different timelines, while time should be observed as a singleton entity by the whole application. Time manipulation seems to be a good use case where the singleton pattern is the least wrong solution.

References

The package was inspired by travisjeffery' timecop project.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func After

func After(d time.Duration) <-chan time.Time
Example
package main

import (
	"testing"
	"time"

	"go.llib.dev/testcase/clock"
	"go.llib.dev/testcase/clock/timecop"
)

func main() {
	var tb testing.TB
	timecop.SetSpeed(tb, 5)    // 5x time speed
	<-clock.After(time.Second) // but only wait 1/5 of the time
}
Output:

func Sleep

func Sleep(d time.Duration)
Example
package main

import (
	"testing"
	"time"

	"go.llib.dev/testcase/clock"
	"go.llib.dev/testcase/clock/timecop"
)

func main() {
	var tb testing.TB
	timecop.SetSpeed(tb, 5)  // 5x time speed
	clock.Sleep(time.Second) // but only sleeps 1/5 of the time
}
Output:

func TimeNow

func TimeNow() time.Time
Example (Freeze)
package main

import (
	"testing"
	"time"

	"go.llib.dev/testcase/assert"
	"go.llib.dev/testcase/clock"
	"go.llib.dev/testcase/clock/timecop"
)

func main() {
	var tb testing.TB

	type Entity struct {
		CreatedAt time.Time
	}

	MyFunc := func() Entity {
		return Entity{
			CreatedAt: clock.TimeNow(),
		}
	}

	expected := Entity{
		CreatedAt: clock.TimeNow(),
	}

	timecop.Travel(tb, expected.CreatedAt, timecop.Freeze())

	assert.Equal(tb, expected, MyFunc())
}
Output:

Example (WithTravelByDate)
package main

import (
	"testing"
	"time"

	"go.llib.dev/testcase/clock"
	"go.llib.dev/testcase/clock/timecop"
)

func main() {
	var tb testing.TB

	date := time.Date(2022, 01, 01, 12, 0, 0, 0, time.Local)
	timecop.Travel(tb, date, timecop.Freeze()) // freeze the time until it is read
	time.Sleep(time.Second)
	_ = clock.TimeNow() // equals with date
}
Output:

Example (WithTravelByDuration)
package main

import (
	"testing"
	"time"

	"go.llib.dev/testcase/clock"
	"go.llib.dev/testcase/clock/timecop"
)

func main() {
	var tb testing.TB

	_ = clock.TimeNow() // now
	timecop.Travel(tb, time.Hour)
	_ = clock.TimeNow() // now + 1 hour
}
Output:

Types

This section is empty.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL