]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7458 Expose SQ Version in SensorContext 869/head
authorJulien HENRY <julien.henry@sonarsource.com>
Thu, 24 Mar 2016 14:53:36 +0000 (15:53 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Wed, 30 Mar 2016 09:42:16 +0000 (11:42 +0200)
19 files changed:
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/OneIssuePerLineSensorTest.java
server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java
sonar-core/src/main/java/org/sonar/core/platform/SonarQubeVersionProvider.java [deleted file]
sonar-core/src/test/java/org/sonar/core/platform/SonarQubeVersionProviderTest.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/SonarQubeVersion.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
sonar-plugin-api/src/main/java/org/sonar/api/internal/SonarQubeVersionFactory.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/utils/Version.java
sonar-plugin-api/src/test/java/org/sonar/api/SonarQubeVersionTest.java
sonar-plugin-api/src/test/java/org/sonar/api/internal/SonarQubeVersionFactoryTest.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
sonar-scanner-engine/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java
sonar-scanner-engine/src/main/java/org/sonar/batch/sensor/DefaultSensorContext.java
sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java

index 679d3c108974032374ac2eedb040c7329bb63f63..74699697e874fc4b675101950827b48a25f9c747 100644 (file)
@@ -19,9 +19,7 @@
  */
 package org.sonar.xoo;
 
-import java.util.Arrays;
-import java.util.List;
-import org.sonar.api.SonarPlugin;
+import org.sonar.api.Plugin;
 import org.sonar.xoo.coverage.ItCoverageSensor;
 import org.sonar.xoo.coverage.OverallCoverageSensor;
 import org.sonar.xoo.coverage.UtCoverageSensor;
@@ -60,17 +58,16 @@ import org.sonar.xoo.scm.XooScmProvider;
 import org.sonar.xoo.test.CoveragePerTestSensor;
 import org.sonar.xoo.test.TestExecutionSensor;
 
+import static org.sonar.api.SonarQubeVersion.V5_5;
+
 /**
  * Plugin entry-point, as declared in pom.xml.
  */
-public class XooPlugin extends SonarPlugin {
+public class XooPlugin implements Plugin {
 
-  /**
-   * Declares all the extensions implemented in the plugin
-   */
   @Override
-  public List getExtensions() {
-    return Arrays.asList(
+  public void define(Context context) {
+    context.addExtensions(
       Xoo.class,
       Xoo2.class,
       XooRulesDefinition.class,
@@ -98,7 +95,6 @@ public class XooPlugin extends SonarPlugin {
       ChecksSensor.class,
       RandomAccessSensor.class,
       DeprecatedResourceApiSensor.class,
-      CpdTokenizerSensor.class,
 
       OneBlockerIssuePerFileSensor.class,
       OneIssuePerLineSensor.class,
@@ -125,6 +121,10 @@ public class XooPlugin extends SonarPlugin {
       // Other
       XooProjectBuilder.class,
       XooPostJob.class);
+
+    if (context.getSonarQubeVersion().isGreaterThanOrEqual(V5_5)) {
+      context.addExtension(CpdTokenizerSensor.class);
+    }
   }
 
 }
index cb1e685803aacfe94d572e747f8a9f74e944ccf5..2b9474ec391c04119ad43e59d09f8292d1a26066 100644 (file)
@@ -19,8 +19,7 @@
  */
 package org.sonar.xoo.rule;
 
-import org.sonar.xoo.Xoo2;
-
+import org.sonar.api.SonarQubeVersion;
 import org.sonar.api.batch.fs.FilePredicates;
 import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.InputFile;
@@ -32,11 +31,12 @@ import org.sonar.api.batch.sensor.SensorDescriptor;
 import org.sonar.api.batch.sensor.issue.NewIssue;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.xoo.Xoo;
+import org.sonar.xoo.Xoo2;
 
 public class OneIssuePerLineSensor implements Sensor {
 
   public static final String RULE_KEY = "OneIssuePerLine";
-  private static final String EFFORT_TO_FIX_PROPERTY = "sonar.oneIssuePerLine.effortToFix";
+  public static final String EFFORT_TO_FIX_PROPERTY = "sonar.oneIssuePerLine.effortToFix";
   public static final String FORCE_SEVERITY_PROPERTY = "sonar.oneIssuePerLine.forceSeverity";
 
   @Override
@@ -72,9 +72,13 @@ public class OneIssuePerLineSensor implements Sensor {
           .on(file)
           .at(file.selectLine(line))
           .message("This issue is generated on each line"))
-        .effortToFix(context.settings().getDouble(EFFORT_TO_FIX_PROPERTY))
-        .overrideSeverity(severity != null ? Severity.valueOf(severity) : null)
-        .save();
+        .overrideSeverity(severity != null ? Severity.valueOf(severity) : null);
+      if (context.getSonarQubeVersion().isGreaterThanOrEqual(SonarQubeVersion.V5_5)) {
+        newIssue.gap(context.settings().getDouble(EFFORT_TO_FIX_PROPERTY));
+      } else {
+        newIssue.effortToFix(context.settings().getDouble(EFFORT_TO_FIX_PROPERTY));
+      }
+      newIssue.save();
     }
   }
 
index f31e1b667d89b26dadb094545fc103df6a416f30..69cc082389516d521d8e0ac2aa4965cd34e851fd 100644 (file)
 package org.sonar.xoo;
 
 import org.junit.Test;
+import org.sonar.api.Plugin;
+import org.sonar.api.SonarQubeVersion;
+import org.sonar.api.utils.Version;
+import org.sonar.xoo.lang.CpdTokenizerSensor;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.SonarQubeVersion.V5_5;
 
 public class XooPluginTest {
 
   @Test
-  public void provide_extensions() {
-    assertThat(new XooPlugin().getExtensions().size()).isGreaterThan(0);
+  public void provide_extensions_for_5_5() {
+    Plugin.Context context = new Plugin.Context(new SonarQubeVersion(V5_5));
+    new XooPlugin().define(context);
+    assertThat(context.getExtensions()).hasSize(39).contains(CpdTokenizerSensor.class);
+
+    context = new Plugin.Context(new SonarQubeVersion(Version.parse("5.4")));
+    new XooPlugin().define(context);
+    assertThat(context.getExtensions()).hasSize(38).doesNotContain(CpdTokenizerSensor.class);
   }
 }
index e225fd5e71cf3e9616f5a37e7865c47f12cd2400..8993bac751ee427e8c40906830bcb42d84b709df 100644 (file)
@@ -24,26 +24,16 @@ import java.io.StringReader;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.batch.rule.Severity;
-import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
-import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
 import org.sonar.api.batch.sensor.issue.Issue;
-import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
-import org.sonar.api.config.Settings;
+import org.sonar.api.utils.Version;
 import org.sonar.xoo.Xoo;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 public class OneIssuePerLineSensorTest {
 
@@ -61,54 +51,69 @@ public class OneIssuePerLineSensorTest {
 
   @Test
   public void testRule() throws IOException {
-    DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder().toPath());
     DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.xoo").setLanguage(Xoo.KEY)
       .initMetadata(new FileMetadata().readMetadata(new StringReader("a\nb\nc\nd\ne\nf\ng\nh\ni\n")));
-    fs.add(inputFile);
-
-    SensorContext context = mock(SensorContext.class);
-    final SensorStorage sensorStorage = mock(SensorStorage.class);
-    when(context.settings()).thenReturn(new Settings());
-    when(context.fileSystem()).thenReturn(fs);
-    when(context.newIssue()).thenAnswer(new Answer<Issue>() {
-      @Override
-      public Issue answer(InvocationOnMock invocation) throws Throwable {
-        return new DefaultIssue(sensorStorage);
-      }
-    });
+
+    SensorContextTester context = SensorContextTester.create(temp.newFolder());
+    context.fileSystem().add(inputFile);
     sensor.execute(context);
 
-    ArgumentCaptor<DefaultIssue> argCaptor = ArgumentCaptor.forClass(DefaultIssue.class);
-    verify(sensorStorage, times(10)).store(argCaptor.capture());
-    assertThat(argCaptor.getAllValues()).hasSize(10); // One issue per line
-    assertThat(argCaptor.getValue().overriddenSeverity()).isNull();
+    assertThat(context.allIssues()).hasSize(10); // One issue per line
+    for (Issue issue : context.allIssues()) {
+      assertThat(issue.gap()).isNull();
+    }
   }
 
   @Test
   public void testForceSeverity() throws IOException {
-    DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder().toPath());
     DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.xoo").setLanguage(Xoo.KEY)
       .initMetadata(new FileMetadata().readMetadata(new StringReader("a\nb\nc\nd\ne\nf\ng\nh\ni\n")));
-    fs.add(inputFile);
-
-    SensorContext context = mock(SensorContext.class);
-    final SensorStorage sensorStorage = mock(SensorStorage.class);
-    Settings settings = new Settings();
-    settings.setProperty(OneIssuePerLineSensor.FORCE_SEVERITY_PROPERTY, "MINOR");
-    when(context.settings()).thenReturn(settings);
-    when(context.fileSystem()).thenReturn(fs);
-    when(context.newIssue()).thenAnswer(new Answer<Issue>() {
-      @Override
-      public Issue answer(InvocationOnMock invocation) throws Throwable {
-        return new DefaultIssue(sensorStorage);
-      }
-    });
+
+    SensorContextTester context = SensorContextTester.create(temp.newFolder());
+    context.fileSystem().add(inputFile);
+    context.settings().setProperty(OneIssuePerLineSensor.FORCE_SEVERITY_PROPERTY, "MINOR");
+
+    sensor.execute(context);
+
+    assertThat(context.allIssues()).hasSize(10); // One issue per line
+    for (Issue issue : context.allIssues()) {
+      assertThat(issue.overriddenSeverity()).isEqualTo(Severity.MINOR);
+    }
+  }
+
+  @Test
+  public void testProvideGap() throws IOException {
+    DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.xoo").setLanguage(Xoo.KEY)
+      .initMetadata(new FileMetadata().readMetadata(new StringReader("a\nb\nc\nd\ne\nf\ng\nh\ni\n")));
+
+    SensorContextTester context = SensorContextTester.create(temp.newFolder());
+    context.fileSystem().add(inputFile);
+    context.settings().setProperty(OneIssuePerLineSensor.EFFORT_TO_FIX_PROPERTY, "1.2");
+
+    sensor.execute(context);
+
+    assertThat(context.allIssues()).hasSize(10); // One issue per line
+    for (Issue issue : context.allIssues()) {
+      assertThat(issue.gap()).isEqualTo(1.2d);
+    }
+  }
+
+  @Test
+  public void testProvideGap_before_5_5() throws IOException {
+    DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.xoo").setLanguage(Xoo.KEY)
+      .initMetadata(new FileMetadata().readMetadata(new StringReader("a\nb\nc\nd\ne\nf\ng\nh\ni\n")));
+
+    SensorContextTester context = SensorContextTester.create(temp.newFolder());
+    context.fileSystem().add(inputFile);
+    context.settings().setProperty(OneIssuePerLineSensor.EFFORT_TO_FIX_PROPERTY, "1.2");
+    context.setSonarQubeVersion(Version.parse("5.4"));
+
     sensor.execute(context);
 
-    ArgumentCaptor<DefaultIssue> argCaptor = ArgumentCaptor.forClass(DefaultIssue.class);
-    verify(sensorStorage, times(10)).store(argCaptor.capture());
-    assertThat(argCaptor.getAllValues()).hasSize(10); // One issue per line
-    assertThat(argCaptor.getValue().overriddenSeverity()).isEqualTo(Severity.MINOR);
+    assertThat(context.allIssues()).hasSize(10); // One issue per line
+    for (Issue issue : context.allIssues()) {
+      assertThat(issue.gap()).isEqualTo(1.2d);
+    }
   }
 
 }
index ca6cc5bc496a0ea5d79bcd7ff97922c061127d09..9a02f215935e5a626dfe92884f3aa0e6575563a5 100644 (file)
@@ -23,6 +23,7 @@ import com.google.common.annotations.VisibleForTesting;
 import java.util.List;
 import javax.annotation.CheckForNull;
 import org.sonar.api.config.EmailSettings;
+import org.sonar.api.internal.SonarQubeVersionFactory;
 import org.sonar.api.profiles.AnnotationProfileParser;
 import org.sonar.api.profiles.XMLProfileParser;
 import org.sonar.api.profiles.XMLProfileSerializer;
@@ -49,7 +50,6 @@ import org.sonar.core.platform.ComponentContainer;
 import org.sonar.core.platform.Module;
 import org.sonar.core.platform.PluginClassloaderFactory;
 import org.sonar.core.platform.PluginLoader;
-import org.sonar.core.platform.SonarQubeVersionProvider;
 import org.sonar.core.timemachine.Periods;
 import org.sonar.core.user.DefaultUserFinder;
 import org.sonar.core.user.DeprecatedUserFinder;
@@ -138,7 +138,7 @@ import org.sonarqube.ws.Rules;
 public class ComputeEngineContainerImpl implements ComputeEngineContainer {
   private static final Object[] LEVEL_1_COMPONENTS = new Object[] {
     ComputeEngineSettings.class,
-    new SonarQubeVersionProvider(),
+    SonarQubeVersionFactory.create(System2.INSTANCE),
     new JmxConnectionFactoryProvider(),
     ServerImpl.class,
     UuidFactoryImpl.INSTANCE,
index ae0469e3237a7a0ab3f9d924998d9c39fe55e2e5..a64ef830bf211be03daf60222ecf4a41d4569651 100644 (file)
@@ -21,10 +21,11 @@ package org.sonar.server.platform.platformlevel;
 
 import java.util.Properties;
 import javax.annotation.Nullable;
+import org.sonar.api.internal.SonarQubeVersionFactory;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.internal.TempFolderCleaner;
+import org.sonar.ce.property.CePropertyDefinitions;
 import org.sonar.core.config.CorePropertyDefinitions;
-import org.sonar.core.platform.SonarQubeVersionProvider;
 import org.sonar.core.util.UuidFactoryImpl;
 import org.sonar.db.DaoModule;
 import org.sonar.db.DatabaseChecker;
@@ -35,15 +36,14 @@ import org.sonar.db.semaphore.SemaphoresImpl;
 import org.sonar.db.version.DatabaseVersion;
 import org.sonar.db.version.MigrationStepModule;
 import org.sonar.server.app.ProcessCommandWrapperImpl;
-import org.sonar.ce.property.CePropertyDefinitions;
 import org.sonar.server.db.EmbeddedDatabaseFactory;
 import org.sonar.server.issue.index.IssueIndex;
 import org.sonar.server.platform.DatabaseServerCompatibility;
 import org.sonar.server.platform.DefaultServerFileSystem;
 import org.sonar.server.platform.Platform;
 import org.sonar.server.platform.ServerImpl;
-import org.sonar.server.platform.WebServerSettings;
 import org.sonar.server.platform.TempFolderProvider;
+import org.sonar.server.platform.WebServerSettings;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
 import org.sonar.server.ruby.PlatformRackBridge;
 import org.sonar.server.rule.index.RuleIndex;
@@ -68,7 +68,7 @@ public class PlatformLevel1 extends PlatformLevel {
     add(platform, properties);
     addExtraRootComponents();
     add(
-      new SonarQubeVersionProvider(),
+      SonarQubeVersionFactory.create(System2.INSTANCE),
       ProcessCommandWrapperImpl.class,
       WebServerSettings.class,
       ServerImpl.class,
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/SonarQubeVersionProvider.java b/sonar-core/src/main/java/org/sonar/core/platform/SonarQubeVersionProvider.java
deleted file mode 100644 (file)
index a0f54de..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.core.platform;
-
-import com.google.common.io.Resources;
-import java.io.IOException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import org.picocontainer.injectors.ProviderAdapter;
-import org.sonar.api.SonarQubeVersion;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.Version;
-
-public class SonarQubeVersionProvider extends ProviderAdapter {
-
-  private static final String FILE_PATH = "/sq-version.txt";
-
-  private SonarQubeVersion version = null;
-
-  public SonarQubeVersion provide(System2 system) {
-    if (version == null) {
-      try {
-        URL url = system.getResource(FILE_PATH);
-        String versionInFile = Resources.toString(url, StandardCharsets.UTF_8);
-        version = new SonarQubeVersion(Version.parse(versionInFile));
-      } catch (IOException e) {
-        throw new IllegalStateException("Can not load " + FILE_PATH + " from classpath", e);
-      }
-    }
-    return version;
-  }
-}
diff --git a/sonar-core/src/test/java/org/sonar/core/platform/SonarQubeVersionProviderTest.java b/sonar-core/src/test/java/org/sonar/core/platform/SonarQubeVersionProviderTest.java
deleted file mode 100644 (file)
index 79d9bad..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.core.platform;
-
-import java.io.File;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.SonarQubeVersion;
-import org.sonar.api.utils.System2;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class SonarQubeVersionProviderTest {
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  SonarQubeVersionProvider underTest = new SonarQubeVersionProvider();
-
-  @Test
-  public void create() {
-    SonarQubeVersion version = underTest.provide(System2.INSTANCE);
-    assertThat(version).isNotNull();
-    assertThat(version.get().major()).isGreaterThanOrEqualTo(5);
-  }
-
-  @Test
-  public void cache_version() {
-    SonarQubeVersion version1 = underTest.provide(System2.INSTANCE);
-    SonarQubeVersion version2 = underTest.provide(System2.INSTANCE);
-    assertThat(version1).isSameAs(version2);
-  }
-
-  @Test
-  public void throw_ISE_if_fail_to_load_version() throws Exception {
-    expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage("Can not load /sq-version.txt from classpath");
-
-    System2 system = mock(System2.class);
-    when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURL());
-    underTest.provide(system);
-  }
-}
index 0889546d04ee0cd8d4ffe3cf16a629dbf6772b37..5387d3ef76563a20a268288ad09719d644ec7249 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.api;
 
 import javax.annotation.concurrent.Immutable;
 import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.sensor.Sensor;
 import org.sonar.api.ce.ComputeEngineSide;
 import org.sonar.api.server.ServerSide;
 import org.sonar.api.utils.Version;
@@ -31,10 +32,24 @@ import static java.util.Objects.requireNonNull;
  * Version of SonarQube at runtime. This component can be injected as a dependency
  * of plugin extensions. The main usage for a plugin is to benefit from new APIs
  * while keeping backward-compatibility with previous versions of SonarQube.
-
- * Example: a plugin needs to use an API introduced in version 5.6 ({@code AnApi} in the following
+ * <br><br>
+ * 
+ * Example 1: a {@link Sensor} wants to use an API introduced in version 5.5 and still requires to support older versions
+ * at runtime.
+ * <pre>
+ * public class MySensor implements Sensor {
+ *
+ *   public void execute(SensorContext context) {
+ *     if (context.getSonarQubeVersion().isGreaterThanOrEqual(SonarQubeVersion.V5_5)) {
+ *       context.newMethodIntroducedIn5_5();
+ *     }
+ *   }
+ * }
+ * </pre>
+ *
+ * Example 2: a plugin needs to use an API introduced in version 5.6 ({@code AnApi} in the following
  * snippet) and still requires to support version 5.5 at runtime.
- * <p></p>
+ * <br>
  * <pre>
  * // Component provided by sonar-plugin-api
  * // @since 5.5
@@ -45,7 +60,7 @@ import static java.util.Objects.requireNonNull;
  *   // @since 5.6
  *   public void bar();
  * }
- *
+ * 
  * // Component provided by plugin
  * public class MyExtension {
  *   private final SonarQubeVersion sonarQubeVersion;
@@ -131,6 +146,7 @@ public class SonarQubeVersion {
   }
 
   public boolean isGreaterThanOrEqual(Version than) {
-    return this.version.compareTo(than) >= 0;
+    return this.version.isGreaterThanOrEqual(than);
   }
+
 }
index 3d49859470e9273e53c8bd6ef2c5b8a1c432c372..1c159c38b78436587cfcc80cc8f2cd89ef80294c 100644 (file)
@@ -33,6 +33,7 @@ import org.sonar.api.batch.sensor.issue.NewIssue;
 import org.sonar.api.batch.sensor.measure.Measure;
 import org.sonar.api.batch.sensor.measure.NewMeasure;
 import org.sonar.api.config.Settings;
+import org.sonar.api.utils.Version;
 
 /**
  * See {@link Sensor#execute(SensorContext)}
@@ -62,6 +63,11 @@ public interface SensorContext {
    */
   InputModule module();
 
+  /**
+   * @since 5.5
+   */
+  Version getSonarQubeVersion();
+
   // ----------- MEASURES --------------
 
   /**
index 24a06a241bde368552cd760257d397f7586fdf7c..b0c19108f166fdb5469babe3f7a13e80c99172c5 100644 (file)
@@ -24,6 +24,7 @@ import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Table;
 import java.io.File;
 import java.io.Serializable;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -32,6 +33,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.CheckForNull;
+import org.sonar.api.SonarQubeVersion;
 import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.fs.InputModule;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
@@ -57,7 +59,10 @@ import org.sonar.api.batch.sensor.measure.Measure;
 import org.sonar.api.batch.sensor.measure.NewMeasure;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.config.Settings;
+import org.sonar.api.internal.SonarQubeVersionFactory;
 import org.sonar.api.measures.Metric;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.Version;
 import org.sonar.duplications.internal.pmd.TokensLine;
 
 /**
@@ -76,16 +81,22 @@ public class SensorContextTester implements SensorContext {
   private ActiveRules activeRules;
   private InMemorySensorStorage sensorStorage;
   private InputModule module;
+  private SonarQubeVersion sqVersion;
 
-  private SensorContextTester(File moduleBaseDir) {
+  private SensorContextTester(Path moduleBaseDir) {
     this.settings = new Settings();
     this.fs = new DefaultFileSystem(moduleBaseDir);
     this.activeRules = new ActiveRulesBuilder().build();
     this.sensorStorage = new InMemorySensorStorage();
     this.module = new DefaultInputModule("projectKey");
+    this.sqVersion = SonarQubeVersionFactory.create(System2.INSTANCE);
   }
 
   public static SensorContextTester create(File moduleBaseDir) {
+    return new SensorContextTester(moduleBaseDir.toPath());
+  }
+
+  public static SensorContextTester create(Path moduleBaseDir) {
     return new SensorContextTester(moduleBaseDir);
   }
 
@@ -94,8 +105,9 @@ public class SensorContextTester implements SensorContext {
     return settings;
   }
 
-  public void setSettings(Settings settings) {
+  public SensorContextTester setSettings(Settings settings) {
     this.settings = settings;
+    return this;
   }
 
   @Override
@@ -103,8 +115,9 @@ public class SensorContextTester implements SensorContext {
     return fs;
   }
 
-  public void setFileSystem(DefaultFileSystem fs) {
+  public SensorContextTester setFileSystem(DefaultFileSystem fs) {
     this.fs = fs;
+    return this;
   }
 
   @Override
@@ -112,8 +125,27 @@ public class SensorContextTester implements SensorContext {
     return activeRules;
   }
 
-  public void setActiveRules(ActiveRules activeRules) {
+  public SensorContextTester setActiveRules(ActiveRules activeRules) {
     this.activeRules = activeRules;
+    return this;
+  }
+
+  /**
+   * Default value is the version of this API. You can override it
+   * using {@link #setSonarQubeVersion(Version)} to test your Sensor behavior.
+   * @since 5.5
+   */
+  @Override
+  public Version getSonarQubeVersion() {
+    return sqVersion.get();
+  }
+
+  /**
+   * @since 5.5
+   */
+  public SensorContextTester setSonarQubeVersion(Version version) {
+    this.sqVersion = new SonarQubeVersion(version);
+    return this;
   }
 
   @Override
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/internal/SonarQubeVersionFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/internal/SonarQubeVersionFactory.java
new file mode 100644 (file)
index 0000000..db85c3f
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.internal;
+
+import com.google.common.io.Resources;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import org.sonar.api.SonarQubeVersion;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.Version;
+
+/**
+ * For internal use only.
+ */
+public class SonarQubeVersionFactory {
+
+  private static final String FILE_PATH = "/sq-version.txt";
+
+  private SonarQubeVersionFactory() {
+    // prevents instantiation
+  }
+
+  public static SonarQubeVersion create(System2 system) {
+    try {
+      URL url = system.getResource(FILE_PATH);
+      String versionInFile = Resources.toString(url, StandardCharsets.UTF_8);
+      return new SonarQubeVersion(Version.parse(versionInFile));
+    } catch (IOException e) {
+      throw new IllegalStateException("Can not load " + FILE_PATH + " from classpath", e);
+    }
+  }
+}
index dcd96d9cbb403a8a655e888505f3d35a138e8606..50813e97c0e8ceca97ecca2d136badef36352584 100644 (file)
@@ -144,6 +144,10 @@ public class Version implements Comparable<Version> {
     return parseInt(sequence);
   }
 
+  public boolean isGreaterThanOrEqual(Version than) {
+    return this.compareTo(than) >= 0;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) {
index 0cc786c3ba45c9a8d334654eed94c1fe4f141a44..50bc4b1c2ec8eb490ab7daad585f6b93ba9b94aa 100644 (file)
  */
 package org.sonar.api;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.Version;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class SonarQubeVersionTest {
 
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
   @Test
   public void isGte() {
     Version version = Version.parse("1.2.3");
@@ -35,4 +40,5 @@ public class SonarQubeVersionTest {
     assertThat(qubeVersion.isGreaterThanOrEqual(Version.parse("1.1"))).isTrue();
     assertThat(qubeVersion.isGreaterThanOrEqual(Version.parse("1.3"))).isFalse();
   }
+
 }
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/internal/SonarQubeVersionFactoryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/internal/SonarQubeVersionFactoryTest.java
new file mode 100644 (file)
index 0000000..d37f769
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.internal;
+
+import java.io.File;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.SonarQubeVersion;
+import org.sonar.api.utils.System2;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+public class SonarQubeVersionFactoryTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Test
+  public void create() {
+    SonarQubeVersion version = SonarQubeVersionFactory.create(System2.INSTANCE);
+    assertThat(version).isNotNull();
+    assertThat(version.get().major()).isGreaterThanOrEqualTo(5);
+  }
+
+  @Test
+  public void throw_ISE_if_fail_to_load_version() throws Exception {
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage("Can not load /sq-version.txt from classpath");
+
+    System2 system = spy(System2.class);
+    when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURI().toURL());
+    SonarQubeVersionFactory.create(system);
+  }
+
+}
index ea6e967da42a9d0dd106b3f7e73c82519edfbf1b..dcc80d2fff556142e2478d262201f2426cf37513 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.batch.bootstrap;
 import java.util.List;
 import java.util.Map;
 import org.sonar.api.Plugin;
+import org.sonar.api.internal.SonarQubeVersionFactory;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.UriReader;
 import org.sonar.batch.cache.GlobalPersistentCacheProvider;
@@ -39,7 +40,6 @@ import org.sonar.core.platform.PluginClassloaderFactory;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginLoader;
 import org.sonar.core.platform.PluginRepository;
-import org.sonar.core.platform.SonarQubeVersionProvider;
 import org.sonar.core.util.DefaultHttpDownloader;
 import org.sonar.core.util.UuidFactoryImpl;
 
@@ -90,7 +90,7 @@ public class GlobalContainer extends ComponentContainer {
       BatchPluginPredicate.class,
       ExtensionInstaller.class,
 
-      new SonarQubeVersionProvider(),
+      SonarQubeVersionFactory.create(System2.INSTANCE),
       CachesManager.class,
       GlobalSettings.class,
       new BatchWsClientProvider(),
index 33a56f3e5eef6c606ea04d549120d52d465b9edc..91a61dfa3d3f444e9e82122368a3741a38f7191f 100644 (file)
@@ -23,6 +23,7 @@ import java.io.Serializable;
 import java.util.Collection;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.sonar.api.SonarQubeVersion;
 import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.SensorContext;
 import org.sonar.api.batch.SonarIndex;
@@ -55,9 +56,8 @@ public class DeprecatedSensorContext extends DefaultSensorContext implements Sen
   private final CoverageExclusions coverageFilter;
 
   public DeprecatedSensorContext(InputModule module, SonarIndex index, Project project, Settings settings, FileSystem fs, ActiveRules activeRules,
-    AnalysisMode analysisMode, CoverageExclusions coverageFilter,
-    SensorStorage sensorStorage) {
-    super(module, settings, fs, activeRules, analysisMode, sensorStorage);
+    AnalysisMode analysisMode, CoverageExclusions coverageFilter, SensorStorage sensorStorage, SonarQubeVersion sqVersion) {
+    super(module, settings, fs, activeRules, analysisMode, sensorStorage, sqVersion);
     this.index = index;
     this.project = project;
     this.coverageFilter = coverageFilter;
index e0a61eb91177e111518fa37b75e2b8409ae251d6..48dde9fd12d9f0cb7a40230f74060963e19d21a5 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.batch.sensor;
 
 import java.io.Serializable;
+import org.sonar.api.SonarQubeVersion;
 import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.InputModule;
@@ -37,6 +38,7 @@ import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
 import org.sonar.api.batch.sensor.measure.NewMeasure;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.config.Settings;
+import org.sonar.api.utils.Version;
 import org.sonar.batch.sensor.noop.NoOpNewCpdTokens;
 import org.sonar.batch.sensor.noop.NoOpNewHighlighting;
 
@@ -51,14 +53,17 @@ public class DefaultSensorContext implements SensorContext {
   private final SensorStorage sensorStorage;
   private final AnalysisMode analysisMode;
   private final InputModule module;
+  private final SonarQubeVersion sqVersion;
 
-  public DefaultSensorContext(InputModule module, Settings settings, FileSystem fs, ActiveRules activeRules, AnalysisMode analysisMode, SensorStorage sensorStorage) {
+  public DefaultSensorContext(InputModule module, Settings settings, FileSystem fs, ActiveRules activeRules, AnalysisMode analysisMode, SensorStorage sensorStorage,
+    SonarQubeVersion sqVersion) {
     this.module = module;
     this.settings = settings;
     this.fs = fs;
     this.activeRules = activeRules;
     this.analysisMode = analysisMode;
     this.sensorStorage = sensorStorage;
+    this.sqVersion = sqVersion;
   }
 
   @Override
@@ -81,6 +86,11 @@ public class DefaultSensorContext implements SensorContext {
     return module;
   }
 
+  @Override
+  public Version getSonarQubeVersion() {
+    return sqVersion.get();
+  }
+
   @Override
   public <G extends Serializable> NewMeasure<G> newMeasure() {
     return new DefaultMeasure<>(sensorStorage);
index ba40858840df291a18030bbfa456c60c9662b908..913a5fa67bc71251ca3b90bd4c95e42eb79325e0 100644 (file)
@@ -24,6 +24,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
+import org.sonar.api.SonarQubeVersion;
 import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.fs.InputModule;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
@@ -33,6 +34,7 @@ import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
 import org.sonar.api.config.Settings;
 import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.utils.Version;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
@@ -52,6 +54,7 @@ public class DefaultSensorContextTest {
   private Settings settings;
   private SensorStorage sensorStorage;
   private AnalysisMode analysisMode;
+  private SonarQubeVersion sqVersion;
 
   @Before
   public void prepare() throws Exception {
@@ -63,7 +66,8 @@ public class DefaultSensorContextTest {
     settings = new Settings();
     sensorStorage = mock(SensorStorage.class);
     analysisMode = mock(AnalysisMode.class);
-    adaptor = new DefaultSensorContext(mock(InputModule.class), settings, fs, activeRules, analysisMode, sensorStorage);
+    sqVersion = new SonarQubeVersion(Version.parse("5.5"));
+    adaptor = new DefaultSensorContext(mock(InputModule.class), settings, fs, activeRules, analysisMode, sensorStorage, sqVersion);
   }
 
   @Test
@@ -71,6 +75,7 @@ public class DefaultSensorContextTest {
     assertThat(adaptor.activeRules()).isEqualTo(activeRules);
     assertThat(adaptor.fileSystem()).isEqualTo(fs);
     assertThat(adaptor.settings()).isEqualTo(settings);
+    assertThat(adaptor.getSonarQubeVersion()).isEqualTo(Version.parse("5.5"));
 
     assertThat(adaptor.newIssue()).isNotNull();
     assertThat(adaptor.newMeasure()).isNotNull();