mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-07 07:42:17 +03:00
* fix(xray): synchronize lifecycle snapshots Protect process replacement and result caching with a lifecycle state object, so read paths keep one process snapshot while restarts swap state safely. Bound version probing to prevent a stalled binary from holding the restart lock. * test(xray): cover concurrent lifecycle reads Exercise status, result, and traffic reads while the managed process is replaced, so the race detector guards the lifecycle snapshot boundary. * fix(xray): guard process config snapshots Synchronize hot-applied config snapshots, keep Telegram reads on one lifecycle snapshot, and strengthen lifecycle timeout and concurrency regression coverage. --------- Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local>
69 lines
1.6 KiB
Go
69 lines
1.6 KiB
Go
package service
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/xray"
|
|
)
|
|
|
|
func TestXrayLifecycleSnapshotDoesNotOverwriteNewerResult(t *testing.T) {
|
|
state := xrayLifecycle{}
|
|
first := xray.NewProcess(&xray.Config{})
|
|
second := xray.NewProcess(&xray.Config{})
|
|
|
|
state.replace(first)
|
|
state.storeResult(first, "first result")
|
|
process, result := state.snapshot()
|
|
if process != first || result != "first result" {
|
|
t.Fatalf("snapshot = (%p, %q), want (%p, %q)", process, result, first, "first result")
|
|
}
|
|
state.replace(second)
|
|
state.storeResult(first, "old result")
|
|
|
|
process, result = state.snapshot()
|
|
if process != second {
|
|
t.Fatal("snapshot returned the replaced process")
|
|
}
|
|
if result != "" {
|
|
t.Fatalf("snapshot result = %q, want empty", result)
|
|
}
|
|
}
|
|
|
|
func TestXrayLifecycleConcurrentStatusResultAndTrafficReads(t *testing.T) {
|
|
previousProcess, previousResult := xrayState.snapshot()
|
|
t.Cleanup(func() {
|
|
xrayState.mu.Lock()
|
|
xrayState.process = previousProcess
|
|
xrayState.result = previousResult
|
|
xrayState.mu.Unlock()
|
|
})
|
|
|
|
first := xray.NewProcess(&xray.Config{})
|
|
second := xray.NewProcess(&xray.Config{})
|
|
service := XrayService{}
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Go(func() {
|
|
for range 200 {
|
|
xrayState.replace(first)
|
|
xrayState.replace(second)
|
|
}
|
|
})
|
|
|
|
for range 4 {
|
|
wg.Go(func() {
|
|
for range 200 {
|
|
_ = service.IsXrayRunning()
|
|
_ = service.GetXrayResult()
|
|
_, _, err := service.GetXrayTraffic()
|
|
if err == nil || err.Error() != "xray is not running" {
|
|
t.Errorf("GetXrayTraffic error = %v, want xray is not running", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|