Files
air/runner/util_linux_test.go
Copilot cfbf60d884 Send signals to process descendants concurrently (#822)
* Initial plan

* Improve performance by sending signals concurrently

Co-authored-by: xiantang <34479567+xiantang@users.noreply.github.com>

* Add test for concurrent signal sending with race detection

Co-authored-by: xiantang <34479567+xiantang@users.noreply.github.com>

* Fix formatting in util_test.go with goimports

Co-authored-by: xiantang <34479567+xiantang@users.noreply.github.com>

* Move Linux-specific test to util_linux_test.go to fix typecheck error

Co-authored-by: xiantang <34479567+xiantang@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: xiantang <34479567+xiantang@users.noreply.github.com>
2025-11-17 17:11:47 +08:00

86 lines
1.8 KiB
Go

package runner
import (
"errors"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func Test_sendSignalToProcessTree_ConcurrentSignalSending(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("requires /proc")
}
_, b, _, _ := runtime.Caller(0)
dir := filepath.Dir(b)
err := os.Chdir(dir)
if err != nil {
t.Fatalf("couldn't change directory: %v", err)
}
_ = os.Remove("pid")
defer os.Remove("pid")
e := Engine{
config: &Config{
Build: cfgBuild{
SendInterrupt: false,
},
},
}
// Start a process tree with multiple children
startChan := make(chan *exec.Cmd)
go func() {
cmd, _, _, err := e.startCmd("sh _testdata/run-many-processes.sh")
if err != nil {
t.Errorf("failed to start command: %v", err)
return
}
startChan <- cmd
if err := cmd.Wait(); err != nil {
t.Logf("wait returned: %v", err)
}
}()
cmd := <-startChan
pid := cmd.Process.Pid
time.Sleep(2 * time.Second)
// Send signal using the concurrent implementation
err = sendSignalToProcessTree(pid, syscall.SIGKILL)
// Should not return an error for successful kill
if err != nil && !errors.Is(err, syscall.ESRCH) {
t.Errorf("unexpected error from sendSignalToProcessTree: %v", err)
}
// Verify all processes were killed
bytesRead, err := os.ReadFile("pid")
require.NoError(t, err)
lines := strings.Split(string(bytesRead), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
if _, err := strconv.Atoi(line); err != nil {
t.Logf("failed to convert str to int %v", err)
continue
}
_, err = exec.Command("ps", "-p", line, "-o", "comm= ").Output()
if err == nil {
t.Fatalf("process should be killed %v", line)
}
}
}