fix(cmd): usage exit codes, asm output file and cross-arch ground truth
Assisted-by: GLM 5.3
This commit is contained in:
@@ -13,6 +13,8 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-devkit/arch"
|
||||
)
|
||||
|
||||
const clean = "#include \"textflag.h\"\n" +
|
||||
@@ -242,6 +244,96 @@ func TestCmdArgErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestUsageExitCodes pins the exit-code contract for the commands whose main
|
||||
// dispatches on a returned error: a wrong argument set exits 2, the same as
|
||||
// the commands that count their arguments themselves, while a runtime
|
||||
// failure (an unreadable file) keeps exit 1.
|
||||
func TestUsageExitCodes(t *testing.T) {
|
||||
for name, err := range map[string]error{
|
||||
"audit-instructions extra argument": cmdAuditInstructions([]string{"amd64", "extra"}),
|
||||
"audit-instructions unknown arch": cmdAuditInstructions([]string{"mips"}),
|
||||
"audit-instructions corpus extra": cmdAuditInstructions([]string{"--corpus", "a", "b"}),
|
||||
"scaffold no arguments": cmdScaffold(nil),
|
||||
"scaffold extra arguments": cmdScaffold([]string{"differential", "a.s", "b.s"}),
|
||||
} {
|
||||
if err == nil {
|
||||
t.Errorf("%s: expected an error", name)
|
||||
continue
|
||||
}
|
||||
if code := exitCodeFor(err); code != 2 {
|
||||
t.Errorf("%s: exit code = %d, want 2 (err: %v)", name, code, err)
|
||||
}
|
||||
}
|
||||
if err := cmdScaffold([]string{"differential", "/nonexistent/file.s"}); err == nil {
|
||||
t.Error("scaffold on a missing file should fail")
|
||||
} else if code := exitCodeFor(err); code != 1 {
|
||||
t.Errorf("scaffold on a missing file: exit code = %d, want 1", code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCmdAsmFormatValidation checks that an unknown --format exits 2 with
|
||||
// and without -o, instead of assembling and silently dumping a raw image.
|
||||
func TestCmdAsmFormatValidation(t *testing.T) {
|
||||
path := writeTemp(t, "f_amd64.s", clean)
|
||||
out := filepath.Join(t.TempDir(), "f.bin")
|
||||
if _, _, code := capture(func() int { return cmdAsm([]string{"--format", "bogus", path}) }); code != 2 {
|
||||
t.Errorf("asm --format bogus without -o: code = %d, want 2", code)
|
||||
}
|
||||
if _, _, code := capture(func() int { return cmdAsm([]string{"--format", "bogus", "-o", out, path}) }); code != 2 {
|
||||
t.Errorf("asm --format bogus with -o: code = %d, want 2", code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCmdAsmOutputFile pins the documented -o behaviour: the output goes to
|
||||
// the file and stdout carries no hex dump; without -o the dump is the output.
|
||||
func TestCmdAsmOutputFile(t *testing.T) {
|
||||
path := writeTemp(t, "f_amd64.s", clean)
|
||||
out := filepath.Join(t.TempDir(), "f.bin")
|
||||
stdout, _, code := capture(func() int { return cmdAsm([]string{"-o", out, path}) })
|
||||
if code != 0 {
|
||||
t.Fatalf("code = %d", code)
|
||||
}
|
||||
if strings.Contains(stdout, "0000:") {
|
||||
t.Errorf("stdout carries a hex dump despite -o:\n%s", stdout)
|
||||
}
|
||||
if !strings.Contains(stdout, "wrote ") {
|
||||
t.Errorf("stdout misses the wrote line:\n%s", stdout)
|
||||
}
|
||||
b, err := os.ReadFile(out)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(b) == 0 {
|
||||
t.Error("the output file is empty")
|
||||
}
|
||||
|
||||
stdout, _, code = capture(func() int { return cmdAsm([]string{path}) })
|
||||
if code != 0 {
|
||||
t.Fatalf("without -o: code = %d", code)
|
||||
}
|
||||
if !strings.Contains(stdout, "0000:") {
|
||||
t.Errorf("without -o the hex dump is missing:\n%s", stdout)
|
||||
}
|
||||
}
|
||||
|
||||
// TestVerifyNonJITAMD64GroundTruth drives the cross-architecture
|
||||
// ground-truth path for an amd64 kernel: the path a host of any other
|
||||
// architecture takes, which must compare against the toolchain rather than
|
||||
// refuse to run.
|
||||
func TestVerifyNonJITAMD64GroundTruth(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("runs go tool asm")
|
||||
}
|
||||
path := writeTemp(t, "f_amd64.s", clean)
|
||||
out, _, code := capture(func() int { return cmdVerifyNonJIT(path, arch.AMD64, true, false) })
|
||||
if code != 0 {
|
||||
t.Fatalf("code = %d (%s)", code, out)
|
||||
}
|
||||
if !strings.Contains(out, "1/1 matched") {
|
||||
t.Errorf("output misses the matched report:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// TestVerifySmokeCrashIsolation checks that a function faulting on its
|
||||
// zeroed smoke arguments is reported as CRASH by a child process instead of
|
||||
// killing `gasm verify` itself.
|
||||
|
||||
Reference in New Issue
Block a user