golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/coordinator/pool/queue/quota_test.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build linux || darwin 6 7 package queue 8 9 import ( 10 "context" 11 "sync" 12 "testing" 13 "time" 14 15 "github.com/google/go-cmp/cmp" 16 ) 17 18 func TestQueueEmpty(t *testing.T) { 19 q := NewQuota() 20 if !q.Empty() { 21 t.Errorf("q.Empty() = %t, wanted %t", q.Empty(), true) 22 } 23 q.Enqueue(100, new(SchedItem)) 24 if q.Empty() { 25 t.Errorf("q.Empty() = %t, wanted %t", q.Empty(), false) 26 } 27 } 28 29 func TestQueueReturnQuotas(t *testing.T) { 30 q := NewQuota() 31 q.UpdateQuotas(7, 15) 32 q.ReturnQuota(3) 33 usage := q.Quotas() 34 if !(usage.Used == 4 && usage.Limit == 15) { 35 t.Errorf("q.Quotas() = %d, %d, wanted %d, %d", usage.Used, usage.Limit, 10, 15) 36 } 37 } 38 39 func TestQueue(t *testing.T) { 40 q := NewQuota() 41 q.UpdateQuotas(14, 16) 42 q.UpdateUntracked(1) 43 item := q.Enqueue(4, new(SchedItem)) 44 45 if q.Empty() { 46 t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), false) 47 } 48 49 want := Usage{ 50 Used: 14, 51 Limit: 16, 52 UntrackedUsed: 1, 53 } 54 got := q.Quotas() 55 if diff := cmp.Diff(want, got); diff != "" { 56 t.Errorf("q.ToExported() mismatch (-want +got):\n%s", diff) 57 } 58 59 ctx := context.Background() 60 done := make(chan error, 1) 61 go func() { 62 done <- item.Await(ctx) 63 }() 64 65 q.ReturnQuota(14) 66 67 if !q.Empty() { 68 t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), true) 69 } 70 71 want = Usage{ 72 Used: 4, 73 Limit: 16, 74 UntrackedUsed: 1, 75 } 76 got = q.Quotas() 77 if diff := cmp.Diff(want, got); diff != "" { 78 t.Errorf("q.ToExported() mismatch (-want +got):\n%s", diff) 79 } 80 select { 81 case err := <-done: 82 if err != nil { 83 t.Fatalf("item.Await() = %v, wanted no error", err) 84 } 85 case <-time.After(time.Second): 86 t.Fatal("item.Await() never returned, wanted return after q.tryPop()") 87 } 88 89 item.ReturnQuota() 90 91 if !q.Empty() { 92 t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), true) 93 } 94 want = Usage{ 95 Used: 0, 96 Limit: 16, 97 UntrackedUsed: 1, 98 } 99 got = q.Quotas() 100 if diff := cmp.Diff(want, got); diff != "" { 101 t.Errorf("q.ToExported() mismatch (-want +got):\n%s", diff) 102 } 103 } 104 105 func TestQueueUpdatedMany(t *testing.T) { 106 q := NewQuota() 107 ctx := context.Background() 108 items := []*Item{ 109 q.Enqueue(3, &SchedItem{IsGomote: true}), 110 q.Enqueue(1, new(SchedItem)), 111 q.Enqueue(1, new(SchedItem)), 112 } 113 114 var wg sync.WaitGroup 115 wg.Add(3) 116 done := make(chan error, 3) 117 for _, item := range items { 118 go func(item *Item) { 119 done <- item.Await(ctx) 120 item.ReturnQuota() 121 wg.Done() 122 }(item) 123 } 124 if q.Len() != 3 { 125 t.Errorf("q.Len() = %d, wanted %d", q.Len(), 3) 126 } 127 q.UpdateLimit(3) 128 for range items { 129 <-done 130 } 131 if !q.Empty() { 132 t.Errorf("q.Empty() = %t, wanted %t", q.Empty(), true) 133 } 134 wg.Wait() 135 136 if !q.Empty() { 137 t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), true) 138 } 139 usage := q.Quotas() 140 if !(usage.Used == 0 && usage.Limit == 3) { 141 t.Errorf("q.Quotas() = %d, %d, wanted %d, %d", usage.Used, usage.Limit, 0, 3) 142 } 143 } 144 145 func TestQueueCancel(t *testing.T) { 146 q := NewQuota() 147 q.UpdateQuotas(0, 15) 148 ctx, cancel := context.WithCancel(context.Background()) 149 150 enqueued := make(chan struct{}) 151 done := make(chan error) 152 go func() { 153 done <- q.AwaitQueue(ctx, 100, new(SchedItem)) 154 }() 155 go func() { 156 for q.Empty() { 157 time.Sleep(100 * time.Millisecond) 158 } 159 close(enqueued) 160 }() 161 select { 162 case <-time.After(time.Second): 163 t.Fatal("q.AwaitQueue() never called, wanted one call") 164 case <-enqueued: 165 // success. 166 } 167 if q.Empty() { 168 t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), false) 169 } 170 171 cancel() 172 select { 173 case err := <-done: 174 if err == nil { 175 t.Fatalf("q.AwaitQueue() = %v, wanted error", err) 176 } 177 case <-time.After(time.Second): 178 t.Fatal("q.AwaitQueue() never returned, wanted return after cancel()") 179 } 180 181 b := q.tryPop() 182 if b != nil { 183 t.Errorf("q.tryPop() = %v, wanted %v", b, nil) 184 } 185 if !q.Empty() { 186 t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), true) 187 } 188 usage := q.Quotas() 189 if !(usage.Used == 0 && usage.Limit == 15) { 190 t.Errorf("q.Quotas() = %d, %d, wanted %d, %d", usage.Used, usage.Limit, 0, 15) 191 } 192 } 193 194 func TestQueueToExported(t *testing.T) { 195 q := NewQuota() 196 q.UpdateLimit(10) 197 q.Enqueue(100, &SchedItem{IsTry: true}) 198 q.Enqueue(100, &SchedItem{IsTry: true}) 199 q.Enqueue(100, &SchedItem{IsTry: true}) 200 q.Enqueue(100, &SchedItem{IsGomote: true}) 201 q.Enqueue(100, &SchedItem{IsGomote: true}) 202 q.Enqueue(100, &SchedItem{IsGomote: true}) 203 q.Enqueue(100, &SchedItem{IsRelease: true}) 204 want := &QuotaStats{ 205 Usage: Usage{ 206 Limit: 10, 207 }, 208 Items: []ItemStats{ 209 {Build: &SchedItem{IsRelease: true}, Cost: 100}, 210 {Build: &SchedItem{IsGomote: true}, Cost: 100}, 211 {Build: &SchedItem{IsGomote: true}, Cost: 100}, 212 {Build: &SchedItem{IsGomote: true}, Cost: 100}, 213 {Build: &SchedItem{IsTry: true}, Cost: 100}, 214 {Build: &SchedItem{IsTry: true}, Cost: 100}, 215 {Build: &SchedItem{IsTry: true}, Cost: 100}, 216 }, 217 } 218 got := q.ToExported() 219 if diff := cmp.Diff(want, got); diff != "" { 220 t.Errorf("q.ToExported() mismatch (-want +got):\n%s", diff) 221 } 222 }