| package test | |
| import ( | |
| "context" | |
| "os" | |
| "testing" | |
| "time" | |
| "github.com/docker/cli/cli/streams" | |
| "github.com/docker/cli/internal/prompt" | |
| "github.com/spf13/cobra" | |
| "gotest.tools/v3/assert" | |
| ) | |
| func TerminatePrompt(ctx context.Context, t *testing.T, cmd *cobra.Command, cli *FakeCli) { | |
| t.Helper() | |
| errChan := make(chan error) | |
| defer close(errChan) | |
| // wrap the out stream to detect when the prompt is ready | |
| writerHookChan := make(chan struct{}) | |
| defer close(writerHookChan) | |
| outStream := streams.NewOut(NewWriterWithHook(cli.OutBuffer(), func(p []byte) { | |
| writerHookChan <- struct{}{} | |
| })) | |
| cli.SetOut(outStream) | |
| r, _, err := os.Pipe() | |
| assert.NilError(t, err) | |
| cli.SetIn(streams.NewIn(r)) | |
| notifyCtx, notifyCancel := context.WithCancel(ctx) | |
| t.Cleanup(notifyCancel) | |
| go func() { | |
| errChan <- cmd.ExecuteContext(notifyCtx) | |
| }() | |
| writeCtx, writeCancel := context.WithTimeout(ctx, 100*time.Millisecond) | |
| defer writeCancel() | |
| // wait for the prompt to be ready | |
| select { | |
| case <-writeCtx.Done(): | |
| t.Fatalf("command %s did not write prompt to stdout", cmd.Name()) | |
| case <-writerHookChan: | |
| // drain the channel for future buffer writes | |
| go func() { | |
| for { | |
| select { | |
| case <-ctx.Done(): | |
| return | |
| case <-writerHookChan: | |
| } | |
| } | |
| }() | |
| } | |
| assert.Check(t, cli.OutBuffer().Len() > 0) | |
| // a small delay to ensure the plugin is prompting | |
| time.Sleep(100 * time.Microsecond) | |
| errCtx, errCancel := context.WithTimeout(ctx, 100*time.Millisecond) | |
| defer errCancel() | |
| // sigint and sigterm are caught by the prompt | |
| // this allows us to gracefully exit the prompt with a 0 exit code | |
| notifyCancel() | |
| select { | |
| case <-errCtx.Done(): | |
| t.Logf("command stdout:\n%s\n", cli.OutBuffer().String()) | |
| t.Logf("command stderr:\n%s\n", cli.ErrBuffer().String()) | |
| t.Fatalf("command %s did not return after SIGINT", cmd.Name()) | |
| case err := <-errChan: | |
| assert.ErrorIs(t, err, prompt.ErrTerminated) | |
| } | |
| } |