From 9fea95aa451ac0bdca470ff30c8826815feee8fd Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 16 Jul 2014 12:10:13 +0200 Subject: [PATCH] SONAR-5338 Read test info from the test_data measure --- .../measure/persistence/MeasureDao.java | 4 + .../sonar/server/test/ws/TestsShowAction.java | 87 +++++++++++++++++-- .../measure/persistence/MeasureDaoTest.java | 17 ++++ .../server/test/ws/TestsShowActionTest.java | 61 +++++++++++-- .../org/sonar/server/test/ws/TestsWsTest.java | 3 +- .../show_from_test_data.json | 18 ++++ .../sonar/core/measure/db/MeasureMapper.java | 5 ++ .../sonar/core/measure/db/MeasureMapper.xml | 16 ++++ 8 files changed, 198 insertions(+), 13 deletions(-) create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsShowActionTest/show_from_test_data.json diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java b/server/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java index 7fbed57dc0f..80b279ec1cb 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java @@ -69,6 +69,10 @@ public class MeasureDao extends BaseDao i return dtos; } + public MeasureDto findByComponentKeyAndMetricKey(String componentKey, String metricKey, DbSession session) { + return session.getMapper(MeasureMapper.class).selectByComponentAndMetric(componentKey, metricKey); + } + @Override protected MeasureDto doInsert(DbSession session, MeasureDto item) { throw notImplemented(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsShowAction.java index 581dad7a90b..117a3b25c54 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsShowAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsShowAction.java @@ -21,6 +21,10 @@ package org.sonar.server.test.ws; import com.google.common.io.Resources; +import org.codehaus.staxmate.SMInputFactory; +import org.codehaus.staxmate.in.SMHierarchicCursor; +import org.codehaus.staxmate.in.SMInputCursor; +import org.sonar.api.measures.CoreMetrics; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.RequestHandler; import org.sonar.api.server.ws.Response; @@ -30,15 +34,27 @@ import org.sonar.api.test.TestCase; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.web.UserRole; import org.sonar.core.component.SnapshotPerspectives; +import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; import org.sonar.server.user.UserSession; +import javax.annotation.CheckForNull; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; + +import java.io.StringReader; + public class TestsShowAction implements RequestHandler { private static final String KEY = "key"; + private final DbClient dbClient; private final SnapshotPerspectives snapshotPerspectives; - public TestsShowAction(SnapshotPerspectives snapshotPerspectives) { + public TestsShowAction(DbClient dbClient, SnapshotPerspectives snapshotPerspectives) { + this.dbClient = dbClient; this.snapshotPerspectives = snapshotPerspectives; } @@ -61,15 +77,20 @@ public class TestsShowAction implements RequestHandler { String fileKey = request.mandatoryParam(KEY); UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, fileKey); - MutableTestPlan testPlan = snapshotPerspectives.as(MutableTestPlan.class, fileKey); + String testData = findTestData(fileKey); JsonWriter json = response.newJsonWriter().beginObject(); - if (testPlan != null) { - writeTests(testPlan, json); + if (testData != null) { + writeFromTestData(testData, json); + } else { + MutableTestPlan testPlan = snapshotPerspectives.as(MutableTestPlan.class, fileKey); + if (testPlan != null) { + writeFromTestable(testPlan, json); + } } json.endObject().close(); } - private void writeTests(MutableTestPlan testPlan, JsonWriter json) { + private void writeFromTestable(MutableTestPlan testPlan, JsonWriter json) { json.name("tests").beginArray(); for (TestCase testCase : testPlan.testCases()) { json.beginObject(); @@ -84,4 +105,60 @@ public class TestsShowAction implements RequestHandler { json.endArray(); } + private void writeFromTestData(String data, JsonWriter json) { + SMInputFactory inputFactory = initStax(); + try { + SMHierarchicCursor root = inputFactory.rootElementCursor(new StringReader(data)); + root.advance(); // tests-details + SMInputCursor cursor = root.childElementCursor(); + json.name("tests").beginArray(); + while (cursor.getNext() != null) { + json.beginObject(); + + json.prop("name", cursor.getAttrValue("name")); + json.prop("status", cursor.getAttrValue("status").toUpperCase()); + json.prop("durationInMs", cursor.getAttrValue("time")); + + SMInputCursor errorCursor = cursor.childElementCursor(); + if (errorCursor.getNext() != null) { + json.prop("message", errorCursor.getAttrValue("message")); + + SMInputCursor stackTraceCursor = cursor.childElementCursor(); + if (stackTraceCursor.getNext() != null) { +// json.prop("stackTrace", stackTraceCursor.getAttrValue("")); + } + } + + json.endObject(); + } + json.endArray(); + } catch (XMLStreamException e) { + throw new IllegalStateException("XML is not valid: " + e.getMessage(), e); + } + } + + @CheckForNull + private String findTestData(String fileKey) { + DbSession session = dbClient.openSession(false); + try { + MeasureDto testData = dbClient.measureDao().findByComponentKeyAndMetricKey(fileKey, CoreMetrics.TEST_DATA_KEY, session); + if (testData != null) { + return testData.getData(); + } + } finally { + MyBatis.closeQuietly(session); + } + return null; + } + + private SMInputFactory initStax() { + XMLInputFactory xmlFactory = XMLInputFactory.newInstance(); + xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); + xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE); + // just so it won't try to load DTD in if there's DOCTYPE + xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE); + xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE); + return new SMInputFactory(xmlFactory); + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java index 15afbb3bdad..a93f066059d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java @@ -100,6 +100,23 @@ public class MeasureDaoTest extends AbstractDaoTestCase { assertThat(result.getVariation(5)).isEqualTo(-5d); } + @Test + public void find_by_component_key_and_metric() throws Exception { + setupData("shared"); + + MeasureDto result = dao.findByComponentKeyAndMetricKey("org.struts:struts-core:src/org/struts/RequestContext.java", "ncloc", session); + assertThat(result.getId()).isEqualTo(22); + assertThat(result.getValue()).isEqualTo(10d); + assertThat(result.getKey()).isEqualTo(MeasureKey.of("org.struts:struts-core:src/org/struts/RequestContext.java", "ncloc")); + assertThat(result.getVariation(1)).isEqualTo(1d); + assertThat(result.getVariation(2)).isEqualTo(2d); + assertThat(result.getVariation(3)).isEqualTo(3d); + assertThat(result.getVariation(4)).isEqualTo(4d); + assertThat(result.getVariation(5)).isEqualTo(-5d); + + assertThat(dao.findByComponentKeyAndMetricKey("org.struts:struts-core:src/org/struts/RequestContext.java", "unknown", session)).isNull(); + } + @Test public void exists_by_key() throws Exception { setupData("shared"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsShowActionTest.java index 80e9617d6ca..07f9be4ab61 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsShowActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsShowActionTest.java @@ -21,6 +21,7 @@ package org.sonar.server.test.ws; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -30,6 +31,11 @@ import org.sonar.api.test.MutableTestPlan; import org.sonar.api.test.TestCase; import org.sonar.api.web.UserRole; import org.sonar.core.component.SnapshotPerspectives; +import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.measure.db.MeasureKey; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.db.DbClient; +import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; @@ -44,29 +50,42 @@ public class TestsShowActionTest { static final String TEST_PLAN_KEY = "src/test/java/org/foo/BarTest.java"; + @Mock + DbSession session; + + @Mock + MeasureDao measureDao; + @Mock MutableTestPlan testPlan; + @Mock + SnapshotPerspectives snapshotPerspectives; + WsTester tester; @Before public void setUp() throws Exception { - SnapshotPerspectives snapshotPerspectives = mock(SnapshotPerspectives.class); - when(snapshotPerspectives.as(MutableTestPlan.class, TEST_PLAN_KEY)).thenReturn(testPlan); - tester = new WsTester(new TestsWs(new TestsShowAction(snapshotPerspectives), mock(TestsTestCasesAction.class), mock(TestsCoveredFilesAction.class))); + DbClient dbClient = mock(DbClient.class); + when(dbClient.openSession(false)).thenReturn(session); + when(dbClient.measureDao()).thenReturn(measureDao); + + tester = new WsTester(new TestsWs(new TestsShowAction(dbClient, snapshotPerspectives), mock(TestsTestCasesAction.class), mock(TestsCoveredFilesAction.class))); } @Test public void show() throws Exception { MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, "SonarQube", TEST_PLAN_KEY); + when(snapshotPerspectives.as(MutableTestPlan.class, TEST_PLAN_KEY)).thenReturn(testPlan); + MutableTestCase testCase1 = testCase("test1", TestCase.Status.OK, 10L, 32, null, null); MutableTestCase testCase2 = testCase("test2", TestCase.Status.ERROR, 97L, 21, "expected: but was:", "java.lang.AssertionError: expected: but was:\n\t" + - "at org.junit.Assert.fail(Assert.java:91)\n\t" + - "at org.junit.Assert.failNotEquals(Assert.java:645)\n\t" + - "at org.junit.Assert.assertEquals(Assert.java:126)\n\t" + - "at org.junit.Assert.assertEquals(Assert.java:145)\n"); + "at org.junit.Assert.fail(Assert.java:91)\n\t" + + "at org.junit.Assert.failNotEquals(Assert.java:645)\n\t" + + "at org.junit.Assert.assertEquals(Assert.java:126)\n\t" + + "at org.junit.Assert.assertEquals(Assert.java:145)\n"); when(testPlan.testCases()).thenReturn(newArrayList(testCase1, testCase2)); WsTester.TestRequest request = tester.newGetRequest("api/tests", "show").setParam("key", TEST_PLAN_KEY); @@ -74,6 +93,34 @@ public class TestsShowActionTest { request.execute().assertJson(getClass(), "show.json"); } + @Test + @Ignore + public void show_from_test_data() throws Exception { + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, "SonarQube", TEST_PLAN_KEY); + + when(measureDao.findByComponentKeyAndMetricKey(TEST_PLAN_KEY, "test_data", session)).thenReturn(MeasureDto.createFor(MeasureKey.of(TEST_PLAN_KEY, "test_data")) + .setTextValue("" + + "" + + "" + + " but was:\">" + + " but was:\n\t" + + "at org.junit.Assert.fail(Assert.java:91)\n\t" + + "at org.junit.Assert.failNotEquals(Assert.java:645)\n\t" + + "at org.junit.Assert.assertEquals(Assert.java:126)\n\t" + + "at org.junit.Assert.assertEquals(Assert.java:145)\n" + + "]]>" + + "" + + "" + + "")); + + // TODO failure + + WsTester.TestRequest request = tester.newGetRequest("api/tests", "show").setParam("key", TEST_PLAN_KEY); + + request.execute().assertJson(getClass(), "show_from_test_data.json"); + } + private MutableTestCase testCase(String name, TestCase.Status status, Long durationInMs, int coveredLines, @Nullable String message, @Nullable String stackTrace) { MutableTestCase testCase = mock(MutableTestCase.class); when(testCase.name()).thenReturn(name); diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsWsTest.java index 5dd2c4f185f..50290be7023 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsWsTest.java @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.server.ws.WebService; import org.sonar.core.component.SnapshotPerspectives; +import org.sonar.server.db.DbClient; import org.sonar.server.ws.WsTester; import static org.fest.assertions.Assertions.assertThat; @@ -36,7 +37,7 @@ public class TestsWsTest { @Before public void setUp() throws Exception { SnapshotPerspectives snapshotPerspectives = mock(SnapshotPerspectives.class); - WsTester tester = new WsTester(new TestsWs(new TestsShowAction(snapshotPerspectives), new TestsTestCasesAction(snapshotPerspectives), new TestsCoveredFilesAction(snapshotPerspectives))); + WsTester tester = new WsTester(new TestsWs(new TestsShowAction(mock(DbClient.class), snapshotPerspectives), new TestsTestCasesAction(snapshotPerspectives), new TestsCoveredFilesAction(snapshotPerspectives))); controller = tester.controller("api/tests"); } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsShowActionTest/show_from_test_data.json b/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsShowActionTest/show_from_test_data.json new file mode 100644 index 00000000000..f8832a0f01f --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsShowActionTest/show_from_test_data.json @@ -0,0 +1,18 @@ +{ +"tests": [ + { + "name": "test1", + "status": "OK", + "durationInMs": 10, + "coveredLines": 32 + }, + { + "name": "test2", + "status": "ERROR", + "durationInMs": 97, + "coveredLines": 21, + "message": "expected: but was:", + "stackTrace" : "java.lang.AssertionError: expected: but was:\n\tat org.junit.Assert.fail(Assert.java:91)\n\tat org.junit.Assert.failNotEquals(Assert.java:645)\n\tat org.junit.Assert.assertEquals(Assert.java:126)\n\tat org.junit.Assert.assertEquals(Assert.java:145)\n" + } + ] +} diff --git a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java index 6752fe6f6b3..572d70715cd 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java @@ -22,6 +22,8 @@ package org.sonar.core.measure.db; import org.apache.ibatis.annotations.Param; +import javax.annotation.CheckForNull; + import java.util.List; public interface MeasureMapper { @@ -30,6 +32,9 @@ public interface MeasureMapper { List selectByComponentAndMetrics(@Param("componentKey") String componentKey, @Param("metricKeys") List metricKeys); + @CheckForNull + MeasureDto selectByComponentAndMetric(@Param("componentKey") String componentKey, @Param("metricKey") String metricKey); + long countByKey(@Param("key") MeasureKey key); } diff --git a/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml b/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml index cde157e101f..0964ff196f1 100644 --- a/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml @@ -50,6 +50,22 @@ + +