| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- //go:build !windows
- package xray
- import (
- "os"
- "os/exec"
- "os/signal"
- "path/filepath"
- "syscall"
- "testing"
- "time"
- xuilogger "github.com/mhsanaei/3x-ui/v3/logger"
- "github.com/op/go-logging"
- )
- func TestStopWaitsForGracefulExit(t *testing.T) {
- initProcessTestLogger(t)
- p := startProcessHelper(t, "delayed-term")
- start := time.Now()
- if err := p.Stop(); err != nil {
- t.Fatalf("Stop: %v", err)
- }
- if elapsed := time.Since(start); elapsed < 150*time.Millisecond {
- t.Fatalf("Stop returned before child exited; elapsed=%s", elapsed)
- }
- if p.IsRunning() {
- t.Fatal("process still reports running after Stop")
- }
- }
- func TestIntentionalStopDoesNotRecordExitError(t *testing.T) {
- initProcessTestLogger(t)
- p := startProcessHelper(t, "default-term")
- if err := p.Stop(); err != nil {
- t.Fatalf("Stop: %v", err)
- }
- if err := p.GetErr(); err != nil {
- t.Fatalf("GetErr after intentional stop = %v, want nil", err)
- }
- if result := p.GetResult(); result != "" {
- t.Fatalf("GetResult after intentional stop = %q, want empty", result)
- }
- }
- func TestStopKillsProcessThatIgnoresSIGTERM(t *testing.T) {
- initProcessTestLogger(t)
- oldGraceful := xrayGracefulStopTimeout
- oldForce := xrayForceStopTimeout
- xrayGracefulStopTimeout = 100 * time.Millisecond
- xrayForceStopTimeout = 2 * time.Second
- t.Cleanup(func() {
- xrayGracefulStopTimeout = oldGraceful
- xrayForceStopTimeout = oldForce
- })
- p := startProcessHelper(t, "ignore-term")
- if err := p.Stop(); err != nil {
- t.Fatalf("Stop: %v", err)
- }
- if p.IsRunning() {
- t.Fatal("process still reports running after forced stop")
- }
- }
- func initProcessTestLogger(t *testing.T) {
- t.Helper()
- t.Setenv("XUI_LOG_FOLDER", t.TempDir())
- xuilogger.InitLogger(logging.ERROR)
- }
- func startProcessHelper(t *testing.T, mode string) *process {
- t.Helper()
- readyPath := filepath.Join(t.TempDir(), "ready")
- cmd := exec.Command(os.Args[0], "-test.run=TestXrayProcessHelper", "--", mode)
- cmd.Env = append(os.Environ(),
- "XRAY_PROCESS_HELPER=1",
- "XRAY_PROCESS_READY="+readyPath,
- )
- p := newProcess(nil)
- if err := p.startCommand(cmd); err != nil {
- t.Fatalf("start helper process: %v", err)
- }
- waitForProcessHelperReady(t, readyPath)
- t.Cleanup(func() {
- if p.IsRunning() {
- p.intentionalStop.Store(true)
- _ = p.cmd.Process.Kill()
- _ = p.waitForExit(2 * time.Second)
- }
- })
- return p
- }
- func waitForProcessHelperReady(t *testing.T, readyPath string) {
- t.Helper()
- deadline := time.Now().Add(2 * time.Second)
- for time.Now().Before(deadline) {
- if _, err := os.Stat(readyPath); err == nil {
- return
- }
- time.Sleep(10 * time.Millisecond)
- }
- t.Fatalf("helper process did not become ready")
- }
- func TestXrayProcessHelper(t *testing.T) {
- if os.Getenv("XRAY_PROCESS_HELPER") != "1" {
- return
- }
- mode := ""
- for i, arg := range os.Args {
- if arg == "--" && i+1 < len(os.Args) {
- mode = os.Args[i+1]
- break
- }
- }
- switch mode {
- case "delayed-term":
- sigCh := make(chan os.Signal, 1)
- signal.Notify(sigCh, syscall.SIGTERM)
- markProcessHelperReady(t)
- <-sigCh
- time.Sleep(200 * time.Millisecond)
- os.Exit(0)
- case "default-term":
- markProcessHelperReady(t)
- select {}
- case "ignore-term":
- signal.Ignore(syscall.SIGTERM)
- markProcessHelperReady(t)
- select {}
- default:
- t.Fatalf("unknown helper mode %q", mode)
- }
- }
- func markProcessHelperReady(t *testing.T) {
- t.Helper()
- readyPath := os.Getenv("XRAY_PROCESS_READY")
- if readyPath == "" {
- t.Fatal("XRAY_PROCESS_READY is not set")
- }
- if err := os.WriteFile(readyPath, []byte("ready"), 0644); err != nil {
- t.Fatalf("write helper ready file: %v", err)
- }
- }
|