I’m stuck on Stage #GM9. I use Go for the challenge.
I just refactored my code to use the terminal raw mode to capture every keystroke on stdin. Upon ‘\r’ or ‘\n’ or on receiving the error io.EOF
, I consider the input provided by the user as “entered” and restore the normal terminal mode. When I submit my program to move on to the next stage, the tests fail with the behavior I cannot reproduce when I run the same commands locally. Compare:
Local interactive mode
❯ ./your_program.sh
$ echo "world script"world script
world scriptworld script
$
Remote test output on submission
[your-program] $ echo "world script"world script
[tester::#TG6] Output does not match expected value.
[tester::#TG6] Expected: "$ echo "world script""
[tester::#TG6] Received: "$ echo "world script"world script"
Another example of test output:
[tester::#GP4] Running tests for Stage #GP4 (Navigation - The cd builtin: Home directory)
[tester::#GP4] Running ./your_program.sh
[your-program] $ cd /tmp/raspberry/orange/pear
[your-program] $ pwd
[your-program] /tmp/raspberry/orange/pear
[tester::#GP4] Received current working directory response
[your-program] $ cd ~
[your-program] $ pwd
[tester::#GP4] Output does not match expected value.
[tester::#GP4] Expected: "$ pwd"
[tester::#GP4] Received: "$ pwd/tmp/strawberry/mango/mango"
[your-program] $
[tester::#GP4] Assertion failed.
[tester::#GP4] Test failed
It appears as if the normal terminal mode is not restored.
The discrepancy in the behavior makes me think the test input is injected into the tested shell program. My question is: how? Is test input passed to my shell program by redirecting/piping it?
My input reading function:
func readInput(inputCh chan string) {
var err error
var oldState *term.State
success := false
oldState, err = term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
panic(err)
}
buf := make([]byte, 1)
input := []byte{}
defer func() {
input = append(input, '\n')
if success {
inputCh <- string(input)
}
fmt.Printf("\r\n")
err := term.Restore(int(os.Stdin.Fd()), oldState)
if err != nil {
panic(err)
}
close(inputCh)
}()
for {
_, err = os.Stdin.Read(buf)
if err != nil {
if errors.Is(err, io.EOF) {
success = true
return
} else {
panic(err)
}
}
b := buf[0]
switch b {
case sigint:
fmt.Printf("^C")
return
case cariageReturn, newLine:
success = true
return
case tab:
success = true
if cmpl, ok := autocomplete(input); ok {
clearPrompt()
input = cmpl
input = append(input, ' ')
for i := range input {
fmt.Printf("%c", input[i])
}
}
case del:
if len(input) > 0 {
fmt.Print("\x1b[D \x1b[D")
input = input[:len(input)-1]
}
continue
default:
fmt.Printf("%c", b)
input = append(input, b)
}
}
}
Source code: go-shell/cmd/myshell/main.go at master · Kristina-Pianykh/go-shell · GitHub