github.com/bigcommerce/nomad@v0.9.3-bc/client/lib/streamframer/framer_test.go (about) 1 package framer 2 3 import ( 4 "bytes" 5 "reflect" 6 "strconv" 7 "testing" 8 "time" 9 10 "github.com/hashicorp/nomad/testutil" 11 "github.com/kr/pretty" 12 ) 13 14 // This test checks, that even if the frame size has not been hit, a flush will 15 // periodically occur. 16 func TestStreamFramer_Flush(t *testing.T) { 17 // Create the stream framer 18 frames := make(chan *StreamFrame, 10) 19 hRate, bWindow := 100*time.Millisecond, 100*time.Millisecond 20 sf := NewStreamFramer(frames, hRate, bWindow, 100) 21 sf.Run() 22 23 f := "foo" 24 fe := "bar" 25 d := []byte{0xa} 26 o := int64(10) 27 28 // Start the reader 29 resultCh := make(chan struct{}) 30 go func() { 31 for { 32 frame := <-frames 33 34 if frame.IsHeartbeat() { 35 continue 36 } 37 38 if reflect.DeepEqual(frame.Data, d) && frame.Offset == o && frame.File == f && frame.FileEvent == fe { 39 resultCh <- struct{}{} 40 return 41 } 42 43 } 44 }() 45 46 // Write only 1 byte so we do not hit the frame size 47 if err := sf.Send(f, fe, d, o); err != nil { 48 t.Fatalf("Send() failed %v", err) 49 } 50 51 select { 52 case <-resultCh: 53 case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * bWindow): 54 t.Fatalf("failed to flush") 55 } 56 57 // Shutdown 58 sf.Destroy() 59 60 select { 61 case <-sf.ExitCh(): 62 case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate): 63 t.Fatalf("exit channel should close") 64 } 65 66 if _, ok := <-frames; ok { 67 t.Fatal("out channel should be closed") 68 } 69 } 70 71 // This test checks that frames will be batched till the frame size is hit (in 72 // the case that is before the flush). 73 func TestStreamFramer_Batch(t *testing.T) { 74 // Ensure the batch window doesn't get hit 75 hRate, bWindow := 100*time.Millisecond, 500*time.Millisecond 76 77 // Create the stream framer 78 frames := make(chan *StreamFrame, 10) 79 sf := NewStreamFramer(frames, hRate, bWindow, 3) 80 sf.Run() 81 82 f := "foo" 83 fe := "bar" 84 d := []byte{0xa, 0xb, 0xc} 85 o := int64(10) 86 87 // Start the reader 88 resultCh := make(chan struct{}) 89 go func() { 90 for { 91 frame := <-frames 92 if frame.IsHeartbeat() { 93 continue 94 } 95 96 if reflect.DeepEqual(frame.Data, d) && frame.Offset == o && frame.File == f && frame.FileEvent == fe { 97 resultCh <- struct{}{} 98 return 99 } 100 } 101 }() 102 103 // Write only 1 byte so we do not hit the frame size 104 if err := sf.Send(f, fe, d[:1], o); err != nil { 105 t.Fatalf("Send() failed %v", err) 106 } 107 108 // Ensure we didn't get any data 109 select { 110 case <-resultCh: 111 t.Fatalf("Got data before frame size reached") 112 case <-time.After(bWindow / 2): 113 } 114 115 // Write the rest so we hit the frame size 116 if err := sf.Send(f, fe, d[1:], o); err != nil { 117 t.Fatalf("Send() failed %v", err) 118 } 119 120 // Ensure we get data 121 select { 122 case <-resultCh: 123 case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * bWindow): 124 t.Fatalf("Did not receive data after batch size reached") 125 } 126 127 // Shutdown 128 sf.Destroy() 129 130 select { 131 case <-sf.ExitCh(): 132 case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate): 133 t.Fatalf("exit channel should close") 134 } 135 136 if f, ok := <-frames; ok { 137 t.Fatalf("out channel should be closed. recv: %s", pretty.Sprint(f)) 138 } 139 } 140 141 func TestStreamFramer_Heartbeat(t *testing.T) { 142 // Create the stream framer 143 frames := make(chan *StreamFrame, 10) 144 hRate, bWindow := 100*time.Millisecond, 100*time.Millisecond 145 sf := NewStreamFramer(frames, hRate, bWindow, 100) 146 sf.Run() 147 148 // Start the reader 149 resultCh := make(chan struct{}) 150 go func() { 151 for { 152 frame := <-frames 153 if frame.IsHeartbeat() { 154 resultCh <- struct{}{} 155 return 156 } 157 } 158 }() 159 160 select { 161 case <-resultCh: 162 case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate): 163 t.Fatalf("failed to heartbeat") 164 } 165 166 // Shutdown 167 sf.Destroy() 168 169 select { 170 case <-sf.ExitCh(): 171 case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate): 172 t.Fatalf("exit channel should close") 173 } 174 175 if _, ok := <-frames; ok { 176 t.Fatal("out channel should be closed") 177 } 178 } 179 180 // This test checks that frames are received in order 181 func TestStreamFramer_Order(t *testing.T) { 182 // Ensure the batch window doesn't get hit 183 hRate, bWindow := 100*time.Millisecond, 10*time.Millisecond 184 // Create the stream framer 185 frames := make(chan *StreamFrame, 10) 186 sf := NewStreamFramer(frames, hRate, bWindow, 10) 187 sf.Run() 188 189 files := []string{"1", "2", "3", "4", "5"} 190 input := bytes.NewBuffer(make([]byte, 0, 100000)) 191 for i := 0; i <= 1000; i++ { 192 str := strconv.Itoa(i) + "," 193 input.WriteString(str) 194 } 195 196 expected := bytes.NewBuffer(make([]byte, 0, 100000)) 197 for range files { 198 expected.Write(input.Bytes()) 199 } 200 receivedBuf := bytes.NewBuffer(make([]byte, 0, 100000)) 201 202 // Start the reader 203 resultCh := make(chan struct{}) 204 go func() { 205 for { 206 frame := <-frames 207 if frame.IsHeartbeat() { 208 continue 209 } 210 211 receivedBuf.Write(frame.Data) 212 213 if reflect.DeepEqual(expected, receivedBuf) { 214 resultCh <- struct{}{} 215 return 216 } 217 } 218 }() 219 220 // Send the data 221 b := input.Bytes() 222 shards := 10 223 each := len(b) / shards 224 for _, f := range files { 225 for i := 0; i < shards; i++ { 226 l, r := each*i, each*(i+1) 227 if i == shards-1 { 228 r = len(b) 229 } 230 231 if err := sf.Send(f, "", b[l:r], 0); err != nil { 232 t.Fatalf("Send() failed %v", err) 233 } 234 } 235 } 236 237 // Ensure we get data 238 select { 239 case <-resultCh: 240 case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * bWindow): 241 if reflect.DeepEqual(expected, receivedBuf) { 242 got := receivedBuf.String() 243 want := expected.String() 244 t.Fatalf("Got %v; want %v", got, want) 245 } 246 } 247 248 // Shutdown 249 sf.Destroy() 250 251 select { 252 case <-sf.ExitCh(): 253 case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate): 254 t.Fatalf("exit channel should close") 255 } 256 257 if _, ok := <-frames; ok { 258 t.Fatal("out channel should be closed") 259 } 260 }