Skip to content

Commit b8d5454

Browse files
authored
Merge pull request #4957 from Benehiko/prompt-test-flakiness
fix: flaky prompt termination on reader close test
2 parents b39bbb4 + 7ea10d5 commit b8d5454

File tree

1 file changed

+35
-104
lines changed

1 file changed

+35
-104
lines changed

cli/command/utils_test.go

Lines changed: 35 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -106,120 +106,68 @@ func TestPromptForConfirmation(t *testing.T) {
106106
}()
107107

108108
for _, tc := range []struct {
109-
desc string
110-
f func(*testing.T, context.Context, chan promptResult)
109+
desc string
110+
f func() error
111+
expected promptResult
111112
}{
112-
{"SIGINT", func(t *testing.T, ctx context.Context, c chan promptResult) {
113-
t.Helper()
114-
113+
{"SIGINT", func() error {
115114
syscall.Kill(syscall.Getpid(), syscall.SIGINT)
116-
117-
select {
118-
case <-ctx.Done():
119-
t.Fatal("PromptForConfirmation did not return after SIGINT")
120-
case r := <-c:
121-
assert.Check(t, !r.result)
122-
assert.ErrorContains(t, r.err, "prompt terminated")
123-
}
124-
}},
125-
{"no", func(t *testing.T, ctx context.Context, c chan promptResult) {
126-
t.Helper()
127-
115+
return nil
116+
}, promptResult{false, command.ErrPromptTerminated}},
117+
{"no", func() error {
128118
_, err := fmt.Fprint(promptWriter, "n\n")
129-
assert.NilError(t, err)
130-
131-
select {
132-
case <-ctx.Done():
133-
t.Fatal("PromptForConfirmation did not return after user input `n`")
134-
case r := <-c:
135-
assert.Check(t, !r.result)
136-
assert.NilError(t, r.err)
137-
}
138-
}},
139-
{"yes", func(t *testing.T, ctx context.Context, c chan promptResult) {
140-
t.Helper()
141-
119+
return err
120+
}, promptResult{false, nil}},
121+
{"yes", func() error {
142122
_, err := fmt.Fprint(promptWriter, "y\n")
143-
assert.NilError(t, err)
144-
145-
select {
146-
case <-ctx.Done():
147-
t.Fatal("PromptForConfirmation did not return after user input `y`")
148-
case r := <-c:
149-
assert.Check(t, r.result)
150-
assert.NilError(t, r.err)
151-
}
152-
}},
153-
{"any", func(t *testing.T, ctx context.Context, c chan promptResult) {
154-
t.Helper()
155-
123+
return err
124+
}, promptResult{true, nil}},
125+
{"any", func() error {
156126
_, err := fmt.Fprint(promptWriter, "a\n")
157-
assert.NilError(t, err)
158-
159-
select {
160-
case <-ctx.Done():
161-
t.Fatal("PromptForConfirmation did not return after user input `a`")
162-
case r := <-c:
163-
assert.Check(t, !r.result)
164-
assert.NilError(t, r.err)
165-
}
166-
}},
167-
{"with space", func(t *testing.T, ctx context.Context, c chan promptResult) {
168-
t.Helper()
169-
127+
return err
128+
}, promptResult{false, nil}},
129+
{"with space", func() error {
170130
_, err := fmt.Fprint(promptWriter, " y\n")
171-
assert.NilError(t, err)
172-
173-
select {
174-
case <-ctx.Done():
175-
t.Fatal("PromptForConfirmation did not return after user input ` y`")
176-
case r := <-c:
177-
assert.Check(t, r.result)
178-
assert.NilError(t, r.err)
179-
}
180-
}},
181-
{"reader closed", func(t *testing.T, ctx context.Context, c chan promptResult) {
182-
t.Helper()
183-
184-
assert.NilError(t, promptReader.Close())
185-
186-
select {
187-
case <-ctx.Done():
188-
t.Fatal("PromptForConfirmation did not return after promptReader was closed")
189-
case r := <-c:
190-
assert.Check(t, !r.result)
191-
assert.NilError(t, r.err)
192-
}
193-
}},
131+
return err
132+
}, promptResult{true, nil}},
133+
{"reader closed", func() error {
134+
return promptReader.Close()
135+
}, promptResult{false, nil}},
194136
} {
195137
t.Run("case="+tc.desc, func(t *testing.T) {
196138
buf.Reset()
197139
promptReader, promptWriter = io.Pipe()
198140

199141
wroteHook := make(chan struct{}, 1)
200-
defer close(wroteHook)
201142
promptOut := test.NewWriterWithHook(bufioWriter, func(p []byte) {
202143
wroteHook <- struct{}{}
203144
})
204145

205146
result := make(chan promptResult, 1)
206-
defer close(result)
207147
go func() {
208148
r, err := command.PromptForConfirmation(ctx, promptReader, promptOut, "")
209149
result <- promptResult{r, err}
210150
}()
211151

212-
// wait for the Prompt to write to the buffer
213-
pollForPromptOutput(ctx, t, wroteHook)
214-
drainChannel(ctx, wroteHook)
152+
select {
153+
case <-time.After(100 * time.Millisecond):
154+
case <-wroteHook:
155+
}
215156

216157
assert.NilError(t, bufioWriter.Flush())
217158
assert.Equal(t, strings.TrimSpace(buf.String()), "Are you sure you want to proceed? [y/N]")
218159

219-
resultCtx, resultCancel := context.WithTimeout(ctx, 100*time.Millisecond)
220-
defer resultCancel()
160+
// wait for the Prompt to write to the buffer
161+
drainChannel(ctx, wroteHook)
162+
163+
assert.NilError(t, tc.f())
221164

222-
tc.f(t, resultCtx, result)
165+
select {
166+
case <-time.After(500 * time.Millisecond):
167+
t.Fatal("timeout waiting for prompt result")
168+
case r := <-result:
169+
assert.Equal(t, r, tc.expected)
170+
}
223171
})
224172
}
225173
}
@@ -235,20 +183,3 @@ func drainChannel(ctx context.Context, ch <-chan struct{}) {
235183
}
236184
}()
237185
}
238-
239-
func pollForPromptOutput(ctx context.Context, t *testing.T, wroteHook <-chan struct{}) {
240-
t.Helper()
241-
242-
ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
243-
defer cancel()
244-
245-
for {
246-
select {
247-
case <-ctx.Done():
248-
t.Fatal("Prompt output was not written to before ctx was cancelled")
249-
return
250-
case <-wroteHook:
251-
return
252-
}
253-
}
254-
}

0 commit comments

Comments
 (0)