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  }