knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/configmap/informer/synced_callback_test.go (about)

     1  /*
     2  Copyright 2021 The Knative 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 informer
    18  
    19  import (
    20  	"testing"
    21  	"time"
    22  )
    23  
    24  func TestNamedWaitGroup(t *testing.T) {
    25  	nwg := newNamedWaitGroup()
    26  
    27  	// nothing has been added so wait returns immediately
    28  	initiallyDone := make(chan struct{})
    29  	go func() {
    30  		defer close(initiallyDone)
    31  		nwg.Wait()
    32  	}()
    33  	select {
    34  	case <-time.After(1 * time.Second):
    35  		t.Fatalf("Wait should have returned immediately but still hadn't after timeout elapsed")
    36  	case <-initiallyDone:
    37  		// the Wait returned as expected since nothing was tracked
    38  	}
    39  
    40  	// Add some keys to track
    41  	nwg.Add("foo")
    42  	nwg.Add("bar")
    43  	// Adding keys multiple times shouldn't increment the counter again
    44  	nwg.Add("bar")
    45  
    46  	// Now that we've added keys, when we Wait, it should block
    47  	done := make(chan struct{})
    48  	go func() {
    49  		defer close(done)
    50  		nwg.Wait()
    51  	}()
    52  
    53  	// Indicate that this key is done
    54  	nwg.Done("foo")
    55  	// Indicating done on a key that doesn't exist should do nothing
    56  	nwg.Done("doesnt exist")
    57  
    58  	// Only one of the tracked keys has completed, so the channel should not yet have closed
    59  	select {
    60  	case <-done:
    61  		t.Fatalf("Wait returned before all keys were done")
    62  	default:
    63  		// as expected, the channel is still open (waiting for the final key to be done)
    64  	}
    65  
    66  	// Indicate the final key is done
    67  	nwg.Done("bar")
    68  
    69  	// Now that all keys are done, the Wait should return
    70  	select {
    71  	case <-time.After(1 * time.Second):
    72  		t.Fatalf("Wait should have returned immediately but still hadn't after timeout elapsed")
    73  	case <-done:
    74  		// completed successfully
    75  	}
    76  }
    77  
    78  func TestSyncedCallback(t *testing.T) {
    79  	keys := []string{"foo", "bar"}
    80  	objs := []interface{}{"fooobj", "barobj"}
    81  	var seen []interface{}
    82  	callback := func(obj interface{}) {
    83  		seen = append(seen, obj)
    84  	}
    85  	sc := newSyncedCallback(keys, callback)
    86  
    87  	// Wait for the callback to be called for all of the keys
    88  	stopCh := make(chan struct{})
    89  	done := make(chan struct{})
    90  	go func() {
    91  		defer close(done)
    92  		sc.WaitForAllKeys(stopCh)
    93  	}()
    94  
    95  	// Call the callback for one of the keys
    96  	sc.Call(objs[0], "foo")
    97  
    98  	// Only one of the tracked keys has been synced so we should still be waiting
    99  	select {
   100  	case <-done:
   101  		t.Fatalf("Wait returned before all keys were done")
   102  	default:
   103  		// as expected, the channel is still open (waiting for the final key to be done)
   104  	}
   105  
   106  	// Call the callback for the other key
   107  	sc.Call(objs[1], "bar")
   108  
   109  	// Now that all keys are done, the Wait should return
   110  	select {
   111  	case <-time.After(1 * time.Second):
   112  		t.Fatalf("WaitForAllKeys should have returned but still hadn't after timeout elapsed")
   113  	case <-done:
   114  		// completed successfully
   115  	}
   116  
   117  	if len(seen) != 2 || seen[0] != objs[0] || seen[1] != objs[1] {
   118  		t.Errorf("callback wasn't called as expected, expected to see %v but saw %v", objs, seen)
   119  	}
   120  }
   121  
   122  func TestSyncedCallbackStops(t *testing.T) {
   123  	sc := newSyncedCallback([]string{"somekey"}, func(obj interface{}) {})
   124  
   125  	// Wait for the callback to be called - which it won't be!
   126  	stopCh := make(chan struct{})
   127  	done := make(chan struct{})
   128  	go func() {
   129  		defer close(done)
   130  		sc.WaitForAllKeys(stopCh)
   131  	}()
   132  
   133  	// Nothing has been synced so we should still be waiting
   134  	select {
   135  	case <-done:
   136  		t.Fatalf("Wait returned before all keys were done")
   137  	default:
   138  		// as expected, the channel is still open
   139  	}
   140  
   141  	// signal to stop via the stop channel
   142  	close(stopCh)
   143  
   144  	// Even though the callback wasn't called, the Wait should return b/c of the stop channel
   145  	select {
   146  	case <-time.After(1 * time.Second):
   147  		t.Fatalf("WaitForAllKeys should have returned because of the stop channel but still hadn't after timeout elapsed")
   148  	case <-done:
   149  		// stopped successfully
   150  	}
   151  }