google.golang.org/grpc@v1.62.1/internal/grpcsync/pubsub_test.go (about)

     1  /*
     2   *
     3   * Copyright 2023 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package grpcsync
    20  
    21  import (
    22  	"context"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  )
    27  
    28  type testSubscriber struct {
    29  	onMsgCh chan int
    30  }
    31  
    32  func newTestSubscriber(chSize int) *testSubscriber {
    33  	return &testSubscriber{onMsgCh: make(chan int, chSize)}
    34  }
    35  
    36  func (ts *testSubscriber) OnMessage(msg any) {
    37  	select {
    38  	case ts.onMsgCh <- msg.(int):
    39  	default:
    40  	}
    41  }
    42  
    43  func (s) TestPubSub_PublishNoMsg(t *testing.T) {
    44  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    45  	defer cancel()
    46  	pubsub := NewPubSub(ctx)
    47  
    48  	ts := newTestSubscriber(1)
    49  	pubsub.Subscribe(ts)
    50  
    51  	select {
    52  	case <-ts.onMsgCh:
    53  		t.Fatal("Subscriber callback invoked when no message was published")
    54  	case <-time.After(defaultTestShortTimeout):
    55  	}
    56  }
    57  
    58  func (s) TestPubSub_PublishMsgs_RegisterSubs_And_Stop(t *testing.T) {
    59  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    60  	defer cancel()
    61  	pubsub := NewPubSub(ctx)
    62  
    63  	const numPublished = 10
    64  
    65  	ts1 := newTestSubscriber(numPublished)
    66  	pubsub.Subscribe(ts1)
    67  
    68  	var wg sync.WaitGroup
    69  	wg.Add(2)
    70  	// Publish ten messages on the pubsub and ensure that they are received in order by the subscriber.
    71  	go func() {
    72  		for i := 0; i < numPublished; i++ {
    73  			pubsub.Publish(i)
    74  		}
    75  		wg.Done()
    76  	}()
    77  
    78  	go func() {
    79  		defer wg.Done()
    80  		for i := 0; i < numPublished; i++ {
    81  			select {
    82  			case m := <-ts1.onMsgCh:
    83  				if m != i {
    84  					t.Errorf("Received unexpected message: %q; want: %q", m, i)
    85  					return
    86  				}
    87  			case <-time.After(defaultTestTimeout):
    88  				t.Error("Timeout when expecting the onMessage() callback to be invoked")
    89  				return
    90  			}
    91  		}
    92  	}()
    93  	wg.Wait()
    94  	if t.Failed() {
    95  		t.FailNow()
    96  	}
    97  
    98  	// Register another subscriber and ensure that it receives the last published message.
    99  	ts2 := newTestSubscriber(numPublished)
   100  	pubsub.Subscribe(ts2)
   101  
   102  	select {
   103  	case m := <-ts2.onMsgCh:
   104  		if m != numPublished-1 {
   105  			t.Fatalf("Received unexpected message: %q; want: %q", m, numPublished-1)
   106  		}
   107  	case <-time.After(defaultTestShortTimeout):
   108  		t.Fatal("Timeout when expecting the onMessage() callback to be invoked")
   109  	}
   110  
   111  	wg.Add(3)
   112  	// Publish ten messages on the pubsub and ensure that they are received in order by the subscribers.
   113  	go func() {
   114  		for i := 0; i < numPublished; i++ {
   115  			pubsub.Publish(i)
   116  		}
   117  		wg.Done()
   118  	}()
   119  	go func() {
   120  		defer wg.Done()
   121  		for i := 0; i < numPublished; i++ {
   122  			select {
   123  			case m := <-ts1.onMsgCh:
   124  				if m != i {
   125  					t.Errorf("Received unexpected message: %q; want: %q", m, i)
   126  					return
   127  				}
   128  			case <-time.After(defaultTestTimeout):
   129  				t.Error("Timeout when expecting the onMessage() callback to be invoked")
   130  				return
   131  			}
   132  		}
   133  
   134  	}()
   135  	go func() {
   136  		defer wg.Done()
   137  		for i := 0; i < numPublished; i++ {
   138  			select {
   139  			case m := <-ts2.onMsgCh:
   140  				if m != i {
   141  					t.Errorf("Received unexpected message: %q; want: %q", m, i)
   142  					return
   143  				}
   144  			case <-time.After(defaultTestTimeout):
   145  				t.Error("Timeout when expecting the onMessage() callback to be invoked")
   146  				return
   147  			}
   148  		}
   149  	}()
   150  	wg.Wait()
   151  	if t.Failed() {
   152  		t.FailNow()
   153  	}
   154  
   155  	cancel()
   156  	<-pubsub.Done()
   157  
   158  	go func() {
   159  		pubsub.Publish(99)
   160  	}()
   161  	// Ensure that the subscriber callback is not invoked as instantiated
   162  	// pubsub has already closed.
   163  	select {
   164  	case <-ts1.onMsgCh:
   165  		t.Fatal("The callback was invoked after pubsub being stopped")
   166  	case <-ts2.onMsgCh:
   167  		t.Fatal("The callback was invoked after pubsub being stopped")
   168  	case <-time.After(defaultTestShortTimeout):
   169  	}
   170  }
   171  
   172  func (s) TestPubSub_PublishMsgs_BeforeRegisterSub(t *testing.T) {
   173  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   174  	defer cancel()
   175  	pubsub := NewPubSub(ctx)
   176  
   177  	const numPublished = 3
   178  	for i := 0; i < numPublished; i++ {
   179  		pubsub.Publish(i)
   180  	}
   181  
   182  	ts := newTestSubscriber(numPublished)
   183  	pubsub.Subscribe(ts)
   184  
   185  	// Ensure that the subscriber callback is invoked with a previously
   186  	// published message.
   187  	select {
   188  	case d := <-ts.onMsgCh:
   189  		if d != numPublished-1 {
   190  			t.Fatalf("Unexpected message received: %q; %q", d, numPublished-1)
   191  		}
   192  
   193  	case <-time.After(defaultTestShortTimeout):
   194  		t.Fatal("Timeout when expecting the onMessage() callback to be invoked")
   195  	}
   196  }