diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2014-03-19 13:38:43 +0600 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2014-03-19 13:38:43 +0600 |
commit | 3c3c1e3e2c091285017986c11f075f5a6d87a643 (patch) | |
tree | dda80d8d899e4679082616251dc22eaff7584dd1 | |
parent | 660591f1137d2dcbd13e9e29a1965bf3dd1febb1 (diff) | |
parent | 398dfaff33c9a2c90c2bbbfa0f7f41a867896f8d (diff) | |
download | sonarqube-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
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>#{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<#{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}}">Δ {{text}}</option>{{/each}} </select> {{else}} - {{periodText}} + {{#if periodText}}Δ {{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 |