]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7876 Add an API for Sensors to report files that can't be analyzed 1092/head
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Fri, 8 Jul 2016 13:59:54 +0000 (15:59 +0200)
committerDuarte Meneses <duarte.meneses@sonarsource.com>
Mon, 11 Jul 2016 08:53:30 +0000 (10:53 +0200)
18 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/AnalysisErrorSensor.java [new file with mode: 0644]
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/AnalysisErrorSensorTest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/AnalysisError.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/NewAnalysisError.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/internal/DefaultAnalysisError.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/internal/package-info.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/package-info.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/InMemorySensorStorage.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/error/internal/DefaultAnalysisErrorTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/noop/NoOpNewAnalysisError.java [new file with mode: 0644]

index 870992596e7ccd3ccc14adc05259066d943607d8..1ff4660920f138121261aeeda5403c07f7d019c6 100644 (file)
@@ -32,6 +32,7 @@ import org.sonar.xoo.lang.SymbolReferencesSensor;
 import org.sonar.xoo.lang.SyntaxHighlightingSensor;
 import org.sonar.xoo.lang.XooCpdMapping;
 import org.sonar.xoo.lang.XooTokenizer;
+import org.sonar.xoo.rule.AnalysisErrorSensor;
 import org.sonar.xoo.rule.ChecksSensor;
 import org.sonar.xoo.rule.CreateIssueByInternalKeySensor;
 import org.sonar.xoo.rule.CustomMessageSensor;
@@ -118,6 +119,9 @@ public class XooPlugin implements Plugin {
       ItCoverageSensor.class,
       OverallCoverageSensor.class,
 
+      // Analysis errors
+      AnalysisErrorSensor.class,
+
       // Tests
       TestExecutionSensor.class,
       CoveragePerTestSensor.class,
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/AnalysisErrorSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/AnalysisErrorSensor.java
new file mode 100644 (file)
index 0000000..1262259
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.xoo.rule;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.xoo.Xoo;
+
+public class AnalysisErrorSensor implements Sensor {
+  private static final Logger LOG = Loggers.get(AnalysisErrorSensor.class);
+  private static final String ERROR_EXTENSION = ".error";
+
+  @Override
+  public void describe(SensorDescriptor descriptor) {
+    descriptor
+      .name("Xoo Analysis Error Sensor")
+      .onlyOnLanguages(Xoo.KEY);
+  }
+
+  @Override
+  public void execute(SensorContext context) {
+    for (InputFile file : context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguages(Xoo.KEY))) {
+      processFileError(file, context);
+    }
+  }
+
+  private void processFileError(InputFile inputFile, SensorContext context) {
+    Path ioFile = inputFile.file().toPath();
+    Path errorFile = ioFile.resolveSibling(ioFile.getFileName() + ERROR_EXTENSION).toAbsolutePath();
+    if (Files.exists(errorFile) && Files.isRegularFile(errorFile)) {
+      LOG.debug("Processing " + errorFile.toString());
+      try {
+        List<String> lines = Files.readAllLines(errorFile, context.fileSystem().encoding());
+        for (String line : lines) {
+          if (StringUtils.isBlank(line) || line.startsWith("#")) {
+            continue;
+          }
+          processLine(line, inputFile, context);
+        }
+      } catch (IOException e) {
+        throw new IllegalStateException(e);
+      }
+    }
+  }
+
+  private void processLine(String fileLine, InputFile inputFile, SensorContext context) {
+    String[] textPointer = fileLine.split(",");
+    if (textPointer.length != 3) {
+      throw new IllegalStateException("Invalid format in error file");
+    }
+
+    try {
+      int line = Integer.parseInt(textPointer[0]);
+      int lineOffset = Integer.parseInt(textPointer[1]);
+      String msg = textPointer[2];
+
+      context.newAnalysisError()
+        .onFile(inputFile)
+        .at(inputFile.newPointer(line, lineOffset))
+        .message(msg)
+        .save();
+
+    } catch (NumberFormatException e) {
+      throw new IllegalStateException("Invalid format in error file", e);
+    }
+  }
+
+}
index b9255f056d80fc26efaf71216d7fe67ccd0ba505..f4184d1b1130adcaa829e43f5f7cbb392a8a12b4 100644 (file)
@@ -35,10 +35,10 @@ public class XooPluginTest {
   public void provide_extensions_for_5_5() {
     Plugin.Context context = new Plugin.Context(new SonarRuntime(Version.parse("5.5"), SonarProduct.SONARQUBE, SonarQubeSide.SCANNER));
     new XooPlugin().define(context);
-    assertThat(context.getExtensions()).hasSize(41).contains(CpdTokenizerSensor.class);
+    assertThat(context.getExtensions()).hasSize(42).contains(CpdTokenizerSensor.class);
 
     context = new Plugin.Context(new SonarRuntime(Version.parse("5.4"), SonarProduct.SONARLINT, null));
     new XooPlugin().define(context);
-    assertThat(context.getExtensions()).hasSize(40).doesNotContain(CpdTokenizerSensor.class);
+    assertThat(context.getExtensions()).hasSize(41).doesNotContain(CpdTokenizerSensor.class);
   }
 }
diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/AnalysisErrorSensorTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/AnalysisErrorSensorTest.java
new file mode 100644 (file)
index 0000000..8067a67
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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.xoo.rule;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.*;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultTextPointer;
+import org.sonar.api.batch.sensor.error.AnalysisError;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+
+public class AnalysisErrorSensorTest {
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  private AnalysisErrorSensor sensor;
+  private SensorContextTester context;
+
+  @Before
+  public void setUp() {
+    sensor = new AnalysisErrorSensor();
+  }
+
+  private void createErrorFile(Path baseDir) throws IOException {
+    Path srcDir = baseDir.resolve("src");
+    Files.createDirectories(srcDir);
+    Path errorFile = srcDir.resolve("foo.xoo.error");
+
+    List<String> errors = new ArrayList<>();
+    errors.add("1,4,my error");
+
+    Files.write(errorFile, errors, StandardCharsets.UTF_8);
+  }
+
+  @Test
+  public void test() throws IOException {
+    Path baseDir = temp.newFolder().toPath().toAbsolutePath();
+    createErrorFile(baseDir);
+
+    DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo");
+    context = SensorContextTester.create(baseDir);
+    context.fileSystem().add(inputFile);
+
+    sensor.execute(context);
+    assertThat(context.allAnalysisErrors()).hasSize(1);
+    AnalysisError error = context.allAnalysisErrors().iterator().next();
+
+    assertThat(error.inputFile()).isEqualTo(inputFile);
+    assertThat(error.location()).isEqualTo(new DefaultTextPointer(1, 4));
+    assertThat(error.message()).isEqualTo("my error");
+  }
+}
index 22319fb461364c8ea18171f676c9a0f922c72e62..c2852681f6066ac0774368de0edb828f80da426c 100644 (file)
@@ -26,6 +26,7 @@ import org.sonar.api.batch.fs.InputModule;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.sensor.coverage.NewCoverage;
 import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
+import org.sonar.api.batch.sensor.error.NewAnalysisError;
 import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
 import org.sonar.api.batch.sensor.internal.SensorContextTester;
 import org.sonar.api.batch.sensor.issue.Issue;
@@ -127,4 +128,10 @@ public interface SensorContext {
    */
   NewCpdTokens newCpdTokens();
 
+  /**
+   * Builder to declare errors that happened while processing a source file.
+   * Don't forget to call {@link NewAnalisisError#save()}.
+   * @since 6.0
+   */
+  NewAnalysisError newAnalysisError();
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/AnalysisError.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/AnalysisError.java
new file mode 100644 (file)
index 0000000..4bacf8c
--- /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.batch.sensor.error;
+
+import javax.annotation.CheckForNull;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextPointer;
+import org.sonar.api.batch.sensor.Sensor;
+
+/**
+ * Represents an analysis error, such as a parsing error, reported by a {@link Sensor}.
+ *
+ * @since 6.0
+ */
+public interface AnalysisError {
+  /**
+   * The file that was being processed when the error occurred.
+   */
+  InputFile inputFile();
+  
+  /**
+   * A description of the error.
+   */
+  @CheckForNull
+  String message();
+  
+  /**
+   * Location of the error.
+   */
+  @CheckForNull
+  TextPointer location();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/NewAnalysisError.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/NewAnalysisError.java
new file mode 100644 (file)
index 0000000..b6a7419
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.batch.sensor.error;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextPointer;
+import org.sonar.api.batch.sensor.Sensor;
+
+/**
+ * Represents an analysis error, such as a parsing error, that occurs during the execution of a {@link Sensor}.
+ * Multiple analysis errors might be reported for the same file and issues may be created for a file which also has analysis errors reported. 
+ * The handling of such situations is unspecified and might vary depending on the implementation.
+ * 
+ * @since 6.0
+ */
+public interface NewAnalysisError {
+  /**
+   * The file that was being processed when the error occurred. This field must be set before saving the error.
+   */
+  NewAnalysisError onFile(InputFile inputFile);
+
+  /**
+   * Message about the error. This field is optional.
+   */
+  NewAnalysisError message(String message);
+
+  /**
+   * Location of this error. This field is optional.
+   */
+  NewAnalysisError at(TextPointer location);
+
+  /**
+   * Save this error information.
+   */
+  void save();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/internal/DefaultAnalysisError.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/internal/DefaultAnalysisError.java
new file mode 100644 (file)
index 0000000..04a6548
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.batch.sensor.error.internal;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextPointer;
+import org.sonar.api.batch.sensor.error.AnalysisError;
+import org.sonar.api.batch.sensor.error.NewAnalysisError;
+import org.sonar.api.batch.sensor.internal.DefaultStorable;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+
+import com.google.common.base.Preconditions;
+
+public class DefaultAnalysisError extends DefaultStorable implements NewAnalysisError, AnalysisError {
+  private InputFile inputFile;
+  private String message;
+  private TextPointer location;
+
+  public DefaultAnalysisError() {
+    super(null);
+  }
+
+  public DefaultAnalysisError(SensorStorage storage) {
+    super(storage);
+  }
+
+  @Override
+  public InputFile inputFile() {
+    return inputFile;
+  }
+
+  @Override
+  public String message() {
+    return message;
+  }
+
+  @Override
+  public TextPointer location() {
+    return location;
+  }
+
+  @Override
+  public NewAnalysisError onFile(InputFile inputFile) {
+    Preconditions.checkArgument(inputFile != null, "Cannot use a inputFile that is null");
+    Preconditions.checkState(this.inputFile == null, "onFile() already called");
+    this.inputFile = inputFile;
+    return this;
+  }
+
+  @Override
+  public NewAnalysisError message(String message) {
+    this.message = message;
+    return this;
+  }
+
+  @Override
+  public NewAnalysisError at(TextPointer location) {
+    Preconditions.checkState(this.location == null, "at() already called");
+    this.location = location;
+    return this;
+  }
+
+  @Override
+  protected void doSave() {
+    Preconditions.checkNotNull(this.inputFile, "inputFile is mandatory on AnalysisError");
+    storage.store(this);
+  }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/internal/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/internal/package-info.java
new file mode 100644 (file)
index 0000000..fbd5d4a
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.api.batch.sensor.error.internal;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/error/package-info.java
new file mode 100644 (file)
index 0000000..b253a6b
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.api.batch.sensor.error;
index 53a1364604b38da9a49052bcbd0b14dabd111d42..5a79149cbc3eea0eda8506af5fdbbefac7a6a319 100644 (file)
@@ -28,6 +28,7 @@ import java.util.Map;
 import org.sonar.api.batch.sensor.coverage.CoverageType;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
 import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
+import org.sonar.api.batch.sensor.error.AnalysisError;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
 import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.measure.Measure;
@@ -39,6 +40,7 @@ class InMemorySensorStorage implements SensorStorage {
   Table<String, String, Measure> measuresByComponentAndMetric = HashBasedTable.create();
 
   Collection<Issue> allIssues = new ArrayList<>();
+  Collection<AnalysisError> allAnalysisErrors = new ArrayList<>();
 
   Map<String, DefaultHighlighting> highlightingByComponent = new HashMap<>();
   Map<String, DefaultCpdTokens> cpdTokensByComponent = new HashMap<>();
@@ -101,4 +103,9 @@ class InMemorySensorStorage implements SensorStorage {
     symbolsPerComponent.put(fileKey, symbolTable);
   }
 
+  @Override
+  public void store(AnalysisError analysisError) {
+    allAnalysisErrors.add(analysisError);
+  }
+
 }
index 53a66952c1754cf01ef855b96bba349b513def70..1b274aaecf8abfd1c605e0479e93f8dc9cea0d93 100644 (file)
@@ -47,6 +47,9 @@ import org.sonar.api.batch.sensor.coverage.NewCoverage;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
 import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
 import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
+import org.sonar.api.batch.sensor.error.AnalysisError;
+import org.sonar.api.batch.sensor.error.NewAnalysisError;
+import org.sonar.api.batch.sensor.error.internal.DefaultAnalysisError;
 import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
@@ -192,6 +195,10 @@ public class SensorContextTester implements SensorContext {
   public Collection<Issue> allIssues() {
     return sensorStorage.allIssues;
   }
+  
+  public Collection<AnalysisError> allAnalysisErrors() {
+    return sensorStorage.allAnalysisErrors;
+  }
 
   @CheckForNull
   public Integer lineHits(String fileKey, CoverageType type, int line) {
@@ -245,6 +252,11 @@ public class SensorContextTester implements SensorContext {
   public NewSymbolTable newSymbolTable() {
     return new DefaultSymbolTable(sensorStorage);
   }
+  
+  @Override
+  public NewAnalysisError newAnalysisError() {
+    return new DefaultAnalysisError(sensorStorage);
+  }
 
   /**
    * Return list of syntax highlighting applied for a given position in a file. The result is a list because in theory you
index a0be68cbc24a36c9a899e22f9fb3644da85c68a2..9f61766afec78843252e19ab265f9abc65459273 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.api.batch.sensor.internal;
 import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
 import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
+import org.sonar.api.batch.sensor.error.AnalysisError;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
 import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.measure.Measure;
@@ -54,5 +55,9 @@ public interface SensorStorage {
    * @since 5.6 
    */
   void store(DefaultSymbolTable symbolTable);
-
+  
+  /**
+   * @since 6.0
+   */
+  void store(AnalysisError analysisError);
 }
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/error/internal/DefaultAnalysisErrorTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/error/internal/DefaultAnalysisErrorTest.java
new file mode 100644 (file)
index 0000000..3b9f62a
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.batch.sensor.error.internal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextPointer;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultTextPointer;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DefaultAnalysisErrorTest {
+  private InputFile inputFile;
+  private SensorStorage storage;
+  private TextPointer textPointer;
+
+  @Rule
+  public ExpectedException exception = ExpectedException.none();
+
+  @Before
+  public void setUp() {
+    inputFile = new DefaultInputFile("module1", "src/File.java");
+    textPointer = new DefaultTextPointer(5, 2);
+    storage = mock(SensorStorage.class);
+  }
+
+  @Test
+  public void test_analysis_error() {
+    DefaultAnalysisError analysisError = new DefaultAnalysisError(storage);
+    analysisError.onFile(inputFile)
+      .at(textPointer)
+      .message("msg");
+
+    assertThat(analysisError.location()).isEqualTo(textPointer);
+    assertThat(analysisError.message()).isEqualTo("msg");
+    assertThat(analysisError.inputFile()).isEqualTo(inputFile);
+  }
+
+  @Test
+  public void test_save() {
+    DefaultAnalysisError analysisError = new DefaultAnalysisError(storage);
+    analysisError.onFile(inputFile).save();
+
+    verify(storage).store(analysisError);
+    verifyNoMoreInteractions(storage);
+  }
+
+  @Test
+  public void test_must_have_file() {
+    exception.expect(NullPointerException.class);
+    new DefaultAnalysisError(storage).save();
+  }
+}
index 712e1eb050078ee5bd6bd0b0f9cd7699028c6cb3..46fe16802a1a847829f493763f3ae9aa87322575 100644 (file)
@@ -26,14 +26,18 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.DefaultTextPointer;
 import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
 import org.sonar.api.batch.sensor.coverage.CoverageType;
 import org.sonar.api.batch.sensor.coverage.NewCoverage;
+import org.sonar.api.batch.sensor.error.AnalysisError;
+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;
@@ -100,6 +104,26 @@ public class SensorContextTesterTest {
     assertThat(tester.allIssues()).hasSize(2);
   }
 
+  @Test
+  public void testAnalysisErrors() {
+    assertThat(tester.allAnalysisErrors()).isEmpty();
+    NewAnalysisError newAnalysisError = tester.newAnalysisError();
+
+    InputFile file = new DefaultInputFile("foo", "src/Foo.java");
+    newAnalysisError.onFile(file)
+      .message("error")
+      .at(new DefaultTextPointer(5, 2))
+      .save();
+
+    assertThat(tester.allAnalysisErrors()).hasSize(1);
+    AnalysisError analysisError = tester.allAnalysisErrors().iterator().next();
+
+    assertThat(analysisError.inputFile()).isEqualTo(file);
+    assertThat(analysisError.message()).isEqualTo("error");
+    assertThat(analysisError.location()).isEqualTo(new DefaultTextPointer(5, 2));
+
+  }
+
   @Test
   public void testMeasures() {
     assertThat(tester.measures("foo:src/Foo.java")).isEmpty();
index 2155392dd71d9281e57fac29d9a8c5216057b93c..a2e4ca8f9325460675950bea08dc0d18b1501809 100644 (file)
@@ -31,6 +31,7 @@ import org.sonar.api.batch.sensor.coverage.NewCoverage;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
 import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
 import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
+import org.sonar.api.batch.sensor.error.NewAnalysisError;
 import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
@@ -42,6 +43,7 @@ import org.sonar.api.batch.sensor.symbol.NewSymbolTable;
 import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.Version;
+import org.sonar.scanner.sensor.noop.NoOpNewAnalysisError;
 import org.sonar.scanner.sensor.noop.NoOpNewCpdTokens;
 import org.sonar.scanner.sensor.noop.NoOpNewHighlighting;
 import org.sonar.scanner.sensor.noop.NoOpNewSymbolTable;
@@ -51,6 +53,7 @@ public class DefaultSensorContext implements SensorContext {
   private static final NoOpNewHighlighting NO_OP_NEW_HIGHLIGHTING = new NoOpNewHighlighting();
   private static final NoOpNewSymbolTable NO_OP_NEW_SYMBOL_TABLE = new NoOpNewSymbolTable();
   private static final NoOpNewCpdTokens NO_OP_NEW_CPD_TOKENS = new NoOpNewCpdTokens();
+  private static final NoOpNewAnalysisError NO_OP_NEW_ANALYSIS_ERROR = new NoOpNewAnalysisError();
 
   private final Settings settings;
   private final FileSystem fs;
@@ -145,4 +148,9 @@ public class DefaultSensorContext implements SensorContext {
     return new DefaultCpdTokens(settings, sensorStorage);
   }
 
+  @Override
+  public NewAnalysisError newAnalysisError() {
+    return NO_OP_NEW_ANALYSIS_ERROR;
+  }
+
 }
index 1024e1747cd6671ad04c479e0ebe0d260957c3f9..8487a37908539beebf8507b052742769085e265a 100644 (file)
@@ -37,6 +37,7 @@ import org.sonar.api.batch.measure.MetricFinder;
 import org.sonar.api.batch.sensor.coverage.CoverageType;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
 import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
+import org.sonar.api.batch.sensor.error.AnalysisError;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
 import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
@@ -288,4 +289,9 @@ public class DefaultSensorStorage implements SensorStorage {
     return blockSize;
   }
 
+  @Override
+  public void store(AnalysisError analysisError) {
+    // no op
+  }
+
 }
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/noop/NoOpNewAnalysisError.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/noop/NoOpNewAnalysisError.java
new file mode 100644 (file)
index 0000000..29e57a3
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.scanner.sensor.noop;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextPointer;
+import org.sonar.api.batch.sensor.error.NewAnalysisError;
+
+public class NoOpNewAnalysisError implements NewAnalysisError {
+
+  @Override
+  public NewAnalysisError onFile(InputFile inputFile) {
+    // no op
+    return this;
+  }
+
+  @Override
+  public NewAnalysisError message(String message) {
+    // no op
+    return this;
+  }
+
+  @Override
+  public NewAnalysisError at(TextPointer location) {
+    // no op
+    return this;
+  }
+
+  @Override
+  public void save() {
+    // no op
+  }
+
+}