diff --git a/gocomplete/complete.go b/gocomplete/complete.go index 753a38e..4068bb5 100644 --- a/gocomplete/complete.go +++ b/gocomplete/complete.go @@ -2,10 +2,17 @@ package main import ( + "io/ioutil" + "log" + "os" + "github.com/posener/complete/v2" "github.com/posener/complete/v2/predict" ) +// envVerbose is the sys env var that controls error output verbosity. +const envVerbose = "GOCOMP_VERBOSE" + var ( ellipsis = predict.Set{"./..."} anyPackage = complete.PredictFunc(predictPackages) @@ -15,6 +22,10 @@ var ( ) func main() { + if os.Getenv(envVerbose) != "1" { + log.SetOutput(ioutil.Discard) + } + build := &complete.Command{ Flags: map[string]complete.Predictor{ "o": anyFile, diff --git a/gocomplete/tests_test.go b/gocomplete/tests_test.go index a1238a4..dbe72cf 100644 --- a/gocomplete/tests_test.go +++ b/gocomplete/tests_test.go @@ -1,8 +1,14 @@ package main import ( + "fmt" + "io" + "io/ioutil" + "log" "os" "sort" + "strconv" + "strings" "testing" "bou.ke/monkey" @@ -21,7 +27,11 @@ func TestPredictions(t *testing.T) { { name: "predict tests ok", predictor: predictTest, - want: []string{"TestPredictions", "Example"}, + want: []string{ + "TestPredictions", + "Example", + "TestErrorSupression", + }, }, { name: "predict benchmark ok", @@ -64,3 +74,71 @@ func equal(s1, s2 []string) bool { } return true } + +func TestErrorSupression(t *testing.T) { + defer monkey.Patch(os.Exit, func(int) {}).Unpatch() + + // Completion API environment variable names. + const envLine, envPoint = "COMP_LINE", "COMP_POINT" + + // line should work out to + // + // * on most POSIX: + // go test /tmp/ + // * on MacOS X: + // go test /var/folders//T// + // * on Windows: + // go test C:\Users\\AppData\Local\Temp\ + // + // which should trigger "failed importing directory: ... no + // buildable Go source files..." error messages. + var line = "go test " + os.TempDir() + string(os.PathSeparator) + + defer os.Unsetenv(envLine) + defer os.Unsetenv(envPoint) + os.Setenv(envLine, line) + os.Setenv(envPoint, strconv.Itoa(len(line))) + + tests := []struct { + verbose string + wantErr bool + }{{ + verbose: "", + wantErr: false, + }, { + verbose: "1", + wantErr: true, + }} + for _, tt := range tests { + t.Run(fmt.Sprintf( + "%s=%q", envVerbose, tt.verbose, + ), func(t *testing.T) { + // Discard completion (stdout). + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + defer w.Close() + defer func(o *os.File) { os.Stdout = o }(os.Stdout) + os.Stdout = w + go io.Copy(ioutil.Discard, r) + + // "Redirect" stderr into a buffer. + b := &strings.Builder{} + log.SetOutput(b) + + defer os.Unsetenv(envVerbose) + os.Setenv(envVerbose, tt.verbose) + + main() + + gotErr := b.Len() != 0 + if tt.wantErr && !gotErr { + t.Fatal("want something in stderr, got nothing") + } else if !tt.wantErr && gotErr { + t.Fatalf("want nothing in stderr, got %d bytes", + b.Len()) + } + }) + } +}