github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/structs/broadcaster_test.go (about) 1 package structs 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 "github.com/hashicorp/nomad/ci" 9 "github.com/hashicorp/nomad/helper/testlog" 10 "github.com/hashicorp/nomad/nomad/mock" 11 "github.com/stretchr/testify/require" 12 ) 13 14 // TestAllocBroadcaster_SendRecv asserts the latest sends to a broadcaster are 15 // received by listeners. 16 func TestAllocBroadcaster_SendRecv(t *testing.T) { 17 ci.Parallel(t) 18 19 b := NewAllocBroadcaster(testlog.HCLogger(t)) 20 defer b.Close() 21 22 // Create a listener and assert it blocks until an update 23 l := b.Listen() 24 defer l.Close() 25 select { 26 case <-l.Ch(): 27 t.Fatalf("unexpected initial alloc") 28 case <-time.After(10 * time.Millisecond): 29 // Ok! Ch is empty until a Send 30 } 31 32 // Send an update 33 alloc := mock.Alloc() 34 alloc.AllocModifyIndex = 10 35 require.NoError(t, b.Send(alloc.Copy())) 36 recvd := <-l.Ch() 37 require.Equal(t, alloc.AllocModifyIndex, recvd.AllocModifyIndex) 38 39 // Send two now copies and assert only the last was received 40 alloc.AllocModifyIndex = 30 41 require.NoError(t, b.Send(alloc.Copy())) 42 alloc.AllocModifyIndex = 40 43 require.NoError(t, b.Send(alloc.Copy())) 44 45 recvd = <-l.Ch() 46 require.Equal(t, alloc.AllocModifyIndex, recvd.AllocModifyIndex) 47 } 48 49 // TestAllocBroadcaster_RecvBlocks asserts listeners are blocked until a send occurs. 50 func TestAllocBroadcaster_RecvBlocks(t *testing.T) { 51 ci.Parallel(t) 52 53 alloc := mock.Alloc() 54 b := NewAllocBroadcaster(testlog.HCLogger(t)) 55 defer b.Close() 56 57 l1 := b.Listen() 58 defer l1.Close() 59 60 l2 := b.Listen() 61 defer l2.Close() 62 63 done := make(chan int, 2) 64 65 // Subsequent listens should block until a subsequent send 66 go func() { 67 <-l1.Ch() 68 done <- 1 69 }() 70 71 go func() { 72 <-l2.Ch() 73 done <- 1 74 }() 75 76 select { 77 case <-done: 78 t.Fatalf("unexpected receive by a listener") 79 case <-time.After(10 * time.Millisecond): 80 } 81 82 // Do a Send and expect both listeners to receive it 83 b.Send(alloc) 84 <-done 85 <-done 86 } 87 88 // TestAllocBroadcaster_Concurrency asserts that the broadcaster behaves 89 // correctly with concurrent listeners being added and closed. 90 func TestAllocBroadcaster_Concurrency(t *testing.T) { 91 ci.Parallel(t) 92 93 alloc := mock.Alloc() 94 b := NewAllocBroadcaster(testlog.HCLogger(t)) 95 defer b.Close() 96 97 errs := make(chan error, 10) 98 listeners := make([]*AllocListener, 10) 99 for i := 0; i < len(listeners); i++ { 100 l := b.Listen() 101 defer l.Close() 102 103 listeners[i] = l 104 go func(index uint64, listener *AllocListener) { 105 defer listener.Close() 106 for { 107 a, ok := <-listener.Ch() 108 if !ok { 109 return 110 } 111 112 if a.AllocModifyIndex < index { 113 errs <- fmt.Errorf("index=%d < %d", a.AllocModifyIndex, index) 114 return 115 } 116 index = a.AllocModifyIndex 117 } 118 }(alloc.AllocModifyIndex, l) 119 } 120 121 for i := 0; i < 100; i++ { 122 alloc.AllocModifyIndex++ 123 require.NoError(t, b.Send(alloc.Copy())) 124 } 125 126 if len(errs) > 0 { 127 t.Fatalf("%d listener errors. First error:\n%v", len(errs), <-errs) 128 } 129 130 // Closing a couple shouldn't cause errors 131 listeners[0].Close() 132 listeners[1].Close() 133 134 for i := 0; i < 100; i++ { 135 alloc.AllocModifyIndex++ 136 require.NoError(t, b.Send(alloc.Copy())) 137 } 138 139 if len(errs) > 0 { 140 t.Fatalf("%d listener errors. First error:\n%v", len(errs), <-errs) 141 } 142 143 // Closing the broadcaster *should* error 144 b.Close() 145 require.Equal(t, ErrAllocBroadcasterClosed, b.Send(alloc)) 146 147 // All Listeners should be closed 148 for _, l := range listeners { 149 select { 150 case _, ok := <-l.Ch(): 151 if ok { 152 // This check can beat the goroutine above to 153 // recv'ing the final update. Listener must be 154 // closed on next recv. 155 if _, ok := <-l.Ch(); ok { 156 t.Fatalf("expected listener to be closed") 157 } 158 } 159 default: 160 t.Fatalf("expected listener to be closed; not blocking") 161 } 162 } 163 } 164 165 // TestAllocBroadcaster_PrimeListener asserts that newly created listeners are 166 // primed with the last sent alloc. 167 func TestAllocBroadcaster_PrimeListener(t *testing.T) { 168 ci.Parallel(t) 169 170 b := NewAllocBroadcaster(testlog.HCLogger(t)) 171 defer b.Close() 172 173 alloc := mock.Alloc() 174 175 // Send an update before creating a listener 176 require.NoError(t, b.Send(alloc)) 177 178 // Create a listener and assert it immediately receives an update 179 l := b.Listen() 180 defer l.Close() 181 select { 182 case recv := <-l.Ch(): 183 require.Equal(t, alloc, recv) 184 case <-time.After(10 * time.Millisecond): 185 t.Fatalf("expected to receive initial value") 186 } 187 } 188 189 // TestAllocBroadcaster_Closed asserts that newly created listeners are 190 // primed with the last sent alloc even when the broadcaster is closed. 191 func TestAllocBroadcaster_Closed(t *testing.T) { 192 ci.Parallel(t) 193 194 b := NewAllocBroadcaster(testlog.HCLogger(t)) 195 196 alloc := mock.Alloc() 197 198 // Send an update before creating a listener 199 require.NoError(t, b.Send(alloc)) 200 201 // Close the broadcaster after sending a single update 202 b.Close() 203 204 // Create a listener and assert it immediately receives an update 205 l := b.Listen() 206 defer l.Close() 207 select { 208 case recv := <-l.Ch(): 209 require.Equal(t, alloc, recv) 210 case <-time.After(10 * time.Millisecond): 211 t.Fatalf("expected to receive initial value") 212 } 213 214 // Ch should now be closed. 215 select { 216 case _, ok := <-l.Ch(): 217 require.False(t, ok) 218 case <-time.After(10 * time.Millisecond): 219 t.Fatalf("expected Ch() to be closed") 220 } 221 }