From fb58f9e6d2e9609590af1417d090f9c0aeb29f7b Mon Sep 17 00:00:00 2001 From: Alisdair McDiarmid Date: Mon, 18 Oct 2021 16:54:31 -0400 Subject: [PATCH] cli: Fix flaky init cancel test There is a race between the MockSource and ShutdownCh which sometimes causes this test to fail. Add a HangingSource implementation of Source which hangs until the context is cancelled, so that there is always time for a user-initiated shutdown to trigger the cancellation code path under test. --- internal/command/init_test.go | 14 +++++------- internal/getproviders/hanging_source.go | 29 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 internal/getproviders/hanging_source.go diff --git a/internal/command/init_test.go b/internal/command/init_test.go index 2d96b27b4..74d4b0f64 100644 --- a/internal/command/init_test.go +++ b/internal/command/init_test.go @@ -1372,14 +1372,12 @@ func TestInit_cancel(t *testing.T) { defer os.RemoveAll(td) defer testChdir(t, td)() - providerSource, closeSrc := newMockProviderSource(t, map[string][]string{ - "test": {"1.2.3", "1.2.4"}, - "test-beta": {"1.2.4"}, - "source": {"1.2.2", "1.2.3", "1.2.1"}, - }) - defer closeSrc() + // Use a provider source implementation which is designed to hang indefinitely, + // to avoid a race between the closed shutdown channel and the provider source + // operations. + providerSource := &getproviders.HangingSource{} - // our shutdown channel is pre-closed so init will exit as soon as it + // Our shutdown channel is pre-closed so init will exit as soon as it // starts a cancelable portion of the process. shutdownCh := make(chan struct{}) close(shutdownCh) @@ -1401,7 +1399,7 @@ func TestInit_cancel(t *testing.T) { args := []string{} if code := c.Run(args); code == 0 { - t.Fatalf("succeeded; wanted error") + t.Fatalf("succeeded; wanted error\n%s", ui.OutputWriter.String()) } // Currently the first operation that is cancelable is provider // installation, so our error message comes from there. If we diff --git a/internal/getproviders/hanging_source.go b/internal/getproviders/hanging_source.go new file mode 100644 index 000000000..388b61701 --- /dev/null +++ b/internal/getproviders/hanging_source.go @@ -0,0 +1,29 @@ +package getproviders + +import ( + "context" + + "github.com/hashicorp/terraform/internal/addrs" +) + +// HangingSource is an implementation of Source which hangs until the given +// context is cancelled. This is useful only for unit tests of user-controlled +// cancels. +type HangingSource struct { +} + +var _ Source = (*HangingSource)(nil) + +func (s *HangingSource) AvailableVersions(ctx context.Context, provider addrs.Provider) (VersionList, Warnings, error) { + <-ctx.Done() + return nil, nil, nil +} + +func (s *HangingSource) PackageMeta(ctx context.Context, provider addrs.Provider, version Version, target Platform) (PackageMeta, error) { + <-ctx.Done() + return PackageMeta{}, nil +} + +func (s *HangingSource) ForDisplay(provider addrs.Provider) string { + return "hanging source" +}