From: Julien Lancelot Date: Wed, 27 May 2015 07:45:46 +0000 (+0200) Subject: SONAR-6589 Remove projectKey from ComputationContext X-Git-Tag: 5.2-RC1~1828 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e556427f59d55888b2c6c181620de7d767e3caf2;p=sonarqube.git SONAR-6589 Remove projectKey from ComputationContext --- diff --git a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java index d4a52759cb6..b5507606e40 100644 --- a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java +++ b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java @@ -35,9 +35,9 @@ import org.sonar.batch.protocol.output.BatchReportReader; import org.sonar.batch.protocol.output.BatchReportWriter; import org.sonar.core.persistence.DbTester; import org.sonar.server.computation.ComputationContext; +import org.sonar.server.computation.component.ComponentTreeBuilders; import org.sonar.server.computation.component.DbComponentsRefCache; import org.sonar.server.computation.component.DbComponentsRefCache.DbComponent; -import org.sonar.server.computation.component.ComponentTreeBuilders; import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.language.LanguageRepository; import org.sonar.server.computation.step.PersistFileSourcesStep; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java index 6be59fd467b..a402a7744b0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java @@ -19,12 +19,8 @@ */ package org.sonar.server.computation; -import java.util.Arrays; -import java.util.List; - import org.sonar.core.issue.db.UpdateConflictResolver; import org.sonar.core.platform.ComponentContainer; -import org.sonar.server.computation.component.ComputeComponentsRefCache; import org.sonar.server.computation.component.DbComponentsRefCache; import org.sonar.server.computation.issue.IssueCache; import org.sonar.server.computation.issue.IssueComputation; @@ -39,6 +35,9 @@ import org.sonar.server.computation.step.ComputationSteps; import org.sonar.server.platform.Platform; import org.sonar.server.view.index.ViewIndex; +import java.util.Arrays; +import java.util.List; + public class ComputationContainer { /** @@ -53,8 +52,7 @@ public class ComputationContainer { ComputationService.class, ComputationSteps.class, - // component caches - ComputeComponentsRefCache.class, + // component cache DbComponentsRefCache.class, // issues diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java index d9f0a9ca0f7..34021ce47e3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java @@ -31,8 +31,6 @@ public class ComputationContext implements org.sonar.server.computation.context. private final BatchReportReader reportReader; private final Settings projectSettings; private final DbClient dbClient; - // Project key (including branch if any) - private final String projectKey; // cache of metadata as it's frequently accessed private final BatchReport.Metadata reportMetadata; private final Component component; @@ -41,7 +39,6 @@ public class ComputationContext implements org.sonar.server.computation.context. public ComputationContext(BatchReportReader reportReader, String projectKey, Settings projectSettings, DbClient dbClient, ComponentTreeBuilder componentTreeBuilder, LanguageRepository languageRepository) { this.reportReader = reportReader; - this.projectKey = projectKey; this.projectSettings = projectSettings; this.dbClient = dbClient; this.reportMetadata = reportReader.readMetadata(); @@ -53,10 +50,6 @@ public class ComputationContext implements org.sonar.server.computation.context. return reportMetadata; } - public String getProjectKey() { - return projectKey; - } - public BatchReportReader getReportReader() { return reportReader; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java index 134e2f411ae..c18ede0d0db 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java @@ -21,9 +21,6 @@ package org.sonar.server.computation; import com.google.common.base.Throwables; -import java.io.File; -import java.io.IOException; -import javax.annotation.Nullable; import org.apache.commons.io.FileUtils; import org.sonar.api.config.Settings; import org.sonar.api.server.ServerSide; @@ -91,7 +88,7 @@ public class ComputationService { File reportDir = extractReportInDir(item); BatchReportReader reader = new BatchReportReader(reportDir); Settings projectSettings = projectSettingsFactory.newProjectSettings(projectKey); - ComputationContext context = new ComputationContext(reader, projectKey, projectSettings, dbClient, ComponentTreeBuilders.from(reader), languageRepository); + ComputationContext context = new ComputationContext(reader, null, projectSettings, dbClient, ComponentTreeBuilders.from(reader), languageRepository); for (ComputationStep step : steps.orderedSteps()) { Profiler stepProfiler = Profiler.createIfDebug(LOG).startDebug(step.getDescription()); step.execute(context); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java index b1014dcb54b..0f296ff859f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java @@ -19,11 +19,12 @@ */ package org.sonar.server.computation.component; -import java.util.List; import org.sonar.server.computation.context.ComputationContext; import org.sonar.server.computation.event.EventRepository; import org.sonar.server.computation.measure.MeasureRepository; +import java.util.List; + public interface Component { enum Type { PROJECT(0), MODULE(1), DIRECTORY(2), FILE(3); @@ -47,6 +48,10 @@ public interface Component { Type getType(); + String getUuid(); + + String getKey(); + // FIXME we should not expose a batch specific information int getRef(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentImpl.java index b9a66977021..a2f2d6ce27c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentImpl.java @@ -22,12 +22,6 @@ package org.sonar.server.computation.component; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import org.sonar.api.measures.Metric; import org.sonar.batch.protocol.Constants; import org.sonar.batch.protocol.output.BatchReport; @@ -39,6 +33,14 @@ import org.sonar.server.computation.event.EventRepository; import org.sonar.server.computation.measure.MeasureRepository; import org.sonar.server.db.DbClient; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import static com.google.common.base.Predicates.notNull; import static com.google.common.collect.ImmutableList.copyOf; import static com.google.common.collect.Iterables.filter; @@ -51,6 +53,10 @@ public class ComponentImpl implements Component { private final List children; private final EventRepository eventRepository = new SetEventRepository(); + // Mutable values + private String key; + private String uuid; + public ComponentImpl(ComputationContext context, BatchReport.Component component, @Nullable Iterable children) { this.context = context; this.component = component; @@ -83,6 +89,27 @@ public class ComponentImpl implements Component { return component.getRef(); } + public String getUuid() { + return uuid; + } + + public ComponentImpl setUuid(String uuid) { + this.uuid = uuid; + return this; + } + + public String getKey() { + if (key == null) { + throw new UnsupportedOperationException(String.format("Component key of ref '%s' has not be fed yet", getRef())); + } + return key; + } + + public ComponentImpl setKey(String key) { + this.key = key; + return this; + } + @Override public List getChildren() { return children; @@ -106,6 +133,7 @@ public class ComponentImpl implements Component { DbClient dbClient = context.getDbClient(); try (DbSession dbSession = dbClient.openSession(false)) { return Optional.fromNullable( + // TODO replace component.getKey() by ${link #getKey} as component.getKey() is only for project/module and does not take into account usage of the branch dbClient.measureDao().findByComponentKeyAndMetricKey(dbSession, component.getKey(), metric.getKey()) ); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComputeComponentsRefCache.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComputeComponentsRefCache.java deleted file mode 100644 index 76b32768b76..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComputeComponentsRefCache.java +++ /dev/null @@ -1,68 +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; - -/** - * Cache of components (uuid and key) that can be used in compute steps (For instance for issue computation) - */ -public class ComputeComponentsRefCache { - - private final Map componentsByRef; - - public ComputeComponentsRefCache() { - componentsByRef = new HashMap<>(); - } - - public ComputeComponentsRefCache addComponent(Integer ref, ComputeComponent computeComponent) { - componentsByRef.put(ref, computeComponent); - return this; - } - - public ComputeComponent getByRef(Integer ref) { - ComputeComponent computeComponent = componentsByRef.get(ref); - if (computeComponent == null) { - throw new IllegalArgumentException(String.format("Component ref '%s' does not exists", ref)); - } - return componentsByRef.get(ref); - } - - public static class ComputeComponent { - private String uuid; - private String key; - - public ComputeComponent(String key, String uuid) { - this.key = key; - this.uuid = uuid; - } - - public String getKey() { - return key; - } - - public String getUuid() { - return uuid; - } - - } -} 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 0ff44fd8b93..cb8c0c39ef2 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 @@ -20,12 +20,11 @@ package org.sonar.server.computation.step; -import java.util.Arrays; -import java.util.List; - +import com.google.common.collect.Lists; import org.sonar.server.computation.ComputationContainer; -import com.google.common.collect.Lists; +import java.util.Arrays; +import java.util.List; /** * Ordered list of steps to be executed @@ -38,7 +37,7 @@ public class ComputationSteps { */ public static List> orderedStepClasses() { return Arrays.asList( - FeedComponentsCacheStep.class, + FeedComponentUuidsStep.class, // Read report ParseReportStep.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedComponentUuidsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedComponentUuidsStep.java new file mode 100644 index 00000000000..ebdf1fd6779 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedComponentUuidsStep.java @@ -0,0 +1,137 @@ +/* + * 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.api.utils.internal.Uuids; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.batch.protocol.output.BatchReportReader; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.component.ComponentKeys; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.computation.ComputationContext; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.ComponentImpl; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; +import org.sonar.server.db.DbClient; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Read all components from the batch report and set components uuid and key + */ +public class FeedComponentUuidsStep implements ComputationStep { + + private final DbClient dbClient; + + public FeedComponentUuidsStep(DbClient dbClient) { + this.dbClient = dbClient; + } + + @Override + public void execute(final ComputationContext context) { + new ComponentDepthTraversalTypeAwareVisitor(context).visit(context.getRoot()); + } + + @Override + public String getDescription() { + return "Feed components uuid"; + } + + private class ComponentDepthTraversalTypeAwareVisitor extends DepthTraversalTypeAwareVisitor { + + private BatchReportReader reportReader; + private Map componentUuidByKey; + private String branch; + private Component nearestModule; + + public ComponentDepthTraversalTypeAwareVisitor(ComputationContext context) { + super(Component.Type.FILE, Order.PRE_ORDER); + this.componentUuidByKey = new HashMap<>(); + this.branch = context.getReportMetadata().hasBranch() ? context.getReportMetadata().getBranch() : null; + this.reportReader = context.getReportReader(); + this.nearestModule = null; + } + + @Override + public void visitProject(Component project) { + executeForProject(project); + nearestModule = project; + } + + @Override + public void visitModule(Component module) { + executeForModule(module); + nearestModule = module; + } + + @Override + public void visitDirectory(Component directory) { + executeForDirectoryAndFile(directory); + } + + @Override + public void visitFile(Component file) { + executeForDirectoryAndFile(file); + } + + private void executeForProject(Component component) { + BatchReport.Component project = reportReader.readComponent(component.getRef()); + String projectKey = ComponentKeys.createKey(project.getKey(), branch); + DbSession session = dbClient.openSession(false); + try { + List components = dbClient.componentDao().selectComponentsFromProjectKey(session, projectKey); + for (ComponentDto componentDto : components) { + componentUuidByKey.put(componentDto.getKey(), componentDto.uuid()); + } + + feedComponent((ComponentImpl) component, projectKey); + } finally { + session.close(); + } + } + + private void executeForModule(Component component) { + BatchReport.Component batchComponent = reportReader.readComponent(component.getRef()); + String componentKey = ComponentKeys.createKey(batchComponent.getKey(), branch); + feedComponent((ComponentImpl) component, componentKey); + } + + private void executeForDirectoryAndFile(Component component) { + BatchReport.Component batchComponent = reportReader.readComponent(component.getRef()); + // TODO fail if path is null + String componentKey = ComponentKeys.createEffectiveKey(nearestModule.getKey(), batchComponent.getPath()); + feedComponent((ComponentImpl) component, componentKey); + } + + private void feedComponent(ComponentImpl projectImpl, String componentKey) { + projectImpl.setKey(componentKey); + + String componentUuid = componentUuidByKey.get(componentKey); + if (componentUuid == null) { + projectImpl.setUuid(Uuids.create()); + } else { + projectImpl.setUuid(componentUuid); + } + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedComponentsCacheStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedComponentsCacheStep.java deleted file mode 100644 index ed88d38932a..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedComponentsCacheStep.java +++ /dev/null @@ -1,104 +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.collect.Maps; -import org.sonar.api.utils.internal.Uuids; -import org.sonar.batch.protocol.Constants; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.batch.protocol.output.BatchReportReader; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.component.ComponentKeys; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.util.NonNullInputFunction; -import org.sonar.server.computation.ComputationContext; -import org.sonar.server.computation.component.ComputeComponentsRefCache; -import org.sonar.server.db.DbClient; - -import java.util.List; -import java.util.Map; - -/** - * Read all components from the batch report and feed the cache containing component uuid and key - */ -public class FeedComponentsCacheStep implements ComputationStep { - - private final DbClient dbClient; - private final ComputeComponentsRefCache computeComponentsRefCache; - - public FeedComponentsCacheStep(DbClient dbClient, ComputeComponentsRefCache computeComponentsRefCache) { - this.dbClient = dbClient; - this.computeComponentsRefCache = computeComponentsRefCache; - } - - @Override - public void execute(ComputationContext context) { - DbSession session = dbClient.openSession(false); - try { - List components = dbClient.componentDao().selectComponentsFromProjectKey(session, context.getProjectKey()); - Map componentDtosByKey = componentDtosByKey(components); - int rootComponentRef = context.getReportMetadata().getRootComponentRef(); - recursivelyProcessComponent(context, rootComponentRef, context.getReportReader().readComponent(rootComponentRef), componentDtosByKey); - } finally { - session.close(); - } - } - - private void recursivelyProcessComponent(ComputationContext context, int componentRef, BatchReport.Component nearestModule, Map componentDtosByKey) { - BatchReportReader reportReader = context.getReportReader(); - BatchReport.Component reportComponent = reportReader.readComponent(componentRef); - - String path = reportComponent.hasPath() ? reportComponent.getPath() : null; - String branch = context.getReportMetadata().hasBranch() ? context.getReportMetadata().getBranch() : null; - String componentKey = reportComponent.hasKey() ? - ComponentKeys.createKey(reportComponent.getKey(), branch) : - ComponentKeys.createKey(nearestModule.getKey(), path, branch); - - ComponentDto componentDto = componentDtosByKey.get(componentKey); - if (componentDto == null) { - computeComponentsRefCache.addComponent(componentRef, new ComputeComponentsRefCache.ComputeComponent(componentKey, Uuids.create())); - } else { - computeComponentsRefCache.addComponent(componentRef, new ComputeComponentsRefCache.ComputeComponent(componentKey, componentDto.uuid())); - } - - for (Integer childRef : reportComponent.getChildRefList()) { - // If current component is not a module or a project, we need to keep the parent reference to the nearest module - BatchReport.Component nextModuleParent = !reportComponent.getType().equals(Constants.ComponentType.PROJECT) - && !reportComponent.getType().equals(Constants.ComponentType.MODULE) ? - nearestModule : reportComponent; - recursivelyProcessComponent(context, childRef, nextModuleParent, componentDtosByKey); - } - } - - private Map componentDtosByKey(List components) { - return Maps.uniqueIndex(components, new NonNullInputFunction() { - @Override - public String doApply(ComponentDto input) { - return input.key(); - } - }); - } - - @Override - public String getDescription() { - return "Feed components cache"; - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java index 04de00edbc8..cc4a96dedba 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java @@ -23,7 +23,8 @@ package org.sonar.server.computation.step; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.batch.protocol.output.BatchReportReader; import org.sonar.server.computation.ComputationContext; -import org.sonar.server.computation.component.ComputeComponentsRefCache; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.issue.IssueComputation; import java.util.List; @@ -31,39 +32,24 @@ import java.util.List; public class ParseReportStep implements ComputationStep { private final IssueComputation issueComputation; - private final ComputeComponentsRefCache computeComponentsRefCache; - public ParseReportStep(IssueComputation issueComputation, ComputeComponentsRefCache computeComponentsRefCache) { + public ParseReportStep(IssueComputation issueComputation) { this.issueComputation = issueComputation; - this.computeComponentsRefCache = computeComponentsRefCache; } @Override - public void execute(ComputationContext context) { - int rootComponentRef = context.getReportMetadata().getRootComponentRef(); - recursivelyProcessComponent(context, rootComponentRef); - processDeletedComponents(context); + public void execute(final ComputationContext context) { + IssueDepthTraversalTypeAwareVisitor visitor = new IssueDepthTraversalTypeAwareVisitor(context); + visitor.visit(context.getRoot()); + processDeletedComponents(context, visitor); issueComputation.afterReportProcessing(); } - private void recursivelyProcessComponent(ComputationContext context, int componentRef) { - BatchReportReader reportReader = context.getReportReader(); - BatchReport.Component component = reportReader.readComponent(componentRef); - List issues = reportReader.readComponentIssues(componentRef); - ComputeComponentsRefCache.ComputeComponent computeProject = computeComponentsRefCache.getByRef(context.getReportMetadata().getRootComponentRef()); - issueComputation.processComponentIssues(context, issues, computeComponentsRefCache.getByRef(componentRef).getUuid(), componentRef, computeProject.getKey(), - computeProject.getUuid()); - for (Integer childRef : component.getChildRefList()) { - recursivelyProcessComponent(context, childRef); - } - } - - private void processDeletedComponents(ComputationContext context) { + private void processDeletedComponents(ComputationContext context, IssueDepthTraversalTypeAwareVisitor visitor) { int deletedComponentsCount = context.getReportMetadata().getDeletedComponentsCount(); - ComputeComponentsRefCache.ComputeComponent computeProject = computeComponentsRefCache.getByRef(context.getReportMetadata().getRootComponentRef()); for (int componentRef = 1; componentRef <= deletedComponentsCount; componentRef++) { BatchReport.Issues issues = context.getReportReader().readDeletedComponentIssues(componentRef); - issueComputation.processComponentIssues(context, issues.getIssueList(), issues.getComponentUuid(), null, computeProject.getKey(), computeProject.getUuid()); + issueComputation.processComponentIssues(context, issues.getIssueList(), issues.getComponentUuid(), null, visitor.projectKey, visitor.projectUuid); } } @@ -71,4 +57,47 @@ public class ParseReportStep implements ComputationStep { public String getDescription() { return "Digest analysis report"; } + + private class IssueDepthTraversalTypeAwareVisitor extends DepthTraversalTypeAwareVisitor { + + private final ComputationContext context; + private final BatchReportReader reportReader; + + private String projectKey; + private String projectUuid; + + public IssueDepthTraversalTypeAwareVisitor(ComputationContext context) { + super(Component.Type.FILE, Order.PRE_ORDER); + this.reportReader = context.getReportReader(); + this.context = context; + } + + @Override + public void visitProject(Component tree) { + projectKey = tree.getKey(); + projectUuid = tree.getUuid(); + executeForComponent(tree, context); + } + + @Override + public void visitModule(Component module) { + executeForComponent(module, context); + } + + @Override + public void visitDirectory(Component directory) { + executeForComponent(directory, context); + } + + @Override + public void visitFile(Component file) { + executeForComponent(file, context); + } + + private void executeForComponent(Component component, ComputationContext context) { + int componentRef = component.getRef(); + List issues = reportReader.readComponentIssues(componentRef); + issueComputation.processComponentIssues(context, issues, component.getUuid(), componentRef, projectKey, projectUuid); + } + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java index a6aeba4b13b..dd75874019d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java @@ -20,37 +20,32 @@ package org.sonar.server.computation.step; -import com.google.common.collect.Maps; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Scopes; -import org.sonar.batch.protocol.Constants; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.batch.protocol.output.BatchReportReader; import org.sonar.core.component.ComponentDto; import org.sonar.core.persistence.DbSession; -import org.sonar.core.util.NonNullInputFunction; import org.sonar.server.computation.ComputationContext; -import org.sonar.server.computation.component.ComputeComponentsRefCache; +import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.db.DbClient; -import javax.annotation.Nullable; - +import java.util.HashMap; import java.util.List; import java.util.Map; public class PersistComponentsStep implements ComputationStep { private final DbClient dbClient; - private final ComputeComponentsRefCache computeComponentsRefCache; private final DbComponentsRefCache dbComponentsRefCache; - public PersistComponentsStep(DbClient dbClient, ComputeComponentsRefCache computeComponentsRefCache, DbComponentsRefCache dbComponentsRefCache) { + public PersistComponentsStep(DbClient dbClient, DbComponentsRefCache dbComponentsRefCache) { this.dbClient = dbClient; - this.computeComponentsRefCache = computeComponentsRefCache; this.dbComponentsRefCache = dbComponentsRefCache; } @@ -58,189 +53,176 @@ public class PersistComponentsStep implements ComputationStep { public void execute(ComputationContext context) { DbSession session = dbClient.openSession(false); try { - List components = dbClient.componentDao().selectComponentsFromProjectKey(session, context.getProjectKey()); - Map componentDtosByKey = componentDtosByKey(components); - int rootComponentRef = context.getReportMetadata().getRootComponentRef(); - ComponentContext componentContext = new ComponentContext(context, session, componentDtosByKey); - recursivelyProcessComponent(componentContext, rootComponentRef, null, null); + new ComponentDepthTraversalTypeAwareVisitor(session, context).visit(context.getRoot()); session.commit(); } finally { session.close(); } } - private void recursivelyProcessComponent(ComponentContext componentContext, int componentRef, @Nullable ComponentDto parentModule, @Nullable ComponentDto project) { - BatchReportReader reportReader = componentContext.context.getReportReader(); - BatchReport.Component reportComponent = reportReader.readComponent(componentRef); - ComponentDto componentDto = processComponent(componentContext, reportComponent, parentModule, project); - dbComponentsRefCache.addComponent(componentRef, new DbComponentsRefCache.DbComponent(componentDto.getId(), componentDto.getKey(), componentDto.uuid())); - - for (Integer childRef : reportComponent.getChildRefList()) { - // If current component is not a module or a project, we need to keep the parent reference to the nearest module - ComponentDto nextParent = !reportComponent.getType().equals(Constants.ComponentType.PROJECT) && !reportComponent.getType().equals(Constants.ComponentType.MODULE) ? - parentModule : componentDto; - // Keep reference to the project - ComponentDto nextProject = reportComponent.getType().equals(Constants.ComponentType.PROJECT) ? componentDto : project; - recursivelyProcessComponent(componentContext, childRef, nextParent, nextProject); + private class ComponentDepthTraversalTypeAwareVisitor extends DepthTraversalTypeAwareVisitor { + + private final DbSession session; + private final BatchReportReader reportReader; + private final Map componentDtosByKey; + + private Long projectId; + private ComponentDto lastModule; + + public ComponentDepthTraversalTypeAwareVisitor(DbSession session, ComputationContext context) { + super(Component.Type.FILE, Order.PRE_ORDER); + this.session = session; + this.reportReader = context.getReportReader(); + this.componentDtosByKey = new HashMap<>(); } - } - private ComponentDto processComponent(ComponentContext componentContext, BatchReport.Component reportComponent, @Nullable ComponentDto parentModule, - @Nullable ComponentDto project) { - ComputeComponentsRefCache.ComputeComponent cacheComputeComponent = computeComponentsRefCache.getByRef(reportComponent.getRef()); - String componentKey = cacheComputeComponent.getKey(); - String componentUuid = cacheComputeComponent.getUuid(); - ComponentDto existingComponent = componentContext.componentDtosByKey.get(componentKey); - if (existingComponent == null) { - ComponentDto component = createComponent(reportComponent, componentKey, componentUuid, parentModule, project); - dbClient.componentDao().insert(componentContext.dbSession, component); - return component; - } else { - ComponentDto component = createComponent(reportComponent, componentKey, existingComponent.uuid(), parentModule, project); - if (updateComponent(existingComponent, component)) { - dbClient.componentDao().update(componentContext.dbSession, existingComponent); + @Override + public void visitProject(Component project) { + List components = dbClient.componentDao().selectComponentsFromProjectKey(session, project.getKey()); + for (ComponentDto componentDto : components) { + componentDtosByKey.put(componentDto.getKey(), componentDto); } - return existingComponent; - } - } - private ComponentDto createComponent(BatchReport.Component reportComponent, String componentKey, String uuid, @Nullable ComponentDto parentModule, - @Nullable ComponentDto project) { - ComponentDto component = new ComponentDto(); - component.setUuid(uuid); - component.setKey(componentKey); - component.setDeprecatedKey(componentKey); - component.setEnabled(true); - component.setScope(getScope(reportComponent)); - component.setQualifier(getQualifier(reportComponent)); - component.setName(getFileName(reportComponent)); - - if (isProjectOrModule(reportComponent)) { - component.setLongName(component.name()); + BatchReport.Component reportComponent = reportReader.readComponent(project.getRef()); + ComponentDto componentDto = createComponentDto(reportComponent, project); + + componentDto.setScope(Scopes.PROJECT); + componentDto.setQualifier(Qualifiers.PROJECT); + componentDto.setName(reportComponent.getName()); + componentDto.setLongName(componentDto.name()); if (reportComponent.hasDescription()) { - component.setDescription(reportComponent.getDescription()); - } - } else { - component.setLongName(reportComponent.getPath()); - if (reportComponent.hasPath()) { - component.setPath(reportComponent.getPath()); + componentDto.setDescription(reportComponent.getDescription()); } - if (reportComponent.hasLanguage()) { - component.setLanguage(reportComponent.getLanguage()); - } - } - if (parentModule != null && project != null) { - component.setParentProjectId(component.scope().equals(Scopes.PROJECT) ? project.getId() : parentModule.getId()); - component.setProjectUuid(parentModule.projectUuid()); - component.setModuleUuid(parentModule.uuid()); - component.setModuleUuidPath(reportComponent.getType().equals(Constants.ComponentType.MODULE) ? - (parentModule.moduleUuidPath() + component.uuid() + ComponentDto.MODULE_UUID_PATH_SEP) : - parentModule.moduleUuidPath()); - } else { - component.setProjectUuid(uuid); - component.setModuleUuidPath(ComponentDto.MODULE_UUID_PATH_SEP + component.uuid() + ComponentDto.MODULE_UUID_PATH_SEP); + componentDto.setProjectUuid(componentDto.uuid()); + componentDto.setModuleUuidPath(ComponentDto.MODULE_UUID_PATH_SEP + componentDto.uuid() + ComponentDto.MODULE_UUID_PATH_SEP); + + persistComponent(project.getRef(), componentDto); + + lastModule = componentDto; + projectId = componentDto.getId(); } - return component; - } - private boolean updateComponent(ComponentDto existingComponent, ComponentDto newComponent) { - boolean isUpdated = false; - if (Scopes.PROJECT.equals(existingComponent.scope())) { - if (!newComponent.name().equals(existingComponent.name())) { - existingComponent.setName(newComponent.name()); - isUpdated = true; - } - if (!StringUtils.equals(existingComponent.description(), newComponent.description())) { - existingComponent.setDescription(newComponent.description()); - isUpdated = true; + @Override + public void visitModule(Component module) { + BatchReport.Component reportComponent = reportReader.readComponent(module.getRef()); + ComponentDto componentDto = createComponentDto(reportComponent, module); + + componentDto.setScope(Scopes.PROJECT); + componentDto.setQualifier(Qualifiers.MODULE); + componentDto.setName(reportComponent.getName()); + componentDto.setLongName(componentDto.name()); + if (reportComponent.hasDescription()) { + componentDto.setDescription(reportComponent.getDescription()); } - } + componentDto.setParentProjectId(projectId); + componentDto.setProjectUuid(lastModule.projectUuid()); + componentDto.setModuleUuid(lastModule.uuid()); + componentDto.setModuleUuidPath((lastModule.moduleUuidPath() + componentDto.uuid() + ComponentDto.MODULE_UUID_PATH_SEP)); - if (!StringUtils.equals(existingComponent.moduleUuid(), newComponent.moduleUuid())) { - existingComponent.setModuleUuid(newComponent.moduleUuid()); - isUpdated = true; - } - if (!existingComponent.moduleUuidPath().equals(newComponent.moduleUuidPath())) { - existingComponent.setModuleUuidPath(newComponent.moduleUuidPath()); - isUpdated = true; - } - if (!ObjectUtils.equals(existingComponent.parentProjectId(), newComponent.parentProjectId())) { - existingComponent.setParentProjectId(newComponent.parentProjectId()); - isUpdated = true; + persistComponent(module.getRef(), componentDto); + + lastModule = componentDto; } - return isUpdated; - } + @Override + public void visitDirectory(Component directory) { + BatchReport.Component reportComponent = reportReader.readComponent(directory.getRef()); + ComponentDto componentDto = createComponentDto(reportComponent, directory); - private static boolean isProjectOrModule(BatchReport.Component reportComponent) { - return reportComponent.getType().equals(Constants.ComponentType.PROJECT) || reportComponent.getType().equals(Constants.ComponentType.MODULE); - } + componentDto.setScope(Scopes.DIRECTORY); + componentDto.setQualifier(Qualifiers.DIRECTORY); + componentDto.setName(reportComponent.getPath()); + componentDto.setLongName(reportComponent.getPath()); + if (reportComponent.hasPath()) { + componentDto.setPath(reportComponent.getPath()); + } - private static String getScope(BatchReport.Component reportComponent) { - switch (reportComponent.getType()) { - case PROJECT: - case MODULE: - return Scopes.PROJECT; - case DIRECTORY: - return Scopes.DIRECTORY; - case FILE: - return Scopes.FILE; - default : - throw new IllegalArgumentException(String.format("Unknown type '%s'", reportComponent.getType())); + componentDto.setParentProjectId(lastModule.getId()); + componentDto.setProjectUuid(lastModule.projectUuid()); + componentDto.setModuleUuid(lastModule.uuid()); + componentDto.setModuleUuidPath(lastModule.moduleUuidPath()); + + persistComponent(directory.getRef(), componentDto); } - } - private static String getQualifier(BatchReport.Component reportComponent) { - switch (reportComponent.getType()) { - case PROJECT: - return Qualifiers.PROJECT; - case MODULE: - return Qualifiers.MODULE; - case DIRECTORY: - return Qualifiers.DIRECTORY; - case FILE: - return getFileQualifier(reportComponent); - default : - throw new IllegalArgumentException(String.format("Unknown type '%s'", reportComponent.getType())); + @Override + public void visitFile(Component file) { + BatchReport.Component reportComponent = reportReader.readComponent(file.getRef()); + ComponentDto componentDto = createComponentDto(reportComponent, file); + + componentDto.setScope(Scopes.FILE); + componentDto.setQualifier(getFileQualifier(reportComponent)); + componentDto.setName(FilenameUtils.getName(reportComponent.getPath())); + componentDto.setLongName(reportComponent.getPath()); + if (reportComponent.hasPath()) { + componentDto.setPath(reportComponent.getPath()); + } + if (reportComponent.hasLanguage()) { + componentDto.setLanguage(reportComponent.getLanguage()); + } + + componentDto.setParentProjectId(lastModule.getId()); + componentDto.setProjectUuid(lastModule.projectUuid()); + componentDto.setModuleUuid(lastModule.uuid()); + componentDto.setModuleUuidPath(lastModule.moduleUuidPath()); + + persistComponent(file.getRef(), componentDto); } - } - private static String getFileQualifier(BatchReport.Component reportComponent){ - return !reportComponent.getIsTest() ? Qualifiers.FILE : Qualifiers.UNIT_TEST_FILE; - } + private ComponentDto createComponentDto(BatchReport.Component reportComponent, Component component) { + String componentKey = component.getKey(); + String componentUuid = component.getUuid(); - private static String getFileName(BatchReport.Component reportComponent) { - String path = reportComponent.getPath(); - if (reportComponent.getType() == Constants.ComponentType.PROJECT || reportComponent.getType() == Constants.ComponentType.MODULE) { - return reportComponent.getName(); - } else if (reportComponent.getType().equals(Constants.ComponentType.DIRECTORY)) { - return path; - } else { - return FilenameUtils.getName(path); + ComponentDto componentDto = new ComponentDto(); + componentDto.setUuid(componentUuid); + componentDto.setKey(componentKey); + componentDto.setDeprecatedKey(componentKey); + componentDto.setEnabled(true); + return componentDto; } - } - private Map componentDtosByKey(List components) { - return Maps.uniqueIndex(components, new NonNullInputFunction() { - @Override - public String doApply(ComponentDto input) { - return input.key(); + private void persistComponent(int componentRef, ComponentDto componentDto) { + ComponentDto existingComponent = componentDtosByKey.get(componentDto.getKey()); + if (existingComponent == null) { + dbClient.componentDao().insert(session, componentDto); + } else { + componentDto.setId(existingComponent.getId()); + componentDto.setParentProjectId(existingComponent.parentProjectId()); + if (updateComponent(existingComponent, componentDto)) { + dbClient.componentDao().update(session, componentDto); + } } - }); - } + dbComponentsRefCache.addComponent(componentRef, new DbComponentsRefCache.DbComponent(componentDto.getId(), componentDto.getKey(), componentDto.uuid())); + } - private static class ComponentContext { - private final ComputationContext context; - private final Map componentDtosByKey; - private final DbSession dbSession; + private boolean updateComponent(ComponentDto existingComponent, ComponentDto newComponent) { + boolean isUpdated = false; + if (Scopes.PROJECT.equals(existingComponent.scope())) { + if (!newComponent.name().equals(existingComponent.name())) { + isUpdated = true; + } + if (!StringUtils.equals(existingComponent.description(), newComponent.description())) { + isUpdated = true; + } + } - public ComponentContext(ComputationContext context, DbSession dbSession, Map componentDtosByKey) { - this.componentDtosByKey = componentDtosByKey; - this.context = context; - this.dbSession = dbSession; + if (!StringUtils.equals(existingComponent.moduleUuid(), newComponent.moduleUuid())) { + isUpdated = true; + } + if (!existingComponent.moduleUuidPath().equals(newComponent.moduleUuidPath())) { + isUpdated = true; + } + if (!ObjectUtils.equals(existingComponent.parentProjectId(), newComponent.parentProjectId())) { + isUpdated = true; + } + return isUpdated; } } + private static String getFileQualifier(BatchReport.Component reportComponent) { + return !reportComponent.getIsTest() ? Qualifiers.FILE : Qualifiers.UNIT_TEST_FILE; + } + @Override public String getDescription() { return "Feed components cache"; 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 7dcbc82c07d..282076d0b6f 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 @@ -39,7 +39,9 @@ import org.sonar.core.persistence.MyBatis; import org.sonar.core.source.db.FileSourceDto; import org.sonar.core.source.db.FileSourceDto.Type; import org.sonar.server.computation.ComputationContext; +import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.source.ReportIterator; import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDb; @@ -69,200 +71,197 @@ public class PersistTestsStep implements ComputationStep { } @Override - public void execute(ComputationContext computationContext) { + public void execute(final ComputationContext context) { DbSession session = dbClient.openSession(true); try { - int rootComponentRef = computationContext.getReportMetadata().getRootComponentRef(); - TestContext context = new TestContext(computationContext, session, dbComponentsRefCache); - - recursivelyProcessComponent(context, rootComponentRef); + TestDepthTraversalTypeAwareVisitor visitor = new TestDepthTraversalTypeAwareVisitor(context, session, dbComponentsRefCache); + visitor.visit(context.getRoot()); session.commit(); - if (context.hasUnprocessedCoverageDetails) { - LOG.warn("Some coverage tests are not taken into account during analysis of project '{}'", computationContext.getProjectKey()); + if (visitor.hasUnprocessedCoverageDetails) { + String projectKey = dbComponentsRefCache.getByRef(context.getReportMetadata().getRootComponentRef()).getKey(); + LOG.warn("Some coverage tests are not taken into account during analysis of project '{}'", projectKey); } } finally { MyBatis.closeQuietly(session); } } - private void recursivelyProcessComponent(TestContext context, int componentRef) { - BatchReportReader reportReader = context.reader; - BatchReport.Component component = reportReader.readComponent(componentRef); - if (component.getIsTest()) { - persistTestResults(component, context); - } - - for (Integer childRef : component.getChildRefList()) { - recursivelyProcessComponent(context, childRef); - } + @Override + public String getDescription() { + return "Persist tests"; } - private void persistTestResults(BatchReport.Component component, TestContext context) { - Multimap testsByName = buildDbTests(context, component); - Table coveredFilesByName = loadCoverageDetails(component.getRef(), context); - List tests = addCoveredFilesToTests(testsByName, coveredFilesByName); - if (checkIfThereAreUnprocessedCoverageDetails(testsByName, coveredFilesByName, component)) { - context.hasUnprocessedCoverageDetails = true; - } - - if (tests.isEmpty()) { - return; - } + private class TestDepthTraversalTypeAwareVisitor extends DepthTraversalTypeAwareVisitor { + final DbSession session; + final BatchReportReader reader; + final DbComponentsRefCache dbComponentsRefCache; + final Map existingFileSourcesByUuid; + final String projectUuid; + boolean hasUnprocessedCoverageDetails = false; - String componentUuid = context.getUuid(component.getRef()); - FileSourceDto existingDto = context.existingFileSourcesByUuid.get(componentUuid); - long now = system.now(); - if (existingDto != null) { - // update - existingDto - .setTestData(tests) - .setUpdatedAt(now); - dbClient.fileSourceDao().update(context.session, existingDto); - } else { - // insert - FileSourceDto newDto = new FileSourceDto() - .setTestData(tests) - .setFileUuid(componentUuid) - .setProjectUuid(context.getUuid(context.context.getReportMetadata().getRootComponentRef())) - .setDataType(Type.TEST) - .setCreatedAt(now) - .setUpdatedAt(now); - dbClient.fileSourceDao().insert(context.session, newDto); + public TestDepthTraversalTypeAwareVisitor(ComputationContext context, DbSession session, DbComponentsRefCache dbComponentsRefCache) { + super(Component.Type.FILE, Order.PRE_ORDER); + this.session = session; + this.dbComponentsRefCache = dbComponentsRefCache; + this.reader = context.getReportReader(); + this.existingFileSourcesByUuid = new HashMap<>(); + this.projectUuid = context.getRoot().getUuid(); + session.select("org.sonar.core.source.db.FileSourceMapper.selectHashesForProject", + ImmutableMap.of("projectUuid", context.getRoot().getUuid(), "dataType", Type.TEST), + new ResultHandler() { + @Override + public void handleResult(ResultContext context) { + FileSourceDto dto = (FileSourceDto) context.getResultObject(); + existingFileSourcesByUuid.put(dto.getFileUuid(), dto); + } + }); } - } - private boolean checkIfThereAreUnprocessedCoverageDetails(Multimap testsByName, - Table coveredFilesByName, - BatchReport.Component component) { - Set unprocessedCoverageDetailNames = new HashSet<>(coveredFilesByName.rowKeySet()); - unprocessedCoverageDetailNames.removeAll(testsByName.keySet()); - boolean hasUnprocessedCoverageDetails = !unprocessedCoverageDetailNames.isEmpty(); - if (hasUnprocessedCoverageDetails) { - LOG.trace("The following test coverages for file '{}' have not been taken into account: {}", component.getPath(), Joiner.on(", ").join(unprocessedCoverageDetailNames)); + @Override + public void visitFile(Component file) { + BatchReport.Component batchComponent = reader.readComponent(file.getRef()); + if (batchComponent.getIsTest()) { + persistTestResults(batchComponent); + } } - return hasUnprocessedCoverageDetails; - } - private List addCoveredFilesToTests(Multimap testsByName, - Table coveredFilesByName) { - List tests = new ArrayList<>(); - for (FileSourceDb.Test.Builder test : testsByName.values()) { - Collection coveredFiles = coveredFilesByName.row(test.getName()).values(); - if (!coveredFiles.isEmpty()) { - for (FileSourceDb.Test.CoveredFile.Builder coveredFile : coveredFiles) { - test.addCoveredFile(coveredFile); - } + private void persistTestResults(BatchReport.Component component) { + Multimap testsByName = buildDbTests(component); + Table coveredFilesByName = loadCoverageDetails(component.getRef()); + List tests = addCoveredFilesToTests(testsByName, coveredFilesByName); + if (checkIfThereAreUnprocessedCoverageDetails(testsByName, coveredFilesByName, component)) { + hasUnprocessedCoverageDetails = true; } - tests.add(test.build()); - } - return tests; - } + if (tests.isEmpty()) { + return; + } - private Multimap buildDbTests(TestContext context, BatchReport.Component component) { - Multimap tests = ArrayListMultimap.create(); - File testsFile = context.reader.readTests(component.getRef()); - if (testsFile == null) { - return tests; + String componentUuid = getUuid(component.getRef()); + FileSourceDto existingDto = existingFileSourcesByUuid.get(componentUuid); + long now = system.now(); + if (existingDto != null) { + // update + existingDto + .setTestData(tests) + .setUpdatedAt(now); + dbClient.fileSourceDao().update(session, existingDto); + } else { + // insert + FileSourceDto newDto = new FileSourceDto() + .setTestData(tests) + .setFileUuid(componentUuid) + .setProjectUuid(projectUuid) + .setDataType(Type.TEST) + .setCreatedAt(now) + .setUpdatedAt(now); + dbClient.fileSourceDao().insert(session, newDto); + } } - ReportIterator testIterator = new ReportIterator<>(testsFile, BatchReport.Test.PARSER); - try { - while (testIterator.hasNext()) { - BatchReport.Test batchTest = testIterator.next(); - FileSourceDb.Test.Builder dbTest = FileSourceDb.Test.newBuilder(); - dbTest.setUuid(Uuids.create()); - dbTest.setName(batchTest.getName()); - if (batchTest.hasStacktrace()) { - dbTest.setStacktrace(batchTest.getStacktrace()); - } - if (batchTest.hasStatus()) { - dbTest.setStatus(TestStatus.valueOf(batchTest.getStatus().name())); - } - if (batchTest.hasMsg()) { - dbTest.setMsg(batchTest.getMsg()); - } - if (batchTest.hasDurationInMs()) { - dbTest.setExecutionTimeMs(batchTest.getDurationInMs()); - } - tests.put(dbTest.getName(), dbTest); + private boolean checkIfThereAreUnprocessedCoverageDetails(Multimap testsByName, + Table coveredFilesByName, + BatchReport.Component component) { + Set unprocessedCoverageDetailNames = new HashSet<>(coveredFilesByName.rowKeySet()); + unprocessedCoverageDetailNames.removeAll(testsByName.keySet()); + boolean hasUnprocessedCoverageDetails = !unprocessedCoverageDetailNames.isEmpty(); + if (hasUnprocessedCoverageDetails) { + LOG.trace("The following test coverages for file '{}' have not been taken into account: {}", component.getPath(), Joiner.on(", ").join(unprocessedCoverageDetailNames)); } - } finally { - testIterator.close(); + return hasUnprocessedCoverageDetails; } - return tests; - } + private List addCoveredFilesToTests(Multimap testsByName, + Table coveredFilesByName) { + List tests = new ArrayList<>(); + for (FileSourceDb.Test.Builder test : testsByName.values()) { + Collection coveredFiles = coveredFilesByName.row(test.getName()).values(); + if (!coveredFiles.isEmpty()) { + for (FileSourceDb.Test.CoveredFile.Builder coveredFile : coveredFiles) { + test.addCoveredFile(coveredFile); + } + } + tests.add(test.build()); + } - /** - * returns a Table of (test name, main file uuid, covered file) - */ - private Table loadCoverageDetails(int testFileRef, TestContext context) { - Table nameToCoveredFiles = HashBasedTable.create(); - File coverageDetailsFile = context.reader.readCoverageDetails(testFileRef); - if (coverageDetailsFile == null) { - return nameToCoveredFiles; + return tests; } - ReportIterator coverageIterator = new ReportIterator<>(coverageDetailsFile, BatchReport.CoverageDetail.PARSER); - try { - while (coverageIterator.hasNext()) { - BatchReport.CoverageDetail batchCoverageDetail = coverageIterator.next(); - for (BatchReport.CoverageDetail.CoveredFile batchCoveredFile : batchCoverageDetail.getCoveredFileList()) { - String testName = batchCoverageDetail.getTestName(); - String mainFileUuid = context.getUuid(batchCoveredFile.getFileRef()); - FileSourceDb.Test.CoveredFile.Builder existingDbCoveredFile = nameToCoveredFiles.get(testName, mainFileUuid); - List batchCoveredLines = batchCoveredFile.getCoveredLineList(); - if (existingDbCoveredFile == null) { - FileSourceDb.Test.CoveredFile.Builder dbCoveredFile = FileSourceDb.Test.CoveredFile.newBuilder() - .setFileUuid(context.getUuid(batchCoveredFile.getFileRef())) - .addAllCoveredLine(batchCoveredLines); - nameToCoveredFiles.put(testName, mainFileUuid, dbCoveredFile); - } else { - List remainingBatchCoveredLines = new ArrayList<>(batchCoveredLines); - remainingBatchCoveredLines.removeAll(existingDbCoveredFile.getCoveredLineList()); - existingDbCoveredFile.addAllCoveredLine(batchCoveredLines); + private Multimap buildDbTests(BatchReport.Component component) { + Multimap tests = ArrayListMultimap.create(); + File testsFile = reader.readTests(component.getRef()); + if (testsFile == null) { + return tests; + } + ReportIterator testIterator = new ReportIterator<>(testsFile, BatchReport.Test.PARSER); + try { + while (testIterator.hasNext()) { + BatchReport.Test batchTest = testIterator.next(); + FileSourceDb.Test.Builder dbTest = FileSourceDb.Test.newBuilder(); + dbTest.setUuid(Uuids.create()); + dbTest.setName(batchTest.getName()); + if (batchTest.hasStacktrace()) { + dbTest.setStacktrace(batchTest.getStacktrace()); + } + if (batchTest.hasStatus()) { + dbTest.setStatus(TestStatus.valueOf(batchTest.getStatus().name())); + } + if (batchTest.hasMsg()) { + dbTest.setMsg(batchTest.getMsg()); + } + if (batchTest.hasDurationInMs()) { + dbTest.setExecutionTimeMs(batchTest.getDurationInMs()); } + + tests.put(dbTest.getName(), dbTest); } + } finally { + testIterator.close(); } - } finally { - coverageIterator.close(); - } - return nameToCoveredFiles; - } - @Override - public String getDescription() { - return "Persist tests"; - } + return tests; + } - private static class TestContext { - final DbSession session; - final ComputationContext context; - final BatchReportReader reader; - final DbComponentsRefCache dbComponentsRefCache; - final Map existingFileSourcesByUuid; - boolean hasUnprocessedCoverageDetails = false; + /** + * returns a Table of (test name, main file uuid, covered file) + */ + private Table loadCoverageDetails(int testFileRef) { + Table nameToCoveredFiles = HashBasedTable.create(); + File coverageDetailsFile = reader.readCoverageDetails(testFileRef); + if (coverageDetailsFile == null) { + return nameToCoveredFiles; + } - TestContext(ComputationContext context, DbSession session, DbComponentsRefCache dbComponentsRefCache) { - this.session = session; - this.context = context; - this.dbComponentsRefCache = dbComponentsRefCache; - this.reader = context.getReportReader(); - this.existingFileSourcesByUuid = new HashMap<>(); - session.select("org.sonar.core.source.db.FileSourceMapper.selectHashesForProject", - ImmutableMap.of("projectUuid", dbComponentsRefCache.getByRef(context.getReportMetadata().getRootComponentRef()).getUuid(), "dataType", Type.TEST), - new ResultHandler() { - @Override - public void handleResult(ResultContext context) { - FileSourceDto dto = (FileSourceDto) context.getResultObject(); - existingFileSourcesByUuid.put(dto.getFileUuid(), dto); + ReportIterator coverageIterator = new ReportIterator<>(coverageDetailsFile, BatchReport.CoverageDetail.PARSER); + try { + while (coverageIterator.hasNext()) { + BatchReport.CoverageDetail batchCoverageDetail = coverageIterator.next(); + for (BatchReport.CoverageDetail.CoveredFile batchCoveredFile : batchCoverageDetail.getCoveredFileList()) { + String testName = batchCoverageDetail.getTestName(); + String mainFileUuid = getUuid(batchCoveredFile.getFileRef()); + FileSourceDb.Test.CoveredFile.Builder existingDbCoveredFile = nameToCoveredFiles.get(testName, mainFileUuid); + List batchCoveredLines = batchCoveredFile.getCoveredLineList(); + if (existingDbCoveredFile == null) { + FileSourceDb.Test.CoveredFile.Builder dbCoveredFile = FileSourceDb.Test.CoveredFile.newBuilder() + .setFileUuid(getUuid(batchCoveredFile.getFileRef())) + .addAllCoveredLine(batchCoveredLines); + nameToCoveredFiles.put(testName, mainFileUuid, dbCoveredFile); + } else { + List remainingBatchCoveredLines = new ArrayList<>(batchCoveredLines); + remainingBatchCoveredLines.removeAll(existingDbCoveredFile.getCoveredLineList()); + existingDbCoveredFile.addAllCoveredLine(batchCoveredLines); + } } - }); + } + } finally { + coverageIterator.close(); + } + return nameToCoveredFiles; } - public String getUuid(int fileRef) { + private String getUuid(int fileRef) { return dbComponentsRefCache.getByRef(fileRef).getUuid(); } } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/ComputeComponentsRefCacheTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/ComputeComponentsRefCacheTest.java deleted file mode 100644 index 92e6ff517a9..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/ComputeComponentsRefCacheTest.java +++ /dev/null @@ -1,44 +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.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ComputeComponentsRefCacheTest { - - @Test - public void add_and_get_component() throws Exception { - ComputeComponentsRefCache cache = new ComputeComponentsRefCache(); - cache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent("Key", "Uuid")); - - assertThat(cache.getByRef(1)).isNotNull(); - assertThat(cache.getByRef(1).getKey()).isEqualTo("Key"); - assertThat(cache.getByRef(1).getUuid()).isEqualTo("Uuid"); - } - - @Test(expected = IllegalArgumentException.class) - public void fail_on_unknown_ref() throws Exception { - new ComputeComponentsRefCache().getByRef(1); - } - -} 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 ca5a03ccb66..f9edbb04bf8 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 @@ -20,17 +20,19 @@ package org.sonar.server.computation.component; import com.google.common.collect.ImmutableList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; import org.sonar.server.computation.context.ComputationContext; import org.sonar.server.computation.event.EventRepository; import org.sonar.server.computation.measure.MeasureRepository; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + public class DumbComponent implements Component { - public static final Component DUMB_PROJECT = new DumbComponent(Type.PROJECT, 1); + public static final Component DUMB_PROJECT = new DumbComponent(Type.PROJECT, 1, "PROJECT_KEY", "PROJECT_UUID"); private static final String UNSUPPORTED_OPERATION_ERROR = "This node has no repository nor context"; @@ -38,16 +40,20 @@ public class DumbComponent implements Component { private final ComputationContext context; private final Type type; private final int ref; + private final String uuid; + private final String key; private final List children; - public DumbComponent(Type type, int ref, @Nullable Component... children) { - this(null, type, ref, children); + public DumbComponent(Type type, int ref, String uuid, String key, @Nullable Component... children) { + this(null, type, ref, uuid, key, children); } - public DumbComponent(@Nullable ComputationContext context, Type type, int ref, @Nullable Component... children) { + public DumbComponent(@Nullable ComputationContext context, Type type, int ref, String uuid, String key, @Nullable Component... children) { this.context = context; this.type = type; this.ref = ref; + this.uuid = uuid; + this.key = key; this.children = children == null ? Collections.emptyList() : ImmutableList.copyOf(Arrays.asList(children)); } @@ -56,6 +62,16 @@ public class DumbComponent implements Component { return type; } + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getKey() { + return key; + } + @Override public int getRef() { return ref; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java index e82c3794e97..d89444f2770 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java @@ -49,14 +49,14 @@ public class ComputationStepsTest { mock(PersistFileSourcesStep.class), mock(PersistTestsStep.class), mock(IndexTestsStep.class), - mock(FeedComponentsCacheStep.class), + mock(FeedComponentUuidsStep.class), mock(PersistComponentsStep.class), mock(IndexTestsStep.class), mock(QualityProfileEventsStep.class) ); assertThat(registry.orderedSteps()).hasSize(20); - assertThat(registry.orderedSteps().get(0)).isInstanceOf(FeedComponentsCacheStep.class); + assertThat(registry.orderedSteps().get(0)).isInstanceOf(FeedComponentUuidsStep.class); assertThat(registry.orderedSteps().get(19)).isInstanceOf(SendIssueNotificationsStep.class); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedComponentUuidsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedComponentUuidsStepTest.java new file mode 100644 index 00000000000..fb92b615286 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedComponentUuidsStepTest.java @@ -0,0 +1,257 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.computation.step; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.config.Settings; +import org.sonar.batch.protocol.Constants; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.batch.protocol.output.BatchReportReader; +import org.sonar.batch.protocol.output.BatchReportWriter; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.computation.ComputationContext; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.ComponentTreeBuilders; +import org.sonar.server.computation.language.LanguageRepository; +import org.sonar.server.db.DbClient; +import org.sonar.test.DbTests; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +@Category(DbTests.class) +public class FeedComponentUuidsStepTest extends BaseStepTest { + + private static final String PROJECT_KEY = "PROJECT_KEY"; + + @ClassRule + public static DbTester dbTester = new DbTester(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + File reportDir; + + DbSession session; + + DbClient dbClient; + + Settings projectSettings; + + LanguageRepository languageRepository = mock(LanguageRepository.class); + + FeedComponentUuidsStep sut; + + @Before + public void setup() throws Exception { + dbTester.truncateTables(); + session = dbTester.myBatis().openSession(false); + dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao()); + + reportDir = temp.newFolder(); + + projectSettings = new Settings(); + sut = new FeedComponentUuidsStep(dbClient); + } + + @Override + protected ComputationStep step() { + return sut; + } + + @Test + public void add_components() throws Exception { + File reportDir = temp.newFolder(); + BatchReportWriter writer = new BatchReportWriter(reportDir); + writer.writeMetadata(BatchReport.Metadata.newBuilder() + .setRootComponentRef(1) + .build()); + + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(1) + .setType(Constants.ComponentType.PROJECT) + .setKey(PROJECT_KEY) + .addChildRef(2) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(2) + .setType(Constants.ComponentType.MODULE) + .setKey("MODULE_KEY") + .addChildRef(3) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(3) + .setType(Constants.ComponentType.DIRECTORY) + .setPath("src/main/java/dir") + .addChildRef(4) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(4) + .setType(Constants.ComponentType.FILE) + .setPath("src/main/java/dir/Foo.java") + .build()); + + BatchReportReader batchReportReader = new BatchReportReader(reportDir); + ComputationContext context = new ComputationContext(batchReportReader, PROJECT_KEY, projectSettings, + dbClient, ComponentTreeBuilders.from(batchReportReader), languageRepository); + sut.execute(context); + + Map componentsByRef = getComponentsByRef(context.getRoot()); + + assertThat(componentsByRef.get(1).getKey()).isEqualTo(PROJECT_KEY); + assertThat(componentsByRef.get(1).getUuid()).isNotNull(); + + assertThat(componentsByRef.get(2).getKey()).isEqualTo("MODULE_KEY"); + assertThat(componentsByRef.get(2).getUuid()).isNotNull(); + + assertThat(componentsByRef.get(3).getKey()).isEqualTo("MODULE_KEY:src/main/java/dir"); + assertThat(componentsByRef.get(3).getUuid()).isNotNull(); + + assertThat(componentsByRef.get(4).getKey()).isEqualTo("MODULE_KEY:src/main/java/dir/Foo.java"); + assertThat(componentsByRef.get(4).getUuid()).isNotNull(); + } + + @Test + public void use_latest_module_for_files_key() throws Exception { + File reportDir = temp.newFolder(); + BatchReportWriter writer = new BatchReportWriter(reportDir); + writer.writeMetadata(BatchReport.Metadata.newBuilder() + .setRootComponentRef(1) + .build()); + + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(1) + .setType(Constants.ComponentType.PROJECT) + .setKey(PROJECT_KEY) + .setName("Project") + .addChildRef(2) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(2) + .setType(Constants.ComponentType.MODULE) + .setKey("MODULE_KEY") + .setName("Module") + .addChildRef(3) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(3) + .setType(Constants.ComponentType.MODULE) + .setKey("SUB_MODULE_KEY") + .setName("Sub Module") + .addChildRef(4) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(4) + .setType(Constants.ComponentType.DIRECTORY) + .setPath("src/main/java/dir") + .addChildRef(5) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(5) + .setType(Constants.ComponentType.FILE) + .setPath("src/main/java/dir/Foo.java") + .build()); + + BatchReportReader batchReportReader = new BatchReportReader(reportDir); + ComputationContext context = new ComputationContext(batchReportReader, PROJECT_KEY, projectSettings, + dbClient, ComponentTreeBuilders.from(batchReportReader), languageRepository); + sut.execute(context); + + Map componentsByRef = getComponentsByRef(context.getRoot()); + + assertThat(componentsByRef.get(4).getKey()).isEqualTo("SUB_MODULE_KEY:src/main/java/dir"); + assertThat(componentsByRef.get(5).getKey()).isEqualTo("SUB_MODULE_KEY:src/main/java/dir/Foo.java"); + } + + @Test + public void use_branch_to_generate_keys() throws Exception { + File reportDir = temp.newFolder(); + BatchReportWriter writer = new BatchReportWriter(reportDir); + writer.writeMetadata(BatchReport.Metadata.newBuilder() + .setRootComponentRef(1) + .setBranch("origin/master") + .setProjectKey("") + .build()); + + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(1) + .setType(Constants.ComponentType.PROJECT) + .setKey(PROJECT_KEY) + .setName("Project") + .addChildRef(2) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(2) + .setType(Constants.ComponentType.MODULE) + .setKey("MODULE_KEY") + .setName("Module") + .addChildRef(3) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(3) + .setType(Constants.ComponentType.DIRECTORY) + .setPath("src/main/java/dir") + .addChildRef(4) + .build()); + writer.writeComponent(BatchReport.Component.newBuilder() + .setRef(4) + .setType(Constants.ComponentType.FILE) + .setPath("src/main/java/dir/Foo.java") + .build()); + + BatchReportReader batchReportReader = new BatchReportReader(reportDir); + ComputationContext context = new ComputationContext(batchReportReader, PROJECT_KEY, projectSettings, + dbClient, ComponentTreeBuilders.from(batchReportReader), languageRepository); + sut.execute(context); + + Map componentsByRef = getComponentsByRef(context.getRoot()); + + assertThat(componentsByRef.get(1).getKey()).isEqualTo("PROJECT_KEY:origin/master"); + assertThat(componentsByRef.get(2).getKey()).isEqualTo("MODULE_KEY:origin/master"); + assertThat(componentsByRef.get(3).getKey()).isEqualTo("MODULE_KEY:origin/master:src/main/java/dir"); + assertThat(componentsByRef.get(4).getKey()).isEqualTo("MODULE_KEY:origin/master:src/main/java/dir/Foo.java"); + } + + private static Map getComponentsByRef(Component root) { + Map componentsByRef = new HashMap<>(); + feedComponentByRef(root, componentsByRef); + return componentsByRef; + } + + private static void feedComponentByRef(Component component, Map map) { + map.put(component.getRef(), component); + for (Component child : component.getChildren()) { + feedComponentByRef(child, map); + } + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedComponentsCacheStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedComponentsCacheStepTest.java deleted file mode 100644 index 820ae78b5bf..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedComponentsCacheStepTest.java +++ /dev/null @@ -1,233 +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 java.io.File; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.config.Settings; -import org.sonar.batch.protocol.Constants; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.batch.protocol.output.BatchReportReader; -import org.sonar.batch.protocol.output.BatchReportWriter; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.DbTester; -import org.sonar.server.component.db.ComponentDao; -import org.sonar.server.computation.ComputationContext; -import org.sonar.server.computation.component.ComponentTreeBuilders; -import org.sonar.server.computation.component.ComputeComponentsRefCache; -import org.sonar.server.computation.component.DumbComponent; -import org.sonar.server.computation.language.LanguageRepository; -import org.sonar.server.db.DbClient; -import org.sonar.test.DbTests; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -@Category(DbTests.class) -public class FeedComponentsCacheStepTest extends BaseStepTest { - - private static final String PROJECT_KEY = "PROJECT_KEY"; - - @ClassRule - public static DbTester dbTester = new DbTester(); - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - File reportDir; - - DbSession session; - - DbClient dbClient; - - Settings projectSettings; - - LanguageRepository languageRepository = mock(LanguageRepository.class); - - ComputeComponentsRefCache computeComponentsRefCache; - - FeedComponentsCacheStep sut; - - @Before - public void setup() throws Exception { - dbTester.truncateTables(); - session = dbTester.myBatis().openSession(false); - dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao()); - - reportDir = temp.newFolder(); - - computeComponentsRefCache = new ComputeComponentsRefCache(); - projectSettings = new Settings(); - sut = new FeedComponentsCacheStep(dbClient, computeComponentsRefCache); - } - - @Override - protected ComputationStep step() { - return sut; - } - - @Test - public void add_components() throws Exception { - File reportDir = temp.newFolder(); - BatchReportWriter writer = new BatchReportWriter(reportDir); - writer.writeMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(1) - .setType(Constants.ComponentType.PROJECT) - .setKey(PROJECT_KEY) - .addChildRef(2) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(2) - .setType(Constants.ComponentType.MODULE) - .setKey("MODULE_KEY") - .addChildRef(3) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(3) - .setType(Constants.ComponentType.DIRECTORY) - .setPath("src/main/java/dir") - .addChildRef(4) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(4) - .setType(Constants.ComponentType.FILE) - .setPath("src/main/java/dir/Foo.java") - .build()); - - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); - - assertThat(computeComponentsRefCache.getByRef(1).getKey()).isEqualTo(PROJECT_KEY); - assertThat(computeComponentsRefCache.getByRef(1).getUuid()).isNotNull(); - - assertThat(computeComponentsRefCache.getByRef(2).getKey()).isEqualTo("MODULE_KEY"); - assertThat(computeComponentsRefCache.getByRef(2).getUuid()).isNotNull(); - - assertThat(computeComponentsRefCache.getByRef(3).getKey()).isEqualTo("MODULE_KEY:src/main/java/dir"); - assertThat(computeComponentsRefCache.getByRef(3).getUuid()).isNotNull(); - - assertThat(computeComponentsRefCache.getByRef(4).getKey()).isEqualTo("MODULE_KEY:src/main/java/dir/Foo.java"); - assertThat(computeComponentsRefCache.getByRef(4).getUuid()).isNotNull(); - } - - @Test - public void use_latest_module_for_files_key() throws Exception { - File reportDir = temp.newFolder(); - BatchReportWriter writer = new BatchReportWriter(reportDir); - writer.writeMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .build()); - - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(1) - .setType(Constants.ComponentType.PROJECT) - .setKey(PROJECT_KEY) - .setName("Project") - .addChildRef(2) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(2) - .setType(Constants.ComponentType.MODULE) - .setKey("MODULE_KEY") - .setName("Module") - .addChildRef(3) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(3) - .setType(Constants.ComponentType.MODULE) - .setKey("SUB_MODULE_KEY") - .setName("Sub Module") - .addChildRef(4) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(4) - .setType(Constants.ComponentType.DIRECTORY) - .setPath("src/main/java/dir") - .addChildRef(5) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(5) - .setType(Constants.ComponentType.FILE) - .setPath("src/main/java/dir/Foo.java") - .build()); - - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); - - assertThat(computeComponentsRefCache.getByRef(4).getKey()).isEqualTo("SUB_MODULE_KEY:src/main/java/dir"); - assertThat(computeComponentsRefCache.getByRef(5).getKey()).isEqualTo("SUB_MODULE_KEY:src/main/java/dir/Foo.java"); - } - - @Test - public void use_branch_to_generate_keys() throws Exception { - File reportDir = temp.newFolder(); - BatchReportWriter writer = new BatchReportWriter(reportDir); - writer.writeMetadata(BatchReport.Metadata.newBuilder() - .setRootComponentRef(1) - .setBranch("origin/master") - .build()); - - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(1) - .setType(Constants.ComponentType.PROJECT) - .setKey(PROJECT_KEY) - .setName("Project") - .addChildRef(2) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(2) - .setType(Constants.ComponentType.MODULE) - .setKey("MODULE_KEY") - .setName("Module") - .addChildRef(3) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(3) - .setType(Constants.ComponentType.DIRECTORY) - .setPath("src/main/java/dir") - .addChildRef(4) - .build()); - writer.writeComponent(BatchReport.Component.newBuilder() - .setRef(4) - .setType(Constants.ComponentType.FILE) - .setPath("src/main/java/dir/Foo.java") - .build()); - - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); - - assertThat(computeComponentsRefCache.getByRef(1).getKey()).isEqualTo("PROJECT_KEY:origin/master"); - assertThat(computeComponentsRefCache.getByRef(2).getKey()).isEqualTo("MODULE_KEY:origin/master"); - assertThat(computeComponentsRefCache.getByRef(3).getKey()).isEqualTo("MODULE_KEY:origin/master:src/main/java/dir"); - assertThat(computeComponentsRefCache.getByRef(4).getKey()).isEqualTo("MODULE_KEY:origin/master:src/main/java/dir/Foo.java"); - } - - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java index 23b9764c80b..220de7f9f63 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java @@ -30,18 +30,18 @@ import org.sonar.batch.protocol.output.BatchReportReader; import org.sonar.batch.protocol.output.BatchReportWriter; import org.sonar.core.persistence.DbTester; import org.sonar.server.computation.ComputationContext; +import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.ComponentTreeBuilders; import org.sonar.server.computation.component.DumbComponent; -import org.sonar.server.computation.component.ComputeComponentsRefCache; import org.sonar.server.computation.issue.IssueComputation; +import org.sonar.server.computation.language.LanguageRepository; +import org.sonar.server.db.DbClient; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.sonar.server.computation.language.LanguageRepository; -import org.sonar.server.db.DbClient; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -62,20 +62,19 @@ public class ParseReportStepTest extends BaseStepTest { public static DbTester dbTester = new DbTester(); IssueComputation issueComputation = mock(IssueComputation.class); - ComputeComponentsRefCache computeComponentsRefCache = new ComputeComponentsRefCache(); - ParseReportStep sut = new ParseReportStep(issueComputation, computeComponentsRefCache); + ParseReportStep sut = new ParseReportStep(issueComputation); @Test public void extract_report_from_db_and_browse_components() throws Exception { - computeComponentsRefCache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent(PROJECT_KEY, "PROJECT_UUID")); - computeComponentsRefCache.addComponent(2, new ComputeComponentsRefCache.ComputeComponent("PROJECT_KEY:file1", "FILE1_UUID")); - computeComponentsRefCache.addComponent(3, new ComputeComponentsRefCache.ComputeComponent("PROJECT_KEY:file2", "FILE2_UUID")); + DumbComponent root = new DumbComponent(Component.Type.PROJECT, 1, "PROJECT_UUID", PROJECT_KEY, + new DumbComponent(Component.Type.FILE, 2, "FILE1_UUID", "PROJECT_KEY:file1"), + new DumbComponent(Component.Type.FILE, 3, "FILE2_UUID", "PROJECT_KEY:file2")); File reportDir = generateReport(); ComputationContext context = new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, new Settings(), - mock(DbClient.class), ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), mock(LanguageRepository.class)); + mock(DbClient.class), ComponentTreeBuilders.from(root), mock(LanguageRepository.class)); sut.execute(context); assertThat(context.getReportMetadata().getRootComponentRef()).isEqualTo(1); @@ -95,8 +94,6 @@ public class ParseReportStepTest extends BaseStepTest { BatchReportWriter writer = new BatchReportWriter(dir); writer.writeMetadata(BatchReport.Metadata.newBuilder() .setRootComponentRef(1) - .setProjectKey(PROJECT_KEY) - .setAnalysisDate(150000000L) .setDeletedComponentsCount(1) .build()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsStepTest.java index 12aa751a461..e63df67eb77 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsStepTest.java @@ -20,7 +20,6 @@ package org.sonar.server.computation.step; -import java.io.File; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; @@ -39,14 +38,16 @@ import org.sonar.core.persistence.DbTester; import org.sonar.server.component.ComponentTesting; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.computation.ComputationContext; +import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.ComponentTreeBuilders; -import org.sonar.server.computation.component.ComputeComponentsRefCache; import org.sonar.server.computation.component.DbComponentsRefCache; import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.language.LanguageRepository; import org.sonar.server.db.DbClient; import org.sonar.test.DbTests; +import java.io.File; + import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -69,7 +70,6 @@ public class PersistComponentsStepTest extends BaseStepTest { Settings projectSettings; LanguageRepository languageRepository; - ComputeComponentsRefCache computeComponentsRefCache; DbComponentsRefCache dbComponentsRefCache; PersistComponentsStep sut; @@ -82,11 +82,10 @@ public class PersistComponentsStepTest extends BaseStepTest { reportDir = temp.newFolder(); - computeComponentsRefCache = new ComputeComponentsRefCache(); projectSettings = new Settings(); languageRepository = mock(LanguageRepository.class); dbComponentsRefCache = new DbComponentsRefCache(); - sut = new PersistComponentsStep(dbClient, computeComponentsRefCache, dbComponentsRefCache); + sut = new PersistComponentsStep(dbClient, dbComponentsRefCache); } @Override @@ -101,11 +100,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void persist_components() throws Exception { - computeComponentsRefCache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent(PROJECT_KEY, "ABCD")); - computeComponentsRefCache.addComponent(2, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY", "BCDE")); - computeComponentsRefCache.addComponent(3, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY:src/main/java/dir", "CDEF")); - computeComponentsRefCache.addComponent(4, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY:src/main/java/dir/Foo.java", "DEFG")); - File reportDir = temp.newFolder(); BatchReportWriter writer = new BatchReportWriter(reportDir); writer.writeMetadata(BatchReport.Metadata.newBuilder() @@ -141,8 +135,12 @@ public class PersistComponentsStepTest extends BaseStepTest { .setLanguage("java") .build()); + DumbComponent root = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, + new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY", + new DumbComponent(Component.Type.DIRECTORY, 3, "CDEF", "MODULE_KEY:src/main/java/dir", + new DumbComponent(Component.Type.FILE, 4, "DEFG", "MODULE_KEY:src/main/java/dir/Foo.java")))); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + dbClient, ComponentTreeBuilders.from(root), languageRepository)); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4); @@ -203,10 +201,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void persist_file_directly_attached_on_root_directory() throws Exception { - computeComponentsRefCache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent(PROJECT_KEY, "ABCD")); - computeComponentsRefCache.addComponent(2, new ComputeComponentsRefCache.ComputeComponent("PROJECT_KEY:/", "CDEF")); - computeComponentsRefCache.addComponent(3, new ComputeComponentsRefCache.ComputeComponent("PROJECT_KEY:pom.xml", "DEFG")); - File reportDir = temp.newFolder(); BatchReportWriter writer = new BatchReportWriter(reportDir); writer.writeMetadata(BatchReport.Metadata.newBuilder() @@ -232,8 +226,11 @@ public class PersistComponentsStepTest extends BaseStepTest { .setPath("pom.xml") .build()); + DumbComponent root = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, + new DumbComponent(Component.Type.DIRECTORY, 2, "CDEF", PROJECT_KEY + ":/", + new DumbComponent(Component.Type.FILE, 3, "DEFG", PROJECT_KEY + ":pom.xml"))); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + dbClient, ComponentTreeBuilders.from(root), languageRepository)); ComponentDto directory = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY:/"); assertThat(directory).isNotNull(); @@ -248,10 +245,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void persist_unit_test() throws Exception { - computeComponentsRefCache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent(PROJECT_KEY, "ABCD")); - computeComponentsRefCache.addComponent(2, new ComputeComponentsRefCache.ComputeComponent("PROJECT_KEY:src/test/java/dir", "CDEF")); - computeComponentsRefCache.addComponent(3, new ComputeComponentsRefCache.ComputeComponent("PROJECT_KEY:src/test/java/dir/FooTest.java", "DEFG")); - File reportDir = temp.newFolder(); BatchReportWriter writer = new BatchReportWriter(reportDir); writer.writeMetadata(BatchReport.Metadata.newBuilder() @@ -278,10 +271,13 @@ public class PersistComponentsStepTest extends BaseStepTest { .setIsTest(true) .build()); + DumbComponent root = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, + new DumbComponent(Component.Type.DIRECTORY, 2, "CDEF", PROJECT_KEY + ":src/test/java/dir", + new DumbComponent(Component.Type.FILE, 3, "DEFG", PROJECT_KEY + ":src/test/java/dir/FooTest.java"))); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + dbClient, ComponentTreeBuilders.from(root), languageRepository)); - ComponentDto file = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY:src/test/java/dir/FooTest.java"); + ComponentDto file = dbClient.componentDao().selectNullableByKey(session, PROJECT_KEY + ":src/test/java/dir/FooTest.java"); assertThat(file).isNotNull(); assertThat(file.name()).isEqualTo("FooTest.java"); assertThat(file.path()).isEqualTo("src/test/java/dir/FooTest.java"); @@ -291,11 +287,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void persist_only_new_components() throws Exception { - computeComponentsRefCache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent(PROJECT_KEY, "ABCD")); - computeComponentsRefCache.addComponent(2, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY", "BCDE")); - computeComponentsRefCache.addComponent(3, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY:src/main/java/dir", "CDEF")); - computeComponentsRefCache.addComponent(4, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY:src/main/java/dir/Foo.java", "DEFG")); - // Project amd module already exists ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey(PROJECT_KEY).setName("Project"); dbClient.componentDao().insert(session, project); @@ -335,8 +326,12 @@ public class PersistComponentsStepTest extends BaseStepTest { .setPath("src/main/java/dir/Foo.java") .build()); + DumbComponent root = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, + new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY", + new DumbComponent(Component.Type.DIRECTORY, 3, "CDEF", "MODULE_KEY:src/main/java/dir", + new DumbComponent(Component.Type.FILE, 4, "DEFG", "MODULE_KEY:src/main/java/dir/Foo.java")))); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + dbClient, ComponentTreeBuilders.from(root), languageRepository)); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4); @@ -369,12 +364,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void compute_parent_project_id() throws Exception { - computeComponentsRefCache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent("PROJECT_KEY", "ABCD")); - computeComponentsRefCache.addComponent(2, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY", "BCDE")); - computeComponentsRefCache.addComponent(3, new ComputeComponentsRefCache.ComputeComponent("SUB_MODULE_1_KEY", "CDEF")); - computeComponentsRefCache.addComponent(4, new ComputeComponentsRefCache.ComputeComponent("SUB_MODULE_2_KEY", "DEFG")); - computeComponentsRefCache.addComponent(5, new ComputeComponentsRefCache.ComputeComponent("SUB_MODULE_2_KEY:src/main/java/dir", "EFGH")); - File reportDir = temp.newFolder(); BatchReportWriter writer = new BatchReportWriter(reportDir); writer.writeMetadata(BatchReport.Metadata.newBuilder() @@ -415,8 +404,13 @@ public class PersistComponentsStepTest extends BaseStepTest { .setPath("src/main/java/dir") .build()); + DumbComponent root = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, + new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY", + new DumbComponent(Component.Type.MODULE, 3, "CDEF", "SUB_MODULE_1_KEY", + new DumbComponent(Component.Type.MODULE, 4, "DEFG", "SUB_MODULE_2_KEY", + new DumbComponent(Component.Type.DIRECTORY, 5, "EFGH", "SUB_MODULE_2_KEY:src/main/java/dir"))))); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + dbClient, ComponentTreeBuilders.from(root), languageRepository)); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(5); @@ -443,11 +437,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void nothing_to_persist() throws Exception { - computeComponentsRefCache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent(PROJECT_KEY, "ABCD")); - computeComponentsRefCache.addComponent(2, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY", "BCDE")); - computeComponentsRefCache.addComponent(3, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY:src/main/java/dir", "CDEF")); - computeComponentsRefCache.addComponent(4, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY:src/main/java/dir/Foo.java", "DEFG")); - ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey(PROJECT_KEY).setName("Project"); dbClient.componentDao().insert(session, project); ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setKey("MODULE_KEY").setName("Module"); @@ -467,9 +456,9 @@ public class PersistComponentsStepTest extends BaseStepTest { .setRef(1) .setType(Constants.ComponentType.PROJECT) .setKey(PROJECT_KEY) - .setName("Project") - .addChildRef(2) - .build()); + .setName("Project") + .addChildRef(2) + .build()); writer.writeComponent(BatchReport.Component.newBuilder() .setRef(2) .setType(Constants.ComponentType.MODULE) @@ -489,8 +478,12 @@ public class PersistComponentsStepTest extends BaseStepTest { .setPath("src/main/java/dir/Foo.java") .build()); + DumbComponent root = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, + new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY", + new DumbComponent(Component.Type.DIRECTORY, 3, "CDEF", "MODULE_KEY:src/main/java/dir", + new DumbComponent(Component.Type.FILE, 4, "DEFG", "MODULE_KEY:src/main/java/dir/Foo.java")))); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + dbClient, ComponentTreeBuilders.from(root), languageRepository)); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4); assertThat(dbClient.componentDao().selectNullableByKey(session, PROJECT_KEY).getId()).isEqualTo(project.getId()); @@ -537,9 +530,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void update_name_and_description() throws Exception { - computeComponentsRefCache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent(PROJECT_KEY, "ABCD")); - computeComponentsRefCache.addComponent(2, new ComputeComponentsRefCache.ComputeComponent("MODULE_KEY", "BCDE")); - ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey(PROJECT_KEY).setName("Project"); dbClient.componentDao().insert(session, project); ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setKey("MODULE_KEY").setName("Module"); @@ -568,8 +558,10 @@ public class PersistComponentsStepTest extends BaseStepTest { .setDescription("New module description") .build()); + DumbComponent root = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, + new DumbComponent(Component.Type.MODULE, 2, "BCDE", "MODULE_KEY")); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + dbClient, ComponentTreeBuilders.from(root), languageRepository)); ComponentDto projectReloaded = dbClient.componentDao().selectNullableByKey(session, PROJECT_KEY); assertThat(projectReloaded.name()).isEqualTo("New project name"); @@ -582,12 +574,6 @@ public class PersistComponentsStepTest extends BaseStepTest { @Test public void update_module_uuid_when_moving_a_module() throws Exception { - computeComponentsRefCache.addComponent(1, new ComputeComponentsRefCache.ComputeComponent(PROJECT_KEY, "ABCD")); - computeComponentsRefCache.addComponent(2, new ComputeComponentsRefCache.ComputeComponent("MODULE_A", "EDCB")); - computeComponentsRefCache.addComponent(3, new ComputeComponentsRefCache.ComputeComponent("MODULE_B", "BCDE")); - computeComponentsRefCache.addComponent(4, new ComputeComponentsRefCache.ComputeComponent("MODULE_B:src/main/java/dir", "CDEF")); - computeComponentsRefCache.addComponent(5, new ComputeComponentsRefCache.ComputeComponent("MODULE_B:src/main/java/dir/Foo.java", "DEFG")); - ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey(PROJECT_KEY).setName("Project"); dbClient.componentDao().insert(session, project); ComponentDto moduleA = ComponentTesting.newModuleDto("EDCB", project).setKey("MODULE_A").setName("Module A"); @@ -638,8 +624,13 @@ public class PersistComponentsStepTest extends BaseStepTest { .setPath("src/main/java/dir/Foo.java") .build()); + DumbComponent root = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY, + new DumbComponent(Component.Type.MODULE, 2, "EDCB", "MODULE_A", + new DumbComponent(Component.Type.MODULE, 3, "BCDE", "MODULE_B", + new DumbComponent(Component.Type.DIRECTORY, 4, "CDEF", "MODULE_B:src/main/java/dir", + new DumbComponent(Component.Type.FILE, 5, "DEFG", "MODULE_B:src/main/java/dir/Foo.java"))))); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + dbClient, ComponentTreeBuilders.from(root), languageRepository)); assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(5); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java index 8f7165ab46f..c6bdf2e8580 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java @@ -20,9 +20,6 @@ package org.sonar.server.computation.step; -import java.io.File; -import java.util.Arrays; -import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; @@ -43,15 +40,20 @@ import org.sonar.core.persistence.DbTester; import org.sonar.core.persistence.MyBatis; import org.sonar.core.source.db.FileSourceDto; import org.sonar.server.computation.ComputationContext; +import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.ComponentTreeBuilders; -import org.sonar.server.computation.component.DumbComponent; -import org.sonar.server.computation.language.LanguageRepository; import org.sonar.server.computation.component.DbComponentsRefCache; import org.sonar.server.computation.component.DbComponentsRefCache.DbComponent; +import org.sonar.server.computation.component.DumbComponent; +import org.sonar.server.computation.language.LanguageRepository; import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDao; import org.sonar.server.source.db.FileSourceDb; +import java.io.File; +import java.util.Arrays; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.mockito.Mockito.mock; @@ -93,6 +95,8 @@ public class PersistTestsStepTest extends BaseStepTest { PersistTestsStep sut; + Component root; + @Before public void setup() throws Exception { db.truncateTables(); @@ -109,12 +113,19 @@ public class PersistTestsStepTest extends BaseStepTest { sut = new PersistTestsStep(dbClient, system2, dbComponentsRefCache); initBasicReport(); + + root = new DumbComponent(Component.Type.PROJECT, 1, PROJECT_UUID, PROJECT_KEY, + new DumbComponent(Component.Type.MODULE, 2, "MODULE", "MODULE_KEY", + new DumbComponent(Component.Type.FILE, 3, TEST_FILE_UUID_1, "TEST_FILE1_KEY"), + new DumbComponent(Component.Type.FILE, 4, TEST_FILE_UUID_2, "TEST_FILE2_KEY"), + new DumbComponent(Component.Type.FILE, 5, MAIN_FILE_UUID_1, "MAIN_FILE1_KEY"), + new DumbComponent(Component.Type.FILE, 6, MAIN_FILE_UUID_2, "MAIN_FILE2_KEY") + )); } @After public void tearDown() { MyBatis.closeQuietly(session); - } @Override @@ -124,7 +135,7 @@ public class PersistTestsStepTest extends BaseStepTest { @Test public void no_test_in_database_and_batch_report() { - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(root), languageRepository)); assertThat(dbClient.fileSourceDao().selectTest(TEST_FILE_UUID_1)).isNull(); assertThat(log.logs()).isEmpty(); @@ -142,7 +153,7 @@ public class PersistTestsStepTest extends BaseStepTest { ); writer.writeCoverageDetails(TEST_FILE_REF_1, coverageDetails); - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(root), languageRepository)); assertThat(db.countRowsOfTable("file_sources")).isEqualTo(1); @@ -166,7 +177,7 @@ public class PersistTestsStepTest extends BaseStepTest { writer.writeTests(TEST_FILE_REF_1, Arrays.asList(newTest(1))); writer.writeCoverageDetails(TEST_FILE_REF_1, Arrays.asList(newCoverageDetail(1, MAIN_FILE_REF_1))); - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(root), languageRepository)); FileSourceDto dto = dbClient.fileSourceDao().selectTest(TEST_FILE_UUID_1); assertThat(dto.getCreatedAt()).isEqualTo(now); @@ -193,7 +204,7 @@ public class PersistTestsStepTest extends BaseStepTest { List batchTests = Arrays.asList(newTest(1)); writer.writeTests(TEST_FILE_REF_1, batchTests); - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(root), languageRepository)); FileSourceDto dto = dbClient.fileSourceDao().selectTest(TEST_FILE_UUID_1); assertThat(dto.getFileUuid()).isEqualTo(TEST_FILE_UUID_1); @@ -212,7 +223,7 @@ public class PersistTestsStepTest extends BaseStepTest { writer.writeCoverageDetails(TEST_FILE_REF_1, coverageDetails); writer.writeCoverageDetails(TEST_FILE_REF_2, coverageDetails); - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(root), languageRepository)); assertThat(log.logs(LoggerLevel.WARN)).hasSize(1); assertThat(log.logs(LoggerLevel.WARN).get(0)).isEqualTo("Some coverage tests are not taken into account during analysis of project 'PROJECT_KEY'"); @@ -230,7 +241,7 @@ public class PersistTestsStepTest extends BaseStepTest { newCoverageDetailWithLines(1, MAIN_FILE_REF_1, 1, 3), newCoverageDetailWithLines(1, MAIN_FILE_REF_1, 2, 4))); - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(root), languageRepository)); FileSourceDto dto = dbClient.fileSourceDao().selectTest(TEST_FILE_UUID_1); List coveredLines = dto.getTestData().get(0).getCoveredFile(0).getCoveredLineList(); @@ -264,7 +275,7 @@ public class PersistTestsStepTest extends BaseStepTest { writer.writeCoverageDetails(TEST_FILE_REF_1, Arrays.asList(newCoverageDetail)); // ACT - sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); + sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(root), languageRepository)); // ASSERT FileSourceDto dto = dbClient.fileSourceDao().selectTest(TEST_FILE_UUID_1); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityProfileEventsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityProfileEventsStepTest.java index bb2e9854cc2..93003167e09 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityProfileEventsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityProfileEventsStepTest.java @@ -21,13 +21,6 @@ package org.sonar.server.computation.step; import com.google.common.base.Optional; import com.google.common.collect.Lists; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import javax.annotation.Nullable; import org.junit.Test; import org.sonar.api.config.Settings; import org.sonar.api.measures.CoreMetrics; @@ -49,6 +42,15 @@ import org.sonar.server.computation.qualityprofile.QPMeasureData; import org.sonar.server.computation.qualityprofile.QualityProfile; import org.sonar.server.db.DbClient; +import javax.annotation.Nullable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; @@ -298,7 +300,7 @@ public class QualityProfileEventsStepTest { public EventAndMeasureRepoComponent(@Nullable org.sonar.server.computation.context.ComputationContext context, Type type, int ref, @Nullable Component... children) { - super(context, type, ref, children); + super(context, type, ref, null, null, children); } @Override diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java index 2301abbcd21..317cb70cd76 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java @@ -101,7 +101,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest { when(notifService.hasProjectSubscribersForTypes(PROJECT_UUID, SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(false); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - mock(DbClient.class), ComponentTreeBuilders.from(new DumbComponent(Component.Type.PROJECT, 1)), mock(LanguageRepository.class))); + mock(DbClient.class), ComponentTreeBuilders.from(new DumbComponent(Component.Type.PROJECT, 1, null, null)), mock(LanguageRepository.class))); verify(notifService, never()).deliver(any(Notification.class)); } @@ -114,7 +114,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest { when(notifService.hasProjectSubscribersForTypes(PROJECT_UUID, SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, - mock(DbClient.class), ComponentTreeBuilders.from(new DumbComponent(Component.Type.PROJECT, 1)), mock(LanguageRepository.class))); + mock(DbClient.class), ComponentTreeBuilders.from(new DumbComponent(Component.Type.PROJECT, 1, null, null)), mock(LanguageRepository.class))); verify(notifService).deliver(any(NewIssuesNotification.class)); verify(notifService, atLeastOnce()).deliver(any(IssueChangeNotification.class)); diff --git a/sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java b/sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java index c27bdc96ff1..b49fef42bef 100644 --- a/sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java +++ b/sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java @@ -23,14 +23,26 @@ public final class BatchReport { /** * optional string project_key = 2; + * + *
+     * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+     * 
*/ boolean hasProjectKey(); /** * optional string project_key = 2; + * + *
+     * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+     * 
*/ java.lang.String getProjectKey(); /** * optional string project_key = 2; + * + *
+     * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+     * 
*/ com.google.protobuf.ByteString getProjectKeyBytes(); @@ -227,12 +239,20 @@ public final class BatchReport { private java.lang.Object projectKey_; /** * optional string project_key = 2; + * + *
+     * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+     * 
*/ public boolean hasProjectKey() { return ((bitField0_ & 0x00000002) == 0x00000002); } /** * optional string project_key = 2; + * + *
+     * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+     * 
*/ public java.lang.String getProjectKey() { java.lang.Object ref = projectKey_; @@ -250,6 +270,10 @@ public final class BatchReport { } /** * optional string project_key = 2; + * + *
+     * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+     * 
*/ public com.google.protobuf.ByteString getProjectKeyBytes() { @@ -713,12 +737,20 @@ public final class BatchReport { private java.lang.Object projectKey_ = ""; /** * optional string project_key = 2; + * + *
+       * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+       * 
*/ public boolean hasProjectKey() { return ((bitField0_ & 0x00000002) == 0x00000002); } /** * optional string project_key = 2; + * + *
+       * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+       * 
*/ public java.lang.String getProjectKey() { java.lang.Object ref = projectKey_; @@ -736,6 +768,10 @@ public final class BatchReport { } /** * optional string project_key = 2; + * + *
+       * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+       * 
*/ public com.google.protobuf.ByteString getProjectKeyBytes() { @@ -752,6 +788,10 @@ public final class BatchReport { } /** * optional string project_key = 2; + * + *
+       * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+       * 
*/ public Builder setProjectKey( java.lang.String value) { @@ -765,6 +805,10 @@ public final class BatchReport { } /** * optional string project_key = 2; + * + *
+       * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+       * 
*/ public Builder clearProjectKey() { bitField0_ = (bitField0_ & ~0x00000002); @@ -774,6 +818,10 @@ public final class BatchReport { } /** * optional string project_key = 2; + * + *
+       * TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
+       * 
*/ public Builder setProjectKeyBytes( com.google.protobuf.ByteString value) { @@ -2652,6 +2700,7 @@ public final class BatchReport { * *
      * Only available on PROJECT and MODULE types
+     * TODO rename this property -> batchKey ? moduleKey ?
      * 
*/ boolean hasKey(); @@ -2660,6 +2709,7 @@ public final class BatchReport { * *
      * Only available on PROJECT and MODULE types
+     * TODO rename this property -> batchKey ? moduleKey ?
      * 
*/ java.lang.String getKey(); @@ -2668,6 +2718,7 @@ public final class BatchReport { * *
      * Only available on PROJECT and MODULE types
+     * TODO rename this property -> batchKey ? moduleKey ?
      * 
*/ com.google.protobuf.ByteString @@ -3252,6 +3303,7 @@ public final class BatchReport { * *
      * Only available on PROJECT and MODULE types
+     * TODO rename this property -> batchKey ? moduleKey ?
      * 
*/ public boolean hasKey() { @@ -3262,6 +3314,7 @@ public final class BatchReport { * *
      * Only available on PROJECT and MODULE types
+     * TODO rename this property -> batchKey ? moduleKey ?
      * 
*/ public java.lang.String getKey() { @@ -3283,6 +3336,7 @@ public final class BatchReport { * *
      * Only available on PROJECT and MODULE types
+     * TODO rename this property -> batchKey ? moduleKey ?
      * 
*/ public com.google.protobuf.ByteString @@ -4723,6 +4777,7 @@ public final class BatchReport { * *
        * Only available on PROJECT and MODULE types
+       * TODO rename this property -> batchKey ? moduleKey ?
        * 
*/ public boolean hasKey() { @@ -4733,6 +4788,7 @@ public final class BatchReport { * *
        * Only available on PROJECT and MODULE types
+       * TODO rename this property -> batchKey ? moduleKey ?
        * 
*/ public java.lang.String getKey() { @@ -4754,6 +4810,7 @@ public final class BatchReport { * *
        * Only available on PROJECT and MODULE types
+       * TODO rename this property -> batchKey ? moduleKey ?
        * 
*/ public com.google.protobuf.ByteString @@ -4774,6 +4831,7 @@ public final class BatchReport { * *
        * Only available on PROJECT and MODULE types
+       * TODO rename this property -> batchKey ? moduleKey ?
        * 
*/ public Builder setKey( @@ -4791,6 +4849,7 @@ public final class BatchReport { * *
        * Only available on PROJECT and MODULE types
+       * TODO rename this property -> batchKey ? moduleKey ?
        * 
*/ public Builder clearKey() { @@ -4804,6 +4863,7 @@ public final class BatchReport { * *
        * Only available on PROJECT and MODULE types
+       * TODO rename this property -> batchKey ? moduleKey ?
        * 
*/ public Builder setKeyBytes( diff --git a/sonar-batch-protocol/src/main/protobuf/batch_report.proto b/sonar-batch-protocol/src/main/protobuf/batch_report.proto index fa7801297ef..869573da18d 100644 --- a/sonar-batch-protocol/src/main/protobuf/batch_report.proto +++ b/sonar-batch-protocol/src/main/protobuf/batch_report.proto @@ -41,6 +41,7 @@ option optimize_for = SPEED; message Metadata { optional int64 analysis_date = 1; + // TODO should we keep this project_key here or not ? Because it's a duplication of Component.key optional string project_key = 2; optional string branch = 6; optional int32 root_component_ref = 3; @@ -76,6 +77,7 @@ message Component { // Only available on PROJECT and MODULE types optional string version = 9; // Only available on PROJECT and MODULE types + // TODO rename this property -> batchKey ? moduleKey ? optional string key = 10; // Only available on FILE type optional int32 lines = 11;