]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6353 new metric days_since_last_commit
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Fri, 4 Sep 2015 15:20:26 +0000 (17:20 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Mon, 7 Sep 2015 07:30:39 +0000 (09:30 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/source/LastCommitVisitor.java
server/sonar-server/src/test/java/org/sonar/server/computation/source/LastCommitVisitorTest.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java

index a3b9d9a85e265a1d23e18ffb3ed8da91cd29c1bf..b667c0d20cac83fc59c854cad5ba580acf943744 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.computation.source;
 
 import com.google.common.base.Optional;
 import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.utils.System2;
 import org.sonar.batch.protocol.output.BatchReport;
 import org.sonar.server.computation.batch.BatchReportReader;
 import org.sonar.server.computation.component.Component;
@@ -35,12 +36,16 @@ import static org.sonar.server.computation.component.ComponentVisitor.Order.POST
 
 public class LastCommitVisitor extends PathAwareVisitorAdapter<LastCommitVisitor.LastCommit> {
 
+  private static final long MILLISECONDS_PER_DAY = 1000L * 60 * 60 * 24;
+
   private final BatchReportReader reportReader;
   private final MeasureRepository measureRepository;
   private final Metric lastCommitDateMetric;
+  private final Metric daysSinceLastCommitDateMetric;
+  private final System2 system2;
 
   public LastCommitVisitor(BatchReportReader reportReader, MetricRepository metricRepository,
-    MeasureRepository measureRepository) {
+    MeasureRepository measureRepository, System2 system2) {
     super(CrawlerDepthLimit.LEAVES, POST_ORDER, new SimpleStackElementFactory<LastCommit>() {
       @Override
       public LastCommit createForAny(Component component) {
@@ -55,7 +60,9 @@ public class LastCommitVisitor extends PathAwareVisitorAdapter<LastCommitVisitor
     });
     this.reportReader = reportReader;
     this.measureRepository = measureRepository;
+    this.system2 = system2;
     this.lastCommitDateMetric = metricRepository.getByKey(CoreMetrics.LAST_COMMIT_DATE_KEY);
+    this.daysSinceLastCommitDateMetric = metricRepository.getByKey(CoreMetrics.DAYS_SINCE_LAST_COMMIT_KEY);
   }
 
   @Override
@@ -117,12 +124,19 @@ public class LastCommitVisitor extends PathAwareVisitorAdapter<LastCommitVisitor
     long maxDate = path.current().getDate();
     if (maxDate > 0L) {
       measureRepository.add(component, lastCommitDateMetric, Measure.newMeasureBuilder().create(maxDate));
+      measureRepository.add(component, daysSinceLastCommitDateMetric, Measure.newMeasureBuilder().create(daysBetween(system2.now(), maxDate)));
+
       if (!path.isRoot()) {
         path.parent().addDate(maxDate);
       }
     }
   }
 
+  private static int daysBetween(long d1, long d2) {
+    // limitation of metric type: long is not supported yet, so casting to int
+    return (int) (Math.abs(d1 - d2) / MILLISECONDS_PER_DAY);
+  }
+
   public static final class LastCommit {
     private long date = 0;
 
index 93dfebdfa928aa215e197287fc68b2e550a8a890..429301d0af92898ea1034258b0eb70b20ea3a073 100644 (file)
@@ -21,9 +21,11 @@ package org.sonar.server.computation.source;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.utils.System2;
 import org.sonar.batch.protocol.output.BatchReport;
 import org.sonar.server.computation.batch.BatchReportReaderRule;
 import org.sonar.server.computation.batch.TreeRootHolderRule;
@@ -38,6 +40,9 @@ 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.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.measures.CoreMetrics.DAYS_SINCE_LAST_COMMIT_KEY;
 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;
@@ -51,7 +56,14 @@ import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
 
 public class LastCommitVisitorTest {
 
-  public static final int FILE_REF = 1;
+  public static final int PROJECT_REF = 1;
+  public static final int MODULE_REF = 2;
+  public static final int FILE_1_REF = 1_111;
+  public static final int FILE_2_REF = 1_112;
+  public static final int FILE_3_REF = 1_121;
+  public static final int DIR_1_REF = 3;
+  public static final int DIR_2_REF = 4;
+  public static final long NOW = 1_800_000_000_000L;
 
   @Rule
   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
@@ -60,34 +72,63 @@ public class LastCommitVisitorTest {
   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
 
   @Rule
-  public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(CoreMetrics.LAST_COMMIT_DATE);
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+    .add(CoreMetrics.LAST_COMMIT_DATE)
+    .add(CoreMetrics.DAYS_SINCE_LAST_COMMIT);
 
   @Rule
   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
 
+  System2 system2 = mock(System2.class);
+
+  @Before
+  public void setUp() {
+    when(system2.now()).thenReturn(NOW);
+  }
+
   @Test
-  public void aggregate_date_of_last_commit_to_directories_and_project() throws Exception {
+  public void aggregate_date_of_last_commit_to_directories_and_project() {
+    final long FILE_1_DATE = 1_100_000_000_000L;
+    // FILE_2 is the most recent file in DIR_1
+    final long FILE_2_DATE = 1_200_000_000_000L;
+    // FILE_3 is the most recent file in the project
+    final long FILE_3_DATE = 1_300_000_000_000L;
+
     // simulate the output of visitFile()
-    LastCommitVisitor visitor = new LastCommitVisitor(reportReader, metricRepository, measureRepository) {
+    LastCommitVisitor visitor = new LastCommitVisitor(reportReader, metricRepository, measureRepository, system2) {
       @Override
       public void visitFile(Component file, Path<LastCommit> path) {
-        path.parent().addDate(file.getReportAttributes().getRef() * 1_000_000_000L);
+        long fileDate;
+        switch (file.getReportAttributes().getRef()) {
+          case FILE_1_REF:
+            fileDate = FILE_1_DATE;
+            break;
+          case FILE_2_REF:
+            fileDate = FILE_2_DATE;
+            break;
+          case FILE_3_REF:
+            fileDate = FILE_3_DATE;
+            break;
+          default:
+            throw new IllegalArgumentException();
+        }
+        path.parent().addDate(fileDate);
       }
     };
 
     // project with 1 module, 2 directories and 3 files
-    ReportComponent project = ReportComponent.builder(PROJECT, 1)
+    ReportComponent project = ReportComponent.builder(PROJECT, PROJECT_REF)
       .addChildren(
-        ReportComponent.builder(MODULE, 11)
+        ReportComponent.builder(MODULE, MODULE_REF)
           .addChildren(
-            ReportComponent.builder(DIRECTORY, 111)
+            ReportComponent.builder(DIRECTORY, DIR_1_REF)
               .addChildren(
-                createFileComponent(1111),
-                createFileComponent(1112))
+                createFileComponent(FILE_1_REF),
+                createFileComponent(FILE_2_REF))
               .build(),
-            ReportComponent.builder(DIRECTORY, 112)
+            ReportComponent.builder(DIRECTORY, DIR_2_REF)
               .addChildren(
-                createFileComponent(1121))
+                createFileComponent(FILE_3_REF))
               .build())
           .build())
       .build();
@@ -96,62 +137,79 @@ public class LastCommitVisitorTest {
     VisitorsCrawler underTest = new VisitorsCrawler(Lists.<ComponentVisitor>newArrayList(visitor));
     underTest.visit(project);
 
-    // directories
-    assertDate(111, 1_112_000_000_000L);
-    assertDate(112, 1_121_000_000_000L);
+    assertDate(DIR_1_REF, FILE_2_DATE);
+    assertDaysSinceLastCommit(DIR_1_REF, 6944 /* number of days between FILE_2_DATE and now */);
+    assertDate(DIR_2_REF, FILE_3_DATE);
+    assertDaysSinceLastCommit(DIR_2_REF, 5787 /* number of days between FILE_3_DATE and now */);
 
     // module = most recent commit date of directories
-    assertDate(11, 1_121_000_000_000L);
+    assertDate(MODULE_REF, FILE_3_DATE);
+    assertDaysSinceLastCommit(MODULE_REF, 5787 /* number of days between FILE_3_DATE and now */);
 
     // project
-    assertDate(1, 1_121_000_000_000L);
+    assertDate(PROJECT_REF, FILE_3_DATE);
+    assertDaysSinceLastCommit(PROJECT_REF, 5787 /* number of days between FILE_3_DATE and now */);
   }
 
   @Test
-  public void aggregate_date_of_last_commit_to_views() throws Exception {
+  public void aggregate_date_of_last_commit_to_views() {
+    final int VIEW_REF = 1;
+    final int SUBVIEW_1_REF = 2;
+    final int SUBVIEW_2_REF = 3;
+    final int SUBVIEW_3_REF = 4;
+    final int PROJECT_1_REF = 5;
+    final int PROJECT_2_REF = 6;
+    final int PROJECT_3_REF = 7;
+    final long PROJECT_1_DATE = 1_500_000_000_000L;
+    // the second project has the most recent commit date
+    final long PROJECT_2_DATE = 1_700_000_000_000L;
+    final long PROJECT_3_DATE = 1_600_000_000_000L;
     // view with 3 nested sub-views and 3 projects
-    ViewsComponent view = ViewsComponent.builder(VIEW, 1)
+    ViewsComponent view = ViewsComponent.builder(VIEW, VIEW_REF)
       .addChildren(
-        builder(SUBVIEW, 11)
+        builder(SUBVIEW, SUBVIEW_1_REF)
           .addChildren(
-            builder(SUBVIEW, 111)
+            builder(SUBVIEW, SUBVIEW_2_REF)
               .addChildren(
-                builder(PROJECT_VIEW, 1111).build(),
-                builder(PROJECT_VIEW, 1112).build())
+                builder(PROJECT_VIEW, PROJECT_1_REF).build(),
+                builder(PROJECT_VIEW, PROJECT_2_REF).build())
               .build(),
-            builder(SUBVIEW, 112)
+            builder(SUBVIEW, SUBVIEW_3_REF)
               .addChildren(
-                builder(PROJECT_VIEW, 1121).build())
+                builder(PROJECT_VIEW, PROJECT_3_REF).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));
+    measureRepository.addRawMeasure(PROJECT_1_REF, LAST_COMMIT_DATE_KEY, newMeasureBuilder().create(PROJECT_1_DATE));
+    measureRepository.addRawMeasure(PROJECT_2_REF, LAST_COMMIT_DATE_KEY, newMeasureBuilder().create(PROJECT_2_DATE));
+    measureRepository.addRawMeasure(PROJECT_3_REF, LAST_COMMIT_DATE_KEY, newMeasureBuilder().create(PROJECT_3_DATE));
 
-    VisitorsCrawler underTest = new VisitorsCrawler(Lists.<ComponentVisitor>newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository)));
+    VisitorsCrawler underTest = new VisitorsCrawler(Lists.<ComponentVisitor>newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository, system2)));
     underTest.visit(view);
 
     // second level of sub-views
-    assertDate(111, 1_700_000_000_000L);
-    assertDate(112, 1_600_000_000_000L);
+    assertDate(SUBVIEW_2_REF, PROJECT_2_DATE);
+    assertDaysSinceLastCommit(SUBVIEW_2_REF, 1157 /* nb of days between PROJECT_2_DATE and NOW */);
+    assertDate(SUBVIEW_3_REF, PROJECT_3_DATE);
+    assertDaysSinceLastCommit(SUBVIEW_3_REF, 2314 /* nb of days between PROJECT_3_DATE and NOW */);
 
     // first level of sub-views
-    assertDate(11, 1_700_000_000_000L);
+    assertDate(SUBVIEW_1_REF, PROJECT_2_DATE);
+    assertDaysSinceLastCommit(SUBVIEW_1_REF, 1157 /* nb of days between PROJECT_2_DATE and NOW */);
 
     // view
-    assertDate(1, 1_700_000_000_000L);
+    assertDate(VIEW_REF, PROJECT_2_DATE);
+    assertDaysSinceLastCommit(VIEW_REF, 1157 /* nb of days between PROJECT_2_DATE and NOW */);
   }
 
   @Test
   public void compute_date_of_file_from_blame_info_of_report() throws Exception {
-    VisitorsCrawler underTest = new VisitorsCrawler(Lists.<ComponentVisitor>newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository)));
+    VisitorsCrawler underTest = new VisitorsCrawler(Lists.<ComponentVisitor>newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository, system2)));
 
     BatchReport.Changesets changesets = BatchReport.Changesets.newBuilder()
-      .setComponentRef(FILE_REF)
+      .setComponentRef(FILE_1_REF)
       .addChangeset(BatchReport.Changesets.Changeset.newBuilder()
         .setAuthor("john")
         .setDate(1_500_000_000_000L)
@@ -171,12 +229,13 @@ public class LastCommitVisitorTest {
       .addChangesetIndexByLine(0)
       .build();
     reportReader.putChangesets(changesets);
-    ReportComponent file = createFileComponent(FILE_REF);
+    ReportComponent file = createFileComponent(FILE_1_REF);
     treeRootHolder.setRoot(file);
 
     underTest.visit(file);
 
-    assertDate(FILE_REF, 1_600_000_000_000L);
+    assertDate(FILE_1_REF, 1_600_000_000_000L);
+    assertDaysSinceLastCommit(FILE_1_REF, 2314);
   }
 
   private void assertDate(int componentRef, long expectedDate) {
@@ -185,6 +244,12 @@ public class LastCommitVisitorTest {
     assertThat(measure.get().getLongValue()).isEqualTo(expectedDate);
   }
 
+  private void assertDaysSinceLastCommit(int componentRef, int numberOfDays) {
+    Optional<Measure> measure = measureRepository.getAddedRawMeasure(componentRef, DAYS_SINCE_LAST_COMMIT_KEY);
+    assertThat(measure.isPresent()).isTrue();
+    assertThat(measure.get().getIntValue()).isEqualTo(numberOfDays);
+  }
+
   /**
    * 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
@@ -192,25 +257,25 @@ public class LastCommitVisitorTest {
    */
   @Test
   public void reuse_date_of_previous_analysis_if_blame_info_is_not_in_report() throws Exception {
-    VisitorsCrawler underTest = new VisitorsCrawler(Lists.<ComponentVisitor>newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository)));
-    ReportComponent file = createFileComponent(FILE_REF);
+    VisitorsCrawler underTest = new VisitorsCrawler(Lists.<ComponentVisitor>newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository, system2)));
+    ReportComponent file = createFileComponent(FILE_1_REF);
     treeRootHolder.setRoot(file);
-    measureRepository.addBaseMeasure(FILE_REF, LAST_COMMIT_DATE_KEY, newMeasureBuilder().create(1_500_000_000L));
+    measureRepository.addBaseMeasure(FILE_1_REF, LAST_COMMIT_DATE_KEY, newMeasureBuilder().create(1_500_000_000L));
 
     underTest.visit(file);
 
-    assertDate(FILE_REF, 1_500_000_000L);
+    assertDate(FILE_1_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.<ComponentVisitor>newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository)));
-    ReportComponent file = createFileComponent(FILE_REF);
+    VisitorsCrawler underTest = new VisitorsCrawler(Lists.<ComponentVisitor>newArrayList(new LastCommitVisitor(reportReader, metricRepository, measureRepository, system2)));
+    ReportComponent file = createFileComponent(FILE_1_REF);
     treeRootHolder.setRoot(file);
 
     underTest.visit(file);
 
-    Optional<Measure> measure = measureRepository.getAddedRawMeasure(FILE_REF, LAST_COMMIT_DATE_KEY);
+    Optional<Measure> measure = measureRepository.getAddedRawMeasure(FILE_1_REF, LAST_COMMIT_DATE_KEY);
     assertThat(measure.isPresent()).isFalse();
   }
 
index 797c48bf78cf61e405ceb5608ee97deb7442debc..f214f49bb414c6e369b4adef925b509446a39b6a 100644 (file)
@@ -2224,7 +2224,7 @@ public final class CoreMetrics {
   /**
    * @since 5.2 – was computed in the dev cockpit plugin previously
    */
-  public static final Metric DAYS_SINCE_LAST_COMMIT = new Metric.Builder(DAYS_SINCE_LAST_COMMIT_KEY, "Days since last commit", Metric.ValueType.INT)
+  public static final Metric<Integer> DAYS_SINCE_LAST_COMMIT = new Metric.Builder(DAYS_SINCE_LAST_COMMIT_KEY, "Days since last commit", Metric.ValueType.INT)
     .setDomain(CoreMetrics.DOMAIN_SCM)
     .create();