diff options
50 files changed, 1296 insertions, 1026 deletions
diff --git a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceDbBenchmarkTest.java b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceDbBenchmarkTest.java index 953f036f12e..5ce4e1e6c55 100644 --- a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceDbBenchmarkTest.java +++ b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceDbBenchmarkTest.java @@ -31,7 +31,7 @@ import org.sonar.core.source.db.FileSourceDto; import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDao; import org.sonar.server.source.db.FileSourceDb; -import org.sonar.server.source.index.FileSourcesUpdaterUtil; +import org.sonar.server.source.index.FileSourcesUpdaterHelper; import org.sonar.server.source.index.SourceLineResultSetIterator; import java.io.IOException; @@ -76,7 +76,7 @@ public class SourceDbBenchmarkTest { long start = System.currentTimeMillis(); SourceLineResultSetIterator it = SourceLineResultSetIterator.create(dbClient, connection, 0L, null); while (it.hasNext()) { - FileSourcesUpdaterUtil.Row row = it.next(); + FileSourcesUpdaterHelper.Row row = it.next(); assertThat(row.getUpdateRequests().size()).isEqualTo(NUMBER_OF_LINES); assertThat(row.getFileUuid()).isNotEmpty(); counter.incrementAndGet(); diff --git a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceIndexBenchmarkTest.java b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceIndexBenchmarkTest.java index de4c7e274e8..73943f97720 100644 --- a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceIndexBenchmarkTest.java +++ b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceIndexBenchmarkTest.java @@ -102,7 +102,7 @@ public class SourceIndexBenchmarkTest { // TODO assertions } - private static class SourceIterator implements Iterator<FileSourcesUpdaterUtil.Row> { + private static class SourceIterator implements Iterator<FileSourcesUpdaterHelper.Row> { private final long nbFiles; private final int nbLinesPerFile; private int currentProject = 0; @@ -125,7 +125,7 @@ public class SourceIndexBenchmarkTest { } @Override - public FileSourcesUpdaterUtil.Row next() { + public FileSourcesUpdaterHelper.Row next() { String projectUuid = "P" + currentProject; String fileUuid = "FILE" + count.get(); dataBuilder.clear(); diff --git a/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/IndexPurgeListener.java b/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/IndexPurgeListener.java index 08db1573952..8be40ab93b1 100644 --- a/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/IndexPurgeListener.java +++ b/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/IndexPurgeListener.java @@ -23,16 +23,20 @@ package org.sonar.core.computation.dbcleaner; import org.sonar.api.ServerComponent; import org.sonar.core.purge.PurgeListener; import org.sonar.server.source.index.SourceLineIndexer; +import org.sonar.server.test.index.TestIndexer; public class IndexPurgeListener implements PurgeListener, ServerComponent { private final SourceLineIndexer sourceLineIndexer; + private final TestIndexer testIndexer; - public IndexPurgeListener(SourceLineIndexer sourceLineIndexer) { + public IndexPurgeListener(SourceLineIndexer sourceLineIndexer, TestIndexer testIndexer) { this.sourceLineIndexer = sourceLineIndexer; + this.testIndexer = testIndexer; } @Override public void onComponentDisabling(String uuid) { sourceLineIndexer.deleteByFile(uuid); + testIndexer.deleteByFile(uuid); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java index 3626869b849..696b31b8864 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java @@ -31,6 +31,7 @@ import org.sonar.server.db.DbClient; import org.sonar.server.issue.index.IssueAuthorizationIndexer; import org.sonar.server.issue.index.IssueIndexer; import org.sonar.server.source.index.SourceLineIndexer; +import org.sonar.server.test.index.TestIndexer; public class ComponentCleanerService implements ServerComponent { @@ -39,14 +40,16 @@ public class ComponentCleanerService implements ServerComponent { private final IssueAuthorizationIndexer issueAuthorizationIndexer; private final IssueIndexer issueIndexer; private final SourceLineIndexer sourceLineIndexer; + private final TestIndexer testIndexer; public ComponentCleanerService(DbClient dbClient, PurgeDao purgeDao, IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer, - SourceLineIndexer sourceLineIndexer) { + SourceLineIndexer sourceLineIndexer, TestIndexer testIndexer) { this.dbClient = dbClient; this.purgeDao = purgeDao; this.issueAuthorizationIndexer = issueAuthorizationIndexer; this.issueIndexer = issueIndexer; this.sourceLineIndexer = sourceLineIndexer; + this.testIndexer = testIndexer; } public void delete(String projectKey) { @@ -70,6 +73,7 @@ public class ComponentCleanerService implements ServerComponent { issueAuthorizationIndexer.deleteProject(projectUuid, false); issueIndexer.deleteProject(projectUuid, true); sourceLineIndexer.deleteByProject(projectUuid); + testIndexer.deleteByProject(projectUuid); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java index 30c26cb844d..e4c0b273fa8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java @@ -48,6 +48,7 @@ public class ComputationSteps { PersistEventsStep.class, PersistDuplicationMeasuresStep.class, PersistFileSourcesStep.class, + PersistTestsStep.class, PersistFileDependenciesStep.class, // Switch snapshot and purge @@ -59,6 +60,7 @@ public class ComputationSteps { ApplyPermissionsStep.class, IndexIssuesStep.class, IndexSourceLinesStep.class, + IndexTestsStep.class, IndexViewsStep.class, // Purge of removed views has to be done after Views has been indexed diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java index 0d955de040e..c3180f8e665 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistTestsStep.java @@ -43,6 +43,7 @@ import org.sonar.server.computation.ComputationContext; import org.sonar.server.computation.source.ReportIterator; import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDb; +import org.sonar.server.source.db.FileSourceDb.Test.TestStatus; import java.io.File; import java.util.ArrayList; @@ -177,7 +178,7 @@ public class PersistTestsStep implements ComputationStep { dbTest.setStacktrace(batchTest.getStacktrace()); } if (batchTest.hasStatus()) { - dbTest.setStatus(batchTest.getStatus()); + dbTest.setStatus(TestStatus.valueOf(batchTest.getStatus().name())); } if (batchTest.hasMsg()) { dbTest.setMsg(batchTest.getMsg()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index ee2eb8f31e2..14edd65aa0a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -349,9 +349,11 @@ import org.sonar.server.startup.RenameDeprecatedPropertyKeys; import org.sonar.server.startup.RenameIssueWidgets; import org.sonar.server.startup.ServerMetadataPersister; import org.sonar.server.test.CoverageService; +import org.sonar.server.test.index.TestIndex; +import org.sonar.server.test.index.TestIndexDefinition; +import org.sonar.server.test.index.TestIndexer; import org.sonar.server.test.ws.TestsCoveredFilesAction; -import org.sonar.server.test.ws.TestsShowAction; -import org.sonar.server.test.ws.TestsTestCasesAction; +import org.sonar.server.test.ws.TestsListAction; import org.sonar.server.test.ws.TestsWs; import org.sonar.server.text.MacroInterpreter; import org.sonar.server.text.RubyTextService; @@ -897,9 +899,11 @@ class ServerComponents { // Tests pico.addSingleton(CoverageService.class); pico.addSingleton(TestsWs.class); - pico.addSingleton(TestsTestCasesAction.class); pico.addSingleton(TestsCoveredFilesAction.class); - pico.addSingleton(TestsShowAction.class); + pico.addSingleton(TestsListAction.class); + pico.addSingleton(TestIndexDefinition.class); + pico.addSingleton(TestIndex.class); + pico.addSingleton(TestIndexer.class); // Properties pico.addSingleton(PropertiesWs.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java index 95bafedf6ee..8d24946a043 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java @@ -31,6 +31,7 @@ import org.sonar.server.issue.index.IssueIndexer; import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.source.index.SourceLineIndexer; +import org.sonar.server.test.index.TestIndexer; import org.sonar.server.user.index.UserIndexer; import org.sonar.server.view.index.ViewIndexer; @@ -43,6 +44,7 @@ public class IndexSynchronizer { private final DbClient db; private final IndexClient index; private final SourceLineIndexer sourceLineIndexer; + private final TestIndexer testIndexer; private final IssueAuthorizationIndexer issueAuthorizationIndexer; private final IssueIndexer issueIndexer; private final UserIndexer userIndexer; @@ -55,11 +57,12 @@ public class IndexSynchronizer { * {@link org.sonar.server.issue.index.IssueIndexer} */ public IndexSynchronizer(DbClient db, IndexClient index, SourceLineIndexer sourceLineIndexer, - IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer, + TestIndexer testIndexer, IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer, UserIndexer userIndexer, ViewIndexer viewIndexer, ActivityIndexer activityIndexer) { this.db = db; this.index = index; this.sourceLineIndexer = sourceLineIndexer; + this.testIndexer = testIndexer; this.issueAuthorizationIndexer = issueAuthorizationIndexer; this.issueIndexer = issueIndexer; this.userIndexer = userIndexer; @@ -89,6 +92,9 @@ public class IndexSynchronizer { LOG.info("Index source lines"); sourceLineIndexer.setEnabled(true).index(); + LOG.info("Index tests"); + testIndexer.setEnabled(true).index(); + LOG.info("Index users"); userIndexer.setEnabled(true).index(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/FileSourcesUpdaterUtil.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/FileSourcesUpdaterHelper.java index 392792e81b3..b8b215a0292 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/index/FileSourcesUpdaterUtil.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/FileSourcesUpdaterHelper.java @@ -32,7 +32,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; -public class FileSourcesUpdaterUtil { +public class FileSourcesUpdaterHelper { private static final String SQL_ALL = "SELECT %s FROM file_sources WHERE data_type='%s' "; private static final String AFTER_DATE_FILTER = " AND updated_at>?"; @@ -44,12 +44,14 @@ public class FileSourcesUpdaterUtil { "updated_at", "binary_data" }; + private static final String FIELDS_ONE_LINE = Joiner.on(",").join(FIELDS); - private FileSourcesUpdaterUtil() { + private FileSourcesUpdaterHelper() { // only static stuff } - public static PreparedStatement preparedStatementToSelectFileSources(DbClient dbClient, Connection connection, String dataType, long afterDate, @Nullable String projectUuid) throws SQLException { + public static PreparedStatement preparedStatementToSelectFileSources(DbClient dbClient, Connection connection, String dataType, long afterDate, @Nullable String projectUuid) + throws SQLException { String sql = createSQL(dataType, afterDate, projectUuid); // rows are big, so they are scrolled once at a time (one row in memory at a time) PreparedStatement stmt = dbClient.newScrollingSingleRowSelectStatement(connection, sql); @@ -65,16 +67,16 @@ public class FileSourcesUpdaterUtil { } private static String createSQL(String dataType, long afterDate, @Nullable String projectUuid) { - String sql = String.format(SQL_ALL, Joiner.on(",").join(FIELDS), dataType); + StringBuilder sql = new StringBuilder(String.format(SQL_ALL, FIELDS_ONE_LINE, dataType)); if (afterDate > 0L || projectUuid != null) { if (afterDate > 0L) { - sql += AFTER_DATE_FILTER; + sql.append(AFTER_DATE_FILTER); } if (projectUuid != null) { - sql += PROJECT_FILTER; + sql.append(PROJECT_FILTER); } } - return sql; + return sql.toString(); } public static class Row { diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java index fa4b10807a3..c22ea01b49f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java @@ -39,7 +39,7 @@ import static org.sonar.server.source.index.SourceLineIndexDefinition.FIELD_PROJ /** * Add to Elasticsearch index {@link SourceLineIndexDefinition} the rows of - * db table FILE_SOURCES that are not indexed yet + * db table FILE_SOURCES of type SOURCE that are not indexed yet */ public class SourceLineIndexer extends BaseIndexer { @@ -81,16 +81,16 @@ public class SourceLineIndexer extends BaseIndexer { } } - public long index(Iterator<FileSourcesUpdaterUtil.Row> dbRows) { + public long index(Iterator<FileSourcesUpdaterHelper.Row> dbRows) { BulkIndexer bulk = new BulkIndexer(esClient, SourceLineIndexDefinition.INDEX); return doIndex(bulk, dbRows); } - private long doIndex(BulkIndexer bulk, Iterator<FileSourcesUpdaterUtil.Row> dbRows) { + private long doIndex(BulkIndexer bulk, Iterator<FileSourcesUpdaterHelper.Row> dbRows) { long maxUpdatedAt = 0L; bulk.start(); while (dbRows.hasNext()) { - FileSourcesUpdaterUtil.Row row = dbRows.next(); + FileSourcesUpdaterHelper.Row row = dbRows.next(); addDeleteRequestsForLinesGreaterThan(bulk, row); for (UpdateRequest updateRequest : row.getUpdateRequests()) { bulk.add(updateRequest); @@ -107,7 +107,7 @@ public class SourceLineIndexer extends BaseIndexer { * - same file has now 5 lines * Lines 6 to 10 must be removed from index. */ - private void addDeleteRequestsForLinesGreaterThan(BulkIndexer bulk, FileSourcesUpdaterUtil.Row fileRow) { + private void addDeleteRequestsForLinesGreaterThan(BulkIndexer bulk, FileSourcesUpdaterHelper.Row fileRow) { int numberOfLines = fileRow.getUpdateRequests().size(); SearchRequestBuilder searchRequest = esClient.prepareSearch(SourceLineIndexDefinition.INDEX) .setTypes(SourceLineIndexDefinition.TYPE) diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java index e187700d869..95561fd4c6b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java @@ -38,17 +38,17 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; -import static org.sonar.server.source.index.FileSourcesUpdaterUtil.Row; +import static org.sonar.server.source.index.FileSourcesUpdaterHelper.Row; /** - * Scroll over table FILE_SOURCES and directly parse data required to + * Scroll over table FILE_SOURCES of type SOURCE and directly parse data required to * populate the index sourcelines */ -public class SourceLineResultSetIterator extends ResultSetIterator<FileSourcesUpdaterUtil.Row> { +public class SourceLineResultSetIterator extends ResultSetIterator<FileSourcesUpdaterHelper.Row> { public static SourceLineResultSetIterator create(DbClient dbClient, Connection connection, long afterDate, @Nullable String projectUuid) { try { - return new SourceLineResultSetIterator(FileSourcesUpdaterUtil.preparedStatementToSelectFileSources(dbClient, connection, FileSourceDto.Type.SOURCE, afterDate, + return new SourceLineResultSetIterator(FileSourcesUpdaterHelper.preparedStatementToSelectFileSources(dbClient, connection, FileSourceDto.Type.SOURCE, afterDate, projectUuid)); } catch (SQLException e) { throw new IllegalStateException("Fail to prepare SQL request to select all file sources", e); diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/index/CoveredFileDoc.java b/server/sonar-server/src/main/java/org/sonar/server/test/index/CoveredFileDoc.java new file mode 100644 index 00000000000..69cc1ab524e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/test/index/CoveredFileDoc.java @@ -0,0 +1,61 @@ +/* + * 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.index; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; +import org.sonar.server.search.BaseDoc; + +import java.util.List; +import java.util.Map; + +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_COVERED_FILE_LINES; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_COVERED_FILE_UUID; + +public class CoveredFileDoc extends BaseDoc { + public CoveredFileDoc(Map<String, Object> fields) { + super(fields); + } + + @VisibleForTesting + public CoveredFileDoc() { + super(Maps.<String, Object>newHashMapWithExpectedSize(2)); + } + + public String fileUuid() { + return getField(FIELD_COVERED_FILE_UUID); + } + + public CoveredFileDoc setFileUuid(String fileUuid) { + setField(FIELD_COVERED_FILE_UUID, fileUuid); + return this; + } + + public List<Integer> coveredLines() { + return getField(FIELD_COVERED_FILE_LINES); + } + + public CoveredFileDoc setCoveredLines(List<Integer> coveredLines) { + setField(FIELD_COVERED_FILE_LINES, coveredLines); + return this; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestDoc.java b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestDoc.java index 7d0210e9468..3490c7e0f25 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestDoc.java @@ -24,10 +24,20 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps; import org.sonar.server.search.BaseDoc; +import javax.annotation.CheckForNull; +import java.util.ArrayList; import java.util.List; import java.util.Map; -import static org.sonar.server.test.index.TestIndexDefinition.*; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_COVERED_FILES; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_DURATION_IN_MS; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_FILE_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_MESSAGE; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_NAME; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_PROJECT_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_STACKTRACE; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_STATUS; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_TEST_UUID; public class TestDoc extends BaseDoc { public TestDoc(Map<String, Object> fields) { @@ -35,7 +45,7 @@ public class TestDoc extends BaseDoc { } @VisibleForTesting - TestDoc() { + public TestDoc() { super(Maps.<String, Object>newHashMapWithExpectedSize(10)); } @@ -84,8 +94,9 @@ public class TestDoc extends BaseDoc { return this; } + @CheckForNull public String message() { - return getField(FIELD_MESSAGE); + return getNullableField(FIELD_MESSAGE); } public TestDoc setMessage(String message) { @@ -93,8 +104,9 @@ public class TestDoc extends BaseDoc { return this; } + @CheckForNull public String stackTrace() { - return getField(FIELD_STACKTRACE); + return getNullableField(FIELD_STACKTRACE); } public TestDoc setStackTrace(String stackTrace) { @@ -102,8 +114,10 @@ public class TestDoc extends BaseDoc { return this; } + @CheckForNull public Long durationInMs() { - return getField(FIELD_DURATION_IN_MS); + Number number = getNullableField(FIELD_DURATION_IN_MS); + return number == null ? null : number.longValue(); } public TestDoc setDurationInMs(Long durationInMs) { @@ -111,13 +125,24 @@ public class TestDoc extends BaseDoc { return this; } - // TODO TBE - it should be a CoverageBlockDoc list - public List<Map<String, Object>> coverageBlocks() { - return getField(FIELD_COVERAGE_BLOCKS); - } - - public TestDoc setCoverageBlocks(List<Map<String, Object>> coverageBlocks) { - setField(FIELD_COVERAGE_BLOCKS, coverageBlocks); + public List<CoveredFileDoc> coveredFiles() { + List<Map<String, Object>> coveredFilesAsMaps = getNullableField(FIELD_COVERED_FILES); + if (coveredFilesAsMaps == null) { + return new ArrayList<>(); + } + List<CoveredFileDoc> coveredFiles = new ArrayList<>(); + for (Map<String, Object> coveredFileMap : coveredFilesAsMaps) { + coveredFiles.add(new CoveredFileDoc(coveredFileMap)); + } + return coveredFiles; + } + + public TestDoc setCoveredFiles(List<CoveredFileDoc> coveredFiles) { + List<Map<String, Object>> coveredFilesAsMaps = new ArrayList<>(); + for (CoveredFileDoc coveredFile : coveredFiles) { + coveredFilesAsMaps.add(coveredFile.getFields()); + } + setField(FIELD_COVERED_FILES, coveredFilesAsMaps); return this; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndex.java b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndex.java index af3abccc405..f862d5dd85d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndex.java @@ -20,66 +20,94 @@ package org.sonar.server.test.index; +import com.google.common.base.Function; +import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; +import org.sonar.core.util.NonNullInputFunction; import org.sonar.server.es.BaseIndex; import org.sonar.server.es.EsClient; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import java.util.ArrayList; import java.util.List; import java.util.Map; -import static org.sonar.server.test.index.TestIndexDefinition.*; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_COVERED_FILES; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_COVERED_FILE_LINES; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_COVERED_FILE_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_FILE_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_TEST_UUID; public class TestIndex extends BaseIndex { + private static final Function<Map<String, Object>, TestDoc> CONVERTER = new NonNullInputFunction<Map<String, Object>, TestDoc>() { + @Override + protected TestDoc doApply(Map<String, Object> fields) { + return new TestDoc(fields); + } + }; + public TestIndex(EsClient client) { super(client); } - public List<Map<String, Object>> coveredLines(String testFileUuid, String methodName) { - List<Map<String, Object>> coverageBlocks = new ArrayList<>(); + public List<CoveredFileDoc> coveredFiles(String testUuid) { + List<CoveredFileDoc> coveredFiles = new ArrayList<>(); for (SearchHit hit : getClient().prepareSearch(TestIndexDefinition.INDEX) .setTypes(TestIndexDefinition.TYPE) .setSize(1) - .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.boolFilter() - .must(FilterBuilders.termFilter(FIELD_FILE_UUID, testFileUuid).cache(false)) - .must(FilterBuilders.termFilter(TestIndexDefinition.FIELD_NAME, methodName).cache(false)))) + .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.termFilter(FIELD_TEST_UUID, testUuid))) .get().getHits().getHits()) { - coverageBlocks.addAll(new TestDoc(hit.sourceAsMap()).coverageBlocks()); + coveredFiles.addAll(new TestDoc(hit.sourceAsMap()).coveredFiles()); } - return coverageBlocks; + return coveredFiles; } - public List<TestDoc> testMethods(String testFileUuid) { - List<TestDoc> testDocs = new ArrayList<>(); - - for (SearchHit hit : getClient().prepareSearch(TestIndexDefinition.INDEX) + public SearchResult<TestDoc> searchByTestFileUuid(String testFileUuid, SearchOptions searchOptions) { + SearchRequestBuilder searchRequest = getClient().prepareSearch(TestIndexDefinition.INDEX) .setTypes(TestIndexDefinition.TYPE) - .setSize(10_000) - .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.termFilter(FIELD_FILE_UUID, testFileUuid))) - .get().getHits().getHits()) { - testDocs.add(new TestDoc(hit.sourceAsMap())); - } + .setSize(searchOptions.getLimit()) + .setFrom(searchOptions.getOffset()) + .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.termFilter(FIELD_FILE_UUID, testFileUuid))); - return testDocs; + return new SearchResult<>(searchRequest.get(), CONVERTER); } - public List<TestDoc> testsCovering(String mainFileUuid, int line) { - List<TestDoc> testDocs = new ArrayList<>(); + public SearchResult<TestDoc> searchBySourceFileUuidAndLineNumber(String sourceFileUuid, int lineNumber, SearchOptions searchOptions) { + SearchRequestBuilder searchRequest = getClient().prepareSearch(TestIndexDefinition.INDEX) + .setTypes(TestIndexDefinition.TYPE) + .setSize(searchOptions.getLimit()) + .setFrom(searchOptions.getOffset()) + .setQuery(QueryBuilders.nestedQuery(FIELD_COVERED_FILES, FilterBuilders.boolFilter() + .must(FilterBuilders.termFilter(FIELD_COVERED_FILES + "." + FIELD_COVERED_FILE_UUID, sourceFileUuid).cache(false)) + .must(FilterBuilders.termFilter(FIELD_COVERED_FILES + "." + FIELD_COVERED_FILE_LINES, lineNumber).cache(false)))); + + return new SearchResult<>(searchRequest.get(), CONVERTER); + } + public TestDoc searchByTestUuid(String testUuid) { for (SearchHit hit : getClient().prepareSearch(TestIndexDefinition.INDEX) .setTypes(TestIndexDefinition.TYPE) - .setSize(10_000) - .setQuery(QueryBuilders.nestedQuery(FIELD_COVERAGE_BLOCKS, FilterBuilders.boolFilter() - .must(FilterBuilders.termFilter(FIELD_COVERAGE_BLOCKS + "." + FIELD_COVERAGE_BLOCK_UUID, mainFileUuid).cache(false)) - .must(FilterBuilders.termFilter(FIELD_COVERAGE_BLOCKS + "." + FIELD_COVERAGE_BLOCK_LINES, line).cache(false)))) + .setSize(1) + .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.termFilter(FIELD_TEST_UUID, testUuid))) .get().getHits().getHits()) { - testDocs.add(new TestDoc(hit.sourceAsMap())); + return new TestDoc(hit.sourceAsMap()); } - return testDocs; + throw new IllegalStateException(String.format("Test uuid '%s' not found", testUuid)); + } + + public SearchResult<TestDoc> searchByTestUuid(String testUuid, SearchOptions searchOptions) { + SearchRequestBuilder searchRequest = getClient().prepareSearch(TestIndexDefinition.INDEX) + .setTypes(TestIndexDefinition.TYPE) + .setSize(searchOptions.getLimit()) + .setFrom(searchOptions.getOffset()) + .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.termFilter(FIELD_TEST_UUID, testUuid))); + + return new SearchResult<>(searchRequest.get(), CONVERTER); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexDefinition.java index 7aef0a61fa9..25be407be28 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexDefinition.java +++ b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexDefinition.java @@ -35,9 +35,9 @@ public class TestIndexDefinition implements IndexDefinition { public static final String FIELD_DURATION_IN_MS = "durationInMs"; public static final String FIELD_MESSAGE = "message"; public static final String FIELD_STACKTRACE = "stacktrace"; - public static final String FIELD_COVERAGE_BLOCKS = "coverageBlocks"; - public static final String FIELD_COVERAGE_BLOCK_UUID = "uuid"; - public static final String FIELD_COVERAGE_BLOCK_LINES = "lines"; + public static final String FIELD_COVERED_FILES = "coveredFiles"; + public static final String FIELD_COVERED_FILE_UUID = "sourceFileUuid"; + public static final String FIELD_COVERED_FILE_LINES = "coveredLines"; public static final String FIELD_UPDATED_AT = "updatedAt"; private final Settings settings; @@ -54,19 +54,19 @@ public class TestIndexDefinition implements IndexDefinition { index.setShards(settings); NewIndex.NewIndexType nestedMapping = index.createType(TYPE); - nestedMapping.stringFieldBuilder(FIELD_COVERAGE_BLOCK_UUID).build(); - nestedMapping.createIntegerField(FIELD_COVERAGE_BLOCK_LINES); + nestedMapping.stringFieldBuilder(FIELD_COVERED_FILE_UUID).build(); + nestedMapping.createIntegerField(FIELD_COVERED_FILE_LINES); NewIndex.NewIndexType mapping = index.createType(TYPE); mapping.stringFieldBuilder(FIELD_PROJECT_UUID).build(); mapping.stringFieldBuilder(FIELD_FILE_UUID).build(); mapping.stringFieldBuilder(FIELD_TEST_UUID).build(); - mapping.stringFieldBuilder(FIELD_NAME).build(); + mapping.stringFieldBuilder(FIELD_NAME).disableSearch().build(); mapping.stringFieldBuilder(FIELD_STATUS).disableSearch().build(); mapping.createLongField(FIELD_DURATION_IN_MS); mapping.stringFieldBuilder(FIELD_MESSAGE).disableSearch().build(); mapping.stringFieldBuilder(FIELD_STACKTRACE).disableSearch().build(); - mapping.nestedObjectBuilder(FIELD_COVERAGE_BLOCKS, nestedMapping).build(); + mapping.nestedObjectBuilder(FIELD_COVERED_FILES, nestedMapping).build(); mapping.createDateTimeField(FIELD_UPDATED_AT); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexer.java index 13da026477d..193db400703 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexer.java @@ -29,7 +29,7 @@ import org.sonar.server.db.DbClient; import org.sonar.server.es.BaseIndexer; import org.sonar.server.es.BulkIndexer; import org.sonar.server.es.EsClient; -import org.sonar.server.source.index.FileSourcesUpdaterUtil; +import org.sonar.server.source.index.FileSourcesUpdaterHelper; import javax.annotation.Nullable; import java.sql.Connection; @@ -40,7 +40,7 @@ import static org.sonar.server.test.index.TestIndexDefinition.*; /** * Add to Elasticsearch index {@link TestIndexDefinition} the rows of - * db table FILE_SOURCES that are not indexed yet + * db table FILE_SOURCES of type TEST that are not indexed yet */ public class TestIndexer extends BaseIndexer { @@ -82,16 +82,16 @@ public class TestIndexer extends BaseIndexer { } } - public long index(Iterator<FileSourcesUpdaterUtil.Row> dbRows) { + public long index(Iterator<FileSourcesUpdaterHelper.Row> dbRows) { BulkIndexer bulk = new BulkIndexer(esClient, INDEX); return doIndex(bulk, dbRows); } - private long doIndex(BulkIndexer bulk, Iterator<FileSourcesUpdaterUtil.Row> dbRows) { + private long doIndex(BulkIndexer bulk, Iterator<FileSourcesUpdaterHelper.Row> dbRows) { long maxUpdatedAt = 0L; bulk.start(); while (dbRows.hasNext()) { - FileSourcesUpdaterUtil.Row row = dbRows.next(); + FileSourcesUpdaterHelper.Row row = dbRows.next(); for (UpdateRequest updateRequest : row.getUpdateRequests()) { bulk.add(updateRequest); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestResultSetIterator.java index e8827abf96a..482b867635a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestResultSetIterator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestResultSetIterator.java @@ -27,8 +27,8 @@ import org.sonar.core.source.db.FileSourceDto; import org.sonar.server.db.DbClient; import org.sonar.server.db.ResultSetIterator; import org.sonar.server.source.db.FileSourceDb; -import org.sonar.server.source.index.FileSourcesUpdaterUtil; -import org.sonar.server.source.index.FileSourcesUpdaterUtil.Row; +import org.sonar.server.source.index.FileSourcesUpdaterHelper; +import org.sonar.server.source.index.FileSourcesUpdaterHelper.Row; import javax.annotation.Nullable; @@ -41,17 +41,29 @@ import java.sql.SQLException; import java.util.Date; import java.util.List; -import static org.sonar.server.test.index.TestIndexDefinition.*; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_COVERED_FILES; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_COVERED_FILE_LINES; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_COVERED_FILE_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_DURATION_IN_MS; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_FILE_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_MESSAGE; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_NAME; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_PROJECT_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_STACKTRACE; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_STATUS; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_TEST_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.INDEX; +import static org.sonar.server.test.index.TestIndexDefinition.TYPE; /** - * Scroll over table FILE_SOURCES and directly parse data required to + * Scroll over table FILE_SOURCES of test type and directly parse data required to * populate the index sourcelines */ public class TestResultSetIterator extends ResultSetIterator<Row> { public static TestResultSetIterator create(DbClient dbClient, Connection connection, long afterDate, @Nullable String projectUuid) { try { - return new TestResultSetIterator(FileSourcesUpdaterUtil.preparedStatementToSelectFileSources(dbClient, connection, FileSourceDto.Type.TEST, afterDate, projectUuid)); + return new TestResultSetIterator(FileSourcesUpdaterHelper.preparedStatementToSelectFileSources(dbClient, connection, FileSourceDto.Type.TEST, afterDate, projectUuid)); } catch (SQLException e) { throw new IllegalStateException("Fail to prepare SQL request to select all tests", e); } @@ -89,12 +101,12 @@ public class TestResultSetIterator extends ResultSetIterator<Row> { writer.prop(FIELD_DURATION_IN_MS, test.hasExecutionTimeMs() ? test.getExecutionTimeMs() : null); writer.prop(FIELD_MESSAGE, test.hasMsg() ? test.getMsg() : null); writer.prop(FIELD_STACKTRACE, test.hasStacktrace() ? test.getStacktrace() : null); - writer.name(FIELD_COVERAGE_BLOCKS); + writer.name(FIELD_COVERED_FILES); writer.beginArray(); for (FileSourceDb.Test.CoveredFile coveredFile : test.getCoveredFileList()) { writer.beginObject(); - writer.prop(FIELD_COVERAGE_BLOCK_UUID, coveredFile.getFileUuid()); - writer.name(FIELD_COVERAGE_BLOCK_LINES).valueObject(coveredFile.getCoveredLineList()); + writer.prop(FIELD_COVERED_FILE_UUID, coveredFile.getFileUuid()); + writer.name(FIELD_COVERED_FILE_LINES).valueObject(coveredFile.getCoveredLineList()); writer.endObject(); } writer.endArray(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestAction.java b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestAction.java new file mode 100644 index 00000000000..7af1805edef --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestAction.java @@ -0,0 +1,27 @@ +/* + * 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.sonar.server.ws.WsAction; + +public interface TestAction extends WsAction { + // marker interface +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsCoveredFilesAction.java b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsCoveredFilesAction.java index d18f57b34a8..7617fc6c2e3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsCoveredFilesAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsCoveredFilesAction.java @@ -20,76 +20,100 @@ package org.sonar.server.test.ws; +import com.google.common.base.Function; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; 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.*; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.web.UserRole; -import org.sonar.core.component.SnapshotPerspectives; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; +import org.sonar.server.test.index.CoveredFileDoc; +import org.sonar.server.test.index.TestIndex; import org.sonar.server.user.UserSession; -public class TestsCoveredFilesAction implements RequestHandler { +import java.util.List; +import java.util.Map; - private static final String KEY = "key"; - private static final String TEST = "test"; +public class TestsCoveredFilesAction implements TestAction { - private final SnapshotPerspectives snapshotPerspectives; + public static final String TEST_UUID = "testUuid"; - public TestsCoveredFilesAction(SnapshotPerspectives snapshotPerspectives) { - this.snapshotPerspectives = snapshotPerspectives; + private final DbClient dbClient; + private final TestIndex index; + + public TestsCoveredFilesAction(DbClient dbClient, TestIndex index) { + this.dbClient = dbClient; + this.index = index; } - void define(WebService.NewController controller) { + @Override + public void define(WebService.NewController controller) { WebService.NewAction action = controller.createAction("covered_files") - .setDescription("Get the list of files covered by a test plan. Require Browse permission on file's project") + .setDescription("Get the list of source files covered by a test. Require Browse permission on test file's project") .setSince("4.4") - .setResponseExample(Resources.getResource(getClass(), "tests-example-plan.json")) - .setHandler(this); - - action - .createParam(KEY) - .setRequired(true) - .setDescription("Test plan key") - .setExampleValue("my_project:/src/test/BarTest.java"); + .setResponseExample(Resources.getResource(getClass(), "tests-example-covered-files.json")) + .setHandler(this) + .addPagingParams(100); action - .createParam(TEST) + .createParam(TEST_UUID) .setRequired(true) - .setDescription("Test case used to list files covered by the test plan") - .setExampleValue("my_test"); + .setDescription("Test uuid") + .setExampleValue("ce4c03d6-430f-40a9-b777-ad877c00aa4d"); } @Override public void handle(Request request, Response response) { - String fileKey = request.mandatoryParam(KEY); - UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, fileKey); - String test = request.mandatoryParam(TEST); + String testUuid = request.mandatoryParam(TEST_UUID); + UserSession.get().checkComponentUuidPermission(UserRole.CODEVIEWER, index.searchByTestUuid(testUuid).fileUuid()); - MutableTestPlan testPlan = snapshotPerspectives.as(MutableTestPlan.class, fileKey); + List<CoveredFileDoc> coveredFiles = index.coveredFiles(testUuid); + Map<String, ComponentDto> componentsByUuid = buildComponentsByUuid(coveredFiles); JsonWriter json = response.newJsonWriter().beginObject(); - if (testPlan != null) { - writeTests(testPlan, test, json); + if (!coveredFiles.isEmpty()) { + writeTests(coveredFiles, componentsByUuid, json); } json.endObject().close(); } - private void writeTests(TestPlan<MutableTestCase> testPlan, String test, JsonWriter json) { + private void writeTests(List<CoveredFileDoc> coveredFiles, Map<String, ComponentDto> componentsByUuid, JsonWriter json) { json.name("files").beginArray(); - for (TestCase testCase : testPlan.testCasesByName(test)) { - for (CoverageBlock coverageBlock : testCase.coverageBlocks()) { - json.beginObject(); - Component file = coverageBlock.testable().component(); - json.prop("key", file.key()); - json.prop("longName", file.longName()); - json.prop("coveredLines", coverageBlock.lines().size()); - json.endObject(); - } + for (CoveredFileDoc coveredFile : coveredFiles) { + json.beginObject(); + json.prop("key", componentsByUuid.get(coveredFile.fileUuid()).key()); + json.prop("longName", componentsByUuid.get(coveredFile.fileUuid()).longName()); + json.prop("coveredLines", coveredFile.coveredLines().size()); + json.endObject(); } json.endArray(); } + private Map<String, ComponentDto> buildComponentsByUuid(List<CoveredFileDoc> coveredFiles) { + List<String> sourceFileUuids = Lists.transform(coveredFiles, new Function<CoveredFileDoc, String>() { + @Override + public String apply(CoveredFileDoc coveredFile) { + return coveredFile.fileUuid(); + } + }); + DbSession dbSession = dbClient.openSession(false); + List<ComponentDto> components; + try { + components = dbClient.componentDao().getByUuids(dbSession, sourceFileUuids); + } finally { + MyBatis.closeQuietly(dbSession); + } + return Maps.uniqueIndex(components, new Function<ComponentDto, String>() { + @Override + public String apply(ComponentDto component) { + return component.uuid(); + } + }); + } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsListAction.java b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsListAction.java new file mode 100644 index 00000000000..95cff159b71 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsListAction.java @@ -0,0 +1,223 @@ +/* + * 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.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.io.Resources; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.util.NonNullInputFunction; +import org.sonar.server.db.DbClient; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; +import org.sonar.server.test.index.CoveredFileDoc; +import org.sonar.server.test.index.TestDoc; +import org.sonar.server.test.index.TestIndex; +import org.sonar.server.user.UserSession; + +import javax.annotation.Nullable; + +import java.util.List; +import java.util.Map; + +public class TestsListAction implements TestAction { + public static final String TEST_UUID = "testUuid"; + public static final String TEST_FILE_UUID = "testFileUuid"; + public static final String TEST_FILE_KEY = "testFileKey"; + public static final String SOURCE_FILE_UUID = "sourceFileUuid"; + public static final String SOURCE_FILE_LINE_NUMBER = "sourceFileLineNumber"; + + private final DbClient dbClient; + private final TestIndex testIndex; + + public TestsListAction(DbClient dbClient, TestIndex testIndex) { + this.dbClient = dbClient; + this.testIndex = testIndex; + } + + @Override + public void define(WebService.NewController controller) { + WebService.NewAction action = controller + .createAction("list") + .setDescription( + "Get the list of tests.<br /> " + + "Require Browse permission on file's project.<br /> " + + "One (and only one) of the following combination of parameters must be provided: " + + "<ul>" + + "<li>Test file UUID</li>" + + "<li>Test UUID</li>" + + "<li>Source file UUID and Source file line number</li>" + + "</ul>") + .setSince("5.2") + .setResponseExample(Resources.getResource(getClass(), "tests-example-list.json")) + .setHandler(this) + .addPagingParams(100); + + action + .createParam(TEST_FILE_UUID) + .setDescription("Test file UUID") + .setExampleValue("ce4c03d6-430f-40a9-b777-ad877c00aa4d"); + + action + .createParam(TEST_FILE_KEY) + .setDescription("Test file key") + .setExampleValue("org.codehaus.sonar:sonar-server:src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java"); + + action + .createParam(TEST_UUID) + .setDescription("Test UUID") + .setExampleValue("c526ef20-131b-4486-9357-063fa64b5079"); + + action + .createParam(SOURCE_FILE_UUID) + .setDescription("Source file UUID. Must be provided with the source file line number.") + .setExampleValue("584a89f2-8037-4f7b-b82c-8b45d2d63fb2"); + + action + .createParam(SOURCE_FILE_LINE_NUMBER) + .setDescription("Source file line number. Must be provided with the source file UUID.") + .setExampleValue("10"); + } + + @Override + public void handle(Request request, Response response) { + String testUuid = request.param(TEST_UUID); + String testFileUuid = request.param(TEST_FILE_UUID); + String testFileKey = request.param(TEST_FILE_KEY); + String sourceFileUuid = request.param(SOURCE_FILE_UUID); + Integer sourceFileLineNumber = request.paramAsInt(SOURCE_FILE_LINE_NUMBER); + SearchOptions searchOptions = new SearchOptions().setPage( + request.mandatoryParamAsInt(WebService.Param.PAGE), + request.mandatoryParamAsInt(WebService.Param.PAGE_SIZE) + ); + + DbSession dbSession = dbClient.openSession(false); + SearchResult<TestDoc> tests; + Map<String, ComponentDto> componentsByTestFileUuid; + try { + tests = searchTests(dbSession, testUuid, testFileUuid, testFileKey, sourceFileUuid, sourceFileLineNumber, searchOptions); + componentsByTestFileUuid = buildComponentsByTestFileUuid(dbSession, tests.getDocs()); + } finally { + MyBatis.closeQuietly(dbSession); + } + + JsonWriter json = response.newJsonWriter().beginObject(); + writeTests(tests.getDocs(), componentsByTestFileUuid, json); + searchOptions.writeJson(json, tests.getTotal()); + json.endObject().close(); + } + + private void writeTests(List<TestDoc> tests, Map<String, ComponentDto> componentsByTestFileUuid, JsonWriter json) { + json.name("tests").beginArray(); + for (TestDoc test : tests) { + String fileUuid = test.fileUuid(); + json.beginObject(); + json.prop("testUuid", test.testUuid()); + json.prop("fileUuid", fileUuid); + json.prop("name", test.name()); + json.prop("status", test.status()); + json.prop("durationInMs", test.durationInMs()); + json.prop("message", test.message()); + json.prop("stacktrace", test.stackTrace()); + json.prop("coveredLines", coveredLines(test.coveredFiles())); + json.prop("fileKey", componentsByTestFileUuid.get(fileUuid).key()); + json.prop("fileLongName", componentsByTestFileUuid.get(fileUuid).longName()); + json.endObject(); + } + json.endArray(); + } + + private long coveredLines(List<CoveredFileDoc> coveredFiles) { + long numberOfLinesCovered = 0L; + for (CoveredFileDoc coveredFile : coveredFiles) { + numberOfLinesCovered += coveredFile.coveredLines().size(); + } + + return numberOfLinesCovered; + } + + private Map<String, ComponentDto> buildComponentsByTestFileUuid(DbSession dbSession, List<TestDoc> tests) { + List<String> fileUuids = Lists.transform(tests, new NonNullInputFunction<TestDoc, String>() { + @Override + protected String doApply(TestDoc testDoc) { + return testDoc.fileUuid(); + } + }); + List<ComponentDto> components = dbClient.componentDao().getByUuids(dbSession, fileUuids); + + return Maps.uniqueIndex(components, new NonNullInputFunction<ComponentDto, String>() { + @Override + public String doApply(ComponentDto componentDto) { + return componentDto.uuid(); + } + }); + } + + private SearchResult<TestDoc> searchTests(DbSession dbSession, @Nullable String testUuid, @Nullable String testFileUuid, @Nullable String testFileKey, + @Nullable String sourceFileUuid, @Nullable Integer sourceFileLineNumber, SearchOptions searchOptions) { + if (testUuid != null) { + return searchTestsByTestUuid(dbSession, testUuid, searchOptions); + } else if (testFileUuid != null) { + return searchTestsByTestFileUuid(dbSession, testFileUuid, searchOptions); + } else if (testFileKey != null) { + return searchTestsByTestFileKey(dbSession, testFileKey, searchOptions); + } else if (sourceFileUuid != null && sourceFileLineNumber != null) { + return searchTestsBySourceFileUuidAndLineNumber(dbSession, sourceFileUuid, sourceFileLineNumber, searchOptions); + } + + throw new IllegalArgumentException( + "One (and only one) of the following combination of parameters must be provided: 1) test UUID. 2) test file UUID. 3) test file key. 4) source file UUID and source file line number."); + } + + private SearchResult<TestDoc> searchTestsBySourceFileUuidAndLineNumber(DbSession dbSession, String sourceFileUuid, Integer sourceFileLineNumber, SearchOptions searchOptions) { + checkComponentUuidPermission(dbSession, sourceFileUuid); + return testIndex.searchBySourceFileUuidAndLineNumber(sourceFileUuid, sourceFileLineNumber, searchOptions); + } + + private SearchResult<TestDoc> searchTestsByTestFileKey(DbSession dbSession, String testFileKey, SearchOptions searchOptions) { + UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, testFileKey); + ComponentDto testFile = dbClient.componentDao().getByKey(dbSession, testFileKey); + + return testIndex.searchByTestFileUuid(testFile.uuid(), searchOptions); + } + + private SearchResult<TestDoc> searchTestsByTestFileUuid(DbSession dbSession, String testFileUuid, SearchOptions searchOptions) { + checkComponentUuidPermission(dbSession, testFileUuid); + return testIndex.searchByTestFileUuid(testFileUuid, searchOptions); + } + + private SearchResult<TestDoc> searchTestsByTestUuid(DbSession dbSession, String testUuid, SearchOptions searchOptions) { + checkComponentUuidPermission(dbSession, testIndex.searchByTestUuid(testUuid).fileUuid()); + return testIndex.searchByTestUuid(testUuid, searchOptions); + } + + private void checkComponentUuidPermission(DbSession dbSession, String componentUuid) { + ComponentDto component = dbClient.componentDao().getByUuid(dbSession, componentUuid); + UserSession.get().checkProjectUuidPermission(UserRole.CODEVIEWER, component.projectUuid()); + } +} 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 deleted file mode 100644 index 2b04e68cf0a..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsShowAction.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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.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; -import org.sonar.api.server.ws.WebService; -import org.sonar.api.test.MutableTestPlan; -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(DbClient dbClient, SnapshotPerspectives snapshotPerspectives) { - this.dbClient = dbClient; - this.snapshotPerspectives = snapshotPerspectives; - } - - void define(WebService.NewController controller) { - WebService.NewAction action = controller.createAction("show") - .setDescription("Get the list of test cases of a test plan. Require Browse permission on file's project") - .setSince("4.4") - .setResponseExample(Resources.getResource(getClass(), "tests-example-show.json")) - .setHandler(this); - - action - .createParam(KEY) - .setRequired(true) - .setDescription("Test plan key") - .setExampleValue("my_project:/src/test/BarTest.java"); - } - - @Override - public void handle(Request request, Response response) { - String fileKey = request.mandatoryParam(KEY); - UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, fileKey); - - String testData = findTestData(fileKey); - JsonWriter json = response.newJsonWriter().beginObject(); - 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 writeFromTestable(MutableTestPlan testPlan, JsonWriter json) { - json.name("tests").beginArray(); - for (TestCase testCase : testPlan.testCases()) { - json.beginObject(); - json.prop("name", testCase.name()); - json.prop("status", testCase.status().name()); - json.prop("durationInMs", testCase.durationInMs()); - json.prop("coveredLines", testCase.countCoveredLines()); - json.prop("message", testCase.message()); - json.prop("stackTrace", testCase.stackTrace()); - json.endObject(); - } - 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()); - // time can contain float value, we have to truncate it - json.prop("durationInMs", ((Double) Double.parseDouble(cursor.getAttrValue("time"))).longValue()); - - SMInputCursor errorCursor = cursor.childElementCursor(); - if (errorCursor.getNext() != null) { - json.prop("message", errorCursor.getAttrValue("message")); - json.prop("stackTrace", errorCursor.getElemStringValue()); - } - - 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(session, fileKey, CoreMetrics.TEST_DATA_KEY); - 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/main/java/org/sonar/server/test/ws/TestsTestCasesAction.java b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsTestCasesAction.java deleted file mode 100644 index f4fa253b13c..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsTestCasesAction.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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.base.Preconditions; -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.ComponentDto; -import org.sonar.core.component.SnapshotPerspectives; -import org.sonar.core.persistence.DbSession; -import org.sonar.server.component.ComponentService; -import org.sonar.server.db.DbClient; -import org.sonar.server.user.UserSession; - -import java.util.Map; - -import static com.google.common.collect.Maps.newHashMap; - -public class TestsTestCasesAction implements RequestHandler { - - private static final String KEY = "key"; - private static final String UUID = "uuid"; - private static final String LINE = "line"; - - private final SnapshotPerspectives snapshotPerspectives; - private final ComponentService componentService; - private final DbClient dbClient; - - public TestsTestCasesAction(SnapshotPerspectives snapshotPerspectives, ComponentService componentService, DbClient dbClient) { - this.snapshotPerspectives = snapshotPerspectives; - this.componentService = componentService; - this.dbClient = dbClient; - } - - void define(WebService.NewController controller) { - WebService.NewAction action = controller.createAction("test_cases") - .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) - .setDescription("File key") - .setExampleValue("my_project:/src/foo/Bar.php"); - - action - .createParam(UUID) - .setDescription("File UUID") - .setExampleValue("584a89f2-8037-4f7b-b82c-8b45d2d63fb2"); - - 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.param(KEY); - String fileUuid = request.param(UUID); - Preconditions.checkArgument(fileKey != null || fileUuid != null, "At least one of 'key' or 'uuid' must be provided"); - - if (fileKey != null) { - UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, fileKey); - } else { - ComponentDto component = componentService.getByUuid(fileUuid); - fileKey = component.getKey(); - UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, fileKey); - } - int line = request.mandatoryParamAsInt(LINE); - - Testable testable = snapshotPerspectives.as(MutableTestable.class, fileKey); - JsonWriter json = response.newJsonWriter().beginObject(); - if (testable != null) { - 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) { - DbSession session = dbClient.openSession(false); - try { - for (ComponentDto componentDto : dbClient.componentDao().getByKeys(session, componentsByKey.keySet())) { - componentsByKey.put(componentDto.key(), componentDto); - } - } finally { - session.close(); - } - - 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("uuid", ((ComponentDto) file).uuid()); - json.prop("longName", file.longName()); - json.endObject(); - } - json.endObject(); - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsWs.java b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsWs.java index 274e56ade2a..dcccd19d7ae 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/test/ws/TestsWs.java @@ -24,14 +24,10 @@ import org.sonar.api.server.ws.WebService; public class TestsWs implements WebService { - private final TestsShowAction showAction; - private final TestsTestCasesAction testableAction; - private final TestsCoveredFilesAction planAction; - - public TestsWs(TestsShowAction showAction, TestsTestCasesAction testableAction, TestsCoveredFilesAction planAction) { - this.showAction = showAction; - this.testableAction = testableAction; - this.planAction = planAction; + private final TestAction[] actions; + + public TestsWs(TestAction... actions) { + this.actions = actions; } @Override @@ -40,9 +36,9 @@ public class TestsWs implements WebService { .setSince("4.4") .setDescription("Tests management"); - showAction.define(controller); - testableAction.define(controller); - planAction.define(controller); + for (TestAction action : actions) { + action.define(controller); + } controller.done(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java index 1186c16141e..a49b0e37d48 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java @@ -36,7 +36,13 @@ import org.sonar.server.platform.Platform; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; @@ -228,6 +234,16 @@ public class UserSession { } /** + * Ensures that user implies the specified component permission on a component. If not a {@link org.sonar.server.exceptions.ForbiddenException} is thrown. + */ + public UserSession checkComponentUuidPermission(String permission, String componentUuid) { + if (!hasComponentUuidPermission(permission, componentUuid)) { + throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE); + } + return this; + } + + /** * Does the user have the given project permission for a component key ? */ public boolean hasComponentPermission(String permission, String componentKey) { diff --git a/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-plan.json b/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-covered-files.json index ce75518aefa..ce75518aefa 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-plan.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-covered-files.json diff --git a/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-list.json b/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-list.json new file mode 100644 index 00000000000..2665f1fa409 --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-list.json @@ -0,0 +1,28 @@ +{ + "tests": [ + { + "testUuid": "ce4c03d6-430f-40a9-b777-ad877c00aa4d", + "fileUuid": "c526ef20-131b-4486-9357-063fa64b5079", + "name": "find_by_params", + "status": "OK", + "durationInMs": 10, + "status": "OK", + "durationInMs": 10, + "coveredLines": 89, + "fileKey": "org.codehaus.sonar:sonar-server:src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java", + "fileLongName": "src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java" + }, + { + "testUuid": "584a89f2-8037-4f7b-b82c-8b45d2d63fb2", + "fileUuid": "c526ef20-131b-4486-9357-063fa64b5079", + "name": "find_rules_by_characteristics", + "status": "ERROR", + "durationInMs": 97, + "coveredLines": 0, + "message": "expected:<true> but was:<false>", + "stackTrace": "java.lang.AssertionError: expected:<true> but was:<false>\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\tat sonar.samples.testFailures.moduleA.FailTest.testAWithFailure(FailTest.java:12)\n", + "fileKey": "org.codehaus.sonar:sonar-server:src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java", + "fileLongName": "src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java" + } + ] +} diff --git a/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-show.json b/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-show.json deleted file mode 100644 index d4a27166e1b..00000000000 --- a/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-show.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "tests": [ - { - "name": "find_by_params", - "status": "OK", - "durationInMs": 10, - "coveredLines" : 89 - }, - { - "name": "find_rules_by_characteristics", - "status": "ERROR", - "durationInMs": 97, - "coveredLines" : 0, - "message": "expected:<true> but was:<false>", - "stackTrace" : "java.lang.AssertionError: expected:<true> but was:<false>\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\tat sonar.samples.testFailures.moduleA.FailTest.testAWithFailure(FailTest.java:12)\n" - } - ] -} diff --git a/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-testable.json b/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-testable.json deleted file mode 100644 index 92e038af28c..00000000000 --- a/server/sonar-server/src/main/resources/org/sonar/server/test/ws/tests-example-testable.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "tests": [ - { - "_ref": "1", - "name": "find_by_params", - "status": "OK", - "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/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/IndexPurgeListenerTest.java b/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/IndexPurgeListenerTest.java index 461e8f31312..137e0fb4b3a 100644 --- a/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/IndexPurgeListenerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/IndexPurgeListenerTest.java @@ -22,6 +22,7 @@ package org.sonar.core.computation.dbcleaner; import org.junit.Test; import org.sonar.server.source.index.SourceLineIndexer; +import org.sonar.server.test.index.TestIndexer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -30,12 +31,14 @@ public class IndexPurgeListenerTest { @Test public void call_source_line_indexer() { - SourceLineIndexer indexer = mock(SourceLineIndexer.class); - IndexPurgeListener sut = new IndexPurgeListener(indexer); + SourceLineIndexer sourceLineIndexer = mock(SourceLineIndexer.class); + TestIndexer testIndexer = mock(TestIndexer.class); + IndexPurgeListener sut = new IndexPurgeListener(sourceLineIndexer, testIndexer); sut.onComponentDisabling("123456"); - verify(indexer).deleteByFile("123456"); + verify(sourceLineIndexer).deleteByFile("123456"); + verify(testIndexer).deleteByFile("123456"); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java index d36e4cb2412..5023190cd91 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java @@ -49,12 +49,14 @@ public class ComputationStepsTest { mock(PersistDuplicationMeasuresStep.class), mock(PersistNumberOfDaysSinceLastCommitStep.class), mock(PersistFileSourcesStep.class), - mock(PersistFileDependenciesStep.class) + mock(PersistFileDependenciesStep.class), + mock(PersistTestsStep.class), + mock(IndexTestsStep.class) ); - assertThat(registry.orderedSteps()).hasSize(18); + assertThat(registry.orderedSteps()).hasSize(20); assertThat(registry.orderedSteps().get(0)).isInstanceOf(ParseReportStep.class); - assertThat(registry.orderedSteps().get(17)).isInstanceOf(SendIssueNotificationsStep.class); + assertThat(registry.orderedSteps().get(19)).isInstanceOf(SendIssueNotificationsStep.class); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexTestsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexTestsStepTest.java index 0dd50aec1e5..28eb9aeaa17 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexTestsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexTestsStepTest.java @@ -86,5 +86,6 @@ public class IndexTestsStepTest extends BaseStepTest { TestDoc doc = new TestDoc(docs.get(0).sourceAsMap()); assertThat(doc.projectUuid()).isEqualTo("ABCD"); assertThat(doc.fileUuid()).isEqualTo("FILE1_UUID"); + assertThat(doc.coveredFiles()).isNotEmpty(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java index 9513ab4f3e1..8884d6476ea 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistTestsStepTest.java @@ -30,7 +30,6 @@ import org.sonar.api.utils.System2; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; import org.sonar.batch.protocol.Constants; -import org.sonar.batch.protocol.Constants.TestStatus; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.batch.protocol.output.BatchReport.CoverageDetail; import org.sonar.batch.protocol.output.BatchReportReader; @@ -128,7 +127,7 @@ public class PersistTestsStepTest extends BaseStepTest { writer.writeTests(TEST_FILE_REF_1, batchTests); List<CoverageDetail> coverageDetails = Arrays.asList( newCoverageDetail(1, MAIN_FILE_REF_1) - ); + ); writer.writeCoverageDetails(TEST_FILE_REF_1, coverageDetails); sut.execute(new ComputationContext(new BatchReportReader(reportDir), newProjectDto(PROJECT_UUID))); @@ -167,7 +166,7 @@ public class PersistTestsStepTest extends BaseStepTest { assertThat(test1.getName()).isEqualTo("name#1"); assertThat(test1.getMsg()).isEqualTo("message#1"); assertThat(test1.getStacktrace()).isEqualTo("stacktrace#1"); - assertThat(test1.getStatus()).isEqualTo(TestStatus.FAILURE); + assertThat(test1.getStatus()).isEqualTo(FileSourceDb.Test.TestStatus.FAILURE); assertThat(test1.getExecutionTimeMs()).isEqualTo(1_000); assertThat(test1.getCoveredFileCount()).isEqualTo(1); assertThat(test1.getCoveredFile(0).getCoveredLineList()).containsOnly(1, 2, 3); @@ -233,7 +232,7 @@ public class PersistTestsStepTest extends BaseStepTest { .setTestData(Arrays.asList(FileSourceDb.Test.newBuilder() .setUuid("test-uuid-1") .setName("name#1") - .setStatus(TestStatus.ERROR) + .setStatus(FileSourceDb.Test.TestStatus.ERROR) .setStacktrace("old-stacktrace#1") .setMsg("old-message#1") .setExecutionTimeMs(987_654_321L) @@ -262,7 +261,7 @@ public class PersistTestsStepTest extends BaseStepTest { FileSourceDb.Test test = dto.getTestData().get(0); assertThat(test.getUuid()).isNotEqualTo("test-uuid-1"); assertThat(test.getName()).isEqualTo("name#1"); - assertThat(test.getStatus()).isEqualTo(newBatchTest.getStatus()); + assertThat(test.getStatus()).isEqualTo(FileSourceDb.Test.TestStatus.valueOf(newBatchTest.getStatus().name())); assertThat(test.getMsg()).isEqualTo(newBatchTest.getMsg()); assertThat(test.getStacktrace()).isEqualTo(newBatchTest.getStacktrace()); assertThat(test.getExecutionTimeMs()).isEqualTo(newBatchTest.getDurationInMs()); @@ -273,7 +272,7 @@ public class PersistTestsStepTest extends BaseStepTest { private BatchReport.Test newTest(int id) { return BatchReport.Test.newBuilder() - .setStatus(TestStatus.FAILURE) + .setStatus(Constants.TestStatus.FAILURE) .setName("name#" + id) .setStacktrace("stacktrace#" + id) .setMsg("message#" + id) diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java index 1ba00b7c1e4..a16bbdbe917 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java @@ -59,7 +59,7 @@ import org.sonar.server.permission.PermissionChange; import org.sonar.server.rule.RuleTesting; import org.sonar.server.rule.db.RuleDao; import org.sonar.server.source.db.FileSourceDb; -import org.sonar.server.source.index.FileSourcesUpdaterUtil; +import org.sonar.server.source.index.FileSourcesUpdaterHelper; import org.sonar.server.source.index.SourceLineResultSetIterator; import org.sonar.server.source.index.SourceLineIndexer; import org.sonar.server.tester.ServerTester; @@ -598,7 +598,7 @@ public class IssueServiceMediumTest { .setLine(line) .setScmAuthor(scmAuthor) .build(); - FileSourcesUpdaterUtil.Row row = SourceLineResultSetIterator.toRow(file.projectUuid(), file.uuid(), new Date(), dataBuilder.build()); + FileSourcesUpdaterHelper.Row row = SourceLineResultSetIterator.toRow(file.projectUuid(), file.uuid(), new Date(), dataBuilder.build()); tester.get(SourceLineIndexer.class).index(Iterators.singletonIterator(row)); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java index e3f4bf04ae3..047c30f8e9e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java @@ -140,7 +140,7 @@ public class SourceLineIndexerTest { .setSource("new source") .addAllDuplication(duplications) .build(); - FileSourcesUpdaterUtil.Row dbRow = SourceLineResultSetIterator.toRow("P1", "F1", new Date(), dataBuilder.build()); + FileSourcesUpdaterHelper.Row dbRow = SourceLineResultSetIterator.toRow("P1", "F1", new Date(), dataBuilder.build()); indexer.index(Iterators.singletonIterator(dbRow)); assertThat(countDocuments()).isEqualTo(2L); @@ -214,7 +214,7 @@ public class SourceLineIndexerTest { .setOverallCoveredConditions(bigValue) .build(); - FileSourcesUpdaterUtil.Row row = SourceLineResultSetIterator.toRow("P1", "F1", new Date(), dataBuilder.build()); + FileSourcesUpdaterHelper.Row row = SourceLineResultSetIterator.toRow("P1", "F1", new Date(), dataBuilder.build()); indexer.index(Iterators.singletonIterator(row)); List<SearchHit> hits = getDocuments(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java index 0429ac3fd18..205e4e4e35a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java @@ -71,7 +71,7 @@ public class SourceLineResultSetIteratorTest { iterator = SourceLineResultSetIterator.create(dbClient, connection, 0L, null); assertThat(iterator.hasNext()).isTrue(); - FileSourcesUpdaterUtil.Row row = iterator.next(); + FileSourcesUpdaterHelper.Row row = iterator.next(); assertThat(row.getProjectUuid()).isEqualTo("P1"); assertThat(row.getFileUuid()).isEqualTo("F1"); assertThat(row.getUpdatedAt()).isEqualTo(1416239042000L); @@ -110,7 +110,7 @@ public class SourceLineResultSetIteratorTest { FileSourceTesting.updateDataColumn(connection, "F1", dataBuilder.build()); iterator = SourceLineResultSetIterator.create(dbClient, connection, 0L, null); - FileSourcesUpdaterUtil.Row row = iterator.next(); + FileSourcesUpdaterHelper.Row row = iterator.next(); assertThat(row.getProjectUuid()).isEqualTo("P1"); assertThat(row.getFileUuid()).isEqualTo("F1"); assertThat(row.getUpdatedAt()).isEqualTo(1416239042000L); @@ -157,7 +157,7 @@ public class SourceLineResultSetIteratorTest { iterator = SourceLineResultSetIterator.create(dbClient, connection, 0L, "P1"); - FileSourcesUpdaterUtil.Row row = iterator.next(); + FileSourcesUpdaterHelper.Row row = iterator.next(); assertThat(row.getProjectUuid()).isEqualTo("P1"); assertThat(row.getFileUuid()).isEqualTo("F1"); @@ -174,7 +174,7 @@ public class SourceLineResultSetIteratorTest { iterator = SourceLineResultSetIterator.create(dbClient, connection, 1400000000000L, "P1"); - FileSourcesUpdaterUtil.Row row = iterator.next(); + FileSourcesUpdaterHelper.Row row = iterator.next(); assertThat(row.getProjectUuid()).isEqualTo("P1"); assertThat(row.getFileUuid()).isEqualTo("F1"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/db/TestTesting.java b/server/sonar-server/src/test/java/org/sonar/server/test/db/TestTesting.java index 7c95d51d9d7..9a451e4fd0e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/test/db/TestTesting.java +++ b/server/sonar-server/src/test/java/org/sonar/server/test/db/TestTesting.java @@ -23,9 +23,9 @@ package org.sonar.server.test.db; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.math.RandomUtils; import org.sonar.api.utils.internal.Uuids; -import org.sonar.batch.protocol.Constants; import org.sonar.core.source.db.FileSourceDto; import org.sonar.server.source.db.FileSourceDb; +import org.sonar.server.source.db.FileSourceDb.Test.TestStatus; import java.io.IOException; import java.sql.Connection; @@ -52,28 +52,6 @@ public class TestTesting { stmt.close(); } - public static List<FileSourceDb.Test> newFakeTests(int numberOfTests) throws IOException { - List<FileSourceDb.Test> tests = new ArrayList<>(); - for (int i = 1; i <= numberOfTests; i++) { - FileSourceDb.Test.Builder test = FileSourceDb.Test.newBuilder() - .setUuid("TEST_FILE_UUID_" + i) - .setName("NAME_" + i) - .setStatus(Constants.TestStatus.FAILURE) - .setStacktrace("STACKTRACE_" + i) - .setMsg("MESSAGE_" + i) - .setExecutionTimeMs(i); - for (int j = 1; j <= numberOfTests; j++) { - test.addCoveredFile( - FileSourceDb.Test.CoveredFile.newBuilder() - .setFileUuid("MAIN_FILE_UUID_" + j) - .addCoveredLine(j) - ); - } - tests.add(test.build()); - } - return tests; - } - /** * Generate random data. */ @@ -83,7 +61,7 @@ public class TestTesting { FileSourceDb.Test.Builder test = FileSourceDb.Test.newBuilder() .setUuid(Uuids.create()) .setName(RandomStringUtils.randomAlphanumeric(20)) - .setStatus(Constants.TestStatus.FAILURE) + .setStatus(TestStatus.FAILURE) .setStacktrace(RandomStringUtils.randomAlphanumeric(50)) .setMsg(RandomStringUtils.randomAlphanumeric(30)) .setExecutionTimeMs(RandomUtils.nextLong()); @@ -93,7 +71,7 @@ public class TestTesting { FileSourceDb.Test.CoveredFile.newBuilder() .setFileUuid(Uuids.create()) .addCoveredLine(RandomUtils.nextInt(500)) - ); + ); } tests.add(test.build()); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/index/TestIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/test/index/TestIndexTest.java index c02292fdf37..5b841bc2cf7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/test/index/TestIndexTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/test/index/TestIndexTest.java @@ -25,8 +25,10 @@ import org.junit.ClassRule; import org.junit.Test; import org.sonar.api.config.Settings; import org.sonar.server.es.EsTester; +import org.sonar.server.es.SearchOptions; -import java.util.*; +import java.util.Arrays; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -37,67 +39,101 @@ public class TestIndexTest { TestIndex sut = new TestIndex(es.client()); @Before - public void before() throws Exception { + public void setup() throws Exception { es.truncateIndices(); } @Test - public void lines_covered_a_test_method() throws Exception { + public void coveredFiles() throws Exception { es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, newTestDoc("1", newCoverageBlock("3"), newCoverageBlock("4"), newCoverageBlock("5")), newTestDoc("2", newCoverageBlock("5"), newCoverageBlock("6"), newCoverageBlock("7"))); - List<Map<String, Object>> result = sut.coveredLines("uuid-1", "name-1"); + List<CoveredFileDoc> result = sut.coveredFiles("uuid-1"); assertThat(result).hasSize(3); - assertThat(result.get(0).get("uuid")).isEqualTo("main-uuid-3"); - assertThat((List<Integer>)result.get(0).get("lines")).containsOnly(25, 33, 82); + assertThat(result).extractingResultOf("fileUuid").containsOnly("main-uuid-3", "main-uuid-4", "main-uuid-5"); + assertThat(result.get(0).coveredLines()).containsOnly(25, 33, 82); } @Test - public void test_methods() throws Exception { + public void searchByTestFileUuid() throws Exception { es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, newTestDoc("1", newCoverageBlock("3"), newCoverageBlock("4"), newCoverageBlock("5")), - newTestDoc("1", newCoverageBlock("5"), newCoverageBlock("6"), newCoverageBlock("7"))); + newTestDoc("1", newCoverageBlock("5"), newCoverageBlock("6"), newCoverageBlock("7")), + newTestDoc("2", newCoverageBlock("5"), newCoverageBlock("6"), newCoverageBlock("7"))); - List<TestDoc> result = sut.testMethods("uuid-1"); + List<TestDoc> result = sut.searchByTestFileUuid("file-uuid-1", searchOptions()).getDocs(); assertThat(result).hasSize(2); + assertThat(result).extractingResultOf("name").containsOnly("name-1"); } @Test - public void test_covering() throws Exception { - List<Map<String, Object>> coverageBlocks = new ArrayList<>(); - coverageBlocks.add(newCoverageBlock("1")); - coverageBlocks.add(newCoverageBlock("2")); - coverageBlocks.add(newCoverageBlock("3")); + public void searchBySourceFileUuidAndLineNumber() throws Exception { + es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, + newTestDoc("1", newCoverageBlock("10"), newCoverageBlock("11"), newCoverageBlock("12")), + newTestDoc("2", newCoverageBlock("3"), newCoverageBlock("4"), newCoverageBlock("5")), + newTestDoc("3", newCoverageBlock("5"), newCoverageBlock("6"), newCoverageBlock("7"))); + + List<TestDoc> result = sut.searchBySourceFileUuidAndLineNumber("main-uuid-5", 82, searchOptions()).getDocs(); + + assertThat(result).hasSize(2); + assertThat(result).extractingResultOf("name").containsOnly("name-2", "name-3"); + } + @Test + public void searchByTestUuid() throws Exception { es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, newTestDoc("1", newCoverageBlock("3"), newCoverageBlock("4"), newCoverageBlock("5")), - newTestDoc("1", newCoverageBlock("5"), newCoverageBlock("6"), newCoverageBlock("7"))); + newTestDoc("2", newCoverageBlock("5"), newCoverageBlock("6"), newCoverageBlock("7"))); - List<TestDoc> result = sut.testsCovering("main-uuid-5", 82); + TestDoc test = sut.searchByTestUuid("uuid-1"); - assertThat(result).hasSize(2); - assertThat(result.get(0).name()).isEqualTo("name-1"); - assertThat(result.get(0).coverageBlocks().get(0).get("uuid")).isEqualTo("main-uuid-3"); + assertThat(test.testUuid()).isEqualTo("uuid-1"); + assertThat(test.fileUuid()).isEqualTo("file-uuid-1"); + assertThat(test.name()).isEqualTo("name-1"); + assertThat(test.durationInMs()).isEqualTo(1L); + assertThat(test.status()).isEqualTo("status-1"); + assertThat(test.message()).isEqualTo("message-1"); + assertThat(test.coveredFiles()).hasSize(3); + assertThat(test.coveredFiles()).extractingResultOf("fileUuid").containsOnly("main-uuid-3", "main-uuid-4", "main-uuid-5"); } - private Map<String, Object> newCoverageBlock(String id) { - Map<String, Object> coverageBlock = new HashMap<>(); - coverageBlock.put("key", "project:file-" + id); - coverageBlock.put("uuid", "main-uuid-" + id); - coverageBlock.put("lines", Arrays.asList(25, 33, 82)); - return coverageBlock; + @Test + public void searchByTestUuid_with_SearchOptions() throws Exception { + es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, + newTestDoc("1", newCoverageBlock("3"), newCoverageBlock("4"), newCoverageBlock("5")), + newTestDoc("2", newCoverageBlock("5"), newCoverageBlock("6"), newCoverageBlock("7"))); + + List<TestDoc> result = sut.searchByTestUuid("uuid-1", searchOptions()).getDocs(); + + assertThat(result).hasSize(1); + assertThat(result.get(0).testUuid()).isEqualTo("uuid-1"); } - private TestDoc newTestDoc(String id, Map<String, Object>... coverageBlocks) { + private CoveredFileDoc newCoverageBlock(String id) { + return new CoveredFileDoc() + .setFileUuid("main-uuid-" + id) + .setCoveredLines(Arrays.asList(25, 33, 82)); + } + + private TestDoc newTestDoc(String id, CoveredFileDoc... coveredFiles) { return new TestDoc() + .setUuid("uuid-" + id) .setName("name-" + id) .setMessage("message-" + id) .setStackTrace("stacktrace-" + id) .setStatus("status-" + id) - .setFileUuid("uuid-" + id) - .setCoverageBlocks(Arrays.asList(coverageBlocks)); + .setDurationInMs(Long.valueOf(id)) + .setFileUuid("file-uuid-" + id) + .setProjectUuid("project-uuid-" + id) + .setCoveredFiles(Arrays.asList(coveredFiles)); + } + + private SearchOptions searchOptions() { + return new SearchOptions() + .setLimit(100) + .setOffset(0); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/index/TestIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/test/index/TestIndexerTest.java index 5356f4d1885..9445800e654 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/test/index/TestIndexerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/test/index/TestIndexerTest.java @@ -31,12 +31,12 @@ import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.sonar.api.config.Settings; -import org.sonar.batch.protocol.Constants; import org.sonar.core.persistence.DbTester; import org.sonar.server.db.DbClient; import org.sonar.server.es.EsTester; import org.sonar.server.source.db.FileSourceDb; -import org.sonar.server.source.index.FileSourcesUpdaterUtil; +import org.sonar.server.source.db.FileSourceDb.Test.TestStatus; +import org.sonar.server.source.index.FileSourcesUpdaterHelper; import org.sonar.server.test.db.TestTesting; import org.sonar.test.DbTests; import org.sonar.test.TestUtils; @@ -50,7 +50,16 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; -import static org.sonar.server.test.index.TestIndexDefinition.*; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_DURATION_IN_MS; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_FILE_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_MESSAGE; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_NAME; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_PROJECT_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_STACKTRACE; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_STATUS; +import static org.sonar.server.test.index.TestIndexDefinition.FIELD_TEST_UUID; +import static org.sonar.server.test.index.TestIndexDefinition.INDEX; +import static org.sonar.server.test.index.TestIndexDefinition.TYPE; @Category(DbTests.class) public class TestIndexerTest { @@ -117,11 +126,11 @@ public class TestIndexerTest { indexTest("P1", "F1", "T1", "U111"); indexTest("P1", "F2", "T1", "U121"); - FileSourcesUpdaterUtil.Row dbRow = TestResultSetIterator.toRow("P1", "F1", new Date(), Arrays.asList( + FileSourcesUpdaterHelper.Row dbRow = TestResultSetIterator.toRow("P1", "F1", new Date(), Arrays.asList( FileSourceDb.Test.newBuilder() .setUuid("U111") .setName("NAME_1") - .setStatus(Constants.TestStatus.FAILURE) + .setStatus(TestStatus.FAILURE) .setMsg("NEW_MESSAGE_1") .setStacktrace("NEW_STACKTRACE_1") .setExecutionTimeMs(123_456L) diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/index/TestResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/test/index/TestResultSetIteratorTest.java index 8aa38143d7e..432e924ec1b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/test/index/TestResultSetIteratorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/test/index/TestResultSetIteratorTest.java @@ -30,12 +30,15 @@ import org.junit.experimental.categories.Category; import org.sonar.core.persistence.DbTester; import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDb; -import org.sonar.server.source.index.FileSourcesUpdaterUtil; +import org.sonar.server.source.db.FileSourceDb.Test.TestStatus; +import org.sonar.server.source.index.FileSourcesUpdaterHelper; import org.sonar.server.source.index.SourceLineResultSetIteratorTest; import org.sonar.server.test.db.TestTesting; import org.sonar.test.DbTests; +import java.io.IOException; import java.sql.Connection; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -72,10 +75,10 @@ public class TestResultSetIteratorTest { @Test public void traverse_db() throws Exception { db.prepareDbUnit(getClass(), "shared.xml"); - TestTesting.updateDataColumn(connection, "F1", TestTesting.newFakeTests(3)); + TestTesting.updateDataColumn(connection, "F1", newFakeTests(3)); sut = TestResultSetIterator.create(dbClient, connection, 0L, null); - FileSourcesUpdaterUtil.Row row = sut.next(); + FileSourcesUpdaterHelper.Row row = sut.next(); assertThat(row.getProjectUuid()).isEqualTo("P1"); assertThat(row.getFileUuid()).isEqualTo("F1"); assertThat(row.getUpdatedAt()).isEqualTo(1416239042000L); @@ -110,7 +113,7 @@ public class TestResultSetIteratorTest { TestTesting.updateDataColumn(connection, "F1", tests); sut = TestResultSetIterator.create(dbClient, connection, 0L, null); - FileSourcesUpdaterUtil.Row row = sut.next(); + FileSourcesUpdaterHelper.Row row = sut.next(); assertThat(row.getProjectUuid()).isEqualTo("P1"); assertThat(row.getFileUuid()).isEqualTo("F1"); @@ -130,7 +133,7 @@ public class TestResultSetIteratorTest { TestIndexDefinition.FIELD_STACKTRACE, TestIndexDefinition.FIELD_MESSAGE, TestIndexDefinition.FIELD_STATUS, - TestIndexDefinition.FIELD_COVERAGE_BLOCKS + TestIndexDefinition.FIELD_COVERED_FILES ); } @@ -145,11 +148,11 @@ public class TestResultSetIteratorTest { @Test public void filter_by_project() throws Exception { db.prepareDbUnit(getClass(), "filter_by_project.xml"); - TestTesting.updateDataColumn(connection, "F1", TestTesting.newFakeTests(1)); + TestTesting.updateDataColumn(connection, "F1", newFakeTests(1)); sut = TestResultSetIterator.create(dbClient, connection, 0L, "P1"); - FileSourcesUpdaterUtil.Row row = sut.next(); + FileSourcesUpdaterHelper.Row row = sut.next(); assertThat(row.getProjectUuid()).isEqualTo("P1"); assertThat(row.getFileUuid()).isEqualTo("F1"); @@ -160,11 +163,11 @@ public class TestResultSetIteratorTest { @Test public void filter_by_project_and_date() throws Exception { db.prepareDbUnit(getClass(), "filter_by_project_and_date.xml"); - TestTesting.updateDataColumn(connection, "F1", TestTesting.newFakeTests(1)); + TestTesting.updateDataColumn(connection, "F1", newFakeTests(1)); sut = TestResultSetIterator.create(dbClient, connection, 1400000000000L, "P1"); - FileSourcesUpdaterUtil.Row row = sut.next(); + FileSourcesUpdaterHelper.Row row = sut.next(); assertThat(row.getProjectUuid()).isEqualTo("P1"); assertThat(row.getFileUuid()).isEqualTo("F1"); @@ -188,4 +191,26 @@ public class TestResultSetIteratorTest { } } + private static List<FileSourceDb.Test> newFakeTests(int numberOfTests) throws IOException { + List<FileSourceDb.Test> tests = new ArrayList<>(); + for (int i = 1; i <= numberOfTests; i++) { + FileSourceDb.Test.Builder test = FileSourceDb.Test.newBuilder() + .setUuid("TEST_FILE_UUID_" + i) + .setName("NAME_" + i) + .setStatus(TestStatus.FAILURE) + .setStacktrace("STACKTRACE_" + i) + .setMsg("MESSAGE_" + i) + .setExecutionTimeMs(i); + for (int j = 1; j <= numberOfTests; j++) { + test.addCoveredFile( + FileSourceDb.Test.CoveredFile.newBuilder() + .setFileUuid("MAIN_FILE_UUID_" + j) + .addCoveredLine(j) + ); + } + tests.add(test.build()); + } + return tests; + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsCoveredFilesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsCoveredFilesActionTest.java index 3cd50188543..35ab69f33a7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsCoveredFilesActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsCoveredFilesActionTest.java @@ -22,79 +22,55 @@ 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.CoverageBlock; -import org.sonar.api.test.MutableTestCase; -import org.sonar.api.test.MutableTestPlan; -import org.sonar.api.test.Testable; import org.sonar.api.web.UserRole; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.component.SnapshotPerspectives; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.db.DbClient; +import org.sonar.server.test.index.CoveredFileDoc; +import org.sonar.server.test.index.TestIndex; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; import java.util.Arrays; -import static com.google.common.collect.Lists.newArrayList; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.server.component.ComponentTesting.newFileDto; +import static org.sonar.server.component.ComponentTesting.newProjectDto; +import static org.sonar.server.test.ws.TestsCoveredFilesAction.TEST_UUID; -@RunWith(MockitoJUnitRunner.class) public class TestsCoveredFilesActionTest { - static final String TEST_PLAN_KEY = "src/test/java/org/foo/BarTest.java"; - - @Mock - MutableTestPlan testPlan; - - WsTester tester; + WsTester ws; + private DbClient dbClient; + private TestIndex testIndex; @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(mock(TestsShowAction.class), mock(TestsTestCasesAction.class), new TestsCoveredFilesAction(snapshotPerspectives))); + dbClient = mock(DbClient.class, RETURNS_DEEP_STUBS); + testIndex = mock(TestIndex.class, RETURNS_DEEP_STUBS); + ws = new WsTester(new TestsWs(new TestsCoveredFilesAction(dbClient, testIndex))); } @Test - public void plan() throws Exception { - MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, "SonarQube", TEST_PLAN_KEY); - - MutableTestCase testCase1 = testCase("org.foo.Bar.java", "src/main/java/org/foo/Bar.java", 10); - MutableTestCase testCase2 = testCase("org.foo.File.java", "src/main/java/org/foo/File.java", 3); - when(testPlan.testCasesByName("my_test")).thenReturn(newArrayList(testCase1, testCase2)); - - WsTester.TestRequest request = tester.newGetRequest("api/tests", "covered_files").setParam("key", TEST_PLAN_KEY).setParam("test", "my_test"); - - request.execute().assertJson("{\n" + - " \"files\": [\n" + - " {\n" + - " \"key\": \"org.foo.Bar.java\",\n" + - " \"longName\": \"src/main/java/org/foo/Bar.java\",\n" + - " \"coveredLines\" : 10\n" + - " },\n" + - " {\n" + - " \"key\": \"org.foo.File.java\",\n" + - " \"longName\": \"src/main/java/org/foo/File.java\",\n" + - " \"coveredLines\" : 3\n" + - " }\n" + - " ]\n" + - "}\n"); + public void covered_files() throws Exception { + MockUserSession.set().addComponentUuidPermission(UserRole.CODEVIEWER, "SonarQube", "test-file-uuid"); + + when(testIndex.searchByTestUuid(anyString()).fileUuid()).thenReturn("test-file-uuid"); + when(testIndex.coveredFiles("test-uuid")).thenReturn(Arrays.asList( + new CoveredFileDoc().setFileUuid("bar-uuid").setCoveredLines(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), + new CoveredFileDoc().setFileUuid("file-uuid").setCoveredLines(Arrays.asList(1, 2, 3)) + )); + when(dbClient.componentDao().getByUuids(any(DbSession.class), anyList())).thenReturn( + Arrays.asList( + newFileDto(newProjectDto(), "bar-uuid").setKey("org.foo.Bar.java").setLongName("src/main/java/org/foo/Bar.java"), + newFileDto(newProjectDto(), "file-uuid").setKey("org.foo.File.java").setLongName("src/main/java/org/foo/File.java"))); + + WsTester.TestRequest request = ws.newGetRequest("api/tests", "covered_files").setParam(TEST_UUID, "test-uuid"); + + request.execute().assertJson(getClass(), "tests-covered-files.json"); } - - private MutableTestCase testCase(String fileKey, String fileLongName, int coveredLines) { - Testable testable = mock(Testable.class); - when(testable.component()).thenReturn(new ComponentDto().setKey(fileKey).setLongName(fileLongName)); - - CoverageBlock block = mock(CoverageBlock.class); - when(block.testable()).thenReturn(testable); - when(block.lines()).thenReturn(Arrays.asList(new Integer[coveredLines])); - - MutableTestCase testCase = mock(MutableTestCase.class); - when(testCase.coverageBlocks()).thenReturn(newArrayList(block)); - return testCase; - } - } diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsListActionTest.java new file mode 100644 index 00000000000..08e15006bd0 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsListActionTest.java @@ -0,0 +1,283 @@ +/* + * 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.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.db.DbClient; +import org.sonar.server.es.EsTester; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.test.index.CoveredFileDoc; +import org.sonar.server.test.index.TestDoc; +import org.sonar.server.test.index.TestIndex; +import org.sonar.server.test.index.TestIndexDefinition; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.ws.WsTester; + +import java.util.Arrays; +import java.util.List; + +public class TestsListActionTest { + DbClient dbClient; + DbSession dbSession; + TestIndex testIndex; + + WsTester ws; + + @ClassRule + public static DbTester db = new DbTester(); + @ClassRule + public static EsTester es = new EsTester().addDefinitions(new TestIndexDefinition(new Settings())); + + @Before + public void setUp() throws Exception { + dbClient = new DbClient(db.database(), db.myBatis(), new ComponentDao()); + dbSession = dbClient.openSession(false); + db.truncateTables(); + es.truncateIndices(); + testIndex = new TestIndex(es.client()); + + ws = new WsTester(new TestsWs(new TestsListAction(dbClient, testIndex))); + } + + @After + public void tearDown() throws Exception { + dbSession.close(); + } + + @Test + public void list_based_on_test_uuid() throws Exception { + MockUserSession.set().addProjectUuidPermissions(UserRole.CODEVIEWER, TestFile1.PROJECT_UUID); + + dbClient.componentDao().insert(dbSession, TestFile1.newDto()); + dbSession.commit(); + + es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, + new TestDoc() + .setUuid(TestFile1.UUID) + .setName(TestFile1.NAME) + .setFileUuid(TestFile1.FILE_UUID) + .setDurationInMs(TestFile1.DURATION_IN_MS) + .setStatus(TestFile1.STATUS) + .setMessage(TestFile1.MESSAGE) + .setCoveredFiles(TestFile1.COVERED_FILES) + .setStackTrace(TestFile1.STACKTRACE) + ); + + WsTester.TestRequest request = ws.newGetRequest("api/tests", "list").setParam(TestsListAction.TEST_UUID, TestFile1.UUID); + + request.execute().assertJson(getClass(), "list-test-uuid.json"); + } + + @Test + public void list_based_on_test_file_uuid() throws Exception { + MockUserSession.set().addProjectUuidPermissions(UserRole.CODEVIEWER, TestFile1.PROJECT_UUID); + dbClient.componentDao().insert(dbSession, TestFile1.newDto()); + dbSession.commit(); + + es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, + new TestDoc() + .setUuid(TestFile1.UUID) + .setName(TestFile1.NAME) + .setFileUuid(TestFile1.FILE_UUID) + .setDurationInMs(TestFile1.DURATION_IN_MS) + .setCoveredFiles(TestFile1.COVERED_FILES) + .setStatus(TestFile1.STATUS) + .setMessage(TestFile1.MESSAGE) + .setStackTrace(TestFile1.STACKTRACE)); + + WsTester.TestRequest request = ws.newGetRequest("api/tests", "list").setParam(TestsListAction.TEST_FILE_UUID, TestFile1.FILE_UUID); + + request.execute().assertJson(getClass(), "list-test-uuid.json"); + } + + @Test + public void list_based_on_test_file_key() throws Exception { + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, TestFile1.PROJECT_UUID, TestFile1.KEY); + dbClient.componentDao().insert(dbSession, TestFile1.newDto()); + dbSession.commit(); + + es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, + new TestDoc() + .setUuid(TestFile1.UUID) + .setName(TestFile1.NAME) + .setFileUuid(TestFile1.FILE_UUID) + .setDurationInMs(TestFile1.DURATION_IN_MS) + .setCoveredFiles(TestFile1.COVERED_FILES) + .setStatus(TestFile1.STATUS) + .setMessage(TestFile1.MESSAGE) + .setStackTrace(TestFile1.STACKTRACE) + ); + + WsTester.TestRequest request = ws.newGetRequest("api/tests", "list").setParam(TestsListAction.TEST_FILE_KEY, TestFile1.KEY); + + request.execute().assertJson(getClass(), "list-test-uuid.json"); + } + + @Test + public void list_based_on_main_file_and_line_number() throws Exception { + String mainFileUuid = "MAIN-FILE-UUID"; + MockUserSession.set().addProjectUuidPermissions(UserRole.CODEVIEWER, TestFile1.PROJECT_UUID); + dbClient.componentDao().insert(dbSession, + new ComponentDto() + .setUuid(TestFile1.FILE_UUID) + .setLongName(TestFile1.LONG_NAME) + .setKey(TestFile1.KEY) + .setProjectUuid(TestFile1.PROJECT_UUID), + new ComponentDto() + .setUuid(TestFile2.FILE_UUID) + .setLongName(TestFile2.LONG_NAME) + .setProjectUuid(TestFile2.PROJECT_UUID) + .setKey(TestFile2.KEY), + new ComponentDto() + .setUuid(mainFileUuid) + .setProjectUuid(TestFile1.PROJECT_UUID) + ); + dbSession.commit(); + + es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, + new TestDoc() + .setUuid(TestFile1.UUID) + .setName(TestFile1.NAME) + .setFileUuid(TestFile1.FILE_UUID) + .setDurationInMs(TestFile1.DURATION_IN_MS) + .setStatus(TestFile1.STATUS) + .setMessage(TestFile1.MESSAGE) + .setCoveredFiles(TestFile1.COVERED_FILES) + .setStackTrace(TestFile1.STACKTRACE), + new TestDoc() + .setUuid(TestFile2.UUID) + .setName(TestFile2.NAME) + .setFileUuid(TestFile2.FILE_UUID) + .setDurationInMs(TestFile2.DURATION_IN_MS) + .setStatus(TestFile2.STATUS) + .setStackTrace(TestFile2.STATUS) + .setMessage(TestFile2.MESSAGE) + .setCoveredFiles(TestFile2.COVERED_FILES) + .setStackTrace(TestFile2.STACKTRACE)); + + WsTester.TestRequest request = ws.newGetRequest("api/tests", "list") + .setParam(TestsListAction.SOURCE_FILE_UUID, mainFileUuid) + .setParam(TestsListAction.SOURCE_FILE_LINE_NUMBER, "10"); + + request.execute().assertJson(getClass(), "list-main-file.json"); + } + + @Test(expected = IllegalArgumentException.class) + public void fail_when_no_argument() throws Exception { + ws.newGetRequest("api/tests", "list").execute(); + } + + @Test(expected = IllegalArgumentException.class) + public void fail_when_main_file_uuid_without_line_number() throws Exception { + ws.newGetRequest("api/tests", "list").setParam(TestsListAction.SOURCE_FILE_UUID, "ANY-UUID").execute(); + } + + @Test(expected = ForbiddenException.class) + public void fail_when_no_sufficent_privilege_on_file_uuid() throws Exception { + MockUserSession.set().addProjectUuidPermissions(UserRole.USER, TestFile1.PROJECT_UUID); + dbClient.componentDao().insert(dbSession, TestFile1.newDto()); + dbSession.commit(); + ws.newGetRequest("api/tests", "list").setParam(TestsListAction.TEST_FILE_UUID, TestFile1.FILE_UUID).execute(); + } + + @Test(expected = ForbiddenException.class) + public void fail_when_no_sufficent_privilege_on_test_uuid() throws Exception { + MockUserSession.set().addProjectUuidPermissions(UserRole.USER, TestFile1.PROJECT_UUID); + dbClient.componentDao().insert(dbSession, TestFile1.newDto()); + dbSession.commit(); + ws.newGetRequest("api/tests", "list").setParam(TestsListAction.TEST_FILE_UUID, TestFile1.FILE_UUID).execute(); + } + + @Test(expected = ForbiddenException.class) + public void fail_when_no_sufficent_privilege_on_file_key() throws Exception { + MockUserSession.set().addProjectUuidPermissions(UserRole.USER, TestFile1.PROJECT_UUID); + dbClient.componentDao().insert(dbSession, TestFile1.newDto()); + dbSession.commit(); + ws.newGetRequest("api/tests", "list").setParam(TestsListAction.TEST_FILE_KEY, TestFile1.KEY).execute(); + } + + @Test(expected = ForbiddenException.class) + public void fail_when_no_sufficient_privilege_on_main_file_uuid() throws Exception { + MockUserSession.set().addProjectUuidPermissions(UserRole.USER, TestFile1.PROJECT_UUID); + String mainFileUuid = "MAIN-FILE-UUID"; + dbClient.componentDao().insert(dbSession, new ComponentDto().setUuid(mainFileUuid).setProjectUuid(TestFile1.PROJECT_UUID)); + dbSession.commit(); + + ws.newGetRequest("api/tests", "list") + .setParam(TestsListAction.SOURCE_FILE_UUID, mainFileUuid) + .setParam(TestsListAction.SOURCE_FILE_LINE_NUMBER, "10") + .execute(); + } + + private static final class TestFile1 { + public static final String UUID = "TEST-UUID-1"; + public static final String FILE_UUID = "ABCD"; + public static final String PROJECT_UUID = "PROJECT-UUID"; + public static final String NAME = "test1"; + public static final String STATUS = "OK"; + public static final long DURATION_IN_MS = 10; + public static final String MESSAGE = "MESSAGE-1"; + public static final String STACKTRACE = "STACKTRACE-1"; + public static final String KEY = "org.foo.BarTest.java"; + public static final String LONG_NAME = "src/test/java/org/foo/BarTest.java"; + public static final List<CoveredFileDoc> COVERED_FILES = Arrays.asList(new CoveredFileDoc().setFileUuid("MAIN-FILE-UUID").setCoveredLines(Arrays.asList(1, 2, 3, 10))); + + public static ComponentDto newDto() { + return new ComponentDto() + .setUuid(TestFile1.FILE_UUID) + .setLongName(TestFile1.LONG_NAME) + .setProjectUuid(TestFile1.PROJECT_UUID) + .setKey(TestFile1.KEY); + } + + private TestFile1() { + // static stuff for test purposes + } + } + + private static final class TestFile2 { + public static final String UUID = "TEST-UUID-2"; + public static final String FILE_UUID = "BCDE"; + public static final String PROJECT_UUID = "PROJECT-UUID"; + public static final String NAME = "test2"; + public static final String STATUS = "ERROR"; + public static final long DURATION_IN_MS = 97; + public static final String MESSAGE = "MESSAGE-2"; + public static final String STACKTRACE = "STACKTRACE-2"; + public static final String KEY = "org.foo.FileTest.java"; + public static final String LONG_NAME = "src/test/java/org/foo/FileTest.java"; + public static final List<CoveredFileDoc> COVERED_FILES = Arrays.asList(new CoveredFileDoc().setFileUuid("MAIN-FILE-UUID").setCoveredLines(Arrays.asList(11, 12, 13, 10))); + + private TestFile2() { + // static stuff for test purposes + } + } + +} 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 deleted file mode 100644 index 0c1117c3fef..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsShowActionTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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.MutableTestCase; -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.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; - -import javax.annotation.Nullable; - -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 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 { - 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:<true> but was:<false>", - "java.lang.AssertionError: expected:<true> but was:<false>\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"); - when(testPlan.testCases()).thenReturn(newArrayList(testCase1, testCase2)); - - WsTester.TestRequest request = tester.newGetRequest("api/tests", "show").setParam("key", TEST_PLAN_KEY); - - request.execute().assertJson(getClass(), "show.json"); - } - - @Test - public void show_from_test_data() throws Exception { - MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, "SonarQube", TEST_PLAN_KEY); - - when(measureDao.findByComponentKeyAndMetricKey(session, TEST_PLAN_KEY, "test_data")).thenReturn(new MeasureDto() - .setComponentKey(TEST_PLAN_KEY) - .setMetricKey("test_data") - .setData("<tests-details>" + - "<testcase status=\"ok\" time=\"10\" name=\"test1\"/>" + - "<testcase status=\"error\" time=\"97\" name=\"test2\">" + - "<error message=\"expected:<true> but was:<false>\">" + - "<![CDATA[" + - "java.lang.AssertionError: expected:<true> but was:<false>\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" + - "]]>" + - "</error>" + - "</testcase>" + - "</tests-details>")); - - WsTester.TestRequest request = tester.newGetRequest("api/tests", "show").setParam("key", TEST_PLAN_KEY); - - request.execute().assertJson(getClass(), "show_from_test_data.json"); - } - - @Test - public void show_from_test_data_with_a_time_in_float() throws Exception { - MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, "SonarQube", TEST_PLAN_KEY); - - when(measureDao.findByComponentKeyAndMetricKey(session, TEST_PLAN_KEY, "test_data")).thenReturn( - new MeasureDto() - .setComponentKey(TEST_PLAN_KEY) - .setMetricKey("test_data") - .setData("<tests-details>" + - "<testcase status=\"ok\" time=\"12.5\" name=\"test1\"/>" + - "</tests-details>")); - - WsTester.TestRequest request = tester.newGetRequest("api/tests", "show").setParam("key", TEST_PLAN_KEY); - - request.execute().assertJson(getClass(), "show_from_test_data_with_a_time_in_float.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); - when(testCase.status()).thenReturn(status); - when(testCase.durationInMs()).thenReturn(durationInMs); - when(testCase.countCoveredLines()).thenReturn(coveredLines); - when(testCase.message()).thenReturn(message); - when(testCase.stackTrace()).thenReturn(stackTrace); - return testCase; - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsTestCasesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsTestCasesActionTest.java deleted file mode 100644 index 3e8f5a9f442..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/test/ws/TestsTestCasesActionTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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.Matchers; -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.ComponentVertex; -import org.sonar.core.component.SnapshotPerspectives; -import org.sonar.core.persistence.DbSession; -import org.sonar.server.component.ComponentService; -import org.sonar.server.component.db.ComponentDao; -import org.sonar.server.db.DbClient; -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 TestsTestCasesActionTest { - - private static final String FILE_KEY = "src/main/java/org/foo/Foo.java"; - - private static final String EXPECTED_JSON = "{\n" + - " \"tests\": [\n" + - " {\n" + - " \"name\": \"test1\",\n" + - " \"status\": \"OK\",\n" + - " \"durationInMs\": 10,\n" + - " \"_ref\": \"1\"\n" + - " },\n" + - " {\n" + - " \"name\": \"test2\",\n" + - " \"status\": \"ERROR\",\n" + - " \"durationInMs\": 97,\n" + - " \"_ref\": \"2\"\n" + - " }\n" + - " ],\n" + - " \"files\": {\n" + - " \"1\": {\n" + - " \"key\": \"org.foo.BarTest.java\",\n" + - " \"uuid\": \"ABCD\",\n" + - " \"longName\": \"src/test/java/org/foo/BarTest.java\"\n" + - " },\n" + - " \"2\": {\n" + - " \"key\": \"org.foo.FileTest.java\",\n" + - " \"uuid\": \"BCDE\",\n" + - " \"longName\": \"src/test/java/org/foo/FileTest.java\"\n" + - " }\n" + - " }\n" + - "}"; - - - @Mock - MutableTestable testable; - - @Mock - ComponentService componentService; - - @Mock - DbClient dbClient; - - @Mock - ComponentDao componentDao; - - WsTester tester; - - @Before - public void setUp() throws Exception { - SnapshotPerspectives snapshotPerspectives = mock(SnapshotPerspectives.class); - when(snapshotPerspectives.as(MutableTestable.class, FILE_KEY)).thenReturn(testable); - when(dbClient.componentDao()).thenReturn(componentDao); - when(dbClient.openSession(false)).thenReturn(mock(DbSession.class)); - tester = new WsTester(new TestsWs(mock(TestsShowAction.class), new TestsTestCasesAction(snapshotPerspectives, componentService, dbClient), mock(TestsCoveredFilesAction.class))); - } - - @Test - public void testable() throws Exception { - MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, "SonarQube", FILE_KEY); - - String key1 = "org.foo.BarTest.java"; - String name1 = "src/test/java/org/foo/BarTest.java"; - String uuid1 = "ABCD"; - TestCase testCase1 = testCase("test1", TestCase.Status.OK, 10L, key1); - ComponentDto component1 = new ComponentDto().setKey(key1).setLongName(name1).setUuid(uuid1); - String key2 = "org.foo.FileTest.java"; - String name2 = "src/test/java/org/foo/FileTest.java"; - String uuid2 = "BCDE"; - TestCase testCase2 = testCase("test2", TestCase.Status.ERROR, 97L, key2); - ComponentDto component2 = new ComponentDto().setKey(key2).setLongName(name2).setUuid(uuid2); - when(testable.testCasesOfLine(10)).thenReturn(newArrayList(testCase1, testCase2)); - when(componentDao.getByKeys(Matchers.isA(DbSession.class), Matchers.anyCollectionOf(String.class))).thenReturn(newArrayList(component1, component2)); - - WsTester.TestRequest request = tester.newGetRequest("api/tests", "test_cases").setParam("key", FILE_KEY).setParam("line", "10"); - - request.execute().assertJson(EXPECTED_JSON); - } - - @Test - public void testable_with_uuid() throws Exception { - String uuid = "1234"; - when(componentService.getByUuid(uuid)).thenReturn(new ComponentDto().setKey(FILE_KEY)); - MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, "SonarQube", FILE_KEY); - - String key1 = "org.foo.BarTest.java"; - String name1 = "src/test/java/org/foo/BarTest.java"; - String uuid1 = "ABCD"; - TestCase testCase1 = testCase("test1", TestCase.Status.OK, 10L, key1); - ComponentDto component1 = new ComponentDto().setKey(key1).setLongName(name1).setUuid(uuid1); - String key2 = "org.foo.FileTest.java"; - String name2 = "src/test/java/org/foo/FileTest.java"; - String uuid2 = "BCDE"; - TestCase testCase2 = testCase("test2", TestCase.Status.ERROR, 97L, key2); - ComponentDto component2 = new ComponentDto().setKey(key2).setLongName(name2).setUuid(uuid2); - when(testable.testCasesOfLine(10)).thenReturn(newArrayList(testCase1, testCase2)); - when(componentDao.getByKeys(Matchers.isA(DbSession.class), Matchers.anyCollectionOf(String.class))).thenReturn(newArrayList(component1, component2)); - - WsTester.TestRequest request = tester.newGetRequest("api/tests", "test_cases").setParam("uuid", uuid).setParam("line", "10"); - - request.execute().assertJson(EXPECTED_JSON); - } - - @Test(expected = IllegalArgumentException.class) - public void fail_on_missing_parameters() throws Exception { - tester.newGetRequest("api/tests", "test_cases").execute(); - } - - private TestCase testCase(String name, TestCase.Status status, Long durationInMs, String testPlanKey) { - 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); - ComponentVertex component = mock(ComponentVertex.class); - when(component.key()).thenReturn(testPlanKey); - when(testPlan.component()).thenReturn(component); - when(testCase.testPlan()).thenReturn(testPlan); - return testCase; - } - -} 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 7337006dede..590777108bc 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 @@ -23,9 +23,8 @@ package org.sonar.server.test.ws; 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.component.ComponentService; import org.sonar.server.db.DbClient; +import org.sonar.server.test.index.TestIndex; import org.sonar.server.ws.WsTester; import static org.assertj.core.api.Assertions.assertThat; @@ -37,11 +36,9 @@ public class TestsWsTest { @Before public void setUp() throws Exception { - SnapshotPerspectives snapshotPerspectives = mock(SnapshotPerspectives.class); WsTester tester = new WsTester(new TestsWs( - new TestsShowAction(mock(DbClient.class), snapshotPerspectives), - new TestsTestCasesAction(snapshotPerspectives, mock(ComponentService.class), mock(DbClient.class)), - new TestsCoveredFilesAction(snapshotPerspectives))); + new TestsListAction(mock(DbClient.class), mock(TestIndex.class)), + new TestsCoveredFilesAction(mock(DbClient.class), mock(TestIndex.class)))); controller = tester.controller("api/tests"); } @@ -50,40 +47,28 @@ public class TestsWsTest { assertThat(controller).isNotNull(); assertThat(controller.since()).isEqualTo("4.4"); assertThat(controller.description()).isNotEmpty(); - assertThat(controller.actions()).hasSize(3); + assertThat(controller.actions()).hasSize(2); } @Test - public void define_show_action() throws Exception { - WebService.Action action = controller.action("show"); + public void define_list_action() throws Exception { + WebService.Action action = controller.action("list"); assertThat(action).isNotNull(); assertThat(action.isInternal()).isFalse(); assertThat(action.isPost()).isFalse(); assertThat(action.handler()).isNotNull(); assertThat(action.responseExampleAsString()).isNotEmpty(); - assertThat(action.params()).hasSize(1); + assertThat(action.params()).hasSize(7); } @Test - public void define_covered_files_action() throws Exception { + public void define_covered_files() throws Exception { WebService.Action action = controller.action("covered_files"); assertThat(action).isNotNull(); assertThat(action.isInternal()).isFalse(); assertThat(action.isPost()).isFalse(); assertThat(action.handler()).isNotNull(); assertThat(action.responseExampleAsString()).isNotEmpty(); - assertThat(action.params()).hasSize(2); - } - - @Test - public void define_test_cases_action() throws Exception { - WebService.Action action = controller.action("test_cases"); - assertThat(action).isNotNull(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.isPost()).isFalse(); - assertThat(action.handler()).isNotNull(); - assertThat(action.responseExampleAsString()).isNotEmpty(); assertThat(action.params()).hasSize(3); } - } diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionTest.java index 299b2631a71..589ccd36a6e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionTest.java @@ -31,7 +31,6 @@ import org.sonar.server.component.ComponentTesting; import org.sonar.server.exceptions.ForbiddenException; import javax.annotation.Nullable; - import java.util.Arrays; import java.util.Locale; @@ -211,6 +210,34 @@ public class UserSessionTest { session.checkComponentPermission(UserRole.USER, "com.foo:Bar:BarFile.xoo"); } + @Test + public void check_component_uuid_permission_ok() throws Exception { + AuthorizationDao authorizationDao = mock(AuthorizationDao.class); + ResourceDao resourceDao = mock(ResourceDao.class); + UserSession session = new SpyUserSession("marius", authorizationDao,resourceDao).setUserId(1); + + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file = ComponentTesting.newFileDto(project, "file-uuid"); + when(resourceDao.getResource("file-uuid")).thenReturn(new ResourceDto().setProjectUuid(project.uuid())); + when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid())); + + session.checkComponentUuidPermission(UserRole.USER, file.uuid()); + } + + @Test(expected = ForbiddenException.class) + public void check_component_uuid_permission_ko() throws Exception { + AuthorizationDao authorizationDao = mock(AuthorizationDao.class); + ResourceDao resourceDao = mock(ResourceDao.class); + UserSession session = new SpyUserSession("marius", authorizationDao,resourceDao).setUserId(1); + + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file = ComponentTesting.newFileDto(project, "file-uuid"); + when(resourceDao.getResource("file-uuid")).thenReturn(new ResourceDto().setProjectUuid(project.uuid())); + when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid())); + + session.checkComponentUuidPermission(UserRole.USER, "another-uuid"); + } + @Test(expected = ForbiddenException.class) public void check_component_key_permission_when_project_not_found() throws Exception { AuthorizationDao authorizationDao = mock(AuthorizationDao.class); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsCoveredFilesActionTest/tests-covered-files.json b/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsCoveredFilesActionTest/tests-covered-files.json new file mode 100644 index 00000000000..7b37f47bd5d --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsCoveredFilesActionTest/tests-covered-files.json @@ -0,0 +1,14 @@ +{ + "files": [ + { + "key": "org.foo.Bar.java", + "longName": "src/main/java/org/foo/Bar.java", + "coveredLines": 10 + }, + { + "key": "org.foo.File.java", + "longName": "src/main/java/org/foo/File.java", + "coveredLines": 3 + } + ] +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsListActionTest/list-main-file.json b/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsListActionTest/list-main-file.json new file mode 100644 index 00000000000..5a1e62547be --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsListActionTest/list-main-file.json @@ -0,0 +1,28 @@ +{ + "tests": [ + { + "testUuid": "TEST-UUID-1", + "fileUuid": "ABCD", + "name": "test1", + "status": "OK", + "durationInMs": 10, + "coveredLines": 4, + "message": "MESSAGE-1", + "stacktrace": "STACKTRACE-1", + "fileKey": "org.foo.BarTest.java", + "fileLongName": "src/test/java/org/foo/BarTest.java" + }, + { + "testUuid": "TEST-UUID-2", + "fileUuid": "BCDE", + "name": "test2", + "status": "ERROR", + "durationInMs": 97, + "coveredLines": 4, + "message": "MESSAGE-2", + "stacktrace": "STACKTRACE-2", + "fileKey": "org.foo.FileTest.java", + "fileLongName": "src/test/java/org/foo/FileTest.java" + } + ] +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsListActionTest/list-test-uuid.json b/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsListActionTest/list-test-uuid.json new file mode 100644 index 00000000000..de6d8d86fa2 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/test/ws/TestsListActionTest/list-test-uuid.json @@ -0,0 +1,16 @@ +{ + "tests": [ + { + "testUuid": "TEST-UUID-1", + "fileUuid": "ABCD", + "name": "test1", + "status": "OK", + "durationInMs": 10, + "coveredLines": 4, + "message": "MESSAGE-1", + "stacktrace": "STACKTRACE-1", + "fileKey": "org.foo.BarTest.java", + "fileLongName": "src/test/java/org/foo/BarTest.java" + } + ] +} diff --git a/sonar-batch-protocol/src/main/gen-java/org/sonar/server/source/db/FileSourceDb.java b/sonar-batch-protocol/src/main/gen-java/org/sonar/server/source/db/FileSourceDb.java index 987acb9659f..afc771a998f 100644 --- a/sonar-batch-protocol/src/main/gen-java/org/sonar/server/source/db/FileSourceDb.java +++ b/sonar-batch-protocol/src/main/gen-java/org/sonar/server/source/db/FileSourceDb.java @@ -2988,13 +2988,13 @@ public final class FileSourceDb { getNameBytes(); /** - * <code>optional .TestStatus status = 3;</code> + * <code>optional .org.sonar.server.source.db.Test.TestStatus status = 3;</code> */ boolean hasStatus(); /** - * <code>optional .TestStatus status = 3;</code> + * <code>optional .org.sonar.server.source.db.Test.TestStatus status = 3;</code> */ - org.sonar.batch.protocol.Constants.TestStatus getStatus(); + org.sonar.server.source.db.FileSourceDb.Test.TestStatus getStatus(); /** * <code>optional int64 execution_time_ms = 4;</code> @@ -3123,7 +3123,7 @@ public final class FileSourceDb { } case 24: { int rawValue = input.readEnum(); - org.sonar.batch.protocol.Constants.TestStatus value = org.sonar.batch.protocol.Constants.TestStatus.valueOf(rawValue); + org.sonar.server.source.db.FileSourceDb.Test.TestStatus value = org.sonar.server.source.db.FileSourceDb.Test.TestStatus.valueOf(rawValue); if (value == null) { unknownFields.mergeVarintField(3, rawValue); } else { @@ -3199,6 +3199,106 @@ public final class FileSourceDb { return PARSER; } + /** + * Protobuf enum {@code org.sonar.server.source.db.Test.TestStatus} + */ + public enum TestStatus + implements com.google.protobuf.ProtocolMessageEnum { + /** + * <code>OK = 1;</code> + */ + OK(0, 1), + /** + * <code>FAILURE = 2;</code> + */ + FAILURE(1, 2), + /** + * <code>ERROR = 3;</code> + */ + ERROR(2, 3), + /** + * <code>SKIPPED = 4;</code> + */ + SKIPPED(3, 4), + ; + + /** + * <code>OK = 1;</code> + */ + public static final int OK_VALUE = 1; + /** + * <code>FAILURE = 2;</code> + */ + public static final int FAILURE_VALUE = 2; + /** + * <code>ERROR = 3;</code> + */ + public static final int ERROR_VALUE = 3; + /** + * <code>SKIPPED = 4;</code> + */ + public static final int SKIPPED_VALUE = 4; + + + public final int getNumber() { return value; } + + public static TestStatus valueOf(int value) { + switch (value) { + case 1: return OK; + case 2: return FAILURE; + case 3: return ERROR; + case 4: return SKIPPED; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap<TestStatus> + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap<TestStatus> + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap<TestStatus>() { + public TestStatus findValueByNumber(int number) { + return TestStatus.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.sonar.server.source.db.FileSourceDb.Test.getDescriptor().getEnumTypes().get(0); + } + + private static final TestStatus[] VALUES = values(); + + public static TestStatus valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private TestStatus(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:org.sonar.server.source.db.Test.TestStatus) + } + public interface CoveredFileOrBuilder extends // @@protoc_insertion_point(interface_extends:org.sonar.server.source.db.Test.CoveredFile) com.google.protobuf.MessageOrBuilder { @@ -3926,17 +4026,17 @@ public final class FileSourceDb { } public static final int STATUS_FIELD_NUMBER = 3; - private org.sonar.batch.protocol.Constants.TestStatus status_; + private org.sonar.server.source.db.FileSourceDb.Test.TestStatus status_; /** - * <code>optional .TestStatus status = 3;</code> + * <code>optional .org.sonar.server.source.db.Test.TestStatus status = 3;</code> */ public boolean hasStatus() { return ((bitField0_ & 0x00000004) == 0x00000004); } /** - * <code>optional .TestStatus status = 3;</code> + * <code>optional .org.sonar.server.source.db.Test.TestStatus status = 3;</code> */ - public org.sonar.batch.protocol.Constants.TestStatus getStatus() { + public org.sonar.server.source.db.FileSourceDb.Test.TestStatus getStatus() { return status_; } @@ -4077,7 +4177,7 @@ public final class FileSourceDb { private void initFields() { uuid_ = ""; name_ = ""; - status_ = org.sonar.batch.protocol.Constants.TestStatus.OK; + status_ = org.sonar.server.source.db.FileSourceDb.Test.TestStatus.OK; executionTimeMs_ = 0L; stacktrace_ = ""; msg_ = ""; @@ -4276,7 +4376,7 @@ public final class FileSourceDb { bitField0_ = (bitField0_ & ~0x00000001); name_ = ""; bitField0_ = (bitField0_ & ~0x00000002); - status_ = org.sonar.batch.protocol.Constants.TestStatus.OK; + status_ = org.sonar.server.source.db.FileSourceDb.Test.TestStatus.OK; bitField0_ = (bitField0_ & ~0x00000004); executionTimeMs_ = 0L; bitField0_ = (bitField0_ & ~0x00000008); @@ -4598,23 +4698,23 @@ public final class FileSourceDb { return this; } - private org.sonar.batch.protocol.Constants.TestStatus status_ = org.sonar.batch.protocol.Constants.TestStatus.OK; + private org.sonar.server.source.db.FileSourceDb.Test.TestStatus status_ = org.sonar.server.source.db.FileSourceDb.Test.TestStatus.OK; /** - * <code>optional .TestStatus status = 3;</code> + * <code>optional .org.sonar.server.source.db.Test.TestStatus status = 3;</code> */ public boolean hasStatus() { return ((bitField0_ & 0x00000004) == 0x00000004); } /** - * <code>optional .TestStatus status = 3;</code> + * <code>optional .org.sonar.server.source.db.Test.TestStatus status = 3;</code> */ - public org.sonar.batch.protocol.Constants.TestStatus getStatus() { + public org.sonar.server.source.db.FileSourceDb.Test.TestStatus getStatus() { return status_; } /** - * <code>optional .TestStatus status = 3;</code> + * <code>optional .org.sonar.server.source.db.Test.TestStatus status = 3;</code> */ - public Builder setStatus(org.sonar.batch.protocol.Constants.TestStatus value) { + public Builder setStatus(org.sonar.server.source.db.FileSourceDb.Test.TestStatus value) { if (value == null) { throw new NullPointerException(); } @@ -4624,11 +4724,11 @@ public final class FileSourceDb { return this; } /** - * <code>optional .TestStatus status = 3;</code> + * <code>optional .org.sonar.server.source.db.Test.TestStatus status = 3;</code> */ public Builder clearStatus() { bitField0_ = (bitField0_ & ~0x00000004); - status_ = org.sonar.batch.protocol.Constants.TestStatus.OK; + status_ = org.sonar.server.source.db.FileSourceDb.Test.TestStatus.OK; onChanged(); return this; } @@ -5098,25 +5198,27 @@ public final class FileSourceDb { static { java.lang.String[] descriptorData = { "\n\024file_source_db.proto\022\032org.sonar.server" + - ".source.db\032\017constants.proto\"\223\003\n\004Line\022\014\n\004" + - "line\030\001 \001(\005\022\016\n\006source\030\002 \001(\t\022\024\n\014scm_revisi" + - "on\030\003 \001(\t\022\022\n\nscm_author\030\004 \001(\t\022\020\n\010scm_date" + - "\030\005 \001(\003\022\024\n\014ut_line_hits\030\006 \001(\005\022\025\n\rut_condi" + - "tions\030\007 \001(\005\022\035\n\025ut_covered_conditions\030\010 \001" + - "(\005\022\024\n\014it_line_hits\030\t \001(\005\022\025\n\rit_condition" + - "s\030\n \001(\005\022\035\n\025it_covered_conditions\030\013 \001(\005\022\031" + - "\n\021overall_line_hits\030\014 \001(\005\022\032\n\022overall_con" + - "ditions\030\r \001(\005\022\"\n\032overall_covered_conditi", - "ons\030\016 \001(\005\022\024\n\014highlighting\030\017 \001(\t\022\017\n\007symbo" + - "ls\030\020 \001(\t\022\027\n\013duplication\030\021 \003(\005B\002\020\001\"7\n\004Dat" + - "a\022/\n\005lines\030\001 \003(\0132 .org.sonar.server.sour" + - "ce.db.Line\"\373\001\n\004Test\022\014\n\004uuid\030\001 \001(\t\022\014\n\004nam" + - "e\030\002 \001(\t\022\033\n\006status\030\003 \001(\0162\013.TestStatus\022\031\n\021" + - "execution_time_ms\030\004 \001(\003\022\022\n\nstacktrace\030\005 " + - "\001(\t\022\013\n\003msg\030\006 \001(\t\022B\n\014covered_file\030\007 \003(\0132," + - ".org.sonar.server.source.db.Test.Covered" + - "File\032:\n\013CoveredFile\022\021\n\tfile_uuid\030\001 \001(\t\022\030" + - "\n\014covered_line\030\002 \003(\005B\002\020\001B\002H\001" + ".source.db\"\223\003\n\004Line\022\014\n\004line\030\001 \001(\005\022\016\n\006sou" + + "rce\030\002 \001(\t\022\024\n\014scm_revision\030\003 \001(\t\022\022\n\nscm_a" + + "uthor\030\004 \001(\t\022\020\n\010scm_date\030\005 \001(\003\022\024\n\014ut_line" + + "_hits\030\006 \001(\005\022\025\n\rut_conditions\030\007 \001(\005\022\035\n\025ut" + + "_covered_conditions\030\010 \001(\005\022\024\n\014it_line_hit" + + "s\030\t \001(\005\022\025\n\rit_conditions\030\n \001(\005\022\035\n\025it_cov" + + "ered_conditions\030\013 \001(\005\022\031\n\021overall_line_hi" + + "ts\030\014 \001(\005\022\032\n\022overall_conditions\030\r \001(\005\022\"\n\032" + + "overall_covered_conditions\030\016 \001(\005\022\024\n\014high", + "lighting\030\017 \001(\t\022\017\n\007symbols\030\020 \001(\t\022\027\n\013dupli" + + "cation\030\021 \003(\005B\002\020\001\"7\n\004Data\022/\n\005lines\030\001 \003(\0132" + + " .org.sonar.server.source.db.Line\"\326\002\n\004Te" + + "st\022\014\n\004uuid\030\001 \001(\t\022\014\n\004name\030\002 \001(\t\022;\n\006status" + + "\030\003 \001(\0162+.org.sonar.server.source.db.Test" + + ".TestStatus\022\031\n\021execution_time_ms\030\004 \001(\003\022\022" + + "\n\nstacktrace\030\005 \001(\t\022\013\n\003msg\030\006 \001(\t\022B\n\014cover" + + "ed_file\030\007 \003(\0132,.org.sonar.server.source." + + "db.Test.CoveredFile\032:\n\013CoveredFile\022\021\n\tfi" + + "le_uuid\030\001 \001(\t\022\030\n\014covered_line\030\002 \003(\005B\002\020\001\"", + "9\n\nTestStatus\022\006\n\002OK\020\001\022\013\n\007FAILURE\020\002\022\t\n\005ER" + + "ROR\020\003\022\013\n\007SKIPPED\020\004B\002H\001" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { @@ -5129,7 +5231,6 @@ public final class FileSourceDb { com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] { - org.sonar.batch.protocol.Constants.getDescriptor(), }, assigner); internal_static_org_sonar_server_source_db_Line_descriptor = getDescriptor().getMessageTypes().get(0); @@ -5155,7 +5256,6 @@ public final class FileSourceDb { com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_org_sonar_server_source_db_Test_CoveredFile_descriptor, new java.lang.String[] { "FileUuid", "CoveredLine", }); - org.sonar.batch.protocol.Constants.getDescriptor(); } // @@protoc_insertion_point(outer_class_scope) diff --git a/sonar-batch-protocol/src/main/protobuf/file_source_db.proto b/sonar-batch-protocol/src/main/protobuf/file_source_db.proto index b14bd891038..48b916dbd78 100644 --- a/sonar-batch-protocol/src/main/protobuf/file_source_db.proto +++ b/sonar-batch-protocol/src/main/protobuf/file_source_db.proto @@ -37,8 +37,6 @@ Notes package org.sonar.server.source.db; -import "constants.proto"; - option optimize_for = SPEED; message Line { @@ -87,4 +85,11 @@ message Test { optional string file_uuid = 1; repeated int32 covered_line = 2 [packed = true]; } + + enum TestStatus { + OK = 1; + FAILURE = 2; + ERROR = 3; + SKIPPED = 4; + } } |