...

Source file src/github.com/redhat-developer/odo/tests/helper/helper_interactive.go

Documentation: github.com/redhat-developer/odo/tests/helper

     1  package helper
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"time"
     9  
    10  	"github.com/ActiveState/termtest"
    11  	"github.com/ActiveState/termtest/expect"
    12  	. "github.com/onsi/ginkgo/v2"
    13  	. "github.com/onsi/gomega"
    14  )
    15  
    16  // InteractiveContext represents the context of an interactive command to be run.
    17  type InteractiveContext struct {
    18  
    19  	// Command represents the original command ran
    20  	Command []string
    21  
    22  	// cp is the internal interface used by the interactive command
    23  	cp *termtest.ConsoleProcess
    24  
    25  	// buffer is the internal bytes buffer containing the console output.
    26  	// Its content will get updated as long as there are interactions with the console, like sending lines or
    27  	// expecting lines.
    28  	buffer *bytes.Buffer
    29  
    30  	// A function to call to stop the process
    31  	StopCommand func()
    32  }
    33  
    34  // Tester represents the function that contains all steps to test the given interactive command.
    35  // The InteractiveContext argument needs to be passed to the various helper.SendLine and helper.ExpectString methods.
    36  type Tester func(InteractiveContext)
    37  
    38  // RunInteractive runs the command in interactive mode and returns the output, and error.
    39  // It takes command as array of strings, and a function `tester` that contains steps to run the test as an argument.
    40  // The command is executed as a separate process, the environment of which is controlled via the `env` argument.
    41  // The initial value of the sub-process environment is a copy of the environment of the current process.
    42  // If `env` is not `nil`, it will be appended to the end of the sub-process environment.
    43  // If there are duplicate environment keys, only the last value in the slice for each duplicate key is used.
    44  func RunInteractive(command []string, env []string, tester Tester) (string, error) {
    45  
    46  	fmt.Fprintln(GinkgoWriter, "running command", command, "with env", env)
    47  
    48  	wd, err := os.Getwd()
    49  	if err != nil {
    50  		log.Fatal(err)
    51  	}
    52  
    53  	opts := termtest.Options{
    54  		CmdName:       command[0],
    55  		Args:          command[1:],
    56  		WorkDirectory: wd,
    57  		RetainWorkDir: true,
    58  		ExtraOpts:     []expect.ConsoleOpt{},
    59  	}
    60  
    61  	if env != nil {
    62  		opts.Environment = append(os.Environ(), env...)
    63  	}
    64  
    65  	cp, err := termtest.New(opts)
    66  	if err != nil {
    67  		log.Fatal(err)
    68  	}
    69  	defer cp.Close()
    70  
    71  	buf := new(bytes.Buffer)
    72  	ctx := InteractiveContext{
    73  		Command: command,
    74  		buffer:  buf,
    75  		StopCommand: func() {
    76  			_ = cp.Signal(os.Kill)
    77  		},
    78  		cp: cp,
    79  	}
    80  	tester(ctx)
    81  
    82  	_, err = cp.ExpectExitCode(0)
    83  
    84  	return buf.String(), err
    85  }
    86  
    87  // expectDescriptionSupplier returns a function intended to be used as description supplier
    88  // when checking errors do not occur in ExpectString and SendLine.
    89  // Note that the function returned is evaluated lazily, only in case an error occurs.
    90  func expectDescriptionSupplier(ctx InteractiveContext, line string) func() string {
    91  	return func() string {
    92  		return fmt.Sprintf("error while sending or expecting line: \"%s\"\n"+
    93  			"=== output of command '%+q' read so far ===\n%v\n======================",
    94  			line,
    95  			ctx.Command,
    96  			ctx.buffer)
    97  	}
    98  }
    99  
   100  func SendLine(ctx InteractiveContext, line string) {
   101  	ctx.cp.Send(line)
   102  }
   103  
   104  func PressKey(ctx InteractiveContext, c byte) {
   105  	ctx.cp.SendUnterminated(string(c))
   106  }
   107  
   108  func ExpectString(ctx InteractiveContext, line string) {
   109  	res, err := ctx.cp.Expect(line, 120*time.Second)
   110  	fmt.Fprint(ctx.buffer, res)
   111  	Expect(err).ShouldNot(HaveOccurred(), expectDescriptionSupplier(ctx, line))
   112  }
   113  

View as plain text