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 }