Skip to content

Commit 088d9da

Browse files
alban-auzeillpynicolas
authored andcommitted
SONARPY-255 Deprecate "IT" and "Overall" coverage properties (SonarSource#138)
* SONARPY-255 Deprecate "IT" and "Overall" coverage properties * Fix from review
1 parent 167e005 commit 088d9da

9 files changed

Lines changed: 82 additions & 44 deletions

File tree

its/plugin/src/test/java/com/sonar/python/it/plugin/CoverageTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public void empty_coverage_report() throws Exception {
9696
nbLog++;
9797
}
9898
}
99-
assertThat(nbLog).isEqualTo(3);
99+
assertThat(nbLog).isEqualTo(1);
100100
assertThat(Tests.getMeasureAsDouble(PROJECT_KEY, "coverage")).isZero();
101101
}
102102

sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonPlugin.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class PythonPlugin implements Plugin {
3939
private static final String GENERAL = "General";
4040
private static final String TEST_AND_COVERAGE = "Tests and Coverage";
4141
private static final String PYLINT = "Pylint";
42+
private static final String DEPRECATED_PREFIX = "DEPRECATED : Use " + PythonCoverageSensor.REPORT_PATH_KEY + " instead. ";
4243

4344
public static final String FILE_SUFFIXES_KEY = "sonar.python.file.suffixes";
4445

@@ -49,6 +50,7 @@ public void define(Context context) {
4950
context.addExtensions(
5051

5152
PropertyDefinition.builder(FILE_SUFFIXES_KEY)
53+
.index(10)
5254
.name("File Suffixes")
5355
.description("Comma-separated list of suffixes of Python files to analyze.")
5456
.category(PYTHON_CATEGORY)
@@ -59,24 +61,31 @@ public void define(Context context) {
5961

6062
// COVERAGE
6163
PropertyDefinition.builder(PythonCoverageSensor.REPORT_PATH_KEY)
64+
.index(20)
6265
.name("Path to coverage report(s)")
6366
.description("Path to coverage reports. Ant patterns are accepted for relative path. The reports have to conform to the Cobertura XML format.")
6467
.category(PYTHON_CATEGORY)
6568
.subCategory(TEST_AND_COVERAGE)
6669
.onQualifiers(Qualifiers.PROJECT)
6770
.defaultValue(PythonCoverageSensor.DEFAULT_REPORT_PATH)
6871
.build(),
72+
// deprecated
6973
PropertyDefinition.builder(PythonCoverageSensor.IT_REPORT_PATH_KEY)
74+
.index(21)
7075
.name("Path to coverage report(s) for integration tests")
71-
.description("Path to coverage reports for integration tests. Ant patterns are accepted for relative path. The reports have to conform to the Cobertura XML format.")
76+
.description(DEPRECATED_PREFIX +
77+
"Path to coverage reports for integration tests. Ant patterns are accepted for relative path. The reports have to conform to the Cobertura XML format.")
7278
.category(PYTHON_CATEGORY)
7379
.subCategory(TEST_AND_COVERAGE)
7480
.onQualifiers(Qualifiers.PROJECT)
7581
.defaultValue(PythonCoverageSensor.IT_DEFAULT_REPORT_PATH)
7682
.build(),
83+
// deprecated
7784
PropertyDefinition.builder(PythonCoverageSensor.OVERALL_REPORT_PATH_KEY)
85+
.index(22)
7886
.name("Path to overall (combined UT+IT) coverage report(s)")
79-
.description("Path to a report containing overall test coverage data (i.e. test coverage gained by all tests of all kinds). " +
87+
.description(DEPRECATED_PREFIX +
88+
"Path to a report containing overall test coverage data (i.e. test coverage gained by all tests of all kinds). " +
8089
"Ant patterns are accepted for relative path. The reports have to conform to the Cobertura XML format.")
8190
.category(PYTHON_CATEGORY)
8291
.subCategory(TEST_AND_COVERAGE)
@@ -86,6 +95,7 @@ public void define(Context context) {
8695

8796
// XUNIT
8897
PropertyDefinition.builder(PythonXUnitSensor.SKIP_DETAILS)
98+
.index(23)
8999
.name("Skip the details when importing the Xunit reports")
90100
.description("When enabled the test execution statistics is provided only on project level. Use this mode when paths in report are not found. Disabled by default.")
91101
.category(PYTHON_CATEGORY)
@@ -95,6 +105,7 @@ public void define(Context context) {
95105
.type(PropertyType.BOOLEAN)
96106
.build(),
97107
PropertyDefinition.builder(PythonXUnitSensor.REPORT_PATH_KEY)
108+
.index(24)
98109
.name("Path to xunit report(s)")
99110
.description("Path to the report of test execution, relative to project's root. Ant patterns are accepted. The reports have to conform to the junitreport XML format.")
100111
.category(PYTHON_CATEGORY)
@@ -105,6 +116,7 @@ public void define(Context context) {
105116

106117
// PYLINT
107118
PropertyDefinition.builder(PylintConfiguration.PYLINT_CONFIG_KEY)
119+
.index(30)
108120
.name("Pylint configuration")
109121
.description("Path to the pylint configuration file to use in pylint analysis. Set to empty to use the default.")
110122
.category(PYTHON_CATEGORY)
@@ -113,6 +125,7 @@ public void define(Context context) {
113125
.defaultValue("")
114126
.build(),
115127
PropertyDefinition.builder(PylintConfiguration.PYLINT_KEY)
128+
.index(31)
116129
.name("Pylint executable")
117130
.description("Path to the pylint executable to use in pylint analysis. Set to empty to use the default one.")
118131
.category(PYTHON_CATEGORY)
@@ -121,6 +134,7 @@ public void define(Context context) {
121134
.defaultValue("")
122135
.build(),
123136
PropertyDefinition.builder(PylintImportSensor.REPORT_PATH_KEY)
137+
.index(32)
124138
.name("Pylint's reports")
125139
.description("Path to Pylint's report file, relative to projects root")
126140
.category(PYTHON_CATEGORY)

sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonReportSensor.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,20 @@
2424
import java.util.Objects;
2525
import org.slf4j.Logger;
2626
import org.slf4j.LoggerFactory;
27+
import org.sonar.api.batch.fs.InputFile;
2728
import org.sonar.api.batch.sensor.Sensor;
2829
import org.sonar.api.batch.sensor.SensorContext;
29-
import org.sonar.api.batch.fs.InputFile;
3030
import org.sonar.api.batch.sensor.SensorDescriptor;
31-
import org.sonar.api.config.Settings;
31+
import org.sonar.api.config.Configuration;
3232
import org.sonar.api.utils.WildcardPattern;
3333

3434
public abstract class PythonReportSensor implements Sensor {
3535

3636
private static final Logger LOG = LoggerFactory.getLogger(PythonReportSensor.class);
3737

38-
protected Settings conf;
38+
protected Configuration conf;
3939

40-
public PythonReportSensor(Settings conf) {
40+
public PythonReportSensor(Configuration conf) {
4141
this.conf = conf;
4242
}
4343

@@ -64,12 +64,9 @@ public void execute(SensorContext context) {
6464
}
6565
}
6666

67-
public static List<File> getReports(Settings conf, String baseDirPath, String reportPathPropertyKey, String defaultReportPath) {
68-
String reportPath = conf.getString(reportPathPropertyKey);
67+
public static List<File> getReports(Configuration conf, String baseDirPath, String reportPathPropertyKey, String defaultReportPath) {
68+
String reportPath = conf.get(reportPathPropertyKey).orElse(defaultReportPath);
6969
boolean propertyIsProvided = !Objects.equals(reportPath, defaultReportPath);
70-
if (reportPath == null) {
71-
reportPath = defaultReportPath;
72-
}
7370

7471
LOG.debug("Using pattern '{}' to find reports", reportPath);
7572

sonar-python-plugin/src/main/java/org/sonar/plugins/python/coverage/PythonCoverageSensor.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,18 @@
2323
import java.util.ArrayList;
2424
import java.util.HashMap;
2525
import java.util.HashSet;
26+
import java.util.LinkedHashSet;
2627
import java.util.List;
2728
import java.util.Map;
29+
import java.util.Set;
30+
import java.util.stream.Collectors;
2831
import javax.xml.stream.XMLStreamException;
2932
import org.slf4j.Logger;
3033
import org.slf4j.LoggerFactory;
3134
import org.sonar.api.batch.fs.InputFile;
3235
import org.sonar.api.batch.sensor.SensorContext;
3336
import org.sonar.api.batch.sensor.coverage.NewCoverage;
34-
import org.sonar.api.config.Settings;
37+
import org.sonar.api.config.Configuration;
3538
import org.sonar.plugins.python.EmptyReportException;
3639

3740
import static org.sonar.plugins.python.PythonReportSensor.getReports;
@@ -41,30 +44,46 @@ public class PythonCoverageSensor {
4144
private static final Logger LOG = LoggerFactory.getLogger(PythonCoverageSensor.class);
4245

4346
public static final String REPORT_PATH_KEY = "sonar.python.coverage.reportPath";
47+
public static final String DEFAULT_REPORT_PATH = "coverage-reports/*coverage-*.xml";
48+
// Deprecated report path keys
4449
public static final String IT_REPORT_PATH_KEY = "sonar.python.coverage.itReportPath";
50+
public static final String IT_DEFAULT_REPORT_PATH = "";
4551
public static final String OVERALL_REPORT_PATH_KEY = "sonar.python.coverage.overallReportPath";
46-
public static final String DEFAULT_REPORT_PATH = "coverage-reports/coverage-*.xml";
47-
public static final String IT_DEFAULT_REPORT_PATH = "coverage-reports/it-coverage-*.xml";
48-
public static final String OVERALL_DEFAULT_REPORT_PATH = "coverage-reports/overall-coverage-*.xml";
52+
public static final String OVERALL_DEFAULT_REPORT_PATH = "";
4953

5054
public void execute(SensorContext context) {
5155
String baseDir = context.fileSystem().baseDir().getPath();
52-
Settings settings = context.settings();
56+
Configuration config = context.config();
57+
58+
logDeprecatedPropertyUsage(config, IT_REPORT_PATH_KEY, REPORT_PATH_KEY);
59+
logDeprecatedPropertyUsage(config, OVERALL_REPORT_PATH_KEY, REPORT_PATH_KEY);
5360

5461
HashSet<InputFile> filesCovered = new HashSet<>();
5562
List<File> reports = new ArrayList<>();
56-
reports.addAll(getReports(settings, baseDir, REPORT_PATH_KEY, DEFAULT_REPORT_PATH));
57-
reports.addAll(getReports(settings, baseDir, IT_REPORT_PATH_KEY, IT_DEFAULT_REPORT_PATH));
58-
reports.addAll(getReports(settings, baseDir, OVERALL_REPORT_PATH_KEY, OVERALL_DEFAULT_REPORT_PATH));
63+
reports.addAll(getReports(config, baseDir, REPORT_PATH_KEY, DEFAULT_REPORT_PATH));
64+
reports.addAll(getReports(config, baseDir, IT_REPORT_PATH_KEY, IT_DEFAULT_REPORT_PATH));
65+
reports.addAll(getReports(config, baseDir, OVERALL_REPORT_PATH_KEY, OVERALL_DEFAULT_REPORT_PATH));
5966
if (!reports.isEmpty()) {
6067
LOG.info("Python test coverage");
61-
for (File report : reports) {
68+
for (File report : uniqueAbsolutePaths(reports)) {
6269
Map<InputFile, NewCoverage> coverageMeasures = parseReport(report, context);
6370
saveMeasures(coverageMeasures, filesCovered);
6471
}
6572
}
6673
}
6774

75+
private static void logDeprecatedPropertyUsage(Configuration config, String deprecatedKey, String replacementKey) {
76+
if (!config.get(deprecatedKey).orElse("").isEmpty()) {
77+
LOG.warn("Property '{}' is deprecated. Please use '{}' instead.", deprecatedKey, replacementKey);
78+
}
79+
}
80+
81+
private static Set<File> uniqueAbsolutePaths(List<File> reports) {
82+
return reports.stream()
83+
.map(File::getAbsoluteFile)
84+
.collect(Collectors.toCollection(LinkedHashSet::new));
85+
}
86+
6887
private static Map<InputFile, NewCoverage> parseReport(File report, SensorContext context) {
6988
Map<InputFile, NewCoverage> coverageMeasures = new HashMap<>();
7089
try {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import org.sonar.api.batch.sensor.SensorContext;
3434
import org.sonar.api.batch.sensor.SensorDescriptor;
3535
import org.sonar.api.batch.sensor.issue.NewIssue;
36-
import org.sonar.api.config.Settings;
36+
import org.sonar.api.config.Configuration;
3737
import org.sonar.api.rule.RuleKey;
3838
import org.sonar.plugins.python.PythonReportSensor;
3939

@@ -43,7 +43,7 @@ public class PylintImportSensor extends PythonReportSensor {
4343

4444
private static final Logger LOG = LoggerFactory.getLogger(PylintImportSensor.class);
4545

46-
public PylintImportSensor(Settings conf) {
46+
public PylintImportSensor(Configuration conf) {
4747
super(conf);
4848
}
4949

sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/PythonXUnitSensor.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,16 @@
2424
import java.util.HashMap;
2525
import java.util.List;
2626
import java.util.Map;
27-
2827
import javax.xml.stream.XMLStreamException;
29-
3028
import org.apache.commons.lang.StringUtils;
3129
import org.slf4j.Logger;
3230
import org.slf4j.LoggerFactory;
31+
import org.sonar.api.batch.fs.FileSystem;
3332
import org.sonar.api.batch.fs.InputComponent;
33+
import org.sonar.api.batch.fs.InputFile;
3434
import org.sonar.api.batch.measure.Metric;
3535
import org.sonar.api.batch.sensor.SensorContext;
36-
import org.sonar.api.batch.fs.FileSystem;
37-
import org.sonar.api.batch.fs.InputFile;
38-
import org.sonar.api.config.Settings;
36+
import org.sonar.api.config.Configuration;
3937
import org.sonar.api.measures.CoreMetrics;
4038
import org.sonar.plugins.python.PythonReportSensor;
4139
import org.sonar.plugins.python.parser.StaxParser;
@@ -49,7 +47,7 @@ public class PythonXUnitSensor extends PythonReportSensor {
4947

5048
private final FileSystem fileSystem;
5149

52-
public PythonXUnitSensor(Settings conf, FileSystem fileSystem) {
50+
public PythonXUnitSensor(Configuration conf, FileSystem fileSystem) {
5351
super(conf);
5452
this.fileSystem = fileSystem;
5553
}
@@ -66,7 +64,7 @@ protected String defaultReportPath() {
6664

6765
@Override
6866
protected void processReports(final SensorContext context, List<File> reports) throws XMLStreamException {
69-
if (conf.getBoolean(SKIP_DETAILS)) {
67+
if (conf.getBoolean(SKIP_DETAILS).orElse(Boolean.FALSE)) {
7068
simpleMode(context, reports);
7169
} else {
7270
detailedMode(context, reports);

sonar-python-plugin/src/test/java/org/sonar/plugins/python/coverage/PythonCoverageSensorTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,17 @@ public void test_coverage_4_4_2() {
146146
assertThat(context.coveredConditions(FILE4_KEY, 10)).isEqualTo(1);
147147
}
148148

149+
@Test
150+
public void test_unique_report() {
151+
settings.setProperty(PythonCoverageSensor.REPORT_PATH_KEY, "*coverage.4.4.2*.xml");
152+
settings.setProperty(PythonCoverageSensor.IT_REPORT_PATH_KEY, "coverage*.4.4.2.xml");
153+
settings.setProperty(PythonCoverageSensor.OVERALL_REPORT_PATH_KEY, "*coverage.4.4.2.xml");
154+
coverageSensor.execute(context);
155+
List<Integer> actual = IntStream.range(1, 18).mapToObj(line -> context.lineHits(FILE4_KEY, line)).collect(Collectors.toList());
156+
Integer coverageAtLine6 = actual.get(5);
157+
assertThat(coverageAtLine6).isEqualTo(1);
158+
}
159+
149160
@Test
150161
public void test_unresolved_path() {
151162
settings.setProperty(PythonCoverageSensor.REPORT_PATH_KEY, "coverage_with_unresolved_path.xml");

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@
4242

4343
public class PylintImportSensorTest {
4444

45+
private final File baseDir = new File("src/test/resources/org/sonar/plugins/python/pylint");
46+
private final SensorContextTester context = SensorContextTester.create(baseDir);
47+
4548
@Test
4649
public void parse_report() {
47-
File baseDir = new File("src/test/resources/org/sonar/plugins/python/pylint");
48-
SensorContextTester context = SensorContextTester.create(baseDir);
49-
5050
context.settings().setProperty(PylintImportSensor.REPORT_PATH_KEY, "pylint-report.txt");
5151

5252
File file = new File(baseDir, "src/file1.py");
@@ -65,7 +65,7 @@ public void parse_report() {
6565
.activate()
6666
.build());
6767

68-
PylintImportSensor sensor = new PylintImportSensor(context.settings());
68+
PylintImportSensor sensor = new PylintImportSensor(context.config());
6969
sensor.execute(context);
7070
assertThat(context.allIssues()).hasSize(3);
7171
assertThat(context.allIssues()).extracting(issue -> issue.primaryLocation().inputComponent().key())
@@ -75,7 +75,7 @@ public void parse_report() {
7575
@Test
7676
public void sensor_descriptor() {
7777
DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
78-
new PylintImportSensor(new MapSettings()).describe(descriptor);
78+
new PylintImportSensor(context.config()).describe(descriptor);
7979
assertThat(descriptor.name()).isEqualTo("PylintImportSensor");
8080
assertThat(descriptor.languages()).containsOnly("py");
8181
assertThat(descriptor.type()).isEqualTo(InputFile.Type.MAIN);

sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/PythonXUnitSensorTest.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
2929
import org.sonar.api.batch.sensor.internal.SensorContextTester;
3030
import org.sonar.api.config.Settings;
31-
import org.sonar.api.config.internal.MapSettings;
3231
import org.sonar.api.measures.CoreMetrics;
3332
import org.sonar.api.measures.Metric;
3433

@@ -44,15 +43,15 @@ public class PythonXUnitSensorTest {
4443

4544
@Before
4645
public void setUp() {
47-
settings = new MapSettings();
46+
settings = context.settings();
4847
fs = new DefaultFileSystem(baseDir);
49-
sensor = new PythonXUnitSensor(settings, fs);
48+
sensor = new PythonXUnitSensor(context.config(), fs);
5049
}
5150

5251
@Test
5352
public void shouldSaveCorrectMeasures() {
5453
DefaultInputFile testFile1 = TestInputFileBuilder.create("", "test_sample1.py").build();
55-
DefaultInputFile testFile2 = TestInputFileBuilder.create("", "tests/dir/test_sample2.py").build();
54+
DefaultInputFile testFile2 = TestInputFileBuilder.create("", "tests/dir/test_sample2.py").build();
5655
fs.add(testFile1);
5756
fs.add(testFile2);
5857
sensor.execute(context);
@@ -73,8 +72,8 @@ public void shouldSaveCorrectMeasures() {
7372
@Test
7473
public void shouldSaveCorrectMeasuresSimpleMode() {
7574
settings.setProperty(PythonXUnitSensor.SKIP_DETAILS, true);
76-
fs.add( TestInputFileBuilder.create("", "test_sample.py").build());
77-
fs.add( TestInputFileBuilder.create("", "tests/dir/test_sample.py").build());
75+
fs.add(TestInputFileBuilder.create("", "test_sample.py").build());
76+
fs.add(TestInputFileBuilder.create("", "tests/dir/test_sample.py").build());
7877
sensor.execute(context);
7978

8079
// includes test with not found file
@@ -86,11 +85,11 @@ public void shouldSaveCorrectMeasuresSimpleMode() {
8685

8786
@Test
8887
public void shouldReportNothingWhenNoReportFound() {
89-
DefaultInputFile testFile1 = TestInputFileBuilder.create("", "test_sample1.py").build();
88+
DefaultInputFile testFile1 = TestInputFileBuilder.create("", "test_sample1.py").build();
9089
fs.add(testFile1);
9190

9291
settings.setProperty(PythonXUnitSensor.REPORT_PATH_KEY, "notexistingpath");
93-
sensor = new PythonXUnitSensor(settings, fs);
92+
sensor = new PythonXUnitSensor(context.config(), fs);
9493
sensor.execute(context);
9594

9695
assertThat(context.measures(context.module().key())).isEmpty();
@@ -100,7 +99,7 @@ public void shouldReportNothingWhenNoReportFound() {
10099
@Test(expected = IllegalStateException.class)
101100
public void shouldThrowWhenGivenInvalidTime() {
102101
settings.setProperty(PythonXUnitSensor.REPORT_PATH_KEY, "xunit-reports/invalid-time-xunit-report.xml");
103-
sensor = new PythonXUnitSensor(settings, fs);
102+
sensor = new PythonXUnitSensor(context.config(), fs);
104103
sensor.execute(context);
105104
}
106105

0 commit comments

Comments
 (0)