sigs.k8s.io/kueue@v0.6.2/pkg/queue/manager_test.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package queue
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"strings"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	"github.com/google/go-cmp/cmp/cmpopts"
    29  	corev1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/util/sets"
    33  	"sigs.k8s.io/controller-runtime/pkg/client"
    34  
    35  	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
    36  	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
    37  	"sigs.k8s.io/kueue/pkg/workload"
    38  )
    39  
    40  const headsTimeout = 3 * time.Second
    41  
    42  var cmpDump = []cmp.Option{
    43  	cmpopts.SortSlices(func(a, b string) bool { return a < b }),
    44  	cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime"),
    45  }
    46  
    47  // TestAddLocalQueueOrphans verifies that pods added before adding the queue are
    48  // present when the queue is added.
    49  func TestAddLocalQueueOrphans(t *testing.T) {
    50  	kClient := utiltesting.NewFakeClient(
    51  		utiltesting.MakeWorkload("a", "earth").Queue("foo").Obj(),
    52  		utiltesting.MakeWorkload("b", "earth").Queue("bar").Obj(),
    53  		utiltesting.MakeWorkload("c", "earth").Queue("foo").Obj(),
    54  		utiltesting.MakeWorkload("d", "earth").Queue("foo").
    55  			ReserveQuota(utiltesting.MakeAdmission("cq").Obj()).Obj(),
    56  		utiltesting.MakeWorkload("a", "moon").Queue("foo").Obj(),
    57  	)
    58  	manager := NewManager(kClient, nil)
    59  	q := utiltesting.MakeLocalQueue("foo", "earth").Obj()
    60  	if err := manager.AddLocalQueue(context.Background(), q); err != nil {
    61  		t.Fatalf("Failed adding queue: %v", err)
    62  	}
    63  	qImpl := manager.localQueues[Key(q)]
    64  	workloadNames := workloadNamesFromLQ(qImpl)
    65  	if diff := cmp.Diff(sets.New("earth/a", "earth/c"), workloadNames); diff != "" {
    66  		t.Errorf("Unexpected items in queue foo (-want,+got):\n%s", diff)
    67  	}
    68  }
    69  
    70  // TestAddClusterQueueOrphans verifies that when a ClusterQueue is recreated,
    71  // it adopts the existing workloads.
    72  func TestAddClusterQueueOrphans(t *testing.T) {
    73  	scheme := runtime.NewScheme()
    74  	if err := kueue.AddToScheme(scheme); err != nil {
    75  		t.Fatalf("Failed adding kueue scheme: %v", err)
    76  	}
    77  	now := time.Now()
    78  	queues := []*kueue.LocalQueue{
    79  		utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj(),
    80  		utiltesting.MakeLocalQueue("bar", "").ClusterQueue("cq").Obj(),
    81  	}
    82  	kClient := utiltesting.NewFakeClient(
    83  		utiltesting.MakeWorkload("a", "").Queue("foo").Creation(now.Add(time.Second)).Obj(),
    84  		utiltesting.MakeWorkload("b", "").Queue("bar").Creation(now).Obj(),
    85  		utiltesting.MakeWorkload("c", "").Queue("foo").
    86  			ReserveQuota(utiltesting.MakeAdmission("cq").Obj()).Obj(),
    87  		utiltesting.MakeWorkload("d", "").Queue("baz").Obj(),
    88  		queues[0],
    89  		queues[1],
    90  	)
    91  	ctx := context.Background()
    92  	manager := NewManager(kClient, nil)
    93  	cq := utiltesting.MakeClusterQueue("cq").Obj()
    94  	if err := manager.AddClusterQueue(ctx, cq); err != nil {
    95  		t.Fatalf("Failed adding cluster queue %s: %v", cq.Name, err)
    96  	}
    97  	for _, q := range queues {
    98  		if err := manager.AddLocalQueue(ctx, q); err != nil {
    99  			t.Fatalf("Failed adding queue %s: %v", q.Name, err)
   100  		}
   101  	}
   102  
   103  	wantActiveWorkloads := map[string][]string{
   104  		"cq": {"/a", "/b"},
   105  	}
   106  	if diff := cmp.Diff(wantActiveWorkloads, manager.Dump(), cmpDump...); diff != "" {
   107  		t.Errorf("Unexpected active workloads after creating all objects (-want,+got):\n%s", diff)
   108  	}
   109  
   110  	// Recreating the ClusterQueue.
   111  	manager.DeleteClusterQueue(cq)
   112  	wantActiveWorkloads = nil
   113  	if diff := cmp.Diff(wantActiveWorkloads, manager.Dump(), cmpDump...); diff != "" {
   114  		t.Errorf("Unexpected active workloads after deleting ClusterQueue (-want,+got):\n%s", diff)
   115  	}
   116  
   117  	if err := manager.AddClusterQueue(ctx, cq); err != nil {
   118  		t.Fatalf("Could not re-add ClusterQueue: %v", err)
   119  	}
   120  	workloads := popNamesFromCQ(manager.clusterQueues["cq"])
   121  	wantWorkloads := []string{"/b", "/a"}
   122  	if diff := cmp.Diff(wantWorkloads, workloads); diff != "" {
   123  		t.Errorf("Workloads popped in the wrong order from clusterQueue:\n%s", diff)
   124  	}
   125  }
   126  
   127  // TestUpdateClusterQueue tests that a ClusterQueue transfers cohorts on update.
   128  // Inadmissible workloads should become active.
   129  func TestUpdateClusterQueue(t *testing.T) {
   130  	clusterQueues := []*kueue.ClusterQueue{
   131  		utiltesting.MakeClusterQueue("cq1").Cohort("alpha").Obj(),
   132  		utiltesting.MakeClusterQueue("cq2").Cohort("beta").Obj(),
   133  	}
   134  	queues := []*kueue.LocalQueue{
   135  		utiltesting.MakeLocalQueue("foo", defaultNamespace).ClusterQueue("cq1").Obj(),
   136  		utiltesting.MakeLocalQueue("bar", defaultNamespace).ClusterQueue("cq2").Obj(),
   137  	}
   138  	now := time.Now()
   139  	workloads := []*kueue.Workload{
   140  		utiltesting.MakeWorkload("a", defaultNamespace).Queue("foo").Creation(now.Add(time.Second)).Obj(),
   141  		utiltesting.MakeWorkload("b", defaultNamespace).Queue("bar").Creation(now).Obj(),
   142  	}
   143  	// Setup.
   144  	ctx := context.Background()
   145  	cl := utiltesting.NewFakeClient(
   146  		&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: defaultNamespace}},
   147  	)
   148  	manager := NewManager(cl, nil)
   149  	for _, cq := range clusterQueues {
   150  		if err := manager.AddClusterQueue(ctx, cq); err != nil {
   151  			t.Fatalf("Failed adding clusterQueue %s: %v", cq.Name, err)
   152  		}
   153  	}
   154  	for _, q := range queues {
   155  		if err := manager.AddLocalQueue(ctx, q); err != nil {
   156  			t.Fatalf("Failed adding queue %s: %v", q.Name, err)
   157  		}
   158  	}
   159  	// Add inadmissible workloads.
   160  	for _, w := range workloads {
   161  		if err := cl.Create(ctx, w); err != nil {
   162  			t.Fatalf("Failed adding workload to client: %v", err)
   163  		}
   164  		manager.RequeueWorkload(ctx, workload.NewInfo(w), RequeueReasonGeneric)
   165  	}
   166  
   167  	// Put cq2 in the same cohort as cq1.
   168  	clusterQueues[1].Spec.Cohort = clusterQueues[0].Spec.Cohort
   169  	if err := manager.UpdateClusterQueue(ctx, clusterQueues[1], true); err != nil {
   170  		t.Fatalf("Failed to update ClusterQueue: %v", err)
   171  	}
   172  
   173  	wantCohorts := map[string]sets.Set[string]{
   174  		"alpha": sets.New("cq1", "cq2"),
   175  	}
   176  	if diff := cmp.Diff(manager.cohorts, wantCohorts); diff != "" {
   177  		t.Errorf("Unexpected ClusterQueues in cohorts (-want,+got):\n%s", diff)
   178  	}
   179  
   180  	// Verify all workloads are active after the update.
   181  	activeWorkloads := manager.Dump()
   182  	wantActiveWorkloads := map[string][]string{
   183  		"cq1": []string{"default/a"},
   184  		"cq2": []string{"default/b"},
   185  	}
   186  	if diff := cmp.Diff(wantActiveWorkloads, activeWorkloads); diff != "" {
   187  		t.Errorf("Unexpected active workloads (-want +got):\n%s", diff)
   188  	}
   189  }
   190  
   191  // TestClusterQueueToActive tests that managers cond gets a broadcast when
   192  // a cluster queue becomes active.
   193  func TestClusterQueueToActive(t *testing.T) {
   194  	stoppedCq := utiltesting.MakeClusterQueue("cq1").Cohort("alpha").Condition(kueue.ClusterQueueActive, metav1.ConditionFalse, "ByTest", "by test").Obj()
   195  	runningCq := utiltesting.MakeClusterQueue("cq1").Cohort("alpha").Condition(kueue.ClusterQueueActive, metav1.ConditionTrue, "ByTest", "by test").Obj()
   196  	ctx := context.Background()
   197  	cl := utiltesting.NewFakeClient(
   198  		&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: defaultNamespace}},
   199  	)
   200  	manager := NewManager(cl, nil)
   201  
   202  	wgCounterStart := sync.WaitGroup{}
   203  	wgCounterStart.Add(1)
   204  	wgCounterEnd := sync.WaitGroup{}
   205  	wgCounterEnd.Add(1)
   206  	condRec := make(chan struct{})
   207  	counterCtx, counterCancel := context.WithCancel(ctx)
   208  	go func() {
   209  		manager.cond.L.Lock()
   210  		defer manager.cond.L.Unlock()
   211  		wgCounterStart.Done()
   212  		defer wgCounterEnd.Done()
   213  		manager.cond.Wait()
   214  		select {
   215  		case <-counterCtx.Done():
   216  			// the context was canceled before cond.Wait()
   217  		default:
   218  			condRec <- struct{}{}
   219  		}
   220  	}()
   221  	wgCounterStart.Wait()
   222  	go manager.CleanUpOnContext(counterCtx)
   223  
   224  	if err := manager.AddClusterQueue(ctx, stoppedCq); err != nil {
   225  		t.Fatalf("Failed adding clusterQueue %v", err)
   226  	}
   227  
   228  	if err := manager.UpdateClusterQueue(ctx, runningCq, false); err != nil {
   229  		t.Fatalf("Failed to update ClusterQueue: %v", err)
   230  	}
   231  
   232  	gotCondBeforeCleanup := false
   233  	select {
   234  	case <-condRec:
   235  		gotCondBeforeCleanup = true
   236  	case <-time.After(100 * time.Millisecond):
   237  		//nothing
   238  	}
   239  
   240  	counterCancel()
   241  	wgCounterEnd.Wait()
   242  
   243  	if !gotCondBeforeCleanup {
   244  		t.Fatalf("m.Broadcast was not called before cleanup")
   245  	}
   246  }
   247  
   248  // TestUpdateLocalQueue tests that workloads are transferred between clusterQueues
   249  // when the queue points to a different clusterQueue.
   250  func TestUpdateLocalQueue(t *testing.T) {
   251  	clusterQueues := []*kueue.ClusterQueue{
   252  		utiltesting.MakeClusterQueue("cq1").Obj(),
   253  		utiltesting.MakeClusterQueue("cq2").Obj(),
   254  	}
   255  	queues := []*kueue.LocalQueue{
   256  		utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq1").Obj(),
   257  		utiltesting.MakeLocalQueue("bar", "").ClusterQueue("cq2").Obj(),
   258  	}
   259  	now := time.Now()
   260  	workloads := []*kueue.Workload{
   261  		utiltesting.MakeWorkload("a", "").Queue("foo").Creation(now.Add(time.Second)).Obj(),
   262  		utiltesting.MakeWorkload("b", "").Queue("bar").Creation(now).Obj(),
   263  	}
   264  	// Setup.
   265  	scheme := runtime.NewScheme()
   266  	if err := kueue.AddToScheme(scheme); err != nil {
   267  		t.Fatalf("Failed adding kueue scheme: %s", err)
   268  	}
   269  	ctx := context.Background()
   270  	manager := NewManager(utiltesting.NewFakeClient(), nil)
   271  	for _, cq := range clusterQueues {
   272  		if err := manager.AddClusterQueue(ctx, cq); err != nil {
   273  			t.Fatalf("Failed adding clusterQueue %s: %v", cq.Name, err)
   274  		}
   275  	}
   276  	for _, q := range queues {
   277  		if err := manager.AddLocalQueue(ctx, q); err != nil {
   278  			t.Fatalf("Failed adding queue %s: %v", q.Name, err)
   279  		}
   280  	}
   281  	for _, w := range workloads {
   282  		manager.AddOrUpdateWorkload(w)
   283  	}
   284  
   285  	// Update cluster queue of first queue.
   286  	queues[0].Spec.ClusterQueue = "cq2"
   287  	if err := manager.UpdateLocalQueue(queues[0]); err != nil {
   288  		t.Fatalf("Failed updating queue: %v", err)
   289  	}
   290  
   291  	// Verification.
   292  	workloadOrders := make(map[string][]string)
   293  	for name, cq := range manager.clusterQueues {
   294  		workloadOrders[name] = popNamesFromCQ(cq)
   295  	}
   296  	wantWorkloadOrders := map[string][]string{
   297  		"cq1": nil,
   298  		"cq2": {"/b", "/a"},
   299  	}
   300  	if diff := cmp.Diff(wantWorkloadOrders, workloadOrders); diff != "" {
   301  		t.Errorf("workloads popped in the wrong order from clusterQueues:\n%s", diff)
   302  	}
   303  }
   304  
   305  // TestDeleteLocalQueue tests that when a LocalQueue is deleted, all its
   306  // workloads are not listed in the ClusterQueue.
   307  func TestDeleteLocalQueue(t *testing.T) {
   308  	cq := utiltesting.MakeClusterQueue("cq").Obj()
   309  	q := utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj()
   310  	wl := utiltesting.MakeWorkload("a", "").Queue("foo").Obj()
   311  
   312  	ctx := context.Background()
   313  	cl := utiltesting.NewFakeClient(wl)
   314  	manager := NewManager(cl, nil)
   315  
   316  	if err := manager.AddClusterQueue(ctx, cq); err != nil {
   317  		t.Fatalf("Could not create ClusterQueue: %v", err)
   318  	}
   319  	if err := manager.AddLocalQueue(ctx, q); err != nil {
   320  		t.Fatalf("Could not create LocalQueue: %v", err)
   321  	}
   322  
   323  	wantActiveWorkloads := map[string][]string{
   324  		"cq": {"/a"},
   325  	}
   326  	if diff := cmp.Diff(wantActiveWorkloads, manager.Dump(), cmpDump...); diff != "" {
   327  		t.Errorf("Unexpected workloads after setup (-want,+got):\n%s", diff)
   328  	}
   329  
   330  	manager.DeleteLocalQueue(q)
   331  	wantActiveWorkloads = nil
   332  	if diff := cmp.Diff(wantActiveWorkloads, manager.Dump(), cmpDump...); diff != "" {
   333  		t.Errorf("Unexpected workloads after deleting LocalQueue (-want,+got):\n%s", diff)
   334  	}
   335  }
   336  
   337  func TestAddWorkload(t *testing.T) {
   338  	manager := NewManager(utiltesting.NewFakeClient(), nil)
   339  	cq := utiltesting.MakeClusterQueue("cq").Obj()
   340  	if err := manager.AddClusterQueue(context.Background(), cq); err != nil {
   341  		t.Fatalf("Failed adding clusterQueue %s: %v", cq.Name, err)
   342  	}
   343  	queues := []*kueue.LocalQueue{
   344  		utiltesting.MakeLocalQueue("foo", "earth").ClusterQueue("cq").Obj(),
   345  		utiltesting.MakeLocalQueue("bar", "mars").Obj(),
   346  	}
   347  	for _, q := range queues {
   348  		if err := manager.AddLocalQueue(context.Background(), q); err != nil {
   349  			t.Fatalf("Failed adding queue %s: %v", q.Name, err)
   350  		}
   351  	}
   352  	cases := []struct {
   353  		workload  *kueue.Workload
   354  		wantAdded bool
   355  	}{
   356  		{
   357  			workload: &kueue.Workload{
   358  				ObjectMeta: metav1.ObjectMeta{
   359  					Namespace: "earth",
   360  					Name:      "existing_queue",
   361  				},
   362  				Spec: kueue.WorkloadSpec{QueueName: "foo"},
   363  			},
   364  			wantAdded: true,
   365  		},
   366  		{
   367  			workload: &kueue.Workload{
   368  				ObjectMeta: metav1.ObjectMeta{
   369  					Namespace: "earth",
   370  					Name:      "non_existing_queue",
   371  				},
   372  				Spec: kueue.WorkloadSpec{QueueName: "baz"},
   373  			},
   374  		},
   375  		{
   376  			workload: &kueue.Workload{
   377  				ObjectMeta: metav1.ObjectMeta{
   378  					Namespace: "mars",
   379  					Name:      "non_existing_cluster_queue",
   380  				},
   381  				Spec: kueue.WorkloadSpec{QueueName: "bar"},
   382  			},
   383  		},
   384  		{
   385  			workload: &kueue.Workload{
   386  				ObjectMeta: metav1.ObjectMeta{
   387  					Namespace: "mars",
   388  					Name:      "wrong_namespace",
   389  				},
   390  				Spec: kueue.WorkloadSpec{QueueName: "foo"},
   391  			},
   392  		},
   393  	}
   394  	for _, tc := range cases {
   395  		t.Run(tc.workload.Name, func(t *testing.T) {
   396  			if added := manager.AddOrUpdateWorkload(tc.workload); added != tc.wantAdded {
   397  				t.Errorf("AddWorkload returned %t, want %t", added, tc.wantAdded)
   398  			}
   399  		})
   400  	}
   401  }
   402  
   403  func TestStatus(t *testing.T) {
   404  	ctx := context.Background()
   405  	scheme := runtime.NewScheme()
   406  	if err := kueue.AddToScheme(scheme); err != nil {
   407  		t.Fatalf("Failed adding kueue scheme: %s", err)
   408  	}
   409  	now := time.Now().Truncate(time.Second)
   410  
   411  	queues := []kueue.LocalQueue{
   412  		{
   413  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   414  			Spec: kueue.LocalQueueSpec{
   415  				ClusterQueue: "fooCq",
   416  			},
   417  		},
   418  		{
   419  			ObjectMeta: metav1.ObjectMeta{Name: "bar"},
   420  			Spec: kueue.LocalQueueSpec{
   421  				ClusterQueue: "barCq",
   422  			},
   423  		},
   424  	}
   425  	workloads := []kueue.Workload{
   426  		{
   427  			ObjectMeta: metav1.ObjectMeta{
   428  				Name:              "a",
   429  				CreationTimestamp: metav1.NewTime(now.Add(time.Hour)),
   430  			},
   431  			Spec: kueue.WorkloadSpec{QueueName: "foo"},
   432  		},
   433  		{
   434  			ObjectMeta: metav1.ObjectMeta{
   435  				Name:              "b",
   436  				CreationTimestamp: metav1.NewTime(now),
   437  			},
   438  			Spec: kueue.WorkloadSpec{QueueName: "bar"},
   439  		},
   440  		{
   441  			ObjectMeta: metav1.ObjectMeta{
   442  				Name:              "c",
   443  				CreationTimestamp: metav1.NewTime(now),
   444  			},
   445  			Spec: kueue.WorkloadSpec{QueueName: "foo"},
   446  		},
   447  		{
   448  			ObjectMeta: metav1.ObjectMeta{
   449  				Name:              "d",
   450  				CreationTimestamp: metav1.NewTime(now),
   451  			},
   452  			Spec: kueue.WorkloadSpec{QueueName: "foo"},
   453  		},
   454  	}
   455  
   456  	manager := NewManager(utiltesting.NewFakeClient(), nil)
   457  	for _, q := range queues {
   458  		if err := manager.AddLocalQueue(ctx, &q); err != nil {
   459  			t.Errorf("Failed adding queue: %s", err)
   460  		}
   461  	}
   462  	for _, wl := range workloads {
   463  		wl := wl
   464  		manager.AddOrUpdateWorkload(&wl)
   465  	}
   466  
   467  	cases := map[string]struct {
   468  		queue      *kueue.LocalQueue
   469  		wantStatus int32
   470  		wantErr    error
   471  	}{
   472  		"foo": {
   473  			queue:      &queues[0],
   474  			wantStatus: 3,
   475  			wantErr:    nil,
   476  		},
   477  		"bar": {
   478  			queue:      &queues[1],
   479  			wantStatus: 1,
   480  			wantErr:    nil,
   481  		},
   482  		"fake": {
   483  			queue:      &kueue.LocalQueue{ObjectMeta: metav1.ObjectMeta{Name: "fake"}},
   484  			wantStatus: 0,
   485  			wantErr:    errQueueDoesNotExist,
   486  		},
   487  	}
   488  	for name, tc := range cases {
   489  		t.Run(name, func(t *testing.T) {
   490  			status, err := manager.PendingWorkloads(tc.queue)
   491  			if !errors.Is(err, tc.wantErr) {
   492  				t.Errorf("Should have failed with: %s", err)
   493  			}
   494  			if diff := cmp.Diff(tc.wantStatus, status); diff != "" {
   495  				t.Errorf("Status func returned wrong queue status: %s", diff)
   496  			}
   497  		})
   498  	}
   499  }
   500  
   501  func TestRequeueWorkloadStrictFIFO(t *testing.T) {
   502  	cq := utiltesting.MakeClusterQueue("cq").Obj()
   503  	queues := []*kueue.LocalQueue{
   504  		utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj(),
   505  		utiltesting.MakeLocalQueue("bar", "").Obj(),
   506  	}
   507  	cases := []struct {
   508  		workload     *kueue.Workload
   509  		inClient     bool
   510  		inQueue      bool
   511  		wantRequeued bool
   512  	}{
   513  		{
   514  			workload: &kueue.Workload{
   515  				ObjectMeta: metav1.ObjectMeta{Name: "existing_queue_and_obj"},
   516  				Spec:       kueue.WorkloadSpec{QueueName: "foo"},
   517  			},
   518  			inClient:     true,
   519  			wantRequeued: true,
   520  		},
   521  		{
   522  			workload: &kueue.Workload{
   523  				ObjectMeta: metav1.ObjectMeta{Name: "non_existing_queue"},
   524  				Spec:       kueue.WorkloadSpec{QueueName: "baz"},
   525  			},
   526  			inClient: true,
   527  		},
   528  		{
   529  			workload: &kueue.Workload{
   530  				ObjectMeta: metav1.ObjectMeta{Name: "non_existing_cluster_queue"},
   531  				Spec:       kueue.WorkloadSpec{QueueName: "bar"},
   532  			},
   533  			inClient: true,
   534  		},
   535  		{
   536  			workload: &kueue.Workload{
   537  				ObjectMeta: metav1.ObjectMeta{Name: "not_in_client"},
   538  				Spec:       kueue.WorkloadSpec{QueueName: "foo"},
   539  			},
   540  		},
   541  		{
   542  			workload: &kueue.Workload{
   543  				ObjectMeta: metav1.ObjectMeta{Name: "already_in_queue"},
   544  				Spec:       kueue.WorkloadSpec{QueueName: "foo"},
   545  			},
   546  			inClient: true,
   547  			inQueue:  true,
   548  		},
   549  		{
   550  			workload: &kueue.Workload{
   551  				ObjectMeta: metav1.ObjectMeta{Name: "already_admitted"},
   552  				Spec: kueue.WorkloadSpec{
   553  					QueueName: "foo",
   554  				},
   555  				Status: kueue.WorkloadStatus{
   556  					Admission: &kueue.Admission{},
   557  				},
   558  			},
   559  			inClient: true,
   560  			inQueue:  true,
   561  		},
   562  	}
   563  	for _, tc := range cases {
   564  		t.Run(tc.workload.Name, func(t *testing.T) {
   565  			cl := utiltesting.NewFakeClient()
   566  			manager := NewManager(cl, nil)
   567  			ctx := context.Background()
   568  			if err := manager.AddClusterQueue(ctx, cq); err != nil {
   569  				t.Fatalf("Failed adding cluster queue %s: %v", cq.Name, err)
   570  			}
   571  			for _, q := range queues {
   572  				if err := manager.AddLocalQueue(ctx, q); err != nil {
   573  					t.Fatalf("Failed adding queue %s: %v", q.Name, err)
   574  				}
   575  			}
   576  			// Adding workload to client after the queues are created, otherwise it
   577  			// will be in the queue.
   578  			if tc.inClient {
   579  				if err := cl.Create(ctx, tc.workload); err != nil {
   580  					t.Fatalf("Failed adding workload to client: %v", err)
   581  				}
   582  			}
   583  			if tc.inQueue {
   584  				_ = manager.AddOrUpdateWorkload(tc.workload)
   585  			}
   586  			info := workload.NewInfo(tc.workload)
   587  			if requeued := manager.RequeueWorkload(ctx, info, RequeueReasonGeneric); requeued != tc.wantRequeued {
   588  				t.Errorf("RequeueWorkload returned %t, want %t", requeued, tc.wantRequeued)
   589  			}
   590  		})
   591  	}
   592  }
   593  
   594  func TestUpdateWorkload(t *testing.T) {
   595  	scheme := runtime.NewScheme()
   596  	if err := kueue.AddToScheme(scheme); err != nil {
   597  		t.Fatalf("Failed adding kueue scheme: %s", err)
   598  	}
   599  	now := time.Now()
   600  	cases := map[string]struct {
   601  		clusterQueues    []*kueue.ClusterQueue
   602  		queues           []*kueue.LocalQueue
   603  		workloads        []*kueue.Workload
   604  		update           func(*kueue.Workload)
   605  		wantUpdated      bool
   606  		wantQueueOrder   map[string][]string
   607  		wantQueueMembers map[string]sets.Set[string]
   608  	}{
   609  		"in queue": {
   610  			clusterQueues: []*kueue.ClusterQueue{
   611  				utiltesting.MakeClusterQueue("cq").Obj(),
   612  			},
   613  			queues: []*kueue.LocalQueue{
   614  				utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj(),
   615  			},
   616  			workloads: []*kueue.Workload{
   617  				utiltesting.MakeWorkload("a", "").Queue("foo").Creation(now).Obj(),
   618  				utiltesting.MakeWorkload("b", "").Queue("foo").Creation(now.Add(time.Second)).Obj(),
   619  			},
   620  			update: func(w *kueue.Workload) {
   621  				w.CreationTimestamp = metav1.NewTime(now.Add(time.Minute))
   622  			},
   623  			wantUpdated: true,
   624  			wantQueueOrder: map[string][]string{
   625  				"cq": {"/b", "/a"},
   626  			},
   627  			wantQueueMembers: map[string]sets.Set[string]{
   628  				"/foo": sets.New("/a", "/b"),
   629  			},
   630  		},
   631  		"between queues": {
   632  			clusterQueues: []*kueue.ClusterQueue{
   633  				utiltesting.MakeClusterQueue("cq").Obj(),
   634  			},
   635  			queues: []*kueue.LocalQueue{
   636  				utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj(),
   637  				utiltesting.MakeLocalQueue("bar", "").ClusterQueue("cq").Obj(),
   638  			},
   639  			workloads: []*kueue.Workload{
   640  				utiltesting.MakeWorkload("a", "").Queue("foo").Obj(),
   641  			},
   642  			update: func(w *kueue.Workload) {
   643  				w.Spec.QueueName = "bar"
   644  			},
   645  			wantUpdated: true,
   646  			wantQueueOrder: map[string][]string{
   647  				"cq": {"/a"},
   648  			},
   649  			wantQueueMembers: map[string]sets.Set[string]{
   650  				"/foo": nil,
   651  				"/bar": sets.New("/a"),
   652  			},
   653  		},
   654  		"between cluster queues": {
   655  			clusterQueues: []*kueue.ClusterQueue{
   656  				utiltesting.MakeClusterQueue("cq1").Obj(),
   657  				utiltesting.MakeClusterQueue("cq2").Obj(),
   658  			},
   659  			queues: []*kueue.LocalQueue{
   660  				utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq1").Obj(),
   661  				utiltesting.MakeLocalQueue("bar", "").ClusterQueue("cq2").Obj(),
   662  			},
   663  			workloads: []*kueue.Workload{
   664  				utiltesting.MakeWorkload("a", "").Queue("foo").Obj(),
   665  			},
   666  			update: func(w *kueue.Workload) {
   667  				w.Spec.QueueName = "bar"
   668  			},
   669  			wantUpdated: true,
   670  			wantQueueOrder: map[string][]string{
   671  				"cq1": nil,
   672  				"cq2": {"/a"},
   673  			},
   674  			wantQueueMembers: map[string]sets.Set[string]{
   675  				"/foo": nil,
   676  				"/bar": sets.New("/a"),
   677  			},
   678  		},
   679  		"to non existent queue": {
   680  			clusterQueues: []*kueue.ClusterQueue{
   681  				utiltesting.MakeClusterQueue("cq").Obj(),
   682  			},
   683  			queues: []*kueue.LocalQueue{
   684  				utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj(),
   685  			},
   686  			workloads: []*kueue.Workload{
   687  				utiltesting.MakeWorkload("a", "").Queue("foo").Obj(),
   688  			},
   689  			update: func(w *kueue.Workload) {
   690  				w.Spec.QueueName = "bar"
   691  			},
   692  			wantQueueOrder: map[string][]string{
   693  				"cq": nil,
   694  			},
   695  			wantQueueMembers: map[string]sets.Set[string]{
   696  				"/foo": nil,
   697  			},
   698  		},
   699  		"from non existing queue": {
   700  			clusterQueues: []*kueue.ClusterQueue{
   701  				utiltesting.MakeClusterQueue("cq").Obj(),
   702  			},
   703  			queues: []*kueue.LocalQueue{
   704  				utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj(),
   705  			},
   706  			workloads: []*kueue.Workload{
   707  				utiltesting.MakeWorkload("a", "").Queue("bar").Obj(),
   708  			},
   709  			update: func(w *kueue.Workload) {
   710  				w.Spec.QueueName = "foo"
   711  			},
   712  			wantUpdated: true,
   713  			wantQueueOrder: map[string][]string{
   714  				"cq": {"/a"},
   715  			},
   716  			wantQueueMembers: map[string]sets.Set[string]{
   717  				"/foo": sets.New("/a"),
   718  			},
   719  		},
   720  	}
   721  	for name, tc := range cases {
   722  		t.Run(name, func(t *testing.T) {
   723  			manager := NewManager(utiltesting.NewFakeClient(), nil)
   724  			ctx := context.Background()
   725  			for _, cq := range tc.clusterQueues {
   726  				if err := manager.AddClusterQueue(ctx, cq); err != nil {
   727  					t.Fatalf("Adding cluster queue %s: %v", cq.Name, err)
   728  				}
   729  			}
   730  			for _, q := range tc.queues {
   731  				if err := manager.AddLocalQueue(ctx, q); err != nil {
   732  					t.Fatalf("Adding queue %q: %v", q.Name, err)
   733  				}
   734  			}
   735  			for _, w := range tc.workloads {
   736  				manager.AddOrUpdateWorkload(w)
   737  			}
   738  			wl := tc.workloads[0].DeepCopy()
   739  			tc.update(wl)
   740  			if updated := manager.UpdateWorkload(tc.workloads[0], wl); updated != tc.wantUpdated {
   741  				t.Errorf("UpdatedWorkload returned %t, want %t", updated, tc.wantUpdated)
   742  			}
   743  			q := manager.localQueues[workload.QueueKey(wl)]
   744  			if q != nil {
   745  				key := workload.Key(wl)
   746  				item := q.items[key]
   747  				if item == nil {
   748  					t.Errorf("Object not stored in queue")
   749  				} else if diff := cmp.Diff(wl, item.Obj); diff != "" {
   750  					t.Errorf("Object stored in queue differs (-want,+got):\n%s", diff)
   751  				}
   752  				cq := manager.clusterQueues[q.ClusterQueue]
   753  				if cq != nil {
   754  					item := cq.Info(key)
   755  					if item == nil {
   756  						t.Errorf("Object not stored in clusterQueue")
   757  					} else if diff := cmp.Diff(wl, item.Obj); diff != "" {
   758  						t.Errorf("Object stored in clusterQueue differs (-want,+got):\n%s", diff)
   759  					}
   760  				}
   761  			}
   762  			queueOrder := make(map[string][]string)
   763  			for name, cq := range manager.clusterQueues {
   764  				queueOrder[name] = popNamesFromCQ(cq)
   765  			}
   766  			if diff := cmp.Diff(tc.wantQueueOrder, queueOrder); diff != "" {
   767  				t.Errorf("Elements popped in the wrong order from clusterQueues (-want,+got):\n%s", diff)
   768  			}
   769  			queueMembers := make(map[string]sets.Set[string])
   770  			for name, q := range manager.localQueues {
   771  				queueMembers[name] = workloadNamesFromLQ(q)
   772  			}
   773  			if diff := cmp.Diff(tc.wantQueueMembers, queueMembers); diff != "" {
   774  				t.Errorf("Elements present in wrong queues (-want,+got):\n%s", diff)
   775  			}
   776  		})
   777  	}
   778  }
   779  
   780  func TestHeads(t *testing.T) {
   781  	scheme := runtime.NewScheme()
   782  	if err := kueue.AddToScheme(scheme); err != nil {
   783  		t.Fatalf("Failed adding kueue scheme: %s", err)
   784  	}
   785  	now := time.Now().Truncate(time.Second)
   786  
   787  	clusterQueues := []*kueue.ClusterQueue{
   788  		utiltesting.MakeClusterQueue("active-fooCq").Obj(),
   789  		utiltesting.MakeClusterQueue("active-barCq").Obj(),
   790  		utiltesting.MakeClusterQueue("pending-bazCq").Obj(),
   791  	}
   792  	queues := []*kueue.LocalQueue{
   793  		utiltesting.MakeLocalQueue("foo", "").ClusterQueue("active-fooCq").Obj(),
   794  		utiltesting.MakeLocalQueue("bar", "").ClusterQueue("active-barCq").Obj(),
   795  		utiltesting.MakeLocalQueue("baz", "").ClusterQueue("pending-bazCq").Obj(),
   796  	}
   797  	tests := []struct {
   798  		name          string
   799  		workloads     []*kueue.Workload
   800  		wantWorkloads sets.Set[string]
   801  	}{
   802  		{
   803  			name:          "empty clusterQueues",
   804  			workloads:     []*kueue.Workload{},
   805  			wantWorkloads: sets.Set[string]{},
   806  		},
   807  		{
   808  			name: "active clusterQueues",
   809  			workloads: []*kueue.Workload{
   810  				utiltesting.MakeWorkload("a", "").Creation(now).Queue("foo").Obj(),
   811  				utiltesting.MakeWorkload("b", "").Creation(now).Queue("bar").Obj(),
   812  			},
   813  			wantWorkloads: sets.New("a", "b"),
   814  		},
   815  		{
   816  			name: "active clusterQueues with multiple workloads",
   817  			workloads: []*kueue.Workload{
   818  				utiltesting.MakeWorkload("a1", "").Creation(now).Queue("foo").Obj(),
   819  				utiltesting.MakeWorkload("a2", "").Creation(now.Add(time.Hour)).Queue("foo").Obj(),
   820  				utiltesting.MakeWorkload("b", "").Creation(now).Queue("bar").Obj(),
   821  			},
   822  			wantWorkloads: sets.New("a1", "b"),
   823  		},
   824  		{
   825  			name: "inactive clusterQueues",
   826  			workloads: []*kueue.Workload{
   827  				utiltesting.MakeWorkload("a", "").Creation(now).Queue("foo").Obj(),
   828  				utiltesting.MakeWorkload("b", "").Creation(now).Queue("bar").Obj(),
   829  				utiltesting.MakeWorkload("c", "").Creation(now.Add(time.Hour)).Queue("baz").Obj(),
   830  			},
   831  			wantWorkloads: sets.New("a", "b"),
   832  		},
   833  	}
   834  	for _, tc := range tests {
   835  		t.Run(tc.name, func(t *testing.T) {
   836  			ctx, cancel := context.WithTimeout(context.Background(), headsTimeout)
   837  			defer cancel()
   838  			fakeC := &fakeStatusChecker{}
   839  			manager := NewManager(utiltesting.NewFakeClient(), fakeC)
   840  			for _, cq := range clusterQueues {
   841  				if err := manager.AddClusterQueue(ctx, cq); err != nil {
   842  					t.Fatalf("Failed adding clusterQueue %s to manager: %v", cq.Name, err)
   843  				}
   844  			}
   845  			for _, q := range queues {
   846  				if err := manager.AddLocalQueue(ctx, q); err != nil {
   847  					t.Fatalf("Failed adding queue %s: %s", q.Name, err)
   848  				}
   849  			}
   850  
   851  			go manager.CleanUpOnContext(ctx)
   852  			for _, wl := range tc.workloads {
   853  				manager.AddOrUpdateWorkload(wl)
   854  			}
   855  
   856  			wlNames := sets.New[string]()
   857  			heads := manager.Heads(ctx)
   858  			for _, h := range heads {
   859  				wlNames.Insert(h.Obj.Name)
   860  			}
   861  			if diff := cmp.Diff(tc.wantWorkloads, wlNames); diff != "" {
   862  				t.Errorf("GetHeads returned wrong heads (-want,+got):\n%s", diff)
   863  			}
   864  		})
   865  	}
   866  }
   867  
   868  var ignoreTypeMeta = cmpopts.IgnoreTypes(metav1.TypeMeta{})
   869  
   870  // TestHeadAsync ensures that Heads call is blocked until the queues are filled
   871  // asynchronously.
   872  func TestHeadsAsync(t *testing.T) {
   873  	now := time.Now().Truncate(time.Second)
   874  	clusterQueues := []*kueue.ClusterQueue{
   875  		utiltesting.MakeClusterQueue("fooCq").Obj(),
   876  		utiltesting.MakeClusterQueue("barCq").Obj(),
   877  	}
   878  	wl := kueue.Workload{
   879  		ObjectMeta: metav1.ObjectMeta{
   880  			Name:              "a",
   881  			CreationTimestamp: metav1.NewTime(now),
   882  		},
   883  		Spec: kueue.WorkloadSpec{QueueName: "foo"},
   884  	}
   885  	var newWl kueue.Workload
   886  	queues := []kueue.LocalQueue{
   887  		{
   888  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   889  			Spec: kueue.LocalQueueSpec{
   890  				ClusterQueue: "fooCq",
   891  			},
   892  		},
   893  		{
   894  			ObjectMeta: metav1.ObjectMeta{Name: "bar"},
   895  			Spec: kueue.LocalQueueSpec{
   896  				ClusterQueue: "barCq",
   897  			},
   898  		},
   899  	}
   900  	cases := map[string]struct {
   901  		initialObjs []client.Object
   902  		op          func(context.Context, *Manager)
   903  		wantHeads   []workload.Info
   904  	}{
   905  		"AddClusterQueue": {
   906  			initialObjs: []client.Object{&wl, &queues[0]},
   907  			op: func(ctx context.Context, mgr *Manager) {
   908  				if err := mgr.AddLocalQueue(ctx, &queues[0]); err != nil {
   909  					t.Errorf("Failed adding queue: %s", err)
   910  				}
   911  				mgr.AddOrUpdateWorkload(&wl)
   912  				go func() {
   913  					if err := mgr.AddClusterQueue(ctx, clusterQueues[0]); err != nil {
   914  						t.Errorf("Failed adding clusterQueue: %v", err)
   915  					}
   916  				}()
   917  			},
   918  			wantHeads: []workload.Info{
   919  				{
   920  					Obj:          &wl,
   921  					ClusterQueue: "fooCq",
   922  				},
   923  			},
   924  		},
   925  		"AddLocalQueue": {
   926  			initialObjs: []client.Object{&wl},
   927  			op: func(ctx context.Context, mgr *Manager) {
   928  				if err := mgr.AddClusterQueue(ctx, clusterQueues[0]); err != nil {
   929  					t.Errorf("Failed adding clusterQueue: %v", err)
   930  				}
   931  				go func() {
   932  					if err := mgr.AddLocalQueue(ctx, &queues[0]); err != nil {
   933  						t.Errorf("Failed adding queue: %s", err)
   934  					}
   935  				}()
   936  			},
   937  			wantHeads: []workload.Info{
   938  				{
   939  					Obj:          &wl,
   940  					ClusterQueue: "fooCq",
   941  				},
   942  			},
   943  		},
   944  		"AddWorkload": {
   945  			op: func(ctx context.Context, mgr *Manager) {
   946  				if err := mgr.AddClusterQueue(ctx, clusterQueues[0]); err != nil {
   947  					t.Errorf("Failed adding clusterQueue: %v", err)
   948  				}
   949  				if err := mgr.AddLocalQueue(ctx, &queues[0]); err != nil {
   950  					t.Errorf("Failed adding queue: %s", err)
   951  				}
   952  				go func() {
   953  					mgr.AddOrUpdateWorkload(&wl)
   954  				}()
   955  			},
   956  			wantHeads: []workload.Info{
   957  				{
   958  					Obj:          &wl,
   959  					ClusterQueue: "fooCq",
   960  				},
   961  			},
   962  		},
   963  		"UpdateWorkload": {
   964  			op: func(ctx context.Context, mgr *Manager) {
   965  				if err := mgr.AddClusterQueue(ctx, clusterQueues[0]); err != nil {
   966  					t.Errorf("Failed adding clusterQueue: %v", err)
   967  				}
   968  				if err := mgr.AddLocalQueue(ctx, &queues[0]); err != nil {
   969  					t.Errorf("Failed adding queue: %s", err)
   970  				}
   971  				go func() {
   972  					wlCopy := wl.DeepCopy()
   973  					wlCopy.ResourceVersion = "old"
   974  					mgr.UpdateWorkload(wlCopy, &wl)
   975  				}()
   976  			},
   977  			wantHeads: []workload.Info{
   978  				{
   979  					Obj:          &wl,
   980  					ClusterQueue: "fooCq",
   981  				},
   982  			},
   983  		},
   984  		"RequeueWorkload": {
   985  			initialObjs: []client.Object{&wl},
   986  			op: func(ctx context.Context, mgr *Manager) {
   987  				if err := mgr.AddClusterQueue(ctx, clusterQueues[0]); err != nil {
   988  					t.Errorf("Failed adding clusterQueue: %v", err)
   989  				}
   990  				if err := mgr.AddLocalQueue(ctx, &queues[0]); err != nil {
   991  					t.Errorf("Failed adding queue: %s", err)
   992  				}
   993  				// Remove the initial workload from the manager.
   994  				mgr.Heads(ctx)
   995  				go func() {
   996  					mgr.RequeueWorkload(ctx, workload.NewInfo(&wl), RequeueReasonFailedAfterNomination)
   997  				}()
   998  			},
   999  			wantHeads: []workload.Info{
  1000  				{
  1001  					Obj:          &wl,
  1002  					ClusterQueue: "fooCq",
  1003  				},
  1004  			},
  1005  		},
  1006  		"RequeueWithOutOfDateWorkload": {
  1007  			initialObjs: []client.Object{&wl},
  1008  			op: func(ctx context.Context, mgr *Manager) {
  1009  				if err := mgr.AddClusterQueue(ctx, clusterQueues[0]); err != nil {
  1010  					t.Errorf("Failed adding clusterQueue: %v", err)
  1011  				}
  1012  				if err := mgr.AddLocalQueue(ctx, &queues[0]); err != nil {
  1013  					t.Errorf("Failed adding queue: %s", err)
  1014  				}
  1015  
  1016  				newWl = wl
  1017  				newWl.Annotations = map[string]string{"foo": "bar"}
  1018  				if err := mgr.client.Update(ctx, &newWl, &client.UpdateOptions{}); err != nil {
  1019  					t.Errorf("Failed to update the workload; %s", err)
  1020  				}
  1021  				// Remove the initial workload from the manager.
  1022  				mgr.Heads(ctx)
  1023  				go func() {
  1024  					mgr.RequeueWorkload(ctx, workload.NewInfo(&wl), RequeueReasonFailedAfterNomination)
  1025  				}()
  1026  			},
  1027  			wantHeads: []workload.Info{
  1028  				{
  1029  					Obj:          &newWl,
  1030  					ClusterQueue: "fooCq",
  1031  				},
  1032  			},
  1033  		},
  1034  		"RequeueWithQueueChangedWorkload": {
  1035  			initialObjs: []client.Object{&wl},
  1036  			op: func(ctx context.Context, mgr *Manager) {
  1037  				for _, cq := range clusterQueues {
  1038  					if err := mgr.AddClusterQueue(ctx, cq); err != nil {
  1039  						t.Errorf("Failed adding clusterQueue: %v", err)
  1040  					}
  1041  				}
  1042  				for _, q := range queues {
  1043  					if err := mgr.AddLocalQueue(ctx, &q); err != nil {
  1044  						t.Errorf("Failed adding queue: %s", err)
  1045  					}
  1046  				}
  1047  
  1048  				newWl = wl
  1049  				newWl.Spec.QueueName = "bar"
  1050  				if err := mgr.client.Update(ctx, &newWl, &client.UpdateOptions{}); err != nil {
  1051  					t.Errorf("Failed to update the workload; %s", err)
  1052  				}
  1053  				// Remove the initial workload from the manager.
  1054  				mgr.Heads(ctx)
  1055  				go func() {
  1056  					mgr.RequeueWorkload(ctx, workload.NewInfo(&wl), RequeueReasonFailedAfterNomination)
  1057  				}()
  1058  			},
  1059  			wantHeads: []workload.Info{
  1060  				{
  1061  					Obj:          &newWl,
  1062  					ClusterQueue: "barCq",
  1063  				},
  1064  			},
  1065  		},
  1066  	}
  1067  	for name, tc := range cases {
  1068  		t.Run(name, func(t *testing.T) {
  1069  			ctx, cancel := context.WithTimeout(context.Background(), headsTimeout)
  1070  			defer cancel()
  1071  			client := utiltesting.NewFakeClient(tc.initialObjs...)
  1072  			manager := NewManager(client, nil)
  1073  			go manager.CleanUpOnContext(ctx)
  1074  			tc.op(ctx, manager)
  1075  			heads := manager.Heads(ctx)
  1076  			if diff := cmp.Diff(tc.wantHeads, heads, ignoreTypeMeta); diff != "" {
  1077  				t.Errorf("GetHeads returned wrong heads (-want,+got):\n%s", diff)
  1078  			}
  1079  		})
  1080  	}
  1081  }
  1082  
  1083  // TestHeadsCancelled ensures that the Heads call returns when the context is closed.
  1084  func TestHeadsCancelled(t *testing.T) {
  1085  	manager := NewManager(utiltesting.NewFakeClient(), nil)
  1086  	ctx, cancel := context.WithCancel(context.Background())
  1087  	go func() {
  1088  		cancel()
  1089  	}()
  1090  	manager.CleanUpOnContext(ctx)
  1091  	heads := manager.Heads(ctx)
  1092  	if len(heads) != 0 {
  1093  		t.Errorf("GetHeads returned elements, expected none")
  1094  	}
  1095  }
  1096  
  1097  // popNamesFromCQ pops all the workloads from the clusterQueue and returns
  1098  // the keyed names in the order they are popped.
  1099  func popNamesFromCQ(cq ClusterQueue) []string {
  1100  	var names []string
  1101  	for w := cq.Pop(); w != nil; w = cq.Pop() {
  1102  		names = append(names, workload.Key(w.Obj))
  1103  	}
  1104  	return names
  1105  }
  1106  
  1107  // workloadNamesFromLQ returns all the names of the workloads in a localQueue.
  1108  func workloadNamesFromLQ(q *LocalQueue) sets.Set[string] {
  1109  	names := sets.New[string]()
  1110  	for k := range q.items {
  1111  		names.Insert(k)
  1112  	}
  1113  	return names
  1114  }
  1115  
  1116  type fakeStatusChecker struct{}
  1117  
  1118  func (c *fakeStatusChecker) ClusterQueueActive(name string) bool {
  1119  	return strings.Contains(name, "active-")
  1120  }
  1121  
  1122  func TestGetPendingWorkloadsInfo(t *testing.T) {
  1123  	now := time.Now().Truncate(time.Second)
  1124  
  1125  	clusterQueues := []*kueue.ClusterQueue{
  1126  		utiltesting.MakeClusterQueue("cq").Obj(),
  1127  	}
  1128  	queues := []*kueue.LocalQueue{
  1129  		utiltesting.MakeLocalQueue("foo", "").ClusterQueue("cq").Obj(),
  1130  	}
  1131  	workloads := []*kueue.Workload{
  1132  		utiltesting.MakeWorkload("a", "").Queue("foo").Creation(now).Obj(),
  1133  		utiltesting.MakeWorkload("b", "").Queue("foo").Creation(now.Add(time.Second)).Obj(),
  1134  	}
  1135  
  1136  	// Setup.
  1137  	scheme := runtime.NewScheme()
  1138  	if err := kueue.AddToScheme(scheme); err != nil {
  1139  		t.Fatalf("Failed adding kueue scheme: %s", err)
  1140  	}
  1141  	ctx := context.Background()
  1142  	manager := NewManager(utiltesting.NewFakeClient(), nil)
  1143  	for _, cq := range clusterQueues {
  1144  		if err := manager.AddClusterQueue(ctx, cq); err != nil {
  1145  			t.Fatalf("Failed adding clusterQueue %s: %v", cq.Name, err)
  1146  		}
  1147  	}
  1148  	for _, q := range queues {
  1149  		if err := manager.AddLocalQueue(ctx, q); err != nil {
  1150  			t.Fatalf("Failed adding queue %s: %v", q.Name, err)
  1151  		}
  1152  	}
  1153  	for _, w := range workloads {
  1154  		manager.AddOrUpdateWorkload(w)
  1155  	}
  1156  
  1157  	cases := map[string]struct {
  1158  		cqName                   string
  1159  		wantPendingWorkloadsInfo []*workload.Info
  1160  	}{
  1161  		"Invalid ClusterQueue name": {
  1162  			cqName:                   "invalid",
  1163  			wantPendingWorkloadsInfo: nil,
  1164  		},
  1165  		"ClusterQueue with 2 pending workloads": {
  1166  			cqName: "cq",
  1167  			wantPendingWorkloadsInfo: []*workload.Info{
  1168  				{
  1169  					Obj: &kueue.Workload{
  1170  						ObjectMeta: metav1.ObjectMeta{
  1171  							Name:      "a",
  1172  							Namespace: "",
  1173  						},
  1174  						Spec: kueue.WorkloadSpec{
  1175  							QueueName: "foo",
  1176  						},
  1177  					},
  1178  				},
  1179  				{
  1180  					Obj: &kueue.Workload{
  1181  						ObjectMeta: metav1.ObjectMeta{
  1182  							Name:      "b",
  1183  							Namespace: "",
  1184  						},
  1185  						Spec: kueue.WorkloadSpec{
  1186  							QueueName: "foo",
  1187  						},
  1188  					},
  1189  				},
  1190  			},
  1191  		},
  1192  	}
  1193  	for name, tc := range cases {
  1194  		t.Run(name, func(t *testing.T) {
  1195  			pendingWorkloadsInfo := manager.PendingWorkloadsInfo(tc.cqName)
  1196  			if diff := cmp.Diff(tc.wantPendingWorkloadsInfo, pendingWorkloadsInfo,
  1197  				ignoreTypeMeta,
  1198  				cmpopts.IgnoreFields(metav1.ObjectMeta{}, "CreationTimestamp"),
  1199  				cmpopts.IgnoreFields(kueue.WorkloadSpec{}, "PodSets"),
  1200  				cmpopts.IgnoreFields(workload.Info{}, "TotalRequests"),
  1201  			); diff != "" {
  1202  				t.Errorf("GetPendingWorkloadsInfo returned wrong heads (-want,+got):\n%s", diff)
  1203  			}
  1204  		})
  1205  	}
  1206  }