Skip to content

Commit ac2ca22

Browse files
authored
Provide ability to ignore duplications for unit tests (#43)
Add new property to possibily ignore duplication on tests
1 parent 3a85d6c commit ac2ca22

File tree

5 files changed

+64
-27
lines changed

5 files changed

+64
-27
lines changed

DOC.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ and provided in the form of reports.
8282

8383
Currently, only `junit report` formats are supported :
8484

85-
Insert a parameter `community.rust.test.reportPath` into you `sonar-project.properties` file.
85+
Insert a parameter `community.rust.test.reportPath` into your `sonar-project.properties` file.
8686
As an example, one of such tool for Rust that converts `cargo test` report to `junit report` is [cargo2junit](https://crates.io/crates/cargo2junit).
8787

88+
### Ignore duplications on unit tests
89+
90+
By default the unit tests are included as part as the duplication calculation
91+
92+
You may override the default `false` for the property `community.rust.cpd.ignoretests` either globally from the UI
93+
(*Administration->Configuration->Rust*) or by setting `community.rust.cpd.ignoretests=true` in your `sonar-project.properties` file.

community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elegoff.plugins.communityrust.rules.RustRulesDefinition;
2626
import org.elegoff.plugins.communityrust.xunit.XUnitSensor;
2727
import org.sonar.api.Plugin;
28+
import org.sonar.api.PropertyType;
2829
import org.sonar.api.config.PropertyDefinition;
2930
import org.elegoff.plugins.communityrust.clippy.ClippySensor;
3031
import org.elegoff.plugins.communityrust.clippy.ClippyRulesDefinition;
@@ -45,6 +46,8 @@ public class CommunityRustPlugin implements Plugin {
4546
public static final String COBERTURA_REPORT_PATHS = "community.rust.cobertura.reportPaths";
4647
public static final String DEFAULT_COBERTURA_REPORT_PATHS = "cobertura.xml";
4748
public static final String UNIT_TEST_ATTRIBUTES = "community.rust.unittests.attributes";
49+
public static final String IGNORE_DUPLICATION_FOR_TESTS = "community.rust.cpd.ignoretests";
50+
4851
public static final String TEST_AND_COVERAGE = "Test and Coverage";
4952
public static final String DEFAULT_UNIT_TEST_ATTRIBUTES="test,tokio::test";
5053

@@ -95,11 +98,20 @@ public void define(Context context) {
9598
PropertyDefinition.builder(UNIT_TEST_ATTRIBUTES)
9699
.defaultValue(DEFAULT_UNIT_TEST_ATTRIBUTES)
97100
.name("Unit tests")
98-
.description("Comme separated list of Rust attributes for Unit Tests")
101+
.description("Comma separated list of Rust attributes for Unit Tests")
99102
.onQualifiers(Qualifiers.PROJECT)
100103
.subCategory(TEST_AND_COVERAGE)
101104
.category("Rust")
102105
.multiValues(true)
106+
.build(),
107+
PropertyDefinition.builder(IGNORE_DUPLICATION_FOR_TESTS)
108+
.defaultValue(Boolean.toString(false))
109+
.name("Duplications on Unit tests")
110+
.description("If true, CPD ignores functions identified as unit tests (see " + UNIT_TEST_ATTRIBUTES + ")")
111+
.onQualifiers(Qualifiers.PROJECT)
112+
.subCategory(TEST_AND_COVERAGE)
113+
.category("Rust")
114+
.type(PropertyType.BOOLEAN)
103115
.build()
104116
);
105117

community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/RustTokensVisitor.java

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
* Copyright (C) 2021 Eric Le Goff
44
* mailto:community-rust AT pm DOT me
55
* http://github.com/elegoff/sonar-rust
6-
*
6+
* <p>
77
* This program is free software; you can redistribute it and/or
88
* modify it under the terms of the GNU Lesser General Public
99
* License as published by the Free Software Foundation; either
1010
* version 3 of the License, or (at your option) any later version.
11-
*
11+
* <p>
1212
* This program is distributed in the hope that it will be useful,
1313
* but WITHOUT ANY WARRANTY; without even the implied warranty of
1414
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1515
* Lesser General Public License for more details.
16-
*
16+
* <p>
1717
* You should have received a copy of the GNU Lesser General Public License
1818
* along with this program; if not, write to the Free Software Foundation,
1919
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -47,10 +47,12 @@ public class RustTokensVisitor {
4747
private final Set<String> keywords = new HashSet<>(Arrays.asList(RustKeyword.keywordValues()));
4848
private final SensorContext context;
4949
private final ParserAdapter<LexerlessGrammar> lexer;
50+
private final boolean ignoreCPDTests;
5051

5152
public RustTokensVisitor(SensorContext context, ParserAdapter<LexerlessGrammar> lexer) {
5253
this.context = context;
5354
this.lexer = lexer;
55+
this.ignoreCPDTests = context.config().getBoolean(CommunityRustPlugin.IGNORE_DUPLICATION_FOR_TESTS).orElse(false);
5456
}
5557

5658
private static String getTokenImage(Token token) {
@@ -79,26 +81,10 @@ public void scanFile(InputFile inputFile, RustVisitorContext visitorContext) {
7981
Set<Token> unitTestTokens = identifyUnitTestTokens(parsedTokens);
8082

8183
for (Token token : parsedTokens) {
82-
final String tokenImage = getTokenImage(token);
83-
final var tokenLocation = tokenLocation(token);
84-
85-
if (token.getType().equals(RustTokenType.CHARACTER_LITERAL)
86-
|| token.getType().equals(RustTokenType.STRING_LITERAL)
87-
|| token.getType().equals(RustTokenType.RAW_STRING_LITERAL)
88-
|| token.getType().equals(RustTokenType.RAW_BYTE_STRING_LITERAL)
8984

90-
) {
91-
highlight(highlighting, tokenLocation, TypeOfText.STRING);
92-
93-
} else if (keywords.contains(tokenImage)) {
94-
highlight(highlighting, tokenLocation, TypeOfText.KEYWORD);
95-
}
85+
final var tokenLocation = tokenLocation(token);
9686

97-
if (token.getType().equals(RustTokenType.FLOAT_LITERAL)
98-
|| token.getType().equals(RustTokenType.BOOLEAN_LITERAL)
99-
|| token.getType().equals(RustTokenType.INTEGER_LITERAL)) {
100-
highlight(highlighting, tokenLocation, TypeOfText.CONSTANT);
101-
}
87+
highlightToken(token,tokenLocation, highlighting );
10288

10389
for (Trivia trivia : token.getTrivia()) {
10490
highlight(highlighting, tokenLocation(trivia.getToken()), TypeOfText.COMMENT);
@@ -109,15 +95,36 @@ public void scanFile(InputFile inputFile, RustVisitorContext visitorContext) {
10995
);
11096
}
11197

112-
if (!GenericTokenType.EOF.equals(token.getType())) {
113-
cpdTokens.addToken(tokenLocation.startLine(), tokenLocation.startLineOffset(), tokenLocation.endLine(), tokenLocation.endLineOffset(), tokenImage);
98+
if (!GenericTokenType.EOF.equals(token.getType()) && !(unitTestTokens.contains(token) && this.ignoreCPDTests)) {
99+
cpdTokens.addToken(tokenLocation.startLine(), tokenLocation.startLineOffset(), tokenLocation.endLine(), tokenLocation.endLineOffset(), getTokenImage(token));
114100
}
115101
}
116102

117103
highlighting.save();
118104
cpdTokens.save();
119105
}
120106

107+
private void highlightToken(Token token, TokenLocation tokenLocation, NewHighlighting highlighting){
108+
final String tokenImage = getTokenImage(token);
109+
if (token.getType().equals(RustTokenType.CHARACTER_LITERAL)
110+
|| token.getType().equals(RustTokenType.STRING_LITERAL)
111+
|| token.getType().equals(RustTokenType.RAW_STRING_LITERAL)
112+
|| token.getType().equals(RustTokenType.RAW_BYTE_STRING_LITERAL)
113+
114+
) {
115+
highlight(highlighting, tokenLocation, TypeOfText.STRING);
116+
117+
} else if (keywords.contains(tokenImage)) {
118+
highlight(highlighting, tokenLocation, TypeOfText.KEYWORD);
119+
}
120+
121+
if (token.getType().equals(RustTokenType.FLOAT_LITERAL)
122+
|| token.getType().equals(RustTokenType.BOOLEAN_LITERAL)
123+
|| token.getType().equals(RustTokenType.INTEGER_LITERAL)) {
124+
highlight(highlighting, tokenLocation, TypeOfText.CONSTANT);
125+
}
126+
}
127+
121128
private Set<Token> identifyUnitTestTokens(List<Token> parsedTokens) {
122129
Set<Token> testTokens = new HashSet<>();
123130
Set<String> unitTestsAttributes = getUnitTestAttributes();

community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/CommunityRustPluginTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public class CommunityRustPluginTest extends TestCase {
3939
public void testGetExtensions() {
4040
Version v79 = Version.create(7, 9);
4141
SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(v79, SonarQubeSide.SERVER, SonarEdition.DEVELOPER);
42-
assertThat(extensions(runtime)).hasSize(16);
42+
assertThat(extensions(runtime)).hasSize(17);
4343
assertThat(extensions(runtime)).contains(ClippyRulesDefinition.class);
44-
assertThat(extensions(SonarRuntimeImpl.forSonarLint(v79))).hasSize(16);
44+
assertThat(extensions(SonarRuntimeImpl.forSonarLint(v79))).hasSize(17);
4545
}
4646

4747
private static List extensions(SonarRuntime runtime) {

community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/RustSensorTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,24 @@ public void canParse() throws IOException {
117117
@Test
118118
public void checkDuplication() throws IOException {
119119
DefaultInputFile inputFile = executeSensorOnSingleFile("sensor/cpd.rs");
120+
120121
assertEquals(212, tester.cpdTokens(inputFile.key()).size());
121122
verify(fileLinesContext).save();
122123
assertEquals(Collections.singletonList(TypeOfText.ANNOTATION), tester.highlightingTypeAt(inputFile.key(), 5, 5));
123124
Assertions.assertThat(tester.allAnalysisErrors()).isEmpty();
124125
}
125126

127+
@Test
128+
public void checkDuplicationIgnoringTests() throws IOException {
129+
tester.settings().setProperty(CommunityRustPlugin.IGNORE_DUPLICATION_FOR_TESTS,true);
130+
DefaultInputFile inputFile = executeSensorOnSingleFile("sensor/cpd.rs");
131+
132+
assertEquals(3, tester.cpdTokens(inputFile.key()).size());
133+
verify(fileLinesContext).save();
134+
assertEquals(Collections.singletonList(TypeOfText.ANNOTATION), tester.highlightingTypeAt(inputFile.key(), 5, 5));
135+
Assertions.assertThat(tester.allAnalysisErrors()).isEmpty();
136+
}
137+
126138

127139

128140
private DefaultInputFile executeSensorOnSingleFile(String fileName) throws IOException {

0 commit comments

Comments
 (0)