github.com/gofunct/common@v0.0.0-20190131174352-fd058c7fbf22/pkg/exec/testing/fake_exec.go (about) 1 package testingexec 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 8 "github.com/gofunct/common/pkg/exec" 9 ) 10 11 // FakeExec is a simple scripted Interface type. 12 type FakeExec struct { 13 CommandScript []FakeCommandAction 14 CommandCalls int 15 LookPathFunc func(string) (string, error) 16 } 17 18 var _ exec.Interface = &FakeExec{} 19 20 // FakeCommandAction is the function to be executed 21 type FakeCommandAction func(cmd string, args ...string) exec.Cmd 22 23 // Command is to track the commands that are executed 24 func (fake *FakeExec) Command(cmd string, args ...string) exec.Cmd { 25 if fake.CommandCalls > len(fake.CommandScript)-1 { 26 panic(fmt.Sprintf("ran out of Command() actions. Could not handle command [%d]: %s args: %v", fake.CommandCalls, cmd, args)) 27 } 28 i := fake.CommandCalls 29 fake.CommandCalls++ 30 return fake.CommandScript[i](cmd, args...) 31 } 32 33 // CommandContext wraps arguments into exec.Cmd 34 func (fake *FakeExec) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd { 35 return fake.Command(cmd, args...) 36 } 37 38 // LookPath is for finding the path of a file 39 func (fake *FakeExec) LookPath(file string) (string, error) { 40 return fake.LookPathFunc(file) 41 } 42 43 // FakeCmd is a simple scripted Cmd type. 44 type FakeCmd struct { 45 Argv []string 46 CombinedOutputScript []FakeCombinedOutputAction 47 CombinedOutputCalls int 48 CombinedOutputLog [][]string 49 RunScript []FakeRunAction 50 RunCalls int 51 RunLog [][]string 52 Dirs []string 53 Stdin io.Reader 54 Stdout io.Writer 55 Stderr io.Writer 56 Env []string 57 StdoutPipeResponse FakeStdIOPipeResponse 58 StderrPipeResponse FakeStdIOPipeResponse 59 WaitResponse error 60 StartResponse error 61 } 62 63 var _ exec.Cmd = &FakeCmd{} 64 65 // InitFakeCmd is for creating a fake exec.Cmd 66 func InitFakeCmd(fake *FakeCmd, cmd string, args ...string) exec.Cmd { 67 fake.Argv = append([]string{cmd}, args...) 68 return fake 69 } 70 71 // FakeStdIOPipeResponse holds responses to use as fakes for the StdoutPipe and 72 // StderrPipe method calls 73 type FakeStdIOPipeResponse struct { 74 ReadCloser io.ReadCloser 75 Error error 76 } 77 78 // FakeCombinedOutputAction is a function type 79 type FakeCombinedOutputAction func() ([]byte, error) 80 81 // FakeRunAction is a function type 82 type FakeRunAction func() ([]byte, []byte, error) 83 84 // SetDir sets the directory 85 func (fake *FakeCmd) SetDir(dir string) { 86 fake.Dirs = append(fake.Dirs, dir) 87 } 88 89 // SetStdin sets the stdin 90 func (fake *FakeCmd) SetStdin(in io.Reader) { 91 fake.Stdin = in 92 } 93 94 // SetStdout sets the stdout 95 func (fake *FakeCmd) SetStdout(out io.Writer) { 96 fake.Stdout = out 97 } 98 99 // SetStderr sets the stderr 100 func (fake *FakeCmd) SetStderr(out io.Writer) { 101 fake.Stderr = out 102 } 103 104 // SetEnv sets the environment variables 105 func (fake *FakeCmd) SetEnv(env []string) { 106 fake.Env = env 107 } 108 109 // StdoutPipe returns an injected ReadCloser & error (via StdoutPipeResponse) 110 // to be able to inject an output stream on Stdout 111 func (fake *FakeCmd) StdoutPipe() (io.ReadCloser, error) { 112 return fake.StdoutPipeResponse.ReadCloser, fake.StdoutPipeResponse.Error 113 } 114 115 // StderrPipe returns an injected ReadCloser & error (via StderrPipeResponse) 116 // to be able to inject an output stream on Stderr 117 func (fake *FakeCmd) StderrPipe() (io.ReadCloser, error) { 118 return fake.StderrPipeResponse.ReadCloser, fake.StderrPipeResponse.Error 119 } 120 121 // Start mimicks starting the process (in the background) and returns the 122 // injected StartResponse 123 func (fake *FakeCmd) Start() error { 124 return fake.StartResponse 125 } 126 127 // Wait mimicks waiting for the process to exit returns the 128 // injected WaitResponse 129 func (fake *FakeCmd) Wait() error { 130 return fake.WaitResponse 131 } 132 133 // Run sets runs the command 134 func (fake *FakeCmd) Run() error { 135 if fake.RunCalls > len(fake.RunScript)-1 { 136 panic("ran out of Run() actions") 137 } 138 if fake.RunLog == nil { 139 fake.RunLog = [][]string{} 140 } 141 i := fake.RunCalls 142 fake.RunLog = append(fake.RunLog, append([]string{}, fake.Argv...)) 143 fake.RunCalls++ 144 stdout, stderr, err := fake.RunScript[i]() 145 if stdout != nil { 146 fake.Stdout.Write(stdout) 147 } 148 if stderr != nil { 149 fake.Stderr.Write(stderr) 150 } 151 return err 152 } 153 154 // CombinedOutput returns the output from the command 155 func (fake *FakeCmd) CombinedOutput() ([]byte, error) { 156 if fake.CombinedOutputCalls > len(fake.CombinedOutputScript)-1 { 157 panic("ran out of CombinedOutput() actions") 158 } 159 if fake.CombinedOutputLog == nil { 160 fake.CombinedOutputLog = [][]string{} 161 } 162 i := fake.CombinedOutputCalls 163 fake.CombinedOutputLog = append(fake.CombinedOutputLog, append([]string{}, fake.Argv...)) 164 fake.CombinedOutputCalls++ 165 return fake.CombinedOutputScript[i]() 166 } 167 168 // Output is the response from the command 169 func (fake *FakeCmd) Output() ([]byte, error) { 170 return nil, fmt.Errorf("unimplemented") 171 } 172 173 // Stop is to stop the process 174 func (fake *FakeCmd) Stop() { 175 // no-op 176 } 177 178 // FakeExitError is a simple fake ExitError type. 179 type FakeExitError struct { 180 Status int 181 } 182 183 var _ exec.ExitError = FakeExitError{} 184 185 func (fake FakeExitError) String() string { 186 return fmt.Sprintf("exit %d", fake.Status) 187 } 188 189 func (fake FakeExitError) Error() string { 190 return fake.String() 191 } 192 193 // Exited always returns true 194 func (fake FakeExitError) Exited() bool { 195 return true 196 } 197 198 // ExitStatus returns the fake status 199 func (fake FakeExitError) ExitStatus() int { 200 return fake.Status 201 }