feat: show actual vs expected on fail
This commit is contained in:
+29
-1
@@ -2,6 +2,7 @@ package mire
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@@ -38,6 +39,15 @@ type testFixtureFiles struct {
|
|||||||
outPath string
|
outPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testMismatchError struct {
|
||||||
|
expected []byte
|
||||||
|
actual []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *testMismatchError) Error() string {
|
||||||
|
return "output differed"
|
||||||
|
}
|
||||||
|
|
||||||
// RunTests replays all scenarios under the configured test directory.
|
// RunTests replays all scenarios under the configured test directory.
|
||||||
func RunTests(path string) error {
|
func RunTests(path string) error {
|
||||||
return runTests(path, testIO{
|
return runTests(path, testIO{
|
||||||
@@ -89,6 +99,10 @@ func runTests(path string, tio testIO) error {
|
|||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
summary.failed++
|
summary.failed++
|
||||||
output.Fprintf(tio.out, "%s %s (%s): %v\n", output.Label("FAIL", output.Fail), scenario.relPath, formatElapsed(elapsed), err)
|
output.Fprintf(tio.out, "%s %s (%s): %v\n", output.Label("FAIL", output.Fail), scenario.relPath, formatElapsed(elapsed), err)
|
||||||
|
var mismatchErr *testMismatchError
|
||||||
|
if errors.As(err, &mismatchErr) {
|
||||||
|
writeScenarioMismatch(tio.out, mismatchErr)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,12 +270,26 @@ func replayScenario(scenario testScenario, shellPath string, _ testIO, sandboxCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(got, want) {
|
if !bytes.Equal(got, want) {
|
||||||
return fmt.Errorf("output differed")
|
return &testMismatchError{
|
||||||
|
expected: want,
|
||||||
|
actual: got,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeScenarioMismatch(w io.Writer, mismatch *testMismatchError) {
|
||||||
|
output.Fprintf(w, "Expected:\n%s", string(mismatch.expected))
|
||||||
|
if len(mismatch.expected) == 0 || mismatch.expected[len(mismatch.expected)-1] != '\n' {
|
||||||
|
output.Fprintf(w, "\n")
|
||||||
|
}
|
||||||
|
output.Fprintf(w, "Actual:\n%s", string(mismatch.actual))
|
||||||
|
if len(mismatch.actual) == 0 || mismatch.actual[len(mismatch.actual)-1] != '\n' {
|
||||||
|
output.Fprintf(w, "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func trimReplayOutputToMarker(data []byte, shellPath string) ([]byte, error) {
|
func trimReplayOutputToMarker(data []byte, shellPath string) ([]byte, error) {
|
||||||
idx := bytes.Index(data, []byte(compareOutputMarker))
|
idx := bytes.Index(data, []byte(compareOutputMarker))
|
||||||
if idx == -1 {
|
if idx == -1 {
|
||||||
|
|||||||
@@ -157,6 +157,45 @@ func TestReplayScenarioFailsWhenCompareMarkerMissing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunTestsShowsExpectedAndActualForMismatchedScenario(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
testDir := filepath.Join(root, "e2e")
|
||||||
|
testutil.AddFakeRecordDependencies(t, "script")
|
||||||
|
t.Setenv("FAKE_SCRIPT_ECHO_STDIN", "1")
|
||||||
|
|
||||||
|
testutil.WriteFile(t, filepath.Join(root, "mire.toml"), testutil.ValidConfigContent("e2e"))
|
||||||
|
mustWriteRecordShell(t, testDir)
|
||||||
|
testutil.WriteScenarioFixtures(t, filepath.Join(testDir, "suite", "spec"), "echo two\n", "echo one\n")
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
err := testutil.WithWorkingDir(t, root, func() error {
|
||||||
|
return runTests("", testIO{
|
||||||
|
out: &stdout,
|
||||||
|
err: &stderr,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("runTests() error = %v", err)
|
||||||
|
}
|
||||||
|
if stderr.String() != "" {
|
||||||
|
t.Fatalf("stderr = %q, want empty", stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, want := range []string{
|
||||||
|
"RUN suite/spec",
|
||||||
|
"FAIL suite/spec (",
|
||||||
|
"output differed",
|
||||||
|
"Expected:\necho one\n",
|
||||||
|
"Actual:\necho two\n",
|
||||||
|
"Summary: total=1 passed=0 failed=1",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(stdout.String(), want) {
|
||||||
|
t.Fatalf("stdout = %q, want substring %q", stdout.String(), want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDiscoverTestScenariosUsesDisplayRootForRelativePaths(t *testing.T) {
|
func TestDiscoverTestScenariosUsesDisplayRootForRelativePaths(t *testing.T) {
|
||||||
testDir := filepath.Join(t.TempDir(), "e2e")
|
testDir := filepath.Join(t.TempDir(), "e2e")
|
||||||
scopedDir := filepath.Join(testDir, "nested")
|
scopedDir := filepath.Join(testDir, "nested")
|
||||||
|
|||||||
Reference in New Issue
Block a user