github.com/safing/portbase@v0.19.5/modules/tasks_test.go (about) 1 package modules 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "runtime/pprof" 8 "sync" 9 "testing" 10 "time" 11 ) 12 13 func init() { 14 go taskQueueHandler() 15 go taskScheduleHandler() 16 17 go func() { 18 <-time.After(30 * time.Second) 19 fmt.Fprintln(os.Stderr, "taking too long") 20 _ = pprof.Lookup("goroutine").WriteTo(os.Stderr, 2) 21 os.Exit(1) 22 }() 23 24 // always trigger task timeslot for testing 25 go func() { 26 for { 27 taskTimeslot <- struct{}{} 28 } 29 }() 30 } 31 32 // Test queued tasks. 33 34 // Queued task test globals. 35 36 var ( 37 qtWg sync.WaitGroup 38 qtOutputChannel chan string 39 qtSleepDuration time.Duration 40 qtModule *Module 41 ) 42 43 func init() { 44 qtModule = initNewModule("task test module", nil, nil, nil) 45 qtModule.status = StatusOnline 46 } 47 48 // Queued task test functions. 49 50 func queuedTaskTester(s string) { 51 qtModule.NewTask(s, func(ctx context.Context, t *Task) error { 52 time.Sleep(qtSleepDuration * 2) 53 qtOutputChannel <- s 54 qtWg.Done() 55 return nil 56 }).Queue() 57 } 58 59 func prioritizedTaskTester(s string) { 60 qtModule.NewTask(s, func(ctx context.Context, t *Task) error { 61 time.Sleep(qtSleepDuration * 2) 62 qtOutputChannel <- s 63 qtWg.Done() 64 return nil 65 }).QueuePrioritized() 66 } 67 68 func TestQueuedTask(t *testing.T) { //nolint:paralleltest // Too much interference expected. 69 // skip 70 if testing.Short() { 71 t.Skip("skipping test in short mode, as it is not fully deterministic") 72 } 73 74 // init 75 expectedOutput := "0123456789" 76 qtSleepDuration = 20 * time.Millisecond 77 qtOutputChannel = make(chan string, 100) 78 qtWg.Add(10) 79 80 // TEST 81 queuedTaskTester("0") 82 queuedTaskTester("1") 83 queuedTaskTester("3") 84 queuedTaskTester("4") 85 queuedTaskTester("6") 86 queuedTaskTester("7") 87 queuedTaskTester("9") 88 89 time.Sleep(qtSleepDuration * 3) 90 prioritizedTaskTester("2") 91 time.Sleep(qtSleepDuration * 6) 92 prioritizedTaskTester("5") 93 time.Sleep(qtSleepDuration * 6) 94 prioritizedTaskTester("8") 95 96 // wait for test to finish 97 qtWg.Wait() 98 99 // collect output 100 close(qtOutputChannel) 101 completeOutput := "" 102 for s := <-qtOutputChannel; s != ""; s = <-qtOutputChannel { 103 completeOutput += s 104 } 105 // check if test succeeded 106 if completeOutput != expectedOutput { 107 t.Errorf("QueuedTask test failed, expected sequence %s, got %s", expectedOutput, completeOutput) 108 } 109 } 110 111 // Test scheduled tasks. 112 113 // Scheduled task test globals. 114 115 var ( 116 stWg sync.WaitGroup 117 stOutputChannel chan string 118 stSleepDuration time.Duration 119 stWaitCh chan bool 120 ) 121 122 // Scheduled task test functions. 123 124 func scheduledTaskTester(s string, sched time.Time) { 125 qtModule.NewTask(s, func(ctx context.Context, t *Task) error { 126 time.Sleep(stSleepDuration) 127 stOutputChannel <- s 128 stWg.Done() 129 return nil 130 }).Schedule(sched) 131 } 132 133 func TestScheduledTaskWaiting(t *testing.T) { //nolint:paralleltest // Too much interference expected. 134 135 // skip 136 if testing.Short() { 137 t.Skip("skipping test in short mode, as it is not fully deterministic") 138 } 139 140 // init 141 expectedOutput := "0123456789" 142 stSleepDuration = 10 * time.Millisecond 143 stOutputChannel = make(chan string, 100) 144 stWaitCh = make(chan bool) 145 146 stWg.Add(10) 147 148 // TEST 149 scheduledTaskTester("4", time.Now().Add(stSleepDuration*8)) 150 scheduledTaskTester("0", time.Now().Add(stSleepDuration*0)) 151 scheduledTaskTester("8", time.Now().Add(stSleepDuration*16)) 152 scheduledTaskTester("1", time.Now().Add(stSleepDuration*2)) 153 scheduledTaskTester("7", time.Now().Add(stSleepDuration*14)) 154 scheduledTaskTester("9", time.Now().Add(stSleepDuration*18)) 155 scheduledTaskTester("3", time.Now().Add(stSleepDuration*6)) 156 scheduledTaskTester("2", time.Now().Add(stSleepDuration*4)) 157 scheduledTaskTester("6", time.Now().Add(stSleepDuration*12)) 158 scheduledTaskTester("5", time.Now().Add(stSleepDuration*10)) 159 160 // wait for test to finish 161 close(stWaitCh) 162 stWg.Wait() 163 164 // collect output 165 close(stOutputChannel) 166 completeOutput := "" 167 for s := <-stOutputChannel; s != ""; s = <-stOutputChannel { 168 completeOutput += s 169 } 170 // check if test succeeded 171 if completeOutput != expectedOutput { 172 t.Errorf("ScheduledTask test failed, expected sequence %s, got %s", expectedOutput, completeOutput) 173 } 174 } 175 176 func TestRequeueingTask(t *testing.T) { //nolint:paralleltest // Too much interference expected. 177 blockWg := &sync.WaitGroup{} 178 wg := &sync.WaitGroup{} 179 180 // block task execution 181 blockWg.Add(1) // mark done at beginning 182 wg.Add(2) // mark done at end 183 block := qtModule.NewTask("TestRequeueingTask:block", func(ctx context.Context, t *Task) error { 184 blockWg.Done() 185 time.Sleep(100 * time.Millisecond) 186 wg.Done() 187 return nil 188 }).StartASAP() 189 // make sure first task has started 190 blockWg.Wait() 191 // fmt.Printf("%s: %+v\n", time.Now(), block) 192 193 // schedule again while executing 194 blockWg.Add(1) // mark done at beginning 195 block.StartASAP() 196 // fmt.Printf("%s: %+v\n", time.Now(), block) 197 198 // test task 199 wg.Add(1) 200 task := qtModule.NewTask("TestRequeueingTask:test", func(ctx context.Context, t *Task) error { 201 wg.Done() 202 return nil 203 }).Schedule(time.Now().Add(2 * time.Second)) 204 205 // reschedule 206 task.Schedule(time.Now().Add(1 * time.Second)) 207 task.Queue() 208 task.QueuePrioritized() 209 task.StartASAP() 210 wg.Wait() 211 time.Sleep(100 * time.Millisecond) // let tasks finalize execution 212 213 // do it again 214 215 // block task execution (while first block task is still running!) 216 blockWg.Add(1) // mark done at beginning 217 wg.Add(1) // mark done at end 218 block.StartASAP() 219 blockWg.Wait() 220 // reschedule 221 wg.Add(1) 222 task.Schedule(time.Now().Add(1 * time.Second)) 223 task.Queue() 224 task.QueuePrioritized() 225 task.StartASAP() 226 wg.Wait() 227 } 228 229 func TestQueueSuccession(t *testing.T) { //nolint:paralleltest // Too much interference expected. 230 var cnt int 231 wg := &sync.WaitGroup{} 232 wg.Add(10) 233 234 tt := qtModule.NewTask("TestRequeueingTask:test", func(ctx context.Context, task *Task) error { 235 time.Sleep(10 * time.Millisecond) 236 wg.Done() 237 cnt++ 238 fmt.Printf("completed succession %d\n", cnt) 239 switch cnt { 240 case 1, 4, 6: 241 task.Queue() 242 case 2, 5, 8: 243 task.StartASAP() 244 case 3, 7, 9: 245 task.Schedule(time.Now().Add(10 * time.Millisecond)) 246 } 247 return nil 248 }) 249 // fmt.Printf("%+v\n", tt) 250 tt.StartASAP() 251 // time.Sleep(100 * time.Millisecond) 252 // fmt.Printf("%+v\n", tt) 253 254 wg.Wait() 255 }