github.com/GoogleCloudPlatform/testgrid@v0.0.174/internal/result/results_test.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package result 18 19 import ( 20 "context" 21 "fmt" 22 "math/rand" 23 "reflect" 24 "testing" 25 26 configpb "github.com/GoogleCloudPlatform/testgrid/pb/config" 27 statuspb "github.com/GoogleCloudPlatform/testgrid/pb/test_status" 28 ) 29 30 func TestPassing(t *testing.T) { 31 cases := []struct { 32 status statuspb.TestStatus 33 expected bool 34 }{ 35 { 36 status: statuspb.TestStatus_NO_RESULT, 37 }, 38 { 39 status: statuspb.TestStatus_BUILD_PASSED, 40 expected: true, 41 }, 42 { 43 status: statuspb.TestStatus_PASS, 44 expected: true, 45 }, 46 { 47 status: statuspb.TestStatus_PASS_WITH_SKIPS, 48 expected: true, 49 }, 50 { 51 status: statuspb.TestStatus_PASS_WITH_ERRORS, 52 expected: true, 53 }, 54 { 55 status: statuspb.TestStatus_RUNNING, 56 }, 57 { 58 status: statuspb.TestStatus_CATEGORIZED_ABORT, 59 }, 60 { 61 status: statuspb.TestStatus_FLAKY, 62 }, 63 { 64 status: statuspb.TestStatus_FAIL, 65 }, 66 } 67 68 for _, tc := range cases { 69 t.Run(tc.status.String(), func(t *testing.T) { 70 if actual := Passing(tc.status); actual != tc.expected { 71 t.Errorf("Passing(%v) got %t, want %t", tc.status, actual, tc.expected) 72 } 73 }) 74 } 75 } 76 77 func TestFailing(t *testing.T) { 78 cases := []struct { 79 status statuspb.TestStatus 80 expected bool 81 }{ 82 { 83 status: statuspb.TestStatus_NO_RESULT, 84 }, 85 { 86 status: statuspb.TestStatus_BUILD_PASSED, 87 }, 88 { 89 status: statuspb.TestStatus_PASS, 90 }, 91 { 92 status: statuspb.TestStatus_RUNNING, 93 }, 94 { 95 status: statuspb.TestStatus_CATEGORIZED_ABORT, 96 }, 97 { 98 status: statuspb.TestStatus_UNKNOWN, 99 }, 100 { 101 status: statuspb.TestStatus_CANCEL, 102 }, 103 { 104 status: statuspb.TestStatus_FLAKY, 105 }, 106 { 107 status: statuspb.TestStatus_TOOL_FAIL, 108 expected: true, 109 }, 110 { 111 status: statuspb.TestStatus_TIMED_OUT, 112 expected: true, 113 }, 114 { 115 status: statuspb.TestStatus_CATEGORIZED_FAIL, 116 expected: true, 117 }, 118 { 119 status: statuspb.TestStatus_BUILD_FAIL, 120 expected: true, 121 }, 122 { 123 status: statuspb.TestStatus_FAIL, 124 expected: true, 125 }, 126 } 127 128 for _, tc := range cases { 129 t.Run(tc.status.String(), func(t *testing.T) { 130 if actual := Failing(tc.status); actual != tc.expected { 131 t.Errorf("Failing(%v) got %t, want %t", tc.status, actual, tc.expected) 132 } 133 }) 134 } 135 } 136 137 func TestIgnored(t *testing.T) { 138 cases := []struct { 139 name string 140 status statuspb.TestStatus 141 opts *configpb.DashboardTabStatusCustomizationOptions 142 expected bool 143 }{ 144 { 145 name: "categorized abort ignored", 146 status: statuspb.TestStatus_CATEGORIZED_ABORT, 147 opts: &configpb.DashboardTabStatusCustomizationOptions{ 148 IgnoredTestStatuses: []configpb.DashboardTabStatusCustomizationOptions_IgnoredTestStatus{ 149 configpb.DashboardTabStatusCustomizationOptions_CATEGORIZED_ABORT, 150 }, 151 }, 152 expected: true, 153 }, 154 { 155 name: "categorized abort not ignored", 156 status: statuspb.TestStatus_CATEGORIZED_ABORT, 157 opts: &configpb.DashboardTabStatusCustomizationOptions{ 158 IgnoredTestStatuses: []configpb.DashboardTabStatusCustomizationOptions_IgnoredTestStatus{ 159 configpb.DashboardTabStatusCustomizationOptions_UNKNOWN, 160 configpb.DashboardTabStatusCustomizationOptions_BLOCKED, 161 }, 162 }, 163 }, 164 { 165 name: "unknown ignored", 166 status: statuspb.TestStatus_UNKNOWN, 167 opts: &configpb.DashboardTabStatusCustomizationOptions{ 168 IgnoredTestStatuses: []configpb.DashboardTabStatusCustomizationOptions_IgnoredTestStatus{ 169 configpb.DashboardTabStatusCustomizationOptions_UNKNOWN, 170 configpb.DashboardTabStatusCustomizationOptions_CANCEL, 171 }, 172 }, 173 expected: true, 174 }, 175 { 176 name: "unknown not ignored", 177 status: statuspb.TestStatus_UNKNOWN, 178 opts: &configpb.DashboardTabStatusCustomizationOptions{ 179 IgnoredTestStatuses: []configpb.DashboardTabStatusCustomizationOptions_IgnoredTestStatus{ 180 configpb.DashboardTabStatusCustomizationOptions_BLOCKED, 181 configpb.DashboardTabStatusCustomizationOptions_CANCEL, 182 }, 183 }, 184 }, 185 { 186 name: "cancel ignored", 187 status: statuspb.TestStatus_CANCEL, 188 opts: &configpb.DashboardTabStatusCustomizationOptions{ 189 IgnoredTestStatuses: []configpb.DashboardTabStatusCustomizationOptions_IgnoredTestStatus{ 190 configpb.DashboardTabStatusCustomizationOptions_CANCEL, 191 configpb.DashboardTabStatusCustomizationOptions_BLOCKED, 192 }, 193 }, 194 expected: true, 195 }, 196 { 197 name: "cancel not ignored", 198 status: statuspb.TestStatus_CANCEL, 199 opts: &configpb.DashboardTabStatusCustomizationOptions{ 200 IgnoredTestStatuses: []configpb.DashboardTabStatusCustomizationOptions_IgnoredTestStatus{ 201 configpb.DashboardTabStatusCustomizationOptions_BLOCKED, 202 }, 203 }, 204 }, 205 { 206 name: "blocked ignored", 207 status: statuspb.TestStatus_BLOCKED, 208 opts: &configpb.DashboardTabStatusCustomizationOptions{ 209 IgnoredTestStatuses: []configpb.DashboardTabStatusCustomizationOptions_IgnoredTestStatus{ 210 configpb.DashboardTabStatusCustomizationOptions_CANCEL, 211 configpb.DashboardTabStatusCustomizationOptions_BLOCKED, 212 }, 213 }, 214 expected: true, 215 }, 216 { 217 name: "blocked not ignored", 218 status: statuspb.TestStatus_BLOCKED, 219 opts: &configpb.DashboardTabStatusCustomizationOptions{ 220 IgnoredTestStatuses: []configpb.DashboardTabStatusCustomizationOptions_IgnoredTestStatus{ 221 configpb.DashboardTabStatusCustomizationOptions_CANCEL, 222 }, 223 }, 224 }, 225 { 226 name: "ignored statuses not set", 227 status: statuspb.TestStatus_BLOCKED, 228 }, 229 } 230 231 for _, tc := range cases { 232 t.Run(tc.name, func(t *testing.T) { 233 if actual := Ignored(tc.status, tc.opts); actual != tc.expected { 234 t.Errorf("Ignored(%v) got %t, want %t", tc.status, actual, tc.expected) 235 } 236 }) 237 } 238 } 239 240 func TestCoalesce(t *testing.T) { 241 cases := []struct { 242 status statuspb.TestStatus 243 ignoreRunning bool 244 expected statuspb.TestStatus 245 }{ 246 { 247 status: statuspb.TestStatus_NO_RESULT, 248 ignoreRunning: true, 249 expected: statuspb.TestStatus_NO_RESULT, 250 }, 251 { 252 status: statuspb.TestStatus_NO_RESULT, 253 expected: statuspb.TestStatus_NO_RESULT, 254 }, 255 { 256 status: statuspb.TestStatus_BUILD_PASSED, 257 expected: statuspb.TestStatus_PASS, 258 }, 259 { 260 status: statuspb.TestStatus_PASS, 261 expected: statuspb.TestStatus_PASS, 262 }, 263 { 264 status: statuspb.TestStatus_PASS_WITH_ERRORS, 265 ignoreRunning: true, 266 expected: statuspb.TestStatus_PASS, 267 }, 268 { 269 status: statuspb.TestStatus_RUNNING, 270 ignoreRunning: true, 271 expected: statuspb.TestStatus_NO_RESULT, 272 }, 273 { 274 status: statuspb.TestStatus_RUNNING, 275 expected: statuspb.TestStatus_UNKNOWN, 276 }, 277 { 278 status: statuspb.TestStatus_CANCEL, 279 expected: statuspb.TestStatus_UNKNOWN, 280 }, 281 { 282 status: statuspb.TestStatus_TOOL_FAIL, 283 ignoreRunning: true, 284 expected: statuspb.TestStatus_FAIL, 285 }, 286 { 287 status: statuspb.TestStatus_TOOL_FAIL, 288 expected: statuspb.TestStatus_FAIL, 289 }, 290 { 291 status: statuspb.TestStatus_CATEGORIZED_FAIL, 292 ignoreRunning: true, 293 expected: statuspb.TestStatus_FAIL, 294 }, 295 { 296 status: statuspb.TestStatus_FAIL, 297 expected: statuspb.TestStatus_FAIL, 298 }, 299 } 300 301 for _, tc := range cases { 302 name := fmt.Sprintf("Coalesce(%v,%t)", tc.status, tc.ignoreRunning) 303 t.Run(name, func(t *testing.T) { 304 if actual := Coalesce(tc.status, tc.ignoreRunning); actual != tc.expected { 305 t.Errorf("got %v, want %v", actual, tc.expected) 306 } 307 }) 308 } 309 } 310 311 func TestIter(t *testing.T) { 312 stoi := func(s statuspb.TestStatus) int32 { return int32(s) } 313 cases := []struct { 314 name string 315 ctx context.Context 316 results []int32 317 expected []statuspb.TestStatus 318 }{ 319 { 320 name: "basically works", 321 }, 322 { 323 name: "works correctly", 324 results: []int32{ 325 stoi(statuspb.TestStatus_FAIL), 1, 326 stoi(statuspb.TestStatus_PASS), 2, 327 stoi(statuspb.TestStatus_FLAKY), 3, 328 }, 329 expected: []statuspb.TestStatus{ 330 statuspb.TestStatus_FAIL, 331 statuspb.TestStatus_PASS, 332 statuspb.TestStatus_PASS, 333 statuspb.TestStatus_FLAKY, 334 statuspb.TestStatus_FLAKY, 335 statuspb.TestStatus_FLAKY, 336 }, 337 }, 338 } 339 340 for _, tc := range cases { 341 t.Run(tc.name, func(t *testing.T) { 342 if tc.ctx == nil { 343 tc.ctx = context.Background() 344 } 345 var actual []statuspb.TestStatus 346 f := Iter(tc.results) 347 for { 348 item, more := f() 349 if !more { 350 break 351 } 352 actual = append(actual, item) 353 } 354 if !reflect.DeepEqual(actual, tc.expected) { 355 t.Errorf("Iter(%v) got %v, want %v", tc.results, actual, tc.expected) 356 } 357 }) 358 } 359 } 360 361 const totalResults = 10e6 362 363 func benchmarkResults(remain int32) []int32 { 364 rand.Seed(42) 365 366 var statuses []int32 367 for num := range statuspb.TestStatus_name { 368 statuses = append(statuses, num) 369 } 370 var results []int32 371 n := int32(len(statuses)) 372 for remain > 0 { 373 result := rand.Int31() % n 374 count := rand.Int31() % 100 375 if count > remain { 376 count = remain 377 } 378 results = append(results, result, count) 379 remain -= count 380 } 381 return results 382 } 383 384 func BenchmarkIter(b *testing.B) { 385 386 loopCh := func(results []int32) int32 { 387 var n int32 388 ch := iterSlow(context.Background(), results) 389 for range ch { 390 n++ 391 } 392 return n 393 } 394 395 loopF := func(results []int32) int32 { 396 var n int32 397 f := iterFast(results) 398 for { 399 _, more := f() 400 if !more { 401 break 402 } 403 n++ 404 } 405 return n 406 } 407 408 const ( 409 few = 1e6 410 many = 10e6 411 ) 412 413 cases := []struct { 414 name string 415 results int32 416 loop func([]int32) int32 417 }{ 418 { 419 name: "few chan", 420 results: few, 421 loop: loopCh, 422 }, 423 { 424 name: "few func", 425 results: few, 426 loop: loopF, 427 }, 428 { 429 name: "many chan", 430 results: many, 431 loop: loopCh, 432 }, 433 { 434 name: "many func", 435 results: many, 436 loop: loopF, 437 }, 438 } 439 440 for _, tc := range cases { 441 b.Run(tc.name, func(b *testing.B) { 442 results := benchmarkResults(int32(tc.results)) 443 b.ResetTimer() 444 for i := 0; i < b.N; i++ { 445 n := tc.loop(results) 446 if n != tc.results { 447 b.Fatalf("Got %d results, wanted %d", n, tc.results) 448 } 449 } 450 }) 451 } 452 }