acc: add -tail option to see output.txt in real time (#2467)

## Changes
Add new option -tail to acceptance test runner. This logs output.txt in
real time with relative timestamp prefix.
 
## Why
When working on long running integration tests, it's useful to see what
stage they are at.

## Tests
Manually

```
~/work/cli/acceptance/selftest % testme -v -tail
+ go test .. -run ^TestAccept$/^selftest$ -v -tail
...
=== CONT  TestAccept/selftest/diff
    acceptance_test.go:774:  0.051 >>> diff.py out_dir_a out_dir_b
=== NAME  TestAccept/selftest/basic
    acceptance_test.go:774:  0.051 === Capturing STDERR
    acceptance_test.go:774:  0.051 >>> python3 -c import sys; sys.stderr.write("STDERR\n")
=== NAME  TestAccept/selftest/server
    acceptance_test.go:774:  0.050 >>> curl -s http://127.0.0.1:55850/api/2.0/preview/scim/v2/Me
...
    acceptance_test.go:774:  0.204 >>> /Users/denis.bilenko/work/cli/acceptance/build/darwin_arm64/databricks --version
=== NAME  TestAccept/selftest/server
    acceptance_test.go:774:  0.510 Error: Workspace path not found
...
```
This commit is contained in:
Denis Bilenko 2025-03-11 14:36:58 +01:00 committed by GitHub
parent e6ddb84964
commit b30184f8c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 48 additions and 7 deletions

View File

@ -1,6 +1,7 @@
package acceptance_test
import (
"bufio"
"context"
"encoding/json"
"errors"
@ -36,6 +37,7 @@ var (
KeepTmp bool
NoRepl bool
VerboseTest bool = os.Getenv("VERBOSE_TEST") != ""
Tail bool
)
// In order to debug CLI running under acceptance test, set this to full subtest name, e.g. "bundle/variables/empty"
@ -52,6 +54,7 @@ func init() {
flag.BoolVar(&InprocessMode, "inprocess", SingleTest != "", "Run CLI in the same process as test (for debugging)")
flag.BoolVar(&KeepTmp, "keeptmp", false, "Do not delete TMP directory after run")
flag.BoolVar(&NoRepl, "norepl", false, "Do not apply any replacements (for debugging)")
flag.BoolVar(&Tail, "tail", false, "Log output of script in real time. Use with -v to see the logs: -tail -v")
}
const (
@ -364,14 +367,14 @@ func runTest(t *testing.T, dir, coverDir string, repls testdiff.ReplacementsCont
require.NoError(t, err)
cmd.Env = append(cmd.Env, "TESTDIR="+absDir)
cmd.Env = append(cmd.Env, "CLOUD_ENV="+cloudEnv)
// Write combined output to a file
out, err := os.Create(filepath.Join(tmpDir, "output.txt"))
require.NoError(t, err)
cmd.Stdout = out
cmd.Stderr = out
cmd.Dir = tmpDir
err = cmd.Run()
outputPath := filepath.Join(tmpDir, "output.txt")
out, err := os.Create(outputPath)
require.NoError(t, err)
defer out.Close()
err = runWithLog(t, cmd, out)
// Include exit code in output (if non-zero)
formatOutput(out, err)
@ -721,3 +724,41 @@ func filterHeaders(h http.Header, includedHeaders []string) http.Header {
func isTruePtr(value *bool) bool {
return value != nil && *value
}
func runWithLog(t *testing.T, cmd *exec.Cmd, out *os.File) error {
r, w := io.Pipe()
cmd.Stdout = w
cmd.Stderr = w
start := time.Now()
err := cmd.Start()
require.NoError(t, err)
processErrCh := make(chan error, 1)
go func() {
processErrCh <- cmd.Wait()
w.Close()
}()
reader := bufio.NewReader(r)
for {
line, err := reader.ReadString('\n')
if Tail {
msg := strings.TrimRight(line, "\n")
if len(msg) > 0 {
d := time.Since(start)
t.Logf("%2d.%03d %s", d/time.Second, (d%time.Second)/time.Millisecond, msg)
}
}
if len(line) > 0 {
_, err = out.WriteString(line)
require.NoError(t, err)
}
if err == io.EOF {
break
}
require.NoError(t, err)
}
return <-processErrCh
}