1
0

process_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. //go:build !windows
  2. package xray
  3. import (
  4. "os"
  5. "os/exec"
  6. "os/signal"
  7. "path/filepath"
  8. "syscall"
  9. "testing"
  10. "time"
  11. xuilogger "github.com/mhsanaei/3x-ui/v3/logger"
  12. "github.com/op/go-logging"
  13. )
  14. func TestStopWaitsForGracefulExit(t *testing.T) {
  15. initProcessTestLogger(t)
  16. p := startProcessHelper(t, "delayed-term")
  17. start := time.Now()
  18. if err := p.Stop(); err != nil {
  19. t.Fatalf("Stop: %v", err)
  20. }
  21. if elapsed := time.Since(start); elapsed < 150*time.Millisecond {
  22. t.Fatalf("Stop returned before child exited; elapsed=%s", elapsed)
  23. }
  24. if p.IsRunning() {
  25. t.Fatal("process still reports running after Stop")
  26. }
  27. }
  28. func TestIntentionalStopDoesNotRecordExitError(t *testing.T) {
  29. initProcessTestLogger(t)
  30. p := startProcessHelper(t, "default-term")
  31. if err := p.Stop(); err != nil {
  32. t.Fatalf("Stop: %v", err)
  33. }
  34. if err := p.GetErr(); err != nil {
  35. t.Fatalf("GetErr after intentional stop = %v, want nil", err)
  36. }
  37. if result := p.GetResult(); result != "" {
  38. t.Fatalf("GetResult after intentional stop = %q, want empty", result)
  39. }
  40. }
  41. func TestStopKillsProcessThatIgnoresSIGTERM(t *testing.T) {
  42. initProcessTestLogger(t)
  43. oldGraceful := xrayGracefulStopTimeout
  44. oldForce := xrayForceStopTimeout
  45. xrayGracefulStopTimeout = 100 * time.Millisecond
  46. xrayForceStopTimeout = 2 * time.Second
  47. t.Cleanup(func() {
  48. xrayGracefulStopTimeout = oldGraceful
  49. xrayForceStopTimeout = oldForce
  50. })
  51. p := startProcessHelper(t, "ignore-term")
  52. if err := p.Stop(); err != nil {
  53. t.Fatalf("Stop: %v", err)
  54. }
  55. if p.IsRunning() {
  56. t.Fatal("process still reports running after forced stop")
  57. }
  58. }
  59. func initProcessTestLogger(t *testing.T) {
  60. t.Helper()
  61. t.Setenv("XUI_LOG_FOLDER", t.TempDir())
  62. xuilogger.InitLogger(logging.ERROR)
  63. }
  64. func startProcessHelper(t *testing.T, mode string) *process {
  65. t.Helper()
  66. readyPath := filepath.Join(t.TempDir(), "ready")
  67. cmd := exec.Command(os.Args[0], "-test.run=TestXrayProcessHelper", "--", mode)
  68. cmd.Env = append(os.Environ(),
  69. "XRAY_PROCESS_HELPER=1",
  70. "XRAY_PROCESS_READY="+readyPath,
  71. )
  72. p := newProcess(nil)
  73. if err := p.startCommand(cmd); err != nil {
  74. t.Fatalf("start helper process: %v", err)
  75. }
  76. waitForProcessHelperReady(t, readyPath)
  77. t.Cleanup(func() {
  78. if p.IsRunning() {
  79. p.intentionalStop.Store(true)
  80. _ = p.cmd.Process.Kill()
  81. _ = p.waitForExit(2 * time.Second)
  82. }
  83. })
  84. return p
  85. }
  86. func waitForProcessHelperReady(t *testing.T, readyPath string) {
  87. t.Helper()
  88. deadline := time.Now().Add(2 * time.Second)
  89. for time.Now().Before(deadline) {
  90. if _, err := os.Stat(readyPath); err == nil {
  91. return
  92. }
  93. time.Sleep(10 * time.Millisecond)
  94. }
  95. t.Fatalf("helper process did not become ready")
  96. }
  97. func TestXrayProcessHelper(t *testing.T) {
  98. if os.Getenv("XRAY_PROCESS_HELPER") != "1" {
  99. return
  100. }
  101. mode := ""
  102. for i, arg := range os.Args {
  103. if arg == "--" && i+1 < len(os.Args) {
  104. mode = os.Args[i+1]
  105. break
  106. }
  107. }
  108. switch mode {
  109. case "delayed-term":
  110. sigCh := make(chan os.Signal, 1)
  111. signal.Notify(sigCh, syscall.SIGTERM)
  112. markProcessHelperReady(t)
  113. <-sigCh
  114. time.Sleep(200 * time.Millisecond)
  115. os.Exit(0)
  116. case "default-term":
  117. markProcessHelperReady(t)
  118. select {}
  119. case "ignore-term":
  120. signal.Ignore(syscall.SIGTERM)
  121. markProcessHelperReady(t)
  122. select {}
  123. default:
  124. t.Fatalf("unknown helper mode %q", mode)
  125. }
  126. }
  127. func markProcessHelperReady(t *testing.T) {
  128. t.Helper()
  129. readyPath := os.Getenv("XRAY_PROCESS_READY")
  130. if readyPath == "" {
  131. t.Fatal("XRAY_PROCESS_READY is not set")
  132. }
  133. if err := os.WriteFile(readyPath, []byte("ready"), 0644); err != nil {
  134. t.Fatalf("write helper ready file: %v", err)
  135. }
  136. }