From a2942f3c1c6b43be145f6342eb87bb6e745cc15b Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 6 Aug 2015 18:16:52 +0200 Subject: [PATCH] Create step to execute visitors and replace SqaleMeasureStep to SqaleMeasureVisitor --- .../component/VisitorsCrawler.java | 143 +++++++ .../container/ComputeEngineContainer.java | 4 + .../container/ComputeEngineContainerImpl.java | 11 + .../sqale/SqaleMeasuresVisitor.java | 173 ++++++++ .../computation/step/ComponentVisitors.java | 66 +++ .../computation/step/ComputationSteps.java | 8 +- .../computation/step/ExecuteVisitorsStep.java | 45 ++ .../computation/step/SqaleMeasuresStep.java | 192 --------- .../computation/component/DumbComponent.java | 9 + .../component/VisitorsCrawlerTest.java | 148 +++++++ ...sitorsCrawlerWithPathAwareVisitorTest.java | 392 ++++++++++++++++++ ...wlerWithPostOrderTypeAwareVisitorTest.java | 303 ++++++++++++++ ...awlerWithPreOrderTypeAwareVisitorTest.java | 296 +++++++++++++ .../SqaleMeasuresVisitorTest.java} | 40 +- .../step/ExecuteVisitorsStepTest.java | 194 +++++++++ 15 files changed, 1809 insertions(+), 215 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/component/VisitorsCrawler.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/sqale/SqaleMeasuresVisitor.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/step/ComponentVisitors.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/step/ExecuteVisitorsStep.java delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/step/SqaleMeasuresStep.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPathAwareVisitorTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPostOrderTypeAwareVisitorTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPreOrderTypeAwareVisitorTest.java rename server/sonar-server/src/test/java/org/sonar/server/computation/{step/SqaleMeasuresStepTest.java => sqale/SqaleMeasuresVisitorTest.java} (92%) create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/step/ExecuteVisitorsStepTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/VisitorsCrawler.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/VisitorsCrawler.java new file mode 100644 index 00000000000..134ff9f8b48 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/VisitorsCrawler.java @@ -0,0 +1,143 @@ +/* + * 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 preOrderVisitorWrappers; + private final List postOrderVisitorWrappers; + + public VisitorsCrawler(Iterable visitors) { + List 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 preOrderVisitorWrappersToExecute = from(preOrderVisitorWrappers).filter(new MatchVisitorMaxDepth(component)).toList(); + List 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 { + 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 { + 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 { + INSTANCE; + + @Override + public boolean apply(@Nonnull VisitorWrapper visitorWrapper) { + return visitorWrapper.getOrder() == Visitor.Order.PRE_ORDER; + } + } + + private enum MatchPostOrderVisitor implements Predicate { + INSTANCE; + + @Override + public boolean apply(@Nonnull VisitorWrapper visitorWrapper) { + return visitorWrapper.getOrder() == Visitor.Order.POST_ORDER; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java index 6a0aa739af6..742d898059d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java @@ -21,6 +21,7 @@ package org.sonar.server.computation.container; 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; /** @@ -45,4 +46,7 @@ public interface ComputeEngineContainer { */ T getStep(Class type); + T getComponentVisitor(Class type); + + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java index 0a0b20c2b20..add42ca7f71 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java @@ -46,6 +46,7 @@ import org.sonar.server.computation.batch.BatchReportReaderImpl; 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; @@ -86,6 +87,7 @@ import org.sonar.server.computation.qualitygate.QualityGateHolderImpl; 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; @@ -101,12 +103,14 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co 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(); @@ -120,8 +124,10 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co private void populateContainer(ReportQueue.Item item) { add(item); add(steps); + add(visitors); addSingletons(componentClasses()); addSingletons(steps.orderedStepClasses()); + addSingletons(visitors.orderedClasses()); populateFromModules(); } @@ -267,6 +273,11 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co return getComponentByType(type); } + @Override + public T getComponentVisitor(Class type) { + return getComponentByType(type); + } + @Override public String toString() { return "ComputeEngineContainerImpl"; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/sqale/SqaleMeasuresVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/sqale/SqaleMeasuresVisitor.java new file mode 100644 index 00000000000..8a9170a2476 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/sqale/SqaleMeasuresVisitor.java @@ -0,0 +1,173 @@ +/* + * 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 { + + 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() { + @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 path) { + computeAndSaveMeasures(project, path); + } + + @Override + public void visitDirectory(Component directory, Path path) { + computeAndSaveMeasures(directory, path); + } + + @Override + public void visitModule(Component module, Path path) { + computeAndSaveMeasures(module, path); + } + + @Override + public void visitFile(Component file, Path path) { + if (!file.getFileAttributes().isUnitTest()) { + long developmentCosts = computeDevelopmentCost(file); + path.current().add(developmentCosts); + computeAndSaveMeasures(file, path); + } + } + + private void computeAndSaveMeasures(Component component, Path 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 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) { + 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; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComponentVisitors.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComponentVisitors.java new file mode 100644 index 00000000000..35673b68066 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComponentVisitors.java @@ -0,0 +1,66 @@ +/* + * 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> ORDERED_VISITOR_CLASSES = ImmutableList.of( + SqaleMeasuresVisitor.class + ); + + /** + * List of all {@link Visitor}, ordered by execution sequence. + */ + public List> orderedClasses() { + return ORDERED_VISITOR_CLASSES; + } + + private final ComputeEngineContainer computeEngineContainer; + + public ComponentVisitors(ComputeEngineContainer computeEngineContainer) { + this.computeEngineContainer = computeEngineContainer; + } + + public Iterable instances() { + return Iterables.transform(orderedClasses(), new Function, Visitor>() { + @Override + public Visitor apply(@Nonnull Class input) { + Visitor visitor = computeEngineContainer.getComponentVisitor(input); + Preconditions.checkState(visitor != null, String.format("Visitor not found: %s", input)); + return visitor; + } + }); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java index ed0d26b0286..982942e0517 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java @@ -58,14 +58,14 @@ public class ComputationSteps { 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, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ExecuteVisitorsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ExecuteVisitorsStep.java new file mode 100644 index 00000000000..7d93a3e7829 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ExecuteVisitorsStep.java @@ -0,0 +1,45 @@ +/* + * 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 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"; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/SqaleMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/SqaleMeasuresStep.java deleted file mode 100644 index 441d09746e6..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/SqaleMeasuresStep.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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 { - 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() { - @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 path) { - computeAndSaveMeasures(project, path); - } - - @Override - public void visitDirectory(Component directory, Path path) { - computeAndSaveMeasures(directory, path); - } - - @Override - public void visitModule(Component module, Path path) { - computeAndSaveMeasures(module, path); - } - - @Override - public void visitFile(Component file, Path path) { - if (!file.getFileAttributes().isUnitTest()) { - long developmentCosts = computeDevelopmentCost(file); - path.current().add(developmentCosts); - computeAndSaveMeasures(file, path); - } - } - - private void computeAndSaveMeasures(Component component, Path 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 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) { - 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; - } - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/DumbComponent.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/DumbComponent.java index 46a1e09ac61..ef9f04ec00a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/DumbComponent.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/DumbComponent.java @@ -123,6 +123,15 @@ public class DumbComponent implements Component { 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); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerTest.java new file mode 100644 index 00000000000..a381ed21007 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerTest.java @@ -0,0 +1,148 @@ +/* + * 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.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 { + + public TestPathAwareVisitor(Component.Type maxDepth, Visitor.Order order) { + super(maxDepth, order, new SimpleStackElementFactory() { + @Override + public Integer createForAny(Component component) { + return component.getRef(); + } + }); + } + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPathAwareVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPathAwareVisitorTest.java new file mode 100644 index 00000000000..fb7741eb68d --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPathAwareVisitorTest.java @@ -0,0 +1,392 @@ +/* + * 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 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 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 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 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 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 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 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 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 expected, Iterator actual) { + while (expected.hasNext()) { + assertThat(actual.next()).isEqualTo(expected.next()); + } + } + + private static CallRecord newCallRecord(String method, int currentRef, @Nullable Integer parentRef, List 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 { + private final List callsRecords = new ArrayList<>(); + + public TestPathAwareVisitor(Component.Type maxDepth, Visitor.Order order) { + super(maxDepth, order, new SimpleStackElementFactory() { + @Override + public Integer createForAny(Component component) { + return component.getRef(); + } + }); + } + + @Override + public void visitProject(Component project, Path path) { + callsRecords.add(newCallRecord(project, path, "visitProject")); + } + + @Override + public void visitModule(Component module, Path path) { + callsRecords.add(newCallRecord(module, path, "visitModule")); + } + + @Override + public void visitDirectory(Component directory, Path path) { + callsRecords.add(newCallRecord(directory, path, "visitDirectory")); + } + + @Override + public void visitFile(Component file, Path path) { + callsRecords.add(newCallRecord(file, path, "visitFile")); + } + + @Override + public void visitUnknown(Component unknownComponent, Path path) { + callsRecords.add(newCallRecord(unknownComponent, path, "visitUnknown")); + } + + @Override + public void visitAny(Component component, Path path) { + callsRecords.add(newCallRecord(component, path, "visitAny")); + } + + private static CallRecord newCallRecord(Component project, Path path, String method) { + return new CallRecord(method, project.getRef(), path.current(), getParent(path), path.root(), + toValueList(path)); + } + + private static List toValueList(Path path) { + return from(path.getCurrentPath()).transform(new Function, Integer>() { + @Nonnull + @Override + public Integer apply(@Nonnull PathElement input) { + return input.getElement(); + } + }).toList(); + } + + private static Integer getParent(Path 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 path; + + private CallRecord(String method, int ref, int current, @Nullable Integer parent, int root, List 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 + + '}'; + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPostOrderTypeAwareVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPostOrderTypeAwareVisitorTest.java new file mode 100644 index 00000000000..ceddd9b92e6 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPostOrderTypeAwareVisitorTest.java @@ -0,0 +1,303 @@ +/* + * 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)); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPreOrderTypeAwareVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPreOrderTypeAwareVisitorTest.java new file mode 100644 index 00000000000..5ceabfcdd5e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/VisitorsCrawlerWithPreOrderTypeAwareVisitorTest.java @@ -0,0 +1,296 @@ +/* + * 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)); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SqaleMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/sqale/SqaleMeasuresVisitorTest.java similarity index 92% rename from server/sonar-server/src/test/java/org/sonar/server/computation/step/SqaleMeasuresStepTest.java rename to server/sonar-server/src/test/java/org/sonar/server/computation/sqale/SqaleMeasuresVisitorTest.java index 4031ad62dec..e0f846fc5ab 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SqaleMeasuresStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/sqale/SqaleMeasuresVisitorTest.java @@ -17,8 +17,10 @@ * 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; +package org.sonar.server.computation.sqale; + +import java.util.Arrays; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -26,14 +28,14 @@ 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 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; @@ -51,7 +53,7 @@ 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 { +public class SqaleMeasuresVisitorTest { private static final String METRIC_KEY_1 = "mKey1"; private static final String METRIC_KEY_2 = "mKey2"; @@ -59,7 +61,7 @@ public class SqaleMeasuresStepTest { 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 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; @@ -67,14 +69,20 @@ public class SqaleMeasuresStepTest { public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); @Rule - public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(METRIC_1).add(METRIC_2); + 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 SqaleMeasuresStep underTest = new SqaleMeasuresStep(treeRootHolder, metricRepository, measureRepository, sqaleRatingSettings); + private VisitorsCrawler underTest = new VisitorsCrawler(Arrays.asList(new SqaleMeasuresVisitor(metricRepository, measureRepository, sqaleRatingSettings))); @Before public void setUp() { @@ -84,12 +92,6 @@ public class SqaleMeasuresStepTest { 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 @@ -97,13 +99,13 @@ public class SqaleMeasuresStepTest { DumbComponent root = DumbComponent.builder(PROJECT, 1).build(); treeRootHolder.setRoot(root); - underTest.execute(); + 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) { @@ -125,7 +127,7 @@ public class SqaleMeasuresStepTest { * 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) { + SqaleRatingGrid.SqaleRating expectedRating) { long measureValue = 10; DumbComponent fileComponent = createFileComponent(languageKey, 1); @@ -133,7 +135,7 @@ public class SqaleMeasuresStepTest { measureRepository.addRawMeasure(fileComponent.getRef(), metricKey, newMeasureBuilder().create(measureValue)); measureRepository.addRawMeasure(fileComponent.getRef(), TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt)); - underTest.execute(); + underTest.visit(fileComponent); verifyFileMeasures(fileComponent.getRef(), measureValue, debt, languageCost, expectedRating); } @@ -199,7 +201,7 @@ public class SqaleMeasuresStepTest { long debt1 = 9999l; measureRepository.addRawMeasure(1, TECHNICAL_DEBT_KEY, newMeasureBuilder().create(debt1)); - underTest.execute(); + underTest.visit(root); // verify measures on files verifyFileMeasures(1111, measureValue1111, debt1111, DEV_COST_LANGUAGE_1, C); @@ -245,7 +247,7 @@ public class SqaleMeasuresStepTest { 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)) - ); + ); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ExecuteVisitorsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ExecuteVisitorsStepTest.java new file mode 100644 index 00000000000..137fdd8a75f --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ExecuteVisitorsStepTest.java @@ -0,0 +1,194 @@ +/* + * 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.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.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 { + + public TestPathAwareVisitor() { + super(Component.Type.FILE, Visitor.Order.POST_ORDER, new SimpleStackElementFactory() { + @Override + public Counter createForAny(Component component) { + return new Counter(); + } + }); + } + + @Override + public void visitProject(Component project, Path path) { + computeAndSaveMeasures(project, path); + } + + @Override + public void visitModule(Component module, Path path) { + computeAndSaveMeasures(module, path); + } + + @Override + public void visitDirectory(Component directory, Path path) { + computeAndSaveMeasures(directory, path); + } + + @Override + public void visitFile(Component file, Path 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 path) { + measureRepository.add(component, metricRepository.getByKey(TEST_METRIC_KEY), newMeasureBuilder().create(path.current().getValue())); + increaseParentValue(path); + } + + private void increaseParentValue(Path 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; + } + } +} -- 2.39.5