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.Optional;
029import javax.annotation.CheckForNull;
030import javax.annotation.Nullable;
031
032import static com.google.common.base.Preconditions.checkArgument;
033import static com.google.common.base.Preconditions.checkState;
034import static java.util.Objects.requireNonNull;
035
036/**
037 * This class can be used to test {@link PostProjectAnalysisTask} implementations, see example below:
038 * <pre>
039 * import static org.assertj.core.api.Assertions.assertThat;
040 * import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newCeTaskBuilder;
041 * import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newConditionBuilder;
042 * import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newProjectBuilder;
043 * import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newQualityGateBuilder;
044 *
045 * public class CaptorPostProjectAnalysisTaskTest {
046 *   private class CaptorPostProjectAnalysisTask implements PostProjectAnalysisTask {
047 *     private ProjectAnalysis projectAnalysis;
048 *
049 *    {@literal @}Override
050 *     public void finished(ProjectAnalysis analysis) {
051 *       this.projectAnalysis = analysis;
052 *     }
053 *   }
054 *
055 *  {@literal @}Test
056 *   public void execute_is_passed_a_non_null_ProjectAnalysis_object() {
057 *     CaptorPostProjectAnalysisTask postProjectAnalysisTask = new CaptorPostProjectAnalysisTask();
058 *
059 *     PostProjectAnalysisTaskTester.of(postProjectAnalysisTask)
060 *       .withCeTask(
061 *           newCeTaskBuilder()
062 *               .setId("id")
063 *               .setStatus(CeTask.Status.SUCCESS)
064 *               .build())
065 *       .withProject(
066 *         PostProjectAnalysisTaskTester.newProjectBuilder()
067 *           .setUuid("uuid")
068 *           .setKey("key")
069 *           .setName("name")
070 *           .build())
071 *       .at(new Date())
072 *       .withQualityGate(
073 *         newQualityGateBuilder()
074 *           .setId("id")
075 *           .setName("name")
076 *           .setStatus(QualityGate.Status.OK)
077 *           .add(
078 *             newConditionBuilder()
079 *               .setMetricKey("metric key")
080 *               .setOperator(QualityGate.Operator.GREATER_THAN)
081 *               .setErrorThreshold("12")
082 *               .setOnLeakPeriod(true)
083 *               .build(QualityGate.EvaluationStatus.OK, "value"))
084 *           .build())
085 *       .execute();
086 *
087 *     assertThat(postProjectAnalysisTask.projectAnalysis).isNotNull();
088 *   }
089 * }
090 * </pre>
091 *
092 * @since 5.5
093 */
094public class PostProjectAnalysisTaskTester {
095  private static final String DATE_CAN_NOT_BE_NULL = "date cannot be null";
096  private static final String PROJECT_CAN_NOT_BE_NULL = "project cannot be null";
097  private static final String CE_TASK_CAN_NOT_BE_NULL = "ceTask cannot be null";
098  private static final String STATUS_CAN_NOT_BE_NULL = "status cannot be null";
099  private static final String SCANNER_CONTEXT_CAN_NOT_BE_NULL = "scannerContext cannot be null";
100
101  private final PostProjectAnalysisTask underTest;
102  @CheckForNull
103  private CeTask ceTask;
104  @CheckForNull
105  private Project project;
106  @CheckForNull
107  private Date date;
108  @CheckForNull
109  private QualityGate qualityGate;
110  private ScannerContext scannerContext;
111
112  private PostProjectAnalysisTaskTester(PostProjectAnalysisTask underTest) {
113    this.underTest = requireNonNull(underTest, "PostProjectAnalysisTask instance cannot be null");
114  }
115
116  public static PostProjectAnalysisTaskTester of(PostProjectAnalysisTask underTest) {
117    return new PostProjectAnalysisTaskTester(underTest);
118  }
119
120  public static CeTaskBuilder newCeTaskBuilder() {
121    return new CeTaskBuilder();
122  }
123
124  public static ProjectBuilder newProjectBuilder() {
125    return new ProjectBuilder();
126  }
127
128  public static QualityGateBuilder newQualityGateBuilder() {
129    return new QualityGateBuilder();
130  }
131
132  public static ConditionBuilder newConditionBuilder() {
133    return new ConditionBuilder();
134  }
135
136  public static ScannerContextBuilder newScannerContextBuilder() {
137    return new ScannerContextBuilder();
138  }
139
140  public PostProjectAnalysisTaskTester withCeTask(CeTask ceTask) {
141    this.ceTask = requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL);
142    return this;
143  }
144
145  public PostProjectAnalysisTaskTester withProject(Project project) {
146    this.project = requireNonNull(project, PROJECT_CAN_NOT_BE_NULL);
147    return this;
148  }
149
150  /**
151      * @since 6.1
152   */
153  public PostProjectAnalysisTaskTester withScannerContext(ScannerContext scannerContext) {
154    this.scannerContext = requireNonNull(scannerContext, SCANNER_CONTEXT_CAN_NOT_BE_NULL);
155    return this;
156  }
157
158  public PostProjectAnalysisTaskTester at(Date date) {
159    this.date = requireNonNull(date, DATE_CAN_NOT_BE_NULL);
160    return this;
161  }
162
163  public PostProjectAnalysisTaskTester withQualityGate(@Nullable QualityGate qualityGate) {
164    this.qualityGate = qualityGate;
165    return this;
166  }
167
168  public void execute() {
169    this.ceTask = requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL);
170    this.project = requireNonNull(project, PROJECT_CAN_NOT_BE_NULL);
171    this.date = requireNonNull(date, DATE_CAN_NOT_BE_NULL);
172
173    this.underTest.finished(
174      new PostProjectAnalysisTask.ProjectAnalysis() {
175        @Override
176        public ScannerContext getScannerContext() {
177          return scannerContext;
178        }
179
180        @Override
181        public CeTask getCeTask() {
182          return ceTask;
183        }
184
185        @Override
186        public Project getProject() {
187          return project;
188        }
189
190        @Override
191        public QualityGate getQualityGate() {
192          return qualityGate;
193        }
194
195        @Override
196        public Date getDate() {
197          return date;
198        }
199
200        @Override
201        public Optional<Date> getAnalysisDate() {
202          return Optional.of(date);
203        }
204
205        @Override
206        public String toString() {
207          return "ProjectAnalysis{" +
208            "ceTask=" + ceTask +
209            ", project=" + project +
210            ", date=" + date.getTime() +
211            ", analysisDate=" + date.getTime() +
212            ", qualityGate=" + qualityGate +
213            '}';
214        }
215      });
216
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 QualityGateBuilder {
327    private static final String ID_CAN_NOT_BE_NULL = "id cannot be null";
328    private static final String NAME_CAN_NOT_BE_NULL = "name cannot be null";
329
330    private String id;
331    private String name;
332    private QualityGate.Status status;
333    private final List<QualityGate.Condition> conditions = new ArrayList<>();
334
335    private QualityGateBuilder() {
336      // prevents instantiation outside PostProjectAnalysisTaskTester
337    }
338
339    public QualityGateBuilder setId(String id) {
340      this.id = requireNonNull(id, ID_CAN_NOT_BE_NULL);
341      return this;
342    }
343
344    public QualityGateBuilder setName(String name) {
345      this.name = requireNonNull(name, NAME_CAN_NOT_BE_NULL);
346      return this;
347    }
348
349    public QualityGateBuilder setStatus(QualityGate.Status status) {
350      this.status = requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
351      return this;
352    }
353
354    public QualityGateBuilder add(QualityGate.Condition condition) {
355      conditions.add(requireNonNull(condition, "condition cannot be null"));
356      return this;
357    }
358
359    public QualityGateBuilder clearConditions() {
360      this.conditions.clear();
361      return this;
362    }
363
364    public QualityGate build() {
365      requireNonNull(id, ID_CAN_NOT_BE_NULL);
366      requireNonNull(name, NAME_CAN_NOT_BE_NULL);
367      requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
368
369      return new QualityGate() {
370        @Override
371        public String getId() {
372          return id;
373        }
374
375        @Override
376        public String getName() {
377          return name;
378        }
379
380        @Override
381        public Status getStatus() {
382          return status;
383        }
384
385        @Override
386        public Collection<Condition> getConditions() {
387          return conditions;
388        }
389
390        @Override
391        public String toString() {
392          return "QualityGate{" +
393            "id='" + id + '\'' +
394            ", name='" + name + '\'' +
395            ", status=" + status +
396            ", conditions=" + conditions +
397            '}';
398        }
399      };
400    }
401  }
402
403  public static final class ConditionBuilder {
404    private static final String METRIC_KEY_CAN_NOT_BE_NULL = "metricKey cannot be null";
405    private static final String OPERATOR_CAN_NOT_BE_NULL = "operator cannot be null";
406
407    private String metricKey;
408    private QualityGate.Operator operator;
409    private String errorThreshold;
410    private String warningThreshold;
411    private boolean onLeakPeriod;
412
413    private ConditionBuilder() {
414      // prevents instantiation outside PostProjectAnalysisTaskTester
415    }
416
417    public ConditionBuilder setMetricKey(String metricKey) {
418      this.metricKey = requireNonNull(metricKey, METRIC_KEY_CAN_NOT_BE_NULL);
419      return this;
420    }
421
422    public ConditionBuilder setOperator(QualityGate.Operator operator) {
423      this.operator = requireNonNull(operator, OPERATOR_CAN_NOT_BE_NULL);
424      return this;
425    }
426
427    public ConditionBuilder setErrorThreshold(@Nullable String errorThreshold) {
428      this.errorThreshold = errorThreshold;
429      return this;
430    }
431
432    public ConditionBuilder setWarningThreshold(@Nullable String warningThreshold) {
433      this.warningThreshold = warningThreshold;
434      return this;
435    }
436
437    public ConditionBuilder setOnLeakPeriod(boolean onLeakPeriod) {
438      this.onLeakPeriod = onLeakPeriod;
439      return this;
440    }
441
442    public QualityGate.Condition buildNoValue() {
443      checkCommonProperties();
444      return new QualityGate.Condition() {
445        @Override
446        public QualityGate.EvaluationStatus getStatus() {
447          return QualityGate.EvaluationStatus.NO_VALUE;
448        }
449
450        @Override
451        public String getMetricKey() {
452          return metricKey;
453        }
454
455        @Override
456        public QualityGate.Operator getOperator() {
457          return operator;
458        }
459
460        @Override
461        public String getErrorThreshold() {
462          return errorThreshold;
463        }
464
465        @Override
466        public String getWarningThreshold() {
467          return warningThreshold;
468        }
469
470        @Override
471        public boolean isOnLeakPeriod() {
472          return onLeakPeriod;
473        }
474
475        @Override
476        public String getValue() {
477          throw new IllegalStateException("There is no value when status is NO_VALUE");
478        }
479
480        @Override
481        public String toString() {
482          return "Condition{" +
483            "status=" + QualityGate.EvaluationStatus.NO_VALUE +
484            ", metricKey='" + metricKey + '\'' +
485            ", operator=" + operator +
486            ", errorThreshold='" + errorThreshold + '\'' +
487            ", warningThreshold='" + warningThreshold + '\'' +
488            ", onLeakPeriod=" + onLeakPeriod +
489            '}';
490        }
491      };
492    }
493
494    public QualityGate.Condition build(final QualityGate.EvaluationStatus status, final String value) {
495      checkCommonProperties();
496      requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
497      checkArgument(status != QualityGate.EvaluationStatus.NO_VALUE, "status cannot be NO_VALUE, use method buildNoValue() instead");
498      requireNonNull(value, "value cannot be null, use method buildNoValue() instead");
499      return new QualityGate.Condition() {
500        @Override
501        public QualityGate.EvaluationStatus getStatus() {
502          return status;
503        }
504
505        @Override
506        public String getMetricKey() {
507          return metricKey;
508        }
509
510        @Override
511        public QualityGate.Operator getOperator() {
512          return operator;
513        }
514
515        @Override
516        public String getErrorThreshold() {
517          return errorThreshold;
518        }
519
520        @Override
521        public String getWarningThreshold() {
522          return warningThreshold;
523        }
524
525        @Override
526        public boolean isOnLeakPeriod() {
527          return onLeakPeriod;
528        }
529
530        @Override
531        public String getValue() {
532          return value;
533        }
534
535        @Override
536        public String toString() {
537          return "Condition{" +
538            "status=" + status +
539            ", metricKey='" + metricKey + '\'' +
540            ", operator=" + operator +
541            ", errorThreshold='" + errorThreshold + '\'' +
542            ", warningThreshold='" + warningThreshold + '\'' +
543            ", onLeakPeriod=" + onLeakPeriod +
544            ", value='" + value + '\'' +
545            '}';
546        }
547      };
548    }
549
550    private void checkCommonProperties() {
551      requireNonNull(metricKey, METRIC_KEY_CAN_NOT_BE_NULL);
552      requireNonNull(operator, OPERATOR_CAN_NOT_BE_NULL);
553      checkState(errorThreshold != null || warningThreshold != null, "At least one of errorThreshold and warningThreshold must be non null");
554    }
555  }
556
557  public static final class ScannerContextBuilder {
558    private final Map<String, String> properties = new HashMap<>();
559
560    private ScannerContextBuilder() {
561      // prevents instantiation outside PostProjectAnalysisTaskTester
562    }
563
564    public ScannerContextBuilder addProperties(Map<String, String> map) {
565      properties.putAll(map);
566      return this;
567    }
568
569    public ScannerContext build() {
570      return () -> properties;
571    }
572  }
573}