001/*
002 * SonarQube
003 * Copyright (C) 2009-2017 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
103  private final PostProjectAnalysisTask underTest;
104  @CheckForNull
105  private CeTask ceTask;
106  @CheckForNull
107  private Project project;
108  @CheckForNull
109  private Date date;
110  @CheckForNull
111  private QualityGate qualityGate;
112  @CheckForNull
113  private Branch branch;
114  private ScannerContext scannerContext;
115  private String analysisUuid;
116
117  private PostProjectAnalysisTaskTester(PostProjectAnalysisTask underTest) {
118    this.underTest = requireNonNull(underTest, "PostProjectAnalysisTask instance cannot be null");
119  }
120
121  public static PostProjectAnalysisTaskTester of(PostProjectAnalysisTask underTest) {
122    return new PostProjectAnalysisTaskTester(underTest);
123  }
124
125  public static CeTaskBuilder newCeTaskBuilder() {
126    return new CeTaskBuilder();
127  }
128
129  public static ProjectBuilder newProjectBuilder() {
130    return new ProjectBuilder();
131  }
132
133  public static BranchBuilder newBranchBuilder() {
134    return new BranchBuilder();
135  }
136
137  public static QualityGateBuilder newQualityGateBuilder() {
138    return new QualityGateBuilder();
139  }
140
141  public static ConditionBuilder newConditionBuilder() {
142    return new ConditionBuilder();
143  }
144
145  public static ScannerContextBuilder newScannerContextBuilder() {
146    return new ScannerContextBuilder();
147  }
148
149  public PostProjectAnalysisTaskTester withCeTask(CeTask ceTask) {
150    this.ceTask = requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL);
151    return this;
152  }
153
154  public PostProjectAnalysisTaskTester withProject(Project project) {
155    this.project = requireNonNull(project, PROJECT_CAN_NOT_BE_NULL);
156    return this;
157  }
158
159  /**
160   * @since 6.1
161   */
162  public PostProjectAnalysisTaskTester withScannerContext(ScannerContext scannerContext) {
163    this.scannerContext = requireNonNull(scannerContext, SCANNER_CONTEXT_CAN_NOT_BE_NULL);
164    return this;
165  }
166
167  public PostProjectAnalysisTaskTester at(Date date) {
168    this.date = requireNonNull(date, DATE_CAN_NOT_BE_NULL);
169    return this;
170  }
171
172  public PostProjectAnalysisTaskTester withQualityGate(@Nullable QualityGate qualityGate) {
173    this.qualityGate = qualityGate;
174    return this;
175  }
176
177  public PostProjectAnalysisTaskTester withBranch(@Nullable Branch b) {
178    this.branch = b;
179    return this;
180  }
181
182  /**
183   * @since 6.6
184   */
185  public PostProjectAnalysisTaskTester withAnalysisUuid(@Nullable String analysisUuid) {
186    this.analysisUuid = analysisUuid;
187    return this;
188  }
189
190  public PostProjectAnalysisTask.ProjectAnalysis execute() {
191    requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL);
192    requireNonNull(project, PROJECT_CAN_NOT_BE_NULL);
193    requireNonNull(date, DATE_CAN_NOT_BE_NULL);
194
195    Analysis analysis = null;
196    if (analysisUuid != null) {
197      analysis = new AnalysisBuilder()
198        .setDate(date)
199        .setAnalysisUuid(analysisUuid)
200        .build();
201    }
202
203    PostProjectAnalysisTask.ProjectAnalysis projectAnalysis = new ProjectAnalysisBuilder()
204      .setCeTask(ceTask)
205      .setProject(project)
206      .setBranch(branch)
207      .setQualityGate(qualityGate)
208      .setAnalysis(analysis)
209      .setScannerContext(scannerContext)
210      .setDate(date)
211      .build();
212
213    this.underTest.
214      finished(projectAnalysis);
215
216    return projectAnalysis;
217  }
218
219  public static final class CeTaskBuilder {
220    private static final String ID_CAN_NOT_BE_NULL = "id cannot be null";
221
222    @CheckForNull
223    private String id;
224    @CheckForNull
225    private CeTask.Status status;
226
227    private CeTaskBuilder() {
228      // prevents instantiation outside PostProjectAnalysisTaskTester
229    }
230
231    public CeTaskBuilder setId(String id) {
232      this.id = requireNonNull(id, ID_CAN_NOT_BE_NULL);
233      return this;
234    }
235
236    public CeTaskBuilder setStatus(CeTask.Status status) {
237      this.status = requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
238      return this;
239    }
240
241    public CeTask build() {
242      requireNonNull(id, ID_CAN_NOT_BE_NULL);
243      requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
244      return new CeTask() {
245        @Override
246        public String getId() {
247          return id;
248        }
249
250        @Override
251        public Status getStatus() {
252          return status;
253        }
254
255        @Override
256        public String toString() {
257          return "CeTask{" +
258            "id='" + id + '\'' +
259            ", status=" + status +
260            '}';
261        }
262      };
263    }
264  }
265
266  public static final class ProjectBuilder {
267    private static final String UUID_CAN_NOT_BE_NULL = "uuid cannot be null";
268    private static final String KEY_CAN_NOT_BE_NULL = "key cannot be null";
269    private static final String NAME_CAN_NOT_BE_NULL = "name cannot be null";
270    private String uuid;
271    private String key;
272    private String name;
273
274    private ProjectBuilder() {
275      // prevents instantiation outside PostProjectAnalysisTaskTester
276    }
277
278    public ProjectBuilder setUuid(String uuid) {
279      this.uuid = requireNonNull(uuid, UUID_CAN_NOT_BE_NULL);
280      return this;
281    }
282
283    public ProjectBuilder setKey(String key) {
284      this.key = requireNonNull(key, KEY_CAN_NOT_BE_NULL);
285      return this;
286    }
287
288    public ProjectBuilder setName(String name) {
289      this.name = requireNonNull(name, NAME_CAN_NOT_BE_NULL);
290      return this;
291    }
292
293    public Project build() {
294      requireNonNull(uuid, UUID_CAN_NOT_BE_NULL);
295      requireNonNull(key, KEY_CAN_NOT_BE_NULL);
296      requireNonNull(name, NAME_CAN_NOT_BE_NULL);
297      return new Project() {
298        @Override
299        public String getUuid() {
300          return uuid;
301        }
302
303        @Override
304        public String getKey() {
305          return key;
306        }
307
308        @Override
309        public String getName() {
310          return name;
311        }
312
313        @Override
314        public String toString() {
315          return "Project{" +
316            "uuid='" + uuid + '\'' +
317            ", key='" + key + '\'' +
318            ", name='" + name + '\'' +
319            '}';
320        }
321
322      };
323    }
324  }
325
326  public static final class BranchBuilder {
327    private boolean isMain = true;
328    private String name = null;
329    private Branch.Type type = Branch.Type.LONG;
330
331    private BranchBuilder() {
332      // prevents instantiation outside PostProjectAnalysisTaskTester
333    }
334
335    public BranchBuilder setName(@Nullable String s) {
336      this.name = s;
337      return this;
338    }
339
340    public BranchBuilder setType(Branch.Type t) {
341      this.type = Objects.requireNonNull(t);
342      return this;
343    }
344
345    public BranchBuilder setIsMain(boolean b) {
346      this.isMain = b;
347      return this;
348    }
349
350    public Branch build() {
351      return new Branch() {
352
353
354        @Override
355        public boolean isMain() {
356          return isMain;
357        }
358
359        @Override
360        public Optional<String> getName() {
361          return Optional.ofNullable(name);
362        }
363
364        @Override
365        public Type getType() {
366          return type;
367        }
368      };
369    }
370  }
371
372  public static final class QualityGateBuilder {
373    private static final String ID_CAN_NOT_BE_NULL = "id cannot be null";
374    private static final String NAME_CAN_NOT_BE_NULL = "name cannot be null";
375
376    private String id;
377    private String name;
378    private QualityGate.Status status;
379    private final List<QualityGate.Condition> conditions = new ArrayList<>();
380
381    private QualityGateBuilder() {
382      // prevents instantiation outside PostProjectAnalysisTaskTester
383    }
384
385    public QualityGateBuilder setId(String id) {
386      this.id = requireNonNull(id, ID_CAN_NOT_BE_NULL);
387      return this;
388    }
389
390    public QualityGateBuilder setName(String name) {
391      this.name = requireNonNull(name, NAME_CAN_NOT_BE_NULL);
392      return this;
393    }
394
395    public QualityGateBuilder setStatus(QualityGate.Status status) {
396      this.status = requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
397      return this;
398    }
399
400    public QualityGateBuilder add(QualityGate.Condition condition) {
401      conditions.add(requireNonNull(condition, "condition cannot be null"));
402      return this;
403    }
404
405    public QualityGateBuilder clearConditions() {
406      this.conditions.clear();
407      return this;
408    }
409
410    public QualityGate build() {
411      requireNonNull(id, ID_CAN_NOT_BE_NULL);
412      requireNonNull(name, NAME_CAN_NOT_BE_NULL);
413      requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
414
415      return new QualityGate() {
416        @Override
417        public String getId() {
418          return id;
419        }
420
421        @Override
422        public String getName() {
423          return name;
424        }
425
426        @Override
427        public Status getStatus() {
428          return status;
429        }
430
431        @Override
432        public Collection<Condition> getConditions() {
433          return conditions;
434        }
435
436        @Override
437        public String toString() {
438          return "QualityGate{" +
439            "id='" + id + '\'' +
440            ", name='" + name + '\'' +
441            ", status=" + status +
442            ", conditions=" + conditions +
443            '}';
444        }
445      };
446    }
447  }
448
449  public static final class ConditionBuilder {
450    private static final String METRIC_KEY_CAN_NOT_BE_NULL = "metricKey cannot be null";
451    private static final String OPERATOR_CAN_NOT_BE_NULL = "operator cannot be null";
452
453    private String metricKey;
454    private QualityGate.Operator operator;
455    private String errorThreshold;
456    private String warningThreshold;
457    private boolean onLeakPeriod;
458
459    private ConditionBuilder() {
460      // prevents instantiation outside PostProjectAnalysisTaskTester
461    }
462
463    public ConditionBuilder setMetricKey(String metricKey) {
464      this.metricKey = requireNonNull(metricKey, METRIC_KEY_CAN_NOT_BE_NULL);
465      return this;
466    }
467
468    public ConditionBuilder setOperator(QualityGate.Operator operator) {
469      this.operator = requireNonNull(operator, OPERATOR_CAN_NOT_BE_NULL);
470      return this;
471    }
472
473    public ConditionBuilder setErrorThreshold(@Nullable String errorThreshold) {
474      this.errorThreshold = errorThreshold;
475      return this;
476    }
477
478    public ConditionBuilder setWarningThreshold(@Nullable String warningThreshold) {
479      this.warningThreshold = warningThreshold;
480      return this;
481    }
482
483    public ConditionBuilder setOnLeakPeriod(boolean onLeakPeriod) {
484      this.onLeakPeriod = onLeakPeriod;
485      return this;
486    }
487
488    public QualityGate.Condition buildNoValue() {
489      checkCommonProperties();
490      return new QualityGate.Condition() {
491        @Override
492        public QualityGate.EvaluationStatus getStatus() {
493          return QualityGate.EvaluationStatus.NO_VALUE;
494        }
495
496        @Override
497        public String getMetricKey() {
498          return metricKey;
499        }
500
501        @Override
502        public QualityGate.Operator getOperator() {
503          return operator;
504        }
505
506        @Override
507        public String getErrorThreshold() {
508          return errorThreshold;
509        }
510
511        @Override
512        public String getWarningThreshold() {
513          return warningThreshold;
514        }
515
516        @Override
517        public boolean isOnLeakPeriod() {
518          return onLeakPeriod;
519        }
520
521        @Override
522        public String getValue() {
523          throw new IllegalStateException("There is no value when status is NO_VALUE");
524        }
525
526        @Override
527        public String toString() {
528          return "Condition{" +
529            "status=" + QualityGate.EvaluationStatus.NO_VALUE +
530            ", metricKey='" + metricKey + '\'' +
531            ", operator=" + operator +
532            ", errorThreshold='" + errorThreshold + '\'' +
533            ", warningThreshold='" + warningThreshold + '\'' +
534            ", onLeakPeriod=" + onLeakPeriod +
535            '}';
536        }
537      };
538    }
539
540    public QualityGate.Condition build(final QualityGate.EvaluationStatus status, final String value) {
541      checkCommonProperties();
542      requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
543      checkArgument(status != QualityGate.EvaluationStatus.NO_VALUE, "status cannot be NO_VALUE, use method buildNoValue() instead");
544      requireNonNull(value, "value cannot be null, use method buildNoValue() instead");
545      return new QualityGate.Condition() {
546        @Override
547        public QualityGate.EvaluationStatus getStatus() {
548          return status;
549        }
550
551        @Override
552        public String getMetricKey() {
553          return metricKey;
554        }
555
556        @Override
557        public QualityGate.Operator getOperator() {
558          return operator;
559        }
560
561        @Override
562        public String getErrorThreshold() {
563          return errorThreshold;
564        }
565
566        @Override
567        public String getWarningThreshold() {
568          return warningThreshold;
569        }
570
571        @Override
572        public boolean isOnLeakPeriod() {
573          return onLeakPeriod;
574        }
575
576        @Override
577        public String getValue() {
578          return value;
579        }
580
581        @Override
582        public String toString() {
583          return "Condition{" +
584            "status=" + status +
585            ", metricKey='" + metricKey + '\'' +
586            ", operator=" + operator +
587            ", errorThreshold='" + errorThreshold + '\'' +
588            ", warningThreshold='" + warningThreshold + '\'' +
589            ", onLeakPeriod=" + onLeakPeriod +
590            ", value='" + value + '\'' +
591            '}';
592        }
593      };
594    }
595
596    private void checkCommonProperties() {
597      requireNonNull(metricKey, METRIC_KEY_CAN_NOT_BE_NULL);
598      requireNonNull(operator, OPERATOR_CAN_NOT_BE_NULL);
599      checkState(errorThreshold != null || warningThreshold != null, "At least one of errorThreshold and warningThreshold must be non null");
600    }
601  }
602
603  public static final class ScannerContextBuilder {
604    private final Map<String, String> properties = new HashMap<>();
605
606    private ScannerContextBuilder() {
607      // prevents instantiation outside PostProjectAnalysisTaskTester
608    }
609
610    public ScannerContextBuilder addProperties(Map<String, String> map) {
611      properties.putAll(map);
612      return this;
613    }
614
615    public ScannerContext build() {
616      return () -> properties;
617    }
618  }
619
620  public static final class ProjectAnalysisBuilder {
621    private CeTask ceTask;
622    private Project project;
623    private Branch branch;
624    private QualityGate qualityGate;
625    private Analysis analysis;
626    private ScannerContext scannerContext;
627    private Date date;
628
629    private ProjectAnalysisBuilder() {
630      // prevents instantiation outside PostProjectAnalysisTaskTester
631    }
632
633    public ProjectAnalysisBuilder setCeTask(CeTask ceTask) {
634      this.ceTask = ceTask;
635      return this;
636    }
637
638    public ProjectAnalysisBuilder setProject(Project project) {
639      this.project = project;
640      return this;
641    }
642
643    public ProjectAnalysisBuilder setBranch(@Nullable Branch branch) {
644      this.branch = branch;
645      return this;
646    }
647
648    public ProjectAnalysisBuilder setQualityGate(QualityGate qualityGate) {
649      this.qualityGate = qualityGate;
650      return this;
651    }
652
653    public ProjectAnalysisBuilder setAnalysis(@Nullable Analysis analysis) {
654      this.analysis = analysis;
655      return this;
656    }
657
658    public ProjectAnalysisBuilder setScannerContext(ScannerContext scannerContext) {
659      this.scannerContext = scannerContext;
660      return this;
661    }
662
663    public ProjectAnalysisBuilder setDate(Date date) {
664      this.date = date;
665      return this;
666    }
667
668    public PostProjectAnalysisTask.ProjectAnalysis build() {
669      return new PostProjectAnalysisTask.ProjectAnalysis() {
670        @Override
671        public CeTask getCeTask() {
672          return ceTask;
673        }
674
675        @Override
676        public Project getProject() {
677          return project;
678        }
679
680        @Override
681        public Optional<Branch> getBranch() {
682          return Optional.ofNullable(branch);
683        }
684
685        @CheckForNull
686        @Override
687        public QualityGate getQualityGate() {
688          return qualityGate;
689        }
690
691        @Override
692        public Date getDate() {
693          return date;
694        }
695
696        @Override
697        public Optional<Date> getAnalysisDate() {
698          return getAnalysis().map(Analysis::getDate);
699        }
700
701        @Override
702        public Optional<Analysis> getAnalysis() {
703          return Optional.ofNullable(analysis);
704        }
705
706        @Override
707        public ScannerContext getScannerContext() {
708          return scannerContext;
709        }
710
711        @Override
712        public String toString() {
713          return "ProjectAnalysis{" +
714            "ceTask=" + ceTask +
715            ", project=" + project +
716            ", date=" + date.getTime() +
717            ", analysisDate=" + date.getTime() +
718            ", qualityGate=" + qualityGate +
719            '}';
720        }
721      };
722    }
723  }
724
725  public static final class AnalysisBuilder {
726    private String analysisUuid;
727    private Date date;
728
729    private AnalysisBuilder() {
730      // prevents instantiation outside PostProjectAnalysisTaskTester
731    }
732
733    public AnalysisBuilder setAnalysisUuid(String analysisUuid) {
734      this.analysisUuid = analysisUuid;
735      return this;
736    }
737
738    public AnalysisBuilder setDate(Date date) {
739      this.date = date;
740      return this;
741    }
742
743    public Analysis build() {
744      return new Analysis() {
745
746        @Override
747        public String getAnalysisUuid() {
748          return analysisUuid;
749        }
750
751        @Override
752        public Date getDate() {
753          return date;
754        }
755      };
756    }
757  }
758}