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  }