From: Julien Lancelot Date: Thu, 22 May 2014 17:36:09 +0000 (+0200) Subject: SONAR-5338 Create s WS /api/tests/testable returning list of test cases from a file... X-Git-Tag: 4.4-RC1~863 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=da49db75f2ca87b7c999e89caf0fcd380e2ff767;p=sonarqube.git SONAR-5338 Create s WS /api/tests/testable returning list of test cases from a file and a line --- diff --git a/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsWriter.java b/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsWriter.java index 28e8e633068..663afccc3c3 100644 --- a/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsWriter.java +++ b/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsWriter.java @@ -89,7 +89,7 @@ public class DuplicationsWriter implements ServerComponent { String ref = refByComponentKey.get(componentKey); if (ref == null) { ref = Integer.toString(refByComponentKey.size() + 1); - refByComponentKey.put(componentKey, Integer.toString(refByComponentKey.size() + 1)); + refByComponentKey.put(componentKey, ref); } json.beginObject(); diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 4bd0d16d9b9..db5db86223d 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -145,6 +145,7 @@ import org.sonar.server.startup.*; import org.sonar.server.test.CoverageService; import org.sonar.server.test.ws.CoverageShowAction; import org.sonar.server.test.ws.CoverageWs; +import org.sonar.server.test.ws.TestsTestableAction; import org.sonar.server.test.ws.TestsWs; import org.sonar.server.text.MacroInterpreter; import org.sonar.server.text.RubyTextService; @@ -487,6 +488,7 @@ class ServerComponents { pico.addSingleton(CoverageService.class); pico.addSingleton(CoverageWs.class); pico.addSingleton(CoverageShowAction.class); + pico.addSingleton(TestsTestableAction.class); // graphs and perspective related classes pico.addSingleton(TestablePerspectiveLoader.class); diff --git a/sonar-server/src/main/java/org/sonar/server/test/ws/TestsTestableAction.java b/sonar-server/src/main/java/org/sonar/server/test/ws/TestsTestableAction.java new file mode 100644 index 00000000000..c403bc763fc --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/test/ws/TestsTestableAction.java @@ -0,0 +1,125 @@ +/* + * 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.test.ws; + +import com.google.common.io.Resources; +import org.sonar.api.component.Component; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.test.MutableTestable; +import org.sonar.api.test.TestCase; +import org.sonar.api.test.Testable; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.SnapshotPerspectives; +import org.sonar.server.user.UserSession; + +import java.util.Map; + +import static com.google.common.collect.Maps.newHashMap; + +public class TestsTestableAction implements RequestHandler { + + private static final String KEY = "key"; + private static final String LINE = "line"; + + private final SnapshotPerspectives snapshotPerspectives; + + public TestsTestableAction(SnapshotPerspectives snapshotPerspectives) { + this.snapshotPerspectives = snapshotPerspectives; + } + + void define(WebService.NewController controller) { + WebService.NewAction action = controller.createAction("testable") + .setDescription("Get the list of test cases covering a given file and line. Require Browse permission on file's project") + .setSince("4.4") + .setResponseExample(Resources.getResource(getClass(), "tests-example-testable.json")) + .setHandler(this); + + action + .createParam(KEY) + .setRequired(true) + .setDescription("File key") + .setExampleValue("my_project:/src/foo/Bar.php"); + + action + .createParam(LINE) + .setRequired(true) + .setDescription("Line of the file used to get test cases") + .setExampleValue("10"); + } + + @Override + public void handle(Request request, Response response) { + String fileKey = request.mandatoryParam(KEY); + UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, fileKey); + int line = request.mandatoryParamAsInt(LINE); + + Testable testable = snapshotPerspectives.as(MutableTestable.class, fileKey); + if (testable != null) { + JsonWriter json = response.newJsonWriter().beginObject(); + + Map refByTestPlan = newHashMap(); + Map componentsByKey = newHashMap(); + writeTests(testable, line, refByTestPlan, componentsByKey, json); + writeFiles(refByTestPlan, componentsByKey, json); + + json.endObject().close(); + } + } + + private void writeTests(Testable testable, Integer line, Map refByTestPlan, Map componentsByKey, JsonWriter json) { + json.name("tests").beginArray(); + for (TestCase testCase : testable.testCasesOfLine(line)) { + json.beginObject(); + json.prop("name", testCase.name()); + json.prop("status", testCase.status().name()); + json.prop("durationInMs", testCase.durationInMs()); + + Component testPlan = testCase.testPlan().component(); + Integer ref = refByTestPlan.get(testPlan.key()); + if (ref == null) { + ref = refByTestPlan.size() + 1; + refByTestPlan.put(testPlan.key(), ref); + componentsByKey.put(testPlan.key(), testPlan); + } + json.prop("_ref", Integer.toString(ref)); + json.endObject(); + } + json.endArray(); + } + + private void writeFiles(Map refByTestPlan, Map componentsByKey, JsonWriter json) { + json.name("files").beginObject(); + for (Map.Entry entry : refByTestPlan.entrySet()) { + String componentKey = entry.getKey(); + Integer ref = entry.getValue(); + Component file = componentsByKey.get(componentKey); + json.name(Integer.toString(ref)).beginObject(); + json.prop("key", file.key()); + json.prop("longName", file.longName()); + json.endObject(); + } + json.endObject(); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/test/ws/TestsWs.java b/sonar-server/src/main/java/org/sonar/server/test/ws/TestsWs.java index c5b02ef3a5c..396aaf362a5 100644 --- a/sonar-server/src/main/java/org/sonar/server/test/ws/TestsWs.java +++ b/sonar-server/src/main/java/org/sonar/server/test/ws/TestsWs.java @@ -26,14 +26,20 @@ import org.sonar.api.server.ws.WebService; public class TestsWs implements WebService { + private final TestsTestableAction testsTestableAction; + + public TestsWs(TestsTestableAction testsTestableAction) { + this.testsTestableAction = testsTestableAction; + } + @Override public void define(Context context) { NewController controller = context.createController("api/tests") - .setSince("3.5") + .setSince("4.4") .setDescription("Tests management"); definePlanAction(controller); - defineTestableAction(controller); + testsTestableAction.define(controller); controller.done(); } @@ -53,19 +59,4 @@ public class TestsWs implements WebService { RailsHandler.addJsonOnlyFormatParam(action); } - private void defineTestableAction(NewController controller) { - NewAction action = controller.createAction("testable") - .setDescription("Get the details of a given resource : test plan, test cases covering lines. Requires Browse permission on resource") - .setSince("3.5") - .setInternal(true) - .setHandler(RailsHandler.INSTANCE) - .setResponseExample(Resources.getResource(this.getClass(), "tests-example-testable.json")); - - action.createParam("resource") - .setRequired(true) - .setDescription("id or key of the resource") - .setExampleValue("org.codehaus.sonar.plugins:sonar-cpd-plugin:src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java"); - RailsHandler.addJsonOnlyFormatParam(action); - } - } diff --git a/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-testable.json b/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-testable.json index 9a146d89a7f..92e038af28c 100644 --- a/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-testable.json +++ b/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-testable.json @@ -1,33 +1,26 @@ { - "key": "org.codehaus.sonar.plugins:sonar-cpd-plugin:src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java", - "name": "SonarBridgeEngine.java", - "longName": "src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java", - "coveredLines": [ - 68, - 69, - 70, - 71, - 72, - 73, - 76, - 77 - ], - "coverages": [ + "tests": [ { - "lines": [ - 68, - 69, - 70, - 71, - 72, - 73, - 76, - 77 - ], - "name": "test_engine", + "_ref": "1", + "name": "find_by_params", "status": "OK", - "durationInMs": 2, - "testPlan": "org.codehaus.sonar.plugins:sonar-cpd-plugin:src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java" + "durationInMs": 10 + }, + { + "_ref": "2", + "name": "find_rules_by_characteristics", + "status": "ERROR", + "durationInMs": 97 + } + ], + "files": { + "1": { + "key": "org.codehaus.sonar:sonar-server:src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java", + "longName": "src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java" + }, + "2": { + "key": "org.codehaus.sonar:sonar-server:src/test/java/org/sonar/server/rule/RuleRegistryTest.java", + "longName": "src/test/java/org/sonar/server/rule/RuleRegistryTest.java" } - ] + } } diff --git a/sonar-server/src/test/java/org/sonar/server/test/ws/TestsTestableActionTest.java b/sonar-server/src/test/java/org/sonar/server/test/ws/TestsTestableActionTest.java new file mode 100644 index 00000000000..9c32a328b8e --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/test/ws/TestsTestableActionTest.java @@ -0,0 +1,83 @@ +/* + * 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.test.ws; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.test.MutableTestable; +import org.sonar.api.test.TestCase; +import org.sonar.api.test.TestPlan; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.component.SnapshotPerspectives; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.ws.WsTester; + +import static com.google.common.collect.Lists.newArrayList; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class TestsTestableActionTest { + + static final String FILE_KEY = "src/Foo.java"; + + @Mock + MutableTestable testable; + + WsTester tester; + + @Before + public void setUp() throws Exception { + SnapshotPerspectives snapshotPerspectives = mock(SnapshotPerspectives.class); + when(snapshotPerspectives.as(MutableTestable.class, FILE_KEY)).thenReturn(testable); + tester = new WsTester(new TestsWs(new TestsTestableAction(snapshotPerspectives))); + } + + @Test + public void testable() throws Exception { + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, "SonarQube", FILE_KEY); + + TestCase testCase1 = testCase("test1", TestCase.Status.OK, 10L, "org.foo.BarTest.java", "src/test/java/org/foo/BarTest.java"); + TestCase testCase2 = testCase("test2", TestCase.Status.ERROR, 97L, "org.foo.FileTest.java", "src/test/java/org/foo/FileTest.java"); + when(testable.testCasesOfLine(10)).thenReturn(newArrayList(testCase1, testCase2)); + + WsTester.TestRequest request = tester.newGetRequest("api/tests", "testable").setParam("key", FILE_KEY).setParam("line", "10"); + + request.execute().assertJson(getClass(), "testable.json"); + } + + private TestCase testCase(String name, TestCase.Status status, Long durationInMs, String testPlanKey, String testPlanLongName) { + TestCase testCase = mock(TestCase.class); + when(testCase.name()).thenReturn(name); + when(testCase.status()).thenReturn(status); + when(testCase.durationInMs()).thenReturn(durationInMs); + + TestPlan testPlan = mock(TestPlan.class); + when(testPlan.component()).thenReturn(new ComponentDto().setKey(testPlanKey).setLongName(testPlanLongName)); + when(testCase.testPlan()).thenReturn(testPlan); + return testCase; + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/test/ws/TestsWsTest.java b/sonar-server/src/test/java/org/sonar/server/test/ws/TestsWsTest.java index b0475fdec8e..2cbed21b1cb 100644 --- a/sonar-server/src/test/java/org/sonar/server/test/ws/TestsWsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/test/ws/TestsWsTest.java @@ -24,9 +24,11 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.server.ws.RailsHandler; import org.sonar.api.server.ws.WebService; +import org.sonar.core.component.SnapshotPerspectives; import org.sonar.server.ws.WsTester; import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; public class TestsWsTest { @@ -34,14 +36,14 @@ public class TestsWsTest { @Before public void setUp() throws Exception { - WsTester tester = new WsTester(new TestsWs()); + WsTester tester = new WsTester(new TestsWs(new TestsTestableAction(mock(SnapshotPerspectives.class)))); controller = tester.controller("api/tests"); } @Test public void define_controller() throws Exception { assertThat(controller).isNotNull(); - assertThat(controller.since()).isEqualTo("3.5"); + assertThat(controller.since()).isEqualTo("4.4"); assertThat(controller.description()).isNotEmpty(); assertThat(controller.actions()).hasSize(2); } @@ -61,9 +63,9 @@ public class TestsWsTest { public void define_testable_action() throws Exception { WebService.Action action = controller.action("testable"); assertThat(action).isNotNull(); - assertThat(action.isInternal()).isTrue(); + assertThat(action.isInternal()).isFalse(); assertThat(action.isPost()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); + assertThat(action.handler()).isNotNull(); assertThat(action.responseExampleAsString()).isNotEmpty(); assertThat(action.params()).hasSize(2); } diff --git a/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsTestableActionTest/testable.json b/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsTestableActionTest/testable.json new file mode 100644 index 00000000000..c2ed0fff18f --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsTestableActionTest/testable.json @@ -0,0 +1,26 @@ +{ + "tests": [ + { + "name": "test1", + "status": "OK", + "durationInMs": 10, + "_ref": "1" + }, + { + "name": "test2", + "status": "ERROR", + "durationInMs": 97, + "_ref": "2" + } + ], + "files": { + "1": { + "key": "org.foo.BarTest.java", + "longName": "src/test/java/org/foo/BarTest.java" + }, + "2": { + "key": "org.foo.FileTest.java", + "longName": "src/test/java/org/foo/FileTest.java" + } + } +}