github.1git.de/docker/cli@v26.1.3+incompatible/internal/test/cmd.go (about)

     1  package test
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"syscall"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/docker/cli/cli/command"
    11  	"github.com/docker/cli/cli/streams"
    12  	"github.com/spf13/cobra"
    13  	"gotest.tools/v3/assert"
    14  )
    15  
    16  func TerminatePrompt(ctx context.Context, t *testing.T, cmd *cobra.Command, cli *FakeCli) {
    17  	t.Helper()
    18  
    19  	errChan := make(chan error)
    20  	defer close(errChan)
    21  
    22  	// wrap the out stream to detect when the prompt is ready
    23  	writerHookChan := make(chan struct{})
    24  	defer close(writerHookChan)
    25  
    26  	outStream := streams.NewOut(NewWriterWithHook(cli.OutBuffer(), func(p []byte) {
    27  		writerHookChan <- struct{}{}
    28  	}))
    29  	cli.SetOut(outStream)
    30  
    31  	r, _, err := os.Pipe()
    32  	assert.NilError(t, err)
    33  	cli.SetIn(streams.NewIn(r))
    34  
    35  	go func() {
    36  		errChan <- cmd.ExecuteContext(ctx)
    37  	}()
    38  
    39  	writeCtx, writeCancel := context.WithTimeout(ctx, 100*time.Millisecond)
    40  	defer writeCancel()
    41  
    42  	// wait for the prompt to be ready
    43  	select {
    44  	case <-writeCtx.Done():
    45  		t.Fatalf("command %s did not write prompt to stdout", cmd.Name())
    46  	case <-writerHookChan:
    47  		// drain the channel for future buffer writes
    48  		go func() {
    49  			for {
    50  				select {
    51  				case <-ctx.Done():
    52  					return
    53  				case <-writerHookChan:
    54  				}
    55  			}
    56  		}()
    57  	}
    58  
    59  	assert.Check(t, cli.OutBuffer().Len() > 0)
    60  
    61  	// a small delay to ensure the plugin is prompting
    62  	time.Sleep(100 * time.Microsecond)
    63  
    64  	errCtx, errCancel := context.WithTimeout(ctx, 100*time.Millisecond)
    65  	defer errCancel()
    66  
    67  	// sigint and sigterm are caught by the prompt
    68  	// this allows us to gracefully exit the prompt with a 0 exit code
    69  	syscall.Kill(syscall.Getpid(), syscall.SIGINT)
    70  
    71  	select {
    72  	case <-errCtx.Done():
    73  		t.Logf("command stdout:\n%s\n", cli.OutBuffer().String())
    74  		t.Logf("command stderr:\n%s\n", cli.ErrBuffer().String())
    75  		t.Fatalf("command %s did not return after SIGINT", cmd.Name())
    76  	case err := <-errChan:
    77  		assert.ErrorIs(t, err, command.ErrPromptTerminated)
    78  	}
    79  }