package main import ( "fmt" "regexp" "strings" ) var exitCodeMarkerRe = regexp.MustCompile(`^\[(\d+|\*)\]$`) func rewriteFile(file ShoutFile, results []CommandResult, originalContent string) string { lines := strings.Split(originalContent, "\n") var output []string cmdIdx := 0 for i := 0; i < len(lines); i++ { line := lines[i] if strings.HasPrefix(line, "$ ") { output = append(output, line) if cmdIdx >= len(file.Commands) || cmdIdx >= len(results) { // Out of bounds — skip past expected output, preserving original lines j := i + 1 for j < len(lines) && !strings.HasPrefix(lines[j], "$ ") { j++ } output = append(output, lines[i+1:j]...) i = j - 1 cmdIdx++ continue } cmd := file.Commands[cmdIdx] result := results[cmdIdx] // Skip past old expected output lines j := i + 1 for j < len(lines) && !strings.HasPrefix(lines[j], "$ ") { j++ } oldExpectedRaw := lines[i+1 : j] // Count trailing blank lines trailingBlanks := 0 for k := len(oldExpectedRaw) - 1; k >= 0; k-- { if oldExpectedRaw[k] == "" { trailingBlanks++ } else { break } } outputMatch := matchOutput(cmd.Expected, result.Actual) ecMatch := exitCodeOK(cmd.ExitCodeType, cmd.ExitCodeValue, result.ExitCode) if outputMatch && ecMatch { // Both match — preserve original verbatim output = append(output, oldExpectedRaw...) } else if outputMatch { // Output matches but exit code changed — preserve output, fix exit code raw := oldExpectedRaw rawTrailing := 0 for k := len(raw) - 1; k >= 0; k-- { if raw[k] == "" { rawTrailing++ } else { break } } content := raw[:len(raw)-rawTrailing] // Strip old exit code marker if present if len(content) > 0 && exitCodeMarkerRe.MatchString(content[len(content)-1]) { content = content[:len(content)-1] } output = append(output, content...) marker := updateExitCodeMarker(cmd, result.ExitCode) if marker != "" { output = append(output, marker) } else if len(content) > 0 && exitCodeMarkerRe.MatchString(content[len(content)-1]) { output = append(output, "[0]") } for k := 0; k < rawTrailing; k++ { output = append(output, "") } } else { // Output doesn't match — rewrite everything output = append(output, result.Actual...) marker := updateExitCodeMarker(cmd, result.ExitCode) if marker != "" { output = append(output, marker) } else if len(result.Actual) > 0 && exitCodeMarkerRe.MatchString(result.Actual[len(result.Actual)-1]) { output = append(output, "[0]") } for k := 0; k < trailingBlanks; k++ { output = append(output, "") } } i = j - 1 cmdIdx++ } else { output = append(output, line) } } return strings.Join(output, "\n") } // updateExitCodeMarker returns the exit code marker to write, preserving [*] // wildcards when the command still exits non-zero. func updateExitCodeMarker(cmd Command, actualExitCode int) string { if cmd.ExitCodeType == ExitCodeWildcard && actualExitCode != 0 { return "[*]" } if actualExitCode != 0 { return fmt.Sprintf("[%d]", actualExitCode) } if cmd.ExitCodeType == ExitCodeWildcard || cmd.ExitCodeType == ExitCodeExact { return "[0]" } return "" }