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}