import org.sonar.server.computation.measure.MeasureRepositoryImpl;
import org.sonar.server.computation.measure.MetricCache;
import org.sonar.server.computation.period.PeriodFinder;
-import org.sonar.server.computation.period.PeriodsRepository;
+import org.sonar.server.computation.period.PeriodsHolderImpl;
import org.sonar.server.computation.step.ComputationStep;
import org.sonar.server.computation.step.ComputationSteps;
import org.sonar.server.view.index.ViewIndex;
MeasureRepositoryImpl.class,
EventRepositoryImpl.class,
ProjectSettingsRepository.class,
- PeriodsRepository.class,
+ PeriodsHolderImpl.class,
DbIdsRepository.class,
// issues
package org.sonar.server.computation.period;
-import com.google.common.annotations.VisibleForTesting;
import java.util.Calendar;
import java.util.Date;
import javax.annotation.Nullable;
return this;
}
- @VisibleForTesting
- Long getTargetDate() {
+ public Long getTargetDate() {
return targetDate;
}
}
@CheckForNull
- Period findByDate(DbSession session, Long projectId, Date date) {
+ public 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;
}
@CheckForNull
- Period findByDays(DbSession session, Long projectId, long analysisDate, int days) {
+ public 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);
}
@CheckForNull
- Period findByPreviousAnalysis(DbSession session, Long projectId, long analysisDate) {
+ public 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;
}
@CheckForNull
- Period findByPreviousVersion(DbSession session, Long projectId, String version) {
+ public Period findByPreviousVersion(DbSession session, Long projectId, String version) {
List<SnapshotDto> snapshotDtos = dbClient.snapshotDao().selectPreviousVersionSnapshots(session, projectId, version);
if (snapshotDtos.isEmpty()) {
return null;
}
@CheckForNull
- Period findByVersion(DbSession session, Long projectId, String version) {
+ public 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;
--- /dev/null
+/*
+ * 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.util.List;
+import org.sonar.api.CoreProperties;
+
+/**
+ * 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 interface PeriodsHolder {
+
+ List<Period> getPeriods();
+
+}
--- /dev/null
+/*
+ * 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.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PeriodsHolderImpl implements PeriodsHolder {
+
+ private boolean isPeriodsInitialized = false;
+ private List<Period> periods = new ArrayList<>();
+
+ public void setPeriods(List<Period> periods) {
+ this.periods = periods;
+ isPeriodsInitialized = true;
+ }
+
+ @Override
+ public List<Period> getPeriods() {
+ Preconditions.checkArgument(isPeriodsInitialized, "Periods have not been initialized yet");
+ return periods;
+ }
+
+}
+++ /dev/null
-/*
- * 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;
- }
-
-}
QualityGateEventsStep.class,
// Persist data
+ FeedPeriodsStep.class,
PersistComponentsStep.class,
PersistSnapshotsStep.class,
PersistNumberOfDaysSinceLastCommitStep.class,
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.step;
+
+import 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.computation.period.Period;
+import org.sonar.server.computation.period.PeriodFinder;
+import org.sonar.server.computation.period.PeriodsHolderImpl;
+import org.sonar.server.db.DbClient;
+
+/**
+ * Populates the {@link org.sonar.server.computation.period.PeriodsHolder}
+ *
+ * Here is how these periods are computed :
+ * - 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 FeedPeriodsStep implements ComputationStep {
+ private static final Logger LOG = LoggerFactory.getLogger(PeriodsHolderImpl.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 final PeriodsHolderImpl periodsHolder;
+
+ public FeedPeriodsStep(DbClient dbClient, Settings settings, TreeRootHolder treeRootHolder, PeriodFinder periodFinder, BatchReportReader batchReportReader,
+ PeriodsHolderImpl periodsHolder) {
+ this.dbClient = dbClient;
+ this.settings = settings;
+ this.treeRootHolder = treeRootHolder;
+ this.periodFinder = periodFinder;
+ this.batchReportReader = batchReportReader;
+ this.periodsHolder = periodsHolder;
+ }
+
+ @Override
+ public void execute() {
+ periodsHolder.setPeriods(createPeriods());
+ }
+
+ private List<Period> createPeriods() {
+ List<Period> periods = new ArrayList<>();
+ 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();
+ }
+ return periods;
+ }
+
+ 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;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Feed differential periods";
+ }
+}
--- /dev/null
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.core.component.SnapshotDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PeriodsHolderImplTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void get_periods() throws Exception {
+ List<Period> periods = new ArrayList<>();
+ periods.add(new Period("mode", null, new SnapshotDto()));
+
+ PeriodsHolderImpl periodsHolder = new PeriodsHolderImpl();
+ periodsHolder.setPeriods(periods);
+
+ assertThat(periodsHolder.getPeriods()).hasSize(1);
+ }
+
+ @Test
+ public void fail_to_get_periods_if_not_initialized() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Periods have not been initialized yet");
+
+ new PeriodsHolderImpl().getPeriods();
+ }
+}
+++ /dev/null
-/*
- * 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);
- }
-
-}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.computation.step;
+
+import 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.computation.period.Period;
+import org.sonar.server.computation.period.PeriodFinder;
+import org.sonar.server.computation.period.PeriodsHolderImpl;
+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 FeedPeriodsStepTest extends BaseStepTest {
+
+ 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();
+
+ PeriodsHolderImpl periodsHolder = new PeriodsHolderImpl();
+
+ DbClient dbClient;
+
+ DbSession dbSession;
+
+ Settings settings = new Settings();
+
+ FeedPeriodsStep sut;
+
+ @Override
+ protected ComputationStep step() {
+ return sut;
+ }
+
+ @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);
+
+ sut = new FeedPeriodsStep(dbClient, settings, treeRootHolder, new PeriodFinder(dbClient), reportReader, periodsHolder);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ dbSession.close();
+ }
+
+ @Test
+ public void no_period_on_first_analysis() throws Exception {
+ // No project, no snapshot
+
+ sut.execute();
+ assertThat(periodsHolder.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);
+
+ sut.execute();
+ List<Period> periods = periodsHolder.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");
+
+ sut.execute();
+ assertThat(periodsHolder.getPeriods()).isEmpty();
+ }
+
+ @Test
+ public void no_period_when_settings_is_empty() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+ settings.setProperty("sonar.timemachine.period1", "");
+
+ sut.execute();
+ assertThat(periodsHolder.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
+
+ sut.execute();
+ List<Period> periods = periodsHolder.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");
+
+ sut.execute();
+ assertThat(periodsHolder.getPeriods()).hasSize(2);
+ }
+
+}
+++ /dev/null
-<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>
--- /dev/null
+<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>