]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6260 Create a PeriodsRepository
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 5 Jun 2015 13:31:20 +0000 (15:31 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 10 Jun 2015 12:30:39 +0000 (14:30 +0200)
The goal is to copy behaviour of PeriodsDefinition and TimeMachineConfiguration from batch in the Compute Engin

24 files changed:
server/sonar-server/src/main/java/org/sonar/server/component/db/SnapshotDao.java
server/sonar-server/src/main/java/org/sonar/server/computation/period/Period.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodFinder.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsRepository.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/period/package-info.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/component/db/SnapshotDaoTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodFinderTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsRepositoryTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStepTest.java
server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_previous_version_snapshots.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_snapshots_by_query.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_date.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_days.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_analysis.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_version.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_version.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/no_previous_version.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/previous_version_deleted.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodsRepositoryTest/shared.xml [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/SnapshotQuery.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/db/SnapshotMapper.java
sonar-core/src/main/resources/org/sonar/core/component/db/SnapshotMapper.xml
sonar-core/src/test/java/org/sonar/core/component/SnapshotQueryTest.java [new file with mode: 0644]

index 6682f541987967429975c059385087ceb14f2674..fde3d9be2e74d39e988e1f54b76e928d43cbea66 100644 (file)
@@ -25,6 +25,7 @@ import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.api.resources.Scopes;
 import org.sonar.core.component.SnapshotDto;
+import org.sonar.core.component.SnapshotQuery;
 import org.sonar.core.component.db.SnapshotMapper;
 import org.sonar.core.persistence.DaoComponent;
 import org.sonar.core.persistence.DbSession;
@@ -51,7 +52,15 @@ public class SnapshotDao implements DaoComponent {
   }
 
   public List<SnapshotDto> selectSnapshotsByComponentId(DbSession session, long componentId) {
-    return mapper(session).selectSnapshotsByComponentId(componentId);
+    return mapper(session).selectSnapshotsByQuery(new SnapshotQuery().setComponentId(componentId));
+  }
+
+  public List<SnapshotDto> selectSnapshotsByQuery(DbSession session, SnapshotQuery query) {
+    return mapper(session).selectSnapshotsByQuery(query);
+  }
+
+  public List<SnapshotDto> selectPreviousVersionSnapshots(DbSession session, long componentId, String lastVersion) {
+    return mapper(session).selectPreviousVersionSnapshots(componentId, lastVersion);
   }
 
   public List<SnapshotDto> selectSnapshotAndChildrenOfProjectScope(DbSession session, long snapshotId) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/period/Period.java b/server/sonar-server/src/main/java/org/sonar/server/computation/period/Period.java
new file mode 100644 (file)
index 0000000..0ad15f7
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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.period;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Calendar;
+import java.util.Date;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.component.SnapshotDto;
+
+public class Period {
+
+  private int index;
+  private String mode, modeParameter;
+  private SnapshotDto projectSnapshot;
+  /**
+   * Date resolved from the settings of the period
+   * - date : the given date
+   * - days : nearest analysis date found
+   * - previous analysis : date of the last analysis
+   * - previous version : date of the analysis of the previous version
+   * - version : date of the analysis of the given version
+   */
+  private Long targetDate = null;
+
+  public Period(String mode, @Nullable Long targetDate, SnapshotDto projectSnapshot) {
+    this.mode = mode;
+    if (targetDate != null) {
+      this.targetDate = targetDate;
+    }
+    this.projectSnapshot = projectSnapshot;
+  }
+
+  public Period setIndex(int index) {
+    this.index = index;
+    return this;
+  }
+
+  public int getIndex() {
+    return index;
+  }
+
+  public SnapshotDto getProjectSnapshot() {
+    return projectSnapshot;
+  }
+
+  /**
+   * Date of the snapshot
+   */
+  public Long getSnapshotDate() {
+    return projectSnapshot != null ? projectSnapshot.getCreatedAt() : null;
+  }
+
+  public String getMode() {
+    return mode;
+  }
+
+  public String getModeParameter() {
+    return modeParameter;
+  }
+
+  public Period setModeParameter(String s) {
+    this.modeParameter = s;
+    return this;
+  }
+
+  @VisibleForTesting
+  Long getTargetDate() {
+    return targetDate;
+  }
+
+  @Override
+  public String toString() {
+    Date snapshotDate = new Date(getSnapshotDate());
+    if (StringUtils.equals(mode, CoreProperties.TIMEMACHINE_MODE_VERSION)) {
+      String label = String.format("Compare to version %s", modeParameter);
+      if (targetDate != null) {
+        label += String.format(" (%s)", DateUtils.formatDate(snapshotDate));
+      }
+      return label;
+    }
+    if (StringUtils.equals(mode, CoreProperties.TIMEMACHINE_MODE_DAYS)) {
+      return String.format("Compare over %s days (%s, analysis of %s)", modeParameter, formatDate(),DateUtils.formatDate(snapshotDate));
+    }
+    if (StringUtils.equals(mode, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS)) {
+      return String.format("Compare to previous analysis (%s)", DateUtils.formatDate(snapshotDate));
+    }
+    if (StringUtils.equals(mode, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION)) {
+      return String.format("Compare to previous version (%s)", DateUtils.formatDate(snapshotDate));
+    }
+    if (StringUtils.equals(mode, CoreProperties.TIMEMACHINE_MODE_DATE)) {
+      return String.format("Compare to date %s (analysis of %s)", formatDate(), DateUtils.formatDate(snapshotDate));
+    }
+    return ReflectionToStringBuilder.toString(this);
+  }
+
+  private String formatDate() {
+    return DateUtils.formatDate(org.apache.commons.lang.time.DateUtils.truncate(new Date(targetDate), Calendar.SECOND));
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodFinder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodFinder.java
new file mode 100644 (file)
index 0000000..a451fd6
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.period;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.component.SnapshotDto;
+import org.sonar.core.component.SnapshotQuery;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.server.db.DbClient;
+
+import static org.sonar.core.component.SnapshotQuery.SORT_FIELD.BY_DATE;
+import static org.sonar.core.component.SnapshotQuery.SORT_ORDER.ASC;
+import static org.sonar.core.component.SnapshotQuery.SORT_ORDER.DESC;
+
+public class PeriodFinder {
+
+  private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(DateUtils.DATE_FORMAT);
+
+  private final DbClient dbClient;
+
+  public PeriodFinder(DbClient dbClient) {
+    this.dbClient = dbClient;
+  }
+
+  @CheckForNull
+  Period findByDate(DbSession session, Long projectId, Date date) {
+    SnapshotDto snapshot = findFirstSnapshot(session, createCommonQuery(projectId).setCreatedAfter(date.getTime()).setSort(BY_DATE, ASC));
+    if (snapshot == null) {
+      return null;
+    }
+    return new Period(CoreProperties.TIMEMACHINE_MODE_DATE, date.getTime(), snapshot).setModeParameter(DATE_FORMAT.format(date));
+  }
+
+  @CheckForNull
+  Period findByDays(DbSession session, Long projectId, long analysisDate, int days) {
+    List<SnapshotDto> snapshots = dbClient.snapshotDao().selectSnapshotsByQuery(session, createCommonQuery(projectId).setCreatedBefore(analysisDate).setSort(BY_DATE, ASC));
+    long targetDate = DateUtils.addDays(new Date(analysisDate), -days).getTime();
+    SnapshotDto snapshot = findNearestSnapshotToTargetDate(snapshots, targetDate);
+    if (snapshot == null) {
+      return null;
+    }
+    return new Period(CoreProperties.TIMEMACHINE_MODE_DAYS, targetDate, snapshot).setModeParameter(String.valueOf(days));
+  }
+
+  @CheckForNull
+  Period findByPreviousAnalysis(DbSession session, Long projectId, long analysisDate) {
+    SnapshotDto snapshot = findFirstSnapshot(session, createCommonQuery(projectId).setCreatedBefore(analysisDate).setIsLast(true).setSort(BY_DATE, DESC));
+    if (snapshot == null) {
+      return null;
+    }
+    return new Period(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS, snapshot.getCreatedAt(), snapshot).setModeParameter(DATE_FORMAT.format(snapshot.getCreatedAt()));
+  }
+
+  @CheckForNull
+  Period findByPreviousVersion(DbSession session, Long projectId, String version) {
+    List<SnapshotDto> snapshotDtos = dbClient.snapshotDao().selectPreviousVersionSnapshots(session, projectId, version);
+    if (snapshotDtos.isEmpty()) {
+      return null;
+    }
+    SnapshotDto snapshotDto = snapshotDtos.get(0);
+    return new Period(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, snapshotDto.getCreatedAt(), snapshotDto).setModeParameter(snapshotDto.getVersion());
+  }
+
+  @CheckForNull
+  Period findByVersion(DbSession session, Long projectId, String version) {
+    SnapshotDto snapshot = findFirstSnapshot(session, createCommonQuery(projectId).setVersion(version).setSort(BY_DATE, DESC));
+    if (snapshot == null) {
+      return null;
+    }
+    return new Period(CoreProperties.TIMEMACHINE_MODE_VERSION, snapshot.getCreatedAt(), snapshot).setModeParameter(version);
+  }
+
+  @VisibleForTesting
+  @CheckForNull
+  static SnapshotDto findNearestSnapshotToTargetDate(List<SnapshotDto> snapshots, Long targetDate) {
+    long bestDistance = Long.MAX_VALUE;
+    SnapshotDto nearest = null;
+    for (SnapshotDto snapshot : snapshots) {
+      long distance = Math.abs(snapshot.getCreatedAt() - targetDate);
+      if (distance <= bestDistance) {
+        bestDistance = distance;
+        nearest = snapshot;
+      }
+    }
+    return nearest;
+  }
+
+  @CheckForNull
+  private SnapshotDto findFirstSnapshot(DbSession session, SnapshotQuery query) {
+    List<SnapshotDto> snapshots = dbClient.snapshotDao().selectSnapshotsByQuery(session, query);
+    if (snapshots.size() >= 1) {
+      return snapshots.get(0);
+    }
+    return null;
+  }
+
+  private static SnapshotQuery createCommonQuery(Long projectId) {
+    return new SnapshotQuery().setComponentId(projectId).setStatus(SnapshotDto.STATUS_PROCESSED);
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsRepository.java
new file mode 100644 (file)
index 0000000..cf2181f
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * 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.period;
+
+import com.google.common.base.Strings;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.server.computation.batch.BatchReportReader;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.TreeRootHolder;
+import org.sonar.server.db.DbClient;
+
+/**
+ * Repository of periods used to compute differential measures.
+ * Here are the steps to retrieve these periods :
+ * - Read the 5 period properties ${@link CoreProperties#TIMEMACHINE_PERIOD_PREFIX}
+ * - Try to find the matching snapshots from the properties
+ * - If a snapshot is found, a new period is added to the repository
+ */
+public class PeriodsRepository {
+
+  private static final Logger LOG = LoggerFactory.getLogger(PeriodsRepository.class);
+
+  private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(DateUtils.DATE_FORMAT);
+
+  private static final int NUMBER_OF_PERIODS = 5;
+
+  private final DbClient dbClient;
+  private final Settings settings;
+  private final TreeRootHolder treeRootHolder;
+  private final PeriodFinder periodFinder;
+  private final BatchReportReader batchReportReader;
+
+  private List<Period> periods = new ArrayList<>();
+
+  public PeriodsRepository(DbClient dbClient, Settings settings, TreeRootHolder treeRootHolder, PeriodFinder periodFinder, BatchReportReader batchReportReader) {
+    this.dbClient = dbClient;
+    this.settings = settings;
+    this.treeRootHolder = treeRootHolder;
+    this.periodFinder = periodFinder;
+    this.batchReportReader = batchReportReader;
+  }
+
+  public List<Period> getPeriods(){
+   if (periods.isEmpty()) {
+      initPeriods();
+    }
+    return periods;
+  }
+
+  private void initPeriods() {
+    DbSession session = dbClient.openSession(false);
+    try {
+      Component project = treeRootHolder.getRoot();
+      ComponentDto projectDto = dbClient.componentDao().selectNullableByKey(session, project.getKey());
+      // No project on first analysis, no period
+      if (projectDto != null) {
+        BatchReport.Component batchProject = batchReportReader.readComponent(project.getRef());
+        PeriodResolver periodResolver = new PeriodResolver(session, projectDto.getId(), batchReportReader.readMetadata().getAnalysisDate(), batchProject.getVersion(),
+          // TODO qualifier will be different for Views
+          Qualifiers.PROJECT);
+
+        for (int index = 1; index <= NUMBER_OF_PERIODS; index++) {
+          Period period = periodResolver.resolve(index);
+          // SONAR-4700 Add a past snapshot only if it exists
+          if (period != null) {
+            periods.add(period.setIndex(index));
+            LOG.debug(period.toString());
+          }
+        }
+      }
+    } finally {
+      session.close();
+    }
+  }
+
+  private class PeriodResolver {
+
+    private final DbSession session;
+    private final long projectId;
+    private final long analysisDate;
+    private final String currentVersion;
+    private final String qualifier;
+
+    public PeriodResolver(DbSession session, long projectId, long analysisDate, String currentVersion, String qualifier) {
+      this.session = session;
+      this.projectId = projectId;
+      this.analysisDate = analysisDate;
+      this.currentVersion = currentVersion;
+      this.qualifier = qualifier;
+    }
+
+    @CheckForNull
+    private Period resolve(int index) {
+      String propertyValue = getPropertyValue(qualifier, settings, index);
+      Period period = resolve(index, propertyValue);
+      if (period == null && StringUtils.isNotBlank(propertyValue)) {
+        LOG.debug("Property " + CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index + " is not valid: " + propertyValue);
+      }
+      return period;
+    }
+
+    @CheckForNull
+    private Period resolve(int index, String property) {
+      if (StringUtils.isBlank(property)) {
+        return null;
+      }
+
+      Integer days = tryToResolveByDays(property);
+      if (days != null) {
+        return periodFinder.findByDays(session, projectId, analysisDate, days);
+      }
+      Date date = tryToResolveByDate(property);
+      if (date != null) {
+        return periodFinder.findByDate(session, projectId, date);
+      }
+      if (StringUtils.equals(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS, property)) {
+        return periodFinder.findByPreviousAnalysis(session, projectId, analysisDate);
+      }
+      if (StringUtils.equals(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, property)) {
+        return periodFinder.findByPreviousVersion(session, projectId, currentVersion);
+      }
+      return periodFinder.findByVersion(session, projectId, property);
+    }
+
+    @CheckForNull
+    private Integer tryToResolveByDays(String property){
+      try {
+        return Integer.parseInt(property);
+      } catch (NumberFormatException e) {
+        return null;
+      }
+    }
+
+    @CheckForNull
+    private Date tryToResolveByDate(String property){
+      try {
+        return DATE_FORMAT.parse(property);
+      } catch (ParseException e) {
+        return null;
+      }
+    }
+  }
+
+  private static String getPropertyValue(@Nullable String qualifier, Settings settings, int index) {
+    String value = settings.getString(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index);
+    // For periods 4 and 5 we're also searching for a property prefixed by the qualifier
+    if (index > 3 && Strings.isNullOrEmpty(value)) {
+      value = settings.getString(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index + "." + qualifier);
+    }
+    return value;
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/period/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/computation/period/package-info.java
new file mode 100644 (file)
index 0000000..d3d038b
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation.period;
+
+import javax.annotation.ParametersAreNonnullByDefault;
index a139417951bc7bece18e1c0f00a0e278a2424c8d..2000c1b1ba7762279fef97926bf3e467837a2455 100644 (file)
@@ -27,10 +27,14 @@ import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.core.component.SnapshotDto;
+import org.sonar.core.component.SnapshotQuery;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 import org.sonar.core.persistence.DbSession;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.component.SnapshotQuery.SORT_FIELD.BY_DATE;
+import static org.sonar.core.component.SnapshotQuery.SORT_ORDER.ASC;
+import static org.sonar.core.component.SnapshotQuery.SORT_ORDER.DESC;
 
 public class SnapshotDaoTest extends AbstractDaoTestCase {
 
@@ -91,19 +95,6 @@ public class SnapshotDaoTest extends AbstractDaoTestCase {
     assertThat(sut.selectNullableById(session, 999L)).isNull();
   }
 
-  @Test
-  public void insert() {
-    setupData("empty");
-
-    SnapshotDto dto = defaultSnapshot().setCreatedAt(1403042400000L);
-
-    sut.insert(session, dto);
-    session.commit();
-
-    assertThat(dto.getId()).isNotNull();
-    checkTables("insert", new String[] {"id"}, "snapshots");
-  }
-
   @Test
   public void lastSnapshot_returns_null_when_no_last_snapshot() {
     setupData("empty");
@@ -151,13 +142,48 @@ public class SnapshotDaoTest extends AbstractDaoTestCase {
     assertThat(snapshots).hasSize(3);
   }
 
+  @Test
+  public void select_snapshots_by_query() {
+    setupData("select_snapshots_by_query");
+
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery())).hasSize(6);
+
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(1L))).hasSize(3);
+
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(1L).setVersion("2.2-SNAPSHOT"))).extracting("id").containsOnly(3L);
+
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(1L).setIsLast(true))).extracting("id").containsOnly(1L);
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(1L).setIsLast(false))).extracting("id").containsOnly(2L, 3L);
+
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(1L).setCreatedAfter(1228172400002L))).extracting("id").containsOnly(2L, 3L);
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(1L).setCreatedBefore(1228172400002L))).extracting("id").containsOnly(1L);
+
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(2L).setStatus("P"))).hasSize(1);
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(2L).setStatus("U"))).hasSize(1);
+
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(1L).setSort(BY_DATE, ASC)).get(0).getId()).isEqualTo(1L);
+    assertThat(sut.selectSnapshotsByQuery(session, new SnapshotQuery().setComponentId(1L).setSort(BY_DATE, DESC)).get(0).getId()).isEqualTo(3L);
+  }
+
+  @Test
+  public void select_previous_version_snapshots() throws Exception {
+    setupData("select_previous_version_snapshots");
+
+    List<SnapshotDto> snapshots = sut.selectPreviousVersionSnapshots(session, 1L, "1.2-SNAPSHOT");
+    assertThat(snapshots).hasSize(2);
+
+    SnapshotDto firstSnapshot = snapshots.get(0);
+    assertThat(firstSnapshot.getVersion()).isEqualTo("1.1");
+
+    // All snapshots are returned on an unknown version
+    assertThat(sut.selectPreviousVersionSnapshots(session, 1L, "UNKNOWN")).hasSize(3);
+  }
+
   @Test
   public void insert() {
     setupData("empty");
 
-    when(system2.now()).thenReturn(1403042400000L);
-
-    SnapshotDto dto = defaultSnapshot();
+    SnapshotDto dto = defaultSnapshot().setCreatedAt(1403042400000L);
 
     sut.insert(session, dto);
     session.commit();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodFinderTest.java
new file mode 100644 (file)
index 0000000..37dd198
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * 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.period;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.api.CoreProperties;
+import org.sonar.core.component.SnapshotDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.component.db.SnapshotDao;
+import org.sonar.server.db.DbClient;
+import org.sonar.test.DbTests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Category(DbTests.class)
+public class PeriodFinderTest {
+
+  private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+
+  @ClassRule
+  public static final DbTester dbTester = new DbTester();
+
+  DbClient dbClient;
+
+  DbSession dbSession;
+
+  PeriodFinder periodFinder;
+
+  @Before
+  public void setUp() throws Exception {
+    dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new SnapshotDao());
+    dbSession = dbClient.openSession(false);
+    periodFinder = new PeriodFinder(dbClient);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    dbSession.close();
+  }
+
+  @Test
+  public void find_by_date() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_date.xml");
+    String textDate = "2008-11-22";
+    Date date = DATE_FORMAT.parse(textDate);
+
+    Period result = periodFinder.findByDate(dbSession, 1L, date);
+
+    // Return analysis from given date 2008-11-22
+    assertThat(result.getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_DATE);
+    assertThat(result.getModeParameter()).isEqualTo(textDate);
+    assertThat(result.getSnapshotDate()).isEqualTo(1227358680000L);
+    assertThat(result.getTargetDate()).isEqualTo(date.getTime());
+    assertThat(result.getProjectSnapshot().getId()).isEqualTo(1006L);
+  }
+
+  @Test
+  public void find_by_date_search_for_nearest_later_analysis() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_date.xml");
+    String date = "2008-11-24";
+
+    Period result = periodFinder.findByDate(dbSession, 1L, DATE_FORMAT.parse(date));
+
+    // No analysis have been done at this date, it should return the one from the date after (2008-11-25)
+    assertThat(result.getSnapshotDate()).isEqualTo(1227617880000L);
+    assertThat(result.getProjectSnapshot().getId()).isEqualTo(1009L);
+  }
+
+  @Test
+  public void not_find_by_date() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_date.xml");
+    String date = "2008-11-22";
+
+    // No analysis for this project
+    assertThat(periodFinder.findByDate(dbSession, 123L, DATE_FORMAT.parse(date))).isNull();
+  }
+
+  @Test
+  public void find_by_days() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_days.xml");
+
+    Period result = periodFinder.findByDays(dbSession, 1L, DATE_FORMAT.parse("2008-11-16").getTime(), 50);
+
+    // Return analysis from the 2008-11-01
+    assertThat(result.getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_DAYS);
+    assertThat(result.getModeParameter()).isEqualTo("50");
+    assertThat(result.getSnapshotDate()).isEqualTo(1225544280000L);
+    assertThat(result.getTargetDate()).isBetween(DATE_FORMAT.parse("2008-09-26").getTime(), DATE_FORMAT.parse("2008-09-27").getTime());
+    assertThat(result.getProjectSnapshot().getId()).isEqualTo(1000L);
+  }
+
+  @Test
+  public void ignore_unprocessed_snapshots() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_days.xml");
+
+    Period result = periodFinder.findByDays(dbSession, 1L, DATE_FORMAT.parse("2008-11-16").getTime(), 7);
+
+    // Return analysis from the 2008-11-13, the matching one from 2008-11-12 should be ignored
+    assertThat(result.getProjectSnapshot().getId()).isEqualTo(1006L);
+  }
+
+  @Test
+  public void not_find_by_days() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_days.xml");
+
+    // No analysis for this project
+    assertThat(periodFinder.findByDays(dbSession, 123L, DATE_FORMAT.parse("2008-11-16").getTime(), 7)).isNull();
+  }
+
+  @Test
+  public void locate_nearest_snapshot_before() throws ParseException {
+    Date current = DATE_FORMAT.parse("2010-10-20");
+    // distance: 15 => target is 2010-10-05
+
+    List<SnapshotDto> snapshots = Arrays.asList(
+      new SnapshotDto().setId(1L).setCreatedAt(DATE_FORMAT.parse("2010-09-30").getTime()),
+      new SnapshotDto().setId(2L).setCreatedAt(DATE_FORMAT.parse("2010-10-03").getTime()), // -2 days
+      new SnapshotDto().setId(3L).setCreatedAt(DATE_FORMAT.parse("2010-10-08").getTime()), // +3 days
+      new SnapshotDto().setId(4L).setCreatedAt(DATE_FORMAT.parse("2010-10-12").getTime())  // +7 days
+    );
+    assertThat(PeriodFinder.findNearestSnapshotToTargetDate(snapshots, DateUtils.addDays(current, -15).getTime()).getId()).isEqualTo(2);
+  }
+
+  @Test
+  public void locate_nearest_snapshot_after() throws ParseException {
+    Date current = DATE_FORMAT.parse("2010-10-20");
+    // distance: 15 => target is 2010-10-05
+
+    List<SnapshotDto> snapshots = Arrays.asList(
+      new SnapshotDto().setId(1L).setCreatedAt(DATE_FORMAT.parse("2010-09-30").getTime()),
+      new SnapshotDto().setId(2L).setCreatedAt(DATE_FORMAT.parse("2010-10-01").getTime()), // -4 days
+      new SnapshotDto().setId(3L).setCreatedAt(DATE_FORMAT.parse("2010-10-08").getTime()), // +3 days
+      new SnapshotDto().setId(4L).setCreatedAt(DATE_FORMAT.parse("2010-10-12").getTime()) // +7 days
+    );
+    assertThat(PeriodFinder.findNearestSnapshotToTargetDate(snapshots, DateUtils.addDays(current, -15).getTime()).getId()).isEqualTo(3);
+  }
+
+  @Test
+  public void find_by_previous_analysis() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_previous_analysis.xml");
+
+    Period result = periodFinder.findByPreviousAnalysis(dbSession, 1L, DATE_FORMAT.parse("2008-11-27").getTime());
+
+    // Return analysis from given date 2008-11-22
+    assertThat(result.getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS);
+    assertThat(result.getModeParameter()).isEqualTo(DATE_FORMAT.format(new Date(1227617880000L)));
+    assertThat(result.getSnapshotDate()).isEqualTo(1227617880000L);
+    assertThat(result.getTargetDate()).isEqualTo(1227617880000L);
+    assertThat(result.getProjectSnapshot().getId()).isEqualTo(1009L);
+  }
+
+  @Test
+  public void not_find_by_previous_analysis() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_previous_analysis.xml");
+
+    // No analysis for this project
+    assertThat(periodFinder.findByPreviousAnalysis(dbSession, 2L, DATE_FORMAT.parse("2008-11-27").getTime())).isNull();
+  }
+
+  @Test
+  public void find_by_previous_version() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_previous_version.xml");
+
+    Period result = periodFinder.findByPreviousVersion(dbSession, 1L, "1.2-SNAPSHOT");
+
+    // Return analysis from given date 2008-11-22
+    assertThat(result.getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION);
+    assertThat(result.getModeParameter()).isEqualTo("1.1");
+    assertThat(result.getSnapshotDate()).isEqualTo(1225803480000L);
+    assertThat(result.getTargetDate()).isEqualTo(1225803480000L);
+    assertThat(result.getProjectSnapshot().getId()).isEqualTo(1001L);
+  }
+
+  @Test
+  public void find_by_previous_version_when_previous_version_deleted() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "previous_version_deleted.xml");
+
+    Period result = periodFinder.findByPreviousVersion(dbSession, 1L, "1.2-SNAPSHOT");
+
+    // Return analysis from given date 2008-11-22
+    assertThat(result.getModeParameter()).isEqualTo("1.0");
+    assertThat(result.getSnapshotDate()).isEqualTo(1225630680000L);
+    assertThat(result.getTargetDate()).isEqualTo(1225630680000L);
+    assertThat(result.getProjectSnapshot().getId()).isEqualTo(1000L);
+  }
+
+  @Test
+  public void not_find_previous_version() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "no_previous_version.xml");
+
+    assertThat(periodFinder.findByPreviousVersion(dbSession, 1L, "1.2-SNAPSHOT")).isNull();
+  }
+
+  @Test
+  public void find_by_version() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_version.xml");
+
+    Period result = periodFinder.findByVersion(dbSession, 1L, "1.1");
+
+    assertThat(result.getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_VERSION);
+    assertThat(result.getModeParameter()).isEqualTo("1.1");
+    assertThat(result.getSnapshotDate()).isEqualTo(1225803480000L);
+    assertThat(result.getTargetDate()).isEqualTo(1225803480000L);
+    assertThat(result.getProjectSnapshot().getId()).isEqualTo(1009L);
+  }
+
+  @Test
+  public void not_find_by_version() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "find_by_version.xml");
+
+    assertThat(periodFinder.findByVersion(dbSession, 1L, "1.0")).isNull();
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodTest.java
new file mode 100644 (file)
index 0000000..99cfae1
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.period;
+
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.core.component.SnapshotDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PeriodTest {
+
+  @Test
+  public void test_some_setters_and_getters() {
+    Long date = System.currentTimeMillis();
+    SnapshotDto snapshotDto = new SnapshotDto().setCreatedAt(date);
+    Period period = new Period(CoreProperties.TIMEMACHINE_MODE_VERSION, 1000L, snapshotDto)
+      .setModeParameter("2.3")
+      .setIndex(1);
+
+    assertThat(period.getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_VERSION);
+    assertThat(period.getModeParameter()).isEqualTo("2.3");
+    assertThat(period.getIndex()).isEqualTo(1);
+    assertThat(period.getProjectSnapshot()).isEqualTo(snapshotDto);
+    assertThat(period.getSnapshotDate()).isEqualTo(date);
+    assertThat(period.getTargetDate()).isEqualTo(1000L);
+  }
+
+  @Test
+  public void to_string_for_version() {
+    Period period = new Period(CoreProperties.TIMEMACHINE_MODE_VERSION, 1000L, new SnapshotDto().setCreatedAt(System.currentTimeMillis())).setModeParameter("2.3");
+    assertThat(period.toString()).startsWith("Compare to version 2.3");
+  }
+
+  @Test
+  public void to_string_for_version_without_date() {
+    Period period = new Period(CoreProperties.TIMEMACHINE_MODE_VERSION, null, new SnapshotDto().setCreatedAt(System.currentTimeMillis())).setModeParameter("2.3");
+    assertThat(period.toString()).isEqualTo("Compare to version 2.3");
+  }
+
+  @Test
+  public void to_string_for_number_of_days() {
+    Period period = new Period(CoreProperties.TIMEMACHINE_MODE_DAYS, 1000L, new SnapshotDto().setCreatedAt(System.currentTimeMillis())).setModeParameter("30");
+    assertThat(period.toString()).startsWith("Compare over 30 days (");
+  }
+
+  @Test
+  public void to_string_for_number_of_days_with_snapshot() {
+    Period period = new Period(CoreProperties.TIMEMACHINE_MODE_DAYS, 1000L, new SnapshotDto().setCreatedAt(System.currentTimeMillis())).setModeParameter("30");
+    assertThat(period.toString()).startsWith("Compare over 30 days (");
+  }
+
+  @Test
+  public void to_string_for_date() {
+    Period period = new Period(CoreProperties.TIMEMACHINE_MODE_DATE, 1000L, new SnapshotDto().setCreatedAt(System.currentTimeMillis()));
+    assertThat(period.toString()).startsWith("Compare to date ");
+  }
+
+  @Test
+  public void to_string_for_date_with_snapshot() {
+    Period period = new Period(CoreProperties.TIMEMACHINE_MODE_DATE, 1000L, new SnapshotDto().setCreatedAt(System.currentTimeMillis()));
+    assertThat(period.toString()).startsWith("Compare to date ");
+  }
+
+  @Test
+  public void to_string_for_previous_analysis() {
+    Period period = new Period(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS, 1000L, new SnapshotDto().setCreatedAt(System.currentTimeMillis()));
+    assertThat(period.toString()).startsWith("Compare to previous analysis ");
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsRepositoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsRepositoryTest.java
new file mode 100644 (file)
index 0000000..9a61d70
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * 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.period;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.protocol.Constants;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.component.db.SnapshotDao;
+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.DumbComponent;
+import org.sonar.server.db.DbClient;
+import org.sonar.test.DbTests;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Category(DbTests.class)
+public class PeriodsRepositoryTest {
+
+  private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+  private static final String PROJECT_KEY = "PROJECT_KEY";
+
+  @ClassRule
+  public static final DbTester dbTester = new DbTester();
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+  @Rule
+  public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+
+  DbClient dbClient;
+
+  DbSession dbSession;
+
+  Settings settings = new Settings();
+
+  PeriodsRepository periodsRepository;
+
+  @Before
+  public void setUp() throws Exception {
+    dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao(), new SnapshotDao());
+    dbSession = dbClient.openSession(false);
+
+    reportReader.setMetadata(BatchReport.Metadata.newBuilder()
+      .setAnalysisDate(DATE_FORMAT.parse("2008-11-30").getTime())
+      .build());
+
+    reportReader.putComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey(PROJECT_KEY)
+      .setVersion("1.1")
+      .build());
+
+    Component project = new DumbComponent(Component.Type.PROJECT, 1, "ABCD", PROJECT_KEY);
+    treeRootHolder.setRoot(project);
+
+    periodsRepository = new PeriodsRepository(dbClient, settings, treeRootHolder, new PeriodFinder(dbClient), reportReader);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    dbSession.close();
+  }
+
+  @Test
+  public void no_period_on_first_analysis() throws Exception {
+    // No project, no snapshot
+
+    assertThat(periodsRepository.getPeriods()).isEmpty();
+  }
+
+  @Test
+  public void get_one_period() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+    String textDate = "2008-11-22";
+    Date date = DATE_FORMAT.parse(textDate);
+    settings.setProperty("sonar.timemachine.period1", textDate);
+
+    List<Period> periods = periodsRepository.getPeriods();
+    assertThat(periods).hasSize(1);
+
+    Period period =  periods.get(0);
+    assertThat(period.getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_DATE);
+    assertThat(period.getModeParameter()).isEqualTo(textDate);
+    assertThat(period.getSnapshotDate()).isEqualTo(1227358680000L);
+    assertThat(period.getTargetDate()).isEqualTo(date.getTime());
+    assertThat(period.getProjectSnapshot().getId()).isEqualTo(1003L);
+  }
+
+  @Test
+  public void no_period_when_settings_match_no_analysis() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+    settings.setProperty("sonar.timemachine.period1", "UNKNWOWN VERSION");
+
+    assertThat(periodsRepository.getPeriods()).isEmpty();
+  }
+
+  @Test
+  public void no_period_when_settings_is_empty() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+    settings.setProperty("sonar.timemachine.period1", "");
+
+    assertThat(periodsRepository.getPeriods()).isEmpty();
+  }
+
+  @Test
+  public void get_five_different_periods() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+    settings.setProperty("sonar.timemachine.period1", "2008-11-22"); // Analysis from 2008-11-22 should be returned
+    settings.setProperty("sonar.timemachine.period2", "10"); // Analysis from 2008-11-20 should be returned
+    settings.setProperty("sonar.timemachine.period3", "previous_analysis"); // Analysis from 2008-11-29 should be returned
+    settings.setProperty("sonar.timemachine.period4", "previous_version"); // Analysis from 2008-11-12 should be returned
+    settings.setProperty("sonar.timemachine.period5", "0.9"); // Anaylsis from 2008-11-11
+
+    List<Period> periods = periodsRepository.getPeriods();
+    List<String> periodModes = newArrayList(Iterables.transform(periods, new Function<Period, String>() {
+      @Override
+      public String apply(Period input) {
+        return input.getMode();
+      }
+    }));
+    assertThat(periodModes).containsOnly(CoreProperties.TIMEMACHINE_MODE_DATE, CoreProperties.TIMEMACHINE_MODE_DAYS, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS,
+      CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, CoreProperties.TIMEMACHINE_MODE_VERSION);
+
+    assertThat(periods.get(0).getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_DATE);
+    assertThat(periods.get(0).getIndex()).isEqualTo(1);
+    assertThat(periods.get(0).getProjectSnapshot().getId()).isEqualTo(1003L);
+
+    assertThat(periods.get(1).getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_DAYS);
+    assertThat(periods.get(1).getIndex()).isEqualTo(2);
+    assertThat(periods.get(1).getProjectSnapshot().getId()).isEqualTo(1002L);
+
+    assertThat(periods.get(2).getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS);
+    assertThat(periods.get(2).getIndex()).isEqualTo(3);
+    assertThat(periods.get(2).getProjectSnapshot().getId()).isEqualTo(1004L);
+
+    assertThat(periods.get(3).getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION);
+    assertThat(periods.get(3).getIndex()).isEqualTo(4);
+    assertThat(periods.get(3).getProjectSnapshot().getId()).isEqualTo(1001L);
+
+    assertThat(periods.get(4).getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_VERSION);
+    assertThat(periods.get(4).getIndex()).isEqualTo(5);
+    assertThat(periods.get(4).getProjectSnapshot().getId()).isEqualTo(1000L);
+  }
+
+  @Test
+  public void can_use_qualifier_in_settings() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+    settings.setProperty("sonar.timemachine.period4.TRK", "2008-11-22");
+    settings.setProperty("sonar.timemachine.period5.TRK", "previous_analysis");
+
+    assertThat(periodsRepository.getPeriods()).hasSize(2);
+  }
+
+  @Test
+  public void only_load_periods_on_first_call_of_get_periods() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+    settings.setProperty("sonar.timemachine.period1", "2008-11-22");
+
+    // First call, periods are loaded
+    assertThat(periodsRepository.getPeriods()).hasSize(1);
+
+    // Second call, set project without key to check that it won't fail with a NPE
+    treeRootHolder.setRoot(new DumbComponent(Component.Type.PROJECT, 1, null, null));
+    assertThat(periodsRepository.getPeriods()).hasSize(1);
+  }
+
+}
index 8f89c938805fdcd466bfefa0b65475bcfa976ce3..becc43aacdef14505cf40ceb8ded85fd87af4b4d 100644 (file)
@@ -79,7 +79,7 @@ public class PersistComponentsAndSnapshotsStepTest extends BaseStepTest {
   public void setup() throws Exception {
     dbTester.truncateTables();
     session = dbTester.myBatis().openSession(false);
-    dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao(), new SnapshotDao(system2));
+    dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao(), new SnapshotDao());
     reportReader.setMetadata(BatchReport.Metadata.newBuilder()
       .setAnalysisDate(ANALYSIS_DATE)
       .build());
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_previous_version_snapshots.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_previous_version_snapshots.xml
new file mode 100644 (file)
index 0000000..8329098
--- /dev/null
@@ -0,0 +1,40 @@
+<dataset>
+
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
+            root_id="[null]" uuid="ABCD"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+  <!-- version 1.0 -->
+  <snapshots id="1000" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225630680000" build_date="1225630680000" version="1.0" path=""
+             status="P" islast="false" depth="0" />
+
+  <!-- version 1.1 -->
+  <snapshots id="1001" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225803480000" build_date="1225803480000" version="1.1" path=""
+             status="P" islast="false" depth="0" />
+
+  <!-- version 1.2-SNAPSHOT -->
+  <snapshots id="1002" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225976280000" build_date="1225976280000" version="1.2-SNAPSHOT" path=""
+             status="P" islast="false" depth="0" />
+
+  <!-- version 1.2-SNAPSHOT, current analysis -->
+  <snapshots id="1003" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1226235480000" build_date="1226235480000" version="1.2-SNAPSHOT" path=""
+             status="U" islast="true" depth="0" />
+
+  <events id="1" name="1.0" component_uuid="ABCD" snapshot_id="1000" category="Version" event_date="1225630680000" created_at="1225630680000" description="" event_data="[null]"/>
+  <events id="2" name="Foo" component_uuid="ABCD" snapshot_id="1000" category="Other" event_date="1225717080000" created_at="1225717080000" description="" event_data="[null]"/>
+  <events id="3" name="1.1" component_uuid="ABCD" snapshot_id="1001" category="Version" event_date="1225803480000" created_at="1225803480000" description="" event_data="[null]"/>
+  <events id="4" name="Bar" component_uuid="ABCD" snapshot_id="1001" category="Other" event_date="1225889880000" created_at="1225889880000" description="" event_data="[null]"/>
+  <events id="5" name="Uhh" component_uuid="ABCD" snapshot_id="1002" category="Other" event_date="1226062680000" created_at="1226062680000" description="" event_data="[null]"/>
+  <events id="6" name="1.2-SNAPSHOT" component_uuid="ABCD" snapshot_id="1003" category="Version" event_date="1226235480000" created_at="1226235480000" description="" event_data="[null]"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_snapshots_by_query.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_snapshots_by_query.xml
new file mode 100644 (file)
index 0000000..1f32010
--- /dev/null
@@ -0,0 +1,64 @@
+<dataset>
+
+  <!-- PROJECT_ID = 1 -->
+  <snapshots id="1" project_id="1" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="1"
+             period1_mode="days1" period1_param="30" period1_date="1316815200000"
+             period2_mode="days2" period2_param="31" period2_date="1316901600000"
+             period3_mode="days3" period3_param="32" period3_date="1316988000000"
+             period4_mode="days4" period4_param="33" period4_date="1317074400000"
+             period5_mode="days5" period5_param="34" period5_date="1317160800000"
+             depth="1" scope="PRJ" qualifier="PAC" created_at="1228172400001" build_date="1317247200000"
+             version="2.0-SNAPSHOT" path="1.2."/>
+  <snapshots id="2" project_id="1" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="3"
+             status="P" islast="[false]" purge_status="1"
+             period1_mode="days1" period1_param="30" period1_date="1316815200000"
+             period2_mode="days2" period2_param="31" period2_date="1316901600000"
+             period3_mode="days3" period3_param="32" period3_date="1316988000000"
+             period4_mode="days4" period4_param="33" period4_date="1317074400000"
+             period5_mode="days5" period5_param="34" period5_date="1317160800000"
+             depth="1" scope="DIR" qualifier="PAC" created_at="1228172400002" build_date="1317247200000"
+             version="2.1-SNAPSHOT" path="1.2."/>
+  <snapshots id="3" project_id="1" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="3"
+             status="P" islast="[false]" purge_status="1"
+             period1_mode="days1" period1_param="30" period1_date="1316815200000"
+             period2_mode="days2" period2_param="31" period2_date="1316901600000"
+             period3_mode="days3" period3_param="32" period3_date="1316988000000"
+             period4_mode="days4" period4_param="33" period4_date="1317074400000"
+             period5_mode="days5" period5_param="34" period5_date="1317160800000"
+             depth="1" scope="DIR" qualifier="PAC" created_at="1228172400003" build_date="1317247200000"
+             version="2.2-SNAPSHOT" path="1.2."/>
+
+  <!-- PROJECT_ID = 2 -->
+  <snapshots id="4" project_id="2" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="3"
+             status="P" islast="[true]" purge_status="1"
+             period1_mode="days1" period1_param="30" period1_date="1316815200000"
+             period2_mode="days2" period2_param="31" period2_date="1316901600000"
+             period3_mode="days3" period3_param="32" period3_date="1316988000000"
+             period4_mode="days4" period4_param="33" period4_date="1317074400000"
+             period5_mode="days5" period5_param="34" period5_date="1317160800000"
+             depth="1" scope="DIR" qualifier="PAC" created_at="1228172400000" build_date="1317247200000"
+             version="2.1-SNAPSHOT" path="1.2."/>
+  <!-- Unprocessed snapshot -->
+  <snapshots id="5" project_id="2" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="3"
+             status="U" islast="[true]" purge_status="1"
+             period1_mode="days1" period1_param="30" period1_date="1316815200000"
+             period2_mode="days2" period2_param="31" period2_date="1316901600000"
+             period3_mode="days3" period3_param="32" period3_date="1316988000000"
+             period4_mode="days4" period4_param="33" period4_date="1317074400000"
+             period5_mode="days5" period5_param="34" period5_date="1317160800000"
+             depth="1" scope="DIR" qualifier="PAC" created_at="1228172400000" build_date="1317247200000"
+             version="2.1-SNAPSHOT" path="1.2."/>
+
+  <!-- PROJECT_ID = 3 - no last snapshot -->
+  <snapshots id="6" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="3"
+             status="P" islast="[false]" purge_status="1"
+             period1_mode="days1" period1_param="30" period1_date="1316815200000"
+             period2_mode="days2" period2_param="31" period2_date="1316901600000"
+             period3_mode="days3" period3_param="32" period3_date="1316988000000"
+             period4_mode="days4" period4_param="33" period4_date="1317074400000"
+             period5_mode="days5" period5_param="34" period5_date="1317160800000"
+             depth="1" scope="DIR" qualifier="PAC" created_at="1228172400000" build_date="1317247200000"
+             version="2.1-SNAPSHOT" path="1.2."/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_date.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_date.xml
new file mode 100644 (file)
index 0000000..16c44b7
--- /dev/null
@@ -0,0 +1,44 @@
+<dataset>
+
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
+            root_id="[null]"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+  <!-- 2008-11-01 -->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1000"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225544280000" build_date="1225544280000" version="1.1-SNAPSHOT" path=""
+             status="P" islast="false" depth="0"/>
+
+
+  <!-- 2008-11-12 -->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1003"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1226494680000" build_date="1226494680000" version="1.1-SNAPSHOT" path=""
+             status="P" islast="true" depth="0"/>
+
+
+  <!-- 2008-11-22 -->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1006"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1227358680000" build_date="1227358680000" version="1.1" path=""
+             status="P" islast="false" depth="0"/>
+
+
+  <!-- 2008-11-25-->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1009"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1227617880000" build_date="1227617880000" version="1.1" path=""
+             status="P" islast="false" depth="0"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_days.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_days.xml
new file mode 100644 (file)
index 0000000..382c921
--- /dev/null
@@ -0,0 +1,81 @@
+<dataset>
+
+  <!-- project -->
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
+            root_id="[null]"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+  <!-- package -->
+  <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="project:org.foo" name="org.foo"
+            root_id="1"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+  <!-- file -->
+  <projects long_name="org.foo.Bar" id="3" scope="FIL" qualifier="CLA" kee="project:org.foo.Bar"
+            name="Bar" root_id="[null]"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+  <!-- first analysis : 2008-11-01-->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1000" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225544280000" build_date="1225544280000" version="[null]" path=""
+             status="P" islast="false" depth="0"/>
+
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1001" project_id="2" parent_snapshot_id="1000" root_project_id="1" root_snapshot_id="1000"
+             scope="DIR" qualifier="PAC" created_at="1225544280000" build_date="1225544280000" version="[null]" path="1000."
+             status="P" islast="false" depth="1"/>
+
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1002" project_id="3" parent_snapshot_id="1001" root_project_id="1" root_snapshot_id="1000"
+             scope="FIL" qualifier="CLA" created_at="1225544280000" build_date="1225544280000" version="[null]" path="1000.1001."
+             status="P" islast="false" depth="2"/>
+
+
+  <!-- second unprocessed analysis - to ignore: 2008-11-12 -->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1003" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1226494680000" build_date="1226494680000" version="[null]" path=""
+             status="U" islast="false" depth="0"/>
+
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1004" project_id="2" parent_snapshot_id="1003" root_project_id="1" root_snapshot_id="1003"
+             scope="DIR" qualifier="PAC" created_at="1226494680000" build_date="1226494680000" version="[null]" path="1003."
+             status="U" islast="false" depth="1"/>
+
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1005" project_id="3" parent_snapshot_id="1004" root_project_id="1" root_snapshot_id="1003"
+             scope="FIL" qualifier="CLA" created_at="1226494680000" build_date="1226494680000" version="[null]" path="1003.1004."
+             status="U" islast="false" depth="2"/>
+
+
+  <!-- second analysis : 2008-11-13-->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1006" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1226581080000" build_date="1226581080000" version="[null]" path=""
+             status="P" islast="true" depth="0"/>
+
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1007" project_id="2" parent_snapshot_id="1006" root_project_id="1" root_snapshot_id="1006"
+             scope="DIR" qualifier="PAC" created_at="1226581080000" build_date="1226581080000" version="[null]" path="1006."
+             status="P" islast="true" depth="1"/>
+
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1008" project_id="3" parent_snapshot_id="1007" root_project_id="1" root_snapshot_id="1006"
+             scope="FIL" qualifier="CLA" created_at="1226581080000" build_date="1226581080000" version="[null]" path="1006.1007."
+             status="P" islast="true" depth="2"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_analysis.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_analysis.xml
new file mode 100644 (file)
index 0000000..99c7c8e
--- /dev/null
@@ -0,0 +1,23 @@
+<dataset>
+
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
+            root_id="[null]"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1006"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1227358680000" build_date="1227358680000" version="1.1" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- last analysis -->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1009"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1227617880000" build_date="1227617880000" version="1.1" path=""
+             status="P" islast="true" depth="0"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_version.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_version.xml
new file mode 100644 (file)
index 0000000..da4c54f
--- /dev/null
@@ -0,0 +1,40 @@
+<dataset>
+
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
+            root_id="[null]" uuid="ABCD"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+  <!-- version 1.0 -->
+  <snapshots id="1000" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225630680000" build_date="1225630680000" version="1.0" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- version 1.1 -->
+  <snapshots id="1001" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225803480000" build_date="1225803480000" version="1.1" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- version 1.2-SNAPSHOT -->
+  <snapshots id="1002" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225976280000" build_date="1225976280000" version="1.2-SNAPSHOT" path=""
+             status="P" islast="false" depth="0"/>
+
+  <events id="1" name="1.0" component_uuid="ABCD" snapshot_id="1000" category="Version" event_date="1225630680000" created_at="1225630680000" description="" event_data="[null]"/>
+  <events id="2" name="Foo" component_uuid="ABCD" snapshot_id="1000" category="Other" event_date="1225717080000" created_at="1225717080000" description="" event_data="[null]"/>
+  <events id="3" name="1.1" component_uuid="ABCD" snapshot_id="1001" category="Version" event_date="1225803480000" created_at="1225803480000" description="" event_data="[null]"/>
+  <events id="4" name="Bar" component_uuid="ABCD" snapshot_id="1001" category="Other" event_date="1225889880000" created_at="1225889880000" description="" event_data="[null]"/>
+  <events id="5" name="Uhh" component_uuid="ABCD" snapshot_id="1002" category="Other" event_date="1226062680000" created_at="1226062680000" description="" event_data="[null]"/>
+  <events id="6" name="1.2-SNAPSHOT" component_uuid="ABCD" snapshot_id="1003" category="Version" event_date="1225976280000" created_at="1225976280000" description=""
+          event_data="[null]"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_version.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_version.xml
new file mode 100644 (file)
index 0000000..33e4f78
--- /dev/null
@@ -0,0 +1,44 @@
+<dataset>
+
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
+            root_id="[null]"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+  <!-- version 1.1-SNAPSHOT -->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1000"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225544280000" build_date="1225544280000" version="1.1-SNAPSHOT" path=""
+             status="P" islast="false" depth="0"/>
+
+
+  <!-- version 1.1-SNAPSHOT -->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1003"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225630680000" build_date="1225630680000" version="1.1-SNAPSHOT" path=""
+             status="P" islast="true" depth="0"/>
+
+
+  <!-- unprocessed version 1.1 (to ignore) -->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1006"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225717080000" build_date="1225717080000" version="1.1" path=""
+             status="U" islast="false" depth="0"/>
+
+
+  <!-- version 1.1 -->
+  <snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" id="1009"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225803480000" build_date="1225803480000" version="1.1" path=""
+             status="P" islast="false" depth="0"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/no_previous_version.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/no_previous_version.xml
new file mode 100644 (file)
index 0000000..c35918a
--- /dev/null
@@ -0,0 +1,50 @@
+<dataset>
+
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
+            root_id="[null]" uuid="ABCD"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+  <!-- version 1.0 -->
+  <snapshots id="1000" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225630680000" build_date="1225630680000" version="1.0" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- version 1.1 -->
+  <snapshots id="1001" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225803480000" build_date="1225803480000" version="1.1" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- version 1.2-SNAPSHOT -->
+  <snapshots id="1002" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225976280000" build_date="1225976280000" version="1.2-SNAPSHOT" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- version 1.2-SNAPSHOT, current analysis -->
+  <snapshots id="1003" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1226235480000" build_date="1226235480000" version="1.2-SNAPSHOT" path=""
+             status="U" islast="true" depth="0"/>
+
+  <events id="2" name="Foo" component_uuid="ABCD" snapshot_id="1000" category="Other" event_date="1225717080000" created_at="1225717080000" description=""
+          event_data="[null]"/>
+  <events id="4" name="Bar" component_uuid="ABCD" snapshot_id="1001" category="Other" event_date="1225889880000" created_at="1225889880000" description=""
+          event_data="[null]"/>
+  <events id="5" name="Uhh" component_uuid="ABCD" snapshot_id="1002" category="Other" event_date="1226062680000" created_at="1226062680000" description=""
+          event_data="[null]"/>
+  <events id="6" name="1.2-SNAPSHOT" component_uuid="ABCD" snapshot_id="1003" category="Version" event_date="1226235480000" created_at="1226235480000" description=""
+          event_data="[null]"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/previous_version_deleted.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/previous_version_deleted.xml
new file mode 100644 (file)
index 0000000..0e7ea64
--- /dev/null
@@ -0,0 +1,46 @@
+<dataset>
+
+  <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project"
+            root_id="[null]" uuid="ABCD"
+            description="[null]"
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+  <!-- version 1.0 -->
+  <snapshots id="1000" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225630680000" build_date="1225630680000" version="1.0" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- version 1.1 -->
+  <snapshots id="1001" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225803480000" build_date="1225803480000" version="1.1" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- version 1.2-SNAPSHOT -->
+  <snapshots id="1002" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1225976280000" build_date="1225976280000" version="1.2-SNAPSHOT" path=""
+             status="P" islast="false" depth="0"/>
+
+  <events id="1" name="1.0" component_uuid="ABCD" snapshot_id="1000" category="Version" event_date="1225630680000" created_at="1225630680000" description=""
+          event_data="[null]"/>
+  <events id="2" name="Foo" component_uuid="ABCD" snapshot_id="1000" category="Other" event_date="1225717080000" created_at="1225717080000" description=""
+          event_data="[null]"/>
+  <!-- The "1.1" version was deleted from the history :  -->
+  <!-- events id="3" name="1.1" component_uuid="ABCD" snapshot_id="1001" category="Version" event_date="2008-11-04 13:58:00.00" created_at="2008-11-04 13:58:00.00" description=""/-->
+  <events id="4" name="Bar" component_uuid="ABCD" snapshot_id="1001" category="Other" event_date="1225889880000" created_at="1225889880000" description=""
+          event_data="[null]"/>
+  <events id="5" name="Uhh" component_uuid="ABCD" snapshot_id="1002" category="Other" event_date="1226062680000" created_at="1226062680000" description=""
+          event_data="[null]"/>
+  <events id="6" name="1.2-SNAPSHOT" component_uuid="ABCD" snapshot_id="1003" category="Version" event_date="1226235480000" created_at="1226235480000" description=""
+          event_data="[null]"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodsRepositoryTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodsRepositoryTest/shared.xml
new file mode 100644 (file)
index 0000000..ba062d9
--- /dev/null
@@ -0,0 +1,54 @@
+<dataset>
+
+  <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="PROJECT_KEY" name="project" long_name="[null]" description="[null]"
+            uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=".ABCD."
+            enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+  <!-- 2008-11-11 -->
+  <!-- Version 0.9 -->
+  <snapshots id="1000" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1226379600000" build_date="1226379600000" version="0.9" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- 2008-11-12 -->
+  <!-- Version 1.0 -->
+  <snapshots id="1001" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1226494680000" build_date="1226494680000" version="1.0" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- 2008-11-20 -->
+  <!-- First version 1.1 -->
+  <snapshots id="1002" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1227157200000" build_date="1227157200000" version="1.1" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- 2008-11-22 -->
+  <snapshots id="1003" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1227358680000" build_date="1227358680000" version="1.1" path=""
+             status="P" islast="false" depth="0"/>
+
+  <!-- 2008-11-29 -->
+  <!-- Last version 1.1 -->
+  <snapshots id="1004" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]"
+             period5_param="[null]" period5_date="[null]" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             scope="PRJ" qualifier="TRK" created_at="1227934800000" build_date="1227934800000" version="1.1" path=""
+             status="P" islast="true" depth="0"/>
+
+
+  <events id="1" name="0.9" component_uuid="ABCD" snapshot_id="1000" category="Version" event_date="1226379600000" created_at="1226379600000" description="" event_data="[null]"/>
+  <events id="2" name="1.0" component_uuid="ABCD" snapshot_id="1001" category="Version" event_date="1226494680000" created_at="1226494680000" description="" event_data="[null]"/>
+  <events id="3" name="1.1" component_uuid="ABCD" snapshot_id="1004" category="Version" event_date="1227934800000" created_at="1227934800000" description="" event_data="[null]"/>
+
+</dataset>
diff --git a/sonar-core/src/main/java/org/sonar/core/component/SnapshotQuery.java b/sonar-core/src/main/java/org/sonar/core/component/SnapshotQuery.java
new file mode 100644 (file)
index 0000000..4525a0a
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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.core.component;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public final class SnapshotQuery {
+
+  public enum SORT_FIELD {
+    BY_DATE("created_at");
+    final String fieldName;
+
+    SORT_FIELD(String fieldName) {
+      this.fieldName = fieldName;
+    }
+  }
+
+  public enum SORT_ORDER {
+    ASC("asc"), DESC("desc");
+    final String order;
+
+    SORT_ORDER(String order) {
+      this.order = order;
+    }
+  }
+
+  private Long componentId;
+  private Long createdAfter;
+  private Long createdBefore;
+  private String status;
+  private String version;
+  private Boolean isLast;
+  private String sortField;
+  private String sortOrder;
+
+  /**
+   * filter to return snapshots created at or after a given date
+   */
+  @CheckForNull
+  public Long getCreatedAfter() {
+    return createdAfter;
+  }
+
+  public SnapshotQuery setCreatedAfter(@Nullable Long createdAfter) {
+    this.createdAfter = createdAfter;
+    return this;
+  }
+
+  /**
+   * filter to return snapshots created before a given date
+   */
+  @CheckForNull
+  public Long getCreatedBefore() {
+    return createdBefore;
+  }
+
+  public SnapshotQuery setCreatedBefore(@Nullable Long createdBefore) {
+    this.createdBefore = createdBefore;
+    return this;
+  }
+
+  @CheckForNull
+  public Boolean getIsLast() {
+    return isLast;
+  }
+
+  public SnapshotQuery setIsLast(@Nullable Boolean isLast) {
+    this.isLast = isLast;
+    return this;
+  }
+
+  @CheckForNull
+  public Long getComponentId() {
+    return componentId;
+  }
+
+  public SnapshotQuery setComponentId(@Nullable Long componentId) {
+    this.componentId = componentId;
+    return this;
+  }
+
+  @CheckForNull
+  public String getStatus() {
+    return status;
+  }
+
+  public SnapshotQuery setStatus(@Nullable String status) {
+    this.status = status;
+    return this;
+  }
+
+  @CheckForNull
+  public String getVersion() {
+    return version;
+  }
+
+  public SnapshotQuery setVersion(@Nullable String version) {
+    this.version = version;
+    return this;
+  }
+
+  public SnapshotQuery setSort(SORT_FIELD sortField, SORT_ORDER sortOrder){
+    this.sortField = sortField.fieldName;
+    this.sortOrder = sortOrder.order;
+    return this;
+  }
+
+  @CheckForNull
+  public String getSortField() {
+    return sortField;
+  }
+
+  @CheckForNull
+  public String getSortOrder() {
+    return sortOrder;
+  }
+}
index 806128b0d9d4c7a95256b6c158ca7aabe5d2978b..4f047976ab11d12f9ef82910adb6eb61fc20ad20 100644 (file)
@@ -24,6 +24,7 @@ import java.util.List;
 import javax.annotation.CheckForNull;
 import org.apache.ibatis.annotations.Param;
 import org.sonar.core.component.SnapshotDto;
+import org.sonar.core.component.SnapshotQuery;
 
 public interface SnapshotMapper {
 
@@ -35,7 +36,9 @@ public interface SnapshotMapper {
   @CheckForNull
   SnapshotDto selectLastSnapshot(Long resourceId);
 
-  List<SnapshotDto> selectSnapshotsByComponentId(Long resourceId);
+  List<SnapshotDto> selectSnapshotsByQuery(@Param("query") SnapshotQuery query);
+
+  List<SnapshotDto> selectPreviousVersionSnapshots(@Param(value = "componentId") Long componentId, @Param(value = "lastVersion") String lastVersion);
 
   List<SnapshotDto> selectSnapshotAndChildrenOfScope(@Param(value = "snapshot") Long resourceId, @Param(value = "scope") String scope);
 
index fb9c6b440623d1baabba9e445b872c05c766c218..8d5b1d0576b368af72396d6c533ffa68433446c7 100644 (file)
     where s.islast=${_true} and s.project_id = #{resource}
   </select>
 
-  <select id="selectSnapshotsByComponentId" resultType="Snapshot">
-    select
+  <select id="selectSnapshotsByQuery" parameterType="map" resultType="Snapshot">
+    SELECT
     <include refid="snapshotColumns"/>
-    from snapshots s
-    where s.project_id = #{resource}
+    FROM snapshots s
+    <where>
+      <if test="query.componentId != null">
+        AND s.project_id=#{query.componentId}
+      </if>
+      <if test="query.status != null">
+        AND status=#{query.status}
+      </if>
+      <if test="query.version != null">
+        AND version=#{query.version}
+      </if>
+      <if test="query.isLast != null">
+        AND islast=#{query.isLast}
+      </if>
+      <if test="query.createdAfter != null">
+        AND created_at>=#{query.createdAfter}
+      </if>
+      <if test="query.createdBefore != null">
+        AND created_at&lt;#{query.createdBefore}
+      </if>
+    </where>
+    <if test="query.sortField != null">
+    ORDER BY
+      <if test="query.sortField == 'created_at'">
+        created_at
+      </if>
+      <if test="query.sortOrder == 'asc'">
+        asc
+      </if>
+      <if test="query.sortOrder == 'desc'">
+        desc
+      </if>
+    </if>
+  </select>
+
+  <select id="selectPreviousVersionSnapshots" parameterType="map" resultType="Snapshot">
+    SELECT
+    <include refid="snapshotColumns"/>
+    FROM snapshots s
+    INNER JOIN events e ON s.id = e.snapshot_id AND e.name &lt;&gt; #{lastVersion} AND e.category='Version'
+    INNER JOIN projects p ON p.uuid=e.component_uuid AND p.id=#{componentId}
+    ORDER BY e.event_date DESC
   </select>
 
   <select id="selectSnapshotAndChildrenOfScope" parameterType="map" resultType="Snapshot">
diff --git a/sonar-core/src/test/java/org/sonar/core/component/SnapshotQueryTest.java b/sonar-core/src/test/java/org/sonar/core/component/SnapshotQueryTest.java
new file mode 100644 (file)
index 0000000..6c76f16
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.core.component;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.component.SnapshotQuery.SORT_FIELD.BY_DATE;
+import static org.sonar.core.component.SnapshotQuery.SORT_ORDER.ASC;
+
+public class SnapshotQueryTest {
+
+  @Test
+  public void test_setters_and_getters() throws Exception {
+    SnapshotQuery query = new SnapshotQuery()
+      .setComponentId(1L)
+      .setIsLast(true)
+      .setStatus("P")
+      .setVersion("1.0")
+      .setCreatedAfter(10L)
+      .setCreatedBefore(20L)
+      .setSort(BY_DATE, ASC);
+
+    assertThat(query.getComponentId()).isEqualTo(1L);
+    assertThat(query.getIsLast()).isTrue();
+    assertThat(query.getStatus()).isEqualTo("P");
+    assertThat(query.getVersion()).isEqualTo("1.0");
+    assertThat(query.getCreatedAfter()).isEqualTo(10L);
+    assertThat(query.getCreatedBefore()).isEqualTo(20L);
+    assertThat(query.getSortField()).isEqualTo("created_at");
+    assertThat(query.getSortOrder()).isEqualTo("asc");
+  }
+}