From 5544b41c921fbbb516aafbf0c96aca349c5a2983 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 5 Jun 2015 15:31:20 +0200 Subject: [PATCH] SONAR-6260 Create a PeriodsRepository The goal is to copy behaviour of PeriodsDefinition and TimeMachineConfiguration from batch in the Compute Engin --- .../server/component/db/SnapshotDao.java | 11 +- .../server/computation/period/Period.java | 123 +++++++++ .../computation/period/PeriodFinder.java | 125 +++++++++ .../computation/period/PeriodsRepository.java | 187 ++++++++++++++ .../computation/period/package-info.java | 24 ++ .../server/component/db/SnapshotDaoTest.java | 58 +++-- .../computation/period/PeriodFinderTest.java | 243 ++++++++++++++++++ .../server/computation/period/PeriodTest.java | 89 +++++++ .../period/PeriodsRepositoryTest.java | 211 +++++++++++++++ ...PersistComponentsAndSnapshotsStepTest.java | 2 +- .../select_previous_version_snapshots.xml | 40 +++ .../select_snapshots_by_query.xml | 64 +++++ .../period/PeriodFinderTest/find_by_date.xml | 44 ++++ .../period/PeriodFinderTest/find_by_days.xml | 81 ++++++ .../find_by_previous_analysis.xml | 23 ++ .../find_by_previous_version.xml | 40 +++ .../PeriodFinderTest/find_by_version.xml | 44 ++++ .../PeriodFinderTest/no_previous_version.xml | 50 ++++ .../previous_version_deleted.xml | 46 ++++ .../period/PeriodsRepositoryTest/shared.xml | 54 ++++ .../sonar/core/component/SnapshotQuery.java | 136 ++++++++++ .../core/component/db/SnapshotMapper.java | 5 +- .../core/component/db/SnapshotMapper.xml | 48 +++- .../core/component/SnapshotQueryTest.java | 51 ++++ 24 files changed, 1776 insertions(+), 23 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/period/Period.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodFinder.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsRepository.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/period/package-info.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodFinderTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsRepositoryTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_previous_version_snapshots.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_snapshots_by_query.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_date.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_days.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_analysis.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_version.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_version.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/no_previous_version.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/previous_version_deleted.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodsRepositoryTest/shared.xml create mode 100644 sonar-core/src/main/java/org/sonar/core/component/SnapshotQuery.java create mode 100644 sonar-core/src/test/java/org/sonar/core/component/SnapshotQueryTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/db/SnapshotDao.java b/server/sonar-server/src/main/java/org/sonar/server/component/db/SnapshotDao.java index 6682f541987..fde3d9be2e7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/db/SnapshotDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/db/SnapshotDao.java @@ -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 selectSnapshotsByComponentId(DbSession session, long componentId) { - return mapper(session).selectSnapshotsByComponentId(componentId); + return mapper(session).selectSnapshotsByQuery(new SnapshotQuery().setComponentId(componentId)); + } + + public List selectSnapshotsByQuery(DbSession session, SnapshotQuery query) { + return mapper(session).selectSnapshotsByQuery(query); + } + + public List selectPreviousVersionSnapshots(DbSession session, long componentId, String lastVersion) { + return mapper(session).selectPreviousVersionSnapshots(componentId, lastVersion); } public List 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 index 00000000000..0ad15f72db8 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/period/Period.java @@ -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 index 00000000000..a451fd68d70 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodFinder.java @@ -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 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 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 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 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 index 00000000000..cf2181f8cf8 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsRepository.java @@ -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 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 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 index 00000000000..d3d038b8e93 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/period/package-info.java @@ -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; diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/db/SnapshotDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/db/SnapshotDaoTest.java index a139417951b..2000c1b1ba7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/db/SnapshotDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/db/SnapshotDaoTest.java @@ -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 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 index 00000000000..37dd19862fd --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodFinderTest.java @@ -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 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 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 index 00000000000..99cfae19792 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodTest.java @@ -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 index 00000000000..9a61d70a266 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/period/PeriodsRepositoryTest.java @@ -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 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 periods = periodsRepository.getPeriods(); + List periodModes = newArrayList(Iterables.transform(periods, new Function() { + @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); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStepTest.java index 8f89c938805..becc43aacde 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsAndSnapshotsStepTest.java @@ -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 index 00000000000..83290989826 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_previous_version_snapshots.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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 index 00000000000..1f32010f385 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/db/SnapshotDaoTest/select_snapshots_by_query.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + 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 index 00000000000..16c44b7f177 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_date.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + 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 index 00000000000..382c921d3cc --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_days.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 index 00000000000..99c7c8ec7be --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_analysis.xml @@ -0,0 +1,23 @@ + + + + + + + + + + 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 index 00000000000..da4c54f7c90 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_previous_version.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + 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 index 00000000000..33e4f78d390 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/find_by_version.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + 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 index 00000000000..c35918a016e --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/no_previous_version.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + 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 index 00000000000..0e7ea64c198 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodFinderTest/previous_version_deleted.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + 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 index 00000000000..ba062d9fd54 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/period/PeriodsRepositoryTest/shared.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 index 00000000000..4525a0a1b03 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/component/SnapshotQuery.java @@ -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; + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/component/db/SnapshotMapper.java b/sonar-core/src/main/java/org/sonar/core/component/db/SnapshotMapper.java index 806128b0d9d..4f047976ab1 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/db/SnapshotMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/component/db/SnapshotMapper.java @@ -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 selectSnapshotsByComponentId(Long resourceId); + List selectSnapshotsByQuery(@Param("query") SnapshotQuery query); + + List selectPreviousVersionSnapshots(@Param(value = "componentId") Long componentId, @Param(value = "lastVersion") String lastVersion); List selectSnapshotAndChildrenOfScope(@Param(value = "snapshot") Long resourceId, @Param(value = "scope") String scope); diff --git a/sonar-core/src/main/resources/org/sonar/core/component/db/SnapshotMapper.xml b/sonar-core/src/main/resources/org/sonar/core/component/db/SnapshotMapper.xml index fb9c6b44062..8d5b1d0576b 100644 --- a/sonar-core/src/main/resources/org/sonar/core/component/db/SnapshotMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/component/db/SnapshotMapper.xml @@ -51,11 +51,51 @@ where s.islast=${_true} and s.project_id = #{resource} - + SELECT - from snapshots s - where s.project_id = #{resource} + FROM snapshots s + + + AND s.project_id=#{query.componentId} + + + AND status=#{query.status} + + + AND version=#{query.version} + + + AND islast=#{query.isLast} + + + AND created_at>=#{query.createdAfter} + + + AND created_at<#{query.createdBefore} + + + + ORDER BY + + created_at + + + asc + + + desc + + + + +