Am trying to use fzf in a Go code. I referred to the example given here by the author. When I tried to write a test for the function its getting stuck as fzf
requires interactive input.
Code:
func withFilter(command string, input func(in io.WriteCloser)) []string {
shell := os.Getenv("SHELL")
if len(shell) == 0 {
shell = "sh"
}
cmd := exec.Command(shell, "-c", command)
cmd.Stderr = os.Stderr
in, _ := cmd.StdinPipe()
go func() {
input(in)
in.Close()
}()
result, _ := cmd.Output()
return strings.Split(string(result), "
")
}
func filter() []string {
filtered := withFilter("fzf -m", func(in io.WriteCloser) {
for i := 0; i < 10; i++ {
fmt.Fprintln(in, i)
time.Sleep(5 * time.Millisecond)
}
})
return filtered
}
Test:
func TestFilter(t *testing.T) {
assert.Equal(t, []string{"1", "2", "3"}, filter())
}
I tried debugging and noticed that it gets stuck at cmd.Output()
. By digging a bit deeper it looks like the command is waiting indefinitely for input, however am not sure how to provide it programatically. I tried writing to
os.Stdin
but it didn't work.
Anyone pointers or explanation would be appreciated. Thanks.
The beauty of go: Its io.Reader
/io.Writer
interfaces.
You have to provide from where your withFilter
(and by extension filter
) method should read. So, in
has to be provided it.
// example
func readFrom(reader io.Reader) {
r := bufio.NewScanner(reader)
for r.Scan() {
fmt.Println("got", r.Text())
}
fmt.Println("Done.")
}
func main() {
const input = "Now is the winter of our discontent,
Made glorious summer by this sun of York.
"
readFrom(strings.NewReader(input))
// okay, with stdin
readFrom(os.Stdin)
}
As you can see, the first readFrom
is fully testable, you just change the input
variable to whatever you want to test. The second readFrom
will only return when the stdin
is closed.
The solution is not to write to os.Stdin
. Consider os.Stdin
an implementation detail in your OS—not something you have to include in your testing.
https://play.golang.org/p/D2wzHYrV2TM (os.Stdin is closed in the playground)