From 7a1de48e56dcefa56e13891205c84adbd86da741 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Wed, 30 Jan 2013 15:29:04 +0100 Subject: [PATCH] SONAR-2501 refactor test API and fix extraction of subgraphs --- .../core/component/PerspectiveBuilder.java | 4 +- .../sonar/core/component/ScanGraphStore.java | 2 +- .../java/org/sonar/core/graph/GraphUtil.java | 3 +- .../java/org/sonar/core/graph/SubGraph.java | 13 ++-- .../org/sonar/core/test/DefaultTestable.java | 33 ++++----- .../org/sonar/core/test/TestPlanBuilder.java | 2 +- .../org/sonar/core/test/TestableBuilder.java | 2 +- .../core/component/ComponentVertexTest.java | 60 ++++++++++++++++ .../org/sonar/core/graph/GraphUtilTest.java | 29 ++++++++ .../org/sonar/core/graph/SubGraphTest.java | 72 +++++++++++++------ .../sonar/core/test/DefaultTestableTest.java | 51 +++++++++++++ .../java/org/sonar/api/test/Testable.java | 6 +- .../app/controllers/resource_controller.rb | 2 +- 13 files changed, 226 insertions(+), 53 deletions(-) create mode 100644 sonar-core/src/test/java/org/sonar/core/component/ComponentVertexTest.java create mode 100644 sonar-core/src/test/java/org/sonar/core/test/DefaultTestableTest.java diff --git a/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java b/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java index 2e7cef03a6a..d74ef70f998 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java +++ b/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java @@ -19,8 +19,6 @@ */ package org.sonar.core.component; -import com.tinkerpop.blueprints.Graph; -import com.tinkerpop.blueprints.Vertex; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; import org.sonar.api.component.Perspective; @@ -50,6 +48,6 @@ public abstract class PerspectiveBuilder implements Batch public abstract T create(ComponentVertex component); - public abstract Object[] path(); + public abstract Object[] storagePath(); } diff --git a/sonar-core/src/main/java/org/sonar/core/component/ScanGraphStore.java b/sonar-core/src/main/java/org/sonar/core/component/ScanGraphStore.java index 3ed2220d7fd..eb91efe5834 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/ScanGraphStore.java +++ b/sonar-core/src/main/java/org/sonar/core/component/ScanGraphStore.java @@ -52,7 +52,7 @@ public class ScanGraphStore { for (PerspectiveBuilder builder : builders) { Perspective perspective = builder.load(component); if (perspective != null) { - Graph subGraph = SubGraph.extract(component.element(), builder.path()); + Graph subGraph = SubGraph.extract(component.element(), builder.storagePath()); String data = writer.write(subGraph); mapper.insert(new GraphDto() .setData(data) diff --git a/sonar-core/src/main/java/org/sonar/core/graph/GraphUtil.java b/sonar-core/src/main/java/org/sonar/core/graph/GraphUtil.java index d0a701064d2..15622d7631a 100644 --- a/sonar-core/src/main/java/org/sonar/core/graph/GraphUtil.java +++ b/sonar-core/src/main/java/org/sonar/core/graph/GraphUtil.java @@ -25,7 +25,6 @@ import com.tinkerpop.blueprints.Element; import com.tinkerpop.blueprints.Vertex; import javax.annotation.CheckForNull; -import javax.annotation.Nullable; import java.util.Iterator; @@ -58,7 +57,7 @@ public class GraphUtil { if (iterator.hasNext()) { result = iterator.next(); if (iterator.hasNext()) { - throw new MultipleElementsException("More than one elements"); + throw new MultipleElementsException("More than one element"); } } return result; diff --git a/sonar-core/src/main/java/org/sonar/core/graph/SubGraph.java b/sonar-core/src/main/java/org/sonar/core/graph/SubGraph.java index 39e4bc86a40..22d3d27c4c9 100644 --- a/sonar-core/src/main/java/org/sonar/core/graph/SubGraph.java +++ b/sonar-core/src/main/java/org/sonar/core/graph/SubGraph.java @@ -19,7 +19,7 @@ */ package org.sonar.core.graph; -import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.tinkerpop.blueprints.Direction; import com.tinkerpop.blueprints.Edge; import com.tinkerpop.blueprints.Graph; @@ -27,7 +27,7 @@ import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.impls.tg.TinkerGraph; import com.tinkerpop.blueprints.util.ElementHelper; -import java.util.List; +import java.util.Set; /** * Not thread-safe @@ -35,7 +35,7 @@ import java.util.List; public class SubGraph { private TinkerGraph sub = new TinkerGraph(); - private List edgesToCopy = Lists.newArrayList(); + private Set edgesToCopy = Sets.newHashSet(); private SubGraph() { } @@ -73,8 +73,11 @@ public class SubGraph { } private Vertex copy(Vertex v) { - Vertex to = sub.addVertex(v.getId()); - ElementHelper.copyProperties(v, to); + Vertex to = sub.getVertex(v.getId()); + if (to == null) { + to = sub.addVertex(v.getId()); + ElementHelper.copyProperties(v, to); + } return to; } } diff --git a/sonar-core/src/main/java/org/sonar/core/test/DefaultTestable.java b/sonar-core/src/main/java/org/sonar/core/test/DefaultTestable.java index 661e7b3ce97..7a5db14987b 100644 --- a/sonar-core/src/main/java/org/sonar/core/test/DefaultTestable.java +++ b/sonar-core/src/main/java/org/sonar/core/test/DefaultTestable.java @@ -19,6 +19,8 @@ */ package org.sonar.core.test; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.tinkerpop.blueprints.Direction; import com.tinkerpop.blueprints.Edge; @@ -35,7 +37,6 @@ import java.util.List; import java.util.SortedSet; import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Sets.newTreeSet; public class DefaultTestable extends BeanVertex implements MutableTestable { @@ -44,40 +45,40 @@ public class DefaultTestable extends BeanVertex implements MutableTestable { return beanGraph().wrap(component, ComponentVertex.class); } - public Collection coveringTestCases() { + public Collection testCases() { List testCases = newArrayList(); - for (Edge edge : getCovers()){ + for (Edge edge : getCovers()) { Vertex testable = edge.getVertex(Direction.OUT); testCases.add(beanGraph().wrap(testable, DefaultTestCase.class)); } return testCases; } - public Collection testCasesCoveringLine(int line) { - List testCases = newArrayList(); - for (Edge edge : getCovers()){ - if (Iterables.contains(getCoveredLines(edge), Long.valueOf(line))){ + public Collection testCasesOfLine(int line) { + ImmutableList.Builder cases = ImmutableList.builder(); + for (Edge edge : getCovers()) { + if (Iterables.contains(testedLines(edge), Long.valueOf(line))) { Vertex vertexTestable = edge.getVertex(Direction.OUT); DefaultTestCase testCase = beanGraph().wrap(vertexTestable, DefaultTestCase.class); - testCases.add(testCase); + cases.add(testCase); } } - return testCases; + return cases.build(); } - public SortedSet coveredLines() { - SortedSet coveredLines = newTreeSet(); - for (Edge edge : getCovers()){ - coveredLines.addAll(getCoveredLines(edge)); + public SortedSet testedLines() { + ImmutableSortedSet.Builder coveredLines = ImmutableSortedSet.naturalOrder(); + for (Edge edge : getCovers()) { + coveredLines.addAll(testedLines(edge)); } - return coveredLines; + return coveredLines.build(); } - private Iterable getCovers(){ + private Iterable getCovers() { return element().getEdges(Direction.IN, "covers"); } - private List getCoveredLines(Edge edge){ + private List testedLines(Edge edge) { return (List) edge.getProperty("lines"); } diff --git a/sonar-core/src/main/java/org/sonar/core/test/TestPlanBuilder.java b/sonar-core/src/main/java/org/sonar/core/test/TestPlanBuilder.java index 5d3cbf207a6..98e7f44f0bc 100644 --- a/sonar-core/src/main/java/org/sonar/core/test/TestPlanBuilder.java +++ b/sonar-core/src/main/java/org/sonar/core/test/TestPlanBuilder.java @@ -56,7 +56,7 @@ public class TestPlanBuilder extends PerspectiveBuilder { } @Override - public Object[] path() { + public Object[] storagePath() { return PATH; } } diff --git a/sonar-core/src/main/java/org/sonar/core/test/TestableBuilder.java b/sonar-core/src/main/java/org/sonar/core/test/TestableBuilder.java index 009773ea117..e19380784f8 100644 --- a/sonar-core/src/main/java/org/sonar/core/test/TestableBuilder.java +++ b/sonar-core/src/main/java/org/sonar/core/test/TestableBuilder.java @@ -56,7 +56,7 @@ public class TestableBuilder extends PerspectiveBuilder { } @Override - public Object[] path() { + public Object[] storagePath() { return PATH; } } diff --git a/sonar-core/src/test/java/org/sonar/core/component/ComponentVertexTest.java b/sonar-core/src/test/java/org/sonar/core/component/ComponentVertexTest.java new file mode 100644 index 00000000000..71953b8fbbe --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/component/ComponentVertexTest.java @@ -0,0 +1,60 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.core.component; + +import com.tinkerpop.blueprints.impls.tg.TinkerGraph; +import org.junit.Test; +import org.sonar.api.component.Component; +import org.sonar.api.component.mock.MockSourceFile; +import org.sonar.api.resources.Qualifiers; +import org.sonar.core.graph.BeanGraph; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ComponentVertexTest { + @Test + public void should_copy() { + BeanGraph beanGraph = new BeanGraph(new TinkerGraph()); + ComponentVertex vertex = beanGraph.createVertex(ComponentVertex.class); + Component file = MockSourceFile.createMain("myproject:org/Foo.java").setName("Foo.java").setQualifier(Qualifiers.FILE); + + vertex.copyFrom(file); + + assertThat(vertex.key()).isEqualTo("myproject:org/Foo.java"); + assertThat(vertex.name()).isEqualTo("Foo.java"); + assertThat(vertex.qualifier()).isEqualTo(Qualifiers.FILE); + } + + @Test + public void should_copy_db_ids() { + BeanGraph beanGraph = new BeanGraph(new TinkerGraph()); + ComponentVertex vertex = beanGraph.createVertex(ComponentVertex.class); + ResourceComponent component = mock(ResourceComponent.class); + when(component.resourceId()).thenReturn(123L); + when(component.snapshotId()).thenReturn(456L); + + vertex.copyFrom(component); + + assertThat(vertex.element().getProperty("rid")).isEqualTo(123L); + assertThat(vertex.element().getProperty("sid")).isEqualTo(456L); + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/graph/GraphUtilTest.java b/sonar-core/src/test/java/org/sonar/core/graph/GraphUtilTest.java index f45c6d0a65c..23d9c938457 100644 --- a/sonar-core/src/test/java/org/sonar/core/graph/GraphUtilTest.java +++ b/sonar-core/src/test/java/org/sonar/core/graph/GraphUtilTest.java @@ -62,4 +62,33 @@ public class GraphUtilTest { GraphUtil.singleAdjacent(a, Direction.OUT, "likes", "hates"); } + + @Test + public void should_extract_single_element() { + TinkerGraph graph = new TinkerGraph(); + Vertex a = graph.addVertex(null); + + Vertex single = GraphUtil.single(graph.getVertices()); + assertThat(single).isSameAs(a); + } + + @Test + public void should_extract_null() { + TinkerGraph graph = new TinkerGraph(); + Vertex single = GraphUtil.single(graph.getVertices()); + assertThat(single).isNull(); + } + + @Test + public void should_fail_to_extract_single_element() { + thrown.expect(MultipleElementsException.class); + thrown.expectMessage("More than one element"); + + TinkerGraph graph = new TinkerGraph(); + graph.addVertex(null); + graph.addVertex(null); + graph.addVertex(null); + + GraphUtil.single(graph.getVertices()); + } } diff --git a/sonar-core/src/test/java/org/sonar/core/graph/SubGraphTest.java b/sonar-core/src/test/java/org/sonar/core/graph/SubGraphTest.java index 0f3d2f92e83..d34d9ff087e 100644 --- a/sonar-core/src/test/java/org/sonar/core/graph/SubGraphTest.java +++ b/sonar-core/src/test/java/org/sonar/core/graph/SubGraphTest.java @@ -25,37 +25,29 @@ import com.tinkerpop.blueprints.Graph; import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.impls.tg.TinkerGraph; import com.tinkerpop.blueprints.util.GraphHelper; -import org.junit.Before; import org.junit.Test; import static org.fest.assertions.Assertions.assertThat; public class SubGraphTest { - TinkerGraph graph; - Vertex a, b, c, d, e; - Edge ab, bc, ad, de; - - @Before - public void before() { - graph = new TinkerGraph(); - a = GraphHelper.addVertex(graph, null, "key", "a"); - b = GraphHelper.addVertex(graph, null, "key", "b"); - c = GraphHelper.addVertex(graph, null, "key", "c"); - d = GraphHelper.addVertex(graph, null, "key", "d"); - e = GraphHelper.addVertex(graph, null, "key", "e"); + @Test + public void should_extract_graph() { + TinkerGraph graph = new TinkerGraph(); + Vertex a = GraphHelper.addVertex(graph, null, "key", "a"); + Vertex b = GraphHelper.addVertex(graph, null, "key", "b"); + Vertex c = GraphHelper.addVertex(graph, null, "key", "c"); + Vertex d = GraphHelper.addVertex(graph, null, "key", "d"); + Vertex e = GraphHelper.addVertex(graph, null, "key", "e"); - ab = GraphHelper.addEdge(graph, null, a, b, "uses"); - bc = GraphHelper.addEdge(graph, null, b, c, "inherits"); - ad = GraphHelper.addEdge(graph, null, a, d, "uses"); - de = GraphHelper.addEdge(graph, null, d, e, "implements"); + Edge ab = GraphHelper.addEdge(graph, null, a, b, "uses"); + Edge bc = GraphHelper.addEdge(graph, null, b, c, "inherits"); + Edge ad = GraphHelper.addEdge(graph, null, a, d, "uses"); + Edge de = GraphHelper.addEdge(graph, null, d, e, "implements"); // a -uses-> b -inherits -> c // a -uses-> d -implements-> e - } - @Test - public void should_extract_graph() { Graph sub = SubGraph.extract(a, "uses", Direction.OUT, "implements", Direction.OUT); // a -uses-> b @@ -75,8 +67,48 @@ public class SubGraphTest { assertThat(sub.getEdge(de.getId()).toString()).isEqualTo(de.toString()); } + @Test + public void should_extract_cyclic_graph() { + TinkerGraph graph = new TinkerGraph(); + Vertex a = GraphHelper.addVertex(graph, null, "key", "a"); + Vertex b = GraphHelper.addVertex(graph, null, "key", "b"); + Vertex c = GraphHelper.addVertex(graph, null, "key", "c"); + Vertex d = GraphHelper.addVertex(graph, null, "key", "d"); + Vertex e = GraphHelper.addVertex(graph, null, "key", "e"); + + Edge ab = GraphHelper.addEdge(graph, null, a, b, "uses"); + Edge bc = GraphHelper.addEdge(graph, null, b, c, "implements"); + Edge ce = GraphHelper.addEdge(graph, null, c, e, "package"); + Edge ad = GraphHelper.addEdge(graph, null, a, d, "uses"); + Edge dc = GraphHelper.addEdge(graph, null, d, c, "implements"); + + // a -uses-> b -implements-> c -package-> e + // a -uses-> d -implements-> c -package-> e + + Graph sub = SubGraph.extract(a, "uses", Direction.OUT, "implements", Direction.OUT, "package", Direction.OUT); + + // same graph + assertThat(sub.getVertices()).hasSize(5); + assertThat(sub.getEdges()).hasSize(5); + } + @Test public void should_check_edge_direction() { + TinkerGraph graph = new TinkerGraph(); + Vertex a = GraphHelper.addVertex(graph, null, "key", "a"); + Vertex b = GraphHelper.addVertex(graph, null, "key", "b"); + Vertex c = GraphHelper.addVertex(graph, null, "key", "c"); + Vertex d = GraphHelper.addVertex(graph, null, "key", "d"); + Vertex e = GraphHelper.addVertex(graph, null, "key", "e"); + + Edge ab = GraphHelper.addEdge(graph, null, a, b, "uses"); + Edge bc = GraphHelper.addEdge(graph, null, b, c, "inherits"); + Edge ad = GraphHelper.addEdge(graph, null, a, d, "uses"); + Edge de = GraphHelper.addEdge(graph, null, d, e, "implements"); + + // a -uses-> b -inherits -> c + // a -uses-> d -implements-> e + Graph sub = SubGraph.extract(a, "uses", Direction.IN /* instead of out */, "implements", Direction.OUT); assertThat(sub.getVertices()).hasSize(1); diff --git a/sonar-core/src/test/java/org/sonar/core/test/DefaultTestableTest.java b/sonar-core/src/test/java/org/sonar/core/test/DefaultTestableTest.java new file mode 100644 index 00000000000..a149c0b7714 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/test/DefaultTestableTest.java @@ -0,0 +1,51 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.core.test; + +import com.tinkerpop.blueprints.impls.tg.TinkerGraph; +import org.junit.Test; +import org.sonar.core.graph.BeanGraph; + +import java.util.Arrays; + +import static org.fest.assertions.Assertions.assertThat; + +public class DefaultTestableTest { + @Test + public void no_covered_lines() { + BeanGraph beanGraph = new BeanGraph(new TinkerGraph()); + + DefaultTestable testable = beanGraph.createVertex(DefaultTestable.class); + assertThat(testable.testedLines()).isEmpty(); + } + + @Test + public void covered_lines() { + BeanGraph beanGraph = new BeanGraph(new TinkerGraph()); + + DefaultTestable testable = beanGraph.createVertex(DefaultTestable.class); + DefaultTestCase testCase1 = beanGraph.createVertex(DefaultTestCase.class); + testCase1.covers(testable, Arrays.asList(10, 11, 12)); + DefaultTestCase testCase2 = beanGraph.createVertex(DefaultTestCase.class); + testCase2.covers(testable, Arrays.asList(12, 48, 49)); + + assertThat(testable.testedLines()).containsOnly(10, 11, 12, 48, 49); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/Testable.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/Testable.java index 9d4a2e120da..67522ea2135 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/test/Testable.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/test/Testable.java @@ -26,10 +26,10 @@ import java.util.SortedSet; public interface Testable extends Perspective { - Collection coveringTestCases(); + Collection testCases(); - Collection testCasesCoveringLine(int line); + Collection testCasesOfLine(int line); - SortedSet coveredLines(); + SortedSet testedLines(); } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb index 3d5b5e2ee1f..d70648b3c21 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb @@ -195,7 +195,7 @@ class ResourceController < ApplicationController @hits_by_line.each_pair do |line_id, hits| line = @lines[line_id-1] if line - line.covered_lines = testable.testCasesCoveringLine(line_id).size if testable && testable.testCasesCoveringLine(line_id).size > 0 + line.covered_lines = testable.testCasesOfLine(line_id).size if testable && testable.testCasesOfLine(line_id).size > 0 line.hits = hits.to_i line.conditions = @conditions_by_line[line_id].to_i line.covered_conditions = @covered_conditions_by_line[line_id].to_i -- 2.39.5