diff --git a/app_test.go b/app_test.go index c9b9000b0..a45b4cd37 100644 --- a/app_test.go +++ b/app_test.go @@ -35,13 +35,13 @@ import ( "testing" "time" - "github.com/benbjohnson/clock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" . "go.uber.org/fx" "go.uber.org/fx/fxevent" "go.uber.org/fx/fxtest" + "go.uber.org/fx/internal/fxclock" "go.uber.org/fx/internal/fxlog" "go.uber.org/goleak" "go.uber.org/multierr" @@ -1221,7 +1221,7 @@ func TestAppRunTimeout(t *testing.T) { } // Builds a hook that takes much longer than the application timeout. - takeVeryLong := func(clock *clock.Mock) func() error { + takeVeryLong := func(clock *fxclock.Mock) func() error { return func() error { // We'll exceed the start and stop timeouts, // and then some. @@ -1237,7 +1237,7 @@ func TestAppRunTimeout(t *testing.T) { desc string // buildHook builds and returns the hooks for this test case. - buildHooks func(*clock.Mock) []Hook + buildHooks func(*fxclock.Mock) []Hook // Type of the fxevent we want. // Does not reflect the exact value. @@ -1246,7 +1246,7 @@ func TestAppRunTimeout(t *testing.T) { { // Timeout starting an application. desc: "OnStart timeout", - buildHooks: func(clock *clock.Mock) []Hook { + buildHooks: func(clock *fxclock.Mock) []Hook { return []Hook{ StartHook(takeVeryLong(clock)), } @@ -1256,7 +1256,7 @@ func TestAppRunTimeout(t *testing.T) { { // Timeout during a rollback because start failed. desc: "rollback timeout", - buildHooks: func(clock *clock.Mock) []Hook { + buildHooks: func(clock *fxclock.Mock) []Hook { return []Hook{ // The hooks are separate because // OnStop will not be run if that hook failed. @@ -1269,7 +1269,7 @@ func TestAppRunTimeout(t *testing.T) { { // Timeout during a stop. desc: "OnStop timeout", - buildHooks: func(clock *clock.Mock) []Hook { + buildHooks: func(clock *fxclock.Mock) []Hook { return []Hook{ StopHook(takeVeryLong(clock)), } @@ -1283,7 +1283,7 @@ func TestAppRunTimeout(t *testing.T) { t.Run(tt.desc, func(t *testing.T) { t.Parallel() - mockClock := clock.NewMock() + mockClock := fxclock.NewMock() var ( exitCode int @@ -1351,7 +1351,7 @@ func TestAppStart(t *testing.T) { t.Run("Timeout", func(t *testing.T) { t.Parallel() - mockClock := clock.NewMock() + mockClock := fxclock.NewMock() type A struct{} blocker := func(lc Lifecycle) *A { @@ -1388,7 +1388,7 @@ func TestAppStart(t *testing.T) { t.Run("TimeoutWithFinishedHooks", func(t *testing.T) { t.Parallel() - mockClock := clock.NewMock() + mockClock := fxclock.NewMock() type A struct{} type B struct{ A *A } @@ -1540,7 +1540,7 @@ func TestAppStart(t *testing.T) { t.Parallel() var ran bool - mockClock := clock.NewMock() + mockClock := fxclock.NewMock() app := New( WithClock(mockClock), Invoke(func(lc Lifecycle) { @@ -1820,7 +1820,7 @@ func TestAppStop(t *testing.T) { t.Run("Timeout", func(t *testing.T) { t.Parallel() - mockClock := clock.NewMock() + mockClock := fxclock.NewMock() block := func(ctx context.Context) error { mockClock.Add(5 * time.Second) diff --git a/docs/go.mod b/docs/go.mod index 865b30b7b..1f4d20db5 100644 --- a/docs/go.mod +++ b/docs/go.mod @@ -3,17 +3,16 @@ module go.uber.org/fx/docs go 1.20 require ( - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 go.uber.org/fx v1.18.2 - go.uber.org/zap v1.23.0 + go.uber.org/zap v1.26.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.uber.org/atomic v1.7.0 // indirect go.uber.org/dig v1.17.1 // indirect - go.uber.org/multierr v1.6.0 // indirect + go.uber.org/multierr v1.10.0 // indirect golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/docs/go.sum b/docs/go.sum index 83453e186..2e829754b 100644 --- a/docs/go.sum +++ b/docs/go.sum @@ -1,25 +1,23 @@ -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/go.mod b/go.mod index 777417de1..e322f63d5 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,12 @@ module go.uber.org/fx go 1.20 require ( - github.com/benbjohnson/clock v1.3.0 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 go.uber.org/atomic v1.7.0 go.uber.org/dig v1.17.1 - go.uber.org/goleak v1.1.11 - go.uber.org/multierr v1.6.0 - go.uber.org/zap v1.23.0 + go.uber.org/goleak v1.2.0 + go.uber.org/multierr v1.10.0 + go.uber.org/zap v1.26.0 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad ) diff --git a/go.sum b/go.sum index 443ecde43..b20efccb7 100644 --- a/go.sum +++ b/go.sum @@ -1,66 +1,34 @@ -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/e2e/go.mod b/internal/e2e/go.mod index 5335cc330..3f0ed8f99 100644 --- a/internal/e2e/go.mod +++ b/internal/e2e/go.mod @@ -10,10 +10,9 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.uber.org/atomic v1.7.0 // indirect go.uber.org/dig v1.17.1 // indirect - go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.23.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/internal/e2e/go.sum b/internal/e2e/go.sum index 36c0e3960..e5abd8557 100644 --- a/internal/e2e/go.sum +++ b/internal/e2e/go.sum @@ -1,27 +1,23 @@ -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/internal/fxclock/clock.go b/internal/fxclock/clock.go index bf1c7058f..6a2bdbcdc 100644 --- a/internal/fxclock/clock.go +++ b/internal/fxclock/clock.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Uber Technologies, Inc. +// Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -22,13 +22,13 @@ package fxclock import ( "context" + "sort" + "sync" "time" ) // Clock defines how Fx accesses time. -// The interface is pretty minimal but it matches github.com/benbjohnson/clock. -// We intentionally don't use that interface directly; -// this keeps it a test dependency for us. +// We keep the interface pretty minimal. type Clock interface { Now() time.Time Since(time.Time) time.Duration @@ -56,3 +56,199 @@ func (systemClock) Sleep(d time.Duration) { func (systemClock) WithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { return context.WithTimeout(ctx, d) } + +// Mock adapted from +// https://github.com/uber-go/zap/blob/7db06bc9b095571d3dc3d4eebdfbe4dd9bd20405/internal/ztest/clock.go. + +// Mock is a fake source of time. +// It implements standard time operations, +// but allows the user to control the passage of time. +// +// Use the [Add] method to progress time. +type Mock struct { + mu sync.RWMutex + now time.Time + + // The MockClock works by maintaining a list of waiters. + // Each waiter knows the time at which it should be resolved. + // When the clock advances, all waiters that are in range are resolved + // in chronological order. + waiters []waiter + waiterAdded *sync.Cond +} + +var _ Clock = (*Mock)(nil) + +// NewMock builds a new mock clock +// using the current actual time as the initial time. +func NewMock() *Mock { + m := &Mock{now: time.Now()} + m.waiterAdded = sync.NewCond(&m.mu) + return m +} + +// Now reports the current time. +func (c *Mock) Now() time.Time { + c.mu.RLock() + defer c.mu.RUnlock() + return c.now +} + +// Since reports the time elapsed since t. +// This is short for Now().Sub(t). +func (c *Mock) Since(t time.Time) time.Duration { + return c.Now().Sub(t) +} + +// Sleep pauses the current goroutine for the given duration. +// +// With the mock clock, this will freeze +// until the clock is advanced with [Add] past the deadline. +func (c *Mock) Sleep(d time.Duration) { + ch := make(chan struct{}) + c.runAt(c.Now().Add(d), func() { close(ch) }) + <-ch +} + +// WithTimeout returns a new context with a deadline of now + d. +// +// When the deadline is passed, the returned context's Done channel is closed +// and the context's Err method returns context.DeadlineExceeded. +// If the cancel function is called before the deadline is passed, +// the context's Err method returns context.Canceled. +func (c *Mock) WithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { + // Unfortunately, we can't use context.WithCancelCause here. + // Per its documentation (and verified by trying it): + // + // ctx, cancel := context.WithCancelCause(parent) + // cancel(myError) + // ctx.Err() // returns context.Canceled + // context.Cause(ctx) // returns myError + // + // So it won't do for our purposes. + deadline := c.Now().Add(d) + inner, cancelInner := context.WithCancel(ctx) + dctx := &deadlineCtx{ + inner: inner, + cancelInner: cancelInner, + done: make(chan struct{}), + deadline: deadline, + } + ctx = dctx + + c.runAt(deadline, func() { + dctx.cancel(context.DeadlineExceeded) + }) + return ctx, func() { dctx.cancel(context.Canceled) } +} + +type deadlineCtx struct { + inner context.Context + cancelInner func() + + done chan struct{} + deadline time.Time + + mu sync.Mutex // guards err; the rest is immutable + err error +} + +var _ context.Context = (*deadlineCtx)(nil) + +func (c *deadlineCtx) Deadline() (deadline time.Time, ok bool) { return c.deadline, true } +func (c *deadlineCtx) Done() <-chan struct{} { return c.done } +func (c *deadlineCtx) Value(key any) any { return c.inner.Value(key) } + +func (c *deadlineCtx) Err() error { + c.mu.Lock() + err := c.err + c.mu.Unlock() + return err +} + +func (c *deadlineCtx) cancel(err error) { + c.mu.Lock() + if c.err == nil { + c.err = err + close(c.done) + c.cancelInner() + } + c.mu.Unlock() +} + +// runAt schedules the given function to be run at the given time. +// The function runs without a lock held, so it may schedule more work. +func (c *Mock) runAt(t time.Time, fn func()) { + c.mu.Lock() + defer c.mu.Unlock() + + c.waiters = append(c.waiters, waiter{until: t, fn: fn}) + c.waiterAdded.Broadcast() +} + +// AwaitScheduled blocks until there are are at least N +// operations scheduled for the future. +func (c *Mock) AwaitScheduled(n int) { + c.mu.Lock() + defer c.mu.Unlock() + + for len(c.waiters) < n { + c.waiterAdded.Wait() + } +} + +type waiter struct { + until time.Time + fn func() +} + +// Add progresses time by the given duration. +// Other operations waiting for the time to advance +// will be resolved if they are within range. +// +// Side effects of operations waiting for the time to advance +// will take effect on a best-effort basis. +// Avoid racing with operations that have side effects. +// +// Panics if the duration is negative. +func (c *Mock) Add(d time.Duration) { + if d < 0 { + panic("cannot add negative duration") + } + + c.mu.Lock() + defer c.mu.Unlock() + + sort.Slice(c.waiters, func(i, j int) bool { + return c.waiters[i].until.Before(c.waiters[j].until) + }) + + newTime := c.now.Add(d) + // newTime won't be recorded until the end of this method. + // This ensures that any waiters that are resolved + // are resolved at the time they were expecting. + + for len(c.waiters) > 0 { + w := c.waiters[0] + if w.until.After(newTime) { + break + } + c.waiters[0] = waiter{} // avoid memory leak + c.waiters = c.waiters[1:] + + // The waiter is within range. + // Travel to the time of the waiter and resolve it. + c.now = w.until + + // The waiter may schedule more work + // so we must release the lock. + c.mu.Unlock() + w.fn() + // Sleeping here is necessary to let the side effects of waiters + // take effect before we continue. + time.Sleep(1 * time.Millisecond) + c.mu.Lock() + } + + c.now = newTime +} diff --git a/internal/fxclock/clock_test.go b/internal/fxclock/clock_test.go index 560867ecd..677dfde22 100644 --- a/internal/fxclock/clock_test.go +++ b/internal/fxclock/clock_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Uber Technologies, Inc. +// Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -22,44 +22,150 @@ package fxclock import ( "context" + "sync" "testing" "time" - "github.com/benbjohnson/clock" "github.com/stretchr/testify/assert" ) -var _ Clock = clock.Clock(nil) - -// Just a basic sanity check that everything is in order. func TestSystemClock(t *testing.T) { - t.Parallel() - clock := System + testClock(t, System, clock.Sleep) +} + +func TestMockClock(t *testing.T) { + clock := NewMock() + testClock(t, clock, clock.Add) +} - t.Run("Now and Since", func(t *testing.T) { - t.Parallel() +func testClock(t *testing.T, clock Clock, advance func(d time.Duration)) { + now := clock.Now() + assert.False(t, now.IsZero()) - before := clock.Now() - assert.GreaterOrEqual(t, clock.Since(before), time.Duration(0)) + t.Run("Since", func(t *testing.T) { + advance(1 * time.Millisecond) + assert.NotZero(t, clock.Since(now), "time must have advanced") }) t.Run("Sleep", func(t *testing.T) { - t.Parallel() + start := clock.Now() + go advance(1 * time.Millisecond) + clock.Sleep(1 * time.Millisecond) - assert.NotPanics(t, func() { - clock.Sleep(time.Millisecond) - }) + assert.NotZero(t, clock.Since(start), "time must have advanced") }) t.Run("WithTimeout", func(t *testing.T) { - t.Parallel() + ctx, cancel := clock.WithTimeout(context.Background(), 1*time.Millisecond) + defer cancel() + + t.Run("Deadline", func(t *testing.T) { + dl, ok := ctx.Deadline() + assert.True(t, ok, "must have a deadline") + assert.True(t, dl.After(now), "deadline must be in the future") + }) - ctx := context.Background() - ctx, cancel := clock.WithTimeout(ctx, time.Second) + advance(1 * time.Millisecond) + + select { + case <-ctx.Done(): + assert.Error(t, ctx.Err(), "done context must error") + assert.ErrorIs(t, ctx.Err(), context.DeadlineExceeded, + "context must have exceeded its deadline") + + case <-time.After(10 * time.Millisecond): + t.Fatal("expected context to be done") + } + }) + + t.Run("WithTimeout/Value", func(t *testing.T) { + type contextKey string + key := contextKey("foo") + + ctx1 := context.WithValue(context.Background(), key, "bar") + + ctx2, cancel := clock.WithTimeout(ctx1, 1*time.Millisecond) defer cancel() - _, ok := ctx.Deadline() - assert.True(t, ok, "must have deadline") + assert.Equal(t, "bar", ctx2.Value(key), "value must be preserved") }) + + t.Run("WithTimeout/Cancel", func(t *testing.T) { + ctx, cancel := clock.WithTimeout(context.Background(), 1*time.Millisecond) + cancel() + + select { + case <-ctx.Done(): + assert.Error(t, ctx.Err(), "done context must error") + assert.ErrorIs(t, ctx.Err(), context.Canceled, + "context must have been canceled") + + case <-time.After(10 * time.Millisecond): + t.Fatal("expected context to be done") + } + }) +} + +func TestMock_Sleep(t *testing.T) { + clock := NewMock() + + ch := make(chan struct{}) + go func() { + clock.Sleep(2 * time.Millisecond) + close(ch) + }() + + // We cannot advance time until we're certain + // that the Sleep call has started waiting. + // Otherwise, we'll advance that one millisecond, + // and then the Sleep will start waiting for another Advance, + // which will never come. + // + // AwaitScheduled will block until there is at least one + // scheduled event. + clock.AwaitScheduled(1) + + // Adbance only one millisecond, the Sleep should not return. + clock.Add(1 * time.Millisecond) + select { + case <-ch: + t.Fatal("sleep should not have returned") + case <-time.After(1 * time.Millisecond): + // ok + } + + // Avance to the next millisecond, the Sleep should return. + clock.Add(1 * time.Millisecond) + select { + case <-ch: + // ok + case <-time.After(10 * time.Millisecond): + t.Fatal("expected Sleep to return") + } +} + +func TestMock_AddNegative(t *testing.T) { + clock := NewMock() + assert.Panics(t, func() { clock.Add(-1) }) +} + +func TestMock_ManySleepers(t *testing.T) { + const N = 100 + + clock := NewMock() + + var wg sync.WaitGroup + wg.Add(N) + for i := 0; i < N; i++ { + go func() { + defer wg.Done() + + clock.Sleep(1 * time.Millisecond) + }() + } + + clock.AwaitScheduled(N) + clock.Add(1 * time.Millisecond) + wg.Wait() }