]> source.dussan.org Git - sonarqube.git/commitdiff
Write raw issues directly in protobuf report
authorJulien HENRY <julien.henry@sonarsource.com>
Tue, 28 Jul 2015 23:45:49 +0000 (01:45 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Fri, 31 Jul 2015 09:00:25 +0000 (11:00 +0200)
Avoid intermediate storage in persistit

29 files changed:
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java
sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java
sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java
sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java
sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java
sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java
sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/EmptyFileTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java
sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java

index bf0cdfcd7364b828331e4a92e05f1716059bdf99..8c68fa7fce57e7d78937444b362d85c808f954b1 100644 (file)
  */
 package org.sonar.batch.protocol.output;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import org.sonar.core.util.ContextException;
 import org.sonar.core.util.Protobuf;
 
 public class BatchReportWriter {
@@ -67,6 +71,15 @@ public class BatchReportWriter {
     return file;
   }
 
+  public void appendComponentIssue(int componentRef, BatchReport.Issue issue) {
+    File file = fileStructure.fileFor(FileStructure.Domain.ISSUES, componentRef);
+    try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file, true))) {
+      issue.writeDelimitedTo(out);
+    } catch (Exception e) {
+      throw ContextException.of("Unable to write issue", e).addContext("file", file);
+    }
+  }
+
   public File writeComponentMeasures(int componentRef, Iterable<BatchReport.Measure> measures) {
     File file = fileStructure.fileFor(FileStructure.Domain.MEASURES, componentRef);
     Protobuf.writeStream(measures, file, false);
index 76970b5c80f4d51ceb1a15221ba4f242c3def559..c2639e7cfbaf66c78a09cf27e1f4442acd06dc81 100644 (file)
@@ -19,7 +19,7 @@
  */
 package org.sonar.batch.issue;
 
-import com.google.common.collect.Lists;
+import java.util.Collections;
 import java.util.List;
 import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
@@ -32,13 +32,11 @@ import org.sonar.batch.index.BatchComponent;
  */
 public class DefaultIssuable implements Issuable {
 
-  private final IssueCache cache;
   private final BatchComponent component;
   private final SensorContext sensorContext;
 
-  DefaultIssuable(BatchComponent component, IssueCache cache, SensorContext sensorContext) {
+  DefaultIssuable(BatchComponent component, SensorContext sensorContext) {
     this.component = component;
-    this.cache = cache;
     this.sensorContext = sensorContext;
   }
 
@@ -56,24 +54,12 @@ public class DefaultIssuable implements Issuable {
 
   @Override
   public List<Issue> resolvedIssues() {
-    List<Issue> result = Lists.newArrayList();
-    for (org.sonar.core.issue.DefaultIssue issue : cache.byComponent(component.key())) {
-      if (issue.resolution() != null) {
-        result.add(issue);
-      }
-    }
-    return result;
+    return Collections.emptyList();
   }
 
   @Override
   public List<Issue> issues() {
-    List<Issue> result = Lists.newArrayList();
-    for (org.sonar.core.issue.DefaultIssue issue : cache.byComponent(component.key())) {
-      if (issue.resolution() == null) {
-        result.add(issue);
-      }
-    }
-    return result;
+    return Collections.emptyList();
   }
 
 }
index 6425abaca60f5d53bb5a9e66dc3cfb80025e66e6..62767bd49e206e30196e4861aad6e83636e2c978 100644 (file)
@@ -31,17 +31,15 @@ import org.sonar.batch.sensor.DefaultSensorContext;
  */
 public class IssuableFactory extends PerspectiveBuilder<Issuable> {
 
-  private final IssueCache cache;
   private final SensorContext sensorContext;
 
-  public IssuableFactory(IssueCache cache, DefaultSensorContext sensorContext) {
+  public IssuableFactory(DefaultSensorContext sensorContext) {
     super(Issuable.class);
-    this.cache = cache;
     this.sensorContext = sensorContext;
   }
 
   @Override
   public Issuable loadPerspective(Class<Issuable> perspectiveClass, BatchComponent component) {
-    return new DefaultIssuable(component, cache, sensorContext);
+    return new DefaultIssuable(component, sensorContext);
   }
 }
index 70bd4d54d5bf897fd370caa8051398aa3bcd20cf..203e495d01a14aa78f9f5b6c943bb2cccbc6b113 100644 (file)
@@ -20,8 +20,8 @@
 package org.sonar.batch.issue;
 
 import org.sonar.api.batch.BatchSide;
+import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.batch.IssueFilter;
-import org.sonar.core.issue.DefaultIssue;
 
 @BatchSide
 public class IssueFilters {
@@ -46,7 +46,7 @@ public class IssueFilters {
     this(new org.sonar.api.issue.IssueFilter[0], new IssueFilter[0]);
   }
 
-  public boolean accept(DefaultIssue issue) {
+  public boolean accept(Issue issue) {
     if (new DefaultIssueFilterChain(filters).accept(issue)) {
       // Apply deprecated rules only if filter chain accepts the current issue
       for (org.sonar.api.issue.IssueFilter filter : exclusionFilters) {
index a27b4c272a3a33e582b405fb7d0b809a821bc22e..d74fef6dcb07572416805b61cf55da2fb96ec0c2 100644 (file)
@@ -21,13 +21,23 @@ package org.sonar.batch.issue;
 
 import com.google.common.base.Strings;
 import javax.annotation.Nullable;
+import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.TextRange;
 import org.sonar.api.batch.rule.ActiveRule;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.rule.Rule;
 import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.batch.rule.Severity;
+import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.resources.Project;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.api.utils.MessageException;
+import org.sonar.batch.index.BatchComponent;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.protocol.Constants;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.core.issue.DefaultIssue;
 
 /**
@@ -37,35 +47,88 @@ public class ModuleIssues {
 
   private final ActiveRules activeRules;
   private final Rules rules;
-  private final IssueCache cache;
   private final Project project;
   private final IssueFilters filters;
+  private final ReportPublisher reportPublisher;
+  private final BatchComponentCache componentCache;
+  private final BatchReport.Issue.Builder builder = BatchReport.Issue.newBuilder();
 
-  public ModuleIssues(ActiveRules activeRules, Rules rules, IssueCache cache, Project project, IssueFilters filters) {
+  public ModuleIssues(ActiveRules activeRules, Rules rules, Project project, IssueFilters filters, ReportPublisher reportPublisher, BatchComponentCache componentCache) {
     this.activeRules = activeRules;
     this.rules = rules;
-    this.cache = cache;
     this.project = project;
     this.filters = filters;
+    this.reportPublisher = reportPublisher;
+    this.componentCache = componentCache;
   }
 
-  public boolean initAndAddIssue(DefaultIssue issue) {
-    RuleKey ruleKey = issue.ruleKey();
+  public boolean initAndAddIssue(Issue issue) {
+    BatchComponent component;
+    InputPath inputPath = issue.locations().get(0).inputPath();
+    if (inputPath != null) {
+      component = componentCache.get(inputPath);
+    } else {
+      component = componentCache.get(project);
+    }
+    DefaultIssue defaultIssue = toDefaultIssue(project.getKey(), component.key(), issue);
+    RuleKey ruleKey = defaultIssue.ruleKey();
     Rule rule = rules.find(ruleKey);
-    validateRule(issue, rule);
+    validateRule(defaultIssue, rule);
     ActiveRule activeRule = activeRules.find(ruleKey);
     if (activeRule == null) {
       // rule does not exist or is not enabled -> ignore the issue
       return false;
     }
-    updateIssue(issue, rule, activeRule);
-    if (filters.accept(issue)) {
-      cache.put(issue);
+    updateIssue(defaultIssue, rule, activeRule);
+    if (filters.accept(defaultIssue)) {
+      write(component, defaultIssue);
       return true;
     }
     return false;
   }
 
+  public void write(BatchComponent component, DefaultIssue issue) {
+    reportPublisher.getWriter().appendComponentIssue(component.batchId(), toReportIssue(builder, issue));
+  }
+
+  private static BatchReport.Issue toReportIssue(BatchReport.Issue.Builder builder, DefaultIssue issue) {
+    builder.clear();
+    // non-null fields
+    builder.setSeverity(Constants.Severity.valueOf(issue.severity()));
+    builder.setRuleRepository(issue.ruleKey().repository());
+    builder.setRuleKey(issue.ruleKey().rule());
+    builder.setAttributes(KeyValueFormat.format(issue.attributes()));
+
+    // nullable fields
+    Integer line = issue.line();
+    if (line != null) {
+      builder.setLine(line);
+    }
+    String message = issue.message();
+    if (message != null) {
+      builder.setMsg(message);
+    }
+    Double effortToFix = issue.effortToFix();
+    if (effortToFix != null) {
+      builder.setEffortToFix(effortToFix);
+    }
+    return builder.build();
+  }
+
+  public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) {
+    Severity overriddenSeverity = issue.overriddenSeverity();
+    TextRange textRange = issue.locations().get(0).textRange();
+    return new org.sonar.core.issue.DefaultIssueBuilder()
+      .componentKey(componentKey)
+      .projectKey(projectKey)
+      .ruleKey(RuleKey.of(issue.ruleKey().repository(), issue.ruleKey().rule()))
+      .effortToFix(issue.effortToFix())
+      .line(textRange != null ? textRange.start().line() : null)
+      .message(issue.locations().get(0).message())
+      .severity(overriddenSeverity != null ? overriddenSeverity.name() : null)
+      .build();
+  }
+
   private static void validateRule(DefaultIssue issue, @Nullable Rule rule) {
     RuleKey ruleKey = issue.ruleKey();
     if (rule == null) {
index ee08935732038e9fa07fb9b30c1fc045c8025374..b5a2dbd872b300dad3916c1dc027dea7ab881dcb 100644 (file)
@@ -27,102 +27,92 @@ import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
-import org.sonar.api.batch.BatchSide;
-import org.sonar.api.batch.InstantiationStrategy;
-import org.sonar.core.issue.DefaultIssue;
-
-import javax.annotation.Nullable;
-
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.protocol.output.BatchReport;
 
 @InstantiationStrategy(InstantiationStrategy.PER_BATCH)
 @BatchSide
 public class IssueTracking {
 
+  private SourceHashHolder sourceHashHolder;
+
   /**
    * @param sourceHashHolder Null when working on resource that is not a file (directory/project)
    */
-  public IssueTrackingResult track(@Nullable SourceHashHolder sourceHashHolder, Collection<ServerIssue> previousIssues, Collection<DefaultIssue> newIssues) {
+  public IssueTrackingResult track(@Nullable SourceHashHolder sourceHashHolder, Collection<ServerIssue> previousIssues, Collection<BatchReport.Issue> rawIssues) {
+    this.sourceHashHolder = sourceHashHolder;
     IssueTrackingResult result = new IssueTrackingResult();
 
-    if (sourceHashHolder != null) {
-      setChecksumOnNewIssues(newIssues, sourceHashHolder);
-    }
-
     // Map new issues with old ones
-    mapIssues(newIssues, previousIssues, sourceHashHolder, result);
+    mapIssues(rawIssues, previousIssues, sourceHashHolder, result);
     return result;
   }
 
-  private void setChecksumOnNewIssues(Collection<DefaultIssue> issues, SourceHashHolder sourceHashHolder) {
-    if (issues.isEmpty()) {
-      return;
-    }
+  private String checksum(BatchReport.Issue rawIssue) {
     FileHashes hashedSource = sourceHashHolder.getHashedSource();
-    for (DefaultIssue issue : issues) {
-      Integer line = issue.line();
-      if (line != null) {
-        // Extra verification if some plugin managed to create issue on a wrong line
-        Preconditions.checkState(line <= hashedSource.length(), "Invalid line number for issue %s. File has only %s line(s)", issue, hashedSource.length());
-        issue.setChecksum(hashedSource.getHash(line));
-      }
+    if (rawIssue.hasLine()) {
+      // Extra verification if some plugin managed to create issue on a wrong line
+      Preconditions.checkState(rawIssue.getLine() <= hashedSource.length(), "Invalid line number for issue %s. File has only %s line(s)", rawIssue, hashedSource.length());
+      return hashedSource.getHash(rawIssue.getLine());
     }
+    return null;
   }
 
   @VisibleForTesting
-  void mapIssues(Collection<DefaultIssue> newIssues, @Nullable Collection<ServerIssue> previousIssues, @Nullable SourceHashHolder sourceHashHolder, IssueTrackingResult result) {
+  void mapIssues(Collection<BatchReport.Issue> rawIssues, @Nullable Collection<ServerIssue> previousIssues, @Nullable SourceHashHolder sourceHashHolder,
+    IssueTrackingResult result) {
     boolean hasLastScan = false;
 
     if (previousIssues != null) {
       hasLastScan = true;
-      mapLastIssues(newIssues, previousIssues, result);
+      mapLastIssues(rawIssues, previousIssues, result);
     }
 
     // If each new issue matches an old one we can stop the matching mechanism
-    if (result.matched().size() != newIssues.size()) {
+    if (result.matched().size() != rawIssues.size()) {
       if (sourceHashHolder != null && hasLastScan) {
         FileHashes hashedReference = sourceHashHolder.getHashedReference();
         if (hashedReference != null) {
-          mapNewissues(hashedReference, sourceHashHolder.getHashedSource(), newIssues, result);
+          mapNewissues(hashedReference, sourceHashHolder.getHashedSource(), rawIssues, result);
         }
       }
-      mapIssuesOnSameRule(newIssues, result);
+      mapIssuesOnSameRule(rawIssues, result);
     }
   }
 
-  private void mapLastIssues(Collection<DefaultIssue> newIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) {
+  private void mapLastIssues(Collection<BatchReport.Issue> rawIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) {
     for (ServerIssue lastIssue : previousIssues) {
       result.addUnmatched(lastIssue);
     }
 
-    // Match the key of the issue. (For manual issues)
-    for (DefaultIssue newIssue : newIssues) {
-      mapIssue(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).get(newIssue.key()), result);
-    }
-
     // Try first to match issues on same rule with same line and with same checksum (but not necessarily with same message)
-    for (DefaultIssue newIssue : newIssues) {
-      if (isNotAlreadyMapped(newIssue, result)) {
+    for (BatchReport.Issue rawIssue : rawIssues) {
+      if (isNotAlreadyMapped(rawIssue, result)) {
         mapIssue(
-          newIssue,
-          findLastIssueWithSameLineAndChecksum(newIssue, result),
+          rawIssue,
+          findLastIssueWithSameLineAndChecksum(rawIssue, result),
           result);
       }
     }
   }
 
-  private void mapNewissues(FileHashes hashedReference, FileHashes hashedSource, Collection<DefaultIssue> newIssues, IssueTrackingResult result) {
+  private void mapNewissues(FileHashes hashedReference, FileHashes hashedSource, Collection<BatchReport.Issue> rawIssues, IssueTrackingResult result) {
 
     IssueTrackingBlocksRecognizer rec = new IssueTrackingBlocksRecognizer(hashedReference, hashedSource);
 
     RollingFileHashes a = RollingFileHashes.create(hashedReference, 5);
     RollingFileHashes b = RollingFileHashes.create(hashedSource, 5);
 
-    Multimap<Integer, DefaultIssue> newIssuesByLines = newIssuesByLines(newIssues, rec, result);
+    Multimap<Integer, BatchReport.Issue> rawIssuesByLines = rawIssuesByLines(rawIssues, rec, result);
     Multimap<Integer, ServerIssue> lastIssuesByLines = lastIssuesByLines(result.unmatched(), rec);
 
     Map<Integer, HashOccurrence> map = Maps.newHashMap();
@@ -141,7 +131,7 @@ public class IssueTracking {
       }
     }
 
-    for (Integer line : newIssuesByLines.keySet()) {
+    for (Integer line : rawIssuesByLines.keySet()) {
       int hash = b.getHash(line);
       HashOccurrence hashOccurrence = map.get(hash);
       if (hashOccurrence != null) {
@@ -153,17 +143,17 @@ public class IssueTracking {
     for (HashOccurrence hashOccurrence : map.values()) {
       if (hashOccurrence.countA == 1 && hashOccurrence.countB == 1) {
         // Guaranteed that lineA has been moved to lineB, so we can map all issues on lineA to all issues on lineB
-        map(newIssuesByLines.get(hashOccurrence.lineB), lastIssuesByLines.get(hashOccurrence.lineA), result);
+        map(rawIssuesByLines.get(hashOccurrence.lineB), lastIssuesByLines.get(hashOccurrence.lineA), result);
         lastIssuesByLines.removeAll(hashOccurrence.lineA);
-        newIssuesByLines.removeAll(hashOccurrence.lineB);
+        rawIssuesByLines.removeAll(hashOccurrence.lineB);
       }
     }
 
     // Check if remaining number of lines exceeds threshold
-    if (lastIssuesByLines.keySet().size() * newIssuesByLines.keySet().size() < 250000) {
+    if (lastIssuesByLines.keySet().size() * rawIssuesByLines.keySet().size() < 250000) {
       List<LinePair> possibleLinePairs = Lists.newArrayList();
       for (Integer oldLine : lastIssuesByLines.keySet()) {
-        for (Integer newLine : newIssuesByLines.keySet()) {
+        for (Integer newLine : rawIssuesByLines.keySet()) {
           int weight = rec.computeLengthOfMaximalBlock(oldLine, newLine);
           possibleLinePairs.add(new LinePair(oldLine, newLine, weight));
         }
@@ -171,50 +161,54 @@ public class IssueTracking {
       Collections.sort(possibleLinePairs, LINE_PAIR_COMPARATOR);
       for (LinePair linePair : possibleLinePairs) {
         // High probability that lineA has been moved to lineB, so we can map all Issues on lineA to all Issues on lineB
-        map(newIssuesByLines.get(linePair.lineB), lastIssuesByLines.get(linePair.lineA), result);
+        map(rawIssuesByLines.get(linePair.lineB), lastIssuesByLines.get(linePair.lineA), result);
       }
     }
   }
 
-  private void mapIssuesOnSameRule(Collection<DefaultIssue> newIssues, IssueTrackingResult result) {
+  private void mapIssuesOnSameRule(Collection<BatchReport.Issue> rawIssues, IssueTrackingResult result) {
     // Try then to match issues on same rule with same message and with same checksum
-    for (DefaultIssue newIssue : newIssues) {
-      if (isNotAlreadyMapped(newIssue, result)) {
+    for (BatchReport.Issue rawIssue : rawIssues) {
+      if (isNotAlreadyMapped(rawIssue, result)) {
         mapIssue(
-          newIssue,
-          findLastIssueWithSameChecksumAndMessage(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).values()),
+          rawIssue,
+          findLastIssueWithSameChecksumAndMessage(rawIssue, result.unmatchedByKeyForRule(ruleKey(rawIssue)).values()),
           result);
       }
     }
 
     // Try then to match issues on same rule with same line and with same message
-    for (DefaultIssue newIssue : newIssues) {
-      if (isNotAlreadyMapped(newIssue, result)) {
+    for (BatchReport.Issue rawIssue : rawIssues) {
+      if (isNotAlreadyMapped(rawIssue, result)) {
         mapIssue(
-          newIssue,
-          findLastIssueWithSameLineAndMessage(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).values()),
+          rawIssue,
+          findLastIssueWithSameLineAndMessage(rawIssue, result.unmatchedByKeyForRule(ruleKey(rawIssue)).values()),
           result);
       }
     }
 
     // Last check: match issue if same rule and same checksum but different line and different message
     // See SONAR-2812
-    for (DefaultIssue newIssue : newIssues) {
-      if (isNotAlreadyMapped(newIssue, result)) {
+    for (BatchReport.Issue rawIssue : rawIssues) {
+      if (isNotAlreadyMapped(rawIssue, result)) {
         mapIssue(
-          newIssue,
-          findLastIssueWithSameChecksum(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).values()),
+          rawIssue,
+          findLastIssueWithSameChecksum(rawIssue, result.unmatchedByKeyForRule(ruleKey(rawIssue)).values()),
           result);
       }
     }
   }
 
-  private void map(Collection<DefaultIssue> newIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) {
-    for (DefaultIssue newIssue : newIssues) {
-      if (isNotAlreadyMapped(newIssue, result)) {
+  private RuleKey ruleKey(BatchReport.Issue rawIssue) {
+    return RuleKey.of(rawIssue.getRuleRepository(), rawIssue.getRuleKey());
+  }
+
+  private void map(Collection<BatchReport.Issue> rawIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) {
+    for (BatchReport.Issue rawIssue : rawIssues) {
+      if (isNotAlreadyMapped(rawIssue, result)) {
         for (ServerIssue previousIssue : previousIssues) {
-          if (isNotAlreadyMapped(previousIssue, result) && Objects.equal(newIssue.ruleKey(), previousIssue.ruleKey())) {
-            mapIssue(newIssue, previousIssue, result);
+          if (isNotAlreadyMapped(previousIssue, result) && Objects.equal(ruleKey(rawIssue), previousIssue.ruleKey())) {
+            mapIssue(rawIssue, previousIssue, result);
             break;
           }
         }
@@ -222,14 +216,14 @@ public class IssueTracking {
     }
   }
 
-  private Multimap<Integer, DefaultIssue> newIssuesByLines(Collection<DefaultIssue> newIssues, IssueTrackingBlocksRecognizer rec, IssueTrackingResult result) {
-    Multimap<Integer, DefaultIssue> newIssuesByLines = LinkedHashMultimap.create();
-    for (DefaultIssue newIssue : newIssues) {
-      if (isNotAlreadyMapped(newIssue, result) && rec.isValidLineInSource(newIssue.line())) {
-        newIssuesByLines.put(newIssue.line(), newIssue);
+  private Multimap<Integer, BatchReport.Issue> rawIssuesByLines(Collection<BatchReport.Issue> rawIssues, IssueTrackingBlocksRecognizer rec, IssueTrackingResult result) {
+    Multimap<Integer, BatchReport.Issue> rawIssuesByLines = LinkedHashMultimap.create();
+    for (BatchReport.Issue rawIssue : rawIssues) {
+      if (isNotAlreadyMapped(rawIssue, result) && rawIssue.hasLine() && rec.isValidLineInSource(rawIssue.getLine())) {
+        rawIssuesByLines.put(rawIssue.getLine(), rawIssue);
       }
     }
-    return newIssuesByLines;
+    return rawIssuesByLines;
   }
 
   private Multimap<Integer, ServerIssue> lastIssuesByLines(Collection<ServerIssue> previousIssues, IssueTrackingBlocksRecognizer rec) {
@@ -242,64 +236,74 @@ public class IssueTracking {
     return previousIssuesByLines;
   }
 
-  private ServerIssue findLastIssueWithSameChecksum(DefaultIssue newIssue, Collection<ServerIssue> previousIssues) {
+  private ServerIssue findLastIssueWithSameChecksum(BatchReport.Issue rawIssue, Collection<ServerIssue> previousIssues) {
     for (ServerIssue previousIssue : previousIssues) {
-      if (isSameChecksum(newIssue, previousIssue)) {
+      if (isSameChecksum(rawIssue, previousIssue)) {
         return previousIssue;
       }
     }
     return null;
   }
 
-  private ServerIssue findLastIssueWithSameLineAndMessage(DefaultIssue newIssue, Collection<ServerIssue> previousIssues) {
+  private ServerIssue findLastIssueWithSameLineAndMessage(BatchReport.Issue rawIssue, Collection<ServerIssue> previousIssues) {
     for (ServerIssue previousIssue : previousIssues) {
-      if (isSameLine(newIssue, previousIssue) && isSameMessage(newIssue, previousIssue)) {
+      if (isSameLine(rawIssue, previousIssue) && isSameMessage(rawIssue, previousIssue)) {
         return previousIssue;
       }
     }
     return null;
   }
 
-  private ServerIssue findLastIssueWithSameChecksumAndMessage(DefaultIssue newIssue, Collection<ServerIssue> previousIssues) {
+  private ServerIssue findLastIssueWithSameChecksumAndMessage(BatchReport.Issue rawIssue, Collection<ServerIssue> previousIssues) {
     for (ServerIssue previousIssue : previousIssues) {
-      if (isSameChecksum(newIssue, previousIssue) && isSameMessage(newIssue, previousIssue)) {
+      if (isSameChecksum(rawIssue, previousIssue) && isSameMessage(rawIssue, previousIssue)) {
         return previousIssue;
       }
     }
     return null;
   }
 
-  private ServerIssue findLastIssueWithSameLineAndChecksum(DefaultIssue newIssue, IssueTrackingResult result) {
-    Collection<ServerIssue> sameRuleAndSameLineAndSameChecksum = result.unmatchedForRuleAndForLineAndForChecksum(newIssue.ruleKey(), newIssue.line(), newIssue.checksum());
+  private ServerIssue findLastIssueWithSameLineAndChecksum(BatchReport.Issue rawIssue, IssueTrackingResult result) {
+    Collection<ServerIssue> sameRuleAndSameLineAndSameChecksum = result.unmatchedForRuleAndForLineAndForChecksum(ruleKey(rawIssue), line(rawIssue), checksum(rawIssue));
     if (!sameRuleAndSameLineAndSameChecksum.isEmpty()) {
       return sameRuleAndSameLineAndSameChecksum.iterator().next();
     }
     return null;
   }
 
+  @CheckForNull
+  private Integer line(BatchReport.Issue rawIssue) {
+    return rawIssue.hasLine() ? rawIssue.getLine() : null;
+  }
+
   private boolean isNotAlreadyMapped(ServerIssue previousIssue, IssueTrackingResult result) {
     return result.unmatched().contains(previousIssue);
   }
 
-  private boolean isNotAlreadyMapped(DefaultIssue newIssue, IssueTrackingResult result) {
-    return !result.isMatched(newIssue);
+  private boolean isNotAlreadyMapped(BatchReport.Issue rawIssue, IssueTrackingResult result) {
+    return !result.isMatched(rawIssue);
+  }
+
+  private boolean isSameChecksum(BatchReport.Issue rawIssue, ServerIssue previousIssue) {
+    return Objects.equal(previousIssue.checksum(), checksum(rawIssue));
   }
 
-  private boolean isSameChecksum(DefaultIssue newIssue, ServerIssue previousIssue) {
-    return Objects.equal(previousIssue.checksum(), newIssue.checksum());
+  private boolean isSameLine(BatchReport.Issue rawIssue, ServerIssue previousIssue) {
+    return Objects.equal(previousIssue.line(), line(rawIssue));
   }
 
-  private boolean isSameLine(DefaultIssue newIssue, ServerIssue previousIssue) {
-    return Objects.equal(previousIssue.line(), newIssue.line());
+  private boolean isSameMessage(BatchReport.Issue rawIssue, ServerIssue previousIssue) {
+    return Objects.equal(message(rawIssue), previousIssue.message());
   }
 
-  private boolean isSameMessage(DefaultIssue newIssue, ServerIssue previousIssue) {
-    return Objects.equal(newIssue.message(), previousIssue.message());
+  @CheckForNull
+  private String message(BatchReport.Issue rawIssue) {
+    return rawIssue.hasMsg() ? rawIssue.getMsg() : null;
   }
 
-  private void mapIssue(DefaultIssue issue, @Nullable ServerIssue ref, IssueTrackingResult result) {
+  private void mapIssue(BatchReport.Issue rawIssue, @Nullable ServerIssue ref, IssueTrackingResult result) {
     if (ref != null) {
-      result.setMatch(issue, ref);
+      result.setMatch(rawIssue, ref);
     }
   }
 
index e04cdc240b433b6ac032e092035e59614d8427c0..e55ef46b03f4e8d8c84a3f49e194cfa68ccd2d8c 100644 (file)
@@ -22,22 +22,20 @@ package org.sonar.batch.issue.tracking;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.api.rule.RuleKey;
-
-import javax.annotation.Nullable;
-
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.protocol.output.BatchReport;
 
 class IssueTrackingResult {
   private final Map<String, ServerIssue> unmatchedByKey = new HashMap<>();
   private final Map<RuleKey, Map<String, ServerIssue>> unmatchedByRuleAndKey = new HashMap<>();
   private final Map<RuleKey, Map<Integer, Multimap<String, ServerIssue>>> unmatchedByRuleAndLineAndChecksum = new HashMap<>();
-  private final Map<DefaultIssue, ServerIssue> matched = Maps.newIdentityHashMap();
+  private final Map<BatchReport.Issue, ServerIssue> matched = Maps.newIdentityHashMap();
 
   Collection<ServerIssue> unmatched() {
     return unmatchedByKey.values();
@@ -64,15 +62,15 @@ class IssueTrackingResult {
     return unmatchedForRuleAndLine.get(checksumNotNull);
   }
 
-  Collection<DefaultIssue> matched() {
+  Collection<BatchReport.Issue> matched() {
     return matched.keySet();
   }
 
-  boolean isMatched(DefaultIssue issue) {
+  boolean isMatched(BatchReport.Issue issue) {
     return matched.containsKey(issue);
   }
 
-  ServerIssue matching(DefaultIssue issue) {
+  ServerIssue matching(BatchReport.Issue issue) {
     return matched.get(issue);
   }
 
@@ -99,7 +97,7 @@ class IssueTrackingResult {
     return line != null ? line : 0;
   }
 
-  void setMatch(DefaultIssue issue, ServerIssue matching) {
+  void setMatch(BatchReport.Issue issue, ServerIssue matching) {
     matched.put(issue, matching);
     RuleKey ruleKey = matching.ruleKey();
     unmatchedByRuleAndKey.get(ruleKey).remove(matching.key());
index 729e4d7cff1b4334e2a50a12d4de13835b562aa5..ee316017c2157bb3177f095ef56563dc0eacd9f1 100644 (file)
@@ -21,33 +21,40 @@ package org.sonar.batch.issue.tracking;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.CheckForNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.BatchSide;
 import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.BatchSide;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.rule.ActiveRule;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.issue.Issue;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.ResourceUtils;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.issue.IssueCache;
 import org.sonar.batch.protocol.input.ProjectRepositories;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.protocol.output.BatchReportReader;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.scan.filesystem.InputPathCache;
 import org.sonar.core.component.ComponentKeys;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.core.issue.IssueUpdater;
 import org.sonar.core.issue.workflow.IssueWorkflow;
-
-import javax.annotation.CheckForNull;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
+import org.sonar.core.util.CloseableIterator;
 
 @BatchSide
 public class LocalIssueTracking {
@@ -62,16 +69,18 @@ public class LocalIssueTracking {
   private final IssueChangeContext changeContext;
   private final ActiveRules activeRules;
   private final InputPathCache inputPathCache;
-  private final BatchComponentCache resourceCache;
+  private final BatchComponentCache componentCache;
   private final ServerIssueRepository serverIssueRepository;
   private final ProjectRepositories projectRepositories;
   private final AnalysisMode analysisMode;
+  private final ReportPublisher reportPublisher;
+  private final Date analysisDate;
 
   public LocalIssueTracking(BatchComponentCache resourceCache, IssueCache issueCache, IssueTracking tracking,
     ServerLineHashesLoader lastLineHashes, IssueWorkflow workflow, IssueUpdater updater,
     ActiveRules activeRules, InputPathCache inputPathCache, ServerIssueRepository serverIssueRepository,
-    ProjectRepositories projectRepositories, AnalysisMode analysisMode) {
-    this.resourceCache = resourceCache;
+    ProjectRepositories projectRepositories, AnalysisMode analysisMode, ReportPublisher reportPublisher) {
+    this.componentCache = resourceCache;
     this.issueCache = issueCache;
     this.tracking = tracking;
     this.lastLineHashes = lastLineHashes;
@@ -81,7 +90,9 @@ public class LocalIssueTracking {
     this.serverIssueRepository = serverIssueRepository;
     this.projectRepositories = projectRepositories;
     this.analysisMode = analysisMode;
-    this.changeContext = IssueChangeContext.createScan(((Project) resourceCache.getRoot().resource()).getAnalysisDate());
+    this.reportPublisher = reportPublisher;
+    this.analysisDate = ((Project) resourceCache.getRoot().resource()).getAnalysisDate();
+    this.changeContext = IssueChangeContext.createScan(analysisDate);
     this.activeRules = activeRules;
   }
 
@@ -92,49 +103,82 @@ public class LocalIssueTracking {
     }
 
     serverIssueRepository.load();
+    BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir());
 
-    for (BatchComponent component : resourceCache.all()) {
-      trackIssues(component);
+    for (BatchComponent component : componentCache.all()) {
+      trackIssues(reader, component);
     }
   }
 
-  public void trackIssues(BatchComponent component) {
-
-    Collection<DefaultIssue> issues = Lists.newArrayList();
-    for (Issue issue : issueCache.byComponent(component.resource().getEffectiveKey())) {
-      issues.add((DefaultIssue) issue);
-    }
-    issueCache.clear(component.resource().getEffectiveKey());
-    // issues = all the issues created by rule engines during this module scan and not excluded by filters
+  public void trackIssues(BatchReportReader reader, BatchComponent component) {
 
     if (analysisMode.isIncremental() && !component.isFile()) {
       // No need to report issues on project or directories in preview mode since it is likely to be wrong anyway
       return;
     }
 
+    // raw issues = all the issues created by rule engines during this module scan and not excluded by filters
+    Set<BatchReport.Issue> rawIssues = Sets.newIdentityHashSet();
+    try (CloseableIterator<BatchReport.Issue> it = reader.readComponentIssues(component.batchId())) {
+      while (it.hasNext()) {
+        rawIssues.add(it.next());
+      }
+    } catch (Exception e) {
+      throw new IllegalStateException("Can't read issues for " + component.key(), e);
+    }
+
     // all the issues that are not closed in db before starting this module scan, including manual issues
     Collection<ServerIssue> serverIssues = loadServerIssues(component);
 
     SourceHashHolder sourceHashHolder = loadSourceHashes(component);
 
-    IssueTrackingResult trackingResult = tracking.track(sourceHashHolder, serverIssues, issues);
+    IssueTrackingResult trackingResult = tracking.track(sourceHashHolder, serverIssues, rawIssues);
 
-    // unmatched = issues that have been resolved + issues on disabled/removed rules + manual issues
-    addUnmatched(trackingResult.unmatched(), sourceHashHolder, issues);
+    List<DefaultIssue> trackedIssues = Lists.newArrayList();
+    // unmatched from server = issues that have been resolved + issues on disabled/removed rules + manual issues
+    addUnmatchedFromServer(trackingResult.unmatched(), sourceHashHolder, trackedIssues);
 
-    mergeMatched(trackingResult);
+    mergeMatched(component, trackingResult, trackedIssues, rawIssues);
+
+    // Unmatched raw issues = new issues
+    addUnmatchedRawIssues(component, rawIssues, trackedIssues);
 
     if (ResourceUtils.isRootProject(component.resource())) {
       // issues that relate to deleted components
-      addIssuesOnDeletedComponents(issues);
+      addIssuesOnDeletedComponents(trackedIssues);
     }
 
-    for (DefaultIssue issue : issues) {
+    for (DefaultIssue issue : trackedIssues) {
       workflow.doAutomaticTransition(issue, changeContext);
       issueCache.put(issue);
     }
   }
 
+  private void addUnmatchedRawIssues(BatchComponent component, Set<org.sonar.batch.protocol.output.BatchReport.Issue> rawIssues, List<DefaultIssue> trackedIssues) {
+    for (BatchReport.Issue rawIssue : rawIssues) {
+
+      DefaultIssue tracked = toTracked(component, rawIssue);
+      tracked.setNew(true);
+      tracked.setCreationDate(analysisDate);
+
+      trackedIssues.add(tracked);
+    }
+  }
+
+  private DefaultIssue toTracked(BatchComponent component, BatchReport.Issue rawIssue) {
+    DefaultIssue trackedIssue = new org.sonar.core.issue.DefaultIssueBuilder()
+      .componentKey(component.key())
+      .projectKey("unused")
+      .ruleKey(RuleKey.of(rawIssue.getRuleRepository(), rawIssue.getRuleKey()))
+      .effortToFix(rawIssue.hasEffortToFix() ? rawIssue.getEffortToFix() : null)
+      .line(rawIssue.hasLine() ? rawIssue.getLine() : null)
+      .message(rawIssue.hasMsg() ? rawIssue.getMsg() : null)
+      .severity(rawIssue.getSeverity().name())
+      .build();
+    trackedIssue.setAttributes(rawIssue.hasAttributes() ? KeyValueFormat.parse(rawIssue.getAttributes()) : Collections.<String, String>emptyMap());
+    return trackedIssue;
+  }
+
   @CheckForNull
   private SourceHashHolder loadSourceHashes(BatchComponent component) {
     SourceHashHolder sourceHashHolder = null;
@@ -157,32 +201,36 @@ public class LocalIssueTracking {
   }
 
   @VisibleForTesting
-  protected void mergeMatched(IssueTrackingResult result) {
-    for (DefaultIssue issue : result.matched()) {
-      org.sonar.batch.protocol.input.BatchInput.ServerIssue ref = ((ServerIssueFromWs) result.matching(issue)).getDto();
+  protected void mergeMatched(BatchComponent component, IssueTrackingResult result, List<DefaultIssue> trackedIssues, Collection<BatchReport.Issue> rawIssues) {
+    for (BatchReport.Issue rawIssue : result.matched()) {
+      rawIssues.remove(rawIssue);
+      org.sonar.batch.protocol.input.BatchInput.ServerIssue ref = ((ServerIssueFromWs) result.matching(rawIssue)).getDto();
+
+      DefaultIssue tracked = toTracked(component, rawIssue);
 
       // invariant fields
-      issue.setKey(ref.getKey());
+      tracked.setKey(ref.getKey());
 
       // non-persisted fields
-      issue.setNew(false);
-      issue.setBeingClosed(false);
-      issue.setOnDisabledRule(false);
+      tracked.setNew(false);
+      tracked.setBeingClosed(false);
+      tracked.setOnDisabledRule(false);
 
       // fields to update with old values
-      issue.setResolution(ref.hasResolution() ? ref.getResolution() : null);
-      issue.setStatus(ref.getStatus());
-      issue.setAssignee(ref.hasAssigneeLogin() ? ref.getAssigneeLogin() : null);
-      issue.setCreationDate(new Date(ref.getCreationDate()));
+      tracked.setResolution(ref.hasResolution() ? ref.getResolution() : null);
+      tracked.setStatus(ref.getStatus());
+      tracked.setAssignee(ref.hasAssigneeLogin() ? ref.getAssigneeLogin() : null);
+      tracked.setCreationDate(new Date(ref.getCreationDate()));
 
       if (ref.getManualSeverity()) {
         // Severity overriden by user
-        issue.setSeverity(ref.getSeverity().name());
+        tracked.setSeverity(ref.getSeverity().name());
       }
+      trackedIssues.add(tracked);
     }
   }
 
-  private void addUnmatched(Collection<ServerIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) {
+  private void addUnmatchedFromServer(Collection<ServerIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) {
     for (ServerIssue unmatchedIssue : unmatchedIssues) {
       org.sonar.batch.protocol.input.BatchInput.ServerIssue unmatchedPreviousIssue = ((ServerIssueFromWs) unmatchedIssue).getDto();
       DefaultIssue unmatched = toUnmatchedIssue(unmatchedPreviousIssue);
index 10341e8dcbe3baa420afd931d394d289c991f5a7..1b02c9090d54f419cf8575ec8c178fabc27fe9e3 100644 (file)
@@ -36,8 +36,10 @@ import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.fs.InputDir;
 import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.batch.fs.TextPointer;
 import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.duplication.Duplication;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
@@ -121,10 +123,27 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
     }
   }
 
-  public List<Issue> issues() {
+  public List<Issue> trackedIssues() {
     return issues;
   }
 
+  public List<BatchReport.Issue> issuesFor(InputPath inputPath) {
+    List<BatchReport.Issue> result = Lists.newArrayList();
+    int ref = reportComponents.get(key(inputPath)).getRef();
+    try (CloseableIterator<BatchReport.Issue> it = reader.readComponentIssues(ref)) {
+      while (it.hasNext()) {
+        result.add(it.next());
+      }
+    } catch (Exception e) {
+      throw new IllegalStateException("Can't read issues for " + inputPath.absolutePath(), e);
+    }
+    return result;
+  }
+
+  private String key(InputPath inputPath) {
+    return inputPath instanceof InputFile ? ((DefaultInputFile) inputPath).key() : ((DefaultInputDir) inputPath).key();
+  }
+
   public Collection<InputFile> inputFiles() {
     return inputFiles.values();
   }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java
deleted file mode 100644 (file)
index 24e1a95..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.report;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-import org.sonar.api.utils.KeyValueFormat;
-import org.sonar.batch.index.BatchComponent;
-import org.sonar.batch.index.BatchComponentCache;
-import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.protocol.Constants;
-import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.batch.protocol.output.BatchReportWriter;
-import org.sonar.core.issue.DefaultIssue;
-
-public class IssuesPublisher implements ReportPublisherStep {
-
-  private final BatchComponentCache componentCache;
-  private final IssueCache issueCache;
-
-  public IssuesPublisher(BatchComponentCache componentCache, IssueCache issueCache) {
-    this.componentCache = componentCache;
-    this.issueCache = issueCache;
-
-  }
-
-  @Override
-  public void publish(BatchReportWriter writer) {
-    for (BatchComponent resource : componentCache.all()) {
-      String componentKey = resource.resource().getEffectiveKey();
-      Iterable<DefaultIssue> issues = issueCache.byComponent(componentKey);
-      writer.writeComponentIssues(resource.batchId(), Iterables.transform(issues, new Function<DefaultIssue, BatchReport.Issue>() {
-        private BatchReport.Issue.Builder builder = BatchReport.Issue.newBuilder();
-
-        @Override
-        public BatchReport.Issue apply(DefaultIssue input) {
-          return toReportIssue(builder, input);
-        }
-      }));
-    }
-  }
-
-  private BatchReport.Issue toReportIssue(BatchReport.Issue.Builder builder, DefaultIssue issue) {
-    builder.clear();
-    // non-null fields
-    builder.setSeverity(Constants.Severity.valueOf(issue.severity()));
-    builder.setRuleRepository(issue.ruleKey().repository());
-    builder.setRuleKey(issue.ruleKey().rule());
-    builder.setAttributes(KeyValueFormat.format(issue.attributes()));
-
-    // nullable fields
-    Integer line = issue.line();
-    if (line != null) {
-      builder.setLine(line);
-    }
-    String message = issue.message();
-    if (message != null) {
-      builder.setMsg(message);
-    }
-    Double effortToFix = issue.effortToFix();
-    if (effortToFix != null) {
-      builder.setEffortToFix(effortToFix);
-    }
-    return builder.build();
-  }
-
-}
index ea086f5a068a8d52f37e9ad644f5be9c2c36ec65..28367ee042c3ed29ef4faa5dc6145c04368fb6b4 100644 (file)
@@ -59,7 +59,6 @@ import org.sonar.batch.report.ActiveRulesPublisher;
 import org.sonar.batch.report.ComponentsPublisher;
 import org.sonar.batch.report.CoveragePublisher;
 import org.sonar.batch.report.DuplicationsPublisher;
-import org.sonar.batch.report.IssuesPublisher;
 import org.sonar.batch.report.MeasuresPublisher;
 import org.sonar.batch.report.MetadataPublisher;
 import org.sonar.batch.report.ReportPublisher;
@@ -189,7 +188,6 @@ public class ProjectScanContainer extends ComponentContainer {
       MetadataPublisher.class,
       ActiveRulesPublisher.class,
       ComponentsPublisher.class,
-      IssuesPublisher.class,
       MeasuresPublisher.class,
       DuplicationsPublisher.class,
       CoveragePublisher.class,
index 1ab8482079a659938b1730dc4bcf40931175ea6f..adacbb995466d7305a9e6abb8f9bd7610c76c79f 100644 (file)
@@ -30,12 +30,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.batch.fs.TextRange;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.measure.MetricFinder;
 import org.sonar.api.batch.rule.ActiveRules;
-import org.sonar.api.batch.rule.Severity;
 import org.sonar.api.batch.sensor.coverage.CoverageType;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
 import org.sonar.api.batch.sensor.duplication.Duplication;
@@ -52,7 +50,6 @@ import org.sonar.api.measures.Metric;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
-import org.sonar.api.rule.RuleKey;
 import org.sonar.api.source.Symbol;
 import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.api.utils.SonarException;
@@ -67,8 +64,6 @@ import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.scan.measure.MeasureCache;
 import org.sonar.batch.sensor.coverage.CoverageExclusions;
 import org.sonar.batch.source.DefaultSymbol;
-import org.sonar.core.component.ComponentKeys;
-import org.sonar.core.issue.DefaultIssue;
 
 public class DefaultSensorStorage implements SensorStorage {
 
@@ -194,28 +189,7 @@ public class DefaultSensorStorage implements SensorStorage {
 
   @Override
   public void store(Issue issue) {
-    String componentKey;
-    InputPath inputPath = issue.locations().get(0).inputPath();
-    if (inputPath != null) {
-      componentKey = ComponentKeys.createEffectiveKey(project.getKey(), inputPath);
-    } else {
-      componentKey = project.getKey();
-    }
-    moduleIssues.initAndAddIssue(toDefaultIssue(project.getKey(), componentKey, issue));
-  }
-
-  public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) {
-    Severity overriddenSeverity = issue.overriddenSeverity();
-    TextRange textRange = issue.locations().get(0).textRange();
-    return new org.sonar.core.issue.DefaultIssueBuilder()
-      .componentKey(componentKey)
-      .projectKey(projectKey)
-      .ruleKey(RuleKey.of(issue.ruleKey().repository(), issue.ruleKey().rule()))
-      .effortToFix(issue.effortToFix())
-      .line(textRange != null ? textRange.start().line() : null)
-      .message(issue.locations().get(0).message())
-      .severity(overriddenSeverity != null ? overriddenSeverity.name() : null)
-      .build();
+    moduleIssues.initAndAddIssue(issue);
   }
 
   private File getFile(InputFile file) {
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java
deleted file mode 100644 (file)
index 5df8429..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.issue;
-
-import java.util.Arrays;
-import java.util.List;
-import org.junit.Test;
-import org.sonar.api.batch.sensor.SensorContext;
-import org.sonar.api.issue.Issue;
-import org.sonar.batch.index.BatchComponent;
-import org.sonar.core.issue.DefaultIssue;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class DefaultIssuableTest {
-
-  IssueCache cache = mock(IssueCache.class);
-  BatchComponent component = mock(BatchComponent.class);
-
-  @Test
-  public void test_unresolved_issues() throws Exception {
-    when(component.key()).thenReturn("struts:org.apache.Action");
-    DefaultIssue resolved = new DefaultIssue().setResolution(Issue.RESOLUTION_FALSE_POSITIVE);
-    DefaultIssue unresolved = new DefaultIssue();
-    when(cache.byComponent("struts:org.apache.Action")).thenReturn(Arrays.asList(resolved, unresolved));
-
-    DefaultIssuable perspective = new DefaultIssuable(component, cache, mock(SensorContext.class));
-
-    List<Issue> issues = perspective.issues();
-    assertThat(issues).containsOnly(unresolved);
-  }
-
-  @Test
-  public void test_resolved_issues() throws Exception {
-    when(component.key()).thenReturn("struts:org.apache.Action");
-    DefaultIssue resolved = new DefaultIssue().setResolution(Issue.RESOLUTION_FALSE_POSITIVE);
-    DefaultIssue unresolved = new DefaultIssue();
-    when(cache.byComponent("struts:org.apache.Action")).thenReturn(Arrays.asList(resolved, unresolved));
-
-    DefaultIssuable perspective = new DefaultIssuable(component, cache, mock(SensorContext.class));
-
-    List<Issue> issues = perspective.resolvedIssues();
-    assertThat(issues).containsOnly(resolved);
-  }
-}
index ddabedba240914a6fb07d77def49b675c1693c51..5525d4ea04932d7c0a0c8d5ab42514942083a387 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.batch.issue;
 
 import org.junit.Test;
-import org.mockito.Mockito;
 import org.sonar.api.issue.Issuable;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
@@ -34,12 +33,11 @@ import static org.mockito.Mockito.mock;
 public class IssuableFactoryTest {
 
   ModuleIssues moduleIssues = mock(ModuleIssues.class);
-  IssueCache cache = mock(IssueCache.class, Mockito.RETURNS_MOCKS);
   DefaultProjectTree projectTree = mock(DefaultProjectTree.class);
 
   @Test
   public void file_should_be_issuable() {
-    IssuableFactory factory = new IssuableFactory(cache, mock(DefaultSensorContext.class));
+    IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class));
     BatchComponent component = new BatchComponent(1, File.create("foo/bar.c").setEffectiveKey("foo/bar.c"), null);
     Issuable issuable = factory.loadPerspective(Issuable.class, component);
 
@@ -49,7 +47,7 @@ public class IssuableFactoryTest {
 
   @Test
   public void project_should_be_issuable() {
-    IssuableFactory factory = new IssuableFactory(cache, mock(DefaultSensorContext.class));
+    IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class));
     BatchComponent component = new BatchComponent(1, new Project("Foo").setEffectiveKey("foo"), null);
     Issuable issuable = factory.loadPerspective(Issuable.class, component);
 
index a602f1f654939732642d0f3c8ad47be71c5e6587..fb9cf921e2f4b4c007776feed4ede96355b9333c 100644 (file)
  */
 package org.sonar.batch.issue;
 
-import java.util.Calendar;
+import java.io.StringReader;
 import java.util.Date;
-import org.apache.commons.lang.time.DateUtils;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
 import org.sonar.api.batch.rule.internal.RulesBuilder;
+import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
+import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
+import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.MessageException;
-import org.sonar.core.issue.DefaultIssue;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.report.ReportPublisher;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
@@ -48,11 +59,7 @@ public class ModuleIssuesTest {
   static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle");
   static final String SQUID_RULE_NAME = "Avoid Cycle";
 
-  @Mock
-  IssueCache cache;
-
-  @Mock
-  Project project;
+  Project project = new Project("foo").setAnalysisDate(new Date());
 
   @Mock
   IssueFilters filters;
@@ -62,18 +69,21 @@ public class ModuleIssuesTest {
 
   ModuleIssues moduleIssues;
 
+  BatchComponentCache componentCache = new BatchComponentCache();
+  InputFile file = new DefaultInputFile("foo", "src/Foo.php").initMetadata(new FileMetadata().readMetadata(new StringReader("Foo\nBar\nBiz\n")));
+  ReportPublisher reportPublisher = mock(ReportPublisher.class, RETURNS_DEEP_STUBS);
+
   @Before
-  public void setUp() {
-    when(project.getAnalysisDate()).thenReturn(new Date());
-    when(project.getEffectiveKey()).thenReturn("org.apache:struts-core");
-    when(project.getRoot()).thenReturn(project);
+  public void prepare() {
+    componentCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputPath(file);
   }
 
   @Test
   public void fail_on_unknown_rule() {
     initModuleIssues();
-    DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY);
-
+    DefaultIssue issue = new DefaultIssue()
+      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .forRule(SQUID_RULE_KEY);
     try {
       moduleIssues.initAndAddIssue(issue);
       fail();
@@ -81,15 +91,16 @@ public class ModuleIssuesTest {
       assertThat(e).isInstanceOf(MessageException.class);
     }
 
-    verifyZeroInteractions(cache);
+    verifyZeroInteractions(reportPublisher);
   }
 
   @Test
   public void fail_if_rule_has_no_name_and_issue_has_no_message() {
     ruleBuilder.add(SQUID_RULE_KEY).setInternalKey(SQUID_RULE_KEY.rule());
     initModuleIssues();
-    DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY).setMessage("");
-
+    DefaultIssue issue = new DefaultIssue()
+      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message(""))
+      .forRule(SQUID_RULE_KEY);
     try {
       moduleIssues.initAndAddIssue(issue);
       fail();
@@ -97,19 +108,20 @@ public class ModuleIssuesTest {
       assertThat(e).isInstanceOf(MessageException.class);
     }
 
-    verifyZeroInteractions(cache);
+    verifyZeroInteractions(reportPublisher);
   }
 
   @Test
   public void ignore_null_active_rule() {
     ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
     initModuleIssues();
-
-    DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY);
+    DefaultIssue issue = new DefaultIssue()
+      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .forRule(SQUID_RULE_KEY);
     boolean added = moduleIssues.initAndAddIssue(issue);
 
     assertThat(added).isFalse();
-    verifyZeroInteractions(cache);
+    verifyZeroInteractions(reportPublisher);
   }
 
   @Test
@@ -118,11 +130,13 @@ public class ModuleIssuesTest {
     activeRulesBuilder.create(SQUID_RULE_KEY).activate();
     initModuleIssues();
 
-    DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY);
+    DefaultIssue issue = new DefaultIssue()
+      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .forRule(SQUID_RULE_KEY);
     boolean added = moduleIssues.initAndAddIssue(issue);
 
     assertThat(added).isFalse();
-    verifyZeroInteractions(cache);
+    verifyZeroInteractions(reportPublisher);
   }
 
   @Test
@@ -131,22 +145,19 @@ public class ModuleIssuesTest {
     activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
     initModuleIssues();
 
-    Date analysisDate = new Date();
-    when(project.getAnalysisDate()).thenReturn(analysisDate);
-
     DefaultIssue issue = new DefaultIssue()
-      .setKey("ABCDE")
-      .setRuleKey(SQUID_RULE_KEY)
-      .setSeverity(Severity.CRITICAL);
-    when(filters.accept(issue)).thenReturn(true);
+      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .forRule(SQUID_RULE_KEY)
+      .overrideSeverity(org.sonar.api.batch.rule.Severity.CRITICAL);
+
+    when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true);
 
     boolean added = moduleIssues.initAndAddIssue(issue);
 
     assertThat(added).isTrue();
-    ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
-    verify(cache).put(argument.capture());
-    assertThat(argument.getValue().severity()).isEqualTo(Severity.CRITICAL);
-    assertThat(argument.getValue().creationDate()).isEqualTo(DateUtils.truncate(analysisDate, Calendar.SECOND));
+    ArgumentCaptor<BatchReport.Issue> argument = ArgumentCaptor.forClass(BatchReport.Issue.class);
+    verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture());
+    assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.batch.protocol.Constants.Severity.CRITICAL);
   }
 
   @Test
@@ -155,17 +166,15 @@ public class ModuleIssuesTest {
     activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
     initModuleIssues();
 
-    Date analysisDate = new Date();
-    when(project.getAnalysisDate()).thenReturn(analysisDate);
-
-    DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY).setSeverity(null);
-    when(filters.accept(issue)).thenReturn(true);
+    DefaultIssue issue = new DefaultIssue()
+      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+      .forRule(SQUID_RULE_KEY);
+    when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true);
     moduleIssues.initAndAddIssue(issue);
 
-    ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
-    verify(cache).put(argument.capture());
-    assertThat(argument.getValue().severity()).isEqualTo(Severity.INFO);
-    assertThat(argument.getValue().creationDate()).isEqualTo(DateUtils.truncate(analysisDate, Calendar.SECOND));
+    ArgumentCaptor<BatchReport.Issue> argument = ArgumentCaptor.forClass(BatchReport.Issue.class);
+    verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture());
+    assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.batch.protocol.Constants.Severity.INFO);
   }
 
   @Test
@@ -174,22 +183,17 @@ public class ModuleIssuesTest {
     activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).setName(SQUID_RULE_NAME).activate();
     initModuleIssues();
 
-    Date analysisDate = new Date();
-    when(project.getAnalysisDate()).thenReturn(analysisDate);
-
     DefaultIssue issue = new DefaultIssue()
-      .setKey("ABCDE")
-      .setRuleKey(SQUID_RULE_KEY)
-      .setSeverity(Severity.CRITICAL)
-      .setMessage("");
-    when(filters.accept(issue)).thenReturn(true);
+      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message(""))
+      .forRule(SQUID_RULE_KEY);
+    when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true);
 
     boolean added = moduleIssues.initAndAddIssue(issue);
 
     assertThat(added).isTrue();
-    ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
-    verify(cache).put(argument.capture());
-    assertThat(argument.getValue().message()).isEqualTo("Avoid Cycle");
+    ArgumentCaptor<BatchReport.Issue> argument = ArgumentCaptor.forClass(BatchReport.Issue.class);
+    verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture());
+    assertThat(argument.getValue().getMsg()).isEqualTo("Avoid Cycle");
   }
 
   @Test
@@ -199,23 +203,22 @@ public class ModuleIssuesTest {
     initModuleIssues();
 
     DefaultIssue issue = new DefaultIssue()
-      .setKey("ABCDE")
-      .setRuleKey(SQUID_RULE_KEY)
-      .setSeverity(Severity.CRITICAL);
+      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message(""))
+      .forRule(SQUID_RULE_KEY);
 
-    when(filters.accept(issue)).thenReturn(false);
+    when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(false);
 
     boolean added = moduleIssues.initAndAddIssue(issue);
 
     assertThat(added).isFalse();
-    verifyZeroInteractions(cache);
+    verifyZeroInteractions(reportPublisher);
   }
 
   /**
    * Every rules and active rules has to be added in builders before creating ModuleIssues
    */
   private void initModuleIssues() {
-    moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), cache, project, filters);
+    moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), project, filters, reportPublisher, componentCache);
   }
 
 }
index f73d1c60f460dc2c07040bc041666798288b3b9c..660b861bece6bd8c7dd193808000b33bc79e3550 100644 (file)
@@ -19,8 +19,6 @@
  */
 package org.sonar.batch.mediumtest.deprecated;
 
-import org.sonar.xoo.rule.XooRulesDefinition;
-
 import com.google.common.collect.ImmutableMap;
 import java.io.File;
 import java.io.IOException;
@@ -33,6 +31,7 @@ import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.input.ActiveRule;
 import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.groups.Tuple.tuple;
@@ -84,13 +83,16 @@ public class DeprecatedApiMediumTest {
         .build())
       .start();
 
-    assertThat(result.issues()).extracting("componentKey", "message", "line").containsOnly(
-      tuple("com.foo.project:src/sample.xoo", "Issue created using deprecated API", null),
-      tuple("com.foo.project:src/sample.xoo", "Issue created using deprecated API", 1),
-      tuple("com.foo.project:src/package/sample.xoo", "Issue created using deprecated API", null),
-      tuple("com.foo.project:src/package/sample.xoo", "Issue created using deprecated API", 1),
-      tuple("com.foo.project:src", "Issue created using deprecated API", null),
-      tuple("com.foo.project:src/package", "Issue created using deprecated API", null));
+    assertThat(result.issuesFor(result.inputFile("src/sample.xoo"))).extracting("msg", "line").containsOnly(
+      tuple("Issue created using deprecated API", 0),
+      tuple("Issue created using deprecated API", 1));
+    assertThat(result.issuesFor(result.inputFile("src/package/sample.xoo"))).extracting("msg", "line").containsOnly(
+      tuple("Issue created using deprecated API", 0),
+      tuple("Issue created using deprecated API", 1));
+    assertThat(result.issuesFor(result.inputDir("src"))).extracting("msg", "line").containsOnly(
+      tuple("Issue created using deprecated API", 0));
+    assertThat(result.issuesFor(result.inputDir("src/package"))).extracting("msg", "line").containsOnly(
+      tuple("Issue created using deprecated API", 0));
 
   }
 
index 2beb8f133b5a323ec7eae46a52536e87e00dcee4..42ef0b874330012e579461dce70224d8fffeaa9b 100644 (file)
  */
 package org.sonar.batch.mediumtest.fs;
 
-import org.sonar.xoo.rule.XooRulesDefinition;
-
 import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
-import org.sonar.api.CoreProperties;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.output.BatchReport.Issue;
 import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Date;
+import org.sonar.xoo.rule.XooRulesDefinition;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -48,7 +47,6 @@ public class ProjectBuilderMediumTest {
     .registerPlugin("xoo", new XooPlugin())
     .addRules(new XooRulesDefinition())
     .addDefaultQProfile("xoo", "Sonar Way")
-    .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PREVIEW))
     .setPreviousAnalysisDate(new Date())
     .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo"))
     .build();
@@ -88,19 +86,19 @@ public class ProjectBuilderMediumTest {
         .put("sonar.xoo.enableProjectBuilder", "true")
         .build())
       .start();
-
-    assertThat(result.issues()).hasSize(10);
+    List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+    assertThat(issues).hasSize(10);
 
     boolean foundIssueAtLine1 = false;
-    for (org.sonar.api.issue.Issue issue : result.issues()) {
-      if (issue.line() == 1) {
+    for (Issue issue : issues) {
+      if (issue.getLine() == 1) {
         foundIssueAtLine1 = true;
-        assertThat(issue.componentKey()).isEqualTo("com.foo.project:module1:src/sample.xoo");
-        assertThat(issue.message()).isEqualTo("This issue is generated on each line");
-        assertThat(issue.effortToFix()).isNull();
+        assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line");
+        assertThat(issue.hasEffortToFix()).isFalse();
       }
     }
     assertThat(foundIssueAtLine1).isTrue();
+
   }
 
   @Test
@@ -130,15 +128,15 @@ public class ProjectBuilderMediumTest {
         .build())
       .start();
 
-    assertThat(result.issues()).hasSize(10);
+    List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+    assertThat(issues).hasSize(10);
 
     boolean foundIssueAtLine1 = false;
-    for (org.sonar.api.issue.Issue issue : result.issues()) {
-      if (issue.line() == 1) {
+    for (Issue issue : issues) {
+      if (issue.getLine() == 1) {
         foundIssueAtLine1 = true;
-        assertThat(issue.componentKey()).isEqualTo("com.foo.project:module1:my-branch:src/sample.xoo");
-        assertThat(issue.message()).isEqualTo("This issue is generated on each line");
-        assertThat(issue.effortToFix()).isNull();
+        assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line");
+        assertThat(issue.hasEffortToFix()).isFalse();
       }
     }
     assertThat(foundIssueAtLine1).isTrue();
index f27f72c57694c286bf0ed2a0ff291df08994ac4d..cfbd881e224d907e746f3e165be9259d34625cf4 100644 (file)
  */
 package org.sonar.batch.mediumtest.fs;
 
-import org.sonar.xoo.rule.XooRulesDefinition;
-
 import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
@@ -32,11 +34,9 @@ import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.Benchmark;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.output.BatchReport.Issue;
 import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
+import org.sonar.xoo.rule.XooRulesDefinition;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -92,7 +92,8 @@ public class RandomFsAccessMediumTest {
         .build())
       .start();
 
-    assertThat(result.issues()).hasSize(ISSUE_COUNT);
+    List<Issue> issues = result.issuesFor(result.inputFile("src/sample1.xoo"));
+    assertThat(issues).hasSize(10);
     bench.expectLessThanOrEqualTo("Time to create " + ISSUE_COUNT + " issues on random files using FileSystem query", System.currentTimeMillis() - start, 2000);
   }
 
@@ -121,7 +122,8 @@ public class RandomFsAccessMediumTest {
         .build())
       .start();
 
-    assertThat(result.issues()).hasSize(ISSUE_COUNT);
+    List<Issue> issues = result.issuesFor(result.inputFile("src/sample1.xoo"));
+    assertThat(issues).hasSize(10);
 
   }
 
index d5bd3b44c9aedd1ec637cddc8e2f2c44670cb42b..fe31dbe7d252447d2c6bdab2f1985d592b44cc51 100644 (file)
  */
 package org.sonar.batch.mediumtest.issues;
 
-import org.sonar.batch.protocol.input.Rule;
-
-import org.sonar.xoo.rule.XooRulesDefinition;
 import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
@@ -31,10 +31,10 @@ import org.junit.rules.TemporaryFolder;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.input.Rule;
+import org.sonar.batch.protocol.output.BatchReport.Issue;
 import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
+import org.sonar.xoo.rule.XooRulesDefinition;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -85,20 +85,19 @@ public class ChecksMediumTest {
         .build())
       .start();
 
-    assertThat(result.issues()).hasSize(2);
+    List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+    assertThat(issues).hasSize(2);
 
     boolean foundIssueAtLine1 = false;
     boolean foundIssueAtLine2 = false;
-    for (org.sonar.api.issue.Issue issue : result.issues()) {
-      if (issue.line() == 1) {
+    for (Issue issue : issues) {
+      if (issue.getLine() == 1) {
         foundIssueAtLine1 = true;
-        assertThat(issue.componentKey()).isEqualTo("com.foo.project:src/sample.xoo");
-        assertThat(issue.message()).isEqualTo("A template rule");
+        assertThat(issue.getMsg()).isEqualTo("A template rule");
       }
-      if (issue.line() == 2) {
+      if (issue.getLine() == 2) {
         foundIssueAtLine2 = true;
-        assertThat(issue.componentKey()).isEqualTo("com.foo.project:src/sample.xoo");
-        assertThat(issue.message()).isEqualTo("Another template rule");
+        assertThat(issue.getMsg()).isEqualTo("Another template rule");
       }
     }
     assertThat(foundIssueAtLine1).isTrue();
index 1399a135f72350a321c3e57b30f159faa2cbc91a..6979b4e6e2cfc14a67fe95c22df552c7c550751f 100644 (file)
  */
 package org.sonar.batch.mediumtest.issues;
 
-import org.sonar.api.issue.Issue;
-import org.sonar.batch.bootstrapper.IssueListener;
-import org.sonar.xoo.rule.XooRulesDefinition;
 import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.rule.Severity;
+import org.sonar.batch.bootstrapper.IssueListener;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.output.BatchReport.Issue;
 import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
+import org.sonar.xoo.rule.XooRulesDefinition;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -52,44 +50,15 @@ public class IssuesMediumTest {
     .addRules(new XooRulesDefinition())
     .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo"))
     .build();
-  
-  public BatchMediumTester testerPreview = BatchMediumTester.builder()
-    .registerPlugin("xoo", new XooPlugin())
-    .addDefaultQProfile("xoo", "Sonar Way")
-    .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "preview"))
-    .addRules(new XooRulesDefinition())
-    .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo"))
-    .build();
 
   @Before
   public void prepare() {
     tester.start();
-    testerPreview.start();
   }
 
   @After
   public void stop() {
     tester.stop();
-    testerPreview.stop();
-  }
-
-  @Test
-  public void testIssueCallback() throws Exception {
-    File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
-    File tmpDir = temp.newFolder();
-    FileUtils.copyDirectory(projectDir, tmpDir);
-    IssueRecorder issueListener = new IssueRecorder();
-
-    TaskResult result = testerPreview
-      .newScanTask(new File(tmpDir, "sonar-project.properties"))
-      .setIssueListener(issueListener)
-      .property("sonar.analysis.mode", "preview")
-      .start();
-
-    assertThat(result.issues()).hasSize(14);
-    assertThat(issueListener.issueList).hasSize(14);
-
-    assertThat(result.issues()).containsExactlyElementsOf(issueListener.issueList);
   }
 
   @Test
@@ -104,7 +73,7 @@ public class IssuesMediumTest {
       .setIssueListener(issueListener)
       .start();
 
-    assertThat(result.issues()).hasSize(14);
+    assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(8);
     assertThat(issueListener.issueList).hasSize(0);
   }
 
@@ -118,7 +87,8 @@ public class IssuesMediumTest {
       .newScanTask(new File(tmpDir, "sonar-project.properties"))
       .start();
 
-    assertThat(result.issues()).hasSize(14);
+    List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+    assertThat(issues).hasSize(8 /* lines */);
   }
 
   @Test
@@ -132,7 +102,8 @@ public class IssuesMediumTest {
       .property("sonar.xoo.internalKey", "OneIssuePerLine.internal")
       .start();
 
-    assertThat(result.issues()).hasSize(14 /* 8 + 6 lines */+ 2 /* 2 files */);
+    List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+    assertThat(issues).hasSize(8 /* lines */ + 1 /* file */);
   }
 
   @Test
@@ -146,7 +117,8 @@ public class IssuesMediumTest {
       .property("sonar.oneIssuePerLine.forceSeverity", "CRITICAL")
       .start();
 
-    assertThat(result.issues().iterator().next().severity()).isEqualTo(Severity.CRITICAL.name());
+    List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+    assertThat(issues.get(0).getSeverity()).isEqualTo(org.sonar.batch.protocol.Constants.Severity.CRITICAL);
   }
 
   @Test
@@ -161,7 +133,8 @@ public class IssuesMediumTest {
       .property("sonar.issue.ignore.allfile.1.fileRegexp", "object")
       .start();
 
-    assertThat(result.issues()).hasSize(8);
+    assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(8 /* lines */);
+    assertThat(result.issuesFor(result.inputFile("xources/hello/helloscala.xoo"))).isEmpty();
   }
 
   @Test
@@ -186,25 +159,25 @@ public class IssuesMediumTest {
         .build())
       .start();
 
-    assertThat(result.issues()).hasSize(10);
+    List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+    assertThat(issues).hasSize(10);
 
     boolean foundIssueAtLine1 = false;
-    for (org.sonar.api.issue.Issue issue : result.issues()) {
-      if (issue.line() == 1) {
+    for (Issue issue : issues) {
+      if (issue.getLine() == 1) {
         foundIssueAtLine1 = true;
-        assertThat(issue.componentKey()).isEqualTo("com.foo.project:src/sample.xoo");
-        assertThat(issue.message()).isEqualTo("This issue is generated on each line");
-        assertThat(issue.effortToFix()).isNull();
+        assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line");
+        assertThat(issue.hasEffortToFix()).isFalse();
       }
     }
     assertThat(foundIssueAtLine1).isTrue();
   }
 
   private class IssueRecorder implements IssueListener {
-    List<Issue> issueList = new LinkedList<>();
+    List<org.sonar.api.issue.Issue> issueList = new LinkedList<>();
 
     @Override
-    public void handle(Issue issue) {
+    public void handle(org.sonar.api.issue.Issue issue) {
       issueList.add(issue);
     }
   }
index 2d389a5d8cf8f44116cea790b72b84661e0a2bba..d181918a16b2cd05d636a3f94535a5aadc76ac1b 100644 (file)
@@ -19,9 +19,9 @@
  */
 package org.sonar.batch.mediumtest.issues;
 
-import org.sonar.xoo.rule.XooRulesDefinition;
-
 import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
@@ -31,9 +31,7 @@ import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.input.ActiveRule;
 import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
+import org.sonar.xoo.rule.XooRulesDefinition;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -84,9 +82,7 @@ public class IssuesOnDirMediumTest {
         .build())
       .start();
 
-    assertThat(result.issues()).hasSize(2);
-    assertThat(result.issues().iterator().next().componentKey()).isEqualTo("com.foo.project:src");
-
+    assertThat(result.issuesFor(result.inputDir("src"))).hasSize(2);
   }
 
 }
index 02b61ca0cdbaa90fe095ee4efc4fb2fadff0fb8d..a9c1317b014e402f06fffa4486531aabd3b98bdd 100644 (file)
  */
 package org.sonar.batch.mediumtest.issues;
 
-import org.sonar.xoo.rule.XooRulesDefinition;
-
-import org.sonar.batch.protocol.input.Rule;
-
 import java.io.File;
-
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
@@ -33,7 +28,10 @@ import org.junit.rules.TemporaryFolder;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.input.Rule;
 import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class MultilineIssuesMediumTest {
@@ -69,7 +67,7 @@ public class MultilineIssuesMediumTest {
       .newScanTask(new File(tmpDir, "sonar-project.properties"))
       .start();
 
-    assertThat(result.issues()).hasSize(1);
+    assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(1);
 
   }
 
index 3f7be697caf02e021441e67ca1a9dbc199ff8dc6..37cb1fd994dd2ee05b53373d26875c7e5edd4113 100644 (file)
@@ -75,7 +75,7 @@ public class EmptyFileTest {
       .property("sonar.xoo.internalKey", "my/internal/key")
       .start();
 
-    assertThat(result.issues()).hasSize(11);
+    assertThat(result.trackedIssues()).hasSize(11);
   }
 
 }
index 86697dcea42ede7664c92d38faf9f3523da321e9..6d1685a3d1fd3d37d6fe963c289fe0e3b99cb166 100644 (file)
  */
 package org.sonar.batch.mediumtest.preview;
 
-import org.sonar.xoo.rule.XooRulesDefinition;
-
 import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
@@ -29,20 +31,17 @@ import org.junit.Before;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.CoreProperties;
-import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.Constants.Severity;
 import org.sonar.batch.protocol.input.ActiveRule;
 import org.sonar.batch.protocol.input.FileData;
 import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import org.sonar.xoo.rule.XooRulesDefinition;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.groups.Tuple.tuple;
 
 public class IncrementalModeMediumTest {
 
@@ -143,21 +142,14 @@ public class IncrementalModeMediumTest {
         .build())
       .start();
 
-    int newIssues = 0;
-    int openIssues = 0;
-    int resolvedIssue = 0;
-    for (Issue issue : result.issues()) {
-      if (issue.isNew()) {
-        newIssues++;
-      } else if (issue.resolution() != null) {
-        resolvedIssue++;
-      } else {
-        openIssues++;
-      }
-    }
-    assertThat(newIssues).isEqualTo(4);
-    assertThat(openIssues).isEqualTo(2);
-    assertThat(resolvedIssue).isEqualTo(1);
+    assertThat(result.trackedIssues()).extracting("ruleKey", "line", "message", "status", "resolution", "new").containsOnly(
+      tuple(RuleKey.of("xoo", "OneIssuePerLine"), 1, "This issue is generated on each line", "OPEN", null, false),
+      tuple(RuleKey.of("xoo", "OneIssuePerLine"), 2, "This issue is generated on each line", "OPEN", null, true),
+      tuple(RuleKey.of("xoo", "OneIssuePerLine"), 3, "This issue is generated on each line", "OPEN", null, true),
+      tuple(RuleKey.of("xoo", "OneIssuePerLine"), 4, "This issue is generated on each line", "OPEN", null, true),
+      tuple(RuleKey.of("xoo", "OneIssuePerLine"), 5, "This issue is generated on each line", "OPEN", null, true),
+      tuple(RuleKey.of("manual", "MyManualIssue"), 4, null, "OPEN", null, false),
+      tuple(RuleKey.of("xoo", "OneIssuePerFile"), null, "An issue that is no more detected", "CLOSED", "REMOVED", false));
   }
 
 }
index e09631ac9fd6eb74d3929908c45a7927e7c91b70..3ba49c04107d3f117e059b8be8e39c10bf6fe79b 100644 (file)
  */
 package org.sonar.batch.mediumtest.preview;
 
-import org.sonar.batch.protocol.input.Rule;
-
-import org.sonar.xoo.rule.XooRulesDefinition;
 import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -32,17 +36,16 @@ import org.junit.rules.TemporaryFolder;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.bootstrapper.IssueListener;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.batch.mediumtest.issues.IssuesMediumTest;
 import org.sonar.batch.protocol.Constants.Severity;
 import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.input.Rule;
 import org.sonar.batch.scan.report.ConsoleReport;
 import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import org.sonar.xoo.rule.XooRulesDefinition;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -131,9 +134,16 @@ public class PreviewAndReportsMediumTest {
     tester.stop();
   }
 
+  private File copyProject(String path) throws Exception {
+    File projectDir = temp.newFolder();
+    File originalProjectDir = new File(PreviewAndReportsMediumTest.class.getResource(path).toURI());
+    FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar")));
+    return projectDir;
+  }
+
   @Test
   public void testIssueTracking() throws Exception {
-    File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+    File projectDir = copyProject("/mediumtest/xoo/sample");
 
     TaskResult result = tester
       .newScanTask(new File(projectDir, "sonar-project.properties"))
@@ -142,7 +152,7 @@ public class PreviewAndReportsMediumTest {
     int newIssues = 0;
     int openIssues = 0;
     int resolvedIssue = 0;
-    for (Issue issue : result.issues()) {
+    for (Issue issue : result.trackedIssues()) {
       if (issue.isNew()) {
         newIssues++;
       } else if (issue.resolution() != null) {
@@ -158,7 +168,7 @@ public class PreviewAndReportsMediumTest {
 
   @Test
   public void testConsoleReport() throws Exception {
-    File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+    File projectDir = copyProject("/mediumtest/xoo/sample");
 
     tester
       .newScanTask(new File(projectDir, "sonar-project.properties"))
@@ -170,7 +180,7 @@ public class PreviewAndReportsMediumTest {
 
   @Test
   public void testPostJob() throws Exception {
-    File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+    File projectDir = copyProject("/mediumtest/xoo/sample");
 
     tester
       .newScanTask(new File(projectDir, "sonar-project.properties"))
@@ -191,7 +201,7 @@ public class PreviewAndReportsMediumTest {
 
   @Test
   public void testHtmlReport() throws Exception {
-    File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+    File projectDir = copyProject("/mediumtest/xoo/sample");
 
     tester
       .newScanTask(new File(projectDir, "sonar-project.properties"))
@@ -225,4 +235,30 @@ public class PreviewAndReportsMediumTest {
     assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report-light.html"))).contains("No file analyzed");
   }
 
+  @Test
+  public void testIssueCallback() throws Exception {
+    File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+    File tmpDir = temp.newFolder();
+    FileUtils.copyDirectory(projectDir, tmpDir);
+    IssueRecorder issueListener = new IssueRecorder();
+
+    TaskResult result = tester
+      .newScanTask(new File(tmpDir, "sonar-project.properties"))
+      .setIssueListener(issueListener)
+      .start();
+
+    assertThat(result.trackedIssues()).hasSize(14);
+    assertThat(issueListener.issueList).hasSize(14);
+    assertThat(result.trackedIssues()).containsExactlyElementsOf(issueListener.issueList);
+  }
+
+  private class IssueRecorder implements IssueListener {
+    List<org.sonar.api.issue.Issue> issueList = new LinkedList<>();
+
+    @Override
+    public void handle(org.sonar.api.issue.Issue issue) {
+      issueList.add(issue);
+    }
+  }
+
 }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java
deleted file mode 100644 (file)
index 3e0f790..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.report;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.resources.Project;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.batch.index.BatchComponentCache;
-import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.protocol.output.BatchReportReader;
-import org.sonar.batch.protocol.output.BatchReportWriter;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.FieldDiffs;
-
-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 IssuesPublisherTest {
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  IssueCache issueCache;
-  ProjectDefinition projectDef;
-  Project project;
-
-  IssuesPublisher underTest;
-
-  @Before
-  public void prepare() {
-    projectDef = ProjectDefinition.create().setKey("foo");
-    project = new Project("foo").setAnalysisDate(new Date(1234567L));
-    BatchComponentCache componentCache = new BatchComponentCache();
-    org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
-    componentCache.add(project, null);
-    componentCache.add(sampleFile, project);
-    issueCache = mock(IssueCache.class);
-    when(issueCache.byComponent(anyString())).thenReturn(Collections.<DefaultIssue>emptyList());
-    underTest = new IssuesPublisher(componentCache, issueCache);
-  }
-
-  @Test
-  public void write_issues() throws Exception {
-    DefaultIssue issue1 = new DefaultIssue();
-    issue1.setKey("uuid");
-    issue1.setSeverity("MAJOR");
-    issue1.setRuleKey(RuleKey.of("repo", "rule"));
-    DefaultIssue issue2 = new DefaultIssue();
-    issue2.setKey("uuid2");
-    issue2.setSeverity("MAJOR");
-    issue2.setRuleKey(RuleKey.of("repo", "rule"));
-    issue2.setLine(2);
-    issue2.setMessage("msg");
-    issue2.setEffortToFix(2d);
-    issue2.setResolution("FIXED");
-    issue2.setStatus("RESOLVED");
-    issue2.setChecksum("checksum");
-    issue2.setReporter("reporter");
-    issue2.setAssignee("assignee");
-    issue2.setActionPlanKey("action");
-    issue2.setAuthorLogin("author");
-    issue2.setCurrentChange(new FieldDiffs().setUserLogin("foo"));
-    issue2.setCreationDate(new Date());
-    issue2.setUpdateDate(new Date());
-    issue2.setCloseDate(new Date());
-    issue2.setSelectedAt(1234L);
-    when(issueCache.byComponent("foo:src/Foo.php")).thenReturn(Arrays.asList(issue1, issue2));
-
-    File outputDir = temp.newFolder();
-    BatchReportWriter writer = new BatchReportWriter(outputDir);
-
-    underTest.publish(writer);
-
-    BatchReportReader reader = new BatchReportReader(outputDir);
-    assertThat(reader.readComponentIssues(1)).hasSize(0);
-    assertThat(reader.readComponentIssues(2)).hasSize(2);
-  }
-
-}
index aa2e32f96e68051610875fab56d29261b84a36e2..5d8a9a5cdf31090af7aeab032a30a42e9b2977e2 100644 (file)
  */
 package org.sonar.batch.sensor;
 
-import java.io.StringReader;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.mockito.ArgumentCaptor;
-import org.sonar.api.batch.fs.InputDir;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
-import org.sonar.api.batch.fs.internal.DefaultInputDir;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.batch.measure.MetricFinder;
 import org.sonar.api.batch.rule.ActiveRules;
-import org.sonar.api.batch.rule.Severity;
 import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
-import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
-import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.config.Settings;
 import org.sonar.api.measures.CoreMetrics;
@@ -45,7 +38,6 @@ import org.sonar.api.measures.Measure;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
-import org.sonar.api.rule.RuleKey;
 import org.sonar.batch.duplication.DuplicationCache;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.issue.ModuleIssues;
@@ -57,7 +49,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 public class DefaultSensorStorageTest {
@@ -143,66 +134,4 @@ public class DefaultSensorStorageTest {
     assertThat(m.getMetric()).isEqualTo(CoreMetrics.NCLOC);
   }
 
-  @Test
-  public void shouldAddIssueOnFile() {
-    InputFile file = new DefaultInputFile("foo", "src/Foo.php").initMetadata(new FileMetadata().readMetadata(new StringReader("Foo\nBar\nBiz\n")));
-
-    ArgumentCaptor<org.sonar.core.issue.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.core.issue.DefaultIssue.class);
-
-    sensorStorage.store(new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
-      .forRule(RuleKey.of("foo", "bar"))
-      .effortToFix(10.0));
-
-    verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
-
-    org.sonar.core.issue.DefaultIssue issue = argumentCaptor.getValue();
-    assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
-    assertThat(issue.message()).isEqualTo("Foo");
-    assertThat(issue.line()).isEqualTo(3);
-    assertThat(issue.severity()).isNull();
-    assertThat(issue.effortToFix()).isEqualTo(10.0);
-  }
-
-  @Test
-  public void shouldAddIssueOnDirectory() {
-    InputDir dir = new DefaultInputDir("foo", "src");
-
-    ArgumentCaptor<org.sonar.core.issue.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.core.issue.DefaultIssue.class);
-
-    sensorStorage.store(new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onDir(dir).message("Foo"))
-      .forRule(RuleKey.of("foo", "bar"))
-      .effortToFix(10.0));
-
-    verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
-
-    org.sonar.core.issue.DefaultIssue issue = argumentCaptor.getValue();
-    assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
-    assertThat(issue.message()).isEqualTo("Foo");
-    assertThat(issue.line()).isNull();
-    assertThat(issue.severity()).isNull();
-    assertThat(issue.effortToFix()).isEqualTo(10.0);
-  }
-
-  @Test
-  public void shouldAddIssueOnProject() {
-    ArgumentCaptor<org.sonar.core.issue.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.core.issue.DefaultIssue.class);
-
-    sensorStorage.store(new DefaultIssue()
-      .addLocation(new DefaultIssueLocation().onProject().message("Foo"))
-      .forRule(RuleKey.of("foo", "bar"))
-      .overrideSeverity(Severity.BLOCKER)
-      .effortToFix(10.0));
-
-    verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
-
-    org.sonar.core.issue.DefaultIssue issue = argumentCaptor.getValue();
-    assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
-    assertThat(issue.message()).isEqualTo("Foo");
-    assertThat(issue.line()).isNull();
-    assertThat(issue.severity()).isEqualTo("BLOCKER");
-    assertThat(issue.effortToFix()).isEqualTo(10.0);
-  }
-
 }
index 428d49d56f0aa531330346ff894e97047b3dc1fe..7cf03cb057938753d0f718ae45ace29e8ba40343 100644 (file)
@@ -133,16 +133,14 @@ public interface Issuable extends Perspective {
   boolean addIssue(Issue issue);
 
   /**
-   * Unresolved issues, including the issues reported by end-users.
-   * <p/>
-   * {@link org.sonar.api.batch.Decorator}s calling this method must be annotated with {@code @DependsUpon(DecoratorBarriers.ISSUES_TRACKED)}.
+   * @deprecated since 5.2 no more decorators on batch side
    */
+  @Deprecated
   List<Issue> issues();
 
   /**
-   * Issues marked as resolved during this scan.
-   * <p/>
-   * {@link org.sonar.api.batch.Decorator}s calling this method must be annotated with {@code @DependsUpon(DecoratorBarriers.ISSUES_TRACKED)}.
+   * @deprecated since 5.2 no more decorators on batch side
    */
+  @Deprecated
   List<Issue> resolvedIssues();
 }
index a1fda4b16713f040554433740b055002269004bb..654d40e3a31596da01ce17b2d13f507c3292be5e 100644 (file)
  */
 package org.sonar.api.issue;
 
-import org.sonar.api.batch.BatchSide;
 import org.sonar.api.ExtensionPoint;
+import org.sonar.api.batch.BatchSide;
 
 /**
  * @since 3.6
- * @deprecated since 4.0
+ * @deprecated since 4.0 use {@link org.sonar.api.issue.batch.IssueFilter}
  */
 @Deprecated
 @BatchSide