blob: 130708669015d89556f4e838bab63719aa37ac1c [file] [log] [blame]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package queue
import (
"context"
"sync"
"testing"
"time"
"github.com/google/go-cmp/cmp"
)
func TestQueueEmpty(t *testing.T) {
q := NewQuota()
if !q.Empty() {
t.Errorf("q.Empty() = %t, wanted %t", q.Empty(), true)
}
q.Enqueue(100, new(SchedItem))
if q.Empty() {
t.Errorf("q.Empty() = %t, wanted %t", q.Empty(), false)
}
}
func TestQueueReturnQuotas(t *testing.T) {
q := NewQuota()
q.UpdateQuotas(7, 15)
q.ReturnQuota(3)
usage := q.Quotas()
if !(usage.Used == 4 && usage.Limit == 15) {
t.Errorf("q.Quotas() = %d, %d, wanted %d, %d", usage.Used, usage.Limit, 10, 15)
}
}
func TestQueue(t *testing.T) {
q := NewQuota()
q.UpdateQuotas(14, 16)
q.UpdateUntracked(1)
item := q.Enqueue(4, new(SchedItem))
if q.Empty() {
t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), false)
}
want := Usage{
Used: 14,
Limit: 16,
UntrackedUsed: 1,
}
got := q.Quotas()
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("q.ToExported() mismatch (-want +got):\n%s", diff)
}
ctx := context.Background()
done := make(chan error, 1)
go func() {
done <- item.Await(ctx)
}()
q.ReturnQuota(14)
if !q.Empty() {
t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), true)
}
want = Usage{
Used: 4,
Limit: 16,
UntrackedUsed: 1,
}
got = q.Quotas()
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("q.ToExported() mismatch (-want +got):\n%s", diff)
}
select {
case err := <-done:
if err != nil {
t.Fatalf("item.Await() = %v, wanted no error", err)
}
case <-time.After(time.Second):
t.Fatal("item.Await() never returned, wanted return after q.tryPop()")
}
item.ReturnQuota()
if !q.Empty() {
t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), true)
}
want = Usage{
Used: 0,
Limit: 16,
UntrackedUsed: 1,
}
got = q.Quotas()
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("q.ToExported() mismatch (-want +got):\n%s", diff)
}
}
func TestQueueUpdatedMany(t *testing.T) {
q := NewQuota()
ctx := context.Background()
items := []*Item{
q.Enqueue(3, &SchedItem{IsGomote: true}),
q.Enqueue(1, new(SchedItem)),
q.Enqueue(1, new(SchedItem)),
}
var wg sync.WaitGroup
wg.Add(3)
done := make(chan error, 3)
for _, item := range items {
go func(item *Item) {
done <- item.Await(ctx)
item.ReturnQuota()
wg.Done()
}(item)
}
if q.Len() != 3 {
t.Errorf("q.Len() = %d, wanted %d", q.Len(), 3)
}
q.UpdateLimit(3)
for range items {
<-done
}
if !q.Empty() {
t.Errorf("q.Empty() = %t, wanted %t", q.Empty(), true)
}
wg.Wait()
if !q.Empty() {
t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), true)
}
usage := q.Quotas()
if !(usage.Used == 0 && usage.Limit == 3) {
t.Errorf("q.Quotas() = %d, %d, wanted %d, %d", usage.Used, usage.Limit, 0, 3)
}
}
func TestQueueCancel(t *testing.T) {
q := NewQuota()
q.UpdateQuotas(0, 15)
ctx, cancel := context.WithCancel(context.Background())
enqueued := make(chan struct{})
done := make(chan error)
go func() {
done <- q.AwaitQueue(ctx, 100, new(SchedItem))
}()
go func() {
for q.Empty() {
time.Sleep(100 * time.Millisecond)
}
close(enqueued)
}()
select {
case <-time.After(time.Second):
t.Fatal("q.AwaitQueue() never called, wanted one call")
case <-enqueued:
// success.
}
if q.Empty() {
t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), false)
}
cancel()
select {
case err := <-done:
if err == nil {
t.Fatalf("q.AwaitQueue() = %v, wanted error", err)
}
case <-time.After(time.Second):
t.Fatal("q.AwaitQueue() never returned, wanted return after cancel()")
}
b := q.tryPop()
if b != nil {
t.Errorf("q.tryPop() = %v, wanted %v", b, nil)
}
if !q.Empty() {
t.Errorf("q.Empty() = %v, wanted %v", q.Empty(), true)
}
usage := q.Quotas()
if !(usage.Used == 0 && usage.Limit == 15) {
t.Errorf("q.Quotas() = %d, %d, wanted %d, %d", usage.Used, usage.Limit, 0, 15)
}
}
func TestQueueToExported(t *testing.T) {
q := NewQuota()
q.UpdateLimit(10)
q.Enqueue(100, &SchedItem{IsTry: true})
q.Enqueue(100, &SchedItem{IsTry: true})
q.Enqueue(100, &SchedItem{IsTry: true})
q.Enqueue(100, &SchedItem{IsGomote: true})
q.Enqueue(100, &SchedItem{IsGomote: true})
q.Enqueue(100, &SchedItem{IsGomote: true})
q.Enqueue(100, &SchedItem{IsRelease: true})
want := &QuotaStats{
Usage: Usage{
Limit: 10,
},
Items: []ItemStats{
{Build: &SchedItem{IsRelease: true}, Cost: 100},
{Build: &SchedItem{IsGomote: true}, Cost: 100},
{Build: &SchedItem{IsGomote: true}, Cost: 100},
{Build: &SchedItem{IsGomote: true}, Cost: 100},
{Build: &SchedItem{IsTry: true}, Cost: 100},
{Build: &SchedItem{IsTry: true}, Cost: 100},
{Build: &SchedItem{IsTry: true}, Cost: 100},
},
}
got := q.ToExported()
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("q.ToExported() mismatch (-want +got):\n%s", diff)
}
}