github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/pubsub/pubsub_test.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package pubsub
    19  
    20  import (
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  )
    25  
    26  func TestSubscribe(t *testing.T) {
    27  	ps := New[Maskable, Mask](2)
    28  	ch1 := make(chan Maskable, 1)
    29  	ch2 := make(chan Maskable, 1)
    30  	doneCh := make(chan struct{})
    31  	defer close(doneCh)
    32  	if err := ps.Subscribe(MaskAll, ch1, doneCh, nil); err != nil {
    33  		t.Fatalf("unexpected error: %v", err)
    34  	}
    35  	if err := ps.Subscribe(MaskAll, ch2, doneCh, nil); err != nil {
    36  		t.Fatalf("unexpected error: %v", err)
    37  	}
    38  	ps.Lock()
    39  	defer ps.Unlock()
    40  
    41  	if len(ps.subs) != 2 || ps.NumSubscribers(MaskAll) != 2 || ps.Subscribers() != 2 {
    42  		t.Fatalf("expected 2 subscribers")
    43  	}
    44  }
    45  
    46  func TestNumSubscribersMask(t *testing.T) {
    47  	ps := New[Maskable, Mask](2)
    48  	ch1 := make(chan Maskable, 1)
    49  	ch2 := make(chan Maskable, 1)
    50  	doneCh := make(chan struct{})
    51  	defer close(doneCh)
    52  	if err := ps.Subscribe(Mask(1), ch1, doneCh, nil); err != nil {
    53  		t.Fatalf("unexpected error: %v", err)
    54  	}
    55  	if err := ps.Subscribe(Mask(2), ch2, doneCh, nil); err != nil {
    56  		t.Fatalf("unexpected error: %v", err)
    57  	}
    58  	ps.Lock()
    59  	defer ps.Unlock()
    60  
    61  	if len(ps.subs) != 2 {
    62  		t.Fatalf("expected 2 subscribers")
    63  	}
    64  	if want, got := int32(2), ps.NumSubscribers(Mask(1)); got != want {
    65  		t.Fatalf("want %d subscribers, got %d", want, got)
    66  	}
    67  	if want, got := int32(2), ps.NumSubscribers(Mask(2)); got != want {
    68  		t.Fatalf("want %d subscribers, got %d", want, got)
    69  	}
    70  	if want, got := int32(2), ps.NumSubscribers(Mask(1|2)); got != want {
    71  		t.Fatalf("want %d subscribers, got %d", want, got)
    72  	}
    73  	if want, got := int32(2), ps.NumSubscribers(MaskAll); got != want {
    74  		t.Fatalf("want %d subscribers, got %d", want, got)
    75  	}
    76  	if want, got := int32(0), ps.NumSubscribers(Mask(4)); got != want {
    77  		t.Fatalf("want %d subscribers, got %d", want, got)
    78  	}
    79  }
    80  
    81  func TestSubscribeExceedingLimit(t *testing.T) {
    82  	ps := New[Maskable, Maskable](2)
    83  	ch1 := make(chan Maskable, 1)
    84  	ch2 := make(chan Maskable, 1)
    85  	ch3 := make(chan Maskable, 1)
    86  	doneCh := make(chan struct{})
    87  	defer close(doneCh)
    88  	if err := ps.Subscribe(MaskAll, ch1, doneCh, nil); err != nil {
    89  		t.Fatalf("unexpected error: %v", err)
    90  	}
    91  	if err := ps.Subscribe(MaskAll, ch2, doneCh, nil); err != nil {
    92  		t.Fatalf("unexpected error: %v", err)
    93  	}
    94  	if err := ps.Subscribe(MaskAll, ch3, doneCh, nil); err == nil {
    95  		t.Fatalf("unexpected nil err")
    96  	}
    97  }
    98  
    99  func TestUnsubscribe(t *testing.T) {
   100  	ps := New[Maskable, Maskable](2)
   101  	ch1 := make(chan Maskable, 1)
   102  	ch2 := make(chan Maskable, 1)
   103  	doneCh1 := make(chan struct{})
   104  	doneCh2 := make(chan struct{})
   105  	if err := ps.Subscribe(MaskAll, ch1, doneCh1, nil); err != nil {
   106  		t.Fatalf("unexpected error: %v", err)
   107  	}
   108  	if err := ps.Subscribe(MaskAll, ch2, doneCh2, nil); err != nil {
   109  		t.Fatalf("unexpected error: %v", err)
   110  	}
   111  
   112  	close(doneCh1)
   113  	// Allow for the above statement to take effect.
   114  	time.Sleep(100 * time.Millisecond)
   115  	ps.Lock()
   116  	if len(ps.subs) != 1 {
   117  		t.Fatal("expected 1 subscriber")
   118  	}
   119  	ps.Unlock()
   120  	close(doneCh2)
   121  }
   122  
   123  type maskString string
   124  
   125  func (m maskString) Mask() uint64 {
   126  	return 1
   127  }
   128  
   129  func TestPubSub(t *testing.T) {
   130  	ps := New[Maskable, Maskable](1)
   131  	ch1 := make(chan Maskable, 1)
   132  	doneCh1 := make(chan struct{})
   133  	defer close(doneCh1)
   134  	if err := ps.Subscribe(MaskAll, ch1, doneCh1, func(entry Maskable) bool { return true }); err != nil {
   135  		t.Fatalf("unexpected error: %v", err)
   136  	}
   137  	val := maskString("hello")
   138  	ps.Publish(val)
   139  	msg := <-ch1
   140  	if msg != val {
   141  		t.Fatalf(fmt.Sprintf("expected %s , found %s", val, msg))
   142  	}
   143  }
   144  
   145  func TestMultiPubSub(t *testing.T) {
   146  	ps := New[Maskable, Maskable](2)
   147  	ch1 := make(chan Maskable, 1)
   148  	ch2 := make(chan Maskable, 1)
   149  	doneCh := make(chan struct{})
   150  	defer close(doneCh)
   151  	if err := ps.Subscribe(MaskAll, ch1, doneCh, func(entry Maskable) bool { return true }); err != nil {
   152  		t.Fatalf("unexpected error: %v", err)
   153  	}
   154  	if err := ps.Subscribe(MaskAll, ch2, doneCh, func(entry Maskable) bool { return true }); err != nil {
   155  		t.Fatalf("unexpected error: %v", err)
   156  	}
   157  	val := maskString("hello")
   158  	ps.Publish(val)
   159  
   160  	msg1 := <-ch1
   161  	msg2 := <-ch2
   162  	if msg1 != val && msg2 != val {
   163  		t.Fatalf(fmt.Sprintf("expected both subscribers to have%s , found %s and  %s", val, msg1, msg2))
   164  	}
   165  }
   166  
   167  func TestMultiPubSubMask(t *testing.T) {
   168  	ps := New[Maskable, Maskable](3)
   169  	ch1 := make(chan Maskable, 1)
   170  	ch2 := make(chan Maskable, 1)
   171  	ch3 := make(chan Maskable, 1)
   172  	doneCh := make(chan struct{})
   173  	defer close(doneCh)
   174  	// Mask matches maskString, should get result
   175  	if err := ps.Subscribe(Mask(1), ch1, doneCh, func(entry Maskable) bool { return true }); err != nil {
   176  		t.Fatalf("unexpected error: %v", err)
   177  	}
   178  	// Mask matches maskString, should get result
   179  	if err := ps.Subscribe(Mask(1|2), ch2, doneCh, func(entry Maskable) bool { return true }); err != nil {
   180  		t.Fatalf("unexpected error: %v", err)
   181  	}
   182  	// Does NOT overlap maskString
   183  	if err := ps.Subscribe(Mask(2), ch3, doneCh, func(entry Maskable) bool { return true }); err != nil {
   184  		t.Fatalf("unexpected error: %v", err)
   185  	}
   186  	val := maskString("hello")
   187  	ps.Publish(val)
   188  
   189  	msg1 := <-ch1
   190  	msg2 := <-ch2
   191  	if msg1 != val && msg2 != val {
   192  		t.Fatalf(fmt.Sprintf("expected both subscribers to have%s , found %s and  %s", val, msg1, msg2))
   193  	}
   194  
   195  	select {
   196  	case msg := <-ch3:
   197  		t.Fatalf(fmt.Sprintf("unexpected msg, f got %s", msg))
   198  	default:
   199  	}
   200  }