From db3fc5ba02391c48120db382578b0d3097c43b5b Mon Sep 17 00:00:00 2001 From: Eric Le Goff Date: Mon, 24 Jan 2022 11:46:21 +0100 Subject: [PATCH 1/3] Add new property to possibily ignore duplication on tests --- .../plugins/communityrust/CommunityRustPlugin.java | 12 ++++++++++++ .../plugins/communityrust/RustTokensVisitor.java | 13 +++++++++---- .../communityrust/CommunityRustPluginTest.java | 4 ++-- .../plugins/communityrust/RustSensorTest.java | 12 ++++++++++++ 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java b/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java index e1138ff7..a701f709 100644 --- a/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java +++ b/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java @@ -25,6 +25,7 @@ import org.elegoff.plugins.communityrust.rules.RustRulesDefinition; import org.elegoff.plugins.communityrust.xunit.XUnitSensor; import org.sonar.api.Plugin; +import org.sonar.api.PropertyType; import org.sonar.api.config.PropertyDefinition; import org.elegoff.plugins.communityrust.clippy.ClippySensor; import org.elegoff.plugins.communityrust.clippy.ClippyRulesDefinition; @@ -45,6 +46,8 @@ public class CommunityRustPlugin implements Plugin { public static final String COBERTURA_REPORT_PATHS = "community.rust.cobertura.reportPaths"; public static final String DEFAULT_COBERTURA_REPORT_PATHS = "cobertura.xml"; public static final String UNIT_TEST_ATTRIBUTES = "community.rust.unittests.attributes"; + public static final String IGNORE_DUPLICATION_FOR_TESTS = "community.rust.cpd.ignoretests"; + public static final String TEST_AND_COVERAGE = "Test and Coverage"; public static final String DEFAULT_UNIT_TEST_ATTRIBUTES="test,tokio::test"; @@ -100,6 +103,15 @@ public void define(Context context) { .subCategory(TEST_AND_COVERAGE) .category("Rust") .multiValues(true) + .build(), + PropertyDefinition.builder(IGNORE_DUPLICATION_FOR_TESTS) + .defaultValue(Boolean.toString(false)) + .name("Unit tests") + .description("If true, CPD ignores functions identified as unit tests (see " + UNIT_TEST_ATTRIBUTES + ")") + .onQualifiers(Qualifiers.PROJECT) + .subCategory("Duplications") + .category("Rust") + .type(PropertyType.BOOLEAN) .build() ); diff --git a/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/RustTokensVisitor.java b/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/RustTokensVisitor.java index 3a7adf27..7afdfd4e 100644 --- a/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/RustTokensVisitor.java +++ b/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/RustTokensVisitor.java @@ -3,17 +3,17 @@ * Copyright (C) 2021 Eric Le Goff * mailto:community-rust AT pm DOT me * http://github.com/elegoff/sonar-rust - * + *

* This program 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. - * + *

* This program 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. @@ -47,10 +47,12 @@ public class RustTokensVisitor { private final Set keywords = new HashSet<>(Arrays.asList(RustKeyword.keywordValues())); private final SensorContext context; private final ParserAdapter lexer; + private final boolean ignoreCPDTests; public RustTokensVisitor(SensorContext context, ParserAdapter lexer) { this.context = context; this.lexer = lexer; + this.ignoreCPDTests = context.config().getBoolean(CommunityRustPlugin.IGNORE_DUPLICATION_FOR_TESTS).orElse(false); } private static String getTokenImage(Token token) { @@ -110,7 +112,10 @@ public void scanFile(InputFile inputFile, RustVisitorContext visitorContext) { } if (!GenericTokenType.EOF.equals(token.getType())) { - cpdTokens.addToken(tokenLocation.startLine(), tokenLocation.startLineOffset(), tokenLocation.endLine(), tokenLocation.endLineOffset(), tokenImage); + boolean ignoreCpdInTest = true; + if (!(unitTestTokens.contains(token) && this.ignoreCPDTests)) { + cpdTokens.addToken(tokenLocation.startLine(), tokenLocation.startLineOffset(), tokenLocation.endLine(), tokenLocation.endLineOffset(), tokenImage); + } } } diff --git a/community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/CommunityRustPluginTest.java b/community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/CommunityRustPluginTest.java index 5ab33fa4..9734742d 100644 --- a/community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/CommunityRustPluginTest.java +++ b/community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/CommunityRustPluginTest.java @@ -39,9 +39,9 @@ public class CommunityRustPluginTest extends TestCase { public void testGetExtensions() { Version v79 = Version.create(7, 9); SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(v79, SonarQubeSide.SERVER, SonarEdition.DEVELOPER); - assertThat(extensions(runtime)).hasSize(16); + assertThat(extensions(runtime)).hasSize(17); assertThat(extensions(runtime)).contains(ClippyRulesDefinition.class); - assertThat(extensions(SonarRuntimeImpl.forSonarLint(v79))).hasSize(16); + assertThat(extensions(SonarRuntimeImpl.forSonarLint(v79))).hasSize(17); } private static List extensions(SonarRuntime runtime) { diff --git a/community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/RustSensorTest.java b/community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/RustSensorTest.java index 3ba11489..5d39dfb8 100644 --- a/community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/RustSensorTest.java +++ b/community-rust-plugin/src/test/java/org/elegoff/plugins/communityrust/RustSensorTest.java @@ -117,12 +117,24 @@ public void canParse() throws IOException { @Test public void checkDuplication() throws IOException { DefaultInputFile inputFile = executeSensorOnSingleFile("sensor/cpd.rs"); + assertEquals(212, tester.cpdTokens(inputFile.key()).size()); verify(fileLinesContext).save(); assertEquals(Collections.singletonList(TypeOfText.ANNOTATION), tester.highlightingTypeAt(inputFile.key(), 5, 5)); Assertions.assertThat(tester.allAnalysisErrors()).isEmpty(); } + @Test + public void checkDuplicationIgnoringTests() throws IOException { + tester.settings().setProperty(CommunityRustPlugin.IGNORE_DUPLICATION_FOR_TESTS,true); + DefaultInputFile inputFile = executeSensorOnSingleFile("sensor/cpd.rs"); + + assertEquals(3, tester.cpdTokens(inputFile.key()).size()); + verify(fileLinesContext).save(); + assertEquals(Collections.singletonList(TypeOfText.ANNOTATION), tester.highlightingTypeAt(inputFile.key(), 5, 5)); + Assertions.assertThat(tester.allAnalysisErrors()).isEmpty(); + } + private DefaultInputFile executeSensorOnSingleFile(String fileName) throws IOException { From 3a5efca317a4ea1ddd17e9562d6cca365844c926 Mon Sep 17 00:00:00 2001 From: Eric Le Goff Date: Mon, 24 Jan 2022 11:58:52 +0100 Subject: [PATCH 2/3] Fix configuration page for Rust parameters --- DOC.md | 8 +++++++- .../plugins/communityrust/CommunityRustPlugin.java | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/DOC.md b/DOC.md index 8b438906..1dfc53b7 100644 --- a/DOC.md +++ b/DOC.md @@ -82,6 +82,12 @@ and provided in the form of reports. Currently, only `junit report` formats are supported : -Insert a parameter `community.rust.test.reportPath` into you `sonar-project.properties` file. +Insert a parameter `community.rust.test.reportPath` into your `sonar-project.properties` file. As an example, one of such tool for Rust that converts `cargo test` report to `junit report` is [cargo2junit](https://crates.io/crates/cargo2junit). +### Ignore duplications on unit tests + +By default the unit tests are included as part as the duplication calculation + +You may override the default `false` for the property `community.rust.cpd.ignoretests` either globally from the UI +(*Administration->Configuration->Rust*) or by setting `community.rust.cpd.ignoretests=true` in your `sonar-project.properties` file. \ No newline at end of file diff --git a/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java b/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java index a701f709..ded52be2 100644 --- a/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java +++ b/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java @@ -98,7 +98,7 @@ public void define(Context context) { PropertyDefinition.builder(UNIT_TEST_ATTRIBUTES) .defaultValue(DEFAULT_UNIT_TEST_ATTRIBUTES) .name("Unit tests") - .description("Comme separated list of Rust attributes for Unit Tests") + .description("Comma separated list of Rust attributes for Unit Tests") .onQualifiers(Qualifiers.PROJECT) .subCategory(TEST_AND_COVERAGE) .category("Rust") @@ -106,10 +106,10 @@ public void define(Context context) { .build(), PropertyDefinition.builder(IGNORE_DUPLICATION_FOR_TESTS) .defaultValue(Boolean.toString(false)) - .name("Unit tests") + .name("Duplications on Unit tests") .description("If true, CPD ignores functions identified as unit tests (see " + UNIT_TEST_ATTRIBUTES + ")") .onQualifiers(Qualifiers.PROJECT) - .subCategory("Duplications") + .subCategory(TEST_AND_COVERAGE) .category("Rust") .type(PropertyType.BOOLEAN) .build() From 1f0b184d6133765212ba355f2d5bb9e0bf8ebec3 Mon Sep 17 00:00:00 2001 From: Eric Le Goff Date: Mon, 24 Jan 2022 13:31:03 +0100 Subject: [PATCH 3/3] reduce complexity --- .../communityrust/RustTokensVisitor.java | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/RustTokensVisitor.java b/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/RustTokensVisitor.java index 7afdfd4e..6fb64493 100644 --- a/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/RustTokensVisitor.java +++ b/community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/RustTokensVisitor.java @@ -81,26 +81,10 @@ public void scanFile(InputFile inputFile, RustVisitorContext visitorContext) { Set unitTestTokens = identifyUnitTestTokens(parsedTokens); for (Token token : parsedTokens) { - final String tokenImage = getTokenImage(token); - final var tokenLocation = tokenLocation(token); - - if (token.getType().equals(RustTokenType.CHARACTER_LITERAL) - || token.getType().equals(RustTokenType.STRING_LITERAL) - || token.getType().equals(RustTokenType.RAW_STRING_LITERAL) - || token.getType().equals(RustTokenType.RAW_BYTE_STRING_LITERAL) - ) { - highlight(highlighting, tokenLocation, TypeOfText.STRING); - - } else if (keywords.contains(tokenImage)) { - highlight(highlighting, tokenLocation, TypeOfText.KEYWORD); - } + final var tokenLocation = tokenLocation(token); - if (token.getType().equals(RustTokenType.FLOAT_LITERAL) - || token.getType().equals(RustTokenType.BOOLEAN_LITERAL) - || token.getType().equals(RustTokenType.INTEGER_LITERAL)) { - highlight(highlighting, tokenLocation, TypeOfText.CONSTANT); - } + highlightToken(token,tokenLocation, highlighting ); for (Trivia trivia : token.getTrivia()) { highlight(highlighting, tokenLocation(trivia.getToken()), TypeOfText.COMMENT); @@ -111,11 +95,8 @@ public void scanFile(InputFile inputFile, RustVisitorContext visitorContext) { ); } - if (!GenericTokenType.EOF.equals(token.getType())) { - boolean ignoreCpdInTest = true; - if (!(unitTestTokens.contains(token) && this.ignoreCPDTests)) { - cpdTokens.addToken(tokenLocation.startLine(), tokenLocation.startLineOffset(), tokenLocation.endLine(), tokenLocation.endLineOffset(), tokenImage); - } + if (!GenericTokenType.EOF.equals(token.getType()) && !(unitTestTokens.contains(token) && this.ignoreCPDTests)) { + cpdTokens.addToken(tokenLocation.startLine(), tokenLocation.startLineOffset(), tokenLocation.endLine(), tokenLocation.endLineOffset(), getTokenImage(token)); } } @@ -123,6 +104,27 @@ public void scanFile(InputFile inputFile, RustVisitorContext visitorContext) { cpdTokens.save(); } + private void highlightToken(Token token, TokenLocation tokenLocation, NewHighlighting highlighting){ + final String tokenImage = getTokenImage(token); + if (token.getType().equals(RustTokenType.CHARACTER_LITERAL) + || token.getType().equals(RustTokenType.STRING_LITERAL) + || token.getType().equals(RustTokenType.RAW_STRING_LITERAL) + || token.getType().equals(RustTokenType.RAW_BYTE_STRING_LITERAL) + + ) { + highlight(highlighting, tokenLocation, TypeOfText.STRING); + + } else if (keywords.contains(tokenImage)) { + highlight(highlighting, tokenLocation, TypeOfText.KEYWORD); + } + + if (token.getType().equals(RustTokenType.FLOAT_LITERAL) + || token.getType().equals(RustTokenType.BOOLEAN_LITERAL) + || token.getType().equals(RustTokenType.INTEGER_LITERAL)) { + highlight(highlighting, tokenLocation, TypeOfText.CONSTANT); + } + } + private Set identifyUnitTestTokens(List parsedTokens) { Set testTokens = new HashSet<>(); Set unitTestsAttributes = getUnitTestAttributes();