From 2f92fe93b2d7bb37ca31357fbcccbffeda02d039 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 1 Sep 2015 23:14:19 +0200 Subject: [PATCH] SONAR-6824 add metric last_commit_date --- ...ReportComputeEngineContainerPopulator.java | 2 + .../server/computation/metric/Metric.java | 2 +- .../computation/source/LastCommitVisitor.java | 138 +++++++++++ ...ersistNumberOfDaysSinceLastCommitStep.java | 137 ----------- .../step/ReportComputationSteps.java | 1 - .../sonar/server/source/ws/ShowAction.java | 2 +- .../source/LastCommitVisitorTest.java | 220 ++++++++++++++++++ ...stNumberOfDaysSinceLastCommitStepTest.java | 132 ----------- .../org/sonar/api/measures/CoreMetrics.java | 14 ++ 9 files changed, 376 insertions(+), 272 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/source/LastCommitVisitor.java delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/source/LastCommitVisitorTest.java delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStepTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java index 7c1e502aff4..0756e92821c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java @@ -77,6 +77,7 @@ import org.sonar.server.computation.qualitygate.EvaluationResultTextConverterImp import org.sonar.server.computation.qualitygate.QualityGateHolderImpl; import org.sonar.server.computation.qualitygate.QualityGateServiceImpl; import org.sonar.server.computation.qualityprofile.ActiveRulesHolderImpl; +import org.sonar.server.computation.source.LastCommitVisitor; import org.sonar.server.computation.sqale.SqaleMeasuresVisitor; import org.sonar.server.computation.sqale.SqaleRatingSettings; import org.sonar.server.computation.step.ComputationSteps; @@ -168,6 +169,7 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop IntegrateIssuesVisitor.class, CloseIssuesOnRemovedComponentsVisitor.class, SqaleMeasuresVisitor.class, + LastCommitVisitor.class, MeasureComputersVisitor.class, UpdateConflictResolver.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/Metric.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/Metric.java index 5446495665b..ee6771b6cfe 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/Metric.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/Metric.java @@ -51,7 +51,7 @@ public interface Metric { enum MetricType { INT(Measure.ValueType.INT), - MILLISEC(Measure.ValueType.INT), + MILLISEC(Measure.ValueType.LONG), RATING(Measure.ValueType.INT), WORK_DUR(Measure.ValueType.LONG), FLOAT(Measure.ValueType.DOUBLE), diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/source/LastCommitVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/source/LastCommitVisitor.java new file mode 100644 index 00000000000..63a137871c5 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/source/LastCommitVisitor.java @@ -0,0 +1,138 @@ +/* + * 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.source; + +import com.google.common.base.Optional; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.server.computation.batch.BatchReportReader; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.CrawlerDepthLimit; +import org.sonar.server.computation.component.PathAwareVisitorAdapter; +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.server.computation.component.ComponentVisitor.Order.POST_ORDER; + +public class LastCommitVisitor extends PathAwareVisitorAdapter { + + private final BatchReportReader reportReader; + private final MeasureRepository measureRepository; + private final Metric lastCommitDateMetric; + + public LastCommitVisitor(BatchReportReader reportReader, MetricRepository metricRepository, + MeasureRepository measureRepository) { + super(CrawlerDepthLimit.LEAVES, POST_ORDER, new SimpleStackElementFactory() { + @Override + public LastCommit createForAny(Component component) { + return new LastCommit(); + } + + /** Stack item is not used at ProjectView level, saves on instantiating useless objects */ + @Override + public LastCommit createForProjectView(Component projectView) { + return null; + } + }); + this.reportReader = reportReader; + this.measureRepository = measureRepository; + this.lastCommitDateMetric = metricRepository.getByKey(CoreMetrics.LAST_COMMIT_DATE_KEY); + } + + @Override + public void visitProject(Component project, Path path) { + saveAndAggregate(project, path); + } + + @Override + public void visitDirectory(Component directory, Path path) { + saveAndAggregate(directory, path); + } + + @Override + public void visitModule(Component module, Path path) { + saveAndAggregate(module, path); + } + + @Override + public void visitFile(Component file, Path path) { + // load SCM blame information from report. It can be absent when the file was not touched + // since previous analysis (optimization to decrease execution of blame commands). In this case + // the date is loaded from database, as it did not change from previous analysis. + BatchReport.Changesets changesets = reportReader.readChangesets(file.getReportAttributes().getRef()); + if (changesets == null) { + Optional baseMeasure = measureRepository.getBaseMeasure(file, lastCommitDateMetric); + if (baseMeasure.isPresent()) { + path.current().addDate(baseMeasure.get().getLongValue()); + } + } else { + for (BatchReport.Changesets.Changeset changeset : changesets.getChangesetList()) { + if (changeset.hasDate()) { + path.current().addDate(changeset.getDate()); + } + } + } + saveAndAggregate(file, path); + } + + @Override + public void visitView(Component view, Path path) { + saveAndAggregate(view, path); + } + + @Override + public void visitSubView(Component subView, Path path) { + saveAndAggregate(subView, path); + } + + @Override + public void visitProjectView(Component projectView, Path path) { + if (!path.isRoot()) { + Optional rawMeasure = measureRepository.getRawMeasure(projectView, lastCommitDateMetric); + if (rawMeasure.isPresent()) { + path.parent().addDate(rawMeasure.get().getLongValue()); + } + } + } + + private void saveAndAggregate(Component component, Path path) { + long maxDate = path.current().getDate(); + if (maxDate > 0L) { + measureRepository.add(component, lastCommitDateMetric, Measure.newMeasureBuilder().create(maxDate)); + if (!path.isRoot()) { + path.parent().addDate(maxDate); + } + } + } + + public static final class LastCommit { + private long date = 0; + + public void addDate(long l) { + this.date = Math.max(this.date, l); + } + + public long getDate() { + return date; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java deleted file mode 100644 index e28c529d121..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStep.java +++ /dev/null @@ -1,137 +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.Date; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.utils.System2; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.MyBatis; -import org.sonar.db.measure.MeasureDto; -import org.sonar.server.computation.batch.BatchReportReader; -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; -import org.sonar.server.computation.metric.MetricRepository; -import org.sonar.server.source.index.SourceLineIndex; - -import static com.google.common.base.Objects.firstNonNull; -import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; - -public class PersistNumberOfDaysSinceLastCommitStep implements ComputationStep { - - private static final long MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24L; - - private final DbClient dbClient; - private final SourceLineIndex sourceLineIndex; - private final MetricRepository metricRepository; - private final System2 system; - private final TreeRootHolder treeRootHolder; - private final BatchReportReader reportReader; - private final DbIdsRepository dbIdsRepository; - - public PersistNumberOfDaysSinceLastCommitStep(System2 system, DbClient dbClient, SourceLineIndex sourceLineIndex, MetricRepository metricRepository, - TreeRootHolder treeRootHolder, BatchReportReader reportReader, DbIdsRepository dbIdsRepository) { - this.dbClient = dbClient; - this.sourceLineIndex = sourceLineIndex; - this.metricRepository = metricRepository; - this.system = system; - this.treeRootHolder = treeRootHolder; - this.reportReader = reportReader; - this.dbIdsRepository = dbIdsRepository; - } - - @Override - public String getDescription() { - return "Compute and persist the number of days since last commit"; - } - - @Override - public void execute() { - NumberOfDaysSinceLastCommitVisitor visitor = new NumberOfDaysSinceLastCommitVisitor(); - Component project = treeRootHolder.getRoot(); - new DepthTraversalTypeAwareCrawler(visitor).visit(project); - - long lastCommitTimestamp = visitor.lastCommitTimestampFromReport; - if (lastCommitTimestamp == 0L) { - Long lastCommitFromIndex = lastCommitFromIndex(treeRootHolder.getRoot().getUuid()); - lastCommitTimestamp = firstNonNull(lastCommitFromIndex, lastCommitTimestamp); - } - - if (lastCommitTimestamp != 0L) { - persistNumberOfDaysSinceLastCommit(lastCommitTimestamp, dbIdsRepository.getSnapshotId(project)); - } - } - - @CheckForNull - private Long lastCommitFromIndex(String projectUuid) { - Date lastCommitDate = sourceLineIndex.lastCommitDateOnProject(projectUuid); - return lastCommitDate == null ? null : lastCommitDate.getTime(); - } - - private void persistNumberOfDaysSinceLastCommit(long lastCommitTimestamp, long projectSnapshotId) { - long numberOfDaysSinceLastCommit = (system.now() - lastCommitTimestamp) / MILLISECONDS_PER_DAY; - DbSession dbSession = dbClient.openSession(true); - try { - dbClient.measureDao().insert(dbSession, new MeasureDto() - .setValue((double) numberOfDaysSinceLastCommit) - .setMetricId(metricRepository.getByKey(CoreMetrics.DAYS_SINCE_LAST_COMMIT_KEY).getId()) - .setSnapshotId(projectSnapshotId)); - dbSession.commit(); - } finally { - MyBatis.closeQuietly(dbSession); - } - } - - private class NumberOfDaysSinceLastCommitVisitor extends TypeAwareVisitorAdapter { - - private long lastCommitTimestampFromReport = 0L; - - private NumberOfDaysSinceLastCommitVisitor() { - super(CrawlerDepthLimit.FILE, PRE_ORDER); - } - - @Override - public void visitFile(Component component) { - BatchReport.Changesets scm = reportReader.readChangesets(component.getReportAttributes().getRef()); - processScm(scm); - } - - private void processScm(@Nullable BatchReport.Changesets scm) { - if (scm == null) { - return; - } - - for (BatchReport.Changesets.Changeset changeset : scm.getChangesetList()) { - if (changeset.hasDate() && changeset.getDate() > lastCommitTimestampFromReport) { - lastCommitTimestampFromReport = changeset.getDate(); - } - } - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java index d65b2d96add..2a784b5c1b7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java @@ -81,7 +81,6 @@ public class ReportComputationSteps implements ComputationSteps { // Persist data PersistComponentsStep.class, PersistSnapshotsStep.class, - PersistNumberOfDaysSinceLastCommitStep.class, PersistMeasuresStep.class, PersistIssuesStep.class, PersistProjectLinksStep.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/source/ws/ShowAction.java index 278d4c8b350..2d9a12109a4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/ws/ShowAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/ws/ShowAction.java @@ -106,7 +106,7 @@ public class ShowAction implements SourcesWsAction { for (int i = 0; i < lines.size(); i++) { String line = lines.get(i); json.beginArray(); - json.value(1L * i + from); + json.value((long) i + from); json.value(line); json.endArray(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/source/LastCommitVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/source/LastCommitVisitorTest.java new file mode 100644 index 00000000000..93dfebdfa92 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/source/LastCommitVisitorTest.java @@ -0,0 +1,220 @@ +/* + * 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.source; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.server.computation.batch.BatchReportReaderRule; +import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.ComponentVisitor; +import org.sonar.server.computation.component.FileAttributes; +import org.sonar.server.computation.component.ReportComponent; +import org.sonar.server.computation.component.ViewsComponent; +import org.sonar.server.computation.component.VisitorsCrawler; +import org.sonar.server.computation.measure.Measure; +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.sonar.api.measures.CoreMetrics.LAST_COMMIT_DATE_KEY; +import static org.sonar.server.computation.component.Component.Type.DIRECTORY; +import static org.sonar.server.computation.component.Component.Type.FILE; +import static org.sonar.server.computation.component.Component.Type.MODULE; +import static org.sonar.server.computation.component.Component.Type.PROJECT; +import static org.sonar.server.computation.component.Component.Type.PROJECT_VIEW; +import static org.sonar.server.computation.component.Component.Type.SUBVIEW; +import static org.sonar.server.computation.component.Component.Type.VIEW; +import static org.sonar.server.computation.component.ViewsComponent.builder; +import static org.sonar.server.computation.measure.Measure.newMeasureBuilder; + +public class LastCommitVisitorTest { + + public static final int FILE_REF = 1; + + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + + @Rule + public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + + @Rule + public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(CoreMetrics.LAST_COMMIT_DATE); + + @Rule + public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); + + @Test + public void aggregate_date_of_last_commit_to_directories_and_project() throws Exception { + // simulate the output of visitFile() + LastCommitVisitor visitor = new LastCommitVisitor(reportReader, metricRepository, measureRepository) { + @Override + public void visitFile(Component file, Path path) { + path.parent().addDate(file.getReportAttributes().getRef() * 1_000_000_000L); + } + }; + + // project with 1 module, 2 directories and 3 files + ReportComponent project = ReportComponent.builder(PROJECT, 1) + .addChildren( + ReportComponent.builder(MODULE, 11) + .addChildren( + ReportComponent.builder(DIRECTORY, 111) + .addChildren( + createFileComponent(1111), + createFileComponent(1112)) + .build(), + ReportComponent.builder(DIRECTORY, 112) + .addChildren( + createFileComponent(1121)) + .build()) + .build()) + .build(); + treeRootHolder.setRoot(project); + + VisitorsCrawler underTest = new VisitorsCrawler(Lists.newArrayList(visitor)); + underTest.visit(project); + + // directories + assertDate(111, 1_112_000_000_000L); + assertDate(112, 1_121_000_000_000L); + + // module = most recent commit date of directories + assertDate(11, 1_121_000_000_000L); + + // project + assertDate(1, 1_121_000_000_000L); + } + + @Test + public void aggregate_date_of_last_commit_to_views() throws Exception { + // view with 3 nested sub-views and 3 projects + ViewsComponent view = ViewsComponent.builder(VIEW, 1) + .addChildren( + builder(SUBVIEW, 11) + .addChildren( + builder(SUBVIEW, 111) + .addChildren( + builder(PROJECT_VIEW, 1111).build(), + builder(PROJECT_VIEW, 1112).build()) + .build(), + builder(SUBVIEW, 112) + .addChildren( + builder(PROJECT_VIEW, 1121).build()) + .build()) + .build()) + .build(); + treeRootHolder.setRoot(view); + + // the second project has the most recent commit date + measureRepository.addRawMeasure(1111, CoreMetrics.LAST_COMMIT_DATE_KEY, newMeasureBuilder().create(1_500_000_000_000L)); + measureRepository.addRawMeasure(1112, CoreMetrics.LAST_COMMIT_DATE_KEY, newMeasureBuilder().create(1_700_000_000_000L)); + measureRepository.addRawMeasure(1121, CoreMetrics.LAST_COMMIT_DATE_KEY, newMeasureBuilder().create(1_600_000_000_000L)); + + VisitorsCrawler underTest = new VisitorsCrawler(Lists.newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository))); + underTest.visit(view); + + // second level of sub-views + assertDate(111, 1_700_000_000_000L); + assertDate(112, 1_600_000_000_000L); + + // first level of sub-views + assertDate(11, 1_700_000_000_000L); + + // view + assertDate(1, 1_700_000_000_000L); + } + + @Test + public void compute_date_of_file_from_blame_info_of_report() throws Exception { + VisitorsCrawler underTest = new VisitorsCrawler(Lists.newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository))); + + BatchReport.Changesets changesets = BatchReport.Changesets.newBuilder() + .setComponentRef(FILE_REF) + .addChangeset(BatchReport.Changesets.Changeset.newBuilder() + .setAuthor("john") + .setDate(1_500_000_000_000L) + .setRevision("rev-1") + .build()) + .addChangeset(BatchReport.Changesets.Changeset.newBuilder() + .setAuthor("tom") + // this is the most recent change + .setDate(1_600_000_000_000L) + .setRevision("rev-2") + .build()) + .addChangeset(BatchReport.Changesets.Changeset.newBuilder() + .setAuthor("john") + .setDate(1_500_000_000_000L) + .setRevision("rev-1") + .build()) + .addChangesetIndexByLine(0) + .build(); + reportReader.putChangesets(changesets); + ReportComponent file = createFileComponent(FILE_REF); + treeRootHolder.setRoot(file); + + underTest.visit(file); + + assertDate(FILE_REF, 1_600_000_000_000L); + } + + private void assertDate(int componentRef, long expectedDate) { + Optional measure = measureRepository.getAddedRawMeasure(componentRef, LAST_COMMIT_DATE_KEY); + assertThat(measure.isPresent()).isTrue(); + assertThat(measure.get().getLongValue()).isEqualTo(expectedDate); + } + + /** + * When the file was not changed since previous analysis, than the report may not contain + * the SCM blame information. In this case the date of last commit is loaded + * from the base measure of previous analysis, directly from database + */ + @Test + public void reuse_date_of_previous_analysis_if_blame_info_is_not_in_report() throws Exception { + VisitorsCrawler underTest = new VisitorsCrawler(Lists.newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository))); + ReportComponent file = createFileComponent(FILE_REF); + treeRootHolder.setRoot(file); + measureRepository.addBaseMeasure(FILE_REF, LAST_COMMIT_DATE_KEY, newMeasureBuilder().create(1_500_000_000L)); + + underTest.visit(file); + + assertDate(FILE_REF, 1_500_000_000L); + } + + @Test + public void date_is_not_computed_on_file_if_blame_is_not_in_report_nor_in_previous_analysis() throws Exception { + VisitorsCrawler underTest = new VisitorsCrawler(Lists.newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository))); + ReportComponent file = createFileComponent(FILE_REF); + treeRootHolder.setRoot(file); + + underTest.visit(file); + + Optional measure = measureRepository.getAddedRawMeasure(FILE_REF, LAST_COMMIT_DATE_KEY); + assertThat(measure.isPresent()).isFalse(); + } + + private ReportComponent createFileComponent(int fileRef) { + return ReportComponent.builder(FILE, fileRef).setFileAttributes(new FileAttributes(false, "js")).build(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStepTest.java deleted file mode 100644 index 37487ea77aa..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistNumberOfDaysSinceLastCommitStepTest.java +++ /dev/null @@ -1,132 +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.Date; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.config.Settings; -import org.sonar.api.utils.DateUtils; -import org.sonar.api.utils.System2; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.db.DbClient; -import org.sonar.db.DbTester; -import org.sonar.server.computation.batch.BatchReportReaderRule; -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.language.LanguageRepository; -import org.sonar.server.computation.metric.Metric; -import org.sonar.server.computation.metric.MetricImpl; -import org.sonar.server.computation.metric.MetricRepository; -import org.sonar.server.source.index.SourceLineIndex; - -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class PersistNumberOfDaysSinceLastCommitStepTest extends BaseStepTest { - - @Rule - public DbTester db = DbTester.create(System2.INSTANCE); - - @Rule - public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); - - @Rule - public BatchReportReaderRule reportReader = new BatchReportReaderRule(); - - DbIdsRepositoryImpl dbIdsRepository = new DbIdsRepositoryImpl(); - - PersistNumberOfDaysSinceLastCommitStep underTest; - - DbClient dbClient = db.getDbClient(); - SourceLineIndex sourceLineIndex; - MetricRepository metricRepository; - Settings projectSettings; - LanguageRepository languageRepository; - - @Before - public void setUp() { - db.truncateTables(); - sourceLineIndex = mock(SourceLineIndex.class); - metricRepository = mock(MetricRepository.class); - projectSettings = new Settings(); - languageRepository = mock(LanguageRepository.class); - when(metricRepository.getByKey(anyString())).thenReturn(new MetricImpl(10, "key", "name", Metric.MetricType.STRING)); - - underTest = new PersistNumberOfDaysSinceLastCommitStep(System2.INSTANCE, dbClient, sourceLineIndex, metricRepository, treeRootHolder, reportReader, dbIdsRepository); - } - - @Override - protected ComputationStep step() { - return underTest; - } - - @Test - public void persist_number_of_days_since_last_commit_from_report() { - long threeDaysAgo = DateUtils.addDays(new Date(), -3).getTime(); - initProject(); - reportReader.putChangesets( - BatchReport.Changesets.newBuilder() - .setComponentRef(2) - .addChangeset( - BatchReport.Changesets.Changeset.newBuilder() - .setDate(threeDaysAgo) - ) - .build() - ); - - underTest.execute(); - - db.assertDbUnit(getClass(), "insert-from-report-result.xml", new String[]{"id"}, "project_measures"); - } - - @Test - public void persist_number_of_days_since_last_commit_from_index() { - Date sixDaysAgo = DateUtils.addDays(new Date(), -6); - when(sourceLineIndex.lastCommitDateOnProject("project-uuid")).thenReturn(sixDaysAgo); - initProject(); - - underTest.execute(); - - db.assertDbUnit(getClass(), "insert-from-index-result.xml", new String[]{"id"}, "project_measures"); - } - - @Test - public void no_scm_information_in_report_and_index() { - initProject(); - - underTest.execute(); - - db.assertDbUnit(getClass(), "empty.xml"); - } - - private void initProject() { - Component project = ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("project-uuid").addChildren( - ReportComponent.builder(Component.Type.FILE, 2).setUuid("file-uuid").build()) - .build(); - treeRootHolder.setRoot(project); - dbIdsRepository.setSnapshotId(project, 1000); - } -} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java index 937ac3c0ba9..93d954fa621 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java @@ -2228,6 +2228,20 @@ public final class CoreMetrics { .setDomain(CoreMetrics.DOMAIN_SCM) .create(); + /** + * @since 5.2 + */ + public static final String LAST_COMMIT_DATE_KEY = "last_commit_date"; + + /** + * Date of the most recent commit. Current implementation is based on commits touching lines of source code. It + * ignores other changes like file renaming or file deletion. + * @since 5.2 + */ + public static final Metric LAST_COMMIT_DATE = new Metric.Builder(LAST_COMMIT_DATE_KEY, "Date of last commit", Metric.ValueType.MILLISEC) + .setDomain(CoreMetrics.DOMAIN_SCM) + .create(); + private static final List METRICS; static { -- 2.39.5