terminal: add transcript command (#2814)

Adds a transcript command that appends all command output to a file.
This command is equivalent to gdb's 'set logging'.

As part of this refactor the pkg/terminal commands to always write to a
io.Writer instead of using os.Stdout directly (through
fmt.Printf/fmt.Println).

Fixes #2237
This commit is contained in:
Alessandro Arzilli
2022-01-27 22:18:25 +01:00
committed by GitHub
parent c3eb1cf828
commit 5b925d4f5d
8 changed files with 392 additions and 187 deletions

View File

@ -1,6 +1,7 @@
package terminal
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
@ -52,52 +53,28 @@ type FakeTerminal struct {
const logCommandOutput = false
func (ft *FakeTerminal) Exec(cmdstr string) (outstr string, err error) {
outfh, err := ioutil.TempFile("", "cmdtestout")
if err != nil {
ft.t.Fatalf("could not create temporary file: %v", err)
}
stdout, stderr, termstdout := os.Stdout, os.Stderr, ft.Term.stdout
os.Stdout, os.Stderr, ft.Term.stdout = outfh, outfh, outfh
defer func() {
os.Stdout, os.Stderr, ft.Term.stdout = stdout, stderr, termstdout
outfh.Close()
outbs, err1 := ioutil.ReadFile(outfh.Name())
if err1 != nil {
ft.t.Fatalf("could not read temporary output file: %v", err)
}
outstr = string(outbs)
if logCommandOutput {
ft.t.Logf("command %q -> %q", cmdstr, outstr)
}
os.Remove(outfh.Name())
}()
var buf bytes.Buffer
ft.Term.stdout.w = &buf
ft.Term.starlarkEnv.Redirect(ft.Term.stdout)
err = ft.cmds.Call(cmdstr, ft.Term)
outstr = buf.String()
if logCommandOutput {
ft.t.Logf("command %q -> %q", cmdstr, outstr)
}
ft.Term.stdout.Flush()
return
}
func (ft *FakeTerminal) ExecStarlark(starlarkProgram string) (outstr string, err error) {
outfh, err := ioutil.TempFile("", "cmdtestout")
if err != nil {
ft.t.Fatalf("could not create temporary file: %v", err)
}
stdout, stderr, termstdout := os.Stdout, os.Stderr, ft.Term.stdout
os.Stdout, os.Stderr, ft.Term.stdout = outfh, outfh, outfh
defer func() {
os.Stdout, os.Stderr, ft.Term.stdout = stdout, stderr, termstdout
outfh.Close()
outbs, err1 := ioutil.ReadFile(outfh.Name())
if err1 != nil {
ft.t.Fatalf("could not read temporary output file: %v", err)
}
outstr = string(outbs)
if logCommandOutput {
ft.t.Logf("command %q -> %q", starlarkProgram, outstr)
}
os.Remove(outfh.Name())
}()
var buf bytes.Buffer
ft.Term.stdout.w = &buf
ft.Term.starlarkEnv.Redirect(ft.Term.stdout)
_, err = ft.Term.starlarkEnv.Execute("<stdin>", starlarkProgram, "main", nil)
outstr = buf.String()
if logCommandOutput {
ft.t.Logf("command %q -> %q", starlarkProgram, outstr)
}
ft.Term.stdout.Flush()
return
}
@ -1269,3 +1246,48 @@ func TestBreakpointEditing(t *testing.T) {
}
}
}
func TestTranscript(t *testing.T) {
withTestTerminal("math", t, func(term *FakeTerminal) {
term.MustExec("break main.main")
out := term.MustExec("continue")
if !strings.HasPrefix(out, "> main.main()") {
t.Fatalf("Wrong output for next: <%s>", out)
}
fh, err := ioutil.TempFile("", "test-transcript-*")
if err != nil {
t.Fatalf("TempFile: %v", err)
}
name := fh.Name()
fh.Close()
t.Logf("output to %q", name)
slurp := func() string {
b, err := ioutil.ReadFile(name)
if err != nil {
t.Fatalf("could not read transcript file: %v", err)
}
return string(b)
}
term.MustExec(fmt.Sprintf("transcript %s", name))
out = term.MustExec("list")
//term.MustExec("transcript -off")
if out != slurp() {
t.Logf("output of list %s", out)
t.Logf("contents of transcript: %s", slurp())
t.Errorf("transcript and command out differ")
}
term.MustExec(fmt.Sprintf("transcript -t -x %s", name))
out = term.MustExec(`print "hello"`)
if out != "" {
t.Errorf("output of print is %q but should have been suppressed by transcript", out)
}
if slurp() != "\"hello\"\n" {
t.Errorf("wrong contents of transcript: %q", slurp())
}
os.Remove(name)
})
}