aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2014-11-07 15:53:07 +0100
committerJulien HENRY <julien.henry@sonarsource.com>2014-11-07 16:15:09 +0100
commit07dee4c58e8be5e83c3c3c17ee2c47600ccf27e6 (patch)
tree951a2857990ea8148130c682c2f6260940bcb948
parent1ce597bb9c13cc9b403da355b48e2188344fed71 (diff)
downloadsonarqube-07dee4c58e8be5e83c3c3c17ee2c47600ccf27e6.tar.gz
sonarqube-07dee4c58e8be5e83c3c3c17ee2c47600ccf27e6.zip
SONAR-5672 Don't save DSM when there is no dependency or more than 200 components
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java98
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java127
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java67
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java100
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java10
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java214
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java149
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java157
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java4
-rw-r--r--sonar-graph/src/main/java/org/sonar/graph/DirectedGraph.java10
-rw-r--r--sonar-graph/src/main/java/org/sonar/graph/Dsm.java34
-rw-r--r--sonar-graph/src/main/java/org/sonar/graph/DsmScanner.java6
-rw-r--r--sonar-graph/src/main/java/org/sonar/graph/FeedbackCycle.java12
-rw-r--r--sonar-graph/src/main/java/org/sonar/graph/IncrementalCyclesAndFESSolver.java6
-rw-r--r--sonar-graph/src/main/java/org/sonar/graph/MinimumFeedbackEdgeSetSolver.java14
15 files changed, 766 insertions, 242 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java
index 0446d61fd38..95c3da4ae8c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java
@@ -19,107 +19,55 @@
*/
package org.sonar.batch.design;
-import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.SonarIndex;
-import org.sonar.api.design.Dependency;
import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.measures.PersistenceMode;
-import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
import org.sonar.graph.Cycle;
-import org.sonar.graph.Dsm;
-import org.sonar.graph.DsmTopologicalSorter;
import org.sonar.graph.Edge;
import org.sonar.graph.IncrementalCyclesAndFESSolver;
import org.sonar.graph.MinimumFeedbackEdgeSetSolver;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import java.util.Set;
-public class DirectoryDsmDecorator implements Decorator {
-
- private SonarIndex index;
+public class DirectoryDsmDecorator extends DsmDecorator {
public DirectoryDsmDecorator(SonarIndex index) {
- this.index = index;
+ super(index);
}
@Override
- public boolean shouldExecuteOnProject(Project project) {
- return true;
- }
-
- @Override
- public void decorate(final Resource resource, DecoratorContext context) {
- if (shouldDecorateResource(resource, context)) {
- List<DecoratorContext> fileContexts = context.getChildren();
- List<Resource> files = new ArrayList<Resource>(fileContexts.size());
- for (DecoratorContext decoratorContext : fileContexts) {
- files.add(decoratorContext.getResource());
- }
-
- IncrementalCyclesAndFESSolver<Resource> cycleDetector = new IncrementalCyclesAndFESSolver<Resource>(index, files);
- Set<Cycle> cycles = cycleDetector.getCycles();
-
- MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
- Set<Edge> feedbackEdges = solver.getEdges();
- int tangles = solver.getWeightOfFeedbackEdgeSet();
-
- savePositiveMeasure(context, CoreMetrics.FILE_CYCLES, cycles.size());
- savePositiveMeasure(context, CoreMetrics.FILE_FEEDBACK_EDGES, feedbackEdges.size());
- savePositiveMeasure(context, CoreMetrics.FILE_TANGLES, tangles);
- savePositiveMeasure(context, CoreMetrics.FILE_EDGES_WEIGHT, getEdgesWeight(files));
-
- Dsm<Resource> dsm = getDsm(files, feedbackEdges);
- saveDsm(context, dsm);
- }
- }
-
- private void savePositiveMeasure(DecoratorContext context, Metric<Integer> metric, double value) {
- if (value >= 0.0) {
- context.saveMeasure(new Measure(metric, value));
+ protected List<Resource> getChildren(Resource resource, DecoratorContext context) {
+ List<DecoratorContext> fileContexts = context.getChildren();
+ List<Resource> files = new ArrayList<Resource>(fileContexts.size());
+ for (DecoratorContext decoratorContext : fileContexts) {
+ files.add(decoratorContext.getResource());
}
+ return files;
}
- private int getEdgesWeight(Collection<Resource> sourceCodes) {
- List<Dependency> edges = getEdges(sourceCodes);
- int total = 0;
- for (Dependency edge : edges) {
- total += edge.getWeight();
- }
- return total;
- }
+ @Override
+ protected Set<Edge> doProcess(List<Resource> children, DecoratorContext context) {
+ IncrementalCyclesAndFESSolver<Resource> cycleDetector = new IncrementalCyclesAndFESSolver<Resource>(getIndex(), children);
+ Set<Cycle> cycles = cycleDetector.getCycles();
- public List<Dependency> getEdges(Collection<Resource> vertices) {
- List<Dependency> result = new ArrayList<Dependency>();
- for (Resource vertice : vertices) {
- Collection<Dependency> outgoingEdges = index.getOutgoingEdges(vertice);
- if (outgoingEdges != null) {
- result.addAll(outgoingEdges);
- }
- }
- return result;
- }
+ MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
+ Set<Edge> feedbackEdges = solver.getEdges();
+ int tangles = solver.getWeightOfFeedbackEdgeSet();
- private void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
- Measure measure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm));
- measure.setPersistenceMode(PersistenceMode.DATABASE);
- context.saveMeasure(measure);
- }
+ savePositiveMeasure(context, CoreMetrics.FILE_CYCLES, cycles.size());
+ savePositiveMeasure(context, CoreMetrics.FILE_FEEDBACK_EDGES, feedbackEdges.size());
+ savePositiveMeasure(context, CoreMetrics.FILE_TANGLES, tangles);
+ savePositiveMeasure(context, CoreMetrics.FILE_EDGES_WEIGHT, getEdgesWeight(children));
- private Dsm<Resource> getDsm(Collection<Resource> files, Set<Edge> feedbackEdges) {
- Dsm<Resource> dsm = new Dsm<Resource>(index, files, feedbackEdges);
- DsmTopologicalSorter.sort(dsm);
- return dsm;
+ return feedbackEdges;
}
- private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
- return ResourceUtils.isDirectory(resource) && context.getMeasure(CoreMetrics.DEPENDENCY_MATRIX) == null;
+ @Override
+ protected boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ return ResourceUtils.isDirectory(resource);
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java
new file mode 100644
index 00000000000..3569ba9a913
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java
@@ -0,0 +1,127 @@
+/*
+ * 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.batch.design;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.PersistenceMode;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.graph.Dsm;
+import org.sonar.graph.DsmTopologicalSorter;
+import org.sonar.graph.Edge;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+public abstract class DsmDecorator implements Decorator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DsmDecorator.class);
+
+ private static final int MAX_DSM_DIMENSION = 200;
+ private SonarIndex index;
+
+ public DsmDecorator(SonarIndex index) {
+ this.index = index;
+ }
+
+ public final SonarIndex getIndex() {
+ return index;
+ }
+
+ @Override
+ public final boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @Override
+ public final void decorate(final Resource resource, DecoratorContext context) {
+ if (shouldDecorateResource(resource, context)) {
+ List<Resource> children = getChildren(resource, context);
+ if (children.isEmpty()) {
+ return;
+ }
+ Set<Edge> feedbackEdges = doProcess(children, context);
+
+ if (children.size() > MAX_DSM_DIMENSION) {
+ LOG.warn("Too many components under resource '" + resource.getName() + "'. DSM will not be displayed.");
+ return;
+ }
+ Dsm<Resource> dsm = getDsm(children, feedbackEdges);
+ // Optimization, don't save DSM if there is no dependency at all
+ if (dsm.hasAtLeastOneDependency()) {
+ saveDsm(context, dsm);
+ }
+ }
+ }
+
+ protected abstract boolean shouldDecorateResource(Resource resource, DecoratorContext context);
+
+ protected abstract List<Resource> getChildren(Resource resource, DecoratorContext context);
+
+ protected abstract Set<Edge> doProcess(List<Resource> children, DecoratorContext context);
+
+ protected final void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
+ Measure measure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm));
+ measure.setPersistenceMode(PersistenceMode.DATABASE);
+ context.saveMeasure(measure);
+ }
+
+ protected final Dsm<Resource> getDsm(Collection<Resource> children, Set<Edge> feedbackEdges) {
+ Dsm<Resource> dsm = new Dsm<Resource>(index, children, feedbackEdges);
+ DsmTopologicalSorter.sort(dsm);
+ return dsm;
+ }
+
+ protected final void savePositiveMeasure(DecoratorContext context, Metric<Integer> metric, double value) {
+ if (value >= 0.0) {
+ context.saveMeasure(new Measure(metric, value));
+ }
+ }
+
+ protected final int getEdgesWeight(Collection<Resource> sourceCodes) {
+ List<Dependency> edges = getEdges(sourceCodes);
+ int total = 0;
+ for (Dependency edge : edges) {
+ total += edge.getWeight();
+ }
+ return total;
+ }
+
+ protected final List<Dependency> getEdges(Collection<Resource> vertices) {
+ List<Dependency> result = new ArrayList<Dependency>();
+ for (Resource vertice : vertices) {
+ Collection<Dependency> outgoingEdges = index.getOutgoingEdges(vertice);
+ if (outgoingEdges != null) {
+ result.addAll(outgoingEdges);
+ }
+ }
+ return result;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java
index f9ae4de6a50..66471955795 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java
@@ -20,81 +20,38 @@
package org.sonar.batch.design;
import com.google.common.collect.Lists;
-import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.SonarIndex;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
import org.sonar.graph.Cycle;
import org.sonar.graph.CycleDetector;
-import org.sonar.graph.Dsm;
-import org.sonar.graph.DsmTopologicalSorter;
import org.sonar.graph.Edge;
import org.sonar.graph.MinimumFeedbackEdgeSetSolver;
-import java.util.Collection;
import java.util.List;
import java.util.Set;
-public class ProjectDsmDecorator implements Decorator {
-
- private SonarIndex index;
+public class ProjectDsmDecorator extends DsmDecorator {
public ProjectDsmDecorator(SonarIndex index) {
- this.index = index;
- }
-
- @Override
- public boolean shouldExecuteOnProject(Project project) {
- return true;
- }
-
- @Override
- public void decorate(final Resource resource, DecoratorContext context) {
- if (shouldDecorateResource(resource, context)) {
- Collection<Resource> subProjects = getSubProjects((Project) resource);
-
- if (!subProjects.isEmpty()) {
- Dsm<Resource> dsm = getDsm(subProjects);
- saveDsm(context, dsm);
- }
- }
- }
-
- private void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
- Measure measure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm));
- measure.setPersistenceMode(PersistenceMode.DATABASE);
- context.saveMeasure(measure);
- }
-
- private Dsm<Resource> getDsm(Collection<Resource> subProjects) {
- CycleDetector<Resource> cycleDetector = new CycleDetector<Resource>(index, subProjects);
- Set<Cycle> cycles = cycleDetector.getCycles();
-
- MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
- Set<Edge> feedbackEdges = solver.getEdges();
-
- Dsm<Resource> dsm = new Dsm<Resource>(index, subProjects, feedbackEdges);
- DsmTopologicalSorter.sort(dsm);
- return dsm;
+ super(index);
}
/**
* sub-projects, including all descendants but not only direct children
*/
- private Collection<Resource> getSubProjects(final Project project) {
+ @Override
+ protected List<Resource> getChildren(Resource resource, DecoratorContext context) {
List<Resource> subProjects = Lists.newArrayList();
- addSubProjects(project, subProjects);
+ addSubProjects((Project) resource, subProjects);
return subProjects;
}
private void addSubProjects(Project project, List<Resource> subProjects) {
for (Project subProject : project.getModules()) {
- Project indexedSubProject = index.getResource(subProject);
+ Project indexedSubProject = getIndex().getResource(subProject);
if (indexedSubProject != null) {
subProjects.add(indexedSubProject);
}
@@ -102,7 +59,17 @@ public class ProjectDsmDecorator implements Decorator {
}
}
- private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ @Override
+ protected Set<Edge> doProcess(List<Resource> children, DecoratorContext context) {
+ CycleDetector<Resource> cycleDetector = new CycleDetector<Resource>(getIndex(), children);
+ Set<Cycle> cycles = cycleDetector.getCycles();
+
+ MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
+ return solver.getEdges();
+ }
+
+ @Override
+ protected boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
// Should not execute on views
return (ResourceUtils.isRootProject(resource) || ResourceUtils.isModuleProject(resource))
&& !((Project) resource).getModules().isEmpty();
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java
index 3d850f3ab50..9e03a13c7df 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java
@@ -19,107 +19,55 @@
*/
package org.sonar.batch.design;
-import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.SonarIndex;
-import org.sonar.api.design.Dependency;
import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
import org.sonar.graph.Cycle;
-import org.sonar.graph.Dsm;
-import org.sonar.graph.DsmTopologicalSorter;
import org.sonar.graph.Edge;
import org.sonar.graph.IncrementalCyclesAndFESSolver;
import org.sonar.graph.MinimumFeedbackEdgeSetSolver;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import java.util.Set;
-public class SubProjectDsmDecorator implements Decorator {
-
- private SonarIndex index;
+public class SubProjectDsmDecorator extends DsmDecorator {
public SubProjectDsmDecorator(SonarIndex index) {
- this.index = index;
- }
-
- @Override
- public boolean shouldExecuteOnProject(Project project) {
- return true;
+ super(index);
}
@Override
- public void decorate(final Resource resource, DecoratorContext context) {
- if (shouldDecorateResource(resource, context)) {
- List<DecoratorContext> directoryContexts = context.getChildren();
- List<Resource> directories = new ArrayList<Resource>(directoryContexts.size());
- for (DecoratorContext decoratorContext : directoryContexts) {
- directories.add(decoratorContext.getResource());
- }
-
- IncrementalCyclesAndFESSolver<Resource> cycleDetector = new IncrementalCyclesAndFESSolver<Resource>(index, directories);
- Set<Cycle> cycles = cycleDetector.getCycles();
-
- MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
- Set<Edge> feedbackEdges = solver.getEdges();
- int tangles = solver.getWeightOfFeedbackEdgeSet();
-
- savePositiveMeasure(context, CoreMetrics.DIRECTORY_CYCLES, cycles.size());
- savePositiveMeasure(context, CoreMetrics.DIRECTORY_FEEDBACK_EDGES, feedbackEdges.size());
- savePositiveMeasure(context, CoreMetrics.DIRECTORY_TANGLES, tangles);
- savePositiveMeasure(context, CoreMetrics.DIRECTORY_EDGES_WEIGHT, getEdgesWeight(directories));
-
- Dsm<Resource> dsm = getDsm(directories, feedbackEdges);
- saveDsm(context, dsm);
- }
- }
-
- private void savePositiveMeasure(DecoratorContext context, Metric<Integer> metric, double value) {
- if (value >= 0.0) {
- context.saveMeasure(new Measure(metric, value));
+ protected List<Resource> getChildren(Resource resource, DecoratorContext context) {
+ List<DecoratorContext> directoryContexts = context.getChildren();
+ List<Resource> directories = new ArrayList<Resource>(directoryContexts.size());
+ for (DecoratorContext decoratorContext : directoryContexts) {
+ directories.add(decoratorContext.getResource());
}
+ return directories;
}
- private int getEdgesWeight(Collection<Resource> sourceCodes) {
- List<Dependency> edges = getEdges(sourceCodes);
- int total = 0;
- for (Dependency edge : edges) {
- total += edge.getWeight();
- }
- return total;
- }
-
- public List<Dependency> getEdges(Collection<Resource> vertices) {
- List<Dependency> result = new ArrayList<Dependency>();
- for (Resource vertice : vertices) {
- Collection<Dependency> outgoingEdges = index.getOutgoingEdges(vertice);
- if (outgoingEdges != null) {
- result.addAll(outgoingEdges);
- }
- }
- return result;
- }
-
- private void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
- Measure measure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm));
- measure.setPersistenceMode(PersistenceMode.DATABASE);
- context.saveMeasure(measure);
- }
-
- private Dsm<Resource> getDsm(Collection<Resource> directories, Set<Edge> feedbackEdges) {
- Dsm<Resource> dsm = new Dsm<Resource>(index, directories, feedbackEdges);
- DsmTopologicalSorter.sort(dsm);
- return dsm;
+ @Override
+ protected Set<Edge> doProcess(List<Resource> children, DecoratorContext context) {
+ IncrementalCyclesAndFESSolver<Resource> cycleDetector = new IncrementalCyclesAndFESSolver<Resource>(getIndex(), children);
+ Set<Cycle> cycles = cycleDetector.getCycles();
+
+ MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
+ Set<Edge> feedbackEdges = solver.getEdges();
+ int tangles = solver.getWeightOfFeedbackEdgeSet();
+
+ savePositiveMeasure(context, CoreMetrics.DIRECTORY_CYCLES, cycles.size());
+ savePositiveMeasure(context, CoreMetrics.DIRECTORY_FEEDBACK_EDGES, feedbackEdges.size());
+ savePositiveMeasure(context, CoreMetrics.DIRECTORY_TANGLES, tangles);
+ savePositiveMeasure(context, CoreMetrics.DIRECTORY_EDGES_WEIGHT, getEdgesWeight(children));
+ return feedbackEdges;
}
- private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ @Override
+ protected boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
// Should not execute on views
return (ResourceUtils.isRootProject(resource) || ResourceUtils.isModuleProject(resource))
// Only on leaf projects
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
index 9cc85c83c2b..b1235f09964 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
@@ -80,11 +80,11 @@ public class DefaultIndex extends SonarIndex {
// caches
private Project currentProject;
- private Map<Resource, Bucket> buckets = Maps.newHashMap();
- private Map<String, Bucket> bucketsByDeprecatedKey = Maps.newHashMap();
- private Set<Dependency> dependencies = Sets.newHashSet();
- private Map<Resource, Map<Resource, Dependency>> outgoingDependenciesByResource = Maps.newHashMap();
- private Map<Resource, Map<Resource, Dependency>> incomingDependenciesByResource = Maps.newHashMap();
+ private Map<Resource, Bucket> buckets = Maps.newLinkedHashMap();
+ private Map<String, Bucket> bucketsByDeprecatedKey = Maps.newLinkedHashMap();
+ private Set<Dependency> dependencies = Sets.newLinkedHashSet();
+ private Map<Resource, Map<Resource, Dependency>> outgoingDependenciesByResource = Maps.newLinkedHashMap();
+ private Map<Resource, Map<Resource, Dependency>> incomingDependenciesByResource = Maps.newLinkedHashMap();
private ProjectTree projectTree;
private final DeprecatedViolations deprecatedViolations;
private ModuleIssues moduleIssues;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java
new file mode 100644
index 00000000000..48a7b814361
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.batch.design;
+
+import edu.emory.mathcs.backport.java.util.Collections;
+import org.apache.commons.lang.ObjectUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DirectoryDsmDecoratorTest {
+
+ private DirectoryDsmDecorator decorator;
+ private Directory dir;
+ private DecoratorContext dirContext;
+ private SonarIndex index;
+ private File file1;
+ private File file2;
+ private DecoratorContext file1Context;
+ private DecoratorContext file2Context;
+
+ @Before
+ public void prepare() {
+ index = mock(SonarIndex.class);
+ decorator = new DirectoryDsmDecorator(index);
+ dir = Directory.create("src");
+ dirContext = mock(DecoratorContext.class);
+
+ file1 = File.create("src/Foo1.java", "Foo1.java", null, false);
+ file1.setId(1);
+ file2 = File.create("src/Foo2.java", "Foo2.java", null, false);
+ file2.setId(2);
+
+ file1Context = mock(DecoratorContext.class);
+ when(file1Context.getResource()).thenReturn(file1);
+ file2Context = mock(DecoratorContext.class);
+ when(file2Context.getResource()).thenReturn(file2);
+
+ when(dirContext.getChildren()).thenReturn(Arrays.asList(file1Context, file2Context));
+
+ }
+
+ @Test
+ public void testDirectoryDsmDecoratorNoExecution() {
+ assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+
+ // Should not execute on project
+ decorator.decorate(new Project("foo"), dirContext);
+
+ // Should not do anything if dir has no files
+ when(dirContext.getChildren()).thenReturn(Collections.emptyList());
+ decorator.decorate(dir, dirContext);
+
+ verify(dirContext, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void testDirectoryDsmDecoratorNoDependency() {
+ decorator.decorate(dir, dirContext);
+
+ verify(dirContext, times(4)).saveMeasure(any(Measure.class));
+
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_CYCLES, 0.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_FEEDBACK_EDGES, 0.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_TANGLES, 0.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_EDGES_WEIGHT, 0.0));
+ }
+
+ @Test
+ public void testDirectoryDsmDecoratorDependency() {
+ Dependency dependency = new Dependency(file1, file2).setWeight(1).setId(51L);
+ when(index.getEdge(file1, file2)).thenReturn(dependency);
+ when(index.hasEdge(file1, file2)).thenReturn(true);
+ when(index.getOutgoingEdges(file1)).thenReturn(Arrays.asList(dependency));
+ when(index.getIncomingEdges(file2)).thenReturn(Arrays.asList(dependency));
+
+ decorator.decorate(dir, dirContext);
+
+ verify(dirContext, times(5)).saveMeasure(any(Measure.class));
+
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_CYCLES, 0.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_FEEDBACK_EDGES, 0.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_TANGLES, 0.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_EDGES_WEIGHT, 1.0));
+
+ verify(dirContext).saveMeasure(
+ isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+ "[{\"i\":1,\"n\":\"Foo1.java\",\"q\":\"FIL\",\"v\":[{},{}]},{\"i\":2,\"n\":\"Foo2.java\",\"q\":\"FIL\",\"v\":[{\"i\":51,\"w\":1},{}]}]"));
+ }
+
+ @Test
+ public void testDirectoryDsmDecoratorNoDSMIfMoreThan200Components() {
+ Dependency dependency = new Dependency(file1, file2).setWeight(1).setId(51L);
+ when(index.getEdge(file1, file2)).thenReturn(dependency);
+ when(index.hasEdge(file1, file2)).thenReturn(true);
+ when(index.getOutgoingEdges(file1)).thenReturn(Arrays.asList(dependency));
+ when(index.getIncomingEdges(file2)).thenReturn(Arrays.asList(dependency));
+
+ List<DecoratorContext> contexts = new ArrayList<DecoratorContext>(201);
+ contexts.add(file1Context);
+ contexts.add(file2Context);
+ for (int i = 0; i < 199; i++) {
+ DecoratorContext fileContext = mock(DecoratorContext.class);
+ when(fileContext.getResource()).thenReturn(File.create("file" + i));
+ contexts.add(fileContext);
+ }
+
+ when(dirContext.getChildren()).thenReturn(contexts);
+
+ decorator.decorate(dir, dirContext);
+
+ verify(dirContext, times(4)).saveMeasure(any(Measure.class));
+
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_CYCLES, 0.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_FEEDBACK_EDGES, 0.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_TANGLES, 0.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_EDGES_WEIGHT, 1.0));
+ }
+
+ @Test
+ public void testDirectoryDsmDecoratorCycleDependency() {
+ Dependency dependency1to2 = new Dependency(file1, file2).setWeight(1).setId(50L);
+ when(index.getEdge(file1, file2)).thenReturn(dependency1to2);
+ when(index.hasEdge(file1, file2)).thenReturn(true);
+ when(index.getOutgoingEdges(file1)).thenReturn(Arrays.asList(dependency1to2));
+ when(index.getIncomingEdges(file2)).thenReturn(Arrays.asList(dependency1to2));
+ Dependency dependency2to1 = new Dependency(file2, file1).setWeight(2).setId(51L);
+ when(index.getEdge(file2, file1)).thenReturn(dependency2to1);
+ when(index.hasEdge(file2, file1)).thenReturn(true);
+ when(index.getOutgoingEdges(file2)).thenReturn(Arrays.asList(dependency2to1));
+ when(index.getIncomingEdges(file1)).thenReturn(Arrays.asList(dependency2to1));
+
+ decorator.decorate(dir, dirContext);
+
+ verify(dirContext, times(5)).saveMeasure(any(Measure.class));
+
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_CYCLES, 1.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_FEEDBACK_EDGES, 1.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_TANGLES, 1.0));
+ verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_EDGES_WEIGHT, 3.0));
+
+ verify(dirContext).saveMeasure(
+ isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+ "[{\"i\":2,\"n\":\"Foo2.java\",\"q\":\"FIL\",\"v\":[{},{\"i\":50,\"w\":1}]},{\"i\":1,\"n\":\"Foo1.java\",\"q\":\"FIL\",\"v\":[{\"i\":51,\"w\":2},{}]}]"));
+ }
+
+ Measure isMeasureWithValue(Metric metric, Double value) {
+ return argThat(new IsMeasureWithValue(metric, value));
+ }
+
+ Measure isMeasureWithValue(Metric metric, String data) {
+ return argThat(new IsMeasureWithValue(metric, data));
+ }
+
+ class IsMeasureWithValue extends ArgumentMatcher<Measure> {
+
+ private Metric metric;
+ private Double value;
+ private String data;
+
+ public IsMeasureWithValue(Metric metric, Double value) {
+ this.metric = metric;
+ this.value = value;
+ }
+
+ public IsMeasureWithValue(Metric metric, String data) {
+ this.metric = metric;
+ this.data = data;
+ }
+
+ public boolean matches(Object m) {
+ return ((Measure) m).getMetric().equals(metric) && ObjectUtils.equals(((Measure) m).getValue(), value) && ObjectUtils.equals(((Measure) m).getData(), data);
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java
new file mode 100644
index 00000000000..3850d032de8
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.batch.design;
+
+import edu.emory.mathcs.backport.java.util.Collections;
+import org.apache.commons.lang.ObjectUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.Project;
+
+import java.util.Arrays;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class ProjectDsmDecoratorTest {
+
+ private ProjectDsmDecorator decorator;
+ private Project root;
+ private DecoratorContext rootContext;
+ private SonarIndex index;
+ private Project module1;
+ private Project module2;
+
+ @Before
+ public void prepare() {
+ index = mock(SonarIndex.class);
+ decorator = new ProjectDsmDecorator(index);
+ root = new Project("root");
+ rootContext = mock(DecoratorContext.class);
+
+ module1 = new Project("module1").setName("Module1").setParent(root);
+ module1.setId(1);
+ when(index.getResource(module1)).thenReturn(module1);
+ module2 = new Project("module2").setName("Module2").setParent(root);
+ module2.setId(2);
+ when(index.getResource(module2)).thenReturn(module2);
+
+ DecoratorContext module1Context = mock(DecoratorContext.class);
+ when(module1Context.getResource()).thenReturn(module1);
+ DecoratorContext module2Context = mock(DecoratorContext.class);
+ when(module2Context.getResource()).thenReturn(module2);
+
+ when(rootContext.getChildren()).thenReturn(Arrays.asList(module1Context, module2Context));
+
+ }
+
+ @Test
+ public void testProjectDsmDecoratorNoExecution() {
+ assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+
+ // Should not execute on directory
+ decorator.decorate(Directory.create("foo"), rootContext);
+ // Should not execute on aggregator projects
+ Project p = new Project("parent");
+ Project child = new Project("child").setParent(p);
+ decorator.decorate(p, rootContext);
+
+ // Should not do anything if module has no dir
+ when(rootContext.getChildren()).thenReturn(Collections.emptyList());
+ decorator.decorate(root, rootContext);
+
+ verify(rootContext, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void testProjectDsmDecoratorNoDependency() {
+ decorator.decorate(root, rootContext);
+
+ verify(rootContext, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void testProjectDsmDecoratorDependency() {
+ Dependency dependency = new Dependency(module1, module2).setWeight(1).setId(51L);
+ when(index.getEdge(module1, module2)).thenReturn(dependency);
+ when(index.hasEdge(module1, module2)).thenReturn(true);
+ when(index.getOutgoingEdges(module1)).thenReturn(Arrays.asList(dependency));
+ when(index.getIncomingEdges(module2)).thenReturn(Arrays.asList(dependency));
+
+ decorator.decorate(root, rootContext);
+
+ verify(rootContext, times(1)).saveMeasure(any(Measure.class));
+
+ verify(rootContext).saveMeasure(
+ isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+ "[{\"i\":1,\"n\":\"Module1\",\"q\":\"BRC\",\"v\":[{},{}]},{\"i\":2,\"n\":\"Module2\",\"q\":\"BRC\",\"v\":[{\"i\":51,\"w\":1},{}]}]"));
+ }
+
+ Measure isMeasureWithValue(Metric metric, Double value) {
+ return argThat(new IsMeasureWithValue(metric, value));
+ }
+
+ Measure isMeasureWithValue(Metric metric, String data) {
+ return argThat(new IsMeasureWithValue(metric, data));
+ }
+
+ class IsMeasureWithValue extends ArgumentMatcher<Measure> {
+
+ private Metric metric;
+ private Double value;
+ private String data;
+
+ public IsMeasureWithValue(Metric metric, Double value) {
+ this.metric = metric;
+ this.value = value;
+ }
+
+ public IsMeasureWithValue(Metric metric, String data) {
+ this.metric = metric;
+ this.data = data;
+ }
+
+ public boolean matches(Object m) {
+ return ((Measure) m).getMetric().equals(metric) && ObjectUtils.equals(((Measure) m).getValue(), value) && ObjectUtils.equals(((Measure) m).getData(), data);
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java
new file mode 100644
index 00000000000..f4f2b4203a9
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.batch.design;
+
+import edu.emory.mathcs.backport.java.util.Collections;
+import org.apache.commons.lang.ObjectUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.Project;
+
+import java.util.Arrays;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SubProjectDsmDecoratorTest {
+
+ private SubProjectDsmDecorator decorator;
+ private Project module;
+ private DecoratorContext moduleContext;
+ private SonarIndex index;
+ private Directory dir1;
+ private Directory dir2;
+
+ @Before
+ public void prepare() {
+ index = mock(SonarIndex.class);
+ decorator = new SubProjectDsmDecorator(index);
+ module = new Project("foo");
+ moduleContext = mock(DecoratorContext.class);
+
+ dir1 = Directory.create("src/foo1", "foo1");
+ dir1.setId(1);
+ dir2 = Directory.create("src/foo2", "foo2");
+ dir2.setId(2);
+
+ DecoratorContext dir1Context = mock(DecoratorContext.class);
+ when(dir1Context.getResource()).thenReturn(dir1);
+ DecoratorContext dir2Context = mock(DecoratorContext.class);
+ when(dir2Context.getResource()).thenReturn(dir2);
+
+ when(moduleContext.getChildren()).thenReturn(Arrays.asList(dir1Context, dir2Context));
+
+ }
+
+ @Test
+ public void testSubProjectDsmDecoratorNoExecution() {
+ assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+
+ // Should not execute on directory
+ decorator.decorate(Directory.create("foo"), moduleContext);
+ // Should not execute on aggregator projects
+ Project p = new Project("parent");
+ Project child = new Project("child").setParent(p);
+ decorator.decorate(p, moduleContext);
+
+ // Should not do anything if module has no dir
+ when(moduleContext.getChildren()).thenReturn(Collections.emptyList());
+ decorator.decorate(module, moduleContext);
+
+ verify(moduleContext, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void testSubProjectDsmDecoratorNoDependency() {
+ decorator.decorate(module, moduleContext);
+
+ verify(moduleContext, times(4)).saveMeasure(any(Measure.class));
+
+ verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_CYCLES, 0.0));
+ verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_FEEDBACK_EDGES, 0.0));
+ verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_TANGLES, 0.0));
+ verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_EDGES_WEIGHT, 0.0));
+ }
+
+ @Test
+ public void testSubProjectDsmDecoratorDependency() {
+ Dependency dependency = new Dependency(dir1, dir2).setWeight(1).setId(51L);
+ when(index.getEdge(dir1, dir2)).thenReturn(dependency);
+ when(index.hasEdge(dir1, dir2)).thenReturn(true);
+ when(index.getOutgoingEdges(dir1)).thenReturn(Arrays.asList(dependency));
+ when(index.getIncomingEdges(dir2)).thenReturn(Arrays.asList(dependency));
+
+ decorator.decorate(module, moduleContext);
+
+ verify(moduleContext, times(5)).saveMeasure(any(Measure.class));
+
+ verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_CYCLES, 0.0));
+ verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_FEEDBACK_EDGES, 0.0));
+ verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_TANGLES, 0.0));
+ verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_EDGES_WEIGHT, 1.0));
+
+ verify(moduleContext).saveMeasure(
+ isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+ "[{\"i\":1,\"n\":\"src/foo1\",\"q\":\"DIR\",\"v\":[{},{}]},{\"i\":2,\"n\":\"src/foo2\",\"q\":\"DIR\",\"v\":[{\"i\":51,\"w\":1},{}]}]"));
+ }
+
+ Measure isMeasureWithValue(Metric metric, Double value) {
+ return argThat(new IsMeasureWithValue(metric, value));
+ }
+
+ Measure isMeasureWithValue(Metric metric, String data) {
+ return argThat(new IsMeasureWithValue(metric, data));
+ }
+
+ class IsMeasureWithValue extends ArgumentMatcher<Measure> {
+
+ private Metric metric;
+ private Double value;
+ private String data;
+
+ public IsMeasureWithValue(Metric metric, Double value) {
+ this.metric = metric;
+ this.value = value;
+ }
+
+ public IsMeasureWithValue(Metric metric, String data) {
+ this.metric = metric;
+ this.data = data;
+ }
+
+ public boolean matches(Object m) {
+ return ((Measure) m).getMetric().equals(metric) && ObjectUtils.equals(((Measure) m).getValue(), value) && ObjectUtils.equals(((Measure) m).getData(), data);
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
index 82653d849fc..ba81aba16b1 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
@@ -111,10 +111,6 @@ public class MeasureCacheTest {
assertThat(cache.byResource(p)).hasSize(2);
}
- /**
- * This test fails when compression is not enabled for measures. PersistIt seems to be ok with
- * put but fail when reading value.
- */
@Test
public void should_add_measure_with_big_data() throws Exception {
MeasureCache cache = new MeasureCache(caches, metricFinder, techDebtModel);
diff --git a/sonar-graph/src/main/java/org/sonar/graph/DirectedGraph.java b/sonar-graph/src/main/java/org/sonar/graph/DirectedGraph.java
index ecbb0fb6d72..c0d63887b89 100644
--- a/sonar-graph/src/main/java/org/sonar/graph/DirectedGraph.java
+++ b/sonar-graph/src/main/java/org/sonar/graph/DirectedGraph.java
@@ -22,7 +22,7 @@ package org.sonar.graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -32,7 +32,7 @@ public class DirectedGraph<V, E extends Edge<V>> implements DirectedGraphAccesso
private EdgeFactory<V, E> edgeFactory;
private Map<V, Map<V, E>> outgoingEdgesByVertex = new HashMap<V, Map<V, E>>();
private Map<V, Map<V, E>> incomingEdgesByVertex = new HashMap<V, Map<V, E>>();
- private Set<V> vertices = new HashSet<V>();
+ private Set<V> vertices = new LinkedHashSet<V>();
public DirectedGraph() {
}
@@ -60,7 +60,7 @@ public class DirectedGraph<V, E extends Edge<V>> implements DirectedGraphAccesso
private void checkEdgeFacory() {
if (edgeFactory == null) {
throw new IllegalStateException(
- "EdgeFactory<V, E> has not been defined. Please use the 'public E addEdge(V from, V to, E edge)' method.");
+ "EdgeFactory<V, E> has not been defined. Please use the 'public E addEdge(V from, V to, E edge)' method.");
}
}
@@ -133,7 +133,7 @@ public class DirectedGraph<V, E extends Edge<V>> implements DirectedGraphAccesso
public Collection<E> getOutgoingEdges(V from) {
Map<V, E> outgoingEdges = outgoingEdgesByVertex.get(from);
if (outgoingEdges == null) {
- return new HashSet<E>();
+ return new LinkedHashSet<E>();
}
return outgoingEdges.values();
}
@@ -142,7 +142,7 @@ public class DirectedGraph<V, E extends Edge<V>> implements DirectedGraphAccesso
public Collection<E> getIncomingEdges(V to) {
Map<V, E> incomingEdges = incomingEdgesByVertex.get(to);
if (incomingEdges == null) {
- return new HashSet<E>();
+ return new LinkedHashSet<E>();
}
return incomingEdges.values();
}
diff --git a/sonar-graph/src/main/java/org/sonar/graph/Dsm.java b/sonar-graph/src/main/java/org/sonar/graph/Dsm.java
index fc9f386fff2..69e85c251da 100644
--- a/sonar-graph/src/main/java/org/sonar/graph/Dsm.java
+++ b/sonar-graph/src/main/java/org/sonar/graph/Dsm.java
@@ -27,17 +27,17 @@ import java.util.Set;
public class Dsm<V> {
- private V[] vertices;
- private DsmCell[][] cells;
- private int dimension;
- private DirectedGraphAccessor<V, ? extends Edge<V>> graph;
+ private final V[] vertices;
+ private final DsmCell[][] cells;
+ private final int dimension;
+ private final DirectedGraphAccessor<V, ? extends Edge<V>> graph;
+ private boolean atLeastOneDependency = false;
public Dsm(DirectedGraphAccessor<V, ? extends Edge<V>> graph, Collection<V> vertices, Set<Edge> feedbackEdges) {
this.graph = graph;
this.dimension = vertices.size();
- this.cells = new DsmCell[dimension][dimension];
- initVertices(vertices);
- initCells(feedbackEdges);
+ this.vertices = initVertices(vertices);
+ this.cells = initCells(feedbackEdges);
}
public Dsm(DirectedGraphAccessor<V, ? extends Edge<V>> acyclicGraph, Set<Edge> feedbackEdges) {
@@ -48,7 +48,8 @@ public class Dsm<V> {
this(acyclicGraph, acyclicGraph.getVertices(), Collections.<Edge>emptySet());
}
- private void initCells(Set<Edge> feedbackEdges) {
+ private DsmCell[][] initCells(Set<Edge> feedbackEdges) {
+ DsmCell[][] cells = new DsmCell[dimension][dimension];
for (int x = 0; x < dimension; x++) {
for (int y = 0; y < dimension; y++) {
V from = vertices[x];
@@ -56,20 +57,23 @@ public class Dsm<V> {
Edge<V> edge = graph.getEdge(from, to);
if (edge != null) {
+ atLeastOneDependency = true;
boolean isFeedbackEdge = feedbackEdges.contains(edge);
cells[x][y] = new DsmCell(edge, isFeedbackEdge);
}
}
}
+ return cells;
}
- private void initVertices(Collection<V> verticesCol) {
- this.vertices = (V[]) new Object[dimension];
+ private V[] initVertices(Collection<V> verticesCol) {
+ V[] vertices = (V[]) new Object[dimension];
int i = 0;
for (V vertex : verticesCol) {
vertices[i] = vertex;
i++;
}
+ return vertices;
}
public V getVertex(int rowIndex) {
@@ -159,11 +163,21 @@ public class Dsm<V> {
return cell != null ? cell : new DsmCell(null, false);
}
+ /**
+ * @since 5.0
+ */
@CheckForNull
public DsmCell cell(int x, int y) {
return cells[x][y];
}
+ /**
+ * @since 5.0
+ */
+ public boolean hasAtLeastOneDependency() {
+ return atLeastOneDependency;
+ }
+
public V[] getVertices() {
V[] verticesCopy = (V[]) new Object[vertices.length];
System.arraycopy(vertices, 0, verticesCopy, 0, vertices.length);
diff --git a/sonar-graph/src/main/java/org/sonar/graph/DsmScanner.java b/sonar-graph/src/main/java/org/sonar/graph/DsmScanner.java
index 4057748c2c6..beb5318f74b 100644
--- a/sonar-graph/src/main/java/org/sonar/graph/DsmScanner.java
+++ b/sonar-graph/src/main/java/org/sonar/graph/DsmScanner.java
@@ -26,7 +26,7 @@ import java.io.LineNumberReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.Arrays;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Set;
public final class DsmScanner {
@@ -36,7 +36,7 @@ public final class DsmScanner {
private static final char FEEDBACK_EDGE_FLAG = '*';
private final DirectedGraph<String, StringEdge> graph = DirectedGraph.createStringDirectedGraph();
private String[] vertices;
- private Set<Edge> feedbackEdges = new HashSet<Edge>();
+ private Set<Edge> feedbackEdges = new LinkedHashSet<Edge>();
private DsmScanner(Reader reader) {
this.reader = new LineNumberReader(reader);
@@ -49,7 +49,7 @@ public final class DsmScanner {
readRow(i);
}
} catch (IOException e) {
- throw new RuntimeException("Unable to read DSM content.", e); //NOSONAR
+ throw new RuntimeException("Unable to read DSM content.", e); // NOSONAR
}
Dsm<String> dsm = new Dsm<String>(graph, graph.getVertices(), feedbackEdges);
DsmManualSorter.sort(dsm, Arrays.asList(vertices));
diff --git a/sonar-graph/src/main/java/org/sonar/graph/FeedbackCycle.java b/sonar-graph/src/main/java/org/sonar/graph/FeedbackCycle.java
index 81de9669e43..7a22fcd0c0d 100644
--- a/sonar-graph/src/main/java/org/sonar/graph/FeedbackCycle.java
+++ b/sonar-graph/src/main/java/org/sonar/graph/FeedbackCycle.java
@@ -20,15 +20,15 @@
package org.sonar.graph;
+import com.google.common.collect.LinkedHashMultiset;
+import com.google.common.collect.Multiset;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.Multiset;
-
/**
* Note: this class has a natural ordering that is inconsistent with equals
*/
@@ -70,7 +70,7 @@ public final class FeedbackCycle implements Iterable<FeedbackEdge>, Comparable<F
}
private static Multiset<Edge> createBagWithAllEdgesOfCycles(Set<Cycle> cycles) {
- Multiset<Edge> edgesBag = HashMultiset.create();
+ Multiset<Edge> edgesBag = LinkedHashMultiset.create();
for (Cycle cycle : cycles) {
for (Edge edge : cycle.getEdges()) {
edgesBag.add(edge);
@@ -94,7 +94,9 @@ public final class FeedbackCycle implements Iterable<FeedbackEdge>, Comparable<F
@Override
public int compareTo(FeedbackCycle feedbackCycle) {
- if (getTotalOccurrencesOfEdgesInCycle() < feedbackCycle.getTotalOccurrencesOfEdgesInCycle()) {//NOSONAR this class has a natural ordering that is inconsistent with equals
+ if (getTotalOccurrencesOfEdgesInCycle() < feedbackCycle.getTotalOccurrencesOfEdgesInCycle()) {// NOSONAR this class has a natural
+ // ordering that is inconsistent with
+ // equals
return -1;
}
if (getTotalOccurrencesOfEdgesInCycle() == feedbackCycle.getTotalOccurrencesOfEdgesInCycle()) {
diff --git a/sonar-graph/src/main/java/org/sonar/graph/IncrementalCyclesAndFESSolver.java b/sonar-graph/src/main/java/org/sonar/graph/IncrementalCyclesAndFESSolver.java
index 576f461e4c1..cd814c615f4 100644
--- a/sonar-graph/src/main/java/org/sonar/graph/IncrementalCyclesAndFESSolver.java
+++ b/sonar-graph/src/main/java/org/sonar/graph/IncrementalCyclesAndFESSolver.java
@@ -20,12 +20,12 @@
package org.sonar.graph;
import java.util.Collection;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Set;
public class IncrementalCyclesAndFESSolver<V> {
- private Set<Cycle> cycles = new HashSet<Cycle>();
+ private Set<Cycle> cycles = new LinkedHashSet<Cycle>();
private long searchCyclesCalls = 0;
private static final int DEFAULT_MAX_SEARCH_DEPTH_AT_FIRST = 3;
private static final int DEFAULT_MAX_CYCLES_TO_FOUND_BY_ITERATION = 100;
@@ -37,7 +37,7 @@ public class IncrementalCyclesAndFESSolver<V> {
}
public IncrementalCyclesAndFESSolver(DirectedGraphAccessor<V, ? extends Edge> graph, Collection<V> vertices, int maxSearchDepthAtFirst,
- int maxCyclesToFoundByIteration) {
+ int maxCyclesToFoundByIteration) {
iterations++;
CycleDetector<V> cycleDetector = new CycleDetector<V>(graph, vertices);
diff --git a/sonar-graph/src/main/java/org/sonar/graph/MinimumFeedbackEdgeSetSolver.java b/sonar-graph/src/main/java/org/sonar/graph/MinimumFeedbackEdgeSetSolver.java
index 39a5c374b9f..aaebdced22c 100644
--- a/sonar-graph/src/main/java/org/sonar/graph/MinimumFeedbackEdgeSetSolver.java
+++ b/sonar-graph/src/main/java/org/sonar/graph/MinimumFeedbackEdgeSetSolver.java
@@ -19,7 +19,9 @@
*/
package org.sonar.graph;
-import java.util.*;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
public class MinimumFeedbackEdgeSetSolver {
@@ -62,7 +64,7 @@ public class MinimumFeedbackEdgeSetSolver {
* Get edges tagged as feedback.
*/
public Set<Edge> getEdges() {
- Set<Edge> edges = new HashSet<Edge>();
+ Set<Edge> edges = new LinkedHashSet<Edge>();
for (FeedbackEdge fe : feedbackEdges) {
edges.add(fe.getEdge());
}
@@ -70,7 +72,7 @@ public class MinimumFeedbackEdgeSetSolver {
}
private void run() {
- Set<FeedbackEdge> pendingFeedbackEdges = new HashSet<FeedbackEdge>();
+ Set<FeedbackEdge> pendingFeedbackEdges = new LinkedHashSet<FeedbackEdge>();
if (cyclesNumber < maxNumberCyclesForSearchingMinimumFeedback) {
searchFeedbackEdges(0, 0, pendingFeedbackEdges);
} else {
@@ -79,7 +81,7 @@ public class MinimumFeedbackEdgeSetSolver {
}
private void lightResearchForFeedbackEdges() {
- feedbackEdges = new HashSet<FeedbackEdge>();
+ feedbackEdges = new LinkedHashSet<FeedbackEdge>();
for (FeedbackCycle cycle : feedbackCycles) {
for (FeedbackEdge edge : cycle) {
feedbackEdges.add(edge);
@@ -87,7 +89,7 @@ public class MinimumFeedbackEdgeSetSolver {
}
}
minimumFeedbackEdgesWeight = 0;
- for(FeedbackEdge edge : feedbackEdges) {
+ for (FeedbackEdge edge : feedbackEdges) {
minimumFeedbackEdgesWeight += edge.getWeight();
}
}
@@ -103,7 +105,7 @@ public class MinimumFeedbackEdgeSetSolver {
if (level == cyclesNumber) {
minimumFeedbackEdgesWeight = pendingWeight;
- feedbackEdges = new HashSet<FeedbackEdge>(pendingFeedbackEdges);
+ feedbackEdges = new LinkedHashSet<FeedbackEdge>(pendingFeedbackEdges);
return;
}