Skip to content

Commit 52f3148

Browse files
pynicolasalban-auzeill
authored andcommitted
SONARPY-235 PylintImportSensor: use new sensor API
1 parent e0de0ef commit 52f3148

4 files changed

Lines changed: 79 additions & 90 deletions

File tree

sonar-python-plugin/src/main/java/org/sonar/plugins/python/pylint/PylintImportSensor.java

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,13 @@
2727
import javax.annotation.Nullable;
2828
import org.slf4j.Logger;
2929
import org.slf4j.LoggerFactory;
30-
import org.sonar.api.batch.SensorContext;
31-
import org.sonar.api.batch.fs.FilePredicates;
3230
import org.sonar.api.batch.fs.FileSystem;
3331
import org.sonar.api.batch.fs.InputFile;
3432
import org.sonar.api.batch.rule.ActiveRule;
35-
import org.sonar.api.batch.rule.ActiveRules;
36-
import org.sonar.api.component.ResourcePerspectives;
33+
import org.sonar.api.batch.sensor.SensorContext;
34+
import org.sonar.api.batch.sensor.issue.NewIssue;
3735
import org.sonar.api.config.Settings;
38-
import org.sonar.api.issue.Issuable;
39-
import org.sonar.api.resources.Project;
4036
import org.sonar.api.rule.RuleKey;
41-
import org.sonar.plugins.python.Python;
4237
import org.sonar.plugins.python.PythonReportSensor;
4338

4439
public class PylintImportSensor extends PythonReportSensor {
@@ -47,22 +42,20 @@ public class PylintImportSensor extends PythonReportSensor {
4742

4843
private static final Logger LOG = LoggerFactory.getLogger(PylintImportSensor.class);
4944

50-
private ActiveRules activeRules;
51-
private ResourcePerspectives resourcePerspectives;
52-
53-
public PylintImportSensor(Settings conf, ActiveRules activeRules, FileSystem fileSystem, ResourcePerspectives resourcePerspectives) {
54-
super(conf, fileSystem);
55-
56-
this.activeRules = activeRules;
57-
this.resourcePerspectives = resourcePerspectives;
45+
public PylintImportSensor(Settings conf) {
46+
super(conf);
5847
}
5948

6049
@Override
61-
public boolean shouldExecuteOnProject(Project project) {
62-
FilePredicates p = fileSystem.predicates();
63-
boolean hasFiles = fileSystem.hasFiles(p.and(p.hasType(InputFile.Type.MAIN), p.hasLanguage(Python.KEY)));
64-
boolean hasRules = !activeRules.findByRepository(PylintRuleRepository.REPOSITORY_KEY).isEmpty();
65-
return hasFiles && hasRules && conf.getString(REPORT_PATH_KEY) != null;
50+
public void execute(SensorContext context) {
51+
if (shouldExecute(context)) {
52+
super.execute(context);
53+
}
54+
}
55+
56+
private boolean shouldExecute(SensorContext context) {
57+
boolean hasRules = !context.activeRules().findByRepository(PylintRuleRepository.REPOSITORY_KEY).isEmpty();
58+
return hasRules && conf.getString(REPORT_PATH_KEY) != null;
6659
}
6760

6861
@Override
@@ -76,23 +69,22 @@ protected String defaultReportPath() {
7669
}
7770

7871
@Override
79-
protected void processReports(final SensorContext context, List<File> reports)
80-
throws javax.xml.stream.XMLStreamException {
72+
protected void processReports(final SensorContext context, List<File> reports) {
8173
List<Issue> issues = new LinkedList<>();
8274
for (File report : reports) {
8375
try {
84-
issues.addAll(parse(report));
76+
issues.addAll(parse(report, context.fileSystem()));
8577
} catch (java.io.FileNotFoundException e) {
8678
LOG.error("Report '{}' cannot be found, details: '{}'", report, e);
8779
} catch (IOException e) {
8880
LOG.error("Report '{}' cannot be read, details: '{}'", report, e);
8981
}
9082
}
9183

92-
saveIssues(issues);
84+
saveIssues(issues, context);
9385
}
9486

95-
private List<Issue> parse(File report) throws IOException {
87+
private static List<Issue> parse(File report, FileSystem fileSystem) throws IOException {
9688
List<Issue> issues = new LinkedList<>();
9789

9890
PylintReportParser parser = new PylintReportParser();
@@ -108,36 +100,34 @@ private List<Issue> parse(File report) throws IOException {
108100
return issues;
109101
}
110102

111-
private void saveIssues(List<Issue> issues) {
103+
private static void saveIssues(List<Issue> issues, SensorContext context) {
104+
FileSystem fileSystem = context.fileSystem();
112105
for (Issue pylintIssue : issues) {
113106
String filepath = pylintIssue.getFilename();
114107
InputFile pyfile = fileSystem.inputFile(fileSystem.predicates().hasPath(filepath));
115108
if (pyfile != null) {
116-
ActiveRule rule = activeRules.find(RuleKey.of(PylintRuleRepository.REPOSITORY_KEY, pylintIssue.getRuleId()));
117-
processRule(pylintIssue, pyfile, rule);
109+
ActiveRule rule = context.activeRules().find(RuleKey.of(PylintRuleRepository.REPOSITORY_KEY, pylintIssue.getRuleId()));
110+
processRule(pylintIssue, pyfile, rule, context);
118111
} else {
119112
LOG.warn("Cannot find the file '{}' in SonarQube, ignoring violation", filepath);
120113
}
121114
}
122115
}
123116

124-
private void processRule(Issue pylintIssue, InputFile pyfile, @Nullable ActiveRule rule) {
117+
private static void processRule(Issue pylintIssue, InputFile pyfile, @Nullable ActiveRule rule, SensorContext context) {
125118
if (rule != null) {
126-
Issuable issuable = resourcePerspectives.as(Issuable.class, pyfile);
127-
addIssue(pylintIssue, rule, issuable);
119+
NewIssue newIssue = context
120+
.newIssue()
121+
.forRule(rule.ruleKey());
122+
newIssue.at(
123+
newIssue.newLocation()
124+
.on(pyfile)
125+
.at(pyfile.selectLine(pylintIssue.getLine()))
126+
.message(pylintIssue.getDescription()));
127+
newIssue.save();
128128
} else {
129129
LOG.warn("Pylint rule '{}' is unknown in Sonar", pylintIssue.getRuleId());
130130
}
131131
}
132132

133-
private static void addIssue(Issue pylintIssue, ActiveRule rule, @Nullable Issuable issuable) {
134-
if (issuable != null) {
135-
org.sonar.api.issue.Issue issue = issuable.newIssueBuilder()
136-
.ruleKey(rule.ruleKey())
137-
.line(pylintIssue.getLine())
138-
.message(pylintIssue.getDescription())
139-
.build();
140-
issuable.addIssue(issue);
141-
}
142-
}
143133
}

sonar-python-plugin/src/test/java/org/sonar/plugins/python/pylint/PylintImportSensorTest.java

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,44 +20,44 @@
2020
package org.sonar.plugins.python.pylint;
2121

2222
import java.io.File;
23-
import java.nio.file.Paths;
23+
import java.nio.charset.StandardCharsets;
24+
import java.util.List;
25+
import java.util.concurrent.atomic.AtomicBoolean;
2426
import org.junit.Before;
2527
import org.junit.Test;
26-
import org.mockito.Mockito;
27-
import org.sonar.api.batch.SensorContext;
2828
import org.sonar.api.batch.fs.internal.DefaultFileSystem;
2929
import org.sonar.api.batch.fs.internal.DefaultInputFile;
30+
import org.sonar.api.batch.fs.internal.FileMetadata;
3031
import org.sonar.api.batch.rule.ActiveRules;
3132
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
32-
import org.sonar.api.component.ResourcePerspectives;
33+
import org.sonar.api.batch.sensor.internal.SensorContextTester;
3334
import org.sonar.api.config.MapSettings;
3435
import org.sonar.api.config.Settings;
35-
import org.sonar.api.issue.Issuable;
36-
import org.sonar.api.resources.Project;
3736
import org.sonar.api.rule.RuleKey;
3837
import org.sonar.plugins.python.Python;
3938

4039
import static org.assertj.core.api.Assertions.assertThat;
41-
import static org.mockito.Matchers.any;
4240
import static org.mockito.Mockito.mock;
43-
import static org.mockito.Mockito.times;
44-
import static org.mockito.Mockito.verify;
45-
import static org.mockito.Mockito.when;
4641

4742
public class PylintImportSensorTest {
43+
44+
private File baseDir = new File("src/test/resources/org/sonar/plugins/python/pylint");
4845
private Settings settings;
4946
private DefaultFileSystem fileSystem;
5047
private ActiveRules activeRules;
51-
private SensorContext context;
5248
private DefaultInputFile inputFile;
5349

5450
@Before
5551
public void init() {
5652
settings = new MapSettings();
5753
settings.setProperty(PylintImportSensor.REPORT_PATH_KEY, "pylint-report.txt");
58-
fileSystem = new DefaultFileSystem(new File("src/test/resources/org/sonar/plugins/python/pylint"));
5954

60-
inputFile = new DefaultInputFile("", "src/prod.py").setLanguage(Python.KEY);
55+
fileSystem = new DefaultFileSystem(baseDir);
56+
57+
File file = new File(baseDir, "src/file1.py");
58+
inputFile = new DefaultInputFile("", "src/file1.py")
59+
.setLanguage(Python.KEY)
60+
.initMetadata(new FileMetadata().readMetadata(file, StandardCharsets.UTF_8));
6161
fileSystem.add(inputFile);
6262
activeRules = (new ActiveRulesBuilder())
6363
.create(RuleKey.of(PylintRuleRepository.REPOSITORY_KEY, "C0103"))
@@ -67,50 +67,48 @@ public void init() {
6767
.setName("Missing docstring")
6868
.activate()
6969
.build();
70-
context = mock(SensorContext.class);
71-
}
72-
73-
@Test
74-
public void shouldNotThrowWhenInstantiating() {
75-
new PylintImportSensor(settings, activeRules, fileSystem, mock(ResourcePerspectives.class));
7670
}
7771

7872
@Test
7973
public void shouldExecuteOnlyWhenNecessary() {
80-
DefaultFileSystem fileSystemForeign = new DefaultFileSystem(Paths.get(""));
81-
82-
Project project = mock(Project.class);
83-
8474
ActiveRules emptyProfile = mock(ActiveRules.class);
75+
Settings settingsWithoutProperty = new MapSettings();
8576

86-
checkNecessityOfExecution(project, activeRules, fileSystem, true);
87-
checkNecessityOfExecution(project, emptyProfile, fileSystem, false);
77+
assertThat(shouldExecute(activeRules, settings)).isTrue();
78+
assertThat(shouldExecute(emptyProfile, settings)).isFalse();
8879

89-
checkNecessityOfExecution(project, activeRules, fileSystemForeign, false);
90-
checkNecessityOfExecution(project, emptyProfile, fileSystemForeign, false);
80+
assertThat(shouldExecute(activeRules, settingsWithoutProperty)).isFalse();
81+
assertThat(shouldExecute(emptyProfile, settingsWithoutProperty)).isFalse();
9182
}
9283

93-
@Test
94-
public void parse_report() {
95-
ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
96-
Issuable issuable = mock(Issuable.class);
97-
when(perspectives.as(Issuable.class, inputFile)).thenReturn(issuable);
98-
Issuable.IssueBuilder issueBuilder = mock(Issuable.IssueBuilder.class);
99-
when(issuable.newIssueBuilder()).thenReturn(issueBuilder);
100-
when(issueBuilder.ruleKey(Mockito.any(RuleKey.class))).thenReturn(issueBuilder);
101-
when(issueBuilder.line(Mockito.any(Integer.class))).thenReturn(issueBuilder);
102-
when(issueBuilder.message(Mockito.any(String.class))).thenReturn(issueBuilder);
103-
104-
PylintImportSensor sensor = new PylintImportSensor(settings, activeRules, fileSystem, perspectives);
105-
sensor.analyse(mock(Project.class), context);
106-
107-
verify(issuable, times(3)).addIssue(any(org.sonar.api.issue.Issue.class));
108-
84+
private boolean shouldExecute(ActiveRules activeRules, Settings settings) {
85+
SensorContextTester ctx = SensorContextTester.create(baseDir);
86+
ctx.setActiveRules(activeRules);
87+
ctx.setSettings(settings);
88+
ctx.setFileSystem(fileSystem);
89+
AtomicBoolean executed = new AtomicBoolean(false);
90+
PylintImportSensor sensor = new PylintImportSensor(settings) {
91+
@Override
92+
protected void processReports(org.sonar.api.batch.sensor.SensorContext context, List<File> reports) {
93+
super.processReports(context, reports);
94+
executed.set(true);
95+
}
96+
};
97+
sensor.execute(ctx);
98+
return executed.get();
10999
}
110100

111-
private void checkNecessityOfExecution(Project project, ActiveRules currentActiveRules, DefaultFileSystem fileSystem, boolean shouldExecute) {
112-
PylintImportSensor sensor = new PylintImportSensor(settings, currentActiveRules, fileSystem, mock(ResourcePerspectives.class));
113-
assertThat(sensor.shouldExecuteOnProject(project)).isEqualTo(shouldExecute);
101+
@Test
102+
public void parse_report() {
103+
SensorContextTester context = SensorContextTester.create(baseDir);
104+
context.setActiveRules(activeRules);
105+
context.setFileSystem(fileSystem);
106+
107+
PylintImportSensor sensor = new PylintImportSensor(settings);
108+
sensor.execute(context);
109+
assertThat(context.allIssues()).hasSize(3);
110+
assertThat(context.allIssues()).extracting(issue -> issue.primaryLocation().inputComponent().key())
111+
.containsOnly(inputFile.key());
114112
}
115113

116114
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
************* Module src.prod
2-
src/prod.py:1: [C0111(missing-docstring), ] Missing module docstring
3-
src/prod.py:1: [C0103(invalid-name), factorial] Invalid argument name "n"
4-
src/prod.py:1: [C0111(missing-docstring), factorial] Missing function docstring
2+
src/file1.py:1: [C0111(missing-docstring), ] Missing module docstring
3+
src/file1.py:1: [C0103(invalid-name), factorial] Invalid argument name "n"
4+
src/file1.py:1: [C0111(missing-docstring), factorial] Missing function docstring
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print("hello")

0 commit comments

Comments
 (0)