blob: 778089b296144424070116fe9f4c12bb465e216d [file] [edit]
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package script
import (
"context"
"fmt"
"internal/testenv"
"io"
"os"
"os/exec"
"runtime"
"slices"
"strings"
"testing"
"time"
)
func TestInterruptCmd(t *testing.T) {
if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
t.Skip(runtime.GOOS + " does not support executables")
}
const msg = "Hello world\n"
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
fmt.Printf(msg)
time.Sleep(24 * time.Hour) // sleep forever
os.Exit(3)
}
exe := testenv.Executable(t)
cmd := testenv.CommandContext(t, t.Context(), exe, "-test.run=^TestInterruptCmd$")
cmd = testenv.CleanCmdEnv(cmd)
cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
stdout, err := cmd.StdoutPipe()
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
buf := make([]byte, len(msg))
n, err := io.ReadFull(stdout, buf)
if n != len(buf) || err != nil || string(buf) != msg {
t.Fatalf("ReadFull = %d, %v, %q", n, err, buf[:n])
}
interruptCmdErr := make(chan error)
go func() {
interruptCmdErr <- InterruptCmd(cmd)
}()
err = cmd.Wait()
if err == nil {
t.Fatal("expected Wait failure")
} else if err, ok := err.(*exec.ExitError); ok {
checkInterruptCmdError(t, err)
} else {
t.Fatalf("unexpected error while running executable: %s\n%s", err, string(buf))
}
err = <-interruptCmdErr
if err != nil {
t.Errorf("InterruptCmd failed: %v", err)
}
}
// checkInterruptCmdError calls t.Fatal if err is interrupt cmd error.
func checkInterruptCmdError(t *testing.T, err error) {
errstr := err.Error()
if runtime.GOOS == "plan9" {
expectedPrefixError := "exit status: "
if !strings.HasPrefix(errstr, expectedPrefixError) {
t.Fatalf("unexpected error prefix while exiting executable: got=%q, want=%q", errstr, expectedPrefixError)
}
expectedSuffixError := ": interrupt'"
if !strings.HasSuffix(errstr, expectedSuffixError) {
t.Fatalf("unexpected error suffix while exiting executable: got=%q, want=%q", errstr, expectedSuffixError)
}
return
}
expectedError := "signal: interrupt"
if runtime.GOOS == "windows" {
expectedError = "exit status 1"
}
if errstr != expectedError {
t.Fatalf("unexpected error while exiting executable: got=%q, want=%q", errstr, expectedError)
}
}
func FuzzQuoteArgs(f *testing.F) {
state, err := NewState(context.Background(), f.TempDir(), nil /* env */)
if err != nil {
f.Fatalf("failed to create state: %v", err)
}
f.Add("foo")
f.Add(`"foo"`)
f.Add(`'foo'`)
f.Fuzz(func(t *testing.T, s string) {
give := []string{s}
quoted := quoteArgs(give)
cmd, err := parse("file.txt", 42, "cmd "+quoted)
if err != nil {
t.Fatalf("quoteArgs(%q) = %q cannot be parsed: %v", give, quoted, err)
}
args := expandArgs(state, cmd.rawArgs, nil /* regexpArgs */)
if !slices.Equal(give, args) {
t.Fatalf("quoteArgs failed to round-trip.\ninput:\n\t%#q\nquoted:\n\t%q\nparsed:\n\t%#q", give, quoted, args)
}
})
}