aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJenkins CI <ci@sonarsource.com>2016-02-12 08:01:23 +0100
committerJenkins CI <ci@sonarsource.com>2016-02-12 08:01:23 +0100
commit45ad6ed9b0212c7e542005cb95518bd3f8d52d6f (patch)
tree462068b53c77788666c9867dbbc341497909f22f /server
parentc885d23b868fe6d038c2e4a557f826e878316fcb (diff)
parent54cdd41e77bf99b81134435499219eaef4862525 (diff)
downloadsonarqube-45ad6ed9b0212c7e542005cb95518bd3f8d52d6f.tar.gz
sonarqube-45ad6ed9b0212c7e542005cb95518bd3f8d52d6f.zip
Automatic merge from branch-5.4
* origin/branch-5.4: Fix shutdown of server after DB migration SONAR-7103 Fix new debt ratio when 5 periods are defined SONAR-7311 fix file leak because CE appenders are closed too late
Diffstat (limited to 'server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/log/CeLogging.java22
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitor.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/container/ComputeEngineContainerImplTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/log/CeLoggingTest.java195
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitorTest.java92
6 files changed, 266 insertions, 51 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/log/CeLogging.java b/server/sonar-server/src/main/java/org/sonar/server/computation/log/CeLogging.java
index 948949af205..1b746df41ea 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/log/CeLogging.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/log/CeLogging.java
@@ -19,11 +19,14 @@
*/
package org.sonar.server.computation.log;
+import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.sift.MDCBasedDiscriminator;
import ch.qos.logback.classic.sift.SiftingAppender;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.sift.AppenderTracker;
+import ch.qos.logback.core.util.Duration;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import java.io.File;
@@ -34,11 +37,13 @@ import org.apache.commons.io.comparator.LastModifiedFileComparator;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.log4j.MDC;
import org.sonar.api.config.Settings;
+import org.sonar.process.LogbackHelper;
import org.sonar.process.ProcessProperties;
import org.sonar.process.Props;
import org.sonar.server.computation.queue.CeTask;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
@@ -52,10 +57,16 @@ import static java.lang.String.format;
*/
public class CeLogging {
+ private static final long TIMEOUT_2_MINUTES = 1000 * 60 * 2L;
+ private static final String CE_APPENDER_NAME = "ce";
+ // using 0L as timestamp when retrieving appender to stop it will make it instantly eligible for removal
+ private static final long STOPPING_TRACKER_TIMESTAMP = 0L;
+
@VisibleForTesting
static final String MDC_LOG_PATH = "ceLogPath";
public static final String MAX_LOGS_PROPERTY = "sonar.ce.maxLogsPerTask";
+ private final LogbackHelper helper = new LogbackHelper();
private final File logsDir;
private final Settings settings;
@@ -107,10 +118,18 @@ public class CeLogging {
MDC.remove(MDC_LOG_PATH);
if (relativePath != null) {
+ stopAppender(relativePath);
purgeDir(new File(logsDir, relativePath).getParentFile());
}
}
+ private void stopAppender(String relativePath) {
+ Appender<ILoggingEvent> appender = helper.getRootContext().getLogger(Logger.ROOT_LOGGER_NAME).getAppender(CE_APPENDER_NAME);
+ checkState(appender instanceof SiftingAppender, "Appender with name %s is null or not a SiftingAppender", CE_APPENDER_NAME);
+ AppenderTracker<ILoggingEvent> ceAppender = ((SiftingAppender) appender).getAppenderTracker();
+ ceAppender.getOrCreate(relativePath, STOPPING_TRACKER_TIMESTAMP).stop();
+ }
+
@VisibleForTesting
void purgeDir(File dir) {
if (dir.exists()) {
@@ -161,7 +180,8 @@ public class CeLogging {
siftingAppender.setContext(ctx);
siftingAppender.setDiscriminator(mdcDiscriminator);
siftingAppender.setAppenderFactory(new CeFileAppenderFactory(logsDir));
- siftingAppender.setName("ce");
+ siftingAppender.setName(CE_APPENDER_NAME);
+ siftingAppender.setTimeout(new Duration(TIMEOUT_2_MINUTES));
siftingAppender.start();
return siftingAppender;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitor.java
index 531137e8c93..b41c07b6b39 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitor.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitor.java
@@ -177,12 +177,12 @@ public class SqaleNewMeasuresVisitor extends PathAwareVisitorAdapter<SqaleNewMea
for (Period period : periodsHolder.getPeriods()) {
if (isLineInPeriod(changeset.getDate(), period)) {
devCostCounter.incrementDevCost(period, lineDevCost);
- hasDevCost[period.getIndex()] = true;
+ hasDevCost[period.getIndex() - 1] = true;
}
}
}
for (Period period : periodsHolder.getPeriods()) {
- if (hasDevCost[period.getIndex()]) {
+ if (hasDevCost[period.getIndex() - 1]) {
long newDebt = getLongValue(measureRepository.getRawMeasure(file, this.newDebtMetric), period);
devCostCounter.incrementNewDebt(period, newDebt);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel.java
index 75f29777a22..d060d8bc2c6 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel.java
@@ -100,7 +100,7 @@ public abstract class PlatformLevel {
*/
public PlatformLevel destroy() {
if (parent != null) {
- parent.container.removeChild();
+ parent.container.removeChild(container);
}
return this;
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/container/ComputeEngineContainerImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/container/ComputeEngineContainerImplTest.java
index e37c1856e9c..13abce63cc8 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/container/ComputeEngineContainerImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/container/ComputeEngineContainerImplTest.java
@@ -54,7 +54,7 @@ public class ComputeEngineContainerImplTest {
ContainerPopulator populator = mock(ContainerPopulator.class);
ComputeEngineContainerImpl ceContainer = new ComputeEngineContainerImpl(parent, populator);
- assertThat(parent.getChild()).isNull();
+ assertThat(parent.getChildren()).isEmpty();
verify(populator).populateContainer(ceContainer);
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/log/CeLoggingTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/log/CeLoggingTest.java
index d10df360754..7df4d130dc6 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/log/CeLoggingTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/log/CeLoggingTest.java
@@ -19,38 +19,65 @@
*/
package org.sonar.server.computation.log;
+import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.sift.SiftingAppender;
import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
import ch.qos.logback.core.filter.Filter;
+import ch.qos.logback.core.joran.spi.JoranException;
import com.google.common.base.Optional;
import java.io.File;
import java.io.IOException;
+import java.util.Collection;
import java.util.List;
import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
+import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.sonar.api.config.Settings;
+import org.sonar.process.LogbackHelper;
import org.sonar.process.ProcessProperties;
import org.sonar.server.computation.queue.CeTask;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.computation.log.CeLogging.MDC_LOG_PATH;
public class CeLoggingTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
-
@Rule
public TemporaryFolder temp = new TemporaryFolder();
+ private LogbackHelper helper = new LogbackHelper();
+ private File dataDir;
+
+ @Before
+ public void setUp() throws Exception {
+ this.dataDir = temp.newFolder();
+ }
+
+ @After
+ public void resetLogback() throws JoranException {
+ helper.resetFromXml("/logback-test.xml");
+ }
+
+ @After
+ public void cleanMDC() throws Exception {
+ MDC.clear();
+ }
+
@Test
public void getFile() throws IOException {
- File dataDir = temp.newFolder();
Settings settings = newSettings(dataDir, 10);
CeLogging underTest = new CeLogging(settings);
@@ -73,57 +100,124 @@ public class CeLoggingTest {
}
@Test
- public void use_MDC_to_store_path_to_in_progress_task_logs() throws IOException {
- CeLogging underTest = new CeLogging(newSettings(temp.newFolder(), 5));
+ public void initForTask_adds_path_of_ce_log_file_in_MDC() throws IOException {
+ CeLogging underTest = new CeLogging(newSettings(dataDir, 5));
+
+ CeTask task = createCeTask("TYPE1", "U1");
+ underTest.initForTask(task);
+ assertThat(MDC.get(MDC_LOG_PATH)).isNotEmpty().isEqualTo(LogFileRef.from(task).getRelativePath());
+ }
+
+ @Test
+ public void clearForTask_throws_ISE_if_CE_appender_is_not_configured() throws IOException {
+ CeLogging underTest = new CeLogging(newSettings(dataDir, 5));
+
+ CeTask task = createCeTask("TYPE1", "U1");
+ underTest.initForTask(task);
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Appender with name ce is null or not a SiftingAppender");
+
+ underTest.clearForTask();
+ }
+
+ @Test
+ public void clearForTask_throws_ISE_if_CE_appender_is_not_a_SiftingAppender() throws IOException {
+ Appender<ILoggingEvent> mockCeAppender = mock(Appender.class);
+ when(mockCeAppender.getName()).thenReturn("ce");
+ helper.getRootContext().getLogger(Logger.ROOT_LOGGER_NAME).addAppender(mockCeAppender);
+
+ CeLogging underTest = new CeLogging(newSettings(dataDir, 5));
+
+ CeTask task = createCeTask("TYPE1", "U1");
+ underTest.initForTask(task);
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Appender with name ce is null or not a SiftingAppender");
+
+ underTest.clearForTask();
+ }
+
+ @Test
+ public void clearForTask_clears_MDC() throws IOException {
+ setupCeAppender();
- CeTask task = new CeTask.Builder().setType("TYPE1").setUuid("U1").build();
+ CeLogging underTest = new CeLogging(newSettings(dataDir, 5));
+
+ CeTask task = createCeTask("TYPE1", "U1");
underTest.initForTask(task);
- assertThat(MDC.get(CeLogging.MDC_LOG_PATH)).isNotEmpty().isEqualTo(LogFileRef.from(task).getRelativePath());
+ assertThat(MDC.get(MDC_LOG_PATH)).isNotEmpty().isEqualTo(LogFileRef.from(task).getRelativePath());
+
+ underTest.clearForTask();
+ assertThat(MDC.get(MDC_LOG_PATH)).isNull();
+ }
+
+ @Test
+ public void cleanForTask_stops_only_appender_for_MDC_value() throws IOException {
+ Logger rootLogger = setupCeAppender();
+
+ CeLogging underTest = new CeLogging(newSettings(dataDir, 5));
+
+ // init MDC
+ underTest.initForTask(createCeTask("TYPE1", "U1"));
+ verifyNoAppender(rootLogger);
+
+ // logging will create and start the appender
+ LoggerFactory.getLogger(getClass()).info("some log!");
+ verifyAllAppenderStarted(rootLogger, 1);
+
+ // init MDC and create appender for another task
+ // (in the same thread, which should not happen, but it's good enough for our test)
+ CeTask ceTask = createCeTask("TYPE1", "U2");
+ underTest.initForTask(ceTask);
+ LoggerFactory.getLogger(getClass()).info("some other log!");
+ verifyAllAppenderStarted(rootLogger, 2);
+
+ // stop appender which is currently referenced in MDC
underTest.clearForTask();
- assertThat(MDC.get(CeLogging.MDC_LOG_PATH)).isNull();
+
+ Appender appender = verifySingleAppenderIsStopped(rootLogger, 2);
+ assertThat(appender.getName()).isEqualTo("ce-" + LogFileRef.from(ceTask).getRelativePath());
}
@Test
public void delete_oldest_files_of_same_directory_to_keep_only_max_allowed_files() throws IOException {
- File dir = temp.newFolder();
for (int i = 1; i <= 5; i++) {
- File file = new File(dir, format("U%d.log", i));
+ File file = new File(dataDir, format("U%d.log", i));
FileUtils.touch(file);
// see javadoc: "all platforms support file-modification times to the nearest second,
// but some provide more precision" --> increment by second, not by millisecond
file.setLastModified(1_450_000_000_000L + i * 1000);
}
- assertThat(dir.listFiles()).hasSize(5);
+ assertThat(dataDir.listFiles()).hasSize(5);
// keep 3 files in each dir
- CeLogging underTest = new CeLogging(newSettings(dir, 3));
- underTest.purgeDir(dir);
+ CeLogging underTest = new CeLogging(newSettings(dataDir, 3));
+ underTest.purgeDir(dataDir);
- assertThat(dir.listFiles()).hasSize(3);
- assertThat(dir.listFiles()).extracting("name")
+ assertThat(dataDir.listFiles()).hasSize(3);
+ assertThat(dataDir.listFiles()).extracting("name")
.containsOnly("U3.log", "U4.log", "U5.log");
}
@Test
public void do_not_delete_files_if_dir_has_less_files_than_max_allowed() throws IOException {
- File dir = temp.newFolder();
- FileUtils.touch(new File(dir, "U1.log"));
+ FileUtils.touch(new File(dataDir, "U1.log"));
- CeLogging underTest = new CeLogging(newSettings(dir, 5));
- underTest.purgeDir(dir);
+ CeLogging underTest = new CeLogging(newSettings(dataDir, 5));
+ underTest.purgeDir(dataDir);
- assertThat(dir.listFiles()).extracting("name").containsOnly("U1.log");
+ assertThat(dataDir.listFiles()).extracting("name").containsOnly("U1.log");
}
@Test
public void do_not_keep_any_logs() throws IOException {
- File dir = temp.newFolder();
- FileUtils.touch(new File(dir, "U1.log"));
+ FileUtils.touch(new File(dataDir, "U1.log"));
- CeLogging underTest = new CeLogging(newSettings(temp.newFolder(), 0));
- underTest.purgeDir(dir);
+ CeLogging underTest = new CeLogging(newSettings(dataDir, 0));
+ underTest.purgeDir(dataDir);
- assertThat(dir.listFiles()).isEmpty();
+ assertThat(dataDir.listFiles()).isEmpty();
}
@Test
@@ -131,22 +225,61 @@ public class CeLoggingTest {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Property sonar.ce.maxLogsPerTask must be positive. Got: -1");
- Settings settings = newSettings(temp.newFolder(), -1);
+ Settings settings = newSettings(dataDir, -1);
CeLogging logging = new CeLogging(settings);
- logging.purgeDir(temp.newFolder());
+ logging.purgeDir(dataDir);
}
@Test
public void createConfiguration() throws Exception {
- File logsDir = temp.newFolder();
- SiftingAppender siftingAppender = CeLogging.createAppenderConfiguration(new LoggerContext(), logsDir);
+ SiftingAppender siftingAppender = CeLogging.createAppenderConfiguration(new LoggerContext(), dataDir);
// filter on CE logs
List<Filter<ILoggingEvent>> filters = siftingAppender.getCopyOfAttachedFiltersList();
assertThat(filters).hasSize(1);
assertThat(filters.get(0)).isInstanceOf(CeLogAcceptFilter.class);
- assertThat(siftingAppender.getDiscriminator().getKey()).isEqualTo(CeLogging.MDC_LOG_PATH);
+ assertThat(siftingAppender.getDiscriminator().getKey()).isEqualTo(MDC_LOG_PATH);
+ assertThat(siftingAppender.getTimeout().getMilliseconds()).isEqualTo(1000 * 60 * 2);
+ }
+
+ private Logger setupCeAppender() {
+ Logger rootLogger = helper.getRootContext().getLogger(Logger.ROOT_LOGGER_NAME);
+ rootLogger.addAppender(CeLogging.createAppenderConfiguration(helper.getRootContext(), dataDir));
+ return rootLogger;
+ }
+
+ private void verifyNoAppender(Logger rootLogger) {
+ Collection<Appender<ILoggingEvent>> allAppenders = getAllAppenders(rootLogger);
+ assertThat(allAppenders).isEmpty();
+ }
+
+ private void verifyAllAppenderStarted(Logger rootLogger, int expectedSize) {
+ Collection<Appender<ILoggingEvent>> allAppenders = getAllAppenders(rootLogger);
+ assertThat(allAppenders).hasSize(expectedSize);
+ for (Appender<ILoggingEvent> appender : allAppenders) {
+ assertThat(appender.isStarted()).isTrue();
+ }
+ }
+
+ private Appender verifySingleAppenderIsStopped(Logger rootLogger, int expectedSize) {
+ Collection<Appender<ILoggingEvent>> allAppenders = getAllAppenders(rootLogger);
+ assertThat(allAppenders).hasSize(expectedSize);
+ Appender res = null;
+ for (Appender<ILoggingEvent> appender : allAppenders) {
+ if (!appender.isStarted()) {
+ assertThat(res).describedAs("More than one appender found stopped").isNull();
+ res = appender;
+ }
+ }
+ assertThat(res).describedAs("There should be one stopped appender").isNotNull();
+ return res;
+ }
+
+ private Collection<Appender<ILoggingEvent>> getAllAppenders(Logger rootLogger) {
+ Appender<ILoggingEvent> ceAppender = rootLogger.getAppender("ce");
+ assertThat(ceAppender).isInstanceOf(SiftingAppender.class);
+ return ((SiftingAppender) ceAppender).getAppenderTracker().allComponents();
}
private static Settings newSettings(File dataDir, int maxLogs) {
@@ -155,4 +288,8 @@ public class CeLoggingTest {
settings.setProperty(CeLogging.MAX_LOGS_PROPERTY, maxLogs);
return settings;
}
+
+ private static CeTask createCeTask(String type, String uuid) {
+ return new CeTask.Builder().setType(type).setUuid(uuid).build();
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitorTest.java
index 44ea83c022d..4305f8ddd5f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitorTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/sqale/SqaleNewMeasuresVisitorTest.java
@@ -25,10 +25,8 @@ import com.google.common.collect.Ordering;
import java.util.Arrays;
import java.util.Set;
import org.assertj.core.data.Offset;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.server.computation.batch.TreeRootHolderRule;
import org.sonar.server.computation.component.Component;
@@ -48,7 +46,11 @@ import org.sonar.server.computation.scm.ScmInfoRepositoryRule;
import static com.google.common.base.Preconditions.checkArgument;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_DATA;
import static org.sonar.api.measures.CoreMetrics.NCLOC_DATA_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SQALE_DEBT_RATIO;
+import static org.sonar.api.measures.CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT;
import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT_KEY;
import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
import static org.sonar.server.computation.component.Component.Type.FILE;
@@ -74,9 +76,9 @@ public class SqaleNewMeasuresVisitorTest {
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
@Rule
public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
- .add(CoreMetrics.NEW_TECHNICAL_DEBT)
- .add(CoreMetrics.NCLOC_DATA)
- .add(CoreMetrics.NEW_SQALE_DEBT_RATIO);
+ .add(NEW_TECHNICAL_DEBT)
+ .add(NCLOC_DATA)
+ .add(NEW_SQALE_DEBT_RATIO);
@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
@Rule
@@ -87,15 +89,9 @@ public class SqaleNewMeasuresVisitorTest {
private VisitorsCrawler underTest = new VisitorsCrawler(Arrays.<ComponentVisitor>asList(new SqaleNewMeasuresVisitor(metricRepository, measureRepository, scmInfoRepository,
periodsHolder, sqaleRatingSettings)));
- @Before
- public void setUp() throws Exception {
- periodsHolder.setPeriods(
- new Period(2, SOME_PERIOD_MODE, null, PERIOD_2_SNAPSHOT_DATE, SOME_SNAPSHOT_ID),
- new Period(4, SOME_PERIOD_MODE, null, PERIOD_5_SNAPSHOT_DATE, SOME_SNAPSHOT_ID));
- }
-
@Test
public void project_has_new_debt_ratio_variation_for_each_defined_period() {
+ setTwoPeriods();
treeRootHolder.setRoot(builder(PROJECT, ROOT_REF).build());
underTest.visit(treeRootHolder.getRoot());
@@ -128,13 +124,14 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void file_has_0_new_debt_ratio_if_all_scm_dates_are_before_snapshot_dates() {
+ setTwoPeriods();
treeRootHolder.setRoot(
builder(PROJECT, ROOT_REF)
.addChildren(
builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build()
)
.build()
- );
+ );
measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(50, 12));
measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NCLOC_DATA_KEY, createNclocDataMeasure(2, 3, 4));
scmInfoRepository.setScmInfo(LANGUAGE_1_FILE_REF, createChangesets(PERIOD_2_SNAPSHOT_DATE - 100, 4));
@@ -147,6 +144,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void file_has_new_debt_ratio_if_some_scm_dates_are_after_snapshot_dates() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(50, 12));
@@ -159,6 +157,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void new_debt_ratio_changes_with_language_cost() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST * 10);
setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(50, 12));
@@ -171,6 +170,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void new_debt_ratio_changes_with_new_technical_debt() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(500, 120, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(500, 120));
@@ -183,6 +183,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void new_debt_ratio_on_non_file_level_is_based_on_new_technical_debt_of_that_level() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(500, 120, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(1200, 820));
@@ -195,6 +196,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void no_new_debt_ratio_when_file_is_unit_test() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(50, 12, Flag.UT_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(50, 12));
@@ -207,6 +209,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void new_debt_ratio_is_0_on_non_file_level_when_all_files_are_unit_test() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(50, 12, Flag.UT_FILE, Flag.WITH_NCLOC, Flag.WITH_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(200, 162));
@@ -219,6 +222,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void new_debt_ratio_is_0_when_file_has_no_changesets() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.NO_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(50, 12));
@@ -231,6 +235,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void new_debt_ratio_is_0_on_non_file_level_when_no_file_has_changesets() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.WITH_NCLOC, Flag.NO_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(200, 162));
@@ -243,6 +248,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void new_debt_ratio_is_0_when_there_is_no_ncloc_in_file() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.NO_NCLOC, Flag.WITH_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(50, 12));
@@ -255,6 +261,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void new_debt_ratio_is_0_on_non_file_level_when_one_file_has_zero_new_debt_because_of_no_changeset() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.NO_NCLOC, Flag.WITH_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(200, 162));
@@ -267,6 +274,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void new_debt_ratio_is_0_when_ncloc_measure_is_missing() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
setupOneFileAloneInAProject(50, 12, Flag.SRC_FILE, Flag.MISSING_MEASURE_NCLOC, Flag.WITH_CHANGESET);
measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(50, 12));
@@ -279,6 +287,7 @@ public class SqaleNewMeasuresVisitorTest {
@Test
public void leaf_components_always_have_a_measure_when_at_least_one_period_exist_and_ratio_is_computed_from_current_level_new_debt() {
+ setTwoPeriods();
when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
treeRootHolder.setRoot(
builder(PROJECT, ROOT_REF)
@@ -291,7 +300,7 @@ public class SqaleNewMeasuresVisitorTest {
).build()
).build()
).build()
- );
+ );
Measure newDebtMeasure = createNewDebtMeasure(50, 12);
measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
@@ -311,6 +320,49 @@ public class SqaleNewMeasuresVisitorTest {
assertNewDebtRatioValues(ROOT_REF, 83.33, 0);
}
+ @Test
+ public void new_debt_ratio_is_computed_for_five_periods() throws Exception {
+ long period1 = 10000L;
+ long period2 = 20000L;
+ long period3 = 30000L;
+ long period4 = 40000L;
+ long period5 = 50000L;
+
+ periodsHolder.setPeriods(
+ new Period(1, SOME_PERIOD_MODE, null, period1, SOME_SNAPSHOT_ID),
+ new Period(2, SOME_PERIOD_MODE, null, period2, SOME_SNAPSHOT_ID),
+ new Period(3, SOME_PERIOD_MODE, null, period3, SOME_SNAPSHOT_ID),
+ new Period(4, SOME_PERIOD_MODE, null, period4, SOME_SNAPSHOT_ID),
+ new Period(5, SOME_PERIOD_MODE, null, period5, SOME_SNAPSHOT_ID));
+
+ when(sqaleRatingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
+
+ treeRootHolder.setRoot(
+ builder(PROJECT, ROOT_REF)
+ .addChildren(builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build())
+ .build()
+ );
+
+ Measure newDebtMeasure = newMeasureBuilder().setVariations(new MeasureVariations(500d, 500d, 500d, 120d, 120d)).createNoValue();
+ measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
+ // 4 lines file, only first one is not ncloc
+ measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NCLOC_DATA_KEY, createNclocDataMeasure(2, 3, 4));
+ // first 2 lines are before all snapshots, 2 last lines are after PERIOD 2's snapshot date
+ scmInfoRepository.setScmInfo(LANGUAGE_1_FILE_REF, createChangesets(period2 - 100, 2, period2 + 100, 2));
+
+ measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY,
+ newMeasureBuilder().setVariations(new MeasureVariations(1200d, 1200d, 1200d, 820d, 820d)).createNoValue());
+
+ underTest.visit(treeRootHolder.getRoot());
+
+ assertThat(measureRepository.getAddedRawMeasure(LANGUAGE_1_FILE_REF, NEW_SQALE_DEBT_RATIO_KEY))
+ .hasVariation1(833.333, VARIATION_COMPARISON_OFFSET)
+ .hasVariation2(833.333, VARIATION_COMPARISON_OFFSET)
+ .hasVariation3(0d, VARIATION_COMPARISON_OFFSET)
+ .hasVariation4(0d, VARIATION_COMPARISON_OFFSET)
+ .hasVariation5(0d, VARIATION_COMPARISON_OFFSET);
+ }
+
private void setupOneFileAloneInAProject(int newDebtPeriod2, int newDebtPeriod4, Flag isUnitTest, Flag withNclocLines, Flag withChangeSets) {
checkArgument(isUnitTest == Flag.UT_FILE || isUnitTest == Flag.SRC_FILE);
checkArgument(withNclocLines == Flag.WITH_NCLOC || withNclocLines == Flag.NO_NCLOC || withNclocLines == Flag.MISSING_MEASURE_NCLOC);
@@ -322,7 +374,7 @@ public class SqaleNewMeasuresVisitorTest {
builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(isUnitTest == Flag.UT_FILE, LANGUAGE_1_KEY)).build()
)
.build()
- );
+ );
Measure newDebtMeasure = createNewDebtMeasure(newDebtPeriod2, newDebtPeriod4);
measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
@@ -399,13 +451,19 @@ public class SqaleNewMeasuresVisitorTest {
}
private void assertNoNewDebtRatioMeasure(int componentRef) {
- assertThat(measureRepository.getAddedRawMeasure(componentRef, CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY))
+ assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SQALE_DEBT_RATIO_KEY))
.isAbsent();
}
private void assertNewDebtRatioValues(int componentRef, double expectedPeriod2Value, double expectedPeriod4Value) {
- assertThat(measureRepository.getAddedRawMeasure(componentRef, CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY))
+ assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SQALE_DEBT_RATIO_KEY))
.hasVariation2(expectedPeriod2Value, VARIATION_COMPARISON_OFFSET)
.hasVariation4(expectedPeriod4Value, VARIATION_COMPARISON_OFFSET);
}
+
+ private void setTwoPeriods() {
+ periodsHolder.setPeriods(
+ new Period(2, SOME_PERIOD_MODE, null, PERIOD_2_SNAPSHOT_DATE, SOME_SNAPSHOT_ID),
+ new Period(4, SOME_PERIOD_MODE, null, PERIOD_5_SNAPSHOT_DATE, SOME_SNAPSHOT_ID));
+ }
}