github.com/MontFerret/ferret@v0.18.0/pkg/drivers/cdp/events/loop_test.go (about)

     1  package events_test
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"sync/atomic"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/mafredri/cdp/rpcc"
    11  	. "github.com/smartystreets/goconvey/convey"
    12  
    13  	"github.com/MontFerret/ferret/pkg/drivers/cdp/events"
    14  )
    15  
    16  type TestEventStream struct {
    17  	closed   atomic.Value
    18  	ready    chan struct{}
    19  	messages chan string
    20  }
    21  
    22  var TestEvent = events.New("test_event")
    23  
    24  func NewTestEventStream() *TestEventStream {
    25  	return NewBufferedTestEventStream(0)
    26  }
    27  
    28  func NewBufferedTestEventStream(buffer int) *TestEventStream {
    29  	es := new(TestEventStream)
    30  	es.ready = make(chan struct{}, buffer)
    31  	es.messages = make(chan string, buffer)
    32  	es.closed.Store(false)
    33  
    34  	return es
    35  }
    36  
    37  func (es *TestEventStream) IsClosed() bool {
    38  	return es.closed.Load().(bool)
    39  }
    40  
    41  func (es *TestEventStream) Ready() <-chan struct{} {
    42  	return es.ready
    43  }
    44  
    45  func (es *TestEventStream) RecvMsg(i interface{}) error {
    46  	// NOT IMPLEMENTED
    47  	return nil
    48  }
    49  
    50  func (es *TestEventStream) Recv() (interface{}, error) {
    51  	msg := <-es.messages
    52  
    53  	return msg, nil
    54  }
    55  
    56  func (es *TestEventStream) Close() error {
    57  	es.closed.Store(true)
    58  	close(es.messages)
    59  	close(es.ready)
    60  	return nil
    61  }
    62  
    63  func (es *TestEventStream) EmitP(msg string, skipCheck bool) {
    64  	if !skipCheck {
    65  		isClosed := es.closed.Load().(bool)
    66  
    67  		if isClosed {
    68  			return
    69  		}
    70  	}
    71  
    72  	es.ready <- struct{}{}
    73  	es.messages <- msg
    74  }
    75  
    76  func (es *TestEventStream) Emit(msg string) {
    77  	es.EmitP(msg, false)
    78  }
    79  
    80  func (es *TestEventStream) EmitDefault() {
    81  	es.Emit("")
    82  }
    83  
    84  func wait() {
    85  	time.Sleep(time.Duration(50) * time.Millisecond)
    86  }
    87  
    88  type Counter struct {
    89  	mu    sync.Mutex
    90  	value int64
    91  }
    92  
    93  func NewCounter() *Counter {
    94  	return new(Counter)
    95  }
    96  
    97  func (c *Counter) Increase() *Counter {
    98  	c.mu.Lock()
    99  	defer c.mu.Unlock()
   100  
   101  	c.value++
   102  
   103  	return c
   104  }
   105  
   106  func (c *Counter) Decrease() *Counter {
   107  	c.mu.Lock()
   108  	defer c.mu.Unlock()
   109  
   110  	c.value--
   111  
   112  	return c
   113  }
   114  
   115  func (c *Counter) Value() int64 {
   116  	c.mu.Lock()
   117  	defer c.mu.Unlock()
   118  
   119  	return c.value
   120  }
   121  
   122  func TestLoop(t *testing.T) {
   123  	Convey(".AddListener", t, func() {
   124  		Convey("Should add a new listener", func() {
   125  			counter := NewCounter()
   126  
   127  			var tes *TestEventStream
   128  
   129  			loop := events.NewLoop(events.NewStreamSourceFactory(TestEvent, func(ctx context.Context) (rpcc.Stream, error) {
   130  				tes = NewTestEventStream()
   131  				return tes, nil
   132  			}, func(stream rpcc.Stream) (interface{}, error) {
   133  				return stream.(*TestEventStream).Recv()
   134  			}))
   135  
   136  			ctx, cancel := context.WithCancel(context.Background())
   137  
   138  			err := loop.Run(ctx)
   139  			defer cancel()
   140  
   141  			So(err, ShouldBeNil)
   142  
   143  			tes.EmitDefault()
   144  
   145  			wait()
   146  
   147  			So(counter.Value(), ShouldEqual, 0)
   148  
   149  			loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   150  				counter.Increase()
   151  			}))
   152  
   153  			wait()
   154  
   155  			tes.EmitDefault()
   156  
   157  			wait()
   158  
   159  			So(counter.Value(), ShouldEqual, 1)
   160  		})
   161  	})
   162  
   163  	Convey(".RemoveListener", t, func() {
   164  		Convey("Should remove a listener", func() {
   165  			Convey("Should add a new listener", func() {
   166  				counter := NewCounter()
   167  
   168  				var test *TestEventStream
   169  
   170  				loop := events.NewLoop(events.NewStreamSourceFactory(TestEvent, func(ctx context.Context) (rpcc.Stream, error) {
   171  					test = NewTestEventStream()
   172  					return test, nil
   173  				}, func(stream rpcc.Stream) (interface{}, error) {
   174  					return stream.(*TestEventStream).Recv()
   175  				}))
   176  
   177  				id := loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   178  					counter.Increase()
   179  				}))
   180  
   181  				ctx, cancel := context.WithCancel(context.Background())
   182  
   183  				err := loop.Run(ctx)
   184  				defer cancel()
   185  
   186  				So(err, ShouldBeNil)
   187  
   188  				test.EmitDefault()
   189  
   190  				wait()
   191  
   192  				So(counter.Value(), ShouldEqual, 1)
   193  
   194  				wait()
   195  
   196  				loop.RemoveListener(TestEvent, id)
   197  
   198  				wait()
   199  
   200  				test.EmitDefault()
   201  
   202  				wait()
   203  
   204  				So(counter.Value(), ShouldEqual, 1)
   205  			})
   206  		})
   207  	})
   208  
   209  	Convey("Should not call listener once it was removed", t, func() {
   210  		var tes *TestEventStream
   211  
   212  		loop := events.NewLoop(events.NewStreamSourceFactory(TestEvent, func(ctx context.Context) (rpcc.Stream, error) {
   213  			tes = NewTestEventStream()
   214  			return tes, nil
   215  		}, func(stream rpcc.Stream) (interface{}, error) {
   216  			return stream.(*TestEventStream).Recv()
   217  		}))
   218  
   219  		onEvent := make(chan struct{})
   220  
   221  		counter := NewCounter()
   222  
   223  		id := loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   224  			counter.Increase()
   225  
   226  			onEvent <- struct{}{}
   227  		}))
   228  
   229  		go func() {
   230  			<-onEvent
   231  
   232  			loop.RemoveListener(TestEvent, id)
   233  		}()
   234  
   235  		ctx, cancel := context.WithCancel(context.Background())
   236  
   237  		err := loop.Run(ctx)
   238  		So(err, ShouldBeNil)
   239  		defer cancel()
   240  
   241  		time.Sleep(time.Duration(100) * time.Millisecond)
   242  
   243  		tes.EmitDefault()
   244  
   245  		time.Sleep(time.Duration(10) * time.Millisecond)
   246  
   247  		So(counter.Value(), ShouldEqual, 1)
   248  	})
   249  
   250  	Convey("Should stop on Context.Done", t, func() {
   251  		eventsToFire := 5
   252  		counter := NewCounter()
   253  
   254  		var tes *TestEventStream
   255  
   256  		loop := events.NewLoop(events.NewStreamSourceFactory(TestEvent, func(ctx context.Context) (rpcc.Stream, error) {
   257  			tes = NewTestEventStream()
   258  			return tes, nil
   259  		}, func(stream rpcc.Stream) (interface{}, error) {
   260  			return stream.(*TestEventStream).Recv()
   261  		}))
   262  
   263  		loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   264  			counter.Increase()
   265  		}))
   266  
   267  		ctx, cancel := context.WithCancel(context.Background())
   268  		err := loop.Run(ctx)
   269  		So(err, ShouldBeNil)
   270  
   271  		for i := 0; i <= eventsToFire; i++ {
   272  			time.Sleep(time.Duration(100) * time.Millisecond)
   273  
   274  			tes.EmitDefault()
   275  		}
   276  
   277  		// Stop the loop
   278  		cancel()
   279  
   280  		time.Sleep(time.Duration(100) * time.Millisecond)
   281  
   282  		So(tes.IsClosed(), ShouldBeTrue)
   283  	})
   284  }
   285  
   286  func BenchmarkLoop_AddListenerSync(b *testing.B) {
   287  	loop := events.NewLoop()
   288  
   289  	for n := 0; n < b.N; n++ {
   290  		loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {}))
   291  	}
   292  }
   293  
   294  func BenchmarkLoop_AddListenerAsync(b *testing.B) {
   295  	loop := events.NewLoop()
   296  	ctx, cancel := context.WithCancel(context.Background())
   297  
   298  	loop.Run(ctx)
   299  	defer cancel()
   300  
   301  	for n := 0; n < b.N; n++ {
   302  		loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {}))
   303  	}
   304  }
   305  
   306  func BenchmarkLoop_AddListenerAsync2(b *testing.B) {
   307  	loop := events.NewLoop()
   308  	ctx, cancel := context.WithCancel(context.Background())
   309  
   310  	loop.Run(ctx)
   311  	defer cancel()
   312  
   313  	b.RunParallel(func(pb *testing.PB) {
   314  		for pb.Next() {
   315  			loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {}))
   316  		}
   317  	})
   318  }
   319  
   320  func BenchmarkLoop_Start(b *testing.B) {
   321  	var tes *TestEventStream
   322  
   323  	loop := events.NewLoop(events.NewStreamSourceFactory(TestEvent, func(ctx context.Context) (rpcc.Stream, error) {
   324  		tes = NewTestEventStream()
   325  		return tes, nil
   326  	}, func(stream rpcc.Stream) (interface{}, error) {
   327  		return stream.(*TestEventStream).Recv()
   328  	}))
   329  
   330  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   331  
   332  	}))
   333  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   334  
   335  	}))
   336  
   337  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   338  
   339  	}))
   340  
   341  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   342  
   343  	}))
   344  
   345  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   346  
   347  	}))
   348  
   349  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   350  
   351  	}))
   352  
   353  	ctx, cancel := context.WithCancel(context.Background())
   354  
   355  	if err := loop.Run(ctx); err != nil {
   356  		panic(err)
   357  	}
   358  
   359  	defer cancel()
   360  
   361  	for n := 0; n < b.N; n++ {
   362  		tes.EmitP("", true)
   363  	}
   364  }
   365  
   366  func BenchmarkLoop_StartAsync(b *testing.B) {
   367  	var tes *TestEventStream
   368  
   369  	loop := events.NewLoop(events.NewStreamSourceFactory(TestEvent, func(ctx context.Context) (rpcc.Stream, error) {
   370  		tes = NewBufferedTestEventStream(b.N)
   371  		return tes, nil
   372  	}, func(stream rpcc.Stream) (interface{}, error) {
   373  		return stream.(*TestEventStream).Recv()
   374  	}))
   375  
   376  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   377  
   378  	}))
   379  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   380  
   381  	}))
   382  
   383  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   384  
   385  	}))
   386  
   387  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   388  
   389  	}))
   390  
   391  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   392  
   393  	}))
   394  
   395  	loop.AddListener(TestEvent, events.Always(func(ctx context.Context, message interface{}) {
   396  
   397  	}))
   398  
   399  	ctx, cancel := context.WithCancel(context.Background())
   400  
   401  	if err := loop.Run(ctx); err != nil {
   402  		panic(err)
   403  	}
   404  
   405  	defer cancel()
   406  
   407  	b.RunParallel(func(pb *testing.PB) {
   408  		for pb.Next() {
   409  			tes.EmitP("", true)
   410  		}
   411  	})
   412  }