complete: enable complete.Complete() output capturing (#138)
* complete: test capturing Complete() output Implement an Example test to demonstrate capturing the output of Complete(), which is crucial for integration tests. * complete: do not hard-code the I/O streams at the package initialization Instead of defining the input/output streams as unexported global vars that only get their values assigned to once, at the very initialization of the package, use the values that `os.Stdin` and `os.Stdout` have at the particular moment on every `complete.Complete()` call. Fix #137 * complete: capture and discard output in TestComplete Restore earlier behavior using proper stream redirection this time. * complete: output capturing example: define things in the package scope Define the `stringLookup` func type and `promptEnv` func in the package scope instead of the `ExampleComplete_outputCapturing` test. * complete: rename the `stringLookup` func type to `getEnvFn`
This commit is contained in:
parent
002575c9d5
commit
4a17a04514
|
@ -51,8 +51,6 @@ func (p PredictFunc) Predict(prefix string) []string {
|
||||||
var (
|
var (
|
||||||
getEnv = os.Getenv
|
getEnv = os.Getenv
|
||||||
exit = os.Exit
|
exit = os.Exit
|
||||||
out io.Writer = os.Stdout
|
|
||||||
in io.Reader = os.Stdin
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Complete the command line arguments for the given command in the case that the program
|
// Complete the command line arguments for the given command in the case that the program
|
||||||
|
@ -66,6 +64,10 @@ func Complete(name string, cmd Completer) {
|
||||||
doUninstall = getEnv("COMP_UNINSTALL") == "1"
|
doUninstall = getEnv("COMP_UNINSTALL") == "1"
|
||||||
yes = getEnv("COMP_YES") == "1"
|
yes = getEnv("COMP_YES") == "1"
|
||||||
)
|
)
|
||||||
|
var (
|
||||||
|
out io.Writer = os.Stdout
|
||||||
|
in io.Reader = os.Stdin
|
||||||
|
)
|
||||||
if doInstall || doUninstall {
|
if doInstall || doUninstall {
|
||||||
install.Run(name, doUninstall, yes, out, in)
|
install.Run(name, doUninstall, yes, out, in)
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package complete
|
package complete
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/posener/complete/v2/internal/arg"
|
"github.com/posener/complete/v2/internal/arg"
|
||||||
|
@ -159,9 +161,17 @@ func TestComplete(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
getEnv = os.Getenv
|
getEnv = os.Getenv
|
||||||
exit = os.Exit
|
exit = os.Exit
|
||||||
out = os.Stdout
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
in, out, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func(o *os.File) { os.Stdout = o }(os.Stdout)
|
||||||
|
defer out.Close()
|
||||||
|
os.Stdout = out
|
||||||
|
go io.Copy(ioutil.Discard, in)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
line, point string
|
line, point string
|
||||||
shouldExit bool
|
shouldExit bool
|
||||||
|
@ -203,7 +213,6 @@ func TestComplete(t *testing.T) {
|
||||||
exit = func(int) {
|
exit = func(int) {
|
||||||
isExit = true
|
isExit = true
|
||||||
}
|
}
|
||||||
out = ioutil.Discard
|
|
||||||
if tt.shouldPanic {
|
if tt.shouldPanic {
|
||||||
assert.Panics(t, func() { testCmd.Complete("") })
|
assert.Panics(t, func() { testCmd.Complete("") })
|
||||||
} else {
|
} else {
|
||||||
|
@ -214,6 +223,24 @@ func TestComplete(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExampleComplete_outputCapturing demonstrates the ability to capture
|
||||||
|
// the output of Complete() invocations, crucial for integration tests.
|
||||||
|
func ExampleComplete_outputCapturing() {
|
||||||
|
defer func(f func(int)) { exit = f }(exit)
|
||||||
|
defer func(f getEnvFn) { getEnv = f }(getEnv)
|
||||||
|
exit = func(int) {}
|
||||||
|
|
||||||
|
// This is where the actual example starts:
|
||||||
|
|
||||||
|
cmd := &Command{Sub: map[string]*Command{"bar": {}}}
|
||||||
|
getEnv = promptEnv("foo b")
|
||||||
|
|
||||||
|
Complete("foo", cmd)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// bar
|
||||||
|
}
|
||||||
|
|
||||||
type set []string
|
type set []string
|
||||||
|
|
||||||
func (s set) Predict(_ string) []string {
|
func (s set) Predict(_ string) []string {
|
||||||
|
@ -245,3 +272,20 @@ func TestHasPrefix(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getEnvFn emulates os.GetEnv by mapping one string to another.
|
||||||
|
type getEnvFn = func(string) string
|
||||||
|
|
||||||
|
// promptEnv returns getEnvFn that emulates the environment variables
|
||||||
|
// a shell would set when its prompt has the given contents.
|
||||||
|
var promptEnv = func(contents string) getEnvFn {
|
||||||
|
return func(key string) string {
|
||||||
|
switch key {
|
||||||
|
case "COMP_LINE":
|
||||||
|
return contents
|
||||||
|
case "COMP_POINT":
|
||||||
|
return strconv.Itoa(len(contents))
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue