1
0

process_race_test.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package mtproto
  2. import (
  3. "errors"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "strings"
  8. "sync"
  9. "testing"
  10. "time"
  11. )
  12. func TestProcessLifecycleFieldsRaceSafe(t *testing.T) {
  13. proc := newProcess("", "test")
  14. stop := make(chan struct{})
  15. var workers sync.WaitGroup
  16. defer func() {
  17. close(stop)
  18. workers.Wait()
  19. }()
  20. workers.Go(func() {
  21. for {
  22. select {
  23. case <-stop:
  24. return
  25. default:
  26. }
  27. proc.mu.Lock()
  28. proc.cmd = &exec.Cmd{}
  29. proc.done = make(chan struct{})
  30. proc.mu.Unlock()
  31. proc.setExitErr(errors.New("exit"))
  32. }
  33. })
  34. for range 4 {
  35. workers.Go(func() {
  36. for {
  37. select {
  38. case <-stop:
  39. return
  40. default:
  41. }
  42. _ = proc.IsRunning()
  43. _ = proc.GetResult()
  44. }
  45. })
  46. }
  47. time.Sleep(50 * time.Millisecond)
  48. }
  49. func TestProcessStatusDuringExit(t *testing.T) {
  50. pidFile := installFakeMtg(t)
  51. exitFile := filepath.Join(t.TempDir(), "exit")
  52. t.Setenv("MTG_FAKE_EXIT_FILE", exitFile)
  53. configPath := filepath.Join(t.TempDir(), "mtg.toml")
  54. if err := os.WriteFile(configPath, nil, 0o600); err != nil {
  55. t.Fatalf("write config: %v", err)
  56. }
  57. proc := newProcess(configPath, "test")
  58. if err := proc.Start(); err != nil {
  59. t.Fatalf("start process: %v", err)
  60. }
  61. t.Cleanup(func() {
  62. _ = proc.Stop()
  63. })
  64. waitSpawnCount(t, pidFile, 1)
  65. stopReads := make(chan struct{})
  66. var readers sync.WaitGroup
  67. defer func() {
  68. close(stopReads)
  69. readers.Wait()
  70. }()
  71. for range 4 {
  72. readers.Go(func() {
  73. for {
  74. select {
  75. case <-stopReads:
  76. return
  77. default:
  78. _ = proc.IsRunning()
  79. _ = proc.GetResult()
  80. }
  81. }
  82. })
  83. }
  84. proc.mu.RLock()
  85. done := proc.done
  86. proc.mu.RUnlock()
  87. if err := os.WriteFile(exitFile, nil, 0o600); err != nil {
  88. t.Fatalf("trigger exit: %v", err)
  89. }
  90. if err := waitForExit(done, time.Second); err != nil {
  91. t.Fatalf("wait for process exit: %v", err)
  92. }
  93. if proc.IsRunning() {
  94. t.Fatal("process must not be running after exit")
  95. }
  96. if got := proc.GetResult(); !strings.Contains(got, "exit status 1") {
  97. t.Fatalf("GetResult after an unexpected exit = %q, want exit status", got)
  98. }
  99. }