001/* 002 * SonarQube 003 * Copyright (C) 2009-2018 SonarSource SA 004 * mailto:info AT sonarsource DOT com 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.api.ce.posttask; 021 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.Objects; 029import java.util.Optional; 030import javax.annotation.CheckForNull; 031import javax.annotation.Nullable; 032 033import static com.google.common.base.Preconditions.checkArgument; 034import static com.google.common.base.Preconditions.checkState; 035import static java.util.Objects.requireNonNull; 036 037/** 038 * This class can be used to test {@link PostProjectAnalysisTask} implementations, see example below: 039 * <pre> 040 * import static org.assertj.core.api.Assertions.assertThat; 041 * import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newCeTaskBuilder; 042 * import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newConditionBuilder; 043 * import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newProjectBuilder; 044 * import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newQualityGateBuilder; 045 * 046 * public class CaptorPostProjectAnalysisTaskTest { 047 * private class CaptorPostProjectAnalysisTask implements PostProjectAnalysisTask { 048 * private ProjectAnalysis projectAnalysis; 049 * 050 * {@literal @}Override 051 * public void finished(ProjectAnalysis analysis) { 052 * this.projectAnalysis = analysis; 053 * } 054 * } 055 * 056 * {@literal @}Test 057 * public void execute_is_passed_a_non_null_ProjectAnalysis_object() { 058 * CaptorPostProjectAnalysisTask postProjectAnalysisTask = new CaptorPostProjectAnalysisTask(); 059 * 060 * PostProjectAnalysisTaskTester.of(postProjectAnalysisTask) 061 * .withCeTask( 062 * newCeTaskBuilder() 063 * .setId("id") 064 * .setStatus(CeTask.Status.SUCCESS) 065 * .build()) 066 * .withProject( 067 * PostProjectAnalysisTaskTester.newProjectBuilder() 068 * .setUuid("uuid") 069 * .setKey("key") 070 * .setName("name") 071 * .build()) 072 * .at(new Date()) 073 * .withAnalysisUuid("uuid") 074 * .withQualityGate( 075 * newQualityGateBuilder() 076 * .setId("id") 077 * .setName("name") 078 * .setStatus(QualityGate.Status.OK) 079 * .add( 080 * newConditionBuilder() 081 * .setMetricKey("metric key") 082 * .setOperator(QualityGate.Operator.GREATER_THAN) 083 * .setErrorThreshold("12") 084 * .setOnLeakPeriod(true) 085 * .build(QualityGate.EvaluationStatus.OK, "value")) 086 * .build()) 087 * .execute(); 088 * 089 * assertThat(postProjectAnalysisTask.projectAnalysis).isNotNull(); 090 * } 091 * } 092 * </pre> 093 * 094 * @since 5.5 095 */ 096public class PostProjectAnalysisTaskTester { 097 private static final String DATE_CAN_NOT_BE_NULL = "date cannot be null"; 098 private static final String PROJECT_CAN_NOT_BE_NULL = "project cannot be null"; 099 private static final String CE_TASK_CAN_NOT_BE_NULL = "ceTask cannot be null"; 100 private static final String STATUS_CAN_NOT_BE_NULL = "status cannot be null"; 101 private static final String SCANNER_CONTEXT_CAN_NOT_BE_NULL = "scannerContext cannot be null"; 102 private static final String KEY_CAN_NOT_BE_NULL = "key cannot be null"; 103 private static final String NAME_CAN_NOT_BE_NULL = "name cannot be null"; 104 105 private final PostProjectAnalysisTask underTest; 106 @Nullable 107 private Organization organization; 108 @CheckForNull 109 private CeTask ceTask; 110 @CheckForNull 111 private Project project; 112 @CheckForNull 113 private Date date; 114 @CheckForNull 115 private QualityGate qualityGate; 116 @CheckForNull 117 private Branch branch; 118 private ScannerContext scannerContext; 119 private String analysisUuid; 120 121 private PostProjectAnalysisTaskTester(PostProjectAnalysisTask underTest) { 122 this.underTest = requireNonNull(underTest, "PostProjectAnalysisTask instance cannot be null"); 123 } 124 125 public static PostProjectAnalysisTaskTester of(PostProjectAnalysisTask underTest) { 126 return new PostProjectAnalysisTaskTester(underTest); 127 } 128 129 /** 130 * @since 7.0 131 */ 132 public static OrganizationBuilder newOrganizationBuilder() { 133 return new OrganizationBuilder(); 134 } 135 136 public static CeTaskBuilder newCeTaskBuilder() { 137 return new CeTaskBuilder(); 138 } 139 140 public static ProjectBuilder newProjectBuilder() { 141 return new ProjectBuilder(); 142 } 143 144 public static BranchBuilder newBranchBuilder() { 145 return new BranchBuilder(); 146 } 147 148 public static QualityGateBuilder newQualityGateBuilder() { 149 return new QualityGateBuilder(); 150 } 151 152 public static ConditionBuilder newConditionBuilder() { 153 return new ConditionBuilder(); 154 } 155 156 public static ScannerContextBuilder newScannerContextBuilder() { 157 return new ScannerContextBuilder(); 158 } 159 160 /** 161 * @since 7.0 162 */ 163 public PostProjectAnalysisTaskTester withOrganization(@Nullable Organization organization) { 164 this.organization = organization; 165 return this; 166 } 167 168 public PostProjectAnalysisTaskTester withCeTask(CeTask ceTask) { 169 this.ceTask = requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL); 170 return this; 171 } 172 173 public PostProjectAnalysisTaskTester withProject(Project project) { 174 this.project = requireNonNull(project, PROJECT_CAN_NOT_BE_NULL); 175 return this; 176 } 177 178 /** 179 * @since 6.1 180 */ 181 public PostProjectAnalysisTaskTester withScannerContext(ScannerContext scannerContext) { 182 this.scannerContext = requireNonNull(scannerContext, SCANNER_CONTEXT_CAN_NOT_BE_NULL); 183 return this; 184 } 185 186 public PostProjectAnalysisTaskTester at(Date date) { 187 this.date = requireNonNull(date, DATE_CAN_NOT_BE_NULL); 188 return this; 189 } 190 191 public PostProjectAnalysisTaskTester withQualityGate(@Nullable QualityGate qualityGate) { 192 this.qualityGate = qualityGate; 193 return this; 194 } 195 196 public PostProjectAnalysisTaskTester withBranch(@Nullable Branch b) { 197 this.branch = b; 198 return this; 199 } 200 201 /** 202 * @since 6.6 203 */ 204 public PostProjectAnalysisTaskTester withAnalysisUuid(@Nullable String analysisUuid) { 205 this.analysisUuid = analysisUuid; 206 return this; 207 } 208 209 public PostProjectAnalysisTask.ProjectAnalysis execute() { 210 requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL); 211 requireNonNull(project, PROJECT_CAN_NOT_BE_NULL); 212 requireNonNull(date, DATE_CAN_NOT_BE_NULL); 213 214 Analysis analysis = null; 215 if (analysisUuid != null) { 216 analysis = new AnalysisBuilder() 217 .setDate(date) 218 .setAnalysisUuid(analysisUuid) 219 .build(); 220 } 221 222 PostProjectAnalysisTask.ProjectAnalysis projectAnalysis = new ProjectAnalysisBuilder() 223 .setOrganization(organization) 224 .setCeTask(ceTask) 225 .setProject(project) 226 .setBranch(branch) 227 .setQualityGate(qualityGate) 228 .setAnalysis(analysis) 229 .setScannerContext(scannerContext) 230 .setDate(date) 231 .build(); 232 233 this.underTest. 234 finished(projectAnalysis); 235 236 return projectAnalysis; 237 } 238 239 public static final class OrganizationBuilder { 240 @CheckForNull 241 private String name; 242 @CheckForNull 243 private String key; 244 245 private OrganizationBuilder() { 246 // prevents instantiation 247 } 248 249 public OrganizationBuilder setName(String name) { 250 this.name = requireNonNull(name, NAME_CAN_NOT_BE_NULL); 251 return this; 252 } 253 254 public OrganizationBuilder setKey(String key) { 255 this.key = requireNonNull(key, KEY_CAN_NOT_BE_NULL); 256 return this; 257 } 258 259 public Organization build() { 260 requireNonNull(this.name, NAME_CAN_NOT_BE_NULL); 261 requireNonNull(this.key, KEY_CAN_NOT_BE_NULL); 262 return new Organization() { 263 @Override 264 public String getName() { 265 return name; 266 } 267 268 @Override 269 public String getKey() { 270 return key; 271 } 272 273 @Override 274 public String toString() { 275 return "Organization{" + 276 "name='" + name + '\'' + 277 ", key='" + key + '\'' + 278 '}'; 279 } 280 }; 281 } 282 } 283 284 public static final class CeTaskBuilder { 285 private static final String ID_CAN_NOT_BE_NULL = "id cannot be null"; 286 287 @CheckForNull 288 private String id; 289 @CheckForNull 290 private CeTask.Status status; 291 292 private CeTaskBuilder() { 293 // prevents instantiation outside PostProjectAnalysisTaskTester 294 } 295 296 public CeTaskBuilder setId(String id) { 297 this.id = requireNonNull(id, ID_CAN_NOT_BE_NULL); 298 return this; 299 } 300 301 public CeTaskBuilder setStatus(CeTask.Status status) { 302 this.status = requireNonNull(status, STATUS_CAN_NOT_BE_NULL); 303 return this; 304 } 305 306 public CeTask build() { 307 requireNonNull(id, ID_CAN_NOT_BE_NULL); 308 requireNonNull(status, STATUS_CAN_NOT_BE_NULL); 309 return new CeTask() { 310 @Override 311 public String getId() { 312 return id; 313 } 314 315 @Override 316 public Status getStatus() { 317 return status; 318 } 319 320 @Override 321 public String toString() { 322 return "CeTask{" + 323 "id='" + id + '\'' + 324 ", status=" + status + 325 '}'; 326 } 327 }; 328 } 329 } 330 331 public static final class ProjectBuilder { 332 private static final String UUID_CAN_NOT_BE_NULL = "uuid cannot be null"; 333 private String uuid; 334 private String key; 335 private String name; 336 337 private ProjectBuilder() { 338 // prevents instantiation outside PostProjectAnalysisTaskTester 339 } 340 341 public ProjectBuilder setUuid(String uuid) { 342 this.uuid = requireNonNull(uuid, UUID_CAN_NOT_BE_NULL); 343 return this; 344 } 345 346 public ProjectBuilder setKey(String key) { 347 this.key = requireNonNull(key, KEY_CAN_NOT_BE_NULL); 348 return this; 349 } 350 351 public ProjectBuilder setName(String name) { 352 this.name = requireNonNull(name, NAME_CAN_NOT_BE_NULL); 353 return this; 354 } 355 356 public Project build() { 357 requireNonNull(uuid, UUID_CAN_NOT_BE_NULL); 358 requireNonNull(key, KEY_CAN_NOT_BE_NULL); 359 requireNonNull(name, NAME_CAN_NOT_BE_NULL); 360 return new Project() { 361 @Override 362 public String getUuid() { 363 return uuid; 364 } 365 366 @Override 367 public String getKey() { 368 return key; 369 } 370 371 @Override 372 public String getName() { 373 return name; 374 } 375 376 @Override 377 public String toString() { 378 return "Project{" + 379 "uuid='" + uuid + '\'' + 380 ", key='" + key + '\'' + 381 ", name='" + name + '\'' + 382 '}'; 383 } 384 385 }; 386 } 387 } 388 389 public static final class BranchBuilder { 390 private boolean isMain = true; 391 private String name = null; 392 private Branch.Type type = Branch.Type.LONG; 393 394 private BranchBuilder() { 395 // prevents instantiation outside PostProjectAnalysisTaskTester 396 } 397 398 public BranchBuilder setName(@Nullable String s) { 399 this.name = s; 400 return this; 401 } 402 403 public BranchBuilder setType(Branch.Type t) { 404 this.type = Objects.requireNonNull(t); 405 return this; 406 } 407 408 public BranchBuilder setIsMain(boolean b) { 409 this.isMain = b; 410 return this; 411 } 412 413 public Branch build() { 414 return new Branch() { 415 416 @Override 417 public boolean isMain() { 418 return isMain; 419 } 420 421 @Override 422 public Optional<String> getName() { 423 return Optional.ofNullable(name); 424 } 425 426 @Override 427 public Type getType() { 428 return type; 429 } 430 }; 431 } 432 } 433 434 public static final class QualityGateBuilder { 435 private static final String ID_CAN_NOT_BE_NULL = "id cannot be null"; 436 private static final String NAME_CAN_NOT_BE_NULL = "name cannot be null"; 437 438 private String id; 439 private String name; 440 private QualityGate.Status status; 441 private final List<QualityGate.Condition> conditions = new ArrayList<>(); 442 443 private QualityGateBuilder() { 444 // prevents instantiation outside PostProjectAnalysisTaskTester 445 } 446 447 public QualityGateBuilder setId(String id) { 448 this.id = requireNonNull(id, ID_CAN_NOT_BE_NULL); 449 return this; 450 } 451 452 public QualityGateBuilder setName(String name) { 453 this.name = requireNonNull(name, NAME_CAN_NOT_BE_NULL); 454 return this; 455 } 456 457 public QualityGateBuilder setStatus(QualityGate.Status status) { 458 this.status = requireNonNull(status, STATUS_CAN_NOT_BE_NULL); 459 return this; 460 } 461 462 public QualityGateBuilder add(QualityGate.Condition condition) { 463 conditions.add(requireNonNull(condition, "condition cannot be null")); 464 return this; 465 } 466 467 public QualityGateBuilder clearConditions() { 468 this.conditions.clear(); 469 return this; 470 } 471 472 public QualityGate build() { 473 requireNonNull(id, ID_CAN_NOT_BE_NULL); 474 requireNonNull(name, NAME_CAN_NOT_BE_NULL); 475 requireNonNull(status, STATUS_CAN_NOT_BE_NULL); 476 477 return new QualityGate() { 478 @Override 479 public String getId() { 480 return id; 481 } 482 483 @Override 484 public String getName() { 485 return name; 486 } 487 488 @Override 489 public Status getStatus() { 490 return status; 491 } 492 493 @Override 494 public Collection<Condition> getConditions() { 495 return conditions; 496 } 497 498 @Override 499 public String toString() { 500 return "QualityGate{" + 501 "id='" + id + '\'' + 502 ", name='" + name + '\'' + 503 ", status=" + status + 504 ", conditions=" + conditions + 505 '}'; 506 } 507 }; 508 } 509 } 510 511 public static final class ConditionBuilder { 512 private static final String METRIC_KEY_CAN_NOT_BE_NULL = "metricKey cannot be null"; 513 private static final String OPERATOR_CAN_NOT_BE_NULL = "operator cannot be null"; 514 515 private String metricKey; 516 private QualityGate.Operator operator; 517 private String errorThreshold; 518 private String warningThreshold; 519 private boolean onLeakPeriod; 520 521 private ConditionBuilder() { 522 // prevents instantiation outside PostProjectAnalysisTaskTester 523 } 524 525 public ConditionBuilder setMetricKey(String metricKey) { 526 this.metricKey = requireNonNull(metricKey, METRIC_KEY_CAN_NOT_BE_NULL); 527 return this; 528 } 529 530 public ConditionBuilder setOperator(QualityGate.Operator operator) { 531 this.operator = requireNonNull(operator, OPERATOR_CAN_NOT_BE_NULL); 532 return this; 533 } 534 535 public ConditionBuilder setErrorThreshold(@Nullable String errorThreshold) { 536 this.errorThreshold = errorThreshold; 537 return this; 538 } 539 540 public ConditionBuilder setWarningThreshold(@Nullable String warningThreshold) { 541 this.warningThreshold = warningThreshold; 542 return this; 543 } 544 545 public ConditionBuilder setOnLeakPeriod(boolean onLeakPeriod) { 546 this.onLeakPeriod = onLeakPeriod; 547 return this; 548 } 549 550 public QualityGate.Condition buildNoValue() { 551 checkCommonProperties(); 552 return new QualityGate.Condition() { 553 @Override 554 public QualityGate.EvaluationStatus getStatus() { 555 return QualityGate.EvaluationStatus.NO_VALUE; 556 } 557 558 @Override 559 public String getMetricKey() { 560 return metricKey; 561 } 562 563 @Override 564 public QualityGate.Operator getOperator() { 565 return operator; 566 } 567 568 @Override 569 public String getErrorThreshold() { 570 return errorThreshold; 571 } 572 573 @Override 574 public String getWarningThreshold() { 575 return warningThreshold; 576 } 577 578 @Override 579 public boolean isOnLeakPeriod() { 580 return onLeakPeriod; 581 } 582 583 @Override 584 public String getValue() { 585 throw new IllegalStateException("There is no value when status is NO_VALUE"); 586 } 587 588 @Override 589 public String toString() { 590 return "Condition{" + 591 "status=" + QualityGate.EvaluationStatus.NO_VALUE + 592 ", metricKey='" + metricKey + '\'' + 593 ", operator=" + operator + 594 ", errorThreshold='" + errorThreshold + '\'' + 595 ", warningThreshold='" + warningThreshold + '\'' + 596 ", onLeakPeriod=" + onLeakPeriod + 597 '}'; 598 } 599 }; 600 } 601 602 public QualityGate.Condition build(final QualityGate.EvaluationStatus status, final String value) { 603 checkCommonProperties(); 604 requireNonNull(status, STATUS_CAN_NOT_BE_NULL); 605 checkArgument(status != QualityGate.EvaluationStatus.NO_VALUE, "status cannot be NO_VALUE, use method buildNoValue() instead"); 606 requireNonNull(value, "value cannot be null, use method buildNoValue() instead"); 607 return new QualityGate.Condition() { 608 @Override 609 public QualityGate.EvaluationStatus getStatus() { 610 return status; 611 } 612 613 @Override 614 public String getMetricKey() { 615 return metricKey; 616 } 617 618 @Override 619 public QualityGate.Operator getOperator() { 620 return operator; 621 } 622 623 @Override 624 public String getErrorThreshold() { 625 return errorThreshold; 626 } 627 628 @Override 629 public String getWarningThreshold() { 630 return warningThreshold; 631 } 632 633 @Override 634 public boolean isOnLeakPeriod() { 635 return onLeakPeriod; 636 } 637 638 @Override 639 public String getValue() { 640 return value; 641 } 642 643 @Override 644 public String toString() { 645 return "Condition{" + 646 "status=" + status + 647 ", metricKey='" + metricKey + '\'' + 648 ", operator=" + operator + 649 ", errorThreshold='" + errorThreshold + '\'' + 650 ", warningThreshold='" + warningThreshold + '\'' + 651 ", onLeakPeriod=" + onLeakPeriod + 652 ", value='" + value + '\'' + 653 '}'; 654 } 655 }; 656 } 657 658 private void checkCommonProperties() { 659 requireNonNull(metricKey, METRIC_KEY_CAN_NOT_BE_NULL); 660 requireNonNull(operator, OPERATOR_CAN_NOT_BE_NULL); 661 checkState(errorThreshold != null || warningThreshold != null, "At least one of errorThreshold and warningThreshold must be non null"); 662 } 663 } 664 665 public static final class ScannerContextBuilder { 666 private final Map<String, String> properties = new HashMap<>(); 667 668 private ScannerContextBuilder() { 669 // prevents instantiation outside PostProjectAnalysisTaskTester 670 } 671 672 public ScannerContextBuilder addProperties(Map<String, String> map) { 673 properties.putAll(map); 674 return this; 675 } 676 677 public ScannerContext build() { 678 return () -> properties; 679 } 680 } 681 682 public static final class ProjectAnalysisBuilder { 683 private Organization organization; 684 private CeTask ceTask; 685 private Project project; 686 private Branch branch; 687 private QualityGate qualityGate; 688 private Analysis analysis; 689 private ScannerContext scannerContext; 690 private Date date; 691 692 private ProjectAnalysisBuilder() { 693 // prevents instantiation outside PostProjectAnalysisTaskTester 694 } 695 696 public ProjectAnalysisBuilder setOrganization(@Nullable Organization organization) { 697 this.organization = organization; 698 return this; 699 } 700 701 public ProjectAnalysisBuilder setCeTask(CeTask ceTask) { 702 this.ceTask = ceTask; 703 return this; 704 } 705 706 public ProjectAnalysisBuilder setProject(Project project) { 707 this.project = project; 708 return this; 709 } 710 711 public ProjectAnalysisBuilder setBranch(@Nullable Branch branch) { 712 this.branch = branch; 713 return this; 714 } 715 716 public ProjectAnalysisBuilder setQualityGate(QualityGate qualityGate) { 717 this.qualityGate = qualityGate; 718 return this; 719 } 720 721 public ProjectAnalysisBuilder setAnalysis(@Nullable Analysis analysis) { 722 this.analysis = analysis; 723 return this; 724 } 725 726 public ProjectAnalysisBuilder setScannerContext(ScannerContext scannerContext) { 727 this.scannerContext = scannerContext; 728 return this; 729 } 730 731 public ProjectAnalysisBuilder setDate(Date date) { 732 this.date = date; 733 return this; 734 } 735 736 public PostProjectAnalysisTask.ProjectAnalysis build() { 737 return new PostProjectAnalysisTask.ProjectAnalysis() { 738 @Override 739 public Optional<Organization> getOrganization() { 740 return Optional.ofNullable(organization); 741 } 742 743 @Override 744 public CeTask getCeTask() { 745 return ceTask; 746 } 747 748 @Override 749 public Project getProject() { 750 return project; 751 } 752 753 @Override 754 public Optional<Branch> getBranch() { 755 return Optional.ofNullable(branch); 756 } 757 758 @CheckForNull 759 @Override 760 public QualityGate getQualityGate() { 761 return qualityGate; 762 } 763 764 @Override 765 public Date getDate() { 766 return date; 767 } 768 769 @Override 770 public Optional<Date> getAnalysisDate() { 771 return getAnalysis().map(Analysis::getDate); 772 } 773 774 @Override 775 public Optional<Analysis> getAnalysis() { 776 return Optional.ofNullable(analysis); 777 } 778 779 @Override 780 public ScannerContext getScannerContext() { 781 return scannerContext; 782 } 783 784 @Override 785 public String toString() { 786 return "ProjectAnalysis{" + 787 "organization=" + organization + 788 ", ceTask=" + ceTask + 789 ", project=" + project + 790 ", date=" + date.getTime() + 791 ", analysisDate=" + date.getTime() + 792 ", qualityGate=" + qualityGate + 793 '}'; 794 } 795 }; 796 } 797 } 798 799 public static final class AnalysisBuilder { 800 private String analysisUuid; 801 private Date date; 802 803 private AnalysisBuilder() { 804 // prevents instantiation outside PostProjectAnalysisTaskTester 805 } 806 807 public AnalysisBuilder setAnalysisUuid(String analysisUuid) { 808 this.analysisUuid = analysisUuid; 809 return this; 810 } 811 812 public AnalysisBuilder setDate(Date date) { 813 this.date = date; 814 return this; 815 } 816 817 public Analysis build() { 818 return new Analysis() { 819 820 @Override 821 public String getAnalysisUuid() { 822 return analysisUuid; 823 } 824 825 @Override 826 public Date getDate() { 827 return date; 828 } 829 }; 830 } 831 } 832}