From 875e23e29f4ed2d41146d8bba8b22448b23df113 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Thu, 29 Jun 2017 15:08:28 +0200 Subject: SONAR-9478 Deprecate Settings and introduce new Configuration interface --- .../src/main/java/org/sonar/api/Properties.java | 3 +- .../api/batch/bootstrap/ProjectDefinition.java | 5 +- .../api/batch/debt/DebtRemediationFunction.java | 1 + .../sonar/api/batch/postjob/PostJobContext.java | 9 +- .../org/sonar/api/batch/sensor/SensorContext.java | 10 +- .../sensor/cpd/internal/DefaultCpdTokens.java | 10 +- .../batch/sensor/internal/SensorContextTester.java | 11 +- .../java/org/sonar/api/config/Configuration.java | 183 +++++++++ .../java/org/sonar/api/config/EmailSettings.java | 4 - .../java/org/sonar/api/config/MapSettings.java | 75 ---- .../org/sonar/api/config/PropertyDefinition.java | 2 +- .../org/sonar/api/config/PropertyDefinitions.java | 1 + .../main/java/org/sonar/api/config/Settings.java | 60 +-- .../api/config/internal/ConfigurationBridge.java | 52 +++ .../org/sonar/api/config/internal/MapSettings.java | 89 +++++ .../sonar/api/config/internal/package-info.java | 23 ++ .../sonar/api/scan/filesystem/FileExclusions.java | 6 +- .../api/batch/bootstrap/ProjectBuilderTest.java | 2 +- .../sensor/cpd/internal/DefaultCpdTokensTest.java | 16 +- .../sensor/internal/SensorContextTesterTest.java | 2 +- .../org/sonar/api/config/EmailSettingsTest.java | 1 + .../java/org/sonar/api/config/MapSettingsTest.java | 427 -------------------- .../sonar/api/config/internal/MapSettingsTest.java | 430 +++++++++++++++++++++ .../api/scan/filesystem/FileExclusionsTest.java | 17 +- 24 files changed, 843 insertions(+), 596 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/config/Configuration.java delete mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/config/MapSettings.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/config/internal/ConfigurationBridge.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/config/internal/MapSettings.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/config/internal/package-info.java delete mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/config/MapSettingsTest.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/config/internal/MapSettingsTest.java (limited to 'sonar-plugin-api') diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/Properties.java b/sonar-plugin-api/src/main/java/org/sonar/api/Properties.java index 9efca22299a..c38f1ecc257 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/Properties.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/Properties.java @@ -21,6 +21,7 @@ package org.sonar.api; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import org.sonar.api.config.Configuration; /** * Plugin properties. This annotation is only used on classes implementing org.sonar.api.Plugin. @@ -28,7 +29,7 @@ import java.lang.annotation.RetentionPolicy; * Note that {@link org.sonar.api.config.PropertyDefinition} is an alternative, programmatic and recommended approach * to declare properties. *
- * Effective property values are accessible at runtime through the component {@link org.sonar.api.config.Settings} + * Effective property values are accessible at runtime through the component {@link Configuration} * * @since 1.10 */ diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java index 74cad4f2ae6..9bf8eef05b4 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java @@ -186,7 +186,7 @@ public class ProjectDefinition { } return null; } - + @CheckForNull public String getOriginalVersion() { return properties.get(CoreProperties.PROJECT_VERSION_PROPERTY); @@ -199,7 +199,7 @@ public class ProjectDefinition { } return version; } - + @CheckForNull public String getOriginalName() { return properties.get(CoreProperties.PROJECT_NAME_PROPERTY); @@ -320,6 +320,7 @@ public class ProjectDefinition { return this; } + @CheckForNull public ProjectDefinition getParent() { return parent; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java index 44917519e72..96ff255364c 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java @@ -25,6 +25,7 @@ import org.sonar.api.utils.Duration; /** * @since 4.3 + * @deprecated since 6.5 debt model will soon be unavailable on batch side */ public class DebtRemediationFunction { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/PostJobContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/PostJobContext.java index 2baa34478d2..cfe881f2866 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/PostJobContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/PostJobContext.java @@ -21,6 +21,7 @@ package org.sonar.api.batch.postjob; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.postjob.issue.PostJobIssue; +import org.sonar.api.config.Configuration; import org.sonar.api.config.Settings; /** @@ -30,10 +31,16 @@ import org.sonar.api.config.Settings; public interface PostJobContext { /** - * Get settings of the current project. + * @deprecated since 6.5 use {@link PostJobContext#config()} */ Settings settings(); + /** + * Get configuration of the current project. + * @since 6.5 + */ + Configuration config(); + AnalysisMode analysisMode(); // ----------- Only available in preview mode -------------- diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java index f47c408ccf0..8da60fb974e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java @@ -24,6 +24,7 @@ import org.sonar.api.SonarRuntime; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.postjob.PostJobContext; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.sensor.coverage.NewCoverage; import org.sonar.api.batch.sensor.cpd.NewCpdTokens; @@ -36,6 +37,7 @@ import org.sonar.api.batch.sensor.measure.Measure; import org.sonar.api.batch.sensor.measure.NewMeasure; import org.sonar.api.batch.sensor.symbol.NewSymbolTable; import org.sonar.api.config.Settings; +import org.sonar.api.config.Configuration; import org.sonar.api.utils.Version; /** @@ -46,10 +48,16 @@ import org.sonar.api.utils.Version; public interface SensorContext { /** - * Get settings of the current module. + * @deprecated since 6.5 use {@link PostJobContext#config()} */ Settings settings(); + /** + * Get settings of the current module, or of the project for a global Sensor. + * @since 6.5 + */ + Configuration config(); + /** * Get filesystem of the current module. */ diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java index 3ff672988da..abdb03c9ecc 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java @@ -28,7 +28,7 @@ import org.sonar.api.batch.fs.internal.PathPattern; import org.sonar.api.batch.sensor.cpd.NewCpdTokens; import org.sonar.api.batch.sensor.internal.DefaultStorable; import org.sonar.api.batch.sensor.internal.SensorStorage; -import org.sonar.api.config.Settings; +import org.sonar.api.config.Configuration; import org.sonar.duplications.internal.pmd.TokensLine; import static com.google.common.base.Preconditions.checkState; @@ -37,7 +37,7 @@ import static java.util.Objects.requireNonNull; public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens { - private final Settings settings; + private final Configuration config; private final ArrayList result = new ArrayList<>(); private InputFile inputFile; private int startLine = Integer.MIN_VALUE; @@ -47,15 +47,15 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens { private TextRange lastRange; private boolean excluded; - public DefaultCpdTokens(Settings settings, SensorStorage storage) { + public DefaultCpdTokens(Configuration config, SensorStorage storage) { super(storage); - this.settings = settings; + this.config = config; } @Override public DefaultCpdTokens onFile(InputFile inputFile) { this.inputFile = requireNonNull(inputFile, "file can't be null"); - String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS); + String[] cpdExclusions = config.getStringArray(CoreProperties.CPD_EXCLUSIONS); for (PathPattern cpdExclusion : PathPattern.create(cpdExclusions)) { if (cpdExclusion.match(inputFile)) { this.excluded = true; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java index 4a3d60763ec..711821e7909 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java @@ -64,8 +64,10 @@ import org.sonar.api.batch.sensor.measure.NewMeasure; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.batch.sensor.symbol.NewSymbolTable; import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; -import org.sonar.api.config.MapSettings; +import org.sonar.api.config.Configuration; import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.ConfigurationBridge; +import org.sonar.api.config.internal.MapSettings; import org.sonar.api.internal.ApiVersion; import org.sonar.api.internal.SonarRuntimeImpl; import org.sonar.api.measures.Metric; @@ -122,6 +124,11 @@ public class SensorContextTester implements SensorContext { return settings; } + @Override + public Configuration config() { + return new ConfigurationBridge(settings); + } + public SensorContextTester setSettings(Settings settings) { this.settings = settings; return this; @@ -269,7 +276,7 @@ public class SensorContextTester implements SensorContext { @Override public NewCpdTokens newCpdTokens() { - return new DefaultCpdTokens(settings, sensorStorage); + return new DefaultCpdTokens(config(), sensorStorage); } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/Configuration.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/Configuration.java new file mode 100644 index 00000000000..f91996a0734 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/Configuration.java @@ -0,0 +1,183 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.config; + +import java.util.Optional; +import org.sonar.api.batch.ScannerSide; +import org.sonar.api.ce.ComputeEngineSide; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.server.ServerSide; +import org.sonarsource.api.sonarlint.SonarLintSide; + +/** + * Component to get effective configuration. Values of properties depend on the runtime environment: + * + * + *

Usage

+ *
+ * public class MyExtension {
+ *
+ *   private final Configuration config;
+ *
+ *   public MyExtension(Configuration config) {
+ *     this.config = config;
+ *   }
+ *   public void doSomething() {
+ *     String fooValue = config.get("sonar.foo").orElse(null);
+ *     // ..
+ *   }
+ * }
+ * 
+ * + *

Scanner example

+ * Scanner sensor can get the reference on Configuration directly through SensorContext, + * without injecting the component into constructor. + * + *
+ * public class MySensor implements Sensor {
+ *   {@literal @}Override
+ *   public void execute(SensorContext context) {
+ *     String fooValue = context.config().get("sonar.foo").orElse(null);
+ *     // ..
+ *   }
+ * }
+ * 
+ * + *

+ * For testing, and only for testing, the in-memory implementation {@link MapSettings} can be used. + *

+ * {@literal @}Test
+ * public void my_test() {
+ *   MapSettings settings = new MapSettings();
+ *   settings.setProperty("foo", "bar");
+ *   MyExtension underTest = new MyExtension(settings.asConfig());
+ *   // ...
+ * }
+ * 
+ * + * @see MapSettings + * @see PropertyDefinition + * @since 6.5 + */ +@ScannerSide +@ServerSide +@ComputeEngineSide +@SonarLintSide +public interface Configuration { + + /** + * The effective value of the specified property. Can return {@code Optional#empty()} if the property is not set and has no defined default value. + *

+ * If the property is encrypted with a secret key, then the returned value is decrypted. + *

+ * + * @throws IllegalStateException if value is encrypted but fails to be decrypted. + */ + Optional get(String key); + + /** + * @return {@code true} if the property has a non-default value, else {@code false}. + */ + boolean hasKey(String key); + + /** + * Used to read multi-valued properties. + *

+ * See {@link PropertyDefinition.Builder#multiValues(boolean)} + * Multi-valued properties coming from scanner are parsed as CSV lines (ie comma separator and optional double quotes to escape values). + * Non quoted values are trimmed. + *
+ * Examples : + *

    + *
  • "one,two,three " -> ["one", "two", "three"]
  • + *
  • " one, two, three " -> ["one", "two", "three"]
  • + *
  • "one, , three" -> ["one", "", "three"]
  • + *
  • "one,\"two,three\",\" four \"" -> ["one", "two,three", " four "]
  • + *
+ */ + String[] getStringArray(String key); + + /** + * Effective value as boolean. It is {@code empty} if {@link #get(String)} is empty or if it + * does not return {@code "true"}, even if it's not a boolean representation. + * @return {@code true} if the effective value is {@code "true"}, {@code false} for any other non empty value. + * If the property does not have value nor default value, then {@code empty} is returned. + */ + default Optional getBoolean(String key) { + return get(key).map(Boolean::parseBoolean); + } + + /** + * Effective value as {@code int}. + * @return the value as {@code int}. If the property does not have value nor default value, then {@code empty} is returned. + * @throws NumberFormatException if value is not empty and is not a parsable integer + */ + default Optional getInt(String key) { + try { + return get(key).map(Integer::parseInt); + } catch (NumberFormatException e) { + throw new IllegalStateException(String.format("The property '%s' is not an int value: %s", key, e.getMessage())); + } + } + + /** + * Effective value as {@code long}. + * @return the value as {@code long}. If the property does not have value nor default value, then {@code empty} is returned. + * @throws NumberFormatException if value is not empty and is not a parsable {@code long} + */ + default Optional getLong(String key) { + try { + return get(key).map(Long::parseLong); + } catch (NumberFormatException e) { + throw new IllegalStateException(String.format("The property '%s' is not an long value: %s", key, e.getMessage())); + } + } + + /** + * Effective value as {@code Float}. + * @return the value as {@code Float}. If the property does not have value nor default value, then {@code empty} is returned. + * @throws NumberFormatException if value is not empty and is not a parsable number + */ + default Optional getFloat(String key) { + try { + return get(key).map(Float::valueOf); + } catch (NumberFormatException e) { + throw new IllegalStateException(String.format("The property '%s' is not an float value: %s", key, e.getMessage())); + } + } + + /** + * Effective value as {@code Double}. + * @return the value as {@code Double}. If the property does not have value nor default value, then {@code empty} is returned. + * @throws NumberFormatException if value is not empty and is not a parsable number + */ + default Optional getDouble(String key) { + try { + return get(key).map(Double::valueOf); + } catch (NumberFormatException e) { + throw new IllegalStateException(String.format("The property '%s' is not an double value: %s", key, e.getMessage())); + } + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/EmailSettings.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/EmailSettings.java index dc449884522..dae2594593a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/EmailSettings.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/EmailSettings.java @@ -21,7 +21,6 @@ package org.sonar.api.config; import java.util.List; import org.sonar.api.PropertyType; -import org.sonar.api.batch.ScannerSide; import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.server.ServerSide; @@ -34,11 +33,8 @@ import static org.sonar.api.PropertyType.INTEGER; import static org.sonar.api.PropertyType.SINGLE_SELECT_LIST; /** - * If batch extensions use this component, then batch must be executed with administrator rights (see properties sonar.login and sonar.password) - * * @since 3.2 */ -@ScannerSide @ServerSide @ComputeEngineSide public class EmailSettings { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/MapSettings.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/MapSettings.java deleted file mode 100644 index 5a73567a808..00000000000 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/MapSettings.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.api.config; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import static java.util.Collections.unmodifiableMap; - -/** - * In-memory map-based implementation of {@link Settings}. It must be used - * only for unit tests. This is not the implementation - * deployed at runtime, so non-test code must never cast - * {@link Settings} to {@link MapSettings}. - * - * @since 6.1 - */ -public class MapSettings extends Settings { - - private final Map props = new HashMap<>(); - - public MapSettings() { - super(new PropertyDefinitions(), new Encryption(null)); - } - - public MapSettings(PropertyDefinitions definitions) { - super(definitions, new Encryption(null)); - } - - @Override - protected Optional get(String key) { - return Optional.ofNullable(props.get(key)); - } - - @Override - protected void set(String key, String value) { - props.put(key, value); - } - - @Override - protected void remove(String key) { - props.remove(key); - } - - @Override - public Map getProperties() { - return unmodifiableMap(props); - } - - /** - * Delete all properties - */ - public MapSettings clear() { - props.clear(); - return this; - } -} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java index 02d64853e8e..13c407d000b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java @@ -57,7 +57,7 @@ import static org.sonar.api.PropertyType.REGULAR_EXPRESSION; import static org.sonar.api.PropertyType.SINGLE_SELECT_LIST; /** - * Declare a plugin property. Values are available at runtime through the component {@link Settings}. + * Declare a plugin property. Values are available at runtime through the components {@link Settings} or {@link org.sonar.api.Configuration.SettingsReader}. *
* It's the programmatic alternative to the annotation {@link org.sonar.api.Property}. It is more * testable and adds new features like sub-categories and ordering. 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 9a0834b62be..b0e5275674f 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 @@ -165,6 +165,7 @@ public final class PropertyDefinitions { return byCategory; } + @CheckForNull public String getDefaultValue(String key) { PropertyDefinition def = get(key); if (def == null) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java index 7e182dcd555..20fb15bff45 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java @@ -35,69 +35,19 @@ import org.sonar.api.batch.ScannerSide; import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.DateUtils; +import org.sonarsource.api.sonarlint.SonarLintSide; import static java.util.Objects.requireNonNull; import static org.apache.commons.lang.StringUtils.trim; /** - * Component to get effective settings. Values of properties depend on the runtime environment: - *
    - *
  • project settings in scanner.
  • - *
  • global settings in web server. It does not allow to get the settings overridden on projects.
  • - *
  • project settings in Compute Engine.
  • - *
- * - *

Usage

- *
- * public class MyExtension {
- *
- *   private final Settings settings;
- *
- *   public MyExtension(Settings settings) {
- *     this.settings = settings;
- *   }
- *   public void doSomething() {
- *     String fooValue = settings.getString("sonar.foo");
- *     // ..
- *   }
- * }
- * 
- * - *

Scanner example

- * Scanner sensor can get the reference on Settings directly through SensorContext, - * without injecting the component into constructor. - * - *
- * public class MySensor implements Sensor {
- *   {@literal @}Override
- *   public void execute(SensorContext context) {
- *     String fooValue = context.settings().getString("sonar.foo");
- *     // ..
- *   }
- * }
- * 
- * - *

- * For testing, and only for testing, the in-memory implementation {@link MapSettings} can be used. - *

- * {@literal @}Test
- * public void my_test() {
- *   Settings settings = new MapSettings();
- *   settings.setProperty("foo", "bar");
- *   MyExtension underTest = new MyExtension(settings);
- *   // ...
- * }
- * 
- * - * History - this class is abstract since 6.1. - * - * @see MapSettings - * @see PropertyDefinition - * @since 2.12 + * @deprecated since 6.5 use {@link Configuration} */ -@ScannerSide @ServerSide @ComputeEngineSide +@ScannerSide +@SonarLintSide +@Deprecated public abstract class Settings { private final PropertyDefinitions definitions; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/internal/ConfigurationBridge.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/internal/ConfigurationBridge.java new file mode 100644 index 00000000000..4e8ef584fd3 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/internal/ConfigurationBridge.java @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.config.internal; + +import java.util.Optional; +import org.sonar.api.config.Settings; +import org.sonar.api.config.Configuration; + +/** + * Used to help migration from {@link Settings} to {@link Configuration} + */ +public class ConfigurationBridge implements Configuration { + + private final Settings settings; + + public ConfigurationBridge(Settings settings) { + this.settings = settings; + } + + @Override + public Optional get(String key) { + return Optional.ofNullable(settings.getString(key)); + } + + @Override + public boolean hasKey(String key) { + return settings.hasKey(key); + } + + @Override + public String[] getStringArray(String key) { + return settings.getStringArray(key); + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/internal/MapSettings.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/internal/MapSettings.java new file mode 100644 index 00000000000..6d45e12ddb6 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/internal/MapSettings.java @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.config.internal; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.sonar.api.config.Configuration; +import org.sonar.api.config.Encryption; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; + +import static java.util.Collections.unmodifiableMap; + +/** + * In-memory map-based implementation of {@link Settings}. It must be used + * only for unit tests. This is not the implementation + * deployed at runtime, so non-test code must never cast + * {@link Settings} to {@link MapSettings}. + * + * @since 6.1 + */ +public class MapSettings extends Settings { + + private final Map props = new HashMap<>(); + private final ConfigurationBridge configurationBridge; + + public MapSettings() { + this(new PropertyDefinitions()); + } + + public MapSettings(PropertyDefinitions definitions) { + super(definitions, new Encryption(null)); + configurationBridge = new ConfigurationBridge(this); + } + + @Override + protected Optional get(String key) { + return Optional.ofNullable(props.get(key)); + } + + @Override + protected void set(String key, String value) { + props.put(key, value); + } + + @Override + protected void remove(String key) { + props.remove(key); + } + + @Override + public Map getProperties() { + return unmodifiableMap(props); + } + + /** + * Delete all properties + */ + public MapSettings clear() { + props.clear(); + return this; + } + + /** + * @return a {@link Configuration} proxy on top of this existing {@link Settings} implementation. Changes are reflected in the {@link Configuration} object. + * @since 6.5 + */ + public Configuration asConfig() { + return configurationBridge; + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/internal/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/internal/package-info.java new file mode 100644 index 00000000000..4fb297e0a30 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/internal/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.api.config.internal; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileExclusions.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileExclusions.java index 1a071e62758..af7e9550b96 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileExclusions.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileExclusions.java @@ -24,7 +24,7 @@ import java.util.stream.Stream; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.config.Settings; +import org.sonar.api.config.Configuration; /** * Configuration of file inclusions and exclusions. @@ -35,9 +35,9 @@ import org.sonar.api.config.Settings; */ @ScannerSide public class FileExclusions { - private final Settings settings; + private final Configuration settings; - public FileExclusions(Settings settings) { + public FileExclusions(Configuration settings) { this.settings = settings; } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectBuilderTest.java index b8b08a4e4dc..72fd4a8539c 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectBuilderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectBuilderTest.java @@ -23,7 +23,7 @@ import java.io.File; import org.junit.Test; import org.sonar.api.batch.bootstrap.internal.ProjectBuilderContext; import org.sonar.api.config.Settings; -import org.sonar.api.config.MapSettings; +import org.sonar.api.config.internal.MapSettings; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.core.Is.is; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokensTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokensTest.java index 9b20b94d700..62a268f00f7 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokensTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokensTest.java @@ -24,7 +24,7 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.config.Settings; -import org.sonar.api.config.MapSettings; +import org.sonar.api.config.internal.MapSettings; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -44,7 +44,7 @@ public class DefaultCpdTokensTest { @Test public void save_no_tokens() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings(), sensorStorage) + DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage) .onFile(INPUT_FILE); tokens.save(); @@ -57,7 +57,7 @@ public class DefaultCpdTokensTest { @Test public void save_one_token() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings(), sensorStorage) + DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage) .onFile(INPUT_FILE) .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo"); @@ -71,9 +71,9 @@ public class DefaultCpdTokensTest { @Test public void handle_exclusions_by_pattern() { SensorStorage sensorStorage = mock(SensorStorage.class); - Settings settings = new MapSettings(); + MapSettings settings = new MapSettings(); settings.setProperty("sonar.cpd.exclusions", "src/Foo.java,another"); - DefaultCpdTokens tokens = new DefaultCpdTokens(settings, sensorStorage) + DefaultCpdTokens tokens = new DefaultCpdTokens(settings.asConfig(), sensorStorage) .onFile(INPUT_FILE) .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo"); @@ -87,7 +87,7 @@ public class DefaultCpdTokensTest { @Test public void save_many_tokens() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings(), sensorStorage) + DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage) .onFile(INPUT_FILE) .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo") .addToken(INPUT_FILE.newRange(1, 6, 1, 10), "bar") @@ -108,7 +108,7 @@ public class DefaultCpdTokensTest { @Test public void basic_validation() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings(), sensorStorage); + DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage); try { tokens.save(); fail("Expected exception"); @@ -138,7 +138,7 @@ public class DefaultCpdTokensTest { @Test public void validate_tokens_order() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings(), sensorStorage) + DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage) .onFile(INPUT_FILE) .addToken(INPUT_FILE.newRange(1, 6, 1, 10), "bar"); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java index 4893f6fdd63..566facb4a1a 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java @@ -38,8 +38,8 @@ import org.sonar.api.batch.sensor.error.NewAnalysisError; import org.sonar.api.batch.sensor.highlighting.TypeOfText; import org.sonar.api.batch.sensor.issue.NewIssue; import org.sonar.api.batch.sensor.symbol.NewSymbolTable; -import org.sonar.api.config.MapSettings; import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.SonarException; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/EmailSettingsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/EmailSettingsTest.java index b5504b5d2b3..507ab123b08 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/config/EmailSettingsTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/EmailSettingsTest.java @@ -21,6 +21,7 @@ package org.sonar.api.config; import org.junit.Test; import org.sonar.api.CoreProperties; +import org.sonar.api.config.internal.MapSettings; import static org.assertj.core.api.Assertions.assertThat; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/MapSettingsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/MapSettingsTest.java deleted file mode 100644 index 284d35ea507..00000000000 --- a/sonar-plugin-api/src/test/java/org/sonar/api/config/MapSettingsTest.java +++ /dev/null @@ -1,427 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.api.config; - -import java.util.Date; -import org.assertj.core.data.Offset; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.sonar.api.Properties; -import org.sonar.api.Property; -import org.sonar.api.PropertyType; -import org.sonar.api.utils.DateUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -public class MapSettingsTest { - - private PropertyDefinitions definitions; - - @Properties({ - @Property(key = "hello", name = "Hello", defaultValue = "world"), - @Property(key = "date", name = "Date", defaultValue = "2010-05-18"), - @Property(key = "datetime", name = "DateTime", defaultValue = "2010-05-18T15:50:45+0100"), - @Property(key = "boolean", name = "Boolean", defaultValue = "true"), - @Property(key = "falseboolean", name = "False Boolean", defaultValue = "false"), - @Property(key = "integer", name = "Integer", defaultValue = "12345"), - @Property(key = "array", name = "Array", defaultValue = "one,two,three"), - @Property(key = "multi_values", name = "Array", defaultValue = "1,2,3", multiValues = true), - @Property(key = "sonar.jira", name = "Jira Server", type = PropertyType.PROPERTY_SET, propertySetKey = "jira"), - @Property(key = "newKey", name = "New key", deprecatedKey = "oldKey"), - @Property(key = "newKeyWithDefaultValue", name = "New key with default value", deprecatedKey = "oldKeyWithDefaultValue", defaultValue = "default_value"), - @Property(key = "new_multi_values", name = "New multi values", defaultValue = "1,2,3", multiValues = true, deprecatedKey = "old_multi_values") - }) - private static class Init { - } - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Before - public void init_definitions() { - definitions = new PropertyDefinitions(); - definitions.addComponent(Init.class); - } - - @Test - public void default_values_should_be_loaded_from_definitions() { - Settings settings = new MapSettings(definitions); - assertThat(settings.getDefaultValue("hello")).isEqualTo("world"); - } - - @Test - public void set_property_int() { - Settings settings = new MapSettings(); - settings.setProperty("foo", 123); - assertThat(settings.getInt("foo")).isEqualTo(123); - assertThat(settings.getString("foo")).isEqualTo("123"); - assertThat(settings.getBoolean("foo")).isFalse(); - } - - @Test - public void default_number_values_are_zero() { - Settings settings = new MapSettings(); - assertThat(settings.getInt("foo")).isEqualTo(0); - assertThat(settings.getLong("foo")).isEqualTo(0L); - } - - @Test - public void getInt_value_must_be_valid() { - thrown.expect(NumberFormatException.class); - - Settings settings = new MapSettings(); - settings.setProperty("foo", "not a number"); - settings.getInt("foo"); - } - - @Test - public void all_values_should_be_trimmed_set_property() { - Settings settings = new MapSettings(); - settings.setProperty("foo", " FOO "); - assertThat(settings.getString("foo")).isEqualTo("FOO"); - } - - @Test - public void test_get_default_value() { - Settings settings = new MapSettings(definitions); - assertThat(settings.getDefaultValue("unknown")).isNull(); - } - - @Test - public void test_get_string() { - Settings settings = new MapSettings(definitions); - settings.setProperty("hello", "Russia"); - assertThat(settings.getString("hello")).isEqualTo("Russia"); - } - - @Test - public void setProperty_date() { - Settings settings = new MapSettings(); - Date date = DateUtils.parseDateTime("2010-05-18T15:50:45+0100"); - settings.setProperty("aDate", date); - settings.setProperty("aDateTime", date, true); - - assertThat(settings.getString("aDate")).isEqualTo("2010-05-18"); - assertThat(settings.getString("aDateTime")).startsWith("2010-05-18T"); - } - - @Test - public void test_get_date() { - Settings settings = new MapSettings(definitions); - assertThat(settings.getDate("unknown")).isNull(); - assertThat(settings.getDate("date").getDate()).isEqualTo(18); - assertThat(settings.getDate("date").getMonth()).isEqualTo(4); - } - - @Test - public void test_get_date_not_found() { - Settings settings = new MapSettings(definitions); - assertThat(settings.getDate("unknown")).isNull(); - } - - @Test - public void test_get_datetime() { - Settings settings = new MapSettings(definitions); - assertThat(settings.getDateTime("unknown")).isNull(); - assertThat(settings.getDateTime("datetime").getDate()).isEqualTo(18); - assertThat(settings.getDateTime("datetime").getMonth()).isEqualTo(4); - assertThat(settings.getDateTime("datetime").getMinutes()).isEqualTo(50); - } - - @Test - public void test_get_double() { - Settings settings = new MapSettings(); - settings.setProperty("from_double", 3.14159); - settings.setProperty("from_string", "3.14159"); - assertThat(settings.getDouble("from_double")).isEqualTo(3.14159, Offset.offset(0.00001)); - assertThat(settings.getDouble("from_string")).isEqualTo(3.14159, Offset.offset(0.00001)); - assertThat(settings.getDouble("unknown")).isNull(); - } - - @Test - public void test_get_float() { - Settings settings = new MapSettings(); - settings.setProperty("from_float", 3.14159f); - settings.setProperty("from_string", "3.14159"); - assertThat(settings.getDouble("from_float")).isEqualTo(3.14159f, Offset.offset(0.00001)); - assertThat(settings.getDouble("from_string")).isEqualTo(3.14159f, Offset.offset(0.00001)); - assertThat(settings.getDouble("unknown")).isNull(); - } - - @Test - public void test_get_bad_float() { - Settings settings = new MapSettings(); - settings.setProperty("foo", "bar"); - - thrown.expect(IllegalStateException.class); - thrown.expectMessage("The property 'foo' is not a float value"); - settings.getFloat("foo"); - } - - @Test - public void test_get_bad_double() { - Settings settings = new MapSettings(); - settings.setProperty("foo", "bar"); - - thrown.expect(IllegalStateException.class); - thrown.expectMessage("The property 'foo' is not a double value"); - settings.getDouble("foo"); - } - - @Test - public void testSetNullFloat() { - Settings settings = new MapSettings(); - settings.setProperty("foo", (Float) null); - assertThat(settings.getFloat("foo")).isNull(); - } - - @Test - public void testSetNullDouble() { - Settings settings = new MapSettings(); - settings.setProperty("foo", (Double) null); - assertThat(settings.getDouble("foo")).isNull(); - } - - @Test - public void getStringArray() { - Settings settings = new MapSettings(definitions); - String[] array = settings.getStringArray("array"); - assertThat(array).isEqualTo(new String[] {"one", "two", "three"}); - } - - @Test - public void setStringArray() { - Settings settings = new MapSettings(definitions); - settings.setProperty("multi_values", new String[] {"A", "B"}); - String[] array = settings.getStringArray("multi_values"); - assertThat(array).isEqualTo(new String[] {"A", "B"}); - } - - @Test - public void setStringArrayTrimValues() { - Settings settings = new MapSettings(definitions); - settings.setProperty("multi_values", new String[] {" A ", " B "}); - String[] array = settings.getStringArray("multi_values"); - assertThat(array).isEqualTo(new String[] {"A", "B"}); - } - - @Test - public void setStringArrayEscapeCommas() { - Settings settings = new MapSettings(definitions); - settings.setProperty("multi_values", new String[] {"A,B", "C,D"}); - String[] array = settings.getStringArray("multi_values"); - assertThat(array).isEqualTo(new String[] {"A,B", "C,D"}); - } - - @Test - public void setStringArrayWithEmptyValues() { - Settings settings = new MapSettings(definitions); - settings.setProperty("multi_values", new String[] {"A,B", "", "C,D"}); - String[] array = settings.getStringArray("multi_values"); - assertThat(array).isEqualTo(new String[] {"A,B", "", "C,D"}); - } - - @Test - public void setStringArrayWithNullValues() { - Settings settings = new MapSettings(definitions); - settings.setProperty("multi_values", new String[] {"A,B", null, "C,D"}); - String[] array = settings.getStringArray("multi_values"); - assertThat(array).isEqualTo(new String[] {"A,B", "", "C,D"}); - } - - @Test(expected = IllegalStateException.class) - public void shouldFailToSetArrayValueOnSingleValueProperty() { - Settings settings = new MapSettings(definitions); - settings.setProperty("array", new String[] {"A", "B", "C"}); - } - - @Test - public void getStringArray_no_value() { - Settings settings = new MapSettings(); - String[] array = settings.getStringArray("array"); - assertThat(array).isEmpty(); - } - - @Test - public void shouldTrimArray() { - Settings settings = new MapSettings(); - settings.setProperty("foo", " one, two, three "); - String[] array = settings.getStringArray("foo"); - assertThat(array).isEqualTo(new String[] {"one", "two", "three"}); - } - - @Test - public void shouldKeepEmptyValuesWhenSplitting() { - Settings settings = new MapSettings(); - settings.setProperty("foo", " one, , two"); - String[] array = settings.getStringArray("foo"); - assertThat(array).isEqualTo(new String[] {"one", "", "two"}); - } - - @Test - public void testDefaultValueOfGetString() { - Settings settings = new MapSettings(definitions); - assertThat(settings.getString("hello")).isEqualTo("world"); - } - - @Test - public void set_property_boolean() { - Settings settings = new MapSettings(); - settings.setProperty("foo", true); - settings.setProperty("bar", false); - assertThat(settings.getBoolean("foo")).isTrue(); - assertThat(settings.getBoolean("bar")).isFalse(); - assertThat(settings.getString("foo")).isEqualTo("true"); - assertThat(settings.getString("bar")).isEqualTo("false"); - } - - @Test - public void ignore_case_of_boolean_values() { - Settings settings = new MapSettings(); - settings.setProperty("foo", "true"); - settings.setProperty("bar", "TRUE"); - // labels in UI - settings.setProperty("baz", "True"); - - assertThat(settings.getBoolean("foo")).isTrue(); - assertThat(settings.getBoolean("bar")).isTrue(); - assertThat(settings.getBoolean("baz")).isTrue(); - } - - @Test - public void get_boolean() { - Settings settings = new MapSettings(definitions); - assertThat(settings.getBoolean("boolean")).isTrue(); - assertThat(settings.getBoolean("falseboolean")).isFalse(); - assertThat(settings.getBoolean("unknown")).isFalse(); - assertThat(settings.getBoolean("hello")).isFalse(); - } - - @Test - public void shouldCreateByIntrospectingComponent() { - Settings settings = new MapSettings(); - settings.getDefinitions().addComponent(MyComponent.class); - - // property definition has been loaded, ie for default value - assertThat(settings.getDefaultValue("foo")).isEqualTo("bar"); - } - - @Property(key = "foo", name = "Foo", defaultValue = "bar") - public static class MyComponent { - - } - - @Test - public void getStringLines_no_value() { - assertThat(new MapSettings().getStringLines("foo")).hasSize(0); - } - - @Test - public void getStringLines_single_line() { - Settings settings = new MapSettings(); - settings.setProperty("foo", "the line"); - assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"the line"}); - } - - @Test - public void getStringLines_linux() { - Settings settings = new MapSettings(); - settings.setProperty("foo", "one\ntwo"); - assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two"}); - - settings.setProperty("foo", "one\ntwo\n"); - assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two"}); - } - - @Test - public void getStringLines_windows() { - Settings settings = new MapSettings(); - settings.setProperty("foo", "one\r\ntwo"); - assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two"}); - - settings.setProperty("foo", "one\r\ntwo\r\n"); - assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two"}); - } - - @Test - public void getStringLines_mix() { - Settings settings = new MapSettings(); - settings.setProperty("foo", "one\r\ntwo\nthree"); - assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two", "three"}); - } - - @Test - public void getKeysStartingWith() { - Settings settings = new MapSettings(); - settings.setProperty("sonar.jdbc.url", "foo"); - settings.setProperty("sonar.jdbc.username", "bar"); - settings.setProperty("sonar.security", "admin"); - - assertThat(settings.getKeysStartingWith("sonar")).containsOnly("sonar.jdbc.url", "sonar.jdbc.username", "sonar.security"); - assertThat(settings.getKeysStartingWith("sonar.jdbc")).containsOnly("sonar.jdbc.url", "sonar.jdbc.username"); - assertThat(settings.getKeysStartingWith("other")).hasSize(0); - } - - @Test - public void should_fallback_deprecated_key_to_default_value_of_new_key() { - Settings settings = new MapSettings(definitions); - - assertThat(settings.getString("newKeyWithDefaultValue")).isEqualTo("default_value"); - assertThat(settings.getString("oldKeyWithDefaultValue")).isEqualTo("default_value"); - } - - @Test - public void should_fallback_deprecated_key_to_new_key() { - Settings settings = new MapSettings(definitions); - settings.setProperty("newKey", "value of newKey"); - - assertThat(settings.getString("newKey")).isEqualTo("value of newKey"); - assertThat(settings.getString("oldKey")).isEqualTo("value of newKey"); - } - - @Test - public void should_load_value_of_deprecated_key() { - // it's used for example when deprecated settings are set through command-line - Settings settings = new MapSettings(definitions); - settings.setProperty("oldKey", "value of oldKey"); - - assertThat(settings.getString("newKey")).isEqualTo("value of oldKey"); - assertThat(settings.getString("oldKey")).isEqualTo("value of oldKey"); - } - - @Test - public void should_load_values_of_deprecated_key() { - Settings settings = new MapSettings(definitions); - settings.setProperty("oldKey", "a,b"); - - assertThat(settings.getStringArray("newKey")).containsOnly("a", "b"); - assertThat(settings.getStringArray("oldKey")).containsOnly("a", "b"); - } - - @Test - public void should_support_deprecated_props_with_multi_values() { - Settings settings = new MapSettings(definitions); - settings.setProperty("new_multi_values", new String[] {" A ", " B "}); - assertThat(settings.getStringArray("new_multi_values")).isEqualTo(new String[] {"A", "B"}); - assertThat(settings.getStringArray("old_multi_values")).isEqualTo(new String[] {"A", "B"}); - } -} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/internal/MapSettingsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/internal/MapSettingsTest.java new file mode 100644 index 00000000000..e28a73cdb9c --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/internal/MapSettingsTest.java @@ -0,0 +1,430 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.config.internal; + +import java.util.Date; +import org.assertj.core.data.Offset; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.Properties; +import org.sonar.api.Property; +import org.sonar.api.PropertyType; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.utils.DateUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MapSettingsTest { + + private PropertyDefinitions definitions; + + @Properties({ + @Property(key = "hello", name = "Hello", defaultValue = "world"), + @Property(key = "date", name = "Date", defaultValue = "2010-05-18"), + @Property(key = "datetime", name = "DateTime", defaultValue = "2010-05-18T15:50:45+0100"), + @Property(key = "boolean", name = "Boolean", defaultValue = "true"), + @Property(key = "falseboolean", name = "False Boolean", defaultValue = "false"), + @Property(key = "integer", name = "Integer", defaultValue = "12345"), + @Property(key = "array", name = "Array", defaultValue = "one,two,three"), + @Property(key = "multi_values", name = "Array", defaultValue = "1,2,3", multiValues = true), + @Property(key = "sonar.jira", name = "Jira Server", type = PropertyType.PROPERTY_SET, propertySetKey = "jira"), + @Property(key = "newKey", name = "New key", deprecatedKey = "oldKey"), + @Property(key = "newKeyWithDefaultValue", name = "New key with default value", deprecatedKey = "oldKeyWithDefaultValue", defaultValue = "default_value"), + @Property(key = "new_multi_values", name = "New multi values", defaultValue = "1,2,3", multiValues = true, deprecatedKey = "old_multi_values") + }) + private static class Init { + } + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Before + public void init_definitions() { + definitions = new PropertyDefinitions(); + definitions.addComponent(Init.class); + } + + @Test + public void default_values_should_be_loaded_from_definitions() { + Settings settings = new MapSettings(definitions); + assertThat(settings.getDefaultValue("hello")).isEqualTo("world"); + } + + @Test + public void set_property_int() { + Settings settings = new MapSettings(); + settings.setProperty("foo", 123); + assertThat(settings.getInt("foo")).isEqualTo(123); + assertThat(settings.getString("foo")).isEqualTo("123"); + assertThat(settings.getBoolean("foo")).isFalse(); + } + + @Test + public void default_number_values_are_zero() { + Settings settings = new MapSettings(); + assertThat(settings.getInt("foo")).isEqualTo(0); + assertThat(settings.getLong("foo")).isEqualTo(0L); + } + + @Test + public void getInt_value_must_be_valid() { + thrown.expect(NumberFormatException.class); + + Settings settings = new MapSettings(); + settings.setProperty("foo", "not a number"); + settings.getInt("foo"); + } + + @Test + public void all_values_should_be_trimmed_set_property() { + Settings settings = new MapSettings(); + settings.setProperty("foo", " FOO "); + assertThat(settings.getString("foo")).isEqualTo("FOO"); + } + + @Test + public void test_get_default_value() { + Settings settings = new MapSettings(definitions); + assertThat(settings.getDefaultValue("unknown")).isNull(); + } + + @Test + public void test_get_string() { + Settings settings = new MapSettings(definitions); + settings.setProperty("hello", "Russia"); + assertThat(settings.getString("hello")).isEqualTo("Russia"); + } + + @Test + public void setProperty_date() { + Settings settings = new MapSettings(); + Date date = DateUtils.parseDateTime("2010-05-18T15:50:45+0100"); + settings.setProperty("aDate", date); + settings.setProperty("aDateTime", date, true); + + assertThat(settings.getString("aDate")).isEqualTo("2010-05-18"); + assertThat(settings.getString("aDateTime")).startsWith("2010-05-18T"); + } + + @Test + public void test_get_date() { + Settings settings = new MapSettings(definitions); + assertThat(settings.getDate("unknown")).isNull(); + assertThat(settings.getDate("date").getDate()).isEqualTo(18); + assertThat(settings.getDate("date").getMonth()).isEqualTo(4); + } + + @Test + public void test_get_date_not_found() { + Settings settings = new MapSettings(definitions); + assertThat(settings.getDate("unknown")).isNull(); + } + + @Test + public void test_get_datetime() { + Settings settings = new MapSettings(definitions); + assertThat(settings.getDateTime("unknown")).isNull(); + assertThat(settings.getDateTime("datetime").getDate()).isEqualTo(18); + assertThat(settings.getDateTime("datetime").getMonth()).isEqualTo(4); + assertThat(settings.getDateTime("datetime").getMinutes()).isEqualTo(50); + } + + @Test + public void test_get_double() { + Settings settings = new MapSettings(); + settings.setProperty("from_double", 3.14159); + settings.setProperty("from_string", "3.14159"); + assertThat(settings.getDouble("from_double")).isEqualTo(3.14159, Offset.offset(0.00001)); + assertThat(settings.getDouble("from_string")).isEqualTo(3.14159, Offset.offset(0.00001)); + assertThat(settings.getDouble("unknown")).isNull(); + } + + @Test + public void test_get_float() { + Settings settings = new MapSettings(); + settings.setProperty("from_float", 3.14159f); + settings.setProperty("from_string", "3.14159"); + assertThat(settings.getDouble("from_float")).isEqualTo(3.14159f, Offset.offset(0.00001)); + assertThat(settings.getDouble("from_string")).isEqualTo(3.14159f, Offset.offset(0.00001)); + assertThat(settings.getDouble("unknown")).isNull(); + } + + @Test + public void test_get_bad_float() { + Settings settings = new MapSettings(); + settings.setProperty("foo", "bar"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("The property 'foo' is not a float value"); + settings.getFloat("foo"); + } + + @Test + public void test_get_bad_double() { + Settings settings = new MapSettings(); + settings.setProperty("foo", "bar"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("The property 'foo' is not a double value"); + settings.getDouble("foo"); + } + + @Test + public void testSetNullFloat() { + Settings settings = new MapSettings(); + settings.setProperty("foo", (Float) null); + assertThat(settings.getFloat("foo")).isNull(); + } + + @Test + public void testSetNullDouble() { + Settings settings = new MapSettings(); + settings.setProperty("foo", (Double) null); + assertThat(settings.getDouble("foo")).isNull(); + } + + @Test + public void getStringArray() { + Settings settings = new MapSettings(definitions); + String[] array = settings.getStringArray("array"); + assertThat(array).isEqualTo(new String[] {"one", "two", "three"}); + } + + @Test + public void setStringArray() { + Settings settings = new MapSettings(definitions); + settings.setProperty("multi_values", new String[] {"A", "B"}); + String[] array = settings.getStringArray("multi_values"); + assertThat(array).isEqualTo(new String[] {"A", "B"}); + } + + @Test + public void setStringArrayTrimValues() { + Settings settings = new MapSettings(definitions); + settings.setProperty("multi_values", new String[] {" A ", " B "}); + String[] array = settings.getStringArray("multi_values"); + assertThat(array).isEqualTo(new String[] {"A", "B"}); + } + + @Test + public void setStringArrayEscapeCommas() { + Settings settings = new MapSettings(definitions); + settings.setProperty("multi_values", new String[] {"A,B", "C,D"}); + String[] array = settings.getStringArray("multi_values"); + assertThat(array).isEqualTo(new String[] {"A,B", "C,D"}); + } + + @Test + public void setStringArrayWithEmptyValues() { + Settings settings = new MapSettings(definitions); + settings.setProperty("multi_values", new String[] {"A,B", "", "C,D"}); + String[] array = settings.getStringArray("multi_values"); + assertThat(array).isEqualTo(new String[] {"A,B", "", "C,D"}); + } + + @Test + public void setStringArrayWithNullValues() { + Settings settings = new MapSettings(definitions); + settings.setProperty("multi_values", new String[] {"A,B", null, "C,D"}); + String[] array = settings.getStringArray("multi_values"); + assertThat(array).isEqualTo(new String[] {"A,B", "", "C,D"}); + } + + @Test(expected = IllegalStateException.class) + public void shouldFailToSetArrayValueOnSingleValueProperty() { + Settings settings = new MapSettings(definitions); + settings.setProperty("array", new String[] {"A", "B", "C"}); + } + + @Test + public void getStringArray_no_value() { + Settings settings = new MapSettings(); + String[] array = settings.getStringArray("array"); + assertThat(array).isEmpty(); + } + + @Test + public void shouldTrimArray() { + Settings settings = new MapSettings(); + settings.setProperty("foo", " one, two, three "); + String[] array = settings.getStringArray("foo"); + assertThat(array).isEqualTo(new String[] {"one", "two", "three"}); + } + + @Test + public void shouldKeepEmptyValuesWhenSplitting() { + Settings settings = new MapSettings(); + settings.setProperty("foo", " one, , two"); + String[] array = settings.getStringArray("foo"); + assertThat(array).isEqualTo(new String[] {"one", "", "two"}); + } + + @Test + public void testDefaultValueOfGetString() { + Settings settings = new MapSettings(definitions); + assertThat(settings.getString("hello")).isEqualTo("world"); + } + + @Test + public void set_property_boolean() { + Settings settings = new MapSettings(); + settings.setProperty("foo", true); + settings.setProperty("bar", false); + assertThat(settings.getBoolean("foo")).isTrue(); + assertThat(settings.getBoolean("bar")).isFalse(); + assertThat(settings.getString("foo")).isEqualTo("true"); + assertThat(settings.getString("bar")).isEqualTo("false"); + } + + @Test + public void ignore_case_of_boolean_values() { + Settings settings = new MapSettings(); + settings.setProperty("foo", "true"); + settings.setProperty("bar", "TRUE"); + // labels in UI + settings.setProperty("baz", "True"); + + assertThat(settings.getBoolean("foo")).isTrue(); + assertThat(settings.getBoolean("bar")).isTrue(); + assertThat(settings.getBoolean("baz")).isTrue(); + } + + @Test + public void get_boolean() { + Settings settings = new MapSettings(definitions); + assertThat(settings.getBoolean("boolean")).isTrue(); + assertThat(settings.getBoolean("falseboolean")).isFalse(); + assertThat(settings.getBoolean("unknown")).isFalse(); + assertThat(settings.getBoolean("hello")).isFalse(); + } + + @Test + public void shouldCreateByIntrospectingComponent() { + Settings settings = new MapSettings(); + settings.getDefinitions().addComponent(MyComponent.class); + + // property definition has been loaded, ie for default value + assertThat(settings.getDefaultValue("foo")).isEqualTo("bar"); + } + + @Property(key = "foo", name = "Foo", defaultValue = "bar") + public static class MyComponent { + + } + + @Test + public void getStringLines_no_value() { + assertThat(new MapSettings().getStringLines("foo")).hasSize(0); + } + + @Test + public void getStringLines_single_line() { + Settings settings = new MapSettings(); + settings.setProperty("foo", "the line"); + assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"the line"}); + } + + @Test + public void getStringLines_linux() { + Settings settings = new MapSettings(); + settings.setProperty("foo", "one\ntwo"); + assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two"}); + + settings.setProperty("foo", "one\ntwo\n"); + assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two"}); + } + + @Test + public void getStringLines_windows() { + Settings settings = new MapSettings(); + settings.setProperty("foo", "one\r\ntwo"); + assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two"}); + + settings.setProperty("foo", "one\r\ntwo\r\n"); + assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two"}); + } + + @Test + public void getStringLines_mix() { + Settings settings = new MapSettings(); + settings.setProperty("foo", "one\r\ntwo\nthree"); + assertThat(settings.getStringLines("foo")).isEqualTo(new String[] {"one", "two", "three"}); + } + + @Test + public void getKeysStartingWith() { + Settings settings = new MapSettings(); + settings.setProperty("sonar.jdbc.url", "foo"); + settings.setProperty("sonar.jdbc.username", "bar"); + settings.setProperty("sonar.security", "admin"); + + assertThat(settings.getKeysStartingWith("sonar")).containsOnly("sonar.jdbc.url", "sonar.jdbc.username", "sonar.security"); + assertThat(settings.getKeysStartingWith("sonar.jdbc")).containsOnly("sonar.jdbc.url", "sonar.jdbc.username"); + assertThat(settings.getKeysStartingWith("other")).hasSize(0); + } + + @Test + public void should_fallback_deprecated_key_to_default_value_of_new_key() { + Settings settings = new MapSettings(definitions); + + assertThat(settings.getString("newKeyWithDefaultValue")).isEqualTo("default_value"); + assertThat(settings.getString("oldKeyWithDefaultValue")).isEqualTo("default_value"); + } + + @Test + public void should_fallback_deprecated_key_to_new_key() { + Settings settings = new MapSettings(definitions); + settings.setProperty("newKey", "value of newKey"); + + assertThat(settings.getString("newKey")).isEqualTo("value of newKey"); + assertThat(settings.getString("oldKey")).isEqualTo("value of newKey"); + } + + @Test + public void should_load_value_of_deprecated_key() { + // it's used for example when deprecated settings are set through command-line + Settings settings = new MapSettings(definitions); + settings.setProperty("oldKey", "value of oldKey"); + + assertThat(settings.getString("newKey")).isEqualTo("value of oldKey"); + assertThat(settings.getString("oldKey")).isEqualTo("value of oldKey"); + } + + @Test + public void should_load_values_of_deprecated_key() { + Settings settings = new MapSettings(definitions); + settings.setProperty("oldKey", "a,b"); + + assertThat(settings.getStringArray("newKey")).containsOnly("a", "b"); + assertThat(settings.getStringArray("oldKey")).containsOnly("a", "b"); + } + + @Test + public void should_support_deprecated_props_with_multi_values() { + Settings settings = new MapSettings(definitions); + settings.setProperty("new_multi_values", new String[] {" A ", " B "}); + assertThat(settings.getStringArray("new_multi_values")).isEqualTo(new String[] {"A", "B"}); + assertThat(settings.getStringArray("old_multi_values")).isEqualTo(new String[] {"A", "B"}); + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/scan/filesystem/FileExclusionsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/scan/filesystem/FileExclusionsTest.java index bd98496fdf9..bd03eec7fed 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/scan/filesystem/FileExclusionsTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/scan/filesystem/FileExclusionsTest.java @@ -21,27 +21,26 @@ package org.sonar.api.scan.filesystem; import org.junit.Test; import org.sonar.api.CoreProperties; -import org.sonar.api.config.Settings; -import org.sonar.api.config.MapSettings; +import org.sonar.api.config.internal.MapSettings; import static org.assertj.core.api.Assertions.assertThat; public class FileExclusionsTest { @Test public void ignore_inclusion_of_world() { - Settings settings = new MapSettings(); + MapSettings settings = new MapSettings(); settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*"); settings.setProperty(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY, "**/*"); - assertThat(new FileExclusions(settings).sourceInclusions()).isEmpty(); - assertThat(new FileExclusions(settings).testInclusions()).isEmpty(); + assertThat(new FileExclusions(settings.asConfig()).sourceInclusions()).isEmpty(); + assertThat(new FileExclusions(settings.asConfig()).testInclusions()).isEmpty(); } @Test public void load_inclusions() { - Settings settings = new MapSettings(); + MapSettings settings = new MapSettings(); settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Foo.java"); settings.setProperty(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY, "**/*FooTest.java"); - FileExclusions moduleExclusions = new FileExclusions(settings); + FileExclusions moduleExclusions = new FileExclusions(settings.asConfig()); assertThat(moduleExclusions.sourceInclusions()).containsOnly("**/*Foo.java"); assertThat(moduleExclusions.testInclusions()).containsOnly("**/*FooTest.java"); @@ -49,10 +48,10 @@ public class FileExclusionsTest { @Test public void load_exclusions() { - Settings settings = new MapSettings(); + MapSettings settings = new MapSettings(); settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "**/*Foo.java"); settings.setProperty(CoreProperties.PROJECT_TEST_EXCLUSIONS_PROPERTY, "**/*FooTest.java"); - FileExclusions moduleExclusions = new FileExclusions(settings); + FileExclusions moduleExclusions = new FileExclusions(settings.asConfig()); assertThat(moduleExclusions.sourceInclusions()).isEmpty(); assertThat(moduleExclusions.sourceExclusions()).containsOnly("**/*Foo.java"); -- cgit v1.2.3