Compare commits

...

1 Commits

Author SHA1 Message Date
Jesse Duffield
2f555ddb74 Utilise file instead of stderr for integration tests
Windows for some reason is not properly making use of stderr so instead
we're going to write our result to a file explicitly
2023-05-26 18:22:03 +10:00
4 changed files with 148 additions and 5 deletions

View File

@@ -1,12 +1,15 @@
package gui
import (
"fmt"
"log"
"os"
"runtime"
"time"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/result"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -24,7 +27,15 @@ func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) {
go func() {
time.Sleep(time.Millisecond * 100)
test.Run(&GuiDriver{gui: gui})
defer handlePanic()
guiDriver := &GuiDriver{gui: gui}
test.Run(guiDriver)
// if we're here then the test must have passed: it panics upon failure
if err := result.LogSuccess(); err != nil {
panic(err)
}
gui.g.Update(func(*gocui.Gui) error {
return gocui.ErrQuit
@@ -42,6 +53,21 @@ func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) {
}
}
func handlePanic() {
if r := recover(); r != nil {
buf := make([]byte, 4096*4) // arbitrarily large buffer size
stackSize := runtime.Stack(buf, false)
stackTrace := string(buf[:stackSize])
if err := result.LogFailure(fmt.Sprintf("%v\n%s", r, stackTrace)); err != nil {
panic(err)
}
// Re-panic
panic(r)
}
}
func Headless() bool {
return os.Getenv("HEADLESS") != ""
}

View File

@@ -9,6 +9,7 @@ package clients
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
@@ -17,6 +18,7 @@ import (
"github.com/creack/pty"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/result"
"github.com/jesseduffield/lazygit/pkg/integration/tests"
"github.com/stretchr/testify/assert"
)
@@ -63,6 +65,9 @@ func runCmdHeadless(cmd *exec.Cmd) error {
"TERM=xterm",
)
resultPath := result.GetResultPath()
result.SetResultPathEnvVar(cmd, resultPath)
// not writing stderr to the pty because we want to capture a panic if
// there is one. But some commands will not be in tty mode if stderr is
// not a terminal. We'll need to keep an eye out for that.
@@ -79,10 +84,20 @@ func runCmdHeadless(cmd *exec.Cmd) error {
_, _ = io.Copy(ioutil.Discard, f)
if cmd.Wait() != nil {
// return an error with the stderr output
return errors.New(stderr.String())
_ = cmd.Wait()
result, err := result.ReadResult(resultPath)
if err != nil {
return fmt.Errorf("Error reading integration test result: %w", err)
}
if !result.Success {
return errors.New(result.Message)
}
// if cmd.Wait() != nil {
// // return an error with the stderr output
// return errors.New(stderr.String())
// }
return f.Close()
}

View File

@@ -0,0 +1,102 @@
package result
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
)
// On windows the pty package is failing to obtain stderr text when a test fails,
// so this is our workaround: when a test fails, it writes the result to a file,
// and then the test runner reads the file and checks the result
const PathEnvVar = "LAZYGIT_INTEGRATION_TEST_RESULT_PATH"
type IntegrationTestResult struct {
Success bool
Message string
}
func LogFailure(message string) error {
return writeResult(failure(message))
}
func LogSuccess() error {
return writeResult(success())
}
func failure(message string) IntegrationTestResult {
return IntegrationTestResult{Success: false, Message: message}
}
func success() IntegrationTestResult {
return IntegrationTestResult{Success: true}
}
func writeResult(result IntegrationTestResult) error {
resultPath := os.Getenv(PathEnvVar)
if resultPath == "" {
return fmt.Errorf("Environment variable %s not set", PathEnvVar)
}
file, err := os.Create(resultPath)
if err != nil {
return fmt.Errorf("Error creating file: %w", err)
}
defer file.Close()
encoder := json.NewEncoder(file)
err = encoder.Encode(result)
if err != nil {
return fmt.Errorf("Error encoding JSON: %w", err)
}
return nil
}
// Reads the result file stored by the lazygit test, and deletes the file
func ReadResult(resultPath string) (IntegrationTestResult, error) {
file, err := os.Open(resultPath)
if err != nil {
return IntegrationTestResult{}, fmt.Errorf("Error creating file: %w", err)
}
decoder := json.NewDecoder(file)
var result IntegrationTestResult
err = decoder.Decode(&result)
if err != nil {
file.Close()
return IntegrationTestResult{}, fmt.Errorf("Error decoding JSON: %w", err)
}
file.Close()
_ = os.Remove(resultPath)
return result, nil
}
func SetResultPathEnvVar(cmd *exec.Cmd, resultPath string) {
cmd.Env = append(
cmd.Env,
fmt.Sprintf("%s=%s", PathEnvVar, resultPath),
)
}
func GetResultPath() string {
return filepath.Join(os.TempDir(), fmt.Sprintf("lazygit_result_%s.json", generateRandomString(10)))
}
func generateRandomString(length int) string {
buffer := make([]byte, length)
_, err := rand.Read(buffer)
if err != nil {
panic(fmt.Sprintf("Could not generate random string: %s", err))
}
randomString := base64.URLEncoding.EncodeToString(buffer)
return randomString[:length]
}

View File

@@ -27,7 +27,7 @@ var Commit = NewIntegrationTest(NewIntegrationTestArgs{
PressPrimaryAction(). // stage file
Lines(
Contains("A myfile").IsSelected(),
Contains("?? myfile2"),
Contains("?? myfile23"),
).
SelectNextItem().
PressPrimaryAction(). // stage other file