diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-11-05 14:14:35 +0100 |
---|---|---|
committer | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-11-09 16:58:03 +0100 |
commit | ae7efa83ddab2008efab798ef9b8b119009aca69 (patch) | |
tree | 20c7f4cec49de24f96c8ce09275767aa48f4db99 /sonar-batch | |
parent | f1b4428c014da7768d14bbd34cc322594b283bb4 (diff) | |
download | sonarqube-ae7efa83ddab2008efab798ef9b8b119009aca69.tar.gz sonarqube-ae7efa83ddab2008efab798ef9b8b119009aca69.zip |
SONAR-6978 Provide enhanced issue locations (start/end) offset in issues mode
Diffstat (limited to 'sonar-batch')
30 files changed, 816 insertions, 274 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/IssueListener.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/IssueListener.java index b2bf0fd57aa..3ccee7b3dee 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/IssueListener.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/IssueListener.java @@ -21,11 +21,19 @@ package org.sonar.batch.bootstrapper; public interface IssueListener { void handle(Issue issue); - + class Issue { + /** @since 5.3 */ + private Integer startLine; + /** @since 5.3 */ + private Integer startLineOffset; + /** @since 5.3 */ + private Integer endLine; + /** @since 5.3 */ + private Integer endLineOffset; + private String key; private String componentKey; - private Integer line; private String message; private String ruleKey; private String ruleName; @@ -60,12 +68,36 @@ public interface IssueListener { this.componentKey = componentKey; } - public Integer getLine() { - return line; + public Integer getStartLine() { + return startLine; + } + + public void setStartLine(Integer startLine) { + this.startLine = startLine; + } + + public Integer getStartLineOffset() { + return startLineOffset; + } + + public void setStartLineOffset(Integer startLineOffset) { + this.startLineOffset = startLineOffset; + } + + public Integer getEndLine() { + return endLine; + } + + public void setEndLine(Integer endLine) { + this.endLine = endLine; + } + + public Integer getEndLineOffset() { + return endLineOffset; } - public void setLine(Integer line) { - this.line = line; + public void setEndLineOffset(Integer endLineOffset) { + this.endLineOffset = endLineOffset; } public String getMessage() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssueCallback.java b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssueCallback.java index 8f7342e05e5..92bbc63fa65 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssueCallback.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssueCallback.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.issue; -import org.apache.commons.lang.StringUtils; +import org.sonar.batch.issue.tracking.TrackedIssue; +import org.apache.commons.lang.StringUtils; import org.sonar.api.rule.RuleKey; import org.sonar.api.batch.rule.Rule; import org.sonar.api.batch.rule.Rules; @@ -33,7 +34,6 @@ import java.util.Set; import org.sonar.batch.repository.user.UserRepositoryLoader; import org.sonar.batch.bootstrapper.IssueListener; -import org.sonar.core.issue.DefaultIssue; public class DefaultIssueCallback implements IssueCallback { private final IssueCache issues; @@ -65,37 +65,40 @@ public class DefaultIssueCallback implements IssueCallback { return; } - for (DefaultIssue issue : issues.all()) { + for (TrackedIssue issue : issues.all()) { collectInfo(issue); } getUsers(); - for (DefaultIssue issue : issues.all()) { + for (TrackedIssue issue : issues.all()) { IssueListener.Issue newIssue = new IssueListener.Issue(); newIssue.setAssigneeLogin(issue.assignee()); newIssue.setAssigneeName(getAssigneeName(issue.assignee())); newIssue.setComponentKey(issue.componentKey()); newIssue.setKey(issue.key()); - newIssue.setLine(issue.getLine()); - newIssue.setMessage(issue.getMessage()); + newIssue.setMessage(issue.message()); newIssue.setNew(issue.isNew()); newIssue.setResolution(issue.resolution()); - newIssue.setRuleKey(issue.getRuleKey().toString()); - newIssue.setRuleName(getRuleName(issue.getRuleKey())); + newIssue.setRuleKey(issue.ruleKey().toString()); + newIssue.setRuleName(getRuleName(issue.ruleKey())); newIssue.setSeverity(issue.severity()); newIssue.setStatus(issue.status()); + newIssue.setStartLine(issue.startLine()); + newIssue.setStartLineOffset(issue.startLineOffset()); + newIssue.setEndLine(issue.endLine()); + newIssue.setEndLineOffset(issue.endLineOffset()); listener.handle(newIssue); } } - private void collectInfo(DefaultIssue issue) { + private void collectInfo(TrackedIssue issue) { if (!StringUtils.isEmpty(issue.assignee())) { userLoginNames.add(issue.assignee()); } - if (issue.getRuleKey() != null) { - ruleKeys.add(issue.getRuleKey()); + if (issue.ruleKey() != null) { + ruleKeys.add(issue.ruleKey()); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultProjectIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultProjectIssues.java index 9f869ca95ef..244eb01b1bf 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultProjectIssues.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultProjectIssues.java @@ -19,11 +19,12 @@ */ package org.sonar.batch.issue; +import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import org.sonar.api.issue.Issue; import org.sonar.api.issue.ProjectIssues; -import org.sonar.core.issue.DefaultIssue; +import org.sonar.batch.issue.tracking.TrackedIssue; import javax.annotation.Nullable; @@ -41,15 +42,19 @@ public class DefaultProjectIssues implements ProjectIssues { @Override public Iterable<Issue> issues() { - return (Iterable) Iterables.filter(cache.all(), new ResolvedPredicate(false)); + return Iterables.transform( + Iterables.filter(cache.all(), new ResolvedPredicate(false)), + new IssueTransformer()); } @Override public Iterable<Issue> resolvedIssues() { - return (Iterable) Iterables.filter(cache.all(), new ResolvedPredicate(true)); + return Iterables.transform( + Iterables.filter(cache.all(), new ResolvedPredicate(true)), + new IssueTransformer()); } - private static class ResolvedPredicate implements Predicate<DefaultIssue> { + private static class ResolvedPredicate implements Predicate<TrackedIssue> { private final boolean resolved; private ResolvedPredicate(boolean resolved) { @@ -57,11 +62,18 @@ public class DefaultProjectIssues implements ProjectIssues { } @Override - public boolean apply(@Nullable DefaultIssue issue) { + public boolean apply(@Nullable TrackedIssue issue) { if (issue != null) { return resolved ? (issue.resolution() != null) : (issue.resolution() == null); } return false; } } + + private static class IssueTransformer implements Function<TrackedIssue, Issue> { + @Override + public Issue apply(TrackedIssue issue) { + return new TrackedIssueAdapter(issue); + } + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueCache.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueCache.java index 52a29fb7483..af80f5f7358 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueCache.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.issue; +import org.sonar.batch.issue.tracking.TrackedIssue; + import org.sonar.api.batch.BatchSide; -import org.sonar.core.issue.DefaultIssue; import org.sonar.batch.index.Cache; import org.sonar.batch.index.Caches; @@ -33,17 +34,17 @@ import java.util.Collection; public class IssueCache { // component key -> issue key -> issue - private final Cache<DefaultIssue> cache; + private final Cache<TrackedIssue> cache; public IssueCache(Caches caches) { cache = caches.createCache("issues"); } - public Iterable<DefaultIssue> byComponent(String componentKey) { + public Iterable<TrackedIssue> byComponent(String componentKey) { return cache.values(componentKey); } - public Iterable<DefaultIssue> all() { + public Iterable<TrackedIssue> all() { return cache.values(); } @@ -51,7 +52,7 @@ public class IssueCache { return cache.keySet(); } - public IssueCache put(DefaultIssue issue) { + public IssueCache put(TrackedIssue issue) { cache.put(issue.componentKey(), issue.key(), issue); return this; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueTransformer.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueTransformer.java new file mode 100644 index 00000000000..65bd924ea73 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueTransformer.java @@ -0,0 +1,102 @@ +/* + * 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 com.google.common.base.Preconditions; + +import org.sonar.core.util.Uuids; +import org.sonar.api.issue.Issue; +import org.sonar.batch.protocol.output.BatchReport.TextRange; +import org.sonar.core.component.ComponentKeys; + +import java.util.Date; + +import org.sonar.batch.issue.tracking.TrackedIssue; +import org.sonar.api.rule.RuleKey; +import org.sonar.batch.index.BatchComponent; +import org.sonar.batch.protocol.output.BatchReport; + +public class IssueTransformer { + private IssueTransformer() { + // static only + } + + public static TrackedIssue toTrackedIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue serverIssue) { + TrackedIssue issue = new TrackedIssue(); + issue.setKey(serverIssue.getKey()); + issue.setStatus(serverIssue.getStatus()); + issue.setResolution(serverIssue.hasResolution() ? serverIssue.getResolution() : null); + issue.setMessage(serverIssue.hasMsg() ? serverIssue.getMsg() : null); + issue.setStartLine(serverIssue.hasLine() ? serverIssue.getLine() : null); + issue.setEndLine(serverIssue.hasLine() ? serverIssue.getLine() : null); + issue.setSeverity(serverIssue.getSeverity().name()); + issue.setAssignee(serverIssue.hasAssigneeLogin() ? serverIssue.getAssigneeLogin() : null); + issue.setComponentKey(ComponentKeys.createEffectiveKey(serverIssue.getModuleKey(), serverIssue.hasPath() ? serverIssue.getPath() : null)); + issue.setCreationDate(new Date(serverIssue.getCreationDate())); + issue.setRuleKey(RuleKey.of(serverIssue.getRuleRepository(), serverIssue.getRuleKey())); + issue.setNew(false); + return issue; + } + + public static void close(TrackedIssue issue) { + issue.setStatus(Issue.STATUS_CLOSED); + issue.setStartLine(null); + issue.setEndLine(null); + issue.setResolution(Issue.RESOLUTION_FIXED); + } + + public static void resolveRemove(TrackedIssue issue) { + issue.setStatus(Issue.STATUS_CLOSED); + issue.setStartLine(null); + issue.setEndLine(null); + issue.setResolution(Issue.RESOLUTION_REMOVED); + } + + public static TrackedIssue toTrackedIssue(BatchComponent component, BatchReport.Issue rawIssue) { + RuleKey ruleKey = RuleKey.of(rawIssue.getRuleRepository(), rawIssue.getRuleKey()); + + Preconditions.checkNotNull(component.key(), "Component key must be set"); + Preconditions.checkNotNull(ruleKey, "Rule key must be set"); + + TrackedIssue issue = new TrackedIssue(); + + issue.setKey(Uuids.create()); + issue.setComponentKey(component.key()); + issue.setRuleKey(ruleKey); + issue.setEffortToFix(rawIssue.hasEffortToFix() ? rawIssue.getEffortToFix() : null); + issue.setSeverity(rawIssue.getSeverity().name()); + issue.setMessage(rawIssue.hasMsg() ? rawIssue.getMsg() : null); + issue.setResolution(null); + issue.setStatus(Issue.STATUS_OPEN); + issue.setNew(true); + + if (rawIssue.hasTextRange()) { + TextRange r = rawIssue.getTextRange(); + + issue.setStartLine(r.hasStartLine() ? rawIssue.getTextRange().getStartLine() : null); + issue.setStartLineOffset(r.hasStartOffset() ? r.getStartOffset() : null); + issue.setEndLine(r.hasEndLine() ? r.getEndLine() : issue.startLine()); + issue.setEndLineOffset(r.hasEndOffset() ? r.getEndOffset() : null); + } + + return issue; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/TrackedIssueAdapter.java b/sonar-batch/src/main/java/org/sonar/batch/issue/TrackedIssueAdapter.java new file mode 100644 index 00000000000..a10a1391bf5 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/TrackedIssueAdapter.java @@ -0,0 +1,186 @@ +/* + * 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 org.sonar.api.issue.IssueComment; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.Duration; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.sonar.batch.issue.tracking.TrackedIssue; +import org.sonar.api.issue.Issue; + +public class TrackedIssueAdapter implements Issue { + private TrackedIssue issue; + + public TrackedIssueAdapter(TrackedIssue issue) { + this.issue = issue; + } + + @Override + public String key() { + return issue.key(); + } + + @Override + public String componentKey() { + return issue.componentKey(); + } + + @Override + public RuleKey ruleKey() { + return issue.ruleKey(); + } + + @Override + public String severity() { + return issue.severity(); + } + + @Override + public String message() { + return issue.message(); + } + + @Override + public Integer line() { + return issue.startLine(); + } + + @Override + public Double effortToFix() { + return issue.effortToFix(); + } + + @Override + public String status() { + return issue.status(); + } + + @Override + public String resolution() { + return issue.resolution(); + } + + @Override + public String reporter() { + return issue.reporter(); + } + + @Override + public String assignee() { + return issue.assignee(); + } + + @Override + public boolean isNew() { + return issue.isNew(); + } + + @Override + public Map<String, String> attributes() { + return new HashMap<>(); + } + + @Override + public Date creationDate() { + return null; + } + + @Override + public String language() { + return null; + } + + @Override + public Date updateDate() { + return null; + } + + @Override + public Date closeDate() { + return null; + } + + @Override + public String attribute(String key) { + return null; + } + + @Override + public String authorLogin() { + return null; + } + + @Override + public String actionPlanKey() { + return null; + } + + @Override + public List<IssueComment> comments() { + return new ArrayList<>(); + } + + @Override + public Duration debt() { + return null; + } + + @Override + public String projectKey() { + return null; + } + + @Override + public String projectUuid() { + return null; + } + + @Override + public String componentUuid() { + return null; + } + + @Override + public Collection<String> tags() { + return new ArrayList<>(); + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof Issue)) { + return false; + } + Issue that = (Issue) o; + return !(issue.key() != null ? !issue.key().equals(that.key()) : (that.key() != null)); + } + + @Override + public int hashCode() { + return issue.key() != null ? issue.key().hashCode() : 0; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTransition.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTransition.java index 6dcbb685209..ff79165cff3 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTransition.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTransition.java @@ -19,50 +19,47 @@ */ package org.sonar.batch.issue.tracking; +import org.sonar.batch.issue.IssueTransformer; + import com.google.common.collect.Lists; import com.google.common.collect.Sets; + import java.util.Date; import java.util.List; import java.util.Set; + import javax.annotation.Nullable; + import org.sonar.api.batch.BatchSide; import org.sonar.api.resources.Project; -import org.sonar.api.rule.RuleKey; import org.sonar.batch.index.BatchComponent; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.batch.protocol.output.BatchReportReader; import org.sonar.batch.report.ReportPublisher; -import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.util.CloseableIterator; @BatchSide public class IssueTransition { private final IssueCache issueCache; - private final IssueWorkflow workflow; - private final IssueChangeContext changeContext; private final BatchComponentCache componentCache; private final ReportPublisher reportPublisher; private final Date analysisDate; @Nullable private final LocalIssueTracking localIssueTracking; - public IssueTransition(BatchComponentCache componentCache, IssueCache issueCache, IssueWorkflow workflow, ReportPublisher reportPublisher, + public IssueTransition(BatchComponentCache componentCache, IssueCache issueCache, ReportPublisher reportPublisher, @Nullable LocalIssueTracking localIssueTracking) { this.componentCache = componentCache; this.issueCache = issueCache; - this.workflow = workflow; this.reportPublisher = reportPublisher; this.localIssueTracking = localIssueTracking; this.analysisDate = ((Project) componentCache.getRoot().resource()).getAnalysisDate(); - this.changeContext = IssueChangeContext.createScan(analysisDate); } - public IssueTransition(BatchComponentCache componentCache, IssueCache issueCache, IssueWorkflow workflow, ReportPublisher reportPublisher) { - this(componentCache, issueCache, workflow, reportPublisher, null); + public IssueTransition(BatchComponentCache componentCache, IssueCache issueCache, ReportPublisher reportPublisher) { + this(componentCache, issueCache, reportPublisher, null); } public void execute() { @@ -88,7 +85,7 @@ public class IssueTransition { throw new IllegalStateException("Can't read issues for " + component.key(), e); } - List<DefaultIssue> trackedIssues; + List<TrackedIssue> trackedIssues; if (localIssueTracking != null) { trackedIssues = localIssueTracking.trackIssues(component, rawIssues); } else { @@ -98,32 +95,19 @@ public class IssueTransition { // Unmatched raw issues = new issues addUnmatchedRawIssues(component, rawIssues, trackedIssues); - for (DefaultIssue issue : trackedIssues) { - workflow.doAutomaticTransition(issue, changeContext); + for (TrackedIssue issue : trackedIssues) { issueCache.put(issue); } } - - private void addUnmatchedRawIssues(BatchComponent component, Set<org.sonar.batch.protocol.output.BatchReport.Issue> rawIssues, List<DefaultIssue> trackedIssues) { + + private void addUnmatchedRawIssues(BatchComponent component, Set<org.sonar.batch.protocol.output.BatchReport.Issue> rawIssues, List<TrackedIssue> trackedIssues) { for (BatchReport.Issue rawIssue : rawIssues) { - DefaultIssue tracked = toTracked(component, rawIssue); - tracked.setNew(true); + TrackedIssue tracked = IssueTransformer.toTrackedIssue(component, rawIssue); tracked.setCreationDate(analysisDate); trackedIssues.add(tracked); } } - private DefaultIssue toTracked(BatchComponent component, BatchReport.Issue rawIssue) { - return 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(); - } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java index 8c77079f5f6..77c8ee7b0cd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.issue.tracking; -import org.sonar.api.batch.fs.InputFile.Status; +import org.sonar.batch.issue.IssueTransformer; +import org.sonar.api.batch.fs.InputFile.Status; import org.sonar.batch.analysis.DefaultAnalysisMode; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; @@ -38,42 +39,27 @@ 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.api.resources.Project; import org.sonar.api.resources.ResourceUtils; -import org.sonar.api.rule.RuleKey; import org.sonar.batch.index.BatchComponent; -import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.repository.ProjectRepositories; -import org.sonar.core.component.ComponentKeys; -import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; @BatchSide public class LocalIssueTracking { private final IssueTracking tracking; private final ServerLineHashesLoader lastLineHashes; - private final IssueUpdater updater; - private final IssueChangeContext changeContext; private final ActiveRules activeRules; private final ServerIssueRepository serverIssueRepository; - private final Date analysisDate; private final DefaultAnalysisMode mode; private boolean hasServerAnalysis; - public LocalIssueTracking(BatchComponentCache resourceCache, IssueTracking tracking, ServerLineHashesLoader lastLineHashes, IssueUpdater updater, - ActiveRules activeRules, ServerIssueRepository serverIssueRepository, ProjectRepositories projectRepositories, ReportPublisher reportPublisher, - DefaultAnalysisMode mode) { + public LocalIssueTracking(IssueTracking tracking, ServerLineHashesLoader lastLineHashes, + ActiveRules activeRules, ServerIssueRepository serverIssueRepository, ProjectRepositories projectRepositories, DefaultAnalysisMode mode) { this.tracking = tracking; this.lastLineHashes = lastLineHashes; - this.updater = updater; this.serverIssueRepository = serverIssueRepository; this.mode = mode; - this.analysisDate = ((Project) resourceCache.getRoot().resource()).getAnalysisDate(); - this.changeContext = IssueChangeContext.createScan(analysisDate); this.activeRules = activeRules; this.hasServerAnalysis = projectRepositories.lastAnalysisDate() != null; } @@ -84,8 +70,8 @@ public class LocalIssueTracking { } } - public List<DefaultIssue> trackIssues(BatchComponent component, Set<BatchReport.Issue> rawIssues) { - List<DefaultIssue> trackedIssues = Lists.newArrayList(); + public List<TrackedIssue> trackIssues(BatchComponent component, Set<BatchReport.Issue> rawIssues) { + List<TrackedIssue> trackedIssues = Lists.newArrayList(); if (hasServerAnalysis) { // all the issues that are not closed in db before starting this module scan, including manual issues Collection<ServerIssue> serverIssues = loadServerIssues(component); @@ -124,33 +110,23 @@ public class LocalIssueTracking { return false; } - private void copyServerIssues(Collection<ServerIssue> serverIssues, List<DefaultIssue> trackedIssues) { + private void copyServerIssues(Collection<ServerIssue> serverIssues, List<TrackedIssue> trackedIssues) { for (ServerIssue serverIssue : serverIssues) { org.sonar.batch.protocol.input.BatchInput.ServerIssue unmatchedPreviousIssue = ((ServerIssueFromWs) serverIssue).getDto(); - DefaultIssue unmatched = toUnmatchedIssue(unmatchedPreviousIssue); + TrackedIssue unmatched = IssueTransformer.toTrackedIssue(unmatchedPreviousIssue); ActiveRule activeRule = activeRules.find(unmatched.ruleKey()); unmatched.setNew(false); - boolean isRemovedRule = activeRule == null; - unmatched.setBeingClosed(isRemovedRule); - unmatched.setOnDisabledRule(isRemovedRule); + if (activeRule == null) { + // rule removed + IssueTransformer.resolveRemove(unmatched); + } + trackedIssues.add(unmatched); } } - private DefaultIssue toTracked(BatchComponent component, BatchReport.Issue rawIssue) { - return 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(); - } - @CheckForNull private SourceHashHolder loadSourceHashes(BatchComponent component) { SourceHashHolder sourceHashHolder = null; @@ -173,20 +149,18 @@ public class LocalIssueTracking { } @VisibleForTesting - protected void mergeMatched(BatchComponent component, IssueTrackingResult result, List<DefaultIssue> trackedIssues, Collection<BatchReport.Issue> rawIssues) { + protected void mergeMatched(BatchComponent component, IssueTrackingResult result, List<TrackedIssue> 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); + TrackedIssue tracked = IssueTransformer.toTrackedIssue(component, rawIssue); // invariant fields tracked.setKey(ref.getKey()); // non-persisted fields tracked.setNew(false); - tracked.setBeingClosed(false); - tracked.setOnDisabledRule(false); // fields to update with old values tracked.setResolution(ref.hasResolution() ? ref.getResolution() : null); @@ -202,10 +176,10 @@ public class LocalIssueTracking { } } - private void addUnmatchedFromServer(Collection<ServerIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) { + private void addUnmatchedFromServer(Collection<ServerIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<TrackedIssue> issues) { for (ServerIssue unmatchedIssue : unmatchedIssues) { org.sonar.batch.protocol.input.BatchInput.ServerIssue unmatchedPreviousIssue = ((ServerIssueFromWs) unmatchedIssue).getDto(); - DefaultIssue unmatched = toUnmatchedIssue(unmatchedPreviousIssue); + TrackedIssue unmatched = IssueTransformer.toTrackedIssue(unmatchedPreviousIssue); if (unmatchedIssue.ruleKey().isManual() && !Issue.STATUS_CLOSED.equals(unmatchedPreviousIssue.getStatus())) { relocateManualIssue(unmatched, unmatchedIssue, sourceHashHolder); } @@ -214,46 +188,29 @@ public class LocalIssueTracking { } } - private void addIssuesOnDeletedComponents(Collection<DefaultIssue> issues) { + private void addIssuesOnDeletedComponents(Collection<TrackedIssue> issues) { for (org.sonar.batch.protocol.input.BatchInput.ServerIssue previous : serverIssueRepository.issuesOnMissingComponents()) { - DefaultIssue dead = toUnmatchedIssue(previous); + TrackedIssue dead = IssueTransformer.toTrackedIssue(previous); updateUnmatchedIssue(dead, true); issues.add(dead); } } - private DefaultIssue toUnmatchedIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue serverIssue) { - DefaultIssue issue = new DefaultIssue(); - issue.setKey(serverIssue.getKey()); - issue.setStatus(serverIssue.getStatus()); - issue.setResolution(serverIssue.hasResolution() ? serverIssue.getResolution() : null); - issue.setMessage(serverIssue.hasMsg() ? serverIssue.getMsg() : null); - issue.setLine(serverIssue.hasLine() ? serverIssue.getLine() : null); - issue.setSeverity(serverIssue.getSeverity().name()); - issue.setAssignee(serverIssue.hasAssigneeLogin() ? serverIssue.getAssigneeLogin() : null); - issue.setComponentKey(ComponentKeys.createEffectiveKey(serverIssue.getModuleKey(), serverIssue.hasPath() ? serverIssue.getPath() : null)); - issue.setManualSeverity(serverIssue.getManualSeverity()); - issue.setCreationDate(new Date(serverIssue.getCreationDate())); - issue.setRuleKey(RuleKey.of(serverIssue.getRuleRepository(), serverIssue.getRuleKey())); - issue.setNew(false); - return issue; - } - - private void updateUnmatchedIssue(DefaultIssue issue, boolean forceEndOfLife) { + private void updateUnmatchedIssue(TrackedIssue issue, boolean forceEndOfLife) { ActiveRule activeRule = activeRules.find(issue.ruleKey()); issue.setNew(false); boolean manualIssue = issue.ruleKey().isManual(); boolean isRemovedRule = activeRule == null; - if (manualIssue) { - issue.setBeingClosed(forceEndOfLife || isRemovedRule); - } else { - issue.setBeingClosed(true); + + if (isRemovedRule) { + IssueTransformer.resolveRemove(issue); + } else if (forceEndOfLife || !manualIssue) { + IssueTransformer.close(issue); } - issue.setOnDisabledRule(isRemovedRule); } - private void relocateManualIssue(DefaultIssue newIssue, ServerIssue oldIssue, SourceHashHolder sourceHashHolder) { + private static void relocateManualIssue(TrackedIssue newIssue, ServerIssue oldIssue, SourceHashHolder sourceHashHolder) { Integer previousLine = oldIssue.line(); if (previousLine == null) { return; @@ -262,17 +219,12 @@ public class LocalIssueTracking { Collection<Integer> newLinesWithSameHash = sourceHashHolder.getNewLinesMatching(previousLine); if (newLinesWithSameHash.isEmpty()) { if (previousLine > sourceHashHolder.getHashedSource().length()) { - newIssue.setLine(null); - updater.setStatus(newIssue, Issue.STATUS_CLOSED, changeContext); - updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext); - updater.setPastLine(newIssue, previousLine); - updater.setPastMessage(newIssue, oldIssue.message(), changeContext); + IssueTransformer.resolveRemove(newIssue); } } else if (newLinesWithSameHash.size() == 1) { Integer newLine = newLinesWithSameHash.iterator().next(); - newIssue.setLine(newLine); - updater.setPastLine(newIssue, previousLine); - updater.setPastMessage(newIssue, oldIssue.message(), changeContext); + newIssue.setStartLine(newLine); + newIssue.setEndLine(newLine); } } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/TrackedIssue.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/TrackedIssue.java new file mode 100644 index 00000000000..480d055f765 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/TrackedIssue.java @@ -0,0 +1,206 @@ +/* + * 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.tracking; + +import java.io.Serializable; +import java.util.Date; + +import org.sonar.api.rule.RuleKey; + +public class TrackedIssue implements Serializable { + private static final long serialVersionUID = -1755017079070964287L; + + private RuleKey ruleKey; + private String key; + private String severity; + private Integer startLine; + private Integer startLineOffset; + private Integer endLine; + private Integer endLineOffset; + private Double effortToFix; + private boolean isNew; + private Date creationDate; + private String resolution; + private String status; + private String assignee; + private String reporter; + private String componentKey; + private String message; + + public String message() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String componentKey() { + return componentKey; + } + + public void setComponentKey(String componentKey) { + this.componentKey = componentKey; + } + + public String key() { + return key; + } + + public Integer startLine() { + return startLine; + } + + public void setStartLine(Integer startLine) { + this.startLine = startLine; + } + + public Integer startLineOffset() { + return startLineOffset; + } + + public void setStartLineOffset(Integer startLineOffset) { + this.startLineOffset = startLineOffset; + } + + public Integer endLine() { + return endLine; + } + + public void setEndLine(Integer endLine) { + this.endLine = endLine; + } + + public Integer endLineOffset() { + return endLineOffset; + } + + public void setEndLineOffset(Integer endLineOffset) { + this.endLineOffset = endLineOffset; + } + + public void setKey(String key) { + this.key = key; + } + + public String assignee() { + return assignee; + } + + public void setAssignee(String assignee) { + this.assignee = assignee; + } + + public String reporter() { + return reporter; + } + + public void setReporter(String reporter) { + this.reporter = reporter; + } + + public String resolution() { + return resolution; + } + + public void setResolution(String resolution) { + this.resolution = resolution; + } + + public String status() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public RuleKey ruleKey() { + return ruleKey; + } + + public String severity() { + return severity; + } + + public Double effortToFix() { + return effortToFix; + } + + public Date getCreationDate() { + return creationDate; + } + + public boolean isNew() { + return isNew; + } + + public void setNew(boolean isNew) { + this.isNew = isNew; + } + + public Date creationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + + public void setRuleKey(RuleKey ruleKey) { + this.ruleKey = ruleKey; + } + + public void setSeverity(String severity) { + this.severity = severity; + } + + public void setEffortToFix(Double effortToFix) { + this.effortToFix = effortToFix; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + TrackedIssue other = (TrackedIssue) obj; + if (key == null) { + if (other.key != null) { + return false; + } + } else if (!key.equals(other.key)) { + return false; + } + return true; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java index 49c7f95736b..2254fa0798f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java +++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java @@ -19,8 +19,11 @@ */ package org.sonar.batch.mediumtest; +import org.sonar.batch.issue.tracking.TrackedIssue; + import com.google.common.collect.Iterators; import com.google.common.collect.Lists; + import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; @@ -28,8 +31,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; + import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +47,6 @@ 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.highlighting.TypeOfText; -import org.sonar.api.issue.Issue; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.batch.protocol.output.BatchReport.Component; @@ -53,14 +57,13 @@ import org.sonar.batch.report.BatchReportUtils; import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.scan.ProjectScanContainer; import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.core.issue.DefaultIssue; import org.sonar.core.util.CloseableIterator; public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver { private static final Logger LOG = LoggerFactory.getLogger(TaskResult.class); - private List<Issue> issues = new ArrayList<>(); + private List<TrackedIssue> issues = new ArrayList<>(); private Map<String, InputFile> inputFiles = new HashMap<>(); private Map<String, Component> reportComponents = new HashMap<>(); private Map<String, InputDir> inputDirs = new HashMap<>(); @@ -69,7 +72,7 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver { @Override public void scanTaskCompleted(ProjectScanContainer container) { LOG.info("Store analysis results in memory for later assertions in medium test"); - for (DefaultIssue issue : container.getComponentByType(IssueCache.class).all()) { + for (TrackedIssue issue : container.getComponentByType(IssueCache.class).all()) { issues.add(issue); } @@ -112,7 +115,7 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver { } } - public List<Issue> trackedIssues() { + public List<TrackedIssue> trackedIssues() { return issues; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/postjob/DefaultPostJobContext.java b/sonar-batch/src/main/java/org/sonar/batch/postjob/DefaultPostJobContext.java index 38787dea74c..de4ba05f3a1 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/postjob/DefaultPostJobContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/postjob/DefaultPostJobContext.java @@ -19,10 +19,14 @@ */ package org.sonar.batch.postjob; +import org.sonar.batch.issue.tracking.TrackedIssue; + import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; + import javax.annotation.Nullable; + import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.postjob.PostJobContext; @@ -33,7 +37,6 @@ import org.sonar.api.rule.RuleKey; import org.sonar.batch.index.BatchComponent; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.issue.IssueCache; -import org.sonar.core.issue.DefaultIssue; public class DefaultPostJobContext implements PostJobContext { @@ -61,29 +64,19 @@ public class DefaultPostJobContext implements PostJobContext { @Override public Iterable<Issue> issues() { - return Iterables.transform(Iterables.filter(cache.all(), new ResolvedPredicate(false)), new Function<DefaultIssue, Issue>() { - @Override - public Issue apply(DefaultIssue input) { - return new DefaultIssueWrapper(input); - } - }); + return Iterables.transform(Iterables.filter(cache.all(), new ResolvedPredicate(false)), new IssueTransformer()); } @Override public Iterable<Issue> resolvedIssues() { - return Iterables.transform(Iterables.filter(cache.all(), new ResolvedPredicate(true)), new Function<DefaultIssue, Issue>() { - @Override - public Issue apply(DefaultIssue input) { - return new DefaultIssueWrapper(input); - } - }); + return Iterables.transform(Iterables.filter(cache.all(), new ResolvedPredicate(true)), new IssueTransformer()); } private class DefaultIssueWrapper implements Issue { - private final DefaultIssue wrapped; + private final TrackedIssue wrapped; - public DefaultIssueWrapper(DefaultIssue wrapped) { + public DefaultIssueWrapper(TrackedIssue wrapped) { this.wrapped = wrapped; } @@ -110,7 +103,7 @@ public class DefaultPostJobContext implements PostJobContext { @Override public Integer line() { - return wrapped.line(); + return wrapped.startLine(); } @Override @@ -133,10 +126,16 @@ public class DefaultPostJobContext implements PostJobContext { public boolean isNew() { return wrapped.isNew(); } + } + private class IssueTransformer implements Function<TrackedIssue, Issue> { + @Override + public Issue apply(TrackedIssue input) { + return new DefaultIssueWrapper(input); + } } - private static class ResolvedPredicate implements Predicate<DefaultIssue> { + private static class ResolvedPredicate implements Predicate<TrackedIssue> { private final boolean resolved; private ResolvedPredicate(boolean resolved) { @@ -144,7 +143,7 @@ public class DefaultPostJobContext implements PostJobContext { } @Override - public boolean apply(@Nullable DefaultIssue issue) { + public boolean apply(@Nullable TrackedIssue issue) { if (issue != null) { return resolved ? issue.resolution() != null : issue.resolution() == null; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 7b605d2b9b0..55a10b6fd25 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.scan; +import org.sonar.batch.issue.DefaultProjectIssues; + import com.google.common.annotations.VisibleForTesting; import org.sonar.api.CoreProperties; import org.sonar.api.batch.InstantiationStrategy; @@ -47,7 +49,6 @@ import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.index.Caches; import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.issue.DefaultIssueCallback; -import org.sonar.batch.issue.DefaultProjectIssues; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.issue.tracking.DefaultServerLineHashesLoader; import org.sonar.batch.issue.tracking.IssueTransition; @@ -91,9 +92,7 @@ import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.batch.source.CodeColorizers; import org.sonar.batch.test.TestPlanBuilder; import org.sonar.batch.test.TestableBuilder; -import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.workflow.FunctionExecutor; -import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.platform.ComponentContainer; public class ProjectScanContainer extends ComponentContainer { @@ -166,9 +165,7 @@ public class ProjectScanContainer extends ComponentContainer { new QualityProfileProvider(), // issues - IssueUpdater.class, FunctionExecutor.class, - IssueWorkflow.class, IssueCache.class, DefaultProjectIssues.class, IssueTransition.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java index 0627d986451..50752b9900f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.scan.report; +import org.sonar.batch.issue.tracking.TrackedIssue; + import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.StringUtils; import org.sonar.api.Properties; @@ -30,7 +32,6 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.core.issue.DefaultIssue; @Properties({ @Property(key = ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, name = "Enable console report", description = "Set this to true to generate a report in console output", @@ -65,7 +66,7 @@ public class ConsoleReport implements Reporter { int newMinorIssues = 0; int newInfoIssues = 0; - public void process(DefaultIssue issue) { + public void process(TrackedIssue issue) { if (issue.isNew()) { totalNewIssues++; switch (issue.severity()) { @@ -100,7 +101,7 @@ public class ConsoleReport implements Reporter { if (settings.getBoolean(CONSOLE_REPORT_ENABLED_KEY)) { Report r = new Report(); r.setNoFile(!inputPathCache.allFiles().iterator().hasNext()); - for (DefaultIssue issue : issueCache.all()) { + for (TrackedIssue issue : issueCache.all()) { r.process(issue); } printReport(r); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReport.java index 63abaace82e..8a36447daba 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReport.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReport.java @@ -19,13 +19,16 @@ */ package org.sonar.batch.scan.report; +import org.sonar.batch.issue.tracking.TrackedIssue; + import com.google.common.collect.Maps; + import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; + import org.sonar.api.batch.rule.Rule; -import org.sonar.api.issue.Issue; import org.sonar.api.rules.RulePriority; import org.sonar.batch.index.BatchComponent; @@ -78,16 +81,16 @@ public class IssuesReport { return new ArrayList<>(resourceReportsByResource.keySet()); } - public void addIssueOnResource(BatchComponent resource, Issue issue, Rule rule, RulePriority severity) { + public void addIssueOnResource(BatchComponent resource, TrackedIssue issue, Rule rule, RulePriority severity) { addResource(resource); getSummary().addIssue(issue, rule, severity); resourceReportsByResource.get(resource).addIssue(issue, rule, RulePriority.valueOf(issue.severity())); } - public void addResolvedIssueOnResource(BatchComponent resource, Issue issue, Rule rule, RulePriority severity) { + public void addResolvedIssueOnResource(BatchComponent resource, TrackedIssue issue, Rule rule, RulePriority severity) { addResource(resource); getSummary().addResolvedIssue(issue, rule, severity); - resourceReportsByResource.get(resource).addResolvedIssue(issue, rule, RulePriority.valueOf(issue.severity())); + resourceReportsByResource.get(resource).addResolvedIssue(rule, RulePriority.valueOf(issue.severity())); } private void addResource(BatchComponent resource) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReportBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReportBuilder.java index ec89540927a..87db0a3bc69 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReportBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReportBuilder.java @@ -19,13 +19,15 @@ */ package org.sonar.batch.scan.report; +import org.sonar.batch.issue.tracking.TrackedIssue; + import javax.annotation.CheckForNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.BatchSide; import org.sonar.api.batch.rule.Rule; import org.sonar.api.batch.rule.Rules; -import org.sonar.api.issue.Issue; import org.sonar.api.resources.Project; import org.sonar.api.rules.RulePriority; import org.sonar.batch.DefaultProjectTree; @@ -33,7 +35,6 @@ import org.sonar.batch.index.BatchComponent; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.core.issue.DefaultIssue; @BatchSide public class IssuesReportBuilder { @@ -66,8 +67,8 @@ public class IssuesReportBuilder { return issuesReport; } - private void processIssues(IssuesReport issuesReport, Iterable<DefaultIssue> issues) { - for (Issue issue : issues) { + private void processIssues(IssuesReport issuesReport, Iterable<TrackedIssue> issues) { + for (TrackedIssue issue : issues) { Rule rule = findRule(issue); RulePriority severity = RulePriority.valueOf(issue.severity()); BatchComponent resource = resourceCache.get(issue.componentKey()); @@ -82,7 +83,7 @@ public class IssuesReportBuilder { } } - private static boolean validate(Issue issue, Rule rule, BatchComponent resource) { + private static boolean validate(TrackedIssue issue, Rule rule, BatchComponent resource) { if (rule == null) { LOG.warn("Unknow rule for issue {}", issue); return false; @@ -95,7 +96,7 @@ public class IssuesReportBuilder { } @CheckForNull - private Rule findRule(Issue issue) { + private Rule findRule(TrackedIssue issue) { return rules.find(issue.ruleKey()); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/JSONReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/JSONReport.java index 68c2f5f94db..847d4ceb256 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/JSONReport.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/JSONReport.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.scan.report; -import org.sonar.batch.protocol.input.BatchInput.User; +import org.sonar.batch.issue.tracking.TrackedIssue; +import org.sonar.batch.protocol.input.BatchInput.User; import com.google.common.annotations.VisibleForTesting; import java.io.BufferedWriter; @@ -57,7 +58,6 @@ import org.sonar.batch.issue.IssueCache; import org.sonar.batch.protocol.input.BatchInput; import org.sonar.batch.repository.user.UserRepositoryLoader; import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.core.issue.DefaultIssue; import static com.google.common.collect.Sets.newHashSet; @Properties({ @@ -133,13 +133,17 @@ public class JSONReport implements Reporter { private void writeJsonIssues(JsonWriter json, Set<RuleKey> ruleKeys, Set<String> logins) throws IOException { json.name("issues").beginArray(); - for (DefaultIssue issue : getIssues()) { + for (TrackedIssue issue : getIssues()) { if (issue.resolution() == null) { json .beginObject() .prop("key", issue.key()) .prop("component", issue.componentKey()) - .prop("line", issue.line()) + .prop("line", issue.startLine()) + .prop("startLine", issue.startLine()) + .prop("startOffset", issue.startLineOffset()) + .prop("endLine", issue.endLine()) + .prop("endOffset", issue.endLineOffset()) .prop("message", issue.message()) .prop("severity", issue.severity()) .prop("rule", issue.ruleKey().toString()) @@ -240,7 +244,7 @@ public class JSONReport implements Reporter { } @VisibleForTesting - Iterable<DefaultIssue> getIssues() { + Iterable<TrackedIssue> getIssues() { return issueCache.all(); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/ReportSummary.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ReportSummary.java index 2ebb4a2f9a9..45af927e0bd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/ReportSummary.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ReportSummary.java @@ -19,13 +19,16 @@ */ package org.sonar.batch.scan.report; +import org.sonar.batch.issue.tracking.TrackedIssue; + import com.google.common.collect.Maps; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; + import org.sonar.api.batch.rule.Rule; -import org.sonar.api.issue.Issue; import org.sonar.api.rules.RulePriority; public class ReportSummary { @@ -40,7 +43,7 @@ public class ReportSummary { return total; } - public void addIssue(Issue issue, Rule rule, RulePriority severity) { + public void addIssue(TrackedIssue issue, Rule rule, RulePriority severity) { ReportRuleKey reportRuleKey = new ReportRuleKey(rule, severity); initMaps(reportRuleKey); ruleReportByRuleKey.get(reportRuleKey).getTotal().incrementCountInCurrentAnalysis(); @@ -63,7 +66,7 @@ public class ReportSummary { return totalByRuleKey; } - public void addResolvedIssue(Issue issue, Rule rule, RulePriority severity) { + public void addResolvedIssue(TrackedIssue issue, Rule rule, RulePriority severity) { ReportRuleKey reportRuleKey = new ReportRuleKey(rule, severity); initMaps(reportRuleKey); total.incrementResolvedIssuesCount(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/ResourceReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ResourceReport.java index 2a6135e5c17..a088d8361bb 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/ResourceReport.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ResourceReport.java @@ -19,10 +19,10 @@ */ package org.sonar.batch.scan.report; -import org.sonar.api.batch.rule.Rule; +import org.sonar.batch.issue.tracking.TrackedIssue; +import org.sonar.api.batch.rule.Rule; import com.google.common.collect.Maps; -import org.sonar.api.issue.Issue; import org.sonar.api.rules.RulePriority; import org.sonar.batch.index.BatchComponent; @@ -37,9 +37,9 @@ public final class ResourceReport { private final IssueVariation total = new IssueVariation(); private final Map<ReportRuleKey, RuleReport> ruleReportByRuleKey = Maps.newHashMap(); - private List<Issue> issues = new ArrayList<>(); - private Map<Integer, List<Issue>> issuesPerLine = Maps.newHashMap(); - private Map<Integer, List<Issue>> newIssuesPerLine = Maps.newHashMap(); + private List<TrackedIssue> issues = new ArrayList<>(); + private Map<Integer, List<TrackedIssue>> issuesPerLine = Maps.newHashMap(); + private Map<Integer, List<TrackedIssue>> newIssuesPerLine = Maps.newHashMap(); private Map<Rule, AtomicInteger> issuesByRule = Maps.newHashMap(); private Map<RulePriority, AtomicInteger> issuesBySeverity = Maps.newHashMap(); @@ -63,15 +63,15 @@ public final class ResourceReport { return total; } - public List<Issue> getIssues() { + public List<TrackedIssue> getIssues() { return issues; } - public Map<Integer, List<Issue>> getIssuesPerLine() { + public Map<Integer, List<TrackedIssue>> getIssuesPerLine() { return issuesPerLine; } - public List<Issue> getIssuesAtLine(int lineId, boolean all) { + public List<TrackedIssue> getIssuesAtLine(int lineId, boolean all) { if (all) { if (issuesPerLine.containsKey(lineId)) { return issuesPerLine.get(lineId); @@ -82,14 +82,14 @@ public final class ResourceReport { return Collections.emptyList(); } - public void addIssue(Issue issue, Rule rule, RulePriority severity) { + public void addIssue(TrackedIssue issue, Rule rule, RulePriority severity) { ReportRuleKey reportRuleKey = new ReportRuleKey(rule, severity); initMaps(reportRuleKey); issues.add(issue); - Integer line = issue.line(); + Integer line = issue.startLine(); line = line != null ? line : 0; if (!issuesPerLine.containsKey(line)) { - issuesPerLine.put(line, new ArrayList<Issue>()); + issuesPerLine.put(line, new ArrayList<TrackedIssue>()); } issuesPerLine.get(line).add(issue); if (!issuesByRule.containsKey(rule)) { @@ -104,7 +104,7 @@ public final class ResourceReport { total.incrementCountInCurrentAnalysis(); if (issue.isNew()) { if (!newIssuesPerLine.containsKey(line)) { - newIssuesPerLine.put(line, new ArrayList<Issue>()); + newIssuesPerLine.put(line, new ArrayList<TrackedIssue>()); } newIssuesPerLine.get(line).add(issue); total.incrementNewIssuesCount(); @@ -112,7 +112,7 @@ public final class ResourceReport { } } - public void addResolvedIssue(Issue issue, Rule rule, RulePriority severity) { + public void addResolvedIssue(Rule rule, RulePriority severity) { ReportRuleKey reportRuleKey = new ReportRuleKey(rule, severity); initMaps(reportRuleKey); total.incrementResolvedIssuesCount(); @@ -139,10 +139,10 @@ public final class ResourceReport { private boolean hasIssues(Integer lineId, boolean all) { if (all) { - List<Issue> issuesAtLine = issuesPerLine.get(lineId); + List<TrackedIssue> issuesAtLine = issuesPerLine.get(lineId); return issuesAtLine != null && !issuesAtLine.isEmpty(); } - List<Issue> newIssuesAtLine = newIssuesPerLine.get(lineId); + List<TrackedIssue> newIssuesAtLine = newIssuesPerLine.get(lineId); return newIssuesAtLine != null && !newIssuesAtLine.isEmpty(); } diff --git a/sonar-batch/src/main/resources/org/sonar/batch/scan/report/issuesreport.ftl b/sonar-batch/src/main/resources/org/sonar/batch/scan/report/issuesreport.ftl index 3c5827f43e8..28101545651 100644 --- a/sonar-batch/src/main/resources/org/sonar/batch/scan/report/issuesreport.ftl +++ b/sonar-batch/src/main/resources/org/sonar/batch/scan/report/issuesreport.ftl @@ -13,7 +13,7 @@ <#assign issues=resourceReport.getIssues()> <#list issues as issue> <#if complete || issue.isNew()> - {'k': '${issue.key()}', 'r': 'R${issue.ruleKey()}', 'l': ${(issue.line()!0)?c}, 'new': ${issue.isNew()?string}, 's': '${issue.severity()?lower_case}'}<#if issue_has_next>,</#if> + {'k': '${issue.key()}', 'r': 'R${issue.ruleKey()}', 'l': ${(issue.startLine()!0)?c}, 'new': ${issue.isNew()?string}, 's': '${issue.severity()?lower_case}'}<#if issue_has_next>,</#if> </#if> </#list> ] diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java index de1b59fed36..ea8ca35d831 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java @@ -21,6 +21,9 @@ package org.sonar.batch.issue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; + +import org.sonar.batch.issue.tracking.TrackedIssue; + import org.sonar.api.batch.rule.Rule; import org.sonar.api.rule.RuleKey; import org.sonar.batch.bootstrapper.IssueListener.Issue; @@ -32,13 +35,11 @@ import org.sonar.batch.repository.user.UserRepositoryLoader; import org.sonar.batch.bootstrapper.IssueListener; import org.junit.Before; import com.google.common.collect.ImmutableList; -import org.sonar.core.issue.DefaultIssue; import java.util.LinkedList; import java.util.List; import static org.mockito.Matchers.any; - import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; @@ -50,14 +51,14 @@ public class DefaultIssueCallbackTest { @Mock private Rules rules; - private DefaultIssue issue; + private TrackedIssue issue; @Before public void setUp() { MockitoAnnotations.initMocks(this); RuleKey ruleKey = RuleKey.of("repo", "key"); - issue = new DefaultIssue(); + issue = new TrackedIssue(); issue.setKey("key"); issue.setAssignee("user"); issue.setRuleKey(ruleKey); diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java index 99c6d5f7100..c4af828349b 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.issue; +import org.sonar.batch.issue.tracking.TrackedIssue; + import com.google.common.collect.Lists; import org.junit.Test; import org.sonar.api.issue.Issue; @@ -49,9 +51,9 @@ public class DefaultProjectIssuesTest { DefaultIssue issueOnRoot = new DefaultIssue().setKey("4").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts"); DefaultIssue issueInRoot = new DefaultIssue().setKey("5").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts:FileInRoot"); - when(cache.all()).thenReturn(Arrays.<DefaultIssue> asList( - issueOnRoot, issueInRoot, - issueOnModule, issueInModule, resolvedIssueInModule + when(cache.all()).thenReturn(Arrays.<TrackedIssue>asList( + toTrackedIssue(issueOnRoot), toTrackedIssue(issueInRoot), + toTrackedIssue(issueOnModule), toTrackedIssue(issueInModule), toTrackedIssue(resolvedIssueInModule) )); // unresolved issues @@ -61,4 +63,16 @@ public class DefaultProjectIssuesTest { List<Issue> resolvedIssues = Lists.newArrayList(projectIssues.resolvedIssues()); assertThat(resolvedIssues).containsOnly(resolvedIssueInModule); } + + private TrackedIssue toTrackedIssue(DefaultIssue issue) { + TrackedIssue trackedIssue = new TrackedIssue(); + + trackedIssue.setKey(issue.key()); + trackedIssue.setRuleKey(issue.ruleKey()); + trackedIssue.setComponentKey(issue.componentKey()); + trackedIssue.setSeverity(issue.severity()); + trackedIssue.setResolution(issue.resolution()); + + return trackedIssue; + } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/IssueCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/IssueCacheTest.java index 18e90aff802..7d752856ff6 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/IssueCacheTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/IssueCacheTest.java @@ -19,19 +19,17 @@ */ package org.sonar.batch.issue; -import org.sonar.batch.index.AbstractCachesTest; +import org.sonar.batch.issue.tracking.TrackedIssue; +import org.sonar.batch.index.AbstractCachesTest; import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import org.junit.Test; -import org.sonar.api.issue.Issue; -import org.sonar.core.issue.DefaultIssue; import org.sonar.api.rule.Severity; import javax.annotation.Nullable; -import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -42,28 +40,29 @@ public class IssueCacheTest extends AbstractCachesTest { @Test public void should_add_new_issue() { IssueCache cache = new IssueCache(caches); - DefaultIssue issue1 = new DefaultIssue().setKey("111").setComponentKey("org.struts.Action"); - DefaultIssue issue2 = new DefaultIssue().setKey("222").setComponentKey("org.struts.Action"); - DefaultIssue issue3 = new DefaultIssue().setKey("333").setComponentKey("org.struts.Filter").setTags(Arrays.asList("foo", "bar")); + TrackedIssue issue1 = createIssue("111", "org.struts.Action", null); + TrackedIssue issue2 = createIssue("222", "org.struts.Action", null); + TrackedIssue issue3 = createIssue("333", "org.struts.Filter", null); + issue3.setAssignee("foo"); cache.put(issue1).put(issue2).put(issue3); assertThat(issueKeys(cache.byComponent("org.struts.Action"))).containsOnly("111", "222"); assertThat(issueKeys(cache.byComponent("org.struts.Filter"))).containsOnly("333"); - assertThat(cache.byComponent("org.struts.Filter").iterator().next().tags()).containsOnly("foo", "bar"); + assertThat(cache.byComponent("org.struts.Filter").iterator().next().assignee()).isEqualTo("foo"); } @Test public void should_update_existing_issue() { IssueCache cache = new IssueCache(caches); - DefaultIssue issue = new DefaultIssue().setKey("111").setComponentKey("org.struts.Action").setSeverity(Severity.BLOCKER); + TrackedIssue issue = createIssue("111", "org.struts.Action", Severity.BLOCKER); cache.put(issue); issue.setSeverity(Severity.MINOR); cache.put(issue); - List<DefaultIssue> issues = ImmutableList.copyOf(cache.byComponent("org.struts.Action")); + List<TrackedIssue> issues = ImmutableList.copyOf(cache.byComponent("org.struts.Action")); assertThat(issues).hasSize(1); - Issue reloaded = issues.iterator().next(); + TrackedIssue reloaded = issues.iterator().next(); assertThat(reloaded.key()).isEqualTo("111"); assertThat(reloaded.severity()).isEqualTo(Severity.MINOR); } @@ -71,20 +70,29 @@ public class IssueCacheTest extends AbstractCachesTest { @Test public void should_get_all_issues() { IssueCache cache = new IssueCache(caches); - DefaultIssue issue1 = new DefaultIssue().setKey("111").setComponentKey("org.struts.Action").setSeverity(Severity.BLOCKER); - DefaultIssue issue2 = new DefaultIssue().setKey("222").setComponentKey("org.struts.Filter").setSeverity(Severity.INFO); + TrackedIssue issue1 = createIssue("111", "org.struts.Action", Severity.BLOCKER); + TrackedIssue issue2 = createIssue("222", "org.struts.Filter", Severity.INFO); cache.put(issue1).put(issue2); - List<DefaultIssue> issues = ImmutableList.copyOf(cache.all()); + List<TrackedIssue> issues = ImmutableList.copyOf(cache.all()); assertThat(issues).containsOnly(issue1, issue2); } - private Collection<String> issueKeys(Iterable<DefaultIssue> issues) { - return Collections2.transform(ImmutableList.copyOf(issues), new Function<DefaultIssue, String>() { + private Collection<String> issueKeys(Iterable<TrackedIssue> issues) { + return Collections2.transform(ImmutableList.copyOf(issues), new Function<TrackedIssue, String>() { @Override - public String apply(@Nullable DefaultIssue issue) { + public String apply(@Nullable TrackedIssue issue) { return issue.key(); } }); } + + private TrackedIssue createIssue(String key, String componentKey, String severity) { + TrackedIssue issue = new TrackedIssue(); + issue.setKey(key); + issue.setComponentKey(componentKey); + issue.setSeverity(severity); + + return issue; + } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java index 2226796cc40..80c35ec1286 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java @@ -88,6 +88,10 @@ public class IssuesMediumTest { List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo")); assertThat(issues).hasSize(8 /* lines */); + + Issue issue = issues.get(0); + assertThat(issue.getTextRange().getStartLine()).isEqualTo(issue.getLine()); + assertThat(issue.getTextRange().getEndLine()).isEqualTo(issue.getLine()); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java index 586bba47896..42c092dc536 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.mediumtest.issuesmode; -import org.apache.commons.io.filefilter.FileFilterUtils; +import org.sonar.batch.issue.tracking.TrackedIssue; +import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.commons.io.FileUtils; import org.sonar.xoo.rule.XooRulesDefinition; import com.google.common.collect.ImmutableMap; @@ -76,6 +77,10 @@ public class EmptyFileTest { .property("sonar.xoo.internalKey", "my/internal/key") .start(); + for(TrackedIssue i : result.trackedIssues()) { + System.out.println(i.startLine() + " " + i.message()); + } + assertThat(result.trackedIssues()).hasSize(11); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java index 0e0ee39b9e3..5916ff1a81e 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java @@ -19,7 +19,10 @@ */ package org.sonar.batch.mediumtest.issuesmode; +import org.sonar.batch.issue.tracking.TrackedIssue; + import com.google.common.collect.ImmutableMap; + import java.io.File; import java.io.IOException; import java.text.ParseException; @@ -27,6 +30,7 @@ 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; @@ -35,7 +39,6 @@ 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.utils.log.LogTester; import org.sonar.batch.bootstrapper.IssueListener; import org.sonar.batch.mediumtest.BatchMediumTester; @@ -45,7 +48,6 @@ import org.sonar.batch.protocol.input.BatchInput.ServerIssue; import org.sonar.batch.scan.report.ConsoleReport; import org.sonar.xoo.XooPlugin; import org.sonar.xoo.rule.XooRulesDefinition; - import static org.assertj.core.api.Assertions.assertThat; public class IssueModeAndReportsMediumTest { @@ -153,9 +155,10 @@ public class IssueModeAndReportsMediumTest { int newIssues = 0; int openIssues = 0; int resolvedIssue = 0; - for (Issue issue : result.trackedIssues()) { + for (TrackedIssue issue : result.trackedIssues()) { System.out - .println(issue.message() + " " + issue.key() + " " + issue.ruleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " " + issue.line()); + .println(issue.message() + " " + issue.key() + " " + issue.ruleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " " + + issue.startLine()); if (issue.isNew()) { newIssues++; } else if (issue.resolution() != null) { diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java index 2141351e852..855d6b6c986 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.mediumtest.issuesmode; +import org.sonar.batch.issue.tracking.TrackedIssue; + import org.assertj.core.api.Condition; import com.google.common.io.Resources; import org.sonar.batch.repository.FileData; @@ -37,7 +39,6 @@ import org.sonar.xoo.rule.XooRulesDefinition; import org.junit.rules.TemporaryFolder; import org.sonar.api.utils.log.LogTester; import org.junit.Test; -import org.sonar.api.issue.Issue; import org.sonar.batch.mediumtest.TaskResult; import java.io.File; @@ -178,9 +179,9 @@ public class ScanOnlyChangedTest { } private static void assertNumberIssuesOnFile(TaskResult result, final String fileNameEndsWith, int issues) { - assertThat(result.trackedIssues()).haveExactly(issues, new Condition<Issue>() { + assertThat(result.trackedIssues()).haveExactly(issues, new Condition<TrackedIssue>() { @Override - public boolean matches(Issue value) { + public boolean matches(TrackedIssue value) { return value.componentKey().endsWith(fileNameEndsWith); } }); @@ -190,9 +191,10 @@ public class ScanOnlyChangedTest { int newIssues = 0; int openIssues = 0; int resolvedIssue = 0; - for (Issue issue : result.trackedIssues()) { + for (TrackedIssue issue : result.trackedIssues()) { System.out - .println(issue.message() + " " + issue.key() + " " + issue.ruleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " " + issue.line()); + .println(issue.message() + " " + issue.key() + " " + issue.ruleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " " + + issue.startLine()); if (issue.isNew()) { newIssues++; } else if (issue.resolution() != null) { diff --git a/sonar-batch/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java b/sonar-batch/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java index a350ba4a952..fa3fb23b031 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java @@ -19,7 +19,10 @@ */ package org.sonar.batch.postjob; +import org.sonar.batch.issue.tracking.TrackedIssue; + import java.util.Arrays; + import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.AnalysisMode; @@ -30,8 +33,6 @@ import org.sonar.api.config.Settings; import org.sonar.api.resources.File; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.issue.IssueCache; -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; @@ -58,12 +59,12 @@ public class DefaultPostJobContextTest { assertThat(context.settings()).isSameAs(settings); assertThat(context.analysisMode()).isSameAs(analysisMode); - DefaultIssue defaultIssue = new DefaultIssue(); + TrackedIssue defaultIssue = new TrackedIssue(); defaultIssue.setComponentKey("foo:src/Foo.php"); defaultIssue.setEffortToFix(2.0); defaultIssue.setNew(true); defaultIssue.setKey("xyz"); - defaultIssue.setLine(1); + defaultIssue.setStartLine(1); defaultIssue.setMessage("msg"); defaultIssue.setSeverity("BLOCKER"); when(issueCache.all()).thenReturn(Arrays.asList(defaultIssue)); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java index fc3355952c0..998192b7985 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java @@ -19,13 +19,15 @@ */ package org.sonar.batch.scan.report; +import javax.annotation.Nullable; + +import org.sonar.batch.issue.tracking.TrackedIssue; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.config.Settings; -import org.sonar.core.issue.DefaultIssue; import org.sonar.api.rule.Severity; import org.sonar.api.utils.log.LogTester; import org.sonar.batch.issue.IssueCache; @@ -68,7 +70,7 @@ public class ConsoleReportTest { public void testNoFile() { settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); when(inputPathCache.allFiles()).thenReturn(Collections.<InputFile>emptyList()); - when(issueCache.all()).thenReturn(Collections.<DefaultIssue>emptyList()); + when(issueCache.all()).thenReturn(Collections.<TrackedIssue>emptyList()); report.execute(); assertThat(getReportLog()).isEqualTo( "\n\n------------- Issues Report -------------\n\n" + @@ -80,7 +82,7 @@ public class ConsoleReportTest { public void testNoNewIssue() { settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php"))); - when(issueCache.all()).thenReturn(Arrays.asList(new DefaultIssue().setNew(false))); + when(issueCache.all()).thenReturn(Arrays.asList(createIssue(false, null))); report.execute(); assertThat(getReportLog()).isEqualTo( "\n\n------------- Issues Report -------------\n\n" + @@ -92,7 +94,7 @@ public class ConsoleReportTest { public void testOneNewIssue() { settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php"))); - when(issueCache.all()).thenReturn(Arrays.asList(new DefaultIssue().setNew(true).setSeverity(Severity.BLOCKER))); + when(issueCache.all()).thenReturn(Arrays.asList(createIssue(true, Severity.BLOCKER))); report.execute(); assertThat(getReportLog()).isEqualTo( "\n\n------------- Issues Report -------------\n\n" + @@ -105,11 +107,12 @@ public class ConsoleReportTest { public void testOneNewIssuePerSeverity() { settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php"))); - when(issueCache.all()).thenReturn(Arrays.asList(new DefaultIssue().setNew(true).setSeverity(Severity.BLOCKER), - new DefaultIssue().setNew(true).setSeverity(Severity.CRITICAL), - new DefaultIssue().setNew(true).setSeverity(Severity.MAJOR), - new DefaultIssue().setNew(true).setSeverity(Severity.MINOR), - new DefaultIssue().setNew(true).setSeverity(Severity.INFO))); + when(issueCache.all()).thenReturn(Arrays.asList( + createIssue(true, Severity.BLOCKER), + createIssue(true, Severity.CRITICAL), + createIssue(true, Severity.MAJOR), + createIssue(true, Severity.MINOR), + createIssue(true, Severity.INFO))); report.execute(); assertThat(getReportLog()).isEqualTo( "\n\n------------- Issues Report -------------\n\n" + @@ -131,4 +134,12 @@ public class ConsoleReportTest { throw new IllegalStateException("No console report"); } + private TrackedIssue createIssue(boolean isNew, @Nullable String severity) { + TrackedIssue issue = new TrackedIssue(); + issue.setNew(isNew); + issue.setSeverity(severity); + + return issue; + } + } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java index 92f8207baac..0cf45185cfe 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.scan.report; +import org.sonar.batch.issue.tracking.TrackedIssue; + import com.google.common.collect.Lists; import java.io.File; @@ -49,7 +51,6 @@ import org.sonar.batch.issue.IssueCache; import org.sonar.batch.protocol.input.BatchInput; import org.sonar.batch.repository.user.UserRepositoryLoader; import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.core.issue.DefaultIssue; import org.sonar.test.JsonAssert; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -99,21 +100,23 @@ public class JSONReportTest { @Test public void should_write_json() throws Exception { - DefaultIssue issue = new DefaultIssue() - .setKey("200") - .setComponentKey("struts:src/main/java/org/apache/struts/Action.java") - .setRuleKey(RuleKey.of("squid", "AvoidCycles")) - .setMessage("There are 2 cycles") - .setSeverity("MINOR") - .setStatus(Issue.STATUS_OPEN) - .setResolution(null) - .setLine(1) - .setEffortToFix(3.14) - .setReporter("julien") - .setAssignee("simon") - .setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24")) - .setUpdateDate(SIMPLE_DATE_FORMAT.parse("2013-04-25")) - .setNew(false); + TrackedIssue issue = new TrackedIssue(); + issue.setKey("200"); + issue.setComponentKey("struts:src/main/java/org/apache/struts/Action.java"); + issue.setRuleKey(RuleKey.of("squid", "AvoidCycles")); + issue.setMessage("There are 2 cycles"); + issue.setSeverity("MINOR"); + issue.setStatus(Issue.STATUS_OPEN); + issue.setResolution(null); + issue.setStartLine(1); + issue.setEndLine(2); + issue.setStartLineOffset(3); + issue.setEndLineOffset(4); + issue.setEffortToFix(3.14); + issue.setReporter("julien"); + issue.setAssignee("simon"); + issue.setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24")); + issue.setNew(false); when(issueCache.all()).thenReturn(Lists.newArrayList(issue)); BatchInput.User user1 = BatchInput.User.newBuilder().setLogin("julien").setName("Julien").build(); BatchInput.User user2 = BatchInput.User.newBuilder().setLogin("simon").setName("Simon").build(); @@ -129,16 +132,14 @@ public class JSONReportTest { @Test public void should_exclude_resolved_issues() throws Exception { RuleKey ruleKey = RuleKey.of("squid", "AvoidCycles"); - DefaultIssue issue = new DefaultIssue() - .setKey("200") - .setComponentKey("struts:src/main/java/org/apache/struts/Action.java") - .setRuleKey(ruleKey) - .setStatus(Issue.STATUS_CLOSED) - .setResolution(Issue.RESOLUTION_FIXED) - .setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24")) - .setUpdateDate(SIMPLE_DATE_FORMAT.parse("2013-04-25")) - .setCloseDate(SIMPLE_DATE_FORMAT.parse("2013-04-26")) - .setNew(false); + TrackedIssue issue = new TrackedIssue(); + issue.setKey("200"); + issue.setComponentKey("struts:src/main/java/org/apache/struts/Action.java"); + issue.setRuleKey(ruleKey); + issue.setStatus(Issue.STATUS_CLOSED); + issue.setResolution(Issue.RESOLUTION_FIXED); + issue.setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24")); + issue.setNew(false); when(issueCache.all()).thenReturn(Lists.newArrayList(issue)); StringWriter writer = new StringWriter(); @@ -149,7 +150,7 @@ public class JSONReportTest { @Test public void should_ignore_components_without_issue() { - when(issueCache.all()).thenReturn(Collections.<DefaultIssue>emptyList()); + when(issueCache.all()).thenReturn(Collections.<TrackedIssue>emptyList()); StringWriter writer = new StringWriter(); jsonReport.writeJson(writer); @@ -172,7 +173,7 @@ public class JSONReportTest { File workDir = temp.newFolder("sonar"); fs.setWorkDir(workDir); - when(issueCache.all()).thenReturn(Collections.<DefaultIssue>emptyList()); + when(issueCache.all()).thenReturn(Collections.<TrackedIssue>emptyList()); settings.setProperty("sonar.report.export.path", "output.json"); diff --git a/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JSONReportTest/report.json b/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JSONReportTest/report.json index 91c1e3a3eac..d05703e5d30 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JSONReportTest/report.json +++ b/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JSONReportTest/report.json @@ -4,7 +4,10 @@ { "key": "200", "component": "struts:src/main/java/org/apache/struts/Action.java", - "line": 1, + "startLine": 1, + "startOffset": 3, + "endLine": 2, + "endOffset": 4, "message": "There are 2 cycles", "severity": "MINOR", "rule": "squid:AvoidCycles", |