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();
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;
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);
--- /dev/null
+/*
+ * 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<String, Integer> refByTestPlan = newHashMap();
+ Map<String, Component> componentsByKey = newHashMap();
+ writeTests(testable, line, refByTestPlan, componentsByKey, json);
+ writeFiles(refByTestPlan, componentsByKey, json);
+
+ json.endObject().close();
+ }
+ }
+
+ private void writeTests(Testable testable, Integer line, Map<String, Integer> refByTestPlan, Map<String, Component> 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<String, Integer> refByTestPlan, Map<String, Component> componentsByKey, JsonWriter json) {
+ json.name("files").beginObject();
+ for (Map.Entry<String, Integer> 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();
+ }
+}
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();
}
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);
- }
-
}
{
- "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"
}
- ]
+ }
}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
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 {
@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);
}
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);
}
--- /dev/null
+{
+ "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"
+ }
+ }
+}