|
1 | 1 | package internal |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "sync" |
5 | | - "sync/atomic" |
6 | 4 | "testing" |
7 | | - "time" |
8 | 5 | ) |
9 | 6 |
|
10 | | -func TestRateGate_AllowsConcurrentReaders(t *testing.T) { |
11 | | - var gate sync.RWMutex |
12 | | - var concurrent int64 |
13 | | - var maxConcurrent int64 |
14 | | - |
15 | | - var wg sync.WaitGroup |
16 | | - for i := 0; i < 10; i++ { |
17 | | - wg.Add(1) |
18 | | - go func() { |
19 | | - defer wg.Done() |
20 | | - |
21 | | - // Simulate the read-lock gate pattern from downloadWithRateGate |
22 | | - gate.RLock() |
23 | | - gate.RUnlock() |
24 | | - |
25 | | - c := atomic.AddInt64(&concurrent, 1) |
26 | | - for { |
27 | | - old := atomic.LoadInt64(&maxConcurrent) |
28 | | - if c <= old || atomic.CompareAndSwapInt64(&maxConcurrent, old, c) { |
29 | | - break |
30 | | - } |
31 | | - } |
32 | | - |
33 | | - time.Sleep(time.Millisecond) |
34 | | - atomic.AddInt64(&concurrent, -1) |
35 | | - }() |
36 | | - } |
37 | | - wg.Wait() |
38 | | - |
39 | | - // RLock is shared, so all goroutines should run concurrently |
40 | | - if maxConcurrent < 2 { |
41 | | - t.Errorf("expected concurrent execution with RLock gate, max concurrent was %d", maxConcurrent) |
42 | | - } |
43 | | -} |
44 | | - |
45 | | -func TestRateGate_WriteLockBlocksAllReaders(t *testing.T) { |
46 | | - var gate sync.RWMutex |
47 | | - ready := make(chan struct{}, 4) |
48 | | - |
49 | | - // Simulate a rate-limited worker holding the write lock |
50 | | - gate.Lock() |
51 | | - |
52 | | - var wg sync.WaitGroup |
53 | | - for i := 0; i < 4; i++ { |
54 | | - wg.Add(1) |
55 | | - go func() { |
56 | | - defer wg.Done() |
57 | | - ready <- struct{}{} |
58 | | - gate.RLock() |
59 | | - gate.RUnlock() |
60 | | - }() |
61 | | - } |
62 | | - |
63 | | - // Wait for all goroutines to start |
64 | | - for i := 0; i < 4; i++ { |
65 | | - <-ready |
66 | | - } |
67 | | - time.Sleep(10 * time.Millisecond) |
68 | | - |
69 | | - // Release the write lock (simulating rate limit wait done) |
70 | | - gate.Unlock() |
71 | | - wg.Wait() |
72 | | -} |
73 | | - |
74 | | -func TestRateGate_TryLockPreventsDoubleWait(t *testing.T) { |
75 | | - var gate sync.RWMutex |
76 | | - var waitCount int64 |
77 | | - |
78 | | - // Simulate two workers hitting rate limit simultaneously |
79 | | - gate.Lock() // first worker takes the write lock |
80 | | - |
81 | | - var wg sync.WaitGroup |
82 | | - wg.Add(1) |
83 | | - go func() { |
84 | | - defer wg.Done() |
85 | | - // Second worker tries TryLock, should fail |
86 | | - if gate.TryLock() { |
87 | | - atomic.AddInt64(&waitCount, 1) |
88 | | - gate.Unlock() |
89 | | - } |
90 | | - }() |
91 | | - |
92 | | - time.Sleep(10 * time.Millisecond) |
93 | | - atomic.AddInt64(&waitCount, 1) // first worker counts |
94 | | - gate.Unlock() |
95 | | - wg.Wait() |
96 | | - |
97 | | - // Only 1 worker should have done the wait (the first one) |
98 | | - if atomic.LoadInt64(&waitCount) != 1 { |
99 | | - t.Errorf("expected exactly 1 rate limit wait, got %d", waitCount) |
100 | | - } |
101 | | -} |
102 | | - |
103 | 7 | func TestBuildDownloadOpts_DefaultFileFormat(t *testing.T) { |
104 | 8 | target := &Target{ |
105 | 9 | File: "locales/<locale_name>.json", |
|
0 commit comments