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  }