Files
loggerx/loggerx_remove_retry_test.go
T

122 lines
2.9 KiB
Go
Raw Normal View History

2026-09-13 21:53:37 +08:00
package loggerx
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
)
// removeWithRetry 遇到「文件被占用」应重试;占用解除后必须删掉
func TestRemoveWithRetryHandlesTransientLock(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "locked.log")
if err := os.WriteFile(path, []byte("data"), 0644); err != nil {
t.Fatal(err)
}
// 先独占打开,制造占用
holder, err := os.Open(path)
if err != nil {
t.Fatal(err)
}
locked := make(chan struct{})
release := make(chan struct{})
closed := make(chan struct{})
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
close(locked)
<-release
_ = holder.Close()
close(closed)
}()
<-locked
// 120ms 后释放占用;退避总时长(20+40+80+160+320+640 ≈ 1.2s)要能覆盖
go func() {
time.Sleep(120 * time.Millisecond)
close(release)
}()
start := time.Now()
err = removeWithRetry(path, 7)
elapsed := time.Since(start)
<-closed
wg.Wait()
if err != nil {
t.Fatalf("占用解除后仍未删除成功: %v", err)
}
if _, statErr := os.Stat(path); !os.IsNotExist(statErr) {
t.Errorf("文件仍然存在")
}
t.Logf("重试 %v 后删除成功", elapsed.Round(time.Millisecond))
}
// 不可重试的错误(文件不存在)应立即返回,不做无谓等待
func TestRemoveWithRetryFastFail(t *testing.T) {
dir := t.TempDir()
missing := filepath.Join(dir, "nope.log")
start := time.Now()
err := removeWithRetry(missing, 5)
elapsed := time.Since(start)
if err == nil {
t.Fatal("删除不存在的文件应该返回错误")
}
if elapsed > 100*time.Millisecond {
t.Errorf("不该对不可重试的错误做退避等待,耗时 %v", elapsed)
}
}
// isTransientRemoveErr 判定
func TestIsTransientRemoveErr(t *testing.T) {
cases := []struct {
msg string
want bool
}{
{"remove x.log: The process cannot access the file because it is being used by another process.", true},
{"sharing violation", true},
{"resource busy", true},
{"permission denied", false},
{"no such file or directory", false},
}
for _, c := range cases {
got := isTransientRemoveErr(fmt.Errorf("%s", c.msg))
if got != c.want {
t.Errorf("isTransientRemoveErr(%q) = %v, 期望 %v", c.msg, got, c.want)
}
}
if isTransientRemoveErr(nil) {
t.Error("nil 不应判为可重试")
}
}
// 压缩完成后,源文件与 .gz 不应长期并存
func TestCompressCleansSourceFile(t *testing.T) {
dir := t.TempDir()
src := filepath.Join(dir, "a.log")
if err := os.WriteFile(src, []byte(strings.Repeat("log line\n", 500)), 0644); err != nil {
t.Fatal(err)
}
if err := gzipFile(src, 1); err != nil {
t.Fatalf("gzipFile: %v", err)
}
if _, err := os.Stat(src + ".gz"); err != nil {
t.Fatalf("没有生成 .gz: %v", err)
}
if _, err := os.Stat(src); !os.IsNotExist(err) {
t.Errorf("源文件没有被删除: %v", err)
}
if _, err := os.Stat(src + ".gz.tmp"); !os.IsNotExist(err) {
t.Errorf("残留了 .gz.tmp")
}
}