diff options
9 files changed, 280 insertions, 47 deletions
diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/Issue.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/Issue.java index 23b81e4..7c9a89f 100644 --- a/sonar-runner-api/src/main/java/org/sonar/runner/api/Issue.java +++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/Issue.java @@ -25,7 +25,6 @@ import javax.annotation.concurrent.Immutable; public final class Issue { private final String key; private final String componentKey; - private final Integer line; private final String message; private final String ruleKey; private final String ruleName; @@ -35,13 +34,16 @@ public final class Issue { private final String assigneeLogin; private final String assigneeName; private final String severity; + private final Integer startLine; + private final Integer startLineOffset; + private final Integer endLine; + private final Integer endLineOffset; - private Issue(String key, String componentKey, Integer line, String message, String ruleKey, String ruleName, String status, String resolution, boolean isNew, - String assigneeLogin, String assigneeName, String severity) { + private Issue(String key, String componentKey, String message, String ruleKey, String ruleName, String status, String resolution, boolean isNew, + String assigneeLogin, String assigneeName, String severity, Integer startLine, Integer startLineOffset, Integer endLine, Integer endLineOffset) { super(); this.key = key; this.componentKey = componentKey; - this.line = line; this.message = message; this.ruleKey = ruleKey; this.ruleName = ruleName; @@ -51,12 +53,15 @@ public final class Issue { this.assigneeLogin = assigneeLogin; this.assigneeName = assigneeName; this.severity = severity; + this.startLine = startLine; + this.startLineOffset = startLineOffset; + this.endLine = endLine; + this.endLineOffset = endLineOffset; } public static class Builder { private String key; private String componentKey; - private Integer line; private String message; private String ruleKey; private String ruleName; @@ -66,6 +71,46 @@ public final class Issue { private String assigneeLogin; private String assigneeName; private String severity; + private Integer startLine; + private Integer startLineOffset; + private Integer endLine; + private Integer endLineOffset; + + public Integer getStartLine() { + return startLine; + } + + public Builder setStartLine(Integer startLine) { + this.startLine = startLine; + return this; + } + + public Integer getStartLineOffset() { + return startLineOffset; + } + + public Builder setStartLineOffset(Integer startLineOffset) { + this.startLineOffset = startLineOffset; + return this; + } + + public Integer getEndLine() { + return endLine; + } + + public Builder setEndLine(Integer endLine) { + this.endLine = endLine; + return this; + } + + public Integer getEndLineOffset() { + return endLineOffset; + } + + public Builder setEndLineOffset(Integer endLineOffset) { + this.endLineOffset = endLineOffset; + return this; + } public String getKey() { return key; @@ -85,15 +130,6 @@ public final class Issue { return this; } - public Integer getLine() { - return line; - } - - public Builder setLine(Integer line) { - this.line = line; - return this; - } - public String getMessage() { return message; } @@ -176,7 +212,8 @@ public final class Issue { } public Issue build() { - return new Issue(key, componentKey, line, message, ruleKey, ruleName, status, resolution, isNew, assigneeLogin, assigneeName, severity); + return new Issue(key, componentKey, message, ruleKey, ruleName, status, resolution, isNew, assigneeLogin, + assigneeName, severity, startLine, startLineOffset, endLine, endLineOffset); } } @@ -192,8 +229,26 @@ public final class Issue { return componentKey; } - public Integer getLine() { - return line; + public Integer getStartLine() { + return startLine; + } + + /** + * @return <code>null</code> if it isn't supported by the sonar-batch being used (< 5.3). + */ + public Integer getStartLineOffset() { + return startLineOffset; + } + + public Integer getEndLine() { + return endLine; + } + + /** + * @return <code>null</code> if it isn't supported by the sonar-batch being used (< 5.3). + */ + public Integer getEndLineOffset() { + return endLineOffset; } public String getMessage() { diff --git a/sonar-runner-api/src/main/java/org/sonar/runner/api/IssueListenerAdapter.java b/sonar-runner-api/src/main/java/org/sonar/runner/api/IssueListenerAdapter.java index 88b1879..cb14175 100644 --- a/sonar-runner-api/src/main/java/org/sonar/runner/api/IssueListenerAdapter.java +++ b/sonar-runner-api/src/main/java/org/sonar/runner/api/IssueListenerAdapter.java @@ -38,7 +38,6 @@ class IssueListenerAdapter implements org.sonar.runner.batch.IssueListener { issueBuilder.setAssigneeName(batchIssue.getAssigneeName()); issueBuilder.setComponentKey(batchIssue.getComponentKey()); issueBuilder.setKey(batchIssue.getKey()); - issueBuilder.setLine(batchIssue.getLine()); issueBuilder.setMessage(batchIssue.getMessage()); issueBuilder.setNew(batchIssue.isNew()); issueBuilder.setResolution(batchIssue.getResolution()); @@ -46,6 +45,10 @@ class IssueListenerAdapter implements org.sonar.runner.batch.IssueListener { issueBuilder.setRuleName(batchIssue.getRuleName()); issueBuilder.setSeverity(batchIssue.getSeverity()); issueBuilder.setStatus(batchIssue.getStatus()); + issueBuilder.setStartLine(batchIssue.getStartLine()); + issueBuilder.setStartLineOffset(batchIssue.getStartLineOffset()); + issueBuilder.setEndLine(batchIssue.getEndLine()); + issueBuilder.setEndLineOffset(batchIssue.getEndLineOffset()); return issueBuilder.build(); } diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/api/IssueListenerAdapterTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/IssueListenerAdapterTest.java index 0b5e2a9..67b00a7 100644 --- a/sonar-runner-api/src/test/java/org/sonar/runner/api/IssueListenerAdapterTest.java +++ b/sonar-runner-api/src/test/java/org/sonar/runner/api/IssueListenerAdapterTest.java @@ -43,11 +43,15 @@ public class IssueListenerAdapterTest { org.sonar.runner.batch.IssueListener.Issue issue = new org.sonar.runner.batch.IssueListener.Issue(); issue.setAssigneeName("dummy"); + issue.setStartLineOffset(2); adapter.handle(issue); ArgumentCaptor<Issue> argument = ArgumentCaptor.forClass(Issue.class); verify(issueListener).handle(argument.capture()); - assertThat(argument.getValue().getAssigneeName()).isEqualTo("dummy"); + Issue i = argument.getValue(); + + assertThat(i.getAssigneeName()).isEqualTo("dummy"); + assertThat(i.getStartLineOffset()).isEqualTo(2); } } diff --git a/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IssueListener.java b/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IssueListener.java index f6b596f..5fdc004 100644 --- a/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IssueListener.java +++ b/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/IssueListener.java @@ -23,9 +23,13 @@ public interface IssueListener { void handle(Issue issue); class Issue { + private Integer startLine; + private Integer startLineOffset; + private Integer endLine; + private Integer endLineOffset; + private String key; private String componentKey; - private Integer line; private String message; private String ruleKey; private String ruleName; @@ -60,14 +64,38 @@ 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 void setLine(Integer line) { - this.line = line; + public Integer getEndLine() { + return endLine; } + public void setEndLine(Integer endLine) { + this.endLine = endLine; + } + + public Integer getEndLineOffset() { + return endLineOffset; + } + + public void setEndLineOffset(Integer endLineOffset) { + this.endLineOffset = endLineOffset; + } + public String getMessage() { return message; } @@ -131,6 +159,5 @@ public interface IssueListener { public void setAssigneeName(String assigneeName) { this.assigneeName = assigneeName; } - } } diff --git a/sonar-runner-batch/src/main/java/org/sonar/batch/bootstrapper/IssueListener.java b/sonar-runner-batch/src/main/java/org/sonar/batch/bootstrapper/IssueListener.java index 193ae46..5b4ba1b 100644 --- a/sonar-runner-batch/src/main/java/org/sonar/batch/bootstrapper/IssueListener.java +++ b/sonar-runner-batch/src/main/java/org/sonar/batch/bootstrapper/IssueListener.java @@ -23,9 +23,20 @@ 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; + + /** no longer exists since 5.3 */ + private Integer line; + private String key; private String componentKey; - private Integer line; private String message; private String ruleKey; private String ruleName; @@ -60,6 +71,38 @@ public interface IssueListener { this.componentKey = componentKey; } + 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 setEndLineOffset(Integer endLineOffset) { + this.endLineOffset = endLineOffset; + } + public Integer getLine() { return line; } diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java index 1ac82f1..da3716c 100644 --- a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java +++ b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java @@ -27,6 +27,8 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.sonar.batch.bootstrapper.Batch; @@ -35,15 +37,18 @@ import org.sonar.batch.bootstrapper.Batch; * the same version of sonar-batch as the server. */ public class BatchIsolatedLauncher implements IsolatedLauncher { + private static final String VERSION_FORMAT = "^(\\d+)\\.(\\d+)"; private Batch batch = null; - private BatchFactory factory = null; + private final BatchFactory factory; + private final Pattern versionPattern; public BatchIsolatedLauncher() { - this.factory = new DefaultBatchFactory(); + this(new DefaultBatchFactory()); } public BatchIsolatedLauncher(BatchFactory factory) { this.factory = factory; + this.versionPattern = Pattern.compile(VERSION_FORMAT); } @Override @@ -64,7 +69,7 @@ public class BatchIsolatedLauncher implements IsolatedLauncher { @Override public void execute(Properties properties, IssueListener listener) { - org.sonar.batch.bootstrapper.IssueListener batchIssueListener = Compatibility.getBatchIssueListener(listener); + org.sonar.batch.bootstrapper.IssueListener batchIssueListener = Compatibility.getBatchIssueListener(listener, hasPreciseIssueLocation(getVersion())); batch.executeTask((Map) properties, batchIssueListener); } @@ -81,6 +86,22 @@ public class BatchIsolatedLauncher implements IsolatedLauncher { factory.createBatch(properties, null, extensions).execute(); } + boolean hasPreciseIssueLocation(String version) { + if (version == null) { + return false; + } + + Matcher matcher = versionPattern.matcher(version); + if (!matcher.find() || matcher.groupCount() < 2) { + return false; + } + + Integer major = Integer.parseInt(matcher.group(1)); + Integer minor = Integer.parseInt(matcher.group(2)); + + return major > 5 || (major == 5 && minor >= 3); + } + @Override public String getVersion() { InputStream is = this.getClass().getClassLoader().getResourceAsStream("sq-version.txt"); diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/Compatibility.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/Compatibility.java index f37e1b9..d932a50 100644 --- a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/Compatibility.java +++ b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/Compatibility.java @@ -38,15 +38,17 @@ public class Compatibility { }); } - static org.sonar.batch.bootstrapper.IssueListener getBatchIssueListener(IssueListener listener) { - return new IssueListenerAdapter(listener); + static org.sonar.batch.bootstrapper.IssueListener getBatchIssueListener(IssueListener listener, boolean hasPreciseLocation) { + return new IssueListenerAdapter(listener, hasPreciseLocation); } static class IssueListenerAdapter implements org.sonar.batch.bootstrapper.IssueListener { private IssueListener listener; + private boolean hasPreciseLocation; - public IssueListenerAdapter(IssueListener listener) { + public IssueListenerAdapter(IssueListener listener, boolean hasPreciseLocation) { this.listener = listener; + this.hasPreciseLocation = hasPreciseLocation; } @Override @@ -54,7 +56,7 @@ public class Compatibility { listener.handle(transformIssue(issue)); } - private static IssueListener.Issue transformIssue(Issue batchIssue) { + private IssueListener.Issue transformIssue(Issue batchIssue) { IssueListener.Issue newIssue = new IssueListener.Issue(); newIssue.setAssigneeLogin(batchIssue.getAssigneeLogin()); @@ -66,10 +68,19 @@ public class Compatibility { newIssue.setRuleName(batchIssue.getRuleName()); newIssue.setMessage(batchIssue.getMessage()); newIssue.setNew(batchIssue.isNew()); - newIssue.setLine(batchIssue.getLine()); newIssue.setSeverity(batchIssue.getSeverity()); newIssue.setStatus(batchIssue.getStatus()); + if (hasPreciseLocation) { + newIssue.setStartLine(batchIssue.getStartLine()); + newIssue.setStartLineOffset(batchIssue.getStartLineOffset()); + newIssue.setEndLine(batchIssue.getEndLine()); + newIssue.setEndLineOffset(batchIssue.getEndLineOffset()); + } else { + newIssue.setStartLine(batchIssue.getLine()); + newIssue.setEndLine(batchIssue.getLine()); + } + return newIssue; } } diff --git a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/BatchIsolatedLauncherTest.java b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/BatchIsolatedLauncherTest.java index d222a6d..cb6a9cd 100644 --- a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/BatchIsolatedLauncherTest.java +++ b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/BatchIsolatedLauncherTest.java @@ -28,8 +28,9 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import static org.mockito.Matchers.eq; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -51,6 +52,26 @@ public class BatchIsolatedLauncherTest { } @Test + public void supportsPreciseLocation() { + assertThat(launcher.hasPreciseIssueLocation("5.2.9")).isFalse(); + assertThat(launcher.hasPreciseIssueLocation("5.2")).isFalse(); + assertThat(launcher.hasPreciseIssueLocation("5.")).isFalse(); + assertThat(launcher.hasPreciseIssueLocation(null)).isFalse(); + assertThat(launcher.hasPreciseIssueLocation("4.5")).isFalse(); + assertThat(launcher.hasPreciseIssueLocation("4.5-SNAPSHOT")).isFalse(); + assertThat(launcher.hasPreciseIssueLocation("4-RC1")).isFalse(); + assertThat(launcher.hasPreciseIssueLocation("6")).isFalse(); + assertThat(launcher.hasPreciseIssueLocation("e6.0")).isFalse(); + + assertThat(launcher.hasPreciseIssueLocation("5.3")).isTrue(); + assertThat(launcher.hasPreciseIssueLocation("5.10.1")).isTrue(); + assertThat(launcher.hasPreciseIssueLocation("5.3.1")).isTrue(); + assertThat(launcher.hasPreciseIssueLocation("6.0")).isTrue(); + assertThat(launcher.hasPreciseIssueLocation("5.3-SNAPSHOT")).isTrue(); + assertThat(launcher.hasPreciseIssueLocation("5.3-RC1")).isTrue(); + } + + @Test public void executeOld() { Properties prop = new Properties(); List<Object> list = new LinkedList<>(); diff --git a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/CompatibilityTest.java b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/CompatibilityTest.java index 6bfa09c..1c3ae66 100644 --- a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/CompatibilityTest.java +++ b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/CompatibilityTest.java @@ -19,29 +19,62 @@ */ package org.sonar.runner.batch; +import static org.mockito.Mockito.times; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.assertj.core.api.Assertions.assertThat; +public class CompatibilityTest { + private IssueListener issueListener; -import org.mockito.ArgumentCaptor; -import org.junit.Test; + @Before + public void setUp() { + issueListener = mock(IssueListener.class); + } -public class CompatibilityTest { @Test public void test() { - IssueListener issueListener = mock(IssueListener.class); + org.sonar.batch.bootstrapper.IssueListener.Issue batchIssue = new org.sonar.batch.bootstrapper.IssueListener.Issue(); + setIssue(batchIssue); - org.sonar.batch.bootstrapper.IssueListener.Issue issue = new org.sonar.batch.bootstrapper.IssueListener.Issue(); - setIssue(issue); + org.sonar.batch.bootstrapper.IssueListener adaptedIssueListener = Compatibility.getBatchIssueListener(issueListener, false); - org.sonar.batch.bootstrapper.IssueListener adaptedIssueListener = Compatibility.getBatchIssueListener(issueListener); + adaptedIssueListener.handle(batchIssue); - adaptedIssueListener.handle(issue); + ArgumentCaptor<IssueListener.Issue> arg = ArgumentCaptor.forClass(IssueListener.Issue.class); + verify(issueListener).handle(arg.capture()); + assertIssue(arg.getValue(), false); + } + + @Test + public void testPrecise() { + org.sonar.batch.bootstrapper.IssueListener.Issue batchIssue = new org.sonar.batch.bootstrapper.IssueListener.Issue(); + setIssue(batchIssue); + + org.sonar.batch.bootstrapper.IssueListener adaptedIssueListener = Compatibility.getBatchIssueListener(issueListener, true); + + adaptedIssueListener.handle(batchIssue); ArgumentCaptor<IssueListener.Issue> arg = ArgumentCaptor.forClass(IssueListener.Issue.class); verify(issueListener).handle(arg.capture()); - assertIssue(arg.getValue()); + assertIssue(arg.getValue(), true); + } + + @Test + public void preciseIssueLocationCompatibility() { + org.sonar.batch.bootstrapper.IssueListener.Issue batchIssue = mock(org.sonar.batch.bootstrapper.IssueListener.Issue.class); + + org.sonar.batch.bootstrapper.IssueListener adaptedIssueListener = Compatibility.getBatchIssueListener(issueListener, false); + adaptedIssueListener.handle(batchIssue); + + verify(batchIssue, times(0)).getEndLine(); + verify(batchIssue, times(0)).getStartLine(); + verify(batchIssue, times(0)).getStartLineOffset(); + verify(batchIssue, times(0)).getEndLineOffset(); } private static void setIssue(org.sonar.batch.bootstrapper.IssueListener.Issue issue) { @@ -51,22 +84,37 @@ public class CompatibilityTest { issue.setMessage("msg"); issue.setAssigneeLogin("login"); issue.setLine(10); + issue.setStartLine(5); + issue.setEndLine(6); + issue.setStartLineOffset(1); + issue.setEndLineOffset(2); issue.setComponentKey("component"); issue.setSeverity("severity"); issue.setNew(true); issue.setStatus("status"); } - private static void assertIssue(IssueListener.Issue issue) { + private static void assertIssue(IssueListener.Issue issue, boolean precise) { assertThat(issue.getAssigneeName()).isEqualTo("name"); assertThat(issue.getRuleName()).isEqualTo("rule"); assertThat(issue.getRuleKey()).isEqualTo("key"); assertThat(issue.getMessage()).isEqualTo("msg"); assertThat(issue.getAssigneeLogin()).isEqualTo("login"); - assertThat(issue.getLine()).isEqualTo(10); assertThat(issue.getComponentKey()).isEqualTo("component"); assertThat(issue.getSeverity()).isEqualTo("severity"); assertThat(issue.isNew()).isEqualTo(true); assertThat(issue.getStatus()).isEqualTo("status"); + + if (precise) { + assertThat(issue.getStartLine()).isEqualTo(5); + assertThat(issue.getEndLine()).isEqualTo(6); + assertThat(issue.getStartLineOffset()).isEqualTo(1); + assertThat(issue.getEndLineOffset()).isEqualTo(2); + } else { + assertThat(issue.getStartLine()).isEqualTo(10); + assertThat(issue.getEndLine()).isEqualTo(10); + assertThat(issue.getStartLineOffset()).isEqualTo(null); + assertThat(issue.getEndLineOffset()).isEqualTo(null); + } } } |