github.com/brandur/modulir@v0.0.0-20240305213423-94ee82929cbd/pool_test.go (about) 1 package modulir 2 3 import ( 4 "testing" 5 6 assert "github.com/stretchr/testify/require" 7 "golang.org/x/xerrors" 8 ) 9 10 func TestEmptyPool(t *testing.T) { 11 p := NewPool(&Logger{Level: LevelDebug}, 10) 12 13 p.StartRound(0) 14 p.Wait() 15 16 assert.Equal(t, 0, len(p.JobsAll)) 17 assert.Equal(t, 0, len(p.JobsErrored)) 18 assert.Equal(t, 0, len(p.JobsExecuted)) 19 assert.Equal(t, []error(nil), p.JobErrors()) 20 } 21 22 func TestWithWork(t *testing.T) { 23 p := NewPool(&Logger{Level: LevelDebug}, 10) 24 25 p.StartRound(0) 26 j0 := NewJob("job 0", func() (bool, error) { return true, nil }) 27 p.Jobs <- j0 28 j1 := NewJob("job 1", func() (bool, error) { return true, nil }) 29 p.Jobs <- j1 30 j2 := NewJob("job 2", func() (bool, error) { return false, nil }) 31 p.Jobs <- j2 32 p.Wait() 33 34 // Check state on the pool 35 assert.Equal(t, 3, len(p.JobsAll)) 36 assert.Equal(t, 0, len(p.JobsErrored)) 37 assert.Equal(t, 2, len(p.JobsExecuted)) // Number of `return true` above 38 assert.Equal(t, []error(nil), p.JobErrors()) 39 40 // Check state on individual jobs 41 assert.Equal(t, true, j0.Executed) 42 assert.Equal(t, nil, j0.Err) 43 assert.Equal(t, true, j1.Executed) 44 assert.Equal(t, nil, j1.Err) 45 assert.Equal(t, false, j2.Executed) 46 assert.Equal(t, nil, j2.Err) 47 } 48 49 // Tests the pool with lots of fast jobs that do nothing across multiple 50 // rounds. Originally written to try to suss out a race condition. 51 func TestWithLargeNonWork(t *testing.T) { 52 p := NewPool(&Logger{Level: LevelDebug}, 30) 53 54 numJobs := 300 55 numRounds := 50 56 57 for i := 0; i < numRounds; i++ { 58 p.StartRound(0) 59 for j := 0; j < numJobs; j++ { 60 p.Jobs <- NewJob("job", func() (bool, error) { return false, nil }) 61 } 62 p.Wait() 63 64 // Check state on the pool 65 assert.Equal(t, numJobs, len(p.JobsAll)) 66 assert.Equal(t, 0, len(p.JobsErrored)) 67 assert.Equal(t, 0, len(p.JobsExecuted)) // Number of `return true` above 68 assert.Equal(t, []error(nil), p.JobErrors()) 69 } 70 } 71 72 func TestWithError(t *testing.T) { 73 p := NewPool(&Logger{Level: LevelDebug}, 10) 74 75 p.StartRound(0) 76 j0 := NewJob("job 0", func() (bool, error) { return true, nil }) 77 p.Jobs <- j0 78 j1 := NewJob("job 1", func() (bool, error) { return true, nil }) 79 p.Jobs <- j1 80 j2 := NewJob("job 2", func() (bool, error) { return true, xerrors.Errorf("error") }) 81 p.Jobs <- j2 82 p.Wait() 83 84 // Check state on the pool 85 assert.Equal(t, 3, len(p.JobsAll)) 86 assert.Equal(t, 1, len(p.JobsErrored)) 87 assert.Equal(t, 3, len(p.JobsExecuted)) // Number of `return true` above 88 assert.Equal(t, []string{"error"}, errorStrings(p.JobErrors())) 89 90 // Check state on individual jobs 91 assert.Equal(t, true, j0.Executed) 92 assert.Equal(t, nil, j0.Err) 93 assert.Equal(t, true, j1.Executed) 94 assert.Equal(t, nil, j1.Err) 95 assert.Equal(t, true, j2.Executed) 96 assert.Equal(t, "error", j2.Err.Error()) 97 } 98 99 func TestWorkJob(t *testing.T) { 100 p := NewPool(&Logger{Level: LevelDebug}, 1) 101 102 executed := false 103 j := &Job{ 104 F: func() (bool, error) { 105 executed = true 106 return true, nil 107 }, 108 Name: "TestJob", 109 } 110 111 p.wg.Add(1) 112 p.workJob(0, j) 113 114 assert.True(t, executed) 115 116 assert.Equal(t, 0, len(p.JobsErrored)) 117 assert.Equal(t, 1, len(p.JobsExecuted)) 118 119 assert.Equal(t, true, j.Executed) 120 assert.Equal(t, nil, j.Err) 121 } 122 123 func TestWorkJob_Error(t *testing.T) { 124 p := NewPool(&Logger{Level: LevelDebug}, 1) 125 126 executed := false 127 j := &Job{ 128 F: func() (bool, error) { 129 executed = true 130 return true, xerrors.Errorf("error") 131 }, 132 Name: "TestJob", 133 } 134 135 p.wg.Add(1) 136 p.workJob(0, j) 137 138 assert.True(t, executed) 139 140 assert.Equal(t, 1, len(p.JobsErrored)) 141 assert.Equal(t, 1, len(p.JobsExecuted)) 142 assert.Equal(t, []string{"error"}, errorStrings(p.JobErrors())) 143 144 assert.Equal(t, true, j.Executed) 145 assert.Equal(t, "error", j.Err.Error()) 146 } 147 148 func TestWorkJob_Panic(t *testing.T) { 149 p := NewPool(&Logger{Level: LevelDebug}, 1) 150 151 executed := false 152 j := &Job{ 153 F: func() (bool, error) { 154 executed = true 155 panic(xerrors.Errorf("error")) 156 }, 157 Name: "TestJob", 158 } 159 160 p.wg.Add(1) 161 p.workJob(0, j) 162 163 assert.True(t, executed) 164 165 assert.Equal(t, 1, len(p.JobsErrored)) 166 assert.Equal(t, 0, len(p.JobsExecuted)) 167 168 err := p.JobErrors()[0] 169 assert.Equal(t, "job panicked: error", err.Error()) 170 171 assert.Equal(t, false, j.Executed) 172 assert.Equal(t, "job panicked: error", j.Err.Error()) 173 } 174 175 func TestWorkJob_PanicString(t *testing.T) { 176 p := NewPool(&Logger{Level: LevelDebug}, 1) 177 178 executed := false 179 j := &Job{ 180 F: func() (bool, error) { 181 executed = true 182 panic("error") 183 }, 184 Name: "TestJob", 185 } 186 187 p.wg.Add(1) 188 p.workJob(0, j) 189 190 assert.True(t, executed) 191 192 assert.Equal(t, 1, len(p.JobsErrored)) 193 assert.Equal(t, 0, len(p.JobsExecuted)) 194 assert.Equal(t, []string{"job panicked: error"}, errorStrings(p.JobErrors())) 195 196 assert.Equal(t, false, j.Executed) 197 assert.Equal(t, "job panicked: error", j.Err.Error()) 198 } 199 200 func errorStrings(errs []error) []string { 201 strs := make([]string, len(errs)) 202 for i, err := range errs { 203 strs[i] = err.Error() 204 } 205 return strs 206 }