github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/query/cache/monitor/monitor_test.go (about)

     1  //go:build small
     2  // +build small
     3  
     4  // Copyright 2018 The WPT Dashboard Project. All rights reserved.
     5  // Use of this source code is governed by a BSD-style license that can be
     6  // found in the LICENSE file.
     7  
     8  package monitor
     9  
    10  import (
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/golang/mock/gomock"
    16  	"github.com/stretchr/testify/assert"
    17  
    18  	"github.com/web-platform-tests/wpt.fyi/api/query/cache/index"
    19  	"github.com/web-platform-tests/wpt.fyi/shared"
    20  )
    21  
    22  const testMaxHeapBytes uint64 = 10
    23  
    24  func getTestHarness(t *testing.T) (*gomock.Controller, *index.MockIndex, *MockRuntime, Monitor) {
    25  	ctrl := gomock.NewController(t)
    26  	idx := index.NewMockIndex(ctrl)
    27  	rt := NewMockRuntime(ctrl)
    28  	mon, err := NewIndexMonitor(shared.NewNilLogger(), rt, time.Microsecond, 10, testMaxHeapBytes, 0.0, idx)
    29  	assert.Nil(t, err)
    30  	return ctrl, idx, rt, mon
    31  }
    32  
    33  func TestStopErr(t *testing.T) {
    34  	ctrl, _, rt, mon := getTestHarness(t)
    35  	defer ctrl.Finish()
    36  	rt.EXPECT().GetHeapBytes().Return(uint64(0)).AnyTimes()
    37  	err := mon.Stop()
    38  	assert.Equal(t, errStopped, err)
    39  }
    40  
    41  func TestStartStop(t *testing.T) {
    42  	ctrl, idx, rt, mon := getTestHarness(t)
    43  	defer ctrl.Finish()
    44  	rt.EXPECT().GetHeapBytes().Return(uint64(0)).AnyTimes()
    45  	var startErr, stopErr error
    46  	go func() {
    47  		time.Sleep(time.Microsecond * 10)
    48  		stopErr = mon.Stop()
    49  	}()
    50  	idx.EXPECT().SetIngestChan(gomock.Any())
    51  	startErr = mon.Start()
    52  	assert.Nil(t, stopErr)
    53  	assert.Equal(t, errStopped, startErr)
    54  }
    55  
    56  func TestDoubleStart(t *testing.T) {
    57  	ctrl, idx, rt, mon := getTestHarness(t)
    58  	defer ctrl.Finish()
    59  	rt.EXPECT().GetHeapBytes().Return(uint64(0)).AnyTimes()
    60  	idx.EXPECT().SetIngestChan(gomock.Any())
    61  	var err1, err2 error
    62  	var wg sync.WaitGroup
    63  	wg.Add(2)
    64  	go func() {
    65  		defer wg.Done()
    66  		err1 = mon.Start()
    67  	}()
    68  	time.Sleep(time.Microsecond * 100)
    69  	go func() {
    70  		defer wg.Done()
    71  		err2 = mon.Start()
    72  	}()
    73  	time.Sleep(time.Microsecond * 100)
    74  	mon.Stop()
    75  	wg.Wait()
    76  	assert.True(t, (err1 == errStopped && err2 == errRunning) || (err1 == errRunning && err2 == errStopped))
    77  }
    78  
    79  func TestOOM(t *testing.T) {
    80  	ctrl, idx, rt, mon := getTestHarness(t)
    81  	defer ctrl.Finish()
    82  	rt.EXPECT().GetHeapBytes().Return(uint64(0))
    83  	rt.EXPECT().GetHeapBytes().Return(uint64(0))
    84  	rt.EXPECT().GetHeapBytes().Return(uint64(testMaxHeapBytes + 1))
    85  	rt.EXPECT().GetHeapBytes().Return(uint64(0)).AnyTimes()
    86  	idx.EXPECT().EvictRuns(gomock.Any()).DoAndReturn(func(float64) (int, error) {
    87  		go func() { mon.Stop() }()
    88  		return 0, nil
    89  	})
    90  	idx.EXPECT().SetIngestChan(gomock.Any())
    91  	err := mon.Start()
    92  	assert.Equal(t, errStopped, err)
    93  }
    94  
    95  type syncingIndex struct {
    96  	index.ProxyIndex
    97  
    98  	IngestsStarted   int
    99  	IngestsCompleted int
   100  	c                chan bool
   101  }
   102  
   103  func (i *syncingIndex) SetIngestChan(c chan bool) {
   104  	i.c = c
   105  }
   106  
   107  func (i *syncingIndex) IngestRun(r shared.TestRun) error {
   108  	i.IngestsStarted++
   109  	if i.c != nil {
   110  		i.c <- true
   111  	}
   112  	i.IngestsCompleted++
   113  
   114  	return nil
   115  }
   116  
   117  func TestIngestTriggered(t *testing.T) {
   118  	ctrl := gomock.NewController(t)
   119  	defer ctrl.Finish()
   120  	idx := index.NewMockIndex(ctrl)
   121  	rt := NewMockRuntime(ctrl)
   122  
   123  	// Long timeout of 1 minute and low "max ingestions before running monitor" of
   124  	// 2.
   125  	mon, err := NewIndexMonitor(shared.NewNilLogger(), rt, time.Minute, 2, testMaxHeapBytes, 0.0, idx)
   126  	assert.Nil(t, err)
   127  
   128  	// Send to done when all goroutines expectations should have been checked.
   129  	done := make(chan bool)
   130  
   131  	// Wait for monitor to set index's ingest run chan, then ingest two runs,
   132  	// triggering monitor check (and call to rt.GetHeapBytes()).
   133  	var c chan bool
   134  	idx.EXPECT().SetIngestChan(gomock.Any()).DoAndReturn(func(ch chan bool) {
   135  		c = ch
   136  
   137  		go func() {
   138  			idx.IngestRun(shared.TestRun{})
   139  			idx.IngestRun(shared.TestRun{})
   140  
   141  			// Second IngestRun() should have already triggered monitor (and
   142  			// rt.GetHeapBytes()) to run.
   143  			done <- true
   144  		}()
   145  	})
   146  
   147  	// Track number of ingest requests started and finished. Sync over c.
   148  	ingestStarted := 0
   149  	ingestFinished := 0
   150  	idx.EXPECT().IngestRun(gomock.Any()).DoAndReturn(func(shared.TestRun) error {
   151  		ingestStarted++
   152  		c <- true
   153  		ingestFinished++
   154  		return nil
   155  	}).AnyTimes()
   156  
   157  	// Start the monitor (triggering idx.SetIngestChan()).
   158  	go mon.Start()
   159  
   160  	// GetHeapBytes() is called to monitor memory usage when limit=2 IngestRun
   161  	// calls have started and monitor hasn't run yet. (Monitor should not run due
   162  	// to timeout set to 1 minute.)
   163  	rt.EXPECT().GetHeapBytes().DoAndReturn(func() uint64 {
   164  		assert.Equal(t, 2, ingestStarted)
   165  		assert.Equal(t, 1, ingestFinished)
   166  		return uint64(0)
   167  	})
   168  
   169  	<-done
   170  }