miller/pkg/terminals/regtest/entry.go
John Kerl 2f3e54d1f6
Remove os.Exit callsites below the entrypoint: phase 4 (plans/exit.md) (#2205)
Phase 4 of plans/exit.md: the aux/terminal sub-entrypoints. pkg/auxents and
pkg/terminals are now os.Exit-free; even the dispatchers return exit codes
instead of exiting.

- auxents.Dispatch returns (handled bool, exitCode int); entrypoint.Main --
  the one sanctioned exit point -- exits with the code.
- terminals.Dispatch returns an exit code; the climain caller wraps it in a
  lib.ExitRequest, which also retires the 'terminal did not exit the process'
  panic.
- Usage functions (hex, unhex, lecat, termcvt, repl, script, regtest) no
  longer take an exitCode parameter and exit; call sites print usage and
  return the code explicitly.
- Inner I/O helpers (hexDumpFile, unhexFile, lecatFile, termcvtFile) return
  errors; each sub-main prints 'mlr {verb}: ...' as before and returns 1.
  lecatFile formerly looped forever on a non-EOF read error; it now returns
  the error.
- The regtest directory walk (Execute / executeSinglePath /
  executeSingleDirectory / hasCaseSubdirectories) plumbs errors up to
  RegTestMain; regression_test.go updated for the new Execute signature.

Stderr messages and exit codes are unchanged across mlr aux-list, hex, unhex,
lecat, termcvt, help, version, repl, script, and regtest surfaces (checked by
hand); all 4779 regression cases pass; make lint is clean; docs rebuild with
zero churn.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 15:27:26 -04:00

122 lines
3.1 KiB
Go

// TODO
package regtest
import (
"fmt"
"os"
"strconv"
"strings"
)
const defaultPath = "./test/cases"
func regTestUsage(verbName string, o *os.File) {
fmt.Fprintf(o, "Usage: mlr %s [options] [one or more directories/files]\n", verbName)
fmt.Fprintf(o, "If no directories/files are specified, the directory %s is used by default.\n", defaultPath)
fmt.Fprintf(o, "Recursively walks the directory/ies looking for foo.cmd files having Miller command-lines,\n")
fmt.Fprintf(o, "with foo.expout and foo.experr files having expected stdout and stderr, respectively.\n")
fmt.Fprintf(o, "If foo.should-fail exists and is a file, the command is expected to exit non-zero back to\n")
fmt.Fprintf(o, "the shell.\n")
fmt.Fprintf(o, "\n")
fmt.Fprintf(o, "Options:\n")
fmt.Fprintf(o, "[none] Print directory-level pass/fails, and overall pass/fail.\n")
fmt.Fprintf(o, "-m {...} Specify name of Miller executable to use.\n")
fmt.Fprintf(o, "-c Shorthand for -m ../c/mlr.\n")
fmt.Fprintf(o, "-p Create the .expout and .experr files, rather than checking them.\n")
fmt.Fprintf(o, "-v Also include pass/fail at command-file level.\n")
fmt.Fprintf(o, "-vv Also include pass/fail reasons for each command-file.\n")
fmt.Fprintf(o, "-vvv Also include full stdout/stderr/exit-code for each command-file.\n")
fmt.Fprintf(o, "-j Just show the Miller command-line, put/filter script if any, and output.\n")
fmt.Fprintf(o, "-s {n} After running tests, re-run first n failed .cmd files with verbosity level 3.\n")
fmt.Fprintf(o, "-S After running tests, re-run all failed .cmd files with verbosity level 3.\n")
}
// Here the args are the full Miller command line: "mlr regtest --foo bar".
func RegTestMain(args []string) int {
exeName := os.Args[0]
verbName := args[0]
argc := len(args)
argi := 1
verbosityLevel := 0
doPopulate := false
plainMode := false
firstNFailsToShow := 0
for argi < argc /* variable increment: 1 or 2 depending on flag */ {
arg := args[argi]
if !strings.HasPrefix(arg, "-") {
break // No more flag options to process
}
argi++
switch arg {
case "-h", "--help":
regTestUsage(verbName, os.Stdout)
return 0
case "-m":
if argi >= argc {
regTestUsage(verbName, os.Stderr)
return 1
}
exeName = args[argi]
argi++
case "-s":
if argi >= argc {
regTestUsage(verbName, os.Stderr)
return 1
}
temp, err := strconv.Atoi(args[argi])
if err != nil {
regTestUsage(verbName, os.Stderr)
return 1
}
firstNFailsToShow = temp
argi++
case "-S":
firstNFailsToShow = 1000000000
case "-p":
doPopulate = true
case "-v":
verbosityLevel++
case "-j":
plainMode = true
default:
regTestUsage(verbName, os.Stderr)
return 1
}
}
casePaths := args[argi:]
if len(casePaths) == 0 {
casePaths = []string{defaultPath}
}
regtester := NewRegTester(
exeName,
doPopulate,
verbosityLevel,
plainMode,
firstNFailsToShow,
)
ok, err := regtester.Execute(casePaths)
if err != nil {
fmt.Fprintf(os.Stderr, "mlr %s: %v\n", verbName, err)
return 1
}
if !ok {
return 1
}
return 0
}