carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package cgroup
4
5import (
6 "context"
7 "errors"
8 "sync"
9 "testing"
10 "time"
11)
12
13func TestPlacementsUnderCountsTheSubtree(t *testing.T) {
14 m := &Manager{root: "/r", logf: t.Logf}
15 done := m.beginPlacement("/r/carlos-exec@a.example/g2")
16 if m.placementsUnder("/r/carlos-exec@a.example") != 1 || m.placementsAt("/r/carlos-exec@a.example") != 0 {
17 t.Fatal("a child's placement is under its parent, not at it")
18 }
19 if m.placementsUnder("/r/carlos-exec@a.exampleX") != 0 {
20 t.Fatal("a prefix without a separator matched")
21 }
22 if m.placementsAt("/r/carlos-exec@a.example/g2/") != 1 || m.placementsUnder("/r") != 1 {
23 t.Fatal("the target itself or its root does not count it")
24 }
25 done()
26 done() // idempotent
27 if m.placementsUnder("/r/carlos-exec@a.example") != 0 {
28 t.Fatal("done did not drop the count")
29 }
30}
31
32func TestPlacementsCountEachInFlight(t *testing.T) {
33 m := &Manager{root: "/r", logf: t.Logf}
34 a := m.beginPlacement("/r/h")
35 b := m.beginPlacement("/r/h")
36 if m.placementsAt("/r/h") != 2 {
37 t.Fatal("two placements at one target count as one")
38 }
39 a()
40 a()
41 if m.placementsAt("/r/h") != 1 {
42 t.Fatal("a repeated done dropped another placement's count")
43 }
44 b()
45 if m.placementsAt("/r/h") != 0 {
46 t.Fatal("the count did not reach zero")
47 }
48}
49
50func TestWaitPlacementsUnderWakes(t *testing.T) {
51 m := &Manager{root: "/r", logf: t.Logf}
52 if err := m.waitPlacementsUnder(context.Background(), "/r"); err != nil {
53 t.Fatalf("nothing in flight: %v", err)
54 }
55 done := m.beginPlacement("/r/h/g1")
56
57 ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
58 defer cancel()
59 if err := m.waitPlacementsUnder(ctx, "/r/h"); !errors.Is(err, context.DeadlineExceeded) {
60 t.Fatalf("err=%v, want context.DeadlineExceeded", err)
61 }
62
63 got := make(chan error, 1)
64 go func() { got <- m.waitPlacementsUnder(context.Background(), "/r/h") }()
65 // An unrelated placement ending wakes the waiter, which must keep waiting.
66 other := m.beginPlacement("/r/elsewhere")
67 other()
68 select {
69 case err := <-got:
70 t.Fatalf("the waiter returned %v while a placement was in flight", err)
71 case <-time.After(50 * time.Millisecond):
72 }
73 done()
74 select {
75 case err := <-got:
76 if err != nil {
77 t.Fatal(err)
78 }
79 case <-time.After(time.Second):
80 t.Fatal("the waiter did not wake within 1s of done")
81 }
82}
83
84func TestNilManagerPlacements(t *testing.T) {
85 var m *Manager
86 done := m.beginPlacement("/r/h")
87 done()
88 if m.placementsUnder("/r") != 0 || m.placementsAt("/r/h") != 0 {
89 t.Fatal("a nil manager counted a placement")
90 }
91 if err := m.waitPlacementsUnder(context.Background(), "/r"); err != nil {
92 t.Fatal(err)
93 }
94}
95
96// readQuiet answers quiet only when nothing under its directory was in
97// flight at any moment of the read: not at its start, not registered or
98// ended during it, not at its end. Placements elsewhere do not count.
99func TestReadQuiet(t *testing.T) {
100 m := &Manager{root: "/r", logf: t.Logf}
101 if !m.readQuiet("/r/h", func() {}) {
102 t.Fatal("nothing in flight read as not quiet")
103 }
104 other := m.beginPlacement("/r/hx/g1")
105 defer other()
106 if !m.readQuiet("/r/h", func() { m.beginPlacement("/r/other")() }) {
107 t.Fatal("placements outside the directory made the read not quiet")
108 }
109 done := m.beginPlacement("/r/h/g1")
110 if m.readQuiet("/r/h", func() {}) {
111 t.Fatal("a placement in flight throughout read as quiet")
112 }
113 if m.readQuiet("/r/h", done) {
114 t.Fatal("a placement that ended during the read read as quiet")
115 }
116 var late func()
117 if m.readQuiet("/r/h", func() { late = m.beginPlacement("/r/h/g2"); late() }) {
118 t.Fatal("a placement that registered and ended during the read read as quiet")
119 }
120 if !m.readQuiet("/r/h", func() {}) {
121 t.Fatal("the read after everything ended is not quiet")
122 }
123 m.placeMu.Lock()
124 open := len(m.windows)
125 m.placeMu.Unlock()
126 if open != 0 {
127 t.Fatalf("%d windows left open", open)
128 }
129 var nilm *Manager
130 ran := false
131 if !nilm.readQuiet("/r", func() { ran = true }) || !ran {
132 t.Fatal("a nil manager's read did not run or was not quiet")
133 }
134}
135
136// holdPlacements stops registrations in and below its directory until it
137// is released, counts the ones already in flight, and leaves others alone.
138func TestHoldPlacementsBlocksRegistrationUntilReleased(t *testing.T) {
139 m := &Manager{root: "/r", logf: t.Logf}
140 before := m.beginPlacement("/r/h/g1")
141 at, under, release := m.holdPlacements("/r/h")
142 if at != 0 || under != 1 {
143 t.Fatalf("hold counted %d at, %d under: want 0, 1", at, under)
144 }
145 // A placement already in flight still ends under the hold.
146 before()
147 // One elsewhere is not held.
148 m.beginPlacement("/r/elsewhere")()
149
150 blocked := make(chan struct{})
151 var once sync.Once
152 orig := placementHeldHook
153 placementHeldHook = func(string) { once.Do(func() { close(blocked) }) }
154 t.Cleanup(func() { placementHeldHook = orig })
155 got := make(chan func(), 1)
156 go func() { got <- m.beginPlacement("/r/h/g2") }()
157 select {
158 case <-blocked:
159 case d := <-got:
160 d()
161 t.Fatal("a placement registered under a hold")
162 case <-time.After(5 * time.Second):
163 t.Fatal("the registration neither registered nor waited")
164 }
165 if n := m.placementsUnder("/r/h"); n != 0 {
166 t.Fatalf("%d placements registered under the hold", n)
167 }
168 release()
169 release() // idempotent
170 select {
171 case d := <-got:
172 if n := m.placementsAt("/r/h/g2"); n != 1 {
173 t.Fatalf("after the release %d placements at g2, want 1", n)
174 }
175 d()
176 case <-time.After(5 * time.Second):
177 t.Fatal("the registration did not go ahead once the hold was released")
178 }
179 var nilm *Manager
180 if a, u, r := nilm.holdPlacements("/r"); a != 0 || u != 0 {
181 t.Fatal("a nil manager counted placements")
182 } else {
183 r()
184 }
185}
186