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