--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.component;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import java.util.List;
+import javax.annotation.Nonnull;
+
+import static com.google.common.collect.FluentIterable.from;
+import static com.google.common.collect.Iterables.concat;
+
+/**
+ * This crawler make any number of {@link TypeAwareVisitor} or {@link PathAwareVisitor} defined in a list visit a component tree, component per component, in the order of the list
+ */
+public class VisitorsCrawler implements ComponentCrawler {
+
+ private final List<VisitorWrapper> preOrderVisitorWrappers;
+ private final List<VisitorWrapper> postOrderVisitorWrappers;
+
+ public VisitorsCrawler(Iterable<Visitor> visitors) {
+ List<VisitorWrapper> visitorWrappers = from(visitors).transform(ToVisitorWrapper.INSTANCE).toList();
+ this.preOrderVisitorWrappers = from(visitorWrappers).filter(MathPreOrderVisitor.INSTANCE).toList();
+ this.postOrderVisitorWrappers = from(visitorWrappers).filter(MatchPostOrderVisitor.INSTANCE).toList();
+ }
+
+ @Override
+ public void visit(final Component component) {
+ List<VisitorWrapper> preOrderVisitorWrappersToExecute = from(preOrderVisitorWrappers).filter(new MatchVisitorMaxDepth(component)).toList();
+ List<VisitorWrapper> postOrderVisitorWrappersToExecute = from(postOrderVisitorWrappers).filter(new MatchVisitorMaxDepth(component)).toList();
+ if (preOrderVisitorWrappersToExecute.isEmpty() && postOrderVisitorWrappersToExecute.isEmpty()) {
+ return;
+ }
+
+ for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappers, postOrderVisitorWrappers)) {
+ visitorWrapper.beforeComponent(component);
+ }
+
+ for (VisitorWrapper visitorWrapper : preOrderVisitorWrappersToExecute) {
+ visitNode(component, visitorWrapper);
+ }
+
+ visitChildren(component);
+
+ for (VisitorWrapper visitorWrapper : postOrderVisitorWrappersToExecute) {
+ visitNode(component, visitorWrapper);
+ }
+
+ for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappersToExecute, postOrderVisitorWrappersToExecute)) {
+ visitorWrapper.afterComponent(component);
+ }
+ }
+
+ private void visitChildren(Component component) {
+ for (Component child : component.getChildren()) {
+ visit(child);
+ }
+ }
+
+ private void visitNode(Component component, VisitorWrapper visitor) {
+ visitor.visitAny(component);
+ switch (component.getType()) {
+ case PROJECT:
+ visitor.visitProject(component);
+ break;
+ case MODULE:
+ visitor.visitModule(component);
+ break;
+ case DIRECTORY:
+ visitor.visitDirectory(component);
+ break;
+ case FILE:
+ visitor.visitFile(component);
+ break;
+ default:
+ throw new IllegalStateException(String.format("Unknown type %s", component.getType().name()));
+ }
+ }
+
+ private enum ToVisitorWrapper implements Function<Visitor, VisitorWrapper> {
+ INSTANCE;
+
+ @Override
+ public VisitorWrapper apply(@Nonnull Visitor visitor) {
+ if (visitor instanceof TypeAwareVisitor) {
+ return new TypeAwareVisitorWrapper((TypeAwareVisitor) visitor);
+ } else if (visitor instanceof PathAwareVisitor) {
+ return new PathAwareVisitorWrapper((PathAwareVisitor) visitor);
+ } else {
+ throw new IllegalArgumentException("Only TypeAwareVisitor and PathAwareVisitor can be used");
+ }
+ }
+ }
+
+ private static class MatchVisitorMaxDepth implements Predicate<VisitorWrapper> {
+ private final Component component;
+
+ private MatchVisitorMaxDepth(Component component) {
+ this.component = component;
+ }
+
+ @Override
+ public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
+ return !component.getType().isDeeperThan(visitorWrapper.getMaxDepth());
+ }
+ }
+
+ private enum MathPreOrderVisitor implements Predicate<VisitorWrapper> {
+ INSTANCE;
+
+ @Override
+ public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
+ return visitorWrapper.getOrder() == Visitor.Order.PRE_ORDER;
+ }
+ }
+
+ private enum MatchPostOrderVisitor implements Predicate<VisitorWrapper> {
+ INSTANCE;
+
+ @Override
+ public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
+ return visitorWrapper.getOrder() == Visitor.Order.POST_ORDER;
+ }
+ }
+}
import org.sonar.core.platform.ComponentContainer;
import org.sonar.server.computation.ReportQueue.Item;
+import org.sonar.server.computation.component.Visitor;
import org.sonar.server.computation.step.ComputationStep;
/**
*/
<T extends ComputationStep> T getStep(Class<T> type);
+ <T extends Visitor> T getComponentVisitor(Class<T> type);
+
+
}
import org.sonar.server.computation.component.DbIdsRepository;
import org.sonar.server.computation.component.ProjectSettingsRepository;
import org.sonar.server.computation.component.TreeRootHolderImpl;
+import org.sonar.server.computation.component.Visitor;
import org.sonar.server.computation.debt.DebtModelHolderImpl;
import org.sonar.server.computation.event.EventRepositoryImpl;
import org.sonar.server.computation.issue.BaseIssuesLoader;
import org.sonar.server.computation.qualitygate.QualityGateServiceImpl;
import org.sonar.server.computation.qualityprofile.ActiveRulesHolderImpl;
import org.sonar.server.computation.sqale.SqaleRatingSettings;
+import org.sonar.server.computation.step.ComponentVisitors;
import org.sonar.server.computation.step.ComputationStep;
import org.sonar.server.computation.step.ComputationSteps;
import org.sonar.server.view.index.ViewIndex;
private final ReportQueue.Item item;
private final ComputationSteps steps;
+ private final ComponentVisitors visitors;
public ComputeEngineContainerImpl(ComponentContainer parent, ReportQueue.Item item) {
super(createContainer(requireNonNull(parent)));
this.item = item;
this.steps = new ComputationSteps(this);
+ this.visitors = new ComponentVisitors(this);
populateContainer(requireNonNull(item));
startComponents();
private void populateContainer(ReportQueue.Item item) {
add(item);
add(steps);
+ add(visitors);
addSingletons(componentClasses());
addSingletons(steps.orderedStepClasses());
+ addSingletons(visitors.orderedClasses());
populateFromModules();
}
return getComponentByType(type);
}
+ @Override
+ public <T extends Visitor> T getComponentVisitor(Class<T> type) {
+ return getComponentByType(type);
+ }
+
@Override
public String toString() {
return "ComputeEngineContainerImpl";
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.sqale;
+
+import com.google.common.base.Optional;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.PathAwareVisitorAdapter;
+import org.sonar.server.computation.component.Visitor;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricRepository;
+
+import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
+
+public class SqaleMeasuresVisitor extends PathAwareVisitorAdapter<SqaleMeasuresVisitor.DevelopmentCost> {
+
+ private final MetricRepository metricRepository;
+ private final MeasureRepository measureRepository;
+ private final SqaleRatingSettings sqaleRatingSettings;
+
+ private final Metric developmentCostMetric;
+ private final Metric technicalDebtMetric;
+ private final Metric debtRatioMetric;
+ private final Metric sqaleRatingMetric;
+
+ public SqaleMeasuresVisitor(MetricRepository metricRepository, MeasureRepository measureRepository, SqaleRatingSettings sqaleRatingSettings) {
+ super(Component.Type.FILE, Visitor.Order.POST_ORDER, new SimpleStackElementFactory<DevelopmentCost>() {
+ @Override
+ public DevelopmentCost createForAny(Component component) {
+ return new DevelopmentCost();
+ }
+ });
+ this.metricRepository = metricRepository;
+ this.measureRepository = measureRepository;
+ this.sqaleRatingSettings = sqaleRatingSettings;
+
+ this.developmentCostMetric = this.metricRepository.getByKey(CoreMetrics.DEVELOPMENT_COST_KEY);
+ this.technicalDebtMetric = this.metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY);
+ this.debtRatioMetric = this.metricRepository.getByKey(CoreMetrics.SQALE_DEBT_RATIO_KEY);
+ this.sqaleRatingMetric = this.metricRepository.getByKey(CoreMetrics.SQALE_RATING_KEY);
+ }
+
+ @Override
+ public void visitProject(Component project, Path<DevelopmentCost> path) {
+ computeAndSaveMeasures(project, path);
+ }
+
+ @Override
+ public void visitDirectory(Component directory, Path<DevelopmentCost> path) {
+ computeAndSaveMeasures(directory, path);
+ }
+
+ @Override
+ public void visitModule(Component module, Path<DevelopmentCost> path) {
+ computeAndSaveMeasures(module, path);
+ }
+
+ @Override
+ public void visitFile(Component file, Path<DevelopmentCost> path) {
+ if (!file.getFileAttributes().isUnitTest()) {
+ long developmentCosts = computeDevelopmentCost(file);
+ path.current().add(developmentCosts);
+ computeAndSaveMeasures(file, path);
+ }
+ }
+
+ private void computeAndSaveMeasures(Component component, Path<DevelopmentCost> path) {
+ saveDevelopmentCostMeasure(component, path.current());
+
+ double density = computeDensity(component, path.current());
+ saveDebtRatioMeasure(component, density);
+ saveSqaleRatingMeasure(component, density);
+
+ increaseParentDevelopmentCost(path);
+ }
+
+ private void saveDevelopmentCostMeasure(Component component, DevelopmentCost developmentCost) {
+ // the value of this measure is stored as a string because it can exceed the size limit of number storage on some DB
+ measureRepository.add(component, developmentCostMetric, newMeasureBuilder().create(Long.toString(developmentCost.getValue())));
+ }
+
+ private double computeDensity(Component component, DevelopmentCost developmentCost) {
+ double debt = getLongValue(measureRepository.getRawMeasure(component, technicalDebtMetric));
+ if (Double.doubleToRawLongBits(developmentCost.getValue()) != 0L) {
+ return debt / (double) developmentCost.getValue();
+ }
+ return 0d;
+ }
+
+ private void saveDebtRatioMeasure(Component component, double density) {
+ measureRepository.add(component, debtRatioMetric, newMeasureBuilder().create(100.0 * density));
+ }
+
+ private void saveSqaleRatingMeasure(Component component, double density) {
+ SqaleRatingGrid ratingGrid = new SqaleRatingGrid(sqaleRatingSettings.getRatingGrid());
+ int rating = ratingGrid.getRatingForDensity(density);
+ String ratingLetter = toRatingLetter(rating);
+ measureRepository.add(component, sqaleRatingMetric, newMeasureBuilder().create(rating, ratingLetter));
+ }
+
+ private void increaseParentDevelopmentCost(Path<DevelopmentCost> path) {
+ if (!path.isRoot()) {
+ // increase parent's developmentCost with our own
+ path.parent().add(path.current().getValue());
+ }
+ }
+
+ private long computeDevelopmentCost(Component file) {
+ String languageKey = file.getFileAttributes().getLanguageKey();
+ String sizeMetricKey = sqaleRatingSettings.getSizeMetricKey(languageKey);
+ Metric sizeMetric = metricRepository.getByKey(sizeMetricKey);
+ return getLongValue(measureRepository.getRawMeasure(file, sizeMetric)) * sqaleRatingSettings.getDevCost(languageKey);
+ }
+
+ private static long getLongValue(Optional<Measure> measure) {
+ if (!measure.isPresent()) {
+ return 0L;
+ }
+ return getLongValue(measure.get());
+ }
+
+ private static long getLongValue(Measure measure) {
+ switch (measure.getValueType()) {
+ case INT:
+ return measure.getIntValue();
+ case LONG:
+ return measure.getLongValue();
+ case DOUBLE:
+ return (long) measure.getDoubleValue();
+ default:
+ return 0L;
+ }
+ }
+
+ private static String toRatingLetter(int rating) {
+ return SqaleRatingGrid.SqaleRating.createForIndex(rating).name();
+ }
+
+ /**
+ * A wrapper class around a long which can be increased and represents the development cost of a Component
+ */
+ public static final class DevelopmentCost {
+ private long value = 0;
+
+ public void add(long developmentCosts) {
+ this.value += developmentCosts;
+ }
+
+ public long getValue() {
+ return value;
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.step;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.sonar.server.computation.component.Visitor;
+import org.sonar.server.computation.container.ComputeEngineContainer;
+import org.sonar.server.computation.sqale.SqaleMeasuresVisitor;
+
+/**
+ * Ordered list of component visitors to be executed by {@link ExecuteVisitorsStep}
+ */
+public class ComponentVisitors {
+
+ private static final List<Class<? extends Visitor>> ORDERED_VISITOR_CLASSES = ImmutableList.<Visitor>of(
+ SqaleMeasuresVisitor.class
+ );
+
+ /**
+ * List of all {@link Visitor}, ordered by execution sequence.
+ */
+ public List<Class<? extends Visitor>> orderedClasses() {
+ return ORDERED_VISITOR_CLASSES;
+ }
+
+ private final ComputeEngineContainer computeEngineContainer;
+
+ public ComponentVisitors(ComputeEngineContainer computeEngineContainer) {
+ this.computeEngineContainer = computeEngineContainer;
+ }
+
+ public Iterable<Visitor> instances() {
+ return Iterables.transform(orderedClasses(), new Function<Class<? extends Visitor>, Visitor>() {
+ @Override
+ public Visitor apply(@Nonnull Class<? extends Visitor> input) {
+ Visitor visitor = computeEngineContainer.getComponentVisitor(input);
+ Preconditions.checkState(visitor != null, String.format("Visitor not found: %s", input));
+ return visitor;
+ }
+ });
+ }
+
+}
CommentMeasuresStep.class,
CustomMeasuresCopyStep.class,
DuplicationMeasuresStep.class,
- // must be executed after the measures required for common rules (coverage, comment density, duplications)
- IntegrateIssuesStep.class,
LanguageDistributionMeasuresStep.class,
UnitTestMeasuresStep.class,
ComplexityMeasuresStep.class,
- // SQALE measures depend on issues
- SqaleMeasuresStep.class,
+ // must be executed after the measures required for common rules (coverage, comment density, duplications)
+ IntegrateIssuesStep.class,
+
+ ExecuteVisitorsStep.class,
FeedMeasureComputers.class,
ComputePluginMeasuresStep.class,
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.step;
+
+import org.sonar.server.computation.component.TreeRootHolder;
+import org.sonar.server.computation.component.Visitor;
+import org.sonar.server.computation.component.VisitorsCrawler;
+
+public class ExecuteVisitorsStep implements ComputationStep {
+ private final TreeRootHolder treeRootHolder;
+ private final Iterable<Visitor> visitors;
+
+ public ExecuteVisitorsStep(TreeRootHolder treeRootHolder, ComponentVisitors visitors) {
+ this.treeRootHolder = treeRootHolder;
+ this.visitors = visitors.instances();
+ }
+
+ @Override
+ public void execute() {
+ new VisitorsCrawler(visitors).visit(treeRootHolder.getRoot());
+ }
+
+ @Override
+ public String getDescription() {
+ return "Execute Visitors";
+ }
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.computation.step;
-
-import com.google.common.base.Optional;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.server.computation.component.Component;
-import org.sonar.server.computation.component.PathAwareCrawler;
-import org.sonar.server.computation.component.TreeRootHolder;
-import org.sonar.server.computation.component.Visitor;
-import org.sonar.server.computation.measure.Measure;
-import org.sonar.server.computation.measure.MeasureRepository;
-import org.sonar.server.computation.metric.Metric;
-import org.sonar.server.computation.metric.MetricRepository;
-import org.sonar.server.computation.sqale.SqaleRatingGrid;
-import org.sonar.server.computation.sqale.SqaleRatingSettings;
-
-import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
-
-public class SqaleMeasuresStep implements ComputationStep {
- private final TreeRootHolder treeRootHolder;
- private final MetricRepository metricRepository;
- private final MeasureRepository measureRepository;
- private final SqaleRatingSettings sqaleRatingSettings;
-
- public SqaleMeasuresStep(TreeRootHolder treeRootHolder, MetricRepository metricRepository, MeasureRepository measureRepository,
- SqaleRatingSettings sqaleRatingSettings) {
- this.treeRootHolder = treeRootHolder;
- this.metricRepository = metricRepository;
- this.measureRepository = measureRepository;
- this.sqaleRatingSettings = sqaleRatingSettings;
- }
-
- @Override
- public void execute() {
- new SqaleMeasuresCrawler().visit(treeRootHolder.getRoot());
- }
-
- @Override
- public String getDescription() {
- return "Compute Sqale related measures";
- }
-
- private class SqaleMeasuresCrawler extends PathAwareCrawler<DevelopmentCost> {
- private final Metric developmentCostMetric;
- private final Metric technicalDebtMetric;
- private final Metric debtRatioMetric;
- private final Metric sqaleRatingMetric;
-
- public SqaleMeasuresCrawler() {
- super(Component.Type.FILE, Visitor.Order.POST_ORDER, new SimpleStackElementFactory<DevelopmentCost>() {
- @Override
- public DevelopmentCost createForAny(Component component) {
- return new DevelopmentCost();
- }
- });
-
- this.developmentCostMetric = metricRepository.getByKey(CoreMetrics.DEVELOPMENT_COST_KEY);
- this.technicalDebtMetric = metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY);
- this.debtRatioMetric = metricRepository.getByKey(CoreMetrics.SQALE_DEBT_RATIO_KEY);
- this.sqaleRatingMetric = metricRepository.getByKey(CoreMetrics.SQALE_RATING_KEY);
- }
-
- @Override
- public void visitProject(Component project, Path<DevelopmentCost> path) {
- computeAndSaveMeasures(project, path);
- }
-
- @Override
- public void visitDirectory(Component directory, Path<DevelopmentCost> path) {
- computeAndSaveMeasures(directory, path);
- }
-
- @Override
- public void visitModule(Component module, Path<DevelopmentCost> path) {
- computeAndSaveMeasures(module, path);
- }
-
- @Override
- public void visitFile(Component file, Path<DevelopmentCost> path) {
- if (!file.getFileAttributes().isUnitTest()) {
- long developmentCosts = computeDevelopmentCost(file);
- path.current().add(developmentCosts);
- computeAndSaveMeasures(file, path);
- }
- }
-
- private void computeAndSaveMeasures(Component component, Path<DevelopmentCost> path) {
- saveDevelopmentCostMeasure(component, path.current());
-
- double density = computeDensity(component, path.current());
- saveDebtRatioMeasure(component, density);
- saveSqaleRatingMeasure(component, density);
-
- increaseParentDevelopmentCost(path);
- }
-
- private void saveDevelopmentCostMeasure(Component component, DevelopmentCost developmentCost) {
- // the value of this measure is stored as a string because it can exceed the size limit of number storage on some DB
- measureRepository.add(component, developmentCostMetric, newMeasureBuilder().create(Long.toString(developmentCost.getValue())));
- }
-
- private double computeDensity(Component component, DevelopmentCost developmentCost) {
- double debt = getLongValue(measureRepository.getRawMeasure(component, technicalDebtMetric));
- if (Double.doubleToRawLongBits(developmentCost.getValue()) != 0L) {
- return debt / (double) developmentCost.getValue();
- }
- return 0d;
- }
-
- private void saveDebtRatioMeasure(Component component, double density) {
- measureRepository.add(component, debtRatioMetric, newMeasureBuilder().create(100.0 * density));
- }
-
- private void saveSqaleRatingMeasure(Component component, double density) {
- SqaleRatingGrid ratingGrid = new SqaleRatingGrid(sqaleRatingSettings.getRatingGrid());
- int rating = ratingGrid.getRatingForDensity(density);
- String ratingLetter = toRatingLetter(rating);
- measureRepository.add(component, sqaleRatingMetric, newMeasureBuilder().create(rating, ratingLetter));
- }
-
- private void increaseParentDevelopmentCost(Path<DevelopmentCost> path) {
- if (!path.isRoot()) {
- // increase parent's developmentCost with our own
- path.parent().add(path.current().getValue());
- }
- }
- }
-
- private long computeDevelopmentCost(Component file) {
- String languageKey = file.getFileAttributes().getLanguageKey();
- String sizeMetricKey = sqaleRatingSettings.getSizeMetricKey(languageKey);
- Metric sizeMetric = metricRepository.getByKey(sizeMetricKey);
- return getLongValue(measureRepository.getRawMeasure(file, sizeMetric)) * sqaleRatingSettings.getDevCost(languageKey);
- }
-
- private static long getLongValue(Optional<Measure> measure) {
- if (!measure.isPresent()) {
- return 0L;
- }
- return getLongValue(measure.get());
- }
-
- private static long getLongValue(Measure measure) {
- switch (measure.getValueType()) {
- case INT:
- return measure.getIntValue();
- case LONG:
- return measure.getLongValue();
- case DOUBLE:
- return (long) measure.getDoubleValue();
- default:
- return 0L;
- }
- }
-
- private static String toRatingLetter(int rating) {
- return SqaleRatingGrid.SqaleRating.createForIndex(rating).name();
- }
-
- /**
- * A wrapper class around a long which can be increased and represents the development cost of a Component
- */
- private static final class DevelopmentCost {
- private long value = 0;
-
- public void add(long developmentCosts) {
- this.value += developmentCosts;
- }
-
- public long getValue() {
- return value;
- }
- }
-}
return ref;
}
+ @Override
+ public String toString() {
+ return "DumbComponent{" +
+ "ref=" + ref +
+ ", key='" + key + '\'' +
+ ", type=" + type +
+ '}';
+ }
+
public static Builder builder(Type type, int ref) {
return new Builder(type, ref);
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.component;
+
+import java.util.Arrays;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.InOrder;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.spy;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.Component.Type.MODULE;
+import static org.sonar.server.computation.component.Component.Type.PROJECT;
+import static org.sonar.server.computation.component.Visitor.Order.POST_ORDER;
+import static org.sonar.server.computation.component.Visitor.Order.PRE_ORDER;
+
+public class VisitorsCrawlerTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private static final Component FILE_5 = component(FILE, 5);
+ private static final Component DIRECTORY_4 = component(DIRECTORY, 4, FILE_5);
+ private static final Component MODULE_3 = component(MODULE, 3, DIRECTORY_4);
+ private static final Component MODULE_2 = component(MODULE, 2, MODULE_3);
+ private static final Component COMPONENT_TREE = component(PROJECT, 1, MODULE_2);
+
+ private final TypeAwareVisitor spyPreOrderTypeAwareVisitor = spy(new TestTypeAwareVisitor(FILE, PRE_ORDER));
+ private final TypeAwareVisitor spyPostOrderTypeAwareVisitor = spy(new TestTypeAwareVisitor(FILE, POST_ORDER));
+ private final TestPathAwareVisitor spyPathAwareVisitor = spy(new TestPathAwareVisitor(FILE, POST_ORDER));
+
+ @Test
+ public void execute_each_visitor_on_each_level() throws Exception {
+ InOrder inOrder = inOrder(spyPostOrderTypeAwareVisitor, spyPathAwareVisitor);
+ VisitorsCrawler underTest = new VisitorsCrawler(Arrays.asList(spyPostOrderTypeAwareVisitor, spyPathAwareVisitor));
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitAny(FILE_5);
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitFile(FILE_5);
+ inOrder.verify(spyPathAwareVisitor).visitAny(eq(FILE_5), any(PathAwareVisitor.Path.class));
+ inOrder.verify(spyPathAwareVisitor).visitFile(eq(FILE_5), any(PathAwareVisitor.Path.class));
+
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitAny(DIRECTORY_4);
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitDirectory(DIRECTORY_4);
+ inOrder.verify(spyPathAwareVisitor).visitAny(eq(DIRECTORY_4), any(PathAwareVisitor.Path.class));
+ inOrder.verify(spyPathAwareVisitor).visitDirectory(eq(DIRECTORY_4), any(PathAwareVisitor.Path.class));
+
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitAny(MODULE_3);
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitModule(MODULE_3);
+ inOrder.verify(spyPathAwareVisitor).visitAny(eq(MODULE_3), any(PathAwareVisitor.Path.class));
+ inOrder.verify(spyPathAwareVisitor).visitModule(eq(MODULE_3), any(PathAwareVisitor.Path.class));
+
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitAny(MODULE_2);
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitModule(MODULE_2);
+ inOrder.verify(spyPathAwareVisitor).visitAny(eq(MODULE_2), any(PathAwareVisitor.Path.class));
+ inOrder.verify(spyPathAwareVisitor).visitModule(eq(MODULE_2), any(PathAwareVisitor.Path.class));
+
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitAny(COMPONENT_TREE);
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verify(spyPathAwareVisitor).visitAny(eq(COMPONENT_TREE), any(PathAwareVisitor.Path.class));
+ inOrder.verify(spyPathAwareVisitor).visitProject(eq(COMPONENT_TREE), any(PathAwareVisitor.Path.class));
+ }
+
+ @Test
+ public void execute_pre_visitor_before_post_visitor() throws Exception {
+ InOrder inOrder = inOrder(spyPreOrderTypeAwareVisitor, spyPostOrderTypeAwareVisitor);
+ VisitorsCrawler underTest = new VisitorsCrawler(Arrays.<Visitor>asList(spyPreOrderTypeAwareVisitor, spyPostOrderTypeAwareVisitor));
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyPreOrderTypeAwareVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verify(spyPreOrderTypeAwareVisitor).visitModule(MODULE_2);
+ inOrder.verify(spyPreOrderTypeAwareVisitor).visitModule(MODULE_3);
+ inOrder.verify(spyPreOrderTypeAwareVisitor).visitDirectory(DIRECTORY_4);
+ inOrder.verify(spyPreOrderTypeAwareVisitor).visitFile(FILE_5);
+
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitFile(FILE_5);
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitDirectory(DIRECTORY_4);
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitModule(MODULE_3);
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitModule(MODULE_2);
+ inOrder.verify(spyPostOrderTypeAwareVisitor).visitProject(COMPONENT_TREE);
+ }
+
+ @Test
+ public void fail_with_IAE_when_visitor_is_not_path_aware_or_type_aware() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Only TypeAwareVisitor and PathAwareVisitor can be used");
+
+ Visitor visitor = new Visitor() {
+ @Override
+ public Order getOrder() {
+ return PRE_ORDER;
+ }
+
+ @Override
+ public Component.Type getMaxDepth() {
+ return FILE;
+ }
+ };
+ new VisitorsCrawler(Arrays.asList(visitor));
+ }
+
+ private static Component component(final Component.Type type, final int ref, final Component... children) {
+ return DumbComponent.builder(type, ref).addChildren(children).build();
+ }
+
+ private static class TestTypeAwareVisitor extends TypeAwareVisitorAdapter {
+
+ public TestTypeAwareVisitor(Component.Type maxDepth, Visitor.Order order) {
+ super(maxDepth, order);
+ }
+ }
+
+ private static class TestPathAwareVisitor extends PathAwareVisitorAdapter<Integer> {
+
+ public TestPathAwareVisitor(Component.Type maxDepth, Visitor.Order order) {
+ super(maxDepth, order, new SimpleStackElementFactory<Integer>() {
+ @Override
+ public Integer createForAny(Component component) {
+ return component.getRef();
+ }
+ });
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.component;
+
+import com.google.common.base.Function;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.junit.Test;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.FluentIterable.from;
+import static com.google.common.collect.ImmutableList.of;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.Component.Type.MODULE;
+import static org.sonar.server.computation.component.Component.Type.PROJECT;
+import static org.sonar.server.computation.component.Visitor.Order.POST_ORDER;
+import static org.sonar.server.computation.component.Visitor.Order.PRE_ORDER;
+
+public class VisitorsCrawlerWithPathAwareVisitorTest {
+
+ private static final int ROOT_REF = 1;
+ private static final DumbComponent SOME_TREE_ROOT = DumbComponent.builder(PROJECT, ROOT_REF)
+ .addChildren(
+ DumbComponent.builder(MODULE, 11)
+ .addChildren(
+ DumbComponent.builder(DIRECTORY, 111)
+ .addChildren(
+ DumbComponent.builder(FILE, 1111).build(),
+ DumbComponent.builder(FILE, 1112).build()
+ )
+ .build(),
+ DumbComponent.builder(DIRECTORY, 112)
+ .addChildren(
+ DumbComponent.builder(FILE, 1121).build()
+ )
+ .build())
+ .build(),
+ DumbComponent.builder(MODULE, 12)
+ .addChildren(
+ DumbComponent.builder(MODULE, 121)
+ .addChildren(
+ DumbComponent.builder(DIRECTORY, 1211)
+ .addChildren(
+ DumbComponent.builder(FILE, 12111).build()
+ )
+ .build()
+ ).build()
+ ).build()
+ ).build();
+
+ @Test
+ public void verify_preOrder_visit_call_when_visit_tree_with_depth_FILE() {
+ TestPathAwareVisitor visitor = new TestPathAwareVisitor(FILE, PRE_ORDER);
+ VisitorsCrawler underTest = newVisitorsCrawler(visitor);
+ underTest.visit(SOME_TREE_ROOT);
+
+ Iterator<CallRecord> expected = of(
+ newCallRecord("visitAny", 1, null, of(1)),
+ newCallRecord("visitProject", 1, null, of(1)),
+ newCallRecord("visitAny", 11, 1, of(11, 1)),
+ newCallRecord("visitModule", 11, 1, of(11, 1)),
+ newCallRecord("visitAny", 111, 11, of(111, 11, 1)),
+ newCallRecord("visitDirectory", 111, 11, of(111, 11, 1)),
+ newCallRecord("visitAny", 1111, 111, of(1111, 111, 11, 1)),
+ newCallRecord("visitFile", 1111, 111, of(1111, 111, 11, 1)),
+ newCallRecord("visitAny", 1112, 111, of(1112, 111, 11, 1)),
+ newCallRecord("visitFile", 1112, 111, of(1112, 111, 11, 1)),
+ newCallRecord("visitAny", 112, 11, of(112, 11, 1)),
+ newCallRecord("visitDirectory", 112, 11, of(112, 11, 1)),
+ newCallRecord("visitAny", 1121, 112, of(1121, 112, 11, 1)),
+ newCallRecord("visitFile", 1121, 112, of(1121, 112, 11, 1)),
+ newCallRecord("visitAny", 12, 1, of(12, 1)),
+ newCallRecord("visitModule", 12, 1, of(12, 1)),
+ newCallRecord("visitAny", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitModule", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitAny", 1211, 121, of(1211, 121, 12, 1)),
+ newCallRecord("visitDirectory", 1211, 121, of(1211, 121, 12, 1)),
+ newCallRecord("visitAny", 12111, 1211, of(12111, 1211, 121, 12, 1)),
+ newCallRecord("visitFile", 12111, 1211, of(12111, 1211, 121, 12, 1))
+ ).iterator();
+ verifyCallRecords(expected, visitor.callsRecords.iterator());
+ }
+
+ @Test
+ public void verify_preOrder_visit_call_when_visit_tree_with_depth_DIRECTORY() {
+ TestPathAwareVisitor visitor = new TestPathAwareVisitor(DIRECTORY, PRE_ORDER);
+ VisitorsCrawler underTest = newVisitorsCrawler(visitor);
+ underTest.visit(SOME_TREE_ROOT);
+
+ Iterator<CallRecord> expected = of(
+ newCallRecord("visitAny", 1, null, of(1)),
+ newCallRecord("visitProject", 1, null, of(1)),
+ newCallRecord("visitAny", 11, 1, of(11, 1)),
+ newCallRecord("visitModule", 11, 1, of(11, 1)),
+ newCallRecord("visitAny", 111, 11, of(111, 11, 1)),
+ newCallRecord("visitDirectory", 111, 11, of(111, 11, 1)),
+ newCallRecord("visitAny", 112, 11, of(112, 11, 1)),
+ newCallRecord("visitDirectory", 112, 11, of(112, 11, 1)),
+ newCallRecord("visitAny", 12, 1, of(12, 1)),
+ newCallRecord("visitModule", 12, 1, of(12, 1)),
+ newCallRecord("visitAny", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitModule", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitAny", 1211, 121, of(1211, 121, 12, 1)),
+ newCallRecord("visitDirectory", 1211, 121, of(1211, 121, 12, 1))
+ ).iterator();
+ verifyCallRecords(expected, visitor.callsRecords.iterator());
+ }
+
+ @Test
+ public void verify_preOrder_visit_call_when_visit_tree_with_depth_MODULE() {
+ TestPathAwareVisitor visitor = new TestPathAwareVisitor(MODULE, PRE_ORDER);
+ VisitorsCrawler underTest = newVisitorsCrawler(visitor);
+ underTest.visit(SOME_TREE_ROOT);
+
+ Iterator<CallRecord> expected = of(
+ newCallRecord("visitAny", 1, null, of(1)),
+ newCallRecord("visitProject", 1, null, of(1)),
+ newCallRecord("visitAny", 11, 1, of(11, 1)),
+ newCallRecord("visitModule", 11, 1, of(11, 1)),
+ newCallRecord("visitAny", 12, 1, of(12, 1)),
+ newCallRecord("visitModule", 12, 1, of(12, 1)),
+ newCallRecord("visitAny", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitModule", 121, 12, of(121, 12, 1))
+ ).iterator();
+ verifyCallRecords(expected, visitor.callsRecords.iterator());
+ }
+
+ @Test
+ public void verify_preOrder_visit_call_when_visit_tree_with_depth_PROJECT() {
+ TestPathAwareVisitor visitor = new TestPathAwareVisitor(PROJECT, PRE_ORDER);
+ VisitorsCrawler underTest = newVisitorsCrawler(visitor);
+ underTest.visit(SOME_TREE_ROOT);
+
+ Iterator<CallRecord> expected = of(
+ newCallRecord("visitAny", 1, null, of(1)),
+ newCallRecord("visitProject", 1, null, of(1))
+ ).iterator();
+ verifyCallRecords(expected, visitor.callsRecords.iterator());
+ }
+
+ @Test
+ public void verify_postOrder_visit_call_when_visit_tree_with_depth_FILE() {
+ TestPathAwareVisitor visitor = new TestPathAwareVisitor(FILE, POST_ORDER);
+ VisitorsCrawler underTest = newVisitorsCrawler(visitor);
+ underTest.visit(SOME_TREE_ROOT);
+
+ Iterator<CallRecord> expected = of(
+ newCallRecord("visitAny", 1111, 111, of(1111, 111, 11, 1)),
+ newCallRecord("visitFile", 1111, 111, of(1111, 111, 11, 1)),
+ newCallRecord("visitAny", 1112, 111, of(1112, 111, 11, 1)),
+ newCallRecord("visitFile", 1112, 111, of(1112, 111, 11, 1)),
+ newCallRecord("visitAny", 111, 11, of(111, 11, 1)),
+ newCallRecord("visitDirectory", 111, 11, of(111, 11, 1)),
+ newCallRecord("visitAny", 1121, 112, of(1121, 112, 11, 1)),
+ newCallRecord("visitFile", 1121, 112, of(1121, 112, 11, 1)),
+ newCallRecord("visitAny", 112, 11, of(112, 11, 1)),
+ newCallRecord("visitDirectory", 112, 11, of(112, 11, 1)),
+ newCallRecord("visitAny", 11, 1, of(11, 1)),
+ newCallRecord("visitModule", 11, 1, of(11, 1)),
+ newCallRecord("visitAny", 12111, 1211, of(12111, 1211, 121, 12, 1)),
+ newCallRecord("visitFile", 12111, 1211, of(12111, 1211, 121, 12, 1)),
+ newCallRecord("visitAny", 1211, 121, of(1211, 121, 12, 1)),
+ newCallRecord("visitDirectory", 1211, 121, of(1211, 121, 12, 1)),
+ newCallRecord("visitAny", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitModule", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitAny", 12, 1, of(12, 1)),
+ newCallRecord("visitModule", 12, 1, of(12, 1)),
+ newCallRecord("visitAny", 1, null, of(1)),
+ newCallRecord("visitProject", 1, null, of(1))
+ ).iterator();
+ verifyCallRecords(expected, visitor.callsRecords.iterator());
+ }
+
+ @Test
+ public void verify_postOrder_visit_call_when_visit_tree_with_depth_DIRECTORY() {
+ TestPathAwareVisitor visitor = new TestPathAwareVisitor(DIRECTORY, POST_ORDER);
+ VisitorsCrawler underTest = newVisitorsCrawler(visitor);
+ underTest.visit(SOME_TREE_ROOT);
+
+ Iterator<CallRecord> expected = of(
+ newCallRecord("visitAny", 111, 11, of(111, 11, 1)),
+ newCallRecord("visitDirectory", 111, 11, of(111, 11, 1)),
+ newCallRecord("visitAny", 112, 11, of(112, 11, 1)),
+ newCallRecord("visitDirectory", 112, 11, of(112, 11, 1)),
+ newCallRecord("visitAny", 11, 1, of(11, 1)),
+ newCallRecord("visitModule", 11, 1, of(11, 1)),
+ newCallRecord("visitAny", 1211, 121, of(1211, 121, 12, 1)),
+ newCallRecord("visitDirectory", 1211, 121, of(1211, 121, 12, 1)),
+ newCallRecord("visitAny", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitModule", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitAny", 12, 1, of(12, 1)),
+ newCallRecord("visitModule", 12, 1, of(12, 1)),
+ newCallRecord("visitAny", 1, null, of(1)),
+ newCallRecord("visitProject", 1, null, of(1))
+ ).iterator();
+ verifyCallRecords(expected, visitor.callsRecords.iterator());
+ }
+
+ @Test
+ public void verify_postOrder_visit_call_when_visit_tree_with_depth_MODULE() {
+ TestPathAwareVisitor visitor = new TestPathAwareVisitor(MODULE, POST_ORDER);
+ VisitorsCrawler underTest = newVisitorsCrawler(visitor);
+ underTest.visit(SOME_TREE_ROOT);
+
+ Iterator<CallRecord> expected = of(
+ newCallRecord("visitAny", 11, 1, of(11, 1)),
+ newCallRecord("visitModule", 11, 1, of(11, 1)),
+ newCallRecord("visitAny", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitModule", 121, 12, of(121, 12, 1)),
+ newCallRecord("visitAny", 12, 1, of(12, 1)),
+ newCallRecord("visitModule", 12, 1, of(12, 1)),
+ newCallRecord("visitAny", 1, null, of(1)),
+ newCallRecord("visitProject", 1, null, of(1))
+ ).iterator();
+ verifyCallRecords(expected, visitor.callsRecords.iterator());
+ }
+
+ @Test
+ public void verify_postOrder_visit_call_when_visit_tree_with_depth_PROJECT() {
+ TestPathAwareVisitor visitor = new TestPathAwareVisitor(PROJECT, POST_ORDER);
+ VisitorsCrawler underTest = newVisitorsCrawler(visitor);
+ underTest.visit(SOME_TREE_ROOT);
+
+ Iterator<CallRecord> expected = of(
+ newCallRecord("visitAny", 1, null, of(1)),
+ newCallRecord("visitProject", 1, null, of(1))
+ ).iterator();
+ verifyCallRecords(expected, visitor.callsRecords.iterator());
+ }
+
+ private static void verifyCallRecords(Iterator<CallRecord> expected, Iterator<CallRecord> actual) {
+ while (expected.hasNext()) {
+ assertThat(actual.next()).isEqualTo(expected.next());
+ }
+ }
+
+ private static CallRecord newCallRecord(String method, int currentRef, @Nullable Integer parentRef, List<Integer> path) {
+ return new CallRecord(method, currentRef, currentRef, parentRef, ROOT_REF, path);
+ }
+
+ private static VisitorsCrawler newVisitorsCrawler(Visitor visitor) {
+ return new VisitorsCrawler(Arrays.asList(visitor));
+ }
+
+ private static class TestPathAwareVisitor extends PathAwareVisitorAdapter<Integer> {
+ private final List<CallRecord> callsRecords = new ArrayList<>();
+
+ public TestPathAwareVisitor(Component.Type maxDepth, Visitor.Order order) {
+ super(maxDepth, order, new SimpleStackElementFactory<Integer>() {
+ @Override
+ public Integer createForAny(Component component) {
+ return component.getRef();
+ }
+ });
+ }
+
+ @Override
+ public void visitProject(Component project, Path<Integer> path) {
+ callsRecords.add(newCallRecord(project, path, "visitProject"));
+ }
+
+ @Override
+ public void visitModule(Component module, Path<Integer> path) {
+ callsRecords.add(newCallRecord(module, path, "visitModule"));
+ }
+
+ @Override
+ public void visitDirectory(Component directory, Path<Integer> path) {
+ callsRecords.add(newCallRecord(directory, path, "visitDirectory"));
+ }
+
+ @Override
+ public void visitFile(Component file, Path<Integer> path) {
+ callsRecords.add(newCallRecord(file, path, "visitFile"));
+ }
+
+ @Override
+ public void visitUnknown(Component unknownComponent, Path<Integer> path) {
+ callsRecords.add(newCallRecord(unknownComponent, path, "visitUnknown"));
+ }
+
+ @Override
+ public void visitAny(Component component, Path<Integer> path) {
+ callsRecords.add(newCallRecord(component, path, "visitAny"));
+ }
+
+ private static CallRecord newCallRecord(Component project, Path<Integer> path, String method) {
+ return new CallRecord(method, project.getRef(), path.current(), getParent(path), path.root(),
+ toValueList(path));
+ }
+
+ private static List<Integer> toValueList(Path<Integer> path) {
+ return from(path.getCurrentPath()).transform(new Function<PathElement<Integer>, Integer>() {
+ @Nonnull
+ @Override
+ public Integer apply(@Nonnull PathElement<Integer> input) {
+ return input.getElement();
+ }
+ }).toList();
+ }
+
+ private static Integer getParent(Path<Integer> path) {
+ try {
+ Integer parent = path.parent();
+ checkArgument(parent != null, "Path.parent returned a null value!");
+ return parent;
+ } catch (NoSuchElementException e) {
+ return null;
+ }
+ }
+ }
+
+ private static class CallRecord {
+ private final String method;
+ private final int ref;
+ private final int current;
+ @CheckForNull
+ private final Integer parent;
+ private final int root;
+ private final List<Integer> path;
+
+ private CallRecord(String method, int ref, int current, @Nullable Integer parent, int root, List<Integer> path) {
+ this.method = method;
+ this.ref = ref;
+ this.current = current;
+ this.parent = parent;
+ this.root = root;
+ this.path = path;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CallRecord that = (CallRecord) o;
+ return Objects.equals(ref, that.ref) &&
+ Objects.equals(current, that.current) &&
+ Objects.equals(root, that.root) &&
+ Objects.equals(method, that.method) &&
+ Objects.equals(parent, that.parent) &&
+ Objects.equals(path, that.path);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, ref, current, parent, root, path);
+ }
+
+ @Override
+ public String toString() {
+ return "{" +
+ "method='" + method + '\'' +
+ ", ref=" + ref +
+ ", current=" + current +
+ ", parent=" + parent +
+ ", root=" + root +
+ ", path=" + path +
+ '}';
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.component;
+
+import java.util.Arrays;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.Component.Type.MODULE;
+import static org.sonar.server.computation.component.Component.Type.PROJECT;
+import static org.sonar.server.computation.component.Visitor.Order.POST_ORDER;
+
+public class VisitorsCrawlerWithPostOrderTypeAwareVisitorTest {
+
+ private static final Component FILE_5 = component(FILE, 5);
+ private static final Component FILE_6 = component(FILE, 6);
+ private static final Component DIRECTORY_4 = component(DIRECTORY, 4, FILE_5, FILE_6);
+ private static final Component MODULE_3 = component(MODULE, 3, DIRECTORY_4);
+ private static final Component MODULE_2 = component(MODULE, 2, MODULE_3);
+ private static final Component COMPONENT_TREE = component(PROJECT, 1, MODULE_2);
+
+ private final TypeAwareVisitor spyProjectVisitor = spy(new TypeAwareVisitorAdapter(PROJECT, POST_ORDER) {
+ });
+ private final TypeAwareVisitor spyModuleVisitor = spy(new TypeAwareVisitorAdapter(MODULE, POST_ORDER) {
+ });
+ private final TypeAwareVisitor spyDirectoryVisitor = spy(new TypeAwareVisitorAdapter(DIRECTORY, POST_ORDER) {
+ });
+ private final TypeAwareVisitor spyFileVisitor = spy(new TypeAwareVisitorAdapter(FILE, POST_ORDER) {
+ });
+ private final InOrder inOrder = inOrder(spyProjectVisitor, spyModuleVisitor, spyDirectoryVisitor, spyFileVisitor);
+
+ @Test(expected = NullPointerException.class)
+ public void visit_null_Component_throws_NPE() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(null);
+ }
+
+ @Test
+ public void visit_file_with_depth_FILE_calls_visit_file() {
+ Component component = component(FILE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyFileVisitor).visitAny(component);
+ inOrder.verify(spyFileVisitor).visitFile(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_module_with_depth_FILE_calls_visit_module() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyFileVisitor).visitAny(component);
+ inOrder.verify(spyFileVisitor).visitModule(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_directory_with_depth_FILE_calls_visit_directory() {
+ Component component = component(DIRECTORY, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyFileVisitor).visitAny(component);
+ inOrder.verify(spyFileVisitor).visitDirectory(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_project_with_depth_FILE_calls_visit_project() {
+ Component component = component(PROJECT, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyFileVisitor).visitAny(component);
+ inOrder.verify(spyFileVisitor).visitProject(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_file_with_depth_DIRECTORY_does_not_call_visit_file_nor_visitAny() {
+ Component component = component(FILE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyDirectoryVisitor, never()).visitFile(component);
+ inOrder.verify(spyDirectoryVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_directory_with_depth_DIRECTORY_calls_visit_directory() {
+ Component component = component(DIRECTORY, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyDirectoryVisitor).visitAny(component);
+ inOrder.verify(spyDirectoryVisitor).visitDirectory(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_module_with_depth_DIRECTORY_calls_visit_module() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyDirectoryVisitor).visitAny(component);
+ inOrder.verify(spyDirectoryVisitor).visitModule(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_project_with_depth_DIRECTORY_calls_visit_project() {
+ Component component = component(PROJECT, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyDirectoryVisitor).visitAny(component);
+ inOrder.verify(spyDirectoryVisitor).visitProject(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_file_with_depth_MODULE_does_not_call_visit_file_nor_visitAny() {
+ Component component = component(FILE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyModuleVisitor, never()).visitFile(component);
+ inOrder.verify(spyModuleVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_directory_with_depth_MODULE_does_not_call_visit_directory_nor_visitAny() {
+ Component component = component(DIRECTORY, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyModuleVisitor, never()).visitDirectory(component);
+ inOrder.verify(spyModuleVisitor, never()).visitFile(component);
+ inOrder.verify(spyModuleVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_module_with_depth_MODULE_calls_visit_module() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyModuleVisitor).visitAny(component);
+ inOrder.verify(spyModuleVisitor).visitModule(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_project_with_depth_MODULE_calls_visit_project() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyModuleVisitor).visitAny(component);
+ inOrder.verify(spyModuleVisitor).visitModule(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_file_with_depth_PROJECT_does_not_call_visit_file_nor_visitAny() {
+ Component component = component(FILE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyProjectVisitor, never()).visitFile(component);
+ inOrder.verify(spyProjectVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_directory_with_depth_PROJECT_does_not_call_visit_directory_nor_visitAny() {
+ Component component = component(DIRECTORY, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyProjectVisitor, never()).visitDirectory(component);
+ inOrder.verify(spyProjectVisitor, never()).visitFile(component);
+ inOrder.verify(spyProjectVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_module_with_depth_PROJECT_does_not_call_visit_module_nor_visitAny() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyProjectVisitor, never()).visitModule(component);
+ inOrder.verify(spyProjectVisitor, never()).visitDirectory(component);
+ inOrder.verify(spyProjectVisitor, never()).visitFile(component);
+ inOrder.verify(spyProjectVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_project_with_depth_PROJECT_calls_visit_project() {
+ Component component = component(PROJECT, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyProjectVisitor).visitAny(component);
+ inOrder.verify(spyProjectVisitor).visitProject(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void verify_visit_call_when_visit_tree_with_depth_FILE() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyFileVisitor).visitAny(FILE_5);
+ inOrder.verify(spyFileVisitor).visitFile(FILE_5);
+ inOrder.verify(spyFileVisitor).visitAny(FILE_6);
+ inOrder.verify(spyFileVisitor).visitFile(FILE_6);
+ inOrder.verify(spyFileVisitor).visitAny(DIRECTORY_4);
+ inOrder.verify(spyFileVisitor).visitDirectory(DIRECTORY_4);
+ inOrder.verify(spyFileVisitor).visitAny(MODULE_3);
+ inOrder.verify(spyFileVisitor).visitModule(MODULE_3);
+ inOrder.verify(spyFileVisitor).visitAny(MODULE_2);
+ inOrder.verify(spyFileVisitor).visitModule(MODULE_2);
+ inOrder.verify(spyFileVisitor).visitAny(COMPONENT_TREE);
+ inOrder.verify(spyFileVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void verify_visit_call_when_visit_tree_with_depth_DIRECTORY() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyDirectoryVisitor).visitAny(DIRECTORY_4);
+ inOrder.verify(spyDirectoryVisitor).visitDirectory(DIRECTORY_4);
+ inOrder.verify(spyDirectoryVisitor).visitAny(MODULE_3);
+ inOrder.verify(spyDirectoryVisitor).visitModule(MODULE_3);
+ inOrder.verify(spyDirectoryVisitor).visitAny(MODULE_2);
+ inOrder.verify(spyDirectoryVisitor).visitModule(MODULE_2);
+ inOrder.verify(spyDirectoryVisitor).visitAny(COMPONENT_TREE);
+ inOrder.verify(spyDirectoryVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void verify_visit_call_when_visit_tree_with_depth_MODULE() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyModuleVisitor).visitAny(MODULE_3);
+ inOrder.verify(spyModuleVisitor).visitModule(MODULE_3);
+ inOrder.verify(spyModuleVisitor).visitAny(MODULE_2);
+ inOrder.verify(spyModuleVisitor).visitModule(MODULE_2);
+ inOrder.verify(spyModuleVisitor).visitAny(COMPONENT_TREE);
+ inOrder.verify(spyModuleVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void verify_visit_call_when_visit_tree_with_depth_PROJECT() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyProjectVisitor).visitAny(COMPONENT_TREE);
+ inOrder.verify(spyProjectVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ private static Component component(final Component.Type type, final int ref, final Component... children) {
+ return DumbComponent.builder(type, ref).addChildren(children).build();
+ }
+
+ private static VisitorsCrawler newVisitorsCrawler(Visitor visitor) {
+ return new VisitorsCrawler(Arrays.asList(visitor));
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.component;
+
+import java.util.Arrays;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.Component.Type.MODULE;
+import static org.sonar.server.computation.component.Component.Type.PROJECT;
+import static org.sonar.server.computation.component.Visitor.Order.PRE_ORDER;
+
+public class VisitorsCrawlerWithPreOrderTypeAwareVisitorTest {
+
+ private static final Component FILE_5 = component(FILE, 5);
+ private static final Component FILE_6 = component(FILE, 6);
+ private static final Component DIRECTORY_4 = component(DIRECTORY, 4, FILE_5, FILE_6);
+ private static final Component MODULE_3 = component(MODULE, 3, DIRECTORY_4);
+ private static final Component MODULE_2 = component(MODULE, 2, MODULE_3);
+ private static final Component COMPONENT_TREE = component(PROJECT, 1, MODULE_2);
+
+ private final TypeAwareVisitor spyProjectVisitor = spy(new TypeAwareVisitorAdapter(PROJECT, PRE_ORDER) {
+ });
+ private final TypeAwareVisitor spyModuleVisitor = spy(new TypeAwareVisitorAdapter(MODULE, PRE_ORDER) {
+ });
+ private final TypeAwareVisitor spyDirectoryVisitor = spy(new TypeAwareVisitorAdapter(DIRECTORY, PRE_ORDER) {
+ });
+ private final TypeAwareVisitor spyFileVisitor = spy(new TypeAwareVisitorAdapter(FILE, PRE_ORDER) {
+ });
+ private final InOrder inOrder = inOrder(spyProjectVisitor, spyModuleVisitor, spyDirectoryVisitor, spyFileVisitor);
+
+ @Test(expected = NullPointerException.class)
+ public void visit_null_Component_throws_NPE() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(null);
+ }
+
+ @Test
+ public void visit_file_with_depth_FILE_calls_visit_file() {
+ Component component = component(FILE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyFileVisitor).visitAny(component);
+ inOrder.verify(spyFileVisitor).visitFile(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_module_with_depth_FILE_calls_visit_module() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyFileVisitor).visitAny(component);
+ inOrder.verify(spyFileVisitor).visitModule(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_directory_with_depth_FILE_calls_visit_directory() {
+ Component component = component(DIRECTORY, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyFileVisitor).visitAny(component);
+ inOrder.verify(spyFileVisitor).visitDirectory(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_project_with_depth_FILE_calls_visit_project() {
+ Component component = component(PROJECT, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyFileVisitor).visitProject(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_file_with_depth_DIRECTORY_does_not_call_visit_file_nor_visitAny() {
+ Component component = component(FILE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyDirectoryVisitor, never()).visitFile(component);
+ inOrder.verify(spyDirectoryVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_directory_with_depth_DIRECTORY_calls_visit_directory() {
+ Component component = component(DIRECTORY, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyDirectoryVisitor).visitAny(component);
+ inOrder.verify(spyDirectoryVisitor).visitDirectory(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_module_with_depth_DIRECTORY_calls_visit_module() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyDirectoryVisitor).visitAny(component);
+ inOrder.verify(spyDirectoryVisitor).visitModule(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_project_with_depth_DIRECTORY_calls_visit_project() {
+ Component component = component(PROJECT, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyDirectoryVisitor).visitAny(component);
+ inOrder.verify(spyDirectoryVisitor).visitProject(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_file_with_depth_MODULE_does_not_call_visit_file_nor_visit_any() {
+ Component component = component(FILE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyModuleVisitor, never()).visitFile(component);
+ inOrder.verify(spyModuleVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_directory_with_depth_MODULE_does_not_call_visit_directory_not_visit_any() {
+ Component component = component(DIRECTORY, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyModuleVisitor, never()).visitFile(component);
+ inOrder.verify(spyModuleVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_module_with_depth_MODULE_calls_visit_module() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyModuleVisitor).visitAny(component);
+ inOrder.verify(spyModuleVisitor).visitModule(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_project_with_depth_MODULE_calls_visit_project() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyModuleVisitor).visitAny(component);
+ inOrder.verify(spyModuleVisitor).visitModule(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void visit_file_with_depth_PROJECT_does_not_call_visit_file_nor_visitAny() {
+ Component component = component(FILE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyProjectVisitor, never()).visitFile(component);
+ inOrder.verify(spyProjectVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_directory_with_depth_PROJECT_does_not_call_visit_directory_nor_visitAny() {
+ Component component = component(DIRECTORY, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyProjectVisitor, never()).visitFile(component);
+ inOrder.verify(spyProjectVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_module_with_depth_PROJECT_does_not_call_visit_module_nor_visitAny() {
+ Component component = component(MODULE, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyProjectVisitor, never()).visitFile(component);
+ inOrder.verify(spyProjectVisitor, never()).visitAny(component);
+ }
+
+ @Test
+ public void visit_project_with_depth_PROJECT_calls_visit_project_nor_visitAny() {
+ Component component = component(PROJECT, 1);
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(component);
+
+ inOrder.verify(spyProjectVisitor).visitAny(component);
+ inOrder.verify(spyProjectVisitor).visitProject(component);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void verify_visit_call_when_visit_tree_with_depth_FILE() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyFileVisitor);
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyFileVisitor).visitAny(COMPONENT_TREE);
+ inOrder.verify(spyFileVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verify(spyFileVisitor).visitAny(MODULE_2);
+ inOrder.verify(spyFileVisitor).visitModule(MODULE_2);
+ inOrder.verify(spyFileVisitor).visitAny(MODULE_3);
+ inOrder.verify(spyFileVisitor).visitModule(MODULE_3);
+ inOrder.verify(spyFileVisitor).visitAny(DIRECTORY_4);
+ inOrder.verify(spyFileVisitor).visitDirectory(DIRECTORY_4);
+ inOrder.verify(spyFileVisitor).visitAny(FILE_5);
+ inOrder.verify(spyFileVisitor).visitFile(FILE_5);
+ inOrder.verify(spyFileVisitor).visitAny(FILE_6);
+ inOrder.verify(spyFileVisitor).visitFile(FILE_6);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void verify_visit_call_when_visit_tree_with_depth_DIRECTORY() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyDirectoryVisitor);
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyDirectoryVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verify(spyDirectoryVisitor).visitModule(MODULE_2);
+ inOrder.verify(spyDirectoryVisitor).visitModule(MODULE_3);
+ inOrder.verify(spyDirectoryVisitor).visitDirectory(DIRECTORY_4);
+ inOrder.verify(spyProjectVisitor, never()).visitFile(FILE_5);
+ inOrder.verify(spyProjectVisitor, never()).visitFile(FILE_6);
+ }
+
+ @Test
+ public void verify_visit_call_when_visit_tree_with_depth_MODULE() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyModuleVisitor);
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyModuleVisitor).visitAny(COMPONENT_TREE);
+ inOrder.verify(spyModuleVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verify(spyModuleVisitor).visitAny(MODULE_2);
+ inOrder.verify(spyModuleVisitor).visitModule(MODULE_2);
+ inOrder.verify(spyModuleVisitor).visitAny(MODULE_3);
+ inOrder.verify(spyModuleVisitor).visitModule(MODULE_3);
+ inOrder.verify(spyProjectVisitor, never()).visitDirectory(DIRECTORY_4);
+ }
+
+ @Test
+ public void verify_visit_call_when_visit_tree_with_depth_PROJECT() {
+ VisitorsCrawler underTest = newVisitorsCrawler(spyProjectVisitor);
+ underTest.visit(COMPONENT_TREE);
+
+ inOrder.verify(spyProjectVisitor).visitAny(COMPONENT_TREE);
+ inOrder.verify(spyProjectVisitor).visitProject(COMPONENT_TREE);
+ inOrder.verify(spyProjectVisitor, never()).visitModule(MODULE_2);
+ inOrder.verify(spyProjectVisitor, never()).visitModule(MODULE_3);
+ }
+
+ private static Component component(final Component.Type type, final int ref, final Component... children) {
+ return DumbComponent.builder(type, ref).addChildren(children).build();
+ }
+
+ private static VisitorsCrawler newVisitorsCrawler(Visitor visitor) {
+ return new VisitorsCrawler(Arrays.asList(visitor));
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.sqale;
+
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.component.FileAttributes;
+import org.sonar.server.computation.component.Visitor;
+import org.sonar.server.computation.component.VisitorsCrawler;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepoEntry;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricImpl;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.measures.CoreMetrics.DEVELOPMENT_COST_KEY;
+import static org.sonar.api.measures.CoreMetrics.SQALE_DEBT_RATIO_KEY;
+import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY;
+import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.Component.Type.MODULE;
+import static org.sonar.server.computation.component.Component.Type.PROJECT;
+import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
+import static org.sonar.server.computation.measure.MeasureRepoEntry.toEntries;
+import static org.sonar.server.computation.sqale.SqaleRatingGrid.SqaleRating.A;
+import static org.sonar.server.computation.sqale.SqaleRatingGrid.SqaleRating.C;
+
+public class SqaleMeasuresVisitorTest {
+
+ private static final String METRIC_KEY_1 = "mKey1";
+ private static final String METRIC_KEY_2 = "mKey2";
+ private static final Metric METRIC_1 = new MetricImpl(1, METRIC_KEY_1, "metric1", Metric.MetricType.FLOAT);
+ private static final Metric METRIC_2 = new MetricImpl(2, METRIC_KEY_2, "metric2", Metric.MetricType.WORK_DUR);
+ private static final String LANGUAGE_KEY_1 = "lKey1";
+ private static final String LANGUAGE_KEY_2 = "lKey2";
+ private static final double[] RATING_GRID = new double[] {34, 50, 362, 900, 36258};
+ private static final long DEV_COST_LANGUAGE_1 = 33;
+ private static final long DEV_COST_LANGUAGE_2 = 42;
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(METRIC_1)
+ .add(METRIC_2)
+ .add(CoreMetrics.DEVELOPMENT_COST)
+ .add(CoreMetrics.TECHNICAL_DEBT)
+ .add(CoreMetrics.SQALE_DEBT_RATIO)
+ .add(CoreMetrics.SQALE_RATING);
+
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ private SqaleRatingSettings sqaleRatingSettings = mock(SqaleRatingSettings.class);
+
+ private VisitorsCrawler underTest = new VisitorsCrawler(Arrays.<Visitor>asList(new SqaleMeasuresVisitor(metricRepository, measureRepository, sqaleRatingSettings)));
+
+ @Before
+ public void setUp() {
+ // assumes SQALE rating configuration is consistent
+ when(sqaleRatingSettings.getRatingGrid()).thenReturn(RATING_GRID);
+ when(sqaleRatingSettings.getSizeMetricKey(LANGUAGE_KEY_1)).thenReturn(METRIC_KEY_1);
+ when(sqaleRatingSettings.getSizeMetricKey(LANGUAGE_KEY_2)).thenReturn(METRIC_KEY_2);
+ when(sqaleRatingSettings.getDevCost(LANGUAGE_KEY_1)).thenReturn(DEV_COST_LANGUAGE_1);
+ when(sqaleRatingSettings.getDevCost(LANGUAGE_KEY_2)).thenReturn(DEV_COST_LANGUAGE_2);
+ }
+
+ @Test
+ public void measures_created_for_project_are_all_zero_when_they_have_no_FILE_child() {
+ DumbComponent root = DumbComponent.builder(PROJECT, 1).build();
+ treeRootHolder.setRoot(root);
+
+ underTest.visit(root);
+
+ assertThat(toEntries(measureRepository.getRawMeasures(root))).containsOnly(
+ MeasureRepoEntry.entryOf(DEVELOPMENT_COST_KEY, newMeasureBuilder().create("0")),
+ MeasureRepoEntry.entryOf(SQALE_DEBT_RATIO_KEY, newMeasureBuilder().create(0d)),
+ MeasureRepoEntry.entryOf(SQALE_RATING_KEY, createSqaleRatingMeasure(A))
+ );
+ }
+
+ private Measure createSqaleRatingMeasure(SqaleRatingGrid.SqaleRating sqaleRating) {
+ return newMeasureBuilder().create(sqaleRating.getIndex(), sqaleRating.name());
+ }
+
+ @Test
+ public void verify_computation_of_measures_for_file_depending_upon_language_1() {
+ verify_computation_of_measure_for_file(33000l, DEV_COST_LANGUAGE_1, METRIC_KEY_1, LANGUAGE_KEY_1, C);
+ }
+
+ @Test
+ public void verify_computation_of_measures_for_file_depending_upon_language_2() {
+ verify_computation_of_measure_for_file(4200l, DEV_COST_LANGUAGE_2, METRIC_KEY_2, LANGUAGE_KEY_2, A);
+ }
+
+ /**
+ * Verify the computation of measures values depending upon which language is associated to the file by
+ * processing a tree of a single Component of type FILE.
+ */
+ private void verify_computation_of_measure_for_file(long debt, long languageCost, String metricKey, String languageKey,
+ SqaleRatingGrid.SqaleRating expectedRating) {
+ long measureValue = 10;
+
+ DumbComponent fileComponent = createFileComponent(languageKey, 1);
+ treeRootHolder.setRoot(fileComponent);
+ measureRepository.addRawMeasure(fileComponent.getRef(), metricKey, newMeasureBuilder().create(measureValue));
+ measureRepository.addRawMeasure(fileComponent.getRef(), TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt));
+
+ underTest.visit(fileComponent);
+
+ verifyFileMeasures(fileComponent.getRef(), measureValue, debt, languageCost, expectedRating);
+ }
+
+ @Test
+ public void verify_aggregation_of_developmentCost_and_value_of_measures_computed_from_that() {
+ DumbComponent root = DumbComponent.builder(PROJECT, 1)
+ .addChildren(
+ DumbComponent.builder(MODULE, 11)
+ .addChildren(
+ DumbComponent.builder(DIRECTORY, 111)
+ .addChildren(
+ createFileComponent(LANGUAGE_KEY_1, 1111),
+ createFileComponent(LANGUAGE_KEY_2, 1112)
+ ).build(),
+ DumbComponent.builder(DIRECTORY, 112)
+ .addChildren(
+ createFileComponent(LANGUAGE_KEY_2, 1121)
+ ).build()
+ ).build(),
+ DumbComponent.builder(MODULE, 12)
+ .addChildren(
+ DumbComponent.builder(DIRECTORY, 121)
+ .addChildren(
+ createFileComponent(LANGUAGE_KEY_1, 1211)
+ ).build(),
+ DumbComponent.builder(DIRECTORY, 122).build()
+ ).build(),
+ DumbComponent.builder(MODULE, 13).build()
+ ).build();
+
+ treeRootHolder.setRoot(root);
+
+ long measureValue1111 = 10;
+ long debt1111 = 66000l;
+ measureRepository.addRawMeasure(1111, METRIC_KEY_1, newMeasureBuilder().create(measureValue1111));
+ measureRepository.addRawMeasure(1111, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1111));
+
+ long measureValue1112 = 10;
+ long debt1112 = 4200l;
+ measureRepository.addRawMeasure(1112, METRIC_KEY_2, newMeasureBuilder().create(measureValue1112));
+ measureRepository.addRawMeasure(1112, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1112));
+
+ long debt111 = 96325l;
+ measureRepository.addRawMeasure(111, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt111));
+
+ long measureValue1121 = 30;
+ long debt1121 = 25200l;
+ measureRepository.addRawMeasure(1121, METRIC_KEY_2, newMeasureBuilder().create(measureValue1121));
+ measureRepository.addRawMeasure(1121, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1121));
+
+ long debt112 = 99633l;
+ measureRepository.addRawMeasure(112, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt112));
+
+ long measureValue1211 = 20;
+ long debt1211 = 33000l;
+ measureRepository.addRawMeasure(1211, METRIC_KEY_1, newMeasureBuilder().create(measureValue1211));
+ measureRepository.addRawMeasure(1211, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1211));
+
+ long debt121 = 7524l;
+ measureRepository.addRawMeasure(121, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt121));
+
+ long debt1 = 9999l;
+ measureRepository.addRawMeasure(1, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1));
+
+ underTest.visit(root);
+
+ // verify measures on files
+ verifyFileMeasures(1111, measureValue1111, debt1111, DEV_COST_LANGUAGE_1, C);
+ verifyFileMeasures(1112, measureValue1112, debt1112, DEV_COST_LANGUAGE_2, A);
+ verifyFileMeasures(1121, measureValue1121, debt1121, DEV_COST_LANGUAGE_2, A);
+ verifyFileMeasures(1211, measureValue1211, debt1211, DEV_COST_LANGUAGE_1, C);
+ // directory has no children => no file => 0 everywhere and A rating
+ verifyComponentMeasures(122, 0, 0, A);
+ // directory has children => dev cost is aggregated
+ long devCost111 = measureValue1111 * DEV_COST_LANGUAGE_1 + measureValue1112 * DEV_COST_LANGUAGE_2;
+ verifyComponentMeasures(111, devCost111, debt111 / (double) devCost111, C);
+ long devCost112 = measureValue1121 * DEV_COST_LANGUAGE_2;
+ verifyComponentMeasures(112, devCost112, debt112 / (double) devCost112, C);
+ long devCost121 = measureValue1211 * DEV_COST_LANGUAGE_1;
+ verifyComponentMeasures(121, devCost121, debt121 / (double) devCost121, A);
+ // just for fun, we didn't define any debt on module => they must all have rating A
+ long devCost11 = devCost111 + devCost112;
+ verifyComponentMeasures(11, devCost11, 0, A);
+ long devCost12 = devCost121;
+ verifyComponentMeasures(12, devCost12, 0, A);
+ long devCost13 = 0;
+ verifyComponentMeasures(13, devCost13, 0, A);
+ // project has aggregated dev cost of all files
+ long devCost1 = devCost11 + devCost12 + devCost13;
+ verifyComponentMeasures(1, devCost1, debt1 / (double) devCost1, A);
+ }
+
+ private DumbComponent createFileComponent(String languageKey1, int fileRef) {
+ return DumbComponent.builder(FILE, fileRef).setFileAttributes(new FileAttributes(false, languageKey1)).build();
+ }
+
+ private void verifyNoMeasure(int componentRef) {
+ assertThat(measureRepository.getRawMeasures(componentRef).isEmpty()).isTrue();
+ }
+
+ private void verifyFileMeasures(int componentRef, long measureValue, long debt, long languageCost, SqaleRatingGrid.SqaleRating expectedRating) {
+ long developmentCost = measureValue * languageCost;
+ verifyComponentMeasures(componentRef, developmentCost, debt / developmentCost, expectedRating);
+ }
+
+ private void verifyComponentMeasures(int componentRef, long expectedDevCost, double expectedDebtRatio, SqaleRatingGrid.SqaleRating expectedRating) {
+ assertThat(toEntries(measureRepository.getAddedRawMeasures(componentRef))).containsOnly(
+ MeasureRepoEntry.entryOf(DEVELOPMENT_COST_KEY, newMeasureBuilder().create(Long.toString(expectedDevCost))),
+ MeasureRepoEntry.entryOf(SQALE_DEBT_RATIO_KEY, newMeasureBuilder().create(expectedDebtRatio * 100.0)),
+ MeasureRepoEntry.entryOf(SQALE_RATING_KEY, createSqaleRatingMeasure(expectedRating))
+ );
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.step;
+
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.PathAwareVisitorAdapter;
+import org.sonar.server.computation.component.TypeAwareVisitorAdapter;
+import org.sonar.server.computation.component.Visitor;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricImpl;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.measures.CoreMetrics.NCLOC;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.Component.Type.MODULE;
+import static org.sonar.server.computation.component.Component.Type.PROJECT;
+import static org.sonar.server.computation.component.DumbComponent.builder;
+import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
+
+public class ExecuteVisitorsStepTest {
+
+ private static final String TEST_METRIC_KEY = "test";
+
+ private static final int ROOT_REF = 1;
+ private static final int MODULE_REF = 12;
+ private static final int DIRECTORY_REF = 123;
+ private static final int FILE_1_REF = 1231;
+ private static final int FILE_2_REF = 1232;
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(1, NCLOC)
+ .add(new MetricImpl(2, TEST_METRIC_KEY, "name", Metric.MetricType.INT));
+
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ @Before
+ public void setUp() throws Exception {
+ treeRootHolder.setRoot(
+ builder(PROJECT, ROOT_REF).setKey("project")
+ .addChildren(
+ builder(MODULE, MODULE_REF).setKey("module")
+ .addChildren(
+ builder(DIRECTORY, DIRECTORY_REF).setKey("directory")
+ .addChildren(
+ builder(FILE, FILE_1_REF).setKey("file1").build(),
+ builder(FILE, FILE_2_REF).setKey("file2").build()
+ ).build()
+ ).build()
+ ).build());
+ }
+
+ ComponentVisitors visitors = mock(ComponentVisitors.class);
+
+ @Test
+ public void execute_with_type_aware_visitor() throws Exception {
+ when(visitors.instances()).thenReturn(Arrays.<Visitor>asList(new TestTypeAwareVisitor()));
+ ExecuteVisitorsStep underStep = new ExecuteVisitorsStep(treeRootHolder, visitors);
+
+ measureRepository.addRawMeasure(FILE_1_REF, NCLOC_KEY, newMeasureBuilder().create(1));
+ measureRepository.addRawMeasure(FILE_2_REF, NCLOC_KEY, newMeasureBuilder().create(2));
+ measureRepository.addRawMeasure(DIRECTORY_REF, NCLOC_KEY, newMeasureBuilder().create(3));
+ measureRepository.addRawMeasure(MODULE_REF, NCLOC_KEY, newMeasureBuilder().create(3));
+ measureRepository.addRawMeasure(ROOT_REF, NCLOC_KEY, newMeasureBuilder().create(3));
+
+ underStep.execute();
+
+ assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(2);
+ assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(3);
+ assertThat(measureRepository.getAddedRawMeasure(DIRECTORY_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(4);
+ assertThat(measureRepository.getAddedRawMeasure(MODULE_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(4);
+ assertThat(measureRepository.getAddedRawMeasure(ROOT_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(4);
+ }
+
+ @Test
+ public void execute_with_path_aware_visitor() throws Exception {
+ when(visitors.instances()).thenReturn(Arrays.<Visitor>asList(new TestPathAwareVisitor()));
+ ExecuteVisitorsStep underStep = new ExecuteVisitorsStep(treeRootHolder, visitors);
+
+ measureRepository.addRawMeasure(FILE_1_REF, NCLOC_KEY, newMeasureBuilder().create(1));
+ measureRepository.addRawMeasure(FILE_2_REF, NCLOC_KEY, newMeasureBuilder().create(1));
+
+ underStep.execute();
+
+ assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(1);
+ assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(1);
+ assertThat(measureRepository.getAddedRawMeasure(DIRECTORY_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(2);
+ assertThat(measureRepository.getAddedRawMeasure(MODULE_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(2);
+ assertThat(measureRepository.getAddedRawMeasure(ROOT_REF, TEST_METRIC_KEY).get().getIntValue()).isEqualTo(2);
+ }
+
+ private class TestTypeAwareVisitor extends TypeAwareVisitorAdapter {
+
+ public TestTypeAwareVisitor() {
+ super(Component.Type.FILE, Visitor.Order.POST_ORDER);
+ }
+
+ @Override
+ public void visitAny(Component any) {
+ int ncloc = measureRepository.getRawMeasure(any, metricRepository.getByKey(NCLOC_KEY)).get().getIntValue();
+ measureRepository.add(any, metricRepository.getByKey(TEST_METRIC_KEY), newMeasureBuilder().create(ncloc + 1));
+ }
+ }
+
+ private class TestPathAwareVisitor extends PathAwareVisitorAdapter<Counter> {
+
+ public TestPathAwareVisitor() {
+ super(Component.Type.FILE, Visitor.Order.POST_ORDER, new SimpleStackElementFactory<Counter>() {
+ @Override
+ public Counter createForAny(Component component) {
+ return new Counter();
+ }
+ });
+ }
+
+ @Override
+ public void visitProject(Component project, Path<Counter> path) {
+ computeAndSaveMeasures(project, path);
+ }
+
+ @Override
+ public void visitModule(Component module, Path<Counter> path) {
+ computeAndSaveMeasures(module, path);
+ }
+
+ @Override
+ public void visitDirectory(Component directory, Path<Counter> path) {
+ computeAndSaveMeasures(directory, path);
+ }
+
+ @Override
+ public void visitFile(Component file, Path<Counter> path) {
+ int ncloc = measureRepository.getRawMeasure(file, metricRepository.getByKey(NCLOC_KEY)).get().getIntValue();
+ path.current().add(ncloc);
+ computeAndSaveMeasures(file, path);
+ }
+
+ private void computeAndSaveMeasures(Component component, Path<Counter> path) {
+ measureRepository.add(component, metricRepository.getByKey(TEST_METRIC_KEY), newMeasureBuilder().create(path.current().getValue()));
+ increaseParentValue(path);
+ }
+
+ private void increaseParentValue(Path<Counter> path) {
+ if (!path.isRoot()) {
+ path.parent().add(path.current().getValue());
+ }
+ }
+ }
+
+ public class Counter {
+ private int value = 0;
+
+ public void add(int value) {
+ this.value += value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.computation.step;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.server.computation.batch.TreeRootHolderRule;
-import org.sonar.server.computation.component.DumbComponent;
-import org.sonar.server.computation.component.FileAttributes;
-import org.sonar.server.computation.measure.Measure;
-import org.sonar.server.computation.measure.MeasureRepoEntry;
-import org.sonar.server.computation.measure.MeasureRepositoryRule;
-import org.sonar.server.computation.metric.Metric;
-import org.sonar.server.computation.metric.MetricImpl;
-import org.sonar.server.computation.metric.MetricRepositoryRule;
-import org.sonar.server.computation.sqale.SqaleRatingGrid;
-import org.sonar.server.computation.sqale.SqaleRatingSettings;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.measures.CoreMetrics.DEVELOPMENT_COST_KEY;
-import static org.sonar.api.measures.CoreMetrics.SQALE_DEBT_RATIO_KEY;
-import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY;
-import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
-import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
-import static org.sonar.server.computation.component.Component.Type.FILE;
-import static org.sonar.server.computation.component.Component.Type.MODULE;
-import static org.sonar.server.computation.component.Component.Type.PROJECT;
-import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
-import static org.sonar.server.computation.measure.MeasureRepoEntry.toEntries;
-import static org.sonar.server.computation.sqale.SqaleRatingGrid.SqaleRating.A;
-import static org.sonar.server.computation.sqale.SqaleRatingGrid.SqaleRating.C;
-
-public class SqaleMeasuresStepTest {
-
- private static final String METRIC_KEY_1 = "mKey1";
- private static final String METRIC_KEY_2 = "mKey2";
- private static final Metric METRIC_1 = new MetricImpl(1, METRIC_KEY_1, "metric1", Metric.MetricType.FLOAT);
- private static final Metric METRIC_2 = new MetricImpl(2, METRIC_KEY_2, "metric2", Metric.MetricType.WORK_DUR);
- private static final String LANGUAGE_KEY_1 = "lKey1";
- private static final String LANGUAGE_KEY_2 = "lKey2";
- private static final double[] RATING_GRID = new double[]{34, 50, 362, 900, 36258};
- private static final long DEV_COST_LANGUAGE_1 = 33;
- private static final long DEV_COST_LANGUAGE_2 = 42;
-
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
- @Rule
- public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(METRIC_1).add(METRIC_2);
-
- @Rule
- public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
-
- private SqaleRatingSettings sqaleRatingSettings = mock(SqaleRatingSettings.class);
-
- private SqaleMeasuresStep underTest = new SqaleMeasuresStep(treeRootHolder, metricRepository, measureRepository, sqaleRatingSettings);
-
- @Before
- public void setUp() {
- // assumes SQALE rating configuration is consistent
- when(sqaleRatingSettings.getRatingGrid()).thenReturn(RATING_GRID);
- when(sqaleRatingSettings.getSizeMetricKey(LANGUAGE_KEY_1)).thenReturn(METRIC_KEY_1);
- when(sqaleRatingSettings.getSizeMetricKey(LANGUAGE_KEY_2)).thenReturn(METRIC_KEY_2);
- when(sqaleRatingSettings.getDevCost(LANGUAGE_KEY_1)).thenReturn(DEV_COST_LANGUAGE_1);
- when(sqaleRatingSettings.getDevCost(LANGUAGE_KEY_2)).thenReturn(DEV_COST_LANGUAGE_2);
-
- // this measures are always retrieved by the step
- metricRepository.add(CoreMetrics.DEVELOPMENT_COST);
- metricRepository.add(CoreMetrics.TECHNICAL_DEBT);
- metricRepository.add(CoreMetrics.SQALE_DEBT_RATIO);
- metricRepository.add(CoreMetrics.SQALE_RATING);
- }
-
- @Test
- public void measures_created_for_project_are_all_zero_when_they_have_no_FILE_child() {
- DumbComponent root = DumbComponent.builder(PROJECT, 1).build();
- treeRootHolder.setRoot(root);
-
- underTest.execute();
-
- assertThat(toEntries(measureRepository.getRawMeasures(root))).containsOnly(
- MeasureRepoEntry.entryOf(DEVELOPMENT_COST_KEY, newMeasureBuilder().create("0")),
- MeasureRepoEntry.entryOf(SQALE_DEBT_RATIO_KEY, newMeasureBuilder().create(0d)),
- MeasureRepoEntry.entryOf(SQALE_RATING_KEY, createSqaleRatingMeasure(A))
- );
- }
-
- private Measure createSqaleRatingMeasure(SqaleRatingGrid.SqaleRating sqaleRating) {
- return newMeasureBuilder().create(sqaleRating.getIndex(), sqaleRating.name());
- }
-
- @Test
- public void verify_computation_of_measures_for_file_depending_upon_language_1() {
- verify_computation_of_measure_for_file(33000l, DEV_COST_LANGUAGE_1, METRIC_KEY_1, LANGUAGE_KEY_1, C);
- }
-
- @Test
- public void verify_computation_of_measures_for_file_depending_upon_language_2() {
- verify_computation_of_measure_for_file(4200l, DEV_COST_LANGUAGE_2, METRIC_KEY_2, LANGUAGE_KEY_2, A);
- }
-
- /**
- * Verify the computation of measures values depending upon which language is associated to the file by
- * processing a tree of a single Component of type FILE.
- */
- private void verify_computation_of_measure_for_file(long debt, long languageCost, String metricKey, String languageKey,
- SqaleRatingGrid.SqaleRating expectedRating) {
- long measureValue = 10;
-
- DumbComponent fileComponent = createFileComponent(languageKey, 1);
- treeRootHolder.setRoot(fileComponent);
- measureRepository.addRawMeasure(fileComponent.getRef(), metricKey, newMeasureBuilder().create(measureValue));
- measureRepository.addRawMeasure(fileComponent.getRef(), TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt));
-
- underTest.execute();
-
- verifyFileMeasures(fileComponent.getRef(), measureValue, debt, languageCost, expectedRating);
- }
-
- @Test
- public void verify_aggregation_of_developmentCost_and_value_of_measures_computed_from_that() {
- DumbComponent root = DumbComponent.builder(PROJECT, 1)
- .addChildren(
- DumbComponent.builder(MODULE, 11)
- .addChildren(
- DumbComponent.builder(DIRECTORY, 111)
- .addChildren(
- createFileComponent(LANGUAGE_KEY_1, 1111),
- createFileComponent(LANGUAGE_KEY_2, 1112)
- ).build(),
- DumbComponent.builder(DIRECTORY, 112)
- .addChildren(
- createFileComponent(LANGUAGE_KEY_2, 1121)
- ).build()
- ).build(),
- DumbComponent.builder(MODULE, 12)
- .addChildren(
- DumbComponent.builder(DIRECTORY, 121)
- .addChildren(
- createFileComponent(LANGUAGE_KEY_1, 1211)
- ).build(),
- DumbComponent.builder(DIRECTORY, 122).build()
- ).build(),
- DumbComponent.builder(MODULE, 13).build()
- ).build();
-
- treeRootHolder.setRoot(root);
-
- long measureValue1111 = 10;
- long debt1111 = 66000l;
- measureRepository.addRawMeasure(1111, METRIC_KEY_1, newMeasureBuilder().create(measureValue1111));
- measureRepository.addRawMeasure(1111, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1111));
-
- long measureValue1112 = 10;
- long debt1112 = 4200l;
- measureRepository.addRawMeasure(1112, METRIC_KEY_2, newMeasureBuilder().create(measureValue1112));
- measureRepository.addRawMeasure(1112, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1112));
-
- long debt111 = 96325l;
- measureRepository.addRawMeasure(111, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt111));
-
- long measureValue1121 = 30;
- long debt1121 = 25200l;
- measureRepository.addRawMeasure(1121, METRIC_KEY_2, newMeasureBuilder().create(measureValue1121));
- measureRepository.addRawMeasure(1121, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1121));
-
- long debt112 = 99633l;
- measureRepository.addRawMeasure(112, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt112));
-
- long measureValue1211 = 20;
- long debt1211 = 33000l;
- measureRepository.addRawMeasure(1211, METRIC_KEY_1, newMeasureBuilder().create(measureValue1211));
- measureRepository.addRawMeasure(1211, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1211));
-
- long debt121 = 7524l;
- measureRepository.addRawMeasure(121, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt121));
-
- long debt1 = 9999l;
- measureRepository.addRawMeasure(1, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1));
-
- underTest.execute();
-
- // verify measures on files
- verifyFileMeasures(1111, measureValue1111, debt1111, DEV_COST_LANGUAGE_1, C);
- verifyFileMeasures(1112, measureValue1112, debt1112, DEV_COST_LANGUAGE_2, A);
- verifyFileMeasures(1121, measureValue1121, debt1121, DEV_COST_LANGUAGE_2, A);
- verifyFileMeasures(1211, measureValue1211, debt1211, DEV_COST_LANGUAGE_1, C);
- // directory has no children => no file => 0 everywhere and A rating
- verifyComponentMeasures(122, 0, 0, A);
- // directory has children => dev cost is aggregated
- long devCost111 = measureValue1111 * DEV_COST_LANGUAGE_1 + measureValue1112 * DEV_COST_LANGUAGE_2;
- verifyComponentMeasures(111, devCost111, debt111 / (double) devCost111, C);
- long devCost112 = measureValue1121 * DEV_COST_LANGUAGE_2;
- verifyComponentMeasures(112, devCost112, debt112 / (double) devCost112, C);
- long devCost121 = measureValue1211 * DEV_COST_LANGUAGE_1;
- verifyComponentMeasures(121, devCost121, debt121 / (double) devCost121, A);
- // just for fun, we didn't define any debt on module => they must all have rating A
- long devCost11 = devCost111 + devCost112;
- verifyComponentMeasures(11, devCost11, 0, A);
- long devCost12 = devCost121;
- verifyComponentMeasures(12, devCost12, 0, A);
- long devCost13 = 0;
- verifyComponentMeasures(13, devCost13, 0, A);
- // project has aggregated dev cost of all files
- long devCost1 = devCost11 + devCost12 + devCost13;
- verifyComponentMeasures(1, devCost1, debt1 / (double) devCost1, A);
- }
-
- private DumbComponent createFileComponent(String languageKey1, int fileRef) {
- return DumbComponent.builder(FILE, fileRef).setFileAttributes(new FileAttributes(false, languageKey1)).build();
- }
-
- private void verifyNoMeasure(int componentRef) {
- assertThat(measureRepository.getRawMeasures(componentRef).isEmpty()).isTrue();
- }
-
- private void verifyFileMeasures(int componentRef, long measureValue, long debt, long languageCost, SqaleRatingGrid.SqaleRating expectedRating) {
- long developmentCost = measureValue * languageCost;
- verifyComponentMeasures(componentRef, developmentCost, debt / developmentCost, expectedRating);
- }
-
- private void verifyComponentMeasures(int componentRef, long expectedDevCost, double expectedDebtRatio, SqaleRatingGrid.SqaleRating expectedRating) {
- assertThat(toEntries(measureRepository.getAddedRawMeasures(componentRef))).containsOnly(
- MeasureRepoEntry.entryOf(DEVELOPMENT_COST_KEY, newMeasureBuilder().create(Long.toString(expectedDevCost))),
- MeasureRepoEntry.entryOf(SQALE_DEBT_RATIO_KEY, newMeasureBuilder().create(expectedDebtRatio * 100.0)),
- MeasureRepoEntry.entryOf(SQALE_RATING_KEY, createSqaleRatingMeasure(expectedRating))
- );
- }
-
-}