Use bufio.Scanner in BasicUi.Ask so we can read whole lines
The previous version used fmt.Fscanln which sounds like it should scan for a whole line, but it actually errors if there is more than a single word/token given. You can see this here https://play.golang.org/p/1RYXdtPSbk And looking at the only usage of this function in core it won't break anything to change it to actually return whole lines instead. line, err := ui.Ask("[c] Clean up and exit, [a] abort without cleanup, or [r] retry step (build may fail even if retry succeeds)?") Closes #4474
This commit is contained in:
parent
60a0d21d37
commit
242ed49ecb
14
packer/ui.go
14
packer/ui.go
|
@ -1,6 +1,7 @@
|
|||
package packer
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -64,6 +65,7 @@ type BasicUi struct {
|
|||
ErrorWriter io.Writer
|
||||
l sync.Mutex
|
||||
interrupted bool
|
||||
scanner *bufio.Scanner
|
||||
}
|
||||
|
||||
// MachineReadableUi is a UI that only outputs machine-readable output
|
||||
|
@ -174,6 +176,9 @@ func (rw *BasicUi) Ask(query string) (string, error) {
|
|||
return "", errors.New("interrupted")
|
||||
}
|
||||
|
||||
if rw.scanner == nil {
|
||||
rw.scanner = bufio.NewScanner(rw.Reader)
|
||||
}
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt)
|
||||
defer signal.Stop(sigCh)
|
||||
|
@ -188,10 +193,13 @@ func (rw *BasicUi) Ask(query string) (string, error) {
|
|||
result := make(chan string, 1)
|
||||
go func() {
|
||||
var line string
|
||||
if _, err := fmt.Fscanln(rw.Reader, &line); err != nil {
|
||||
log.Printf("ui: scan err: %s", err)
|
||||
if rw.scanner.Scan() {
|
||||
line = rw.scanner.Text()
|
||||
}
|
||||
if err := rw.scanner.Err(); err != nil {
|
||||
log.Printf("ui: scan err: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
result <- line
|
||||
}()
|
||||
|
||||
|
|
|
@ -16,6 +16,12 @@ func readWriter(ui *BasicUi) (result string) {
|
|||
return
|
||||
}
|
||||
|
||||
// Reset the input Reader than add some input to it.
|
||||
func writeReader(ui *BasicUi, input string) {
|
||||
buffer := ui.Reader.(*bytes.Buffer)
|
||||
buffer.WriteString(input)
|
||||
}
|
||||
|
||||
func readErrorWriter(ui *BasicUi) (result string) {
|
||||
buffer := ui.ErrorWriter.(*bytes.Buffer)
|
||||
result = buffer.String()
|
||||
|
@ -192,6 +198,45 @@ func TestBasicUi_Say(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBasicUi_Ask(t *testing.T) {
|
||||
|
||||
var actual, expected string
|
||||
var err error
|
||||
|
||||
var testCases = []struct {
|
||||
Prompt, Input, Answer string
|
||||
}{
|
||||
{"[c]ontinue or [a]bort", "c\n", "c"},
|
||||
{"[c]ontinue or [a]bort", "c", "c"},
|
||||
// Empty input shouldn't give an error
|
||||
{"Name", "Joe Bloggs\n", "Joe Bloggs"},
|
||||
{"Name", "Joe Bloggs", "Joe Bloggs"},
|
||||
{"Name", "\n", ""},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
// Because of the internal bufio we can't eaily reset the input, so create a new one each time
|
||||
bufferUi := testUi()
|
||||
writeReader(bufferUi, testCase.Input)
|
||||
|
||||
actual, err = bufferUi.Ask(testCase.Prompt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if actual != testCase.Answer {
|
||||
t.Fatalf("bad answer: %#v", actual)
|
||||
}
|
||||
|
||||
actual = readWriter(bufferUi)
|
||||
expected = testCase.Prompt + " "
|
||||
if actual != expected {
|
||||
t.Fatalf("bad prompt: %#v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestMachineReadableUi_ImplUi(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = &MachineReadableUi{}
|
||||
|
|
Loading…
Reference in New Issue