|
35 | 35 | import static org.junit.jupiter.api.Assertions.assertTrue; |
36 | 36 | import static org.junit.jupiter.api.Assertions.fail; |
37 | 37 |
|
| 38 | +import hudson.AbortException; |
38 | 39 | import hudson.model.Queue; |
39 | 40 | import hudson.model.Result; |
40 | 41 | import hudson.model.queue.QueueTaskFuture; |
@@ -364,6 +365,206 @@ void testBasicParallelFail() throws Exception { |
364 | 365 | assertEquals(totalBranchTiming, finalTiming.getTotalDurationMillis()); |
365 | 366 | } |
366 | 367 |
|
| 368 | + @Issue("GH#794") |
| 369 | + @Test |
| 370 | + void testParallelFailInProgress() throws Exception { |
| 371 | + WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "ParallelFailsInProgress"); |
| 372 | + job.setDefinition(new CpsFlowDefinition( |
| 373 | + """ |
| 374 | + pipeline { |
| 375 | + agent any |
| 376 | + stages { |
| 377 | + stage('Tests') { |
| 378 | + parallel { |
| 379 | + stage('Failure caught') { |
| 380 | + steps { |
| 381 | + catchError(buildResult: 'SUCCESS', catchInterruptions: false, stageResult: 'FAILURE') { |
| 382 | + semaphore 'catchError' |
| 383 | + } |
| 384 | + } |
| 385 | + } |
| 386 | + stage('Fails') { |
| 387 | + steps { |
| 388 | + semaphore 'error' |
| 389 | + } |
| 390 | + } |
| 391 | + stage('Passes') { |
| 392 | + steps { |
| 393 | + semaphore 'prewait' |
| 394 | + sleep 1 |
| 395 | + semaphore 'wait' |
| 396 | + } |
| 397 | + } |
| 398 | + } |
| 399 | + } |
| 400 | + } |
| 401 | + } |
| 402 | + """, |
| 403 | + true)); |
| 404 | + |
| 405 | + /* |
| 406 | + Node dump follows, format: |
| 407 | + [ID]{parent,ids}(millisSinceStartOfRun) flowNodeClassName stepDisplayName [st=startId if a block end node] |
| 408 | + Action format: |
| 409 | + - actionClassName actionDisplayName |
| 410 | + ------------------------------------------------------------------------------------------ |
| 411 | + [2]{}FlowStartNode Start of Pipeline |
| 412 | + [3]{2}StepStartNode Allocate node : Start |
| 413 | + -LogStorageAction Console Output |
| 414 | + -QueueItemActionImpl null |
| 415 | + -WorkspaceActionImpl Workspace |
| 416 | + [4]{3}StepStartNode Allocate node : Body : Start |
| 417 | + -BodyInvocationAction null |
| 418 | + [5]{4}StepStartNode Stage : Start |
| 419 | + -LogStorageAction Console Output |
| 420 | + -ArgumentsActionImpl null |
| 421 | + [6]{5}StepStartNode Tests |
| 422 | + -BodyInvocationAction null |
| 423 | + -LabelAction Tests |
| 424 | + -TagsAction Tags |
| 425 | + [7]{6}StepStartNode Execute in parallel : Start |
| 426 | + -LogStorageAction Console Output |
| 427 | + [10]{7}StepStartNode Branch: Failure caught |
| 428 | + -BodyInvocationAction null |
| 429 | + -ParallelLabelAction Branch: Failure caught |
| 430 | + [11]{7}StepStartNode Branch: Fails |
| 431 | + -BodyInvocationAction null |
| 432 | + -ParallelLabelAction Branch: Fails |
| 433 | + -TagsAction Tags |
| 434 | + [12]{7}StepStartNode Branch: Passes |
| 435 | + -BodyInvocationAction null |
| 436 | + -ParallelLabelAction Branch: Passes |
| 437 | + [13]{10}StepStartNode Stage : Start |
| 438 | + -LogStorageAction Console Output |
| 439 | + -ArgumentsActionImpl null |
| 440 | + [14]{13}StepStartNode Failure caught |
| 441 | + -BodyInvocationAction null |
| 442 | + -LabelAction Failure caught |
| 443 | + [15]{11}StepStartNode Stage : Start |
| 444 | + -LogStorageAction Console Output |
| 445 | + -ArgumentsActionImpl null |
| 446 | + [16]{15}StepStartNode Fails |
| 447 | + -BodyInvocationAction null |
| 448 | + -LabelAction Fails |
| 449 | + -TagsAction Tags |
| 450 | + [17]{12}StepStartNode Stage : Start |
| 451 | + -LogStorageAction Console Output |
| 452 | + -ArgumentsActionImpl null |
| 453 | + [18]{17}StepStartNode Passes |
| 454 | + -BodyInvocationAction null |
| 455 | + -LabelAction Passes |
| 456 | + [19]{14}StepStartNode Catch error and set build result to failure : Start |
| 457 | + -LogStorageAction Console Output |
| 458 | + -ArgumentsActionImpl null |
| 459 | + [20]{19}StepStartNode Catch error and set build result to failure : Body : Start |
| 460 | + -BodyInvocationAction null |
| 461 | + [21]{16}StepAtomNode Test step |
| 462 | + -ArgumentsActionImpl null |
| 463 | + -LogStorageAction Console Output |
| 464 | + -ErrorAction failure |
| 465 | + [22]{18}StepAtomNode Test step |
| 466 | + -ArgumentsActionImpl null |
| 467 | + -LogStorageAction Console Output |
| 468 | + [23]{20}StepAtomNode Test step |
| 469 | + -ArgumentsActionImpl null |
| 470 | + -LogStorageAction Console Output |
| 471 | + -ErrorAction failure |
| 472 | + [24]{21}StepEndNode Stage : Body : End [st=16] |
| 473 | + -BodyInvocationAction null |
| 474 | + -ErrorAction failure |
| 475 | + [25]{22}StepAtomNode Sleep |
| 476 | + -ArgumentsActionImpl null |
| 477 | + -LogStorageAction Console Output |
| 478 | + [26]{23}StepEndNode Catch error and set build result to failure : Body : End [st=20] |
| 479 | + -BodyInvocationAction null |
| 480 | + -ErrorAction failure |
| 481 | + -LogStorageAction Console Output |
| 482 | + -WarningAction Warning |
| 483 | + [27]{24}StepEndNode Stage : End [st=15] |
| 484 | + -ErrorAction failure |
| 485 | + [28]{26}StepEndNode Catch error and set build result to failure : End [st=19] |
| 486 | + [29]{27}StepEndNode Execute in parallel : Body : End [st=11] |
| 487 | + -BodyInvocationAction null |
| 488 | + -ErrorAction failure |
| 489 | + -LogStorageAction Console Output |
| 490 | + [30]{28}StepEndNode Stage : Body : End [st=14] |
| 491 | + -BodyInvocationAction null |
| 492 | + [31]{30}StepEndNode Stage : End [st=13] |
| 493 | + [32]{31}StepEndNode Execute in parallel : Body : End [st=10] |
| 494 | + -BodyInvocationAction null |
| 495 | + [33]{25}StepAtomNode Test step |
| 496 | + -ArgumentsActionImpl null |
| 497 | + -LogStorageAction Console Output |
| 498 | + [34]{33}StepEndNode Stage : Body : End [st=18] |
| 499 | + -BodyInvocationAction null |
| 500 | + [35]{34}StepEndNode Stage : End [st=17] |
| 501 | + [36]{35}StepEndNode Execute in parallel : Body : End [st=12] |
| 502 | + -BodyInvocationAction null |
| 503 | + [37]{32,29,36}StepEndNode Execute in parallel : End [st=7] |
| 504 | + -ErrorAction failure |
| 505 | + [38]{37}StepEndNode Stage : Body : End [st=6] |
| 506 | + -BodyInvocationAction null |
| 507 | + -ErrorAction failure |
| 508 | + [39]{38}StepEndNode Stage : End [st=5] |
| 509 | + -ErrorAction failure |
| 510 | + [40]{39}StepEndNode Allocate node : Body : End [st=4] |
| 511 | + -BodyInvocationAction null |
| 512 | + -ErrorAction failure |
| 513 | + [41]{40}StepEndNode Allocate node : End [st=3] |
| 514 | + -ErrorAction failure |
| 515 | + -ErrorAction failure |
| 516 | + [42]{41}FlowEndNode End of Pipeline [st=2] |
| 517 | + -ErrorAction failure |
| 518 | + */ |
| 519 | + |
| 520 | + WorkflowRun run = job.scheduleBuild2(0).waitForStart(); |
| 521 | + SemaphoreStep.waitForStart("catchError/1", run); |
| 522 | + SemaphoreStep.failure("catchError/1", new AbortException("failure")); |
| 523 | + SemaphoreStep.waitForStart("error/1", run); |
| 524 | + SemaphoreStep.failure("error/1", new AbortException("failure")); |
| 525 | + SemaphoreStep.waitForStart("prewait/1", run); |
| 526 | + SemaphoreStep.success("prewait/1", null); |
| 527 | + SemaphoreStep.waitForStart("wait/1", run); |
| 528 | + |
| 529 | + FlowExecution exec = run.getExecution(); |
| 530 | + |
| 531 | + // Failure Caught branch |
| 532 | + assertEquals( |
| 533 | + GenericStatus.FAILURE, |
| 534 | + StatusAndTiming.computeChunkStatus2( |
| 535 | + run, exec.getNode("7"), exec.getNode("10"), exec.getNode("32"), null)); |
| 536 | + |
| 537 | + // Failing branch |
| 538 | + assertEquals( |
| 539 | + GenericStatus.FAILURE, |
| 540 | + StatusAndTiming.computeChunkStatus2( |
| 541 | + run, exec.getNode("7"), exec.getNode("11"), exec.getNode("29"), null)); |
| 542 | + |
| 543 | + // In-progress Passing branch |
| 544 | + assertEquals( |
| 545 | + GenericStatus.IN_PROGRESS, |
| 546 | + StatusAndTiming.computeChunkStatus2( |
| 547 | + run, exec.getNode("7"), exec.getNode("12"), exec.getNode("33"), null)); |
| 548 | + |
| 549 | + // Check that branch statuses match |
| 550 | + List<BlockStartNode> parallelStarts = |
| 551 | + Arrays.asList((BlockStartNode) exec.getNode("10"), (BlockStartNode) exec.getNode("11")); |
| 552 | + List<FlowNode> parallelEnds = Arrays.asList(exec.getNode("32"), exec.getNode("29")); |
| 553 | + Map<String, GenericStatus> branchStatuses = |
| 554 | + StatusAndTiming.computeBranchStatuses2(run, exec.getNode("7"), parallelStarts, parallelEnds, null); |
| 555 | + |
| 556 | + assertEquals(2, branchStatuses.size()); |
| 557 | + String[] branches = {"Fails", "Failure caught"}; |
| 558 | + List<String> outputBranchList = new ArrayList<>(branchStatuses.keySet()); |
| 559 | + Collections.sort(outputBranchList); |
| 560 | + assertArrayEquals(branches, outputBranchList.toArray()); |
| 561 | + assertEquals(GenericStatus.FAILURE, branchStatuses.get("Fails")); |
| 562 | + assertEquals(GenericStatus.FAILURE, branchStatuses.get("Failure caught")); |
| 563 | + |
| 564 | + SemaphoreStep.success("wait/1", null); |
| 565 | + j.assertBuildStatus(Result.FAILURE, j.waitForCompletion(run)); |
| 566 | + } |
| 567 | + |
367 | 568 | @Test |
368 | 569 | void testInProgress() throws Exception { |
369 | 570 | WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "Fails"); |
|
0 commit comments