From 71f5c0a0a6071760312a7e69a83ef5e49d36b22e Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Mon, 9 Nov 2015 10:49:46 +0100 Subject: [PATCH] SONAR-6990 merge ReportTreeRootHolder into TreeRootHolder + add methods to get Component by key from TreeRootHolder --- .../component/MutableTreeRootHolder.java | 1 + .../component/ReportTreeRootHolder.java | 31 ---- .../component/ReportTreeRootHolderImpl.java | 56 ------ .../computation/component/TreeRootHolder.java | 25 +++ .../component/TreeRootHolderImpl.java | 84 ++++++++- ...ReportComputeEngineContainerPopulator.java | 4 +- .../issue/TrackerRawInputFactory.java | 13 +- .../step/LoadDuplicationsFromReportStep.java | 6 +- .../step/PersistDuplicationsStep.java | 24 +-- .../computation/step/PersistTestsStep.java | 8 +- .../computation/batch/TreeRootHolderRule.java | 71 ++------ .../component/MutableTreeRootHolderRule.java | 3 +- .../ReportTreeRootHolderImplTest.java | 80 --------- .../component/TreeRootHolderImplTest.java | 169 +++++++++++++++++- .../issue/ComponentIssuesRepositoryRule.java | 12 +- .../LoadDuplicationsFromReportStepTest.java | 2 +- 16 files changed, 321 insertions(+), 268 deletions(-) delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/component/ReportTreeRootHolder.java delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/component/ReportTreeRootHolderImpl.java delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/component/ReportTreeRootHolderImplTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/MutableTreeRootHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/MutableTreeRootHolder.java index cfb79c72184..4fb9331412e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/MutableTreeRootHolder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/MutableTreeRootHolder.java @@ -28,6 +28,7 @@ public interface MutableTreeRootHolder extends TreeRootHolder { * @param newRoot a {@link Component}, can not be {@code null} * * @throws NullPointerException if {@code newRoot} is {@code null} + * @throws IllegalStateException if root {@link Component} has already been set */ MutableTreeRootHolder setRoot(Component newRoot); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ReportTreeRootHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ReportTreeRootHolder.java deleted file mode 100644 index d33c264d555..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ReportTreeRootHolder.java +++ /dev/null @@ -1,31 +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.component; - -public interface ReportTreeRootHolder extends TreeRootHolder { - - /** - * Return a component by its batch reference - * - * @throws IllegalStateException if the holder is empty (ie. there is no root yet) - * @throws IllegalArgumentException if there's no component for the reference - */ - Component getComponentByRef(int ref); -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ReportTreeRootHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ReportTreeRootHolderImpl.java deleted file mode 100644 index 5a6e9449bee..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ReportTreeRootHolderImpl.java +++ /dev/null @@ -1,56 +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.component; - -import java.util.HashMap; -import java.util.Map; - -import static com.google.common.base.Preconditions.checkArgument; -import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; - -public class ReportTreeRootHolderImpl extends TreeRootHolderImpl implements ReportTreeRootHolder { - private Map componentsByRef = new HashMap<>(); - - @Override - public MutableTreeRootHolder setRoot(Component newRoot) { - super.setRoot(newRoot); - feedComponentsByRef(newRoot); - return this; - } - - private void feedComponentsByRef(Component newRoot) { - new DepthTraversalTypeAwareCrawler( - new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, POST_ORDER) { - @Override - public void visitAny(Component component) { - componentsByRef.put(component.getReportAttributes().getRef(), component); - } - }).visit(newRoot); - } - - @Override - public Component getComponentByRef(int ref) { - // makes sure the root is set, hence componentsByRef is populated - getRoot(); - Component component = componentsByRef.get(ref); - checkArgument(component != null, String.format("Component '%s' hasn't been found", ref)); - return component; - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolder.java index 4b990fc9845..59b3c327883 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolder.java @@ -26,4 +26,29 @@ public interface TreeRootHolder { * @throws IllegalStateException if the holder is empty (ie. there is no root yet) */ Component getRoot(); + + /** + * Return a component by its batch reference + * + * @throws IllegalStateException if the holder is empty (ie. there is no root yet) + * @throws IllegalArgumentException if there's no {@link Component} with the specified reference + */ + Component getComponentByRef(int ref); + + /** + * Retrieves the component with the specified key in the {@link Component} tree in the holder. + * + * @throws NullPointerException if {@code key} is {@code null} + * @throws IllegalStateException if the holder is empty (ie. there is not root yet) + * @throws IllegalArgumentException if there is no {@link Component} with the specified key in the tree + */ + Component getComponentByKey(String key); + + /** + * Checks whether the {@link Component} with the specified key exists in the tree. + * + * @throws NullPointerException if {@code key} is {@code null} + * @throws IllegalStateException if the holder is empty (ie. there is not root yet) + */ + boolean hasComponentWithKey(String key); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java index 21588209a1e..1e410a5b80c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java @@ -19,26 +19,104 @@ */ package org.sonar.server.computation.component; +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import javax.annotation.CheckForNull; + +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; /** * Holds the reference to the root of the {@link Component} tree for the current CE run. */ public class TreeRootHolderImpl implements MutableTreeRootHolder { + @CheckForNull + private Map componentsByRef; + @CheckForNull + private Map componentsByKey; private Component root; @Override - public MutableTreeRootHolder setRoot(Component newRoot) { - this.root = requireNonNull(newRoot); + public MutableTreeRootHolder setRoot(Component root) { + checkState(this.root == null, "root can not be set twice in holder"); + this.root = requireNonNull(root, "root can not be null"); return this; } @Override public Component getRoot() { - checkState(this.root != null, "Root has not been created yet"); + checkInitialized(); return this.root; } + @Override + public Component getComponentByRef(int ref) { + checkInitialized(); + ensureComponentByRefIsPopulated(); + Component component = componentsByRef.get(ref); + checkArgument(component != null, "Component with ref '%s' can't be found", ref); + return component; + } + + private void ensureComponentByRefIsPopulated() { + if (componentsByRef != null) { + return; + } + + final ImmutableMap.Builder builder = ImmutableMap.builder(); + new DepthTraversalTypeAwareCrawler( + new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, POST_ORDER) { + @Override + public void visitAny(Component component) { + builder.put(component.getReportAttributes().getRef(), component); + } + }).visit(this.root); + this.componentsByRef = builder.build(); + } + + @Override + public Component getComponentByKey(String key) { + checkKeyArgument(key); + checkInitialized(); + ensureComponentByKeyIsPopulated(); + Component component = componentsByKey.get(key); + checkArgument(component != null, "Component with key '%s' can't be found", key); + return component; + } + + @Override + public boolean hasComponentWithKey(String key) { + checkKeyArgument(key); + checkInitialized(); + ensureComponentByKeyIsPopulated(); + + return componentsByKey.containsKey(key); + } + + private void checkInitialized() { + checkState(this.root != null, "Holder has not been initialized yet"); + } + + private static void checkKeyArgument(String key) { + requireNonNull(key, "key can not be null"); + } + + private void ensureComponentByKeyIsPopulated() { + if (componentsByKey != null) { + return; + } + + final ImmutableMap.Builder builder = ImmutableMap.builder(); + new DepthTraversalTypeAwareCrawler( + new TypeAwareVisitorAdapter(CrawlerDepthLimit.LEAVES, POST_ORDER) { + @Override + public void visitAny(Component component) { + builder.put(component.getKey(), component); + } + }).visit(this.root); + this.componentsByKey = builder.build(); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java index f74ac8ba1ec..b625ee51faf 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java @@ -27,8 +27,8 @@ import org.sonar.server.computation.analysis.AnalysisMetadataHolderImpl; import org.sonar.server.computation.batch.BatchReportDirectoryHolderImpl; import org.sonar.server.computation.batch.BatchReportReaderImpl; import org.sonar.server.computation.component.DbIdsRepositoryImpl; -import org.sonar.server.computation.component.ReportTreeRootHolderImpl; import org.sonar.server.computation.component.SettingsRepositoryImpl; +import org.sonar.server.computation.component.TreeRootHolderImpl; import org.sonar.server.computation.debt.DebtModelHolderImpl; import org.sonar.server.computation.duplication.DuplicationRepositoryImpl; import org.sonar.server.computation.event.EventRepositoryImpl; @@ -117,7 +117,7 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop // holders AnalysisMetadataHolderImpl.class, BatchReportDirectoryHolderImpl.class, - ReportTreeRootHolderImpl.class, + TreeRootHolderImpl.class, PeriodsHolderImpl.class, QualityGateHolderImpl.class, DebtModelHolderImpl.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java index fca45414d4c..053a71d81d8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java @@ -19,6 +19,9 @@ */ package org.sonar.server.computation.issue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.sonar.api.issue.Issue; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.log.Loggers; @@ -32,25 +35,21 @@ import org.sonar.db.protobuf.DbCommons; import org.sonar.db.protobuf.DbIssues; import org.sonar.server.computation.batch.BatchReportReader; import org.sonar.server.computation.component.Component; -import org.sonar.server.computation.component.ReportTreeRootHolder; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.issue.commonrule.CommonRuleEngine; import org.sonar.server.computation.source.SourceLinesRepository; import org.sonar.server.rule.CommonRuleKeys; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import static com.google.common.collect.Lists.newArrayList; public class TrackerRawInputFactory { - private final ReportTreeRootHolder treeRootHolder; + private final TreeRootHolder treeRootHolder; private final BatchReportReader reportReader; private final SourceLinesRepository sourceLinesRepository; private final CommonRuleEngine commonRuleEngine; - public TrackerRawInputFactory(ReportTreeRootHolder treeRootHolder, BatchReportReader reportReader, + public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader, SourceLinesRepository sourceLinesRepository, CommonRuleEngine commonRuleEngine) { this.treeRootHolder = treeRootHolder; this.reportReader = reportReader; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStep.java index 8fea5413b15..3006266bfc2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStep.java @@ -25,7 +25,7 @@ import org.sonar.server.computation.batch.BatchReportReader; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.CrawlerDepthLimit; import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler; -import org.sonar.server.computation.component.ReportTreeRootHolder; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.component.TypeAwareVisitorAdapter; import org.sonar.server.computation.duplication.DuplicationRepository; import org.sonar.server.computation.duplication.TextBlock; @@ -36,11 +36,11 @@ import static org.sonar.server.computation.component.ComponentVisitor.Order.POST * Loads duplication information from the report and loads them into the {@link DuplicationRepository}. */ public class LoadDuplicationsFromReportStep implements ComputationStep { - private final ReportTreeRootHolder treeRootHolder; + private final TreeRootHolder treeRootHolder; private final BatchReportReader batchReportReader; private final DuplicationRepository duplicationRepository; - public LoadDuplicationsFromReportStep(ReportTreeRootHolder treeRootHolder, BatchReportReader batchReportReader, DuplicationRepository duplicationRepository) { + public LoadDuplicationsFromReportStep(TreeRootHolder treeRootHolder, BatchReportReader batchReportReader, DuplicationRepository duplicationRepository) { this.treeRootHolder = treeRootHolder; this.batchReportReader = batchReportReader; this.duplicationRepository = duplicationRepository; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java index 74671bcbae4..901df0b3347 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java @@ -32,7 +32,7 @@ import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.CrawlerDepthLimit; import org.sonar.server.computation.component.DbIdsRepository; import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler; -import org.sonar.server.computation.component.ReportTreeRootHolder; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.component.TypeAwareVisitorAdapter; import org.sonar.server.computation.duplication.CrossProjectDuplicate; import org.sonar.server.computation.duplication.Duplicate; @@ -51,11 +51,11 @@ public class PersistDuplicationsStep implements ComputationStep { private final DbClient dbClient; private final DbIdsRepository dbIdsRepository; - private final ReportTreeRootHolder treeRootHolder; + private final TreeRootHolder treeRootHolder; private final DuplicationRepository duplicationRepository; - public PersistDuplicationsStep(DbClient dbClient, DbIdsRepository dbIdsRepository, ReportTreeRootHolder treeRootHolder, - DuplicationRepository duplicationRepository) { + public PersistDuplicationsStep(DbClient dbClient, DbIdsRepository dbIdsRepository, TreeRootHolder treeRootHolder, + DuplicationRepository duplicationRepository) { this.dbClient = dbClient; this.dbIdsRepository = dbIdsRepository; this.treeRootHolder = treeRootHolder; @@ -68,7 +68,7 @@ public class PersistDuplicationsStep implements ComputationStep { try { MetricDto duplicationMetric = dbClient.metricDao().selectOrFailByKey(session, CoreMetrics.DUPLICATIONS_DATA_KEY); new DepthTraversalTypeAwareCrawler(new DuplicationVisitor(session, duplicationMetric)) - .visit(treeRootHolder.getRoot()); + .visit(treeRootHolder.getRoot()); session.commit(); } finally { MyBatis.closeQuietly(session); @@ -97,10 +97,10 @@ public class PersistDuplicationsStep implements ComputationStep { private void saveDuplications(Component component, Iterable duplications) { String duplicationXml = createXmlDuplications(component.getKey(), duplications); MeasureDto measureDto = new MeasureDto() - .setMetricId(duplicationMetric.getId()) - .setData(duplicationXml) - .setComponentId(dbIdsRepository.getComponentId(component)) - .setSnapshotId(dbIdsRepository.getSnapshotId(component)); + .setMetricId(duplicationMetric.getId()) + .setData(duplicationXml) + .setComponentId(dbIdsRepository.getComponentId(component)) + .setSnapshotId(dbIdsRepository.getSnapshotId(component)); dbClient.measureDao().insert(session, measureDto); } @@ -142,9 +142,9 @@ public class PersistDuplicationsStep implements ComputationStep { private void appendDuplication(StringBuilder xml, String componentKey, TextBlock textBlock) { int length = textBlock.getEnd() - textBlock.getStart() + 1; xml.append(""); + .append("\" l=\"").append(length) + .append("\" r=\"").append(StringEscapeUtils.escapeXml(componentKey)) + .append("\"/>"); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java index a0b8974485d..cb96a8e02f1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java @@ -36,11 +36,11 @@ import java.util.Set; import org.apache.ibatis.session.ResultContext; import org.apache.ibatis.session.ResultHandler; import org.sonar.api.utils.System2; -import org.sonar.core.util.Uuids; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.util.CloseableIterator; +import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.MyBatis; @@ -52,7 +52,7 @@ import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.ComponentVisitor; import org.sonar.server.computation.component.CrawlerDepthLimit; import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler; -import org.sonar.server.computation.component.ReportTreeRootHolder; +import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.component.TypeAwareVisitorAdapter; public class PersistTestsStep implements ComputationStep { @@ -62,9 +62,9 @@ public class PersistTestsStep implements ComputationStep { private final DbClient dbClient; private final System2 system; private final BatchReportReader reportReader; - private final ReportTreeRootHolder treeRootHolder; + private final TreeRootHolder treeRootHolder; - public PersistTestsStep(DbClient dbClient, System2 system, BatchReportReader reportReader, ReportTreeRootHolder treeRootHolder) { + public PersistTestsStep(DbClient dbClient, System2 system, BatchReportReader reportReader, TreeRootHolder treeRootHolder) { this.dbClient = dbClient; this.system = system; this.reportReader = reportReader; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java index 92335b9abde..865f9145966 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java @@ -19,77 +19,42 @@ */ package org.sonar.server.computation.batch; -import java.util.HashMap; -import java.util.Map; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; +import org.junit.rules.ExternalResource; import org.sonar.server.computation.component.Component; -import org.sonar.server.computation.component.CrawlerDepthLimit; -import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler; -import org.sonar.server.computation.component.MutableTreeRootHolder; -import org.sonar.server.computation.component.ReportTreeRootHolder; import org.sonar.server.computation.component.TreeRootHolder; -import org.sonar.server.computation.component.TypeAwareVisitorAdapter; +import org.sonar.server.computation.component.TreeRootHolderImpl; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static java.util.Objects.requireNonNull; -import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER; - -public class TreeRootHolderRule implements TestRule, MutableTreeRootHolder, ReportTreeRootHolder { - private Component root; - private Map componentsByRef = new HashMap<>(); +public class TreeRootHolderRule extends ExternalResource implements TreeRootHolder { + protected TreeRootHolderImpl delegate = new TreeRootHolderImpl(); @Override - public Statement apply(final Statement statement, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - try { - statement.evaluate(); - } finally { - clear(); - } - } - }; + protected void after() { + this.delegate = null; } - private void clear() { - this.root = null; - this.componentsByRef.clear(); + public TreeRootHolderRule setRoot(Component newRoot) { + delegate = new TreeRootHolderImpl(); + delegate.setRoot(newRoot); + return this; } @Override public Component getRoot() { - checkInitialized(); - - return root; + return delegate.getRoot(); } @Override public Component getComponentByRef(int ref) { - checkInitialized(); - - Component component = componentsByRef.get(ref); - checkArgument(component != null, "Component with ref '%s' hasn't been found", ref); - return component; + return delegate.getComponentByRef(ref); } - private void checkInitialized() { - checkState(root != null, "Root has not been set in %s", TreeRootHolder.class.getSimpleName()); + @Override + public Component getComponentByKey(String key) { + return delegate.getComponentByKey(key); } - public TreeRootHolderRule setRoot(Component newRoot) { - this.root = requireNonNull(newRoot); - if (newRoot.getType().isReportType()) { - new DepthTraversalTypeAwareCrawler(new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, POST_ORDER) { - @Override - public void visitAny(Component component) { - componentsByRef.put(component.getReportAttributes().getRef(), component); - } - }).visit(root); - } - return this; + @Override + public boolean hasComponentWithKey(String key) { + return delegate.hasComponentWithKey(key); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/MutableTreeRootHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/MutableTreeRootHolderRule.java index f2309b75722..b9124c96c47 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/MutableTreeRootHolderRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/MutableTreeRootHolderRule.java @@ -24,6 +24,7 @@ import org.sonar.server.computation.batch.TreeRootHolderRule; public class MutableTreeRootHolderRule extends TreeRootHolderRule implements MutableTreeRootHolder { @Override public MutableTreeRootHolderRule setRoot(Component newRoot) { - return (MutableTreeRootHolderRule)super.setRoot(newRoot); + delegate.setRoot(newRoot); + return this; } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/ReportTreeRootHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/ReportTreeRootHolderImplTest.java deleted file mode 100644 index 62e17c73ab3..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/ReportTreeRootHolderImplTest.java +++ /dev/null @@ -1,80 +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.component; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ReportTreeRootHolderImplTest { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - ReportTreeRootHolderImpl treeRootHolder = new ReportTreeRootHolderImpl(); - Component project = ReportComponent.DUMB_PROJECT; - - @Test - public void setRoot_throws_NPE_if_arg_is_null() { - thrown.expect(NullPointerException.class); - treeRootHolder.setRoot(null); - } - - @Test - public void getRoot_throws_ISE_if_root_has_not_been_set_yet() { - thrown.expect(IllegalStateException.class); - treeRootHolder.getRoot(); - } - - @Test - public void verify_setRoot_getRoot() { - treeRootHolder.setRoot(project); - assertThat(treeRootHolder.getRoot()).isSameAs(project); - } - - @Test - public void get_by_ref() { - Component file = ReportComponent.builder(Component.Type.FILE, 4).build(); - Component directory = ReportComponent.builder(Component.Type.DIRECTORY, 3).addChildren(file).build(); - Component module = ReportComponent.builder(Component.Type.MODULE, 2).addChildren(directory).build(); - Component project = ReportComponent.builder(Component.Type.PROJECT, 1).addChildren(module).build(); - treeRootHolder.setRoot(project); - - assertThat(treeRootHolder.getComponentByRef(1)).isEqualTo(project); - assertThat(treeRootHolder.getComponentByRef(2)).isEqualTo(module); - assertThat(treeRootHolder.getComponentByRef(3)).isEqualTo(directory); - assertThat(treeRootHolder.getComponentByRef(4)).isEqualTo(file); - } - - @Test - public void fail_to_get_by_ref_if_root_not_set() { - thrown.expect(IllegalStateException.class); - treeRootHolder.getComponentByRef(project.getReportAttributes().getRef()); - } - - @Test - public void fail_to_get_by_ref_if_ref_not_found() { - thrown.expect(IllegalArgumentException.class); - treeRootHolder.setRoot(project); - treeRootHolder.getComponentByRef(123); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/TreeRootHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/TreeRootHolderImplTest.java index 069d77ea7b7..ecce5f0a22d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/TreeRootHolderImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/TreeRootHolderImplTest.java @@ -24,31 +24,182 @@ import org.junit.Test; import org.junit.rules.ExpectedException; 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.Component.Type.PROJECT_VIEW; +import static org.sonar.server.computation.component.Component.Type.VIEW; +import static org.sonar.server.computation.component.ReportComponent.DUMB_PROJECT; public class TreeRootHolderImplTest { + private static final ReportComponent SOME_REPORT_COMPONENT_TREE = ReportComponent.builder(PROJECT, 1) + .addChildren( + ReportComponent.builder(MODULE, 2) + .addChildren(ReportComponent.builder(DIRECTORY, 3) + .addChildren( + ReportComponent.builder(FILE, 4).build() + ) + .build()) + .build() + ) + .build(); + private static final ViewsComponent SOME_VIEWS_COMPONENT_TREE = ViewsComponent.builder(VIEW, 1) + .addChildren( + ViewsComponent.builder(VIEW, 2) + .addChildren(ViewsComponent.builder(PROJECT_VIEW, 3).build()) + .build() + ) + .build(); + @Rule - public ExpectedException thrown = ExpectedException.none(); + public ExpectedException expectedException = ExpectedException.none(); - TreeRootHolderImpl treeRootHolder = new TreeRootHolderImpl(); - Component project = ReportComponent.DUMB_PROJECT; + private TreeRootHolderImpl underTest = new TreeRootHolderImpl(); @Test public void setRoot_throws_NPE_if_arg_is_null() { - thrown.expect(NullPointerException.class); - treeRootHolder.setRoot(null); + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("root can not be null"); + + underTest.setRoot(null); + } + + @Test + public void setRoot_throws_ISE_when_called_twice() { + underTest.setRoot(DUMB_PROJECT); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("root can not be set twice in holder"); + + underTest.setRoot(DUMB_PROJECT); } @Test public void getRoot_throws_ISE_if_root_has_not_been_set_yet() { - thrown.expect(IllegalStateException.class); - treeRootHolder.getRoot(); + expectNotInitialized_ISE(); + + underTest.getRoot(); + } + + @Test + public void getComponentByRef_throws_ISE_if_root_has_not_been_set() { + expectNotInitialized_ISE(); + + underTest.getComponentByRef(12); + } + + @Test + public void getComponentByRef_returns_any_report_component_in_the_tree() { + underTest.setRoot(SOME_REPORT_COMPONENT_TREE); + + for (int i = 1; i <= 4; i++) { + assertThat(underTest.getComponentByRef(i).getReportAttributes().getRef()).isEqualTo(i); + } + } + + @Test + public void getComponentByRef_throws_IAE_if_holder_does_not_contain_specified_component() { + underTest.setRoot(SOME_REPORT_COMPONENT_TREE); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Component with ref '6' can't be found"); + + underTest.getComponentByRef(6); + } + + @Test + public void getComponentByRef_throws_IAE_if_holder_contains_View_tree() { + underTest.setRoot(SOME_VIEWS_COMPONENT_TREE); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Component with ref '1' can't be found"); + + underTest.getComponentByRef(1); + } + + @Test + public void getComponentByKey_throws_NPE_if_key_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("key can not be null"); + + underTest.getComponentByKey(null); + } + + @Test + public void getComponentByKey_throws_ISE_if_root_has_not_been_set() { + expectNotInitialized_ISE(); + + underTest.getComponentByKey("key"); + } + + @Test + public void getComponentByKey_returns_any_report_component_in_the_tree() { + underTest.setRoot(SOME_REPORT_COMPONENT_TREE); + + for (int i = 1; i <= 4; i++) { + String key = "key_" + i; + assertThat(underTest.getComponentByKey(key).getKey()).isEqualTo(key); + } + } + + @Test + public void getComponentByKey_returns_any_views_component_in_the_tree() { + underTest.setRoot(SOME_VIEWS_COMPONENT_TREE); + + for (int i = 1; i <= 3; i++) { + String key = String.valueOf(i); + assertThat(underTest.getComponentByKey(key).getKey()).isEqualTo(key); + } } @Test public void verify_setRoot_getRoot() { - treeRootHolder.setRoot(project); - assertThat(treeRootHolder.getRoot()).isSameAs(project); + underTest.setRoot(DUMB_PROJECT); + assertThat(underTest.getRoot()).isSameAs(DUMB_PROJECT); + } + + @Test + public void hasComponentWithKey_throws_NPE_if_key_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("key can not be null"); + + underTest.hasComponentWithKey(null); + } + + @Test + public void hasComponentWithKey_throws_ISE_if_root_has_not_been_set() { + expectNotInitialized_ISE(); + + underTest.hasComponentWithKey("key"); + } + + @Test + public void hasComponentWithKey_returns_true_for_any_report_component_in_the_tree() { + underTest.setRoot(SOME_REPORT_COMPONENT_TREE); + + for (int i = 1; i <= 4; i++) { + String key = "key_" + i; + assertThat(underTest.hasComponentWithKey(key)).isTrue(); + } + assertThat(underTest.hasComponentWithKey("toto")).isFalse(); + } + + @Test + public void hasComponentWithKey_returns_true_for_any_views_component_in_the_tree() { + underTest.setRoot(SOME_VIEWS_COMPONENT_TREE); + + for (int i = 1; i <= 3; i++) { + String key = String.valueOf(i); + assertThat(underTest.hasComponentWithKey(key)).isTrue(); + } + assertThat(underTest.hasComponentWithKey("toto")).isFalse(); + } + + private void expectNotInitialized_ISE() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Holder has not been initialized yet"); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java index dd1950d026f..9946698c565 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java @@ -25,7 +25,7 @@ import javax.annotation.CheckForNull; import org.junit.rules.ExternalResource; import org.sonar.core.issue.DefaultIssue; import org.sonar.server.computation.component.Component; -import org.sonar.server.computation.component.ReportTreeRootHolder; +import org.sonar.server.computation.component.TreeRootHolder; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -34,7 +34,7 @@ import static java.util.Objects.requireNonNull; public class ComponentIssuesRepositoryRule extends ExternalResource implements MutableComponentIssuesRepository, ComponentIssuesRepository { - private final ReportTreeRootHolder reportTreeRootHolder; + private final TreeRootHolder treeRootHolder; @CheckForNull private List issues; @@ -42,8 +42,8 @@ public class ComponentIssuesRepositoryRule extends ExternalResource implements M @CheckForNull private Component component; - public ComponentIssuesRepositoryRule(ReportTreeRootHolder reportTreeRootHolder) { - this.reportTreeRootHolder = reportTreeRootHolder; + public ComponentIssuesRepositoryRule(TreeRootHolder treeRootHolder) { + this.treeRootHolder = treeRootHolder; } @Override @@ -54,7 +54,7 @@ public class ComponentIssuesRepositoryRule extends ExternalResource implements M public void setIssues(int componentRef, List issues) { this.issues = requireNonNull(issues, "issues cannot be null"); - Component component = reportTreeRootHolder.getComponentByRef(componentRef); + Component component = treeRootHolder.getComponentByRef(componentRef); checkArgument(component != null, String.format("Component '%s' does not exists in the report ", componentRef)); this.component = component; } @@ -67,7 +67,7 @@ public class ComponentIssuesRepositoryRule extends ExternalResource implements M public List getIssues(int componentRef) { checkState(this.component != null && this.issues != null, "Issues have not been initialized"); - Component component = reportTreeRootHolder.getComponentByRef(componentRef); + Component component = treeRootHolder.getComponentByRef(componentRef); checkArgument(component != null, String.format("Component '%s' does not exists in the report ", componentRef)); checkArgument(component == this.component, String.format("Only issues from component '%s' are available, but wanted component is '%s'.", diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java index 0da0ba15dd4..a57e5807d24 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java @@ -136,7 +136,7 @@ public class LoadDuplicationsFromReportStepTest { reportReader.putDuplications(FILE_1_REF, createDuplication(singleLineTextRange(line), createInProjectDuplicate(666, line + 1))); expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Component with ref '666' hasn't been found"); + expectedException.expectMessage("Component with ref '666' can't be found"); underTest.execute(); } -- 2.39.5