@@ -22,15 +22,8 @@ package org.sonar.server.computation.step; | |||
import java.util.Set; | |||
import org.apache.commons.lang.StringEscapeUtils; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.MyBatis; | |||
import org.sonar.db.measure.MeasureDto; | |||
import org.sonar.db.metric.MetricDto; | |||
import org.sonar.server.computation.component.Component; | |||
import org.sonar.server.computation.component.CrawlerDepthLimit; | |||
import org.sonar.server.computation.component.DbIdsRepository; | |||
import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler; | |||
import org.sonar.server.computation.component.TreeRootHolder; | |||
import org.sonar.server.computation.component.TypeAwareVisitorAdapter; | |||
@@ -41,67 +34,60 @@ import org.sonar.server.computation.duplication.DuplicationRepository; | |||
import org.sonar.server.computation.duplication.InProjectDuplicate; | |||
import org.sonar.server.computation.duplication.InnerDuplicate; | |||
import org.sonar.server.computation.duplication.TextBlock; | |||
import org.sonar.server.computation.measure.Measure; | |||
import org.sonar.server.computation.measure.MeasureRepository; | |||
import org.sonar.server.computation.metric.Metric; | |||
import org.sonar.server.computation.metric.MetricRepository; | |||
import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA_KEY; | |||
import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; | |||
/** | |||
* Persist duplications into | |||
* Compute duplication data measures on files, based on the {@link DuplicationRepository} | |||
*/ | |||
public class PersistDuplicationsStep implements ComputationStep { | |||
public class DuplicationDataMeasuresStep implements ComputationStep { | |||
private final DbClient dbClient; | |||
private final DbIdsRepository dbIdsRepository; | |||
private final MeasureRepository measureRepository; | |||
private final TreeRootHolder treeRootHolder; | |||
private final DuplicationRepository duplicationRepository; | |||
public PersistDuplicationsStep(DbClient dbClient, DbIdsRepository dbIdsRepository, TreeRootHolder treeRootHolder, | |||
private final Metric duplicationDataMetric; | |||
public DuplicationDataMeasuresStep(TreeRootHolder treeRootHolder, MetricRepository metricRepository, MeasureRepository measureRepository, | |||
DuplicationRepository duplicationRepository) { | |||
this.dbClient = dbClient; | |||
this.dbIdsRepository = dbIdsRepository; | |||
this.measureRepository = measureRepository; | |||
this.treeRootHolder = treeRootHolder; | |||
this.duplicationRepository = duplicationRepository; | |||
this.duplicationDataMetric = metricRepository.getByKey(DUPLICATIONS_DATA_KEY); | |||
} | |||
@Override | |||
public void execute() { | |||
DbSession session = dbClient.openSession(true); | |||
try { | |||
MetricDto duplicationMetric = dbClient.metricDao().selectOrFailByKey(session, CoreMetrics.DUPLICATIONS_DATA_KEY); | |||
new DepthTraversalTypeAwareCrawler(new DuplicationVisitor(session, duplicationMetric)) | |||
.visit(treeRootHolder.getRoot()); | |||
session.commit(); | |||
} finally { | |||
MyBatis.closeQuietly(session); | |||
} | |||
new DepthTraversalTypeAwareCrawler(new DuplicationVisitor()) | |||
.visit(treeRootHolder.getRoot()); | |||
} | |||
private class DuplicationVisitor extends TypeAwareVisitorAdapter { | |||
private final DbSession session; | |||
private final MetricDto duplicationMetric; | |||
private DuplicationVisitor(DbSession session, MetricDto duplicationMetric) { | |||
private DuplicationVisitor() { | |||
super(CrawlerDepthLimit.FILE, PRE_ORDER); | |||
this.session = session; | |||
this.duplicationMetric = duplicationMetric; | |||
} | |||
@Override | |||
public void visitFile(Component file) { | |||
Set<Duplication> duplications = duplicationRepository.getDuplications(file); | |||
if (!duplications.isEmpty()) { | |||
saveDuplications(file, duplications); | |||
computeDuplications(file, duplications); | |||
} | |||
} | |||
private void saveDuplications(Component component, Iterable<Duplication> duplications) { | |||
private void computeDuplications(Component component, Iterable<Duplication> duplications) { | |||
String duplicationXml = createXmlDuplications(component.getKey(), duplications); | |||
MeasureDto measureDto = new MeasureDto() | |||
.setMetricId(duplicationMetric.getId()) | |||
.setData(duplicationXml) | |||
.setComponentId(dbIdsRepository.getComponentId(component)) | |||
.setSnapshotId(dbIdsRepository.getSnapshotId(component)); | |||
dbClient.measureDao().insert(session, measureDto); | |||
measureRepository.add( | |||
component, | |||
duplicationDataMetric, | |||
Measure.newMeasureBuilder().create(duplicationXml) | |||
); | |||
} | |||
private String createXmlDuplications(String componentKey, Iterable<Duplication> duplications) { | |||
@@ -150,7 +136,7 @@ public class PersistDuplicationsStep implements ComputationStep { | |||
@Override | |||
public String getDescription() { | |||
return "Persist duplications"; | |||
return "Compute duplication data measures"; | |||
} | |||
} |
@@ -53,6 +53,8 @@ public class ReportComputationSteps implements ComputationSteps { | |||
LoadQualityGateStep.class, | |||
LoadPeriodsStep.class, | |||
// load duplications related stuff | |||
LoadDuplicationsFromReportStep.class, | |||
LoadCrossProjectDuplicationsRepositoryStep.class, | |||
// data computation | |||
@@ -61,8 +63,8 @@ public class ReportComputationSteps implements ComputationSteps { | |||
CoverageMeasuresStep.class, | |||
CommentMeasuresStep.class, | |||
CustomMeasuresCopyStep.class, | |||
LoadDuplicationsFromReportStep.class, | |||
DuplicationMeasuresStep.class, | |||
DuplicationDataMeasuresStep.class, | |||
LanguageDistributionMeasuresStep.class, | |||
UnitTestMeasuresStep.class, | |||
ComplexityMeasuresStep.class, | |||
@@ -89,7 +91,6 @@ public class ReportComputationSteps implements ComputationSteps { | |||
PersistIssuesStep.class, | |||
PersistProjectLinksStep.class, | |||
PersistEventsStep.class, | |||
PersistDuplicationsStep.class, | |||
PersistFileSourcesStep.class, | |||
PersistTestsStep.class, | |||
PersistCrossProjectDuplicationIndexStep.class, |
@@ -0,0 +1,126 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.computation.step; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.server.computation.batch.TreeRootHolderRule; | |||
import org.sonar.server.computation.duplication.DuplicationRepositoryRule; | |||
import org.sonar.server.computation.duplication.TextBlock; | |||
import org.sonar.server.computation.measure.MeasureRepositoryRule; | |||
import org.sonar.server.computation.metric.MetricRepositoryRule; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.guava.api.Assertions.assertThat; | |||
import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA; | |||
import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA_KEY; | |||
import static org.sonar.server.computation.component.Component.Type.FILE; | |||
import static org.sonar.server.computation.component.Component.Type.PROJECT; | |||
import static org.sonar.server.computation.component.ReportComponent.builder; | |||
public class DuplicationDataMeasuresStepTest extends BaseStepTest { | |||
private static final int ROOT_REF = 1; | |||
private static final String PROJECT_KEY = "PROJECT_KEY"; | |||
private static final int FILE_1_REF = 2; | |||
private static final String FILE_1_KEY = "FILE_1_KEY"; | |||
private static final int FILE_2_REF = 3; | |||
private static final String FILE_2_KEY = "FILE_2_KEY"; | |||
@Rule | |||
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule() | |||
.setRoot( | |||
builder(PROJECT, ROOT_REF).setKey(PROJECT_KEY) | |||
.addChildren( | |||
builder(FILE, FILE_1_REF).setKey(FILE_1_KEY) | |||
.build(), | |||
builder(FILE, FILE_2_REF).setKey(FILE_2_KEY) | |||
.build()) | |||
.build()); | |||
@Rule | |||
public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder); | |||
@Rule | |||
public MetricRepositoryRule metricRepository = new MetricRepositoryRule() | |||
.add(DUPLICATIONS_DATA); | |||
@Rule | |||
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); | |||
DuplicationDataMeasuresStep underTest = new DuplicationDataMeasuresStep(treeRootHolder, metricRepository, measureRepository, duplicationRepository); | |||
@Override | |||
protected ComputationStep step() { | |||
return underTest; | |||
} | |||
@Test | |||
public void nothing_to_do_when_no_duplication() { | |||
underTest.execute(); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isAbsent(); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent(); | |||
} | |||
@Test | |||
public void compute_duplications_on_same_file() { | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 5), new TextBlock(6, 10)); | |||
underTest.execute(); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isPresent(); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY).get().getData()).isEqualTo( | |||
"<duplications><g><b s=\"1\" l=\"5\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" r=\"" + FILE_1_KEY + "\"/></g></duplications>" | |||
); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent(); | |||
} | |||
@Test | |||
public void compute_duplications_on_different_files() { | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 5), FILE_2_REF, new TextBlock(6, 10)); | |||
underTest.execute(); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isPresent(); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY).get().getData()).isEqualTo( | |||
"<duplications><g><b s=\"1\" l=\"5\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" r=\"" + FILE_2_KEY + "\"/></g></duplications>" | |||
); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent(); | |||
} | |||
@Test | |||
public void compute_duplications_on_different_projects() { | |||
String fileKeyFromOtherProject = "PROJECT2_KEY:file2"; | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 5), fileKeyFromOtherProject, new TextBlock(6, 10)); | |||
underTest.execute(); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isPresent(); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY).get().getData()).isEqualTo( | |||
"<duplications><g><b s=\"1\" l=\"5\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" r=\"" + fileKeyFromOtherProject + "\"/></g></duplications>" | |||
); | |||
assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent(); | |||
} | |||
} |
@@ -1,165 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.computation.step; | |||
import java.util.Map; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.experimental.categories.Category; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.metric.MetricDto; | |||
import org.sonar.server.computation.batch.TreeRootHolderRule; | |||
import org.sonar.server.computation.component.Component; | |||
import org.sonar.server.computation.component.DbIdsRepositoryImpl; | |||
import org.sonar.server.computation.component.ReportComponent; | |||
import org.sonar.server.computation.duplication.DuplicationRepositoryRule; | |||
import org.sonar.server.computation.duplication.TextBlock; | |||
import org.sonar.test.DbTests; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@Category(DbTests.class) | |||
public class PersistDuplicationsStepTest extends BaseStepTest { | |||
private static final String PROJECT_KEY = "PROJECT_KEY"; | |||
private static final int FILE_1_REF = 2; | |||
private static final int FILE_2_REF = 3; | |||
private static final int ROOT_REF = 1; | |||
@Rule | |||
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); | |||
@Rule | |||
public DbTester dbTester = DbTester.create(System2.INSTANCE); | |||
@Rule | |||
public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder); | |||
DbIdsRepositoryImpl dbIdsRepository = new DbIdsRepositoryImpl(); | |||
DbSession session = dbTester.getSession(); | |||
DbClient dbClient = dbTester.getDbClient(); | |||
PersistDuplicationsStep underTest; | |||
@Before | |||
public void setup() { | |||
dbTester.truncateTables(); | |||
underTest = new PersistDuplicationsStep(dbClient, dbIdsRepository, treeRootHolder, duplicationRepository); | |||
} | |||
@Override | |||
protected ComputationStep step() { | |||
return underTest; | |||
} | |||
@After | |||
public void tearDown() { | |||
session.close(); | |||
} | |||
@Test | |||
public void nothing_to_do_when_no_duplication() { | |||
saveDuplicationMetric(); | |||
initReportWithProjectAndFile(); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(0); | |||
} | |||
@Test | |||
public void persist_duplications_on_same_file() { | |||
MetricDto duplicationMetric = saveDuplicationMetric(); | |||
initReportWithProjectAndFile(); | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 5), new TextBlock(6, 10)); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); | |||
Map<String, Object> dto = dbTester.selectFirst("select snapshot_id as \"snapshotId\", metric_id as \"metricId\", text_value as \"textValue\" from project_measures"); | |||
assertThat(dto.get("snapshotId")).isEqualTo(11L); | |||
assertThat(dto.get("metricId")).isEqualTo(duplicationMetric.getId().longValue()); | |||
assertThat(dto.get("textValue")).isEqualTo("<duplications><g><b s=\"1\" l=\"5\" r=\"PROJECT_KEY:file\"/><b s=\"6\" l=\"5\" r=\"PROJECT_KEY:file\"/></g></duplications>"); | |||
} | |||
@Test | |||
public void persist_duplications_on_different_files() { | |||
saveDuplicationMetric(); | |||
initReportWithProjectAndFile(); | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 5), 3, new TextBlock(6, 10)); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); | |||
Map<String, Object> dto = dbTester.selectFirst("select snapshot_id as \"snapshotId\", text_value as \"textValue\" from project_measures"); | |||
assertThat(dto.get("snapshotId")).isEqualTo(11L); | |||
assertThat(dto.get("textValue")).isEqualTo("<duplications><g><b s=\"1\" l=\"5\" r=\"PROJECT_KEY:file\"/><b s=\"6\" l=\"5\" r=\"PROJECT_KEY:file2\"/></g></duplications>"); | |||
} | |||
@Test | |||
public void persist_duplications_on_different_projects() { | |||
saveDuplicationMetric(); | |||
initReportWithProjectAndFile(); | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 5), "PROJECT2_KEY:file2", new TextBlock(6, 10)); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); | |||
Map<String, Object> dto = dbTester.selectFirst("select snapshot_id as \"snapshotId\", text_value as \"textValue\" from project_measures"); | |||
assertThat(dto.get("snapshotId")).isEqualTo(11L); | |||
assertThat(dto.get("textValue")).isEqualTo("<duplications><g><b s=\"1\" l=\"5\" r=\"PROJECT_KEY:file\"/><b s=\"6\" l=\"5\" r=\"PROJECT2_KEY:file2\"/></g></duplications>"); | |||
} | |||
private void initReportWithProjectAndFile() { | |||
Component file1 = ReportComponent.builder(Component.Type.FILE, FILE_1_REF).setUuid("BCDE").setKey("PROJECT_KEY:file").build(); | |||
Component file2 = ReportComponent.builder(Component.Type.FILE, FILE_2_REF).setUuid("CDEF").setKey("PROJECT_KEY:file2").build(); | |||
Component project = ReportComponent.builder(Component.Type.PROJECT, ROOT_REF).setUuid("ABCD").setKey(PROJECT_KEY).addChildren(file1, file2).build(); | |||
treeRootHolder.setRoot(project); | |||
dbIdsRepository.setComponentId(project, 1); | |||
dbIdsRepository.setSnapshotId(project, 10); | |||
dbIdsRepository.setComponentId(file1, 2); | |||
dbIdsRepository.setSnapshotId(file1, 11); | |||
dbIdsRepository.setComponentId(file2, 2); | |||
dbIdsRepository.setSnapshotId(file2, 12); | |||
} | |||
private MetricDto saveDuplicationMetric() { | |||
MetricDto duplicationMetric = new MetricDto().setKey(CoreMetrics.DUPLICATIONS_DATA_KEY) | |||
.setOptimizedBestValue(false) | |||
.setDeleteHistoricalData(false) | |||
.setHidden(false); | |||
dbClient.metricDao().insert(session, duplicationMetric); | |||
session.commit(); | |||
return duplicationMetric; | |||
} | |||
} |