exec_test.go
1 package util 2 3 import ( 4 "context" 5 "sync/atomic" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/symflower/eval-dev-quality/log" 11 ) 12 13 func TestCommandWithResultTimeout(t *testing.T) { 14 logOutput, logger := log.Buffer() 15 defer func() { 16 if t.Failed() { 17 t.Log(logOutput.String()) 18 } 19 }() 20 21 ctx, cancel := context.WithTimeout(context.Background(), time.Duration(1*time.Second)) 22 defer cancel() 23 24 start := time.Now() 25 _, err := CommandWithResult(ctx, logger, &Command{ 26 Command: []string{ 27 "sleep", 28 "60", 29 }, 30 }) 31 duration := time.Since(start) 32 33 assert.Error(t, err) 34 assert.Less(t, duration.Seconds(), 5.0) 35 } 36 37 func TestFilterArgs(t *testing.T) { 38 type testCase struct { 39 Name string 40 41 Args []string 42 Filter []string 43 Ignore bool 44 45 ExpectedFiltered []string 46 } 47 48 validate := func(t *testing.T, tc *testCase) { 49 t.Run(tc.Name, func(t *testing.T) { 50 actualFiltered := FilterArgs(tc.Args, tc.Filter, tc.Ignore) 51 52 assert.Equal(t, tc.ExpectedFiltered, actualFiltered) 53 }) 54 } 55 56 validate(t, &testCase{ 57 Name: "Filter arguments", 58 59 Args: []string{ 60 "--runtime", 61 "abc", 62 "--runs", 63 "5", 64 }, 65 Filter: []string{ 66 "runtime", 67 }, 68 Ignore: true, 69 70 ExpectedFiltered: []string{ 71 "--runs", 72 "5", 73 }, 74 }) 75 76 validate(t, &testCase{ 77 Name: "Filter arguments with equals sign", 78 79 Args: []string{ 80 "--runtime=abc", 81 "--runs=5", 82 "--foo", 83 "bar", 84 }, 85 Filter: []string{ 86 "runtime", 87 }, 88 Ignore: true, 89 90 ExpectedFiltered: []string{ 91 "--runs", 92 "5", 93 "--foo", 94 "bar", 95 }, 96 }) 97 98 validate(t, &testCase{ 99 Name: "Filter arguments with an allow list", 100 101 Args: []string{ 102 "--runtime=abc", 103 "--runs=5", 104 "--foo", 105 "bar", 106 }, 107 108 Filter: []string{ 109 "runtime", 110 }, 111 112 ExpectedFiltered: []string{ 113 "--runtime", 114 "abc", 115 }, 116 }) 117 } 118 119 func TestFilterArgsKeep(t *testing.T) { 120 type testCase struct { 121 Name string 122 123 Args []string 124 Filter []string 125 126 ExpectedFiltered []string 127 } 128 129 validate := func(t *testing.T, tc *testCase) { 130 t.Run(tc.Name, func(t *testing.T) { 131 actualFiltered := FilterArgsKeep(tc.Args, tc.Filter) 132 133 assert.Equal(t, tc.ExpectedFiltered, actualFiltered) 134 }) 135 } 136 137 validate(t, &testCase{ 138 Name: "Keep arguments", 139 140 Args: []string{ 141 "--runtime=abc", 142 "--runs=5", 143 "--foo", 144 "bar", 145 }, 146 147 Filter: []string{ 148 "runtime", 149 }, 150 151 ExpectedFiltered: []string{ 152 "--runtime", 153 "abc", 154 }, 155 }) 156 157 validate(t, &testCase{ 158 Name: "Multiple arguments", 159 160 Args: []string{ 161 "--runtime=abc", 162 "--repository=abc", 163 "--repository=def", 164 "--foo", 165 "bar", 166 }, 167 168 Filter: []string{ 169 "runtime", 170 "repository", 171 }, 172 173 ExpectedFiltered: []string{ 174 "--runtime", 175 "abc", 176 "--repository", 177 "abc", 178 "--repository", 179 "def", 180 }, 181 }) 182 } 183 184 func TestFilterArgsRemove(t *testing.T) { 185 type testCase struct { 186 Name string 187 188 Args []string 189 Filter []string 190 191 ExpectedFiltered []string 192 } 193 194 validate := func(t *testing.T, tc *testCase) { 195 t.Run(tc.Name, func(t *testing.T) { 196 actualFiltered := FilterArgsRemove(tc.Args, tc.Filter) 197 198 assert.Equal(t, tc.ExpectedFiltered, actualFiltered) 199 }) 200 } 201 202 validate(t, &testCase{ 203 Name: "Remove arguments", 204 205 Args: []string{ 206 "--runtime", 207 "abc", 208 "--runs", 209 "5", 210 }, 211 Filter: []string{ 212 "runtime", 213 }, 214 215 ExpectedFiltered: []string{ 216 "--runs", 217 "5", 218 }, 219 }) 220 } 221 222 func TestParallelExecute(t *testing.T) { 223 type testCase struct { 224 Limit uint 225 Times uint 226 227 Function func() 228 } 229 230 run := func(tc *testCase) { 231 p := NewParallel(tc.Limit) 232 for i := uint(0); i < tc.Times; i++ { 233 p.Execute(tc.Function) 234 } 235 p.Wait() 236 } 237 238 t.Run("Atomic count", func(t *testing.T) { 239 var count atomic.Uint32 240 expectedCount := uint(10) 241 242 tc := &testCase{ 243 Limit: 2, 244 Times: expectedCount, 245 246 Function: func() { 247 count.Add(1) 248 }, 249 } 250 251 run(tc) 252 253 assert.Equal(t, count.Load(), uint32(expectedCount)) 254 }) 255 256 t.Run("Timed limiting", func(t *testing.T) { 257 tc := &testCase{ 258 Limit: 1, 259 Times: 3, 260 261 Function: func() { 262 time.Sleep(500 * time.Millisecond) 263 }, 264 } 265 266 start := time.Now() 267 run(tc) 268 duration := time.Since(start) 269 270 assert.GreaterOrEqual(t, duration, 1500*time.Millisecond) 271 }) 272 273 } 274 275 func TestFlags(t *testing.T) { 276 type testCase struct { 277 Name string 278 279 Cmd any 280 281 ExpectedArgs []string 282 } 283 284 validate := func(t *testing.T, tc *testCase) { 285 t.Run(tc.Name, func(t *testing.T) { 286 actualArgs := Flags(tc.Cmd) 287 288 assert.Equal(t, tc.ExpectedArgs, actualArgs) 289 }) 290 } 291 292 validate(t, &testCase{ 293 Name: "Struct Command", 294 295 Cmd: struct { 296 PropA string `long:"PropA"` 297 PropB string `long:"PropB"` 298 PropC string `short:"hey"` 299 }{}, 300 301 ExpectedArgs: []string{ 302 "PropA", 303 "PropB", 304 }, 305 }) 306 307 validate(t, &testCase{ 308 Name: "Pointer Struct Command", 309 310 Cmd: &struct { 311 PropA string `long:"PropA"` 312 PropB string `long:"PropB"` 313 }{}, 314 315 ExpectedArgs: []string{ 316 "PropA", 317 "PropB", 318 }, 319 }) 320 }