aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2014-03-19 13:38:43 +0600
committerStas Vilchik <vilchiks@gmail.com>2014-03-19 13:38:43 +0600
commit3c3c1e3e2c091285017986c11f075f5a6d87a643 (patch)
treedda80d8d899e4679082616251dc22eaff7584dd1
parent660591f1137d2dcbd13e9e29a1965bf3dd1febb1 (diff)
parent398dfaff33c9a2c90c2bbbfa0f7f41a867896f8d (diff)
downloadsonarqube-3c3c1e3e2c091285017986c11f075f5a6d87a643.tar.gz
sonarqube-3c3c1e3e2c091285017986c11f075f5a6d87a643.zip
Merge branch 'master' into grunt
Conflicts: sonar-server/src/main/webapp/javascripts/quality-gate/views/quality-gate-detail-condition-view.js sonar-server/src/main/webapp/stylesheets/quality-gates.css
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ExclusionProperties.java10
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingResult.java4
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java2
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java6
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java5
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java10
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java46
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateProvider.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/DefaultProjectBootstrapper.java3
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java10
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/qualitygate/ConditionUtilsTest.java97
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/DefaultProjectBootstrapperTest.java10
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicDao.java89
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicMapper.java8
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql1
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/technicaldebt/db/CharacteristicMapper.xml40
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/db/CharacteristicDaoTest.java34
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/select_max_characteristic_order_when_characteristics_are_all_disabled.xml13
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/select_next_and_previous.xml13
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/shared.xml4
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/detector/suffixtree/Search.java2
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/token/TokenQueue.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java2
-rw-r--r--sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java116
-rw-r--r--sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java6
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/characteristic.rb12
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_condition_template.hbs.erb12
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_projects_template.hbs.erb6
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/db/migrate/499_delete_inclusions_properties.rb35
-rw-r--r--sonar-server/src/main/webapp/coffee/quality-gate/views/quality-gate-detail-condition-view.coffee3
-rw-r--r--sonar-server/src/main/webapp/javascripts/quality-gate/views/quality-gate-detail-condition-view.js146
-rw-r--r--sonar-server/src/main/webapp/less/quality-gates.less10
-rw-r--r--sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java245
-rw-r--r--sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGatesTest.java12
35 files changed, 704 insertions, 317 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ExclusionProperties.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ExclusionProperties.java
index 00d7e7c0611..b169a572965 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ExclusionProperties.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ExclusionProperties.java
@@ -34,24 +34,22 @@ class ExclusionProperties {
static List<PropertyDefinition> definitions() {
return ImmutableList.of(
-
- // Do not display inclusions in UI
- // https://jira.codehaus.org/browse/SONAR-5143
PropertyDefinition.builder(CoreProperties.PROJECT_INCLUSIONS_PROPERTY)
.name("Source File Inclusions")
.multiValues(true)
.category(CoreProperties.CATEGORY_EXCLUSIONS)
.subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .hidden()
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(3)
.build(),
PropertyDefinition.builder(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY)
.name("Test File Inclusions")
.multiValues(true)
.category(CoreProperties.CATEGORY_EXCLUSIONS)
.subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .hidden()
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(5)
.build(),
-
PropertyDefinition.builder(CoreProperties.GLOBAL_EXCLUSIONS_PROPERTY)
.name("Global Source File Exclusions")
.multiValues(true)
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingResult.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingResult.java
index ca6a8a11afc..a3f84600a89 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingResult.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingResult.java
@@ -28,13 +28,13 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.core.issue.db.IssueDto;
import java.util.Collection;
-import java.util.IdentityHashMap;
+import java.util.Map;
import java.util.Set;
class IssueTrackingResult {
private final Set<IssueDto> unmatched = Sets.newHashSet();
private final Multimap<RuleKey, IssueDto> unmatchedByRule = LinkedHashMultimap.create();
- private final IdentityHashMap<DefaultIssue, IssueDto> matched = Maps.newIdentityHashMap();
+ private final Map<DefaultIssue, IssueDto> matched = Maps.newIdentityHashMap();
Collection<IssueDto> unmatched() {
return unmatched;
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java
index cf6af57a06c..7c24a89bb3a 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java
@@ -101,7 +101,7 @@ public class SonarBridgeEngine extends CpdEngine {
}
// Create index
- SonarDuplicationsIndex index = indexFactory.create(project);
+ SonarDuplicationsIndex index = indexFactory.create(project, languageKey);
TokenizerBridge bridge = new TokenizerBridge(mapping.getTokenizer(), fs.encoding().name(), getBlockSize(project, languageKey));
for (InputFile inputFile : sourceFiles) {
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java
index c1890f4a08c..8255053db31 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java
@@ -102,12 +102,12 @@ public class SonarEngine extends CpdEngine {
if (sourceFiles.isEmpty()) {
return;
}
- SonarDuplicationsIndex index = createIndex(project, sourceFiles);
+ SonarDuplicationsIndex index = createIndex(project, languageKey, sourceFiles);
detect(index, context, sourceFiles);
}
- private SonarDuplicationsIndex createIndex(Project project, Iterable<InputFile> sourceFiles) {
- final SonarDuplicationsIndex index = indexFactory.create(project);
+ private SonarDuplicationsIndex createIndex(Project project, String language, Iterable<InputFile> sourceFiles) {
+ final SonarDuplicationsIndex index = indexFactory.create(project, language);
TokenChunker tokenChunker = JavaTokenProducer.build();
StatementChunker statementChunker = JavaStatementBuilder.build();
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java
index d91b4c22954..2b587690753 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java
@@ -46,14 +46,15 @@ public class DbDuplicationsIndex {
private DuplicationDao dao;
- public DbDuplicationsIndex(ResourcePersister resourcePersister, Project currentProject, DuplicationDao dao) {
+ public DbDuplicationsIndex(ResourcePersister resourcePersister, Project currentProject, DuplicationDao dao,
+ String language) {
this.dao = dao;
this.resourcePersister = resourcePersister;
Snapshot currentSnapshot = resourcePersister.getSnapshotOrFail(currentProject);
Snapshot lastSnapshot = resourcePersister.getLastSnapshot(currentSnapshot, false);
this.currentProjectSnapshotId = currentSnapshot.getId();
this.lastSnapshotId = lastSnapshot == null ? null : lastSnapshot.getId();
- this.languageKey = currentProject.getLanguageKey();
+ this.languageKey = language;
}
int getSnapshotIdFor(InputFile inputFile) {
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java
index 4a242726e63..f6f8981f6c1 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java
@@ -44,9 +44,9 @@ public class IndexFactory implements BatchExtension {
this.dao = dao;
}
- public SonarDuplicationsIndex create(Project project) {
+ public SonarDuplicationsIndex create(Project project, String languageKey) {
if (verifyCrossProject(project, LOG)) {
- return new SonarDuplicationsIndex(new DbDuplicationsIndex(resourcePersister, project, dao));
+ return new SonarDuplicationsIndex(new DbDuplicationsIndex(resourcePersister, project, dao, languageKey));
}
return new SonarDuplicationsIndex();
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
index e0e366282a1..584ecce1cad 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
@@ -20,6 +20,8 @@
package org.sonar.batch.bootstrap;
import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
@@ -34,7 +36,11 @@ import org.sonar.core.plugins.RemotePlugin;
import java.io.File;
import java.text.MessageFormat;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
@@ -154,7 +160,7 @@ public class BatchPluginRepository implements PluginRepository {
static List<String> propertyValues(Settings settings, String key, String defaultValue) {
String s = StringUtils.defaultIfEmpty(settings.getString(key), defaultValue);
- return Arrays.asList(StringUtils.split(s, ","));
+ return Lists.newArrayList(Splitter.on(",").trimResults().split(s));
}
boolean accepts(String pluginKey) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java
index f82259d62af..18494255960 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/ConditionUtils.java
@@ -92,22 +92,25 @@ class ConditionUtils {
}
private static Comparable getValueForComparison(Metric metric, String value) {
- if (isADouble(metric)) {
- return Double.parseDouble(value);
- }
- if (isAInteger(metric)) {
- return parseInteger(value);
- }
- if (isAString(metric)) {
- return value;
- }
- if (isABoolean(metric)) {
- return Integer.parseInt(value);
- }
- if (isAWorkDuration(metric)) {
- return Long.parseLong(value);
- }
- throw new NotImplementedException(metric.getType().toString());
+ Comparable valueToCompare = null;
+ try {
+ if (isADouble(metric)) {
+ valueToCompare = Double.parseDouble(value);
+ } else if (isAInteger(metric)) {
+ valueToCompare = parseInteger(value);
+ } else if (isAString(metric)) {
+ valueToCompare = value;
+ } else if (isABoolean(metric)) {
+ valueToCompare = Integer.parseInt(value);
+ } else if (isAWorkDuration(metric)) {
+ valueToCompare = Long.parseLong(value);
+ } else {
+ throw new NotImplementedException(metric.getType().toString());
+ }
+ } catch (NumberFormatException badValueFormat) {
+ throw new IllegalArgumentException(String.format("Unable to parse value '%s' to compare against %s", value, metric.getName()));
+ }
+ return valueToCompare;
}
private static Comparable<Integer> parseInteger(String value) {
@@ -176,16 +179,17 @@ class ConditionUtils {
}
private static Double getValue(ResolvedCondition condition, Measure measure) {
- if (condition.period() == null) {
+ Integer period = condition.period();
+ if (period == null) {
return measure.getValue();
- } else if (condition.period() == 1) {
+ } else if (period == 1) {
return measure.getVariation1();
- } else if (condition.period() == 2) {
+ } else if (period == 2) {
return measure.getVariation2();
- } else if (condition.period() == 3) {
+ } else if (period == 3) {
return measure.getVariation3();
} else {
- throw new IllegalStateException("Following index period is not allowed : " + Double.toString(condition.period()));
+ throw new IllegalStateException("Following index period is not allowed : " + Double.toString(period));
}
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateProvider.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateProvider.java
index 5a8cbc98f89..e2e298f2fd1 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateProvider.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateProvider.java
@@ -70,7 +70,7 @@ public class QualityGateProvider extends ProviderAdapter {
configuredGate = fetch(qualityGateSetting, client, metricFinder);
} catch (HttpDownloader.HttpException serverError) {
if (serverError.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
- throw MessageException.of("No quality gate found with configured value '" + qualityGateSetting + "'. Please check your configuration.");
+ throw MessageException.of("Quality gate '" + qualityGateSetting + "' was not found.");
} else {
throw serverError;
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/DefaultProjectBootstrapper.java b/sonar-batch/src/main/java/org/sonar/batch/scan/DefaultProjectBootstrapper.java
index bc0927250de..c84b9d5a2f1 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/DefaultProjectBootstrapper.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/DefaultProjectBootstrapper.java
@@ -91,7 +91,8 @@ class DefaultProjectBootstrapper implements ProjectBootstrapper {
* Array of all mandatory properties required for a project without child.
*/
private static final String[] MANDATORY_PROPERTIES_FOR_SIMPLE_PROJECT = {
- PROPERTY_PROJECT_BASEDIR, CoreProperties.PROJECT_KEY_PROPERTY, CoreProperties.PROJECT_NAME_PROPERTY, CoreProperties.PROJECT_VERSION_PROPERTY
+ PROPERTY_PROJECT_BASEDIR, CoreProperties.PROJECT_KEY_PROPERTY, CoreProperties.PROJECT_NAME_PROPERTY,
+ CoreProperties.PROJECT_VERSION_PROPERTY, PROPERTY_SOURCES
};
/**
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
index 41a6d0048d5..b1a4eb2a999 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
@@ -222,4 +222,14 @@ public class BatchPluginRepositoryTest {
assertThat(filter.whites).containsOnly("cockpit");
assertThat(filter.blacks).containsOnly("views", "checkstyle", "pmd");
}
+
+ @Test
+ public void inclusions_and_exclusions_should_be_trimmed() {
+ Settings settings = new Settings()
+ .setProperty(CoreProperties.BATCH_INCLUDE_PLUGINS, "checkstyle, pmd, findbugs")
+ .setProperty(CoreProperties.BATCH_EXCLUDE_PLUGINS, "cobertura, pmd");
+ BatchPluginRepository.PluginFilter filter = new BatchPluginRepository.PluginFilter(settings, mode);
+ assertThat(filter.accepts("pmd")).isTrue();
+ }
+
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/ConditionUtilsTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/ConditionUtilsTest.java
index 5b1c0136e42..6ea5bee366f 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/ConditionUtilsTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/ConditionUtilsTest.java
@@ -20,13 +20,15 @@
package org.sonar.batch.qualitygate;
-import org.junit.Assert;
+import org.apache.commons.lang.NotImplementedException;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
import org.sonar.core.qualitygate.db.QualityGateConditionDto;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -57,7 +59,7 @@ public class ConditionUtilsTest {
when(condition.errorThreshold()).thenReturn("20");
ConditionUtils.getLevel(condition, measure);
} catch (NumberFormatException ex) {
- Assert.fail();
+ fail();
}
try {
@@ -65,7 +67,7 @@ public class ConditionUtilsTest {
when(condition.errorThreshold()).thenReturn("20.1");
ConditionUtils.getLevel(condition, measure);
} catch (NumberFormatException ex) {
- Assert.fail();
+ fail();
}
try {
@@ -73,7 +75,7 @@ public class ConditionUtilsTest {
when(condition.errorThreshold()).thenReturn("20.1");
ConditionUtils.getLevel(condition, measure);
} catch (NumberFormatException ex) {
- Assert.fail();
+ fail();
}
}
@@ -86,20 +88,20 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("10.2");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
when(condition.errorThreshold()).thenReturn("10.1");
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
metric.setType(Metric.ValueType.STRING);
measure.setData("TEST");
measure.setValue(null);
when(condition.errorThreshold()).thenReturn("TEST");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
when(condition.errorThreshold()).thenReturn("TEST2");
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
}
@@ -112,20 +114,20 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("10.2");
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
when(condition.errorThreshold()).thenReturn("10.1");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
metric.setType(Metric.ValueType.STRING);
measure.setData("TEST");
measure.setValue(null);
when(condition.errorThreshold()).thenReturn("TEST");
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
when(condition.errorThreshold()).thenReturn("TEST2");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
}
@@ -137,10 +139,10 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("10.1");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
when(condition.errorThreshold()).thenReturn("10.3");
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
}
@Test
@@ -151,10 +153,10 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("10.1");
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
when(condition.errorThreshold()).thenReturn("10.3");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
}
@Test
@@ -165,7 +167,7 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("10.2");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
}
@Test
@@ -176,7 +178,7 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("10.2");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
}
@Test
@@ -187,10 +189,10 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("10");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
when(condition.errorThreshold()).thenReturn("10.2");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
}
@Test
@@ -201,13 +203,13 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn(Metric.Level.ERROR.toString());
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
when(condition.errorThreshold()).thenReturn(Metric.Level.OK.toString());
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_NOT_EQUALS);
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
}
@Test
@@ -218,17 +220,25 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("1");
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
when(condition.errorThreshold()).thenReturn("0");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_NOT_EQUALS);
when(condition.errorThreshold()).thenReturn("1");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
when(condition.errorThreshold()).thenReturn("0");
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
+
+ when(condition.errorThreshold()).thenReturn("polop");
+ try {
+ ConditionUtils.getLevel(condition, measure);
+ fail();
+ } catch(Exception expected) {
+ assertThat(expected).isInstanceOf(IllegalArgumentException.class).hasMessage("Unable to parse value 'polop' to compare against name");
+ }
}
@Test
@@ -239,7 +249,15 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("60");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
+
+ when(condition.errorThreshold()).thenReturn("polop");
+ try {
+ ConditionUtils.getLevel(condition, measure);
+ fail();
+ } catch(Exception expected) {
+ assertThat(expected).isInstanceOf(IllegalArgumentException.class).hasMessage("Unable to parse value 'polop' to compare against name");
+ }
}
@Test
@@ -250,13 +268,30 @@ public class ConditionUtilsTest {
when(condition.metric()).thenReturn(metric);
when(condition.errorThreshold()).thenReturn("10.2");
- Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.ERROR);
when(condition.errorThreshold()).thenReturn("10.1");
- Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.OK);
when(condition.errorThreshold()).thenReturn("10.3");
when(condition.warningThreshold()).thenReturn("10.2");
- Assert.assertEquals(Metric.Level.WARN, ConditionUtils.getLevel(condition, measure));
+ assertThat(ConditionUtils.getLevel(condition, measure)).isEqualTo(Metric.Level.WARN);
+ }
+
+ @Test
+ public void testUnsupportedType() {
+ metric.setType(Metric.ValueType.DATA);
+ measure.setValue(3.14159265358);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("1.60217657");
+ try {
+ ConditionUtils.getLevel(condition, measure);
+ fail();
+ } catch (Exception expected) {
+ assertThat(expected).isInstanceOf(NotImplementedException.class);
+ }
}
+
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/DefaultProjectBootstrapperTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/DefaultProjectBootstrapperTest.java
index fbb6dce4889..c96a789ff9f 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan/DefaultProjectBootstrapperTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/DefaultProjectBootstrapperTest.java
@@ -85,7 +85,9 @@ public class DefaultProjectBootstrapperTest {
}
@Test
- public void shouldNotFailIfMissingSourceDirectory() throws IOException {
+ public void fail_if_sources_not_set() throws IOException {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("You must define the following mandatory properties for 'com.foo.project': sonar.sources");
loadProjectDefinition("simple-project-with-missing-source-dir");
}
@@ -469,7 +471,7 @@ public class DefaultProjectBootstrapperTest {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("You must define the following mandatory properties for 'Unknown': foo2, foo3");
- DefaultProjectBootstrapper.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"});
+ DefaultProjectBootstrapper.checkMandatoryProperties(props, new String[]{"foo1", "foo2", "foo3"});
}
@Test
@@ -481,7 +483,7 @@ public class DefaultProjectBootstrapperTest {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("You must define the following mandatory properties for 'my-project': foo2, foo3");
- DefaultProjectBootstrapper.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"});
+ DefaultProjectBootstrapper.checkMandatoryProperties(props, new String[]{"foo1", "foo2", "foo3"});
}
@Test
@@ -490,7 +492,7 @@ public class DefaultProjectBootstrapperTest {
props.setProperty("foo1", "bla");
props.setProperty("foo4", "bla");
- DefaultProjectBootstrapper.checkMandatoryProperties(props, new String[] {"foo1"});
+ DefaultProjectBootstrapper.checkMandatoryProperties(props, new String[]{"foo1"});
// No exception should be thrown
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicDao.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicDao.java
index 42ef216b738..05555a28486 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicDao.java
@@ -39,7 +39,6 @@ public class CharacteristicDao implements BatchComponent, ServerComponent {
/**
* @return enabled root characteristics and characteristics
- *
*/
public List<CharacteristicDto> selectEnabledCharacteristics() {
SqlSession session = mybatis.openSession();
@@ -56,7 +55,6 @@ public class CharacteristicDao implements BatchComponent, ServerComponent {
/**
* @return all characteristics
- *
*/
public List<CharacteristicDto> selectCharacteristics() {
SqlSession session = mybatis.openSession();
@@ -76,36 +74,111 @@ public class CharacteristicDao implements BatchComponent, ServerComponent {
*/
public List<CharacteristicDto> selectEnabledRootCharacteristics() {
SqlSession session = mybatis.openSession();
- CharacteristicMapper mapper = session.getMapper(CharacteristicMapper.class);
try {
- return mapper.selectEnabledRootCharacteristics();
+ return selectEnabledRootCharacteristics(session);
} finally {
MyBatis.closeQuietly(session);
}
}
+ /**
+ * @return only enabled root characteristics, order by order
+ */
+ public List<CharacteristicDto> selectEnabledRootCharacteristics(SqlSession session) {
+ return session.getMapper(CharacteristicMapper.class).selectEnabledRootCharacteristics();
+ }
+
@CheckForNull
public CharacteristicDto selectByKey(String key) {
SqlSession session = mybatis.openSession();
- CharacteristicMapper mapper = session.getMapper(CharacteristicMapper.class);
try {
- return mapper.selectByKey(key);
+ return selectByKey(key, session);
} finally {
MyBatis.closeQuietly(session);
}
}
@CheckForNull
+ public CharacteristicDto selectByKey(String key, SqlSession session) {
+ return session.getMapper(CharacteristicMapper.class).selectByKey(key);
+ }
+
+ @CheckForNull
public CharacteristicDto selectById(int id) {
SqlSession session = mybatis.openSession();
- CharacteristicMapper mapper = session.getMapper(CharacteristicMapper.class);
try {
- return mapper.selectById(id);
+ return selectById(id, session);
} finally {
MyBatis.closeQuietly(session);
}
}
+ @CheckForNull
+ public CharacteristicDto selectById(int id, SqlSession session) {
+ return session.getMapper(CharacteristicMapper.class).selectById(id);
+ }
+
+ @CheckForNull
+ public CharacteristicDto selectByName(String name) {
+ SqlSession session = mybatis.openSession();
+ try {
+ return selectByName(name, session);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ @CheckForNull
+ public CharacteristicDto selectByName(String name, SqlSession session) {
+ return session.getMapper(CharacteristicMapper.class).selectByName(name);
+ }
+
+ @CheckForNull
+ public CharacteristicDto selectNext(int order, SqlSession session) {
+ List<CharacteristicDto> dtos = session.getMapper(CharacteristicMapper.class).selectNext(order);
+ return dtos.isEmpty() ? null : dtos.get(0);
+ }
+
+ @CheckForNull
+ public CharacteristicDto selectNext(int order) {
+ SqlSession session = mybatis.openSession();
+ try {
+ return selectNext(order, session);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ @CheckForNull
+ public CharacteristicDto selectPrevious(int order, SqlSession session) {
+ List<CharacteristicDto> dtos = session.getMapper(CharacteristicMapper.class).selectPrevious(order);
+ return dtos.isEmpty() ? null : dtos.get(0);
+ }
+
+ @CheckForNull
+ public CharacteristicDto selectPrevious(int order) {
+ SqlSession session = mybatis.openSession();
+ try {
+ return selectPrevious(order, session);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public int selectMaxCharacteristicOrder() {
+ SqlSession session = mybatis.openSession();
+ try {
+ return selectMaxCharacteristicOrder(session);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public int selectMaxCharacteristicOrder(SqlSession session) {
+ Integer result = session.getMapper(CharacteristicMapper.class).selectMaxCharacteristicOrder();
+ return result != null ? result : 0;
+ }
+
public void insert(CharacteristicDto dto, SqlSession session) {
session.getMapper(CharacteristicMapper.class).insert(dto);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicMapper.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicMapper.java
index 8557c307c3e..fe995a8c078 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicMapper.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicMapper.java
@@ -34,6 +34,14 @@ public interface CharacteristicMapper {
CharacteristicDto selectById(int id);
+ CharacteristicDto selectByName(String name);
+
+ List<CharacteristicDto> selectNext(int order);
+
+ List<CharacteristicDto> selectPrevious(int order);
+
+ Integer selectMaxCharacteristicOrder();
+
void insert(CharacteristicDto characteristic);
int update(CharacteristicDto characteristic);
diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
index d01a3101ee4..b924803a886 100644
--- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
+++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
@@ -208,7 +208,6 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('495');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('496');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('497');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('498');
-INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('499');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('510');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('511');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('512');
diff --git a/sonar-core/src/main/resources/org/sonar/core/technicaldebt/db/CharacteristicMapper.xml b/sonar-core/src/main/resources/org/sonar/core/technicaldebt/db/CharacteristicMapper.xml
index 4cf3ea98e7f..e4cde1a0ac8 100644
--- a/sonar-core/src/main/resources/org/sonar/core/technicaldebt/db/CharacteristicMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/technicaldebt/db/CharacteristicMapper.xml
@@ -56,6 +56,46 @@
</where>
</select>
+ <select id="selectByName" parameterType="String" resultType="Characteristic">
+ select <include refid="characteristicColumns"/>
+ from characteristics c
+ <where>
+ and c.name=#{name}
+ and c.enabled=${_true}
+ </where>
+ </select>
+
+ <select id="selectNext" parameterType="Integer" resultType="Characteristic">
+ select <include refid="characteristicColumns"/>
+ from characteristics c
+ <where>
+ and c.characteristic_order&gt;#{order}
+ and c.parent_id is null
+ and c.enabled=${_true}
+ </where>
+ order by characteristic_order asc
+ </select>
+
+ <select id="selectPrevious" parameterType="Integer" resultType="Characteristic">
+ select <include refid="characteristicColumns"/>
+ from characteristics c
+ <where>
+ and c.characteristic_order&lt;#{order}
+ and c.parent_id is null
+ and c.enabled=${_true}
+ </where>
+ order by characteristic_order asc
+ </select>
+
+ <select id="selectMaxCharacteristicOrder" resultType="Integer">
+ select max(c.characteristic_order)
+ from characteristics c
+ <where>
+ and c.parent_id is null
+ and c.enabled=${_true}
+ </where>
+ </select>
+
<insert id="insert" parameterType="Characteristic" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
INSERT INTO characteristics (kee, name, parent_id, characteristic_order, enabled, created_at, updated_at)
VALUES (#{kee}, #{name}, #{parentId}, #{characteristicOrder}, #{enabled}, current_timestamp, current_timestamp)
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/db/CharacteristicDaoTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/db/CharacteristicDaoTest.java
index 1a3fa00574a..d89af50367a 100644
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/db/CharacteristicDaoTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/db/CharacteristicDaoTest.java
@@ -120,6 +120,15 @@ public class CharacteristicDaoTest extends AbstractDaoTestCase {
}
@Test
+ public void select_characteristic_by_name() {
+ setupData("shared");
+
+ assertThat(dao.selectByName("Portability")).isNotNull();
+ assertThat(dao.selectByName("Compiler related portability")).isNotNull();
+ assertThat(dao.selectByName("Unknown")).isNull();
+ }
+
+ @Test
public void select_characteristic_by_id() {
setupData("shared");
@@ -130,6 +139,31 @@ public class CharacteristicDaoTest extends AbstractDaoTestCase {
}
@Test
+ public void select_next_and_previous_characteristic() {
+ setupData("select_next_and_previous");
+
+ assertThat(dao.selectNext(1)).isNotNull();
+ assertThat(dao.selectNext(2)).isNull();
+
+ assertThat(dao.selectPrevious(1)).isNull();
+ assertThat(dao.selectPrevious(2)).isNotNull();
+ }
+
+ @Test
+ public void select_max_characteristic_order() {
+ setupData("shared");
+
+ assertThat(dao.selectMaxCharacteristicOrder()).isEqualTo(1);
+ }
+
+ @Test
+ public void select_max_characteristic_order_when_characteristics_are_all_disabled() {
+ setupData("select_max_characteristic_order_when_characteristics_are_all_disabled");
+
+ assertThat(dao.selectMaxCharacteristicOrder()).isEqualTo(0);
+ }
+
+ @Test
public void insert_characteristic() throws Exception {
CharacteristicDto dto = new CharacteristicDto()
.setKey("COMPILER_RELATED_PORTABILITY")
diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/select_max_characteristic_order_when_characteristics_are_all_disabled.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/select_max_characteristic_order_when_characteristics_are_all_disabled.xml
new file mode 100644
index 00000000000..78e951519bd
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/select_max_characteristic_order_when_characteristics_are_all_disabled.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <!-- Disabled root characteristic -->
+ <characteristics id="1" kee="PORTABILITY" name="Portability" parent_id="[null]" characteristic_order="1"
+ enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+ <!-- Disabled root characteristic -->
+ <characteristics id="2" kee="DISABLED_ROOT_CHARACTERISTIC" name="Disabled root characteristic" parent_id="[null]" characteristic_order="2"
+ enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/select_next_and_previous.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/select_next_and_previous.xml
new file mode 100644
index 00000000000..8f96903ef25
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/select_next_and_previous.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <!-- Root characteristic -->
+ <characteristics id="1" kee="PORTABILITY" name="Portability" parent_id="[null]" characteristic_order="1"
+ enabled="[true]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+ <!-- Disabled root characteristic -->
+ <characteristics id="4" kee="MAINTAINABILITY" name="Maintainability" parent_id="[null]" characteristic_order="2"
+ enabled="[true]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/shared.xml
index e8c52c0a142..499959e3c3a 100644
--- a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/shared.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/CharacteristicDaoTest/shared.xml
@@ -6,7 +6,7 @@
created_at="2013-11-20" updated_at="2013-11-22"/>
<!-- Characteristic -->
- <characteristics id="2" kee="COMPILER_RELATED_PORTABILITY" name="Compiler related portability" parent_id="1" root_id="1" characteristic_order="[null]"
+ <characteristics id="2" kee="COMPILER_RELATED_PORTABILITY" name="Compiler related portability" parent_id="1" rcharacteristic_order="[null]"
enabled="[true]"
created_at="2013-11-20" updated_at="2013-11-22"/>
@@ -16,7 +16,7 @@
created_at="2013-11-20" updated_at="2013-11-22"/>
<!-- Disabled characteristic -->
- <characteristics id="5" kee="DISABLED_CHARACTERISTIC" name="Disabled characteristic" parent_id="4" root_id="4" characteristic_order="[null]"
+ <characteristics id="5" kee="DISABLED_CHARACTERISTIC" name="Disabled characteristic" parent_id="4" characteristic_order="[null]"
enabled="[false]"
created_at="2013-11-20" updated_at="2013-11-22"/>
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/detector/suffixtree/Search.java b/sonar-duplications/src/main/java/org/sonar/duplications/detector/suffixtree/Search.java
index 06202b71a45..7d8a3a9877b 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/detector/suffixtree/Search.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/detector/suffixtree/Search.java
@@ -63,7 +63,7 @@ public final class Search {
* Depth-first search (DFS).
*/
private void dfs() {
- LinkedList<Node> stack = Lists.newLinkedList();
+ Deque<Node> stack = Lists.newLinkedList();
stack.add(tree.getRootNode());
while (!stack.isEmpty()) {
Node node = stack.removeLast();
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/token/TokenQueue.java b/sonar-duplications/src/main/java/org/sonar/duplications/token/TokenQueue.java
index 90cfa404c34..e1ad2e8da24 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/token/TokenQueue.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/token/TokenQueue.java
@@ -19,6 +19,7 @@
*/
package org.sonar.duplications.token;
+import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -26,7 +27,7 @@ import java.util.ListIterator;
public class TokenQueue implements Iterable<Token> {
- private final LinkedList<Token> tokenQueue;
+ private final Deque<Token> tokenQueue;
public TokenQueue(List<Token> tokenList) {
tokenQueue = new LinkedList<Token>(tokenList);
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java
index a160deecc11..00b4af26138 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java
@@ -166,7 +166,7 @@ public final class PropertyDefinitions implements BatchComponent, ServerComponen
Map<Category, Map<SubCategory, Collection<PropertyDefinition>>> byCategory = new HashMap<Category, Map<SubCategory, Collection<PropertyDefinition>>>();
if (qualifier == null) {
// Special categories on global page
- HashMap<SubCategory, Collection<PropertyDefinition>> emailSubCategories = new HashMap<SubCategory, Collection<PropertyDefinition>>();
+ Map<SubCategory, Collection<PropertyDefinition>> emailSubCategories = new HashMap<SubCategory, Collection<PropertyDefinition>>();
emailSubCategories.put(new SubCategory("email", true), new ArrayList<PropertyDefinition>());
byCategory.put(new Category(CoreProperties.CATEGORY_GENERAL, false), emailSubCategories);
diff --git a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java
index a5a32807add..367f3bd1052 100644
--- a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java
+++ b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java
@@ -22,13 +22,21 @@ package org.sonar.server.debt;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
+import org.apache.ibatis.session.SqlSession;
import org.sonar.api.server.debt.DebtCharacteristic;
import org.sonar.api.server.debt.DebtModel;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.persistence.MyBatis;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.util.Validation;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
@@ -37,12 +45,15 @@ import static com.google.common.collect.Lists.newArrayList;
/**
* Used through ruby code <pre>Internal.debt</pre>
+ * Also used by SQALE plugin.
*/
public class DebtModelService implements DebtModel {
+ private final MyBatis mybatis;
private final CharacteristicDao dao;
- public DebtModelService(CharacteristicDao dao) {
+ public DebtModelService(MyBatis mybatis, CharacteristicDao dao) {
+ this.mybatis = mybatis;
this.dao = dao;
}
@@ -60,6 +71,109 @@ public class DebtModelService implements DebtModel {
return dto != null ? toCharacteristic(dto) : null;
}
+ public DebtCharacteristic create(String name, @Nullable Integer parentId) {
+ checkPermission();
+
+ SqlSession session = mybatis.openSession();
+ try {
+ checkNotAlreadyExists(name, session);
+
+ CharacteristicDto newCharacteristic = new CharacteristicDto()
+ .setKey(name.toUpperCase().replace(" ", "_"))
+ .setName(name)
+ .setEnabled(true);
+
+ // New sub characteristic
+ if (parentId != null) {
+ CharacteristicDto parent = findCharacteristic(parentId, session);
+ if (parent.getParentId() != null) {
+ throw new BadRequestException("A sub characteristic can not have a sub characteristic as parent.");
+ }
+ newCharacteristic.setParentId(parent.getId());
+ } else {
+ // New root characteristic
+ newCharacteristic.setOrder(dao.selectMaxCharacteristicOrder(session) + 1);
+ }
+ dao.insert(newCharacteristic, session);
+ session.commit();
+ return toCharacteristic(newCharacteristic);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public DebtCharacteristic rename(int characteristicId, String newName) {
+ checkPermission();
+
+ SqlSession session = mybatis.openSession();
+ try {
+ checkNotAlreadyExists(newName, session);
+
+ CharacteristicDto dto = findCharacteristic(characteristicId, session);
+ if (!dto.getName().equals(newName)) {
+ dto.setName(newName);
+ dao.update(dto, session);
+ session.commit();
+ }
+ return toCharacteristic(dto);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public DebtCharacteristic moveUp(int characteristicId) {
+ return move(characteristicId, true);
+ }
+
+ public DebtCharacteristic moveDown(int characteristicId) {
+ return move(characteristicId, false);
+ }
+
+ private DebtCharacteristic move(int characteristicId, boolean moveUpOrDown) {
+ checkPermission();
+
+ SqlSession session = mybatis.openSession();
+ try {
+ CharacteristicDto dto = findCharacteristic(characteristicId, session);
+ int currentOrder = dto.getOrder();
+ CharacteristicDto dtoToSwitchOrderWith = moveUpOrDown ? dao.selectPrevious(currentOrder, session) : dao.selectNext(currentOrder, session);
+
+ // Do nothing when characteristic is already to the new location
+ if (dtoToSwitchOrderWith == null) {
+ return toCharacteristic(dto);
+ }
+ int nextOrder = dtoToSwitchOrderWith.getOrder();
+ dtoToSwitchOrderWith.setOrder(currentOrder);
+ dao.update(dtoToSwitchOrderWith, session);
+
+ dto.setOrder(nextOrder);
+ dao.update(dto, session);
+
+ session.commit();
+ return toCharacteristic(dto);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ private CharacteristicDto findCharacteristic(Integer id, SqlSession session) {
+ CharacteristicDto dto = dao.selectById(id, session);
+ if (dto == null) {
+ throw new NotFoundException(String.format("Characteristic with id %s does not exists.", id));
+ }
+ return dto;
+ }
+
+ private void checkNotAlreadyExists(String name, SqlSession session) {
+ if (dao.selectByName(name, session) != null) {
+ throw BadRequestException.ofL10n(Validation.IS_ALREADY_USED_MESSAGE, name);
+ }
+ }
+
+ private void checkPermission() {
+ UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
+ }
+
private static List<DebtCharacteristic> toCharacteristics(Collection<CharacteristicDto> dtos) {
return newArrayList(Iterables.transform(dtos, new Function<CharacteristicDto, DebtCharacteristic>() {
@Override
diff --git a/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java b/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java
index 10ab51f61a5..5f51aa76eb8 100644
--- a/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java
+++ b/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java
@@ -139,11 +139,11 @@ public class QualityGates {
public void delete(long idToDelete) {
checkPermission(UserSession.get());
QualityGateDto qGate = getNonNullQgate(idToDelete);
- if (isDefault(qGate)) {
- throw new BadRequestException("Impossible to delete default quality gate.");
- }
SqlSession session = myBatis.openSession();
try {
+ if (isDefault(qGate)) {
+ propertiesDao.deleteGlobalProperty(SONAR_QUALITYGATE_PROPERTY, session);
+ }
propertiesDao.deleteProjectProperties(SONAR_QUALITYGATE_PROPERTY, Long.toString(idToDelete), session);
dao.delete(qGate, session);
session.commit();
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/characteristic.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/characteristic.rb
index e04b5154720..2633ee39b42 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/models/characteristic.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/models/characteristic.rb
@@ -30,10 +30,12 @@ class Characteristic < ActiveRecord::Base
MINUTE = "mn"
belongs_to :parent, :class_name => 'Characteristic', :foreign_key => 'parent_id'
+
+ # Needed for Views Plugin. Remove it when the plugin will not used it anymore
belongs_to :rule
- validates_uniqueness_of :name, :scope => [:enabled], :case_sensitive => false, :if => Proc.new { |c| c.rule_id.nil? && c.enabled }
- validates_length_of :name, :in => 1..NAME_MAX_SIZE, :allow_blank => false, :if => Proc.new { |c| c.rule_id.nil? }
+ validates_uniqueness_of :name, :scope => [:enabled], :case_sensitive => false, :if => Proc.new { |c| c.enabled }
+ validates_length_of :name, :in => 1..NAME_MAX_SIZE, :allow_blank => false
def root?
parent_id.nil?
@@ -48,11 +50,7 @@ class Characteristic < ActiveRecord::Base
end
def name(rule_name_if_empty=false)
- result=read_attribute(:name)
- if (result.nil? && rule_name_if_empty && rule_id)
- result=rule.name
- end
- result
+ read_attribute(:name)
end
end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_condition_template.hbs.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_condition_template.hbs.erb
index 6023e5b4357..7ca50016040 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_condition_template.hbs.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_condition_template.hbs.erb
@@ -5,11 +5,13 @@
<td width="10%" nowrap>
{{#if canEdit}}
<select name="period">
- <option value="0">{{t 'value'}}</option>
- {{#each periods}}<option value="{{key}}">{{text}}</option>{{/each}}
+ {{#unless isDiffMetric}}<option value="0">{{t 'value'}}</option>{{/unless}}
+ {{#each periods}}<option value="{{key}}">&Delta; {{text}}</option>{{/each}}
</select>
{{else}}
- {{periodText}}
+ {{#if periodText}}&Delta; {{periodText}}
+ {{else}}{{t 'value'}}
+ {{/if}}
{{/if}}
</td>
<td width="10%" nowrap>
@@ -23,7 +25,7 @@
{{t 'quality_gates.operator' op}}
{{/if}}
</td>
- <td width="15%">
+ <td width="15%" nowrap="nowrap">
<i class="icon-alert-warn" title="{{t 'alerts.warning_tooltip'}}"></i>
{{#if canEdit}}
<input name="warning" class="measure-input" data-type="{{metric.type}}" type="text">
@@ -31,7 +33,7 @@
{{warning}}
{{/if}}
</td>
- <td width="15%">
+ <td width="15%" nowrap="nowrap">
<i class="icon-alert-error" title="{{t 'alerts.error_tooltip'}}"></i>
{{#if canEdit}}
<input name="error" class="measure-input" data-type="{{metric.type}}" type="text">
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_projects_template.hbs.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_projects_template.hbs.erb
index 22487657e3b..7ea0b397156 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_projects_template.hbs.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_projects_template.hbs.erb
@@ -2,11 +2,13 @@
<div class="quality-gate-section-name">{{t 'quality_gates.projects'}}</div>
{{#if default}}
+ <p class="quality-gate-default-message">
{{#if canEdit}}
- <p>{{t 'quality_gates.projects_for_default.edit'}}</p>
+ {{t 'quality_gates.projects_for_default.edit'}}
{{else}}
- <p>{{t 'quality_gates.projects_for_default'}}</p>
+ {{t 'quality_gates.projects_for_default'}}
{{/if}}
+ </p>
{{else}}
<div id="select-list-projects"></div>
{{/if}}
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/499_delete_inclusions_properties.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/499_delete_inclusions_properties.rb
deleted file mode 100644
index 84a8bfb9899..00000000000
--- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/499_delete_inclusions_properties.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2013 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.
-#
-
-#
-# SonarQube 4.2
-# SONAR-5143
-#
-class DeleteInclusionsProperties < ActiveRecord::Migration
-
- class Property < ActiveRecord::Base
- end
-
- def self.up
- Property.delete_all("prop_key = 'sonar.inclusions'")
- Property.delete_all("prop_key = 'sonar.test.inclusions'")
- end
-
-end
diff --git a/sonar-server/src/main/webapp/coffee/quality-gate/views/quality-gate-detail-condition-view.coffee b/sonar-server/src/main/webapp/coffee/quality-gate/views/quality-gate-detail-condition-view.coffee
index da1b8d46f04..26f23dbc32e 100644
--- a/sonar-server/src/main/webapp/coffee/quality-gate/views/quality-gate-detail-condition-view.coffee
+++ b/sonar-server/src/main/webapp/coffee/quality-gate/views/quality-gate-detail-condition-view.coffee
@@ -42,6 +42,7 @@ define [
metricKey = @model.get('metric')
metric = _.findWhere @options.app.metrics, key: metricKey
@model.set { metric: metric }, { silent: true }
+ @model.set { isDiffMetric: metric.key.indexOf('new_') == 0 }, { silent: true }
onRender: ->
@@ -105,7 +106,7 @@ define [
serializeData: ->
- period = _.findWhere(@options.app.periods, key: '' + this.model.get('period'))
+ period = _.findWhere(@options.app.periods, key: this.model.get('period'))
_.extend super,
canEdit: @options.app.canEdit
periods: @options.app.periods
diff --git a/sonar-server/src/main/webapp/javascripts/quality-gate/views/quality-gate-detail-condition-view.js b/sonar-server/src/main/webapp/javascripts/quality-gate/views/quality-gate-detail-condition-view.js
deleted file mode 100644
index d69b084c3e4..00000000000
--- a/sonar-server/src/main/webapp/javascripts/quality-gate/views/quality-gate-detail-condition-view.js
+++ /dev/null
@@ -1,146 +0,0 @@
-(function() {
- var __hasProp = {}.hasOwnProperty,
- __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
-
- define(['backbone.marionette', 'handlebars'], function(Marionette, Handlebars) {
- var QualityGateDetailConditionView;
- return QualityGateDetailConditionView = (function(_super) {
- __extends(QualityGateDetailConditionView, _super);
-
- function QualityGateDetailConditionView() {
- return QualityGateDetailConditionView.__super__.constructor.apply(this, arguments);
- }
-
- QualityGateDetailConditionView.prototype.tagName = 'tr';
-
- QualityGateDetailConditionView.prototype.template = Handlebars.compile(jQuery('#quality-gate-detail-condition-template').html());
-
- QualityGateDetailConditionView.prototype.spinner = '<i class="spinner"></i>';
-
- QualityGateDetailConditionView.prototype.modelEvents = {
- 'change:id': 'render'
- };
-
- QualityGateDetailConditionView.prototype.ui = {
- periodSelect: '[name=period]',
- operatorSelect: '[name=operator]',
- warningInput: '[name=warning]',
- errorInput: '[name=error]',
- actionsBox: '.quality-gate-condition-actions',
- updateButton: '.update-condition'
- };
-
- QualityGateDetailConditionView.prototype.events = {
- 'click @ui.updateButton': 'saveCondition',
- 'click .delete-condition': 'deleteCondition',
- 'click .add-condition': 'saveCondition',
- 'click .cancel-add-condition': 'cancelAddCondition',
- 'keyup :input': 'enableUpdate',
- 'change :input': 'enableUpdate'
- };
-
- QualityGateDetailConditionView.prototype.initialize = function() {
- return this.populateMetric();
- };
-
- QualityGateDetailConditionView.prototype.populateMetric = function() {
- var metric, metricKey;
- metricKey = this.model.get('metric');
- metric = _.findWhere(this.options.app.metrics, {
- key: metricKey
- });
- return this.model.set({
- metric: metric
- }, {
- silent: true
- });
- };
-
- QualityGateDetailConditionView.prototype.onRender = function() {
- this.ui.periodSelect.val(this.model.get('period') || '0');
- this.ui.operatorSelect.val(this.model.get('op'));
- this.ui.warningInput.val(this.model.get('warning'));
- this.ui.errorInput.val(this.model.get('error'));
- this.ui.periodSelect.select2({
- allowClear: false,
- minimumResultsForSearch: 999,
- width: '200px'
- });
- this.ui.operatorSelect.select2({
- allowClear: false,
- minimumResultsForSearch: 999,
- width: '150px'
- });
- if (this.model.isNew()) {
- return this.ui.periodSelect.select2('open');
- }
- };
-
- QualityGateDetailConditionView.prototype.showSpinner = function() {
- jQuery(this.spinner).prependTo(this.ui.actionsBox);
- return this.ui.actionsBox.find(':not(.spinner)').hide();
- };
-
- QualityGateDetailConditionView.prototype.hideSpinner = function() {
- this.ui.actionsBox.find('.spinner').remove();
- return this.ui.actionsBox.find(':not(.spinner)').show();
- };
-
- QualityGateDetailConditionView.prototype.saveCondition = function() {
- this.showSpinner();
- this.model.set({
- period: this.ui.periodSelect.val(),
- op: this.ui.operatorSelect.val(),
- warning: this.ui.warningInput.val(),
- error: this.ui.errorInput.val()
- });
- return this.model.save().always((function(_this) {
- return function() {
- _this.ui.updateButton.prop('disabled', true);
- return _this.hideSpinner();
- };
- })(this)).done((function(_this) {
- return function() {
- return _this.options.collectionView.updateConditions();
- };
- })(this));
- };
-
- QualityGateDetailConditionView.prototype.deleteCondition = function() {
- if (confirm(t('are_you_sure'))) {
- this.showSpinner();
- return this.model["delete"]().done((function(_this) {
- return function() {
- _this.options.collectionView.updateConditions();
- return _this.close();
- };
- })(this));
- }
- };
-
- QualityGateDetailConditionView.prototype.cancelAddCondition = function() {
- return this.close();
- };
-
- QualityGateDetailConditionView.prototype.enableUpdate = function() {
- return this.ui.updateButton.prop('disabled', false);
- };
-
- QualityGateDetailConditionView.prototype.serializeData = function() {
- var period;
- period = _.findWhere(this.options.app.periods, {
- key: '' + this.model.get('period')
- });
- return _.extend(QualityGateDetailConditionView.__super__.serializeData.apply(this, arguments), {
- canEdit: this.options.app.canEdit,
- periods: this.options.app.periods,
- periodText: period != null ? period.text : void 0
- });
- };
-
- return QualityGateDetailConditionView;
-
- })(Marionette.ItemView);
- });
-
-}).call(this);
diff --git a/sonar-server/src/main/webapp/less/quality-gates.less b/sonar-server/src/main/webapp/less/quality-gates.less
index a0293d5e1b3..4d07ee8f986 100644
--- a/sonar-server/src/main/webapp/less/quality-gates.less
+++ b/sonar-server/src/main/webapp/less/quality-gates.less
@@ -66,7 +66,7 @@
&.empty {
cursor: default;
}
-
+
.line {
padding-top: 2px;
padding-bottom: 2px;
@@ -76,7 +76,7 @@
text-transform: lowercase;
}
}
- }
+ }
}
@@ -108,3 +108,9 @@
.quality-gate-condition-actions {
position: relative;
}
+
+.quality-gate-default-message {
+ padding: 6px 5px;
+ border: 1px solid #ddd;
+ background-color: #efefef;
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java
index 8a59e05b387..61a58305daf 100644
--- a/sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java
@@ -19,18 +19,30 @@
*/
package org.sonar.server.debt;
+import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
import org.sonar.api.server.debt.DebtCharacteristic;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.persistence.MyBatis;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.MockUserSession;
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.when;
+import static org.fest.assertions.Fail.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class DebtModelServiceTest {
@@ -38,51 +50,240 @@ public class DebtModelServiceTest {
@Mock
CharacteristicDao dao;
+ @Mock
+ MyBatis mybatis;
+
+ @Mock
+ SqlSession session;
+
+ CharacteristicDto rootCharacteristicDto = new CharacteristicDto()
+ .setId(1)
+ .setKey("MEMORY_EFFICIENCY")
+ .setName("Memory use")
+ .setOrder(2);
+
+ CharacteristicDto characteristicDto = new CharacteristicDto()
+ .setId(2)
+ .setKey("EFFICIENCY")
+ .setName("Efficiency")
+ .setParentId(1);
+
+ int currentId;
+
DebtModelService service;
@Before
public void setUp() throws Exception {
- service = new DebtModelService(dao);
+ MockUserSession.set().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+
+ currentId = 10;
+ // Associate an id when inserting an object to simulate the db id generator
+ doAnswer(new Answer() {
+ public Object answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ CharacteristicDto dto = (CharacteristicDto) args[0];
+ dto.setId(++currentId);
+ return null;
+ }
+ }).when(dao).insert(any(CharacteristicDto.class), any(SqlSession.class));
+
+ when(mybatis.openSession()).thenReturn(session);
+ service = new DebtModelService(mybatis, dao);
}
@Test
public void find_root_characteristics() {
- CharacteristicDto dto = new CharacteristicDto()
- .setId(1)
- .setKey("MEMORY_EFFICIENCY")
- .setName("Memory use");
- when(dao.selectEnabledRootCharacteristics()).thenReturn(newArrayList(dto));
+ when(dao.selectEnabledRootCharacteristics()).thenReturn(newArrayList(rootCharacteristicDto));
assertThat(service.rootCharacteristics()).hasSize(1);
}
@Test
public void find_characteristics() {
- CharacteristicDto dto = new CharacteristicDto()
- .setId(1)
- .setKey("MEMORY_EFFICIENCY")
- .setName("Memory use");
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(dto));
+ when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(rootCharacteristicDto));
assertThat(service.characteristics()).hasSize(1);
}
@Test
public void find_characteristic_by_id() {
- CharacteristicDto dto = new CharacteristicDto()
- .setId(1)
- .setKey("MEMORY_EFFICIENCY")
- .setName("Memory use")
- .setParentId(2)
- .setOrder(1);
- when(dao.selectById(1)).thenReturn(dto);
+ when(dao.selectById(1)).thenReturn(rootCharacteristicDto);
DebtCharacteristic characteristic = service.characteristicById(1);
assertThat(characteristic.id()).isEqualTo(1);
assertThat(characteristic.key()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(characteristic.name()).isEqualTo("Memory use");
- assertThat(characteristic.parentId()).isEqualTo(2);
- assertThat(characteristic.order()).isEqualTo(1);
+ assertThat(characteristic.order()).isEqualTo(2);
+ assertThat(characteristic.parentId()).isNull();
+
+ assertThat(service.characteristicById(111)).isNull();
+ }
+
+ @Test
+ public void create_sub_characteristic() {
+ when(dao.selectById(1, session)).thenReturn(rootCharacteristicDto);
+
+ DebtCharacteristic result = service.create("Compilation name", 1);
+
+ assertThat(result.id()).isEqualTo(currentId);
+ assertThat(result.key()).isEqualTo("COMPILATION_NAME");
+ assertThat(result.name()).isEqualTo("Compilation name");
+ assertThat(result.parentId()).isEqualTo(1);
+ }
+
+ @Test
+ public void fail_to_create_sub_characteristic_when_parent_id_is_not_a_root_characteristic() {
+ when(dao.selectById(1, session)).thenReturn(characteristicDto);
+
+ try {
+ service.create("Compilation", 1);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("A sub characteristic can not have a sub characteristic as parent.");
+ }
+ }
+
+ @Test
+ public void fail_to_create_sub_characteristic_when_parent_does_not_exists() {
+ when(dao.selectById(1, session)).thenReturn(null);
+
+ try {
+ service.create("Compilation", 1);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("Characteristic with id 1 does not exists.");
+ }
+ }
+
+ @Test
+ public void fail_to_create_sub_characteristic_when_name_already_used() {
+ when(dao.selectByName("Compilation", session)).thenReturn(new CharacteristicDto());
+ when(dao.selectById(1, session)).thenReturn(rootCharacteristicDto);
+
+ try {
+ service.create("Compilation", 1);
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.l10nKey()).isEqualTo("errors.is_already_used");
+ assertThat(e.l10nParams().iterator().next()).isEqualTo("Compilation");
+ }
+ }
+
+ @Test
+ public void fail_to_create_sub_characteristic_when_wrong_permission() {
+ MockUserSession.set().setGlobalPermissions(GlobalPermissions.DASHBOARD_SHARING);
+
+ try {
+ service.create("Compilation", 1);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(ForbiddenException.class);
+ }
+ }
+
+ @Test
+ public void create_characteristic() {
+ when(dao.selectMaxCharacteristicOrder(session)).thenReturn(2);
+
+ DebtCharacteristic result = service.create("Portability", null);
+
+ assertThat(result.id()).isEqualTo(currentId);
+ assertThat(result.key()).isEqualTo("PORTABILITY");
+ assertThat(result.name()).isEqualTo("Portability");
+ assertThat(result.order()).isEqualTo(3);
+ }
+
+ @Test
+ public void create_first_characteristic() {
+ when(dao.selectMaxCharacteristicOrder(session)).thenReturn(0);
+
+ DebtCharacteristic result = service.create("Portability", null);
+
+ assertThat(result.id()).isEqualTo(currentId);
+ assertThat(result.key()).isEqualTo("PORTABILITY");
+ assertThat(result.name()).isEqualTo("Portability");
+ assertThat(result.order()).isEqualTo(1);
+ }
+
+ @Test
+ public void rename_characteristic() {
+ when(dao.selectById(10, session)).thenReturn(characteristicDto);
+
+ DebtCharacteristic result = service.rename(10, "New Efficiency");
+
+ assertThat(result.key()).isEqualTo("EFFICIENCY");
+ assertThat(result.name()).isEqualTo("New Efficiency");
+ }
+
+ @Test
+ public void not_rename_characteristic_when_renaming_with_same_name() {
+ when(dao.selectById(10, session)).thenReturn(characteristicDto);
+
+ service.rename(10, "Efficiency");
+
+ verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
+ }
+
+ @Test
+ public void fail_to_rename_unknown_characteristic() {
+ when(dao.selectById(10, session)).thenReturn(null);
+
+ try {
+ service.rename(10, "New Efficiency");
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("Characteristic with id 10 does not exists.");
+ }
+ }
+
+ @Test
+ public void move_up() {
+ when(dao.selectById(10, session)).thenReturn(new CharacteristicDto().setId(10).setOrder(2));
+ when(dao.selectPrevious(2, session)).thenReturn(new CharacteristicDto().setId(2).setOrder(1));
+
+ DebtCharacteristic result = service.moveUp(10);
+
+ ArgumentCaptor<CharacteristicDto> argument = ArgumentCaptor.forClass(CharacteristicDto.class);
+ verify(dao, times(2)).update(argument.capture(), eq(session));
+
+ assertThat(result.order()).isEqualTo(1);
+ assertThat(argument.getAllValues().get(0).getOrder()).isEqualTo(2);
+ assertThat(argument.getAllValues().get(1).getOrder()).isEqualTo(1);
+ }
+
+ @Test
+ public void do_nothing_when_move_up_and_already_on_top() {
+ CharacteristicDto dto = new CharacteristicDto().setId(10).setOrder(1);
+ when(dao.selectById(10, session)).thenReturn(dto);
+ when(dao.selectPrevious(1, session)).thenReturn(null);
+
+ service.moveUp(10);
+
+ verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
+ }
+
+ @Test
+ public void move_down() {
+ when(dao.selectById(10, session)).thenReturn(new CharacteristicDto().setId(10).setOrder(2));
+ when(dao.selectNext(2, session)).thenReturn(new CharacteristicDto().setId(2).setOrder(3));
+
+ DebtCharacteristic result = service.moveDown(10);
+
+ ArgumentCaptor<CharacteristicDto> argument = ArgumentCaptor.forClass(CharacteristicDto.class);
+ verify(dao, times(2)).update(argument.capture(), eq(session));
+
+ assertThat(result.order()).isEqualTo(3);
+ assertThat(argument.getAllValues().get(0).getOrder()).isEqualTo(2);
+ assertThat(argument.getAllValues().get(1).getOrder()).isEqualTo(3);
+ }
+
+ @Test
+ public void do_nothing_when_move_down_and_already_on_bottom() {
+ CharacteristicDto dto = new CharacteristicDto().setId(10).setOrder(5);
+ when(dao.selectById(10, session)).thenReturn(dto);
+ when(dao.selectNext(5, session)).thenReturn(null);
+
+ service.moveDown(10);
- assertThat(service.characteristicById(10)).isNull();
+ verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
}
}
diff --git a/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGatesTest.java b/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGatesTest.java
index 55ef7f025a5..31fb5d55716 100644
--- a/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGatesTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGatesTest.java
@@ -257,14 +257,20 @@ public class QualityGatesTest {
verify(dao).delete(toDelete, session);
}
- @Test(expected = BadRequestException.class)
- public void should_not_delete_qgate_if_default() throws Exception {
+ @Test
+ public void should_delete_qgate_even_if_default() throws Exception {
long idToDelete = 42L;
String name = "To Delete";
QualityGateDto toDelete = new QualityGateDto().setId(idToDelete).setName(name);
when(dao.selectById(idToDelete)).thenReturn(toDelete);
- when(propertiesDao.selectGlobalProperty("sonar.qualitygate")).thenReturn(new PropertyDto().setValue(Long.toString(idToDelete)));
+ when(propertiesDao.selectGlobalProperty("sonar.qualitygate")).thenReturn(new PropertyDto().setValue("42"));
+ SqlSession session = mock(SqlSession.class);
+ when(myBatis.openSession()).thenReturn(session);
qGates.delete(idToDelete);
+ verify(dao).selectById(idToDelete);
+ verify(propertiesDao).deleteGlobalProperty("sonar.qualitygate", session);
+ verify(propertiesDao).deleteProjectProperties("sonar.qualitygate", "42", session);
+ verify(dao).delete(toDelete, session);
}
@Test