- Drop api/tests WS - Drop persistance of tests and coverage details from compute engine - Drop tests and coverage details from scanner reporttags/7.6
*/ | */ | ||||
Optional<CloseableIterator<String>> readFileSource(int fileRef); | Optional<CloseableIterator<String>> readFileSource(int fileRef); | ||||
CloseableIterator<ScannerReport.Test> readTests(int testFileRef); | |||||
CloseableIterator<ScannerReport.CoverageDetail> readCoverageDetails(int testFileRef); | |||||
CloseableIterator<ScannerReport.ContextProperty> readContextProperties(); | CloseableIterator<ScannerReport.ContextProperty> readContextProperties(); | ||||
Optional<CloseableIterator<ScannerReport.LineSgnificantCode>> readComponentSignificantCode(int fileRef); | Optional<CloseableIterator<ScannerReport.LineSgnificantCode>> readComponentSignificantCode(int fileRef); |
*/ | */ | ||||
package org.sonar.ce.task.projectanalysis.batch; | package org.sonar.ce.task.projectanalysis.batch; | ||||
import com.google.common.base.Throwables; | |||||
import com.google.protobuf.InvalidProtocolBufferException; | |||||
import com.google.protobuf.Parser; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||
import java.util.NoSuchElementException; | import java.util.NoSuchElementException; | ||||
return delegate.readAdHocRules(); | return delegate.readAdHocRules(); | ||||
} | } | ||||
@Override | @Override | ||||
public CloseableIterator<ScannerReport.Measure> readComponentMeasures(int componentRef) { | public CloseableIterator<ScannerReport.Measure> readComponentMeasures(int componentRef) { | ||||
ensureInitialized(); | ensureInitialized(); | ||||
} | } | ||||
} | } | ||||
@Override | |||||
public CloseableIterator<ScannerReport.Test> readTests(int testFileRef) { | |||||
ensureInitialized(); | |||||
File file = delegate.readTests(testFileRef); | |||||
if (file == null) { | |||||
return CloseableIterator.emptyCloseableIterator(); | |||||
} | |||||
try { | |||||
return new ParserCloseableIterator<>(ScannerReport.Test.parser(), FileUtils.openInputStream(file)); | |||||
} catch (IOException e) { | |||||
Throwables.propagate(e); | |||||
// actually never reached | |||||
return CloseableIterator.emptyCloseableIterator(); | |||||
} | |||||
} | |||||
@Override | |||||
public CloseableIterator<ScannerReport.CoverageDetail> readCoverageDetails(int testFileRef) { | |||||
ensureInitialized(); | |||||
File file = delegate.readCoverageDetails(testFileRef); | |||||
if (file == null) { | |||||
return CloseableIterator.emptyCloseableIterator(); | |||||
} | |||||
try { | |||||
return new ParserCloseableIterator<>(ScannerReport.CoverageDetail.parser(), FileUtils.openInputStream(file)); | |||||
} catch (IOException e) { | |||||
Throwables.propagate(e); | |||||
// actually never reached | |||||
return CloseableIterator.emptyCloseableIterator(); | |||||
} | |||||
} | |||||
@Override | @Override | ||||
public CloseableIterator<ScannerReport.ContextProperty> readContextProperties() { | public CloseableIterator<ScannerReport.ContextProperty> readContextProperties() { | ||||
ensureInitialized(); | ensureInitialized(); | ||||
return delegate.readContextProperties(); | return delegate.readContextProperties(); | ||||
} | } | ||||
private static class ParserCloseableIterator<T> extends CloseableIterator<T> { | |||||
private final Parser<T> parser; | |||||
private final FileInputStream fileInputStream; | |||||
public ParserCloseableIterator(Parser<T> parser, FileInputStream fileInputStream) { | |||||
this.parser = parser; | |||||
this.fileInputStream = fileInputStream; | |||||
} | |||||
@Override | |||||
protected T doNext() { | |||||
try { | |||||
return parser.parseDelimitedFrom(fileInputStream); | |||||
} catch (InvalidProtocolBufferException e) { | |||||
Throwables.propagate(e); | |||||
// actually never reached | |||||
return null; | |||||
} | |||||
} | |||||
@Override | |||||
protected void doClose() throws Exception { | |||||
fileInputStream.close(); | |||||
} | |||||
} | |||||
public boolean hasSignificantCode(int fileRef) { | |||||
return delegate.hasSignificantCode(fileRef); | |||||
} | |||||
@Override | @Override | ||||
public Optional<CloseableIterator<LineSgnificantCode>> readComponentSignificantCode(int fileRef) { | public Optional<CloseableIterator<LineSgnificantCode>> readComponentSignificantCode(int fileRef) { | ||||
ensureInitialized(); | ensureInitialized(); |
import org.sonar.db.purge.PurgeListener; | import org.sonar.db.purge.PurgeListener; | ||||
import org.sonar.server.component.index.ComponentIndexer; | import org.sonar.server.component.index.ComponentIndexer; | ||||
import org.sonar.server.issue.index.IssueIndexer; | import org.sonar.server.issue.index.IssueIndexer; | ||||
import org.sonar.server.test.index.TestIndexer; | |||||
@ServerSide | @ServerSide | ||||
public class IndexPurgeListener implements PurgeListener { | public class IndexPurgeListener implements PurgeListener { | ||||
private final TestIndexer testIndexer; | |||||
private final IssueIndexer issueIndexer; | private final IssueIndexer issueIndexer; | ||||
private final ComponentIndexer componentIndexer; | private final ComponentIndexer componentIndexer; | ||||
public IndexPurgeListener(TestIndexer testIndexer, IssueIndexer issueIndexer, ComponentIndexer componentIndexer) { | |||||
this.testIndexer = testIndexer; | |||||
public IndexPurgeListener(IssueIndexer issueIndexer, ComponentIndexer componentIndexer) { | |||||
this.issueIndexer = issueIndexer; | this.issueIndexer = issueIndexer; | ||||
this.componentIndexer = componentIndexer; | this.componentIndexer = componentIndexer; | ||||
} | } | ||||
@Override | @Override | ||||
public void onComponentsDisabling(String projectUuid, Collection<String> disabledComponentUuids) { | public void onComponentsDisabling(String projectUuid, Collection<String> disabledComponentUuids) { | ||||
componentIndexer.delete(projectUuid, disabledComponentUuids); | componentIndexer.delete(projectUuid, disabledComponentUuids); | ||||
disabledComponentUuids.forEach(this::onComponentDisabling); | |||||
} | |||||
private void onComponentDisabling(String uuid) { | |||||
testIndexer.deleteByFile(uuid); | |||||
} | } | ||||
@Override | @Override |
import java.util.Optional; | import java.util.Optional; | ||||
import org.sonar.api.utils.log.Logger; | import org.sonar.api.utils.log.Logger; | ||||
import org.sonar.api.utils.log.Loggers; | import org.sonar.api.utils.log.Loggers; | ||||
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||||
import org.sonar.ce.task.projectanalysis.analysis.Branch; | |||||
import org.sonar.ce.task.projectanalysis.component.Component; | import org.sonar.ce.task.projectanalysis.component.Component; | ||||
import org.sonar.ce.task.projectanalysis.component.MergeBranchComponentUuids; | import org.sonar.ce.task.projectanalysis.component.MergeBranchComponentUuids; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.source.FileSourceDto; | import org.sonar.db.source.FileSourceDto; | ||||
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||||
import org.sonar.ce.task.projectanalysis.analysis.Branch; | |||||
public class ScmInfoDbLoader { | public class ScmInfoDbLoader { | ||||
private static final Logger LOGGER = Loggers.get(ScmInfoDbLoader.class); | private static final Logger LOGGER = Loggers.get(ScmInfoDbLoader.class); | ||||
LOGGER.trace("Reading SCM info from DB for file '{}'", uuid.get()); | LOGGER.trace("Reading SCM info from DB for file '{}'", uuid.get()); | ||||
try (DbSession dbSession = dbClient.openSession(false)) { | try (DbSession dbSession = dbClient.openSession(false)) { | ||||
FileSourceDto dto = dbClient.fileSourceDao().selectSourceByFileUuid(dbSession, uuid.get()); | |||||
FileSourceDto dto = dbClient.fileSourceDao().selectByFileUuid(dbSession, uuid.get()); | |||||
if (dto == null) { | if (dto == null) { | ||||
return Optional.empty(); | return Optional.empty(); | ||||
} | } |
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.protobuf.DbFileSources; | import org.sonar.db.protobuf.DbFileSources; | ||||
import org.sonar.db.source.FileSourceDto; | import org.sonar.db.source.FileSourceDto; | ||||
import org.sonar.db.source.FileSourceDto.Type; | |||||
import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER; | import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER; | ||||
@Override | @Override | ||||
public void visitProject(Component project) { | public void visitProject(Component project) { | ||||
this.projectUuid = project.getUuid(); | this.projectUuid = project.getUuid(); | ||||
session.select("org.sonar.db.source.FileSourceMapper.selectHashesForProject", ImmutableMap.of("projectUuid", projectUuid, "dataType", Type.SOURCE), | |||||
session.select("org.sonar.db.source.FileSourceMapper.selectHashesForProject", ImmutableMap.of("projectUuid", projectUuid), | |||||
context -> { | context -> { | ||||
FileSourceDto dto = (FileSourceDto) context.getResultObject(); | FileSourceDto dto = (FileSourceDto) context.getResultObject(); | ||||
previousFileSourcesByUuid.put(dto.getFileUuid(), dto); | previousFileSourcesByUuid.put(dto.getFileUuid(), dto); | ||||
FileSourceDto dto = new FileSourceDto() | FileSourceDto dto = new FileSourceDto() | ||||
.setProjectUuid(projectUuid) | .setProjectUuid(projectUuid) | ||||
.setFileUuid(file.getUuid()) | .setFileUuid(file.getUuid()) | ||||
.setDataType(Type.SOURCE) | |||||
.setBinaryData(binaryData) | .setBinaryData(binaryData) | ||||
.setSrcHash(srcHash) | .setSrcHash(srcHash) | ||||
.setDataHash(dataHash) | .setDataHash(dataHash) |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.ce.task.projectanalysis.step; | |||||
import com.google.common.base.Joiner; | |||||
import com.google.common.collect.ArrayListMultimap; | |||||
import com.google.common.collect.HashBasedTable; | |||||
import com.google.common.collect.ImmutableMap; | |||||
import com.google.common.collect.Multimap; | |||||
import com.google.common.collect.Table; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.HashMap; | |||||
import java.util.HashSet; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import org.sonar.api.utils.System2; | |||||
import org.sonar.api.utils.log.Logger; | |||||
import org.sonar.api.utils.log.Loggers; | |||||
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader; | |||||
import org.sonar.ce.task.projectanalysis.component.Component; | |||||
import org.sonar.ce.task.projectanalysis.component.ComponentVisitor; | |||||
import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit; | |||||
import org.sonar.ce.task.projectanalysis.component.DepthTraversalTypeAwareCrawler; | |||||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; | |||||
import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter; | |||||
import org.sonar.ce.task.step.ComputationStep; | |||||
import org.sonar.core.util.CloseableIterator; | |||||
import org.sonar.core.util.Uuids; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.protobuf.DbFileSources; | |||||
import org.sonar.db.source.FileSourceDto; | |||||
import org.sonar.db.source.FileSourceDto.Type; | |||||
import org.sonar.scanner.protocol.output.ScannerReport; | |||||
import org.sonar.scanner.protocol.output.ScannerReport.Test.TestStatus; | |||||
public class PersistTestsStep implements ComputationStep { | |||||
private static final Logger LOG = Loggers.get(PersistTestsStep.class); | |||||
private final DbClient dbClient; | |||||
private final System2 system; | |||||
private final BatchReportReader reportReader; | |||||
private final TreeRootHolder treeRootHolder; | |||||
public PersistTestsStep(DbClient dbClient, System2 system, BatchReportReader reportReader, TreeRootHolder treeRootHolder) { | |||||
this.dbClient = dbClient; | |||||
this.system = system; | |||||
this.reportReader = reportReader; | |||||
this.treeRootHolder = treeRootHolder; | |||||
} | |||||
@Override | |||||
public void execute(ComputationStep.Context context) { | |||||
try (DbSession dbSession = dbClient.openSession(true)) { | |||||
TestDepthTraversalTypeAwareVisitor visitor = new TestDepthTraversalTypeAwareVisitor(dbSession); | |||||
new DepthTraversalTypeAwareCrawler(visitor).visit(treeRootHolder.getRoot()); | |||||
dbSession.commit(); | |||||
if (visitor.hasUnprocessedCoverageDetails) { | |||||
LOG.warn("Some coverage tests are not taken into account during analysis of project '{}'", visitor.getProjectKey()); | |||||
} | |||||
} | |||||
} | |||||
@Override | |||||
public String getDescription() { | |||||
return "Persist tests"; | |||||
} | |||||
private class TestDepthTraversalTypeAwareVisitor extends TypeAwareVisitorAdapter { | |||||
final DbSession session; | |||||
final Map<String, FileSourceDto> existingFileSourcesByUuid; | |||||
final String projectUuid; | |||||
final String projectKey; | |||||
boolean hasUnprocessedCoverageDetails = false; | |||||
public TestDepthTraversalTypeAwareVisitor(DbSession session) { | |||||
super(CrawlerDepthLimit.FILE, ComponentVisitor.Order.PRE_ORDER); | |||||
this.session = session; | |||||
this.existingFileSourcesByUuid = new HashMap<>(); | |||||
this.projectUuid = treeRootHolder.getRoot().getUuid(); | |||||
this.projectKey = treeRootHolder.getRoot().getDbKey(); | |||||
session.select("org.sonar.db.source.FileSourceMapper.selectHashesForProject", | |||||
ImmutableMap.of("projectUuid", treeRootHolder.getRoot().getUuid(), "dataType", Type.TEST), | |||||
context -> { | |||||
FileSourceDto dto = (FileSourceDto) context.getResultObject(); | |||||
existingFileSourcesByUuid.put(dto.getFileUuid(), dto); | |||||
}); | |||||
} | |||||
@Override | |||||
public void visitFile(Component file) { | |||||
if (file.getFileAttributes().isUnitTest()) { | |||||
persistTestResults(file); | |||||
} | |||||
} | |||||
private void persistTestResults(Component component) { | |||||
Multimap<String, DbFileSources.Test.Builder> testsByName = buildDbTests(component.getReportAttributes().getRef()); | |||||
Table<String, String, DbFileSources.Test.CoveredFile.Builder> coveredFilesByName = loadCoverageDetails(component.getReportAttributes().getRef()); | |||||
List<DbFileSources.Test> tests = addCoveredFilesToTests(testsByName, coveredFilesByName); | |||||
if (checkIfThereAreUnprocessedCoverageDetails(testsByName, coveredFilesByName, component.getDbKey())) { | |||||
hasUnprocessedCoverageDetails = true; | |||||
} | |||||
if (tests.isEmpty()) { | |||||
return; | |||||
} | |||||
String componentUuid = getUuid(component.getReportAttributes().getRef()); | |||||
FileSourceDto existingDto = existingFileSourcesByUuid.get(componentUuid); | |||||
long now = system.now(); | |||||
if (existingDto != null) { | |||||
// update | |||||
existingDto | |||||
.setTestData(tests) | |||||
.setUpdatedAt(now); | |||||
dbClient.fileSourceDao().update(session, existingDto); | |||||
} else { | |||||
// insert | |||||
FileSourceDto newDto = new FileSourceDto() | |||||
.setTestData(tests) | |||||
.setFileUuid(componentUuid) | |||||
.setProjectUuid(projectUuid) | |||||
.setDataType(Type.TEST) | |||||
.setCreatedAt(now) | |||||
.setUpdatedAt(now); | |||||
dbClient.fileSourceDao().insert(session, newDto); | |||||
} | |||||
} | |||||
private boolean checkIfThereAreUnprocessedCoverageDetails(Multimap<String, DbFileSources.Test.Builder> testsByName, | |||||
Table<String, String, DbFileSources.Test.CoveredFile.Builder> coveredFilesByName, String componentKey) { | |||||
Set<String> unprocessedCoverageDetailNames = new HashSet<>(coveredFilesByName.rowKeySet()); | |||||
unprocessedCoverageDetailNames.removeAll(testsByName.keySet()); | |||||
boolean hasUnprocessedCoverage = !unprocessedCoverageDetailNames.isEmpty(); | |||||
if (hasUnprocessedCoverage) { | |||||
LOG.trace("The following test coverages for file '{}' have not been taken into account: {}", componentKey, Joiner.on(", ").join(unprocessedCoverageDetailNames)); | |||||
} | |||||
return hasUnprocessedCoverage; | |||||
} | |||||
private List<DbFileSources.Test> addCoveredFilesToTests(Multimap<String, DbFileSources.Test.Builder> testsByName, | |||||
Table<String, String, DbFileSources.Test.CoveredFile.Builder> coveredFilesByName) { | |||||
List<DbFileSources.Test> tests = new ArrayList<>(); | |||||
for (DbFileSources.Test.Builder test : testsByName.values()) { | |||||
Collection<DbFileSources.Test.CoveredFile.Builder> coveredFiles = coveredFilesByName.row(test.getName()).values(); | |||||
if (!coveredFiles.isEmpty()) { | |||||
for (DbFileSources.Test.CoveredFile.Builder coveredFile : coveredFiles) { | |||||
test.addCoveredFile(coveredFile); | |||||
} | |||||
} | |||||
tests.add(test.build()); | |||||
} | |||||
return tests; | |||||
} | |||||
private Multimap<String, DbFileSources.Test.Builder> buildDbTests(int componentRed) { | |||||
Multimap<String, DbFileSources.Test.Builder> tests = ArrayListMultimap.create(); | |||||
try (CloseableIterator<ScannerReport.Test> testIterator = reportReader.readTests(componentRed)) { | |||||
while (testIterator.hasNext()) { | |||||
ScannerReport.Test batchTest = testIterator.next(); | |||||
DbFileSources.Test.Builder dbTest = DbFileSources.Test.newBuilder(); | |||||
dbTest.setUuid(Uuids.create()); | |||||
dbTest.setName(batchTest.getName()); | |||||
if (!batchTest.getStacktrace().isEmpty()) { | |||||
dbTest.setStacktrace(batchTest.getStacktrace()); | |||||
} | |||||
if (batchTest.getStatus() != TestStatus.UNSET) { | |||||
dbTest.setStatus(DbFileSources.Test.TestStatus.valueOf(batchTest.getStatus().name())); | |||||
} | |||||
if (!batchTest.getMsg().isEmpty()) { | |||||
dbTest.setMsg(batchTest.getMsg()); | |||||
} | |||||
dbTest.setExecutionTimeMs(batchTest.getDurationInMs()); | |||||
tests.put(dbTest.getName(), dbTest); | |||||
} | |||||
} | |||||
return tests; | |||||
} | |||||
/** | |||||
* returns a Table of (test name, main file uuid, covered file) | |||||
*/ | |||||
private Table<String, String, DbFileSources.Test.CoveredFile.Builder> loadCoverageDetails(int testFileRef) { | |||||
Table<String, String, DbFileSources.Test.CoveredFile.Builder> nameToCoveredFiles = HashBasedTable.create(); | |||||
try (CloseableIterator<ScannerReport.CoverageDetail> coverageIterator = reportReader.readCoverageDetails(testFileRef)) { | |||||
while (coverageIterator.hasNext()) { | |||||
ScannerReport.CoverageDetail batchCoverageDetail = coverageIterator.next(); | |||||
String testName = batchCoverageDetail.getTestName(); | |||||
for (ScannerReport.CoverageDetail.CoveredFile batchCoveredFile : batchCoverageDetail.getCoveredFileList()) { | |||||
loadCoverageFile(batchCoveredFile, testName, nameToCoveredFiles); | |||||
} | |||||
} | |||||
} | |||||
return nameToCoveredFiles; | |||||
} | |||||
private void loadCoverageFile(ScannerReport.CoverageDetail.CoveredFile batchCoveredFile, String testName, Table<String, String, | |||||
DbFileSources.Test.CoveredFile.Builder> nameToCoveredFiles) { | |||||
String mainFileUuid = getUuid(batchCoveredFile.getFileRef()); | |||||
DbFileSources.Test.CoveredFile.Builder existingDbCoveredFile = nameToCoveredFiles.get(testName, mainFileUuid); | |||||
List<Integer> batchCoveredLines = batchCoveredFile.getCoveredLineList(); | |||||
if (existingDbCoveredFile == null) { | |||||
DbFileSources.Test.CoveredFile.Builder dbCoveredFile = DbFileSources.Test.CoveredFile.newBuilder() | |||||
.setFileUuid(getUuid(batchCoveredFile.getFileRef())) | |||||
.addAllCoveredLine(batchCoveredLines); | |||||
nameToCoveredFiles.put(testName, mainFileUuid, dbCoveredFile); | |||||
} else { | |||||
List<Integer> remainingBatchCoveredLines = new ArrayList<>(batchCoveredLines); | |||||
remainingBatchCoveredLines.removeAll(existingDbCoveredFile.getCoveredLineList()); | |||||
existingDbCoveredFile.addAllCoveredLine(batchCoveredLines); | |||||
} | |||||
} | |||||
private String getUuid(int fileRef) { | |||||
return treeRootHolder.getComponentByRef(fileRef).getUuid(); | |||||
} | |||||
public String getProjectKey() { | |||||
return projectKey; | |||||
} | |||||
} | |||||
} |
PersistProjectLinksStep.class, | PersistProjectLinksStep.class, | ||||
PersistEventsStep.class, | PersistEventsStep.class, | ||||
PersistFileSourcesStep.class, | PersistFileSourcesStep.class, | ||||
PersistTestsStep.class, | |||||
PersistCrossProjectDuplicationIndexStep.class, | PersistCrossProjectDuplicationIndexStep.class, | ||||
EnableAnalysisStep.class, | EnableAnalysisStep.class, | ||||
private static final ScannerReport.SyntaxHighlightingRule SYNTAX_HIGHLIGHTING_2 = ScannerReport.SyntaxHighlightingRule.newBuilder().build(); | private static final ScannerReport.SyntaxHighlightingRule SYNTAX_HIGHLIGHTING_2 = ScannerReport.SyntaxHighlightingRule.newBuilder().build(); | ||||
private static final ScannerReport.LineCoverage COVERAGE_1 = ScannerReport.LineCoverage.newBuilder().build(); | private static final ScannerReport.LineCoverage COVERAGE_1 = ScannerReport.LineCoverage.newBuilder().build(); | ||||
private static final ScannerReport.LineCoverage COVERAGE_2 = ScannerReport.LineCoverage.newBuilder().build(); | private static final ScannerReport.LineCoverage COVERAGE_2 = ScannerReport.LineCoverage.newBuilder().build(); | ||||
private static final ScannerReport.Test TEST_1 = ScannerReport.Test.newBuilder().setName("1").build(); | |||||
private static final ScannerReport.Test TEST_2 = ScannerReport.Test.newBuilder().setName("2").build(); | |||||
private static final ScannerReport.CoverageDetail COVERAGE_DETAIL_1 = ScannerReport.CoverageDetail.newBuilder().setTestName("1").build(); | |||||
private static final ScannerReport.CoverageDetail COVERAGE_DETAIL_2 = ScannerReport.CoverageDetail.newBuilder().setTestName("2").build(); | |||||
@Rule | @Rule | ||||
public JUnitTempFolder tempFolder = new JUnitTempFolder(); | public JUnitTempFolder tempFolder = new JUnitTempFolder(); | ||||
res.close(); | res.close(); | ||||
} | } | ||||
@Test | |||||
public void readTests_returns_empty_CloseableIterator_when_file_does_not_exist() { | |||||
assertThat(underTest.readTests(COMPONENT_REF)).isEmpty(); | |||||
} | |||||
@Test | |||||
public void verify_readTests() { | |||||
writer.writeTests(COMPONENT_REF, of(TEST_1, TEST_2)); | |||||
CloseableIterator<ScannerReport.Test> res = underTest.readTests(COMPONENT_REF); | |||||
assertThat(res).containsExactly(TEST_1, TEST_2); | |||||
res.close(); | |||||
} | |||||
@Test | |||||
public void readCoverageDetails_returns_empty_CloseableIterator_when_file_does_not_exist() { | |||||
assertThat(underTest.readCoverageDetails(COMPONENT_REF)).isEmpty(); | |||||
} | |||||
@Test | |||||
public void verify_readCoverageDetails() { | |||||
writer.writeCoverageDetails(COMPONENT_REF, of(COVERAGE_DETAIL_1, COVERAGE_DETAIL_2)); | |||||
CloseableIterator<ScannerReport.CoverageDetail> res = underTest.readCoverageDetails(COMPONENT_REF); | |||||
assertThat(res).containsExactly(COVERAGE_DETAIL_1, COVERAGE_DETAIL_2); | |||||
res.close(); | |||||
} | |||||
@Test | @Test | ||||
public void verify_readAnalysisWarnings() { | public void verify_readAnalysisWarnings() { | ||||
ScannerReport.AnalysisWarning warning1 = ScannerReport.AnalysisWarning.newBuilder().setText("warning 1").build(); | ScannerReport.AnalysisWarning warning1 = ScannerReport.AnalysisWarning.newBuilder().setText("warning 1").build(); |
private Map<Integer, List<ScannerReport.SyntaxHighlightingRule>> syntaxHighlightings = new HashMap<>(); | private Map<Integer, List<ScannerReport.SyntaxHighlightingRule>> syntaxHighlightings = new HashMap<>(); | ||||
private Map<Integer, List<ScannerReport.LineCoverage>> coverages = new HashMap<>(); | private Map<Integer, List<ScannerReport.LineCoverage>> coverages = new HashMap<>(); | ||||
private Map<Integer, List<String>> fileSources = new HashMap<>(); | private Map<Integer, List<String>> fileSources = new HashMap<>(); | ||||
private Map<Integer, List<ScannerReport.Test>> tests = new HashMap<>(); | |||||
private Map<Integer, List<ScannerReport.CoverageDetail>> coverageDetails = new HashMap<>(); | |||||
private Map<Integer, List<ScannerReport.LineSgnificantCode>> significantCode = new HashMap<>(); | private Map<Integer, List<ScannerReport.LineSgnificantCode>> significantCode = new HashMap<>(); | ||||
private Map<Integer, ScannerReport.ChangedLines> changedLines = new HashMap<>(); | private Map<Integer, ScannerReport.ChangedLines> changedLines = new HashMap<>(); | ||||
private List<ScannerReport.AnalysisWarning> analysisWarnings = Collections.emptyList(); | private List<ScannerReport.AnalysisWarning> analysisWarnings = Collections.emptyList(); | ||||
this.syntaxHighlightings.clear(); | this.syntaxHighlightings.clear(); | ||||
this.coverages.clear(); | this.coverages.clear(); | ||||
this.fileSources.clear(); | this.fileSources.clear(); | ||||
this.tests.clear(); | |||||
this.coverageDetails.clear(); | |||||
this.significantCode.clear(); | this.significantCode.clear(); | ||||
} | } | ||||
return this; | return this; | ||||
} | } | ||||
@Override | |||||
public CloseableIterator<ScannerReport.Test> readTests(int testFileRef) { | |||||
return closeableIterator(this.tests.get(testFileRef)); | |||||
} | |||||
public BatchReportReaderRule putTests(int testFileRed, List<ScannerReport.Test> tests) { | |||||
this.tests.put(testFileRed, tests); | |||||
return this; | |||||
} | |||||
@Override | |||||
public CloseableIterator<ScannerReport.CoverageDetail> readCoverageDetails(int testFileRef) { | |||||
return closeableIterator(this.coverageDetails.get(testFileRef)); | |||||
} | |||||
public BatchReportReaderRule putCoverageDetails(int testFileRef, List<ScannerReport.CoverageDetail> coverageDetails) { | |||||
this.coverageDetails.put(testFileRef, coverageDetails); | |||||
return this; | |||||
} | |||||
} | } |
*/ | */ | ||||
package org.sonar.ce.task.projectanalysis.dbmigration; | package org.sonar.ce.task.projectanalysis.dbmigration; | ||||
import com.tngtech.java.junit.dataprovider.DataProvider; | |||||
import com.tngtech.java.junit.dataprovider.DataProviderRunner; | |||||
import com.tngtech.java.junit.dataprovider.UseDataProvider; | |||||
import java.sql.SQLException; | import java.sql.SQLException; | ||||
import java.util.Optional; | import java.util.Optional; | ||||
import java.util.Random; | import java.util.Random; | ||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.rules.ExpectedException; | import org.junit.rules.ExpectedException; | ||||
import org.junit.runner.RunWith; | |||||
import org.sonar.api.utils.System2; | import org.sonar.api.utils.System2; | ||||
import org.sonar.ce.task.CeTask; | import org.sonar.ce.task.CeTask; | ||||
import org.sonar.db.DbTester; | import org.sonar.db.DbTester; | ||||
import org.sonar.db.source.FileSourceDto; | |||||
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; | import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||
import static org.sonar.db.source.FileSourceDto.LINE_COUNT_NOT_POPULATED; | import static org.sonar.db.source.FileSourceDto.LINE_COUNT_NOT_POPULATED; | ||||
@RunWith(DataProviderRunner.class) | |||||
public class PopulateFileSourceLineCountTest { | public class PopulateFileSourceLineCountTest { | ||||
@Rule | @Rule | ||||
} | } | ||||
@Test | @Test | ||||
@UseDataProvider("anyType") | |||||
public void execute_populates_line_count_of_any_type(String type) throws SQLException { | |||||
public void execute_populates_line_count_of_any_type() throws SQLException { | |||||
String projectUuid = randomAlphanumeric(4); | String projectUuid = randomAlphanumeric(4); | ||||
String fileUuid = randomAlphanumeric(5); | String fileUuid = randomAlphanumeric(5); | ||||
when(ceTask.getComponent()).thenReturn(newComponent(projectUuid)); | when(ceTask.getComponent()).thenReturn(newComponent(projectUuid)); | ||||
int lineCount = 1 + random.nextInt(15); | int lineCount = 1 + random.nextInt(15); | ||||
insertUnpopulatedFileSource(projectUuid, fileUuid, type, lineCount); | |||||
insertUnpopulatedFileSource(projectUuid, fileUuid, lineCount); | |||||
assertThat(getLineCountByFileUuid(fileUuid)).isEqualTo(LINE_COUNT_NOT_POPULATED); | assertThat(getLineCountByFileUuid(fileUuid)).isEqualTo(LINE_COUNT_NOT_POPULATED); | ||||
underTest.execute(); | underTest.execute(); | ||||
} | } | ||||
@Test | @Test | ||||
@UseDataProvider("anyType") | |||||
public void execute_changes_only_file_source_with_LINE_COUNT_NOT_POPULATED_value(String type) throws SQLException { | |||||
public void execute_changes_only_file_source_with_LINE_COUNT_NOT_POPULATED_value() throws SQLException { | |||||
String projectUuid = randomAlphanumeric(4); | String projectUuid = randomAlphanumeric(4); | ||||
String fileUuid1 = randomAlphanumeric(5); | String fileUuid1 = randomAlphanumeric(5); | ||||
String fileUuid2 = randomAlphanumeric(6); | String fileUuid2 = randomAlphanumeric(6); | ||||
int lineCountFile3 = 150 + random.nextInt(15); | int lineCountFile3 = 150 + random.nextInt(15); | ||||
when(ceTask.getComponent()).thenReturn(newComponent(projectUuid)); | when(ceTask.getComponent()).thenReturn(newComponent(projectUuid)); | ||||
insertPopulatedFileSource(projectUuid, fileUuid1, type, lineCountFile1); | |||||
int badLineCountFile2 = insertInconsistentPopulatedFileSource(projectUuid, fileUuid2, type, lineCountFile2); | |||||
insertUnpopulatedFileSource(projectUuid, fileUuid3, type, lineCountFile3); | |||||
insertPopulatedFileSource(projectUuid, fileUuid1, lineCountFile1); | |||||
int badLineCountFile2 = insertInconsistentPopulatedFileSource(projectUuid, fileUuid2, lineCountFile2); | |||||
insertUnpopulatedFileSource(projectUuid, fileUuid3, lineCountFile3); | |||||
assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(lineCountFile1); | assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(lineCountFile1); | ||||
assertThat(getLineCountByFileUuid(fileUuid2)).isEqualTo(badLineCountFile2); | assertThat(getLineCountByFileUuid(fileUuid2)).isEqualTo(badLineCountFile2); | ||||
assertThat(getLineCountByFileUuid(fileUuid3)).isEqualTo(LINE_COUNT_NOT_POPULATED); | assertThat(getLineCountByFileUuid(fileUuid3)).isEqualTo(LINE_COUNT_NOT_POPULATED); | ||||
} | } | ||||
@Test | @Test | ||||
@UseDataProvider("anyType") | |||||
public void execute_changes_only_file_source_of_CeTask_component_uuid(String type) throws SQLException { | |||||
public void execute_changes_only_file_source_of_CeTask_component_uuid() throws SQLException { | |||||
String projectUuid1 = randomAlphanumeric(4); | String projectUuid1 = randomAlphanumeric(4); | ||||
String projectUuid2 = randomAlphanumeric(5); | String projectUuid2 = randomAlphanumeric(5); | ||||
String fileUuid1 = randomAlphanumeric(6); | String fileUuid1 = randomAlphanumeric(6); | ||||
int lineCountFile2 = 30 + random.nextInt(15); | int lineCountFile2 = 30 + random.nextInt(15); | ||||
when(ceTask.getComponent()).thenReturn(newComponent(projectUuid1)); | when(ceTask.getComponent()).thenReturn(newComponent(projectUuid1)); | ||||
insertUnpopulatedFileSource(projectUuid1, fileUuid1, type, lineCountFile1); | |||||
insertUnpopulatedFileSource(projectUuid2, fileUuid2, type, lineCountFile2); | |||||
insertUnpopulatedFileSource(projectUuid1, fileUuid1, lineCountFile1); | |||||
insertUnpopulatedFileSource(projectUuid2, fileUuid2, lineCountFile2); | |||||
underTest.execute(); | underTest.execute(); | ||||
} | } | ||||
@Test | @Test | ||||
@UseDataProvider("anyType") | |||||
public void execute_set_line_count_to_zero_when_file_source_has_no_line_hashes(String type) throws SQLException { | |||||
public void execute_set_line_count_to_zero_when_file_source_has_no_line_hashes() throws SQLException { | |||||
String projectUuid = randomAlphanumeric(4); | String projectUuid = randomAlphanumeric(4); | ||||
String fileUuid1 = randomAlphanumeric(5); | String fileUuid1 = randomAlphanumeric(5); | ||||
when(ceTask.getComponent()).thenReturn(newComponent(projectUuid)); | when(ceTask.getComponent()).thenReturn(newComponent(projectUuid)); | ||||
insertFileSource(projectUuid, fileUuid1, type, null, LINE_COUNT_NOT_POPULATED); | |||||
insertFileSource(projectUuid, fileUuid1, null, LINE_COUNT_NOT_POPULATED); | |||||
underTest.execute(); | underTest.execute(); | ||||
} | } | ||||
@Test | @Test | ||||
@UseDataProvider("anyType") | |||||
public void execute_set_line_count_to_1_when_file_source_has_empty_line_hashes(String type) throws SQLException { | |||||
public void execute_set_line_count_to_1_when_file_source_has_empty_line_hashes() throws SQLException { | |||||
String projectUuid = randomAlphanumeric(4); | String projectUuid = randomAlphanumeric(4); | ||||
String fileUuid1 = randomAlphanumeric(5); | String fileUuid1 = randomAlphanumeric(5); | ||||
when(ceTask.getComponent()).thenReturn(newComponent(projectUuid)); | when(ceTask.getComponent()).thenReturn(newComponent(projectUuid)); | ||||
insertFileSource(projectUuid, fileUuid1, type, "", LINE_COUNT_NOT_POPULATED); | |||||
insertFileSource(projectUuid, fileUuid1, "", LINE_COUNT_NOT_POPULATED); | |||||
underTest.execute(); | underTest.execute(); | ||||
assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(1); | assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(1); | ||||
} | } | ||||
@DataProvider | |||||
public static Object[][] anyType() { | |||||
return new Object[][] { | |||||
{FileSourceDto.Type.SOURCE}, | |||||
{FileSourceDto.Type.TEST}, | |||||
{null}, | |||||
{randomAlphanumeric(3)}, | |||||
}; | |||||
} | |||||
private int getLineCountByFileUuid(String fileUuid) { | private int getLineCountByFileUuid(String fileUuid) { | ||||
Long res = (Long) db.selectFirst("select line_count as \"LINE_COUNT\" from file_sources where file_uuid = '" + fileUuid + "'") | Long res = (Long) db.selectFirst("select line_count as \"LINE_COUNT\" from file_sources where file_uuid = '" + fileUuid + "'") | ||||
.get("LINE_COUNT"); | .get("LINE_COUNT"); | ||||
return res.intValue(); | return res.intValue(); | ||||
} | } | ||||
private void insertUnpopulatedFileSource(String projectUuid, String fileUuid, @Nullable String dataType, int numberOfHashes) { | |||||
private void insertUnpopulatedFileSource(String projectUuid, String fileUuid, int numberOfHashes) { | |||||
String lineHashes = generateLineHashes(numberOfHashes); | String lineHashes = generateLineHashes(numberOfHashes); | ||||
insertFileSource(projectUuid, fileUuid, dataType, lineHashes, LINE_COUNT_NOT_POPULATED); | |||||
insertFileSource(projectUuid, fileUuid, lineHashes, LINE_COUNT_NOT_POPULATED); | |||||
} | } | ||||
private void insertPopulatedFileSource(String projectUuid, String fileUuid, @Nullable String dataType, int lineCount) { | |||||
private void insertPopulatedFileSource(String projectUuid, String fileUuid, int lineCount) { | |||||
String lineHashes = generateLineHashes(lineCount); | String lineHashes = generateLineHashes(lineCount); | ||||
insertFileSource(projectUuid, fileUuid, dataType, lineHashes, lineCount); | |||||
insertFileSource(projectUuid, fileUuid, lineHashes, lineCount); | |||||
} | } | ||||
private int insertInconsistentPopulatedFileSource(String projectUuid, String fileUuid, @Nullable String dataType, int lineCount) { | |||||
private int insertInconsistentPopulatedFileSource(String projectUuid, String fileUuid, int lineCount) { | |||||
String lineHashes = generateLineHashes(lineCount); | String lineHashes = generateLineHashes(lineCount); | ||||
int badLineCount = lineCount + random.nextInt(6); | int badLineCount = lineCount + random.nextInt(6); | ||||
insertFileSource(projectUuid, fileUuid, dataType, lineHashes, badLineCount); | |||||
insertFileSource(projectUuid, fileUuid, lineHashes, badLineCount); | |||||
return badLineCount; | return badLineCount; | ||||
} | } | ||||
.collect(Collectors.joining("\n")); | .collect(Collectors.joining("\n")); | ||||
} | } | ||||
private void insertFileSource(String projectUuid, String fileUuid, @Nullable String dataType, @Nullable String lineHashes, int lineCount) { | |||||
private void insertFileSource(String projectUuid, String fileUuid, @Nullable String lineHashes, int lineCount) { | |||||
db.executeInsert( | db.executeInsert( | ||||
"FILE_SOURCES", | "FILE_SOURCES", | ||||
"PROJECT_UUID", projectUuid, | "PROJECT_UUID", projectUuid, | ||||
"FILE_UUID", fileUuid, | "FILE_UUID", fileUuid, | ||||
"LINE_HASHES", lineHashes, | "LINE_HASHES", lineHashes, | ||||
"DATA_TYPE", dataType, | |||||
"LINE_COUNT", lineCount, | "LINE_COUNT", lineCount, | ||||
"CREATED_AT", 1_222_333L, | "CREATED_AT", 1_222_333L, | ||||
"UPDATED_AT", 1_222_333L); | "UPDATED_AT", 1_222_333L); |
FileSourceDto fileSourceDto = new FileSourceDto() | FileSourceDto fileSourceDto = new FileSourceDto() | ||||
.setFileUuid(file.uuid()) | .setFileUuid(file.uuid()) | ||||
.setProjectUuid(file.projectUuid()) | .setProjectUuid(file.projectUuid()) | ||||
.setLineHashes(linesHashesComputer.getLineHashes()) | |||||
.setDataType(FileSourceDto.Type.SOURCE); | |||||
.setLineHashes(linesHashesComputer.getLineHashes()); | |||||
dbTester.getDbClient().fileSourceDao().insert(dbTester.getSession(), fileSourceDto); | dbTester.getDbClient().fileSourceDao().insert(dbTester.getSession(), fileSourceDto); | ||||
dbTester.commit(); | dbTester.commit(); | ||||
return fileSourceDto; | return fileSourceDto; |
import org.junit.Test; | import org.junit.Test; | ||||
import org.sonar.server.component.index.ComponentIndexer; | import org.sonar.server.component.index.ComponentIndexer; | ||||
import org.sonar.server.issue.index.IssueIndexer; | import org.sonar.server.issue.index.IssueIndexer; | ||||
import org.sonar.server.test.index.TestIndexer; | |||||
import static java.util.Arrays.asList; | import static java.util.Arrays.asList; | ||||
import static java.util.Collections.singletonList; | import static java.util.Collections.singletonList; | ||||
public class IndexPurgeListenerTest { | public class IndexPurgeListenerTest { | ||||
private TestIndexer testIndexer = mock(TestIndexer.class); | |||||
private IssueIndexer issueIndexer = mock(IssueIndexer.class); | private IssueIndexer issueIndexer = mock(IssueIndexer.class); | ||||
private ComponentIndexer componentIndexer = mock(ComponentIndexer.class); | private ComponentIndexer componentIndexer = mock(ComponentIndexer.class); | ||||
private IndexPurgeListener underTest = new IndexPurgeListener(testIndexer, issueIndexer, componentIndexer); | |||||
private IndexPurgeListener underTest = new IndexPurgeListener(issueIndexer, componentIndexer); | |||||
@Test | @Test | ||||
public void test_onComponentDisabling() { | public void test_onComponentDisabling() { | ||||
List<String> uuids = singletonList(uuid); | List<String> uuids = singletonList(uuid); | ||||
underTest.onComponentsDisabling(projectUuid, uuids); | underTest.onComponentsDisabling(projectUuid, uuids); | ||||
verify(testIndexer).deleteByFile(uuid); | |||||
verify(componentIndexer).delete(projectUuid, uuids); | verify(componentIndexer).delete(projectUuid, uuids); | ||||
} | } | ||||
import org.sonar.db.DbTester; | import org.sonar.db.DbTester; | ||||
import org.sonar.db.protobuf.DbFileSources; | import org.sonar.db.protobuf.DbFileSources; | ||||
import org.sonar.db.source.FileSourceDto; | import org.sonar.db.source.FileSourceDto; | ||||
import org.sonar.db.source.FileSourceDto.Type; | |||||
import org.sonar.db.source.LineHashVersion; | import org.sonar.db.source.LineHashVersion; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getProjectUuid()).isEqualTo(PROJECT_UUID); | assertThat(fileSourceDto.getProjectUuid()).isEqualTo(PROJECT_UUID); | ||||
assertThat(fileSourceDto.getFileUuid()).isEqualTo(FILE1_UUID); | assertThat(fileSourceDto.getFileUuid()).isEqualTo(FILE1_UUID); | ||||
assertThat(fileSourceDto.getBinaryData()).isNotEmpty(); | assertThat(fileSourceDto.getBinaryData()).isNotEmpty(); | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getLineHashes()).containsExactly("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605"); | assertThat(fileSourceDto.getLineHashes()).containsExactly("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605"); | ||||
assertThat(fileSourceDto.getSrcHash()).isEqualTo("ee5a58024a155466b43bc559d953e018"); | assertThat(fileSourceDto.getSrcHash()).isEqualTo("ee5a58024a155466b43bc559d953e018"); | ||||
verify(fileSourceDataWarnings).commitWarnings(); | verify(fileSourceDataWarnings).commitWarnings(); | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData); | assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData); | ||||
verify(fileSourceDataWarnings).commitWarnings(); | verify(fileSourceDataWarnings).commitWarnings(); | ||||
} | } | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData); | assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData); | ||||
assertThat(fileSourceDto.getRevision()).isNull(); | assertThat(fileSourceDto.getRevision()).isNull(); | ||||
verify(fileSourceDataWarnings).commitWarnings(); | verify(fileSourceDataWarnings).commitWarnings(); | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
DbFileSources.Data data = fileSourceDto.getSourceData(); | DbFileSources.Data data = fileSourceDto.getSourceData(); | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
DbFileSources.Data data = fileSourceDto.getSourceData(); | DbFileSources.Data data = fileSourceDto.getSourceData(); | ||||
assertThat(data).isEqualTo(dbData); | assertThat(data).isEqualTo(dbData); | ||||
assertThat(data.getLinesList()).hasSize(1); | assertThat(data.getLinesList()).hasSize(1); | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData); | assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData); | ||||
verify(fileSourceDataWarnings).commitWarnings(); | verify(fileSourceDataWarnings).commitWarnings(); | ||||
} | } | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData); | assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData); | ||||
verify(fileSourceDataWarnings).commitWarnings(); | verify(fileSourceDataWarnings).commitWarnings(); | ||||
} | } | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getRevision()).isEqualTo("rev-1"); | assertThat(fileSourceDto.getRevision()).isEqualTo("rev-1"); | ||||
verify(fileSourceDataWarnings).commitWarnings(); | verify(fileSourceDataWarnings).commitWarnings(); | ||||
} | } | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getRevision()).isNull(); | assertThat(fileSourceDto.getRevision()).isNull(); | ||||
verify(fileSourceDataWarnings).commitWarnings(); | verify(fileSourceDataWarnings).commitWarnings(); | ||||
} | } | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getSrcHash()).isEqualTo("sourceHash"); | assertThat(fileSourceDto.getSrcHash()).isEqualTo("sourceHash"); | ||||
assertThat(fileSourceDto.getLineHashes()).isEqualTo(Collections.singletonList("lineHash")); | assertThat(fileSourceDto.getLineHashes()).isEqualTo(Collections.singletonList("lineHash")); | ||||
assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST); | assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST); | ||||
dbClient.fileSourceDao().insert(dbTester.getSession(), new FileSourceDto() | dbClient.fileSourceDao().insert(dbTester.getSession(), new FileSourceDto() | ||||
.setProjectUuid(PROJECT_UUID) | .setProjectUuid(PROJECT_UUID) | ||||
.setFileUuid(FILE1_UUID) | .setFileUuid(FILE1_UUID) | ||||
.setDataType(Type.SOURCE) | |||||
.setSrcHash("5b4bd9815cdb17b8ceae19eb1810c34c") | .setSrcHash("5b4bd9815cdb17b8ceae19eb1810c34c") | ||||
.setLineHashes(Collections.singletonList("6438c669e0d0de98e6929c2cc0fac474")) | .setLineHashes(Collections.singletonList("6438c669e0d0de98e6929c2cc0fac474")) | ||||
.setDataHash("6cad150e3d065976c230cddc5a09efaa") | .setDataHash("6cad150e3d065976c230cddc5a09efaa") | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past); | assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past); | ||||
assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW); | assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW); | ||||
assertThat(fileSourceDto.getRevision()).isEqualTo("rev-1"); | assertThat(fileSourceDto.getRevision()).isEqualTo("rev-1"); | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST); | assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST); | ||||
assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW); | assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW); | ||||
assertThat(fileSourceDto.getSrcHash()).isEqualTo("newSourceHash"); | assertThat(fileSourceDto.getSrcHash()).isEqualTo("newSourceHash"); | ||||
underTest.execute(new TestComputationStepContext()); | underTest.execute(new TestComputationStepContext()); | ||||
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID); | |||||
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectByFileUuid(session, FILE1_UUID); | |||||
assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST); | assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST); | ||||
assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW); | assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW); | ||||
assertThat(fileSourceDto.getRevision()).isEqualTo("revision"); | assertThat(fileSourceDto.getRevision()).isEqualTo("revision"); | ||||
FileSourceDto dto = new FileSourceDto() | FileSourceDto dto = new FileSourceDto() | ||||
.setProjectUuid(PROJECT_UUID) | .setProjectUuid(PROJECT_UUID) | ||||
.setFileUuid(FILE1_UUID) | .setFileUuid(FILE1_UUID) | ||||
.setDataType(Type.SOURCE) | |||||
.setSrcHash("sourceHash") | .setSrcHash("sourceHash") | ||||
.setLineHashes(Collections.singletonList("lineHash")) | .setLineHashes(Collections.singletonList("lineHash")) | ||||
.setDataHash(dataHash) | .setDataHash(dataHash) |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.ce.task.projectanalysis.step; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import org.junit.Before; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | |||||
import org.sonar.api.utils.System2; | |||||
import org.sonar.api.utils.log.LogTester; | |||||
import org.sonar.api.utils.log.LoggerLevel; | |||||
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule; | |||||
import org.sonar.ce.task.projectanalysis.component.Component; | |||||
import org.sonar.ce.task.projectanalysis.component.FileAttributes; | |||||
import org.sonar.ce.task.projectanalysis.component.ReportComponent; | |||||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; | |||||
import org.sonar.ce.task.step.ComputationStep; | |||||
import org.sonar.ce.task.step.TestComputationStepContext; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbTester; | |||||
import org.sonar.db.protobuf.DbFileSources; | |||||
import org.sonar.db.source.FileSourceDto; | |||||
import org.sonar.scanner.protocol.output.ScannerReport; | |||||
import org.sonar.scanner.protocol.output.ScannerReport.CoverageDetail; | |||||
import org.sonar.scanner.protocol.output.ScannerReport.Test.TestStatus; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.assertj.core.api.Assertions.tuple; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
public class PersistTestsStepTest extends BaseStepTest { | |||||
private static final String PROJECT_UUID = "PROJECT"; | |||||
private static final String PROJECT_KEY = "PROJECT_KEY"; | |||||
private static final int TEST_FILE_REF_1 = 3; | |||||
private static final int TEST_FILE_REF_2 = 4; | |||||
private static final int MAIN_FILE_REF_1 = 5; | |||||
private static final int MAIN_FILE_REF_2 = 6; | |||||
private static final String TEST_FILE_UUID_1 = "TEST-FILE-1"; | |||||
private static final String TEST_FILE_UUID_2 = "TEST-FILE-2"; | |||||
private static final String MAIN_FILE_UUID_1 = "MAIN-FILE-1"; | |||||
private static final String MAIN_FILE_UUID_2 = "MAIN-FILE-2"; | |||||
@Rule | |||||
public DbTester db = DbTester.create(System2.INSTANCE); | |||||
@Rule | |||||
public BatchReportReaderRule reportReader = new BatchReportReaderRule(); | |||||
@Rule | |||||
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); | |||||
@Rule | |||||
public LogTester log = new LogTester(); | |||||
DbClient dbClient = db.getDbClient(); | |||||
Component root; | |||||
PersistTestsStep underTest; | |||||
long now = 123456789L; | |||||
@Before | |||||
public void setup() { | |||||
System2 system2 = mock(System2.class); | |||||
when(system2.now()).thenReturn(now); | |||||
underTest = new PersistTestsStep(dbClient, system2, reportReader, treeRootHolder); | |||||
root = ReportComponent.builder(Component.Type.PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).addChildren( | |||||
ReportComponent.builder(Component.Type.FILE, 3).setUuid(TEST_FILE_UUID_1).setKey("TEST_FILE1_KEY").setFileAttributes(new FileAttributes(true, null, 1)).build(), | |||||
ReportComponent.builder(Component.Type.FILE, 4).setUuid(TEST_FILE_UUID_2).setKey("TEST_FILE2_KEY").setFileAttributes(new FileAttributes(true, null, 1)).build(), | |||||
ReportComponent.builder(Component.Type.FILE, 5).setUuid(MAIN_FILE_UUID_1).setKey("MAIN_FILE1_KEY").build(), | |||||
ReportComponent.builder(Component.Type.FILE, 6).setUuid(MAIN_FILE_UUID_2).setKey("MAIN_FILE2_KEY").build()) | |||||
.build(); | |||||
treeRootHolder.setRoot(root); | |||||
} | |||||
@Override | |||||
protected ComputationStep step() { | |||||
return underTest; | |||||
} | |||||
@Test | |||||
public void no_test_in_database_and_batch_report() { | |||||
underTest.execute(new TestComputationStepContext()); | |||||
assertThat(dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1)).isNull(); | |||||
assertThat(log.logs()).isEmpty(); | |||||
} | |||||
@Test | |||||
public void insert_several_tests_in_a_report() { | |||||
List<ScannerReport.Test> batchTests = Arrays.asList( | |||||
newTest(1), newTest(2)); | |||||
reportReader.putTests(TEST_FILE_REF_1, batchTests); | |||||
List<CoverageDetail> coverageDetails = Arrays.asList( | |||||
newCoverageDetail(1, MAIN_FILE_REF_1)); | |||||
reportReader.putCoverageDetails(TEST_FILE_REF_1, coverageDetails); | |||||
underTest.execute(new TestComputationStepContext()); | |||||
assertThat(db.countRowsOfTable("file_sources")).isEqualTo(1); | |||||
FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); | |||||
assertThat(dto.getCreatedAt()).isEqualTo(now); | |||||
assertThat(dto.getUpdatedAt()).isEqualTo(now); | |||||
assertThat(dto.getProjectUuid()).isEqualTo(PROJECT_UUID); | |||||
assertThat(dto.getFileUuid()).isEqualTo(TEST_FILE_UUID_1); | |||||
assertThat(dto.getTestData()).hasSize(2); | |||||
assertThat(dto.getTestData()).extracting("name", "coveredFileCount").containsOnly( | |||||
tuple("name#1", 1), | |||||
tuple("name#2", 0)); | |||||
assertThat(log.logs()).isEmpty(); | |||||
} | |||||
@Test | |||||
public void insert_all_data_of_a_test() { | |||||
reportReader.putTests(TEST_FILE_REF_1, Arrays.asList(newTest(1))); | |||||
reportReader.putCoverageDetails(TEST_FILE_REF_1, Arrays.asList(newCoverageDetail(1, MAIN_FILE_REF_1))); | |||||
underTest.execute(new TestComputationStepContext()); | |||||
FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); | |||||
assertThat(dto.getCreatedAt()).isEqualTo(now); | |||||
assertThat(dto.getUpdatedAt()).isEqualTo(now); | |||||
assertThat(dto.getProjectUuid()).isEqualTo(PROJECT_UUID); | |||||
assertThat(dto.getFileUuid()).isEqualTo(TEST_FILE_UUID_1); | |||||
assertThat(dto.getTestData()).hasSize(1); | |||||
DbFileSources.Test test1 = dto.getTestData().get(0); | |||||
assertThat(test1.getUuid()).isNotEmpty(); | |||||
assertThat(test1.getName()).isEqualTo("name#1"); | |||||
assertThat(test1.getMsg()).isEqualTo("message#1"); | |||||
assertThat(test1.getStacktrace()).isEqualTo("stacktrace#1"); | |||||
assertThat(test1.getStatus()).isEqualTo(DbFileSources.Test.TestStatus.FAILURE); | |||||
assertThat(test1.getExecutionTimeMs()).isEqualTo(1_000); | |||||
assertThat(test1.getCoveredFileCount()).isEqualTo(1); | |||||
assertThat(test1.getCoveredFile(0).getCoveredLineList()).containsOnly(1, 2, 3); | |||||
assertThat(test1.getCoveredFile(0).getFileUuid()).isEqualTo(MAIN_FILE_UUID_1); | |||||
} | |||||
@Test | |||||
public void insert_tests_without_coverage_details() { | |||||
List<ScannerReport.Test> batchTests = Arrays.asList(newTest(1)); | |||||
reportReader.putTests(TEST_FILE_REF_1, batchTests); | |||||
underTest.execute(new TestComputationStepContext()); | |||||
FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); | |||||
assertThat(dto.getFileUuid()).isEqualTo(TEST_FILE_UUID_1); | |||||
List<DbFileSources.Test> tests = dto.getTestData(); | |||||
assertThat(tests).hasSize(1); | |||||
assertThat(tests.get(0).getCoveredFileList()).isEmpty(); | |||||
assertThat(tests.get(0).getMsg()).isEqualTo("message#1"); | |||||
} | |||||
@Test | |||||
public void insert_coverage_details_not_taken_into_account() { | |||||
List<ScannerReport.Test> batchTests = Arrays.asList(newTest(1)); | |||||
reportReader.putTests(TEST_FILE_REF_1, batchTests); | |||||
List<CoverageDetail> coverageDetails = Arrays.asList(newCoverageDetail(1, MAIN_FILE_REF_1), newCoverageDetail(2, MAIN_FILE_REF_2)); | |||||
reportReader.putCoverageDetails(TEST_FILE_REF_1, coverageDetails); | |||||
reportReader.putCoverageDetails(TEST_FILE_REF_2, coverageDetails); | |||||
underTest.execute(new TestComputationStepContext()); | |||||
assertThat(log.logs(LoggerLevel.WARN)).hasSize(1); | |||||
assertThat(log.logs(LoggerLevel.WARN).get(0)).isEqualTo("Some coverage tests are not taken into account during analysis of project 'PROJECT_KEY'"); | |||||
assertThat(log.logs(LoggerLevel.TRACE)).hasSize(2); | |||||
assertThat(log.logs(LoggerLevel.TRACE).get(0)).isEqualTo("The following test coverages for file 'TEST_FILE1_KEY' have not been taken into account: name#2"); | |||||
assertThat(log.logs(LoggerLevel.TRACE).get(1)).startsWith("The following test coverages for file 'TEST_FILE2_KEY' have not been taken into account: "); | |||||
assertThat(log.logs(LoggerLevel.TRACE).get(1)).contains("name#1", "name#2"); | |||||
} | |||||
@Test | |||||
public void aggregate_coverage_details() { | |||||
reportReader.putTests(TEST_FILE_REF_1, Arrays.asList(newTest(1))); | |||||
reportReader.putCoverageDetails(TEST_FILE_REF_1, Arrays.asList( | |||||
newCoverageDetailWithLines(1, MAIN_FILE_REF_1, 1, 3), | |||||
newCoverageDetailWithLines(1, MAIN_FILE_REF_1, 2, 4))); | |||||
underTest.execute(new TestComputationStepContext()); | |||||
FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); | |||||
List<Integer> coveredLines = dto.getTestData().get(0).getCoveredFile(0).getCoveredLineList(); | |||||
assertThat(coveredLines).containsOnly(1, 2, 3, 4); | |||||
} | |||||
@Test | |||||
public void update_existing_test() { | |||||
// ARRANGE | |||||
dbClient.fileSourceDao().insert(db.getSession(), new FileSourceDto() | |||||
.setProjectUuid(PROJECT_UUID) | |||||
.setFileUuid(TEST_FILE_UUID_1) | |||||
.setTestData(Arrays.asList(DbFileSources.Test.newBuilder() | |||||
.setUuid("test-uuid-1") | |||||
.setName("name#1") | |||||
.setStatus(DbFileSources.Test.TestStatus.ERROR) | |||||
.setStacktrace("old-stacktrace#1") | |||||
.setMsg("old-message#1") | |||||
.setExecutionTimeMs(987_654_321L) | |||||
.build())) | |||||
.setCreatedAt(100_000) | |||||
.setUpdatedAt(100_000)); | |||||
db.getSession().commit(); | |||||
assertThat(dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1)).isNotNull(); | |||||
ScannerReport.Test newBatchTest = newTest(1); | |||||
reportReader.putTests(TEST_FILE_REF_1, Arrays.asList(newBatchTest)); | |||||
CoverageDetail newCoverageDetail = newCoverageDetail(1, MAIN_FILE_REF_1); | |||||
reportReader.putCoverageDetails(TEST_FILE_REF_1, Arrays.asList(newCoverageDetail)); | |||||
// ACT | |||||
underTest.execute(new TestComputationStepContext()); | |||||
// ASSERT | |||||
FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); | |||||
assertThat(dto.getCreatedAt()).isEqualTo(100_000); | |||||
assertThat(dto.getUpdatedAt()).isEqualTo(now); | |||||
assertThat(dto.getTestData()).hasSize(1); | |||||
DbFileSources.Test test = dto.getTestData().get(0); | |||||
assertThat(test.getUuid()).isNotEqualTo("test-uuid-1"); | |||||
assertThat(test.getName()).isEqualTo("name#1"); | |||||
assertThat(test.getStatus()).isEqualTo(DbFileSources.Test.TestStatus.valueOf(newBatchTest.getStatus().name())); | |||||
assertThat(test.getMsg()).isEqualTo(newBatchTest.getMsg()); | |||||
assertThat(test.getStacktrace()).isEqualTo(newBatchTest.getStacktrace()); | |||||
assertThat(test.getExecutionTimeMs()).isEqualTo(newBatchTest.getDurationInMs()); | |||||
assertThat(test.getCoveredFileCount()).isEqualTo(1); | |||||
assertThat(test.getCoveredFile(0).getCoveredLineList()).containsOnly(1, 2, 3); | |||||
assertThat(test.getCoveredFile(0).getFileUuid()).isEqualTo(MAIN_FILE_UUID_1); | |||||
} | |||||
private ScannerReport.Test newTest(int id) { | |||||
return ScannerReport.Test.newBuilder() | |||||
.setStatus(TestStatus.FAILURE) | |||||
.setName("name#" + id) | |||||
.setStacktrace("stacktrace#" + id) | |||||
.setMsg("message#" + id) | |||||
.setDurationInMs(1_000) | |||||
.build(); | |||||
} | |||||
private ScannerReport.CoverageDetail newCoverageDetail(int id, int covered_file_ref) { | |||||
return newCoverageDetailWithLines(id, covered_file_ref, 1, 2, 3); | |||||
} | |||||
private ScannerReport.CoverageDetail newCoverageDetailWithLines(int id, int covered_file_ref, Integer... lines) { | |||||
return CoverageDetail.newBuilder() | |||||
.setTestName("name#" + id) | |||||
.addCoveredFile(CoverageDetail.CoveredFile.newBuilder() | |||||
.addAllCoveredLine(Arrays.asList(lines)) | |||||
.setFileRef(covered_file_ref) | |||||
.build()) | |||||
.build(); | |||||
} | |||||
} |
import org.sonar.server.setting.DatabaseSettingLoader; | import org.sonar.server.setting.DatabaseSettingLoader; | ||||
import org.sonar.server.setting.DatabaseSettingsEnabler; | import org.sonar.server.setting.DatabaseSettingsEnabler; | ||||
import org.sonar.server.setting.ThreadLocalSettings; | import org.sonar.server.setting.ThreadLocalSettings; | ||||
import org.sonar.server.test.index.TestIndexer; | |||||
import org.sonar.server.user.index.UserIndex; | import org.sonar.server.user.index.UserIndex; | ||||
import org.sonar.server.user.index.UserIndexer; | import org.sonar.server.user.index.UserIndexer; | ||||
import org.sonar.server.util.OkHttpClientProvider; | import org.sonar.server.util.OkHttpClientProvider; | ||||
EmailNotificationChannel.class, | EmailNotificationChannel.class, | ||||
ReportAnalysisFailureNotificationModule.class, | ReportAnalysisFailureNotificationModule.class, | ||||
// Tests | |||||
TestIndexer.class, | |||||
// System | // System | ||||
ServerLogging.class, | ServerLogging.class, | ||||
assertThat(picoContainer.getComponentAdapters()) | assertThat(picoContainer.getComponentAdapters()) | ||||
.hasSize( | .hasSize( | ||||
CONTAINER_ITSELF | CONTAINER_ITSELF | ||||
+ 70 // level 4 | |||||
+ 69 // level 4 | |||||
+ 6 // content of CeConfigurationModule | + 6 // content of CeConfigurationModule | ||||
+ 4 // content of CeQueueModule | + 4 // content of CeQueueModule | ||||
+ 3 // content of CeHttpModule | + 3 // content of CeHttpModule |
import org.apache.ibatis.session.ResultHandler; | import org.apache.ibatis.session.ResultHandler; | ||||
import org.sonar.db.Dao; | import org.sonar.db.Dao; | ||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.source.FileSourceDto.Type; | |||||
import static org.sonar.db.DatabaseUtils.toUniqueAndSortedPartitions; | import static org.sonar.db.DatabaseUtils.toUniqueAndSortedPartitions; | ||||
private static final Splitter END_OF_LINE_SPLITTER = Splitter.on('\n'); | private static final Splitter END_OF_LINE_SPLITTER = Splitter.on('\n'); | ||||
@CheckForNull | @CheckForNull | ||||
public FileSourceDto selectSourceByFileUuid(DbSession session, String fileUuid) { | |||||
return mapper(session).select(fileUuid, Type.SOURCE); | |||||
} | |||||
@CheckForNull | |||||
public FileSourceDto selectTestByFileUuid(DbSession dbSession, String fileUuid) { | |||||
return mapper(dbSession).select(fileUuid, Type.TEST); | |||||
public FileSourceDto selectByFileUuid(DbSession session, String fileUuid) { | |||||
return mapper(session).selectByFileUuid(fileUuid); | |||||
} | } | ||||
@CheckForNull | @CheckForNull | ||||
public LineHashVersion selectLineHashesVersion(DbSession dbSession, String fileUuid) { | public LineHashVersion selectLineHashesVersion(DbSession dbSession, String fileUuid) { | ||||
Integer version = mapper(dbSession).selectLineHashesVersion(fileUuid, Type.SOURCE); | |||||
Integer version = mapper(dbSession).selectLineHashesVersion(fileUuid); | |||||
return version == null ? LineHashVersion.WITHOUT_SIGNIFICANT_CODE : LineHashVersion.valueOf(version); | return version == null ? LineHashVersion.WITHOUT_SIGNIFICANT_CODE : LineHashVersion.valueOf(version); | ||||
} | } | ||||
PreparedStatement pstmt = null; | PreparedStatement pstmt = null; | ||||
ResultSet rs = null; | ResultSet rs = null; | ||||
try { | try { | ||||
pstmt = connection.prepareStatement("SELECT line_hashes FROM file_sources WHERE file_uuid=? AND data_type=?"); | |||||
pstmt = connection.prepareStatement("SELECT line_hashes FROM file_sources WHERE file_uuid=? AND data_type='SOURCE'"); | |||||
pstmt.setString(1, fileUuid); | pstmt.setString(1, fileUuid); | ||||
pstmt.setString(2, Type.SOURCE); | |||||
rs = pstmt.executeQuery(); | rs = pstmt.executeQuery(); | ||||
if (rs.next()) { | if (rs.next()) { | ||||
String string = rs.getString(1); | String string = rs.getString(1); | ||||
ResultSet rs = null; | ResultSet rs = null; | ||||
Reader reader = null; | Reader reader = null; | ||||
try { | try { | ||||
pstmt = connection.prepareStatement("SELECT line_hashes FROM file_sources WHERE file_uuid=? AND data_type=?"); | |||||
pstmt = connection.prepareStatement("SELECT line_hashes FROM file_sources WHERE file_uuid=? AND data_type='SOURCE'"); | |||||
pstmt.setString(1, fileUuid); | pstmt.setString(1, fileUuid); | ||||
pstmt.setString(2, Type.SOURCE); | |||||
rs = pstmt.executeQuery(); | rs = pstmt.executeQuery(); | ||||
if (rs.next()) { | if (rs.next()) { | ||||
reader = rs.getCharacterStream(1); | reader = rs.getCharacterStream(1); |
import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||
import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | |||||
import java.util.ArrayList; | |||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.List; | import java.util.List; | ||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
private int lineCount = LINE_COUNT_NOT_POPULATED; | private int lineCount = LINE_COUNT_NOT_POPULATED; | ||||
private String srcHash; | private String srcHash; | ||||
private byte[] binaryData = new byte[0]; | private byte[] binaryData = new byte[0]; | ||||
private String dataType; | |||||
private String dataHash; | private String dataHash; | ||||
private String revision; | private String revision; | ||||
@Nullable | @Nullable | ||||
} | } | ||||
} | } | ||||
public static List<DbFileSources.Test> decodeTestData(byte[] binaryData) { | |||||
// stream is always closed | |||||
return decodeTestData(new ByteArrayInputStream(binaryData)); | |||||
} | |||||
/** | |||||
* Decompress and deserialize content of column FILE_SOURCES.BINARY_DATA. | |||||
* The parameter "input" is always closed by this method. | |||||
*/ | |||||
public static List<DbFileSources.Test> decodeTestData(InputStream binaryInput) { | |||||
LZ4BlockInputStream lz4Input = null; | |||||
List<DbFileSources.Test> tests = new ArrayList<>(); | |||||
try { | |||||
lz4Input = new LZ4BlockInputStream(binaryInput); | |||||
DbFileSources.Test currentTest; | |||||
do { | |||||
currentTest = DbFileSources.Test.parseDelimitedFrom(lz4Input); | |||||
if (currentTest != null) { | |||||
tests.add(currentTest); | |||||
} | |||||
} while (currentTest != null); | |||||
return tests; | |||||
} catch (IOException e) { | |||||
throw new IllegalStateException("Fail to decompress and deserialize source data", e); | |||||
} finally { | |||||
IOUtils.closeQuietly(lz4Input); | |||||
} | |||||
} | |||||
/** | |||||
* Serialize and compress protobuf message {@link org.sonar.db.protobuf.DbFileSources.Data} | |||||
* in the column BINARY_DATA. | |||||
*/ | |||||
public static byte[] encodeTestData(List<DbFileSources.Test> tests) { | |||||
ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); | |||||
LZ4BlockOutputStream compressedOutput = new LZ4BlockOutputStream(byteOutput); | |||||
try { | |||||
for (DbFileSources.Test test : tests) { | |||||
test.writeDelimitedTo(compressedOutput); | |||||
} | |||||
compressedOutput.close(); | |||||
return byteOutput.toByteArray(); | |||||
} catch (IOException e) { | |||||
throw new IllegalStateException("Fail to serialize and compress source tests", e); | |||||
} finally { | |||||
IOUtils.closeQuietly(compressedOutput); | |||||
} | |||||
} | |||||
/** | /** | ||||
* Compressed value of serialized protobuf message {@link org.sonar.db.protobuf.DbFileSources.Data} | * Compressed value of serialized protobuf message {@link org.sonar.db.protobuf.DbFileSources.Data} | ||||
*/ | */ | ||||
} | } | ||||
public FileSourceDto setSourceData(DbFileSources.Data data) { | public FileSourceDto setSourceData(DbFileSources.Data data) { | ||||
this.dataType = Type.SOURCE; | |||||
this.binaryData = encodeSourceData(data); | this.binaryData = encodeSourceData(data); | ||||
return this; | return this; | ||||
} | } | ||||
/** | |||||
* Compressed value of serialized protobuf message {@link org.sonar.db.protobuf.DbFileSources.Data} | |||||
*/ | |||||
public List<DbFileSources.Test> getTestData() { | |||||
return decodeTestData(binaryData); | |||||
} | |||||
public FileSourceDto setTestData(List<DbFileSources.Test> data) { | |||||
this.dataType = Type.TEST; | |||||
this.binaryData = encodeTestData(data); | |||||
return this; | |||||
} | |||||
/** Used by MyBatis */ | /** Used by MyBatis */ | ||||
public String getRawLineHashes() { | public String getRawLineHashes() { | ||||
return lineHashes; | return lineHashes; | ||||
return this; | return this; | ||||
} | } | ||||
public String getDataType() { | |||||
return dataType; | |||||
} | |||||
public FileSourceDto setDataType(String dataType) { | |||||
this.dataType = dataType; | |||||
return this; | |||||
} | |||||
public String getRevision() { | public String getRevision() { | ||||
return revision; | return revision; | ||||
} | } | ||||
return this; | return this; | ||||
} | } | ||||
public static class Type { | |||||
public static final String SOURCE = "SOURCE"; | |||||
public static final String TEST = "TEST"; | |||||
private Type() { | |||||
// utility class | |||||
} | |||||
} | |||||
} | } |
List<FileSourceDto> selectHashesForProject(@Param("projectUuid") String projectUuid, @Param("dataType") String dataType); | List<FileSourceDto> selectHashesForProject(@Param("projectUuid") String projectUuid, @Param("dataType") String dataType); | ||||
@CheckForNull | @CheckForNull | ||||
FileSourceDto select(@Param("fileUuid") String fileUuid, @Param("dataType") String dataType); | |||||
FileSourceDto selectByFileUuid(@Param("fileUuid") String fileUuid); | |||||
void scrollLineHashes(@Param("fileUuids") Collection<String> fileUuids, ResultHandler<LineHashesWithUuidDto> rowHandler); | void scrollLineHashes(@Param("fileUuids") Collection<String> fileUuids, ResultHandler<LineHashesWithUuidDto> rowHandler); | ||||
@CheckForNull | @CheckForNull | ||||
Integer selectLineHashesVersion(@Param("fileUuid") String fileUuid, @Param("dataType") String dataType); | |||||
Integer selectLineHashesVersion(@Param("fileUuid") String fileUuid); | |||||
void insert(FileSourceDto dto); | void insert(FileSourceDto dto); | ||||
<mapper namespace="org.sonar.db.source.FileSourceMapper"> | <mapper namespace="org.sonar.db.source.FileSourceMapper"> | ||||
<select id="select" parameterType="map" resultType="org.sonar.db.source.FileSourceDto"> | |||||
<select id="selectByFileUuid" parameterType="map" resultType="org.sonar.db.source.FileSourceDto"> | |||||
select | select | ||||
id, | id, | ||||
project_uuid as projectUuid, | project_uuid as projectUuid, | ||||
line_count as lineCount, | line_count as lineCount, | ||||
data_hash as dataHash, | data_hash as dataHash, | ||||
src_hash as srcHash, | src_hash as srcHash, | ||||
data_type as | |||||
dataType, | |||||
revision | revision | ||||
from | from | ||||
file_sources | file_sources | ||||
where | where | ||||
file_uuid = #{fileUuid,jdbcType=VARCHAR} | file_uuid = #{fileUuid,jdbcType=VARCHAR} | ||||
and data_type = #{dataType,jdbcType=VARCHAR} | |||||
and data_type = 'SOURCE' | |||||
</select> | </select> | ||||
<select id="selectHashesForProject" parameterType="map" resultType="org.sonar.db.source.FileSourceDto"> | <select id="selectHashesForProject" parameterType="map" resultType="org.sonar.db.source.FileSourceDto"> | ||||
src_hash as srcHash, | src_hash as srcHash, | ||||
revision, | revision, | ||||
updated_at as updatedAt | updated_at as updatedAt | ||||
from | |||||
from | |||||
file_sources | file_sources | ||||
where | where | ||||
project_uuid = #{projectUuid,jdbcType=VARCHAR} | project_uuid = #{projectUuid,jdbcType=VARCHAR} | ||||
and data_type = #{dataType,jdbcType=VARCHAR} | |||||
and data_type = 'SOURCE' | |||||
</select> | </select> | ||||
<select id="scrollLineHashes" parameterType="map" resultType="org.sonar.db.source.LineHashesWithUuidDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY"> | <select id="scrollLineHashes" parameterType="map" resultType="org.sonar.db.source.LineHashesWithUuidDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY"> | ||||
file_sources | file_sources | ||||
WHERE | WHERE | ||||
file_uuid = #{fileUuid,jdbcType=VARCHAR} | file_uuid = #{fileUuid,jdbcType=VARCHAR} | ||||
and data_type=#{dataType,jdbcType=VARCHAR} | |||||
and data_type = 'SOURCE' | |||||
</select> | </select> | ||||
<insert id="insert" parameterType="org.sonar.db.source.FileSourceDto" useGeneratedKeys="false"> | <insert id="insert" parameterType="org.sonar.db.source.FileSourceDto" useGeneratedKeys="false"> | ||||
#{lineCount,jdbcType=INTEGER}, | #{lineCount,jdbcType=INTEGER}, | ||||
#{dataHash,jdbcType=VARCHAR}, | #{dataHash,jdbcType=VARCHAR}, | ||||
#{srcHash,jdbcType=VARCHAR}, | #{srcHash,jdbcType=VARCHAR}, | ||||
#{dataType,jdbcType=VARCHAR}, | |||||
'SOURCE', | |||||
#{revision,jdbcType=VARCHAR} | #{revision,jdbcType=VARCHAR} | ||||
) | ) | ||||
</insert> | </insert> |
verifyFileMoveRowDto(resultHandler, file3); | verifyFileMoveRowDto(resultHandler, file3); | ||||
} | } | ||||
@Test | |||||
public void scrollAllFilesForFileMove_ignores_file_source_of_type_TEST() { | |||||
OrganizationDto organization = db.organizations().insert(); | |||||
ComponentDto project = random.nextBoolean() ? db.components().insertPrivateProject(organization) : db.components().insertPublicProject(organization); | |||||
ComponentDto module1 = db.components().insertComponent(ComponentTesting.newModuleDto(project)); | |||||
ComponentDto module2 = db.components().insertComponent(ComponentTesting.newModuleDto(module1)); | |||||
ComponentAndSource file1 = insertFileAndSource(project, FILE); | |||||
ComponentDto file2 = db.components().insertComponent(ComponentTesting.newFileDto(module1).setQualifier(UNIT_TEST_FILE)); | |||||
db.fileSources().insertFileSource(file2, t -> t.setDataType(UNIT_TEST_FILE)); | |||||
ComponentAndSource file3 = insertFileAndSource(module2, FILE); | |||||
db.fileSources().insertFileSource(file3.component, t -> t.setDataType(UNIT_TEST_FILE)); | |||||
RecordingResultHandler resultHandler = new RecordingResultHandler(); | |||||
underTest.scrollAllFilesForFileMove(dbSession, project.uuid(), resultHandler); | |||||
assertThat(resultHandler.dtos).hasSize(2); | |||||
verifyFileMoveRowDto(resultHandler, file1); | |||||
verifyFileMoveRowDto(resultHandler, file3); | |||||
} | |||||
@Test | @Test | ||||
public void scrollAllFilesForFileMove_scrolls_large_number_of_files_and_uts() { | public void scrollAllFilesForFileMove_scrolls_large_number_of_files_and_uts() { | ||||
OrganizationDto organization = db.organizations().insert(); | OrganizationDto organization = db.organizations().insert(); | ||||
assertThat(dto.getLineCount()).isEqualTo(componentAndSource.source.getLineCount()); | assertThat(dto.getLineCount()).isEqualTo(componentAndSource.source.getLineCount()); | ||||
} | } | ||||
} | } |
assertThat(db.countSql("select count(*) from issues where resolution = 'REMOVED'")).isEqualTo(0); | assertThat(db.countSql("select count(*) from issues where resolution = 'REMOVED'")).isEqualTo(0); | ||||
db.fileSources().insertFileSource(srcFile); | db.fileSources().insertFileSource(srcFile); | ||||
db.fileSources().insertFileSource(testFile, f -> f.setDataType("TEST")); | |||||
FileSourceDto nonSelectedFileSource = db.fileSources().insertFileSource(nonSelectedFile); | FileSourceDto nonSelectedFileSource = db.fileSources().insertFileSource(nonSelectedFile); | ||||
assertThat(db.countRowsOfTable("file_sources")).isEqualTo(3); | |||||
assertThat(db.countRowsOfTable("file_sources")).isEqualTo(2); | |||||
MetricDto metric1 = db.measures().insertMetric(); | MetricDto metric1 = db.measures().insertMetric(); | ||||
MetricDto metric2 = db.measures().insertMetric(); | MetricDto metric2 = db.measures().insertMetric(); | ||||
// delete file sources of selected | // delete file sources of selected | ||||
assertThat(db.countRowsOfTable("file_sources")).isEqualTo(1); | assertThat(db.countRowsOfTable("file_sources")).isEqualTo(1); | ||||
assertThat(db.getDbClient().fileSourceDao().selectSourceByFileUuid(dbSession, nonSelectedFileSource.getFileUuid())).isNotNull(); | |||||
assertThat(db.getDbClient().fileSourceDao().selectByFileUuid(dbSession, nonSelectedFileSource.getFileUuid())).isNotNull(); | |||||
// deletes live measure of selected | // deletes live measure of selected | ||||
assertThat(db.countRowsOfTable("live_measures")).isEqualTo(4); | assertThat(db.countRowsOfTable("live_measures")).isEqualTo(4); | ||||
List<LiveMeasureDto> liveMeasureDtos = db.getDbClient().liveMeasureDao().selectByComponentUuidsAndMetricIds(dbSession, ImmutableSet.of(srcFile.uuid(), dir.uuid(), project.uuid(), nonSelectedFile.uuid()), ImmutableSet.of(metric1.getId(), metric2.getId())); | |||||
List<LiveMeasureDto> liveMeasureDtos = db.getDbClient().liveMeasureDao().selectByComponentUuidsAndMetricIds(dbSession, | |||||
ImmutableSet.of(srcFile.uuid(), dir.uuid(), project.uuid(), nonSelectedFile.uuid()), ImmutableSet.of(metric1.getId(), metric2.getId())); | |||||
assertThat(liveMeasureDtos) | assertThat(liveMeasureDtos) | ||||
.extracting(LiveMeasureDto::getComponentUuid) | .extracting(LiveMeasureDto::getComponentUuid) | ||||
.containsOnly(nonSelectedFile.uuid(), project.uuid()); | .containsOnly(nonSelectedFile.uuid(), project.uuid()); | ||||
db.assertDbUnit(getClass(), "shouldDeleteAnalyses-result.xml", "snapshots"); | db.assertDbUnit(getClass(), "shouldDeleteAnalyses-result.xml", "snapshots"); | ||||
} | } | ||||
@Test | @Test | ||||
public void deleteAnalyses_deletes_rows_in_events_and_event_component_changes() { | public void deleteAnalyses_deletes_rows_in_events_and_event_component_changes() { | ||||
ComponentDto project = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()); | ComponentDto project = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()); |
import org.sonar.db.DbTester; | import org.sonar.db.DbTester; | ||||
import org.sonar.db.component.ComponentDto; | import org.sonar.db.component.ComponentDto; | ||||
import org.sonar.db.organization.OrganizationDto; | import org.sonar.db.organization.OrganizationDto; | ||||
import org.sonar.db.source.FileSourceDto.Type; | |||||
import static com.google.common.collect.ImmutableList.of; | import static com.google.common.collect.ImmutableList.of; | ||||
import static java.util.Collections.emptyList; | import static java.util.Collections.emptyList; | ||||
public void select() { | public void select() { | ||||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | dbTester.prepareDbUnit(getClass(), "shared.xml"); | ||||
FileSourceDto fileSourceDto = underTest.selectSourceByFileUuid(dbSession, "FILE1_UUID"); | |||||
FileSourceDto fileSourceDto = underTest.selectByFileUuid(dbSession, "FILE1_UUID"); | |||||
assertThat(fileSourceDto.getBinaryData()).isNotEmpty(); | assertThat(fileSourceDto.getBinaryData()).isNotEmpty(); | ||||
assertThat(fileSourceDto.getDataHash()).isEqualTo("hash"); | assertThat(fileSourceDto.getDataHash()).isEqualTo("hash"); | ||||
assertThat(fileSourceDto.getFileUuid()).isEqualTo("FILE1_UUID"); | assertThat(fileSourceDto.getFileUuid()).isEqualTo("FILE1_UUID"); | ||||
assertThat(fileSourceDto.getCreatedAt()).isEqualTo(1500000000000L); | assertThat(fileSourceDto.getCreatedAt()).isEqualTo(1500000000000L); | ||||
assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(1500000000000L); | assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(1500000000000L); | ||||
assertThat(fileSourceDto.getDataType()).isEqualTo(Type.SOURCE); | |||||
assertThat(fileSourceDto.getRevision()).isEqualTo("123456789"); | assertThat(fileSourceDto.getRevision()).isEqualTo("123456789"); | ||||
assertThat(fileSourceDto.getLineHashesVersion()).isEqualTo(0); | assertThat(fileSourceDto.getLineHashesVersion()).isEqualTo(0); | ||||
.setDataHash("FILE2_DATA_HASH") | .setDataHash("FILE2_DATA_HASH") | ||||
.setLineHashes(of("LINE1_HASH", "LINE2_HASH")) | .setLineHashes(of("LINE1_HASH", "LINE2_HASH")) | ||||
.setSrcHash("FILE2_HASH") | .setSrcHash("FILE2_HASH") | ||||
.setDataType(Type.SOURCE) | |||||
.setCreatedAt(1500000000000L) | .setCreatedAt(1500000000000L) | ||||
.setUpdatedAt(1500000000001L) | .setUpdatedAt(1500000000001L) | ||||
.setLineHashesVersion(1) | .setLineHashesVersion(1) | ||||
underTest.insert(dbSession, expected); | underTest.insert(dbSession, expected); | ||||
dbSession.commit(); | dbSession.commit(); | ||||
FileSourceDto fileSourceDto = underTest.selectSourceByFileUuid(dbSession, expected.getFileUuid()); | |||||
FileSourceDto fileSourceDto = underTest.selectByFileUuid(dbSession, expected.getFileUuid()); | |||||
assertThat(fileSourceDto.getProjectUuid()).isEqualTo(expected.getProjectUuid()); | assertThat(fileSourceDto.getProjectUuid()).isEqualTo(expected.getProjectUuid()); | ||||
assertThat(fileSourceDto.getFileUuid()).isEqualTo(expected.getFileUuid()); | assertThat(fileSourceDto.getFileUuid()).isEqualTo(expected.getFileUuid()); | ||||
FileSourceDto fileSourceDto = new FileSourceDto() | FileSourceDto fileSourceDto = new FileSourceDto() | ||||
.setProjectUuid("Foo") | .setProjectUuid("Foo") | ||||
.setFileUuid("Bar") | .setFileUuid("Bar") | ||||
.setDataType(Type.SOURCE) | |||||
.setCreatedAt(1500000000000L) | .setCreatedAt(1500000000000L) | ||||
.setUpdatedAt(1500000000001L); | .setUpdatedAt(1500000000001L); | ||||
underTest.insert(dbSession, fileSourceDto); | underTest.insert(dbSession, fileSourceDto); | ||||
dbSession.commit(); | dbSession.commit(); | ||||
FileSourceDto res = underTest.selectSourceByFileUuid(dbSession, fileSourceDto.getFileUuid()); | |||||
assertThat(res.getLineCount()).isEqualTo(0); | |||||
assertThat(res.getLineHashes()).isEmpty(); | |||||
} | |||||
@Test | |||||
public void selectTest_reads_test_without_line_hashes() { | |||||
FileSourceDto fileSourceDto = new FileSourceDto() | |||||
.setProjectUuid("Foo") | |||||
.setFileUuid("Bar") | |||||
.setDataType(Type.TEST) | |||||
.setCreatedAt(1500000000000L) | |||||
.setUpdatedAt(1500000000001L); | |||||
underTest.insert(dbSession, fileSourceDto); | |||||
dbSession.commit(); | |||||
FileSourceDto res = underTest.selectTestByFileUuid(dbSession, fileSourceDto.getFileUuid()); | |||||
FileSourceDto res = underTest.selectByFileUuid(dbSession, fileSourceDto.getFileUuid()); | |||||
assertThat(res.getLineCount()).isEqualTo(0); | assertThat(res.getLineCount()).isEqualTo(0); | ||||
assertThat(res.getLineHashes()).isEmpty(); | assertThat(res.getLineHashes()).isEmpty(); | ||||
.setBinaryData("FILE2_BINARY_DATA".getBytes()) | .setBinaryData("FILE2_BINARY_DATA".getBytes()) | ||||
.setDataHash("FILE2_DATA_HASH") | .setDataHash("FILE2_DATA_HASH") | ||||
.setSrcHash("FILE2_HASH") | .setSrcHash("FILE2_HASH") | ||||
.setDataType(Type.SOURCE) | |||||
.setCreatedAt(1500000000000L) | .setCreatedAt(1500000000000L) | ||||
.setUpdatedAt(1500000000001L) | .setUpdatedAt(1500000000001L) | ||||
.setRevision("123456789")); | .setRevision("123456789")); | ||||
.setDataHash("FILE2_DATA_HASH") | .setDataHash("FILE2_DATA_HASH") | ||||
.setLineHashes(singletonList("hashes")) | .setLineHashes(singletonList("hashes")) | ||||
.setSrcHash("FILE2_HASH") | .setSrcHash("FILE2_HASH") | ||||
.setDataType(Type.SOURCE) | |||||
.setCreatedAt(1500000000000L) | .setCreatedAt(1500000000000L) | ||||
.setUpdatedAt(1500000000001L) | .setUpdatedAt(1500000000001L) | ||||
.setRevision("123456789")); | .setRevision("123456789")); | ||||
.setDataHash("FILE2_DATA_HASH") | .setDataHash("FILE2_DATA_HASH") | ||||
.setLineHashes(singletonList("hashes")) | .setLineHashes(singletonList("hashes")) | ||||
.setSrcHash("FILE2_HASH") | .setSrcHash("FILE2_HASH") | ||||
.setDataType(Type.SOURCE) | |||||
.setCreatedAt(1500000000000L) | .setCreatedAt(1500000000000L) | ||||
.setUpdatedAt(1500000000001L) | .setUpdatedAt(1500000000001L) | ||||
.setLineHashesVersion(1) | .setLineHashesVersion(1) | ||||
.setBinaryData("FILE2_BINARY_DATA".getBytes()) | .setBinaryData("FILE2_BINARY_DATA".getBytes()) | ||||
.setDataHash("FILE2_DATA_HASH") | .setDataHash("FILE2_DATA_HASH") | ||||
.setSrcHash("FILE2_HASH") | .setSrcHash("FILE2_HASH") | ||||
.setDataType(Type.SOURCE) | |||||
.setCreatedAt(1500000000000L) | .setCreatedAt(1500000000000L) | ||||
.setUpdatedAt(1500000000001L) | .setUpdatedAt(1500000000001L) | ||||
.setRevision("123456789")); | .setRevision("123456789")); | ||||
verifyLinesHashes(handler, file1, fileSource1); | verifyLinesHashes(handler, file1, fileSource1); | ||||
} | } | ||||
@Test | |||||
public void scrollLineHashes_does_not_scroll_hashes_of_component_with_TEST_source() { | |||||
OrganizationDto organization = dbTester.organizations().insert(); | |||||
ComponentDto project = new Random().nextBoolean() ? dbTester.components().insertPrivateProject(organization) : dbTester.components().insertPublicProject(organization); | |||||
ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project)); | |||||
FileSourceDto fileSource1 = dbTester.fileSources().insertFileSource(file1); | |||||
ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project)); | |||||
FileSourceDto fileSource2 = dbTester.fileSources().insertFileSource(file2, t -> t.setDataType(Type.TEST)); | |||||
ComponentDto file3 = dbTester.components().insertComponent(newFileDto(project)); | |||||
FileSourceDto fileSource3 = dbTester.fileSources().insertFileSource(file3, t -> t.setDataType(Type.SOURCE)); | |||||
FileSourceDto testFileSource3 = dbTester.fileSources().insertFileSource(file3, t -> t.setDataType(Type.TEST)); | |||||
LineHashesWithKeyDtoHandler handler = scrollLineHashes(file2.uuid(), file1.uuid(), file3.uuid()); | |||||
assertThat(handler.dtos).hasSize(2); | |||||
verifyLinesHashes(handler, file1, fileSource1); | |||||
verifyLinesHashes(handler, file3, fileSource3); | |||||
} | |||||
@Test | @Test | ||||
public void scrollLineHashes_handles_scrolling_more_than_1000_files() { | public void scrollLineHashes_handles_scrolling_more_than_1000_files() { | ||||
OrganizationDto organization = dbTester.organizations().insert(); | OrganizationDto organization = dbTester.organizations().insert(); | ||||
.setDataHash("NEW_DATA_HASH") | .setDataHash("NEW_DATA_HASH") | ||||
.setSrcHash("NEW_FILE_HASH") | .setSrcHash("NEW_FILE_HASH") | ||||
.setLineHashes(singletonList("NEW_LINE_HASHES")) | .setLineHashes(singletonList("NEW_LINE_HASHES")) | ||||
.setDataType(Type.SOURCE) | |||||
.setUpdatedAt(1500000000002L) | .setUpdatedAt(1500000000002L) | ||||
.setLineHashesVersion(1) | .setLineHashesVersion(1) | ||||
.setRevision("987654321")); | .setRevision("987654321")); | ||||
dbSession.commit(); | dbSession.commit(); | ||||
dbTester.assertDbUnitTable(getClass(), "update-result.xml", "file_sources", "project_uuid", "file_uuid", | dbTester.assertDbUnitTable(getClass(), "update-result.xml", "file_sources", "project_uuid", "file_uuid", | ||||
"data_hash", "line_hashes", "src_hash", "created_at", "updated_at", "data_type", "revision", "line_hashes_version"); | |||||
"data_hash", "line_hashes", "src_hash", "created_at", "updated_at", "revision", "line_hashes_version"); | |||||
} | } | ||||
@Test | @Test | ||||
FileSourceDto fileSourceDto = new FileSourceDto() | FileSourceDto fileSourceDto = new FileSourceDto() | ||||
.setProjectUuid("Foo") | .setProjectUuid("Foo") | ||||
.setFileUuid("Bar") | .setFileUuid("Bar") | ||||
.setDataType(Type.SOURCE) | |||||
.setLineHashes(lineHashes) | .setLineHashes(lineHashes) | ||||
.setCreatedAt(1500000000000L) | .setCreatedAt(1500000000000L) | ||||
.setUpdatedAt(1500000000001L); | .setUpdatedAt(1500000000001L); | ||||
underTest.insert(dbSession, fileSourceDto); | underTest.insert(dbSession, fileSourceDto); | ||||
dbSession.commit(); | dbSession.commit(); | ||||
FileSourceDto resBefore = underTest.selectSourceByFileUuid(dbSession, fileSourceDto.getFileUuid()); | |||||
FileSourceDto resBefore = underTest.selectByFileUuid(dbSession, fileSourceDto.getFileUuid()); | |||||
assertThat(resBefore.getLineCount()).isEqualTo(lineHashes.size()); | assertThat(resBefore.getLineCount()).isEqualTo(lineHashes.size()); | ||||
assertThat(resBefore.getLineHashes()).isEqualTo(lineHashes); | assertThat(resBefore.getLineHashes()).isEqualTo(lineHashes); | ||||
underTest.update(dbSession, fileSourceDto); | underTest.update(dbSession, fileSourceDto); | ||||
dbSession.commit(); | dbSession.commit(); | ||||
FileSourceDto res = underTest.selectSourceByFileUuid(dbSession, fileSourceDto.getFileUuid()); | |||||
FileSourceDto res = underTest.selectByFileUuid(dbSession, fileSourceDto.getFileUuid()); | |||||
assertThat(res.getLineHashes()).isEmpty(); | assertThat(res.getLineHashes()).isEmpty(); | ||||
assertThat(res.getLineCount()).isEqualTo(1); | assertThat(res.getLineCount()).isEqualTo(1); | ||||
} | } |
package org.sonar.db.source; | package org.sonar.db.source; | ||||
import com.google.common.base.Joiner; | import com.google.common.base.Joiner; | ||||
import java.util.Arrays; | |||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Random; | import java.util.Random; | ||||
@Rule | @Rule | ||||
public ExpectedException expectedException = ExpectedException.none(); | public ExpectedException expectedException = ExpectedException.none(); | ||||
@Test | |||||
public void encode_and_decode_test_data() { | |||||
List<DbFileSources.Test> tests = Arrays.asList( | |||||
DbFileSources.Test.newBuilder() | |||||
.setName("name#1") | |||||
.build(), | |||||
DbFileSources.Test.newBuilder() | |||||
.setName("name#2") | |||||
.build()); | |||||
FileSourceDto underTest = new FileSourceDto().setTestData(tests); | |||||
assertThat(underTest.getTestData()).hasSize(2); | |||||
assertThat(underTest.getTestData().get(0).getName()).isEqualTo("name#1"); | |||||
} | |||||
@Test | @Test | ||||
public void getSourceData_throws_ISE_with_id_fileUuid_and_projectUuid_in_message_when_data_cant_be_read() { | public void getSourceData_throws_ISE_with_id_fileUuid_and_projectUuid_in_message_when_data_cant_be_read() { | ||||
long id = 12L; | long id = 12L; | ||||
} | } | ||||
@Test | @Test | ||||
public void new_FileSourceDto_as_lineCount_0_and_rawLineHashes_to_null() { | |||||
public void new_FileSourceDto_as_lineCount_0_and_rawLineHashes_to_null() { | |||||
FileSourceDto underTest = new FileSourceDto(); | FileSourceDto underTest = new FileSourceDto(); | ||||
assertThat(underTest.getLineCount()).isZero(); | assertThat(underTest.getLineCount()).isZero(); |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.source.index; | |||||
import com.google.common.base.Joiner; | |||||
import java.sql.PreparedStatement; | |||||
import java.sql.SQLException; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import javax.annotation.Nullable; | |||||
import org.elasticsearch.action.update.UpdateRequest; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
public class FileSourcesUpdaterHelper { | |||||
private static final String SQL_ALL = "SELECT %s FROM file_sources WHERE data_type='%s' "; | |||||
private static final String PROJECT_FILTER = " AND project_uuid=?"; | |||||
private static final String[] FIELDS = { | |||||
"project_uuid", | |||||
"file_uuid", | |||||
"updated_at", | |||||
"binary_data" | |||||
}; | |||||
private static final String FIELDS_ONE_LINE = Joiner.on(",").join(FIELDS); | |||||
private FileSourcesUpdaterHelper() { | |||||
// only static stuff | |||||
} | |||||
public static PreparedStatement preparedStatementToSelectFileSources(DbClient dbClient, DbSession session, String dataType, @Nullable String projectUuid) | |||||
throws SQLException { | |||||
String sql = createSQL(dataType, projectUuid); | |||||
// rows are big, so they are scrolled once at a time (one row in memory at a time) | |||||
PreparedStatement stmt = dbClient.getMyBatis().newScrollingSingleRowSelectStatement(session, sql); | |||||
if (projectUuid != null) { | |||||
stmt.setString(1, projectUuid); | |||||
} | |||||
return stmt; | |||||
} | |||||
private static String createSQL(String dataType, @Nullable String projectUuid) { | |||||
StringBuilder sql = new StringBuilder(String.format(SQL_ALL, FIELDS_ONE_LINE, dataType)); | |||||
if (projectUuid != null) { | |||||
sql.append(PROJECT_FILTER); | |||||
} | |||||
return sql.toString(); | |||||
} | |||||
public static class Row { | |||||
private final String fileUuid; | |||||
private final String projectUuid; | |||||
private final long updatedAt; | |||||
private final List<UpdateRequest> updateRequests = new ArrayList<>(); | |||||
public Row(String projectUuid, String fileUuid, long updatedAt) { | |||||
this.projectUuid = projectUuid; | |||||
this.fileUuid = fileUuid; | |||||
this.updatedAt = updatedAt; | |||||
} | |||||
public String getProjectUuid() { | |||||
return projectUuid; | |||||
} | |||||
public String getFileUuid() { | |||||
return fileUuid; | |||||
} | |||||
public long getUpdatedAt() { | |||||
return updatedAt; | |||||
} | |||||
public List<UpdateRequest> getUpdateRequests() { | |||||
return updateRequests; | |||||
} | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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 java.util.List; | |||||
import java.util.Map; | |||||
import org.sonar.server.es.BaseDoc; | |||||
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.newHashMapWithExpectedSize(2)); | |||||
} | |||||
@Override | |||||
public String getId() { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
@Override | |||||
public String getRouting() { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
@Override | |||||
public String getParent() { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
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; | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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 java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import javax.annotation.CheckForNull; | |||||
import org.sonar.server.es.BaseDoc; | |||||
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) { | |||||
super(fields); | |||||
} | |||||
@VisibleForTesting | |||||
public TestDoc() { | |||||
super(Maps.newHashMapWithExpectedSize(10)); | |||||
} | |||||
@Override | |||||
public String getId() { | |||||
return testUuid(); | |||||
} | |||||
@Override | |||||
public String getRouting() { | |||||
return projectUuid(); | |||||
} | |||||
@Override | |||||
public String getParent() { | |||||
return null; | |||||
} | |||||
public String projectUuid() { | |||||
return getField(FIELD_PROJECT_UUID); | |||||
} | |||||
public TestDoc setProjectUuid(String projectUuid) { | |||||
setField(FIELD_PROJECT_UUID, projectUuid); | |||||
return this; | |||||
} | |||||
public String fileUuid() { | |||||
return getField(FIELD_FILE_UUID); | |||||
} | |||||
public TestDoc setFileUuid(String fileUuid) { | |||||
setField(FIELD_FILE_UUID, fileUuid); | |||||
return this; | |||||
} | |||||
public String testUuid() { | |||||
return getField(FIELD_TEST_UUID); | |||||
} | |||||
public TestDoc setUuid(String testUuid) { | |||||
setField(FIELD_TEST_UUID, testUuid); | |||||
return this; | |||||
} | |||||
public String name() { | |||||
return getField(FIELD_NAME); | |||||
} | |||||
public TestDoc setName(String name) { | |||||
setField(FIELD_NAME, name); | |||||
return this; | |||||
} | |||||
public String status() { | |||||
return getField(FIELD_STATUS); | |||||
} | |||||
public TestDoc setStatus(String status) { | |||||
setField(FIELD_STATUS, status); | |||||
return this; | |||||
} | |||||
@CheckForNull | |||||
public String message() { | |||||
return getNullableField(FIELD_MESSAGE); | |||||
} | |||||
public TestDoc setMessage(String message) { | |||||
setField(FIELD_MESSAGE, message); | |||||
return this; | |||||
} | |||||
@CheckForNull | |||||
public String stackTrace() { | |||||
return getNullableField(FIELD_STACKTRACE); | |||||
} | |||||
public TestDoc setStackTrace(String stackTrace) { | |||||
setField(FIELD_STACKTRACE, stackTrace); | |||||
return this; | |||||
} | |||||
@CheckForNull | |||||
public Long durationInMs() { | |||||
Number number = getNullableField(FIELD_DURATION_IN_MS); | |||||
return number == null ? null : number.longValue(); | |||||
} | |||||
public TestDoc setDurationInMs(Long durationInMs) { | |||||
setField(FIELD_DURATION_IN_MS, durationInMs); | |||||
return this; | |||||
} | |||||
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; | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.base.Optional; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.apache.lucene.search.join.ScoreMode; | |||||
import org.elasticsearch.action.search.SearchRequestBuilder; | |||||
import org.elasticsearch.search.SearchHit; | |||||
import org.sonar.api.utils.System2; | |||||
import org.sonar.server.es.EsClient; | |||||
import org.sonar.server.es.SearchOptions; | |||||
import org.sonar.server.es.SearchResult; | |||||
import static org.elasticsearch.index.query.QueryBuilders.boolQuery; | |||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; | |||||
import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; | |||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery; | |||||
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 { | |||||
private final EsClient client; | |||||
private final System2 system2; | |||||
public TestIndex(EsClient client, System2 system2) { | |||||
this.client = client; | |||||
this.system2 = system2; | |||||
} | |||||
public List<CoveredFileDoc> coveredFiles(String testUuid) { | |||||
List<CoveredFileDoc> coveredFiles = new ArrayList<>(); | |||||
for (SearchHit hit : client.prepareSearch(TestIndexDefinition.INDEX_TYPE_TEST) | |||||
.setSize(1) | |||||
.setQuery(boolQuery().must(matchAllQuery()).filter(termQuery(FIELD_TEST_UUID, testUuid))) | |||||
.get().getHits().getHits()) { | |||||
coveredFiles.addAll(new TestDoc(hit.getSourceAsMap()).coveredFiles()); | |||||
} | |||||
return coveredFiles; | |||||
} | |||||
public SearchResult<TestDoc> searchByTestFileUuid(String testFileUuid, SearchOptions searchOptions) { | |||||
SearchRequestBuilder searchRequest = client.prepareSearch(TestIndexDefinition.INDEX_TYPE_TEST) | |||||
.setSize(searchOptions.getLimit()) | |||||
.setFrom(searchOptions.getOffset()) | |||||
.setQuery(boolQuery().must(matchAllQuery()).filter(termQuery(FIELD_FILE_UUID, testFileUuid))); | |||||
return new SearchResult<>(searchRequest.get(), TestDoc::new, system2.getDefaultTimeZone()); | |||||
} | |||||
public SearchResult<TestDoc> searchBySourceFileUuidAndLineNumber(String sourceFileUuid, int lineNumber, SearchOptions searchOptions) { | |||||
SearchRequestBuilder searchRequest = client.prepareSearch(TestIndexDefinition.INDEX_TYPE_TEST) | |||||
.setSize(searchOptions.getLimit()) | |||||
.setFrom(searchOptions.getOffset()) | |||||
.setQuery(nestedQuery( | |||||
FIELD_COVERED_FILES, | |||||
boolQuery() | |||||
.must(termQuery(FIELD_COVERED_FILES + "." + FIELD_COVERED_FILE_UUID, sourceFileUuid)) | |||||
.must(termQuery(FIELD_COVERED_FILES + "." + FIELD_COVERED_FILE_LINES, lineNumber)), | |||||
ScoreMode.Avg)); | |||||
return new SearchResult<>(searchRequest.get(), TestDoc::new, system2.getDefaultTimeZone()); | |||||
} | |||||
public TestDoc getByTestUuid(String testUuid) { | |||||
Optional<TestDoc> testDoc = getNullableByTestUuid(testUuid); | |||||
if (testDoc.isPresent()) { | |||||
return testDoc.get(); | |||||
} | |||||
throw new IllegalStateException(String.format("Test id '%s' not found", testUuid)); | |||||
} | |||||
public Optional<TestDoc> getNullableByTestUuid(String testUuid) { | |||||
SearchHit[] hits = client.prepareSearch(TestIndexDefinition.INDEX_TYPE_TEST) | |||||
.setSize(1) | |||||
.setQuery(boolQuery().must(matchAllQuery()).filter(termQuery(FIELD_TEST_UUID, testUuid))) | |||||
.get().getHits().getHits(); | |||||
if (hits.length > 0) { | |||||
return Optional.of(new TestDoc(hits[0].getSourceAsMap())); | |||||
} | |||||
return Optional.absent(); | |||||
} | |||||
public SearchResult<TestDoc> searchByTestUuid(String testUuid, SearchOptions searchOptions) { | |||||
SearchRequestBuilder searchRequest = client.prepareSearch(TestIndexDefinition.INDEX_TYPE_TEST) | |||||
.setSize(searchOptions.getLimit()) | |||||
.setFrom(searchOptions.getOffset()) | |||||
.setQuery(boolQuery().must(matchAllQuery()).filter(termQuery(FIELD_TEST_UUID, testUuid))); | |||||
return new SearchResult<>(searchRequest.get(), TestDoc::new, system2.getDefaultTimeZone()); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.collect.ImmutableSet; | |||||
import java.util.Collection; | |||||
import java.util.List; | |||||
import java.util.Set; | |||||
import org.elasticsearch.action.search.SearchRequestBuilder; | |||||
import org.elasticsearch.index.query.QueryBuilders; | |||||
import org.sonar.core.util.stream.MoreCollectors; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.es.EsQueueDto; | |||||
import org.sonar.server.es.BulkIndexer; | |||||
import org.sonar.server.es.BulkIndexer.Size; | |||||
import org.sonar.server.es.EsClient; | |||||
import org.sonar.server.es.IndexType; | |||||
import org.sonar.server.es.IndexingListener; | |||||
import org.sonar.server.es.IndexingResult; | |||||
import org.sonar.server.es.OneToManyResilientIndexingListener; | |||||
import org.sonar.server.es.ProjectIndexer; | |||||
import org.sonar.server.source.index.FileSourcesUpdaterHelper; | |||||
import static java.util.Collections.emptyList; | |||||
import static org.sonar.server.test.index.TestIndexDefinition.FIELD_FILE_UUID; | |||||
import static org.sonar.server.test.index.TestIndexDefinition.INDEX_TYPE_TEST; | |||||
/** | |||||
* Add to Elasticsearch index {@link TestIndexDefinition} the rows of | |||||
* db table FILE_SOURCES of type TEST that are not indexed yet | |||||
* <p> | |||||
* This indexer is not resilient by itself since it's called by Compute Engine | |||||
*/ | |||||
public class TestIndexer implements ProjectIndexer { | |||||
private final DbClient dbClient; | |||||
private final EsClient esClient; | |||||
public TestIndexer(DbClient dbClient, EsClient esClient) { | |||||
this.dbClient = dbClient; | |||||
this.esClient = esClient; | |||||
} | |||||
@Override | |||||
public Set<IndexType> getIndexTypes() { | |||||
return ImmutableSet.of(INDEX_TYPE_TEST); | |||||
} | |||||
@Override | |||||
public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) { | |||||
try (DbSession dbSession = dbClient.openSession(false); | |||||
TestResultSetIterator rowIt = TestResultSetIterator.create(dbClient, dbSession, null)) { | |||||
BulkIndexer bulkIndexer = new BulkIndexer(esClient, INDEX_TYPE_TEST, Size.LARGE); | |||||
bulkIndexer.start(); | |||||
addTestsToBulkIndexer(rowIt, bulkIndexer); | |||||
bulkIndexer.stop(); | |||||
} | |||||
} | |||||
@Override | |||||
public void indexOnAnalysis(String branchUuid) { | |||||
BulkIndexer bulkIndexer = new BulkIndexer(esClient, INDEX_TYPE_TEST, Size.REGULAR); | |||||
bulkIndexer.start(); | |||||
addProjectDeletionToBulkIndexer(bulkIndexer, branchUuid); | |||||
try (DbSession dbSession = dbClient.openSession(false); | |||||
TestResultSetIterator rowIt = TestResultSetIterator.create(dbClient, dbSession, branchUuid)) { | |||||
addTestsToBulkIndexer(rowIt, bulkIndexer); | |||||
} | |||||
bulkIndexer.stop(); | |||||
} | |||||
@Override | |||||
public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, Cause cause) { | |||||
switch (cause) { | |||||
case PROJECT_CREATION: | |||||
// no tests at that time | |||||
case MEASURE_CHANGE: | |||||
case PROJECT_KEY_UPDATE: | |||||
case PROJECT_TAGS_UPDATE: | |||||
case PERMISSION_CHANGE: | |||||
// Measures, project key, tags and permissions are not part of tests/test | |||||
return emptyList(); | |||||
case PROJECT_DELETION: | |||||
List<EsQueueDto> items = projectUuids.stream() | |||||
.map(projectUuid -> EsQueueDto.create(INDEX_TYPE_TEST.format(), projectUuid, null, projectUuid)) | |||||
.collect(MoreCollectors.toArrayList(projectUuids.size())); | |||||
return dbClient.esQueueDao().insert(dbSession, items); | |||||
default: | |||||
// defensive case | |||||
throw new IllegalStateException("Unsupported cause: " + cause); | |||||
} | |||||
} | |||||
public void deleteByFile(String fileUuid) { | |||||
SearchRequestBuilder searchRequest = esClient.prepareSearch(INDEX_TYPE_TEST) | |||||
.setQuery(QueryBuilders.termQuery(FIELD_FILE_UUID, fileUuid)); | |||||
BulkIndexer.delete(esClient, INDEX_TYPE_TEST, searchRequest); | |||||
} | |||||
@Override | |||||
public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) { | |||||
// The items are to be deleted | |||||
if (items.isEmpty()) { | |||||
return new IndexingResult(); | |||||
} | |||||
IndexingListener listener = new OneToManyResilientIndexingListener(dbClient, dbSession, items); | |||||
BulkIndexer bulkIndexer = new BulkIndexer(esClient, INDEX_TYPE_TEST, Size.REGULAR, listener); | |||||
bulkIndexer.start(); | |||||
items.forEach(i -> { | |||||
String projectUuid = i.getDocId(); | |||||
addProjectDeletionToBulkIndexer(bulkIndexer, projectUuid); | |||||
}); | |||||
return bulkIndexer.stop(); | |||||
} | |||||
private void addProjectDeletionToBulkIndexer(BulkIndexer bulkIndexer, String projectUuid) { | |||||
SearchRequestBuilder searchRequest = esClient.prepareSearch(INDEX_TYPE_TEST) | |||||
.setQuery(QueryBuilders.termQuery(TestIndexDefinition.FIELD_PROJECT_UUID, projectUuid)); | |||||
bulkIndexer.addDeletion(searchRequest); | |||||
} | |||||
private static void addTestsToBulkIndexer(TestResultSetIterator rowIt, BulkIndexer bulkIndexer) { | |||||
while (rowIt.hasNext()) { | |||||
FileSourcesUpdaterHelper.Row row = rowIt.next(); | |||||
row.getUpdateRequests().forEach(bulkIndexer::add); | |||||
} | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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 java.io.ByteArrayOutputStream; | |||||
import java.io.InputStream; | |||||
import java.io.OutputStreamWriter; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.sql.PreparedStatement; | |||||
import java.sql.ResultSet; | |||||
import java.sql.SQLException; | |||||
import java.util.Collections; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
import javax.annotation.Nullable; | |||||
import org.elasticsearch.action.update.UpdateRequest; | |||||
import org.elasticsearch.common.xcontent.XContentType; | |||||
import org.sonar.api.utils.log.Loggers; | |||||
import org.sonar.api.utils.text.JsonWriter; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.ResultSetIterator; | |||||
import org.sonar.db.protobuf.DbFileSources; | |||||
import org.sonar.db.source.FileSourceDto; | |||||
import org.sonar.server.es.EsUtils; | |||||
import org.sonar.server.source.index.FileSourcesUpdaterHelper.Row; | |||||
import static org.sonar.server.source.index.FileSourcesUpdaterHelper.preparedStatementToSelectFileSources; | |||||
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.FIELD_UPDATED_AT; | |||||
import static org.sonar.server.test.index.TestIndexDefinition.INDEX_TYPE_TEST; | |||||
/** | |||||
* Scroll over table FILE_SOURCES of test type and directly parse data required to | |||||
* populate the index tests/test | |||||
*/ | |||||
public class TestResultSetIterator extends ResultSetIterator<Row> { | |||||
private TestResultSetIterator(PreparedStatement stmt) throws SQLException { | |||||
super(stmt); | |||||
} | |||||
public static TestResultSetIterator create(DbClient dbClient, DbSession session, @Nullable String projectUuid) { | |||||
try { | |||||
return new TestResultSetIterator(preparedStatementToSelectFileSources(dbClient, session, FileSourceDto.Type.TEST, projectUuid)); | |||||
} catch (SQLException e) { | |||||
throw new IllegalStateException("Fail to prepare SQL request to select all tests", e); | |||||
} | |||||
} | |||||
@Override | |||||
protected Row read(ResultSet rs) throws SQLException { | |||||
String projectUuid = rs.getString(1); | |||||
String fileUuid = rs.getString(2); | |||||
Date updatedAt = new Date(rs.getLong(3)); | |||||
List<DbFileSources.Test> tests = parseData(fileUuid, rs.getBinaryStream(4)); | |||||
return toRow(projectUuid, fileUuid, updatedAt, tests); | |||||
} | |||||
private static List<DbFileSources.Test> parseData(String fileUuid, @Nullable InputStream dataInput) { | |||||
List<DbFileSources.Test> tests = Collections.emptyList(); | |||||
if (dataInput != null) { | |||||
try { | |||||
tests = FileSourceDto.decodeTestData(dataInput); | |||||
} catch (Exception e) { | |||||
Loggers.get(TestResultSetIterator.class).warn(String.format("Invalid file_sources.binary_data on row with file_uuid='%s', test file will be ignored", fileUuid), e); | |||||
} | |||||
} | |||||
return tests; | |||||
} | |||||
/** | |||||
* Convert protobuf message to tests required for Elasticsearch indexing | |||||
*/ | |||||
public static Row toRow(String projectUuid, String fileUuid, Date updatedAt, List<DbFileSources.Test> tests) { | |||||
Row result = new Row(projectUuid, fileUuid, updatedAt.getTime()); | |||||
for (DbFileSources.Test test : tests) { | |||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(); | |||||
// all the fields must be present, even if value is null | |||||
try (JsonWriter writer = JsonWriter.of(new OutputStreamWriter(bytes, StandardCharsets.UTF_8)).setSerializeNulls(true)) { | |||||
writer.beginObject(); | |||||
writer.prop(FIELD_PROJECT_UUID, projectUuid); | |||||
writer.prop(FIELD_FILE_UUID, fileUuid); | |||||
writer.prop(FIELD_TEST_UUID, test.getUuid()); | |||||
writer.prop(FIELD_NAME, test.getName()); | |||||
writer.prop(FIELD_STATUS, test.hasStatus() ? test.getStatus().toString() : null); | |||||
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.prop(FIELD_UPDATED_AT, EsUtils.formatDateTime(updatedAt)); | |||||
writer.name(FIELD_COVERED_FILES); | |||||
writer.beginArray(); | |||||
for (DbFileSources.Test.CoveredFile coveredFile : test.getCoveredFileList()) { | |||||
writer.beginObject(); | |||||
writer.prop(FIELD_COVERED_FILE_UUID, coveredFile.getFileUuid()); | |||||
writer.name(FIELD_COVERED_FILE_LINES).valueObject(coveredFile.getCoveredLineList()); | |||||
writer.endObject(); | |||||
} | |||||
writer.endArray(); | |||||
writer.endObject(); | |||||
} | |||||
// This is an optimization to reduce memory consumption and multiple conversions from Map to JSON. | |||||
// UpdateRequest#doc() and #upsert() take the same parameter values, so: | |||||
// - passing the same Map would execute two JSON serializations | |||||
// - Map is a useless temporarily structure: read JDBC result set -> convert to map -> convert to JSON. Generating | |||||
// directly JSON from result set is more efficient. | |||||
byte[] jsonDoc = bytes.toByteArray(); | |||||
UpdateRequest updateRequest = new UpdateRequest(INDEX_TYPE_TEST.getIndex(), INDEX_TYPE_TEST.getType(), test.getUuid()) | |||||
.routing(projectUuid) | |||||
.doc(jsonDoc, XContentType.JSON) | |||||
.upsert(jsonDoc, XContentType.JSON); | |||||
result.getUpdateRequests().add(updateRequest); | |||||
} | |||||
return result; | |||||
} | |||||
} |
*/ | */ | ||||
package org.sonar.server.source.index; | package org.sonar.server.source.index; | ||||
import java.io.IOException; | |||||
import java.sql.Connection; | |||||
import java.sql.PreparedStatement; | |||||
import java.sql.SQLException; | |||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import org.apache.commons.lang.RandomStringUtils; | |||||
import org.apache.commons.lang.math.RandomUtils; | |||||
import org.sonar.db.protobuf.DbFileSources; | import org.sonar.db.protobuf.DbFileSources; | ||||
import org.sonar.db.source.FileSourceDto; | |||||
public class FileSourceTesting { | public class FileSourceTesting { | ||||
// only static stuff | // only static stuff | ||||
} | } | ||||
public static void updateDataColumn(Connection connection, String fileUuid, DbFileSources.Data data) throws SQLException { | |||||
updateDataColumn(connection, fileUuid, FileSourceDto.encodeSourceData(data)); | |||||
} | |||||
public static void updateDataColumn(Connection connection, String fileUuid, byte[] data) throws SQLException { | |||||
PreparedStatement stmt = connection.prepareStatement("UPDATE file_sources SET binary_data = ? WHERE file_uuid=? AND data_type='" + FileSourceDto.Type.SOURCE + "'"); | |||||
stmt.setBytes(1, data); | |||||
stmt.setString(2, fileUuid); | |||||
stmt.executeUpdate(); | |||||
stmt.close(); | |||||
} | |||||
/** | /** | ||||
* Generate predefined fake data. Result is mutable. | * Generate predefined fake data. Result is mutable. | ||||
*/ | */ | ||||
return dataBuilder; | return dataBuilder; | ||||
} | } | ||||
/** | |||||
* Generate random data. Result is mutable. | |||||
*/ | |||||
public static DbFileSources.Data.Builder newRandomData(int numberOfLines) { | |||||
DbFileSources.Data.Builder dataBuilder = DbFileSources.Data.newBuilder(); | |||||
for (int i = 1; i <= numberOfLines; i++) { | |||||
dataBuilder.addLinesBuilder() | |||||
.setLine(i) | |||||
.setScmRevision(RandomStringUtils.randomAlphanumeric(15)) | |||||
.setScmAuthor(RandomStringUtils.randomAlphanumeric(10)) | |||||
.setScmDate(RandomUtils.nextLong()) | |||||
.setSource(RandomStringUtils.randomAlphanumeric(20)) | |||||
.setLineHits(RandomUtils.nextInt(4)) | |||||
.setConditions(RandomUtils.nextInt(4)) | |||||
.setCoveredConditions(RandomUtils.nextInt(4)) | |||||
.setHighlighting(RandomStringUtils.randomAlphanumeric(40)) | |||||
.setSymbols(RandomStringUtils.randomAlphanumeric(30)) | |||||
.addAllDuplication(Arrays.asList(RandomUtils.nextInt(200), RandomUtils.nextInt(200))) | |||||
.build(); | |||||
} | |||||
return dataBuilder; | |||||
} | |||||
} | } |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.db; | |||||
import java.io.IOException; | |||||
import java.sql.Connection; | |||||
import java.sql.PreparedStatement; | |||||
import java.sql.SQLException; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.apache.commons.lang.RandomStringUtils; | |||||
import org.apache.commons.lang.math.RandomUtils; | |||||
import org.sonar.core.util.Uuids; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.component.ComponentDto; | |||||
import org.sonar.db.protobuf.DbFileSources; | |||||
import org.sonar.db.source.FileSourceDto; | |||||
import static java.util.Arrays.asList; | |||||
public class TestTesting { | |||||
private TestTesting() { | |||||
// only static stuff | |||||
} | |||||
public static void updateDataColumn(DbSession session, String fileUuid, List<DbFileSources.Test> tests) throws SQLException { | |||||
updateDataColumn(session, fileUuid, FileSourceDto.encodeTestData(tests)); | |||||
} | |||||
public static void updateDataColumn(DbSession session, String fileUuid, byte[] data) throws SQLException { | |||||
Connection connection = session.getConnection(); | |||||
PreparedStatement stmt = connection.prepareStatement("UPDATE file_sources SET binary_data = ? WHERE file_uuid=? AND data_type='TEST'"); | |||||
stmt.setBytes(1, data); | |||||
stmt.setString(2, fileUuid); | |||||
stmt.executeUpdate(); | |||||
stmt.close(); | |||||
connection.commit(); | |||||
} | |||||
/** | |||||
* Generate random data. | |||||
*/ | |||||
public static List<DbFileSources.Test> newRandomTests(int numberOfTests) { | |||||
List<DbFileSources.Test> tests = new ArrayList<>(); | |||||
for (int i = 1; i <= numberOfTests; i++) { | |||||
DbFileSources.Test.Builder test = DbFileSources.Test.newBuilder() | |||||
.setUuid(Uuids.create()) | |||||
.setName(RandomStringUtils.randomAlphanumeric(20)) | |||||
.setStatus(DbFileSources.Test.TestStatus.FAILURE) | |||||
.setStacktrace(RandomStringUtils.randomAlphanumeric(50)) | |||||
.setMsg(RandomStringUtils.randomAlphanumeric(30)) | |||||
.setExecutionTimeMs(RandomUtils.nextLong()); | |||||
for (int j = 0; j < numberOfTests; j++) { | |||||
test.addCoveredFile( | |||||
DbFileSources.Test.CoveredFile.newBuilder() | |||||
.setFileUuid(Uuids.create()) | |||||
.addCoveredLine(RandomUtils.nextInt(500))); | |||||
} | |||||
tests.add(test.build()); | |||||
} | |||||
return tests; | |||||
} | |||||
public static DbFileSources.Test.Builder newTest(ComponentDto mainFile, Integer... coveredLines) { | |||||
DbFileSources.Test.Builder test = DbFileSources.Test.newBuilder() | |||||
.setUuid(Uuids.create()) | |||||
.setName(RandomStringUtils.randomAlphanumeric(20)) | |||||
.setStatus(DbFileSources.Test.TestStatus.FAILURE) | |||||
.setStacktrace(RandomStringUtils.randomAlphanumeric(50)) | |||||
.setMsg(RandomStringUtils.randomAlphanumeric(30)) | |||||
.setExecutionTimeMs(RandomUtils.nextLong()); | |||||
test.addCoveredFile( | |||||
DbFileSources.Test.CoveredFile.newBuilder() | |||||
.setFileUuid(mainFile.uuid()) | |||||
.addAllCoveredLine(asList(coveredLines))); | |||||
return test; | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.base.Optional; | |||||
import java.util.List; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | |||||
import org.sonar.api.utils.System2; | |||||
import org.sonar.server.es.EsTester; | |||||
import org.sonar.server.es.SearchOptions; | |||||
import static java.util.Arrays.asList; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.assertj.guava.api.Assertions.assertThat; | |||||
import static org.sonar.server.test.index.TestIndexDefinition.INDEX_TYPE_TEST; | |||||
public class TestIndexTest { | |||||
@Rule | |||||
public EsTester es = EsTester.create(); | |||||
private TestIndex underTest = new TestIndex(es.client(), System2.INSTANCE); | |||||
@Test | |||||
public void coveredFiles() { | |||||
es.putDocuments(INDEX_TYPE_TEST, | |||||
newTestDoc("1", "TESTFILE1", newCoveredFileDoc("3"), newCoveredFileDoc("4"), newCoveredFileDoc("5")), | |||||
newTestDoc("2", "TESTFILE1", newCoveredFileDoc("5"), newCoveredFileDoc("6"), newCoveredFileDoc("7"))); | |||||
List<CoveredFileDoc> result = underTest.coveredFiles("1"); | |||||
assertThat(result).hasSize(3); | |||||
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 searchByTestFileUuid() { | |||||
es.putDocuments(INDEX_TYPE_TEST, | |||||
newTestDoc("1", "TESTFILE1", newCoveredFileDoc("3"), newCoveredFileDoc("4"), newCoveredFileDoc("5")), | |||||
newTestDoc("2", "TESTFILE1", newCoveredFileDoc("5"), newCoveredFileDoc("6"), newCoveredFileDoc("7")), | |||||
newTestDoc("3", "TESTFILE2", newCoveredFileDoc("5"), newCoveredFileDoc("6"), newCoveredFileDoc("7"))); | |||||
List<TestDoc> result = underTest.searchByTestFileUuid("TESTFILE1", searchOptions()).getDocs(); | |||||
assertThat(result).hasSize(2); | |||||
assertThat(result).extractingResultOf("name").containsOnly("name-1", "name-2"); | |||||
} | |||||
@Test | |||||
public void searchBySourceFileUuidAndLineNumber() { | |||||
es.putDocuments(INDEX_TYPE_TEST, | |||||
newTestDoc("1", "TESTFILE1", newCoveredFileDoc("10"), newCoveredFileDoc("11"), newCoveredFileDoc("12")), | |||||
newTestDoc("2", "TESTFILE1", newCoveredFileDoc("3"), newCoveredFileDoc("4"), newCoveredFileDoc("5")), | |||||
newTestDoc("3", "TESTFILE1", newCoveredFileDoc("5"), newCoveredFileDoc("6"), newCoveredFileDoc("7"))); | |||||
List<TestDoc> result = underTest.searchBySourceFileUuidAndLineNumber("main-uuid-5", 82, searchOptions()).getDocs(); | |||||
assertThat(result).hasSize(2); | |||||
assertThat(result).extractingResultOf("name").containsOnly("name-2", "name-3"); | |||||
} | |||||
@Test | |||||
public void searchByTestUuid() { | |||||
es.putDocuments(INDEX_TYPE_TEST, | |||||
newTestDoc("1", "TESTFILE1", newCoveredFileDoc("3"), newCoveredFileDoc("4"), newCoveredFileDoc("5")), | |||||
newTestDoc("2", "TESTFILE1", newCoveredFileDoc("5"), newCoveredFileDoc("6"), newCoveredFileDoc("7"))); | |||||
TestDoc test = underTest.getByTestUuid("1"); | |||||
assertThat(test.testUuid()).isEqualTo("1"); | |||||
assertThat(test.fileUuid()).isEqualTo("TESTFILE1"); | |||||
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"); | |||||
} | |||||
@Test | |||||
public void getNullableByTestUuid() { | |||||
es.putDocuments(INDEX_TYPE_TEST, | |||||
newTestDoc("1", "TESTFILE1", newCoveredFileDoc("3"), newCoveredFileDoc("4"), newCoveredFileDoc("5")), | |||||
newTestDoc("2", "TESTFILE1", newCoveredFileDoc("5"), newCoveredFileDoc("6"), newCoveredFileDoc("7"))); | |||||
Optional<TestDoc> result = underTest.getNullableByTestUuid("1"); | |||||
assertThat(result).isPresent(); | |||||
TestDoc test = result.get(); | |||||
assertThat(test.testUuid()).isEqualTo("1"); | |||||
assertThat(test.fileUuid()).isEqualTo("TESTFILE1"); | |||||
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"); | |||||
} | |||||
@Test | |||||
public void getNullableByTestUuid_with_absent_value() { | |||||
Optional<TestDoc> result = underTest.getNullableByTestUuid("unknown-uuid"); | |||||
assertThat(result).isAbsent(); | |||||
} | |||||
@Test | |||||
public void searchByTestUuid_with_SearchOptions() { | |||||
es.putDocuments(INDEX_TYPE_TEST, | |||||
newTestDoc("1", "TESTFILE1", newCoveredFileDoc("3"), newCoveredFileDoc("4"), newCoveredFileDoc("5")), | |||||
newTestDoc("2", "TESTFILE1", newCoveredFileDoc("5"), newCoveredFileDoc("6"), newCoveredFileDoc("7"))); | |||||
List<TestDoc> result = underTest.searchByTestUuid("1", searchOptions()).getDocs(); | |||||
assertThat(result).hasSize(1); | |||||
assertThat(result.get(0).testUuid()).isEqualTo("1"); | |||||
} | |||||
private CoveredFileDoc newCoveredFileDoc(String id) { | |||||
return new CoveredFileDoc() | |||||
.setFileUuid("main-uuid-" + id) | |||||
.setCoveredLines(asList(25, 33, 82)); | |||||
} | |||||
private TestDoc newTestDoc(String testUuid, String fileUuid, CoveredFileDoc... coveredFiles) { | |||||
return new TestDoc() | |||||
.setUuid(testUuid) | |||||
.setProjectUuid("P1") | |||||
.setName("name-" + testUuid) | |||||
.setMessage("message-" + testUuid) | |||||
.setStackTrace("stacktrace-" + testUuid) | |||||
.setStatus("status-" + testUuid) | |||||
.setDurationInMs(Long.valueOf(testUuid)) | |||||
.setFileUuid(fileUuid) | |||||
.setCoveredFiles(asList(coveredFiles)); | |||||
} | |||||
private SearchOptions searchOptions() { | |||||
return new SearchOptions() | |||||
.setLimit(100) | |||||
.setOffset(0); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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 java.io.IOException; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import org.apache.commons.io.IOUtils; | |||||
import org.elasticsearch.common.xcontent.XContentType; | |||||
import org.elasticsearch.search.SearchHit; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | |||||
import org.sonar.api.utils.System2; | |||||
import org.sonar.db.DbTester; | |||||
import org.sonar.server.es.EsTester; | |||||
import org.sonar.server.test.db.TestTesting; | |||||
import static java.lang.String.format; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.mockito.Mockito.doNothing; | |||||
import static org.mockito.Mockito.spy; | |||||
import static org.mockito.Mockito.verify; | |||||
import static org.sonar.server.es.DefaultIndexSettings.REFRESH_IMMEDIATE; | |||||
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_STACKTRACE; | |||||
import static org.sonar.server.test.index.TestIndexDefinition.INDEX_TYPE_TEST; | |||||
public class TestIndexerTest { | |||||
private System2 system2 = System2.INSTANCE; | |||||
@Rule | |||||
public EsTester es = EsTester.create(); | |||||
@Rule | |||||
public DbTester db = DbTester.create(system2); | |||||
private TestIndexer underTest = new TestIndexer(db.getDbClient(), es.client()); | |||||
@Test | |||||
public void index_on_startup() { | |||||
TestIndexer indexer = spy(underTest); | |||||
doNothing().when(indexer).indexOnStartup(null); | |||||
indexer.indexOnStartup(null); | |||||
verify(indexer).indexOnStartup(null); | |||||
} | |||||
@Test | |||||
public void index_tests() throws Exception { | |||||
db.prepareDbUnit(getClass(), "db.xml"); | |||||
TestTesting.updateDataColumn(db.getSession(), "FILE_UUID", TestTesting.newRandomTests(3)); | |||||
underTest.indexOnStartup(null); | |||||
assertThat(countDocuments()).isEqualTo(3); | |||||
} | |||||
@Test | |||||
public void index_tests_from_project() throws Exception { | |||||
db.prepareDbUnit(getClass(), "db.xml"); | |||||
TestTesting.updateDataColumn(db.getSession(), "FILE_UUID", TestTesting.newRandomTests(3)); | |||||
underTest.indexOnAnalysis("PROJECT_UUID"); | |||||
assertThat(countDocuments()).isEqualTo(3); | |||||
} | |||||
@Test | |||||
public void index_nothing_from_unknown_project() throws Exception { | |||||
db.prepareDbUnit(getClass(), "db.xml"); | |||||
TestTesting.updateDataColumn(db.getSession(), "FILE_UUID", TestTesting.newRandomTests(3)); | |||||
underTest.indexOnAnalysis("UNKNOWN"); | |||||
assertThat(countDocuments()).isZero(); | |||||
} | |||||
@Test | |||||
public void delete_file_by_uuid() throws Exception { | |||||
indexTest("P1", "F1", "T1", "U111"); | |||||
indexTest("P1", "F1", "T2", "U112"); | |||||
indexTest("P1", "F2", "T1", "U121"); | |||||
underTest.deleteByFile("F1"); | |||||
List<SearchHit> hits = getDocuments(); | |||||
Map<String, Object> document = hits.get(0).getSource(); | |||||
assertThat(hits).hasSize(1); | |||||
assertThat(document.get(FIELD_NAME)).isEqualTo("NAME_1"); | |||||
assertThat(document.get(FIELD_FILE_UUID)).isEqualTo("F2"); | |||||
} | |||||
@Test | |||||
public void long_message_can_be_indexed() throws Exception { | |||||
indexTest("P3", "F1", "long_message", "U111"); | |||||
assertThat(countDocuments()).isEqualTo(1); | |||||
List<SearchHit> hits = getDocuments(); | |||||
Map<String, Object> document = hits.get(0).getSource(); | |||||
assertThat(hits).hasSize(1); | |||||
assertThat(document.get(FIELD_MESSAGE).toString()).hasSize(50000); | |||||
assertThat(document.get(FIELD_FILE_UUID)).isEqualTo("F1"); | |||||
} | |||||
@Test | |||||
public void long_stacktrace_can_be_indexed() throws Exception { | |||||
indexTest("P3", "F1", "long_stacktrace", "U111"); | |||||
assertThat(countDocuments()).isEqualTo(1); | |||||
List<SearchHit> hits = getDocuments(); | |||||
Map<String, Object> document = hits.get(0).getSource(); | |||||
assertThat(hits).hasSize(1); | |||||
assertThat(document.get(FIELD_STACKTRACE).toString()).hasSize(50000); | |||||
assertThat(document.get(FIELD_FILE_UUID)).isEqualTo("F1"); | |||||
} | |||||
@Test | |||||
public void long_name_can_be_indexed() throws Exception { | |||||
indexTest("P3", "F1", "long_name", "U111"); | |||||
assertThat(countDocuments()).isEqualTo(1); | |||||
List<SearchHit> hits = getDocuments(); | |||||
Map<String, Object> document = hits.get(0).getSource(); | |||||
assertThat(hits).hasSize(1); | |||||
assertThat(document.get(FIELD_NAME).toString()).hasSize(50000); | |||||
assertThat(document.get(FIELD_FILE_UUID)).isEqualTo("F1"); | |||||
} | |||||
private void indexTest(String projectUuid, String fileUuid, String testName, String uuid) throws IOException { | |||||
String json = IOUtils.toString(getClass().getResource(format("%s/%s_%s_%s.json", getClass().getSimpleName(), projectUuid, fileUuid, testName))); | |||||
es.client().prepareIndex(INDEX_TYPE_TEST) | |||||
.setId(uuid) | |||||
.setRouting(projectUuid) | |||||
.setSource(json, XContentType.JSON) | |||||
.setRefreshPolicy(REFRESH_IMMEDIATE) | |||||
.get(); | |||||
} | |||||
private List<SearchHit> getDocuments() { | |||||
return es.getDocuments(INDEX_TYPE_TEST); | |||||
} | |||||
private long countDocuments() { | |||||
return es.countDocuments(INDEX_TYPE_TEST); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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 java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import org.elasticsearch.action.update.UpdateRequest; | |||||
import org.junit.After; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | |||||
import org.sonar.api.utils.System2; | |||||
import org.sonar.api.utils.log.LogTester; | |||||
import org.sonar.api.utils.log.LoggerLevel; | |||||
import org.sonar.db.DbTester; | |||||
import org.sonar.db.protobuf.DbFileSources; | |||||
import org.sonar.server.source.index.FileSourcesUpdaterHelper; | |||||
import org.sonar.server.test.db.TestTesting; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.assertj.core.data.MapEntry.entry; | |||||
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 TestResultSetIteratorTest { | |||||
@Rule | |||||
public DbTester dbTester = DbTester.create(System2.INSTANCE); | |||||
@Rule | |||||
public LogTester logTester = new LogTester(); | |||||
TestResultSetIterator underTest; | |||||
private static List<DbFileSources.Test> newFakeTests(int numberOfTests) { | |||||
List<DbFileSources.Test> tests = new ArrayList<>(); | |||||
for (int i = 1; i <= numberOfTests; i++) { | |||||
DbFileSources.Test.Builder test = DbFileSources.Test.newBuilder() | |||||
.setUuid("TEST_FILE_UUID_" + i) | |||||
.setName("NAME_" + i) | |||||
.setStatus(DbFileSources.Test.TestStatus.FAILURE) | |||||
.setStacktrace("STACKTRACE_" + i) | |||||
.setMsg("MESSAGE_" + i) | |||||
.setExecutionTimeMs(i); | |||||
for (int j = 1; j <= numberOfTests; j++) { | |||||
test.addCoveredFile( | |||||
DbFileSources.Test.CoveredFile.newBuilder() | |||||
.setFileUuid("MAIN_FILE_UUID_" + j) | |||||
.addCoveredLine(j)); | |||||
} | |||||
tests.add(test.build()); | |||||
} | |||||
return tests; | |||||
} | |||||
@After | |||||
public void after() { | |||||
if (underTest != null) { | |||||
underTest.close(); | |||||
} | |||||
} | |||||
@Test | |||||
public void traverse_db() throws Exception { | |||||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||||
TestTesting.updateDataColumn(dbTester.getSession(), "F1", newFakeTests(3)); | |||||
underTest = TestResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), null); | |||||
FileSourcesUpdaterHelper.Row row = underTest.next(); | |||||
assertThat(row.getProjectUuid()).isEqualTo("P1"); | |||||
assertThat(row.getFileUuid()).isEqualTo("F1"); | |||||
assertThat(row.getUpdatedAt()).isEqualTo(1416239042000L); | |||||
assertThat(row.getUpdateRequests()).hasSize(3); | |||||
UpdateRequest firstRequest = row.getUpdateRequests().get(0); | |||||
Map<String, Object> doc = firstRequest.doc().sourceAsMap(); | |||||
assertThat(doc).contains( | |||||
entry(FIELD_PROJECT_UUID, "P1"), | |||||
entry(FIELD_FILE_UUID, "F1"), | |||||
entry(FIELD_TEST_UUID, "TEST_FILE_UUID_1"), | |||||
entry(FIELD_STATUS, "FAILURE"), | |||||
entry(FIELD_MESSAGE, "MESSAGE_1"), | |||||
entry(FIELD_DURATION_IN_MS, 1), | |||||
entry(FIELD_STACKTRACE, "STACKTRACE_1"), | |||||
entry(FIELD_NAME, "NAME_1")); | |||||
} | |||||
/** | |||||
* File with one line. No metadata available on the line. | |||||
*/ | |||||
@Test | |||||
public void minimal_data() throws Exception { | |||||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||||
List<DbFileSources.Test> tests = Arrays.asList( | |||||
DbFileSources.Test.newBuilder() | |||||
.setUuid("U1") | |||||
.setName("N1") | |||||
.build()); | |||||
TestTesting.updateDataColumn(dbTester.getSession(), "F1", tests); | |||||
underTest = TestResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), null); | |||||
FileSourcesUpdaterHelper.Row row = underTest.next(); | |||||
assertThat(row.getProjectUuid()).isEqualTo("P1"); | |||||
assertThat(row.getFileUuid()).isEqualTo("F1"); | |||||
assertThat(row.getUpdatedAt()).isEqualTo(1416239042000L); | |||||
assertThat(row.getUpdateRequests()).hasSize(1); | |||||
UpdateRequest firstRequest = row.getUpdateRequests().get(0); | |||||
Map<String, Object> doc = firstRequest.doc().sourceAsMap(); | |||||
assertThat(doc).contains( | |||||
entry(FIELD_PROJECT_UUID, "P1"), | |||||
entry(FIELD_FILE_UUID, "F1"), | |||||
entry(FIELD_TEST_UUID, "U1"), | |||||
entry(FIELD_NAME, "N1")); | |||||
// null values | |||||
assertThat(doc).containsKeys( | |||||
FIELD_DURATION_IN_MS, | |||||
FIELD_STACKTRACE, | |||||
FIELD_MESSAGE, | |||||
FIELD_STATUS, | |||||
FIELD_COVERED_FILES); | |||||
} | |||||
@Test | |||||
public void filter_by_project() throws Exception { | |||||
dbTester.prepareDbUnit(getClass(), "filter_by_project.xml"); | |||||
TestTesting.updateDataColumn(dbTester.getSession(), "F1", newFakeTests(1)); | |||||
underTest = TestResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), "P1"); | |||||
FileSourcesUpdaterHelper.Row row = underTest.next(); | |||||
assertThat(row.getProjectUuid()).isEqualTo("P1"); | |||||
assertThat(row.getFileUuid()).isEqualTo("F1"); | |||||
// File from other project P2 is not returned | |||||
assertThat(underTest.hasNext()).isFalse(); | |||||
} | |||||
@Test | |||||
public void read_does_not_fail_if_corrupted_data() throws Exception { | |||||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||||
TestTesting.updateDataColumn(dbTester.getSession(), "F1", "THIS_IS_NOT_PROTOBUF".getBytes()); | |||||
underTest = TestResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), null); | |||||
FileSourcesUpdaterHelper.Row row = underTest.next(); | |||||
assertThat(row.getFileUuid()).isEqualTo("F1"); | |||||
assertThat(row.getUpdateRequests()).isEmpty(); | |||||
assertThat(underTest.hasNext()).isFalse(); | |||||
assertThat(logTester.logs(LoggerLevel.WARN)).contains("Invalid file_sources.binary_data on row with file_uuid='F1', test file will be ignored"); | |||||
} | |||||
@Test | |||||
public void read_does_not_fail_if_null_data() throws Exception { | |||||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||||
TestTesting.updateDataColumn(dbTester.getSession(), "F1", (byte[])null); | |||||
underTest = TestResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), null); | |||||
FileSourcesUpdaterHelper.Row row = underTest.next(); | |||||
assertThat(row.getFileUuid()).isEqualTo("F1"); | |||||
assertThat(row.getUpdateRequests()).isEmpty(); | |||||
assertThat(underTest.hasNext()).isFalse(); | |||||
} | |||||
} |
{ | |||||
"projectUuid": "P1", | |||||
"fileUuid": "F1", | |||||
"testUuid": "U111", | |||||
"name": "NAME_1", | |||||
"status": "FAILURE", | |||||
"durationInMs": 100000, | |||||
"message": "MESSAGE_1", | |||||
"stacktrace": "STACKTRACE_1", | |||||
"updatedAt": "2014-01-01T23:45:01.8+01:00" | |||||
} |
{ | |||||
"projectUuid": "P1", | |||||
"fileUuid": "F1", | |||||
"testUuid": "U112", | |||||
"name": "NAME_2", | |||||
"status": "FAILURE", | |||||
"durationInMs": 100000, | |||||
"message": "MESSAGE_1", | |||||
"stacktrace": "STACKTRACE_1", | |||||
"updatedAt": "2014-01-01T23:45:01.8+01:00" | |||||
} |
{ | |||||
"projectUuid": "P1", | |||||
"fileUuid": "F2", | |||||
"testUuid": "U121", | |||||
"name": "NAME_1", | |||||
"status": "FAILURE", | |||||
"durationInMs": 100000, | |||||
"message": "MESSAGE_1", | |||||
"stacktrace": "STACKTRACE_1", | |||||
"updatedAt": "2014-01-01T23:45:01.8+01:00" | |||||
} |
{ | |||||
"projectUuid": "P2", | |||||
"fileUuid": "F3", | |||||
"testUuid": "U231", | |||||
"name": "NAME_1", | |||||
"status": "FAILURE", | |||||
"durationInMs": 100000, | |||||
"message": "MESSAGE_1", | |||||
"stacktrace": "STACKTRACE_1", | |||||
"updatedAt": "2014-01-01T23:45:01.8+01:00" | |||||
} |
<dataset> | |||||
<file_sources id="1" | |||||
project_uuid="PROJECT_UUID" | |||||
file_uuid="FILE_UUID" | |||||
line_count="0" | |||||
created_at="1416238020000" | |||||
updated_at="1416239042000" | |||||
binary_data="" | |||||
data_hash="" | |||||
data_type="TEST" | |||||
/> | |||||
</dataset> |
<dataset> | |||||
<file_sources id="1" | |||||
project_uuid="P1" | |||||
file_uuid="F1" | |||||
created_at="1416238020000" | |||||
updated_at="1416239042000" | |||||
line_count="0" | |||||
binary_data="" | |||||
data_hash="" | |||||
data_type="TEST"/> | |||||
<file_sources id="2" | |||||
project_uuid="P2" | |||||
file_uuid="F2" | |||||
created_at="1416238020000" | |||||
updated_at="1416239042000" | |||||
line_count="0" | |||||
binary_data="" | |||||
data_hash="" | |||||
data_type="TEST"/> | |||||
</dataset> |
<dataset> | |||||
<file_sources id="1" | |||||
project_uuid="P1" | |||||
file_uuid="F1" | |||||
created_at="1416238020000" | |||||
updated_at="1416239042000" | |||||
line_count="0" | |||||
binary_data="" | |||||
data_hash="" | |||||
data_type="TEST"/> | |||||
<file_sources id="2" | |||||
project_uuid="P1" | |||||
file_uuid="F2" | |||||
created_at="1416238020000" | |||||
updated_at="1300000000000" | |||||
line_count="0" | |||||
binary_data="" | |||||
data_hash="" | |||||
data_type="TEST"/> | |||||
</dataset> |
CREATE TABLE "FILE_SOURCES" ( | |||||
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), | |||||
"PROJECT_UUID" VARCHAR(50) NOT NULL, | |||||
"FILE_UUID" VARCHAR(50) NOT NULL, | |||||
"BINARY_DATA" BINARY(167772150), | |||||
"DATA_HASH" VARCHAR(50) NOT NULL, | |||||
"DATA_TYPE" VARCHAR(50), | |||||
"CREATED_AT" BIGINT NOT NULL, | |||||
"UPDATED_AT" BIGINT NOT NULL | |||||
); |
<dataset> | |||||
<file_sources id="1" | |||||
project_uuid="P1" | |||||
file_uuid="F1" | |||||
created_at="1416238020000" | |||||
updated_at="1416239042000" | |||||
line_count="0" | |||||
binary_data="" | |||||
data_hash="" | |||||
data_type="TEST"/> | |||||
</dataset> |
import org.sonar.server.telemetry.TelemetryClient; | import org.sonar.server.telemetry.TelemetryClient; | ||||
import org.sonar.server.telemetry.TelemetryDaemon; | import org.sonar.server.telemetry.TelemetryDaemon; | ||||
import org.sonar.server.telemetry.TelemetryDataLoader; | import org.sonar.server.telemetry.TelemetryDataLoader; | ||||
import org.sonar.server.test.index.TestIndex; | |||||
import org.sonar.server.test.index.TestIndexDefinition; | import org.sonar.server.test.index.TestIndexDefinition; | ||||
import org.sonar.server.test.index.TestIndexer; | |||||
import org.sonar.server.test.ws.CoveredFilesAction; | |||||
import org.sonar.server.test.ws.TestsWs; | import org.sonar.server.test.ws.TestsWs; | ||||
import org.sonar.server.text.MacroInterpreter; | import org.sonar.server.text.MacroInterpreter; | ||||
import org.sonar.server.ui.DeprecatedViews; | import org.sonar.server.ui.DeprecatedViews; | ||||
// Tests | // Tests | ||||
TestsWs.class, | TestsWs.class, | ||||
CoveredFilesAction.class, | |||||
org.sonar.server.test.ws.ListAction.class, | |||||
TestIndexDefinition.class, | TestIndexDefinition.class, | ||||
TestIndex.class, | |||||
TestIndexer.class, | |||||
// Settings | // Settings | ||||
PersistentSettings.class, | PersistentSettings.class, |
private <E> Optional<Iterable<E>> getLines(DbSession dbSession, String fileUuid, int from, int toInclusive, Function<DbFileSources.Line, E> function) { | private <E> Optional<Iterable<E>> getLines(DbSession dbSession, String fileUuid, int from, int toInclusive, Function<DbFileSources.Line, E> function) { | ||||
verifyLine(from); | verifyLine(from); | ||||
checkArgument(toInclusive >= from, String.format("Line number must greater than or equal to %d, got %d", from, toInclusive)); | checkArgument(toInclusive >= from, String.format("Line number must greater than or equal to %d, got %d", from, toInclusive)); | ||||
FileSourceDto dto = dbClient.fileSourceDao().selectSourceByFileUuid(dbSession, fileUuid); | |||||
FileSourceDto dto = dbClient.fileSourceDao().selectByFileUuid(dbSession, fileUuid); | |||||
if (dto == null) { | if (dto == null) { | ||||
return Optional.empty(); | return Optional.empty(); | ||||
} | } |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.Function; | |||||
import com.google.common.collect.Lists; | |||||
import com.google.common.collect.Maps; | |||||
import com.google.common.io.Resources; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import javax.annotation.Nonnull; | |||||
import org.sonar.api.server.ws.Change; | |||||
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.web.UserRole; | |||||
import org.sonar.core.util.Uuids; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.component.ComponentDto; | |||||
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 org.sonarqube.ws.Tests; | |||||
import static org.sonar.core.util.Protobuf.setNullable; | |||||
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; | |||||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||||
public class CoveredFilesAction implements TestsWsAction { | |||||
public static final String TEST_ID = "testId"; | |||||
private final DbClient dbClient; | |||||
private final TestIndex index; | |||||
private final UserSession userSession; | |||||
public CoveredFilesAction(DbClient dbClient, TestIndex index, UserSession userSession) { | |||||
this.dbClient = dbClient; | |||||
this.index = index; | |||||
this.userSession = userSession; | |||||
} | |||||
@Override | |||||
public void define(WebService.NewController controller) { | |||||
WebService.NewAction action = controller.createAction("covered_files") | |||||
.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-covered-files.json")) | |||||
.setDeprecatedSince("5.6") | |||||
.setHandler(this) | |||||
.setChangelog(new Change("6.6", "\"branch\" field is now returned")) | |||||
.addPagingParams(100); | |||||
action | |||||
.createParam(TEST_ID) | |||||
.setRequired(true) | |||||
.setDescription("Test ID") | |||||
.setExampleValue(Uuids.UUID_EXAMPLE_01); | |||||
} | |||||
@Override | |||||
public void handle(Request request, Response response) throws Exception { | |||||
String testId = request.mandatoryParam(TEST_ID); | |||||
TestDoc testDoc = checkFoundWithOptional(index.getNullableByTestUuid(testId), "Test with id '%s' is not found", testId); | |||||
userSession.checkComponentUuidPermission(UserRole.CODEVIEWER, testDoc.fileUuid()); | |||||
List<CoveredFileDoc> coveredFiles = index.coveredFiles(testId); | |||||
Map<String, ComponentDto> componentsByUuid = buildComponentsByUuid(coveredFiles); | |||||
Tests.CoveredFilesResponse.Builder responseBuilder = Tests.CoveredFilesResponse.newBuilder(); | |||||
if (!coveredFiles.isEmpty()) { | |||||
for (CoveredFileDoc doc : coveredFiles) { | |||||
Tests.CoveredFilesResponse.CoveredFile.Builder fileBuilder = Tests.CoveredFilesResponse.CoveredFile.newBuilder(); | |||||
fileBuilder.setId(doc.fileUuid()); | |||||
fileBuilder.setCoveredLines(doc.coveredLines().size()); | |||||
ComponentDto component = componentsByUuid.get(doc.fileUuid()); | |||||
if (component != null) { | |||||
fileBuilder.setKey(component.getKey()); | |||||
fileBuilder.setLongName(component.longName()); | |||||
setNullable(component.getBranch(), fileBuilder::setBranch); | |||||
} | |||||
responseBuilder.addFiles(fileBuilder); | |||||
} | |||||
} | |||||
writeProtobuf(responseBuilder.build(), request, response); | |||||
} | |||||
private Map<String, ComponentDto> buildComponentsByUuid(List<CoveredFileDoc> coveredFiles) { | |||||
List<String> sourceFileUuids = Lists.transform(coveredFiles, new CoveredFileToFileUuidFunction()); | |||||
List<ComponentDto> components; | |||||
try (DbSession dbSession = dbClient.openSession(false)) { | |||||
components = dbClient.componentDao().selectByUuids(dbSession, sourceFileUuids); | |||||
} | |||||
return Maps.uniqueIndex(components, ComponentDto::uuid); | |||||
} | |||||
private static class CoveredFileToFileUuidFunction implements Function<CoveredFileDoc, String> { | |||||
@Override | |||||
public String apply(@Nonnull CoveredFileDoc coveredFile) { | |||||
return coveredFile.fileUuid(); | |||||
} | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.Function; | |||||
import com.google.common.collect.Lists; | |||||
import com.google.common.collect.Maps; | |||||
import com.google.common.io.Resources; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import javax.annotation.Nonnull; | |||||
import javax.annotation.Nullable; | |||||
import org.apache.commons.lang.StringUtils; | |||||
import org.sonar.api.server.ws.Change; | |||||
import org.sonar.api.server.ws.Request; | |||||
import org.sonar.api.server.ws.Response; | |||||
import org.sonar.api.server.ws.WebService; | |||||
import org.sonar.core.util.Uuids; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbSession; | |||||
import org.sonar.db.component.ComponentDto; | |||||
import org.sonar.server.component.ComponentFinder; | |||||
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 org.sonar.server.ws.KeyExamples; | |||||
import org.sonar.server.ws.WsUtils; | |||||
import org.sonarqube.ws.Common; | |||||
import org.sonarqube.ws.Tests; | |||||
import static org.sonar.api.server.ws.WebService.Param.PAGE; | |||||
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; | |||||
import static org.sonar.api.web.UserRole.CODEVIEWER; | |||||
import static org.sonar.core.util.Protobuf.setNullable; | |||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT; | |||||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||||
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; | |||||
public class ListAction implements TestsWsAction { | |||||
public static final String TEST_ID = "testId"; | |||||
public static final String TEST_FILE_ID = "testFileId"; | |||||
public static final String TEST_FILE_KEY = "testFileKey"; | |||||
public static final String SOURCE_FILE_ID = "sourceFileId"; | |||||
public static final String SOURCE_FILE_KEY = "sourceFileKey"; | |||||
public static final String SOURCE_FILE_LINE_NUMBER = "sourceFileLineNumber"; | |||||
public static final String PARAM_BRANCH = "branch"; | |||||
public static final String PARAM_PULL_REQUEST = "pullRequest"; | |||||
private final DbClient dbClient; | |||||
private final TestIndex testIndex; | |||||
private final UserSession userSession; | |||||
private final ComponentFinder componentFinder; | |||||
public ListAction(DbClient dbClient, TestIndex testIndex, UserSession userSession, ComponentFinder componentFinder) { | |||||
this.dbClient = dbClient; | |||||
this.testIndex = testIndex; | |||||
this.userSession = userSession; | |||||
this.componentFinder = componentFinder; | |||||
} | |||||
@Override | |||||
public void define(WebService.NewController controller) { | |||||
WebService.NewAction action = controller | |||||
.createAction("list") | |||||
.setDescription(String.format( | |||||
"Get the list of tests either in a test file or that test a given line of source code.<br /> " + | |||||
"Requires 'Browse' permission on the file's project.<br /> " + | |||||
"One (and only one) of the following combination of parameters must be provided: " + | |||||
"<ul>" + | |||||
"<li>%s - get a specific test</li>" + | |||||
"<li>%s - get the tests in a test file</li>" + | |||||
"<li>%s - get the tests in a test file</li>" + | |||||
"<li>%s and %6$s - get the tests that cover a specific line of code</li>" + | |||||
"<li>%s and %6$s - get the tests that cover a specific line of code</li>" + | |||||
"</ul>", | |||||
TEST_ID, TEST_FILE_ID, TEST_FILE_KEY, SOURCE_FILE_ID, SOURCE_FILE_KEY, SOURCE_FILE_LINE_NUMBER)) | |||||
.setSince("5.2") | |||||
.setResponseExample(Resources.getResource(getClass(), "tests-example-list.json")) | |||||
.setDeprecatedSince("5.6") | |||||
.setHandler(this) | |||||
.setChangelog(new Change("6.6", "\"fileBranch\" field is now returned")) | |||||
.setChangelog(new Change("7.1", "\"filePullRequest\" field is now returned")) | |||||
.addPagingParams(100, MAX_LIMIT); | |||||
action | |||||
.createParam(TEST_FILE_ID) | |||||
.setDescription("ID of test file") | |||||
.setExampleValue(Uuids.UUID_EXAMPLE_01); | |||||
action | |||||
.createParam(TEST_FILE_KEY) | |||||
.setDescription("Key of test file") | |||||
.setExampleValue("MY_PROJECT:src/test/java/foo/BarTest.java"); | |||||
action | |||||
.createParam(TEST_ID) | |||||
.setDescription("ID of test") | |||||
.setExampleValue(Uuids.UUID_EXAMPLE_02); | |||||
action | |||||
.createParam(SOURCE_FILE_ID) | |||||
.setDescription("ID of source file. Must be provided with the source file line number.") | |||||
.setExampleValue(Uuids.UUID_EXAMPLE_03); | |||||
action | |||||
.createParam(SOURCE_FILE_KEY) | |||||
.setSince("5.4") | |||||
.setDescription("Key of source file. Must be provided with the source file line number.") | |||||
.setExampleValue(KeyExamples.KEY_FILE_EXAMPLE_001); | |||||
action | |||||
.createParam(SOURCE_FILE_LINE_NUMBER) | |||||
.setDescription("Source file line number. Must be provided with the source file ID or key.") | |||||
.setExampleValue("10"); | |||||
action.createParam(PARAM_BRANCH) | |||||
.setDescription("Branch key") | |||||
.setSince("6.6") | |||||
.setInternal(true) | |||||
.setExampleValue(KEY_BRANCH_EXAMPLE_001); | |||||
action.createParam(PARAM_PULL_REQUEST) | |||||
.setDescription("Pull request id") | |||||
.setSince("7.1") | |||||
.setInternal(true) | |||||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001); | |||||
} | |||||
@Override | |||||
public void handle(Request request, Response response) throws Exception { | |||||
String testUuid = request.param(TEST_ID); | |||||
String testFileUuid = request.param(TEST_FILE_ID); | |||||
String testFileKey = request.param(TEST_FILE_KEY); | |||||
String sourceFileUuid = request.param(SOURCE_FILE_ID); | |||||
String sourceFileKey = request.param(SOURCE_FILE_KEY); | |||||
String branch = request.param(PARAM_BRANCH); | |||||
String pullRequest = request.param(PARAM_PULL_REQUEST); | |||||
Integer sourceFileLineNumber = request.paramAsInt(SOURCE_FILE_LINE_NUMBER); | |||||
SearchOptions searchOptions = new SearchOptions().setPage( | |||||
request.mandatoryParamAsInt(PAGE), | |||||
request.mandatoryParamAsInt(PAGE_SIZE)); | |||||
SearchResult<TestDoc> tests; | |||||
Map<String, ComponentDto> componentsByTestFileUuid; | |||||
try (DbSession dbSession = dbClient.openSession(false)) { | |||||
tests = searchTests(dbSession, testUuid, testFileUuid, testFileKey, sourceFileUuid, sourceFileKey, branch, pullRequest, sourceFileLineNumber, searchOptions); | |||||
componentsByTestFileUuid = buildComponentsByTestFileUuid(dbSession, tests.getDocs()); | |||||
} | |||||
Tests.ListResponse.Builder responseBuilder = Tests.ListResponse.newBuilder(); | |||||
responseBuilder.setPaging(Common.Paging.newBuilder() | |||||
.setPageIndex(searchOptions.getPage()) | |||||
.setPageSize(searchOptions.getLimit()) | |||||
.setTotal((int) tests.getTotal()) | |||||
.build()); | |||||
for (TestDoc testDoc : tests.getDocs()) { | |||||
Tests.Test.Builder testBuilder = Tests.Test.newBuilder(); | |||||
testBuilder.setId(testDoc.testUuid()); | |||||
testBuilder.setName(StringUtils.defaultString(testDoc.name())); | |||||
testBuilder.setFileId(testDoc.fileUuid()); | |||||
ComponentDto component = componentsByTestFileUuid.get(testDoc.fileUuid()); | |||||
if (component != null) { | |||||
testBuilder.setFileKey(component.getKey()); | |||||
testBuilder.setFileName(component.longName()); | |||||
setNullable(component.getBranch(), testBuilder::setFileBranch); | |||||
setNullable(component.getPullRequest(), testBuilder::setFilePullRequest); | |||||
} | |||||
testBuilder.setStatus(Tests.TestStatus.valueOf(testDoc.status())); | |||||
if (testDoc.durationInMs() != null) { | |||||
testBuilder.setDurationInMs(testDoc.durationInMs()); | |||||
} | |||||
testBuilder.setCoveredLines(coveredLines(testDoc.coveredFiles())); | |||||
if (testDoc.message() != null) { | |||||
testBuilder.setMessage(testDoc.message()); | |||||
} | |||||
if (testDoc.stackTrace() != null) { | |||||
testBuilder.setStacktrace(testDoc.stackTrace()); | |||||
} | |||||
responseBuilder.addTests(testBuilder.build()); | |||||
} | |||||
WsUtils.writeProtobuf(responseBuilder.build(), request, response); | |||||
} | |||||
private static int coveredLines(List<CoveredFileDoc> coveredFiles) { | |||||
int numberOfLinesCovered = 0; | |||||
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 TestToFileUuidFunction()); | |||||
List<ComponentDto> components = dbClient.componentDao().selectByUuids(dbSession, fileUuids); | |||||
return Maps.uniqueIndex(components, ComponentDto::uuid); | |||||
} | |||||
private SearchResult<TestDoc> searchTests(DbSession dbSession, @Nullable String testUuid, @Nullable String testFileUuid, @Nullable String testFileKey, | |||||
@Nullable String sourceFileUuid, @Nullable String sourceFileKey, @Nullable String branch, @Nullable String pullRequest, | |||||
@Nullable Integer sourceFileLineNumber, SearchOptions searchOptions) { | |||||
if (testUuid != null) { | |||||
TestDoc testDoc = checkFoundWithOptional(testIndex.getNullableByTestUuid(testUuid), "Test with id '%s' is not found", testUuid); | |||||
checkComponentUuidPermission(dbSession, testDoc.fileUuid()); | |||||
return testIndex.searchByTestUuid(testUuid, searchOptions); | |||||
} | |||||
if (testFileUuid != null) { | |||||
checkComponentUuidPermission(dbSession, testFileUuid); | |||||
return testIndex.searchByTestFileUuid(testFileUuid, searchOptions); | |||||
} | |||||
if (testFileKey != null) { | |||||
ComponentDto testFile = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, testFileKey, branch, pullRequest); | |||||
userSession.checkComponentPermission(CODEVIEWER, testFile); | |||||
return testIndex.searchByTestFileUuid(testFile.uuid(), searchOptions); | |||||
} | |||||
if (sourceFileUuid != null && sourceFileLineNumber != null) { | |||||
ComponentDto sourceFile = componentFinder.getByUuid(dbSession, sourceFileUuid); | |||||
userSession.checkComponentPermission(CODEVIEWER, sourceFile); | |||||
return testIndex.searchBySourceFileUuidAndLineNumber(sourceFile.uuid(), sourceFileLineNumber, searchOptions); | |||||
} | |||||
if (sourceFileKey != null && sourceFileLineNumber != null) { | |||||
ComponentDto sourceFile = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, sourceFileKey, branch, pullRequest); | |||||
userSession.checkComponentPermission(CODEVIEWER, sourceFile); | |||||
return testIndex.searchBySourceFileUuidAndLineNumber(sourceFile.uuid(), 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 ID or key with a source file line number."); | |||||
} | |||||
private void checkComponentUuidPermission(DbSession dbSession, String componentUuid) { | |||||
ComponentDto component = componentFinder.getByUuid(dbSession, componentUuid); | |||||
userSession.checkComponentPermission(CODEVIEWER, component); | |||||
} | |||||
private static class TestToFileUuidFunction implements Function<TestDoc, String> { | |||||
@Override | |||||
public String apply(@Nonnull TestDoc testDoc) { | |||||
return testDoc.fileUuid(); | |||||
} | |||||
} | |||||
} |
*/ | */ | ||||
package org.sonar.server.test.ws; | package org.sonar.server.test.ws; | ||||
import org.sonar.api.server.ws.Change; | |||||
import org.sonar.api.server.ws.WebService; | import org.sonar.api.server.ws.WebService; | ||||
import org.sonar.server.ws.RemovedWebServiceHandler; | |||||
public class TestsWs implements WebService { | public class TestsWs implements WebService { | ||||
private final TestsWsAction[] actions; | |||||
public TestsWs(TestsWsAction... actions) { | |||||
this.actions = actions; | |||||
} | |||||
@Override | @Override | ||||
public void define(Context context) { | public void define(Context context) { | ||||
NewController controller = context.createController("api/tests") | NewController controller = context.createController("api/tests") | ||||
.setSince("4.4") | .setSince("4.4") | ||||
.setDescription("Get details on test files. See also api/sources. Deprecated since 5.6."); | |||||
.setDescription("Removed in 7.6"); | |||||
for (TestsWsAction action : actions) { | |||||
action.define(controller); | |||||
} | |||||
controller.createAction("covered_files") | |||||
.setDescription("This web API is no longer supported") | |||||
.setSince("4.4") | |||||
.setDeprecatedSince("5.6") | |||||
.setChangelog(new Change("7.6", "This action has been removed")) | |||||
.setResponseExample(RemovedWebServiceHandler.INSTANCE.getResponseExample()) | |||||
.setHandler(RemovedWebServiceHandler.INSTANCE); | |||||
controller | |||||
.createAction("list") | |||||
.setDescription("This web API is no longer supported") | |||||
.setSince("5.2") | |||||
.setDeprecatedSince("5.6") | |||||
.setChangelog(new Change("7.6", "This action has been removed")) | |||||
.setResponseExample(RemovedWebServiceHandler.INSTANCE.getResponseExample()) | |||||
.setHandler(RemovedWebServiceHandler.INSTANCE); | |||||
controller.done(); | controller.done(); | ||||
} | } |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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 TestsWsAction extends WsAction { | |||||
// marker interface | |||||
} |
{ | |||||
"files": [ | |||||
{ | |||||
"key": "org.codehaus.sonar:sonar-server:src/main/java/org/sonar/server/paging/PagedResult.java", | |||||
"longName": "src/main/java/org/sonar/server/paging/PagedResult.java", | |||||
"coveredLines": 5 | |||||
}, | |||||
{ | |||||
"key": "org.codehaus.sonar:sonar-server:src/main/java/org/sonar/server/rule/RuleDocumentParser.java", | |||||
"longName": "src/main/java/org/sonar/server/rule/RuleDocumentParser.java", | |||||
"coveredLines": 38 | |||||
} | |||||
] | |||||
} |
{ | |||||
"paging": { | |||||
"pageIndex": 1, | |||||
"pageSize": 10, | |||||
"total": 2 | |||||
}, | |||||
"tests": [ | |||||
{ | |||||
"id": "AU-TpxcB-iU5OvuD2FL7", | |||||
"name": "find_by_params", | |||||
"status": "OK", | |||||
"fileId": "AU-TpxcB-iU5OvuD2Fd8", | |||||
"fileKey": "org.codehaus.sonar:sonar-server:src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java", | |||||
"fileName": "src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java", | |||||
"durationInMs": 10, | |||||
"coveredLines": 89 | |||||
}, | |||||
{ | |||||
"id": "AU-TpxcB-iU5OvuD2FP9", | |||||
"name": "find_rules_by_characteristics", | |||||
"status": "ERROR", | |||||
"fileId": "AU-TpxcB-iU5OvuD2FR2", | |||||
"fileKey": "org.codehaus.sonar:sonar-server:src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java", | |||||
"fileName": "src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java", | |||||
"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" | |||||
} | |||||
] | |||||
} |
.setLineHashes(of("8d7b3d6b83c0a517eac07e1aac94b773")) | .setLineHashes(of("8d7b3d6b83c0a517eac07e1aac94b773")) | ||||
.setCreatedAt(System.currentTimeMillis()) | .setCreatedAt(System.currentTimeMillis()) | ||||
.setUpdatedAt(System.currentTimeMillis()) | .setUpdatedAt(System.currentTimeMillis()) | ||||
.setDataType(FileSourceDto.Type.SOURCE) | |||||
.setRevision("123456789") | .setRevision("123456789") | ||||
.setSrcHash("123456"); | .setSrcHash("123456"); | ||||
} | } |
import org.sonar.db.DbTester; | import org.sonar.db.DbTester; | ||||
import org.sonar.db.component.ComponentDto; | import org.sonar.db.component.ComponentDto; | ||||
import org.sonar.db.organization.OrganizationDto; | import org.sonar.db.organization.OrganizationDto; | ||||
import org.sonar.db.protobuf.DbFileSources; | |||||
import org.sonar.db.source.FileSourceDto; | import org.sonar.db.source.FileSourceDto; | ||||
import org.sonar.server.component.TestComponentFinder; | import org.sonar.server.component.TestComponentFinder; | ||||
import org.sonar.server.exceptions.ForbiddenException; | import org.sonar.server.exceptions.ForbiddenException; | ||||
import static java.lang.String.format; | import static java.lang.String.format; | ||||
import static java.util.Collections.singletonList; | import static java.util.Collections.singletonList; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE; | |||||
import static org.sonar.api.web.UserRole.USER; | import static org.sonar.api.web.UserRole.USER; | ||||
import static org.sonar.db.component.ComponentTesting.newFileDto; | import static org.sonar.db.component.ComponentTesting.newFileDto; | ||||
assertThat(request.execute().getInput()).isEqualTo("ABC"); | assertThat(request.execute().getInput()).isEqualTo("ABC"); | ||||
} | } | ||||
@Test | |||||
public void show_hashes_on_test_file() { | |||||
OrganizationDto organizationDto = db.organizations().insert(); | |||||
ComponentDto project = db.components().insertPrivateProject(organizationDto); | |||||
ComponentDto test = db.components().insertComponent(newFileDto(project).setQualifier(UNIT_TEST_FILE)); | |||||
FileSourceDto fileSource = db.fileSources().insertFileSource(test, f -> f.setLineHashes(singletonList("ABC"))); | |||||
FileSourceDto fileTest = db.fileSources().insertFileSource(test, f -> f.setTestData(singletonList(DbFileSources.Test.newBuilder().build()))); | |||||
loginAsProjectViewer(project); | |||||
TestRequest request = tester.newRequest().setParam("key", test.getKey()); | |||||
assertThat(request.execute().getInput()).isEqualTo("ABC"); | |||||
} | |||||
@Test | @Test | ||||
public void hashes_empty_if_no_source() { | public void hashes_empty_if_no_source() { | ||||
OrganizationDto organizationDto = db.organizations().insert(); | OrganizationDto organizationDto = db.organizations().insert(); |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.Rule; | |||||
import org.junit.Test; | |||||
import org.junit.rules.ExpectedException; | |||||
import org.sonar.api.server.ws.WebService; | |||||
import org.sonar.api.utils.System2; | |||||
import org.sonar.core.util.Uuids; | |||||
import org.sonar.db.DbTester; | |||||
import org.sonar.db.component.ComponentDto; | |||||
import org.sonar.db.protobuf.DbFileSources; | |||||
import org.sonar.db.protobuf.DbFileSources.Test.CoveredFile; | |||||
import org.sonar.db.source.FileSourceDto; | |||||
import org.sonar.server.es.EsTester; | |||||
import org.sonar.server.exceptions.NotFoundException; | |||||
import org.sonar.server.test.index.TestIndex; | |||||
import org.sonar.server.test.index.TestIndexer; | |||||
import org.sonar.server.tester.UserSessionRule; | |||||
import org.sonar.server.ws.TestRequest; | |||||
import org.sonar.server.ws.WsActionTester; | |||||
import static java.util.Arrays.asList; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE; | |||||
import static org.sonar.api.web.UserRole.CODEVIEWER; | |||||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||||
import static org.sonar.server.test.ws.CoveredFilesAction.TEST_ID; | |||||
import static org.sonar.test.JsonAssert.assertJson; | |||||
public class CoveredFilesActionTest { | |||||
@Rule | |||||
public UserSessionRule userSession = UserSessionRule.standalone(); | |||||
@Rule | |||||
public ExpectedException expectedException = ExpectedException.none(); | |||||
@Rule | |||||
public EsTester es = EsTester.create(); | |||||
@Rule | |||||
public DbTester db = DbTester.create(); | |||||
private TestIndex testIndex = new TestIndex(es.client(), System2.INSTANCE); | |||||
private TestIndexer testIndexer = new TestIndexer(db.getDbClient(), es.client()); | |||||
private WsActionTester ws = new WsActionTester(new CoveredFilesAction(db.getDbClient(), testIndex, userSession)); | |||||
@Test | |||||
public void define_covered_files() { | |||||
WebService.Action action = ws.getDef(); | |||||
assertThat(action).isNotNull(); | |||||
assertThat(action.isInternal()).isFalse(); | |||||
assertThat(action.isPost()).isFalse(); | |||||
assertThat(action.handler()).isNotNull(); | |||||
assertThat(action.responseExampleAsString()).isNotEmpty(); | |||||
assertThat(action.params()).hasSize(3); | |||||
} | |||||
@Test | |||||
public void covered_files() { | |||||
ComponentDto project = db.components().insertPrivateProject(); | |||||
ComponentDto mainFile1 = db.components().insertComponent(newFileDto(project)); | |||||
ComponentDto mainFile2 = db.components().insertComponent(newFileDto(project)); | |||||
ComponentDto testFile = db.components().insertComponent(newFileDto(project).setQualifier(UNIT_TEST_FILE)); | |||||
userSession.addProjectPermission(CODEVIEWER, project, testFile); | |||||
DbFileSources.Test test = DbFileSources.Test.newBuilder().setUuid(Uuids.create()) | |||||
.addCoveredFile(CoveredFile.newBuilder() | |||||
.setFileUuid(mainFile1.uuid()) | |||||
.addAllCoveredLine(asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))) | |||||
.addCoveredFile(CoveredFile.newBuilder() | |||||
.setFileUuid(mainFile2.uuid()) | |||||
.addAllCoveredLine(asList(1, 2, 3))) | |||||
.build(); | |||||
insertTests(testFile, test); | |||||
TestRequest request = ws.newRequest().setParam(TEST_ID, test.getUuid()); | |||||
assertJson(request.execute().getInput()).isSimilarTo("{\n" + | |||||
" \"files\": [\n" + | |||||
" {\n" + | |||||
" \"id\": \"" + mainFile1.uuid() + "\",\n" + | |||||
" \"key\": \"" + mainFile1.getKey() + "\",\n" + | |||||
" \"longName\": \"" + mainFile1.longName() + "\",\n" + | |||||
" \"coveredLines\": 10\n" + | |||||
" },\n" + | |||||
" {\n" + | |||||
" \"id\": \"" + mainFile2.uuid() + "\",\n" + | |||||
" \"key\": \"" + mainFile2.getKey() + "\",\n" + | |||||
" \"longName\": \"" + mainFile2.longName() + "\",\n" + | |||||
" \"coveredLines\": 3\n" + | |||||
" }\n" + | |||||
" ]\n" + | |||||
"}"); | |||||
} | |||||
@Test | |||||
public void covered_files_on_branch() { | |||||
ComponentDto project = db.components().insertMainBranch(); | |||||
ComponentDto branch = db.components().insertProjectBranch(project); | |||||
ComponentDto mainFile = db.components().insertComponent(newFileDto(branch)); | |||||
ComponentDto testFile = db.components().insertComponent(newFileDto(branch).setQualifier(UNIT_TEST_FILE)); | |||||
userSession.addProjectPermission(CODEVIEWER, project, testFile); | |||||
DbFileSources.Test test = DbFileSources.Test.newBuilder().setUuid(Uuids.create()) | |||||
.addCoveredFile(CoveredFile.newBuilder() | |||||
.setFileUuid(mainFile.uuid()) | |||||
.addAllCoveredLine(asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))) | |||||
.build(); | |||||
insertTests(testFile, test); | |||||
TestRequest request = ws.newRequest().setParam(TEST_ID, test.getUuid()); | |||||
assertJson(request.execute().getInput()).isSimilarTo("{\n" + | |||||
" \"files\": [\n" + | |||||
" {\n" + | |||||
" \"id\": \"" + mainFile.uuid() + "\",\n" + | |||||
" \"key\": \"" + mainFile.getKey() + "\",\n" + | |||||
" \"branch\": \"" + mainFile.getBranch() + "\",\n" + | |||||
" \"longName\": \"" + mainFile.longName() + "\",\n" + | |||||
" \"coveredLines\": 10\n" + | |||||
" }\n" + | |||||
" ]\n" + | |||||
"}"); | |||||
} | |||||
@Test | |||||
public void fail_when_test_uuid_is_unknown() { | |||||
expectedException.expect(NotFoundException.class); | |||||
expectedException.expectMessage("Test with id 'unknown' is not found"); | |||||
ws.newRequest().setParam(TEST_ID, "unknown").execute(); | |||||
} | |||||
private void insertTests(ComponentDto testFile, DbFileSources.Test... tests) { | |||||
db.getDbClient().fileSourceDao().insert(db.getSession(), new FileSourceDto() | |||||
.setProjectUuid(testFile.projectUuid()) | |||||
.setFileUuid(testFile.uuid()) | |||||
.setTestData(asList(tests))); | |||||
db.commit(); | |||||
testIndexer.indexOnStartup(null); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.Rule; | |||||
import org.junit.Test; | |||||
import org.junit.rules.ExpectedException; | |||||
import org.sonar.api.server.ws.WebService; | |||||
import org.sonar.api.utils.System2; | |||||
import org.sonar.db.DbClient; | |||||
import org.sonar.db.DbTester; | |||||
import org.sonar.db.component.ComponentDto; | |||||
import org.sonar.db.component.ComponentTesting; | |||||
import org.sonar.db.protobuf.DbFileSources; | |||||
import org.sonar.db.source.FileSourceDto; | |||||
import org.sonar.server.component.TestComponentFinder; | |||||
import org.sonar.server.es.EsTester; | |||||
import org.sonar.server.exceptions.ForbiddenException; | |||||
import org.sonar.server.exceptions.NotFoundException; | |||||
import org.sonar.server.test.index.TestIndex; | |||||
import org.sonar.server.test.index.TestIndexer; | |||||
import org.sonar.server.tester.UserSessionRule; | |||||
import org.sonar.server.ws.TestRequest; | |||||
import org.sonar.server.ws.WsActionTester; | |||||
import org.sonarqube.ws.Tests; | |||||
import org.sonarqube.ws.Tests.ListResponse; | |||||
import static java.lang.String.format; | |||||
import static java.util.Arrays.asList; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.assertj.core.api.Assertions.tuple; | |||||
import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE; | |||||
import static org.sonar.api.web.UserRole.CODEVIEWER; | |||||
import static org.sonar.api.web.UserRole.USER; | |||||
import static org.sonar.db.component.BranchType.PULL_REQUEST; | |||||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||||
import static org.sonar.db.protobuf.DbFileSources.Test.TestStatus.OK; | |||||
import static org.sonar.server.test.db.TestTesting.newTest; | |||||
import static org.sonar.server.test.ws.ListAction.PARAM_PULL_REQUEST; | |||||
import static org.sonar.server.test.ws.ListAction.SOURCE_FILE_ID; | |||||
import static org.sonar.server.test.ws.ListAction.SOURCE_FILE_KEY; | |||||
import static org.sonar.server.test.ws.ListAction.SOURCE_FILE_LINE_NUMBER; | |||||
import static org.sonar.server.test.ws.ListAction.TEST_FILE_ID; | |||||
import static org.sonar.server.test.ws.ListAction.TEST_FILE_KEY; | |||||
import static org.sonar.server.test.ws.ListAction.TEST_ID; | |||||
public class ListActionTest { | |||||
@Rule | |||||
public ExpectedException expectedException = ExpectedException.none(); | |||||
@Rule | |||||
public EsTester es = EsTester.create(); | |||||
@Rule | |||||
public UserSessionRule userSessionRule = UserSessionRule.standalone(); | |||||
@Rule | |||||
public DbTester db = DbTester.create(); | |||||
private DbClient dbClient = db.getDbClient(); | |||||
private TestIndex testIndex = new TestIndex(es.client(), System2.INSTANCE); | |||||
private TestIndexer testIndexer = new TestIndexer(db.getDbClient(), es.client()); | |||||
private ComponentDto project; | |||||
private ComponentDto mainFile; | |||||
private ComponentDto testFile; | |||||
private WsActionTester ws = new WsActionTester(new ListAction(dbClient, testIndex, userSessionRule, TestComponentFinder.from(db))); | |||||
@Before | |||||
public void setUp() throws Exception { | |||||
project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization())); | |||||
mainFile = db.components().insertComponent(newFileDto(project)); | |||||
testFile = db.components().insertComponent(newFileDto(project).setQualifier(UNIT_TEST_FILE)); | |||||
} | |||||
@Test | |||||
public void test_definition() { | |||||
WebService.Action action = ws.getDef(); | |||||
assertThat(action).isNotNull(); | |||||
assertThat(action.isInternal()).isFalse(); | |||||
assertThat(action.isPost()).isFalse(); | |||||
assertThat(action.handler()).isNotNull(); | |||||
assertThat(action.responseExampleAsString()).isNotEmpty(); | |||||
assertThat(action.params()).hasSize(10); | |||||
assertThat(action.description()).isEqualTo("Get the list of tests either in a test file or that test a given line of source code.<br /> " + | |||||
"Requires 'Browse' permission on the file's project.<br /> " + | |||||
"One (and only one) of the following combination of parameters must be provided: " + | |||||
"<ul>" + | |||||
"<li>testId - get a specific test</li>" + | |||||
"<li>testFileId - get the tests in a test file</li>" + | |||||
"<li>testFileKey - get the tests in a test file</li>" + | |||||
"<li>sourceFileId and sourceFileLineNumber - get the tests that cover a specific line of code</li>" + | |||||
"<li>sourceFileKey and sourceFileLineNumber - get the tests that cover a specific line of code</li>" + | |||||
"</ul>"); | |||||
} | |||||
@Test | |||||
public void list_tests() { | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
DbFileSources.Test test = newTest(mainFile, 10, 11, 12, 20, 21, 25).setStatus(OK).build(); | |||||
insertTests(testFile, test); | |||||
ListResponse request = call(ws.newRequest().setParam(TEST_ID, test.getUuid())); | |||||
assertThat(request.getTestsList()).hasSize(1); | |||||
Tests.Test result = request.getTests(0); | |||||
assertThat(result.getId()).isEqualTo(test.getUuid()); | |||||
assertThat(result.getName()).isEqualTo(test.getName()); | |||||
assertThat(result.getStatus()).isEqualTo(Tests.TestStatus.OK); | |||||
assertThat(result.getFileId()).isEqualTo(testFile.uuid()); | |||||
assertThat(result.getFileKey()).isEqualTo(testFile.getDbKey()); | |||||
assertThat(result.getFileName()).isEqualTo(testFile.path()); | |||||
assertThat(result.getDurationInMs()).isEqualTo(test.getExecutionTimeMs()); | |||||
assertThat(result.getMessage()).isEqualTo(test.getMsg()); | |||||
assertThat(result.getStacktrace()).isEqualTo(test.getStacktrace()); | |||||
assertThat(result.getCoveredLines()).isEqualTo(6); | |||||
} | |||||
@Test | |||||
public void list_tests_by_test_uuid() { | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
DbFileSources.Test test1 = newTest(mainFile, 10).build(); | |||||
DbFileSources.Test test2 = newTest(mainFile, 11).build(); | |||||
insertTests(testFile, test1, test2); | |||||
ListResponse request = call(ws.newRequest() | |||||
.setParam(TEST_ID, test1.getUuid())); | |||||
assertThat(request.getTestsList()) | |||||
.extracting(Tests.Test::getId) | |||||
.containsOnly(test1.getUuid()); | |||||
} | |||||
@Test | |||||
public void list_tests_by_test_file_uuid() { | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
ComponentDto anotherTestFile = db.components().insertComponent(newFileDto(project)); | |||||
DbFileSources.Test test1 = newTest(mainFile, 10).build(); | |||||
DbFileSources.Test test2 = newTest(mainFile, 11).build(); | |||||
DbFileSources.Test test3 = newTest(mainFile, 12).build(); | |||||
insertTests(testFile, test1, test2); | |||||
insertTests(anotherTestFile, test3); | |||||
ListResponse request = call(ws.newRequest() | |||||
.setParam(TEST_FILE_ID, testFile.uuid())); | |||||
assertThat(request.getTestsList()) | |||||
.extracting(Tests.Test::getId) | |||||
.containsOnly(test1.getUuid(), test2.getUuid()); | |||||
} | |||||
@Test | |||||
public void list_tests_by_test_file_key() { | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
ComponentDto anotherTestFile = db.components().insertComponent(newFileDto(project)); | |||||
DbFileSources.Test test1 = newTest(mainFile, 10).build(); | |||||
DbFileSources.Test test2 = newTest(mainFile, 11).build(); | |||||
DbFileSources.Test test3 = newTest(mainFile, 12).build(); | |||||
insertTests(testFile, test1, test2); | |||||
insertTests(anotherTestFile, test3); | |||||
ListResponse request = call(ws.newRequest() | |||||
.setParam(TEST_FILE_KEY, testFile.getDbKey())); | |||||
assertThat(request.getTestsList()) | |||||
.extracting(Tests.Test::getId) | |||||
.containsOnly(test1.getUuid(), test2.getUuid()); | |||||
} | |||||
@Test | |||||
public void list_tests_by_test_file_key_and_branch() { | |||||
ComponentDto project = db.components().insertMainBranch(); | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
ComponentDto branch = db.components().insertProjectBranch(project); | |||||
ComponentDto mainFile = db.components().insertComponent(newFileDto(branch)); | |||||
ComponentDto testFile = db.components().insertComponent(newFileDto(branch).setQualifier(UNIT_TEST_FILE)); | |||||
DbFileSources.Test test1 = newTest(mainFile, 10).build(); | |||||
DbFileSources.Test test2 = newTest(mainFile, 11).build(); | |||||
insertTests(testFile, test1, test2); | |||||
ListResponse request = call(ws.newRequest() | |||||
.setParam(TEST_FILE_KEY, testFile.getKey()) | |||||
.setParam("branch", testFile.getBranch())); | |||||
assertThat(request.getTestsList()) | |||||
.extracting(Tests.Test::getId, Tests.Test::getFileKey, Tests.Test::getFileBranch) | |||||
.containsOnly( | |||||
tuple(test1.getUuid(), testFile.getKey(), testFile.getBranch()), | |||||
tuple(test2.getUuid(), testFile.getKey(), testFile.getBranch())); | |||||
} | |||||
@Test | |||||
public void list_tests_by_test_file_key_and_pull_request() { | |||||
ComponentDto project = db.components().insertMainBranch(); | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST)); | |||||
ComponentDto mainFile = db.components().insertComponent(newFileDto(pullRequest)); | |||||
ComponentDto testFile = db.components().insertComponent(newFileDto(pullRequest).setQualifier(UNIT_TEST_FILE)); | |||||
DbFileSources.Test test1 = newTest(mainFile, 10).build(); | |||||
DbFileSources.Test test2 = newTest(mainFile, 11).build(); | |||||
insertTests(testFile, test1, test2); | |||||
ListResponse request = call(ws.newRequest() | |||||
.setParam(TEST_FILE_KEY, testFile.getKey()) | |||||
.setParam(PARAM_PULL_REQUEST, testFile.getPullRequest())); | |||||
assertThat(request.getTestsList()) | |||||
.extracting(Tests.Test::getId, Tests.Test::getFileKey, Tests.Test::getFilePullRequest) | |||||
.containsOnly( | |||||
tuple(test1.getUuid(), testFile.getKey(), testFile.getPullRequest()), | |||||
tuple(test2.getUuid(), testFile.getKey(), testFile.getPullRequest())); | |||||
} | |||||
@Test | |||||
public void list_tests_by_source_file_uuid_and_line_number() { | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
ComponentDto anotherMainFile = db.components().insertComponent(newFileDto(project)); | |||||
DbFileSources.Test test1 = newTest(mainFile, 10, 11, 12).build(); | |||||
DbFileSources.Test test2 = newTest(mainFile, 9, 11).build(); | |||||
DbFileSources.Test test3 = newTest(mainFile, 10, 12).build(); | |||||
DbFileSources.Test test4 = newTest(anotherMainFile, 11).build(); | |||||
insertTests(testFile, test1, test2, test3, test4); | |||||
ListResponse request = call(ws.newRequest() | |||||
.setParam(SOURCE_FILE_ID, mainFile.uuid()) | |||||
.setParam(SOURCE_FILE_LINE_NUMBER, "11")); | |||||
assertThat(request.getTestsList()).extracting(Tests.Test::getId).containsOnly(test1.getUuid(), test2.getUuid()); | |||||
} | |||||
@Test | |||||
public void list_tests_by_source_file_key_and_line_number() { | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
ComponentDto anotherMainFile = db.components().insertComponent(newFileDto(project)); | |||||
DbFileSources.Test test1 = newTest(mainFile, 10, 11, 12).build(); | |||||
DbFileSources.Test test2 = newTest(mainFile, 9, 11).build(); | |||||
DbFileSources.Test test3 = newTest(mainFile, 10, 12).build(); | |||||
DbFileSources.Test test4 = newTest(anotherMainFile, 11).build(); | |||||
insertTests(testFile, test1, test2, test3, test4); | |||||
ListResponse request = call(ws.newRequest() | |||||
.setParam(SOURCE_FILE_KEY, mainFile.getDbKey()) | |||||
.setParam(SOURCE_FILE_LINE_NUMBER, "10")); | |||||
assertThat(request.getTestsList()) | |||||
.extracting(Tests.Test::getId) | |||||
.containsOnly(test1.getUuid(), test3.getUuid()); | |||||
} | |||||
@Test | |||||
public void list_tests_by_source_file_key_and_branch_and_line_number() { | |||||
ComponentDto project = db.components().insertMainBranch(); | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
ComponentDto branch = db.components().insertProjectBranch(project); | |||||
ComponentDto mainFile = db.components().insertComponent(newFileDto(branch)); | |||||
ComponentDto testFile = db.components().insertComponent(newFileDto(branch).setQualifier(UNIT_TEST_FILE)); | |||||
DbFileSources.Test test1 = newTest(mainFile, 10, 11, 12).build(); | |||||
DbFileSources.Test test2 = newTest(mainFile, 9, 11).build(); | |||||
DbFileSources.Test test3 = newTest(mainFile, 10, 12).build(); | |||||
insertTests(testFile, test1, test2, test3); | |||||
ListResponse request = call(ws.newRequest() | |||||
.setParam(SOURCE_FILE_KEY, mainFile.getKey()) | |||||
.setParam(SOURCE_FILE_LINE_NUMBER, "10") | |||||
.setParam("branch", testFile.getBranch())); | |||||
assertThat(request.getTestsList()) | |||||
.extracting(Tests.Test::getId, Tests.Test::getFileKey, Tests.Test::getFileBranch) | |||||
.containsOnly( | |||||
tuple(test1.getUuid(), testFile.getKey(), testFile.getBranch()), | |||||
tuple(test3.getUuid(), testFile.getKey(), testFile.getBranch())); | |||||
} | |||||
@Test | |||||
public void tests_are_paginated() { | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
insertTests(testFile, newTest(mainFile, 10).build(), newTest(mainFile, 11).build(), newTest(mainFile, 12).build()); | |||||
ListResponse request = call(ws.newRequest().setParam(TEST_FILE_ID, testFile.uuid())); | |||||
assertThat(request.getPaging().getPageIndex()).isEqualTo(1); | |||||
assertThat(request.getPaging().getPageSize()).isEqualTo(100); | |||||
assertThat(request.getPaging().getTotal()).isEqualTo(3); | |||||
} | |||||
@Test | |||||
public void fail_when_no_argument() { | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
expectedException.expect(IllegalArgumentException.class); | |||||
call(ws.newRequest()); | |||||
} | |||||
@Test | |||||
public void fail_when_source_file_uuid_without_line_number() { | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
expectedException.expect(IllegalArgumentException.class); | |||||
call(ws.newRequest().setParam(SOURCE_FILE_ID, mainFile.uuid())); | |||||
} | |||||
@Test | |||||
public void fail_when_not_enough_privilege_on_test_uuid() { | |||||
userSessionRule.addProjectPermission(USER, project); | |||||
DbFileSources.Test test = newTest(mainFile, 10).build(); | |||||
insertTests(testFile, test); | |||||
expectedException.expect(ForbiddenException.class); | |||||
call(ws.newRequest().setParam(TEST_ID, test.getUuid())); | |||||
} | |||||
@Test | |||||
public void fail_when_no_enough_privilege_on_test_file_id() { | |||||
userSessionRule.addProjectPermission(USER, project); | |||||
insertTests(testFile, newTest(mainFile, 10).build()); | |||||
expectedException.expect(ForbiddenException.class); | |||||
call(ws.newRequest().setParam(TEST_FILE_ID, testFile.uuid())); | |||||
} | |||||
@Test | |||||
public void fail_when_not_enough_privilege_on_test_file_key() { | |||||
userSessionRule.addProjectPermission(USER, project); | |||||
insertTests(testFile, newTest(mainFile, 10).build()); | |||||
expectedException.expect(ForbiddenException.class); | |||||
call(ws.newRequest().setParam(TEST_FILE_KEY, testFile.getDbKey())); | |||||
} | |||||
@Test | |||||
public void fail_when_not_enough_privilege_on_main_file_uuid() { | |||||
userSessionRule.addProjectPermission(USER, project); | |||||
insertTests(testFile, newTest(mainFile, 10).build()); | |||||
expectedException.expect(ForbiddenException.class); | |||||
call(ws.newRequest().setParam(SOURCE_FILE_ID, mainFile.uuid()).setParam(SOURCE_FILE_LINE_NUMBER, "10")); | |||||
} | |||||
@Test | |||||
public void fail_when_test_uuid_is_unknown() { | |||||
expectedException.expect(NotFoundException.class); | |||||
call(ws.newRequest().setParam(TEST_ID, "unknown")); | |||||
} | |||||
@Test | |||||
public void fail_when_test_file_id_is_unknown() { | |||||
expectedException.expect(NotFoundException.class); | |||||
call(ws.newRequest().setParam(TEST_FILE_ID, "unknown")); | |||||
} | |||||
@Test | |||||
public void fail_when_test_file_key_is_unknown() { | |||||
expectedException.expect(NotFoundException.class); | |||||
call(ws.newRequest().setParam(TEST_FILE_KEY, "unknown")); | |||||
} | |||||
@Test | |||||
public void fail_when_test_branch_is_unknown() { | |||||
ComponentDto project = db.components().insertMainBranch(); | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
ComponentDto branch = db.components().insertProjectBranch(project); | |||||
ComponentDto testFile = db.components().insertComponent(newFileDto(branch).setQualifier(UNIT_TEST_FILE)); | |||||
expectedException.expect(NotFoundException.class); | |||||
expectedException.expectMessage(format("Component '%s' on branch 'unknown' not found", testFile.getKey())); | |||||
call(ws.newRequest() | |||||
.setParam(TEST_FILE_KEY, testFile.getKey()) | |||||
.setParam("branch", "unknown")); | |||||
} | |||||
@Test | |||||
public void fail_when_source_file_id_is_unknown() { | |||||
expectedException.expect(NotFoundException.class); | |||||
call(ws.newRequest().setParam(SOURCE_FILE_ID, "unknown").setParam(SOURCE_FILE_LINE_NUMBER, "10")); | |||||
} | |||||
@Test | |||||
public void fail_when_source_file_key_is_unknown() { | |||||
expectedException.expect(NotFoundException.class); | |||||
call(ws.newRequest().setParam(SOURCE_FILE_KEY, "unknown").setParam(SOURCE_FILE_LINE_NUMBER, "10")); | |||||
} | |||||
@Test | |||||
public void fail_when_source_branch_is_unknown() { | |||||
ComponentDto project = db.components().insertMainBranch(); | |||||
userSessionRule.addProjectPermission(CODEVIEWER, project); | |||||
ComponentDto branch = db.components().insertProjectBranch(project); | |||||
ComponentDto mainFile = db.components().insertComponent(newFileDto(branch)); | |||||
expectedException.expect(NotFoundException.class); | |||||
expectedException.expectMessage(format("Component '%s' on branch 'unknown' not found", mainFile.getKey())); | |||||
call(ws.newRequest() | |||||
.setParam(SOURCE_FILE_KEY, mainFile.getKey()) | |||||
.setParam("branch", "unknown") | |||||
.setParam(SOURCE_FILE_LINE_NUMBER, "10")); | |||||
} | |||||
private void insertTests(ComponentDto testFile, DbFileSources.Test... tests) { | |||||
db.getDbClient().fileSourceDao().insert(db.getSession(), new FileSourceDto() | |||||
.setProjectUuid(testFile.projectUuid()) | |||||
.setFileUuid(testFile.uuid()) | |||||
.setTestData(asList(tests))); | |||||
db.commit(); | |||||
testIndexer.indexOnStartup(null); | |||||
} | |||||
private static ListResponse call(TestRequest request) { | |||||
return request | |||||
.executeProtobuf(ListResponse.class); | |||||
} | |||||
} |
{ | |||||
"files": [ | |||||
{ | |||||
"id": "FILE1", | |||||
"key": "org.foo.Bar.java", | |||||
"longName": "src/main/java/org/foo/Bar.java", | |||||
"coveredLines": 10 | |||||
}, | |||||
{ | |||||
"id": "FILE2", | |||||
"key": "org.foo.File.java", | |||||
"longName": "src/main/java/org/foo/File.java", | |||||
"coveredLines": 3 | |||||
} | |||||
] | |||||
} |
{ | |||||
"paging": { | |||||
"pageIndex": 1, | |||||
"pageSize": 100, | |||||
"total": 2 | |||||
}, | |||||
"tests": [ | |||||
{ | |||||
"id": "TEST-UUID-1", | |||||
"name": "test1", | |||||
"status": "OK", | |||||
"fileId": "ABCD", | |||||
"fileKey": "org.foo.BarTest.java", | |||||
"fileName": "src/test/java/org/foo/BarTest.java", | |||||
"durationInMs": 10, | |||||
"coveredLines": 4, | |||||
"message": "MESSAGE-1", | |||||
"stacktrace": "STACKTRACE-1" | |||||
}, | |||||
{ | |||||
"id": "TEST-UUID-2", | |||||
"name": "test2", | |||||
"status": "ERROR", | |||||
"fileId": "BCDE", | |||||
"fileKey": "org.foo.FileTest.java", | |||||
"fileName": "src/test/java/org/foo/FileTest.java", | |||||
"durationInMs": 97, | |||||
"coveredLines": 4, | |||||
"message": "MESSAGE-2", | |||||
"stacktrace": "STACKTRACE-2" | |||||
} | |||||
] | |||||
} |
{ | |||||
"tests": [ | |||||
{ | |||||
"id": "TEST-UUID-1", | |||||
"name": "test1", | |||||
"status": "OK", | |||||
"fileId": "ABCD", | |||||
"fileKey": "org.foo.BarTest.java", | |||||
"fileName": "src/test/java/org/foo/BarTest.java", | |||||
"durationInMs": 10, | |||||
"coveredLines": 4, | |||||
"message": "MESSAGE-1", | |||||
"stacktrace": "STACKTRACE-1" | |||||
} | |||||
] | |||||
} |
import com.google.common.collect.Iterators; | import com.google.common.collect.Iterators; | ||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import java.io.InputStream; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import org.apache.commons.io.FileUtils; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import org.sonar.api.batch.fs.InputComponent; | import org.sonar.api.batch.fs.InputComponent; | ||||
return null; | return null; | ||||
} | } | ||||
public ScannerReport.Test firstTestExecutionForName(InputFile testFile, String testName) { | |||||
int ref = ((DefaultInputComponent) testFile).scannerId(); | |||||
try (InputStream inputStream = FileUtils.openInputStream(getReportReader().readTests(ref))) { | |||||
ScannerReport.Test test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream); | |||||
while (test != null) { | |||||
if (test.getName().equals(testName)) { | |||||
return test; | |||||
} | |||||
test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream); | |||||
} | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
return null; | |||||
} | |||||
public ScannerReport.CoverageDetail coveragePerTestFor(InputFile testFile, String testName) { | |||||
int ref = ((DefaultInputComponent) testFile).scannerId(); | |||||
try (InputStream inputStream = FileUtils.openInputStream(getReportReader().readCoverageDetails(ref))) { | |||||
ScannerReport.CoverageDetail details = ScannerReport.CoverageDetail.parser().parseDelimitedFrom(inputStream); | |||||
while (details != null) { | |||||
if (details.getTestName().equals(testName)) { | |||||
return details; | |||||
} | |||||
details = ScannerReport.CoverageDetail.parser().parseDelimitedFrom(inputStream); | |||||
} | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
return null; | |||||
} | |||||
public List<ScannerReport.AdHocRule> adHocRules() { | public List<ScannerReport.AdHocRule> adHocRules() { | ||||
List<ScannerReport.AdHocRule> result = new ArrayList<>(); | List<ScannerReport.AdHocRule> result = new ArrayList<>(); | ||||
try (CloseableIterator<ScannerReport.AdHocRule> it = getReportReader().readAdHocRules()) { | try (CloseableIterator<ScannerReport.AdHocRule> it = getReportReader().readAdHocRules()) { |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.scanner.report; | |||||
import com.google.common.collect.Iterables; | |||||
import java.util.HashSet; | |||||
import java.util.Set; | |||||
import java.util.stream.StreamSupport; | |||||
import org.sonar.api.batch.fs.InputComponent; | |||||
import org.sonar.api.batch.fs.internal.DefaultInputComponent; | |||||
import org.sonar.api.test.CoverageBlock; | |||||
import org.sonar.api.test.MutableTestCase; | |||||
import org.sonar.api.test.MutableTestPlan; | |||||
import org.sonar.api.test.TestCase; | |||||
import org.sonar.scanner.deprecated.test.DefaultTestable; | |||||
import org.sonar.scanner.deprecated.test.TestPlanBuilder; | |||||
import org.sonar.scanner.protocol.output.ScannerReport; | |||||
import org.sonar.scanner.protocol.output.ScannerReport.CoverageDetail; | |||||
import org.sonar.scanner.protocol.output.ScannerReport.Test; | |||||
import org.sonar.scanner.protocol.output.ScannerReport.Test.TestStatus; | |||||
import org.sonar.scanner.protocol.output.ScannerReportWriter; | |||||
import org.sonar.scanner.scan.branch.BranchConfiguration; | |||||
import org.sonar.scanner.scan.filesystem.InputComponentStore; | |||||
import static java.util.stream.Collectors.toList; | |||||
public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { | |||||
private final InputComponentStore componentStore; | |||||
private final TestPlanBuilder testPlanBuilder; | |||||
private final BranchConfiguration branchConfiguration; | |||||
public TestExecutionAndCoveragePublisher(InputComponentStore componentStore, TestPlanBuilder testPlanBuilder, BranchConfiguration branchConfiguration) { | |||||
this.componentStore = componentStore; | |||||
this.testPlanBuilder = testPlanBuilder; | |||||
this.branchConfiguration = branchConfiguration; | |||||
} | |||||
@Override | |||||
public void publish(ScannerReportWriter writer) { | |||||
if (branchConfiguration.isShortOrPullRequest()) { | |||||
return; | |||||
} | |||||
final ScannerReport.Test.Builder testBuilder = ScannerReport.Test.newBuilder(); | |||||
final ScannerReport.CoverageDetail.Builder builder = ScannerReport.CoverageDetail.newBuilder(); | |||||
final ScannerReport.CoverageDetail.CoveredFile.Builder coveredBuilder = ScannerReport.CoverageDetail.CoveredFile.newBuilder(); | |||||
for (final InputComponent c : componentStore.all()) { | |||||
DefaultInputComponent component = (DefaultInputComponent) c; | |||||
final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component); | |||||
if (testPlan == null || Iterables.isEmpty(testPlan.testCases())) { | |||||
continue; | |||||
} | |||||
final Set<String> testNamesWithCoverage = new HashSet<>(); | |||||
writer.writeTests(component.scannerId(), | |||||
StreamSupport.stream(testPlan.testCases().spliterator(), false) | |||||
.map(testCase -> toProtobufTest(testBuilder, testNamesWithCoverage, testCase)) | |||||
.collect(toList())); | |||||
writer.writeCoverageDetails(component.scannerId(), testNamesWithCoverage.stream() | |||||
.map(testName -> toProtobufCoverageDetails(builder, coveredBuilder, testPlan, testName)) | |||||
.collect(toList())); | |||||
} | |||||
} | |||||
private CoverageDetail toProtobufCoverageDetails(final ScannerReport.CoverageDetail.Builder builder, final ScannerReport.CoverageDetail.CoveredFile.Builder coveredBuilder, | |||||
final MutableTestPlan testPlan, String testName) { | |||||
// Take first test with provided name | |||||
MutableTestCase testCase = testPlan.testCasesByName(testName).iterator().next(); | |||||
builder.clear(); | |||||
builder.setTestName(testName); | |||||
for (CoverageBlock block : testCase.coverageBlocks()) { | |||||
coveredBuilder.clear(); | |||||
DefaultInputComponent c = (DefaultInputComponent) componentStore.getByKey(((DefaultTestable) block.testable()).inputFile().key()); | |||||
coveredBuilder.setFileRef(c.scannerId()); | |||||
for (int line : block.lines()) { | |||||
coveredBuilder.addCoveredLine(line); | |||||
} | |||||
builder.addCoveredFile(coveredBuilder.build()); | |||||
} | |||||
return builder.build(); | |||||
} | |||||
private static Test toProtobufTest(final ScannerReport.Test.Builder testBuilder, final Set<String> testNamesWithCoverage, MutableTestCase testCase) { | |||||
testBuilder.clear(); | |||||
testBuilder.setName(testCase.name()); | |||||
if (testCase.doesCover()) { | |||||
testNamesWithCoverage.add(testCase.name()); | |||||
} | |||||
Long durationInMs = testCase.durationInMs(); | |||||
if (durationInMs != null) { | |||||
testBuilder.setDurationInMs(durationInMs.longValue()); | |||||
} | |||||
String msg = testCase.message(); | |||||
if (msg != null) { | |||||
testBuilder.setMsg(msg); | |||||
} | |||||
String stack = testCase.stackTrace(); | |||||
if (stack != null) { | |||||
testBuilder.setStacktrace(stack); | |||||
} | |||||
TestCase.Status status = testCase.status(); | |||||
if (status != null) { | |||||
testBuilder.setStatus(TestStatus.valueOf(status.name())); | |||||
} | |||||
return testBuilder.build(); | |||||
} | |||||
} |
import org.sonar.scanner.report.MetadataPublisher; | import org.sonar.scanner.report.MetadataPublisher; | ||||
import org.sonar.scanner.report.ReportPublisher; | import org.sonar.scanner.report.ReportPublisher; | ||||
import org.sonar.scanner.report.SourcePublisher; | import org.sonar.scanner.report.SourcePublisher; | ||||
import org.sonar.scanner.report.TestExecutionAndCoveragePublisher; | |||||
import org.sonar.scanner.repository.ContextPropertiesCache; | import org.sonar.scanner.repository.ContextPropertiesCache; | ||||
import org.sonar.scanner.repository.DefaultProjectRepositoriesLoader; | import org.sonar.scanner.repository.DefaultProjectRepositoriesLoader; | ||||
import org.sonar.scanner.repository.DefaultQualityProfileLoader; | import org.sonar.scanner.repository.DefaultQualityProfileLoader; | ||||
MeasuresPublisher.class, | MeasuresPublisher.class, | ||||
CoveragePublisher.class, | CoveragePublisher.class, | ||||
SourcePublisher.class, | SourcePublisher.class, | ||||
ChangedLinesPublisher.class, | |||||
TestExecutionAndCoveragePublisher.class); | |||||
ChangedLinesPublisher.class); | |||||
} | } | ||||
private void addIssueTrackingComponents() { | private void addIssueTrackingComponents() { |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.scanner.mediumtest.tests; | |||||
import com.google.common.collect.ImmutableMap; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.nio.charset.StandardCharsets; | |||||
import org.apache.commons.io.FileUtils; | |||||
import org.hamcrest.Description; | |||||
import org.hamcrest.TypeSafeMatcher; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | |||||
import org.junit.rules.ExpectedException; | |||||
import org.junit.rules.TemporaryFolder; | |||||
import org.sonar.api.batch.fs.InputFile; | |||||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||||
import org.sonar.scanner.mediumtest.AnalysisResult; | |||||
import org.sonar.xoo.XooPlugin; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
public class CoveragePerTestMediumTest { | |||||
@Rule | |||||
public TemporaryFolder temp = new TemporaryFolder(); | |||||
@Rule | |||||
public ExpectedException exception = ExpectedException.none(); | |||||
@Rule | |||||
public ScannerMediumTester tester = new ScannerMediumTester() | |||||
.registerPlugin("xoo", new XooPlugin()) | |||||
.addDefaultQProfile("xoo", "Sonar Way"); | |||||
@Test | |||||
// SONAR-6183 | |||||
public void invalidCoverage() throws IOException { | |||||
File baseDir = createTestFiles(); | |||||
File srcDir = new File(baseDir, "src"); | |||||
File coverageFile = new File(srcDir, "sample.xoo.coverage"); | |||||
FileUtils.write(coverageFile, "0:2\n", StandardCharsets.UTF_8); | |||||
exception.expect(IllegalStateException.class); | |||||
exception.expectMessage("Error processing line 1 of file"); | |||||
exception.expectCause(new TypeSafeMatcher<Throwable>() { | |||||
@Override | |||||
public void describeTo(Description description) { | |||||
// nothing to do | |||||
} | |||||
@Override | |||||
protected boolean matchesSafely(Throwable item) { | |||||
return item.getMessage().contains("Line number must be strictly positive"); | |||||
} | |||||
}); | |||||
runTask(baseDir); | |||||
} | |||||
@Test | |||||
public void coveragePerTestInReport() throws IOException { | |||||
File baseDir = createTestFiles(); | |||||
File testDir = new File(baseDir, "test"); | |||||
File xooTestExecutionFile = new File(testDir, "sampleTest.xoo.test"); | |||||
FileUtils.write(xooTestExecutionFile, "some test:4:::OK:UNIT\n" + | |||||
"another test:10:::OK:UNIT\n" + | |||||
"test without coverage:10:::OK:UNIT\n", StandardCharsets.UTF_8); | |||||
File xooCoveragePerTestFile = new File(testDir, "sampleTest.xoo.testcoverage"); | |||||
FileUtils.write(xooCoveragePerTestFile, "some test;src/sample.xoo,10,11;src/sample2.xoo,1,2\n" + | |||||
"another test;src/sample.xoo,10,20\n", StandardCharsets.UTF_8); | |||||
AnalysisResult result = runTask(baseDir); | |||||
InputFile file = result.inputFile("test/sampleTest.xoo"); | |||||
org.sonar.scanner.protocol.output.ScannerReport.CoverageDetail someTest = result.coveragePerTestFor(file, "some test"); | |||||
assertThat(someTest.getCoveredFileList()).hasSize(2); | |||||
assertThat(someTest.getCoveredFile(0).getFileRef()).isGreaterThan(0); | |||||
assertThat(someTest.getCoveredFile(0).getCoveredLineList()).containsExactly(10, 11); | |||||
assertThat(someTest.getCoveredFile(1).getFileRef()).isGreaterThan(0); | |||||
assertThat(someTest.getCoveredFile(1).getCoveredLineList()).containsExactly(1, 2); | |||||
org.sonar.scanner.protocol.output.ScannerReport.CoverageDetail anotherTest = result.coveragePerTestFor(file, "another test"); | |||||
assertThat(anotherTest.getCoveredFileList()).hasSize(1); | |||||
assertThat(anotherTest.getCoveredFile(0).getFileRef()).isGreaterThan(0); | |||||
assertThat(anotherTest.getCoveredFile(0).getCoveredLineList()).containsExactly(10, 20); | |||||
} | |||||
private AnalysisResult runTask(File baseDir) { | |||||
return tester.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.put("sonar.task", "scan") | |||||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||||
.put("sonar.projectKey", "com.foo.project") | |||||
.put("sonar.projectName", "Foo Project") | |||||
.put("sonar.projectVersion", "1.0-SNAPSHOT") | |||||
.put("sonar.projectDescription", "Description of Foo Project") | |||||
.put("sonar.sources", "src") | |||||
.put("sonar.tests", "test") | |||||
.build()) | |||||
.execute(); | |||||
} | |||||
private File createTestFiles() throws IOException { | |||||
File baseDir = temp.getRoot(); | |||||
File srcDir = new File(baseDir, "src"); | |||||
srcDir.mkdir(); | |||||
File testDir = new File(baseDir, "test"); | |||||
testDir.mkdir(); | |||||
File xooFile = new File(srcDir, "sample.xoo"); | |||||
FileUtils.write(xooFile, "foo", StandardCharsets.UTF_8); | |||||
File xooFile2 = new File(srcDir, "sample2.xoo"); | |||||
FileUtils.write(xooFile2, "foo", StandardCharsets.UTF_8); | |||||
File xooTestFile = new File(testDir, "sampleTest.xoo"); | |||||
FileUtils.write(xooTestFile, "failure\nerror\nok\nskipped", StandardCharsets.UTF_8); | |||||
File xooTestFile2 = new File(testDir, "sample2Test.xoo"); | |||||
FileUtils.write(xooTestFile2, "test file tests", StandardCharsets.UTF_8); | |||||
return baseDir; | |||||
} | |||||
} |
*/ | */ | ||||
package org.sonar.scanner.mediumtest.tests; | package org.sonar.scanner.mediumtest.tests; | ||||
import com.google.common.collect.ImmutableMap; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import org.apache.commons.io.FileUtils; | |||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.rules.TemporaryFolder; | |||||
import org.sonar.api.batch.fs.InputFile; | import org.sonar.api.batch.fs.InputFile; | ||||
import org.sonar.api.measures.CoreMetrics; | import org.sonar.api.measures.CoreMetrics; | ||||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||||
import org.sonar.scanner.mediumtest.AnalysisResult; | import org.sonar.scanner.mediumtest.AnalysisResult; | ||||
import org.sonar.scanner.protocol.output.ScannerReport; | |||||
import org.sonar.scanner.protocol.output.ScannerReport.Test.TestStatus; | |||||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||||
import org.sonar.xoo.XooPlugin; | import org.sonar.xoo.XooPlugin; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
public class GenericTestExecutionMediumTest { | public class GenericTestExecutionMediumTest { | ||||
private final List<String> logs = new ArrayList<>(); | private final List<String> logs = new ArrayList<>(); | ||||
@Rule | |||||
public TemporaryFolder temp = new TemporaryFolder(); | |||||
@Rule | @Rule | ||||
public ScannerMediumTester tester = new ScannerMediumTester() | public ScannerMediumTester tester = new ScannerMediumTester() | ||||
.registerPlugin("xoo", new XooPlugin()) | .registerPlugin("xoo", new XooPlugin()) | ||||
.addDefaultQProfile("xoo", "Sonar Way"); | .addDefaultQProfile("xoo", "Sonar Way"); | ||||
@Test | @Test | ||||
public void unitTests() throws IOException { | |||||
File baseDir = temp.getRoot(); | |||||
File srcDir = new File(baseDir, "src"); | |||||
srcDir.mkdir(); | |||||
File testDir = new File(baseDir, "test"); | |||||
testDir.mkdir(); | |||||
File xooFile = new File(srcDir, "sample.xoo"); | |||||
FileUtils.write(xooFile, "foo", StandardCharsets.UTF_8); | |||||
File xooTestFile = new File(testDir, "sampleTest.xoo"); | |||||
FileUtils.write(xooTestFile, "failure\nerror\nok\nskipped", StandardCharsets.UTF_8); | |||||
File xooTestExecutionFile = new File(testDir, "sampleTest.xoo.test"); | |||||
FileUtils.write(xooTestExecutionFile, "skipped::::SKIPPED:UNIT\n" + | |||||
"failure:2:Failure::FAILURE:UNIT\n" + | |||||
"error:2:Error:The stack:ERROR:UNIT\n" + | |||||
"success:4:::OK:INTEGRATION", StandardCharsets.UTF_8); | |||||
AnalysisResult result = tester | |||||
.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.put("sonar.task", "scan") | |||||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||||
.put("sonar.projectKey", "com.foo.project") | |||||
.put("sonar.projectName", "Foo Project") | |||||
.put("sonar.projectVersion", "1.0-SNAPSHOT") | |||||
.put("sonar.projectDescription", "Description of Foo Project") | |||||
.put("sonar.sources", "src") | |||||
.put("sonar.tests", "test") | |||||
.build()) | |||||
.execute(); | |||||
InputFile file = result.inputFile("test/sampleTest.xoo"); | |||||
org.sonar.scanner.protocol.output.ScannerReport.Test success = result.firstTestExecutionForName(file, "success"); | |||||
assertThat(success.getDurationInMs()).isEqualTo(4); | |||||
assertThat(success.getStatus()).isEqualTo(TestStatus.OK); | |||||
org.sonar.scanner.protocol.output.ScannerReport.Test error = result.firstTestExecutionForName(file, "error"); | |||||
assertThat(error.getDurationInMs()).isEqualTo(2); | |||||
assertThat(error.getStatus()).isEqualTo(TestStatus.ERROR); | |||||
assertThat(error.getMsg()).isEqualTo("Error"); | |||||
assertThat(error.getStacktrace()).isEqualTo("The stack"); | |||||
} | |||||
@Test | |||||
public void singleReport() throws IOException { | |||||
public void singleReport() { | |||||
File projectDir = new File("test-resources/mediumtest/xoo/sample-generic-test-exec"); | File projectDir = new File("test-resources/mediumtest/xoo/sample-generic-test-exec"); | ||||
.execute(); | .execute(); | ||||
InputFile testFile = result.inputFile("testx/ClassOneTest.xoo"); | InputFile testFile = result.inputFile("testx/ClassOneTest.xoo"); | ||||
ScannerReport.Test success = result.firstTestExecutionForName(testFile, "test1"); | |||||
assertThat(success.getDurationInMs()).isEqualTo(5); | |||||
assertThat(success.getStatus()).isEqualTo(TestStatus.OK); | |||||
ScannerReport.Test skipped = result.firstTestExecutionForName(testFile, "test2"); | |||||
assertThat(skipped.getDurationInMs()).isEqualTo(500); | |||||
assertThat(skipped.getStatus()).isEqualTo(TestStatus.SKIPPED); | |||||
assertThat(skipped.getMsg()).isEqualTo("short message"); | |||||
assertThat(skipped.getStacktrace()).isEqualTo("other"); | |||||
ScannerReport.Test failed = result.firstTestExecutionForName(testFile, "test3"); | |||||
assertThat(failed.getDurationInMs()).isEqualTo(100); | |||||
assertThat(failed.getStatus()).isEqualTo(TestStatus.FAILURE); | |||||
assertThat(failed.getMsg()).isEqualTo("short"); | |||||
assertThat(failed.getStacktrace()).isEqualTo("stacktrace"); | |||||
ScannerReport.Test error = result.firstTestExecutionForName(testFile, "test4"); | |||||
assertThat(error.getDurationInMs()).isEqualTo(500); | |||||
assertThat(error.getStatus()).isEqualTo(TestStatus.ERROR); | |||||
assertThat(error.getMsg()).isEqualTo("short"); | |||||
assertThat(error.getStacktrace()).isEqualTo("stacktrace"); | |||||
assertThat(result.allMeasures().get(testFile.key())).extracting("metricKey", "intValue.value", "longValue.value") | assertThat(result.allMeasures().get(testFile.key())).extracting("metricKey", "intValue.value", "longValue.value") | ||||
.containsOnly( | .containsOnly( | ||||
tuple(CoreMetrics.TESTS_KEY, 3, 0L), | tuple(CoreMetrics.TESTS_KEY, 3, 0L), | ||||
tuple(CoreMetrics.TEST_ERRORS_KEY, 1, 0L), | tuple(CoreMetrics.TEST_ERRORS_KEY, 1, 0L), | ||||
tuple(CoreMetrics.TEST_EXECUTION_TIME_KEY, 0, 1105L), | tuple(CoreMetrics.TEST_EXECUTION_TIME_KEY, 0, 1105L), | ||||
tuple(CoreMetrics.TEST_FAILURES_KEY, 1, 0L)); | tuple(CoreMetrics.TEST_FAILURES_KEY, 1, 0L)); | ||||
assertThat(logs).noneMatch(l -> l.contains("Please use 'sonar.testExecutionReportPaths'")); | assertThat(logs).noneMatch(l -> l.contains("Please use 'sonar.testExecutionReportPaths'")); | ||||
} | } | ||||
@Test | @Test | ||||
public void twoReports() throws IOException { | |||||
public void twoReports() { | |||||
File projectDir = new File("test-resources/mediumtest/xoo/sample-generic-test-exec"); | File projectDir = new File("test-resources/mediumtest/xoo/sample-generic-test-exec"); | ||||
.execute(); | .execute(); | ||||
InputFile testFile = result.inputFile("testx/ClassOneTest.xoo"); | InputFile testFile = result.inputFile("testx/ClassOneTest.xoo"); | ||||
ScannerReport.Test success = result.firstTestExecutionForName(testFile, "test1"); | |||||
assertThat(success.getDurationInMs()).isEqualTo(5); | |||||
assertThat(success.getStatus()).isEqualTo(TestStatus.OK); | |||||
ScannerReport.Test success2 = result.firstTestExecutionForName(testFile, "test1b"); | |||||
assertThat(success2.getDurationInMs()).isEqualTo(5); | |||||
assertThat(success2.getStatus()).isEqualTo(TestStatus.OK); | |||||
ScannerReport.Test skipped = result.firstTestExecutionForName(testFile, "test2"); | |||||
assertThat(skipped.getDurationInMs()).isEqualTo(500); | |||||
assertThat(skipped.getStatus()).isEqualTo(TestStatus.SKIPPED); | |||||
assertThat(skipped.getMsg()).isEqualTo("short message"); | |||||
assertThat(skipped.getStacktrace()).isEqualTo("other"); | |||||
ScannerReport.Test failed = result.firstTestExecutionForName(testFile, "test3"); | |||||
assertThat(failed.getDurationInMs()).isEqualTo(100); | |||||
assertThat(failed.getStatus()).isEqualTo(TestStatus.FAILURE); | |||||
assertThat(failed.getMsg()).isEqualTo("short"); | |||||
assertThat(failed.getStacktrace()).isEqualTo("stacktrace"); | |||||
ScannerReport.Test error = result.firstTestExecutionForName(testFile, "test4"); | |||||
assertThat(error.getDurationInMs()).isEqualTo(500); | |||||
assertThat(error.getStatus()).isEqualTo(TestStatus.ERROR); | |||||
assertThat(error.getMsg()).isEqualTo("short"); | |||||
assertThat(error.getStacktrace()).isEqualTo("stacktrace"); | |||||
assertThat(result.allMeasures().get(testFile.key())).extracting("metricKey", "intValue.value", "longValue.value") | assertThat(result.allMeasures().get(testFile.key())).extracting("metricKey", "intValue.value", "longValue.value") | ||||
.containsOnly( | .containsOnly( | ||||
tuple(CoreMetrics.TESTS_KEY, 4, 0L), | tuple(CoreMetrics.TESTS_KEY, 4, 0L), | ||||
tuple(CoreMetrics.TEST_ERRORS_KEY, 1, 0L), | tuple(CoreMetrics.TEST_ERRORS_KEY, 1, 0L), | ||||
tuple(CoreMetrics.TEST_EXECUTION_TIME_KEY, 0, 1610L), | tuple(CoreMetrics.TEST_EXECUTION_TIME_KEY, 0, 1610L), | ||||
tuple(CoreMetrics.TEST_FAILURES_KEY, 1, 0L)); | tuple(CoreMetrics.TEST_FAILURES_KEY, 1, 0L)); | ||||
assertThat(logs).noneMatch(l -> l.contains("Please use 'sonar.testExecutionReportPaths'")); | assertThat(logs).noneMatch(l -> l.contains("Please use 'sonar.testExecutionReportPaths'")); | ||||
} | } | ||||
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.scanner.mediumtest.tests; | |||||
import com.google.common.collect.ImmutableMap; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import org.apache.commons.io.FileUtils; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | |||||
import org.junit.rules.TemporaryFolder; | |||||
import org.sonar.api.batch.fs.InputFile; | |||||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||||
import org.sonar.scanner.mediumtest.AnalysisResult; | |||||
import org.sonar.scanner.protocol.output.ScannerReport.Test.TestStatus; | |||||
import org.sonar.xoo.XooPlugin; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
public class TestExecutionMediumTest { | |||||
@org.junit.Rule | |||||
public TemporaryFolder temp = new TemporaryFolder(); | |||||
@Rule | |||||
public ScannerMediumTester tester = new ScannerMediumTester() | |||||
.registerPlugin("xoo", new XooPlugin()) | |||||
.addDefaultQProfile("xoo", "Sonar Way"); | |||||
@Test | |||||
public void unitTests() throws IOException { | |||||
File baseDir = temp.getRoot(); | |||||
File srcDir = new File(baseDir, "src"); | |||||
srcDir.mkdir(); | |||||
File testDir = new File(baseDir, "test"); | |||||
testDir.mkdir(); | |||||
File xooFile = new File(srcDir, "sample.xoo"); | |||||
FileUtils.write(xooFile, "foo"); | |||||
File xooTestFile = new File(testDir, "sampleTest.xoo"); | |||||
FileUtils.write(xooTestFile, "failure\nerror\nok\nskipped"); | |||||
File xooTestExecutionFile = new File(testDir, "sampleTest.xoo.test"); | |||||
FileUtils.write(xooTestExecutionFile, "skipped::::SKIPPED:UNIT\n" + | |||||
"failure:2:Failure::FAILURE:UNIT\n" + | |||||
"error:2:Error:The stack:ERROR:UNIT\n" + | |||||
"success:4:::OK:INTEGRATION"); | |||||
AnalysisResult result = tester.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.put("sonar.task", "scan") | |||||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||||
.put("sonar.projectKey", "com.foo.project") | |||||
.put("sonar.projectName", "Foo Project") | |||||
.put("sonar.projectVersion", "1.0-SNAPSHOT") | |||||
.put("sonar.projectDescription", "Description of Foo Project") | |||||
.put("sonar.sources", "src") | |||||
.put("sonar.tests", "test") | |||||
.build()) | |||||
.execute(); | |||||
InputFile file = result.inputFile("test/sampleTest.xoo"); | |||||
org.sonar.scanner.protocol.output.ScannerReport.Test success = result.firstTestExecutionForName(file, "success"); | |||||
assertThat(success.getDurationInMs()).isEqualTo(4); | |||||
assertThat(success.getStatus()).isEqualTo(TestStatus.OK); | |||||
org.sonar.scanner.protocol.output.ScannerReport.Test error = result.firstTestExecutionForName(file, "error"); | |||||
assertThat(error.getDurationInMs()).isEqualTo(2); | |||||
assertThat(error.getStatus()).isEqualTo(TestStatus.ERROR); | |||||
assertThat(error.getMsg()).isEqualTo("Error"); | |||||
assertThat(error.getStacktrace()).isEqualTo("The stack"); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.scanner.report; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | |||||
import org.junit.rules.TemporaryFolder; | |||||
import org.sonar.scanner.protocol.output.ScannerReportWriter; | |||||
import org.sonar.scanner.scan.branch.BranchConfiguration; | |||||
import org.sonar.scanner.scan.filesystem.InputComponentStore; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.verifyZeroInteractions; | |||||
import static org.mockito.Mockito.when; | |||||
public class TestExecutionAndCoveragePublisherTest { | |||||
@Rule | |||||
public TemporaryFolder temp = new TemporaryFolder(); | |||||
@Test | |||||
public void do_nothing_for_short_living_branches() throws IOException { | |||||
BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); | |||||
when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); | |||||
InputComponentStore componentStore = mock(InputComponentStore.class); | |||||
TestExecutionAndCoveragePublisher publisher = new TestExecutionAndCoveragePublisher(componentStore, null, branchConfiguration); | |||||
File outputDir = temp.newFolder(); | |||||
ScannerReportWriter writer = new ScannerReportWriter(outputDir); | |||||
publisher.publish(writer); | |||||
verifyZeroInteractions(componentStore); | |||||
} | |||||
@Test | |||||
public void do_nothing_for_pull_requests() throws IOException { | |||||
BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); | |||||
when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); | |||||
InputComponentStore componentStore = mock(InputComponentStore.class); | |||||
TestExecutionAndCoveragePublisher publisher = new TestExecutionAndCoveragePublisher(componentStore, null, branchConfiguration); | |||||
File outputDir = temp.newFolder(); | |||||
ScannerReportWriter writer = new ScannerReportWriter(outputDir); | |||||
publisher.publish(writer); | |||||
verifyZeroInteractions(componentStore); | |||||
} | |||||
} |
package org.sonar.scanner.protocol.output; | package org.sonar.scanner.protocol.output; | ||||
import java.io.File; | import java.io.File; | ||||
import javax.annotation.concurrent.Immutable; | import javax.annotation.concurrent.Immutable; | ||||
/** | /** | ||||
CHANGESETS("changesets-", Domain.PB), | CHANGESETS("changesets-", Domain.PB), | ||||
SYMBOLS("symbols-", Domain.PB), | SYMBOLS("symbols-", Domain.PB), | ||||
COVERAGES("coverages-", Domain.PB), | COVERAGES("coverages-", Domain.PB), | ||||
TESTS("tests-", Domain.PB), | |||||
COVERAGE_DETAILS("coverage-details-", Domain.PB), | |||||
SOURCE("source-", ".txt"), | SOURCE("source-", ".txt"), | ||||
SGNIFICANT_CODE("sgnificant-code-", Domain.PB), | SGNIFICANT_CODE("sgnificant-code-", Domain.PB), | ||||
CHANGED_LINES("changed-lines-", Domain.PB); | CHANGED_LINES("changed-lines-", Domain.PB); |
return null; | return null; | ||||
} | } | ||||
@CheckForNull | |||||
public File readTests(int testFileRef) { | |||||
File file = fileStructure.fileFor(FileStructure.Domain.TESTS, testFileRef); | |||||
if (fileExists(file)) { | |||||
return file; | |||||
} | |||||
return null; | |||||
} | |||||
@CheckForNull | |||||
public File readCoverageDetails(int testFileRef) { | |||||
File file = fileStructure.fileFor(FileStructure.Domain.COVERAGE_DETAILS, testFileRef); | |||||
if (fileExists(file)) { | |||||
return file; | |||||
} | |||||
return null; | |||||
} | |||||
public CloseableIterator<ScannerReport.ContextProperty> readContextProperties() { | public CloseableIterator<ScannerReport.ContextProperty> readContextProperties() { | ||||
File file = fileStructure.contextProperties(); | File file = fileStructure.contextProperties(); | ||||
if (!fileExists(file)) { | if (!fileExists(file)) { |
return file; | return file; | ||||
} | } | ||||
public File writeTests(int componentRef, Iterable<ScannerReport.Test> tests) { | |||||
File file = fileStructure.fileFor(FileStructure.Domain.TESTS, componentRef); | |||||
Protobuf.writeStream(tests, file, false); | |||||
return file; | |||||
} | |||||
public File writeCoverageDetails(int componentRef, Iterable<ScannerReport.CoverageDetail> tests) { | |||||
File file = fileStructure.fileFor(FileStructure.Domain.COVERAGE_DETAILS, componentRef); | |||||
Protobuf.writeStream(tests, file, false); | |||||
return file; | |||||
} | |||||
public File writeContextProperties(Iterable<ScannerReport.ContextProperty> properties) { | public File writeContextProperties(Iterable<ScannerReport.ContextProperty> properties) { | ||||
File file = fileStructure.contextProperties(); | File file = fileStructure.contextProperties(); | ||||
Protobuf.writeStream(properties, file, false); | Protobuf.writeStream(properties, file, false); |
import java.awt.EventQueue; | import java.awt.EventQueue; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | |||||
import java.io.PrintWriter; | import java.io.PrintWriter; | ||||
import java.io.StringWriter; | import java.io.StringWriter; | ||||
import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||
updateSymbols(component); | updateSymbols(component); | ||||
updateSource(component); | updateSource(component); | ||||
updateCoverage(component); | updateCoverage(component); | ||||
updateTests(component); | |||||
updateDuplications(component); | updateDuplications(component); | ||||
updateIssues(component); | updateIssues(component); | ||||
updateExternalIssues(component); | updateExternalIssues(component); | ||||
} | } | ||||
} | } | ||||
private void updateTests(Component component) { | |||||
testsEditor.setText(""); | |||||
File tests = reader.readTests(component.getRef()); | |||||
if (tests == null) { | |||||
return; | |||||
} | |||||
try (InputStream inputStream = FileUtils.openInputStream(tests)) { | |||||
ScannerReport.Test test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream); | |||||
while (test != null) { | |||||
testsEditor.getDocument().insertString(testsEditor.getDocument().getEndPosition().getOffset(), test + "\n", null); | |||||
test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream); | |||||
} | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
private void updateSource(Component component) { | private void updateSource(Component component) { | ||||
File sourceFile = reader.getFileStructure().fileFor(Domain.SOURCE, component.getRef()); | File sourceFile = reader.getFileStructure().fileFor(Domain.SOURCE, component.getRef()); | ||||
sourceEditor.setText(""); | sourceEditor.setText(""); |
} | } | ||||
} | } | ||||
message Test { | |||||
string name = 1; | |||||
TestStatus status = 2; | |||||
int64 duration_in_ms = 3; | |||||
string stacktrace = 4; | |||||
string msg = 5; | |||||
enum TestStatus { | |||||
UNSET = 0; | |||||
OK = 1; | |||||
FAILURE = 2; | |||||
ERROR = 3; | |||||
SKIPPED = 4; | |||||
} | |||||
} | |||||
message CoverageDetail { | |||||
string test_name = 1; | |||||
repeated CoveredFile covered_file = 2; | |||||
message CoveredFile { | |||||
int32 file_ref = 1; | |||||
repeated int32 covered_line = 2 [packed = true]; | |||||
} | |||||
} | |||||
message AnalysisWarning { | message AnalysisWarning { | ||||
string text = 1; | string text = 1; | ||||
int64 timestamp = 2; | int64 timestamp = 2; |
import org.sonar.core.util.CloseableIterator; | import org.sonar.core.util.CloseableIterator; | ||||
import org.sonar.scanner.protocol.output.ScannerReport.Measure.StringValue; | import org.sonar.scanner.protocol.output.ScannerReport.Measure.StringValue; | ||||
import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType; | import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType; | ||||
import org.sonar.scanner.protocol.output.ScannerReport.Test.TestStatus; | |||||
import static java.util.Arrays.asList; | import static java.util.Arrays.asList; | ||||
import static java.util.Collections.singletonList; | import static java.util.Collections.singletonList; | ||||
@Rule | @Rule | ||||
public TemporaryFolder temp = new TemporaryFolder(); | public TemporaryFolder temp = new TemporaryFolder(); | ||||
File dir; | |||||
private File dir; | |||||
ScannerReportReader underTest; | |||||
private ScannerReportReader underTest; | |||||
@Before | @Before | ||||
public void setUp() throws Exception { | public void setUp() throws Exception { | ||||
} | } | ||||
@Test | @Test | ||||
public void read_syntax_highlighting() throws Exception { | |||||
public void read_syntax_highlighting() { | |||||
ScannerReportWriter writer = new ScannerReportWriter(dir); | ScannerReportWriter writer = new ScannerReportWriter(dir); | ||||
writer.writeMetadata(ScannerReport.Metadata.newBuilder() | writer.writeMetadata(ScannerReport.Metadata.newBuilder() | ||||
.setRootComponentRef(1) | .setRootComponentRef(1) | ||||
} | } | ||||
@Test | @Test | ||||
public void read_coverage() throws Exception { | |||||
public void read_coverage() { | |||||
ScannerReportWriter writer = new ScannerReportWriter(dir); | ScannerReportWriter writer = new ScannerReportWriter(dir); | ||||
writer.writeMetadata(ScannerReport.Metadata.newBuilder() | writer.writeMetadata(ScannerReport.Metadata.newBuilder() | ||||
.setRootComponentRef(1) | .setRootComponentRef(1) | ||||
assertThat(sourceFile).isEqualTo(file); | assertThat(sourceFile).isEqualTo(file); | ||||
} | } | ||||
@Test | |||||
public void read_tests() throws Exception { | |||||
ScannerReportWriter writer = new ScannerReportWriter(dir); | |||||
writer.writeTests(1, asList( | |||||
ScannerReport.Test.newBuilder() | |||||
.setDurationInMs(60_000) | |||||
.setStacktrace("stacktrace") | |||||
.setMsg("message") | |||||
.setStatus(TestStatus.OK) | |||||
.build())); | |||||
try (InputStream inputStream = FileUtils.openInputStream(underTest.readTests(1))) { | |||||
ScannerReport.Test testResult = ScannerReport.Test.parser().parseDelimitedFrom(inputStream); | |||||
assertThat(testResult.getDurationInMs()).isEqualTo(60_000); | |||||
assertThat(testResult.getStacktrace()).isEqualTo("stacktrace"); | |||||
assertThat(testResult.getMsg()).isEqualTo("message"); | |||||
assertThat(testResult.getStatus()).isEqualTo(TestStatus.OK); | |||||
} | |||||
} | |||||
@Test | |||||
public void null_if_no_test_found() { | |||||
assertThat(underTest.readTests(UNKNOWN_COMPONENT_REF)).isNull(); | |||||
} | |||||
@Test | |||||
public void read_coverage_details() throws Exception { | |||||
ScannerReportWriter writer = new ScannerReportWriter(dir); | |||||
writer.writeCoverageDetails(1, asList( | |||||
ScannerReport.CoverageDetail.newBuilder() | |||||
.setTestName("test-name") | |||||
.addCoveredFile(ScannerReport.CoverageDetail.CoveredFile.newBuilder() | |||||
.addAllCoveredLine(asList(1, 2, 3, 5, 7)) | |||||
.setFileRef(2)) | |||||
.build())); | |||||
try (InputStream inputStream = FileUtils.openInputStream(underTest.readCoverageDetails(1))) { | |||||
ScannerReport.CoverageDetail coverageDetail = ScannerReport.CoverageDetail.parser().parseDelimitedFrom(inputStream); | |||||
assertThat(coverageDetail.getTestName()).isEqualTo("test-name"); | |||||
assertThat(coverageDetail.getCoveredFile(0).getFileRef()).isEqualTo(2); | |||||
assertThat(coverageDetail.getCoveredFile(0).getCoveredLineList()).containsExactly(1, 2, 3, 5, 7); | |||||
} | |||||
} | |||||
@Test | |||||
public void null_if_no_coverage_detail_found() { | |||||
assertThat(underTest.readCoverageDetails(UNKNOWN_COMPONENT_REF)).isNull(); | |||||
} | |||||
@Test | @Test | ||||
public void read_file_source() throws Exception { | public void read_file_source() throws Exception { | ||||
ScannerReportWriter writer = new ScannerReportWriter(dir); | ScannerReportWriter writer = new ScannerReportWriter(dir); | ||||
} | } | ||||
@Test | @Test | ||||
public void return_null_when_no_file_source() throws Exception { | |||||
public void return_null_when_no_file_source() { | |||||
assertThat(underTest.readFileSource(UNKNOWN_COMPONENT_REF)).isNull(); | assertThat(underTest.readFileSource(UNKNOWN_COMPONENT_REF)).isNull(); | ||||
} | } | ||||
} | } |
assertThat(underTest.hasComponentData(FileStructure.Domain.COVERAGES, 1)).isTrue(); | assertThat(underTest.hasComponentData(FileStructure.Domain.COVERAGES, 1)).isTrue(); | ||||
} | } | ||||
@Test | |||||
public void write_tests() { | |||||
assertThat(underTest.hasComponentData(FileStructure.Domain.TESTS, 1)).isFalse(); | |||||
underTest.writeTests(1, asList( | |||||
ScannerReport.Test.getDefaultInstance())); | |||||
assertThat(underTest.hasComponentData(FileStructure.Domain.TESTS, 1)).isTrue(); | |||||
} | |||||
@Test | |||||
public void write_coverage_details() { | |||||
assertThat(underTest.hasComponentData(FileStructure.Domain.COVERAGE_DETAILS, 1)).isFalse(); | |||||
underTest.writeCoverageDetails(1, asList( | |||||
ScannerReport.CoverageDetail.getDefaultInstance())); | |||||
assertThat(underTest.hasComponentData(FileStructure.Domain.COVERAGE_DETAILS, 1)).isTrue(); | |||||
} | |||||
} | } |
import org.sonarqube.ws.client.sources.SourcesService; | import org.sonarqube.ws.client.sources.SourcesService; | ||||
import org.sonarqube.ws.client.support.SupportService; | import org.sonarqube.ws.client.support.SupportService; | ||||
import org.sonarqube.ws.client.system.SystemService; | import org.sonarqube.ws.client.system.SystemService; | ||||
import org.sonarqube.ws.client.tests.TestsService; | |||||
import org.sonarqube.ws.client.timemachine.TimemachineService; | import org.sonarqube.ws.client.timemachine.TimemachineService; | ||||
import org.sonarqube.ws.client.updatecenter.UpdatecenterService; | import org.sonarqube.ws.client.updatecenter.UpdatecenterService; | ||||
import org.sonarqube.ws.client.usergroups.UserGroupsService; | import org.sonarqube.ws.client.usergroups.UserGroupsService; | ||||
private final SourcesService sourcesService; | private final SourcesService sourcesService; | ||||
private final SupportService supportService; | private final SupportService supportService; | ||||
private final SystemService systemService; | private final SystemService systemService; | ||||
private final TestsService testsService; | |||||
private final TimemachineService timemachineService; | private final TimemachineService timemachineService; | ||||
private final UpdatecenterService updatecenterService; | private final UpdatecenterService updatecenterService; | ||||
private final UserGroupsService userGroupsService; | private final UserGroupsService userGroupsService; | ||||
this.sourcesService = new SourcesService(wsConnector); | this.sourcesService = new SourcesService(wsConnector); | ||||
this.supportService = new SupportService(wsConnector); | this.supportService = new SupportService(wsConnector); | ||||
this.systemService = new SystemService(wsConnector); | this.systemService = new SystemService(wsConnector); | ||||
this.testsService = new TestsService(wsConnector); | |||||
this.timemachineService = new TimemachineService(wsConnector); | this.timemachineService = new TimemachineService(wsConnector); | ||||
this.updatecenterService = new UpdatecenterService(wsConnector); | this.updatecenterService = new UpdatecenterService(wsConnector); | ||||
this.userGroupsService = new UserGroupsService(wsConnector); | this.userGroupsService = new UserGroupsService(wsConnector); | ||||
return systemService; | return systemService; | ||||
} | } | ||||
@Override | |||||
public TestsService tests() { | |||||
return testsService; | |||||
} | |||||
@Override | @Override | ||||
public TimemachineService timemachine() { | public TimemachineService timemachine() { | ||||
return timemachineService; | return timemachineService; |
import org.sonarqube.ws.client.sources.SourcesService; | import org.sonarqube.ws.client.sources.SourcesService; | ||||
import org.sonarqube.ws.client.support.SupportService; | import org.sonarqube.ws.client.support.SupportService; | ||||
import org.sonarqube.ws.client.system.SystemService; | import org.sonarqube.ws.client.system.SystemService; | ||||
import org.sonarqube.ws.client.tests.TestsService; | |||||
import org.sonarqube.ws.client.timemachine.TimemachineService; | import org.sonarqube.ws.client.timemachine.TimemachineService; | ||||
import org.sonarqube.ws.client.updatecenter.UpdatecenterService; | import org.sonarqube.ws.client.updatecenter.UpdatecenterService; | ||||
import org.sonarqube.ws.client.usergroups.UserGroupsService; | import org.sonarqube.ws.client.usergroups.UserGroupsService; | ||||
SystemService system(); | SystemService system(); | ||||
TestsService tests(); | |||||
TimemachineService timemachine(); | TimemachineService timemachine(); | ||||
UpdatecenterService updatecenter(); | UpdatecenterService updatecenter(); |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.sonarqube.ws.client.tests; | |||||
import java.util.List; | |||||
import javax.annotation.Generated; | |||||
/** | |||||
* This is part of the internal API. | |||||
* This is a POST request. | |||||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/tests/covered_files">Further information about this action online (including a response example)</a> | |||||
* @since 4.4 | |||||
*/ | |||||
@Generated("sonar-ws-generator") | |||||
public class CoveredFilesRequest { | |||||
private String p; | |||||
private String ps; | |||||
private String testId; | |||||
/** | |||||
* Example value: "42" | |||||
*/ | |||||
public CoveredFilesRequest setP(String p) { | |||||
this.p = p; | |||||
return this; | |||||
} | |||||
public String getP() { | |||||
return p; | |||||
} | |||||
/** | |||||
* Example value: "20" | |||||
*/ | |||||
public CoveredFilesRequest setPs(String ps) { | |||||
this.ps = ps; | |||||
return this; | |||||
} | |||||
public String getPs() { | |||||
return ps; | |||||
} | |||||
/** | |||||
* This is a mandatory parameter. | |||||
* Example value: "AU-Tpxb--iU5OvuD2FLy" | |||||
*/ | |||||
public CoveredFilesRequest setTestId(String testId) { | |||||
this.testId = testId; | |||||
return this; | |||||
} | |||||
public String getTestId() { | |||||
return testId; | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.sonarqube.ws.client.tests; | |||||
import java.util.List; | |||||
import javax.annotation.Generated; | |||||
/** | |||||
* This is part of the internal API. | |||||
* This is a POST request. | |||||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/tests/list">Further information about this action online (including a response example)</a> | |||||
* @since 5.2 | |||||
*/ | |||||
@Generated("sonar-ws-generator") | |||||
public class ListRequest { | |||||
private String branch; | |||||
private String p; | |||||
private String ps; | |||||
private String pullRequest; | |||||
private String sourceFileId; | |||||
private String sourceFileKey; | |||||
private String sourceFileLineNumber; | |||||
private String testFileId; | |||||
private String testFileKey; | |||||
private String testId; | |||||
/** | |||||
* This is part of the internal API. | |||||
* Example value: "feature/my_branch" | |||||
*/ | |||||
public ListRequest setBranch(String branch) { | |||||
this.branch = branch; | |||||
return this; | |||||
} | |||||
public String getBranch() { | |||||
return branch; | |||||
} | |||||
/** | |||||
* Example value: "42" | |||||
*/ | |||||
public ListRequest setP(String p) { | |||||
this.p = p; | |||||
return this; | |||||
} | |||||
public String getP() { | |||||
return p; | |||||
} | |||||
/** | |||||
* Example value: "20" | |||||
*/ | |||||
public ListRequest setPs(String ps) { | |||||
this.ps = ps; | |||||
return this; | |||||
} | |||||
public String getPs() { | |||||
return ps; | |||||
} | |||||
/** | |||||
* This is part of the internal API. | |||||
* Example value: "5461" | |||||
*/ | |||||
public ListRequest setPullRequest(String pullRequest) { | |||||
this.pullRequest = pullRequest; | |||||
return this; | |||||
} | |||||
public String getPullRequest() { | |||||
return pullRequest; | |||||
} | |||||
/** | |||||
* Example value: "AU-TpxcA-iU5OvuD2FL0" | |||||
*/ | |||||
public ListRequest setSourceFileId(String sourceFileId) { | |||||
this.sourceFileId = sourceFileId; | |||||
return this; | |||||
} | |||||
public String getSourceFileId() { | |||||
return sourceFileId; | |||||
} | |||||
/** | |||||
* Example value: "my_project:/src/foo/Bar.php" | |||||
*/ | |||||
public ListRequest setSourceFileKey(String sourceFileKey) { | |||||
this.sourceFileKey = sourceFileKey; | |||||
return this; | |||||
} | |||||
public String getSourceFileKey() { | |||||
return sourceFileKey; | |||||
} | |||||
/** | |||||
* Example value: "10" | |||||
*/ | |||||
public ListRequest setSourceFileLineNumber(String sourceFileLineNumber) { | |||||
this.sourceFileLineNumber = sourceFileLineNumber; | |||||
return this; | |||||
} | |||||
public String getSourceFileLineNumber() { | |||||
return sourceFileLineNumber; | |||||
} | |||||
/** | |||||
* Example value: "AU-Tpxb--iU5OvuD2FLy" | |||||
*/ | |||||
public ListRequest setTestFileId(String testFileId) { | |||||
this.testFileId = testFileId; | |||||
return this; | |||||
} | |||||
public String getTestFileId() { | |||||
return testFileId; | |||||
} | |||||
/** | |||||
* Example value: "MY_PROJECT:src/test/java/foo/BarTest.java" | |||||
*/ | |||||
public ListRequest setTestFileKey(String testFileKey) { | |||||
this.testFileKey = testFileKey; | |||||
return this; | |||||
} | |||||
public String getTestFileKey() { | |||||
return testFileKey; | |||||
} | |||||
/** | |||||
* Example value: "AU-TpxcA-iU5OvuD2FLz" | |||||
*/ | |||||
public ListRequest setTestId(String testId) { | |||||
this.testId = testId; | |||||
return this; | |||||
} | |||||
public String getTestId() { | |||||
return testId; | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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.sonarqube.ws.client.tests; | |||||
import java.util.stream.Collectors; | |||||
import javax.annotation.Generated; | |||||
import org.sonarqube.ws.MediaTypes; | |||||
import org.sonarqube.ws.client.BaseService; | |||||
import org.sonarqube.ws.client.GetRequest; | |||||
import org.sonarqube.ws.client.PostRequest; | |||||
import org.sonarqube.ws.client.WsConnector; | |||||
import org.sonarqube.ws.Tests.CoveredFilesResponse; | |||||
import org.sonarqube.ws.Tests.ListResponse; | |||||
/** | |||||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/tests">Further information about this web service online</a> | |||||
*/ | |||||
@Generated("sonar-ws-generator") | |||||
public class TestsService extends BaseService { | |||||
public TestsService(WsConnector wsConnector) { | |||||
super(wsConnector, "api/tests"); | |||||
} | |||||
/** | |||||
* | |||||
* This is part of the internal API. | |||||
* This is a GET request. | |||||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/tests/covered_files">Further information about this action online (including a response example)</a> | |||||
* @since 4.4 | |||||
* @deprecated since 5.6 | |||||
*/ | |||||
@Deprecated | |||||
public CoveredFilesResponse coveredFiles(CoveredFilesRequest request) { | |||||
return call( | |||||
new GetRequest(path("covered_files")) | |||||
.setParam("p", request.getP()) | |||||
.setParam("ps", request.getPs()) | |||||
.setParam("testId", request.getTestId()), | |||||
CoveredFilesResponse.parser()); | |||||
} | |||||
/** | |||||
* | |||||
* This is part of the internal API. | |||||
* This is a GET request. | |||||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/tests/list">Further information about this action online (including a response example)</a> | |||||
* @since 5.2 | |||||
* @deprecated since 5.6 | |||||
*/ | |||||
@Deprecated | |||||
public ListResponse list(ListRequest request) { | |||||
return call( | |||||
new GetRequest(path("list")) | |||||
.setParam("branch", request.getBranch()) | |||||
.setParam("p", request.getP()) | |||||
.setParam("ps", request.getPs()) | |||||
.setParam("pullRequest", request.getPullRequest()) | |||||
.setParam("sourceFileId", request.getSourceFileId()) | |||||
.setParam("sourceFileKey", request.getSourceFileKey()) | |||||
.setParam("sourceFileLineNumber", request.getSourceFileLineNumber()) | |||||
.setParam("testFileId", request.getTestFileId()) | |||||
.setParam("testFileKey", request.getTestFileKey()) | |||||
.setParam("testId", request.getTestId()), | |||||
ListResponse.parser()); | |||||
} | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program 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. | |||||
* | |||||
* This program 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. | |||||
*/ | |||||
@ParametersAreNonnullByDefault | |||||
@Generated("sonar-ws-generator") | |||||
package org.sonarqube.ws.client.tests; | |||||
import javax.annotation.ParametersAreNonnullByDefault; | |||||
import javax.annotation.Generated; | |||||