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 *       .withQualityGate(
074 *         newQualityGateBuilder()
075 *           .setId("id")
076 *           .setName("name")
077 *           .setStatus(QualityGate.Status.OK)
078 *           .add(
079 *             newConditionBuilder()
080 *               .setMetricKey("metric key")
081 *               .setOperator(QualityGate.Operator.GREATER_THAN)
082 *               .setErrorThreshold("12")
083 *               .setOnLeakPeriod(true)
084 *               .build(QualityGate.EvaluationStatus.OK, "value"))
085 *           .build())
086 *       .execute();
087 *
088 *     assertThat(postProjectAnalysisTask.projectAnalysis).isNotNull();
089 *   }
090 * }
091 * </pre>
092 *
093 * @since 5.5
094 */
095public class PostProjectAnalysisTaskTester {
096  private static final String DATE_CAN_NOT_BE_NULL = "date cannot be null";
097  private static final String PROJECT_CAN_NOT_BE_NULL = "project cannot be null";
098  private static final String CE_TASK_CAN_NOT_BE_NULL = "ceTask cannot be null";
099  private static final String STATUS_CAN_NOT_BE_NULL = "status cannot be null";
100  private static final String SCANNER_CONTEXT_CAN_NOT_BE_NULL = "scannerContext cannot be null";
101
102  private final PostProjectAnalysisTask underTest;
103  @CheckForNull
104  private CeTask ceTask;
105  @CheckForNull
106  private Project project;
107  @CheckForNull
108  private Date date;
109  @CheckForNull
110  private QualityGate qualityGate;
111  @CheckForNull
112  private Branch branch;
113  private ScannerContext scannerContext;
114
115  private PostProjectAnalysisTaskTester(PostProjectAnalysisTask underTest) {
116    this.underTest = requireNonNull(underTest, "PostProjectAnalysisTask instance cannot be null");
117  }
118
119  public static PostProjectAnalysisTaskTester of(PostProjectAnalysisTask underTest) {
120    return new PostProjectAnalysisTaskTester(underTest);
121  }
122
123  public static CeTaskBuilder newCeTaskBuilder() {
124    return new CeTaskBuilder();
125  }
126
127  public static ProjectBuilder newProjectBuilder() {
128    return new ProjectBuilder();
129  }
130
131  public static BranchBuilder newBranchBuilder() {
132    return new BranchBuilder();
133  }
134
135  public static QualityGateBuilder newQualityGateBuilder() {
136    return new QualityGateBuilder();
137  }
138
139  public static ConditionBuilder newConditionBuilder() {
140    return new ConditionBuilder();
141  }
142
143  public static ScannerContextBuilder newScannerContextBuilder() {
144    return new ScannerContextBuilder();
145  }
146
147  public PostProjectAnalysisTaskTester withCeTask(CeTask ceTask) {
148    this.ceTask = requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL);
149    return this;
150  }
151
152  public PostProjectAnalysisTaskTester withProject(Project project) {
153    this.project = requireNonNull(project, PROJECT_CAN_NOT_BE_NULL);
154    return this;
155  }
156
157  /**
158   * @since 6.1
159   */
160  public PostProjectAnalysisTaskTester withScannerContext(ScannerContext scannerContext) {
161    this.scannerContext = requireNonNull(scannerContext, SCANNER_CONTEXT_CAN_NOT_BE_NULL);
162    return this;
163  }
164
165  public PostProjectAnalysisTaskTester at(Date date) {
166    this.date = requireNonNull(date, DATE_CAN_NOT_BE_NULL);
167    return this;
168  }
169
170  public PostProjectAnalysisTaskTester withQualityGate(@Nullable QualityGate qualityGate) {
171    this.qualityGate = qualityGate;
172    return this;
173  }
174
175  public PostProjectAnalysisTaskTester withBranch(@Nullable Branch b) {
176    this.branch = b;
177    return this;
178  }
179
180  public void execute() {
181    requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL);
182    requireNonNull(project, PROJECT_CAN_NOT_BE_NULL);
183    requireNonNull(date, DATE_CAN_NOT_BE_NULL);
184
185    this.underTest.finished(
186      new PostProjectAnalysisTask.ProjectAnalysis() {
187        @Override
188        public ScannerContext getScannerContext() {
189          return scannerContext;
190        }
191
192        @Override
193        public CeTask getCeTask() {
194          return ceTask;
195        }
196
197        @Override
198        public Project getProject() {
199          return project;
200        }
201
202        @Override
203        public Optional<Branch> getBranch() {
204          return Optional.ofNullable(branch);
205        }
206
207        @Override
208        public QualityGate getQualityGate() {
209          return qualityGate;
210        }
211
212        @Override
213        public Date getDate() {
214          return date;
215        }
216
217        @Override
218        public Optional<Date> getAnalysisDate() {
219          return Optional.of(date);
220        }
221
222        @Override
223        public String toString() {
224          return "ProjectAnalysis{" +
225            "ceTask=" + ceTask +
226            ", project=" + project +
227            ", date=" + date.getTime() +
228            ", analysisDate=" + date.getTime() +
229            ", qualityGate=" + qualityGate +
230            '}';
231        }
232      });
233
234  }
235
236  public static final class CeTaskBuilder {
237    private static final String ID_CAN_NOT_BE_NULL = "id cannot be null";
238
239    @CheckForNull
240    private String id;
241    @CheckForNull
242    private CeTask.Status status;
243
244    private CeTaskBuilder() {
245      // prevents instantiation outside PostProjectAnalysisTaskTester
246    }
247
248    public CeTaskBuilder setId(String id) {
249      this.id = requireNonNull(id, ID_CAN_NOT_BE_NULL);
250      return this;
251    }
252
253    public CeTaskBuilder setStatus(CeTask.Status status) {
254      this.status = requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
255      return this;
256    }
257
258    public CeTask build() {
259      requireNonNull(id, ID_CAN_NOT_BE_NULL);
260      requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
261      return new CeTask() {
262        @Override
263        public String getId() {
264          return id;
265        }
266
267        @Override
268        public Status getStatus() {
269          return status;
270        }
271
272        @Override
273        public String toString() {
274          return "CeTask{" +
275            "id='" + id + '\'' +
276            ", status=" + status +
277            '}';
278        }
279      };
280    }
281  }
282
283  public static final class ProjectBuilder {
284    private static final String UUID_CAN_NOT_BE_NULL = "uuid cannot be null";
285    private static final String KEY_CAN_NOT_BE_NULL = "key cannot be null";
286    private static final String NAME_CAN_NOT_BE_NULL = "name cannot be null";
287    private String uuid;
288    private String key;
289    private String name;
290
291    private ProjectBuilder() {
292      // prevents instantiation outside PostProjectAnalysisTaskTester
293    }
294
295    public ProjectBuilder setUuid(String uuid) {
296      this.uuid = requireNonNull(uuid, UUID_CAN_NOT_BE_NULL);
297      return this;
298    }
299
300    public ProjectBuilder setKey(String key) {
301      this.key = requireNonNull(key, KEY_CAN_NOT_BE_NULL);
302      return this;
303    }
304
305    public ProjectBuilder setName(String name) {
306      this.name = requireNonNull(name, NAME_CAN_NOT_BE_NULL);
307      return this;
308    }
309
310    public Project build() {
311      requireNonNull(uuid, UUID_CAN_NOT_BE_NULL);
312      requireNonNull(key, KEY_CAN_NOT_BE_NULL);
313      requireNonNull(name, NAME_CAN_NOT_BE_NULL);
314      return new Project() {
315        @Override
316        public String getUuid() {
317          return uuid;
318        }
319
320        @Override
321        public String getKey() {
322          return key;
323        }
324
325        @Override
326        public String getName() {
327          return name;
328        }
329
330        @Override
331        public String toString() {
332          return "Project{" +
333            "uuid='" + uuid + '\'' +
334            ", key='" + key + '\'' +
335            ", name='" + name + '\'' +
336            '}';
337        }
338
339      };
340    }
341  }
342
343  public static final class BranchBuilder {
344    private boolean isMain = true;
345    private String name = null;
346    private Branch.Type type = Branch.Type.LONG;
347
348    private BranchBuilder() {
349      // prevents instantiation outside PostProjectAnalysisTaskTester
350    }
351
352    public BranchBuilder setName(@Nullable String s) {
353      this.name = s;
354      return this;
355    }
356
357    public BranchBuilder setType(Branch.Type t) {
358      this.type = Objects.requireNonNull(t);
359      return this;
360    }
361
362    public BranchBuilder setIsMain(boolean b) {
363      this.isMain = b;
364      return this;
365    }
366
367    public Branch build() {
368      return new Branch() {
369
370
371        @Override
372        public boolean isMain() {
373          return isMain;
374        }
375
376        @Override
377        public Optional<String> getName() {
378          return Optional.ofNullable(name);
379        }
380
381        @Override
382        public Type getType() {
383          return type;
384        }
385      };
386    }
387  }
388
389  public static final class QualityGateBuilder {
390    private static final String ID_CAN_NOT_BE_NULL = "id cannot be null";
391    private static final String NAME_CAN_NOT_BE_NULL = "name cannot be null";
392
393    private String id;
394    private String name;
395    private QualityGate.Status status;
396    private final List<QualityGate.Condition> conditions = new ArrayList<>();
397
398    private QualityGateBuilder() {
399      // prevents instantiation outside PostProjectAnalysisTaskTester
400    }
401
402    public QualityGateBuilder setId(String id) {
403      this.id = requireNonNull(id, ID_CAN_NOT_BE_NULL);
404      return this;
405    }
406
407    public QualityGateBuilder setName(String name) {
408      this.name = requireNonNull(name, NAME_CAN_NOT_BE_NULL);
409      return this;
410    }
411
412    public QualityGateBuilder setStatus(QualityGate.Status status) {
413      this.status = requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
414      return this;
415    }
416
417    public QualityGateBuilder add(QualityGate.Condition condition) {
418      conditions.add(requireNonNull(condition, "condition cannot be null"));
419      return this;
420    }
421
422    public QualityGateBuilder clearConditions() {
423      this.conditions.clear();
424      return this;
425    }
426
427    public QualityGate build() {
428      requireNonNull(id, ID_CAN_NOT_BE_NULL);
429      requireNonNull(name, NAME_CAN_NOT_BE_NULL);
430      requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
431
432      return new QualityGate() {
433        @Override
434        public String getId() {
435          return id;
436        }
437
438        @Override
439        public String getName() {
440          return name;
441        }
442
443        @Override
444        public Status getStatus() {
445          return status;
446        }
447
448        @Override
449        public Collection<Condition> getConditions() {
450          return conditions;
451        }
452
453        @Override
454        public String toString() {
455          return "QualityGate{" +
456            "id='" + id + '\'' +
457            ", name='" + name + '\'' +
458            ", status=" + status +
459            ", conditions=" + conditions +
460            '}';
461        }
462      };
463    }
464  }
465
466  public static final class ConditionBuilder {
467    private static final String METRIC_KEY_CAN_NOT_BE_NULL = "metricKey cannot be null";
468    private static final String OPERATOR_CAN_NOT_BE_NULL = "operator cannot be null";
469
470    private String metricKey;
471    private QualityGate.Operator operator;
472    private String errorThreshold;
473    private String warningThreshold;
474    private boolean onLeakPeriod;
475
476    private ConditionBuilder() {
477      // prevents instantiation outside PostProjectAnalysisTaskTester
478    }
479
480    public ConditionBuilder setMetricKey(String metricKey) {
481      this.metricKey = requireNonNull(metricKey, METRIC_KEY_CAN_NOT_BE_NULL);
482      return this;
483    }
484
485    public ConditionBuilder setOperator(QualityGate.Operator operator) {
486      this.operator = requireNonNull(operator, OPERATOR_CAN_NOT_BE_NULL);
487      return this;
488    }
489
490    public ConditionBuilder setErrorThreshold(@Nullable String errorThreshold) {
491      this.errorThreshold = errorThreshold;
492      return this;
493    }
494
495    public ConditionBuilder setWarningThreshold(@Nullable String warningThreshold) {
496      this.warningThreshold = warningThreshold;
497      return this;
498    }
499
500    public ConditionBuilder setOnLeakPeriod(boolean onLeakPeriod) {
501      this.onLeakPeriod = onLeakPeriod;
502      return this;
503    }
504
505    public QualityGate.Condition buildNoValue() {
506      checkCommonProperties();
507      return new QualityGate.Condition() {
508        @Override
509        public QualityGate.EvaluationStatus getStatus() {
510          return QualityGate.EvaluationStatus.NO_VALUE;
511        }
512
513        @Override
514        public String getMetricKey() {
515          return metricKey;
516        }
517
518        @Override
519        public QualityGate.Operator getOperator() {
520          return operator;
521        }
522
523        @Override
524        public String getErrorThreshold() {
525          return errorThreshold;
526        }
527
528        @Override
529        public String getWarningThreshold() {
530          return warningThreshold;
531        }
532
533        @Override
534        public boolean isOnLeakPeriod() {
535          return onLeakPeriod;
536        }
537
538        @Override
539        public String getValue() {
540          throw new IllegalStateException("There is no value when status is NO_VALUE");
541        }
542
543        @Override
544        public String toString() {
545          return "Condition{" +
546            "status=" + QualityGate.EvaluationStatus.NO_VALUE +
547            ", metricKey='" + metricKey + '\'' +
548            ", operator=" + operator +
549            ", errorThreshold='" + errorThreshold + '\'' +
550            ", warningThreshold='" + warningThreshold + '\'' +
551            ", onLeakPeriod=" + onLeakPeriod +
552            '}';
553        }
554      };
555    }
556
557    public QualityGate.Condition build(final QualityGate.EvaluationStatus status, final String value) {
558      checkCommonProperties();
559      requireNonNull(status, STATUS_CAN_NOT_BE_NULL);
560      checkArgument(status != QualityGate.EvaluationStatus.NO_VALUE, "status cannot be NO_VALUE, use method buildNoValue() instead");
561      requireNonNull(value, "value cannot be null, use method buildNoValue() instead");
562      return new QualityGate.Condition() {
563        @Override
564        public QualityGate.EvaluationStatus getStatus() {
565          return status;
566        }
567
568        @Override
569        public String getMetricKey() {
570          return metricKey;
571        }
572
573        @Override
574        public QualityGate.Operator getOperator() {
575          return operator;
576        }
577
578        @Override
579        public String getErrorThreshold() {
580          return errorThreshold;
581        }
582
583        @Override
584        public String getWarningThreshold() {
585          return warningThreshold;
586        }
587
588        @Override
589        public boolean isOnLeakPeriod() {
590          return onLeakPeriod;
591        }
592
593        @Override
594        public String getValue() {
595          return value;
596        }
597
598        @Override
599        public String toString() {
600          return "Condition{" +
601            "status=" + status +
602            ", metricKey='" + metricKey + '\'' +
603            ", operator=" + operator +
604            ", errorThreshold='" + errorThreshold + '\'' +
605            ", warningThreshold='" + warningThreshold + '\'' +
606            ", onLeakPeriod=" + onLeakPeriod +
607            ", value='" + value + '\'' +
608            '}';
609        }
610      };
611    }
612
613    private void checkCommonProperties() {
614      requireNonNull(metricKey, METRIC_KEY_CAN_NOT_BE_NULL);
615      requireNonNull(operator, OPERATOR_CAN_NOT_BE_NULL);
616      checkState(errorThreshold != null || warningThreshold != null, "At least one of errorThreshold and warningThreshold must be non null");
617    }
618  }
619
620  public static final class ScannerContextBuilder {
621    private final Map<String, String> properties = new HashMap<>();
622
623    private ScannerContextBuilder() {
624      // prevents instantiation outside PostProjectAnalysisTaskTester
625    }
626
627    public ScannerContextBuilder addProperties(Map<String, String> map) {
628      properties.putAll(map);
629      return this;
630    }
631
632    public ScannerContext build() {
633      return () -> properties;
634    }
635  }
636}