diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..696bcb3d --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,17 @@ +version: '{build}' +image: Visual Studio 2019 + +cache: + - C:\Users\appveyor\.m2 + +install: + - git submodule update --init --recursive + +build_script: + - mvn verify -B -V -e + +artifacts: + - path: 'sonar-groovy-plugin\target\*.jar' + +on_failure: + - ps: Get-ChildItem *\target\surefire-reports\*.txt | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } diff --git a/.github/travis-build.sh b/.github/travis-build.sh new file mode 100755 index 00000000..3fac0d1c --- /dev/null +++ b/.github/travis-build.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +add= + +if [ "$SONAR_VERSION" ] +then + add="$add -Dsonar.version=$SONAR_VERSION" + + case $SONAR_VERSION in + 8*) add="$add -PtestModernSonarQube" ;; + esac + +# Workaround until https://jira.sonarsource.com/browse/TRAVIS-19 is fixed +# See also https://community.sonarsource.com/t/travis-plugin-is-failing-on-external-pull-request/807/7 +elif [ -n "$SONAR_SCANNER_HOME" ] +then + # Only run SonarQube analysis on one Travis-CI matrix configuration + # (namely: empty $SONAR_VERSION and Java 8 + java -Xmx32m -version 2>&1 | grep -q 1.8.0 && add="sonar:sonar $add" +fi + +mvn -B -V -e -Dstyle.color=always verify $add + diff --git a/.gitmodules b/.gitmodules index e69de29b..c758f5e7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "codenarc-converter/CodeNarc"] + path = codenarc-converter/CodeNarc + url = ../../CodeNarc/CodeNarc.git diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 00000000..c7de7245 --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1 @@ +-Pcoverage diff --git a/.travis.yml b/.travis.yml index a0a2ffe7..2dd9811c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,36 @@ language: java -sudo: false jdk: -- oraclejdk8 -addons: - apt: - packages: - - oracle-java8-installer +- openjdk8 +- openjdk11 +env: +- SONAR_VERSION= +- SONAR_VERSION=7.9 +- SONAR_VERSION=8.0 +- SONAR_VERSION=8.3.0.34182 +# Install step is redundant install: true -script: mvn verify -B -e -V -matrix: - fast_finish: true +script: +- .github/travis-build.sh + +dist: xenial cache: directories: - '$HOME/.m2/repository' + - '$HOME/.sonar/cache' + +addons: + sonarcloud: + organization: "tobix-github" + token: + secure: "pYxJ0DaMAkDCTjuRrfvSbkMy4tM82JHMoFagVIIaqVY6fIaltfi9QB4U6p9a8OzXLs601Cr4btwWAPpqi5GfMaDbdOzCBxz2mfoO8skawCTr0M2Ij8yaStgiNoIornqCT1VyKRE4mOJMeX2ayqyiKvn0gCFliV8dwg+4zl1vv1bdCsUzSGLPKFRv2KWFRnPbBjeimwCySImdAzn5IMOA3r583kAkuTQqJNH2ai60QEAZpWRN9E98ZkwYwsf/+7i/1NbURL/a+km3Eq+E10unblx66zpARA5k10Ygg9xP8c4+PtvVOTW3zlQbLsvp+d1Bz+kkkvBjbX6rxhxtlQ5uMbw2H9zyXVgwUtZz/4Dq4J3pbWx//OA6K+NVKwVmEuogR2Kdxbbbehp3v1MjNx7YP+82sMx+6vSHR5aH/wmBe2VEtSXZeOmKPkpGNXlRduMOsgyP+yU3xqCUX9EFCMcepEcoM9wPWfTMxLUyfrD1MP1yzC5gTMRVBNoE5pO+WopE2JFUOYmtRz5hpU6DYLHjQ5dSDRl2g7Ig7/3FrDzaVrAd8XIDvIz34UGwTarpEDIQrIM2EmHglOXclRKtll0n4HhcU5eLVhLd7poJpBbtePNnxF+NcaYOk9Qww/i6SigYoA+jKZrNK9N2aQMBXROculcenLenoBUgQwG9yuPeeFw=" + +# SonarCloud needs unlimited depth +git: + depth: false notifications: email: false diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..2a21a89e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,77 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [Unreleased] + +### Changed +- Remove backwards compatibility hacks (drop support for everything older then + SonarQube 7.8) +- Minor dependency updates +- Deprecates support for JaCoCo binary format (use the SonarQube JaCoCo plugin + instead) + +### Removed +- Compatibility with JaCoCo older the 0.7.5 (released in mid-2015) + +## [1.6] - 2019-07-04 + +### Added +- This changelog + +### Changed +- Moved project from SonarSource to community +- Update plugin to be compatible with SonarQube 6.7-7.8 +- Analyze plugin on SonarCloud +- Do CI builds on Travis & AppVeyor +- Integrate codenarc converter into the default build +- Include CodeNarc as a git submodule (so the converter can run on Travis) +- Fix a bunch of deprecated constructs +- Only report overall JaCoCo test coverage +- Updated CodeNarc to 1.4 & GMetrics to 1.0 + +### Fixed +- fix multiple codenarc files (pmayweg#60) +- Fix bug in JaCoCo package name handling (pmayweg#74) +- Update JaCoCo for Java 10+ support +- Remove old metrics (fixes #6) + +### Removed +- Coupling metrics, since SonarQube doesn't support them anymore + +## [1.5] - 2017-05-10 + +Please see the Git history for older changes + +## [1.4-RC1] - 2016-08-05 + +## [1.3.1] - 2015-12-02 + +## [1.3] - 2015-11-06 + +## [1.2] - 2015-08-12 + +## [1.1.1] - 2015-05-28 + +## [1.1] - 2015-03-17 + +## [1.0.1] - 2014-03-14 + +## [1.0] - 2014-02-24 + +## [0.6] - 2012-08-06 + +[Unreleased]: https://github.com/Inform-Software/sonar-groovy/compare/1.6...HEAD +[1.6]: https://github.com/Inform-Software/sonar-groovy/compare/1.5...1.6 +[1.5]: https://github.com/Inform-Software/sonar-groovy/compare/1.4-RC1...1.5 +[1.4-RC1]: https://github.com/Inform-Software/sonar-groovy/compare/1.3.1...1.4-RC1 +[1.3.1]: https://github.com/Inform-Software/sonar-groovy/compare/1.3...1.3.1 +[1.3]: https://github.com/Inform-Software/sonar-groovy/compare/1.2...1.3 +[1.2]: https://github.com/Inform-Software/sonar-groovy/compare/1.1.1...1.2 +[1.1.1]: https://github.com/Inform-Software/sonar-groovy/compare/1.1...1.1.1 +[1.1]: https://github.com/Inform-Software/sonar-groovy/compare/1.0.1...1.1 +[1.0.1]: https://github.com/Inform-Software/sonar-groovy/compare/1.0...1.0.1 +[1.0]: https://github.com/Inform-Software/sonar-groovy/compare/0.6...1.0 +[0.6]: https://github.com/Inform-Software/sonar-groovy/releases/tag/0.6 + diff --git a/README.md b/README.md index 62ca4e51..1a02ac7c 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,30 @@ -SonarQube plugin for Groovy -========== +# SonarQube plugin for Groovy -### Build status +[![Build status (Travis CI)](https://travis-ci.com/Inform-Software/sonar-groovy.svg?branch=master)](https://travis-ci.com/Inform-Software/sonar-groovy) +[![Build status (AppVeyor)](https://ci.appveyor.com/api/projects/status/si4v2gs7h4qiv27j/branch/master?svg=true)](https://ci.appveyor.com/project/TobiX/sonar-groovy/branch/master) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=org.sonarsource.groovy%3Agroovy&metric=alert_status)](https://sonarcloud.io/dashboard?id=org.sonarsource.groovy%3Agroovy) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=org.sonarsource.groovy%3Agroovy&metric=coverage)](https://sonarcloud.io/dashboard?id=org.sonarsource.groovy%3Agroovy) -[![Build Status](https://api.travis-ci.org/pmayweg/sonar-groovy.png)](https://travis-ci.org/pmayweg/sonar-groovy) +Get test builds from [AppVeyor](https://ci.appveyor.com/project/TobiX/sonar-groovy/build/artifacts). ## Description -The plugin enables analysis of Groovy within SonarQube. -It leverages [CodeNarc](http://codenarc.sourceforge.net/) to raise issues against coding rules, [GMetrics](http://gmetrics.sourceforge.net/) for cyclomatic complexity and [Cobertura](http://cobertura.sourceforge.net/) or [JaCoCo](http://www.eclemma.org/jacoco/) for code coverage. +This plugin enables analysis of Groovy within SonarQube. -Plugin | 0.1 | 0.2 | 0.3 | 0.4 | 0.5 | 0.6 | 1.0 | 1.1 | 1.1.1 | 1.2 | 1.3 | 1.3.1 | 1.4 ----------|-----|-----|------|------|--------|------|------|------|-------|------|--------|--------|------- -CodeNarc | 0.9 | 0.9 | 0.13 | 0.15 | 0.16.1 | 0.17 | 0.20 | 0.23 | 0.23 | 0.24 | 0.24.1 | 0.24.1 | 0.25.2 -GMetrics | 0.2 | 0.2 | 0.3 | 0.3 | 0.4 | 0.5 | 0.6 | 0.6 | 0.7 | 0.7 | 0.7 | 0.7 | 0.7 +It leverages [CodeNarc](http://codenarc.sourceforge.net/) to raise issues +against coding rules and [GMetrics](http://gmetrics.sourceforge.net/) for +cyclomatic complexity. + +For code coverage, the SonarQube [JaCoCo](http://www.eclemma.org/jacoco/) +plugin should be used. Additionally, this plugin still supports importing +binary JaCoCo reports (deprecated, will be removed in the future) and +[Cobertura](http://cobertura.sourceforge.net/). + +Plugin | 1.4/1.5 | 1.6 | 1.7 +----------|---------|---------|----- +CodeNarc | 0.25.2 | 1.4 | 1.4 +GMetrics | 0.7 | 1.0 | 1.0 +SonarQube | 5.6-6.7 | 6.7-7.9 | 7.8-8.3 ## Steps to Analyze a Groovy Project 1. Install SonarQube Server @@ -24,32 +35,79 @@ GMetrics | 0.2 | 0.2 | 0.3 | 0.3 | 0.4 | 0.5 | 0.6 | 0.6 | 0.7 | 0.7 1. Follow the link provided at the end of the analysis to browse your project's quality in SonarQube UI ## Notes -*CodeNarc* -It is possible to reuse a previously generated report from CodeNarc by setting the `sonar.groovy.codenarc.reportPaths` property. -*Groovy File Suffixes* -It is possible to define multiple groovy file suffixes to be recognized by setting the `sonar.groovy.file.suffixes` property. Note that by default, only files having `.groovy` as extension will be analyzed. +*CodeNarc*: It is possible to reuse a previously generated report from CodeNarc +by setting the `sonar.groovy.codenarc.reportPaths` property. -*Unit Tests Execution Reports* -Import unit tests execution reports (JUnit XML format) by setting the sonar.junit.reportsPath property. Default location is _target/surefire-reports_. +*Groovy File Suffixes*: It is possible to define multiple groovy file suffixes +to be recognized by setting the `sonar.groovy.file.suffixes` property. Note +that by default, only files having `.groovy` as extension will be analyzed. -*JaCoCo and Binaries* -The groovy plugin requires access to source binaries when analyzing JaCoCo reports. Consequently, property `sonar.groovy.binaries` has to be configured for the analysis (comma-separated paths to binary folders). For Maven and gradle projects, the property is automatically set. +*Unit Tests Execution Reports*: Import unit tests execution reports (JUnit XML +format) by setting the sonar.junit.reportsPath property. Default location is +_target/surefire-reports_. + +*JaCoCo and Binaries*: The groovy plugin requires access to source binaries +when analyzing JaCoCo reports. Consequently, property `sonar.groovy.binaries` +has to be configured for the analysis (comma-separated paths to binary +folders). For Maven and gradle projects, the property is automatically set. ## Coverage Results Import -The Groovy Plugin does not generate its own test coverage report, but re-uses the ones generated by Cobertura or JaCoCo. + +For coverage, it is recommended to use the generic [SonarQube JaCoCo +plugin](https://community.sonarsource.com/t/coverage-test-data-importing-jacoco-coverage-report-in-xml-format/12151) +instead of relying on this plugin to import coverage into SonarQube. +Nevertheless, we support importing coverage from Cobertura (but this code path +isn't used by the author of the plugin). ### Code Coverage with Cobertura + To display code coverage data: -1. Prior to the SonarQube analysis, execute your unit tests and generate the Cobertura XML report. -1. Import this report while running the SonarQube analysis by setting the `sonar.groovy.cobertura.reportPath` property to the path to the Cobertura XML report. The path may be absolute or relative to the project base directory. +1. Prior to the SonarQube analysis, execute your unit tests and generate the + Cobertura XML report. +1. Import this report while running the SonarQube analysis by setting the + `sonar.groovy.cobertura.reportPath` property to the path to the Cobertura + XML report. The path may be absolute or relative to the project base + directory. -### Code Coverage with JaCoCo -To display code coverage data: +## Contributions + +Contributions via GitHub [issues] and pull requests are very welcome. This +project tries to adhere to the [Google Java Style], but we don't want a global +reformat to keep the Git history readable. To help with this, you can use the +[fmt-maven-plugin] to format your changes: + + mvn fmt:format -DfilesNamePattern=TestUtils\.java + +You can use the `fileNamePattern` option to restrict the formatter to the files +you changed. + +[issues]: https://github.com/Inform-Software/sonar-groovy/issues/new +[Google Java Style]: https://google.github.io/styleguide/javaguide.html +[fmt-maven-plugin]: https://github.com/coveo/fmt-maven-plugin + +### Updating CodeNarc + +In the directory `codenarc-converter` there is a little helper tool to convert +CodeNarc rules to SonarQube rules. To do its job it needs a source copy of +CodeNarc - this is currently achieved by including the used CodeNarc version as +a git subbmodule. If you need to update CodeNarc, you need to update that +submodule too: + +``` +git submodule init +cd codenarc-converter/CodeNarc +git checkout vX.Y.Z +cd .. +git add CodeNarc +``` + +You should then run the `codenarc-converter` (Running `mvn verify` should be +enough if the project is set up correctly) and merge descriptions from +`codenarc-converter/target/results/rules.xml` into +`sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/rules.xml`. +The converter does a pretty crude job converting CodeNarc's [APT] documentation +into SonarQube rule descriptions. -1. Prior to the SonarQube analysis, execute your tests and generate the JaCoCo exec file(s). -1. In order to be able to read the exec report file, and as JaCoCo bases its analysis on binaries, set the sonar.binaries property. -1. Set the `sonar.groovy.jacoco.reportPath` property to the path to the JaCoCo exec file related to your unit tests. -1. (Optional) If you are running integration tests on top of your unit tests, you may want to set the `sonar.groovy.jacoco.itReportPath` to the path to JaCoCo exec file related to the integration tests. -1. Run the SonarQube analysis. +[APT]: https://maven.apache.org/doxia/references/apt-format.html diff --git a/codenarc-converter/CodeNarc b/codenarc-converter/CodeNarc new file mode 160000 index 00000000..e32d9bd5 --- /dev/null +++ b/codenarc-converter/CodeNarc @@ -0,0 +1 @@ +Subproject commit e32d9bd5a182e70ff330dc34a811307e4c420377 diff --git a/codenarc-converter/pom.xml b/codenarc-converter/pom.xml new file mode 100644 index 00000000..c97de6a9 --- /dev/null +++ b/codenarc-converter/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + + org.sonarsource.groovy + groovy + 1.7-CIT-MVN-162-SNAPSHOT + + + sonar-codenarc-converter + + Sonar CodeNarc Converter + + + 1.7 + + + + + org.slf4j + slf4j-simple + + + com.googlecode.java-diff-utils + diffutils + 1.3.0 + test + + + org.sonarsource.sonarqube + sonar-testing-harness + ${sonar.version} + test + + + org.sonarsource.sonarqube + sonar-plugin-api + + + org.codenarc + CodeNarc + + + com.google.guava + guava + 28.1-jre + + + commons-io + commons-io + + + commons-lang + commons-lang + + + junit + junit + + + org.assertj + assertj-core + + + + com.google.auto.value + auto-value-annotations + ${auto.version} + + + com.google.auto.value + auto-value + ${auto.version} + provided + + + + + + run-converter + + + ${basedir}/CodeNarc/src/site/apt + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + run-codenarc-converter + generate-test-sources + + java + + + org.sonar.plugins.groovy.codenarc.Converter + + ${project.basedir} + + + + + + + + + + diff --git a/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Converter.java b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Converter.java new file mode 100644 index 00000000..eaad4aa8 --- /dev/null +++ b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Converter.java @@ -0,0 +1,691 @@ +/* + * Sonar CodeNarc Converter + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com + * + * 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. + */ +package org.sonar.plugins.groovy.codenarc; + +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import org.codenarc.rule.AbstractRule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.plugins.groovy.codenarc.apt.AptParser; +import org.sonar.plugins.groovy.codenarc.apt.AptResult; +import org.sonar.plugins.groovy.codenarc.printer.XMLPrinter; + +public class Converter { + + private static final Logger log = LoggerFactory.getLogger(Converter.class); + + /** location of the apt files in the CodeNarc project (see git submodule) */ + private static final String RULES_APT_FILES_LOCATION = "CodeNarc/src/site/apt"; + + private int count = 0; + private Map rulesByVersion = Maps.newHashMap(); + private Map rulesByTags = Maps.newHashMap(); + private Set duplications = new HashSet<>(); + + public static void main(String[] args) throws Exception { + Path baseDir = Paths.get("."); + if (args.length > 0) { + baseDir = Paths.get(args[0]); + } + + Path targetFile = getResultFile(baseDir); + Converter converter = process(baseDir, targetFile); + + converter.resultsByCategory(); + converter.resultsByVersion(); + log.info("{} rules processed", converter.count); + } + + public static Converter process(Path codeNarcDir, Path targetFile) + throws IOException, ReflectiveOperationException { + Path aptDir = codeNarcDir.resolve(RULES_APT_FILES_LOCATION); + Converter converter = new Converter(); + new XMLPrinter().init(converter).process(Converter.loadRules(aptDir)).printAll(targetFile); + return converter; + } + + private static Path getResultFile(Path baseDir) throws IOException { + Path folder = baseDir.resolve("target/results/rules.xml"); + Files.createDirectories(folder.getParent()); + return folder; + } + + public static Multimap loadRules(Path aptDir) + throws IOException, ReflectiveOperationException { + Properties props = new Properties(); + props.load(Converter.class.getResourceAsStream("/codenarc-base-messages.properties")); + + Map parametersByRule = retrieveRulesParameters(aptDir); + + Multimap rules = LinkedListMultimap.create(); + + insertRules( + rules, /* legacy */ + null, + props, + parametersByRule, + org.codenarc.rule.unused.UnusedArrayRule.class, + org.codenarc.rule.unused.UnusedObjectRule.class, + org.codenarc.rule.unused.UnusedPrivateFieldRule.class, + org.codenarc.rule.unused.UnusedPrivateMethodRule.class, + org.codenarc.rule.unused.UnusedVariableRule.class, + org.codenarc.rule.unnecessary.UnnecessaryBooleanExpressionRule.class, + org.codenarc.rule.unnecessary.UnnecessaryIfStatementRule.class, + org.codenarc.rule.unnecessary.UnnecessaryTernaryExpressionRule.class, + org.codenarc.rule.size.ClassSizeRule.class, + org.codenarc.rule.size.CyclomaticComplexityRule.class, + org.codenarc.rule.size.MethodCountRule.class, + org.codenarc.rule.size.MethodSizeRule.class, + org.codenarc.rule.size.NestedBlockDepthRule.class, + // org.codenarc.rule.size.AbcComplexityRule.class, - deprecated in 0.18 + org.codenarc.rule.naming.AbstractClassNameRule.class, + org.codenarc.rule.naming.ClassNameRule.class, + org.codenarc.rule.naming.FieldNameRule.class, + org.codenarc.rule.naming.InterfaceNameRule.class, + org.codenarc.rule.naming.MethodNameRule.class, + org.codenarc.rule.naming.PackageNameRule.class, + org.codenarc.rule.naming.ParameterNameRule.class, + org.codenarc.rule.naming.PropertyNameRule.class, + org.codenarc.rule.naming.VariableNameRule.class, + org.codenarc.rule.logging.PrintlnRule.class, + org.codenarc.rule.logging.PrintStackTraceRule.class, + org.codenarc.rule.logging.SystemErrPrintRule.class, + org.codenarc.rule.logging.SystemOutPrintRule.class, + org.codenarc.rule.junit.JUnitAssertAlwaysFailsRule.class, + org.codenarc.rule.junit.JUnitAssertAlwaysSucceedsRule.class, + org.codenarc.rule.junit.JUnitPublicNonTestMethodRule.class, + org.codenarc.rule.junit.JUnitSetUpCallsSuperRule.class, + org.codenarc.rule.junit.JUnitTearDownCallsSuperRule.class, + org.codenarc.rule.junit.JUnitUnnecessarySetUpRule.class, + org.codenarc.rule.junit.JUnitUnnecessaryTearDownRule.class, + org.codenarc.rule.imports.DuplicateImportRule.class, + org.codenarc.rule.imports.ImportFromSamePackageRule.class, + org.codenarc.rule.imports.UnnecessaryGroovyImportRule.class, + org.codenarc.rule.imports.UnusedImportRule.class, + org.codenarc.rule.grails.GrailsPublicControllerMethodRule.class, + // org.codenarc.rule.grails.GrailsSessionReferenceRule.class, + // - removed in 1.0 (was already depreacted since 0.9) + org.codenarc.rule.grails.GrailsServletContextReferenceRule.class, + org.codenarc.rule.grails.GrailsStatelessServiceRule.class, + org.codenarc.rule.generic.IllegalRegexRule.class, + org.codenarc.rule.generic.RequiredRegexRule.class, + org.codenarc.rule.generic.RequiredStringRule.class, + org.codenarc.rule.generic.StatelessClassRule.class, + org.codenarc.rule.exceptions.CatchErrorRule.class, + org.codenarc.rule.exceptions.CatchExceptionRule.class, + org.codenarc.rule.exceptions.CatchNullPointerExceptionRule.class, + org.codenarc.rule.exceptions.CatchRuntimeExceptionRule.class, + org.codenarc.rule.exceptions.CatchThrowableRule.class, + org.codenarc.rule.exceptions.ThrowErrorRule.class, + org.codenarc.rule.exceptions.ThrowExceptionRule.class, + org.codenarc.rule.exceptions.ThrowNullPointerExceptionRule.class, + org.codenarc.rule.exceptions.ThrowRuntimeExceptionRule.class, + org.codenarc.rule.exceptions.ThrowThrowableRule.class, + org.codenarc.rule.basic.BigDecimalInstantiationRule.class, + org.codenarc.rule.basic.ConstantIfExpressionRule.class, + org.codenarc.rule.basic.ConstantTernaryExpressionRule.class, + org.codenarc.rule.basic.EmptyCatchBlockRule.class, + org.codenarc.rule.basic.EmptyElseBlockRule.class, + org.codenarc.rule.basic.EmptyFinallyBlockRule.class, + org.codenarc.rule.basic.EmptyForStatementRule.class, + org.codenarc.rule.basic.EmptyIfStatementRule.class, + org.codenarc.rule.basic.EmptySwitchStatementRule.class, + org.codenarc.rule.basic.EmptySynchronizedStatementRule.class, + org.codenarc.rule.basic.EmptyTryBlockRule.class, + org.codenarc.rule.basic.EmptyWhileStatementRule.class, + org.codenarc.rule.basic.EqualsAndHashCodeRule.class, + org.codenarc.rule.basic.ReturnFromFinallyBlockRule.class, + org.codenarc.rule.basic.ThrowExceptionFromFinallyBlockRule.class, + org.codenarc.rule.braces.IfStatementBracesRule.class, + org.codenarc.rule.braces.ElseBlockBracesRule.class, + org.codenarc.rule.braces.ForStatementBracesRule.class, + org.codenarc.rule.braces.WhileStatementBracesRule.class, + org.codenarc.rule.concurrency.NestedSynchronizationRule.class, + org.codenarc.rule.concurrency.SynchronizedMethodRule.class, + org.codenarc.rule.concurrency.SynchronizedOnThisRule.class, + org.codenarc.rule.concurrency.SystemRunFinalizersOnExitRule.class, + org.codenarc.rule.concurrency.ThreadGroupRule.class, + org.codenarc.rule.concurrency.ThreadLocalNotStaticFinalRule.class, + org.codenarc.rule.concurrency.ThreadYieldRule.class, + org.codenarc.rule.concurrency.VolatileLongOrDoubleFieldRule.class, + // moved from basic in 0.16 + org.codenarc.rule.design.CloneableWithoutCloneRule.class, + org.codenarc.rule.design.ImplementationAsTypeRule.class); + + insertRules( + rules, + "0.11", + props, + parametersByRule, + org.codenarc.rule.naming.ConfusingMethodNameRule.class, + org.codenarc.rule.naming.ObjectOverrideMisspelledMethodNameRule.class, + org.codenarc.rule.junit.JUnitStyleAssertionsRule.class, + org.codenarc.rule.junit.UseAssertEqualsInsteadOfAssertTrueRule.class, + org.codenarc.rule.junit.UseAssertFalseInsteadOfNegationRule.class, + org.codenarc.rule.junit.UseAssertTrueInsteadOfAssertEqualsRule.class, + org.codenarc.rule.junit.UseAssertNullInsteadOfAssertEqualsRule.class, + org.codenarc.rule.junit.UseAssertSameInsteadOfAssertTrueRule.class, + org.codenarc.rule.junit.JUnitFailWithoutMessageRule.class, + org.codenarc.rule.exceptions.CatchIllegalMonitorStateExceptionRule.class, + org.codenarc.rule.exceptions.ConfusingClassNamedExceptionRule.class, + org.codenarc.rule.exceptions.ReturnNullFromCatchBlockRule.class, + org.codenarc.rule.dry.DuplicateNumberLiteralRule.class, + org.codenarc.rule.dry.DuplicateStringLiteralRule.class, + org.codenarc.rule.basic.DeadCodeRule.class, + org.codenarc.rule.basic.DoubleNegativeRule.class, + org.codenarc.rule.basic.DuplicateCaseStatementRule.class, + org.codenarc.rule.basic.RemoveAllOnSelfRule.class, + // org.codenarc.rule.basic.SerialVersionUIDRule.class - removed in 0.14 + org.codenarc.rule.concurrency.SynchronizedOnGetClassRule.class, + org.codenarc.rule.concurrency.UseOfNotifyMethodRule.class, + // moved from basic in 0.16 + org.codenarc.rule.design.BooleanMethodReturnsNullRule.class, + // moved from basic in 0.16 + org.codenarc.rule.design.ReturnsNullInsteadOfEmptyArrayRule.class, + // moved from basic in 0.16 + org.codenarc.rule.design.ReturnsNullInsteadOfEmptyCollectionRule.class, + org.codenarc.rule.convention.InvertedIfElseRule.class, + org.codenarc.rule.groovyism.ExplicitArrayListInstantiationRule.class, + org.codenarc.rule.groovyism.ExplicitCallToAndMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToCompareToMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToDivMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToEqualsMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToGetAtMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToLeftShiftMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToMinusMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToMultiplyMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToModMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToOrMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToPlusMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToPowerMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToRightShiftMethodRule.class, + org.codenarc.rule.groovyism.ExplicitCallToXorMethodRule.class, + org.codenarc.rule.groovyism.ExplicitHashMapInstantiationRule.class, + org.codenarc.rule.groovyism.ExplicitHashSetInstantiationRule.class, + org.codenarc.rule.groovyism.ExplicitLinkedListInstantiationRule.class, + org.codenarc.rule.groovyism.ExplicitStackInstantiationRule.class, + org.codenarc.rule.groovyism.ExplicitTreeSetInstantiationRule.class, + org.codenarc.rule.groovyism.GStringAsMapKeyRule.class); + + insertRules( + rules, + "0.12", + props, + parametersByRule, + org.codenarc.rule.unused.UnusedPrivateMethodParameterRule.class, + org.codenarc.rule.unnecessary.UnnecessaryBigDecimalInstantiationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryBigIntegerInstantiationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryBooleanInstantiationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryCallForLastElementRule.class, + org.codenarc.rule.unnecessary.UnnecessaryCatchBlockRule.class, + org.codenarc.rule.unnecessary.UnnecessaryCollectCallRule.class, + org.codenarc.rule.unnecessary.UnnecessaryCollectionCallRule.class, + org.codenarc.rule.unnecessary.UnnecessaryConstructorRule.class, + org.codenarc.rule.unnecessary.UnnecessaryDoubleInstantiationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryFloatInstantiationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryGetterRule.class, + org.codenarc.rule.unnecessary.UnnecessaryGStringRule.class, + org.codenarc.rule.unnecessary.UnnecessaryInstantiationToGetClassRule.class, + org.codenarc.rule.unnecessary.UnnecessaryIntegerInstantiationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryLongInstantiationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryObjectReferencesRule.class, + org.codenarc.rule.unnecessary.UnnecessaryNullCheckRule.class, + org.codenarc.rule.unnecessary.UnnecessaryNullCheckBeforeInstanceOfRule.class, + org.codenarc.rule.unnecessary.UnnecessaryOverridingMethodRule.class, + org.codenarc.rule.unnecessary.UnnecessaryReturnKeywordRule.class, + org.codenarc.rule.unnecessary.UnnecessaryStringInstantiationRule.class, + org.codenarc.rule.logging.LoggerForDifferentClassRule.class, + org.codenarc.rule.logging.LoggingSwallowsStacktraceRule.class, + org.codenarc.rule.logging.LoggerWithWrongModifiersRule.class, + org.codenarc.rule.logging.MultipleLoggersRule.class, + org.codenarc.rule.junit.UseAssertTrueInsteadOfNegationRule.class, + org.codenarc.rule.junit.JUnitTestMethodWithoutAssertRule.class, + org.codenarc.rule.exceptions.CatchArrayIndexOutOfBoundsExceptionRule.class, + org.codenarc.rule.exceptions.CatchIndexOutOfBoundsExceptionRule.class, + org.codenarc.rule.exceptions.MissingNewInThrowStatementRule.class, + org.codenarc.rule.basic.ExplicitGarbageCollectionRule.class, + // moved from basic in 0.16 + org.codenarc.rule.design.CompareToWithoutComparableRule.class, + org.codenarc.rule.design.SimpleDateFormatMissingLocaleRule.class, + org.codenarc.rule.design.AbstractClassWithoutAbstractMethodRule.class, + org.codenarc.rule.design.CloseWithoutCloseableRule.class, + org.codenarc.rule.design.ConstantsOnlyInterfaceRule.class, + org.codenarc.rule.design.EmptyMethodInAbstractClassRule.class, + org.codenarc.rule.design.FinalClassWithProtectedMemberRule.class, + org.codenarc.rule.convention.ConfusingTernaryRule.class); + + insertRules( + rules, + "0.13", + props, + parametersByRule, + // moved from basic in 0.16 + org.codenarc.rule.unnecessary.AddEmptyStringRule.class, + // moved from basic in 0.16 + org.codenarc.rule.unnecessary.ConsecutiveLiteralAppendsRule.class, + // moved from basic in 0.16 + org.codenarc.rule.unnecessary.ConsecutiveStringConcatenationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryCallToSubstringRule.class, + org.codenarc.rule.unnecessary.UnnecessaryDefInMethodDeclarationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryModOneRule.class, + org.codenarc.rule.unnecessary.UnnecessaryPublicModifierRule.class, + org.codenarc.rule.unnecessary.UnnecessarySelfAssignmentRule.class, + org.codenarc.rule.unnecessary.UnnecessarySemicolonRule.class, + org.codenarc.rule.unnecessary.UnnecessaryTransientModifierRule.class, + org.codenarc.rule.junit.ChainedTestRule.class, + org.codenarc.rule.junit.CoupledTestCaseRule.class, + org.codenarc.rule.junit.UnnecessaryFailRule.class, + org.codenarc.rule.exceptions.ExceptionExtendsErrorRule.class, + org.codenarc.rule.basic.AssignmentInConditionalRule.class, + org.codenarc.rule.basic.BooleanGetBooleanRule.class, + org.codenarc.rule.basic.BrokenOddnessCheckRule.class, + org.codenarc.rule.basic.EmptyInstanceInitializerRule.class, + org.codenarc.rule.basic.EmptyMethodRule.class, + org.codenarc.rule.basic.EmptyStaticInitializerRule.class, + org.codenarc.rule.basic.IntegerGetIntegerRule.class, + // org.codenarc.rule.basic.SerializableClassMustDefineSerialVersionUIDRule.class + // - removed in 0.14 + org.codenarc.rule.concurrency.BusyWaitRule.class, + org.codenarc.rule.concurrency.DoubleCheckedLockingRule.class, + org.codenarc.rule.concurrency.InconsistentPropertyLockingRule.class, + org.codenarc.rule.concurrency.InconsistentPropertySynchronizationRule.class, + org.codenarc.rule.concurrency.StaticCalendarFieldRule.class, + org.codenarc.rule.concurrency.StaticDateFormatFieldRule.class, + org.codenarc.rule.concurrency.StaticMatcherFieldRule.class, + org.codenarc.rule.concurrency.SynchronizedOnBoxedPrimitiveRule.class, + org.codenarc.rule.concurrency.SynchronizedOnStringRule.class, + org.codenarc.rule.concurrency.SynchronizedReadObjectMethodRule.class, + org.codenarc.rule.concurrency.SynchronizedOnReentrantLockRule.class, + org.codenarc.rule.concurrency.VolatileArrayFieldRule.class, + org.codenarc.rule.concurrency.WaitOutsideOfWhileLoopRule.class, + org.codenarc.rule.groovyism.GroovyLangImmutableRule.class); + + insertRules( + rules, + "0.14", + props, + parametersByRule, + org.codenarc.rule.security.NonFinalSubclassOfSensitiveInterfaceRule.class, + org.codenarc.rule.security.InsecureRandomRule.class, + org.codenarc.rule.security.FileCreateTempFileRule.class, + org.codenarc.rule.security.SystemExitRule.class, + org.codenarc.rule.security.ObjectFinalizeRule.class, + org.codenarc.rule.security.JavaIoPackageAccessRule.class, + org.codenarc.rule.security.UnsafeArrayDeclarationRule.class, + org.codenarc.rule.security.PublicFinalizeMethodRule.class, + org.codenarc.rule.security.NonFinalPublicFieldRule.class, + org.codenarc.rule.jdbc.DirectConnectionManagementRule.class, + org.codenarc.rule.unnecessary.UnnecessaryFinalOnPrivateMethodRule.class, + org.codenarc.rule.unnecessary.UnnecessaryElseStatementRule.class, + org.codenarc.rule.unnecessary.UnnecessaryParenthesesForMethodCallWithClosureRule.class, + org.codenarc.rule.unnecessary.UnnecessaryPackageReferenceRule.class, + org.codenarc.rule.junit.SpockIgnoreRestUsedRule.class, + org.codenarc.rule.imports.ImportFromSunPackagesRule.class, + org.codenarc.rule.imports.MisorderedStaticImportsRule.class, + org.codenarc.rule.generic.IllegalPackageReferenceRule.class, + org.codenarc.rule.exceptions.SwallowThreadDeathRule.class, + org.codenarc.rule.basic.DuplicateMapKeyRule.class, + org.codenarc.rule.basic.DuplicateSetValueRule.class, + org.codenarc.rule.basic.EqualsOverloadedRule.class, + org.codenarc.rule.basic.ForLoopShouldBeWhileLoopRule.class, + org.codenarc.rule.basic.ClassForNameRule.class, + org.codenarc.rule.basic.ComparisonOfTwoConstantsRule.class, + org.codenarc.rule.basic.ComparisonWithSelfRule.class, + org.codenarc.rule.serialization.SerialVersionUIDRule.class, + org.codenarc.rule.serialization.SerializableClassMustDefineSerialVersionUIDRule.class, + org.codenarc.rule.serialization.SerialPersistentFieldsRule.class, + org.codenarc.rule.concurrency.StaticConnectionRule.class, + org.codenarc.rule.concurrency.StaticSimpleDateFormatFieldRule.class, + org.codenarc.rule.design.PublicInstanceFieldRule.class, + org.codenarc.rule.design.StatelessSingletonRule.class, + org.codenarc.rule.design.AbstractClassWithPublicConstructorRule.class, + org.codenarc.rule.groovyism.ExplicitLinkedHashMapInstantiationRule.class, + org.codenarc.rule.groovyism.ClosureAsLastMethodParameterRule.class); + + insertRules( + rules, + "0.15", + props, + parametersByRule, + org.codenarc.rule.jdbc.JdbcConnectionReferenceRule.class, + org.codenarc.rule.jdbc.JdbcResultSetReferenceRule.class, + org.codenarc.rule.jdbc.JdbcStatementReferenceRule.class, + org.codenarc.rule.unnecessary.UnnecessaryDefInVariableDeclarationRule.class, + org.codenarc.rule.unnecessary.UnnecessaryDotClassRule.class, + org.codenarc.rule.unnecessary.UnnecessaryInstanceOfCheckRule.class, + org.codenarc.rule.unnecessary.UnnecessarySubstringRule.class, + org.codenarc.rule.grails.GrailsDomainHasToStringRule.class, + org.codenarc.rule.grails.GrailsDomainHasEqualsRule.class, + org.codenarc.rule.generic.IllegalClassReferenceRule.class, + org.codenarc.rule.basic.BitwiseOperatorInConditionalRule.class, + org.codenarc.rule.basic.HardCodedWindowsFileSeparatorRule.class, + org.codenarc.rule.basic.RandomDoubleCoercedToZeroRule.class, + org.codenarc.rule.basic.HardCodedWindowsRootDirectoryRule.class, + org.codenarc.rule.formatting.BracesForClassRule.class, + org.codenarc.rule.formatting.LineLengthRule.class, + org.codenarc.rule.formatting.BracesForForLoopRule.class, + org.codenarc.rule.formatting.BracesForIfElseRule.class, + org.codenarc.rule.formatting.BracesForMethodRule.class, + org.codenarc.rule.formatting.BracesForTryCatchFinallyRule.class, + // moved from formatting into comments in 1.3 + org.codenarc.rule.comments.ClassJavadocRule.class, + org.codenarc.rule.groovyism.AssignCollectionUniqueRule.class); + + insertRules( + rules, + "0.16", + props, + parametersByRule, + org.codenarc.rule.unused.UnusedMethodParameterRule.class, + org.codenarc.rule.unnecessary.UnnecessaryDefInFieldDeclarationRule.class, + org.codenarc.rule.naming.FactoryMethodNameRule.class, + org.codenarc.rule.dry.DuplicateMapLiteralRule.class, + org.codenarc.rule.dry.DuplicateListLiteralRule.class, + org.codenarc.rule.design.BuilderMethodWithSideEffectsRule.class, + org.codenarc.rule.convention.CouldBeElvisRule.class, + org.codenarc.rule.convention.LongLiteralWithLowerCaseLRule.class, + org.codenarc.rule.groovyism.AssignCollectionSortRule.class, + org.codenarc.rule.groovyism.ConfusingMultipleReturnsRule.class, + org.codenarc.rule.groovyism.GetterMethodCouldBePropertyRule.class, + org.codenarc.rule.groovyism.UseCollectManyRule.class, + org.codenarc.rule.groovyism.CollectAllIsDeprecatedRule.class, + org.codenarc.rule.groovyism.UseCollectNestedRule.class); + + insertRules( + rules, + "0.17", + props, + parametersByRule, + org.codenarc.rule.size.CrapMetricRule.class, + org.codenarc.rule.basic.AssertWithinFinallyBlockRule.class, + org.codenarc.rule.basic.ConstantAssertExpressionRule.class, + org.codenarc.rule.basic.BrokenNullCheckRule.class, + org.codenarc.rule.design.PrivateFieldCouldBeFinalRule.class, + org.codenarc.rule.convention.ParameterReassignmentRule.class, + org.codenarc.rule.convention.TernaryCouldBeElvisRule.class, + org.codenarc.rule.convention.VectorIsObsoleteRule.class, + org.codenarc.rule.convention.HashtableIsObsoleteRule.class); + + insertRules( + rules, + "0.18", + props, + parametersByRule, + org.codenarc.rule.size.AbcMetricRule.class, + org.codenarc.rule.junit.JUnitLostTestRule.class, + org.codenarc.rule.junit.JUnitUnnecessaryThrowsExceptionRule.class, + org.codenarc.rule.grails.GrailsDuplicateMappingRule.class, + org.codenarc.rule.grails.GrailsDuplicateConstraintRule.class, + org.codenarc.rule.exceptions.ExceptionNotThrownRule.class, + org.codenarc.rule.formatting.SpaceAfterCommaRule.class, + org.codenarc.rule.formatting.SpaceAfterSemicolonRule.class, + org.codenarc.rule.formatting.SpaceAroundOperatorRule.class, + org.codenarc.rule.formatting.SpaceBeforeOpeningBraceRule.class, + org.codenarc.rule.formatting.SpaceAfterOpeningBraceRule.class, + org.codenarc.rule.formatting.SpaceAfterClosingBraceRule.class, + org.codenarc.rule.formatting.SpaceBeforeClosingBraceRule.class, + org.codenarc.rule.formatting.SpaceAfterIfRule.class, + org.codenarc.rule.formatting.SpaceAfterWhileRule.class, + org.codenarc.rule.formatting.SpaceAfterForRule.class, + org.codenarc.rule.formatting.SpaceAfterSwitchRule.class, + org.codenarc.rule.formatting.SpaceAfterCatchRule.class, + org.codenarc.rule.convention.IfStatementCouldBeTernaryRule.class); + + insertRules( + rules, + "0.19", + props, + parametersByRule, + org.codenarc.rule.security.UnsafeImplementationAsMapRule.class, + org.codenarc.rule.naming.ClassNameSameAsFilenameRule.class, + org.codenarc.rule.junit.JUnitPublicFieldRule.class, + org.codenarc.rule.junit.JUnitAssertEqualsConstantActualValueRule.class, + org.codenarc.rule.grails.GrailsDomainReservedSqlKeywordNameRule.class, + org.codenarc.rule.grails.GrailsDomainWithServiceReferenceRule.class, + org.codenarc.rule.generic.IllegalClassMemberRule.class, + org.codenarc.rule.basic.EmptyClassRule.class, + org.codenarc.rule.serialization.EnumCustomSerializationIgnoredRule.class, + org.codenarc.rule.concurrency.ThisReferenceEscapesConstructorRule.class, + org.codenarc.rule.design.CloneWithoutCloneableRule.class, + org.codenarc.rule.formatting.SpaceAroundClosureArrowRule.class, + org.codenarc.rule.groovyism.GStringExpressionWithinStringRule.class); + + insertRules( + rules, + "0.20", + props, + parametersByRule, + org.codenarc.rule.generic.IllegalStringRule.class, + org.codenarc.rule.design.LocaleSetDefaultRule.class, + org.codenarc.rule.formatting.SpaceAroundMapEntryColonRule.class, + org.codenarc.rule.formatting.ClosureStatementOnOpeningLineOfMultipleLineClosureRule.class); + + insertRules( + rules, + "0.21", + props, + parametersByRule, + org.codenarc.rule.unnecessary.UnnecessaryCastRule.class, + org.codenarc.rule.unnecessary.UnnecessaryToStringRule.class, + org.codenarc.rule.junit.JUnitPublicPropertyRule.class, + org.codenarc.rule.imports.NoWildcardImportsRule.class, + org.codenarc.rule.grails.GrailsMassAssignmentRule.class, + org.codenarc.rule.generic.IllegalSubclassRule.class, + org.codenarc.rule.exceptions.ExceptionExtendsThrowableRule.class, + org.codenarc.rule.basic.MultipleUnaryOperatorsRule.class, + org.codenarc.rule.design.ToStringReturnsNullRule.class, + org.codenarc.rule.formatting.ConsecutiveBlankLinesRule.class, + org.codenarc.rule.formatting.BlankLineBeforePackageRule.class, + org.codenarc.rule.formatting.FileEndsWithoutNewlineRule.class, + org.codenarc.rule.formatting.MissingBlankLineAfterImportsRule.class, + org.codenarc.rule.formatting.MissingBlankLineAfterPackageRule.class, + org.codenarc.rule.formatting.TrailingWhitespaceRule.class); + + insertRules( + rules, + "0.22", + props, + parametersByRule, + org.codenarc.rule.unnecessary.UnnecessarySafeNavigationOperatorRule.class, + org.codenarc.rule.naming.PackageNameMatchesFilePathRule.class, + org.codenarc.rule.design.InstanceofRule.class, + org.codenarc.rule.convention.NoDefRule.class); + + insertRules( + rules, + "0.23", + props, + parametersByRule, + org.codenarc.rule.size.ParameterCountRule.class, + org.codenarc.rule.design.NestedForLoopRule.class); + + insertRules( + rules, + "0.24", + props, + parametersByRule, + org.codenarc.rule.naming.ClassNameSameAsSuperclassRule.class, + org.codenarc.rule.naming.InterfaceNameSameAsSuperInterfaceRule.class, + org.codenarc.rule.design.AssignmentToStaticFieldFromInstanceMethodRule.class); + + insertRules( + rules, + "0.25", + props, + parametersByRule, + org.codenarc.rule.convention.TrailingCommaRule.class, + org.codenarc.rule.convention.NoTabCharacterRule.class); + + insertRules( + rules, + "1.0", + props, + parametersByRule, + org.codenarc.rule.convention.CouldBeSwitchStatementRule.class, + org.codenarc.rule.unnecessary.UnnecessarySetterRule.class); + + insertRules( + rules, + "1.1", + props, + parametersByRule, + org.codenarc.rule.convention.FieldTypeRequiredRule.class, + org.codenarc.rule.convention.InvertedConditionRule.class, + org.codenarc.rule.convention.MethodReturnTypeRequiredRule.class, + org.codenarc.rule.convention.VariableTypeRequiredRule.class, + org.codenarc.rule.enhanced.MissingOverrideAnnotationRule.class, + org.codenarc.rule.formatting.BlockEndsWithBlankLineRule.class, + org.codenarc.rule.formatting.BlockStartsWithBlankLineRule.class, + org.codenarc.rule.formatting.IndentationRule.class); + + insertRules( + rules, + "1.2", + props, + parametersByRule, + org.codenarc.rule.convention.StaticFieldsBeforeInstanceFieldsRule.class, + org.codenarc.rule.convention.StaticMethodsBeforeInstanceMethodsRule.class, + org.codenarc.rule.convention.PublicMethodsBeforeNonPublicMethodsRule.class, + org.codenarc.rule.grails.GrailsDomainStringPropertyMaxSizeRule.class, + org.codenarc.rule.convention.NoJavaUtilDateRule.class); + + insertRules( + rules, + "1.3", + props, + parametersByRule, + org.codenarc.rule.formatting.ClassEndsWithBlankLineRule.class, + org.codenarc.rule.formatting.ClassStartsWithBlankLineRule.class, + org.codenarc.rule.groovyism.ExplicitCallToPutAtMethodRule.class, + org.codenarc.rule.comments.JavadocEmptyFirstLineRule.class, + org.codenarc.rule.comments.JavadocEmptyLastLineRule.class, + org.codenarc.rule.comments.JavadocConsecutiveEmptyLinesRule.class, + org.codenarc.rule.comments.JavadocEmptySeeTagRule.class, + org.codenarc.rule.comments.JavadocEmptyParamTagRule.class, + org.codenarc.rule.comments.JavadocEmptyReturnTagRule.class, + org.codenarc.rule.comments.JavadocEmptyThrowsTagRule.class, + org.codenarc.rule.comments.JavadocEmptyExceptionTagRule.class, + org.codenarc.rule.comments.JavadocEmptyAuthorTagRule.class, + org.codenarc.rule.comments.JavadocEmptySinceTagRule.class, + org.codenarc.rule.comments.JavadocEmptyVersionTagRule.class); + + insertRules( + rules, + "1.4", + props, + parametersByRule, + org.codenarc.rule.convention.CompileStaticRule.class, + org.codenarc.rule.comments.JavadocMissingParamDescriptionRule.class, + org.codenarc.rule.comments.JavadocMissingThrowsDescriptionRule.class, + org.codenarc.rule.comments.JavadocMissingExceptionDescriptionRule.class); + + return rules; + } + + @SafeVarargs + private static void insertRules( + Multimap rules, + String version, + Properties props, + Map parametersByRule, + Class... ruleClasses) + throws ReflectiveOperationException { + + for (Class ruleClass : ruleClasses) { + rules.put( + RuleSet.getCategory(ruleClass), new Rule(ruleClass, version, props, parametersByRule)); + } + } + + private static Map retrieveRulesParameters(Path aptDir) throws IOException { + return new AptParser().parse(getRulesAptFile(aptDir)); + } + + private static List getRulesAptFile(Path aptDir) { + List rulesAptFiles = Lists.newArrayList(); + File[] files = aptDir.toFile().listFiles(); + for (File file : files) { + if (file.getName().startsWith("codenarc-rules-")) { + rulesAptFiles.add(file); + } + } + return rulesAptFiles; + } + + private void updateCounters(Rule rule) { + count++; + for (String tag : rule.tags) { + Integer nbByTag = rulesByTags.get(tag); + if (nbByTag == null) { + nbByTag = 0; + } + rulesByTags.put(tag, nbByTag + 1); + } + + String version = rule.version == null ? "legacy" : rule.version; + Integer nbByVersion = rulesByVersion.get(version); + if (nbByVersion == null) { + nbByVersion = 0; + } + rulesByVersion.put(version, nbByVersion + 1); + } + + private void resultsByVersion() { + log.info("Rules by Version:"); + List versions = Lists.newArrayList(rulesByVersion.keySet()); + Collections.sort(versions); + for (String version : versions) { + log.info(" - {} : {}", version, rulesByVersion.get(version)); + } + } + + private void resultsByCategory() { + log.info("Rules by category:"); + List categories = Lists.newArrayList(rulesByTags.keySet()); + Collections.sort(categories); + for (String category : categories) { + log.info(" - {} : {}", category, rulesByTags.get(category)); + } + } + + public void startPrintingRule(Rule rule) { + if (duplications.contains(rule.key)) { + log.info("Duplicated rule {}", rule.key); + } else { + duplications.add(rule.key); + } + + updateCounters(rule); + } +} diff --git a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Rule.java b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Rule.java similarity index 87% rename from tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Rule.java rename to codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Rule.java index cecca38b..3b70be8c 100644 --- a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Rule.java +++ b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Rule.java @@ -1,7 +1,7 @@ /* * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,17 +22,15 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; - -import org.apache.commons.lang.StringUtils; -import org.codenarc.rule.AbstractRule; -import org.sonar.plugins.groovy.codenarc.apt.AptResult; - import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import org.apache.commons.lang.StringUtils; +import org.codenarc.rule.AbstractRule; +import org.sonar.plugins.groovy.codenarc.apt.AptResult; public class Rule { @@ -76,7 +74,12 @@ public class Rule { "org.codenarc.rule.unused.UnusedVariableRule" }; - public Rule(Class ruleClass, String since, Properties props, Map parametersByRule) throws Exception { + public Rule( + Class ruleClass, + String since, + Properties props, + Map parametersByRule) + throws ReflectiveOperationException { ruleInstance = ruleClass.newInstance(); key = ruleClass.getCanonicalName(); internalKey = StringUtils.removeEnd(ruleClass.getSimpleName(), "Rule"); @@ -126,28 +129,29 @@ private Set extractParameters(AptResult results, String descripti private static void addParameters(AptResult results, Map parameters) { if (results.hasParameters()) { for (RuleParameter param : results.getParameters()) { - parameters.put(param.key, param); + parameters.put(param.key(), param); } } } - private void addParameters(String[] parameterNames, AbstractRule ruleInstance, Map parameters) { + private void addParameters( + String[] parameterNames, AbstractRule ruleInstance, Map parameters) { for (String parameterName : parameterNames) { addParameter(parameterName, ruleInstance, parameters); } } - private void addParameter(String parameterName, AbstractRule ruleInstance, Map parameters) { + private void addParameter( + String parameterName, AbstractRule ruleInstance, Map parameters) { RuleParameter current = parameters.get(parameterName); - RuleParameter parameter = new RuleParameter(parameterName); - parameter.defaultValue = extractDefaultValue(parameterName, ruleInstance); + RuleParameter parameter = + RuleParameter.create(parameterName, extractDefaultValue(parameterName, ruleInstance)); if (current == null) { current = parameter; } else { - current.merge(parameter); + current = current.merge(parameter); } - parameters.put(current.key, current); - + parameters.put(current.key(), current); } private String extractDefaultValue(String parameterName, AbstractRule ruleInstance) { @@ -160,12 +164,10 @@ private String extractDefaultValue(String parameterName, AbstractRule ruleInstan if (value != null) { return value.toString(); } - } catch (NoSuchFieldException e) { - // do nothing, there is probably no default value - } catch (IllegalArgumentException e) { - // do nothing, that's probably not the correct name - } catch (IllegalAccessException e) { - // do nothing, can not access field + } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { + // NoSuchFieldException: do nothing, there is probably no default value + // IllegalArgumentException: do nothing, that's probably not the correct name + // IllegalAccessException: do nothing, can not access field } return result; } @@ -250,7 +252,8 @@ private static Set handleParticularCases(String internalKey) { results.add("unused"); } else if ("ExplicitGarbageCollection".equals(internalKey)) { results.add("unpredictable"); - } else if ("HardCodedWindowsFileSeparator".equals(internalKey) || "HardCodedWindowsRootDirectory".equals(internalKey)) { + } else if ("HardCodedWindowsFileSeparator".equals(internalKey) + || "HardCodedWindowsRootDirectory".equals(internalKey)) { results.add("pitfall"); } else if ("ForLoopShouldBeWhileLoop".equals(internalKey)) { results.add("clumsy"); @@ -277,9 +280,9 @@ private String cleanDescription(String description) { /** * Covers URLs such as: - * {{http://blog.bjhargrave.com/2007/09/classforname-caches-defined-class-in.html}} <--- direct link - * {{{http://en.wikipedia.org/wiki/Double-checked_locking}}} <--- direct link - * {{{http://jira.codehaus.org/browse/JETTY-352}JETTY-352}} <--- renamed link + * {{http://blog.bjhargrave.com/2007/09/classforname-caches-defined-class-in.html}} <--- direct + * link {{{http://en.wikipedia.org/wiki/Double-checked_locking}}} <--- direct link + * {{{http://jira.codehaus.org/browse/JETTY-352}JETTY-352}} <--- renamed link */ private String handleUrls(String description) { String result = description; @@ -298,7 +301,10 @@ private String handleUrls(String description) { copy = "") + ""; } } else if (copy.startsWith("{./")) { - copy = "") + ""; + copy = + "") + + ""; } result = result.replace("{{" + url + "}}" + (trailingAcc ? "}" : ""), copy); } @@ -340,7 +346,7 @@ private boolean isPartOfFixedRules() { private boolean hasNullParameters() { for (RuleParameter parameter : parameters) { - if ("null".equals(parameter.defaultValue)) { + if ("null".equals(parameter.defaultValue())) { return true; } } diff --git a/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleParameter.java b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleParameter.java new file mode 100644 index 00000000..b444ca85 --- /dev/null +++ b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleParameter.java @@ -0,0 +1,102 @@ +/* + * Sonar CodeNarc Converter + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com + * + * 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. + */ +package org.sonar.plugins.groovy.codenarc; + +import com.google.auto.value.AutoValue; +import org.apache.commons.lang.StringUtils; + +@AutoValue +public abstract class RuleParameter implements Comparable { + + public abstract String key(); + + public abstract String description(); + + public abstract String defaultValue(); + + public static RuleParameter createEmpty() { + return create("", "", ""); + } + + public static RuleParameter create(String key, String defaultValue) { + return create(key, "", defaultValue); + } + + public static RuleParameter create(String key, String description, String defaultValue) { + return new AutoValue_RuleParameter(key, description, defaultValue); + } + + public boolean isEmpty() { + return StringUtils.isBlank(key()) + && StringUtils.isBlank(defaultValue()) + && StringUtils.isBlank(description()); + } + + public boolean hasDefaultValue() { + return StringUtils.isNotBlank(defaultValue()); + } + + public RuleParameter merge(RuleParameter parameter) { + if (key() != null && key().equals(parameter.key())) { + String newDescription = selectValue(description(), parameter.description()); + String newDefaultValue = selectValue(defaultValue(), parameter.defaultValue()); + return create(key(), newDescription, newDefaultValue); + } else { + return this; + } + } + + public RuleParameter withExpandedKey(String keyAdd) { + return create(key() + keyAdd, description(), defaultValue()); + } + + public RuleParameter withNewDefaultValue(String newDefaultValue) { + return create(key(), description(), newDefaultValue); + } + + public RuleParameter withExpandedDescription(String descAdd) { + String newDesc = description(); + newDesc += (newDesc.isEmpty() ? "" : " ") + descAdd; + return create(key(), newDesc, defaultValue()); + } + + private static String selectValue(String currentValue, String newValue) { + if (StringUtils.isBlank(currentValue) && StringUtils.isNotBlank(newValue)) { + return newValue; + } + return currentValue; + } + + @Override + public String toString() { + return "RuleParameter [key=" + + key() + + ", defaultValue=" + + defaultValue() + + ", description=" + + StringUtils.abbreviate(description(), 30) + + "]"; + } + + @Override + public int compareTo(RuleParameter o) { + return key().compareTo(o.key()); + } +} diff --git a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleSet.java b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleSet.java similarity index 77% rename from tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleSet.java rename to codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleSet.java index 4e41977c..bae19e93 100644 --- a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleSet.java +++ b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleSet.java @@ -1,7 +1,7 @@ /* * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,32 +23,28 @@ public enum RuleSet { BASIC("basic"), - // new rule set in 0.14 - SERIALIZATION("serialization"), BRACES("braces"), + COMMENTS("comments"), // new in 1.3 CONCURRENCY("concurrency"), + CONVENTION("convention"), // new in 0.16 DESIGN("design"), DRY("dry"), + ENHANCED("enhanced"), // new in 1.1 EXCEPTIONS("exceptions"), + FORMATTING("formatting"), // new in 0.15 GENERIC("generic"), GRAILS("grails"), + GROOVYISM("groovyism"), // new in 0.16 IMPORTS("imports"), + JDBC("jdbc"), // new in 0.14 JUNIT("junit"), LOGGING("logging"), NAMING("naming"), + SECURITY("security"), // new in 0.14 + SERIALIZATION("serialization"), // new in 0.14 SIZE("size"), UNNECESSARY("unnecessary"), - UNUSED("unused"), - // new rule set in 0.14 - JDBC("jdbc"), - // new rule set in 0.14 - SECURITY("security"), - // new rule set in 0.15 - FORMATTING("formatting"), - // new rule set in 0.16 - CONVENTION("convention"), - // new rule set in 0.16 - GROOVYISM("groovyism"); + UNUSED("unused"); private final String label; @@ -64,4 +60,4 @@ public static RuleSet getCategory(Class ruleClass) { String[] name = ruleClass.getCanonicalName().split("\\."); return RuleSet.valueOf(name[3].toUpperCase()); } -} \ No newline at end of file +} diff --git a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/apt/AptParser.java b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/apt/AptParser.java similarity index 63% rename from tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/apt/AptParser.java rename to codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/apt/AptParser.java index a13eddbc..31fee74e 100644 --- a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/apt/AptParser.java +++ b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/apt/AptParser.java @@ -1,7 +1,7 @@ /* * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,18 +20,21 @@ package org.sonar.plugins.groovy.codenarc.apt; import com.google.common.collect.Maps; - -import org.apache.commons.lang.StringUtils; -import org.sonar.plugins.groovy.codenarc.RuleParameter; - import java.io.File; -import java.nio.charset.Charset; +import java.io.IOException; import java.nio.file.Files; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.plugins.groovy.codenarc.RuleParameter; public class AptParser { + private static final Logger log = LoggerFactory.getLogger(AptParser.class); + private static final String LIST_PREFIX = "* {{"; private static final String NEW_RULE_PREFIX = "* "; private static final String PARAMETER_START_SEPARATOR = "\\*(-)+\\+(-)+\\+(-)+\\+"; @@ -40,7 +43,7 @@ public class AptParser { private static final String EXAMPLE_SEPARATOR_1 = "(-)+"; private static final String EXAMPLE_SEPARATOR_2 = "\\+(-)+"; - public Map parse(List files) throws Exception { + public Map parse(List files) throws IOException { Map results = Maps.newHashMap(); if (!files.isEmpty()) { for (File file : files) { @@ -51,25 +54,22 @@ public Map parse(List files) throws Exception { return results; } - private Map readFile(File file) throws Exception { + private Map readFile(File file) throws IOException { Map results = Maps.newHashMap(); - List lines = Files.readAllLines(file.toPath(), Charset.forName("UTF-8")); + List lines = Files.readAllLines(file.toPath()); boolean inRule = false; boolean inParameters = false; boolean inExample = false; - boolean inDescription = false; String currentRule = null; AptResult currentResult = null; RuleParameter currentParameter = null; int[] splits = new int[3]; - for (int index = 0; index < lines.size(); index++) { - String fullLine = lines.get(index); + for (String fullLine : lines) { String line = fullLine.trim(); - if (line.startsWith(NEW_RULE_PREFIX) && !line.startsWith(LIST_PREFIX) && inRule) { + if (fullLine.startsWith(NEW_RULE_PREFIX) && !line.startsWith(LIST_PREFIX) && inRule) { results.put(currentRule, currentResult); inRule = false; - inDescription = false; currentRule = null; currentResult = null; } @@ -84,29 +84,17 @@ private Map readFile(File file) throws Exception { } } else if (inRule && !inExample && isExampleSeparator(line)) { inExample = true; - inDescription = false; - currentResult.description += "
\n";
-      } else if (inRule && !inExample && !inDescription && !inParameters && isValidDescriptionLine(line)) {
-        inDescription = true;
-        if (StringUtils.isNotBlank(line)) {
-          currentResult.description += "

" + line; - } - } else if (inRule && !inExample && inDescription && !inParameters && !currentResult.description.endsWith("

\n") && isValidDescriptionLine(line)) { - if (isEndOfParagraph(currentResult, line)) { - currentResult.description += "

\n"; - } else { - currentResult.description += getParagraphLine(currentResult, line); - } + currentResult.appendDescription("
\n");
+      } else if (inRule && !inExample && !inParameters && isValidDescriptionLine(line)) {
+        currentResult.addParagraphLine(cleanDescription(line));
       } else if (inRule && inExample && isExampleSeparator(line)) {
         inExample = false;
-        inDescription = true;
-        currentResult.description += "
\n"; + currentResult.appendDescription("\n"); } else if (inRule && inExample) { - currentResult.description += fullLine + "\n"; + currentResult.appendDescription(cleanExample(fullLine) + "\n"); } else if (inRule && !inParameters && line.matches(PARAMETER_START_SEPARATOR)) { - inDescription = false; inParameters = true; - currentParameter = new RuleParameter(); + currentParameter = RuleParameter.createEmpty(); splits[0] = line.indexOf('*') + 1; splits[1] = line.indexOf('+', splits[0]) + 1; splits[2] = line.indexOf('+', splits[1]) + 1; @@ -120,27 +108,28 @@ private Map readFile(File file) throws Exception { String description = blocks[1].trim(); String defaultValue = blocks[2].trim(); if (StringUtils.isNotBlank(key)) { - currentParameter.key = currentParameter.key + key.replaceAll("(-)+", ""); + currentParameter = currentParameter.withExpandedKey(key.replaceAll("(-)+", "")); } if (StringUtils.isNotBlank(defaultValue) && !currentParameter.hasDefaultValue()) { - currentParameter.defaultValue = cleanDefaultValue(defaultValue); + currentParameter = + currentParameter.withNewDefaultValue(cleanDefaultValue(defaultValue)); } if (StringUtils.isNotBlank(description)) { - currentParameter.description = currentParameter.description + cleanDescription(description, true) + " "; + currentParameter = + currentParameter.withExpandedDescription(cleanParameter(description)); } } } else if (inRule && inParameters && isParameterSeparator(line)) { if (!currentParameter.isEmpty()) { currentResult.parameters.add(currentParameter); } - currentParameter = new RuleParameter(); + currentParameter = RuleParameter.createEmpty(); } else if (inRule && inParameters) { if (!currentParameter.isEmpty()) { currentResult.parameters.add(currentParameter); } - currentParameter = new RuleParameter(); + currentParameter = RuleParameter.createEmpty(); inParameters = false; - inDescription = true; } } @@ -156,18 +145,9 @@ private static boolean isExampleSeparator(String line) { return line.matches(EXAMPLE_SEPARATOR_1) || line.matches(EXAMPLE_SEPARATOR_2); } - private static String getParagraphLine(AptResult currentResult, String line) { - return (StringUtils.isNotBlank(line) && currentResult.description.endsWith("\n") || StringUtils.isBlank(currentResult.description) ? "

" : "") - + cleanDescription(line, false) - + " "; - } - - private static boolean isEndOfParagraph(AptResult currentResult, String line) { - return StringUtils.isBlank(line) && StringUtils.isNotBlank(currentResult.description) && !currentResult.description.endsWith("

\n"); - } - private static boolean isValidDescriptionLine(String line) { - return !startsWith(line, ""); - result = result.replaceAll("<<", ""); - result = result.replaceAll(">>>", ""); - result = result.replaceAll(">>", ""); - } else { - result = result.replaceAll("<<<", ""); - result = result.replaceAll("<<", ""); - result = result.replaceAll(">>>", ""); - result = result.replaceAll(">>", ""); - } - return result; + private static String cleanDescription(String description) { + String result = " " + description + " "; + // This is a bit stupid + result = result.replaceAll("&", "&"); + result = result.replaceAll(" <=> ", " <=> "); + result = result.replaceAll(" <<<= ", " <<<= "); + result = result.replaceAll(" >>>= ", " >>>= "); + result = result.replaceAll(" <<= ", " <<= "); + result = result.replaceAll(" >>= ", " >>= "); + result = result.replaceAll(" <= ", " <= "); + result = result.replaceAll(" >= ", " >= "); + result = result.replaceAll(" < ", " < "); + result = result.replaceAll(" > ", " > "); + result = result.replaceAll("->", "->"); + result = result.replaceAll("\\\\=", "="); + result = result.replaceAll("\\\\<", "<"); + result = result.replaceAll("\\\\>", ">"); + + result = result.replaceAll("<", "\uE000"); + result = result.replaceAll(">", "\uE001"); + + result = result.replaceAll("\uE000\uE000\uE000", ""); + result = result.replaceAll("\uE000\uE000", ""); + result = result.replaceAll("\uE000", ""); + result = result.replaceAll("\uE001\uE001\uE001", ""); + result = result.replaceAll("\uE001\uE001", ""); + result = result.replaceAll("\uE001", ""); + return result.trim(); + } + + private static String cleanParameter(String param) { + return param.replaceAll("<", "").replaceAll(">", ""); + } + + private String cleanExample(String line) { + return line.replaceAll("&", "&").replaceAll("\\\\?<", "<").replaceAll("\\\\?>", ">"); } private static String cleanDefaultValue(String defaultValue) { @@ -205,9 +207,9 @@ private static String cleanDefaultValue(String defaultValue) { result = result.replaceAll(">>>", ""); result = result.replaceAll(">>", ""); if (isBetween(result, "'", "'") - || isBetween(result, "<", ">") - || isBetween(result, "\"", "\"") - || isBetween(result, "/", "/")) { + || isBetween(result, "<", ">") + || isBetween(result, "\"", "\"") + || isBetween(result, "/", "/")) { result = result.substring(1, result.length() - 1); } return result; @@ -234,20 +236,24 @@ private static String getRuleName(String line) { if (result.endsWith("Rule")) { result = result.substring(0, result.length() - 4); } - if (StringUtils.isAllLowerCase(result) || !StringUtils.isAlphanumeric(result) || "References".equals(result)) { + if (StringUtils.isAllLowerCase(result) + || !StringUtils.isAlphanumeric(result) + || "References".equals(result)) { // false positive return null; } return result; } - private static void mergeParameters(Map results, Map parametersByFile) { - for (String rule : parametersByFile.keySet()) { + private static void mergeParameters( + Map results, Map parametersByFile) { + for (Entry parametersByFileEntry : parametersByFile.entrySet()) { + String rule = parametersByFileEntry.getKey(); AptResult currentRuleResult = results.get(rule); if (currentRuleResult == null) { currentRuleResult = new AptResult(rule); } - AptResult resultForRuleInFile = parametersByFile.get(rule); + AptResult resultForRuleInFile = parametersByFileEntry.getValue(); if (resultForRuleInFile.parameters != null) { for (RuleParameter parameter : resultForRuleInFile.getParameters()) { if (!currentRuleResult.parameters.contains(parameter)) { @@ -255,18 +261,17 @@ private static void mergeParameters(Map results, Map parameters = Sets.newHashSet(); + private StringBuilder descriptionBuilder = new StringBuilder(); + private boolean inParagraph = false; + + public AptResult(String rule) { + this.rule = rule; + } + + void display(int ruleFileCounter, int ruleTotalCounter, String filename) { + log.info("=========================================="); + log.info("Rule #{} : {} ({} #{})", ruleTotalCounter, rule, filename, ruleFileCounter); + if (descriptionBuilder.length() > 0) { + log.info("------------------------------------------"); + log.info(getDescription()); + } + if (!parameters.isEmpty()) { + log.info("------------------------------------------"); + log.info("Parameters: "); + for (RuleParameter parameter : parameters) { + log.info(" * \"{}\"", parameter.key()); + log.info(" - defaultValue: {}", parameter.defaultValue()); + log.info(" - description: {}", parameter.description()); + } + } + } + + public String getRule() { + return rule; + } + + public Set getParameters() { + return parameters; + } + + public String getDescription() { + return descriptionBuilder.toString().trim(); + } + + public void appendDescription(String str) { + descriptionBuilder.append(str); + } + + public void appendDescriptionTrimmed(String str) { + while (descriptionBuilder.length() > 0 + && Character.isWhitespace(descriptionBuilder.charAt(descriptionBuilder.length() - 1))) { + descriptionBuilder.deleteCharAt(descriptionBuilder.length() - 1); + } + descriptionBuilder.append(str); + } + + public void addParagraphLine(String line) { + if (StringUtils.isBlank(line)) { + if (inParagraph) descriptionBuilder.append("

\n"); + inParagraph = false; + return; + } + + if (!inParagraph) { + descriptionBuilder.append("

"); + inParagraph = true; + } else { + descriptionBuilder.append(' '); + } + descriptionBuilder.append(line); + } + + public boolean hasParameters() { + return parameters != null && !parameters.isEmpty(); + } + + public void replaceDescription(AptResult other) { + this.descriptionBuilder = new StringBuilder(other.getDescription()); + } +} diff --git a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/printer/XMLPrinter.java b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/printer/XMLPrinter.java similarity index 74% rename from tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/printer/XMLPrinter.java rename to codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/printer/XMLPrinter.java index 9767c172..cce6538b 100644 --- a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/printer/XMLPrinter.java +++ b/codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/printer/XMLPrinter.java @@ -1,7 +1,7 @@ /* * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,7 +21,14 @@ import com.google.common.collect.Lists; import com.google.common.collect.Multimap; - +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.sonar.plugins.groovy.codenarc.Converter; @@ -29,32 +36,24 @@ import org.sonar.plugins.groovy.codenarc.RuleParameter; import org.sonar.plugins.groovy.codenarc.RuleSet; -import java.io.File; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -public final class XMLPrinter implements Printer { +public final class XMLPrinter { public static final String LINE_SEPARATOR = System.getProperty("line.separator"); private Converter converter; private String resultAsXML; - @Override public XMLPrinter init(Converter converter) { this.converter = converter; return this; } - @Override - public XMLPrinter process(Multimap rulesBySet) throws Exception { + public XMLPrinter process(Multimap rulesBySet) throws IOException { StringBuilder xmlStringBuilder = new StringBuilder(); - String version = IOUtils.toString(Converter.class.getResourceAsStream("/codenarc-version.txt")); + String version = + IOUtils.toString( + Converter.class.getResourceAsStream("/codenarc-version.txt"), StandardCharsets.UTF_8); xmlStringBuilder.append(""); xmlStringBuilder.append(LINE_SEPARATOR); @@ -77,23 +76,10 @@ public String generatedXML() { return resultAsXML; } - @Override - public File printAll(File resultDir) throws Exception { - File resultFile = setUpRulesFile(resultDir); - PrintStream out = new PrintStream(resultFile, "UTF-8"); - out.print(resultAsXML); - out.flush(); - out.close(); - return resultFile; - } - - private static File setUpRulesFile(File resultDir) throws IOException { - File rules = new File(resultDir, "rules.xml"); - if (rules.exists()) { - rules.delete(); + public void printAll(Path resultFile) throws IOException { + try (Writer out = Files.newBufferedWriter(resultFile)) { + out.write(resultAsXML); } - rules.createNewFile(); - return rules; } private static void startSet(StringBuilder xmlStringBuilder, String name) { @@ -112,9 +98,7 @@ private static void end(StringBuilder xmlStringBuilder) { xmlStringBuilder.append(LINE_SEPARATOR); } - /** - * Rule format based on {@link org.sonar.api.server.rule.RulesDefinitionXmlLoader} - */ + /** Rule format based on {@link org.sonar.api.server.rule.RulesDefinitionXmlLoader} */ private static void printAsXML(Rule rule, StringBuilder xmlStringBuilder) { if (rule.version != null) { xmlStringBuilder.append(" "); @@ -141,23 +125,21 @@ private static void printAsXML(Rule rule, StringBuilder xmlStringBuilder) { if (!rule.parameters.isEmpty()) { List sortedParameters = Lists.newArrayList(rule.parameters); - Collections.sort(sortedParameters, new Comparator() { - @Override - public int compare(RuleParameter o1, RuleParameter o2) { - return o1.key.compareTo(o2.key); - } - }); + Collections.sort(sortedParameters); for (RuleParameter parameter : sortedParameters) { xmlStringBuilder.append(" "); xmlStringBuilder.append(LINE_SEPARATOR); - xmlStringBuilder.append(" " + parameter.key + ""); + xmlStringBuilder.append(" " + parameter.key() + ""); xmlStringBuilder.append(LINE_SEPARATOR); - if (StringUtils.isNotBlank(parameter.description)) { - xmlStringBuilder.append(" "); + if (StringUtils.isNotBlank(parameter.description())) { + xmlStringBuilder.append( + " "); xmlStringBuilder.append(LINE_SEPARATOR); } - if (StringUtils.isNotBlank(parameter.defaultValue) && !"null".equals(parameter.defaultValue)) { - xmlStringBuilder.append(" " + parameter.defaultValue + ""); + if (StringUtils.isNotBlank(parameter.defaultValue()) + && !"null".equals(parameter.defaultValue())) { + xmlStringBuilder.append( + " " + parameter.defaultValue() + ""); xmlStringBuilder.append(LINE_SEPARATOR); } xmlStringBuilder.append(" "); @@ -169,5 +151,4 @@ public int compare(RuleParameter o1, RuleParameter o2) { xmlStringBuilder.append(LINE_SEPARATOR); xmlStringBuilder.append(LINE_SEPARATOR); } - } diff --git a/tools/sonar-codenarc-converter/src/test/java/org/sonar/plugins/groovy/codenarc/ConverterTest.java b/codenarc-converter/src/test/java/org/sonar/plugins/groovy/codenarc/ConverterTest.java similarity index 55% rename from tools/sonar-codenarc-converter/src/test/java/org/sonar/plugins/groovy/codenarc/ConverterTest.java rename to codenarc-converter/src/test/java/org/sonar/plugins/groovy/codenarc/ConverterTest.java index c6fe0b7d..06589f09 100644 --- a/tools/sonar-codenarc-converter/src/test/java/org/sonar/plugins/groovy/codenarc/ConverterTest.java +++ b/codenarc-converter/src/test/java/org/sonar/plugins/groovy/codenarc/ConverterTest.java @@ -1,7 +1,7 @@ /* * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,43 +19,53 @@ */ package org.sonar.plugins.groovy.codenarc; -import com.google.common.collect.Lists; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.sonar.plugins.groovy.codenarc.printer.XMLPrinter; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; +import static org.junit.Assume.assumeTrue; +import com.google.common.collect.Lists; +import difflib.DiffUtils; +import difflib.Patch; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; - -import java.io.File; -import java.io.StringWriter; -import java.util.List; - -import difflib.Delta; -import difflib.DiffUtils; -import difflib.Patch; +import org.junit.Assert; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; public class ConverterTest { - private static final String PLUGIN_RULES_FILE_LOCATION = "../../sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/rules.xml"; + private static final Logger log = LoggerFactory.getLogger(ConverterTest.class); + + private static final String PLUGIN_RULES_FILE_LOCATION = + "../sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/rules.xml"; - @org.junit.Rule - public TemporaryFolder tmpDir = new TemporaryFolder(); + @org.junit.Rule public TemporaryFolder tmpDir = new TemporaryFolder(); @Test - public void test_xml_equivalence() throws Exception { - assertSimilarXml(getGeneratedXmlRulesFile(), new File(PLUGIN_RULES_FILE_LOCATION)); + public void testXmlEquivalence() throws Exception { + // Only run this test if CodeNarc was put in the correct location (this is guaranteed by a Git + // submodule) + Path codeNarcDir = Paths.get("."); + assumeTrue(Files.isDirectory(codeNarcDir.resolve("src"))); + assertSimilarXml(getGeneratedXmlRulesFile(codeNarcDir), Paths.get(PLUGIN_RULES_FILE_LOCATION)); } static void showDelta(String ruleName, String s1, String s2) { @@ -63,26 +73,32 @@ static void showDelta(String ruleName, String s1, String s2) { } static void showDelta(String ruleName, List s1, List s2) { - System.out.println("------------------------------------------------------------------------------------------"); - System.out.println("DIFFERENCE! " + ruleName); + log.info( + "------------------------------------------------------------------------------------------"); + log.info("DIFFERENCE in {}", ruleName); Patch p = DiffUtils.diff(s1, s2); - for (Delta delta : p.getDeltas()) { - System.out.println(delta); + for (Object delta : p.getDeltas()) { + log.info("{}", delta); } } - private File getGeneratedXmlRulesFile() throws Exception { - File generatedRules = tmpDir.newFolder("xml"); - return new XMLPrinter().init(new Converter()).process(Converter.loadRules()).printAll(generatedRules); + private Path getGeneratedXmlRulesFile(Path codeNarcDir) throws Exception { + Path generatedRules = tmpDir.newFile("rules.xml").toPath(); + Converter.process(codeNarcDir, generatedRules); + return generatedRules; } - private static void assertSimilarXml(File generatedRulesXML, File rulesFromPluginXML) throws Exception { + private static void assertSimilarXml(Path generatedRulesXML, Path rulesFromPluginXML) + throws IOException, ParserConfigurationException, SAXException { int nbrDiff = 0; int nbrMissing = 0; - DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - Document generatedDoc = dBuilder.parse(generatedRulesXML); - Document pluginDoc = dBuilder.parse(rulesFromPluginXML); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + DocumentBuilder dBuilder = dbf.newDocumentBuilder(); + + Document generatedDoc = dBuilder.parse(generatedRulesXML.toFile()); + Document pluginDoc = dBuilder.parse(rulesFromPluginXML.toFile()); NodeList generatedNodes = generatedDoc.getChildNodes().item(1).getChildNodes(); NodeList pluginNodes = pluginDoc.getChildNodes().item(1).getChildNodes(); @@ -111,11 +127,15 @@ private static void assertSimilarXml(File generatedRulesXML, File rulesFromPlugi nbrDiff++; String generatedRuleString = nodeToString(generatedRule); String pluginRuleString = nodeToString(pluginRule); - showDelta(getRuleKey(generatedRule), Lists.newArrayList(generatedRuleString.split("\\r?\\n")), Lists.newArrayList(pluginRuleString.split("\\r?\\n"))); + showDelta( + getRuleKey(generatedRule), + Lists.newArrayList(generatedRuleString.split("\\r?\\n")), + Lists.newArrayList(pluginRuleString.split("\\r?\\n"))); } else if (!found) { nbrMissing++; - System.out.println("------------------------------------------------------------------------------------------"); - System.out.println("NOT FOUND! " + getRuleKey(generatedRule)); + log.info( + "------------------------------------------------------------------------------------------"); + log.info("NOT FOUND: {}", getRuleKey(generatedRule)); } } } @@ -126,23 +146,28 @@ private static void assertSimilarXml(File generatedRulesXML, File rulesFromPlugi * - MisorderedStaticImportsRule : description of 'comesBefore' parameter missing in apt files * - FileCreateTempFileRule: link to website * - BracesForIfElseRule: default value of parameters should be true, not 'the same as sameLine' + * - JUnitTestMethodWithoutAssertRule, UnnecessaryObjectReferencesRule, + * GrailsDomainStringPropertyMaxSizeRule: Non-matching open & close tags */ - Assert.assertEquals(3, nbrDiff); + Assert.assertEquals(6, nbrDiff); } - private static String getRuleKey(Node Rule) { - return Rule.getChildNodes().item(1).getFirstChild().getNodeValue(); + private static String getRuleKey(Node rule) { + return rule.getChildNodes().item(1).getFirstChild().getNodeValue(); } private static String nodeToString(Node node) { StringWriter sw = new StringWriter(); try { - Transformer t = TransformerFactory.newInstance().newTransformer(); + TransformerFactory tf = TransformerFactory.newInstance(); + tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); + Transformer t = tf.newTransformer(); t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); t.setOutputProperty(OutputKeys.INDENT, "yes"); t.transform(new DOMSource(node), new StreamResult(sw)); } catch (TransformerException te) { - System.out.println("nodeToString Transformer Exception"); + log.error("nodeToString Transformer Exception", te); } return sw.toString(); } diff --git a/groovy-jacoco-previous/pom.xml b/groovy-jacoco-previous/pom.xml deleted file mode 100644 index d0bbf444..00000000 --- a/groovy-jacoco-previous/pom.xml +++ /dev/null @@ -1,119 +0,0 @@ - - - 4.0.0 - - - org.sonarsource.groovy - groovy - 1.6-RC2-SNAPSHOT - - - groovy-jacoco-previous - - This module shades the required classes of previous version of JaCoCo to allow analysis of two JaCoCo binary format. - - - - org.jacoco - org.jacoco.core - ${jacoco.previous.version} - - - - - - org.apache.maven.plugins - maven-shade-plugin - 2.3 - - - package - - shade - - - - - *:* - - org/jacoco/core/data/ExecutionDataReader* - org/jacoco/core/data/ExecutionDataWriter* - org/jacoco/core/analysis/Analyzer* - org/jacoco/core/analysis/CoverageNodeImpl* - org/jacoco/core/internal/** - - - - - - org.jacoco.core.data.ExecutionDataReader - org.jacoco.previous.core.data.ExecutionDataReader - - - org.jacoco.core.data.ExecutionDataWriter - org.jacoco.previous.core.data.ExecutionDataWriter - - - org.jacoco.core.analysis.Analyzer - org.jacoco.previous.core.analysis.Analyzer - - - org.jacoco.core.analysis.CoverageNodeImpl - org.jacoco.previous.core.analysis.CoverageNodeImpl - - - org.jacoco.core.internal - org.jacoco.previous.core.internal - - - - - - - - org.apache.maven.plugins - maven-release-plugin - - - release - - - - - - - release - false - - - - org.apache.maven.plugins - maven-jar-plugin - - - empty-javadoc-jar - package - - jar - - - javadoc - - - - empty-sources-jar - package - - jar - - - sources - - - - - - - - - diff --git a/pom.xml b/pom.xml index d882752d..66248006 100644 --- a/pom.xml +++ b/pom.xml @@ -1,23 +1,24 @@ - + 4.0.0 org.sonarsource.parent parent - 36 + 53 org.sonarsource.groovy groovy - 1.6-RC2-SNAPSHOT + 1.7-CIT-MVN-162-SNAPSHOT pom Sonar Groovy + This plugin enables analysis of Groovy within SonarQube. + http://redirect.sonarsource.com/plugins/groovy.html 2010 - SonarSource - http://www.sonarsource.com + SonarQube Community @@ -28,6 +29,10 @@ + + TobiX + Tobias Gruetzmacher + pmayweg Patrick Mayweg @@ -44,28 +49,148 @@ sonar-groovy-plugin - groovy-jacoco-previous + codenarc-converter - - scm:git:git@github.com:pmayweg/sonar-groovy.git - scm:git:git@github.com:pmayweg/sonar-groovy.git - https://github.com/pmayweg/sonar-groovy + + scm:git:git@github.com:Inform-Software/sonar-groovy.git + scm:git:git@github.com:Inform-Software/sonar-groovy.git + + https://github.com/Inform-Software/sonar-groovy/tree/master HEAD GitHub - https://github.com/pmayweg/sonar-groovy/issues + https://github.com/Inform-Software/sonar-groovy/issues - + + travis-ci + https://travis-ci.com/Inform-Software/sonar-groovy + + + + bintray-tobix-sonar-groovy + TobiX-sonar-groovy + https://api.bintray.com/maven/tobix/sonar-groovy/sonar-groovy/;publish=1 + + + - 5.6 - 0.7.4.201502262128 - 0.7.5.201505241946 - 2.4.4 + 7.8 + 2.4.17 3.11 - - sonar-groovy + 3.3.9 + SonarSource SA & Community + + + + org.codehaus.groovy + groovy-all + ${groovy.version} + + + org.codehaus.groovy + groovy-xml + ${groovy.version} + + + + org.sonarsource.sonarqube + sonar-plugin-api + ${sonar.version} + + + org.gmetrics + GMetrics + 1.0 + + + org.codenarc + CodeNarc + 1.4 + + + org.codehaus.groovy + groovy-ant + + + + + commons-io + commons-io + 2.6 + + + commons-lang + commons-lang + 2.6 + + + org.slf4j + slf4j-simple + 1.7.28 + + + + junit + junit + 4.12 + test + + + org.assertj + assertj-core + 3.14.0 + test + + + + + + + sonarsource-bintray + SonarSource Bintray Release repository + https://dl.bintray.com/sonarsource/SonarQube + + + + + + + + com.coveo + fmt-maven-plugin + 2.9 + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + true + @{project.version} + install + + + + + + + + + + testModernSonarQube + + + + org.sonarsource.sonarqube + sonar-plugin-api-impl + ${sonar.version} + test + + + + diff --git a/sonar-groovy-plugin/pom.xml b/sonar-groovy-plugin/pom.xml index d2e523df..76d74f8f 100644 --- a/sonar-groovy-plugin/pom.xml +++ b/sonar-groovy-plugin/pom.xml @@ -1,136 +1,75 @@ - + 4.0.0 org.sonarsource.groovy groovy - 1.6-RC2-SNAPSHOT + 1.7-CIT-MVN-162-SNAPSHOT sonar-groovy-plugin sonar-plugin Sonar Groovy Plugin - Enables scanning of Groovy source files. - http://redirect.sonarsource.com/plugins/groovy.html - - scm:git:git@github.com:pmayweg/sonar-groovy.git - scm:git:git@github.com:pmayweg/sonar-groovy.git - https://github.com/pmayweg/sonar-groovy - HEAD - + Code Analyzer for Groovy org.sonar.plugins.groovy.GroovyPlugin Groovy - - - - org.codehaus.groovy - groovy - ${groovy.version} - - - org.codehaus.groovy - groovy-ant - ${groovy.version} - - - org.codehaus.groovy - groovy-xml - ${groovy.version} - - - org.gmetrics - GMetrics - 0.7 - - - junit - junit - 4.10 - - - - - com.google.code.findbugs - jsr305 - 3.0.2 + com.github.spotbugs + spotbugs-annotations + 3.1.12 provided - - ${project.groupId} - groovy-jacoco-previous - ${project.version} - org.sonarsource.sonarqube sonar-plugin-api - ${sonar.version} provided org.apache.ant ant - 1.9.7 + 1.10.6 commons-io commons-io - 2.5 commons-lang commons-lang - 2.6 - - log4j - log4j - 1.2.17 - provided + com.fasterxml.staxmate + staxmate + 2.3.1 + + com.fasterxml.woodstox + woodstox-core + 5.2.1 + + org.codenarc CodeNarc - 0.25.2 - - - ant - ant - - - org.apache.ant - ant - - - org.apache.ant - ant-launcher - - - junit - junit - - org.jacoco org.jacoco.core - ${jacoco.version} + ${version.jacoco.plugin} junit junit - test xmlunit @@ -141,13 +80,16 @@ org.assertj assertj-core - 3.6.2 - test org.mockito mockito-core - 2.7.22 + 3.2.0 + test + + + org.slf4j + slf4j-simple test @@ -167,7 +109,7 @@ - 15000000 + 16000000 8500000 ${project.build.directory}/${project.build.finalName}.jar @@ -180,5 +122,4 @@ - diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovyMetrics.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovyMetrics.java deleted file mode 100644 index f5d91c03..00000000 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovyMetrics.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy; - -import java.util.Arrays; -import java.util.List; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.Metric.ValueType; -import org.sonar.api.measures.Metrics; - -public final class GroovyMetrics implements Metrics { - - public static final Metric EFFERENT_COUPLING_AVERAGE = new Metric.Builder( - "efferent_coupling_average", - "Efferent Coupling (Average)", - ValueType.FLOAT) - .setDescription( - "Shows the Efferent Coupling for a package. " - + "This is a count of the number of other packages that the classes in a package depend upon, " - + "and is an indicator of the package's independence.") - .setDirection(Metric.DIRECTION_WORST) - .setQualitative(true) - .setDomain(CoreMetrics.DOMAIN_COMPLEXITY) - .create(); - - public static final Metric EFFERENT_COUPLING_TOTAL = new Metric.Builder( - "efferent_coupling_total", - "Efferent Coupling (Total)", - ValueType.INT) - .setDescription( - "Shows the Efferent Coupling for a package. " - + "This is a count of the number of other packages that the classes in a package depend upon, " - + "and is an indicator of the package's independence.") - .setDirection(Metric.DIRECTION_WORST) - .setQualitative(true) - .setDomain(CoreMetrics.DOMAIN_COMPLEXITY) - .create(); - - public static final Metric AFFERENT_COUPLING_AVERAGE = new Metric.Builder( - "afferent_coupling_average", - "Afferent Coupling (Average)", - ValueType.FLOAT) - .setDescription( - "Shows the Afferent Coupling for a package. " - + "This is a count of the number of other packages that depend on the classes within this package. " - + "It is an indicator of the package's responsibility.") - .setDirection(Metric.DIRECTION_WORST) - .setQualitative(true) - .setDomain(CoreMetrics.DOMAIN_COMPLEXITY) - .create(); - - public static final Metric AFFERENT_COUPLING_TOTAL = new Metric.Builder( - "afferent_coupling_total", - "Afferent Coupling (Total)", - ValueType.INT) - .setDescription( - "Shows the Afferent Coupling for a package. " - + "This is a count of the number of other packages that depend on the classes within this package. " - + "It is an indicator of the package's responsibility.") - .setDirection(Metric.DIRECTION_WORST) - .setQualitative(true) - .setDomain(CoreMetrics.DOMAIN_COMPLEXITY) - .create(); - - @Override - public List getMetrics() { - return Arrays.asList( - EFFERENT_COUPLING_AVERAGE, - EFFERENT_COUPLING_TOTAL, - AFFERENT_COUPLING_AVERAGE, - AFFERENT_COUPLING_TOTAL); - } -} diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovyPlugin.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovyPlugin.java index 69f47608..42edbf03 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovyPlugin.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovyPlugin.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -35,45 +35,50 @@ @Properties({ @Property( - key = GroovyPlugin.CODENARC_REPORT_PATHS, - name = "CodeNarc Reports", - description = "Path to the CodeNarc XML reports. Paths may be absolute or relative to the project base directory.", - project = true, - module = true, - global = true, - deprecatedKey = GroovyPlugin.CODENARC_REPORT_PATH), + key = GroovyPlugin.CODENARC_REPORT_PATHS, + name = "CodeNarc Reports", + description = + "Path to the CodeNarc XML reports. Paths may be absolute or relative to the project base directory.", + project = true, + module = true, + global = true, + deprecatedKey = GroovyPlugin.CODENARC_REPORT_PATH), @Property( - key = GroovyPlugin.COBERTURA_REPORT_PATH, - name = "Cobertura Report", - description = "Path to the Cobertura XML report. Path may be absolute or relative to the project base directory.", - project = true, - module = true, - global = true), + key = GroovyPlugin.COBERTURA_REPORT_PATH, + name = "Cobertura Report", + description = + "Path to the Cobertura XML report. Path may be absolute or relative to the project base directory.", + project = true, + module = true, + global = true), @Property( - key = GroovyPlugin.IGNORE_HEADER_COMMENTS, - defaultValue = "true", - name = "Ignore Header Comments", - description = "If set to \"true\", the file headers (that are usually the same on each file: licensing information for example) are not considered as comments. " + - "Thus metrics such as \"Comment lines\" do not get incremented. " + - "If set to \"false\", those file headers are considered as comments and metrics such as \"Comment lines\" get incremented.", - project = true, - global = true, - type = PropertyType.BOOLEAN), + key = GroovyPlugin.IGNORE_HEADER_COMMENTS, + defaultValue = "true", + name = "Ignore Header Comments", + description = + "If set to \"true\", the file headers (that are usually the same on each file: licensing information for example) are not considered as comments. " + + "Thus metrics such as \"Comment lines\" do not get incremented. " + + "If set to \"false\", those file headers are considered as comments and metrics such as \"Comment lines\" get incremented.", + project = true, + global = true, + type = PropertyType.BOOLEAN), @Property( - key = GroovyPlugin.FILE_SUFFIXES_KEY, - defaultValue = GroovyPlugin.DEFAULT_FILE_SUFFIXES, - name = "File suffixes", - description = "Comma-separated list of suffixes for files to analyze. To not filter, leave the list empty.", - project = true, - module = true, - global = true), + key = GroovyPlugin.FILE_SUFFIXES_KEY, + defaultValue = GroovyPlugin.DEFAULT_FILE_SUFFIXES, + name = "File suffixes", + description = + "Comma-separated list of suffixes for files to analyze. To not filter, leave the list empty.", + project = true, + module = true, + global = true), @Property( - key = GroovyPlugin.SONAR_GROOVY_BINARIES, - name = "Binary directories", - description = "Comma-separated list of optional directories that contain the compiled groovy sources.", - project = true, - module = true, - global = true) + key = GroovyPlugin.SONAR_GROOVY_BINARIES, + name = "Binary directories", + description = + "Comma-separated list of optional directories that contain the compiled groovy sources.", + project = true, + module = true, + global = true) }) public class GroovyPlugin implements Plugin { @@ -92,21 +97,20 @@ public class GroovyPlugin implements Plugin { @Override public void define(Context context) { context.addExtensions( - // CodeNarc - CodeNarcRulesDefinition.class, - CodeNarcSensor.class, - SonarWayProfile.class, - // Foundation - Groovy.class, - GroovyFileSystem.class, - // Main sensor - GroovySensor.class, - GroovyMetrics.class, - // Surefire - GroovySurefireParser.class, - GroovySurefireSensor.class, - // Cobertura - CoberturaSensor.class); + // CodeNarc + CodeNarcRulesDefinition.class, + CodeNarcSensor.class, + SonarWayProfile.class, + // Foundation + Groovy.class, + GroovyFileSystem.class, + // Main sensor + GroovySensor.class, + // Surefire + GroovySurefireParser.class, + GroovySurefireSensor.class, + // Cobertura + CoberturaSensor.class); context.addExtensions(JaCoCoExtensions.getExtensions()); } } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovySensor.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovySensor.java index 454d9be1..4f703637 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovySensor.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/GroovySensor.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,13 +22,9 @@ import groovyjarjarantlr.Token; import groovyjarjarantlr.TokenStream; import groovyjarjarantlr.TokenStreamException; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Serializable; -import java.math.BigDecimal; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -37,26 +33,20 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.codehaus.groovy.antlr.GroovySourceToken; import org.codehaus.groovy.antlr.parser.GroovyLexer; import org.codehaus.groovy.antlr.parser.GroovyTokenTypes; import org.gmetrics.result.MetricResult; -import org.gmetrics.result.MutableMapMetricResult; import org.gmetrics.result.NumberMetricResult; -import org.gmetrics.result.SingleNumberMetricResult; import org.gmetrics.resultsnode.ClassResultsNode; -import org.gmetrics.resultsnode.PackageResultsNode; -import org.gmetrics.resultsnode.ResultsNode; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.api.ce.measure.RangeDistributionBuilder; import org.sonar.api.config.Settings; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.FileLinesContext; @@ -72,13 +62,9 @@ public class GroovySensor implements Sensor { private static final Logger LOG = Loggers.get(GroovySensor.class); private static final String CYCLOMATIC_COMPLEXITY_METRIC_NAME = "CyclomaticComplexity"; - private static final String EFFERENT_COUPLING_METRIC_NAME = "EfferentCoupling"; - private static final String AFFERENT_COUPLING_METRIC_NAME = "AfferentCoupling"; - private static final Number[] FUNCTIONS_DISTRIB_BOTTOM_LIMITS = {1, 2, 4, 6, 8, 10, 12}; - private static final Number[] FILES_DISTRIB_BOTTOM_LIMITS = {0, 5, 10, 20, 30, 60, 90}; - - private static final Set EMPTY_COMMENT_LINES = Arrays.stream(new String[] {"/**", "/*", "*", "*/", "//"}).collect(Collectors.toSet()); + private static final Set EMPTY_COMMENT_LINES = + Arrays.stream(new String[] {"/**", "/*", "*", "*/", "//"}).collect(Collectors.toSet()); private final Settings settings; private final FileLinesContextFactory fileLinesContextFactory; @@ -89,7 +75,8 @@ public class GroovySensor implements Sensor { private int currentLine = 0; private FileLinesContext fileLinesContext; - public GroovySensor(Settings settings, FileLinesContextFactory fileLinesContextFactory, FileSystem fileSystem) { + public GroovySensor( + Settings settings, FileLinesContextFactory fileLinesContextFactory, FileSystem fileSystem) { this.settings = settings; this.fileLinesContextFactory = fileLinesContextFactory; this.groovyFileSystem = new GroovyFileSystem(fileSystem); @@ -111,91 +98,48 @@ public void execute(SensorContext context) { } private static void computeGroovyMetrics(SensorContext context, List inputFiles) { - GMetricsSourceAnalyzer metricsAnalyzer = new GMetricsSourceAnalyzer(context.fileSystem(), inputFiles); + GMetricsSourceAnalyzer metricsAnalyzer = + new GMetricsSourceAnalyzer(context.fileSystem(), inputFiles); metricsAnalyzer.analyze(); - for (Entry> entry : metricsAnalyzer.resultsByFile().entrySet()) { + for (Entry> entry : + metricsAnalyzer.resultsByFile().entrySet()) { processFile(context, entry.getKey(), entry.getValue()); } - - for (Entry entry : metricsAnalyzer.resultsByPackage().entrySet()) { - processPackage(context, entry.getKey(), entry.getValue().getMetricResults()); - } } - private static void processFile(SensorContext context, InputFile sonarFile, Collection results) { + private static void processFile( + SensorContext context, InputFile sonarFile, Collection results) { int classes = 0; int methods = 0; int complexity = 0; - int complexityInFunctions = 0; - - RangeDistributionBuilder functionsComplexityDistribution = new RangeDistributionBuilder(FUNCTIONS_DISTRIB_BOTTOM_LIMITS); for (ClassResultsNode result : results) { classes += 1; - for (ResultsNode resultsNode : result.getChildren().values()) { - methods += 1; - Optional cyclomaticComplexity = getCyclomaticComplexity(resultsNode.getMetricResults()); - if (cyclomaticComplexity.isPresent()) { - int value = (Integer) ((SingleNumberMetricResult) cyclomaticComplexity.get()).getNumber(); - functionsComplexityDistribution.add(value); - complexityInFunctions += value; - } - } + methods += result.getChildren().size(); - Optional cyclomaticComplexity = getCyclomaticComplexity(result.getMetricResults()); + Optional cyclomaticComplexity = + getCyclomaticComplexity(result.getMetricResults()); if (cyclomaticComplexity.isPresent()) { - int value = (Integer) ((NumberMetricResult) cyclomaticComplexity.get()).getValues().get("total"); + int value = + (Integer) ((NumberMetricResult) cyclomaticComplexity.get()).getValues().get("total"); complexity += value; } } - saveMetric(context, sonarFile, CoreMetrics.FILES, 1); saveMetric(context, sonarFile, CoreMetrics.CLASSES, classes); saveMetric(context, sonarFile, CoreMetrics.FUNCTIONS, methods); saveMetric(context, sonarFile, CoreMetrics.COMPLEXITY, complexity); - saveMetric(context, sonarFile, CoreMetrics.COMPLEXITY_IN_CLASSES, complexity); - saveMetric(context, sonarFile, CoreMetrics.COMPLEXITY_IN_FUNCTIONS, complexityInFunctions); - saveMetric(context, sonarFile, CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, functionsComplexityDistribution.build()); - - RangeDistributionBuilder fileComplexityDistribution = new RangeDistributionBuilder(FILES_DISTRIB_BOTTOM_LIMITS); - fileComplexityDistribution.add(complexity); - saveMetric(context, sonarFile, CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION, fileComplexityDistribution.build()); } private static Optional getCyclomaticComplexity(List metricResults) { - return metricResults - .stream() - .filter(metricResult -> CYCLOMATIC_COMPLEXITY_METRIC_NAME.equals(metricResult.getMetric().getName())) - .findAny(); - } - - private static void processPackage(SensorContext context, InputDir inputDir, List metricResults) { - for (MetricResult metricResult : metricResults) { - org.gmetrics.metric.Metric metric = metricResult.getMetric(); - String metricName = metric.getName(); - if (EFFERENT_COUPLING_METRIC_NAME.equals(metricName)) { - MutableMapMetricResult result = (MutableMapMetricResult) metricResult; - saveMetric(context, inputDir, GroovyMetrics.EFFERENT_COUPLING_TOTAL, getTotalValue(result)); - saveMetric(context, inputDir, GroovyMetrics.EFFERENT_COUPLING_AVERAGE, getAverageValue(result)); - } else if (AFFERENT_COUPLING_METRIC_NAME.equals(metricName)) { - MutableMapMetricResult result = (MutableMapMetricResult) metricResult; - saveMetric(context, inputDir, GroovyMetrics.AFFERENT_COUPLING_TOTAL, getTotalValue(result)); - saveMetric(context, inputDir, GroovyMetrics.AFFERENT_COUPLING_AVERAGE, getAverageValue(result)); - } - } - } - - private static Integer getTotalValue(MutableMapMetricResult result) { - return (Integer) result.getAt("total"); - } - - private static double getAverageValue(MutableMapMetricResult result) { - Object avg = result.getAt("average"); - BigDecimal avgValue = (avg instanceof Integer) ? new BigDecimal((Integer) avg) : (BigDecimal) avg; - return avgValue.doubleValue(); + return metricResults.stream() + .filter( + metricResult -> + CYCLOMATIC_COMPLEXITY_METRIC_NAME.equals(metricResult.getMetric().getName())) + .findAny(); } private void computeBaseMetrics(SensorContext context, List inputFiles) { @@ -205,36 +149,32 @@ private void computeBaseMetrics(SensorContext context, List inputFile } private void computeBaseMetrics(SensorContext context, InputFile groovyFile) { - File file = groovyFile.file(); - if (file.exists()) { - loc = 0; - comments = 0; - currentLine = 0; - fileLinesContext = fileLinesContextFactory.createFor(groovyFile); - Charset encoding = context.fileSystem().encoding(); - try (InputStreamReader streamReader = new InputStreamReader(new FileInputStream(file), encoding)) { - List lines = FileUtils.readLines(file, encoding); - GroovyLexer groovyLexer = new GroovyLexer(streamReader); - groovyLexer.setWhitespaceIncluded(true); - TokenStream tokenStream = groovyLexer.plumb(); - Token token = tokenStream.nextToken(); - Token nextToken = tokenStream.nextToken(); - while (nextToken.getType() != Token.EOF_TYPE) { - handleToken(token, nextToken.getLine(), lines); - token = nextToken; - nextToken = tokenStream.nextToken(); - } + loc = 0; + comments = 0; + currentLine = 0; + fileLinesContext = fileLinesContextFactory.createFor(groovyFile); + try (InputStreamReader streamReader = + new InputStreamReader(groovyFile.inputStream(), groovyFile.charset())) { + List lines = IOUtils.readLines(groovyFile.inputStream(), groovyFile.charset()); + GroovyLexer groovyLexer = new GroovyLexer(streamReader); + groovyLexer.setWhitespaceIncluded(true); + TokenStream tokenStream = groovyLexer.plumb(); + Token token = tokenStream.nextToken(); + Token nextToken = tokenStream.nextToken(); + while (nextToken.getType() != Token.EOF_TYPE) { handleToken(token, nextToken.getLine(), lines); - saveMetric(context, groovyFile, CoreMetrics.LINES, nextToken.getLine()); - saveMetric(context, groovyFile, CoreMetrics.NCLOC, loc); - saveMetric(context, groovyFile, CoreMetrics.COMMENT_LINES, comments); - } catch (TokenStreamException e) { - LOG.error("Unexpected token when lexing file : " + file.getName(), e); - } catch (IOException e) { - LOG.error("Unable to read file: " + file.getName(), e); + token = nextToken; + nextToken = tokenStream.nextToken(); } - fileLinesContext.save(); + handleToken(token, nextToken.getLine(), lines); + saveMetric(context, groovyFile, CoreMetrics.NCLOC, loc); + saveMetric(context, groovyFile, CoreMetrics.COMMENT_LINES, comments); + } catch (TokenStreamException e) { + LOG.error("Unexpected token when lexing file: {}", groovyFile, e); + } catch (IOException e) { + LOG.error("Unable to read file: {}", groovyFile, e); } + fileLinesContext.save(); } private static void highlightFiles(SensorContext context, List inputFiles) { @@ -243,12 +183,9 @@ private static void highlightFiles(SensorContext context, List inputF } } - private static void saveMetric(SensorContext context, InputComponent inputComponent, Metric metric, T value) { - context.newMeasure() - .withValue(value) - .forMetric(metric) - .on(inputComponent) - .save(); + private static void saveMetric( + SensorContext context, InputComponent inputComponent, Metric metric, T value) { + context.newMeasure().withValue(value).forMetric(metric).on(inputComponent).save(); } private void handleToken(Token token, int nextTokenLine, List lines) { @@ -258,9 +195,6 @@ private void handleToken(Token token, int nextTokenLine, List lines) { if (isNotHeaderComment(tokenLine)) { comments += nextTokenLine - tokenLine + 1 - numberEmptyLines(token, lines); } - for (int commentLineNb = tokenLine; commentLineNb <= nextTokenLine; commentLineNb++) { - fileLinesContext.setIntValue(CoreMetrics.COMMENT_LINES_DATA_KEY, commentLineNb, 1); - } } else if (isNotWhitespace(tokenType) && tokenLine != currentLine) { loc++; fileLinesContext.setIntValue(CoreMetrics.NCLOC_DATA_KEY, tokenLine, 1); @@ -270,7 +204,8 @@ private void handleToken(Token token, int nextTokenLine, List lines) { private int numberEmptyLines(Token token, List lines) { List relatedLines = getLinesFromToken(lines, (GroovySourceToken) token); - long emptyLines = relatedLines.stream().map(String::trim).filter(EMPTY_COMMENT_LINES::contains).count(); + long emptyLines = + relatedLines.stream().map(String::trim).filter(EMPTY_COMMENT_LINES::contains).count(); return (int) emptyLines; } @@ -292,18 +227,20 @@ private boolean isNotHeaderComment(int tokenLine) { } private static boolean isNotWhitespace(int tokenType) { - return !(tokenType == GroovyTokenTypes.WS || - tokenType == GroovyTokenTypes.STRING_NL || - tokenType == GroovyTokenTypes.ONE_NL || tokenType == GroovyTokenTypes.NLS); + return !(tokenType == GroovyTokenTypes.WS + || tokenType == GroovyTokenTypes.STRING_NL + || tokenType == GroovyTokenTypes.ONE_NL + || tokenType == GroovyTokenTypes.NLS); } private static boolean isComment(int tokenType) { - return tokenType == GroovyTokenTypes.SL_COMMENT || tokenType == GroovyTokenTypes.SH_COMMENT || tokenType == GroovyTokenTypes.ML_COMMENT; + return tokenType == GroovyTokenTypes.SL_COMMENT + || tokenType == GroovyTokenTypes.SH_COMMENT + || tokenType == GroovyTokenTypes.ML_COMMENT; } @Override public String toString() { return getClass().getSimpleName(); } - } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/CoberturaReportParser.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/CoberturaReportParser.java index 6b09253c..b58d27c7 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/CoberturaReportParser.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/CoberturaReportParser.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,12 +19,12 @@ */ package org.sonar.plugins.groovy.cobertura; +import static java.util.Locale.ENGLISH; +import static org.sonar.api.utils.ParsingUtils.parseNumber; + import java.io.File; import java.text.ParseException; -import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -34,7 +34,6 @@ import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.batch.sensor.coverage.CoverageType; import org.sonar.api.batch.sensor.coverage.NewCoverage; import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.Logger; @@ -42,65 +41,34 @@ import org.sonar.plugins.groovy.foundation.Groovy; import org.sonar.plugins.groovy.utils.StaxParser; -import static java.util.Locale.ENGLISH; -import static org.sonar.api.utils.ParsingUtils.parseNumber; - public class CoberturaReportParser { private static final Logger LOG = Loggers.get(CoberturaReportParser.class); private final SensorContext context; private final FileSystem fileSystem; - private List sourceDirs = new ArrayList<>(); public CoberturaReportParser(SensorContext context, final FileSystem fileSystem) { this.context = context; this.fileSystem = fileSystem; } - /** - * Parse a Cobertura xml report and create measures accordingly - */ + /** Parse a Cobertura xml report and create measures accordingly */ public void parseReport(File xmlFile) { try { - parseSources(xmlFile); parsePackages(xmlFile); } catch (XMLStreamException e) { throw MessageException.of("Unable to parse Cobertura report.", e); } } - private void parseSources(File xmlFile) throws XMLStreamException { - StaxParser sourceParser = new StaxParser(rootCursor -> { - rootCursor.advance(); - sourceDirs = collectSourceDirs(rootCursor.descendantElementCursor("source")); - }); - sourceParser.parse(xmlFile); - } - - private static List collectSourceDirs(SMInputCursor source) throws XMLStreamException { - List directories = new LinkedList<>(); - while (source.getNext() != null) { - String sourceDir = cleanSourceDir(source.getElemStringValue()); - if (StringUtils.isNotBlank(sourceDir)) { - directories.add(sourceDir); - } - } - return directories; - } - - private static String cleanSourceDir(String sourceDir) { - if (StringUtils.isNotBlank(sourceDir)) { - return sourceDir.trim(); - } - return sourceDir; - } - private void parsePackages(File xmlFile) throws XMLStreamException { - StaxParser fileParser = new StaxParser(rootCursor -> { - rootCursor.advance(); - collectPackageMeasures(rootCursor.descendantElementCursor("package")); - }); + StaxParser fileParser = + new StaxParser( + rootCursor -> { + rootCursor.advance(); + collectPackageMeasures(rootCursor.descendantElementCursor("package")); + }); fileParser.parse(xmlFile); } @@ -114,7 +82,8 @@ private void collectPackageMeasures(SMInputCursor pack) throws XMLStreamExceptio private static void handleFileMeasures(Map resultByFilename) { for (ParsingResult parsingResult : resultByFilename.values()) { - if (parsingResult.inputFile != null && Groovy.KEY.equals(parsingResult.inputFile.language())) { + if (parsingResult.inputFile != null + && Groovy.KEY.equals(parsingResult.inputFile.language())) { parsingResult.coverage.save(); } else { LOG.warn("File not found: {}", parsingResult.filename); @@ -123,39 +92,50 @@ private static void handleFileMeasures(Map resultByFilena } @CheckForNull - private InputFile getInputFile(String filename, List sourceDirs) { - for (String sourceDir : sourceDirs) { - String fileAbsolutePath = sourceDir + "/" + filename; - InputFile file = fileSystem.inputFile(fileSystem.predicates().hasAbsolutePath(fileAbsolutePath)); + private InputFile getInputFile(String filename) { + try { + InputFile file = + fileSystem.inputFile(fileSystem.predicates().matchesPathPattern("**/" + filename)); if (file != null) { return file; } + } catch (IllegalArgumentException e) { + LOG.warn("Multiple matches for coverage of '{}' found", filename); } return null; } private void collectFileMeasures(SMInputCursor clazz, Map resultByFilename) - throws XMLStreamException { + throws XMLStreamException { while (clazz.getNext() != null) { String fileName = clazz.getAttrValue("filename"); - ParsingResult parsingResult = resultByFilename.get(fileName); - if (parsingResult == null) { - InputFile inputFile = getInputFile(fileName, sourceDirs); - NewCoverage onFile = context.newCoverage().onFile(inputFile).ofType(CoverageType.UNIT); - parsingResult = new ParsingResult(fileName, inputFile, onFile); - resultByFilename.put(fileName, parsingResult); - } + ParsingResult parsingResult = + resultByFilename.computeIfAbsent( + fileName, + newFileNme -> { + InputFile inputFile = getInputFile(fileName); + NewCoverage onFile = context.newCoverage().onFile(inputFile); + return new ParsingResult(fileName, inputFile, onFile); + }); collectFileData(clazz, parsingResult); } } - private static void collectFileData(SMInputCursor clazz, ParsingResult parsingResult) throws XMLStreamException { + private static void collectFileData(SMInputCursor clazz, ParsingResult parsingResult) + throws XMLStreamException { SMInputCursor line = clazz.childElementCursor("lines").advance().childElementCursor("line"); while (line.getNext() != null) { int lineId = Integer.parseInt(line.getAttrValue("number")); boolean validLine = parsingResult.isValidLine(lineId); if (!validLine && parsingResult.fileExists()) { - LOG.info("Hit on invalid line for file " + parsingResult.filename + " (line: " + lineId + "/" + parsingResult.inputFile.lines() + ")"); + LOG.info( + "Hit on invalid line for file " + + parsingResult.filename + + " (line: " + + lineId + + "/" + + parsingResult.inputFile.lines() + + ")"); } try { int hits = (int) parseNumber(line.getAttrValue("hits"), ENGLISH); @@ -170,15 +150,16 @@ private static void collectFileData(SMInputCursor clazz, ParsingResult parsingRe String text = line.getAttrValue("condition-coverage"); if (validLine && StringUtils.equals(isBranch, "true") && StringUtils.isNotBlank(text)) { String[] conditions = StringUtils.split(StringUtils.substringBetween(text, "(", ")"), "/"); - parsingResult.coverage = parsingResult.coverage.conditions(lineId, Integer.parseInt(conditions[1]), Integer.parseInt(conditions[0])); + parsingResult.coverage = + parsingResult.coverage.conditions( + lineId, Integer.parseInt(conditions[1]), Integer.parseInt(conditions[0])); } } } private static class ParsingResult { private final String filename; - @Nullable - private final InputFile inputFile; + @Nullable private final InputFile inputFile; private NewCoverage coverage; public ParsingResult(String filename, @Nullable InputFile inputFile, NewCoverage coverage) { diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/CoberturaSensor.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/CoberturaSensor.java index c4fbbb6c..a654aae3 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/CoberturaSensor.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/CoberturaSensor.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/package-info.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/package-info.java index 70a1ec2f..c75a35c3 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/package-info.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/cobertura/package-info.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcProfileExporter.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcProfileExporter.java index a06392a9..4c4f26cc 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcProfileExporter.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcProfileExporter.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,15 +19,14 @@ */ package org.sonar.plugins.groovy.codenarc; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.rules.ActiveRule; -import org.sonar.api.rules.ActiveRuleParam; - import java.io.IOException; import java.io.Writer; -import java.util.List; +import java.util.Collection; +import java.util.Map; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.batch.rule.ActiveRule; +import org.sonar.api.batch.rule.ActiveRules; public class CodeNarcProfileExporter { @@ -38,16 +37,17 @@ public CodeNarcProfileExporter(Writer writer) { this.writer = writer; } - public void exportProfile(RulesProfile profile) { + public void exportProfile(ActiveRules activeRules) { try { - generateXML(profile.getActiveRulesByRepository(CodeNarcRulesDefinition.REPOSITORY_KEY)); + generateXML(activeRules.findByRepository(CodeNarcRulesDefinition.REPOSITORY_KEY)); } catch (IOException e) { - throw new IllegalStateException("Fail to export CodeNarc profile : " + profile, e); + throw new IllegalStateException( + "Fail to export CodeNarc profile : " + CodeNarcRulesDefinition.REPOSITORY_KEY, e); } } - private void generateXML(List activeRules) throws IOException { + private void generateXML(Collection activeRules) throws IOException { appendXmlHeader(); for (ActiveRule activeRule : activeRules) { appendRule(activeRule); @@ -56,12 +56,15 @@ private void generateXML(List activeRules) throws IOException { } private void appendXmlHeader() throws IOException { - writer.append("\n") + writer + .append("\n") .append("\n") .append("\n"); + .append( + " xsi:schemaLocation=\"http://codenarc.org/ruleset/1.0 http://codenarc.org/ruleset-schema.xsd\"\n") + .append( + " xsi:noNamespaceSchemaLocation=\"http://codenarc.org/ruleset-schema.xsd\">\n"); } private void appendXmlFooter() throws IOException { @@ -69,29 +72,28 @@ private void appendXmlFooter() throws IOException { } private void appendRule(ActiveRule activeRule) throws IOException { - String ruleKey = activeRule.getRuleKey(); + String ruleKey = activeRule.ruleKey().rule(); // SONARGROOV-40 : key of rule having null parameters have been suffixed with ".fixed" if (ruleKey.endsWith(".fixed")) { ruleKey = ruleKey.substring(0, ruleKey.length() - ".fixed".length()); } writer.append("\n"); - for (ActiveRuleParam activeRuleParam : activeRule.getActiveRuleParams()) { + for (Map.Entry activeRuleParam : activeRule.params().entrySet()) { String value = activeRuleParam.getValue(); - String defaultValue = activeRuleParam.getRuleParam().getDefaultValue(); - if (StringUtils.isNotBlank(value) && !value.equals(defaultValue)) { - writer.append("\n"); } } - } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcRulesDefinition.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcRulesDefinition.java index 639e30e5..b79ba8a2 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcRulesDefinition.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcRulesDefinition.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,6 +19,12 @@ */ package org.sonar.plugins.groovy.codenarc; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.apache.commons.io.IOUtils; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.rule.RulesDefinition; @@ -26,12 +32,6 @@ import org.sonar.api.utils.MessageException; import org.sonar.plugins.groovy.foundation.Groovy; -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - public class CodeNarcRulesDefinition implements RulesDefinition { public static final String REPOSITORY_KEY = Groovy.KEY; @@ -40,12 +40,14 @@ public class CodeNarcRulesDefinition implements RulesDefinition { @Override public void define(Context context) { - NewRepository repository = context - .createRepository(REPOSITORY_KEY, Groovy.KEY) - .setName(REPOSITORY_NAME); + NewRepository repository = + context.createRepository(REPOSITORY_KEY, Groovy.KEY).setName(REPOSITORY_NAME); RulesDefinitionXmlLoader ruleLoader = new RulesDefinitionXmlLoader(); - ruleLoader.load(repository, CodeNarcRulesDefinition.class.getResourceAsStream("/org/sonar/plugins/groovy/rules.xml"), "UTF-8"); + ruleLoader.load( + repository, + CodeNarcRulesDefinition.class.getResourceAsStream("/org/sonar/plugins/groovy/rules.xml"), + "UTF-8"); addRemediationCost(repository.rules()); repository.done(); } @@ -55,7 +57,8 @@ private static void addRemediationCost(Collection rules) { for (NewRule newRule : rules) { String ruleKey = newRule.key(); if (costByRule.containsKey(ruleKey)) { - DebtRemediationFunction linear = newRule.debtRemediationFunctions().linear(costByRule.get(ruleKey)); + DebtRemediationFunction linear = + newRule.debtRemediationFunctions().linear(costByRule.get(ruleKey)); newRule.setDebtRemediationFunction(linear); } } @@ -65,7 +68,10 @@ private static Map getCostByRule() { Map result = new HashMap<>(); List lines; try { - lines = IOUtils.readLines(CodeNarcRulesDefinition.class.getResourceAsStream(COST_FILE_PATH)); + lines = + IOUtils.readLines( + CodeNarcRulesDefinition.class.getResourceAsStream(COST_FILE_PATH), + StandardCharsets.UTF_8); } catch (IOException e) { throw MessageException.of("Unable to load rules remediation function/factor", e); } @@ -82,5 +88,4 @@ private static void completeCost(String line, Map costByRule) { String ruleCost = blocks[2]; costByRule.put(ruleKey, ruleCost); } - } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcSensor.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcSensor.java index 2780e3a9..0c0ee47e 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcSensor.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcSensor.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,12 +34,12 @@ import org.codenarc.rule.Violation; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.rule.ActiveRule; +import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.issue.NewIssue; import org.sonar.api.batch.sensor.issue.NewIssueLocation; -import org.sonar.api.profiles.RulesProfile; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -52,20 +52,20 @@ public class CodeNarcSensor implements Sensor { private static final Logger LOG = Loggers.get(CodeNarcSensor.class); - private final RulesProfile rulesProfile; + private final ActiveRules activeRules; private final GroovyFileSystem groovyFileSystem; - public CodeNarcSensor(RulesProfile profile, GroovyFileSystem groovyFileSystem) { - this.rulesProfile = profile; + public CodeNarcSensor(ActiveRules activeRules, GroovyFileSystem groovyFileSystem) { + this.activeRules = activeRules; this.groovyFileSystem = groovyFileSystem; } @Override public void describe(SensorDescriptor descriptor) { descriptor - .name("CodeNarc") - .onlyOnLanguage(Groovy.KEY) - .createIssuesForRuleRepositories(CodeNarcRulesDefinition.REPOSITORY_KEY); + .name("CodeNarc") + .onlyOnLanguage(Groovy.KEY) + .createIssuesForRuleRepositories(CodeNarcRulesDefinition.REPOSITORY_KEY); } @Override @@ -73,16 +73,18 @@ public void execute(SensorContext context) { // Should we reuse existing report from CodeNarc ? if (context.settings().hasKey(GroovyPlugin.CODENARC_REPORT_PATHS)) { // Yes - String[] codeNarcReportPaths = context.settings().getStringArray(GroovyPlugin.CODENARC_REPORT_PATHS); + String[] codeNarcReportPaths = + context.settings().getStringArray(GroovyPlugin.CODENARC_REPORT_PATHS); String codeNarcReportPath = context.settings().getString(GroovyPlugin.CODENARC_REPORT_PATH); if (codeNarcReportPaths.length == 0) { - codeNarcReportPaths = new String[] { codeNarcReportPath }; + codeNarcReportPaths = new String[] {codeNarcReportPath}; } List reports = new ArrayList(); for (String path : codeNarcReportPaths) { File report = context.fileSystem().resolvePath(path); if (!report.isFile() || !report.exists()) { - LOG.warn("Groovy report " + GroovyPlugin.CODENARC_REPORT_PATHS + " not found at {}", report); + LOG.warn( + "Groovy report " + GroovyPlugin.CODENARC_REPORT_PATHS + " not found at {}", report); } else { reports.add(report); } @@ -98,24 +100,39 @@ public void execute(SensorContext context) { private void parseReport(SensorContext context, List reports) { for (File report : reports) { - Collection violations = CodeNarcXMLParser.parse(report, context.fileSystem()); + Collection violations = + CodeNarcXMLParser.parse(report, context.fileSystem()); for (CodeNarcViolation violation : violations) { - ActiveRule activeRule = context.activeRules().findByInternalKey(CodeNarcRulesDefinition.REPOSITORY_KEY, violation.getRuleName()); + ActiveRule activeRule = + context + .activeRules() + .findByInternalKey(CodeNarcRulesDefinition.REPOSITORY_KEY, violation.getRuleName()); if (activeRule != null) { InputFile inputFile = inputFileFor(context, violation.getFilename()); insertIssue(context, violation, activeRule.ruleKey(), inputFile); } else { - LOG.warn("No such rule in SonarQube, so violation from CodeNarc will be ignored: {}", violation.getRuleName()); + LOG.warn( + "No such rule in SonarQube, so violation from CodeNarc will be ignored: {}", + violation.getRuleName()); } } } } - private static void insertIssue(SensorContext context, CodeNarcViolation violation, RuleKey ruleKey, @Nullable InputFile inputFile) { + private static void insertIssue( + SensorContext context, + CodeNarcViolation violation, + RuleKey ruleKey, + @Nullable InputFile inputFile) { insertIssue(context, ruleKey, violation.getLine(), violation.getMessage(), inputFile); } - private static void insertIssue(SensorContext context, RuleKey ruleKey, @Nullable Integer lineNumber, @Nullable String message, @Nullable InputFile inputFile) { + private static void insertIssue( + SensorContext context, + RuleKey ruleKey, + @Nullable Integer lineNumber, + @Nullable String message, + @Nullable InputFile inputFile) { if (inputFile != null) { NewIssue newIssue = context.newIssue().forRule(ruleKey); NewIssueLocation location = newIssue.newLocation().on(inputFile); @@ -140,13 +157,15 @@ private void runCodeNarc(SensorContext context) { CodeNarcRunner runner = new CodeNarcRunner(); runner.setRuleSetFiles("file:" + codeNarcConfiguration.getAbsolutePath()); - CodeNarcSourceAnalyzer analyzer = new CodeNarcSourceAnalyzer(groovyFileSystem.sourceInputFiles()); + CodeNarcSourceAnalyzer analyzer = + new CodeNarcSourceAnalyzer(groovyFileSystem.sourceInputFiles()); runner.setSourceAnalyzer(analyzer); runner.execute(); reportViolations(context, analyzer.getViolationsByFile()); } - private void reportViolations(SensorContext context, Map> violationsByFile) { + private void reportViolations( + SensorContext context, Map> violationsByFile) { for (Entry> violationsOnFile : violationsByFile.entrySet()) { InputFile groovyFile = violationsOnFile.getKey(); if (groovyFile == null) { @@ -154,11 +173,20 @@ private void reportViolations(SensorContext context, Map> violationsByFile = new HashMap<>(); private final List sourceFiles; @@ -45,22 +48,23 @@ public CodeNarcSourceAnalyzer(List sourceFiles) { @Override public Results analyze(RuleSet ruleSet) { - Map> resultsByFileByDirectory = processFiles(ruleSet); + List resultsByFile = processFiles(ruleSet); DirectoryResults directoryResults = new DirectoryResults("."); - for (List fileResults : resultsByFileByDirectory.values()) { - fileResults.forEach(directoryResults::addChild); - } + resultsByFile.forEach(directoryResults::addChild); return directoryResults; } - private Map> processFiles(RuleSet ruleSet) { - Map> results = new HashMap<>(); + private List processFiles(RuleSet ruleSet) { + List results = new LinkedList<>(); for (InputFile inputFile : sourceFiles) { - List violations = collectViolations(new SourceFile(inputFile.file()), ruleSet); - violationsByFile.put(inputFile, violations); - FileResults result = new FileResults(inputFile.absolutePath(), violations); - results.putIfAbsent(inputFile.file().getParentFile(), new LinkedList<>()); - results.get(inputFile.file().getParentFile()).add(result); + try { + List violations = collectViolations(new SourceString(inputFile.contents()), ruleSet); + violationsByFile.put(inputFile, violations); + FileResults result = new FileResults(inputFile.uri().toString(), violations); + results.add(result); + } catch (IOException e) { + LOG.error("Could not read input file: " + inputFile.toString(), e); + } } return results; } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcXMLParser.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcXMLParser.java index 1bd3920f..5497ba1a 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcXMLParser.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/CodeNarcXMLParser.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/SonarWayProfile.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/SonarWayProfile.java index 6cf5ed24..48cb1666 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/SonarWayProfile.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/SonarWayProfile.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/package-info.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/package-info.java index 55a0196c..765c3898 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/package-info.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/codenarc/package-info.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/Groovy.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/Groovy.java index 4d3e2f10..41dcafab 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/Groovy.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/Groovy.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/GroovyFileSystem.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/GroovyFileSystem.java index 1bfc906d..28f1dc33 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/GroovyFileSystem.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/GroovyFileSystem.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,14 +23,14 @@ import java.util.ArrayList; import java.util.List; import javax.annotation.CheckForNull; -import org.sonar.api.batch.BatchSide; +import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; -@BatchSide +@ScannerSide public class GroovyFileSystem { private final FileSystem fileSystem; @@ -49,13 +49,6 @@ public boolean hasGroovyFiles() { return fileSystem.hasFiles(isGroovyLanguage); } - public List sourceFiles() { - Iterable files = fileSystem.files(predicates.and(isGroovyLanguage, isMainTypeFile)); - List list = new ArrayList<>(); - files.iterator().forEachRemaining(list::add); - return list; - } - public List groovyInputFiles() { Iterable inputFiles = fileSystem.inputFiles(isGroovyLanguage); List list = new ArrayList<>(); @@ -64,7 +57,8 @@ public List groovyInputFiles() { } public List sourceInputFiles() { - Iterable inputFiles = fileSystem.inputFiles(predicates.and(isGroovyLanguage, isMainTypeFile)); + Iterable inputFiles = + fileSystem.inputFiles(predicates.and(isGroovyLanguage, isMainTypeFile)); List list = new ArrayList<>(); inputFiles.iterator().forEachRemaining(list::add); return list; @@ -72,11 +66,12 @@ public List sourceInputFiles() { @CheckForNull public InputFile sourceInputFileFromRelativePath(String relativePath) { - return fileSystem.inputFile(predicates.and(predicates.matchesPathPattern("**/" + relativePath), isGroovyLanguage, isMainTypeFile)); + return fileSystem.inputFile( + predicates.and( + predicates.matchesPathPattern("**/" + relativePath), isGroovyLanguage, isMainTypeFile)); } public File baseDir() { return fileSystem.baseDir(); } - } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/GroovyHighlighterAndTokenizer.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/GroovyHighlighterAndTokenizer.java index eb7a3a8f..1b4755d3 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/GroovyHighlighterAndTokenizer.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/GroovyHighlighterAndTokenizer.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/package-info.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/package-info.java index b36ac6d3..1b47848d 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/package-info.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/foundation/package-info.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/gmetrics/GMetricsSourceAnalyzer.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/gmetrics/GMetricsSourceAnalyzer.java index 7ebf3255..f88850d7 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/gmetrics/GMetricsSourceAnalyzer.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/gmetrics/GMetricsSourceAnalyzer.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -33,8 +33,6 @@ import org.gmetrics.GMetricsRunner; import org.gmetrics.analyzer.SourceAnalyzer; import org.gmetrics.ant.AntFileSetSourceAnalyzer; -import org.gmetrics.metric.coupling.AfferentCouplingMetric; -import org.gmetrics.metric.coupling.EfferentCouplingMetric; import org.gmetrics.metric.cyclomatic.CyclomaticComplexityMetric; import org.gmetrics.metric.linecount.ClassLineCountMetric; import org.gmetrics.metric.linecount.MethodLineCountMetric; @@ -42,29 +40,24 @@ import org.gmetrics.resultsnode.PackageResultsNode; import org.gmetrics.resultsnode.ResultsNode; import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; public class GMetricsSourceAnalyzer { - private static final List GMETRICS = Arrays.asList( - new CyclomaticComplexityMetric(), - new ClassLineCountMetric(), - new MethodLineCountMetric(), - new EfferentCouplingMetric(), - new AfferentCouplingMetric()); + private static final List GMETRICS = + Arrays.asList( + new CyclomaticComplexityMetric(), + new ClassLineCountMetric(), + new MethodLineCountMetric()); private final Map> resultsByFile = new HashMap<>(); - private final Map resultsByPackage = new HashMap<>(); private final Map pathToInputFile = new HashMap<>(); private final Set files = new HashSet<>(); - private final FileSystem fileSystem; private final File fileSystemBaseDir; public GMetricsSourceAnalyzer(FileSystem fileSystem, List sourceFiles) { - this.fileSystem = fileSystem; this.fileSystemBaseDir = fileSystem.baseDir(); for (InputFile inputFile : sourceFiles) { @@ -77,10 +70,6 @@ public Map> resultsByFile() { return resultsByFile; } - public Map resultsByPackage() { - return resultsByPackage; - } - public void analyze() { FileSet fileSet = new FileSet(); fileSet.setDir(fileSystemBaseDir); @@ -110,21 +99,15 @@ private void processResults(ResultsNode resultNode, Map pathT } } - private void processPackageResults(PackageResultsNode resultNode, Map pathToInputFile) { - String path = resultNode.getPath(); - InputDir inputDir = fileSystem.inputDir(fileSystemBaseDir); - if (path != null) { - inputDir = fileSystem.inputDir(new File(fileSystemBaseDir, path)); - } - if (inputDir != null) { - resultsByPackage.put(inputDir, resultNode); - } + private void processPackageResults( + PackageResultsNode resultNode, Map pathToInputFile) { for (Entry entry : resultNode.getChildren().entrySet()) { processResults(entry.getValue(), pathToInputFile); } } - private void processClassResults(ClassResultsNode resultNode, Map pathToInputFile) { + private void processClassResults( + ClassResultsNode resultNode, Map pathToInputFile) { String filePath = resultNode.getFilePath(); InputFile inputFile = pathToInputFile.get(filePath); if (inputFile != null) { @@ -132,5 +115,4 @@ private void processClassResults(ClassResultsNode resultNode, Map binaryDirs; private final File baseDir; - private final PathResolver pathResolver; private final GroovyFileSystem groovyFileSystem; private Map classFilesCache; + private final Path report; - public AbstractAnalyzer(GroovyFileSystem groovyFileSystem, PathResolver pathResolver, Settings settings) { + public JaCoCoAnalyzer( + GroovyFileSystem groovyFileSystem, + Settings settings, + Path report) { this.groovyFileSystem = groovyFileSystem; baseDir = groovyFileSystem.baseDir(); - this.pathResolver = pathResolver; this.binaryDirs = getFiles(getBinaryDirectories(settings), baseDir); + this.report = report; } private List getBinaryDirectories(Settings settings) { @@ -77,8 +79,9 @@ private static List getFiles(List binaryDirectories, File baseDir) @CheckForNull private InputFile getInputFile(ISourceFileCoverage coverage) { String path = getFileRelativePath(coverage); - InputFile sourceInputFileFromRelativePath = groovyFileSystem.sourceInputFileFromRelativePath(path); - if (sourceInputFileFromRelativePath == null) { + InputFile sourceInputFileFromRelativePath = + groovyFileSystem.sourceInputFileFromRelativePath(path); + if (sourceInputFileFromRelativePath == null && path.endsWith(".groovy")) { JaCoCoExtensions.logger().warn("File not found: " + path); } return sourceInputFileFromRelativePath; @@ -86,7 +89,7 @@ private InputFile getInputFile(ISourceFileCoverage coverage) { private static String getFileRelativePath(ISourceFileCoverage coverage) { String relativePath = ""; - if(coverage.getPackageName() != null && !coverage.getPackageName().isEmpty()) + if (coverage.getPackageName() != null && !coverage.getPackageName().isEmpty()) relativePath += coverage.getPackageName() + "/"; relativePath += coverage.getName(); return relativePath; @@ -94,7 +97,8 @@ private static String getFileRelativePath(ISourceFileCoverage coverage) { public final void analyse(SensorContext context) { if (!atLeastOneBinaryDirectoryExists()) { - JaCoCoExtensions.logger().warn("Project coverage is set to 0% since there is no directories with classes."); + JaCoCoExtensions.logger() + .warn("Project coverage is set to 0% since there is no directories with classes."); return; } classFilesCache = new HashMap<>(); @@ -102,19 +106,19 @@ public final void analyse(SensorContext context) { populateClassFilesCache(classFilesCache, classesDir, ""); } - String path = getReportPath(); - if (path == null) { + if (report == null) { JaCoCoExtensions.logger().warn("No jacoco coverage execution file found."); return; } - File jacocoExecutionData = pathResolver.relativeFile(baseDir, path); + Path jacocoExecutionData = baseDir.toPath().resolve(report).normalize(); readExecutionData(jacocoExecutionData, context); classFilesCache.clear(); } - private static void populateClassFilesCache(Map classFilesCache, File dir, String path) { + private static void populateClassFilesCache( + Map classFilesCache, File dir, String path) { File[] files = dir.listFiles(); if (files == null) { return; @@ -134,7 +138,6 @@ private boolean atLeastOneBinaryDirectoryExists() { JaCoCoExtensions.logger().warn("No binary directories defined."); } for (File binaryDir : binaryDirs) { - JaCoCoExtensions.logger().info("\tChecking binary directory: {}", binaryDir.toString()); if (binaryDir.exists()) { return true; } @@ -142,35 +145,34 @@ private boolean atLeastOneBinaryDirectoryExists() { return false; } - public final void readExecutionData(File jacocoExecutionData, SensorContext context) { + public final void readExecutionData(Path jacocoExecutionData, SensorContext context) { ExecutionDataVisitor executionDataVisitor = new ExecutionDataVisitor(); - File fileToAnalyze = jacocoExecutionData; - if (fileToAnalyze == null || !fileToAnalyze.isFile()) { - JaCoCoExtensions.logger().warn("Project coverage is set to 0% as no JaCoCo execution data has been dumped: {}", jacocoExecutionData); - fileToAnalyze = null; - } else { - JaCoCoExtensions.logger().info("Analysing {}", fileToAnalyze); - } - JaCoCoReportReader jacocoReportReader = new JaCoCoReportReader(fileToAnalyze).readJacocoReport(executionDataVisitor, executionDataVisitor); + JaCoCoReportReader jacocoReportReader = + new JaCoCoReportReader(jacocoExecutionData.toFile()) + .readJacocoReport(executionDataVisitor, executionDataVisitor); - CoverageBuilder coverageBuilder = jacocoReportReader.analyzeFiles(executionDataVisitor.getMerged(), classFilesCache.values()); + CoverageBuilder coverageBuilder = + jacocoReportReader.analyzeFiles(executionDataVisitor.getMerged(), classFilesCache.values()); int analyzedResources = 0; for (ISourceFileCoverage coverage : coverageBuilder.getSourceFiles()) { InputFile groovyFile = getInputFile(coverage); if (groovyFile != null) { - NewCoverage newCoverage = context.newCoverage().onFile(groovyFile).ofType(coverageType()); + NewCoverage newCoverage = context.newCoverage().onFile(groovyFile); analyzeFile(newCoverage, groovyFile, coverage); newCoverage.save(); analyzedResources++; } } if (analyzedResources == 0) { - JaCoCoExtensions.logger().warn("Coverage information was not collected. Perhaps you forget to include debug information into compiled classes?"); + JaCoCoExtensions.logger() + .warn( + "Coverage information was not collected. Perhaps you forget to include debug information into compiled classes?"); } } - private static void analyzeFile(NewCoverage newCoverage, InputFile groovyFile, ISourceFileCoverage coverage) { + private static void analyzeFile( + NewCoverage newCoverage, InputFile groovyFile, ISourceFileCoverage coverage) { for (int lineId = coverage.getFirstLine(); lineId <= coverage.getLastLine(); lineId++) { int hits = -1; ILine line = coverage.getLine(lineId); @@ -196,8 +198,7 @@ private static void analyzeFile(NewCoverage newCoverage, InputFile groovyFile, I } try { newCoverage.lineHits(lineId, hits); - } - catch (IllegalStateException stateException) { + } catch (IllegalStateException stateException) { JaCoCoExtensions.logger().warn("cannot set coverage {}", stateException.getMessage()); } @@ -209,8 +210,4 @@ private static void analyzeFile(NewCoverage newCoverage, InputFile groovyFile, I } } } - - protected abstract CoverageType coverageType(); - - protected abstract String getReportPath(); } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoConfiguration.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoConfiguration.java index 5982819f..eb88fd26 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoConfiguration.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoConfiguration.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,14 +22,14 @@ import java.util.Arrays; import java.util.List; import org.sonar.api.PropertyType; -import org.sonar.api.batch.BatchSide; +import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.config.PropertyDefinition; import org.sonar.api.config.Settings; import org.sonar.api.resources.Qualifiers; import org.sonar.plugins.groovy.foundation.Groovy; -@BatchSide +@ScannerSide public class JaCoCoConfiguration { public static final String REPORT_PATH_PROPERTY = "sonar.groovy.jacoco.reportPath"; diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoExtensions.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoExtensions.java index e5e608f1..a609096e 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoExtensions.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoExtensions.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,20 +29,17 @@ public class JaCoCoExtensions { private static final Logger LOG = Loggers.get(JaCoCoExtensions.class); - private JaCoCoExtensions() { - } + private JaCoCoExtensions() {} public static List getExtensions() { List extensions = new ArrayList<>(); extensions.addAll(JaCoCoConfiguration.getPropertyDefinitions()); - extensions.addAll(Arrays.asList( - JaCoCoConfiguration.class, - // Unit tests - JaCoCoSensor.class, - // Integration tests - JaCoCoItSensor.class, - JaCoCoOverallSensor.class)); + extensions.addAll( + Arrays.asList( + JaCoCoConfiguration.class, + // Unit tests + JaCoCoSensor.class)); return extensions; } @@ -50,5 +47,4 @@ public static List getExtensions() { public static Logger logger() { return LOG; } - } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoItSensor.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoItSensor.java deleted file mode 100644 index c68c3788..00000000 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoItSensor.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy.jacoco; - -import java.io.File; -import org.sonar.api.batch.sensor.Sensor; -import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.api.batch.sensor.coverage.CoverageType; -import org.sonar.api.config.Settings; -import org.sonar.api.scan.filesystem.PathResolver; -import org.sonar.plugins.groovy.foundation.Groovy; -import org.sonar.plugins.groovy.foundation.GroovyFileSystem; - -public class JaCoCoItSensor implements Sensor { - private final JaCoCoConfiguration configuration; - private final GroovyFileSystem fileSystem; - private final PathResolver pathResolver; - private final Settings settings; - - public JaCoCoItSensor(JaCoCoConfiguration configuration, GroovyFileSystem fileSystem, PathResolver pathResolver, Settings settings) { - this.configuration = configuration; - this.fileSystem = fileSystem; - this.pathResolver = pathResolver; - this.settings = settings; - } - - @Override - public void describe(SensorDescriptor descriptor) { - descriptor - .name("Groovy JaCoCo IT") - .onlyOnLanguage(Groovy.KEY); - } - - @Override - public void execute(SensorContext context) { - if (shouldExecuteOnProject()) { - new ITAnalyzer().analyse(context); - } - } - - // VisibleForTesting - boolean shouldExecuteOnProject() { - File report = pathResolver.relativeFile(fileSystem.baseDir(), configuration.getItReportPath()); - boolean foundReport = report.exists() && report.isFile(); - boolean shouldExecute = configuration.shouldExecuteOnProject(foundReport); - if (!foundReport && shouldExecute) { - JaCoCoExtensions.logger().info(this.toString() + ": JaCoCo IT report not found."); - } - return shouldExecute; - } - - class ITAnalyzer extends AbstractAnalyzer { - public ITAnalyzer() { - super(fileSystem, pathResolver, settings); - } - - @Override - protected String getReportPath() { - return configuration.getItReportPath(); - } - - @Override - protected CoverageType coverageType() { - return CoverageType.IT; - } - } - -} diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensor.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensor.java deleted file mode 100644 index dd1e8678..00000000 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensor.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy.jacoco; - -import java.io.File; -import org.sonar.api.batch.sensor.Sensor; -import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.api.batch.sensor.coverage.CoverageType; -import org.sonar.api.config.Settings; -import org.sonar.api.scan.filesystem.PathResolver; -import org.sonar.plugins.groovy.foundation.Groovy; -import org.sonar.plugins.groovy.foundation.GroovyFileSystem; - -public class JaCoCoOverallSensor implements Sensor { - - public static final String JACOCO_OVERALL = "jacoco-overall.exec"; - - private final JaCoCoConfiguration configuration; - private final GroovyFileSystem fileSystem; - private final PathResolver pathResolver; - private final Settings settings; - - public JaCoCoOverallSensor(JaCoCoConfiguration configuration, GroovyFileSystem fileSystem, PathResolver pathResolver, Settings settings) { - this.configuration = configuration; - this.pathResolver = pathResolver; - this.fileSystem = fileSystem; - this.settings = settings; - } - - @Override - public void describe(SensorDescriptor descriptor) { - descriptor - .name("Groovy JaCoCo Overall") - .onlyOnLanguage(Groovy.KEY); - } - - @Override - public void execute(SensorContext context) { - File reportUTs = pathResolver.relativeFile(fileSystem.baseDir(), configuration.getReportPath()); - File reportITs = pathResolver.relativeFile(fileSystem.baseDir(), configuration.getItReportPath()); - if (shouldExecuteOnProject()) { - File reportOverall = new File(context.fileSystem().workDir(), JACOCO_OVERALL); - reportOverall.getParentFile().mkdirs(); - JaCoCoReportMerger.mergeReports(reportOverall, reportUTs, reportITs); - new OverallAnalyzer(reportOverall).analyse(context); - } - } - - // VisibleForTesting - boolean shouldExecuteOnProject() { - File baseDir = fileSystem.baseDir(); - File reportUTs = pathResolver.relativeFile(baseDir, configuration.getReportPath()); - File reportITs = pathResolver.relativeFile(baseDir, configuration.getItReportPath()); - boolean foundOneReport = reportUTs.exists() || reportITs.exists(); - boolean shouldExecute = configuration.shouldExecuteOnProject(foundOneReport); - if (!foundOneReport && shouldExecute) { - JaCoCoExtensions.logger().info("JaCoCoOverallSensor: JaCoCo reports not found."); - } - return shouldExecute; - } - - class OverallAnalyzer extends AbstractAnalyzer { - private final File report; - - OverallAnalyzer(File report) { - super(fileSystem, pathResolver, settings); - this.report = report; - } - - @Override - protected CoverageType coverageType() { - return CoverageType.OVERALL; - } - - @Override - protected String getReportPath() { - return report.getAbsolutePath(); - } - } - -} diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportMerger.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportMerger.java index a2456576..52f21436 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportMerger.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportMerger.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,66 +19,56 @@ */ package org.sonar.plugins.groovy.jacoco; -import org.apache.commons.lang.BooleanUtils; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; import org.jacoco.core.data.ExecutionDataStore; import org.jacoco.core.data.ExecutionDataWriter; import org.jacoco.core.data.IExecutionDataVisitor; import org.jacoco.core.data.ISessionInfoVisitor; import org.jacoco.core.data.SessionInfoStore; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; - /** * Utility class to merge JaCoCo reports. * - * This class handles two versions of JaCoCo binary format to merge. + *

This class handles two versions of JaCoCo binary format to merge. */ public class JaCoCoReportMerger { - private JaCoCoReportMerger() { - } + private JaCoCoReportMerger() {} /** * Merge all reports in reportOverall. + * * @param reportOverall destination file of merge. * @param reports files to be merged. */ - public static void mergeReports(File reportOverall, File... reports) { + public static void mergeReports(Path reportOverall, File... reports) { SessionInfoStore infoStore = new SessionInfoStore(); ExecutionDataStore dataStore = new ExecutionDataStore(); - boolean isCurrentVersionFormat = loadSourceFiles(infoStore, dataStore, reports); + loadSourceFiles(infoStore, dataStore, reports); - try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(reportOverall))) { + try (OutputStream fos = Files.newOutputStream(reportOverall); + BufferedOutputStream outputStream = new BufferedOutputStream(fos)) { Object visitor; - if (isCurrentVersionFormat) { - visitor = new ExecutionDataWriter(outputStream); - } else { - visitor = new org.jacoco.previous.core.data.ExecutionDataWriter(outputStream); - } + visitor = new ExecutionDataWriter(outputStream); infoStore.accept((ISessionInfoVisitor) visitor); dataStore.accept((IExecutionDataVisitor) visitor); } catch (IOException e) { - throw new IllegalStateException(String.format("Unable to write overall coverage report %s", reportOverall.getAbsolutePath()), e); + throw new IllegalStateException( + String.format("Unable to write overall coverage report %s", reportOverall), e); } } - private static boolean loadSourceFiles(ISessionInfoVisitor infoStore, IExecutionDataVisitor dataStore, File... reports) { - Boolean isCurrentVersionFormat = null; + private static void loadSourceFiles( + ISessionInfoVisitor infoStore, IExecutionDataVisitor dataStore, File... reports) { for (File report : reports) { if (report.isFile()) { - JaCoCoReportReader jacocoReportReader = new JaCoCoReportReader(report).readJacocoReport(dataStore, infoStore); - boolean reportFormatIsCurrent = jacocoReportReader.useCurrentBinaryFormat(); - if (isCurrentVersionFormat == null) { - isCurrentVersionFormat = reportFormatIsCurrent; - } else if (!isCurrentVersionFormat.equals(reportFormatIsCurrent)) { - throw new IllegalStateException("You are trying to merge two different JaCoCo binary formats. Please use only one version of JaCoCo."); - } + new JaCoCoReportReader(report).readJacocoReport(dataStore, infoStore); } } - return BooleanUtils.isNotFalse(isCurrentVersionFormat); } - } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportReader.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportReader.java index be4495d2..af167ad1 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportReader.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportReader.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,108 +37,87 @@ public class JaCoCoReportReader { - @Nullable - private final File jacocoExecutionData; - private final boolean useCurrentBinaryFormat; + // Visible for testing + static final String INCOMPATIBLE_JACOCO_ERROR = + "You are using an incompatible JaCoCo binary format version, please consider upgrading to a supported JaCoCo version (0.8.x)."; + + @Nullable private final File jacocoExecutionData; public JaCoCoReportReader(@Nullable File jacocoExecutionData) { this.jacocoExecutionData = jacocoExecutionData; - this.useCurrentBinaryFormat = isCurrentReportFormat(jacocoExecutionData); + verifyCurrentReportFormat(jacocoExecutionData); } /** * Read JaCoCo report determining the format to be used. + * * @param executionDataVisitor visitor to store execution data. * @param sessionInfoStore visitor to store info session. * @return true if binary format is the latest one. * @throws IOException in case of error or binary format not supported. */ - public JaCoCoReportReader readJacocoReport(IExecutionDataVisitor executionDataVisitor, ISessionInfoVisitor sessionInfoStore) { + public JaCoCoReportReader readJacocoReport( + IExecutionDataVisitor executionDataVisitor, ISessionInfoVisitor sessionInfoStore) { if (jacocoExecutionData == null) { return this; } JaCoCoExtensions.logger().info("Analysing {}", jacocoExecutionData); - try (InputStream inputStream = new BufferedInputStream(new FileInputStream(jacocoExecutionData))) { - if (useCurrentBinaryFormat) { - ExecutionDataReader reader = new ExecutionDataReader(inputStream); - reader.setSessionInfoVisitor(sessionInfoStore); - reader.setExecutionDataVisitor(executionDataVisitor); - reader.read(); - } else { - org.jacoco.previous.core.data.ExecutionDataReader reader = new org.jacoco.previous.core.data.ExecutionDataReader(inputStream); - reader.setSessionInfoVisitor(sessionInfoStore); - reader.setExecutionDataVisitor(executionDataVisitor); - reader.read(); - } + try (InputStream inputStream = + new BufferedInputStream(new FileInputStream(jacocoExecutionData))) { + ExecutionDataReader reader = new ExecutionDataReader(inputStream); + reader.setSessionInfoVisitor(sessionInfoStore); + reader.setExecutionDataVisitor(executionDataVisitor); + reader.read(); } catch (IOException e) { - throw new IllegalArgumentException(String.format("Unable to read %s", jacocoExecutionData.getAbsolutePath()), e); + throw new IllegalArgumentException( + String.format("Unable to read %s", jacocoExecutionData.getAbsolutePath()), e); } return this; } - private static boolean isCurrentReportFormat(@Nullable File jacocoExecutionData) { + private static void verifyCurrentReportFormat(@Nullable File jacocoExecutionData) { if (jacocoExecutionData == null) { - return true; + return; } try (DataInputStream dis = new DataInputStream(new FileInputStream(jacocoExecutionData))) { byte firstByte = dis.readByte(); - if (firstByte != ExecutionDataWriter.BLOCK_HEADER || dis.readChar() != ExecutionDataWriter.MAGIC_NUMBER) { + if (firstByte != ExecutionDataWriter.BLOCK_HEADER + || dis.readChar() != ExecutionDataWriter.MAGIC_NUMBER) { throw new IllegalStateException(); } - char version = dis.readChar(); - boolean isCurrentFormat = version == ExecutionDataWriter.FORMAT_VERSION; - if (!isCurrentFormat) { - JaCoCoExtensions.logger().warn("You are not using the latest JaCoCo binary format version, please consider upgrading to latest JaCoCo version."); + if (dis.readChar() != ExecutionDataWriter.FORMAT_VERSION) { + throw new IllegalArgumentException( + INCOMPATIBLE_JACOCO_ERROR); } - return isCurrentFormat; } catch (IOException | IllegalStateException e) { - throw new IllegalArgumentException(String.format("Unable to read %s to determine JaCoCo binary format.", jacocoExecutionData.getAbsolutePath()), e); + throw new IllegalArgumentException( + String.format( + "Unable to read %s to determine JaCoCo binary format.", + jacocoExecutionData.getAbsolutePath()), + e); } } - public boolean useCurrentBinaryFormat() { - return this.useCurrentBinaryFormat; - } - - /** - * Caller must guarantee that {@code classFiles} are actually class file. - */ - public CoverageBuilder analyzeFiles(ExecutionDataStore executionDataStore, Collection classFiles) { + /** Caller must guarantee that {@code classFiles} are actually class file. */ + public CoverageBuilder analyzeFiles( + ExecutionDataStore executionDataStore, Collection classFiles) { CoverageBuilder coverageBuilder = new CoverageBuilder(); - if (useCurrentBinaryFormat) { - Analyzer analyzer = new Analyzer(executionDataStore, coverageBuilder); - for (File classFile : classFiles) { - analyzeClassFile(analyzer, classFile); - } - } else { - org.jacoco.previous.core.analysis.Analyzer analyzer = new org.jacoco.previous.core.analysis.Analyzer(executionDataStore, coverageBuilder); - for (File classFile : classFiles) { - analyzeClassFile(analyzer, classFile); - } + Analyzer analyzer = new Analyzer(executionDataStore, coverageBuilder); + for (File classFile : classFiles) { + analyzeClassFile(analyzer, classFile); } return coverageBuilder; } - /** - * Caller must guarantee that {@code classFile} is actually class file. - */ - private static void analyzeClassFile(org.jacoco.previous.core.analysis.Analyzer analyzer, File classFile) { - try (InputStream inputStream = new FileInputStream(classFile)) { - analyzer.analyzeClass(inputStream, classFile.getPath()); - } catch (IOException e) { - // (Godin): in fact JaCoCo includes name into exception - JaCoCoExtensions.logger().warn("Exception during analysis of file " + classFile.getAbsolutePath(), e); - } - } - + /** Caller must guarantee that {@code classFile} is actually class file. */ private static void analyzeClassFile(Analyzer analyzer, File classFile) { try (InputStream inputStream = new FileInputStream(classFile)) { analyzer.analyzeClass(inputStream, classFile.getPath()); } catch (IOException e) { // (Godin): in fact JaCoCo includes name into exception - JaCoCoExtensions.logger().warn("Exception during analysis of file " + classFile.getAbsolutePath(), e); + JaCoCoExtensions.logger() + .warn("Exception during analysis of file " + classFile.getAbsolutePath(), e); } } - } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoSensor.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoSensor.java index 335ce4fd..450d26e6 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoSensor.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/JaCoCoSensor.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,68 +20,119 @@ package org.sonar.plugins.groovy.jacoco; import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.api.batch.sensor.coverage.CoverageType; import org.sonar.api.config.Settings; +import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; import org.sonar.plugins.groovy.foundation.Groovy; import org.sonar.plugins.groovy.foundation.GroovyFileSystem; public class JaCoCoSensor implements Sensor { + private static final Logger LOG = Loggers.get(JaCoCoSensor.class.getName()); + + public static final String JACOCO_OVERALL = "jacoco-overall.exec"; + private final JaCoCoConfiguration configuration; private final GroovyFileSystem fileSystem; private final PathResolver pathResolver; private final Settings settings; + private final AnalysisWarnings analysisWarnings; + static final String JACOCO_XML_PROPERTY = "sonar.coverage.jacoco.xmlReportPaths"; + private static final String[] JACOCO_XML_DEFAULT_PATHS = { + "target/site/jacoco/jacoco.xml", "build/reports/jacoco/test/jacocoTestReport.xml" + }; - public JaCoCoSensor(JaCoCoConfiguration configuration, GroovyFileSystem fileSystem, PathResolver pathResolver, Settings settings) { + public JaCoCoSensor( + JaCoCoConfiguration configuration, + GroovyFileSystem fileSystem, + PathResolver pathResolver, + Settings settings, + AnalysisWarnings analysisWarnings) { this.configuration = configuration; this.fileSystem = fileSystem; this.pathResolver = pathResolver; this.settings = settings; + this.analysisWarnings = analysisWarnings; } @Override public void describe(SensorDescriptor descriptor) { - descriptor - .name("Groovy JaCoCo") - .onlyOnLanguage(Groovy.KEY); + descriptor.name("Groovy JaCoCo Coverage").onlyOnLanguage(Groovy.KEY); } @Override public void execute(SensorContext context) { - if (shouldExecuteOnProject()) { - new UnitTestsAnalyzer().analyse(context); + boolean hasXmlReport = hasXmlReport(context); + File baseDir = fileSystem.baseDir(); + File reportUTs = pathResolver.relativeFile(baseDir, configuration.getReportPath()); + File reportITs = pathResolver.relativeFile(baseDir, configuration.getItReportPath()); + + if (reportUTs.isFile()) { + warnAboutDeprecatedProperty(hasXmlReport, JaCoCoConfiguration.REPORT_PATH_PROPERTY); + } + if (reportITs.isFile()) { + warnAboutDeprecatedProperty(hasXmlReport, JaCoCoConfiguration.IT_REPORT_PATH_PROPERTY); + } + if (hasXmlReport) { + LOG.debug( + "JaCoCo XML report found, skipping processing of binary JaCoCo exec report.", + JACOCO_XML_PROPERTY); + return; } - } - // VisibleForTesting - boolean shouldExecuteOnProject() { - File report = pathResolver.relativeFile(fileSystem.baseDir(), configuration.getReportPath()); - boolean foundReport = report.exists() && report.isFile(); - boolean shouldExecute = configuration.shouldExecuteOnProject(foundReport); - if (!foundReport && shouldExecute) { - JaCoCoExtensions.logger().info("JaCoCoSensor: JaCoCo report not found."); + if (shouldExecuteOnProject(reportUTs.isFile(), reportITs.isFile())) { + Path reportOverall = context.fileSystem().workDir().toPath().resolve(JACOCO_OVERALL); + JaCoCoReportMerger.mergeReports(reportOverall, reportUTs, reportITs); + new JaCoCoAnalyzer(fileSystem, settings, reportOverall).analyse(context); } - return shouldExecute; } - class UnitTestsAnalyzer extends AbstractAnalyzer { - public UnitTestsAnalyzer() { - super(fileSystem, pathResolver, settings); + private void warnAboutDeprecatedProperty(boolean hasXmlReport, String deprecatedProperty) { + if (!hasXmlReport) { + addAnalysisWarning( + "Property '%s' is deprecated (JaCoCo binary format). '%s' should be used instead (JaCoCo XML format)." + + " Please check that the JaCoCo plugin is installed on your SonarQube Instance.", + deprecatedProperty, JACOCO_XML_PROPERTY); + } else if (settings.hasKey(deprecatedProperty)) { + // only log for those properties which were set explicitly + LOG.info( + "Both '{}' and '{}' were set. '{}' is deprecated therefore, only '{}' will be taken into account." + + " Please check that the JaCoCo plugin is installed on your SonarQube Instance.", + deprecatedProperty, + JACOCO_XML_PROPERTY, + deprecatedProperty, + JACOCO_XML_PROPERTY); } + } - @Override - protected String getReportPath() { - return configuration.getReportPath(); - } + private static boolean hasXmlReport(SensorContext context) { + return context.config().hasKey(JACOCO_XML_PROPERTY) + || Arrays.stream(JACOCO_XML_DEFAULT_PATHS) + .map(path -> context.fileSystem().baseDir().toPath().resolve(path)) + .anyMatch(Files::isRegularFile); + } - @Override - protected CoverageType coverageType() { - return CoverageType.UNIT; - } + private void addAnalysisWarning(String format, Object... args) { + String msg = String.format(format, args); + LOG.warn(msg); + analysisWarnings.addUnique(msg); } + // VisibleForTesting + boolean shouldExecuteOnProject(boolean hasUT, boolean hasIT) { + boolean foundOneReport = hasUT || hasIT; + boolean shouldExecute = configuration.shouldExecuteOnProject(foundOneReport); + if (!foundOneReport && shouldExecute) { + JaCoCoExtensions.logger().info("JaCoCoSensor: No JaCoCo report found."); + } + return shouldExecute; + } } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/package-info.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/package-info.java index c3650a6d..7aeb21d1 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/package-info.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/jacoco/package-info.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/package-info.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/package-info.java index ae07b0ff..074eedea 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/package-info.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/package-info.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/GroovySurefireParser.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/GroovySurefireParser.java index d88ccbaa..8a8eef14 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/GroovySurefireParser.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/GroovySurefireParser.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,39 +26,32 @@ import java.util.Map; import javax.xml.stream.XMLStreamException; import org.apache.commons.lang.StringUtils; -import org.sonar.api.batch.BatchSide; +import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; -import org.sonar.api.test.MutableTestPlan; -import org.sonar.api.test.TestCase; import org.sonar.api.utils.MessageException; -import org.sonar.api.utils.ParsingUtils; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.plugins.groovy.foundation.Groovy; import org.sonar.plugins.groovy.surefire.data.SurefireStaxHandler; import org.sonar.plugins.groovy.surefire.data.UnitTestClassReport; import org.sonar.plugins.groovy.surefire.data.UnitTestIndex; -import org.sonar.plugins.groovy.surefire.data.UnitTestResult; import org.sonar.plugins.groovy.utils.StaxParser; -@BatchSide +@ScannerSide public class GroovySurefireParser { private static final Logger LOGGER = Loggers.get(GroovySurefireParser.class); - private final ResourcePerspectives perspectives; private final Groovy groovy; private final FileSystem fs; - public GroovySurefireParser(Groovy groovy, ResourcePerspectives perspectives, FileSystem fs) { + public GroovySurefireParser(Groovy groovy, FileSystem fs) { this.groovy = groovy; - this.perspectives = perspectives; this.fs = fs; } @@ -131,7 +124,9 @@ private void save(UnitTestIndex index, SensorContext context) { } } if (negativeTimeTestNumber > 0) { - LOGGER.warn("There is {} test(s) reported with negative time by surefire, total duration may not be accurate.", negativeTimeTestNumber); + LOGGER.warn( + "There is {} test(s) reported with negative time by surefire, total duration may not be accurate.", + negativeTimeTestNumber); } } @@ -141,34 +136,17 @@ private void save(UnitTestClassReport report, InputFile inputFile, SensorContext saveMeasure(context, inputFile, CoreMetrics.TESTS, testsCount); saveMeasure(context, inputFile, CoreMetrics.TEST_ERRORS, report.getErrors()); saveMeasure(context, inputFile, CoreMetrics.TEST_FAILURES, report.getFailures()); - saveMeasure(context, inputFile, CoreMetrics.TEST_EXECUTION_TIME, report.getDurationMilliseconds()); - int passedTests = testsCount - report.getErrors() - report.getFailures(); - if (testsCount > 0) { - double percentage = (passedTests * 100D) / testsCount; - saveMeasure(context, inputFile, CoreMetrics.TEST_SUCCESS_DENSITY, ParsingUtils.scaleValue(percentage)); - } - saveResults(inputFile, report); - } - - protected void saveResults(InputFile testFile, UnitTestClassReport report) { - for (UnitTestResult unitTestResult : report.getResults()) { - MutableTestPlan testPlan = perspectives.as(MutableTestPlan.class, testFile); - if (testPlan != null) { - testPlan.addTestCase(unitTestResult.getName()) - .setDurationInMs(Math.max(unitTestResult.getDurationMilliseconds(), 0)) - .setStatus(TestCase.Status.of(unitTestResult.getStatus())) - .setMessage(unitTestResult.getMessage()) - .setType(TestCase.TYPE_UNIT) - .setStackTrace(unitTestResult.getStackTrace()); - } - } + saveMeasure( + context, inputFile, CoreMetrics.TEST_EXECUTION_TIME, report.getDurationMilliseconds()); } protected InputFile getUnitTestInputFile(String classKey) { String fileName = StringUtils.replace(classKey, ".", "/"); FilePredicates p = fs.predicates(); - FilePredicate fileNamePredicates = getFileNamePredicateFromSuffixes(p, fileName, groovy.getFileSuffixes()); - FilePredicate searchPredicate = p.and(p.and(p.hasLanguage(Groovy.KEY), p.hasType(InputFile.Type.TEST)), fileNamePredicates); + FilePredicate fileNamePredicates = + getFileNamePredicateFromSuffixes(p, fileName, groovy.getFileSuffixes()); + FilePredicate searchPredicate = + p.and(p.and(p.hasLanguage(Groovy.KEY), p.hasType(InputFile.Type.TEST)), fileNamePredicates); if (fs.hasFiles(searchPredicate)) { return fs.inputFiles(searchPredicate).iterator().next(); } else { @@ -176,7 +154,8 @@ protected InputFile getUnitTestInputFile(String classKey) { } } - private static FilePredicate getFileNamePredicateFromSuffixes(FilePredicates p, String fileName, String[] suffixes) { + private static FilePredicate getFileNamePredicateFromSuffixes( + FilePredicates p, String fileName, String[] suffixes) { List fileNamePredicates = new ArrayList<>(suffixes.length); for (String suffix : suffixes) { fileNamePredicates.add(p.matchesPathPattern("**/" + fileName + suffix)); @@ -184,8 +163,8 @@ private static FilePredicate getFileNamePredicateFromSuffixes(FilePredicates p, return p.or(fileNamePredicates); } - private static void saveMeasure(SensorContext context, InputFile inputFile, Metric metric, T value) { + private static void saveMeasure( + SensorContext context, InputFile inputFile, Metric metric, T value) { context.newMeasure().forMetric(metric).on(inputFile).withValue(value).save(); } - } diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/GroovySurefireSensor.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/GroovySurefireSensor.java index 381b6c14..c3823873 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/GroovySurefireSensor.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/GroovySurefireSensor.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/api/SurefireUtils.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/api/SurefireUtils.java index 93b36574..b5701364 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/api/SurefireUtils.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/api/SurefireUtils.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/api/package-info.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/api/package-info.java index 5c406ff6..837cc9ee 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/api/package-info.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/api/package-info.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/SurefireStaxHandler.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/SurefireStaxHandler.java index 32309357..e5f8904e 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/SurefireStaxHandler.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/SurefireStaxHandler.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestClassReport.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestClassReport.java index e92ec7ae..92806bb4 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestClassReport.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestClassReport.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestIndex.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestIndex.java index 3b938e79..3e283eb3 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestIndex.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestIndex.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestResult.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestResult.java index f642385b..e45d14c7 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestResult.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/UnitTestResult.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/package-info.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/package-info.java index c58a41a2..a806290c 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/package-info.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/data/package-info.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/package-info.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/package-info.java index 7736b9e9..c254f9ca 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/package-info.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/surefire/package-info.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/utils/StaxParser.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/utils/StaxParser.java index f25efea8..902a72bd 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/utils/StaxParser.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/utils/StaxParser.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/utils/package-info.java b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/utils/package-info.java index b1734b99..269f39b4 100644 --- a/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/utils/package-info.java +++ b/sonar-groovy-plugin/src/main/java/org/sonar/plugins/groovy/utils/package-info.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/cost.csv b/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/cost.csv index 51e8c27e..79037e59 100644 --- a/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/cost.csv +++ b/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/cost.csv @@ -46,6 +46,21 @@ org.codenarc.rule.braces.ElseBlockBracesRule;linear;5min org.codenarc.rule.braces.ForStatementBracesRule;linear;5min org.codenarc.rule.braces.IfStatementBracesRule;linear;5min org.codenarc.rule.braces.WhileStatementBracesRule;linear;5min +org.codenarc.rule.comments.ClassJavadocRule;linear;5min +org.codenarc.rule.comments.JavadocConsecutiveEmptyLinesRule;linear;5min +org.codenarc.rule.comments.JavadocEmptyAuthorTagRule;linear;5min +org.codenarc.rule.comments.JavadocEmptyExceptionTagRule;linear;5min +org.codenarc.rule.comments.JavadocEmptyFirstLineRule;linear;5min +org.codenarc.rule.comments.JavadocEmptyLastLineRule;linear;5min +org.codenarc.rule.comments.JavadocEmptyParamTagRule;linear;5min +org.codenarc.rule.comments.JavadocEmptyReturnTagRule;linear;5min +org.codenarc.rule.comments.JavadocEmptySeeTagRule;linear;5min +org.codenarc.rule.comments.JavadocEmptySinceTagRule;linear;5min +org.codenarc.rule.comments.JavadocEmptyThrowsTagRule;linear;5min +org.codenarc.rule.comments.JavadocEmptyVersionTagRule;linear;5min +org.codenarc.rule.comments.JavadocMissingExceptionDescriptionRule;linear;5min +org.codenarc.rule.comments.JavadocMissingParamDescriptionRule;linear;5min +org.codenarc.rule.comments.JavadocMissingThrowsDescriptionRule;linear;5min org.codenarc.rule.concurrency.BusyWaitRule;linear;1h org.codenarc.rule.concurrency.DoubleCheckedLockingRule;linear;1d org.codenarc.rule.concurrency.InconsistentPropertyLockingRule;linear;20min @@ -72,17 +87,28 @@ org.codenarc.rule.concurrency.UseOfNotifyMethodRule;linear;20min org.codenarc.rule.concurrency.VolatileArrayFieldRule;linear;1h org.codenarc.rule.concurrency.VolatileLongOrDoubleFieldRule;linear;1h org.codenarc.rule.concurrency.WaitOutsideOfWhileLoopRule;linear;1h +org.codenarc.rule.convention.CompileStaticRule;linear;5min org.codenarc.rule.convention.ConfusingTernaryRule;linear;10min org.codenarc.rule.convention.CouldBeElvisRule;linear;10min +org.codenarc.rule.convention.CouldBeSwitchStatementRule;linear;5min +org.codenarc.rule.convention.FieldTypeRequiredRule;linear;5min org.codenarc.rule.convention.HashtableIsObsoleteRule;linear;10min org.codenarc.rule.convention.IfStatementCouldBeTernaryRule;linear;5min +org.codenarc.rule.convention.InvertedConditionRule;linear;5min org.codenarc.rule.convention.InvertedIfElseRule;linear;10min org.codenarc.rule.convention.LongLiteralWithLowerCaseLRule;linear;5min +org.codenarc.rule.convention.MethodReturnTypeRequiredRule;linear;5min org.codenarc.rule.convention.NoDefRule;linear;5min +org.codenarc.rule.convention.NoJavaUtilDateRule;linear;5min org.codenarc.rule.convention.NoTabCharacterRule;linear;2min org.codenarc.rule.convention.ParameterReassignmentRule;linear;20min +org.codenarc.rule.convention.PublicMethodsBeforeNonPublicMethodsRule;linear;5min +org.codenarc.rule.convention.PublicMethodsBeforeNonPublicMethodsRule>;linear;5min +org.codenarc.rule.convention.StaticFieldsBeforeInstanceFieldsRule;linear;5min +org.codenarc.rule.convention.StaticMethodsBeforeInstanceMethodsRule;linear;5min org.codenarc.rule.convention.TernaryCouldBeElvisRule;linear;10min org.codenarc.rule.convention.TrailingCommaRule;linear;5min +org.codenarc.rule.convention.VariableTypeRequiredRule;linear;5min org.codenarc.rule.convention.VectorIsObsoleteRule;linear;20min org.codenarc.rule.design.AbstractClassWithPublicConstructorRule;linear;5min org.codenarc.rule.design.AbstractClassWithoutAbstractMethodRule;linear;5min @@ -111,6 +137,7 @@ org.codenarc.rule.dry.DuplicateListLiteralRule;linear;5min org.codenarc.rule.dry.DuplicateMapLiteralRule;linear;5min org.codenarc.rule.dry.DuplicateNumberLiteralRule;linear;5min org.codenarc.rule.dry.DuplicateStringLiteralRule;linear;5min +org.codenarc.rule.enhanced.MissingOverrideAnnotationRule;linear;5min org.codenarc.rule.exceptions.CatchArrayIndexOutOfBoundsExceptionRule;linear;10min org.codenarc.rule.exceptions.CatchErrorRule;linear;1h org.codenarc.rule.exceptions.CatchExceptionRule;linear;20min @@ -132,15 +159,20 @@ org.codenarc.rule.exceptions.ThrowNullPointerExceptionRule;linear;20min org.codenarc.rule.exceptions.ThrowRuntimeExceptionRule;linear;20min org.codenarc.rule.exceptions.ThrowThrowableRule;linear;20min org.codenarc.rule.formatting.BlankLineBeforePackageRule;linear;5min +org.codenarc.rule.formatting.BlockEndsWithBlankLineRule;linear;5min +org.codenarc.rule.formatting.BlockStartsWithBlankLineRule;linear;5min org.codenarc.rule.formatting.BracesForClassRule;linear;5min org.codenarc.rule.formatting.BracesForForLoopRule;linear;5min org.codenarc.rule.formatting.BracesForIfElseRule;linear;5min org.codenarc.rule.formatting.BracesForMethodRule;linear;5min org.codenarc.rule.formatting.BracesForTryCatchFinallyRule;linear;5min +org.codenarc.rule.formatting.ClassEndsWithBlankLineRule;linear;5min org.codenarc.rule.formatting.ClassJavadocRule;linear;10min +org.codenarc.rule.formatting.ClassStartsWithBlankLineRule;linear;5min org.codenarc.rule.formatting.ClosureStatementOnOpeningLineOfMultipleLineClosureRule;linear;10min org.codenarc.rule.formatting.ConsecutiveBlankLinesRule;linear;5min org.codenarc.rule.formatting.FileEndsWithoutNewlineRule;linear;5min +org.codenarc.rule.formatting.IndentationRule;linear;5min org.codenarc.rule.formatting.LineLengthRule;linear;10min org.codenarc.rule.formatting.MissingBlankLineAfterImportsRule;linear;5min org.codenarc.rule.formatting.MissingBlankLineAfterPackageRule;linear;5min @@ -170,13 +202,13 @@ org.codenarc.rule.generic.StatelessClassRule.fixed;linear;20min org.codenarc.rule.grails.GrailsDomainHasEqualsRule;linear;20min org.codenarc.rule.grails.GrailsDomainHasToStringRule;linear;20min org.codenarc.rule.grails.GrailsDomainReservedSqlKeywordNameRule;linear;20min +org.codenarc.rule.grails.GrailsDomainStringPropertyMaxSizeRule;linear;5min org.codenarc.rule.grails.GrailsDomainWithServiceReferenceRule;linear;20min org.codenarc.rule.grails.GrailsDuplicateConstraintRule;linear;20min org.codenarc.rule.grails.GrailsDuplicateMappingRule;linear;20min org.codenarc.rule.grails.GrailsMassAssignmentRule;linear;20min org.codenarc.rule.grails.GrailsPublicControllerMethodRule.fixed;linear;20min org.codenarc.rule.grails.GrailsServletContextReferenceRule;linear;20min -org.codenarc.rule.grails.GrailsSessionReferenceRule;linear;20min org.codenarc.rule.grails.GrailsStatelessServiceRule;linear;20min org.codenarc.rule.groovyism.AssignCollectionSortRule;linear;5min org.codenarc.rule.groovyism.AssignCollectionUniqueRule;linear;5min @@ -196,6 +228,7 @@ org.codenarc.rule.groovyism.ExplicitCallToMultiplyMethodRule;linear;5min org.codenarc.rule.groovyism.ExplicitCallToOrMethodRule;linear;5min org.codenarc.rule.groovyism.ExplicitCallToPlusMethodRule;linear;5min org.codenarc.rule.groovyism.ExplicitCallToPowerMethodRule;linear;5min +org.codenarc.rule.groovyism.ExplicitCallToPutAtMethodRule;linear;5min org.codenarc.rule.groovyism.ExplicitCallToRightShiftMethodRule;linear;5min org.codenarc.rule.groovyism.ExplicitCallToXorMethodRule;linear;5min org.codenarc.rule.groovyism.ExplicitHashMapInstantiationRule;linear;5min @@ -333,6 +366,7 @@ org.codenarc.rule.unnecessary.UnnecessaryReturnKeywordRule;linear;5min org.codenarc.rule.unnecessary.UnnecessarySafeNavigationOperatorRule;linear;5min org.codenarc.rule.unnecessary.UnnecessarySelfAssignmentRule;linear;5min org.codenarc.rule.unnecessary.UnnecessarySemicolonRule;linear;5min +org.codenarc.rule.unnecessary.UnnecessarySetterRule;linear;5min org.codenarc.rule.unnecessary.UnnecessaryStringInstantiationRule;linear;5min org.codenarc.rule.unnecessary.UnnecessarySubstringRule;linear;5min org.codenarc.rule.unnecessary.UnnecessaryTernaryExpressionRule;linear;5min diff --git a/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/profile-sonar-way.xml b/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/profile-sonar-way.xml index 4f75ee78..295db6bf 100644 --- a/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/profile-sonar-way.xml +++ b/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/profile-sonar-way.xml @@ -47,11 +47,6 @@ org.codenarc.rule.grails.GrailsServletContextReferenceRule MINOR - - grvy - org.codenarc.rule.grails.GrailsSessionReferenceRule - MAJOR - grvy org.codenarc.rule.grails.GrailsStatelessServiceRule diff --git a/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/rules.xml b/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/rules.xml index b3dae61c..3b8c94fb 100644 --- a/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/rules.xml +++ b/sonar-groovy-plugin/src/main/resources/org/sonar/plugins/groovy/rules.xml @@ -1,4 +1,4 @@ - + @@ -7,14 +7,13 @@ MINOR - Checks for calls to the java.math.BigDecimal constructors that take a double value as the first parameter. As described in the BigDecimal javadoc, the results from these constructors can be somewhat unpredictable, and their use is generally not recommended. This is because some numbers, such as 0.1, cannot be represented exactly as a double.

-

For instance, executing println new BigDecimal(0.1) prints out 0.1000000000000000055511151231257827021181583404541015625.

-

Here is an example of code that produces a violation:

+ Checks for calls to the java.math.BigDecimal constructors that take a double value as the first parameter. As described in the BigDecimal javadoc, the results from these constructors can be somewhat unpredictable, and their use is generally not recommended. This is because some numbers, such as 0.1, cannot be represented exactly as a double.

+

For instance, executing println new BigDecimal(0.1) prints out 0.1000000000000000055511151231257827021181583404541015625.

+

Here is an example of code that produces a violation:

     def b1 = new BigDecimal(0.1)               // violation
     def b2 = new java.math.BigDecimal(23.45d)  // violation
-
-]]>
+]]> bug @@ -23,7 +22,7 @@ MINOR - Checks for statements with a constant value for the boolean expression, such as true, false, null, or a literal constant value. These statements can be simplified or avoided altogether. Examples of violations include:

+ Checks for if statements with a constant value for the if boolean expression, such as true, false, null, or a literal constant value. These if statements can be simplified or avoided altogether. Examples of violations include:

     if (true) { .. }
     if (false) { .. }
@@ -37,8 +36,7 @@
     if ([:]) { .. }
     if ([a:123, b:456]) { .. }
     if ([a, b, c]) { .. }
-
-]]>
+]]>
bug @@ -47,7 +45,7 @@ MINOR - Checks for ternary expressions with a constant value for the boolean expression, such as true, false, null, or a literal constant value. Examples of violations include:

+ Checks for ternary expressions with a constant value for the boolean expression, such as true, false, null, or a literal constant value. Examples of violations include:

     true ? x : y
     false ? x : y
@@ -62,6 +60,7 @@
     [a:123, b:456] ? x : y
     [a, b, c] ? x : y
 
+

The rule also checks for the same types of constant values for the boolean expressions within the "short" ternary expressions, also known as the "Elvis" operator, e.g.:

     true ?: y
     null ?: y
@@ -69,8 +68,7 @@
     "abc" ?: y
     [:] ?: y
     [a, b, c] ?: y
-
-]]>
+]]>
bug @@ -79,9 +77,9 @@ MINOR - Checks for empty blocks. In most cases, exceptions should not be caught and ignored (swallowed).

-

The rule has a property named ignoreRegex that defaults to the value 'ignore|ignored'. If the name of the exception matches this regex then no violations are produced.

-

Here is an example of code that produces a violation:

+ Checks for empty catch blocks. In most cases, exceptions should not be caught and ignored (swallowed).

+

The rule has a property named ignoreRegex that defaults to the value 'ignore|ignored'. If the name of the exception matches this regex then no violations are produced.

+

Here is an example of code that produces a violation:

     def myMethod() {
         try {
@@ -98,12 +96,11 @@
             //no violations because the parameter name is ignored
         }
     }
-
-]]>
+]]>
unused ignoreRegex - + ignore|ignored @@ -113,8 +110,8 @@ MINOR - Checks for empty blocks. Empty blocks are confusing and serve no purpose.

-

Here is an example of code that produces a violation:

+ Checks for empty else blocks. Empty else blocks are confusing and serve no purpose.

+

Here is an example of code that produces a violation:

     def myMethod() {
         if (x==23) {
@@ -123,8 +120,7 @@
             // empty
         }
     }
-
-]]>
+]]>
unused @@ -133,8 +129,8 @@ MINOR - Checks for empty blocks. Empty blocks are confusing and serve no purpose.

-

Here is an example of code that produces a violation:

+ Checks for empty finally blocks. Empty finally blocks are confusing and serve no purpose.

+

Here is an example of code that produces a violation:

     def myMethod() {
         try {
@@ -143,8 +139,7 @@
             // empty
         }
     }
-
-]]>
+]]>
unused @@ -153,16 +148,15 @@ MINOR - Checks for empty blocks. Empty statements are confusing and serve no purpose.

-

Here is an example of code that produces a violation:

+ Checks for empty for blocks. Empty for statements are confusing and serve no purpose.

+

Here is an example of code that produces a violation:

     def myMethod() {
-        for (int i=0; i < 23; i++) {
+        for (int i=0; i < 23; i++) {
             // empty
         }
     }
-
-]]>
+]]>
unused @@ -171,16 +165,15 @@ MINOR - Checks for empty statements. Empty statements are confusing and serve no purpose.

-

Here is an example of code that produces a violation:

+ Checks for empty if statements. Empty if statements are confusing and serve no purpose.

+

Here is an example of code that produces a violation:

     def myMethod() {
         if (x==23) {
             // empty
         }
     }
-
-]]>
+]]>
unused @@ -189,16 +182,15 @@ MINOR - Checks for empty statements. Empty statements are confusing and serve no purpose.

-

Here is an example of code that produces a violation:

+ Checks for empty switch statements. Empty switch statements are confusing and serve no purpose.

+

Here is an example of code that produces a violation:

     def myMethod() {
         switch(myVariable) {
             // empty
         }
     }
-
-]]>
+]]>
unused @@ -207,8 +199,8 @@ MINOR - Checks for empty statements. Empty statements are confusing and serve no purpose.

-

Here is an example of code that produces a violation:

+ Checks for empty synchronized statements. Empty synchronized statements are confusing and serve no purpose.

+

Here is an example of code that produces a violation:

     class MyClass {
         def myMethod() {
@@ -216,8 +208,7 @@
             }
         }
     }
-
-]]>
+]]>
unused @@ -226,8 +217,8 @@ MINOR - Checks for empty blocks. Empty blocks are confusing and serve no purpose.

-

Here is an example of code that produces a violation:

+ Checks for empty try blocks. Empty try blocks are confusing and serve no purpose.

+

Here is an example of code that produces a violation:

     def myMethod() {
         try {
@@ -236,8 +227,7 @@
             e.printStackTrace()
         }
     }
-
-]]>
+]]>
unused @@ -246,16 +236,15 @@ MINOR - Checks for empty statements. Empty statements are confusing and serve no purpose.

-

Here is an example of code that produces a violation:

+ Checks for empty while statements. Empty while statements are confusing and serve no purpose.

+

Here is an example of code that produces a violation:

     def myMethod() {
         while (!stopped) {
             // empty
         }
     }
-
-]]>
+]]>
unused @@ -264,8 +253,8 @@ MINOR - Checks that if either the boolean equals(Object) or the int hashCode() methods are overridden within a class, then both must be overridden.

-

Here is an example of code that produces a violation:

+ Checks that if either the boolean equals(Object) or the int hashCode() methods are overridden within a class, then both must be overridden.

+

Here is an example of code that produces a violation:

     class MyClass {
         boolean equals(Object object) {
@@ -273,14 +262,14 @@
         }
     }
 
+

And so does this:

     class MyClass {
         int hashCode() {
             return 0
         }
     }
-
-]]>
+]]>
pitfall @@ -289,8 +278,8 @@ MINOR - Checks for a return from within a block. Returning from a block is confusing and can hide the original exception.

-

Here is an example of code that produces a violation:

+ Checks for a return from within a finally block. Returning from a finally block is confusing and can hide the original exception.

+

Here is an example of code that produces a violation:

     int myMethod() {
         try {
@@ -302,8 +291,7 @@
             return 99               // violation
         }
     }
-
-]]>
+]]>
error-handling @@ -312,8 +300,8 @@ MINOR - Checks for throwing an exception from within a block. Throwing an exception from a block is confusing and can hide the original exception.

-

Here is an example of code that produces a violation:

+ Checks for throwing an exception from within a finally block. Throwing an exception from a finally block is confusing and can hide the original exception.

+

Here is an example of code that produces a violation:

     int myMethod() {
         try {
@@ -324,8 +312,7 @@
             throw new Exception()   // violation
         }
     }
-
-]]>
+]]>
error-handling @@ -335,8 +322,7 @@ MINOR - Dead code appears after a return statement or an exception is thrown. If code appears after one of these statements then it will never be executed and can be safely deleted.

- ]]>
+ Dead code appears after a return statement or an exception is thrown. If code appears after one of these statements then it will never be executed and can be safely deleted.

]]>
unused @@ -346,8 +332,7 @@ MINOR - There is no point in using a double negative, it is always positive. For instance !!x can always be simplified to x. And !(!x) can as well.

- ]]>
+ There is no point in using a double negative, it is always positive. For instance !!x can always be simplified to x. And !(!x) can as well.

]]>
bug @@ -357,14 +342,14 @@ MINOR - Check for duplicate case statements in a switch block, such as two equal integers or strings. Here are some examples of code that produces violations:

+ Check for duplicate case statements in a switch block, such as two equal integers or strings. Here are some examples of code that produces violations:

     switch( 0 ) {
         case 1: break;
         case 2: break;
         case 2: break;          // violation
     }
-    
+
     switch( "test" ) {
         case "$a": break;
         case "$a": break;       // ok; only flags constant values (not GStrings)
@@ -372,8 +357,7 @@
         case "ab": break;       // violation
         case "abc": break;
     }
-
-]]>
+]]>
bug @@ -383,8 +367,7 @@ MINOR - Don't use removeAll to clear a collection. If you want to remove all elements from a collection c, use c.clear, not c.removeAll(c). Calling c.removeAll(c) to clear a collection is less clear, susceptible to errors from typos, less efficient and for some collections, might throw a ConcurrentModificationException.

- ]]>
+ Don't use removeAll to clear a collection. If you want to remove all elements from a collection c, use c.clear, not c.removeAll(c). Calling c.removeAll(c) to clear a collection is less clear, susceptible to errors from typos, less efficient and for some collections, might throw a ConcurrentModificationException.

]]>
bug @@ -394,8 +377,7 @@ MINOR - Calls to System.gc(), Runtime.getRuntime().gc(), and System.runFinalization() are not advised. Code should have the same behavior whether the garbage collection is disabled using the option -Xdisableexplicitgc or not. Moreover, "modern" JVMs do a very good job handling garbage collections. If memory usage issues unrelated to memory leaks develop within an application, it should be dealt with JVM options rather than within the code itself.

- ]]>
+ Calls to System.gc(), Runtime.getRuntime().gc(), and System.runFinalization() are not advised. Code should have the same behavior whether the garbage collection is disabled using the option -Xdisableexplicitgc or not. Moreover, "modern" JVMs do a very good job handling garbage collections. If memory usage issues unrelated to memory leaks develop within an application, it should be dealt with JVM options rather than within the code itself.

]]>
unpredictable @@ -405,8 +387,8 @@ MINOR - An assignment operator (=) was used in a conditional test. This is usually a typo, and the comparison operator (==) was intended.

-

Example of violations:

+ An assignment operator (=) was used in a conditional test. This is usually a typo, and the comparison operator (==) was intended.

+

Example of violations:

     if ((value = true)) {
         // should be ==
@@ -425,8 +407,7 @@
 
     value == true ? x : y
     value == true ?: x
-
-]]>
+]]>
bug @@ -436,8 +417,8 @@ MINOR - This rule catches usages of java.lang.Boolean.getBoolean(String) which reads a boolean from the System properties. It is often mistakenly used to attempt to read user input or parse a String into a boolean. It is a poor piece of API to use; replace it with System.properties['prop̈́'].

-

Example of violations:

+ This rule catches usages of java.lang.Boolean.getBoolean(String) which reads a boolean from the System properties. It is often mistakenly used to attempt to read user input or parse a String into a boolean. It is a poor piece of API to use; replace it with System.properties['prop̈́'].

+

Example of violations:

     // produces violation
     Boolean.getBoolean(value)
@@ -445,8 +426,7 @@
     // zero or two parameters is OK, must be different method
     Boolean.getBoolean(value, 1)
     Boolean.getBoolean()
-
-]]>
+]]>
bug @@ -456,16 +436,15 @@ MINOR - The code uses x % 2 == 1 to check to see if a value is odd, but this won't work for negative numbers (e.g., (-5) % 2 == -1). If this code is intending to check for oddness, consider using x & 1 == 1, or x % 2 != 0.

-

Examples:

+ The code uses x % 2 == 1 to check to see if a value is odd, but this won't work for negative numbers (e.g., (-5) % 2 == -1). If this code is intending to check for oddness, consider using x & 1 == 1, or x % 2 != 0.

+

Examples:

     if (x % 2 == 1) { }             // violation
     if (method() % 2 == 1) { }      // violation
 
-    if (x & 1 == 1) { }             // OK
+    if (x & 1 == 1) { }             // OK
     if (x % 2 != 0) { }             // OK
-
-]]>
+]]>
bug @@ -475,13 +454,12 @@ MINOR - An empty class instance initializer was found. It is safe to remove it. Example:

+ An empty class instance initializer was found. It is safe to remove it. Example:

     class MyClass {
         { }     // empty instance initializer, not a closure
     }
-
-]]>
+]]>
unused @@ -491,8 +469,8 @@ MINOR - A method was found without an implementation. If the method is overriding or implementing a parent method, then mark it with the @Override annotation. This rule should not be used with Java 5 code because you cannot put @Override on a method implementing an interface. Use with Java 6 and higher.

-

Example of violations:

+ A method was found without an implementation. If the method is overriding or implementing a parent method, then mark it with the @Override annotation. This rule should not be used with Java 5 code because you cannot put @Override on a method implementing an interface. Use with Java 6 and higher.

+

Example of violations:

     class MyClass {
 
@@ -511,8 +489,7 @@
         // OK, handled by EmptyMethodInAbstractClass Rule
         public void method() {}
     }
-
-]]>
+]]>
unused @@ -522,13 +499,12 @@ MINOR - An empty static initializer was found. It is safe to remove it. Example:

+ An empty static initializer was found. It is safe to remove it. Example:

     class MyClass {
         static { }
     }
-
-]]>
+]]>
unused @@ -538,8 +514,8 @@ MINOR - This rule catches usages of java.lang.Integer.getInteger(String, ...) which reads an Integer from the System properties. It is often mistakenly used to attempt to read user input or parse a String into an Integer. It is a poor piece of API to use; replace it with System.properties['prop'].

-

Example of violations:

+ This rule catches usages of java.lang.Integer.getInteger(String, ...) which reads an Integer from the System properties. It is often mistakenly used to attempt to read user input or parse a String into an Integer. It is a poor piece of API to use; replace it with System.properties['prop'].

+

Example of violations:

     // violations
     Integer.getInteger(value)
@@ -548,8 +524,7 @@
     // zero or more than 2 parameters is OK, must be different method
     Integer.getInteger()
     Integer.getInteger(value, radix, locale)
-
-]]>
+]]>
bug @@ -559,8 +534,8 @@ MINOR - A literal is created with duplicated key. The map entry will be overwritten.

-

Example of violations:

+ A Map literal is created with duplicated key. The map entry will be overwritten.

+

Example of violations:

     def var1 = [a:1, a:2, b:3]        //violation
     def var2 = [1:1, 1:2, 2:3]        //violation
@@ -570,8 +545,7 @@
     def var4 = [a:1, b:1, c:1]
     def var5 = [1:1, 2:1, 3:1]
     def var6 = ["a":1, "b":1, "c":1]
-
-]]>
+]]>
bug @@ -581,8 +555,8 @@ MINOR - A literal is created with duplicate constant value. A set cannot contain two elements with the same value.

-

Example of violations:

+ A Set literal is created with duplicate constant value. A set cannot contain two elements with the same value.

+

Example of violations:

     def a = [1, 2, 2, 4] as Set
     def b = [1, 2, 2, 4] as HashSet
@@ -597,8 +571,7 @@
     def a = [1, 2, 3, 4] as Set
     def b = ['1', '2', '3', '4'] as Set
     def c = [1, '1'] as Set
-
-]]>
+]]>
bug @@ -608,8 +581,8 @@ MINOR - The class has an equals method, but the parameter of the method is not of type Object. It is not overriding equals but instead overloading it.

-

Example of violations:

+ The class has an equals method, but the parameter of the method is not of type Object. It is not overriding equals but instead overloading it.

+

Example of violations:

     class Object1 {
         //parameter should be Object not String
@@ -644,8 +617,7 @@
     class Object7 {
         boolean equals(other) { true }
     }
-
-]]>
+]]>
pitfall @@ -655,11 +627,11 @@ MAJOR - A for loop without an init and update statement can be simplified to a while loop.

-

Example of violations:

+ A for loop without an init and update statement can be simplified to a while loop.

+

Example of violations:

     int i = 0;
-    for(; i < 5;) {     // Violation
+    for(; i < 5;) {     // Violation
         println i++
     }
 
@@ -667,18 +639,17 @@
     for(i in [1,2])         // OK
        println i
 
-    for(int i = 0; i<5;)    // OK
+    for(int i = 0; i<5;)    // OK
         println i++
 
     int i = 0;
-    for(; i < 5; i++)       // OK
+    for(; i < 5; i++)       // OK
         println i
 
     for (Plan p : plans) {  // OK
         println "Plan=$p"
     }
-
-]]>
+]]>
clumsy @@ -688,16 +659,15 @@ MINOR - Using Class.forName(...) is a common way to add dynamic behavior to a system. However, using this method can cause resource leaks because the classes can be pinned in memory for long periods of time. If you're forced to do dynamic class loading then use ClassLoader.loadClass instead. All variations of the Class.forName(...) method suffer from the same problem.

-

For more information see these links:

-

* http://blog.bjhargrave.com/2007/09/classforname-caches-defined-class-in.html

-

* http://www.osgi.org/blog/2011/05/what-you-should-know-about-class.html

- Example of violations:

+ Using Class.forName(...) is a common way to add dynamic behavior to a system. However, using this method can cause resource leaks because the classes can be pinned in memory for long periods of time. If you're forced to do dynamic class loading then use ClassLoader.loadClass instead. All variations of the Class.forName(...) method suffer from the same problem.

+

For more information see these links:

+

* http://blog.bjhargrave.com/2007/09/classforname-caches-defined-class-in.html

+

* http://www.osgi.org/blog/2011/05/what-you-should-know-about-class.html

+

Example of violations:

     Class.forName('SomeClassName')
     Class.forName(aClassName, true, aClassLoader)
-
-]]>
+]]>
leak owasp-a1 @@ -708,24 +678,23 @@ MINOR - Checks for expressions where a or equals() or compareTo() is used to compare two constants to each other or two literals that contain only constant values.

-

Here are examples of code that produces a violation:

+ Checks for expressions where a comparison operator or equals() or compareTo() is used to compare two constants to each other or two literals that contain only constant values.

+

Here are examples of code that produces a violation:

     23 == 67                    // violation
     Boolean.FALSE != false      // violation
-    23 < 88                     // violation
-    0.17 <= 0.99                // violation
-    "abc" > "ddd"               // violation
-    [Boolean.FALSE] >= [27]     // violation
-    [a:1] <=> [a:2]             // violation
+    23 < 88                     // violation
+    0.17 <= 0.99                // violation
+    "abc" > "ddd"               // violation
+    [Boolean.FALSE] >= [27]     // violation
+    [a:1] <=> [a:2]             // violation
 
     [1,2].equals([3,4])                                     // violation
     [a:123, b:true].equals(['a':222, b:Boolean.FALSE])      // violation
 
     [a:123, b:456].compareTo([a:222, b:567]                 // violation
     [a:false, b:true].compareTo(['a':34.5, b:Boolean.TRUE]  // violation
-
-]]>
+]]>
bug @@ -735,20 +704,19 @@ MINOR - Checks for expressions where a or equals() or compareTo() is used to compare a variable to itself, e.g.: x == x, x != x, x \<=\> x, x \< x, x \>= x, x.equals(x) or x.compareTo(x), where x is a variable.

-

Here are examples of code that produces a violation:

+ Checks for expressions where a comparison operator or equals() or compareTo() is used to compare a variable to itself, e.g.: x == x, x != x, x <=> x, x < x, x >= x, x.equals(x) or x.compareTo(x), where x is a variable.

+

Here are examples of code that produces a violation:

     if (x == x) { }                 // violation
     if (x != x) { }                 // violation
-    while (x < x) { }               // violation
-    if (x <= x) { }                 // violation
-    while (x > x) { }               // violation
-    if (x >= x) { }                 // violation
-    def c = (x <=> x) { }           // violation
+    while (x < x) { }               // violation
+    if (x <= x) { }                 // violation
+    while (x > x) { }               // violation
+    if (x >= x) { }                 // violation
+    def c = (x <=> x) { }           // violation
     println isReady = x.equals(x)   // violation
     println x.compareTo(x)          // violation
-
-]]>
+]]>
bug @@ -758,13 +726,12 @@ MINOR - Checks for bitwise operations in conditionals. For instance, the condition if (a | b) is almost always a mistake and should be if (a || b). If you need to do a bitwise operation then it is best practice to extract a temp variable.

-

Example of violations:

+ Checks for bitwise operations in conditionals. For instance, the condition if (a | b) is almost always a mistake and should be if (a || b). If you need to do a bitwise operation then it is best practice to extract a temp variable.

+

Example of violations:

     if (a | b) { }
-    if (a & b) { }
-
-]]>
+ if (a & b) { } +]]>
bug @@ -774,14 +741,13 @@ MINOR - This rule finds usages of a Windows file separator within the constructor call of a File object. It is better to use the Unix file separator or use the File.separator constant.

-

Example of violations:

+ This rule finds usages of a Windows file separator within the constructor call of a File object. It is better to use the Unix file separator or use the File.separator constant.

+

Example of violations:

    new File('.\\foo\\')
    new File('c:\\dir')
    new File('../foo\\')
-
-]]>
+]]>
pitfall @@ -791,8 +757,8 @@ MINOR - The Math.random() method returns a double result greater than or equal to 0.0 and less than 1.0. If you coerce this result into an Integer, Long, int, or long then it is coerced to zero. Casting the result to int, or assigning it to an int field is probably a bug.

-

Example of violations:

+ The Math.random() method returns a double result greater than or equal to 0.0 and less than 1.0. If you coerce this result into an Integer, Long, int, or long then it is coerced to zero. Casting the result to int, or assigning it to an int field is probably a bug.

+

Example of violations:

     (int) Math.random()
     (Integer) Math.random()
@@ -802,8 +768,7 @@
     Integer m() { Math.random() }
     (Math.random()) as int
     (Math.random()) as Integer
-
-]]>
+]]>
bug @@ -813,14 +778,13 @@ MINOR - This rule find cases where a File object is constructed with a windows-based path. This is not portable across operating systems or different machines, and using the File.listRoots() method is a better alternative.

-

Example of violations:

+ This rule find cases where a File object is constructed with a windows-based path. This is not portable across operating systems or different machines, and using the File.listRoots() method is a better alternative.

+

Example of violations:

    new File('c:\\')
    new File('c:\\dir')
    new File('E:\\dir')
-
-]]>
+]]>
pitfall @@ -830,18 +794,17 @@ MINOR - Checks for statements within a block. An can throw an exception, hiding the original exception, if there is one.

-

Here is an example of code that produces a violation:

+ Checks for assert statements within a finally block. An assert can throw an exception, hiding the original exception, if there is one.

+

Here is an example of code that produces a violation:

     int myMethod(int count) {
         try {
             doSomething()
         } finally {
-            assert count > 0        // violation
+            assert count > 0        // violation
         }
     }
-
-]]>
+]]>
error-handling @@ -851,7 +814,7 @@ MAJOR - Checks for statements with a constant value for the boolean expression, such as true, false, null, or a literal constant value. These statements will always pass or always fail, depending on the constant/literal value. Examples of violations include:

+ Checks for assert statements with a constant value for the assert boolean expression, such as true, false, null, or a literal constant value. These assert statements will always pass or always fail, depending on the constant/literal value. Examples of violations include:

     assert true
     assert false, "assertion message"
@@ -865,8 +828,7 @@
     assert [:]
     assert [a:123, b:456]
     assert [a, b, c]
-
-]]>
+]]>
bug @@ -876,22 +838,21 @@ MINOR - Looks for faulty checks for that can cause a NullPointerException.

-

Examples:

+ Looks for faulty checks for null that can cause a NullPointerException.

+

Examples:

-    if (name != null || name.length > 0) { }            // violation
+    if (name != null || name.length > 0) { }            // violation
     if (name != null || name.length) { }                // violation
-    while (record == null && record.id < 10) { }        // violation
-    if (record == null && record.id && doStuff()) { }   // violation
-    def isNotValid = record == null && record.id < 10   // violation
-    return record == null && !record.id                 // violation
-
-    if (name != null || name.size() > 0) { }            // violation
-    if (string == null && string.equals("")) { }        // violation
-    def isValid = name != null || name.size() > 0       // violation
+    while (record == null && record.id < 10) { }        // violation
+    if (record == null && record.id && doStuff()) { }   // violation
+    def isNotValid = record == null && record.id < 10   // violation
+    return record == null && !record.id                 // violation
+
+    if (name != null || name.size() > 0) { }            // violation
+    if (string == null && string.equals("")) { }        // violation
+    def isValid = name != null || name.size() > 0       // violation
     return name != null || !name.size()                 // violation
-
-]]>
+]]>
bug @@ -901,9 +862,8 @@ MINOR - Reports classes without methods, fields or properties. Why would you need a class like this?

-

This rule ignores interfaces, abstract classes, enums, anonymous inner classes, subclasses (extends), and classes with annotations.

- ]]>
+ Reports classes without methods, fields or properties. Why would you need a class like this?

+

This rule ignores interfaces, abstract classes, enums, anonymous inner classes, subclasses (extends), and classes with annotations.

]]>
unused @@ -913,135 +873,498 @@ MINOR - Checks for multiple consecutive unary operators. These are confusing, and are likely typos and bugs.

-

Example of violations:

+ Checks for multiple consecutive unary operators. These are confusing, and are likely typos and bugs.

+

Example of violations:

     int z = ~~2             // violation
     boolean b = !!true      // violation
     boolean c = !!!false    // 2 violations
     int j = -~7             // violation
     int k = +~8             // violation
-
-]]>
+]]>
bug - + - - org.codenarc.rule.serialization.SerialVersionUIDRule + org.codenarc.rule.braces.IfStatementBracesRule MINOR - - - A serialVersionUID is normally intended to be used with Serialization. It needs to be of type long, static, and final. Also, it should be declared private. Providing no modifier creates a and Groovy generates a , which is probably not intended.

-

From API javadoc for java.io.Serializable:

- ]]>
+ + + Checks that if statements use braces, even for a single statement.

]]>
bug
- - org.codenarc.rule.serialization.SerializableClassMustDefineSerialVersionUIDRule + org.codenarc.rule.braces.ElseBlockBracesRule MINOR - - - Classes that implement Serializable should define a serialVersionUID. Deserialization uses this number to ensure that a loaded class corresponds exactly to a serialized object. If you don't define serialVersionUID, the system will make one by hashing most of your class's features. Then if you change anything, the UID will change and Java won't let you reload old data.

-

An example of a missing serialVersionUID:

-
-    class MyClass imlements Serializable {
-        // missing serialVersionUID
-    }
-
-]]>
+ + + Checks that else blocks use braces, even for a single statement.

+

By default, braces are not required for an else if it is followed immediately by an if. Set the bracesRequiredForElseIf property to true to require braces is that situation as well.

]]>
bug + + bracesRequiredForElseIf + + false +
- - org.codenarc.rule.serialization.SerialPersistentFieldsRule + org.codenarc.rule.braces.ForStatementBracesRule MINOR - - - To use a Serializable object's serialPersistentFields correctly, it must be declared private, static, and final.

-

The Java Object Serialization Specification allows developers to manually define Serializable fields for a class by specifying them in the serialPersistentFields array. This feature will only work if serialPersistentFields is declared as private, static, and final. Also, specific to Groovy, the field must be of type ObjectStreamField[], and cannot be Object.

-

References:

-]]>
+ + + Checks that for statements use braces, even for a single statement.

]]>
bug
- - org.codenarc.rule.serialization.EnumCustomSerializationIgnoredRule + org.codenarc.rule.braces.WhileStatementBracesRule MINOR - - - Checks for enums that define writeObject() or writeReplace() methods, or declare serialPersistentFields or serialVersionUID fields, all of which are ignored for enums.

-

From the javadoc for ObjectOutputStream:

-

-

Example of violations:

-
-    enum MyEnum {
-        ONE, TWO, THREE
-        private static final long serialVersionUID = 1234567L               // violation
-        private static final ObjectStreamField[] serialPersistentFields =   // violation
-            { new ObjectStreamField("name", String.class) }
-        String name;
+    
+    
+    Checks that while statements use braces, even for a single statement.

]]>
+ bug + - Object writeReplace() throws ObjectStreamException { .. } // violation - private void writeObject(ObjectOutputStream stream) { .. } // violation + + + + + org.codenarc.rule.comments.ClassJavadocRule + MINOR + + + Makes sure each class and interface definition is preceded by javadoc. Enum definitions are not checked, due to strange behavior in the Groovy AST. By default, only the main class in a file is checked for Javadoc. The main class is defined as the class that has the same name as the source file, for instance MyClass is the main class in MyClass.groovy but the class MyOtherClass defined in the same source file is not the main class. To check all the classes in the file set the rule property applyToNonMainClasses to true.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ bug +
+ + + + org.codenarc.rule.comments.JavadocEmptyFirstLineRule + MAJOR + + + Check for javadoc comments with an empty top line.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     *                                                      // violation
+     * Sample class
+     *
+     * @author Some Developer
+     */
+    class MyClass {
+
+        /**
+         *                                                  // violation
+         * Return the calculated count of some stuff,
+         * starting with the specified startIndex.
+         *
+         * @param startIndex - the starting index
+         * @return the full count
+         * @throws RuntimeException when the Singularity occurs
+         */
+        int countThings(int startIndex) {
+        }
     }
-
-]]>
+
]]>
bug
- + + + org.codenarc.rule.comments.JavadocEmptyLastLineRule + MAJOR + + + Check for javadoc comments with an empty line at the bottom.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Sample class
+     *
+     * @author Some Developer
+     *                                                      // violation
+     */
+    class MyClass {
+
+        /**
+         * Return the calculated count of some stuff,
+         * starting with the specified startIndex.
+         *
+         * @param startIndex - the starting index
+         * @return the full count
+         * @throws RuntimeException when life finds a way
+         *                                                  // violation
+         */
+        int countThings(int startIndex) {
+        }
+    }
+
]]>
+ bug +
+ - org.codenarc.rule.braces.IfStatementBracesRule - MINOR - - - Checks that statements use braces, even for a single statement.

- ]]>
+ org.codenarc.rule.comments.JavadocConsecutiveEmptyLinesRule + MAJOR + + + Checks for javadoc comments with more than one consecutive empty line.

+

Known limitation: Only the first occurrence of consecutive empty lines within a javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Description
+     *
+     *                                                                          // violation
+     * @param startIndex - the starting index
+     * @return the full count
+     * @throws RuntimeException if you are not pure of spirit
+     *
+     * NOTE: Only the first occurrence of consecutive empty lines
+     *       within a javadoc comment is found, so the following
+     *       lines are not flagged as violations!!!
+     *
+     *
+     */
+    int countThings(int startIndex) { }
+
]]>
bug
+ - org.codenarc.rule.braces.ElseBlockBracesRule - MINOR - - - Checks that blocks use braces, even for a single statement.

-

By default, braces are not required for an if it is followed immediately by an . Set the property to true to require braces is that situation as well.

- ]]>
+ org.codenarc.rule.comments.JavadocEmptySeeTagRule + MAJOR + + + Checks for empty @see tags within javadoc.

+

Known limitation: Only the first occurrence of an empty @see within a javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Sample class
+     *
+     * @see                                                         // violation
+     */
+    class MyClass {
+
+        /**
+         * Return the calculated count of some stuff,
+         * starting with the specified startIndex.
+         *
+         * @param startIndex - the starting index
+         * @return the full count
+         * @throws RuntimeException when you least expect it
+         *     @see                                                 // violation
+         *
+         * NOTE: Only the first occurrence of an empty @see tag
+         *       within a javadoc comment is found, so the
+         *       following line is not flagged as a violation!!!
+         * @see
+         */
+        int countThings(int startIndex) { }
+
+        /**
+         *@see                                                      // violation
+         */
+        String name = 'joe'
+    }
+
]]>
bug - bracesRequiredForElseIf - block followed immediately by an statement. ]]> + allowMultiline + false
+ - org.codenarc.rule.braces.ForStatementBracesRule - MINOR - - - Checks that statements use braces, even for a single statement.

- ]]>
+ org.codenarc.rule.comments.JavadocEmptyParamTagRule + MAJOR + + + Checks for empty @param tags within javadoc

+

Known limitation: Only the first occurrence of an empty @param within a javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param                                               // violation
+     * @return the full count
+     * @throws RuntimeException upon self-reflection
+     */
+    int countThings(int startIndex) { }
+
]]>
bug + + allowMultiline + + false +
+ - org.codenarc.rule.braces.WhileStatementBracesRule - MINOR - - - Checks that while statements use braces, even for a single statement.

-]]>
+ org.codenarc.rule.comments.JavadocEmptyReturnTagRule + MAJOR + + + Checks for empty @return tags within javadoc.

+

Known limitation: Only the first occurrence of an empty @return within a javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param startIndex - the starting index
+     * @return                                  // violation
+     * @throws RuntimeException if you don't say "please"
+     */
+    int countThings(int startIndex) { }
+
]]>
+ bug + + allowMultiline + + false + +
+ + + + org.codenarc.rule.comments.JavadocEmptyThrowsTagRule + MAJOR + + + Checks for empty @throws tag within javadoc.

+

Known limitation: Only the first occurrence of an empty @throws within a javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param startIndex - the starting index
+     * @return the count
+     * @throws                                          // violation
+     */
+    int countThings(int startIndex) { }
+
]]>
+ bug + + allowMultiline + + false + +
+ + + + org.codenarc.rule.comments.JavadocEmptyExceptionTagRule + MAJOR + + + Checks for empty @exception tag within javadoc.

+

Known limitation: Only the first occurrence of an empty @exception within a javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param startIndex - the starting index
+     * @return the count
+     * @exception                                           // violation
+     */
+    int countThings(int startIndex) { }
+
]]>
+ bug + + allowMultiline + + false + +
+ + + + org.codenarc.rule.comments.JavadocEmptyAuthorTagRule + MAJOR + + + Checks for empty @author tags within javadoc.

+

Known limitation: Only the first occurrence of an empty @author within a javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param startIndex - the starting index
+     * @return the count
+     * @author                                             // violation
+     */
+    int countThings(int startIndex) { }
+
]]>
+ bug + + allowMultiline + + false + +
+ + + + org.codenarc.rule.comments.JavadocEmptySinceTagRule + MAJOR + + + Checks for empty @since tags within javadoc.

+

Known limitation: Only the first occurrence of an empty @since within a javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param startIndex - the starting index
+     * @return the count
+     * @since                                          // violation
+     */
+    int countThings(int startIndex) { }
+
]]>
+ bug + + allowMultiline + + false + +
+ + + + org.codenarc.rule.comments.JavadocEmptyVersionTagRule + MAJOR + + + Checks for empty @version tags within javadoc.

+

Known limitation: Only the first occurrence of an empty @version within a javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param startIndex - the starting index
+     * @return the count
+     * @version                                          // violation
+     */
+
]]>
+ bug + + allowMultiline + + false + +
+ + + + org.codenarc.rule.comments.JavadocMissingParamDescriptionRule + MAJOR + + + Checks for missing description within Javadoc @param tags.

+

Known limitation: Only the first occurrence of a missing description for a @param javadoc comment is found

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param startIndex                           // violation
+     * @return the full count
+     * @throws RuntimeException if it senses fear
+     */
+    int countThings(int startIndex) { }
+
]]>
+ bug + + allowMultiline + + false + +
+ + + + org.codenarc.rule.comments.JavadocMissingThrowsDescriptionRule + MAJOR + + + Checks for missing description within Javadoc @throws tags.

+

Known limitation: Only the first occurrence of a missing description for a @throws javadoc comment is found

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param startIndex the starting index; must be >= 0
+     * @return the full count
+     * @throws RuntimeException                   // violation
+     */
+    int countThings(int startIndex) { }
+
]]>
+ bug + + allowMultiline + + false + +
+ + + + org.codenarc.rule.comments.JavadocMissingExceptionDescriptionRule + MAJOR + + + Checks for missing description within @exception javadoc tags.

+

Known limitation: Only the first occurrence of a missing description for an @exception javadoc comment is found.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

+

Example of violations:

+
+    /**
+     * Return the calculated count of some stuff.
+     *
+     * @param startIndex the starting index; must be >= 0
+     * @return the full count
+     * @exception RuntimeException                   // violation
+     */
+    int countThings(int startIndex) { }
+
]]>
bug + + allowMultiline + + false +
@@ -1051,10 +1374,10 @@ MINOR - This rule reports occurrences of nested synchronized statements.

-

Nested synchronized statements should be avoided. Nested synchronized statements are either useless (if the lock objects are identical) or prone to deadlock.

-

Note that a or an carries its own context (scope). A synchronized statement within a or an defined within an outer synchronized statement does not cause a violation (though nested synchronized statements within either of those will).

-

Here is an example of code that produces a violation:

+ This rule reports occurrences of nested synchronized statements.

+

Nested synchronized statements should be avoided. Nested synchronized statements are either useless (if the lock objects are identical) or prone to deadlock.

+

Note that a closure or an anonymous inner class carries its own context (scope). A synchronized statement within a closure or an anonymous inner class defined within an outer synchronized statement does not cause a violation (though nested synchronized statements within either of those will).

+

Here is an example of code that produces a violation:

     def myMethod() {
         synchronized(this) {
@@ -1064,8 +1387,7 @@
             }
         }
     }
-
-]]>
+]]>
multi-threading @@ -1074,14 +1396,13 @@ MINOR - This rule reports uses of the synchronized keyword on methods. Synchronized methods are the same as synchronizing on 'this', which effectively make your synchronization policy public and modifiable by other objects. To avoid possibilities of deadlock, it is better to synchronize on internal objects.

-

Here is an example of code that produces a violation:

+ This rule reports uses of the synchronized keyword on methods. Synchronized methods are the same as synchronizing on 'this', which effectively make your synchronization policy public and modifiable by other objects. To avoid possibilities of deadlock, it is better to synchronize on internal objects.

+

Here is an example of code that produces a violation:

     synchronized def myMethod() {
         // do stuff ...
     }
-
-]]>
+]]>
multi-threading @@ -1090,16 +1411,15 @@ MINOR - This rule reports uses of the synchronized blocks where the synchronization reference is 'this'. Doing this effectively makes your synchronization policy public and modifiable by other objects. To avoid possibilities of deadlock, it is better to synchronize on internal objects.

-

Here is an example of code that produces a violation:

+ This rule reports uses of the synchronized blocks where the synchronization reference is 'this'. Doing this effectively makes your synchronization policy public and modifiable by other objects. To avoid possibilities of deadlock, it is better to synchronize on internal objects.

+

Here is an example of code that produces a violation:

     def method3() {
         synchronized(this) {
             // do stuff ...
         }
     }
-
-]]>
+]]>
multi-threading @@ -1108,15 +1428,14 @@ MINOR - This rule reports uses of the System.runFinalizersOnExit() method.

-

Method calls to System.runFinalizersOnExit() should not be allowed. This method is inherently non-thread-safe, may result in data corruption, deadlock, and may affect parts of the program far removed from it's call point. It is deprecated, and it's use strongly discouraged.

-

Here is an example of code that produces a violation:

+ This rule reports uses of the System.runFinalizersOnExit() method.

+

Method calls to System.runFinalizersOnExit() should not be allowed. This method is inherently non-thread-safe, may result in data corruption, deadlock, and may affect parts of the program far removed from it's call point. It is deprecated, and it's use strongly discouraged.

+

Here is an example of code that produces a violation:

     def method() {
         System.runFinalizersOnExit(true)
     }
-
-]]>
+]]>
multi-threading @@ -1125,15 +1444,14 @@ MINOR - Avoid using ThreadGroup; although it is intended to be used in a threaded environment it contains methods that are not thread safe.

-

Here is an example of code that produces a violation:

+ Avoid using ThreadGroup; although it is intended to be used in a threaded environment it contains methods that are not thread safe.

+

Here is an example of code that produces a violation:

     new ThreadGroup("...")
     new ThreadGroup(tg, "my thread group")
     Thread.currentThread().getThreadGroup()
     System.getSecurityManager().getThreadGroup()
-
-]]>
+]]>
multi-threading @@ -1142,16 +1460,15 @@ MINOR - This rule reports definition of the ThreadLocal fields that are not static and final.

-

fields should be static and final. In the most common case a java.lang.ThreadLocal instance associates state with a thread. A non-static non-final java.lang.ThreadLocal field associates state with an instance-thread combination. This is seldom necessary and often a bug which can cause memory leaks and possibly incorrect behavior.

-

Here is an example of code that produces a violation:

+ This rule reports definition of the ThreadLocal fields that are not static and final.

+

ThreadLocal fields should be static and final. In the most common case a java.lang.ThreadLocal instance associates state with a thread. A non-static non-final java.lang.ThreadLocal field associates state with an instance-thread combination. This is seldom necessary and often a bug which can cause memory leaks and possibly incorrect behavior.

+

Here is an example of code that produces a violation:

     private static ThreadLocal local1 = new ThreadLocal()
     private final ThreadLocal local2 = new ThreadLocal()
     protected ThreadLocal local3 = new ThreadLocal()
     ThreadLocal local4 = new ThreadLocal()
-
-]]>
+]]>
multi-threading @@ -1160,15 +1477,14 @@ MINOR - This rule reports uses of the Thread.yield() method.

-

Method calls to Thread.yield() should not be allowed. This method has no useful guaranteed semantics, and is often used by inexperienced programmers to mask race conditions.

-

Here is an example of code that produces a violation:

+ This rule reports uses of the Thread.yield() method.

+

Method calls to Thread.yield() should not be allowed. This method has no useful guaranteed semantics, and is often used by inexperienced programmers to mask race conditions.

+

Here is an example of code that produces a violation:

      def method() {
          Thread.yield()
      }
-
-]]>
+]]>
multi-threading @@ -1177,16 +1493,15 @@ MINOR - This rule reports on long or double fields that are declared volatile.

-

Long or double fields should not be declared as volatile. Java specifies that reads and writes from such fields are atomic, but many JVM's have violated this specification. Unless you are certain of your JVM, it is better to synchronize access to such fields rather than declare them volatile. This rule flags fields marked volatile when their type is double or long or the name of their type is "Double" or "Long".

-

Here is an example of code that produces a violation:

+ This rule reports on long or double fields that are declared volatile.

+

Long or double fields should not be declared as volatile. Java specifies that reads and writes from such fields are atomic, but many JVM's have violated this specification. Unless you are certain of your JVM, it is better to synchronize access to such fields rather than declare them volatile. This rule flags fields marked volatile when their type is double or long or the name of their type is "Double" or "Long".

+

Here is an example of code that produces a violation:

      def method() {
          private volatile double d
          private volatile long f
      }
-
-]]>
+]]>
multi-threading @@ -1196,8 +1511,7 @@ MINOR - Checks for synchronization on getClass() rather than class literal. This instance method synchronizes on this.getClass(). If this class is subclassed, subclasses will synchronize on the class object for the subclass, which isn't likely what was intended.

- ]]>
+ Checks for synchronization on getClass() rather than class literal. This instance method synchronizes on this.getClass(). If this class is subclassed, subclasses will synchronize on the class object for the subclass, which isn't likely what was intended.

]]>
multi-threading @@ -1207,9 +1521,8 @@ MINOR - Checks for code that calls notify() rather than notifyAll(). Java monitors are often used for multiple conditions. Calling notify() only wakes up one thread, meaning that the awakened thread might not be the one waiting for the condition that the caller just satisfied.

-

Also see Java_Concurrency_in_Practice, Brian Goetz, p 303.

- ]]>
+ Checks for code that calls notify() rather than notifyAll(). Java monitors are often used for multiple conditions. Calling notify() only wakes up one thread, meaning that the awakened thread might not be the one waiting for the condition that the caller just satisfied.

+

Also see Java_Concurrency_in_Practice, Brian Goetz, p 303.

]]>
multi-threading @@ -1219,8 +1532,8 @@ MINOR - Busy waiting (forcing a Thread.sleep() while waiting on a condition) should be avoided. Prefer using the gate and barrier objects in the java.util.concurrent package.

-

Example of violations:

+ Busy waiting (forcing a Thread.sleep() while waiting on a condition) should be avoided. Prefer using the gate and barrier objects in the java.util.concurrent package.

+

Example of violations:

     while (x) { Thread.sleep(1000) }
     while (x) { Thread.sleep(1000) { /* interruption handler */} }
@@ -1242,8 +1555,7 @@
         doSomething()
         sleep(1000)
     }
-
-]]>
+]]>
multi-threading @@ -1253,9 +1565,9 @@ MINOR - This rule detects double checked locking, where a 'lock hint' is tested for null before initializing an object within a synchronized block. Double checked locking does not guarantee correctness and is an anti-pattern.

-

A full explanation of why double checked locking is broken in Java is available on Wikipedia: http://en.wikipedia.org/wiki/Double-checked_locking

-

Example of violations:

+ This rule detects double checked locking, where a 'lock hint' is tested for null before initializing an object within a synchronized block. Double checked locking does not guarantee correctness and is an anti-pattern.

+

A full explanation of why double checked locking is broken in Java is available on Wikipedia: http://en.wikipedia.org/wiki/Double-checked_locking

+

Example of violations:

     if (object == null) {
         synchronized(this) {
@@ -1286,8 +1598,7 @@
             return ObjectHolder.object;
         }
     }
-
-]]>
+]]>
multi-threading @@ -1297,8 +1608,8 @@ MINOR - Class contains similarly-named get and set methods where one method of the pair is marked either @WithReadLock or @WithWriteLock and the other is not locked at all. This may result in incorrect behavior at runtime, as callers of the get and set methods will not necessarily lock correctly and my see an inconsistent state for the object. The get and set method should both be guarded by @WithReadLock/@WithWriteLock or neither should be guarded.

-

Example of violations:

+ Class contains similarly-named get and set methods where one method of the pair is marked either @WithReadLock or @WithWriteLock and the other is not locked at all. This may result in incorrect behavior at runtime, as callers of the get and set methods will not necessarily lock correctly and my see an inconsistent state for the object. The get and set method should both be guarded by @WithReadLock/@WithWriteLock or neither should be guarded.

+

Example of violations:

     class Person {
         String name
@@ -1341,8 +1652,7 @@
             parent
         }
     }
-
-]]>
+]]>
multi-threading @@ -1352,8 +1662,8 @@ MINOR - Class contains similarly-named get and set methods where the set method is synchronized and the get method is not, or the get method is synchronized and the set method is not. This may result in incorrect behavior at runtime, as callers of the get and set methods will not necessarily see a consistent state for the object. The get and set method should both be synchronized or neither should be synchronized.

-

Example of violations:

+ Class contains similarly-named get and set methods where the set method is synchronized and the get method is not, or the get method is synchronized and the set method is not. This may result in incorrect behavior at runtime, as callers of the get and set methods will not necessarily see a consistent state for the object. The get and set method should both be synchronized or neither should be synchronized.

+

Example of violations:

     class Person {
         String name
@@ -1403,8 +1713,7 @@
             weight = value
         }
     }
-
-]]>
+]]>
multi-threading @@ -1414,9 +1723,9 @@ MINOR - Calendar objects should not be used as static fields. Calendars are inherently unsafe for multithreaded use. Sharing a single instance across thread boundaries without proper synchronization will result in erratic behavior of the application. Under 1.4 problems seem to surface less often than under Java 5 where you will probably see random ArrayIndexOutOfBoundsException or IndexOutOfBoundsException in sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(). You may also experience serialization problems. Using an instance field or a ThreadLocal is recommended.

-

For more information on this see Sun Bug #6231579 and Sun Bug #6178997.

-

Examples:

+ Calendar objects should not be used as static fields. Calendars are inherently unsafe for multithreaded use. Sharing a single instance across thread boundaries without proper synchronization will result in erratic behavior of the application. Under 1.4 problems seem to surface less often than under Java 5 where you will probably see random ArrayIndexOutOfBoundsException or IndexOutOfBoundsException in sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(). You may also experience serialization problems. Using an instance field or a ThreadLocal is recommended.

+

For more information on this see Sun Bug #6231579 and Sun Bug #6178997.

+

Examples:

     // Violations
     class MyClass {
@@ -1432,10 +1741,9 @@
     // These usages are OK
     class MyCorrectClass {
         private final Calendar calendar1
-        static ThreadLocal calendar2
+        static ThreadLocal<Calendar> calendar2
     }
-
-]]>
+]]>
multi-threading @@ -1445,9 +1753,9 @@ MINOR - DateFormat objects should not be used as static fields. DateFormats are inherently unsafe for multithreaded use. Sharing a single instance across thread boundaries without proper synchronization will result in erratic behavior of the application. Under 1.4 problems seem to surface less often than under Java 5 where you will probably see random ArrayIndexOutOfBoundsException or IndexOutOfBoundsException in sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(). You may also experience serialization problems. Using an instance field or a ThreadLocal is recommended.

-

For more information on this see Sun Bug #6231579 and Sun Bug #6178997.

-

Examples:

+ DateFormat objects should not be used as static fields. DateFormats are inherently unsafe for multithreaded use. Sharing a single instance across thread boundaries without proper synchronization will result in erratic behavior of the application. Under 1.4 problems seem to surface less often than under Java 5 where you will probably see random ArrayIndexOutOfBoundsException or IndexOutOfBoundsException in sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(). You may also experience serialization problems. Using an instance field or a ThreadLocal is recommended.

+

For more information on this see Sun Bug #6231579 and Sun Bug #6178997.

+

Examples:

     // Violations
     class MyClass {
@@ -1470,10 +1778,9 @@
     // These usages are OK
     class MyCorrectClass {
         private DateFormat calendar1
-        static ThreadLocal calendar2
+        static ThreadLocal<DateFormat> calendar2
     }
-
-]]>
+]]>
multi-threading @@ -1483,8 +1790,8 @@ MINOR - Matcher objects should not be used as static fields. Calendars are inherently unsafe for multithreaded use. Sharing a single instance across thread boundaries without proper synchronization will result in erratic behavior of the application.

-

Example of violations:

+ Matcher objects should not be used as static fields. Calendars are inherently unsafe for multithreaded use. Sharing a single instance across thread boundaries without proper synchronization will result in erratic behavior of the application.

+

Example of violations:

     // two violations
     class MyClass {
@@ -1495,10 +1802,9 @@
     // these usages are OK
     class MyCorrectClass {
       private Matcher matcher1
-      static ThreadLocal matcher2
+      static ThreadLocal<Matcher> matcher2
     }
-
-]]>
+]]>
multi-threading @@ -1508,8 +1814,8 @@ MINOR - The code synchronizes on a boxed primitive constant, such as an Integer. Since Integer objects can be cached and shared, this code could be synchronizing on the same object as other, unrelated code, leading to unresponsiveness and possible deadlock.

-

Example of violations:

+ The code synchronizes on a boxed primitive constant, such as an Integer. Since Integer objects can be cached and shared, this code could be synchronizing on the same object as other, unrelated code, leading to unresponsiveness and possible deadlock.

+

Example of violations:

     class MyClass {
         Byte byte1 = 100
@@ -1612,6 +1918,7 @@
         }
     }
 
+

And here is an in-depth example of how it works within inner classes and such:

     class MyClass {
 
@@ -1673,8 +1980,7 @@
             }
         }
     }
-
-]]>
+]]>
multi-threading @@ -1684,9 +1990,9 @@ MINOR - Synchronization on a String field can lead to deadlock. Constant Strings are interned and shared across all other classes loaded by the JVM. Thus, this could is locking on something that other code might also be locking. This could result in very strange and hard to diagnose blocking and deadlock behavior.

-

See JETTY-352.

-

Examples:

+ Synchronization on a String field can lead to deadlock. Constant Strings are interned and shared across all other classes loaded by the JVM. Thus, this could is locking on something that other code might also be locking. This could result in very strange and hard to diagnose blocking and deadlock behavior.

+

See JETTY-352.

+

Examples:

     class MyClass {
 
@@ -1747,8 +2053,7 @@
             }
         }
     }
-
-]]>
+]]>
multi-threading @@ -1758,8 +2063,8 @@ MINOR - Catches Serializable classes that define a synchronized readObject method. By definition, an object created by deserialization is only reachable by one thread, and thus there is no need for readObject() to be synchronized. If the readObject() method itself is causing the object to become visible to another thread, that is an example of very dubious coding style.

-

Examples:

+ Catches Serializable classes that define a synchronized readObject method. By definition, an object created by deserialization is only reachable by one thread, and thus there is no need for readObject() to be synchronized. If the readObject() method itself is causing the object to become visible to another thread, that is an example of very dubious coding style.

+

Examples:

     class MyClass implements Serializable {
 
@@ -1799,8 +2104,7 @@
             doSomething()
         }
     }
-
-]]>
+]]>
multi-threading @@ -1810,9 +2114,9 @@ MINOR - Synchronizing on a ReentrantLock field is almost never the intended usage. A ReentrantLock should be obtained using the lock() method and released in a finally block using the unlock() method.

-

This rule take from Alex Miller's Java Concurrency in Practice slides.

-

Here is the proper usage of ReentrantLock:

+ Synchronizing on a ReentrantLock field is almost never the intended usage. A ReentrantLock should be obtained using the lock() method and released in a finally block using the unlock() method.

+

This rule take from Alex Miller's Java Concurrency in Practice slides.

+

Here is the proper usage of ReentrantLock:

     import java.util.concurrent.locks.ReentrantLock;
     final lock = new ReentrantLock();
@@ -1827,6 +2131,7 @@
        }
     }
 
+

Example of violations:

     class MyClass {
 
@@ -1887,8 +2192,7 @@
             }
         }
     }
-
-]]>
+]]>
multi-threading @@ -1898,17 +2202,16 @@ MINOR - Volatile array fields are unsafe because the contents of the array are not treated as volatile. Changing the entire array reference is visible to other threads, but changing an array element is not.

-

This rule take from Alex Miller's slides, available at http://www.slideshare.net/alexmiller/java-concurrency-gotchas-3666977.

-

Example of violations:

+ Volatile array fields are unsafe because the contents of the array are not treated as volatile. Changing the entire array reference is visible to other threads, but changing an array element is not.

+

This rule take from Alex Miller's Java Concurrency in Practice slides, available at http://www.slideshare.net/alexmiller/java-concurrency-gotchas-3666977.

+

Example of violations:

     class MyClass {
         private volatile Object[] field1 = value()
         volatile field2 = value as Object[]
         volatile field3 = (Object[])foo
     }
-
-]]>
+]]>
multi-threading @@ -1918,9 +2221,9 @@ MINOR - Calls to Object.wait() must be within a while loop. This ensures that the awaited condition has not already been satisfied by another thread before the wait() is invoked. It also ensures that the proper thread was resumed and guards against incorrect notification. See [1] and [3].

-

As a more modern and flexible alternative, consider using the Java instead of wait() and notify(). See discussion in [2].

-

Example of violation:

+ Calls to Object.wait() must be within a while loop. This ensures that the awaited condition has not already been satisfied by another thread before the wait() is invoked. It also ensures that the proper thread was resumed and guards against incorrect notification. See [1] and [3].

+

As a more modern and flexible alternative, consider using the Java concurrency utilities instead of wait() and notify(). See discussion in Effective Java [2].

+

Example of violation:

     class MyClass {
         private data
@@ -1935,6 +2238,7 @@
         }
     }
 
+

Example of correct usage:

     class MyClass {
         private data
@@ -1949,7 +2253,9 @@
         }
     }
 
-]]>
+

* [1] Effective Java, Programming Language Guide, by Joshua Bloch. Addison Wesley (2001). Chapter 50 (1st edition) is entitled "Never invoke wait outside a loop."

+

* [2] Effective Java, 2nd edition, by Joshua Bloch, Addison Wesley (2008). Item #69: Prefer concurrency utilities to wait and notify.

+

* [3] Software Engineering Institute - Secure Coding discussion of this issue

]]>
multi-threading @@ -1959,10 +2265,14 @@ MINOR - Creates violations when a java.sql.Connection object is used as a static field. Database connections stored in static fields will be shared between threads, which is unsafe and can lead to race conditions.

-

A transactional resource object such as database connection can only be associated with one transaction at a time. For this reason, a connection should not be shared between threads and should not be stored in a static field. See Section 4.2.3 of the for more details.

-

References:

-]]>
+ Creates violations when a java.sql.Connection object is used as a static field. Database connections stored in static fields will be shared between threads, which is unsafe and can lead to race conditions.

+

A transactional resource object such as database connection can only be associated with one transaction at a time. For this reason, a connection should not be shared between threads and should not be stored in a static field. See Section 4.2.3 of the J2EE Specification for more details.

+

References:

+

* Standards Mapping - Security Technical Implementation Guide Version 3 - (STIG 3) APP3630.1 CAT II

+

* Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 362, CWE ID 567

+

* Standards Mapping - SANS Top 25 2009 - (SANS 2009) Insecure Interaction - CWE ID 362

+

* Standards Mapping - SANS Top 25 2010 - (SANS 2010) Insecure Interaction - CWE ID 362

+

* Java 2 Platform Enterprise Edition Specification, v1.4 Sun Microsystems

]]>
multi-threading @@ -1972,9 +2282,9 @@ MINOR - SimpleDateFormat objects should not be used as static fields. SimpleDateFormats are inherently unsafe for multithreaded use. Sharing a single instance across thread boundaries without proper synchronization will result in erratic behavior of the application. Under 1.4 problems seem to surface less often than under Java 5 where you will probably see random ArrayIndexOutOfBoundsException or IndexOutOfBoundsException in sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(). You may also experience serialization problems. Using an instance field or a ThreadLocal is recommended.

-

For more information on this see Sun Bug #6231579 and Sun Bug #6178997.

-

Examples:

+ SimpleDateFormat objects should not be used as static fields. SimpleDateFormats are inherently unsafe for multithreaded use. Sharing a single instance across thread boundaries without proper synchronization will result in erratic behavior of the application. Under 1.4 problems seem to surface less often than under Java 5 where you will probably see random ArrayIndexOutOfBoundsException or IndexOutOfBoundsException in sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(). You may also experience serialization problems. Using an instance field or a ThreadLocal is recommended.

+

For more information on this see Sun Bug #6231579 and Sun Bug #6178997.

+

Examples:

     // Violations
     class MyClass {
@@ -1991,10 +2301,9 @@
     // These usages are OK
     class MyCorrectClass {
         private SimpleDateFormat calendar1
-        static ThreadLocal calendar2
+        static ThreadLocal<SimpleDateFormat> calendar2
     }
-
-]]>
+]]>
multi-threading @@ -2004,3826 +2313,4484 @@ MINOR - Reports constructors passing the 'this' reference to other methods. This equals exposing a half-baked objects and can lead to race conditions during initialization. For reference, see Java Concurrency in Practice by Alex Miller and Java theory and practice: Safe construction techniques by Brian Goetz.

-

Example of violations:

+ Reports constructors passing the 'this' reference to other methods. This equals exposing a half-baked objects and can lead to race conditions during initialization. For reference, see Java Concurrency in Practice by Alex Miller and Java theory and practice: Safe construction techniques by Brian Goetz.

+

Example of violations:

     class EventListener {
         EventListener(EventPublisher publisher) {
-            publisher.register(this)            
+            publisher.register(this)
             new WorkThread(publisher, this).start()
             new AnotherWorkThread(listener: this)
-        }    
+        }
     }
-
-]]>
+]]>
multi-threading - - - - org.codenarc.rule.design.CloneableWithoutCloneRule - MINOR - - - Checks for classes that implement the java.lang.Cloneable interface without implementing the clone() method.

-

Here is an example of code that produces a violation:

-
-    class BadClass implements Cloneable {
-        def someMethod()
-    }
-
-]]>
- design -
+ + - org.codenarc.rule.design.ImplementationAsTypeRule - MINOR - - - Checks for use of the following concrete classes when specifying the type of a method parameter, closure parameter, constructor parameter, method return type or field type. The corresponding interfaces should be used to specify the type instead.

-]]>
- design + org.codenarc.rule.convention.InvertedIfElseRule + MAJOR + + + An inverted if-else statement is one in which there is a single if statement with a single else branch and the boolean test of the if is negated. For instance if (!x) false else true. It is usually clearer to write this as if (x) true else false.

]]>
+ bug
- + - org.codenarc.rule.design.BooleanMethodReturnsNullRule - MINOR - - - Checks for a method with Boolean return type that returns an explicit null. A method that returns either Boolean.TRUE, Boolean.FALSE or null is an accident waiting to happen. This method can be invoked as though it returned a value of type boolean, and the compiler will insert automatic of the Boolean value. If a null value is returned, this will result in a NullPointerException.

- ]]>
- design -
+ org.codenarc.rule.convention.ConfusingTernaryRule + MAJOR + + + In a ternary expression avoid negation in the test. For example, rephrase: (x != y) ? diff : same as: (x == y) ? same : diff. Consistent use of this rule makes the code easier to read. Also, this resolves trivial ordering problems, such as "does the error case go first?" or "does the common case go first?".

+

Example:

+
+    (x != y) ? diff : same      // triggers violation
+    (!x) ? diff : same          // triggers violation
 
-  
-  
-    org.codenarc.rule.design.ReturnsNullInsteadOfEmptyArrayRule
-    MINOR
-    
-    
-    If you have a method or closure that returns an array, then when there are no results return a zero-length (empty) array rather than null. It is often a better design to return a zero-length array rather than a null reference to indicate that there are no results (i.e., an  list of results). This way, no explicit check for null is needed by clients of the method. 

- ]]>
- design -
+ (x == y) ? same : diff // OK + (x) ? same : diff // OK - - - org.codenarc.rule.design.ReturnsNullInsteadOfEmptyCollectionRule - MINOR - - - If you have a method or closure that returns a collection, then when there are no results return a zero-length (empty) collection rather than null. It is often a better design to return a zero-length collection rather than a null reference to indicate that there are no results (i.e., an list of results). This way, no explicit check for null is needed by clients of the method.

- ]]>
- design + // this is OK, because of GroovyTruth there is no inverse of != null + (x != null) ? diff : same + + // this is OK, because of GroovyTruth there is no inverse of != true + (x != true) ? diff : same + + // this is OK, because of GroovyTruth there is no inverse of != false + (x != false) ? diff : same +
]]>
+ bug - + - org.codenarc.rule.design.CompareToWithoutComparableRule - MINOR - - - If you implement a compareTo method then you should also implement the Comparable interface. If you don't then you could possibly get an exception if the Groovy == operator is invoked on your object. This is an issue fixed in Groovy 1.8 but present in previous versions.

-

Here is an example of code that produces a violation:

+ org.codenarc.rule.convention.CouldBeElvisRule + MAJOR + + + Catch an if block that could be written as an elvis expression.

+

Example of violations:

-    class BadClass {
-        int compareTo(Object o) { ... }
+    if (!x) {                   // violation
+        x = 'some value'
     }
-
-]]>
- design -
- - - org.codenarc.rule.design.SimpleDateFormatMissingLocaleRule - MINOR - - - Be sure to specify a Locale when creating a new instance of SimpleDateFormat; the class is locale-sensitive. If you instantiate SimpleDateFormat without a Locale parameter, it will format the date and time according to the default Locale. Both the pattern and the Locale determine the format. For the same pattern, SimpleDateFormat may format a date and time differently if the Locale varies.

-
-    // violation, missing locale
-    new SimpleDateFormat('pattern')
+    if (!x)                     // violation
+        x = "some value"
 
-    // OK, includes locale
-    new SimpleDateFormat('pattern', Locale.US)
+    if (!params.max) {          // violation
+      params.max = 10
+    }
 
-    // OK, includes a variable that perhaps is a locale
-    new SimpleDateFormat('pattern', locale)
-
-]]>
- design + x ?: 'some value' // OK +]]> + bug
- + - org.codenarc.rule.design.AbstractClassWithoutAbstractMethodRule + org.codenarc.rule.convention.LongLiteralWithLowerCaseLRule MINOR - - - The abstract class does not contain any abstract methods. An abstract class suggests an incomplete implementation, which is to be completed by subclasses implementing the abstract methods. If the class is intended to be used as a base class only (not to be instantiated directly) a protected constructor can be provided prevent direct instantiation.

-

Example:

-
-    public abstract class MyBaseClass {
-        void method1() {  }
-        void method2() {  }
-        // consider using abstract methods or removing
-        // the abstract modifier and adding protected constructors
-    }
-
+ + + In Java and Groovy, you can specify long literals with the L or l character, for instance 55L or 24l. It is best practice to always use an uppercase L and never a lowercase l. This is because 11l rendered in some fonts may look like 111 instead of 11L.

+

Example of violations:

-    abstract class MyClass extends AbstractParent {
-        // OK because parent is named Abstract.*
-    }
-    abstract class MyClass extends BaseParent{
-        // OK because parent is named Base.*
-    }
-
-]]>
- design -
- - - - org.codenarc.rule.design.CloseWithoutCloseableRule - MINOR - - - If a class defines a "void close()" then that class should implement java.io.Closeable.

- ]]>
- design + def x = 1l + def y = 55l +]]> + bug
- + - org.codenarc.rule.design.ConstantsOnlyInterfaceRule - MINOR - - - An interface should be used only to model a behaviour of a class: using an interface as a container of constants is a poor usage pattern. Example:

+ org.codenarc.rule.convention.ParameterReassignmentRule + MAJOR + + + Checks for a method or closure parameter being reassigned to a new value within the body of the method/closure, which is a confusing and questionable practice. Use a temporary variable instead.

+

Example of violations:

-    public interface ConstantsInterface {
-        public static final int CONSTANT_1 = 0
-        public static final String CONSTANT_2 = "1"
+    void myMethod(int a, String b) {
+        println a
+        b = 'new value'     // violation
     }
-
-]]>
- design + + def myClosure1 = { int a, b -> + a = 123 // violation + } +]]>
+ bug
- + - org.codenarc.rule.design.EmptyMethodInAbstractClassRule - MINOR - - - An empty method in an abstract class should be abstract instead, as developer may rely on this empty implementation rather than code the appropriate one.

+ org.codenarc.rule.convention.TernaryCouldBeElvisRule + MAJOR + + + Checks for ternary expressions where the boolean and true expressions are the same. These can be simplified to an Elvis expression.

+

Example of violations:

-    abstract class MyClass {
-        def couldBeAbstract_1() {
-            return null  // Should be abstract method
-        }
+    x ? x : false               // violation; can simplify to x ?: false
 
-        void couldBeAbstract_2() {
-            // Should be abstract method
-        }
-    }
+    foo() ? foo() : bar()       // violation; can simplify to foo() ?: bar()
+    foo(1) ? foo(1) : 123       // violation; can simplify to foo(1) ?: 123
+
+    (x == y) ? same : diff      // OK
+    x ? y : z                   // OK
+    x ? x + 1 : x + 2           // OK
+    x ? 1 : 0                   // OK
+    x ? !x : x                  // OK
+    !x ? x : null               // OK
+
+    foo() ? bar() : 123         // OK
+    foo() ? foo(99) : 123       // OK
+    foo(x) ? foo() : 123        // OK
+    foo(1) ? foo(2) : 123       // OK
 
-]]>
- design +

NOTE: If the boolean and true expressions are the same method call, and that method call has side-effects, then converting it to a Elvis expression may produce different behavior. The method will only be called once, rather than twice. But relying on those side-effects as part of a ternary expression behavior is confusing, error-prone and just a bad idea. In any case, that code should be refactored to move the reliance on the side-effects out of the ternary expression.

]]>
+ bug
- + - org.codenarc.rule.design.FinalClassWithProtectedMemberRule + org.codenarc.rule.convention.VectorIsObsoleteRule MINOR - - - This rule finds classes marked final that contain protected members. If a class is final then it may not be subclassed, and there is therefore no point in having a member with protected visibility. Either the class should not be final or the member should be private or protected.

- ]]>
- design + + + Checks for references to the (effectively) obsolete java.util.Vector class. Use the Java Collections Framework classes instead, including ArrayList or Collections.synchronizedList(). See the JDK javadoc.

+

Example of violations:

+
+    def myList = new Vector()           // violation
+
]]>
+ bug
- + - org.codenarc.rule.design.PublicInstanceFieldRule + org.codenarc.rule.convention.HashtableIsObsoleteRule MINOR - - - Using public fields is considered to be a bad design. Use properties instead.

-

Example of violations:

+ + + Checks for references to the (effectively) obsolete java.util.Hashtable class. Use the Java Collections Framework classes instead, including HashMap or ConcurrentHashMap. See the JDK javadoc.

+

Example of violations:

-    class Person {
-        public String name
-    }
-
-]]>
- design + def myMap = new Hashtable() // violation +]]>
+ bug
- + - org.codenarc.rule.design.StatelessSingletonRule + org.codenarc.rule.convention.IfStatementCouldBeTernaryRule MINOR - - - There is no point in creating a stateless Singleton because there is nothing within the class that needs guarding and no side effects to calling the constructor. Just create new instances of the object or write a Utility class with static methods. In the long term, Singletons can cause strong coupling and hard to change systems.

-

If the class has any fields at all, other than a self reference, then it is not considered stateless. A self reference is a field of the same type as the enclosing type, or a field named instance or _instance. The field name self reference is a property named instanceRegex that defaults to the value 'instance|_instance'

-

Example of violations:

+ + + Checks for:

+

* An if statement where both the if and else blocks contain only a single return statement returning a constant or literal value.

+

* A block where the second-to-last statement in a block is an if statement with no else, where the block contains a single return statement, and the last statement in the block is a return statement, and both return statements return a constant or literal value. This check is disabled by setting checkLastStatementImplicitElse to false.

+

Example of violations:

-    @groovy.lang.Singleton
-    class Service {
-       // violation: the class has no fields but is marked Singleton
-        void processItem(item){
-        }
-    }
+    if (condition) { return 44 } else { return 'yes' }                  // violation
+    if (check()) { return [1, 2] } else { return "count=$count" }       // violation
 
-    class Service {
-       // violation: the class has no fields other than 'instance' but is marked Singleton
-        static instance
-        void processItem(item){
-        }
-    }
+    if (condition)                                                      // violation
+        return null
+    else return [a:1]
 
-    class Service {                                       // violation
-        static Service service
-        void processItem(item){
+    def method1() {
+        if (condition) {                                                // violation
+            return 44
         }
+        return 'yes'
     }
-
-]]>
- design +]]>
+ bug
- + - org.codenarc.rule.design.AbstractClassWithPublicConstructorRule - MINOR - - - Checks for abstract classes that define a public constructor, which is useless and confusing.

-

The following code produces a violation:

-
-    abstract class MyClass {
-        MyClass() { }
-    }
-
-]]>
- design + org.codenarc.rule.convention.NoDefRule + MAJOR + + + Do not allow using the def keyword in code. Use a specific type instead.

]]>
+ bug + + excludeRegex + +
- + - org.codenarc.rule.design.BuilderMethodWithSideEffectsRule - MINOR - - - A builder method is defined as one that creates objects. As such, they should never be of void return type. If a method is named build, create, or make, then it should always return a value.

-

This rule has one property: methodNameRegex. The default value is (make.*|create.*|build.*). Update this property if you have some other naming convention for your builder methods.

-

Example of violations:

+ org.codenarc.rule.convention.TrailingCommaRule + MAJOR + + + Check whether list and map literals contain optional trailing comma. Rationale: Putting this comma in make is easier to change the order of the elements or add new elements on the end.

+

This is valid code:

-
-    class MyClass {
-
-            void make() { /* ... */ }
-            void makeSomething() { /* ... */ }
-
-            void create() { /* ... */ }
-            void createSomething() { /* ... */ }
-
-            void build() { /* ... */ }
-            void buildSomething() { /* ... */ }
-    }
+  int[] array1 = [] // one line declaration
+  int[] array2 = [ // empty list
+                 ]
+  int[] array3 = [1,2,3] // one line declaration
+  int[] array4 = [1,
+                  2,
+                  3, // contains trailing comma
+                 ]
 
-]]>
- design -
- - - - org.codenarc.rule.design.PrivateFieldCouldBeFinalRule.fixed - MAJOR - - - This rule finds private fields that are only set within a or . Such fields can safely be made final.

-]]>
- design +

Example of violations:

+
+  int[] array2 = [1,
+                  2 // there is no trailing comma
+                 ]
+
]]> + bug - ignoreFieldNames - + checkList + + true - ignoreJpaEntities - - false + checkMap + + true + + + ignoreSingleElementList + + true + + + ignoreSingleElementMap + + true
- + - org.codenarc.rule.design.CloneWithoutCloneableRule - MINOR - - - The method clone() should only be declared if the class implements the Cloneable interface.

-

NOTE: This is a CodeNarc Enhanced Classpath Rule. It requires CodeNarc to have the application classes being analyzed, as well as any referenced classes, on the classpath.

-

Example of violations:

-
-    class ValueClass {
-        ValueClass clone() {
-        }
-    }
-
-]]>
- design + org.codenarc.rule.convention.NoTabCharacterRule + MAJOR + + + Checks that all source files do not contain the tab character.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ bug
- + - org.codenarc.rule.design.LocaleSetDefaultRule - MINOR - - - Checks for calls to Locale.setDefault(), or Locale.default = Xxx, which sets the Locale across the entire JVM. That can impact other applications on the same web server, for instance.

-

From the java.util.Locale javadoc for setDefault: should only be used if the caller is prepared to reinitialize locale-sensitive code running within the same Java Virtual Machine.>

-

Example of violations:

-
-    Locale.setDefault(Locale.UK)                                // violation
-    java.util.Locale.setDefault(Locale.FRANCE)                  // violation
-    Locale.setDefault(Locale.Category.DISPLAY, Locale.JAPAN)    // violation
-
-    Locale.default = Locale.UK                                  // violation
-
-]]>
- design -
+ org.codenarc.rule.convention.CouldBeSwitchStatementRule + MAJOR + + + Checks for three of more if statements that could be converted to a switch. Only applies to equality and instanceof.

+

Example of violations:

+
+    if (x == 1) {                       // violation
+       y = x
+    } else if (x == 2) {
+       y = x * 2
+    } else if (x == 3) {
+       y = x * 3
+    } else {
+       y = 0
+    }
 
-  
-  
-    org.codenarc.rule.design.ToStringReturnsNullRule
-    MINOR
-    
-    
-    Checks for toString() methods that return null. This is unconventional and could cause unexpected NullPointerExceptions from normal or implicit use of toString(). 

-

Example of violations:

-
-    class MyClass {
-        String toString() {
-            if (foo()) {
-                return 'MyClass'
-            } else {
-                return null         // violation
-            }
-        }
+    if (y instanceof Integer) {         // violation
+       x = y + 1
+    }
+    if (y instanceof String) {
+       x = y + '1'
+    } else if (y instanceof Boolean) {
+       x = !y
+    } else {
+       x = null
     }
 
-    class MyClass {
-        String toString() {
-            calculateStuff()
-            null                    // violation
-        }
+    if (x == 1) {                       // OK
+        y = x
+    }
+    if (x == 2) {
+        y = x * 2
+    } else {
+        y = 0
     }
 
-    class MyClass {
-        String toString() {         // violation - implicit return of null
-        }
+    if (!x && y) {                      // OK
+        doSomething()
+    } else if (!x && z) {
+        doSomethingElse()
+    } else if (!x && i) {
+        doAnotherThing()
     }
-
-]]>
- design + +
]]>
+ bug - + - org.codenarc.rule.design.InstanceofRule - MINOR - - - Checks for use of the instanceof operator. Prefer using instead.

-

Use the ignoreTypeNames property to configure ignored type names (the class name specified as the right-hand expression of the instanceof). It defaults to ignoring instanceof checks against exception classes.

-

Here are a couple references that discuss the problems with using instanceof and the preference for using instead:

-

* Beware of instanceof operator

-

* How does one use polymorphism instead of instanceof? (And why?)

-

By default, the rule does not analyze test files. This rule sets the default value of the property to ignore file names ending in 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

-

Example of violations:

+ org.codenarc.rule.convention.FieldTypeRequiredRule + MAJOR + + + Checks that field types are explicitly specified (and not using def).

+

Example of violations:

     class MyClass {
-        boolean isRunnable = this instanceof Runnable       // violation
+        public static final NAME = "joe"        // violation
+        private static count = 0                // violation
+
+        private def name = NAME                 // violation
+        protected final date = new Date()       // violation
+
+        def defaultName                         // violation
+        def maxSoFar = -1L                      // violation
     }
-
-]]>
- design +]]>
+ bug - ignoreTypeNames - - *Exceptions + ignoreFieldNames +
- + - org.codenarc.rule.design.NestedForLoopRule + org.codenarc.rule.convention.InvertedConditionRule MAJOR - - - Reports classes with nested for loops.

-

Example of violations:

+ + + An inverted condition is one where a constant expression is used on the left hand side of the equals comparision. Such conditions can be confusing especially when used in assertions where the expected value is by convention placed on the right hand side of the comparision.

+

Example of violations:

-for (int i = 0; i < 100; ++i) {
-    for (int j = 0; j < 100; ++j) { // violation
-        println i + j
+    boolean isTenCharactersLong(String value) {
+        10 == value.size()  // violation
     }
-}
+
]]>
+ bug +
-for (int i = 0; i < 100; ++i) { - for (int j = 0; j < 100; ++j) { // violation - println i + j + + + org.codenarc.rule.convention.MethodReturnTypeRequiredRule + MAJOR + + + Checks that method return types are not dynamic, that is they are explicitly stated and different than def.

+

Example of violations:

+
+    def methodWithDynamicReturnType() {    // violation
     }
-    for (int j = 0; j < 100; ++j) { // violation
-        println i + j
+
+    private methodWithoutReturnType() {    // violation
     }
-}
 
-for (int i = 0; i < 100; ++i) {
-    for (int j = 0; j < 100; ++j) { // violation
-        for (int k = 0; k < 100; ++k) { // violation
-            println i + j + k
-        }
+    Object objectReturningMethod() {       // OK
     }
-}
-
-]]>
- design +]]> + bug
- + - org.codenarc.rule.design.AssignmentToStaticFieldFromInstanceMethodRule - MINOR - - - Checks for assignment to a static field from an instance method.

-

Influenced by the AssignmentToNonFinalStatic rule from PMD, and the ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD rule from FindBugs.

-

Example of violations:

+ org.codenarc.rule.convention.VariableTypeRequiredRule + MAJOR + + + Checks that variable types are explicitly specified in declarations (and not using def).

+

Example of violations:

     class MyClass {
-        private static field1
-        protected static String field2 = 'abc'
-        public static int field3 = 123
-        static String property1 = 'abc'
-        private static final NAME = 'joe'
-
-        private void doStuff() {
-            field1 = new Object()       // violation
-            field2 = 'xxx'              // violation
-            field3 = 999                // violation
-            property1 = 'xxx'           // violation
-
-            final NAME = 'martin'       // no violation; local var hides static field
+        void doStuff() {
+            final NAME = "joe"          // violation
+            def count = 0, max = 99     // violation
+            def defaultName             // violation
         }
     }
-
-]]>
- design -
- - - - - - org.codenarc.rule.dry.DuplicateNumberLiteralRule - MINOR - - - This rule checks for duplicate number literals within the current class.

-

Code containing duplicate literals can usually be improved by declaring the as a constant field.

-

By default, the rule does not analyze test files. This rule sets the default value of the property to ignore file names ending in 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

- ]]>
- bug - - ignoreNumbers - - 0,1 - -
- - - - org.codenarc.rule.dry.DuplicateStringLiteralRule - MINOR - - - This rule checks for duplicate String literals within the current class.

-

Code containing duplicate literals can usually be improved by declaring the as a constant field.

-

By default, the rule does not analyze test files. This rule sets the default value of the property to ignore file names ending in 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

- ]]>
+]]> bug - ignoreStrings - - '' (empty string) + ignoreVariableNames +
- + - org.codenarc.rule.dry.DuplicateMapLiteralRule + org.codenarc.rule.convention.StaticFieldsBeforeInstanceFieldsRule MAJOR - - - This rule checks for duplicate literals within the current class. This rule only checks for s where the keys and values are all constants or literals.

-

Code containing duplicate literals can usually be improved by declaring the as a constant field.

-

By default, the rule does not analyze test files. This rule sets the default value of the property to ignore file names ending in 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

-

Examples of violations:

+ + + Enforce that all static fields are above all instance fields within a class

+

Example of violations:

-      def var1 = [a:1, b:null, c:Boolean.FALSE, d:'x', e:true]
-      def var2 = [a:1, b:null, c:Boolean.FALSE, d:'x', e:true]      // violation
-
-      def var1 = [a:1, b:[x:3,y:4]]
-      def var2 = [a:1, b:[x:3,y:4]]     // violation
+    class MyClass {
+        public static final int COUNT = 99
 
-      def var1 = [a:1, b:[3,4]]
-      def var2 = [a:1, b:[3,4]]     // violation
+        public String f1
 
-      def var1 = [null:1, 'b':2, (Boolean.FALSE):3, (4):4, (true):5]
-      def var2 = [null:1, 'b':2, (Boolean.FALSE):3, (4):4, (true):5]    // violation
-
-
-    def name
-    def var1 = [(name):1, b:1, c:1]
-    def var2 = [(name):1, b:1, c:1]   // not a violation; name is a variable
+        public static final String F1 = "xxx"       // violation
+        private static String F4                    // violation
+        static F5 = new Date()                      // violation
 
-    def var1 = [a:1, b:['x', name]]
-    def var2 = [a:1, b:['x', name]]   // not a violation; name is a variable
+        protected String f2
+    }
+
]]>
+ bug +
- def var1 = [a:7+5] - def var2 = [a:7+5] // not a violation; contains a non-constant/literal expression - -]]> + + + org.codenarc.rule.convention.StaticMethodsBeforeInstanceMethodsRule + MAJOR + + + Enforce that all static methods within each visibility level (public, protected, private) are above all instance methods within that same visibility level. In other words, public static methods must be above public instance methods, protected static methods must be above protected instance methods and private static methods must be above private instance methods.

+

Example of violations:

+
+        class MyClass {
+            // Public
+            public static int staticMethod1() { }
+            public String method1() { }
+            int method2() { }
+            static final String staticMethod2(int id) { }       // violation
+
+            // Protected
+            protected String method3() { }
+            protected static staticMethod3() { }                // violation
+
+            // Private
+            private int method4() { }
+            private int method5() { }
+            private static staticMethod4() { }                  // violation
+            private String method5() { }
+        }
+    }
+
]]>
bug
- + - org.codenarc.rule.dry.DuplicateListLiteralRule + org.codenarc.rule.convention.PublicMethodsBeforeNonPublicMethodsRule MAJOR - - - This rule checks for duplicate literals within the current class. This rule only checks for s where values are all constants or literals.

-

Code containing duplicate literals can usually be improved by declaring the as a constant field.

-

By default, the rule does not analyze test files. This rule sets the default value of the property to ignore file names ending in 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

-

Examples of violations:

+ + + Enforce that all public methods are above protected and private methods.

+

Example of violations:

-      def var1 = [1, null, Boolean.FALSE, 'x', true]
-      def var2 = [1, null, Boolean.FALSE, 'x', true]        // violation
+    class MyClass {
+        public static int staticMethod1() { }
 
-      def var1 = [1, [3, 4]]
-      def var2 = [1, [3,4]]     // violation
+        protected String method1() { }
 
-      def var1 = [123, [3, 4, [x:99], 5]]
-      def var2 = [99, [3, 4, [x:99], 5]]        // violation [3, 4, [x:99], 5]
-
-
-    def name
-    def var1 = [name, 'b', 'c']
-    def var2 = [name, 'b', 'c']   // not a violation; name is a variable
+        static final String staticMethod2() { }     // violation
+        public String method2() { }                 // violation
 
-    def var1 = [1, 7+5]
-    def var2 = [1, 7+5]      // not a violation; contains a non-constant/literal expression
-
-]]>
+ private int method3(int id) { } + } +]]>
bug
- - + - org.codenarc.rule.exceptions.CatchErrorRule + org.codenarc.rule.convention.NoJavaUtilDateRule MINOR - - - Checks for catching a Error. In most cases that is much too broad, and is also dangerous because it can catch exceptions such as ThreadDeath and OutOfMemoryError.

- ]]>
- error-handling + + + Do not use the java.util.Date class. Prefer the classes in the java.time.* packages. This rule checks for construction of new java.util.Date objects.

+

If the class imports another Date class, then references to new Date() will not cause a violation.

+

Example of violations:

+
+    def timestamp = new Date()              // violation
+    Date myDate = new java.util.Date()      // violation
+    Date startTime = new Date(123456789L)   // violation
+
+

Known limitations:

+

* Will cause an incorrect violation if the source code is referring to a different Date class from the current package. In that case, it may be better to just disable this rule (either per class or globally).

]]>
+ bug
+ - org.codenarc.rule.exceptions.CatchExceptionRule + org.codenarc.rule.convention.CompileStaticRule MINOR - - - Checks for catching a Exception. In most cases that is too broad or general. It should usually be restricted to framework or infrastructure code, rather than application code.

- ]]>
- error-handling + + + Enforces classes are annotated either with one of the @CompileStatic, @GrailsCompileStatic or @CompileDynamic annotations.

]]>
+ bug
- - org.codenarc.rule.exceptions.CatchNullPointerExceptionRule - MINOR - - - Checks for catching a NullPointerException. Catching NullPointerException is never appropriate. It should be avoided in the first place with proper null checking, and it can mask underlying errors.

- ]]>
- error-handling -
+ - org.codenarc.rule.exceptions.CatchRuntimeExceptionRule + org.codenarc.rule.design.CloneableWithoutCloneRule MINOR - - - Checks for catching a RuntimeException. In most cases that is too broad or general. It should usually be restricted to framework or infrastructure code, rather than application code.

- ]]>
- error-handling + + + Checks for classes that implement the java.lang.Cloneable interface without implementing the clone() method.

+

Here is an example of code that produces a violation:

+
+    class BadClass implements Cloneable {
+        def someMethod()
+    }
+
]]>
+ design
- org.codenarc.rule.exceptions.CatchThrowableRule + org.codenarc.rule.design.ImplementationAsTypeRule MINOR - - - Checks for catching a Throwable. In most cases that is much too broad, and is also dangerous because it can catch exceptions such as ThreadDeath and OutOfMemoryError.

- ]]>
- error-handling + + + Checks for use of the following concrete classes when specifying the type of a method parameter, closure parameter, constructor parameter, method return type or field type. The corresponding interfaces should be used to specify the type instead.

+

* java.util.ArrayList

+

* java.util.GregorianCalendar

+

* java.util.HashMap

+

* java.util.HashSet

+

* java.util.Hashtable

+

* java.util.LinkedHashMap

+

* java.util.LinkedHashSet

+

* java.util.LinkedList

+

* java.util.TreeMap

+

* java.util.TreeSet

+

* java.util.Vector

+

* java.util.concurrent.ArrayBlockingQueue

+

* java.util.concurrent.ConcurrentHashMap

+

* java.util.concurrent.ConcurrentLinkedQueue

+

* java.util.concurrent.CopyOnWriteArrayList

+

* java.util.concurrent.CopyOnWriteArraySet

+

* java.util.concurrent.DelayQueue

+

* java.util.concurrent.LinkedBlockingQueue

+

* java.util.concurrent.PriorityBlockingQueue

+

* java.util.concurrent.PriorityQueue

+

* java.util.concurrent.SynchronousQueue

+

Here are examples of code that produces violations:

+
+    // Method parameter
+    void myMethod(ArrayList list) {                   // violation
+        ...
+    }
+
+    // Constructor parameter
+    class MyClass {
+        MyClass(java.util.HashSet set) {              // violation
+            ...
+        }
+    }
+
+    // Closure parameter
+    def closure = { PriorityQueue queue -> ... }      // violation
+
+    // Method return type
+    GregorianCalendar calculateDate(int num) {        // violation
+        ...
+    }
+
+    // Field type
+    class MyClass {
+        Hashtable map                                 // violation
+    }
+
]]>
+ design
+ - org.codenarc.rule.exceptions.ThrowErrorRule + org.codenarc.rule.design.BooleanMethodReturnsNullRule MINOR - - - Checks for throwing an instance of java.lang.Error. This is not appropriate within normal application code. Throw an instance of a more specific exception subclass instead.

- ]]>
- error-handling + + + Checks for a method with Boolean return type that returns an explicit null. A method that returns either Boolean.TRUE, Boolean.FALSE or null is an accident waiting to happen. This method can be invoked as though it returned a value of type boolean, and the compiler will insert automatic unboxing of the Boolean value. If a null value is returned, this will result in a NullPointerException.

]]>
+ design
+ - org.codenarc.rule.exceptions.ThrowExceptionRule + org.codenarc.rule.design.ReturnsNullInsteadOfEmptyArrayRule MINOR - - - Checks for throwing an instance of java.lang.Exception. Throw an instance of a more specific exception subclass instead.

- ]]>
- error-handling + + + If you have a method or closure that returns an array, then when there are no results return a zero-length (empty) array rather than null. It is often a better design to return a zero-length array rather than a null reference to indicate that there are no results (i.e., an empty list of results). This way, no explicit check for null is needed by clients of the method.

]]>
+ design
+ - org.codenarc.rule.exceptions.ThrowNullPointerExceptionRule + org.codenarc.rule.design.ReturnsNullInsteadOfEmptyCollectionRule MINOR - - - Checks for throwing an instance of java.lang.NullPointerException. Applications should never throw a NullPointerException.

- ]]>
- error-handling + + + If you have a method or closure that returns a collection, then when there are no results return a zero-length (empty) collection rather than null. It is often a better design to return a zero-length collection rather than a null reference to indicate that there are no results (i.e., an empty list of results). This way, no explicit check for null is needed by clients of the method.

]]>
+ design
+ - org.codenarc.rule.exceptions.ThrowRuntimeExceptionRule + org.codenarc.rule.design.CompareToWithoutComparableRule MINOR - - - Checks for throwing an instance of java.lang.RuntimeException. Throw an instance of a more specific exception subclass instead.

- ]]>
- error-handling + + + If you implement a compareTo method then you should also implement the Comparable interface. If you don't then you could possibly get an exception if the Groovy == operator is invoked on your object. This is an issue fixed in Groovy 1.8 but present in previous versions.

+

This rule has a single enhancedMode property which defaults to false. When set to true, this rule will run in enhanced mode and will not produce a violation when a class implements compareTo and extends a class that itself implements Comparable.

+

Here is an example of code that produces a violation:

+
+    class BadClass {
+        int compareTo(Object o) { ... }
+    }
+
+

Known limitations:

+

* When not running in enhanced mode, this rule is not able to determine if the class extends a superclass that itself implements Comparable, or if it implements an interface that extends Comparable. In those cases, this rule produces a false violation.

]]>
+ design
+ - org.codenarc.rule.exceptions.ThrowThrowableRule + org.codenarc.rule.design.SimpleDateFormatMissingLocaleRule MINOR - - - Checks for throwing an instance of java.lang.Throwable. Throw an instance of a more specific exception subclass instead.

-]]>
- error-handling + + + Be sure to specify a Locale when creating a new instance of SimpleDateFormat; the class is locale-sensitive. If you instantiate SimpleDateFormat without a Locale parameter, it will format the date and time according to the default Locale. Both the pattern and the Locale determine the format. For the same pattern, SimpleDateFormat may format a date and time differently if the Locale varies.

+
+    // violation, missing locale
+    new SimpleDateFormat('pattern')
+
+    // OK, includes locale
+    new SimpleDateFormat('pattern', Locale.US)
+
+    // OK, includes a variable that perhaps is a locale
+    new SimpleDateFormat('pattern', locale)
+
]]>
+ design
- + - org.codenarc.rule.exceptions.CatchIllegalMonitorStateExceptionRule + org.codenarc.rule.design.AbstractClassWithoutAbstractMethodRule MINOR - - - Dubious catching of IllegalMonitorStateException. IllegalMonitorStateException is generally only thrown in case of a design flaw in your code (calling wait or notify on an object you do not hold a lock on).

- ]]>
- error-handling + + + The abstract class does not contain any abstract methods. An abstract class suggests an incomplete implementation, which is to be completed by subclasses implementing the abstract methods. If the class is intended to be used as a base class only (not to be instantiated directly) a protected constructor can be provided prevent direct instantiation.

+

Example:

+
+    public abstract class MyBaseClass {
+        void method1() {  }
+        void method2() {  }
+        // consider using abstract methods or removing
+        // the abstract modifier and adding protected constructors
+    }
+
+

The following examples all pass:

+
+    abstract class MyClass extends AbstractParent {
+        // OK because parent is named Abstract.*
+    }
+    abstract class MyClass extends BaseParent{
+        // OK because parent is named Base.*
+    }
+
]]>
+ design
- + - org.codenarc.rule.exceptions.ConfusingClassNamedExceptionRule + org.codenarc.rule.design.CloseWithoutCloseableRule MINOR - - - This class is not derived from another exception, but ends with 'Exception'. This will be confusing to users of this class.

- ]]>
- error-handling + + + If a class defines a void close() method then that class should implement java.io.Closeable.

+

This rule has a single enhancedMode property which defaults to false. When set to true, this rule will run in enhanced mode and will not produce a violation when a class implements close and extends a class that itself implements Closeable.

]]>
+ design
- + - org.codenarc.rule.exceptions.ReturnNullFromCatchBlockRule + org.codenarc.rule.design.ConstantsOnlyInterfaceRule MINOR - - - Returning null from a catch block often masks errors and requires the client to handle error codes. In some coding styles this is discouraged. This rule ignores methods with void return type.

- ]]>
- error-handling + + + An interface should be used only to model a behaviour of a class: using an interface as a container of constants is a poor usage pattern. Example:

+
+    public interface ConstantsInterface {
+        public static final int CONSTANT_1 = 0
+        public static final String CONSTANT_2 = "1"
+    }
+
]]>
+ design
- org.codenarc.rule.exceptions.CatchArrayIndexOutOfBoundsExceptionRule + org.codenarc.rule.design.EmptyMethodInAbstractClassRule MINOR - - - Checks for catching a ArrayIndexOutOfBoundsException. Catching ArrayIndexOutOfBoundsException should be avoided in the first place by checking the array size before accessing an array element. Catching the exception may mask underlying errors.

- ]]>
- error-handling + + + An empty method in an abstract class should be abstract instead, as developer may rely on this empty implementation rather than code the appropriate one.

+
+    abstract class MyClass {
+        def couldBeAbstract_1() {
+            return null  // Should be abstract method
+        }
+
+        void couldBeAbstract_2() {
+            // Should be abstract method
+        }
+    }
+
]]>
+ design
- org.codenarc.rule.exceptions.CatchIndexOutOfBoundsExceptionRule + org.codenarc.rule.design.FinalClassWithProtectedMemberRule MINOR - - - Checks for catching a IndexOutOfBoundsException. Catching IndexOutOfBoundsException should be avoided in the first place by checking for a valid index before accessing an indexed element. Catching the exception may mask underlying errors.

- ]]>
- error-handling + + + This rule finds classes marked final that contain protected members. If a class is final then it may not be subclassed, and there is therefore no point in having a member with protected visibility. Either the class should not be final or the member should be private or protected.

]]>
+ design
- + - org.codenarc.rule.exceptions.MissingNewInThrowStatementRule + org.codenarc.rule.design.PublicInstanceFieldRule MINOR - - - A common Groovy mistake when throwing exceptions is to forget the new keyword. For instance, throw RuntimeException() instead of throw new RuntimeException(). If the error path is not unit tested then the production system will throw a Method Missing exception and hide the root cause. This rule finds constructs like throw RuntimeException() that look like a new keyword was meant to be used but forgotten.

-

The following code will all cause violations:

-
-    throw RuntimeException()    // ends in Exceptions, first letter Capitalized
-    throw RuntimeFailure()      // ends in Failure, first letter Capitalized
-    throw RuntimeFault(foo)     // ends in Fault, first letter Capitalized
-
+ + + Using public fields is considered to be a bad design. Use properties instead.

+

Example of violations:

-    throw new RuntimeException()
-    throw runtimeFailure()      // first letter lowercase, assumed to be method call
-
-]]>
- error-handling + class Person { + public String name + } +]]>
+ design
- + - org.codenarc.rule.exceptions.ExceptionExtendsErrorRule + org.codenarc.rule.design.StatelessSingletonRule MINOR - - - Errors are system exceptions. Do not extend them.

-

Examples:

+ + + There is no point in creating a stateless Singleton because there is nothing within the class that needs guarding and no side effects to calling the constructor. Just create new instances of the object or write a Utility class with static methods. In the long term, Singletons can cause strong coupling and hard to change systems.

+

If the class has any fields at all, other than a self reference, then it is not considered stateless. A self reference is a field of the same type as the enclosing type, or a field named instance or _instance. The field name self reference is a property named instanceRegex that defaults to the value 'instance|_instance'

+

Example of violations:

-    class MyError extends Error { }  // violation
-    class MyError extends java.lang.Error { }  // violation
+    @groovy.lang.Singleton
+    class Service {
+       // violation: the class has no fields but is marked Singleton
+        void processItem(item){
+        }
+    }
 
-    class MyException extends Exception { }  // OK
-
-]]>
- error-handling + class Service { + // violation: the class has no fields other than 'instance' but is marked Singleton + static instance + void processItem(item){ + } + } + + class Service { // violation + static Service service + void processItem(item){ + } + } +]]>
+ design
- org.codenarc.rule.exceptions.SwallowThreadDeathRule + org.codenarc.rule.design.AbstractClassWithPublicConstructorRule MINOR - - - Detects code that catches java.lang.ThreadDeath without re-throwing it.

-

Example of violations:

+ + + Checks for abstract classes that define a public constructor, which is useless and confusing.

+

The following code produces a violation:

-    try {
-        def a = 0
-    } catch (ThreadDeath td) {
-        td.printStackTrace()
+    abstract class MyClass {
+        MyClass() { }
     }
-
-]]>
- error-handling +]]>
+ design
- + - org.codenarc.rule.exceptions.ExceptionNotThrownRule + org.codenarc.rule.design.BuilderMethodWithSideEffectsRule MINOR - - - Checks for an exception constructor call without a throw as the last statement within a catch block. This rule treats any constructor call for a class named Exception as an exception constructor call.

-

Example of violations:

+ + + A builder method is defined as one that creates objects. As such, they should never be of void return type. If a method is named build, create, or make, then it should always return a value.

+

This rule has one property: methodNameRegex. The default value is (make.*|create.*|build.*). Update this property if you have some other naming convention for your builder methods.

+

Example of violations:

-    void execute() {
-        try { } catch(Exception e) { new Exception(e) }     // violation
-    }
 
-    try {
-        doStuff()
-    } catch(DaoException e) {
-        log.warning("Ooops", e)
-        new ServiceException(e)                             // violation
-    } catch(Exception e) {
-        new SystemException(e)                              // violation
-    }
+    class MyClass {
 
-    try {
-        doStuff()
-    } catch(Exception e) { throw new DaoException(e) }      // ok
-
-]]>
- error-handling -
+ void make() { /* ... */ } + void makeSomething() { /* ... */ } - - - org.codenarc.rule.exceptions.ExceptionExtendsThrowableRule - MINOR - - - Checks for classes that extend Throwable. Custom exception classes should subclass Exception or one of its descendants.

-

Example of violations:

-
-    class MyException extends Throwable { }   // violation
-
-]]>
- error-handling -
+ void create() { /* ... */ } + void createSomething() { /* ... */ } - + void build() { /* ... */ } + void buildSomething() { /* ... */ } + } +]]> + design + + - org.codenarc.rule.generic.IllegalRegexRule.fixed + org.codenarc.rule.design.PrivateFieldCouldBeFinalRule.fixed MAJOR - - - Checks for a specified illegal regular expression within the source code.

-

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule and , and (optionally) customized and .

-

NOTE: This rule applies to the text contents of an entire rather than a specific , so it does not support the and configuration properties.

- ]]>
- bug + + + This rule finds private fields that are only set within a constructor or field initializer. Such fields can safely be made final.

]]>
+ design - regex - + ignoreFieldNames + -
- - - org.codenarc.rule.generic.RequiredRegexRule.fixed - MAJOR - - - Checks for a specified regular expression that must exist within the source code.

-

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule and , and (optionally) customized and .

-

NOTE: This rule applies to the text contents of an entire rather than a specific , so it does not support the and configuration properties.

- ]]>
- bug - regex - + ignoreJpaEntities + + false
+ - org.codenarc.rule.generic.RequiredStringRule.fixed - MAJOR - - - Checks for a specified text string that must exist within the source code.

-

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule and , and (optionally) customized and .

-

NOTE: This rule applies to the text contents of an entire rather than a specific , so it does not support the and configuration properties.

- ]]>
- bug - - string - - + org.codenarc.rule.design.CloneWithoutCloneableRule + MINOR + + + The method clone() should only be declared if the class implements the Cloneable interface.

+

NOTE: This is a CodeNarc Enhanced Classpath Rule. It requires CodeNarc to have the application classes being analyzed, as well as any referenced classes, on the classpath.

+

Example of violations:

+
+    class ValueClass {
+        ValueClass clone() {
+        }
+    }
+
]]>
+ design
+ - org.codenarc.rule.generic.StatelessClassRule.fixed + org.codenarc.rule.design.LocaleSetDefaultRule MINOR - - - Checks for non-final fields on a class. The intent of this rule is to check a configured set of classes that should remain "stateless" and reentrant. One example might be Grails service classes which are singletons, by default, and so they should be reentrant.

-

This rule ignores final fields (either instance or static). Fields that are static and non-final, however, do cause a violation.

-

This rule also ignores all classes annotated with the @Immutable transformation. See http://groovy.codehaus.org/Immutable+transformation.

-

This rule also ignores all fields annotated with the @Inject annotation.

-

You can configure this rule to ignore certain fields either by name or by type. This can be useful to ignore fields that hold references to (static) dependencies (such as DAOs or Service objects) or static configuration.

-

Note that you can use the standard rule properties, such as applyToClassNames, doNotApplyToFileNames and applyToFilesMatching to only apply this rule to a subset of all classes/files. These rule properties are described in Standard Properties for Configuring Rules.

- [[1]] The ignoreFieldTypes property matches the field type name as indicated in the field declaration, only including a full package specification IF it is included in the source code. For example, the field declaration BigDecimal value matches an ignoreFieldTypes value of BigDecimal, but not java.lang.BigDecimal.

-

[[2]] There is one exception for the ignoreFieldTypes property: if the field is declared with a modifier/type of def, then the type resolves to java.lang.Object.

-

[[3]] At least one of the (standard) applyToClassNames, applyToFileNames or applyToFilesMatching properties must be set (i.e., not null or empty) or else this rule does nothing. In other words, you must configure this rule to apply to a specific set of classes or files.

-

[[4]] This rule will not catch violations of true / if you define a final field whose value is itself mutable, e.g. a final HashMap.

-]]>
- bug - - addToIgnoreFieldNames - - - - ignoreFieldNames - - - - ignoreFieldTypes - - + + + Checks for calls to Locale.setDefault(), or Locale.default = Xxx, which sets the Locale across the entire JVM. That can impact other applications on the same web server, for instance.

+

From the java.util.Locale javadoc for setDefault: should only be used if the caller is prepared to reinitialize locale-sensitive code running within the same Java Virtual Machine.

+

Example of violations:

+
+    Locale.setDefault(Locale.UK)                                // violation
+    java.util.Locale.setDefault(Locale.FRANCE)                  // violation
+    Locale.setDefault(Locale.Category.DISPLAY, Locale.JAPAN)    // violation
+
+    Locale.default = Locale.UK                                  // violation
+
]]>
+ design
- + - org.codenarc.rule.generic.IllegalPackageReferenceRule.fixed + org.codenarc.rule.design.ToStringReturnsNullRule MINOR - - - Checks for reference to any of the packages configured in packageNames.

-

Note that you can use the standard rule properties, such as applyToClassNames, doNotApplyToFileNames and applyToFilesMatching to only apply this rule to a subset of all classes/files. These rule properties are described in Standard Properties for Configuring Rules.

-

This rule can be useful for governance and enforcement of . For instance, making sure that view or model classes, for instance, do not contain references to JDBC-specific packages (e.g. java.sql and javax.sql).

-

Here is an example configuration of this rule used to ensure that JDBC packages/classes are only referenced within DAO classes:

+ + + Checks for toString() methods that return null. This is unconventional and could cause unexpected NullPointerExceptions from normal or implicit use of toString().

+

Example of violations:

-    ruleset {
-        description "Example CodeNarc Ruleset"
+    class MyClass {
+        String toString() {
+            if (foo()) {
+                return 'MyClass'
+            } else {
+                return null         // violation
+            }
+        }
+    }
 
-        // ...
+    class MyClass {
+        String toString() {
+            calculateStuff()
+            null                    // violation
+        }
+    }
 
-        IllegalPackageReference {
-            name = 'UseJdbcOnlyInDaoClasses'
-            priority = 2
-            packageNames = 'groovy.sql, java.sql, javax.sql'
-            doNotApplyToClassNames = 'com.example.framework.dao.*, *Dao, *DaoImpl'
-            description = 'Reference to JDBC packages should be restricted to DAO classes.'
+    class MyClass {
+        String toString() {         // violation - implicit return of null
         }
     }
-
-]]>
- bug +]]>
+ design +
+ + + + org.codenarc.rule.design.InstanceofRule + MINOR + + + Checks for use of the instanceof operator. Prefer using polymorphism instead.

+

Use the ignoreTypeNames property to configure ignored type names (the class name specified as the right-hand expression of the instanceof). It defaults to ignoring instanceof checks against exception classes.

+

Here are a couple references that discuss the problems with using instanceof and the preference for using polymorphism instead:

+

* Beware of instanceof operator

+

* How does one use polymorphism instead of instanceof? (And why?)

+

By default, the rule does not analyze test files. This rule sets the default value of the doNotApplyToFilesMatching property to ignore file names ending in 'Spec.groovy', 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

+

Example of violations:

+
+    class MyClass {
+        boolean isRunnable = this instanceof Runnable       // violation
+    }
+
]]>
+ design - packageNames - + ignoreTypeNames + + *Exceptions
- + - org.codenarc.rule.generic.IllegalClassReferenceRule.fixed - MINOR - - - Checks for reference to any of the classes configured in classNames.

-

Note that you can use the standard rule properties, such as applyToClassNames, doNotApplyToFileNames and applyToFilesMatching to only apply this rule to a subset of all classes/files. These rule properties are described in Standard Properties for Configuring Rules.

-

This rule can be useful for governance and enforcement of . For instance, making sure that view or model classes, for instance, do not contain references to DAO classes (e.g., *Dao).

-

Here is an example configuration of this rule used to ensure that DAO classes are not referenced from within model classes:

+ org.codenarc.rule.design.NestedForLoopRule + MAJOR + + + Reports classes with nested for loops.

+

Example of violations:

-    ruleset {
-        description "Example CodeNarc Ruleset"
+for (int i = 0; i < 100; ++i) {
+    for (int j = 0; j < 100; ++j) { // violation
+        println i + j
+    }
+}
 
-        // ...
+for (int i = 0; i < 100; ++i) {
+    for (int j = 0; j < 100; ++j) { // violation
+        println i + j
+    }
+    for (int j = 0; j < 100; ++j) { // violation
+        println i + j
+    }
+}
 
-        IllegalClassReference {
-            name = 'DoNotReferenceDaoFromModelClasses'
-            priority = 2
-            classNames = '*Dao'
-            applyToClassNames = 'com.example.model.*'
-            description = 'Do not reference DAOs from model classes.'
+for (int i = 0; i < 100; ++i) {
+    for (int j = 0; j < 100; ++j) { // violation
+        for (int k = 0; k < 100; ++k) { // violation
+            println i + j + k
         }
     }
-
-]]>
- bug - - classNames - - +} +]]>
+ design
- + - org.codenarc.rule.generic.IllegalClassMemberRule.fixed + org.codenarc.rule.design.AssignmentToStaticFieldFromInstanceMethodRule MINOR - - - Checks for classes containing fields/properties/methods matching configured illegal member modifiers or not matching any of the configured allowed member modifiers.

-

Modifiers for fields and methods include:

-]]>
- bug - - allowedFieldModifiers - - - - allowedMethodModifiers - - - - allowedPropertyModifiers - - - - ignoreMethodNames - - - - ignoreMethodsWithAnnotationNames - - - - illegalFieldModifiers - - - - illegalMethodModifiers - - - - illegalPropertyModifiers - - + + + Checks for assignment to a static field from an instance method.

+

Influenced by the AssignmentToNonFinalStatic rule from PMD, and the ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD rule from FindBugs.

+

Example of violations:

+
+    class MyClass {
+        private static field1
+        protected static String field2 = 'abc'
+        public static int field3 = 123
+        static String property1 = 'abc'
+        private static final NAME = 'joe'
+
+        private void doStuff() {
+            field1 = new Object()       // violation
+            field2 = 'xxx'              // violation
+            field3 = 999                // violation
+            property1 = 'xxx'           // violation
+
+            final NAME = 'martin'       // no violation; local var hides static field
+        }
+    }
+
]]>
+ design
- + + + - org.codenarc.rule.generic.IllegalStringRule.fixed + org.codenarc.rule.dry.DuplicateNumberLiteralRule MINOR - - - Checks for a specified illegal string within the source code.

-

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule and , and (optionally) customized and .

-

NOTE: This rule applies to the text contents of an entire rather than a specific , so it does not support the and configuration properties.

- ]]>
+ + + This rule checks for duplicate number literals within the current class.

+

Code containing duplicate Number literals can usually be improved by declaring the Number as a constant field.

+

By default, the rule does not analyze test files. This rule sets the default value of the doNotApplyToFilesMatching property to ignore file names ending in 'Spec.groovy', 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

+

* This rule ignores Long/long values within enums, because the generated code may include generated long id values and produce false positive rule violations.

+

* This rule does not search across several files at once, only in the current file, and only within the current class.

+

* You can suppress the error by annotating a class or method with the @SuppressWarnings('DuplicateNumberLiteral') annotation.

]]>
bug - string - + ignoreNumbers + + 0,1
- + - org.codenarc.rule.generic.IllegalSubclassRule.fixed + org.codenarc.rule.dry.DuplicateStringLiteralRule MINOR - - - Checks for classes that extend one of the specified set of illegal superclasses.

-

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule and , and (optionally) customized and .

- ]]>
+ + + This rule checks for duplicate String literals within the current class.

+

Code containing duplicate String literals can usually be improved by declaring the String as a constant field.

+

By default, the rule does not analyze test files. This rule sets the default value of the doNotApplyToFilesMatching property to ignore file names ending in 'Spec.groovy', 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

+

* This rule does not search across several files at once, only in the current file, and only within the current class.

+

* You can suppress the error by annotating a class or method with the @SuppressWarnings('DuplicateStringLiteral') annotation.

]]>
bug - superclassNames - + ignoreStrings + + '' (empty string)
- - + - org.codenarc.rule.grails.GrailsPublicControllerMethodRule.fixed - MINOR - - - Rule that checks for public methods on Grails controller classes. Static methods are ignored.

-

Grails controller actions and interceptors are defined as properties on the controller class. Public methods on a controller class are unnecessary. They break encapsulation and can be confusing.

-

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/controllers' folder. You can override this with a different regular expression value if appropriate.

-

This rule also sets the default value of applyToClassNames to only match class names ending in 'Controller'. You can override this with a different class name pattern (String with wildcards) if appropriate.

- ]]>
- grails - - ignoreMethodNames - - + org.codenarc.rule.dry.DuplicateMapLiteralRule + MAJOR + + + This rule checks for duplicate Map literals within the current class. This rule only checks for Maps where the keys and values are all constants or literals.

+

Code containing duplicate Map literals can usually be improved by declaring the Map as a constant field.

+

By default, the rule does not analyze test files. This rule sets the default value of the doNotApplyToFilesMatching property to ignore file names ending in 'Spec.groovy', 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

+

Examples of violations:

+
+      def var1 = [a:1, b:null, c:Boolean.FALSE, d:'x', e:true]
+      def var2 = [a:1, b:null, c:Boolean.FALSE, d:'x', e:true]      // violation
+
+      def var1 = [a:1, b:[x:3,y:4]]
+      def var2 = [a:1, b:[x:3,y:4]]     // violation
+
+      def var1 = [a:1, b:[3,4]]
+      def var2 = [a:1, b:[3,4]]     // violation
+
+      def var1 = [null:1, 'b':2, (Boolean.FALSE):3, (4):4, (true):5]
+      def var2 = [null:1, 'b':2, (Boolean.FALSE):3, (4):4, (true):5]    // violation
+
+

Examples of non-violations:

+
+    def name
+    def var1 = [(name):1, b:1, c:1]
+    def var2 = [(name):1, b:1, c:1]   // not a violation; name is a variable
+
+    def var1 = [a:1, b:['x', name]]
+    def var2 = [a:1, b:['x', name]]   // not a violation; name is a variable
+
+    def var1 = [a:7+5]
+    def var2 = [a:7+5]      // not a violation; contains a non-constant/literal expression
+
+

* This rule does not search across several files at once, only in the current file, and only within the current class.

+

* You can suppress the error by annotating a class or method with the @SuppressWarnings('DuplicateMapLiteral') annotation.

]]>
+ bug
+ - org.codenarc.rule.grails.GrailsSessionReferenceRule - MINOR - - - Rule that checks for references to the session object from within Grails controller and taglib classes.

-

This rule is intended as a "governance" rule to enable monitoring and controlling access to the session from within application source code. Storing objects in the session may inhibit scalability and/or performance and should be carefully considered.

-

Note that this rule does not check for direct access to the session from within GSP (Groovy Server Pages) files.

-

Enabling this rule may make most sense in a team environment where team members exhibit a broad range of skill and experience levels. Appropriate session access can be configured as exceptions to this rule by configuring either the doNotApplyToFilenames or doNotApplyToFilesMatching property of the rule. And, as always, it is easy to just turn off the rule if it does not make sense it your environment.

-

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/controllers' or 'grails-app/taglib' folders. You can override this with a different regular expression value if appropriate.

- ]]>
- grails + org.codenarc.rule.dry.DuplicateListLiteralRule + MAJOR + + + This rule checks for duplicate List literals within the current class. This rule only checks for Lists where values are all constants or literals.

+

List literals within annotations are ignored.

+

Code containing duplicate List literals can usually be improved by declaring the List as a constant field.

+

By default, the rule does not analyze test files. This rule sets the default value of the doNotApplyToFilesMatching property to ignore file names ending in 'Spec.groovy', 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'.

+

Examples of violations:

+
+      def var1 = [1, null, Boolean.FALSE, 'x', true]
+      def var2 = [1, null, Boolean.FALSE, 'x', true]        // violation
+
+      def var1 = [1, [3, 4]]
+      def var2 = [1, [3,4]]     // violation
+
+      def var1 = [123, [3, 4, [x:99], 5]]
+      def var2 = [99, [3, 4, [x:99], 5]]        // violation [3, 4, [x:99], 5]
+
+

Examples of non-violations:

+
+    def name
+    def var1 = [name, 'b', 'c']
+    def var2 = [name, 'b', 'c']   // not a violation; name is a variable
+
+    def var1 = [1, 7+5]
+    def var2 = [1, 7+5]      // not a violation; contains a non-constant/literal expression
+
+

* This rule does not search across several files at once, only in the current file, and only within the current class.

+

* You can suppress the error by annotating a class or method with the @SuppressWarnings('DuplicateListLiteral') annotation.

]]>
+ bug
+ + + - org.codenarc.rule.grails.GrailsServletContextReferenceRule - MINOR - - - Rule that checks for references to the servletContext object from within Grails controller and taglib classes.

-

This rule is intended as a "governance" rule to enable monitoring and controlling access to the servletContext from within application source code. Storing objects in the servletContext may inhibit scalability and/or performance and should be carefully considered. Furthermore, access to the servletContext is not synchronized, so reading/writing objects from the servletConext must be manually synchronized, as described in The Definitive Guide to Grails (2nd edition).

-

Note that this rule does not check for direct access to the servletContext from within GSP (Groovy Server Pages) files.

-

Enabling this rule may make most sense in a team environment where team members exhibit a broad range of skill and experience levels. Appropriate servletContext access can be configured as exceptions to this rule by configuring either the doNotApplyToFilenames or doNotApplyToFilesMatching property of the rule. And, as always, it is easy to just turn off the rule if it does not make sense it your environment.

-

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/controllers' or 'grails-app/taglib' folders. You can override this with a different regular expression value if appropriate.

- ]]>
- grails + org.codenarc.rule.enhanced.MissingOverrideAnnotationRule + MAJOR + + + Checks for methods that override a method in a superclass or implement a method in an interface but are not annotated with @Override.

+

Consistent use of @Override annotation helps in spotting situations when the intent was to override a method but because of a mistake in method signature that is not the case. Additionally, applying @Override annotation to all overridden methods helps in spotting unnecessary methods which no longer override any methods after removing them from superclasses or implemented interfaces because such annotated methods will cause compilation errors.

+

Example of violations:

+
+    class ClassOverridingToString {
+        String toString() {
+          "ClassOverridingToString"
+        }
+    }
+
]]>
+ bug
+ + - org.codenarc.rule.grails.GrailsStatelessServiceRule + org.codenarc.rule.exceptions.CatchErrorRule MINOR - - - Checks for non-final fields on a Grails service class. Grails service classes are singletons by default, and so they should be reentrant. In most cases, this implies (or at least encourages) that they should be stateless.

-

This rule ignores (i.e., does not cause violations for) the following:

-]]>
- grails - - ignoreFieldNames - - - ignoreFieldTypes - + + + Checks for catching a Error. In most cases that is much too broad, and is also dangerous because it can catch exceptions such as ThreadDeath and OutOfMemoryError.

]]>
+ error-handling
- - org.codenarc.rule.grails.GrailsDomainHasToStringRule + org.codenarc.rule.exceptions.CatchExceptionRule MINOR - - - Checks that Grails domain classes redefine toString().

-

Ignores classes annotated with @ToString or @Canonical.

-

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/domain' folder. You can override this with a different regular expression value if appropriate.

- ]]>
- grails + + + Checks for catching a Exception. In most cases that is too broad or general. It should usually be restricted to framework or infrastructure code, rather than application code.

]]>
+ error-handling
- - org.codenarc.rule.grails.GrailsDomainHasEqualsRule + org.codenarc.rule.exceptions.CatchNullPointerExceptionRule MINOR - - - Checks that Grails domain classes redefine equals().

-

Ignores classes annotated with @EqualsAndHashCode or @Canonical.

-

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/domain' folder. You can override this with a different regular expression value if appropriate.

- ]]>
- grails + + + Checks for catching a NullPointerException. Catching NullPointerException is never appropriate. It should be avoided in the first place with proper null checking, and it can mask underlying errors.

]]>
+ error-handling
- - org.codenarc.rule.grails.GrailsDuplicateMappingRule + org.codenarc.rule.exceptions.CatchRuntimeExceptionRule MINOR - - - Check for duplicate name in a Grails domain class mapping. Duplicate names/entries are legal, but can be confusing and error-prone.

-

NOTE: This rule does not check that the values of the entries are duplicated, only that there are two entries with the same name.

-

Example of violations:

-
-    class Person {
-        String firstName
-        String lastName
-
-        static mapping = {
-            table 'people'
-            firstName column: 'First_Name'
-            lastName column: 'Last_Name'
-            firstName column: 'First_Name'      // violation
-            table 'people2'                     // violation
-        }
-    }
-
-]]>
- grails + + + Checks for catching a RuntimeException. In most cases that is too broad or general. It should usually be restricted to framework or infrastructure code, rather than application code.

]]>
+ error-handling
- - org.codenarc.rule.grails.GrailsDuplicateConstraintRule + org.codenarc.rule.exceptions.CatchThrowableRule MINOR - - - Check for duplicate name in a Grails domain class constraints. Duplicate names/entries are legal, but can be confusing and error-prone.

-

NOTE: This rule does not check that the values of the entries are duplicated, only that there are two entries with the same name.

-

Example of violations:

-
-    class Person {
-        String firstName
-        String lastName
-
-        static constraints = {
-            firstName nullable:true
-            lastName nullable:true, maxSize:30
-            firstName nullable:false                // violation
-        }
-    }
-
-]]>
- grails + + + Checks for catching a Throwable. In most cases that is much too broad, and is also dangerous because it can catch exceptions such as ThreadDeath and OutOfMemoryError.

]]>
+ error-handling
- - org.codenarc.rule.grails.GrailsDomainReservedSqlKeywordNameRule + org.codenarc.rule.exceptions.ThrowErrorRule MINOR - - - Forbids usage of SQL reserved keywords as class or field names in Grails domain classes. Naming a domain class (or its field) with such a keyword causes SQL schema creation errors and/or redundant table/column name mappings.

-

Note: due to limited type information available during CodeNarc's operation, this rule will report fields of type java.io.Serializable, but not of its implementations. Please specify any implementations used as domain properties in additionalHibernateBasicTypes.

-]]>
- grails - - additionalHibernateBasicTypes - - - - additionalReservedSqlKeywords - - + + + Checks for throwing an instance of java.lang.Error. This is not appropriate within normal application code. Throw an instance of a more specific exception subclass instead.

]]>
+ error-handling
- - org.codenarc.rule.grails.GrailsDomainWithServiceReferenceRule + org.codenarc.rule.exceptions.ThrowExceptionRule MINOR - - - Checks that Grails Domain classes do not have Service classes injected.

-

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/domain' folder. You can override this with a different regular expression value if appropriate.

- ]]>
- grails + + + Checks for throwing an instance of java.lang.Exception. Throw an instance of a more specific exception subclass instead.

]]>
+ error-handling
- - org.codenarc.rule.grails.GrailsMassAssignmentRule + org.codenarc.rule.exceptions.ThrowNullPointerExceptionRule MINOR - - - Untrusted input should not be allowed to set arbitrary object fields without restriction.

-

Example of violations:

-
-   // Person would be a grails domain object
-   def person = new Person(params)
-   person.save()
-
-   // or using .properties
-   def person = Person.get(1)
-   person.properties = params
-   person.save()
-
-]]>
- grails + + + Checks for throwing an instance of java.lang.NullPointerException. Applications should never throw a NullPointerException.

]]>
+ error-handling
- - - org.codenarc.rule.imports.DuplicateImportRule - MAJOR - - - Checks for a duplicate statements.

- ]]>
- bug + org.codenarc.rule.exceptions.ThrowRuntimeExceptionRule + MINOR + + + Checks for throwing an instance of java.lang.RuntimeException. Throw an instance of a more specific exception subclass instead.

]]>
+ error-handling
- org.codenarc.rule.imports.ImportFromSamePackageRule - MAJOR - - - Checks for an of a class that is within the same package as the importing class.

- ]]>
- bug + org.codenarc.rule.exceptions.ThrowThrowableRule + MINOR + + + Checks for throwing an instance of java.lang.Throwable. Throw an instance of a more specific exception subclass instead.

]]>
+ error-handling
+ - org.codenarc.rule.imports.UnnecessaryGroovyImportRule - MAJOR - - - Checks for an from any package that is already automatically imported for Groovy files. A Groovy file does not need to include an import for classes from , , , , and , as well as the classes and .

- ]]>
- bug + org.codenarc.rule.exceptions.CatchIllegalMonitorStateExceptionRule + MINOR + + + Dubious catching of IllegalMonitorStateException. IllegalMonitorStateException is generally only thrown in case of a design flaw in your code (calling wait or notify on an object you do not hold a lock on).

]]>
+ error-handling
+ - org.codenarc.rule.imports.UnusedImportRule - MAJOR - - - Checks for statements for classes that are never referenced within the source file. Also checks static imports.

-

Known limitations:

-]]>
- bug + org.codenarc.rule.exceptions.ConfusingClassNamedExceptionRule + MINOR + + + This class is not derived from another exception, but ends with 'Exception'. This will be confusing to users of this class.

]]>
+ error-handling
- + - org.codenarc.rule.imports.ImportFromSunPackagesRule + org.codenarc.rule.exceptions.ReturnNullFromCatchBlockRule MINOR - - - Avoid importing anything from the 'sun.*' packages. These packages are not portable and are likely to change.

-

Example of violations:

-
-    import sun.misc.foo
-    import sun.misc.foo as Foo
-
-    public class MyClass{}
-
-]]>
- bug -
- - - - org.codenarc.rule.imports.MisorderedStaticImportsRule - MAJOR - - - Checks for static statements which should never be after nonstatic imports.

-

This rule has one property comesBefore, which defaults to true. If you like your static imports to come after the others, then set this property to false.

-

Examples of violations:

-
-    import my.something.another
-    import static foo.bar
-
-    public class MyClass{}
-
-]]>
- bug - - comesBefore - - true - -
- - - - org.codenarc.rule.imports.NoWildcardImportsRule - MAJOR - - - Wildcard imports, static or otherwise, should not be used.

-

Example of violations:

-
-    import my.something.*
-    import static foo.bar.*
-
-    public class MyClass{}
-
-]]>
- bug + + + Returning null from a catch block often masks errors and requires the client to handle error codes. In some coding styles this is discouraged. This rule ignores methods with void return type.

]]>
+ error-handling
- - + - org.codenarc.rule.junit.JUnitAssertAlwaysFailsRule + org.codenarc.rule.exceptions.CatchArrayIndexOutOfBoundsExceptionRule MINOR - - - Rule that checks for JUnit <<>> method calls with constant or literal arguments such that theassertion always fails. This includes:

-]]>
- junit + + + Checks for catching a ArrayIndexOutOfBoundsException. Catching ArrayIndexOutOfBoundsException should be avoided in the first place by checking the array size before accessing an array element. Catching the exception may mask underlying errors.

]]>
+ error-handling
+ - org.codenarc.rule.junit.JUnitAssertAlwaysSucceedsRule + org.codenarc.rule.exceptions.CatchIndexOutOfBoundsExceptionRule MINOR - - - Rule that checks for JUnit assert() method calls with constant arguments such that the assertion always succeeds. This includes:

-]]>
- junit + + + Checks for catching a IndexOutOfBoundsException. Catching IndexOutOfBoundsException should be avoided in the first place by checking for a valid index before accessing an indexed element. Catching the exception may mask underlying errors.

]]>
+ error-handling
+ - org.codenarc.rule.junit.JUnitPublicNonTestMethodRule + org.codenarc.rule.exceptions.MissingNewInThrowStatementRule MINOR - - - Rule that checks if a JUnit test class contains public methods other than standard test methods, JUnit framework methods or methods with JUnit annotations.

-

The following public methods are ignored by this rule:

-]]>
- junit + + + A common Groovy mistake when throwing exceptions is to forget the new keyword. For instance, throw RuntimeException() instead of throw new RuntimeException(). If the error path is not unit tested then the production system will throw a Method Missing exception and hide the root cause. This rule finds constructs like throw RuntimeException() that look like a new keyword was meant to be used but forgotten.

+

The following code will all cause violations:

+
+    throw RuntimeException()    // ends in Exceptions, first letter Capitalized
+    throw RuntimeFailure()      // ends in Failure, first letter Capitalized
+    throw RuntimeFault(foo)     // ends in Fault, first letter Capitalized
+
+

The following code will not cause any exceptions:

+
+    throw new RuntimeException()
+    throw runtimeFailure()      // first letter lowercase, assumed to be method call
+
]]>
+ error-handling
+ - org.codenarc.rule.junit.JUnitSetUpCallsSuperRule + org.codenarc.rule.exceptions.ExceptionExtendsErrorRule MINOR - - - Rule that checks that if the JUnit setUp method is defined, that it includes a call to super.setUp().

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

- ]]>
- junit -
+ + + Errors are system exceptions. Do not extend them.

+

Examples:

+
+    class MyError extends Error { }  // violation
+    class MyError extends java.lang.Error { }  // violation
 
-  
-    org.codenarc.rule.junit.JUnitTearDownCallsSuperRule
-    MINOR
-    
-    
-    Rule that checks that if the JUnit tearDown method is defined, that it includes a call to super.tearDown(). 

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

- ]]>
- junit + class MyException extends Exception { } // OK +
]]>
+ error-handling + - org.codenarc.rule.junit.JUnitUnnecessarySetUpRule - MAJOR - - - Rule that checks checks for JUnit setUp() methods that contain only a call to super.setUp(). The method is then unnecessary.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

-

Here is an example of a violation:

+ org.codenarc.rule.exceptions.SwallowThreadDeathRule + MINOR + + + Detects code that catches java.lang.ThreadDeath without re-throwing it.

+

Example of violations:

-    class MyTest extends TestCase {
-        void setUp() {              // violation
-            super.setUp()
-        }
+    try {
+        def a = 0
+    } catch (ThreadDeath td) {
+        td.printStackTrace()
     }
-
-]]>
- junit +]]>
+ error-handling
+ - org.codenarc.rule.junit.JUnitUnnecessaryTearDownRule - MAJOR - - - Rule that checks checks for JUnit tearDown() methods that contain only a call to super.tearDown(). The method is then unnecessary.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

-

Here is an example of a violation:

+ org.codenarc.rule.exceptions.ExceptionNotThrownRule + MINOR + + + Checks for an exception constructor call without a throw as the last statement within a catch block. This rule treats any constructor call for a class named xxxException as an exception constructor call.

+

Example of violations:

-    class MyTest extends TestCase {
-        void tearDown() {               // violation
-            super.tearDown()
-        }
+    void execute() {
+        try { } catch(Exception e) { new Exception(e) }     // violation
     }
-
-]]>
- junit -
- - - org.codenarc.rule.junit.JUnitStyleAssertionsRule - MAJOR - - - This rule detects calling JUnit style assertions like assertEquals, assertTrue, assertFalse, assertNull, assertNotNull. Groovy 1.7 ships with a feature called the "power assert", which is an assert statement with better error reporting. This is preferable to the JUnit assertions.

- ]]>
- junit -
+ try { + doStuff() + } catch(DaoException e) { + log.warning("Ooops", e) + new ServiceException(e) // violation + } catch(Exception e) { + new SystemException(e) // violation + } - - - org.codenarc.rule.junit.UseAssertEqualsInsteadOfAssertTrueRule - MAJOR - - - This rule detects JUnit assertions in object equality. These assertions should be made by more specific methods, like assertEquals.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

- ]]>
- junit + try { + doStuff() + } catch(Exception e) { throw new DaoException(e) } // ok +]]> + error-handling
- + - org.codenarc.rule.junit.UseAssertFalseInsteadOfNegationRule + org.codenarc.rule.exceptions.ExceptionExtendsThrowableRule MINOR - - - In unit tests, if a condition is expected to be false then there is no sense using assertTrue with the negation operator. For instance, assertTrue(!condition) can always be simplified to assertFalse(condition).

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

- ]]>
- junit + + + Checks for classes that extend Throwable. Custom exception classes should subclass Exception or one of its descendants.

+

Example of violations:

+
+    class MyException extends Throwable { }   // violation
+
]]>
+ error-handling
- - - org.codenarc.rule.junit.UseAssertTrueInsteadOfAssertEqualsRule - MAJOR - - - This rule detects JUnit calling assertEquals where the first parameter is a boolean. These assertions should be made by more specific methods, like assertTrue or assertFalse.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

- All of the following examples can be simplified to assertTrue or remove the true literal:

-
-    assertEquals(true, foo())
-    assertEquals("message", true, foo())
-    assertEquals(foo(), true)
-    assertEquals("message", foo(), true)
-    assertEquals(false, foo())
-    assertEquals("message", false, foo())
-    assertEquals(foo(), false)
-    assertEquals("message", foo(), false)
+  
 
-    assert true == foo()                    // violation only if checkAssertStatements == true
-    assert foo() == true : "message"        // violation only if checkAssertStatements == true
-    assert false == foo()                   // violation only if checkAssertStatements == true
-    assert foo() == false : "message"       // violation only if checkAssertStatements == true
-
-]]>
- junit + + + org.codenarc.rule.formatting.BracesForClassRule + MINOR + + + Checks the location of the opening brace (\{) for classes. By default, requires them on the same line, but the sameLine property can be set to false to override this.

+

NOTE: This rule ignores annotation types, e.g. @interface MyAnnotation {}.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ convention - checkAssertStatements - - false + sameLine + true
- + - org.codenarc.rule.junit.UseAssertNullInsteadOfAssertEqualsRule - MAJOR - - - This rule detects JUnit calling assertEquals where the first or second parameter is null. These assertion should be made against the assertNull method instead.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

- ]]>
- junit + org.codenarc.rule.formatting.LineLengthRule + MINOR + + + Checks the maximum length for each line of source code. It checks for number of characters, so lines that include tabs may appear longer than the allowed number when viewing the file. The maximum line length can be configured by setting the length property, which defaults to 120.

+

NOTE: This rule does not support the @SuppressAnnotations annotation or the classname-based rule properties (applyToClassNames, doNotApplyToClassNames) to enable/disable the rule. If you want to specify or restrict where this rule is applied, you must use the file-based rule properties: applyToFileNames, doNotApplyToFileNames, applyToFilesMatching and doNotApplyToFilesMatching.

]]>
+ convention + + ignoreImportStatements + + true + + + ignoreLineRegex + + + + ignorePackageStatements + + true + + + length + + 120 +
- + - org.codenarc.rule.junit.UseAssertSameInsteadOfAssertTrueRule - MAJOR - - - This rule detects JUnit calling assertTrue or assertFalse where the first or second parameter is an Object#is() call testing for reference equality. These assertion should be made against the assertSame or assertNotSame method instead.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

-]]>
- junit + org.codenarc.rule.formatting.BracesForForLoopRule + MINOR + + + Checks the location of the opening brace (\{) for for loops. By default, requires them on the same line, but the sameLine property can be set to false to override this.

]]>
+ convention + + sameLine + true +
- + - org.codenarc.rule.junit.JUnitFailWithoutMessageRule + org.codenarc.rule.formatting.BracesForIfElseRule MINOR - - - This rule detects JUnit calling the fail() method without an argument. For better error reporting you should always provide a message.

- ]]>
- junit + + + Checks the location of the opening brace (\{) for if statements. By default, requires them on the same line, but the sameLine property can be set to false to override this.

]]>
+ convention + + elseOnSameLineAsClosingBrace + + true + + + elseOnSameLineAsOpeningBrace + + true + + + sameLine + + true + + + validateElse + + false +
- + - org.codenarc.rule.junit.UseAssertTrueInsteadOfNegationRule + org.codenarc.rule.formatting.BracesForMethodRule MINOR - - - In unit tests, if a condition is expected to be true then there is no sense using assertFalse with the negation operator. For instance, assertFalse(!condition) can always be simplified to assertTrue(condition).

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

- ]]>
- junit + + + Checks the location of the opening brace (\{) for constructors and methods. By default, requires them on the same line, but the sameLine property can be set to false to override this.

]]>
+ convention + + sameLine + true +
- + - org.codenarc.rule.junit.JUnitTestMethodWithoutAssertRule + org.codenarc.rule.formatting.BracesForTryCatchFinallyRule MINOR - - - This rule searches for test methods that do not contain assert statements. Either the test method is missing assert statements, which is an error, or the test method contains custom assert statements that do not follow a proper assert naming convention. Test methods are defined as public void methods that begin with the work test or have a @Test annotation. By default this rule applies to the default test class names, but this can be changed using the rule's applyToClassNames property. An assertion is defined as either using the assert keyword or invoking a method that starts with the work assert, like assertEquals, assertNull, or assertMyClassIsSimilar. Also, any method named should.* also counts as an assertion so that shouldFail methods do not trigger an assertion, any method that starts with fail counts as an assertion, and any method that starts with verify counts as an assertion. Since version 0.23 CodeNarc has support for JUnit's ExpectedException.

-

What counts as an assertion method can be overridden using the assertMethodPatterns property of the rule. The default value is this comma separated list of regular expressions:

-
-    String assertMethodPatterns = 'assert.*,should.*,fail.*,verify.*,expect.*'
-
-
-    'assert.*,should.*,fail.*,verify.*,ensure.*'
-
-]]>
- junit + + + Checks the location of the opening brace (\{) for try statements. By default, requires them on the line, but the sameLine property can be set to false to override this.

]]>
+ convention + + sameLine + true +
- + - org.codenarc.rule.junit.ChainedTestRule - MINOR - - - A test method that invokes another test method is a chained test; the methods are dependent on one another. Tests should be isolated, and not be dependent on one another.

-

Example of violations:

+ org.codenarc.rule.formatting.SpaceAfterCommaRule + MAJOR + + + Checks that there is at least one space or whitespace following each comma. That includes checks for method and closure declaration parameter lists, method call parameter lists, Map literals and List literals.

+

Known limitations:

+

* May not catch actual violations if the source line contains unicode character literals, e.g. '\\u00A0'

+

Examples of violations:

-    class MyTest extends GroovyTestCase {
-        public void testFoo() {
+    def value = calculate(1,399, 'abc')         // violation on parameter 399
 
-            // violations, calls test method on self
-            5.times { testBar() }
-            5.times { this.testBar() }
+    def method1(int a,String b) { }             // violation on parameter b
 
-            // OK, no violation: one arg method is not actually a test method
-            5.times { testBar(it) }
-        }
+    def closure1 = { int a,String b -> }        // violation on parameter b
 
-        private static void assertSomething() {
-            testBar() // violation, even if in helper method
-            this.testBar() // violation, even if in helper method
-        }
+    def list1 = [a,b, c]                        // violation on list element b
 
-        public void testBar() {
-            // ...
-        }
-    }
-
-]]>
- junit + def map1 = [a:1,b:2, c:3] // violation on map element b:2 +]]>
+ convention
- + - org.codenarc.rule.junit.CoupledTestCaseRule - MINOR - - - This rule finds test cases that are coupled to other test cases, either by invoking static methods on another test case or by creating instances of another test case. If you require shared logic in test cases then extract that logic to a new class where it can properly be reused. Static references to methods on the current test class are ignored.

-

Example of violations:

+ org.codenarc.rule.formatting.SpaceAfterSemicolonRule + MAJOR + + + Check that there is at least one space (blank) or whitespace following a semicolon that separates:

+

* multiple statements on a single line

+

* the clauses within a classic for loop, e.g. for (i=0;i<10;i++)

+

Examples of violations:

-    class MyTest extends GroovyTestCase {
-        public void testMethod() {
-            // violation, static method call to other test
-            MyOtherTest.helperMethod()
-
-            // violation, instantiation of another test class
-            new MyOtherTest()
+    def myMethod() {
+        println 1;println 2                         // violation
+        def closure = { x -> doStuff();x = 23; }    // violation
 
-            // no violation; same class
-            def input = MyTest.getResourceAsStream('sample.txt')
+        for (int i=0;i < 10;i++) {                  // violations (2)
+            for (int j=0; j < 10;j++) { }           // violation
         }
     }
-
-]]>
- junit +]]>
+ convention
- + - org.codenarc.rule.junit.UnnecessaryFailRule - MINOR - - - In a unit test, catching an exception and immediately calling Assert.fail() is pointless and hides the stack trace. It is better to rethrow the exception or not catch the exception at all.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

-

Example of violations:

+ org.codenarc.rule.formatting.SpaceAroundOperatorRule + MAJOR + + + Check that there is at least one space (blank) or whitespace around each binary operator, including: +, -, *, /, >>, <<, &&, ||, &, |, ?:, =, "as".

+

Do not check dot ('.') operator. Do not check unary operators (!, +, -, ++, --, ?.). Do not check array ('[') operator.

+

Known limitations:

+

* Does not catch violations of certain ternary expressions and standalone elvis operator (?:) expressions. * Does not catch violations of missing space around the equals operator (=) for fields initialization if the field is annotated.

+

Examples of violations:

-    public void testSomething() {
-        try {
-            something()
-        } catch (Exception e) {
-            fail(e.message)
-        }
-
-        try {
-            something()
-        } catch (Exception e) {
-            fail()
-        }
-    }
-
-]]>
- junit + def myMethod() { + 3+ 5-x*23/ 100 // violation + list <<123 // violation + other>> writer // violation + x=99 // violation + x&& y // violation + x ||y // violation + x &y // violation + x| y // violation + [1,2]as String // violation + } +]]>
+ convention + + ignoreParameterDefaultValueAssignments + + true +
- + - org.codenarc.rule.junit.SpockIgnoreRestUsedRule.fixed - MINOR - - - If Spock's @IgnoreRest annotation appears on any method, all non-annotated test methods are not executed. This behaviour is almost always unintended. It's fine to use @IgnoreRest locally during development, but when committing code, it should be removed.

-

The and properties determine which classes are considered Spock classes.

-

Example of violations:

+ org.codenarc.rule.formatting.SpaceBeforeOpeningBraceRule + MAJOR + + + Check that there is at least one space (blank) or whitespace before each opening brace ("\{") for method/class/interface declarations, closure expressions and block statements.

+

A closure expression a preceded by an opening parenthesis, an opening square brace ([), or a dollar sign ($) within a GString does not cause a violation.

+

Known limitations:

+

* May not catch actual violations if the source line contains unicode character literals, e.g. '\\u00A0'

+

Examples of violations:

-    public class MySpec extends spock.lang.Specification {
-        @spock.lang.IgnoreRest
-        def "my first feature"() {
-            expect: false
-        }
+    class MyClass{ }                            // violation
+    class MyOtherClass extends AbstractClass{ } // violation
 
-        def "my second feature"() {
-            given: def a = 2
+    interface MyInterface{ }                    // violation
 
-            when: a *= 2
+    enum MyEnum{ OK, BAD }                      // violation
 
-            then: a == 4
-        }
-    }
-
-]]>
- junit - - specificationClassNames - - + def myMethod(){ } // violation + + if (ready){ } // violation + + if (ready) { + } else{} // violation + + for (int i=0; i<10; i++){ } // violation + + for (String name in names){ } // violation + + for (String name: names){ } // violation + + while (ready){ } // violation + + try{ + } finally { } // violation + + try { + } catch(Exception e){ } // violation + + try { + } finally{ } // violation + + list.each{ name -> } // violation + + shouldFail(Exception){ doStuff() } // violation +]]>
+ convention - specificationSuperclassNames - - *Specification + checkClosureMapEntryValue + + true
- org.codenarc.rule.junit.JUnitLostTestRule - MINOR - - - This rule checks for classes that import JUnit 4 classes and contain a public, instance, void, no-arg method named * that is not annotated with the JUnit 4 @Test annotation.

-

Note: This rule should be disabled for Grails 2.x projects, since the Grails test framework can use AST Transformations to automatically annotate test methods.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

-

Example of violations:

+ org.codenarc.rule.formatting.SpaceAfterOpeningBraceRule + MAJOR + + + Check that there is at least one space (blank) or whitespace after each opening brace ("\{") for method/class/interface declarations, closure expressions and block statements.

+

Examples of violations:

-    import org.junit.Test
+    class MyClass{int count }                   // violation
 
-    class MyTestCase {
-        void testMe() { }           // missing @Test annotation
-    }
-
-]]>
- junit -
+ interface MyInterface {static final OK = 1 }// violation - - - org.codenarc.rule.junit.JUnitUnnecessaryThrowsExceptionRule - MAJOR - - - Check for throws clauses on JUnit test methods. That is not necessary in Groovy.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

-

Example of violations:

-
-    @Test
-    void shouldDoStuff() throws Exception { }           // violation
+    enum MyEnum {OK, BAD }                      // violation
 
-    @BeforeClass void initialize() throws Exception { } // violation
-    @Before void setUp() throws RuntimeException { }    // violation
-    @After void tearDown() throws Exception { }         // violation
-    @AfterClass void cleanUp() throws Exception { }     // violation
-    @Ignore void ignored() throws Exception { }         // violation
+    def myMethod() {int count }                 // violation
 
-    class MyTest extends GroovyTestCase {
-        void test1() throws Exception { }               // violation
-        public void test2() throws IOException { }      // violation
-    }
+    if (ready) {println 9 }                     // violation
 
-
-]]>
- junit + if (ready) { + } else {println 99} // violation + + for (int i=0; i<10; i++) {println i } // violation + + for (String name in names) {println name } // violation + + for (String name: names) {println name } // violation + + while (ready) {println time } // violation + + try {doStuff() // violation + } catch(Exception e) {x=77 } // violation + } finally {println 'error' } // violation + + list.each {name -> } // violation + + shouldFail(Exception) {doStuff() } // violation +]]> + convention + + checkClosureMapEntryValue + + true + + + ignoreEmptyBlock + + false +
- + - org.codenarc.rule.junit.JUnitPublicFieldRule + org.codenarc.rule.formatting.SpaceAfterClosingBraceRule MAJOR - - - Checks for public fields on a JUnit test class. There is usually no reason to have a public field (even a constant) on a test class.

-

Fields within interfaces and fields annotated with @Rule are ignored.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

-

Example of violations:

-
-    import org.junit.Test
-    class MyTestCase {
-        public int count                        // violation
-        public static final MAX_VALUE = 1000    // violation
-
-        @Test
-        void testMe() { }
-    }
-
-]]>
- junit + + + Check that there is at least one space (blank) or whitespace after each closing brace ("\{") for method/class/interface declarations, closure expressions and block statements.

+

A closure expression followed by a dot operator (.), a comma, a closing parenthesis, a closing square brace (]), the spread-dot operator (*.), a semicolon or the null-safe operator (?.) does not cause a violation.

+

Known limitations:

+

* May not catch actual violations if the source line contains unicode character literals, e.g. '\\u00A0'

+

Examples of violations and exceptions:

+
+    if (ready) { return 9 }else { }             // violation
+    try { doStuff() }finally { }                // violation
+
+    def matching = list.find { it.isReady() }.filter()  // no violation for dot operator
+    assert list.every { it.isReady() }, "Error"         // no violation for comma
+    def m = [a:123, b:{ println 7 },c:99]               // no violation for comma
+    processItems(list.select { it.isReady() })          // no violation for closing parenthesis
+    processItems([{ named("a") }, { named("b")}])       // no violation for closing square bracket
+    def names = records.findAll { it.age > 1 }*.name    // no violation for spread operator
+    list?.collect { it?.type }?.join(',')               // no violation for null-safe operator
+
]]>
+ convention + + checkClosureMapEntryValue + + true +
- + - org.codenarc.rule.junit.JUnitAssertEqualsConstantActualValueRule - MINOR - - - Reports usages of org.junit.Assert.assertEquals([message,] expected, actual) where the actual parameter is a constant or a literal. Most likely it was intended to be the expected value.

-

NOTE: This is a CodeNarc Enhanced Classpath Rule. It requires CodeNarc to have the application classes being analyzed, as well as any referenced classes, on the classpath.

-

Example of violations:

+ org.codenarc.rule.formatting.SpaceBeforeClosingBraceRule + MAJOR + + + Check that there is at least one space (blank) or whitespace before each closing brace ("\}") for method/class/interface declarations, closure expressions and block statements.

+

Known limitations:

+

* May not catch actual violations if the source line contains unicode character literals, e.g. '\\u00A0'

+

Examples of violations:

-    assertEquals(result, 2)
-    assertEquals("Message", result, 2)
-    assertEquals(result, 2.3d, 0.5d)
-    assertEquals("Message", result, 2.3d, 0.5d)
-
-]]>
- junit -
+ class MyClass { int count} // violation - - - org.codenarc.rule.junit.JUnitPublicPropertyRule.fixed - MINOR - - - Checks for public properties defined on JUnit test classes. There is typically no need to expose a public property (with public and methods) on a test class.

-

This rule sets the default value of the property to only match class names ending in 'Test', 'Tests' or 'TestCase'.

-

Example of violations:

-
-    import org.junit.Test
-    class MyTestCase {
-        static String id    // violation
-        def helper          // violation
-        String name         // violation
+    interface MyInterface { void doStuff()}     // violation
 
-        @Test
-        void testMe() { }
-    }
-
-]]>
- junit - - ignorePropertyNames - - -
+ enum MyEnum { OK, BAD} // violation - + def myMethod() { return 9} // violation - - org.codenarc.rule.logging.PrintlnRule - MINOR - - - Checks for calls to this.print(), this.println() or this.printf(). Consider using a standard logging facility instead.

- ]]>
- bug -
+ if (ready) { doStuff()} // violation - - org.codenarc.rule.logging.PrintStackTraceRule - MINOR - - - Checks for calls to Throwable.printStackTrace() or StackTraceUtils.printSanitizedStackTrace(Throwable). Consider using a standard logging facility instead.

- ]]>
- bug -
+ if (ready) { + } else { return 9} // violation - - org.codenarc.rule.logging.SystemErrPrintRule - MINOR - - - Checks for calls to System.err.print(), System.err.println() or System.err.printf(). Consider using a standard logging facility instead.

- ]]>
- bug -
+ for (int i=0; i<10; i++) { println i} // violation - - org.codenarc.rule.logging.SystemOutPrintRule - MINOR - - - Checks for calls to System.out.print(), System.out.println() or System.out.printf(). Consider using a standard logging facility instead.

-]]>
- bug + for (String name in names) { println name} // violation + + for (String name: names) { println name} // violation + + while (ready) { doStuff()} // violation + + try { doStuff()} // violation + catch(Exception e) { logError(e)} // violation + finally { cleanUp()} // violation + + list.each { name -> println name} // violation + + shouldFail(Exception) { doStuff()} // violation +]]> + convention + + checkClosureMapEntryValue + + true + + + ignoreEmptyBlock + + false +
- + - org.codenarc.rule.logging.LoggerForDifferentClassRule - MINOR - - - Checks for instantiating a logger for a class other than the current class. Checks for logger instantiations for Log4J, SLF4J, Logback, Apache Commons Logging and Java Logging API (java.util.logging).

-

This rule contains a parameter allowDerivedClasses. When set, a logger may be created about this.getClass().

-

Limitations:

-]]>
- bug + org.codenarc.rule.formatting.SpaceAfterIfRule + MAJOR + + + Check that there is exactly one space (blank) after the if keyword and before the opening parenthesis.

+

Examples of violations:

+
+    if(true) { }                            // violation
+    if  (true) { }                          // violation
+
]]>
+ convention
- + - org.codenarc.rule.logging.LoggingSwallowsStacktraceRule - MINOR - - - If you are logging an exception then the proper API is to call error(Object, Throwable), which will log the message and the exception stack trace. If you call error(Object) then the stacktrace may not be logged.

- ]]>
- bug + org.codenarc.rule.formatting.SpaceAfterWhileRule + MAJOR + + + Check that there is exactly one space (blank) after the while keyword and before the opening parenthesis.

+

Examples of violations:

+
+    while(true) { }             // violation
+    while  (true) { }           // violation
+
]]>
+ convention
- + - org.codenarc.rule.logging.LoggerWithWrongModifiersRule - MINOR - - - Logger objects should be declared private, static and final.

-

This rule has a property: allowProtectedLogger, which defaults to false. Set it to true if you believe subclasses should have access to a Logger in a parent class and that Logger should be declared protected or public.

-

This rule has a property: allowNonStaticLogger, which defaults to false. Set it to true if you believe a logger should be allowed to be non-static.

- ]]>
- bug + org.codenarc.rule.formatting.SpaceAfterForRule + MAJOR + + + Check that there is exactly one space (blank) after the for keyword and before the opening parenthesis.

+

Examples of violations:

+
+    for(name in names) { }                  // violation
+    for  (int i=0; i < 10; i++) { }         // violation
+
]]>
+ convention
- + - org.codenarc.rule.logging.MultipleLoggersRule - MINOR - - - This rule catches classes that have more than one logger object defined. Typically, a class has zero or one logger objects.

- ]]>
- bug + org.codenarc.rule.formatting.SpaceAfterSwitchRule + MAJOR + + + Check that there is exactly one space (blank) after the switch keyword and before the opening parenthesis.

+

Examples of violations:

+
+    switch(x) {                                 // violation
+        case 1: println 'one'
+    }
+    switch  (x) {                               // violation
+        case 1: println 'one'
+    }
+
]]>
+ convention
- - + - org.codenarc.rule.naming.AbstractClassNameRule.fixed - MINOR - - - Verifies that the name of an abstract class matches the regular expression specified in the regex property. If that property is null or empty, then this rule is not applied (i.e., it does nothing). It defaults to null, so this rule must be explicitly configured to be active. This rule ignores interfaces and is applied only to abstract classes.

- ]]>
- bug - - regex - - + org.codenarc.rule.formatting.SpaceAfterCatchRule + MAJOR + + + Check that there is exactly one space (blank) after the catch keyword and before the opening parenthesis.

+

Examples of violations:

+
+    try { } catch(Exception e) { }          // violation
+    try { } catch  (Exception e) { }        // violation
+
]]>
+ convention
+ - org.codenarc.rule.naming.ClassNameRule - MINOR - - - Verifies that the name of a class matches a regular expression. By default it checks that the class name starts with an uppercase letter and is followed by zero or more word characters (letters, numbers or underscores) or dollar signs ($).

- ]]>
- bug - - regex - - ([A-Z]\w*\$?)* - + org.codenarc.rule.formatting.SpaceAroundClosureArrowRule + MAJOR + + + Checks that there is at least one space (blank) or whitespace around each closure arrow (->) symbol.

+

Known limitations:

+

* Does not catch violations if the closure arrow (->) is on a separate line from the start of the closure.

+

Example of violations:

+
+    def closure1 = {->}                             // violation
+    def closure2 = { ->}                            // violation
+    def closure3 = {-> }                            // violation
+    def closure4 = { count-> println 123 }          // violation
+    def closure5 = { count, name ->println 123 }    // violation
+
]]>
+ convention
+ - org.codenarc.rule.naming.FieldNameRule.fixed - MINOR - - - Verifies that the name of each field matches a regular expression. By default it checks that fields that are not have field names that start with a lowercase letter and contains only letters or numbers. By default, field names start with an uppercase letter and contain only uppercase letters, numbers and underscores.

-

NOTE: This rule checks only regular of a class, not . In Groovy, are fields declared with no access modifier (public, protected, private). Thus, this rule only checks fields that specify an access modifier. For naming of , see PropertyNameRule.

-

The order of precedence for the regular expression properties is: staticFinalRegex, finalRegex, staticRegex and finally regex. In other words, the first regex in that list matching the modifiers for the field is the one that is applied for the field name validation.

- ]]>
- bug - - finalRegex - - - - ignoreFieldNames - - serialVersionUID - - - regex - - [a-z][a-zA-Z0-9]* - + org.codenarc.rule.formatting.SpaceAroundMapEntryColonRule + MAJOR + + + Check for proper formatting of whitespace around colons for literal Map entries. By default, no whitespace is allowed either before or after the Map entry colon, but you can change that through the configuration properties below.

+

Example of violations:

+
+    Map m1 = [myKey : 12345]            // violation (both before and after the colon)
+    println [a :[1:11, 2:22],           // violation on a (before colon)
+                b:[(Integer): 33]]      // violation on Integer (after colon)
+
]]>
+ convention - staticFinalRegex - - [A-Z][A-Z0-9_]* + characterAfterColonRegex + + \\S - staticRegex - + characterBeforeColonRegex + + \\S
+ - org.codenarc.rule.naming.InterfaceNameRule.fixed - MINOR - - - Verifies that the name of an interface matches the regular expression specified in the regex property. If that property is null or empty, then this rule is not applied (i.e., it does nothing). It defaults to null, so this rule must be explicitly configured to be active.

- ]]>
- bug - - regex - - + org.codenarc.rule.formatting.ClosureStatementOnOpeningLineOfMultipleLineClosureRule + MAJOR + + + Checks for closure logic on first line (after ->) for a multi-line closure. That breaks the symmetry of indentation (if the subsequent statements are indented normally), and that first statement can be easily missed when reading the code.

+

Example of violations:

+
+    def closure = { name -> println name
+        addToCounts()
+        println “done” }
+
]]>
+ convention
+ - org.codenarc.rule.naming.MethodNameRule.fixed - MINOR - - - Verifies that the name of each method matches a regular expression. By default it checks that the method name starts with a lowercase letter. Implicit method names are ignored (i.e., 'main' and 'run' methods automatically created for Groovy scripts).

- ]]>
- bug - - ignoreMethodNames - - - - regex - - [a-z]\w* - -
+ org.codenarc.rule.formatting.ConsecutiveBlankLinesRule + MAJOR + + + Makes sure there are no consecutive lines that are either blank or whitespace only. This reduces the need to scroll further than necessary when reading code, and increases the likelihood that a logical block of code will fit on one screen for easier comprehension.

+

Example of violation:

+
+    def name
 
-  
-    org.codenarc.rule.naming.PackageNameRule
-    MINOR
-    
-    
-    Verifies that the package name of a class matches a regular expression. By default it checks that the package name consists of only lowercase letters and numbers, separated by periods. 

- ]]>
- bug - - packageNameRequired - - false - - - regex - - [a-z]+[a-z0-9]*(\.[a-z0-9]+)* - -
- - org.codenarc.rule.naming.ParameterNameRule.fixed - MINOR - - - Verifies that the name of each parameter matches a regular expression. This rule applies to method parameters, constructor parameters and closure parameters. By default it checks that parameter names start with a lowercase letter and contains only letters or numbers.

- ]]>
- bug - - ignoreParameterNames - - - - regex - - [a-z][a-zA-Z0-9]* - + def value + + + + def id +
+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ convention
+ - org.codenarc.rule.naming.PropertyNameRule.fixed - MINOR - - - Verifies that the name of each property matches a regular expression. By default it checks that property names (other than ) start with a lowercase letter and contains only letters or numbers. By default, property names start with an uppercase letter and contain only uppercase letters, numbers and underscores.

-

NOTE: This rule checks only of a class, not regular . In Groovy, are fields declared with no access modifier (public, protected, private). For naming of regular , see FieldNameRule.

-

The order of precedence for the regular expression properties is: staticFinalRegex, finalRegex, staticRegex and finally regex. In other words, the first regex in that list matching the modifiers for the property is the one that is applied for the field name validation.

- ]]>
- bug - - finalRegex - - - - ignorePropertyNames - - - - regex - - [a-z][a-zA-Z0-9]* - - - staticFinalRegex - - [A-Z][A-Z0-9_]* - - - staticRegex - - + org.codenarc.rule.formatting.BlankLineBeforePackageRule + MAJOR + + + Makes sure there are no blank lines before the package declaration of a source code file.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ convention
+ - org.codenarc.rule.naming.VariableNameRule.fixed - MINOR - - - Verifies that the name of each variable matches a regular expression. By default it checks that non-final variable names start with a lowercase letter and contains only letters or numbers. By default, final variable names start with an uppercase letter and contain only uppercase letters, numbers and underscores.

-]]>
- bug - - finalRegex - - [A-Z][A-Z0-9_]* - - - ignoreVariableNames - - - - regex - - [a-z][a-zA-Z0-9]* - + org.codenarc.rule.formatting.FileEndsWithoutNewlineRule + MAJOR + + + Makes sure each source file ends with a newline character.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ convention
- + - org.codenarc.rule.naming.ConfusingMethodNameRule - MINOR - - - Checks for very confusing method names. The referenced methods have names that differ only by capitalization. This is very confusing because if the capitalization were identical then one of the methods would override the other.

-

Also, violations are triggered when methods and fields have very similar names.

+ org.codenarc.rule.formatting.MissingBlankLineAfterImportsRule + MAJOR + + + Makes sure there is a blank line after the imports of a source code file.

+

Example of violation:

-    class MyClass {
-        int total
-        int total() {
-            1
-        }
-    }
+    import org.apache.commons.lang.StringUtils
+    class MyClass { }                       // violation
 
-]]>
- bug +

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ convention
- + - org.codenarc.rule.naming.ObjectOverrideMisspelledMethodNameRule - MINOR - - - Verifies that the names of the most commonly overridden methods of Object: equals, hashCode and toString, are correct.

-

Here are some examples of code that produces violations:

+ org.codenarc.rule.formatting.MissingBlankLineAfterPackageRule + MAJOR + + + Makes sure there is a blank line after the package statement of a source code file.

+

Example of violation:

-    boolean equal(Object o) {}                  // violation
-    boolean equal(int other) {}                 // ok; wrong param type
-    boolean equal(Object o, int other) {}       // ok; too many params
-
-    boolean equaLS(Object o) {}                 // violation
-
-    int hashcode() {}                           // violation
-    int hashCOde() {}                           // violation
-    int hashcode(int value) {}                  // ok; not empty params
+  package org.codenarc
+  import java.util.Date                     // violation
 
-    String tostring() {}                        // violation
-    String toSTring() {}                        // violation
-    String tostring(int value) {}               // ok; not empty params
+  class MyClass {
+      void go() { /* ... */ }
+  }
 
-]]>
- bug +

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ convention
- + - org.codenarc.rule.naming.FactoryMethodNameRule - MINOR - - - A factory method is a method that creates objects, and they are typically named either buildFoo(), makeFoo(), or createFoo(). This rule enforces that only one naming convention is used. It defaults to allowing makeFoo(), but that can be changed using the property regex. The regex is a negative expression; it specifically bans methods named build* or create*. However, methods named build or build* receive some special treatment because of the popular Builder Pattern. If the 'build' method is in a class named *Builder then it does not cause a violation.

-

Builder methods are slightly different than factory methods.

-

Example of violations:

-
-    class MyClass {
+    org.codenarc.rule.formatting.TrailingWhitespaceRule
+    MAJOR
+    
+    
+    Checks that no lines of source code end with whitespace characters.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ convention + - // violation. Factory methods should be named make() - def create() { - } + + + org.codenarc.rule.formatting.BlockEndsWithBlankLineRule + MAJOR + + + Checks that code blocks such as method bodies, closures and control structure bodies do not end with an empty line.

+

Example of violations:

+
+    boolean not(boolean value) {
+        !value
+                                // violation
+    }
 
-        // violation. Factory methods should be named make()
-        def createSomething() {
-        }
+    3.times {
+        println 'hello!'
+                                // violation
+    }
 
-        // violation. Builder method not in class named *Builder
-        def build() {
-        }
+    for (value in []) {
+        println value
+                                // violation
+    }
 
-        // violation. Builder method not in class named *Builder
-        def buildSomething() {
-        }
+    for (i = 0; i < 3; i++) {
+        println i
+                                // violation
+    }
 
-        // this is OK because it is called make
-        def make() {
-        }
+    int j = 0
+    while (j < 3) {
+      println j++
+                                // violation
+    }
 
-        // this is also OK
-        def makeSomething() {
-        }
+    if (ready) {
+        println 'ready'
+                                // violation
+    } else {
+        println 'not ready'
+                                // violation
+    }
 
-        // OK, overriding a parent
-        @Override
-        build() { }
+    try {
+        throw new Exception()
+                                // violation
+    } catch (Exception e) {
+        println 'exception'
+                                // violation
+    } finally {
+        println 'finally'
+                                // violation
+    }
 
+    switch (true) {
+        default:
+            println 'switch'
+                                // violation
     }
 
-    class WidgetBuilder {
+    // Known Limitation: If a Closure is within another expression and the closing brace is not followed by anything else on the same line
 
-        // OK, the class name ends in Builder
-        def build() {
+    def list = [
+        123,
+        { id ->
+                                // Known limitation: should be a violation, but is not
         }
-    }
-
-]]>
- bug - - regex - - (build.*\|create.*) - + ] +
]]>
+ convention
- + - org.codenarc.rule.naming.ClassNameSameAsFilenameRule - MINOR - - - Reports files containing only one top level class / enum / interface which is named differently than the file.

- ]]>
- bug + org.codenarc.rule.formatting.BlockStartsWithBlankLineRule + MAJOR + + + Checks that code blocks such as method bodies, closures and control structure bodies do not start with an empty line.

+

Example of violations:

+
+    boolean not(boolean value) {
+                                // violation
+        !value
+    }
+
+    3.times {
+                                // violation
+        println 'hello!'
+    }
+
+    for (value in []) {
+                                // violation
+        println value
+    }
+
+    for (i = 0; i < 3; i++) {
+                                // violation
+        println i
+    }
+
+    int j = 0
+    while (j < 3) {
+                                // violation
+      println j++
+    }
+
+    if (ready) {
+                                // violation
+        println 'ready'
+    } else {
+                                // violation
+        println 'not ready'
+    }
+
+    try {
+                                // violation
+        throw new Exception()
+    } catch (Exception e) {
+                                // violation
+        println 'exception'
+    } finally {
+                                // violation
+        println 'finally'
+    }
+
+    switch (true) {
+                                // violation
+        default:
+            println 'switch'
+    }
+
+
]]>
+ convention
- + - org.codenarc.rule.naming.PackageNameMatchesFilePathRule.fixed - MINOR - - - A package source file's path should match the package declaration.

- ]]>
- bug + org.codenarc.rule.formatting.IndentationRule + MAJOR + + + Check the indentation (spaces only; not tabs) for class, field and method declarations, and statements.

+

This rule is limited, and somewhat opinionated. The default is 4 spaces per indentation level.

+

Known Limitations include:

+

* Checks spaces only (not tabs)

+

* Does not check comments

+

* Does not check line-continuations (i.e., checks only the first line of a statement)

+

* Does not check multiple statements/members on the same line (only checks the first one)

+

* Does not check Map entry expressions

+

* Does not check List expressions

+

* Does not check calls to this() and super() within a constructor

+

* When classes, methods or fields have annotations, the indentation of the annotation is checked, not the actual member. And only the first annotation is checked, if there is more than one.

+

Example of violations:

+
+// Indent Levels:
+0...1...2...3...4...5
+
+class MyClass {                                 // CORRECT
+    protected int count                         // CORRECT
+  private static final NAME = "Joe"             // violation
+           def max, min                         // violation on "max" only
+
+ private String doStuff() {                     // violation
+        def internalCounts = [1, 4, 2]          // CORRECT
+            id.trim()                           // violation
+    }
+
+    private void executeOtherOne() {            // CORRECT
+        try {
+          executeWithArgs(args)                 // violation
+        } catch(Throwable t) {
+                       t.printStackTrace()      // violation
+        }
+        finally {
+                closeResources()                // violation
+        }
+    }
+}
+
]]>
+ convention - groupId - part of a package name, that will appear within all checked package names. It must also map to the file path for the correspondin source file. For instance, a of <"org.sample"> means that for all classes that specify a package, that package name must include <"org.sample">, and the source file must exist under an "org/sample" directory. Then, a MyClass class in a org.sample.util package must be defined in a "MyClass.groovy" file within a <"org/sample/util"> directory. That directory can be the child of any arbitrary , e.g. "src/main/groovy". To find the sub-path relevant for the package the rule searches for the first appearance of in the file path. It's to configure this. If groupId is null or empty, this rule does nothing. ]]> + spacesPerIndentLevel + + 4
- + - org.codenarc.rule.naming.ClassNameSameAsSuperclassRule - MINOR - - - Checks for any class that has an identical name to its superclass, other than the package. This can be very confusing.

-

Also see FindBugs NM_SAME_SIMPLE_NAME_AS_SUPERCLASS rule.

-

Example of violations:

+ org.codenarc.rule.formatting.ClassEndsWithBlankLineRule + MAJOR + + + Check whether the class ends with a blank line. By default, it enforces that there must be a blank line before the closing class brace, except:

+

* If the class is synthetic (generated)

+

* If the class is empty and is written in a single line

+

* If the class is a Script class

+

A blank line is defined as any line that does not contain any visible characters.

+

This rule can be configured with the following properties:

+

Example of violations:

+

If ignoreSingleLineClasses is true and blankLineRequired is true

+
+            class Foo {
+                int a
+
+                void hi() {
+                }
+            }
+
+

If ignoreSingleLineClasses is false and blankLineRequired is true

-    class MyClass extends other.MyClass         // violation
+            class Foo extends Bar<String> { }
 
-]]>
- bug +

If ignoreSingleLineClasses is true and blankLineRequired is false

+
+            class Foo {
+                int a
+
+                void hi() {
+                }
+
+            }
+
+

If ignoreSingleLineClasses is false and blankLineRequired is false

+
+            class Foo {
+                int a
+
+                void hi() {
+                }
+
+            }
+
]]>
+ convention + + blankLineRequired + + true + + + ignoreInnerClasses + + false + + + ignoreSingleLineClasses + + true +
- + - org.codenarc.rule.naming.InterfaceNameSameAsSuperInterfaceRule - MINOR - - - Checks for any interface that has an identical name to its super-interface, other than the package. This can be very confusing.

-

Example of violations:

+ org.codenarc.rule.formatting.ClassStartsWithBlankLineRule + MAJOR + + + Check whether the class starts with a blank line. By default, it enforces that there must be a blank line after the opening class brace, except:

+

* If the class is synthetic (generated)

+

* If the class is empty and is written in a single line

+

* If the class is a Script class

+

A blank line is defined as any line that does not contain any visible characters.

+

This rule can be configured with the following properties:

+

Example of violations:

+

If ignoreSingleLineClasses is true and blankLineRequired is true

+
+            class Foo {
+                int a
+
+                void hi() {
+                }
+            }
+
+

If ignoreSingleLineClasses is false and blankLineRequired is true

-    interface MyInterface extends other.MyInterface { }     // violation
+            class Foo extends Bar<String> { }
 
-]]>
- bug +

If ignoreSingleLineClasses is true and blankLineRequired is false

+
+            class Foo {
+
+                int a
+
+                void hi() {
+                }
+
+            }
+
+

If ignoreSingleLineClasses is false and blankLineRequired is false

+
+            class Foo {
+                int a
+
+                void hi() {
+                }
+
+            }
+
]]>
+ convention + + blankLineRequired + + true + + + ignoreInnerClasses + + false + + + ignoreSingleLineClasses + + true +
- + - org.codenarc.rule.size.ClassSizeRule + org.codenarc.rule.generic.IllegalRegexRule.fixed MAJOR - - - Checks if the size of a class exceeds the number of lines specified by the maxLines property.

- ]]>
+ + + Checks for a specified illegal regular expression within the source code.

+

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule name and regex, and (optionally) customized violationMessage and priority.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
bug - maxLines - - 1000 + regex +
- org.codenarc.rule.size.CyclomaticComplexityRule.fixed - MINOR - - - Calculates the for methods/classes and checks against configured threshold values.

-

The maxMethodComplexity property holds the threshold value for the cyclomatic complexity value for each method. If this value is non-zero, a method with a cyclomatic complexity value greater than this value is considered a violation.

-

The maxClassAverageMethodComplexity property holds the threshold value for the average cyclomatic complexity value for each class. If this value is non-zero, a class with an average cyclomatic complexity value greater than this value is considered a violation.

-

This rule treats "closure fields" as methods. If a class field is initialized to a Closure (ClosureExpression), then that Closure is analyzed and checked just like a method.

- The value is calculated as follows:

-

-]]>
+ org.codenarc.rule.generic.RequiredRegexRule.fixed + MAJOR + + + Checks for a specified regular expression that must exist within the source code.

+

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule name and regex, and (optionally) customized violationMessage and priority.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
bug - ignoreMethodNames - - - - maxClassAverageMethodComplexity - value allowed for a class, calculated as the average complexity of its methods or "closure fields". If zero or , then do not check average class-level complexity. ]]> - 20 - - - maxClassComplexity - value allowed for a class, calculated as the total complexity of its methods or "closure fields". If zero or , then do not check total class-level complexity. ]]> - 0 - - - maxMethodComplexity - value allowed for a single method (or "closure field"). If zero or , then do not check method-level complexity. ]]> - 20 + regex +
- org.codenarc.rule.size.MethodCountRule - MINOR - - - Checks if the number of methods within a class exceeds the number of lines specified by the maxMethod property.

-

A class with too many methods is probably a good suspect for refactoring, in order to reduce its complexity and find a way to have more fine grained objects.

- ]]>
+ org.codenarc.rule.generic.RequiredStringRule.fixed + MAJOR + + + Checks for a specified text string that must exist within the source code.

+

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule name and string, and (optionally) customized violationMessage and priority.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
bug - maxMethods - - 30 + string +
- org.codenarc.rule.size.MethodSizeRule.fixed - MAJOR - - - Checks if the size of a method exceeds the number of lines specified by the maxLines property.

-

Known Limitations:

-]]>
+ org.codenarc.rule.generic.StatelessClassRule.fixed + MINOR + + + Checks for non-final fields on a class. The intent of this rule is to check a configured set of classes that should remain "stateless" and reentrant. One example might be Grails service classes which are singletons, by default, and so they should be reentrant.

+

This rule ignores final fields (either instance or static). Fields that are static and non-final, however, do cause a violation.

+

This rule also ignores all classes annotated with the @Immutable transformation. See http://groovy.codehaus.org/Immutable+transformation.

+

This rule also ignores all fields annotated with the @Inject annotation.

+

You can configure this rule to ignore certain fields either by name or by type. This can be useful to ignore fields that hold references to (static) dependencies (such as DAOs or Service objects) or static configuration.

+

Note that you can use the standard rule properties, such as applyToClassNames, doNotApplyToFileNames and applyToFilesMatching to only apply this rule to a subset of all classes/files. These rule properties are described in Standard Properties for Configuring Rules.

+

[[1]] The ignoreFieldTypes property matches the field type name as indicated in the field declaration, only including a full package specification IF it is included in the source code. For example, the field declaration BigDecimal value matches an ignoreFieldTypes value of BigDecimal, but not java.lang.BigDecimal.

+

[[2]] There is one exception for the ignoreFieldTypes property: if the field is declared with a modifier/type of def, then the type resolves to java.lang.Object.

+

[[3]] At least one of the (standard) applyToClassNames, applyToFileNames or applyToFilesMatching properties must be set (i.e., not null or empty) or else this rule does nothing. In other words, you must configure this rule to apply to a specific set of classes or files.

+

[[4]] This rule will not catch violations of true statelessness/reentrancy if you define a final field whose value is itself mutable, e.g. a final HashMap.

]]>
bug - ignoreMethodNames - + addToIgnoreFieldNames + - maxLines - - 100 + ignoreFieldNames + + + + ignoreFieldTypes +
+ - org.codenarc.rule.size.NestedBlockDepthRule + org.codenarc.rule.generic.IllegalPackageReferenceRule.fixed MINOR - - - Checks for blocks or closures nested more deeply than a configured maximum number. Blocks include if, for, while, switch, try, catch, finally and synchronized blocks/statements, as well as closures.

-

Methods calls, constructor calls, and property access through Builder objects are ignore. For instance, this code does not cause a violation:

+ + + Checks for reference to any of the packages configured in packageNames.

+

Note that you can use the standard rule properties, such as applyToClassNames, doNotApplyToFileNames and applyToFilesMatching to only apply this rule to a subset of all classes/files. These rule properties are described in Standard Properties for Configuring Rules.

+

This rule can be useful for governance and enforcement of architectural layering. For instance, making sure that view or model classes, for instance, do not contain references to JDBC-specific packages (e.g. java.sql and javax.sql).

+

Here is an example configuration of this rule used to ensure that JDBC packages/classes are only referenced within DAO classes:

-    myBuilder.root {
-        foo {
-            bar {
-                baz {
-                    quix {
-                        qux {
-                            quaxz {
-                            }
-                        }
-                    }
-                }
-            }
+    ruleset {
+        description "Example CodeNarc Ruleset"
+
+        // ...
+
+        IllegalPackageReference {
+            name = 'UseJdbcOnlyInDaoClasses'
+            priority = 2
+            packageNames = 'groovy.sql, java.sql, javax.sql'
+            doNotApplyToClassNames = 'com.example.framework.dao.*, *Dao, *DaoImpl'
+            description = 'Reference to JDBC packages should be restricted to DAO classes.'
         }
     }
 
-]]>
+

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule name and packageNames, and (optionally) customized violationMessage and priority.

]]>
bug - ignoreRegex - - .*(b|B)uilder + packageNames + +
+ + + + org.codenarc.rule.generic.IllegalClassReferenceRule.fixed + MINOR + + + Checks for reference to any of the classes configured in classNames.

+

Note that you can use the standard rule properties, such as applyToClassNames, doNotApplyToFileNames and applyToFilesMatching to only apply this rule to a subset of all classes/files. These rule properties are described in Standard Properties for Configuring Rules.

+

This rule can be useful for governance and enforcement of architectural layering. For instance, making sure that view or model classes, for instance, do not contain references to DAO classes (e.g., *Dao).

+

Here is an example configuration of this rule used to ensure that DAO classes are not referenced from within model classes:

+
+    ruleset {
+        description "Example CodeNarc Ruleset"
+
+        // ...
+
+        IllegalClassReference {
+            name = 'DoNotReferenceDaoFromModelClasses'
+            priority = 2
+            classNames = '*Dao'
+            applyToClassNames = 'com.example.model.*'
+            description = 'Do not reference DAOs from model classes.'
+        }
+    }
+
+

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule name and classNames, and (optionally) customized violationMessage and priority.

]]>
+ bug - maxNestedBlockDepth - - 5 + classNames +
- + - org.codenarc.rule.size.CrapMetricRule.fixed + org.codenarc.rule.generic.IllegalClassMemberRule.fixed MINOR - - - Calculates the C.R.A.P. (Change Risk Anti-Patterns) metric score for methods/classes and checks against configured threshold values.

-

The metric score is based on the and test coverage for individual methods. A method with a value greater than the maxMethodCrapScore property causes a violation. Likewise, a class that has an (average method) value greater than the maxClassAverageMethodCrapScore property causes a violation.

-

NOTE: This rule requires the GMetrics[3] jar, version 0.5 (or later), on the classpath, as well as a Cobertura[4]-[6] XML coverage file. If either of these prerequisites is not available, this rule logs a warning messages and exits (i.e., does nothing).

-

The maxMethodCrapScore property holds the threshold value for the CRAP value for each method. If this value is non-zero, a method with a cyclomatic complexity value greater than this value is considered a violation.

-

The maxClassAverageMethodCrapScore property holds the threshold value for the average CRAP value for each class. If this value is non-zero, a class with an average cyclomatic complexity value greater than this value is considered a violation.

-

NOTE: This rule does NOT treat as methods (unlike some of the other size/complexity rules).

-]]>
+ + + Checks for classes containing fields/properties/methods matching configured illegal member modifiers or not matching any of the configured allowed member modifiers.

+

Modifiers for fields and methods include:

+

* public

+

* protected

+

* private

+

* static

+

* final

+

* volatile (fields only)

+

* transient (fields only)

+

Modifiers for properties are only:

+

* static

+

* final

+

Note that you must use the standard rule properties, such as applyToClassNames, doNotApplyToFileNames and applyToFilesMatching to apply this rule to a subset of all classes/files. These rule properties are described in Standard Properties for Configuring Rules.

+

Example of violations for methods:

+
+    // IllegalClassMember.allowedMethodModifiers = 'public final, private, protected static'
+
+    class MyClass {
+        public method1() { }            // violation
+        protected method2() { }         // violation
+        protected static method3() { }
+    }
+
+

Example of violations for properties:

+
+    // IllegalClassMember.illegalPropertyModifiers = 'final'
+
+    class MyClass {
+        def property1
+        final property2         // violation
+        static property3
+    }
+
+

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule name and classNames, and (optionally) customized violationMessage and priority.

+

[[1]] At least one the illegalFieldModifiers, allowedFieldModifiers, illegalPropertyModifiers, allowedPropertyModifiers, illegalMethodModifiers or allowedMethodModifiers properties must be set (i.e., not null or empty) or else this rule does nothing. In other words, you must configure this rule with at least one kind of illegal or allowed class member.

+

[[2]] At least one of the (standard) applyToClassNames, applyToFileNames or applyToFilesMatching properties must be set (i.e., not null or empty) or else this rule does nothing. In other words, you must configure this rule to apply to a specific set of classes or files.

]]>
bug - coberturaXmlFile - + allowedFieldModifiers + - ignoreMethodNames - + allowedMethodModifiers + - maxClassAverageMethodCrapScore - average metric value allowed for a class, calculated as the average CRAP value of its methods. If zero or , then do not check the average class-level CRAP value. ]]> - 30 + allowedPropertyModifiers + - maxClassCrapScore - metric value allowed for a class, calculated as the total CRAP value of its methods. If zero or , then do not check class-level CRAP value. ]]> - 0 + ignoreMethodNames + - maxMethodCrapScore - metric value allowed for a single method. If zero or , then do not check method-level complexity. ]]> - 30 + ignoreMethodsWithAnnotationNames + -
- - - - org.codenarc.rule.size.AbcMetricRule.fixed - MINOR - - - Calculates the size metric for methods/classes and checks against configured threshold values.

-

The maxMethodAbcScore property holds the threshold value for the ABC score for each method. If this value is non-zero, a method with an ABC score greater than this value is considered a violation. The value does not have to be an integer (e.g., 1.7 is allowed).

-

The maxClassAverageMethodAbcScore property holds the threshold value for the average ABC score for each class. If this value is non-zero, a class with an average ABC score value greater than this value is considered a violation. The value does not have to be an integer.

-

The maxClassAbcScore property holds the threshold value for the total ABC score value for each class. If this value is non-zero, a class with a total ABC score greater than this value is considered a violation. The value does not have to be an integer.

-

This rule treats "closure fields" as methods. If a class field is initialized to a Closure (ClosureExpression), then that Closure is analyzed and checked just like a method.

- The score is calculated as follows: The metric measures size by counting the number of Assignments (A), Branches (B) and Conditions (C) and assigns a single numerical score calculated as:

-

|ABC| = sqrt((A*A)+(B*B)+(C*C))

-

The calculation rules for Groovy:

-]]>
- bug - ignoreMethodNames - + illegalFieldModifiers + - maxClassAbcScore - score allowed for a class, calculated as the total ABC score of its methods or "closure fields". If zero or , then do not check class-level scores. ]]> - 0 + illegalMethodModifiers + - maxClassAverageMethodAbcScore - score allowed for a class, calculated as the average score of its methods or "closure fields". If zero or , then do not check class-level average scores. ]]> - 60 + illegalPropertyModifiers + +
+ + + + org.codenarc.rule.generic.IllegalStringRule.fixed + MINOR + + + Checks for a specified illegal string within the source code.

+

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule name and string, and (optionally) customized violationMessage and priority.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ bug - maxMethodAbcScore - score allowed for a single method (or "closure field"). If zero or , then do not check method-level scores. ]]> - 60 + string +
- + - org.codenarc.rule.size.ParameterCountRule + org.codenarc.rule.generic.IllegalSubclassRule.fixed MINOR - - - Checks if the number of parameters in method/constructor exceeds the number of parameters specified by the maxParameters property.

-

Example of violations:

-
-    void someMethod(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6) { // violation
-    }
-
-    class SampleClass {
-        SampleClass(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7) { // violation
-        }
-    }
-
-]]>
+ + + Checks for classes that extend one of the specified set of illegal superclasses.

+

A RuleSet can contain any number of instances of this rule, but each should be configured with a unique rule name and string, and (optionally) customized violationMessage and priority.

]]>
bug - maxParameters - - 5 + superclassNames +
- + - org.codenarc.rule.unnecessary.UnnecessaryBooleanExpressionRule + org.codenarc.rule.grails.GrailsPublicControllerMethodRule.fixed + MINOR + + + Rule that checks for public methods on Grails controller classes. Static methods are ignored.

+

Grails controller actions and interceptors are defined as properties on the controller class. Public methods on a controller class are unnecessary. They break encapsulation and can be confusing.

+

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/controllers' folder. You can override this with a different regular expression value if appropriate.

+

This rule also sets the default value of applyToClassNames to only match class names ending in 'Controller'. You can override this with a different class name pattern (String with wildcards) if appropriate.

]]>
+ grails + + ignoreMethodNames + + +
+ + + org.codenarc.rule.grails.GrailsServletContextReferenceRule + MINOR + + + Rule that checks for references to the servletContext object from within Grails controller and taglib classes.

+

This rule is intended as a "governance" rule to enable monitoring and controlling access to the servletContext from within application source code. Storing objects in the servletContext may inhibit scalability and/or performance and should be carefully considered. Furthermore, access to the servletContext is not synchronized, so reading/writing objects from the servletContext must be manually synchronized, as described in The Definitive Guide to Grails (2nd edition).

+

Note that this rule does not check for direct access to the servletContext from within GSP (Groovy Server Pages) files.

+

Enabling this rule may make most sense in a team environment where team members exhibit a broad range of skill and experience levels. Appropriate servletContext access can be configured as exceptions to this rule by configuring either the doNotApplyToFilenames or doNotApplyToFilesMatching property of the rule. And, as always, it is easy to just turn off the rule if it does not make sense it your environment.

+

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/controllers' or 'grails-app/taglib' folders. You can override this with a different regular expression value if appropriate.

]]>
+ grails +
+ + + org.codenarc.rule.grails.GrailsStatelessServiceRule + MINOR + + + Checks for non-final fields on a Grails service class. Grails service classes are singletons by default, and so they should be reentrant. In most cases, this implies (or at least encourages) that they should be stateless.

+

This rule ignores (i.e., does not cause violations for) the following:

+

* All final fields (either instance or static). Note that fields that are static and non-final, however, do cause a violation.

+

* Non-static properties (i.e., no visibility modifier specified) declared with def.

+

* All classes annotated with the @Immutable transformation. See http://groovy.codehaus.org/Immutable+transformation.

+

* All fields annotated with the @Inject annotation.

+

* All fields with names matching the ignoreFieldNames property.

+

* All fields with types matching the ignoreFieldTypes property.

+

The ignoreFieldNames property of this rule is preconfigured to ignore the standard Grails service configuration field names ('scope', 'transactional') and the standard injected bean names ('dataSource', 'sessionFactory'), as well as all other field names ending with 'Service'.

+

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/services' folder. You can override this with a different regular expression value if appropriate.

+

This rule also sets the default value of applyToClassNames to only match class names ending in 'Service'. You can override this with a different class name pattern (String with wildcards) if appropriate.

+

[[1]] The ignoreFieldTypes property matches the field type name as indicated in the field declaration, only including a full package specification IF it is included in the source code. For example, the field declaration BigDecimal value matches an ignoreFieldTypes value of BigDecimal, but not java.lang.BigDecimal.

+

[[2]] There is one exception for the ignoreFieldTypes property: if the field is declared with a modifier/type of def, then the type resolves to java.lang.Object.

]]>
+ grails + + addToIgnoreFieldName + + + + ignoreFieldNames + + 'dataSource,scope,sessionFactory, + + + ignoreFieldTypes + + +
+ + + + org.codenarc.rule.grails.GrailsDomainHasToStringRule + MINOR + + + Checks that Grails domain classes redefine toString().

+

Ignores classes annotated with @ToString or @Canonical.

+

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/domain' folder. You can override this with a different regular expression value if appropriate.

]]>
+ grails +
+ + + + org.codenarc.rule.grails.GrailsDomainHasEqualsRule + MINOR + + + Checks that Grails domain classes redefine equals().

+

Ignores classes annotated with @EqualsAndHashCode or @Canonical.

+

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/domain' folder. You can override this with a different regular expression value if appropriate.

]]>
+ grails +
+ + + + org.codenarc.rule.grails.GrailsDuplicateMappingRule + MINOR + + + Check for duplicate name in a Grails domain class mapping. Duplicate names/entries are legal, but can be confusing and error-prone.

+

NOTE: This rule does not check that the values of the entries are duplicated, only that there are two entries with the same name.

+

Example of violations:

+
+    class Person {
+        String firstName
+        String lastName
+
+        static mapping = {
+            table 'people'
+            firstName column: 'First_Name'
+            lastName column: 'Last_Name'
+            firstName column: 'First_Name'      // violation
+            table 'people2'                     // violation
+        }
+    }
+
]]>
+ grails +
+ + + + org.codenarc.rule.grails.GrailsDuplicateConstraintRule + MINOR + + + Check for duplicate name in a Grails domain class constraints. Duplicate names/entries are legal, but can be confusing and error-prone.

+

NOTE: This rule does not check that the values of the entries are duplicated, only that there are two entries with the same name.

+

Example of violations:

+
+    class Person {
+        String firstName
+        String lastName
+
+        static constraints = {
+            firstName nullable:true
+            lastName nullable:true, maxSize:30
+            firstName nullable:false                // violation
+        }
+    }
+
]]>
+ grails +
+ + + + org.codenarc.rule.grails.GrailsDomainReservedSqlKeywordNameRule + MINOR + + + Forbids usage of SQL reserved keywords as class or field names in Grails domain classes. Naming a domain class (or its field) with such a keyword causes SQL schema creation errors and/or redundant table/column name mappings.

+

Note: due to limited type information available during CodeNarc's operation, this rule will report fields of type java.io.Serializable, but not of its implementations. Please specify any implementations used as domain properties in additionalHibernateBasicTypes.

]]>
+ grails + + additionalHibernateBasicTypes + + + + additionalReservedSqlKeywords + + +
+ + + + org.codenarc.rule.grails.GrailsDomainWithServiceReferenceRule + MINOR + + + Checks that Grails Domain classes do not have Service classes injected.

+

This rule sets the default value of applyToFilesMatching to only match files under the 'grails-app/domain' folder. You can override this with a different regular expression value if appropriate.

]]>
+ grails +
+ + + + org.codenarc.rule.grails.GrailsMassAssignmentRule + MINOR + + + Untrusted input should not be allowed to set arbitrary object fields without restriction.

+

Example of violations:

+
+   // Person would be a grails domain object
+   def person = new Person(params)
+   person.save()
+
+   // or using .properties
+   def person = Person.get(1)
+   person.properties = params
+   person.save()
+
]]>
+ grails +
+ + + + org.codenarc.rule.grails.GrailsDomainStringPropertyMaxSizeRule + MINOR + + + String properties in Grails domain classes have to define maximum size otherwise the property is mapped to VARCHAR(255) causing runtime exceptions to occur. To fix this issue either declare size or maxSize constraint for the property inside constraints DSL closure of your Grails domain class or declare the type of the property inside mapping DSL closure. If you use the second option inside mapping DSL closure then please pay attention that the value of type is not checked so using for example VARCHAR(50) would still cause runtime exceptions.

+

Example of violations:

+
+    // both firstName and lastName will probably have database limit of 255 characters
+    // which is not validated by Grails validation causing runtime JDBC exception
+    class Person {
+
+        String firstName
+        String lastName
+
+        static constraints = {
+            firstName nullable:true
+            lastName nullable:true
+        }
+    }
+
+

Example of valid configuration:

+
+    class Person {
+
+        String firstName
+        String lastName
+
+        static constraints = {
+            firstName nullable:true, maxSize: 255
+            lastName nullable:true
+        }
+
+        static mapping = {
+            lastName type: 'text'
+        }
+    }
+
]]>
+ grails +
+ + + + + + org.codenarc.rule.groovyism.ExplicitArrayListInstantiationRule + MINOR + + + This rule checks for explicit calls to the no-argument constructor of ArrayList. In Groovy, it is best to write new ArrayList() as [], which creates the same object.

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToAndMethodRule + MINOR + + + This rule detects when the and(Object) method is called directly in code instead of using the & operator. A groovier way to express this: a.and(b) is this: a & b. This rule can be configured to ignore this.and(Object) using the ignoreThisReference property. It defaults to true, so even and(x) will not trigger a violation. The default is true because and appears commonly in Grails criteria.

+

This rule also ignores all calls to super.and(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToCompareToMethodRule + MINOR + + + This rule detects when the compareTo(Object) method is called directly in code instead of using the <=>, >, >=, <, and <= operators. A groovier way to express this: a.compareTo(b) is this: a <=> b, or using the other operators. Here are some other ways to write groovier code:

+
+    a.compareTo(b) == 0               // can be replaced by: a == b
+    a.compareTo(b)                    // can be replaced by: a <=> b
+    a.compareTo(b) > 0                // can be replaced by: a > b
+    a.compareTo(b) >= 0               // can be replaced by: a >= b
+    a.compareTo(b) < 0                // can be replaced by: a < b
+    a.compareTo(b) <= 0               // can be replaced by: a <= b
+
+

This rule can be configured to ignore this.compareTo(Object) using the ignoreThisReference property. It defaults to false, so even compareTo(x) will trigger a violation.

+

This rule also ignores all calls to super.compareTo(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToDivMethodRule + MINOR + + + This rule detects when the div(Object) method is called directly in code instead of using the / operator. A groovier way to express this: a.div(b) is this: a / b. This rule can be configured to ignore div.xor(Object) using the ignoreThisReference property. It defaults to false, so even div(x) will trigger a violation.

+

This rule also ignores all calls to super.div(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToEqualsMethodRule + MINOR + + + This rule detects when the equals(Object) method is called directly in code instead of using the == or != operator. A groovier way to express this: a.equals(b) is this: a == b and a groovier way to express : !a.equals(b) is: a != b. This rule can be configured to ignore this.equals(Object) using the ignoreThisReference property. It defaults to false, so even equals(x) will trigger a violation.

+

This rule also ignores all calls to super.equals(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToGetAtMethodRule + MINOR + + + This rule detects when the getAt(Object) method is called directly in code instead of using the [] index operator. A groovier way to express this: a.getAt(b) is this: a[b]. This rule can be configured to ignore this.getAt(Object) using the ignoreThisReference property. It defaults to false, so even getAt(x) will trigger a violation.

+

This rule also ignores all calls to super.getAt(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToLeftShiftMethodRule + MINOR + + + This rule detects when the leftShift(Object) method is called directly in code instead of using the << operator. A groovier way to express this: a.leftShift(b) is this: a << b. This rule can be configured to ignore this.leftShift(Object) using the ignoreThisReference property. It defaults to false, so even leftShift(x) will trigger a violation.

+

This rule also ignores all calls to super.leftShift(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToMinusMethodRule + MINOR + + + This rule detects when the minus(Object) method is called directly in code instead of using the -> operator. A groovier way to express this: a.minus(b) is this: a - b. This rule can be configured to ignore minus.xor(Object) using the ignoreThisReference property. It defaults to false, so even minus(x) will trigger a violation.

+

This rule also ignores all calls to super.minus(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToMultiplyMethodRule + MINOR + + + This rule detects when the multiply(Object) method is called directly in code instead of using the * operator. A groovier way to express this: a.multiply(b) is this: a * b. This rule can be configured to ignore this.multiply(Object) using the ignoreThisReference property. It defaults to false, so even multiply(x) will trigger a violation.

+

This rule also ignores all calls to super.multiply(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToModMethodRule + MINOR + + + This rule detects when the mod(Object) method is called directly in code instead of using the % operator. A groovier way to express this: a.mod(b) is this: a % b. This rule can be configured to ignore this.mod(Object) using the ignoreThisReference property. It defaults to false, so even mod(x) will trigger a violation.

+

This rule also ignores all calls to super.mod(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToOrMethodRule + MINOR + + + This rule detects when the or(Object) method is called directly in code instead of using the | operator. A groovier way to express this: a.or(b) is this: a | b. This rule can be configured to ignore this.or(Object) using the ignoreThisReference property. It defaults to true, so even or(x) will not trigger a violation. This is the default because it is commonly used in Grails criteria.

+

This rule also ignores all calls to super.or(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToPlusMethodRule + MINOR + + + This rule detects when the plus(Object) method is called directly in code instead of using the + operator. A groovier way to express this: a.plus(b) is this: a + b. This rule can be configured to ignore this.plus(Object) using the ignoreThisReference property. It defaults to false, so even plus(x) will trigger a violation.

+

This rule also ignores all calls to super.plus(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToPowerMethodRule + MINOR + + + This rule detects when the power(Object) method is called directly in code instead of using the ** operator. A groovier way to express this: a.power(b) is this: a ** b. This rule can be configured to ignore this.power(Object) using the ignoreThisReference property. It defaults to false, so even power(x) will trigger a violation.

+

This rule also ignores all calls to super.power(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToRightShiftMethodRule + MINOR + + + This rule detects when the rightShift(Object) method is called directly in code instead of using the >> operator. A groovier way to express this: a.rightShift(b) is this: a >> b. This rule can be configured to ignore this.rightShift(Object) using the ignoreThisReference property. It defaults to false, so even rightShift(x) will trigger a violation.

+

This rule also ignores all calls to super.rightShift(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToXorMethodRule + MINOR + + + This rule detects when the xor(Object) method is called directly in code instead of using the ^ operator. A groovier way to express this: a.xor(b) is this: a ^ b. This rule can be configured to ignore this.xor(Object) using the ignoreThisReference property. It defaults to false, so even xor(x) will trigger a violation.

+

This rule also ignores all calls to super.xor(Object).

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitHashMapInstantiationRule + MINOR + + + This rule checks for explicit calls to the no-argument constructor of HashMap. In Groovy, it is best to replace new HashMap() with [:], which creates (mostly) the same object. [:] is technically a LinkedHashMap but it is very rare that someone absolutely needs an instance of HashMap and not a subclass.

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitHashSetInstantiationRule + MINOR + + + This rule checks for explicit calls to the no-argument constructor of HashSet. In Groovy, it is best to replace new HashSet() with [] as Set, which creates the same object.

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitLinkedListInstantiationRule + MINOR + + + This rule checks for explicit calls to the no-argument constructor of LinkedList. In Groovy, it is best to replace new LinkedList() with [] as Queue, which creates the same object.

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitStackInstantiationRule + MINOR + + + This rule checks for explicit calls to the no-argument constructor of Stack. In Groovy, it is best to replace new Stack() with [] as Stack, which creates the same object.

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitTreeSetInstantiationRule + MINOR + + + This rule checks for explicit calls to the no-argument constructor of TreeSet. In Groovy, it is best to replace new TreeSet() with [] as SortedSet, which creates the same object.

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.GStringAsMapKeyRule + MINOR + + + A GString should not be used as a map key since its hashcode is not guaranteed to be stable. Consider calling key.toString().

+

Here is an example of code that produces a violation:

+
+    Map map = ["${someRef}" : 'invalid' ]       // violation
+
]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.GroovyLangImmutableRule + MINOR + + + The groovy.lang.Immutable annotation has been deprecated and replaced by groovy.transform.Immutable. Do not use the Immutable in groovy.lang.

+

Example of violations:

+
+    @Immutable                          // Violation (no import means groovy.lang.Immutable)
+    class Person { }
+
+    @groovy.lang.Immutable              // Violation
+    class Person { }
+
+    import groovy.lang.Immutable as Imtl
+    @Imtl                               // Violation
+    class Person { }
+
+
+

Example of valid use of @Immutable:

+
+
+    @groovy.transform.Immutable                 // OK
+    class Person { }
+
+    import groovy.transform.Immutable           // OK
+    @Immutable
+    class Person { }
+
+    import groovy.transform.*
+    @Immutable                                  // OK
+    class Person { }
+
+    import groovy.transform.Immutable as Imtl
+    @Imtl                                       // OK
+    class Person { }
+
+    @javax.annotation.concurrent.Immutable      // OK
+    class MyClass { }
+
+
]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitLinkedHashMapInstantiationRule + MINOR + + + This rule checks for the explicit instantiation of a LinkedHashMap using the no-arg constructor. In Groovy, it is best to replace new LinkedHashMap() with [:], which creates the same object.

]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ClosureAsLastMethodParameterRule MAJOR - - - Checks for unnecessary boolean expressions, including ANDing (&&) or ORing (||) with true, false, null, or a Map/List/String/Number literal.

-

This rule also checks for negation (!) of true, false, null, or a Map/List/String/Number literal.

-

Examples of violations include:

+ + + If a method is called and the last parameter is an inline closure then it can be declared outside of the method call parentheses.

+

Example of violations:

-    result = value && true              // AND or OR with boolean constants
-    if (false || value) { .. }
-    return value && Boolean.FALSE
+    // creates violation: poor Groovy style
+    [1,2,3].each({ println it })
 
-    result = null && value              // AND or OR with null
+    // no violation
+    [1,2,3].each { println it }
+
]]>
+ groovyism +
- result = value && "abc" // AND or OR with String literal + + + org.codenarc.rule.groovyism.AssignCollectionUniqueRule + MINOR + + + The Collections.unique() method mutates the list and returns the list as a value. If you are assigning the result of unique() to a variable, then you probably don't realize that you're also modifying the original list as well. This is frequently the cause of subtle bugs.

+

This violation is triggered when a unique() method call that mutates the target collection appears as the right hand side of an assignment, or when it appears as the first method call in a series of chained method calls.

+

Example of violations:

+
+  def a = myList.unique()                   // No-argument
 
-    result = value && 123               // AND or OR with Number literal
-    result = 678.123 || true
+  def x = myList.unique() { it }            // Single-argument: Closure
+  def y = myList.unique { it % 2 }
 
-    result = value && [x, y]            // AND or OR with List literal
+  def c = myList.unique().findAll { x < 1 } // Chained method call
 
-    result = [a:123] && value           // AND or OR with Map literal
+  def comparator = { o1, o2 -> o1 <=> o2 }
+  def x = myList.unique(comparator)         // Single-argument: Comparator
 
-    result = !true                      // Negation of boolean constants
-    result = !false
-    result = !Boolean.TRUE
+  def x = myList.unique(true)               // Single-argument: boolean true
 
-    result = !null                      // Negation of null
+  def x = myList.unique(true, comparator)   // Two arguments: boolean true and Comparator
+  def y = myList.unique(true) { it }        // Two arguments: boolean true and Closure
+
]]>
+ groovyism +
- result = !"abc" // Negation of String literal + + + org.codenarc.rule.groovyism.AssignCollectionSortRule + MINOR + + + The Collections.sort() method mutates the list and returns the list as a value. If you are assigning the result of sort() to a variable, then you probably don't realize that you're also modifying the original list as well. This is frequently the cause of subtle bugs. This violation is triggered when a sort() method call appears as the right hand side of an assignment, or when it appears as the first method call in a series of chained method calls.

+

Example of violations:

+
+  def a = myList.sort()
+  def b = myList.sort() { it }
+  def c = myList.sort().findAll { x < 1 }
+
]]>
+ groovyism +
- result = ![a:123] // Negation of Map literal + + + org.codenarc.rule.groovyism.ConfusingMultipleReturnsRule + MINOR + + + Multiple return values can be used to set several variables at once. To use multiple return values, the left hand side of the assignment must be enclosed in parenthesis. If not, then you are not using multiple return values, you're only assigning the last element.

+

Example of violations:

+
+def a, b = [1, 2] // bad, b is null
+def c, d, e = [1, 2, 3] // bad, c and d are null
+class MyClass {
+    def a, b, c = [1, 2, 3]  // bad, a and b are null
+}
 
-    result = ![a,b]                     // Negation of List literal
-
-]]>
- clumsy +def x = 1 // ok +def (f, g) = [1, 2] // ok +(a, b, c) = [1, 2, 3] // ok +]]> + groovyism
+ - org.codenarc.rule.unnecessary.UnnecessaryIfStatementRule + org.codenarc.rule.groovyism.GetterMethodCouldBePropertyRule MAJOR - - - Checks for unnecessary if statements. The entire if statement, or at least the or block, are considered unnecessary for the four scenarios described below.

-

(1) When the and blocks contain only an explicit return of true and false constants. These cases can be replaced by a simple statement. Examples of violations include:

+ + + If a class defines a public method that follows the Java getter notation and that returns a constant, then it is cleaner to provide a Groovy property for the value rather than a Groovy method.

+

Example of violations:

-    if (someExpression)         // can be replaced by: return someExpression
-        return true
-    else
-        return false
+    interface Parent {
+        String getSomething()
+        String getSomethingElse()
+    }
 
-    if (someExpression) {       // can be replaced by: return !someExpression
-        return false
-    } else {
-        return true
+    class Child extends Parent {
+        static VALUE = 'value'
+
+        @Override
+        String getSomething() {
+            'something'         // this could be simplified
+        }
+
+        @Override
+        String getSomethingElse() {
+            VALUE       // this could be simplified
+        }
+
+        int getOtherValue() {
+            123
+        }
+
+        static String getName() {
+            'MyName'
+        }
     }
 
-    if (someExpression) {       // can be replaced by: return someExpression
-        return Boolean.TRUE
-    } else {
-        return Boolean.FALSE
+    class Child2 extends Parent {
+        static VALUE = 'value'
+        final String something = 'something'    // this is cleaner
+        final String somethingElse = VALUE      // this is cleaner
+        final int otherValue = 123              // this is cleaner
+        static final String name = 'MyName'     // this is cleaner
     }
-
+]]>
+ groovyism + + ignoreMethodsWithOverrideAnnotation + + false + +
+ + + + org.codenarc.rule.groovyism.UseCollectManyRule + MINOR + + + In many case collectMany() yields the same result as collect{}.flatten(). It is easier to understand and more clearly conveys the intent.

+

Example of violations:

-    def myMethod() {
-        doSomething()
-        if (someExpression)
-            true
-        else false
-    }
-
+def l = [1, 2, 3, 4] + +l.collect{ [it, it*2] }.flatten() // suboptimal + +l.collectMany{ [it, it*2] } // same functionality, better readability +]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.CollectAllIsDeprecatedRule + MINOR + + + The collectAll method is deprecated since Groovy 1.8.1. Use collectNested instead.

+

Example of violations:

-    def myMethod() {
-        doSomething()
-        if (expression1) {
-            return true
-        }
-        return false
-    }
-
+def list = [1, 2, [3, 4, [5, 6]], 7] + +list.collectAll { it * 2 } // deprecated + +list.collectNested { it * 2 } // replacement +]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.UseCollectNestedRule + MINOR + + + Instead of nested collect{} calls use collectNested{}.

+

Example of violations:

-    def myMethod() {
-        if (someExpression) { 123 }
-        doSomething()
-    }
-
-]]>
- clumsy +def list = [1, 2, [3, 4, 5, 6], [7]] + +println list.collect { elem -> + if (elem instanceof List) + elem.collect {it *2} // violation + else elem * 2 +} + +println list.collect([8]) { + if (it instanceof List) + it.collect {it *2} // violation + else it * 2 +} + +println list.collectNested { it * 2 } // same functionality, better readability +]]> + groovyism
+ - org.codenarc.rule.unnecessary.UnnecessaryTernaryExpressionRule - MAJOR - - - Checks for ternary expressions where the conditional expression always evaluates to a boolean and the and expressions are merely returning true and false constants. These cases can be replaced by a simple boolean expression. Examples of violations include:

+ org.codenarc.rule.groovyism.GStringExpressionWithinStringRule + MINOR + + + Check for regular (single quote) strings containing a GString-type expression (${..}).

+

Example of violations:

-    x==99 ? true : false                    // can be replaced by: x==99
-    x && y ? true : false                   // can be replaced by: x && y
-    x||y ? false : true                     // can be replaced by: !(x||y)
-    x >= 1 ? true: false                    // can be replaced by: x >= 1
-    x < 99 ? Boolean.TRUE : Boolean.FALSE   // can be replaced by: x < 99
-    !x ? true : false                       // can be replaced by: !x
-
+ def str1 = 'total: ${count}' // violation + def str2 = 'average: ${total / count}' // violation + + def str3 = "abc ${count}" // ok; GString + def str4 = '$123' // ok + def str5 = 'abc {123}' // ok +]]>
+ groovyism +
+ + + + org.codenarc.rule.groovyism.ExplicitCallToPutAtMethodRule + MINOR + + + Detects when the `map.putAt(k, v)` method is called directly rather than using `map[k] = v`.

+

This rule can be configured to ignore this.putAt(k, v) using the ignoreThisReference property. It defaults to false, so even putAt(k, v) will trigger a violation.

+

This rule also ignores all calls to super.putAt(k, v).

+

Example of violations:

-    x ? '123' : '123'              // can be replaced by: '123'
-    x ? null : null                // can be replaced by: null
-    x ? 23 : 23                    // can be replaced by: 23
-    x ? MAX_VALUE : MAX_VALUE      // can be replaced by: MAX_VALUE
-    ready ? minValue : minValue    // can be replaced by: minValue
-
-]]>
- clumsy + map.putAt(k, v) // violation +]]> + groovyism
- + + - org.codenarc.rule.unnecessary.UnnecessaryBigDecimalInstantiationRule + org.codenarc.rule.imports.DuplicateImportRule MAJOR - - - It is unnecessary to instantiate BigDecimal objects. Instead just use the decimal literal or the 'G' identifier to force the type, such as 123.45 or 123.45G.

-

This rule does not produce violations when the parameter evaluates to an integer/long, e.g. new BigDecimal(42), new BigDecimal(42L) or new BigDecimal("42"), because using the "G" suffix on an integer value produces a BigInteger, rather than a BigDecimal, e.g. 45G. So that means there is no way to produce a BigDecimal with exactly that value using a literal.

-

This rule also does not produce violations when the parameter is a double, e.g. new BigDecimal(12.3). That scenario is covered by the BigDecimalInstantiation rule, because that produces an unpredictable (double) value (and so it is , rather than ).

- ]]>
- clumsy + + + Checks for a duplicate import statements.

+

NOTE: This is a file-based rule, rather than a typical AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ bug
- - org.codenarc.rule.unnecessary.UnnecessaryBigIntegerInstantiationRule + org.codenarc.rule.imports.ImportFromSamePackageRule MAJOR - - - It is unnecessary to instantiate BigInteger objects. Instead just use the literal with the 'G' identifier to force the type, such as 8G or 42G.

- ]]>
- clumsy + + + Checks for an import of a class that is within the same package as the importing class.

+

NOTE: This is a file-based rule, rather than a typical AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ bug
- - org.codenarc.rule.unnecessary.UnnecessaryBooleanInstantiationRule + org.codenarc.rule.imports.UnnecessaryGroovyImportRule MAJOR - - - Checks for direct call to a Boolean constructor. Use Boolean.valueOf() or the Boolean.TRUE and Boolean.FALSE constants instead of calling the Boolean() constructor directly.

-

Also checks for Boolean.valueOf(true) or Boolean.valueOf(false). Use the Boolean.TRUE or Boolean.FALSE constants instead.

-

Here is an example of code that produces a violation:

-
-    def b1 = new Boolean(true)             // violation
-    def b2 = new java.lang.Boolean(false)  // violation
-    def b3 = Boolean.valueOf(true)         // violation
-    def b4 = Boolean.valueOf(false)        // violation
-
-]]>
- clumsy + + + Checks for an import from any package that is already automatically imported for Groovy files. A Groovy file does not need to include an import for classes from java.lang, java.util, java.io, java.net, groovy.lang and groovy.util, as well as the classes java.math.BigDecimal and java.math.BigInteger.

+

NOTE: This is a file-based rule, rather than a typical AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ bug
- - org.codenarc.rule.unnecessary.UnnecessaryCallForLastElementRule + org.codenarc.rule.imports.UnusedImportRule MAJOR - - - This rule checks for excessively verbose methods of accessing the last element of an array or list. For instance, it is possible to access the last element of an array by performing array[array.length - 1], in Groovy it is simpler to either call array.last() or array[-1]. The same is true for lists. This violation is triggered whenever a get, getAt, or array-style access is used with an object size check.

-

Code like this all cause violations.

-
-    def x = [0, 1, 2]
-    def a = x.get(x.size() -1)
-    def b = x.get(x.length -1)
-    def c = x.getAt(x.size() -1)
-    def d = x.getAt(x.length -1)
-    def f = x[(x.size() -1]
-    def d = x[(x.length -1]
-
+ + + Checks for import statements for classes that are never referenced within the source file. Also checks static imports.

+

Known limitations:

+

* Does not check for unused imports containing wildcards (e.g. import org.codenarc.*)

+

* Misses unused imports if the class/alias name is contained within strings, comments or other (longer) names (i.e., if that string shows up almost anywhere within the source code).

+

NOTE: This is a file-based rule, rather than a typical AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.]]> + bug + + + + + org.codenarc.rule.imports.ImportFromSunPackagesRule + MINOR + + + Avoid importing anything from the 'sun.*' packages. These packages are not portable and are likely to change.

+

Example of violations:

-    def x = [0, 1, 2]
-    def a = x.last()
-    def b = x[-1]
-    def c = x.getAt(-1)
-    def d = x.get(z.size() -1)     // different objects
-    def e = x.get(z.length -1)     // different objects
-    def f = x.getAt(z.size() -1)   // different objects
+    import sun.misc.foo
+    import sun.misc.foo as Foo
+
+    public class MyClass{}
 
-]]>
- clumsy +

NOTE: This is a file-based rule, rather than a typical AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ bug
- + - org.codenarc.rule.unnecessary.UnnecessaryCatchBlockRule + org.codenarc.rule.imports.MisorderedStaticImportsRule MAJOR - - - Violations are triggered when a block does nothing but throw the original exception. In this scenario there is usually no need for a block, just let the exception be thrown from the original code. This condition frequently occurs when catching an exception for debugging purposes but then forgetting to take the catch statement out.

- ]]>
- clumsy + + + Checks for static import statements which should never be after nonstatic imports.

+

This rule has one property comesBefore, which defaults to true. If you like your static imports to come after the others, then set this property to false.

+

Examples of violations:

+
+    import my.something.another
+    import static foo.bar
+
+    public class MyClass{}
+
+

NOTE: This is a file-based rule, rather than a typical AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ bug + + comesBefore + + true +
- + - org.codenarc.rule.unnecessary.UnnecessaryCollectCallRule + org.codenarc.rule.imports.NoWildcardImportsRule MAJOR - - - Some method calls to Object.collect(Closure) can be replaced with the spread operator. For instance, list.collect { it.multiply(2) } can be replaced by list*.multiply(2).

-

Examples of violations include:

-
-    assert [1, 2, 3].collect { it.multiply(2) }
-    assert [1, 2, 3].collect { x -> x.multiply(2) }
-    ["1", "2", "3"].collect { it.bytes }
-
+ + + Checks for wildcard (star) imports. If the ignoreStaticImports property is true, then do not check static imports. Similarly, do not check the standard imports if ignoreImports is true.

+

Example of violations:

-    [1, 2, 3].collect { it * it }   // OK, closure parameter is referenced twice
-
-    [1, 2, 3].mapMethod { it.multiply(5) } // OK, method call is not collect
-
-    [1, 2, 3].collect(5) // OK, collect parameter is not a closure
-
-    // OK, the closure is not a simple one line statement
-    [1, 2, 3].collect { println it; it.multiply(5) }
+    import static foo.bar.*         // violation (unless ignoreStaticImports is true)
+    import my.something.*           // violation (unless ignoreImports is true)
 
-    // OK, closure has too many arguments
-    [1, 2, 3].collect { a, b -> a.multiply(b) }
+    public class MyClass{}
+
+

NOTE: This is a file-based rule, rather than a typical AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
+ bug + + ignoreImports + + false + + + ignoreStaticImports + + false + +
- // OK, closure statement references parameter multiple times - [1, 2, 3].collect { it.multiply(it) } + - // OK, it is referenced several times in the closure - [1, 2, 3].collect { it.multiply(2).multiply(it) } - ["1", "2", "3"].collect { it.bytes.foo(it) } + + + org.codenarc.rule.jdbc.DirectConnectionManagementRule + MINOR + + + The J2EE standard requires that applications use the container's resource management facilities to obtain connections to resources. Every major web application container provides pooled database connection management as part of its resource management framework. Duplicating this functionality in an application is difficult and error prone, which is part of the reason it is forbidden under the J2EE standard.

+

For more information see: https://www.fortify.com/vulncat/en/vulncat/java/j2ee_badpractices_getconnection.html

+

Example of violations:

+
+    DriverManager.getConnection()
+    java.sql.DriverManager.getConnection()
+
]]>
+ bug +
- // OK, chained methods are too complex to analyze at this point - [1, 2, 3].collect { it.multiply(2).multiply(4) } + + + org.codenarc.rule.jdbc.JdbcConnectionReferenceRule + MINOR + + + Checks for direct use of java.sql.Connection, which is discouraged and almost never necessary in application code.

+

For a more Groovy alternative, see http://groovy.codehaus.org/Database+features for information on the Groovy Sql abstraction layer for JDBC/SQL.

+

Note: If a violation is triggered from an import statement, then you may get multiple violations per import if there are multiple classes in the source file. In that case, the imports are processed once per class.

]]>
+ bug +
- // in general the above examples can be rewritten like this: - [1, 2, 3]*.multiply(2) - ["1", "2", "3"]*.bytes - -]]> - clumsy + + + org.codenarc.rule.jdbc.JdbcResultSetReferenceRule + MINOR + + + Checks for direct use of java.sql.ResultSet, which is not necessary if using the Groovy Sql facility or an ORM framework such as Hibernate.

+

See http://groovy.codehaus.org/Database+features for information on the Groovy Sql abstraction layer for JDBC/SQL.

+

Note: If a violation is triggered from an import statement, then you may get multiple violations per import if there are multiple classes in the source file. In that case, the imports are processed once per class.

]]>
+ bug
- + - org.codenarc.rule.unnecessary.UnnecessaryCollectionCallRule - MAJOR - - - Checks for useless calls to collections. For any collection c, calling c.containsAll(c) should always be true, and c.retainAll(c) should have no effect.

- ]]>
- clumsy + org.codenarc.rule.jdbc.JdbcStatementReferenceRule + MINOR + + + Checks for direct use of java.sql.Statement, java.sql.PreparedStatement, or java.sql.CallableStatement, which is not necessary if using the Groovy Sql facility or an ORM framework such as Hibernate.

+

See http://groovy.codehaus.org/Database+features for information on the Groovy Sql abstraction layer for JDBC/SQL.

+

Note: If a violation is triggered from an import statement, then you may get multiple violations per import if there are multiple classes in the source file. In that case, the imports are processed once per class.

]]>
+ bug
- + + - org.codenarc.rule.unnecessary.UnnecessaryConstructorRule - MAJOR - - - This rule detects when a constructor is not necessary; i.e., when there's only one constructor, it's public, has an empty body, and takes no arguments, or else contains only a single call to super().

-

Example of violations:

-
-    class MyClass {
-        public MyClass() {          // violation; constructor is not necessary
-        }
-    }
+    org.codenarc.rule.junit.JUnitAssertAlwaysFailsRule
+    MINOR
+    
+    
+    Rule that checks for JUnit assert() method calls with constant or literal arguments such that the assertion always fails. This includes:

+

* assertTrue(false)

+

* assertTrue(0)

+

* assertTrue('')

+

* assertTrue([])

+

* assertTrue([:])

+

* assertFalse(true)

+

* assertFalse('abc')

+

* assertFalse(99)

+

* assertFalse([123])

+

* assertFalse([a:123)

+

* assertNull(CONSTANT).

+

* assertNull([]).

+

* assertNull([123]).

+

* assertNull([:]).

+

* assertNull([a:123]).

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit + - class MyClass2 extends OtherClass { - MyClass2() { // violation; constructor is not necessary - super() - } - } -
-]]>
- clumsy + + org.codenarc.rule.junit.JUnitAssertAlwaysSucceedsRule + MINOR + + + Rule that checks for JUnit assert() method calls with constant arguments such that the assertion always succeeds. This includes:

+

* assertTrue(true)

+

* assertTrue(99)

+

* assertTrue('abc')

+

* assertTrue([123])

+

* assertTrue([a:123])

+

* assertFalse(false)

+

* assertFalse('')

+

* assertFalse(0)

+

* assertFalse([])

+

* assertFalse([:)

+

* assertNull(null)

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit
- - org.codenarc.rule.unnecessary.UnnecessaryDoubleInstantiationRule - MAJOR - - - It is unnecessary to instantiate Double objects. Instead just use the double literal with 'D' identifier to force the type, such as 123.45d or 0.42d.

- ]]>
- clumsy + org.codenarc.rule.junit.JUnitPublicNonTestMethodRule + MINOR + + + Rule that checks if a JUnit test class contains public methods other than standard test methods, JUnit framework methods or methods with JUnit annotations.

+

The following public methods are ignored by this rule:

+

* Zero-argument methods with names starting with "test"

+

* The setUp() and tearDown() methods

+

* Methods annotated with @Test

+

* Methods annotated with @Before, @BeforeAll, @BeforeClass and @BeforeEach

+

* Methods annotated with @After, @AfterAll, @AfterClass and @AfterEach

+

* Methods annotated with @Disabled and @Ignore

+

* Methods annotated with @Override

+

Public, non-test methods on a test class violate conventional usage of test classes, and they typically break encapsulation unnecessarily.

+

Public, non-test methods may also hide unintentional 'Lost Tests'. For instance, the test method declaration may (unintentionally) include methods parameters, and thus be ignored by JUnit. Or the method may (unintentionally) not follow the "test.." naming convention and not have the @Test annotation, and thus be ignored by JUnit.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit + + ignoreMethodsWithAnnotations + + After,AfterAll,AfterClass, +
- - org.codenarc.rule.unnecessary.UnnecessaryFloatInstantiationRule - MAJOR - - - It is unnecessary to instantiate Float objects. Instead just use the float literal with the 'F' identifier to force the type, such as 123.45F or 0.42f.

- ]]>
- clumsy + org.codenarc.rule.junit.JUnitSetUpCallsSuperRule + MINOR + + + Rule that checks that if the JUnit setUp method is defined, that it includes a call to super.setUp().

+

This rule ignored methods annotated with @Before or @BeforeClass.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit
- - org.codenarc.rule.unnecessary.UnnecessaryGetterRule - MAJOR - - - Checks for explicit calls to getter/accessor methods which can, for the most part, be replaced by property access. A getter is defined as a method call that matches get[A-Z] but not getClass() or get[A-Z][A-Z] such as getURL(). Getters do not take method arguments.

-

These bits of code produce violations:

-
-    x.getProperty()
-    x.getFirst()
-    x.getFirstName()
-    x.getA()
-
-
-    x.property
-    x.first
-    x.firstName
-    x.a
-    x.getURL()
-    x.getClass()
-    x.getProperty('key')
-
-]]>
- clumsy + org.codenarc.rule.junit.JUnitTearDownCallsSuperRule + MINOR + + + Rule that checks that if the JUnit tearDown method is defined, that it includes a call to super.tearDown().

+

This rule ignored methods annotated with @After or @AfterClass.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit
- - org.codenarc.rule.unnecessary.UnnecessaryGStringRule + org.codenarc.rule.junit.JUnitUnnecessarySetUpRule MAJOR - - - String objects should be created with single quotes, and GString objects created with double quotes. Creating normal String objects with double quotes is confusing to readers.

-

Example of violations:

+ + + Rule that checks checks for JUnit setUp() methods that contain only a call to super.setUp(). The method is then unnecessary.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

+

Here is an example of a violation:

-    def a = "I am a string"     // violation
-
-    // violation
-    def b = """
-        I am a string
-    """
-
-    def c = "I am a ' string"       // OK
-
-    def d = """I am a ' string"""   // OK
-
-    def e = """I am a ' string"""   // OK
-
-    def f = "I am a \$ string"  // OK
-
-    // OK
-    def g = """
-        I am a \$ string
-    """
-
-    // OK
-    def h = """
-        I am a $string
-    """
-
-    def i = 'i am a string'
-    def j = '''i am a
-        string
-    '''
-
-]]>
- clumsy + class MyTest extends TestCase { + void setUp() { // violation + super.setUp() + } + } +]]>
+ junit
- - org.codenarc.rule.unnecessary.UnnecessaryInstantiationToGetClassRule + org.codenarc.rule.junit.JUnitUnnecessaryTearDownRule MAJOR - - - Avoid instantiating an object just to call getClass() on it; use the .class public member instead.

+ + + Rule that checks checks for JUnit tearDown() methods that contain only a call to super.tearDown(). The method is then unnecessary.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

+

Here is an example of a violation:

-    public class Foo {
-     // Replace this
-     Class c = new String().getClass();
-
-     // with this:
-     Class c = String.class;
+    class MyTest extends TestCase {
+        void tearDown() {               // violation
+            super.tearDown()
+        }
     }
-
-]]>
- clumsy +]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessaryIntegerInstantiationRule + org.codenarc.rule.junit.JUnitStyleAssertionsRule MAJOR - - - It is unnecessary to instantiate Integer objects. Instead just use the literal with the 'I' identifier to force the type, such as 8I or 42i.

- ]]>
- clumsy + + + This rule detects calling JUnit style assertions like assertEquals, assertTrue, assertFalse, assertNull, assertNotNull. Groovy 1.7 ships with a feature called the "power assert", which is an assert statement with better error reporting. This is preferable to the JUnit assertions.

]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessaryLongInstantiationRule + org.codenarc.rule.junit.UseAssertEqualsInsteadOfAssertTrueRule MAJOR - - - It is unnecessary to instantiate Long objects. Instead just use the literal with the 'L' identifier to force the type, such as 8L or 42L.

- ]]>
- clumsy + + + This rule detects JUnit assertions in object equality. These assertions should be made by more specific methods, like assertEquals.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessaryObjectReferencesRule - MAJOR - - - Violations are triggered when an excessive set of consecutive statements all reference the same variable. This can be made more readable by using a with or identity block. By default, 5 references are allowed. You can override this property using the maxReferencesAllowed property on the rule.

-

These two bits of code produce violations:

-
-    def p1 = new Person()
-    p1.firstName = 'Hamlet'
-    p1.lastName = "D'Arcy"
-    p1.employer = 'Canoo'
-    p1.street = 'Kirschgaraten 5'
-    p1.city = 'Basel'
-    p1.zipCode = '4051'
-
-    def p2 = new Person()
-    p2.setFirstName('Hamlet')
-    p2.setLastName("D'Arcy")
-    p2.setEmployer('Canoo')
-    p2.setStreet('Kirschgaraten 5')
-    p2.setCity('Basel')
-    p2.setZipCode('4051')
-
-
-    def p1 = new Person().with {
-        firstName = 'Hamlet'
-        lastName = "D'Arcy"
-        employer = 'Canoo'
-        street = 'Kirschgaraten 5'
-        city = 'Basel'
-        zipCode = '4051'
-    }
-
-    def p2 = new Person().identity {
-        firstName = 'Hamlet'
-        lastName = "D'Arcy"
-        employer = 'Canoo'
-        street = 'Kirschgaraten 5'
-        city = 'Basel'
-        zipCode = '4051'
-    }
-
-]]>
- clumsy + org.codenarc.rule.junit.UseAssertFalseInsteadOfNegationRule + MINOR + + + In unit tests, if a condition is expected to be false then there is no sense using assertTrue with the negation operator. For instance, assertTrue(!condition) can always be simplified to assertFalse(condition).

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessaryNullCheckRule + org.codenarc.rule.junit.UseAssertTrueInsteadOfAssertEqualsRule MAJOR - - - Groovy contains the safe dereference operator. It can be used in boolean conditional statements to safely replace explicit x == null tests. Also, testing the 'this' or 'super' reference for null equality is pointless and can be removed.

-

Examples of violations:

-
-    if (obj != null && obj.method()) { }
-
-    if (obj != null && obj.prop) { }
-
-    // this is pointless and won't avoid NullPointerException
-    if (obj.method() && obj != null ) { }
-
-    if (this == null) { }
-    if (null == this) { }
-    if (this != null) { }
-    if (null != this) { }
-
-    if (super == null) { }
-    if (null == super) { }
-    if (super != null) { }
-    if (null != super) { }
-
+ + + This rule detects JUnit calling assertEquals where the first parameter is a boolean. These assertions should be made by more specific methods, like assertTrue or assertFalse.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

+

All of the following examples can be simplified to assertTrue or remove the true literal:

-    // null check it OK
-    if (obj != null) { }
-
-    // null safe dereference in if is OK
-    if (obj?.method()) { }
-
-    // null safe dereference in ternary is OK
-    (obj?.prop && obj?.prop2) ? x : y
-
-    // obj is reused in a parameter list, so OK
-    if (obj != null && obj.method() && isValid(obj)) { }
+    assertEquals(true, foo())
+    assertEquals("message", true, foo())
+    assertEquals(foo(), true)
+    assertEquals("message", foo(), true)
+    assertEquals(false, foo())
+    assertEquals("message", false, foo())
+    assertEquals(foo(), false)
+    assertEquals("message", foo(), false)
 
-    // rule is not so complex yet...
-    (obj != null && obj.prop && obj.method()) ? x : y
-
-]]>
- clumsy + assert true == foo() // violation only if checkAssertStatements == true + assert foo() == true : "message" // violation only if checkAssertStatements == true + assert false == foo() // violation only if checkAssertStatements == true + assert foo() == false : "message" // violation only if checkAssertStatements == true +]]>
+ junit + + checkAssertStatements + + false +
- + - org.codenarc.rule.unnecessary.UnnecessaryNullCheckBeforeInstanceOfRule + org.codenarc.rule.junit.UseAssertNullInsteadOfAssertEqualsRule MAJOR - - - There is no need to check for null before an instanceof; the instanceof keyword returns false when given a null argument.

-

Example:

-
-    if (x != null && x instanceof MyClass) {
-        // should drop the "x != null" check
-    }
-
-    if (x instanceof MyClass && x != null) {
-        // should drop the "x != null" check
-    }
-
-    // should drop the "x != null" check
-    (x != null && x instanceof MyClass) ? foo : bar
-
-    if (x != null && x instanceof MyClass && x.isValid()) {
-        // this is OK and causes no violation because the x.isValid() requires a non null reference
-    }
-
-]]>
- clumsy + + + This rule detects JUnit calling assertEquals where the first or second parameter is null. These assertion should be made against the assertNull method instead.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessaryOverridingMethodRule + org.codenarc.rule.junit.UseAssertSameInsteadOfAssertTrueRule MAJOR - - - Checks for an overriding method that merely calls the same method defined in a superclass. Remove it.

- ]]>
- clumsy + + + This rule detects JUnit calling assertTrue or assertFalse where the first or second parameter is an Object#is() call testing for reference equality. These assertion should be made against the assertSame or assertNotSame method instead.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessaryReturnKeywordRule - MAJOR - - - In Groovy, the return keyword is often optional. If a statement is the last line in a method or closure then you do not need to have the return keyword.

- ]]>
- clumsy + org.codenarc.rule.junit.JUnitFailWithoutMessageRule + MINOR + + + This rule detects JUnit calling the fail() method without an argument. For better error reporting you should always provide a message.

]]>
+ junit
- org.codenarc.rule.unnecessary.UnnecessaryStringInstantiationRule - MAJOR - - - Checks for direct call to the String constructor that accepts a String literal. In almost all cases, this is unnecessary. Use a String literal (e.g., "...") instead of calling the corresponding String constructor (new String("..")) directly.

-

Here is an example of code that produces a violation:

-
-    def s = new String('abc')
-
-]]>
- clumsy + org.codenarc.rule.junit.UseAssertTrueInsteadOfNegationRule + MINOR + + + In unit tests, if a condition is expected to be true then there is no sense using assertFalse with the negation operator. For instance, assertFalse(!condition) can always be simplified to assertTrue(condition).

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

]]>
+ junit
- + - org.codenarc.rule.unnecessary.AddEmptyStringRule + org.codenarc.rule.junit.JUnitTestMethodWithoutAssertRule MINOR - - - Finds empty string literals which are being added. This is an inefficient way to convert any type to a String.

-

Examples:

-
-    // do not add empty strings to things
-    def a = '' + 123
-    def b = method('' + property)
-
-    // these examples are OK and do not trigger violations
-    def c = 456.toString()
-    def d = property?.toString() ?: ""
+    
+    
+    This rule searches for test methods that do not contain assert statements. Either the test method is missing assert statements, which is an error, or the test method contains custom assert statements that do not follow a proper assert naming convention. Test methods are defined as public void methods that begin with the work test or have a @Test annotation. By default this rule applies to the default test class names, but this can be changed using the rule's applyToClassNames property. An assertion is defined as either using the assert keyword or invoking a method that starts with the work assert, like assertEquals, assertNull, or assertMyClassIsSimilar. Also, any method named should.* also counts as an assertion so that shouldFail methods do not trigger an assertion, any method that starts with fail counts as an assertion, and any method that starts with verify counts as an assertion. Since version 0.23 CodeNarc has support for JUnit's ExpectedException.

+

What counts as an assertion method can be overridden using the assertMethodPatterns property of the rule. The default value is this comma separated list of regular expressions:

+
+    String assertMethodPatterns = 'assert.*,should.*,fail.*,verify.*,expect.*'
 
-]]>
- clumsy +

If you'd like to add any method starting with 'ensure' to the ignores then you would set the value to this:

+
+    'assert.*,should.*,fail.*,verify.*,ensure.*'
+
]]> + junit - org.codenarc.rule.unnecessary.ConsecutiveLiteralAppendsRule + org.codenarc.rule.junit.ChainedTestRule MINOR - - - Violations occur when method calls to append(Object) are chained together with literals as parameters. The chained calls can be joined into one invocation.

-

Example of violations:

-
-    writer.append('foo').append('bar')      // strings can be joined
-    writer.append('foo').append(5)          // string and number can be joined
-    writer.append('Hello').append("$World") // GString can be joined
-
+ + + A test method that invokes another test method is a chained test; the methods are dependent on one another. Tests should be isolated, and not be dependent on one another.

+

Example of violations:

-    // usage not chained invocation
-    writer.append('Hello')
-    writer.append('World')
+    class MyTest extends GroovyTestCase {
+        public void testFoo() {
 
-    writer.append(null).append(5)           // nulls cannot be joined
+            // violations, calls test method on self
+            5.times { testBar() }
+            5.times { this.testBar() }
 
-    writer.append().append('Hello')             // no arg append is unknown
-    writer.append('a', 'b').append('Hello')     // two arg append is unknown
-
-]]>
- clumsy + // OK, no violation: one arg method is not actually a test method + 5.times { testBar(it) } + } + + private static void assertSomething() { + testBar() // violation, even if in helper method + this.testBar() // violation, even if in helper method + } + + public void testBar() { + // ... + } + } +
]]>
+ junit
- org.codenarc.rule.unnecessary.ConsecutiveStringConcatenationRule - MAJOR - - - Catches concatenation of two string literals on the same line. These can safely by joined. In Java, the Java compiler will join two String literals together and place them in the Constant Pool. However, Groovy will not because the plus() method may override the + operator.

-

Examples:

+ org.codenarc.rule.junit.CoupledTestCaseRule + MINOR + + + This rule finds test cases that are coupled to other test cases, either by invoking static methods on another test case or by creating instances of another test case. If you require shared logic in test cases then extract that logic to a new class where it can properly be reused. Static references to methods on the current test class are ignored.

+

Example of violations:

-    // Violations
-    def a = 'Hello' + 'World'   // should be 'HelloWorld'
-    def b = "$Hello" + 'World'  // should be "${Hello}World"
-    def c = 'Hello' + "$World"  // should be "Hello${World}"
-    def d = 'Hello' + 5         // should be 'Hello5'
-    def e = 'Hello' + '''
-                        world   // should be joined
-                      '''
-    def f = '''Hello
-                  ''' + 'world'   // should be joined
+    class MyTest extends GroovyTestCase {
+        public void testMethod() {
+            // violation, static method call to other test
+            MyOtherTest.helperMethod()
 
+            // violation, instantiation of another test class
+            new MyOtherTest()
 
-    // Not Violations
-    def g = 'Hello' +           // OK because of line break
-                'World'
-    def h = 'Hello' + null      // OK because not a string
-    def i = 'Hello' + method()  // OK because not a string
-    def j = 'Hello' - "$World"  // OK because not +
-
-]]>
- clumsy + // no violation; same class + def input = MyTest.getResourceAsStream('sample.txt') + } + } +]]>
+ junit
- org.codenarc.rule.unnecessary.UnnecessaryCallToSubstringRule - MAJOR - - - Calling String.substring(0) always returns the original string. This code is meaningless.

-

Examples:

+ org.codenarc.rule.junit.UnnecessaryFailRule + MINOR + + + In a unit test, catching an exception and immediately calling Assert.fail() is pointless and hides the stack trace. It is better to rethrow the exception or not catch the exception at all.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

+

Example of violations:

-    string.substring(0)         // violation
-    method().substring(0)       // violation
+    public void testSomething() {
+        try {
+            something()
+        } catch (Exception e) {
+            fail(e.message)
+        }
 
-    prop.substring(1)           // OK, not constant 0
-    prop.substring(0, 1)        // OK, end is specified
-
-]]>
- clumsy + try { + something() + } catch (Exception e) { + fail() + } + } +]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessaryDefInMethodDeclarationRule - MAJOR - - - If a method has a visibility modifier or a type declaration, then the def keyword is unneeded. For instance 'def private method() {}' is redundant and can be simplified to 'private method() {}'.

-

Examples of violations:

+ org.codenarc.rule.junit.SpockIgnoreRestUsedRule.fixed + MINOR + + + If Spock's @IgnoreRest annotation appears on any method, all non-annotated test methods are not executed. This behaviour is almost always unintended. It's fine to use @IgnoreRest locally during development, but when committing code, it should be removed.

+

The specificationClassNames and specificationSuperclassNames properties determine which classes are considered Spock Specification classes.

+

Example of violations:

-    // def and private is redundant
-    def private method1() { return 4 }
-
-    // def and protected is redundant
-    def protected method2() { return 4 }
-
-    // def and public is redundant
-    def public method3() { return 4 }
+    public class MySpec extends spock.lang.Specification {
+        @spock.lang.IgnoreRest
+        def "my first feature"() {
+            expect: false
+        }
 
-    // def and static is redundant
-    def static method4() { return 4 }
+        def "my second feature"() {
+            given: def a = 2
 
-    // def and type is redundant
-    def Object method5() { return 4 }
+            when: a *= 2
 
-    class MyClass {
-        def MyClass() {}    // def is redundant
+            then: a == 4
+        }
     }
-
-]]>
- clumsy +]]>
+ junit + + specificationClassNames + + + + specificationSuperclassNames + + *Specification +
- + - org.codenarc.rule.unnecessary.UnnecessaryModOneRule - MAJOR - - - Any expression mod 1 (exp % 1) is guaranteed to always return zero. This code is probably an error, and should be either (exp & 1) or (exp % 2).

-

Examples:

+ org.codenarc.rule.junit.JUnitLostTestRule + MINOR + + + This rule checks for classes that import JUnit 4 classes and contain a public, instance, void, no-arg method named test* that is not abstract and not annotated with the JUnit 4 @Test annotation.

+

Note: This rule should be disabled for Grails 2.x projects, since the Grails test framework can use AST Transformations to automatically annotate test methods.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

+

Example of violations:

-    if (exp % 1) {}         // violation
-    if (method() % 1) {}    // violation
+    import org.junit.Test
 
-    if (exp & 1) {}     // ok
-    if (exp % 2) {}     // ok
-
-]]>
- clumsy + class MyTestCase { + void testMe() { } // missing @Test annotation + } +]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessaryPublicModifierRule + org.codenarc.rule.junit.JUnitUnnecessaryThrowsExceptionRule MAJOR - - - The 'public' modifier is not required on methods, constructors or classes.

-

Example of violations:

+ + + Check for throws clauses on JUnit test methods. That is not necessary in Groovy.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

+

Example of violations:

-    // violation on class
-    public class MyClass {
-        // violation on constructor
-        public MyClass() {}
+    @Test
+    void shouldDoStuff() throws Exception { }           // violation
 
-        // violation on method
-        public void myMethod() {}
+    @BeforeClass void initialize() throws Exception { } // violation
+    @Before void setUp() throws RuntimeException { }    // violation
+    @After void tearDown() throws Exception { }         // violation
+    @AfterClass void cleanUp() throws Exception { }     // violation
+    @Ignore void ignored() throws Exception { }         // violation
+
+    class MyTest extends GroovyTestCase {
+        void test1() throws Exception { }               // violation
+        public void test2() throws IOException { }      // violation
     }
-
-]]>
- clumsy + +]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessarySelfAssignmentRule + org.codenarc.rule.junit.JUnitPublicFieldRule MAJOR - - - Method contains a pointless self-assignment to a variable or property. Either the code is pointless or the equals()/get() method has been overridden to have a side effect, which is a terrible way to code getters and violates the contract of equals().

-

Examples:

+ + + Checks for public fields on a JUnit test class. There is usually no reason to have a public field (even a constant) on a test class.

+

Fields within interfaces and fields annotated with @Rule are ignored.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

+

Example of violations:

-    x = x               // violation
-    def method(y) {
-        y = y           // violation
-    }
-    a.b.c = a.b.c       // violation
+    import org.junit.Test
+    class MyTestCase {
+        public int count                        // violation
+        public static final MAX_VALUE = 1000    // violation
 
-    x = y               // acceptable
-    a.b = a.zz          // acceptable
-    a.b = a().b         // acceptable
-
-]]>
- clumsy + @Test + void testMe() { } + } +]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessarySemicolonRule - MAJOR - - - Semicolons as line terminators are not required in Groovy: remove them. Do not use a semicolon as a replacement for empty braces on for and while loops; this is a confusing practice.

-

The rule contains a String property called 'excludePattern'. Any source code line matching this pattern will not trigger a violation. The default value is '\\s?\\*.*|/\\*.*|.*//.*|.*\\*/.*' This is to filter out comments. Any source line that even looks like it is a comment is ignored.

-

\s?\*.* == whitespace plus star character plus anything /\*.* == any line that contains the /* sequence .*//.* == any line that contains the // sequence .*\*/.* == any line that contains the */ sequence

-

Example of violations:

+ org.codenarc.rule.junit.JUnitAssertEqualsConstantActualValueRule + MINOR + + + Reports usages of org.junit.Assert.assertEquals([message,] expected, actual) where the actual parameter is a constant or a literal. Most likely it was intended to be the expected value.

+

NOTE: This is a CodeNarc Enhanced Classpath Rule. It requires CodeNarc to have the application classes being analyzed, as well as any referenced classes, on the classpath.

+

Example of violations:

-    package my.company.server;  // violation
-
-    import java.lang.String;    // violation
-
-    println(value) ;             // violation
-
-    for (def x : list);         // violation
-
-    // this code is OK
-    println(value); println (otherValue)
-
-]]>
- clumsy + assertEquals(result, 2) + assertEquals("Message", result, 2) + assertEquals(result, 2.3d, 0.5d) + assertEquals("Message", result, 2.3d, 0.5d) +]]>
+ junit
- + - org.codenarc.rule.unnecessary.UnnecessaryTransientModifierRule - MAJOR - - - The field is marked as transient, but the class isn't Serializable, so marking it as transient has no effect. This may be leftover marking from a previous version of the code in which the class was transient, or it may indicate a misunderstanding of how serialization works.

-

Some Java frameworks change the semantics of the transient keyword. For instance, when using Terracotta the transient keyword may have slightly different semantics. You may need to turn this rule off depending on which Java frameworks are in use.

- Examples:

+ org.codenarc.rule.junit.JUnitPublicPropertyRule.fixed + MINOR + + + Checks for public properties defined on JUnit test classes. There is typically no need to expose a public property (with public getter and setter methods) on a test class.

+

This rule sets the default value of the applyToClassNames property to only match class names ending in 'Spec', 'Test', 'Tests' or 'TestCase'.

+

Example of violations:

-    class MyClass {
-        // class not serializable, violation occurs
-        transient String property
-    }
+    import org.junit.Test
+    class MyTestCase {
+        static String id    // violation
+        def helper          // violation
+        String name         // violation
 
-    class MySerializableClass implements Serializable {
-        // OK, class is serializable
-        transient String property
+        @Test
+        void testMe() { }
     }
-
-]]>
- clumsy +]]>
+ junit + + ignorePropertyNames + +
- + + - org.codenarc.rule.unnecessary.UnnecessaryFinalOnPrivateMethodRule - MAJOR - - - A private method is marked final. Private methods cannot be overridden, so marking it final is unnecessary.

-

Example of violations:

-
-    private final method() {}
-
-]]>
- clumsy + org.codenarc.rule.logging.PrintlnRule + MINOR + + + Checks for calls to this.print(), this.println() or this.printf(). Consider using a standard logging facility instead.

]]>
+ bug
- - org.codenarc.rule.unnecessary.UnnecessaryElseStatementRule - MAJOR - - - When an if statement block ends with a return statement, then the else is unnecessary. The logic in the else branch can be run without being in a new scope.

-

Example of violations:

-
-    if(value){
-        println 'Executing if logic...'
-        return true
-    } else {
-        println 'Executing else logic...'
-    }
-
-    // can be replaced by:
+    org.codenarc.rule.logging.PrintStackTraceRule
+    MINOR
+    
+    
+    Checks for calls to Throwable.printStackTrace() or StackTraceUtils.printSanitizedStackTrace(Throwable). Consider using a standard logging facility instead.

]]>
+ bug + - if(value){ - println 'Executing if logic...' - return true - } - println 'Executing else logic...' -
-]]>
- clumsy + + org.codenarc.rule.logging.SystemErrPrintRule + MINOR + + + Checks for calls to System.err.print(), System.err.println() or System.err.printf(). Consider using a standard logging facility instead.

]]>
+ bug
- - org.codenarc.rule.unnecessary.UnnecessaryParenthesesForMethodCallWithClosureRule - MAJOR - - - If a method is called and the only parameter to that method is an inline closure then the parentheses of the method call can be omitted.

-

Example of violations:

-
-    [1,2,3].each() { println it }
-
-]]>
- clumsy + org.codenarc.rule.logging.SystemOutPrintRule + MINOR + + + Checks for calls to System.out.print(), System.out.println() or System.out.printf(). Consider using a standard logging facility instead.

]]>
+ bug
- + - org.codenarc.rule.unnecessary.UnnecessaryPackageReferenceRule - MAJOR - - - Checks for explicit package reference for classes that Groovy imports by default, such as java.lang.String, java.util.Map and groovy.lang.Closure, as well as classes that were explicitly imported.

-

You do not need to specify the package for any classes from , , , , and , as well as the classes and .

-

Examples of violations include:

+ org.codenarc.rule.logging.LoggerForDifferentClassRule + MINOR + + + Checks for instantiating a logger for a class other than the current class. Checks for logger instantiations for Log4J, SLF4J, Logback, Apache Commons Logging and Java Logging API (java.util.logging).

+

This rule contains a parameter allowDerivedClasses. When set, a logger may be created about this.getClass().

+

Limitations:

+

* Only checks Loggers instantiated within a class field or property (not variables or expressions within a method)

+

* For Log4J: Does not catch Logger instantiations if you specify the full package name for the Logger class: e.g. org.apache.log4.Logger.getLogger(..)

+

* For SLF4J and Logback: Does not catch Log instantiations if you specify the full package name for the LoggerFactory class: e.g. org.slf4j.LoggerFactory.getLogger(..)

+

* For Commons Logging: Does not catch Log instantiations if you specify the full package name for the LogFactory class: e.g. org.apache.commons.logging.LogFactory.getLog(..)

+

* For Java Logging API: Does not catch Logger instantiations if you specify the full package name for the Logger class: e.g. java.util.logging.Logger.getLogger(..)

+

Here are examples of Log4J or Java Logging API code that cause violations:

-    // Field types
     class MyClass {
-        java.math.BigDecimal amount = 42.10                     // violation
+        private static final LOG = LoggerFactory.getLogger(SomeOtherClass)  // violation
+        def log1 = LoggerFactory.getLogger(SomeOtherClass.class)            // violation
+        def log2 = LoggerFactory.getLogger(SomeOtherClass.class.name)       // violation
     }
-
-    // Within expressions
-    if (value.class == java.math.BigInteger) { }                // violation
-    println "isClosure=${v instanceof groovy.lang.Closure}"     // violation
-    def p = java.lang.Runtime.availableProcessors()             // violation
-
-    // Constructor calls
-    def url = new java.net.URL('http://abc@example.com')        // violation
-
-    // Variable types
-    void doSomething() {
-        java.math.BigInteger maxValue = 0                       // violation
-        java.net.URI uri                                        // violation
+
+

Here are examples of Commons Logging code that cause violations:

+
+    class MyClass {
+        private static final LOG = LogFactory.getLog(SomeOtherClass)    // violation
+        Log log1 = LogFactory.getLog(SomeOtherClass.class)              // violation
+        def log2 = LogFactory.getLog(SomeOtherClass.class.getName())    // violation
     }
+
+

Here are examples of code that does NOT cause violations:

+
+    // Log4J or Java Logging API
 
-    // Method return types
-    java.io.Reader getReader() { }                              // violation
-    groovy.util.AntBuilder getAntBuilder() { }                  // violation
-
-    // Method parameter types
-    void writeCount(java.io.Writer writer, int count) { }       // violation
-    void init(String name, groovy.lang.Binding binding) { }     // violation
-
-    // Closure parameter types
-    def writeCount = { java.io.Writer writer, int count -> }    // violation
-
-    // Extends and implements
-    class MyHashMap extends java.util.HashMap { }               // violation
-    class MyList implements java.util.List { }                  // violation
+    class MyClass {
+        private static final LOG = LoggerFactory.getLogger(MyClass)                    // ok
+        def log2 = LoggerFactory.getLogger(MyClass.class)                              // ok
+        private static log3 = LoggerFactory.getLogger(MyClass.getClass().getName())    // ok
+        private static log4 = LoggerFactory.getLogger(MyClass.getClass().name)         // ok
+        private static log5 = LoggerFactory.getLogger(MyClass.class.getName())         // ok
+        private static log6 = LoggerFactory.getLogger(MyClass.class.name)              // ok
+    }
 
-    // Explicitly imported classes
-    import javax.servlet.http.Cookie
-    import javax.sql.DataSource
+    // Commons Logging
 
     class MyClass {
-        void doStuff(javax.servlet.http.Cookie cookie) {        // violation
-            def dataSource = [:] as javax.sql.DataSource        // violation
-        }
-    }
-
-]]>
- clumsy + private static final LOG = LogFactory.getLog(MyClass) // ok + def log2 = LogFactory.getLog(MyClass.class) // ok + private static log3 = LogFactory.getLog(MyClass.getClass().getName()) // ok + private static log4 = LogFactory.getLog(MyClass.getClass().name) // ok + private static log5 = LogFactory.getLog(MyClass.class.getName()) // ok + private static log6 = LogFactory.getLog(MyClass.class.name) // ok + } +]]>
+ bug
- + - org.codenarc.rule.unnecessary.UnnecessaryDefInVariableDeclarationRule - MAJOR - - - If a variable has a visibility modifier or a type declaration, then the def keyword is unneeded. For instance 'def private n = 2' is redundant and can be simplified to 'private n = 2'.

-

Examples of violations:

-
-    // def and private is redundant
-    def private string1 = 'example'
+    org.codenarc.rule.logging.LoggingSwallowsStacktraceRule
+    MINOR
+    
+    
+    If you are logging an exception then the proper API is to call error(Object, Throwable), which will log the message and the exception stack trace. If you call error(Object) then the stacktrace may not be logged.

]]>
+ bug + - // def and protected is redundant - def protected string2 = 'example' + + + org.codenarc.rule.logging.LoggerWithWrongModifiersRule + MINOR + + + Logger objects should be declared private, static and final.

+

This rule has a property: allowProtectedLogger, which defaults to false. Set it to true if you believe subclasses should have access to a Logger in a parent class and that Logger should be declared protected or public.

+

This rule has a property: allowNonStaticLogger, which defaults to false. Set it to true if you believe a logger should be allowed to be non-static.

]]>
+ bug +
- // def and public is redundant - def public string3 = 'example' + + + org.codenarc.rule.logging.MultipleLoggersRule + MINOR + + + This rule catches classes that have more than one logger object defined. Typically, a class has zero or one logger objects.

]]>
+ bug +
- // def and static is redundant - def static string4 = 'example' + - // def and final is redundant - def final string5 = 'example' + + org.codenarc.rule.naming.AbstractClassNameRule.fixed + MINOR + + + Verifies that the name of an abstract class matches the regular expression specified in the regex property. If that property is null or empty, then this rule is not applied (i.e., it does nothing). It defaults to null, so this rule must be explicitly configured to be active. This rule ignores interfaces and is applied only to abstract classes.

]]>
+ bug + + regex + + +
- // def and a type is redundant - def String string6 = 'example' -
-]]>
- clumsy + + org.codenarc.rule.naming.ClassNameRule + MINOR + + + Verifies that the name of a class matches a regular expression. By default it checks that the class name starts with an uppercase letter and is followed by zero or more word characters (letters, numbers or underscores) or dollar signs ($).

]]>
+ bug + + regex + + ([A-Z]\\w*\\$?)* +
- - org.codenarc.rule.unnecessary.UnnecessaryDotClassRule - MAJOR - - - To make a reference to a class, it is unnecessary to specify the '.class' identifier. For instance String.class can be shortened to String.

-

Example of violations:

-
-    // The '.class' identifier is unnecessary, violation occurs
-    def x = String.class
+    org.codenarc.rule.naming.FieldNameRule.fixed
+    MINOR
+    
+    
+    Verifies that the name of each field matches a regular expression. By default it checks that fields that are not static final have field names that start with a lowercase letter and contains only letters or numbers. By default, static final field names start with an uppercase letter and contain only uppercase letters, numbers and underscores.

+

NOTE: This rule checks only regular fields of a class, not properties. In Groovy, properties are fields declared with no access modifier (public, protected, private). Thus, this rule only checks fields that specify an access modifier. For naming of properties, see PropertyNameRule.

+

The order of precedence for the regular expression properties is: staticFinalRegex, finalRegex, staticRegex and finally regex. In other words, the first regex in that list matching the modifiers for the field is the one that is applied for the field name validation.

]]>
+ bug + + finalRegex + + + + ignoreFieldNames + + serialVersionUID + + + regex + + [a-z][a-zA-Z0-9]* + + + staticFinalRegex + + [A-Z][A-Z0-9_]* + + + staticRegex + + + - // Ok, unnecessary '.class' identifier has been excluded - def x = String -
-]]>
- clumsy + + org.codenarc.rule.naming.InterfaceNameRule.fixed + MINOR + + + Verifies that the name of an interface matches the regular expression specified in the regex property. If that property is null or empty, then this rule is not applied (i.e., it does nothing). It defaults to null, so this rule must be explicitly configured to be active.

]]>
+ bug + + regex + +
- - org.codenarc.rule.unnecessary.UnnecessaryInstanceOfCheckRule - MAJOR - - - This rule finds instanceof checks that cannot possibly evaluate to true. For instance, checking that (!variable instanceof String) will never be true because the result of a not expression is always a boolean.

-

Example of violations:

-
-    if (!variable instanceof String) { ... }    // always false
-    def x = !variable instanceof String         // always false
-
-    if (!variable instanceof Boolean) { ... }    // always true
-    def x = !variable instanceof Boolean         // always true
-
-    // this code is OK
-    if (!(variable instanceof String)) { ... }
-
-]]>
- clumsy + org.codenarc.rule.naming.MethodNameRule.fixed + MINOR + + + Verifies that the name of each method matches a regular expression. By default it checks that the method name starts with a lowercase letter. Implicit method names are ignored (i.e., 'main' and 'run' methods automatically created for Groovy scripts).

]]>
+ bug + + ignoreMethodNames + + + + regex + + [a-z]\\w* +
- - org.codenarc.rule.unnecessary.UnnecessarySubstringRule - MAJOR - - - This rule finds usages of String.substring(int) and String.substring(int, int) that can be replaced by use of the subscript operator. For instance, var.substring(5) can be replaced with var[5..-1].

-

Note that the String.substring(beginIndex,endIndex) method specifies a range of beginIndex..endIndex-1, while Groovy's String subscript specifies an inclusive range. So, "123456".substring(1, 5) is equivalent to "123456"[1..4].

-

Example of violations:

-
-    myVar.substring(5)          // can use myVar[5..-1] instead
-    myVar.substring(1, 5)       // can use myVar[1..4] instead
-
-]]>
- clumsy + org.codenarc.rule.naming.PackageNameRule + MINOR + + + Verifies that the package name of a class matches a regular expression. By default it checks that the package name consists of only lowercase letters and numbers, separated by periods.

]]>
+ bug + + packageNameRequired + + false + + + regex + + [a-z]+[a-z0-9]*(\\.[a-z0-9]+)* +
- - org.codenarc.rule.unnecessary.UnnecessaryDefInFieldDeclarationRule - MAJOR - - - If a field has a visibility modifier or a type declaration, then the def keyword is unneeded. For instance, 'static def constraints = {}' is redundant and can be simplified to 'static constraints = {}.

-

Example of violations:

-
-    class MyClass {
-        // def is redundant
-        static def constraints = {  }
-
-        // def and private is redundant
-        def private field1 = { }
-
-        // def and protected is redundant
-        def protected field2 = { }
-
-        // def and public is redundant
-        def public field3 = { }
-
-        // def and static is redundant
-        def static field4 = { }
-
-        // def and type is redundant
-        def Object field5 = { }
-    }
-
-]]>
- clumsy + org.codenarc.rule.naming.ParameterNameRule.fixed + MINOR + + + Verifies that the name of each parameter matches a regular expression. This rule applies to method parameters, constructor parameters and closure parameters. By default it checks that parameter names start with a lowercase letter and contains only letters or numbers.

]]>
+ bug + + ignoreParameterNames + + + + regex + + [a-z][a-zA-Z0-9]* +
- - org.codenarc.rule.unnecessary.UnnecessaryCastRule + org.codenarc.rule.naming.PropertyNameRule.fixed MINOR - - - Checks for unnecessary cast operations.

-

Example of violations:

-
-    int count = (int)123                    // violation
-    def longValue = (long)123456L           // violation
-    def bigDecimal = (BigDecimal)1234.56    // violation
-    String name = (String) "Joe"            // violation
-    def list = (List)[1, 2, 3]              // violation
-    def map = (Map)[a:1]                    // violation
-
-]]>
- clumsy + + + Verifies that the name of each property matches a regular expression. By default it checks that property names (other than static final) start with a lowercase letter and contains only letters or numbers. By default, static final property names start with an uppercase letter and contain only uppercase letters, numbers and underscores.

+

NOTE: This rule checks only properties of a class, not regular fields. In Groovy, properties are fields declared with no access modifier (public, protected, private). For naming of regular fields, see FieldNameRule.

+

The order of precedence for the regular expression properties is: staticFinalRegex, finalRegex, staticRegex and finally regex. In other words, the first regex in that list matching the modifiers for the property is the one that is applied for the field name validation.

]]>
+ bug + + finalRegex + + + + ignorePropertyNames + + + + regex + + [a-z][a-zA-Z0-9]* + + + staticFinalRegex + + [A-Z][A-Z0-9_]* + + + staticRegex + +
- - org.codenarc.rule.unnecessary.UnnecessaryToStringRule + org.codenarc.rule.naming.VariableNameRule.fixed MINOR - - - Checks for unnecessary calls to toString(). This includes:

-]]>
- clumsy + + + Verifies that the name of each variable matches a regular expression. By default it checks that non-final variable names start with a lowercase letter and contains only letters or numbers. By default, final variable names start with an uppercase letter and contain only uppercase letters, numbers and underscores.

]]>
+ bug + + finalRegex + + [A-Z][A-Z0-9_]* + + + ignoreVariableNames + + + + regex + + [a-z][a-zA-Z0-9]* +
- + - org.codenarc.rule.unnecessary.UnnecessarySafeNavigationOperatorRule - MAJOR - - - Check for the operator (?.) applied to constants and literals, or this or super, or constructor calls, all of which can never be null.

-

Example of violations:

+ org.codenarc.rule.naming.ConfusingMethodNameRule + MINOR + + + Checks for very confusing method names. The referenced methods have names that differ only by capitalization. This is very confusing because if the capitalization were identical then one of the methods would override the other.

+

Also, violations are triggered when methods and fields have very similar names.

-    def myMethod() {
-        "abc"?.bytes            // violation
-        [1,2]?.getSize()        // violation
-        [abc:123]?.name         // violation
-        [:]?.toString()         // violation
-        123?.class              // violation
-        123.45?.getClass()      // violation
-        Boolean.FALSE?.class    // violation
-        Boolean.TRUE?.class     // violation
-        this?.class             // violation
-        super?.getClass()       // violation
-        new Long(100)?.class    // violation
+    class MyClass {
+        int total
+        int total() {
+            1
+        }
     }
-
-]]>
- clumsy +]]>
+ bug
- - + - org.codenarc.rule.unused.UnusedArrayRule + org.codenarc.rule.naming.ObjectOverrideMisspelledMethodNameRule MINOR - - - Checks for array allocations that are not assigned or used, unless it is the last statement within a block (because it may be the intentional return value). Examples include:

+ + + Verifies that the names of the most commonly overridden methods of Object: equals, hashCode and toString, are correct.

+

Here are some examples of code that produces violations:

-    int myMethod() {
-        new String[3]               // unused
-        return -1
-    }
+    boolean equal(Object o) {}                  // violation
+    boolean equal(int other) {}                 // ok; wrong param type
+    boolean equal(Object o, int other) {}       // ok; too many params
 
-    String[] myMethod() {
-        new String[3]               // OK (last statement in block)
-    }
+    boolean equaLS(Object o) {}                 // violation
 
-    def closure = {
-        doStuff()
-        new Date[3]                 // unused
-        doOtherStuff()
-    }
+    int hashcode() {}                           // violation
+    int hashCOde() {}                           // violation
+    int hashcode(int value) {}                  // ok; not empty params
 
-    def closure = { new Date[3] }   // OK (last statement in block)
-
-]]>
+ String tostring() {} // violation + String toSTring() {} // violation + String tostring(int value) {} // ok; not empty params +]]>
bug
+ - org.codenarc.rule.unused.UnusedObjectRule + org.codenarc.rule.naming.FactoryMethodNameRule MINOR - - - Checks for object allocations that are not assigned or used, unless it is the last statement within a block (because it may be the intentional return value). Examples include:

-

By default, this rule does not analyze test files. This rule sets the default value of the property to ignore file names ending in 'Test.groovy', 'Tests.groovy' or 'TestCase.groovy'. Invoking constructors without using the result is a common pattern in tests.

+ + + A factory method is a method that creates objects, and they are typically named either buildFoo(), makeFoo(), or createFoo(). This rule enforces that only one naming convention is used. It defaults to allowing makeFoo(), but that can be changed using the property regex. The regex is a negative expression; it specifically bans methods named build* or create*. However, methods named build or build* receive some special treatment because of the popular Builder Pattern. If the 'build' method is in a class named *Builder then it does not cause a violation.

+

Builder methods are slightly different than factory methods.

+

Example of violations:

-    int myMethod() {
-        new BigDecimal("23.45")     // unused
-        return -1
-    }
+    class MyClass {
 
-    BigDecimal myMethod() {
-        new BigDecimal("23.45")     // OK (last statement in block)
-    }
+        // violation. Factory methods should be named make()
+        def create() {
+        }
 
-    def closure = {
-        doStuff()
-        new Date()                  // unused
-        doOtherStuff()
-    }
+        // violation. Factory methods should be named make()
+        def createSomething() {
+        }
 
-    def closure = { new Date() }    // OK (last statement in block)
-
-]]>
- bug -
+ // violation. Builder method not in class named *Builder + def build() { + } + + // violation. Builder method not in class named *Builder + def buildSomething() { + } + + // this is OK because it is called make + def make() { + } + + // this is also OK + def makeSomething() { + } + + // OK, overriding a parent + @Override + build() { } + + } - - org.codenarc.rule.unused.UnusedPrivateFieldRule - MINOR - - - Checks for private fields that are not referenced within the same class. Note that the private modifier is not currently "respected" by Groovy code (i.e., Groovy can access private members within other classes). By default, fields named serialVersionUID are ignored. The rule has a property named ignoreFieldNames, which can be set to ignore other field names as well. For instance, to ignore fields named 'fieldx', set the property to the 'fieldx, serialVersionUID'

-

Known limitations:

-]]>
+ class WidgetBuilder { + + // OK, the class name ends in Builder + def build() { + } + } +]]> bug - ignoreFieldNames - - serialVersionUID + regex + + (build.*\|create.*)
+ - org.codenarc.rule.unused.UnusedPrivateMethodRule + org.codenarc.rule.naming.ClassNameSameAsFilenameRule MINOR - - - Checks for private methods that are not referenced within the same class. Note that the private modifier is not currently "respected" by Groovy code (i.e., Groovy can access private members within other classes).

-

Known limitations:

-]]>
+ + + Reports files containing only one top level class / enum / interface which is named differently than the file.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
bug
+ - org.codenarc.rule.unused.UnusedVariableRule.fixed + org.codenarc.rule.naming.PackageNameMatchesFilePathRule.fixed MINOR - - - Checks for variables that are never referenced.

-

The rule has a property named ignoreVariableNames, which can be set to ignore some variable names. For instance, to ignore fields named 'unused', set the property to 'unused'.

-

Known limitations:

-]]>
+ + + A package source file's path should match the package declaration.

+

NOTE: This is a file-based rule, rather than an AST-based rule, so the applyToClassNames and doNotApplyToClassNames rule configuration properties are not available. See Standard Properties for Configuring Rules.

]]>
bug - ignoreVariableNames - + groupId +
- - - org.codenarc.rule.unused.UnusedPrivateMethodParameterRule - MINOR - - - Checks for parameters to private methods that are not referenced within the method body. Note that the private modifier is not currently "respected" by Groovy code (i.e., Groovy can access private members within other classes).

-

Known limitations:

-]]>
- bug -
- - - - org.codenarc.rule.unused.UnusedMethodParameterRule - MINOR - - - This rule finds instances of method parameters not being used. It does not analyze private methods (that is done by the UnusedPrivateMethodParameter rule) or methods marked @Override.

-]]>
- bug -
- - - - + - org.codenarc.rule.jdbc.DirectConnectionManagementRule + org.codenarc.rule.naming.ClassNameSameAsSuperclassRule MINOR - - - The J2EE standard requires that applications use the container's resource management facilities to obtain connections to resources. Every major web application container provides pooled database connection management as part of its resource management framework. Duplicating this functionality in an application is difficult and error prone, which is part of the reason it is forbidden under the J2EE standard.

-

For more information see: https://www.fortify.com/vulncat/en/vulncat/java/j2ee_badpractices_getconnection.html

-

Example of violations:

+ + + Checks for any class that has an identical name to its superclass, other than the package. This can be very confusing.

+

Also see FindBugs NM_SAME_SIMPLE_NAME_AS_SUPERCLASS rule.

+

Example of violations:

-    DriverManager.getConnection()
-    java.sql.DriverManager.getConnection()
-
-]]>
- bug -
- - - - org.codenarc.rule.jdbc.JdbcConnectionReferenceRule - MINOR - - - Checks for direct use of java.sql.Connection, which is discouraged and almost never necessary in application code.

-

For a more alternative, see http://groovy.codehaus.org/Database+features for information on the Groovy Sql abstraction layer for JDBC/SQL.

-

Note: If a violation is triggered from an import statement, then you may get multiple violations per import if there are multiple classes in the source file. In that case, the imports are processed once per class.

- ]]>
- bug -
- - - - org.codenarc.rule.jdbc.JdbcResultSetReferenceRule - MINOR - - - Checks for direct use of java.sql.ResultSet, which is not necessary if using the Groovy Sql facility or an ORM framework such as .

-

See http://groovy.codehaus.org/Database+features for information on the Groovy Sql abstraction layer for JDBC/SQL.

-

Note: If a violation is triggered from an import statement, then you may get multiple violations per import if there are multiple classes in the source file. In that case, the imports are processed once per class.

- ]]>
+ class MyClass extends other.MyClass // violation +]]> bug
- + - org.codenarc.rule.jdbc.JdbcStatementReferenceRule + org.codenarc.rule.naming.InterfaceNameSameAsSuperInterfaceRule MINOR - - - Checks for direct use of java.sql.Statement, java.sql.PreparedStatement, or java.sql.CallableStatement, which is not necessary if using the Groovy Sql facility or an ORM framework such as .

-

See http://groovy.codehaus.org/Database+features for information on the Groovy Sql abstraction layer for JDBC/SQL.

-

Note: If a violation is triggered from an import statement, then you may get multiple violations per import if there are multiple classes in the source file. In that case, the imports are processed once per class.

-]]>
+ + + Checks for any interface that has an identical name to its super-interface, other than the package. This can be very confusing.

+

Example of violations:

+
+    interface MyInterface extends other.MyInterface { }     // violation
+
]]>
bug
@@ -5835,9 +6802,9 @@ for (int i = 0; i < 100; ++i) { MINOR - The permissions classes such as java.security.Permission and java.security.BasicPermission are designed to be extended. Classes that derive from these permissions classes, however, must prohibit extension. This prohibition ensures that malicious subclasses cannot change the properties of the derived class. Classes that implement sensitive interfaces such as java.security.PrivilegedAction and java.security.PrivilegedActionException must also be declared final for analogous reasons.

-

For more information see: https://www.securecoding.cert.org/confluence/display/java/SEC07-J.+Classes+that+derive+from+a+sensitive+class+or+implement+a+sensitive+interface+must+be+declared+final

-

Example of violations:

+ The permissions classes such as java.security.Permission and java.security.BasicPermission are designed to be extended. Classes that derive from these permissions classes, however, must prohibit extension. This prohibition ensures that malicious subclasses cannot change the properties of the derived class. Classes that implement sensitive interfaces such as java.security.PrivilegedAction and java.security.PrivilegedActionException must also be declared final for analogous reasons.

+

For more information see: https://www.securecoding.cert.org/confluence/display/java/SEC07-J.+Classes+that+derive+from+a+sensitive+class+or+implement+a+sensitive+interface+must+be+declared+final

+

Example of violations:

     class MyPermission extends java.security.Permission {
         MyPermission(String name) { super(name) }
@@ -5858,8 +6825,7 @@ for (int i = 0; i < 100; ++i) {
     class MyPrivilegedActionException extends PrivilegedActionException {
         MyPrivilegedActionException(Exception exception) { super(exception) }
     }
-
-]]>
+]]>
bug
@@ -5869,10 +6835,10 @@ for (int i = 0; i < 100; ++i) { MINOR - Reports usages of java.util.Random, which can produce very predictable results. If two instances of Random are created with the same seed and sequence of method calls, they will generate the exact same results. Use java.security.SecureRandom instead, which provides a cryptographically strong random number generator. SecureRandom uses PRNG, which means they are using a deterministic algorithm to produce a pseudo-random number from a true random seed. SecureRandom produces non-deterministic output.

-

By default, this rule ignores test classes are ignored.

-

For more information see: http://www.klocwork.com/products/documentation/current/Checkers:SV.RANDOM

-

Example of violations:

+ Reports usages of java.util.Random, which can produce very predictable results. If two instances of Random are created with the same seed and sequence of method calls, they will generate the exact same results. Use java.security.SecureRandom instead, which provides a cryptographically strong random number generator. SecureRandom uses PRNG, which means they are using a deterministic algorithm to produce a pseudo-random number from a true random seed. SecureRandom produces non-deterministic output.

+

By default, this rule ignores test classes are ignored.

+

For more information see: http://www.klocwork.com/products/documentation/current/Checkers:SV.RANDOM

+

Example of violations:

      def r1 = new Random()
      def r2 = new java.util.Random()
@@ -5882,8 +6848,7 @@ for (int i = 0; i < 100; ++i) {
      // this is OK
      new java.security.SecureRandom()
      new SecureRandom()
-
-]]>
+]]>
bug
@@ -5893,9 +6858,8 @@ for (int i = 0; i < 100; ++i) { MINOR - The File.createTempFile() method is insecure, and has been deprecated by the ESAPI secure coding library. It has been replaced by the ESAPI Randomizer.getRandomFilename(String) method.

-

For more information see the ESAPI website and the Randomizer Javadoc.

- ]]>
+ The File.createTempFile() method is insecure, and has been deprecated by the ESAPI secure coding library. It has been replaced by the ESAPI Randomizer.getRandomFilename(String) method.

+

For more information see the ESAPI website and the Randomizer Javadoc.

]]>
bug
@@ -5905,12 +6869,11 @@ for (int i = 0; i < 100; ++i) { MINOR - Web applications should never call System.exit(). A call to System.exit() is probably part of leftover debug code or code imported from a non-J2EE application.

-

[[1]] Standards Mapping - OWASP Top 10 2004 - (OWASP 2004) A9 Application Denial of Service

-

[[2]] Standards Mapping - Security Technical Implementation Guide Version 3 - (STIG 3) APP6080 CAT II

-

[[3]] Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 382

-

[[4]] Standards Mapping - Payment Card Industry Data Security Standard Version 1.1 - (PCI 1.1) Requirement 6.5.9

- ]]>
+ Web applications should never call System.exit(). A call to System.exit() is probably part of leftover debug code or code imported from a non-J2EE application.

+

[[1]] Standards Mapping - OWASP Top 10 2004 - (OWASP 2004) A9 Application Denial of Service

+

[[2]] Standards Mapping - Security Technical Implementation Guide Version 3 - (STIG 3) APP6080 CAT II

+

[[3]] Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 382

+

[[4]] Standards Mapping - Payment Card Industry Data Security Standard Version 1.1 - (PCI 1.1) Requirement 6.5.9

]]>
bug
@@ -5920,10 +6883,9 @@ for (int i = 0; i < 100; ++i) { MINOR - The finalize() method should only be called by the JVM after the object has been garbage collected.

-

While the Java Language Specification allows an object's finalize() method to be called from outside the finalizer, doing so is usually a bad idea. For example, calling finalize() explicitly means that finalize() will be called more than once: the first time will be the explicit call and the last time will be the call that is made after the object is garbage collected.

-

References: Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 586

- ]]>
+ The finalize() method should only be called by the JVM after the object has been garbage collected.

+

While the Java Language Specification allows an object's finalize() method to be called from outside the finalizer, doing so is usually a bad idea. For example, calling finalize() explicitly means that finalize() will be called more than once: the first time will be the explicit call and the last time will be the call that is made after the object is garbage collected.

+

References: Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 586

]]>
bug @@ -5933,15 +6895,15 @@ for (int i = 0; i < 100; ++i) { MINOR - This rule reports violations of the Enterprise JavaBeans specification by using the java.io package to access files or the file system.

-

The Enterprise JavaBeans specification requires that every bean provider follow a set of programming guidelines designed to ensure that the bean will be portable and behave consistently in any EJB container [1].

-

In this case, the program violates the following EJB guideline: "An enterprise bean must not use the java.io package to attempt to access files and directories in the file system."

-

A requirement that the specification justifies in the following way: "The file system APIs are not well-suited for business components to access data. Business components should use a resource manager API, such as JDBC, to store data."

-

REFERENCES

-

[[1]] Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 576

-

[[2]] The Enterprise JavaBeans 2.1 Specification Sun Microsystems

- By default, this rule is not applied to tests and test cases.

-

Example of violations:

+ This rule reports violations of the Enterprise JavaBeans specification by using the java.io package to access files or the file system.

+

The Enterprise JavaBeans specification requires that every bean provider follow a set of programming guidelines designed to ensure that the bean will be portable and behave consistently in any EJB container [1].

+

In this case, the program violates the following EJB guideline: "An enterprise bean must not use the java.io package to attempt to access files and directories in the file system."

+

A requirement that the specification justifies in the following way: "The file system APIs are not well-suited for business components to access data. Business components should use a resource manager API, such as JDBC, to store data."

+

REFERENCES

+

[[1]] Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 576

+

[[2]] The Enterprise JavaBeans 2.1 Specification Sun Microsystems

+

By default, this rule is not applied to tests and test cases.

+

Example of violations:

     FileSystem.getFileSystem()          // any method on FileSystem
     FileSystem.fileSystem.delete(aFile) // property access of FileSystem
@@ -5959,8 +6921,7 @@ for (int i = 0; i < 100; ++i) {
 
     // don't create random access file
     new RandomAccessFile(name, parent)
-
-]]>
+]]>
bug @@ -5970,16 +6931,15 @@ for (int i = 0; i < 100; ++i) { MINOR - Triggers a violation when an array is declared public, final, and static.

-

In most cases an array declared public, final and static is a bug. Because arrays are mutable objects, the final constraint requires that the array object itself be assigned only once, but makes no guarantees about the values of the array elements. Since the array is public, a malicious program can change the values stored in the array. In most situations the array should be made private.

-

Example of violations:

+ Triggers a violation when an array is declared public, final, and static.

+

In most cases an array declared public, final and static is a bug. Because arrays are mutable objects, the final constraint requires that the array object itself be assigned only once, but makes no guarantees about the values of the array elements. Since the array is public, a malicious program can change the values stored in the array. In most situations the array should be made private.

+

Example of violations:

     class MyClass {
         public static final String[] myArray = init()
         public static final def myArray = [] as String[]
     }
-
-]]>
+]]>
bug @@ -5989,10 +6949,11 @@ for (int i = 0; i < 100; ++i) { MINOR - Creates a violation when the program violates secure coding principles by declaring a finalize() method public.

-

A program should never call finalize explicitly, except to call super.finalize() inside an implementation of finalize(). In mobile code situations, the otherwise error prone practice of manual garbage collection can become a security threat if an attacker can maliciously invoke one of your finalize() methods because it is declared with public access. If you are using finalize() as it was designed, there is no reason to declare finalize() with anything other than protected access.

-

References:

-]]>
+ Creates a violation when the program violates secure coding principles by declaring a finalize() method public.

+

A program should never call finalize explicitly, except to call super.finalize() inside an implementation of finalize(). In mobile code situations, the otherwise error prone practice of manual garbage collection can become a security threat if an attacker can maliciously invoke one of your finalize() methods because it is declared with public access. If you are using finalize() as it was designed, there is no reason to declare finalize() with anything other than protected access.

+

References:

+

* Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 583

+

* G. McGraw Securing Java. Chapter 7: Java Security Guidelines

]]>
bug @@ -6002,10 +6963,11 @@ for (int i = 0; i < 100; ++i) { MINOR - Finds code that violates secure coding principles for mobile code by declaring a member variable public but not final.

-

All public member variables in an Applet and in classes used by an Applet should be declared final to prevent an attacker from manipulating or gaining unauthorized access to the internal state of the Applet.

-

References:

-]]>
+ Finds code that violates secure coding principles for mobile code by declaring a member variable public but not final.

+

All public member variables in an Applet and in classes used by an Applet should be declared final to prevent an attacker from manipulating or gaining unauthorized access to the internal state of the Applet.

+

References:

+

* Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 493

+

* G. McGraw Securing Java. Chapter 7: Java Security Guidelines

]]>
bug @@ -6015,1288 +6977,1676 @@ for (int i = 0; i < 100; ++i) { MINOR - Reports incomplete interface implementations created by map-to-interface coercions.

-

By default, this rule does not apply to test files.

-

NOTE: This is a CodeNarc Enhanced Classpath Rule. It requires CodeNarc to have the application classes being analyzed, as well as any referenced classes, on the classpath.

-

Example of violations:

+ Reports incomplete interface implementations created by map-to-interface coercions.

+

By default, this rule does not apply to test files.

+

NOTE: This is a CodeNarc Enhanced Classpath Rule. It requires CodeNarc to have the application classes being analyzed, as well as any referenced classes, on the classpath.

+

Example of violations:

     [mouseClicked: { ... }] as MouseListener
     //not all MouseListener methods are implemented which can lead to UnsupportedOperationException-s
-
-]]>
+]]>
bug - + - + - org.codenarc.rule.formatting.BracesForClassRule + org.codenarc.rule.serialization.SerialVersionUIDRule MINOR - - - Checks the location of the opening brace (\{) for classes. By default, requires them on the same line, but the sameLine property can be set to false to override this.

-

NOTE: This rule ignores annotation types, e.g. @interface MyAnnotation {}.

- ]]>
- convention + + + A serialVersionUID is normally intended to be used with Serialization. It needs to be of type long, static, and final. Also, it should be declared private. Providing no modifier creates a Property and Groovy generates a getter, which is probably not intended.

+

From API javadoc for java.io.Serializable: It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members.

]]>
+ bug +
+ + + + org.codenarc.rule.serialization.SerializableClassMustDefineSerialVersionUIDRule + MINOR + + + Classes that implement Serializable should define a serialVersionUID. Deserialization uses this number to ensure that a loaded class corresponds exactly to a serialized object. If you don't define serialVersionUID, the system will make one by hashing most of your class's features. Then if you change anything, the UID will change and Java won't let you reload old data.

+

An example of a missing serialVersionUID:

+
+    class MyClass implements Serializable {
+        // missing serialVersionUID
+    }
+
]]>
+ bug +
+ + + + org.codenarc.rule.serialization.SerialPersistentFieldsRule + MINOR + + + To use a Serializable object's serialPersistentFields correctly, it must be declared private, static, and final.

+

The Java Object Serialization Specification allows developers to manually define Serializable fields for a class by specifying them in the serialPersistentFields array. This feature will only work if serialPersistentFields is declared as private, static, and final. Also, specific to Groovy, the field must be of type ObjectStreamField[], and cannot be Object.

+

References:

+

* Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 485

+

* Sun Microsystems, Inc. Java Sun Tutorial

+

Example of violations:

+
+    class MyClass implements Serializable {
+        public ObjectStreamField[] serialPersistentFields = [ new ObjectStreamField("myField", List.class) ] as ObjectStreamField[]
+    }
+
+    // the JVM sees the field type as Object, which won't work
+    class MyOtherClass implements Serializable {
+        private static final serialPersistentFields = [ new ObjectStreamField("myField", List.class) ] as ObjectStreamField[]
+    }
+
]]>
+ bug +
+ + + + org.codenarc.rule.serialization.EnumCustomSerializationIgnoredRule + MINOR + + + Checks for enums that define writeObject() or writeReplace() methods, or declare serialPersistentFields or serialVersionUID fields, all of which are ignored for enums.

+

From the javadoc for ObjectOutputStream:

+

The process by which enum constants are serialized cannot be customized; any class-specific writeObject and writeReplace methods defined by enum types are ignored during serialization. Similarly, any serialPersistentFields or serialVersionUID field declarations are also ignored--all enum types have a fixed serialVersionUID of 0L.

+

Example of violations:

+
+    enum MyEnum {
+        ONE, TWO, THREE
+        private static final long serialVersionUID = 1234567L               // violation
+        private static final ObjectStreamField[] serialPersistentFields =   // violation
+            { new ObjectStreamField("name", String.class) }
+        String name;
+
+        Object writeReplace() throws ObjectStreamException { .. }      // violation
+        private void writeObject(ObjectOutputStream stream) { .. }     // violation
+    }
+
]]>
+ bug +
+ + + + + org.codenarc.rule.size.ClassSizeRule + MAJOR + + + Checks if the size of a class exceeds the number of lines specified by the maxLines property.

]]>
+ bug - sameLine - true + maxLines + + 1000
- - org.codenarc.rule.formatting.LineLengthRule + org.codenarc.rule.size.CyclomaticComplexityRule.fixed MINOR - - - Checks the maximum length for each line of source code. It checks for number of characters, so lines that include tabs may appear longer than the allowed number when viewing the file. The maximum line length can be configured by setting the length property, which defaults to 120.

-

NOTE: This rule does not support the @SuppressAnnotations annotation or the classname-based rule properties (applyToClassNames, doNotApplyToClassNames) to enable/disable the rule. If you want to specify or restrict where this rule is applied, you must use the file-based rule properties: applyToFileNames, doNotApplyToFileNames, applyToFilesMatching and doNotApplyToFilesMatching.

- ]]>
- convention + + + Calculates the Cyclomatic Complexity for methods/classes and checks against configured threshold values.

+

The maxMethodComplexity property holds the threshold value for the cyclomatic complexity value for each method. If this value is non-zero, a method with a cyclomatic complexity value greater than this value is considered a violation.

+

The maxClassAverageMethodComplexity property holds the threshold value for the average cyclomatic complexity value for each class. If this value is non-zero, a class with an average cyclomatic complexity value greater than this value is considered a violation.

+

This rule treats "closure fields" as methods. If a class field is initialized to a Closure (ClosureExpression), then that Closure is analyzed and checked just like a method.

+

The cyclomatic complexity value is calculated as follows:

+

Start with a initial (default) value of one (1). Add one (1) for each occurrence of each of the following:

+

* if statement

+

* while statement

+

* for statement

+

* case statement

+

* catch statement

+

* && and || boolean operations

+

* ?: ternary operator and ?: Elvis operator.

+

* ?. null-check operator

+

* See the Cyclomatic Complexity Wikipedia entry

+

* See the original paper describing Cyclomatic Complexity

+

* See the GMetrics Cyclomatic Complexity metric. This includes a discussion of guidelines for interpreting cyclomatic complexity values.

+

* This rule requires Groovy 1.6 (or later).

+

* This rule requires the GMetrics jar on the classpath. See GMetrics.

]]>
+ bug - ignoreImportStatements - - true + ignoreMethodNames + - ignoreLineRegex - + maxClassAverageMethodComplexity + + 20 - ignorePackageStatements - - true + maxClassComplexity + + 0 - length - - 120 + maxMethodComplexity + + 20
- - org.codenarc.rule.formatting.BracesForForLoopRule + org.codenarc.rule.size.MethodCountRule MINOR - - - Checks the location of the opening brace (\{) for for loops. By default, requires them on the same line, but the sameLine property can be set to false to override this.

- ]]>
- convention + + + Checks if the number of methods within a class exceeds the number of lines specified by the maxMethod property.

+

A class with too many methods is probably a good suspect for refactoring, in order to reduce its complexity and find a way to have more fine grained objects.

]]>
+ bug - sameLine - true + maxMethods + + 30
- - org.codenarc.rule.formatting.BracesForIfElseRule + org.codenarc.rule.size.MethodSizeRule.fixed + MAJOR + + + Checks if the size of a method exceeds the number of lines specified by the maxLines property.

+

Known Limitations:

+

* Annotations on a method are included in the size (line count) for that method.

]]>
+ bug + + ignoreMethodNames + + + + maxLines + + 100 + +
+ + + org.codenarc.rule.size.NestedBlockDepthRule MINOR - - - Checks the location of the opening brace (\{) for if statements. By default, requires them on the same line, but the sameLine property can be set to false to override this.

-]]>
- convention + + + Checks for blocks or closures nested more deeply than a configured maximum number. Blocks include if, for, while, switch, try, catch, finally and synchronized blocks/statements, as well as closures.

+

Methods calls, constructor calls, and property access through Builder objects are ignore. For instance, this code does not cause a violation:

+
+    myBuilder.root {
+        foo {
+            bar {
+                baz {
+                    quix {
+                        qux {
+                            quaxz {
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
]]>
+ bug + + ignoreRegex + + .*(b|B)uilder + + + maxNestedBlockDepth + + 5 + +
+ + + + org.codenarc.rule.size.CrapMetricRule.fixed + MINOR + + + Calculates the C.R.A.P. (Change Risk Anti-Patterns) metric score for methods/classes and checks against configured threshold values.

+

The CRAP metric score is based on the cyclomatic complexity and test coverage for individual methods. A method with a CRAP value greater than the maxMethodCrapScore property causes a violation. Likewise, a class that has an (average method) CRAP value greater than the maxClassAverageMethodCrapScore property causes a violation.

+

NOTE: This rule requires the GMetrics[3] jar, version 0.5 (or later), on the classpath, as well as a Cobertura[4]-[6] XML coverage file. If either of these prerequisites is not available, this rule logs a warning messages and exits (i.e., does nothing).

+

The maxMethodCrapScore property holds the threshold value for the CRAP value for each method. If this value is non-zero, a method with a cyclomatic complexity value greater than this value is considered a violation.

+

The maxClassAverageMethodCrapScore property holds the threshold value for the average CRAP value for each class. If this value is non-zero, a class with an average cyclomatic complexity value greater than this value is considered a violation.

+

NOTE: This rule does NOT treat closure fields as methods (unlike some of the other size/complexity rules).

]]>
+ bug - elseOnSameLineAsClosingBrace - - true + coberturaXmlFile + - elseOnSameLineAsOpeningBrace - - true + ignoreMethodNames + - sameLine - - true + maxClassAverageMethodCrapScore + + 30 - validateElse - - false + maxClassCrapScore + + 0 -
- - - - org.codenarc.rule.formatting.BracesForMethodRule - MINOR - - - Checks the location of the opening brace (\{) for constructors and methods. By default, requires them on the same line, but the sameLine property can be set to false to override this.

- ]]>
- convention - sameLine - true + maxMethodCrapScore + + 30
- + - org.codenarc.rule.formatting.BracesForTryCatchFinallyRule + org.codenarc.rule.size.AbcMetricRule.fixed MINOR - - - Checks the location of the opening brace (\{) for try statements. By default, requires them on the line, but the sameLine property can be set to false to override this.

- ]]>
- convention + + + Calculates the ABC size metric for methods/classes and checks against configured threshold values.

+

The maxMethodAbcScore property holds the threshold value for the ABC score for each method. If this value is non-zero, a method with an ABC score greater than this value is considered a violation. The value does not have to be an integer (e.g., 1.7 is allowed).

+

The maxClassAverageMethodAbcScore property holds the threshold value for the average ABC score for each class. If this value is non-zero, a class with an average ABC score value greater than this value is considered a violation. The value does not have to be an integer.

+

The maxClassAbcScore property holds the threshold value for the total ABC score value for each class. If this value is non-zero, a class with a total ABC score greater than this value is considered a violation. The value does not have to be an integer.

+

This rule treats "closure fields" as methods. If a class field is initialized to a Closure (ClosureExpression), then that Closure is analyzed and checked just like a method.

+

The ABC score is calculated as follows: The ABC metric measures size by counting the number of Assignments (A), Branches (B) and Conditions (C) and assigns a single numerical score calculated as:

+

|ABC| = sqrt((A*A)+(B*B)+(C*C))

+

The ABC Metric calculation rules for Groovy:

+

* Add one to the assignment count for each occurrence of an assignment operator, excluding constant declarations: = *= /= %= += <<= >>= &= |= ^= >>>=

+

* Add one to the assignment count for each occurrence of an increment or decrement operator (prefix or postfix): ++ --

+

* Add one to the branch count for each function call or class method call.

+

* Add one to the branch count for each occurrence of the new operator.

+

* Add one to the condition count for each use of a conditional operator: == != <= >= < > <=> =~ ==~

+

* Add one to the condition count for each use of the following keywords: else case default try catch ?

+

* Add one to the condition count for each unary conditional expression.

+

* See the ABC Metric specification

+

* See the Blog post describing guidelines for interpreting an ABC score

+

* This (Spanish) blog post about the eXcentia Sonar ABC Metric Plugin (for Java) includes a table of risk classifications for ABC scores for both methods and classes.

+

* See the GMetrics ABC metric. This includes a discussion of guidelines for interpreting ABC scores.

+

* This rule requires Groovy 1.6 (or later).

+

* This rule requires the GMetrics jar on the classpath. See GMetrics.

]]>
+ bug - sameLine - true + ignoreMethodNames + + + + maxClassAbcScore + + 0 + + + maxClassAverageMethodAbcScore + + 60 + + + maxMethodAbcScore + + 60
- + - org.codenarc.rule.formatting.ClassJavadocRule + org.codenarc.rule.size.ParameterCountRule MINOR - - - Makes sure each class and interface definition is preceded by javadoc. Enum definitions are not checked, due to strange behavior in the Groovy AST. By default, only the main class in a file is checked for Javadoc. The main class is defined as the class that has the same name as the source file, for instance MyClass is the main class in MyClass.groovy but the class MyOtherClass defined in the same source file is not the main class. To check all the classes in the file set the rule property applyToNonMainClasses to true.

- ]]>
- convention -
- - - - org.codenarc.rule.formatting.SpaceAfterCommaRule - MAJOR - - - Checks that there is at least one space or whitespace following each comma. That includes checks for method and closure declaration parameter lists, method call parameter lists, Map literals and List literals.

-

Known limitations:

-]]>
- convention -
- - - - org.codenarc.rule.formatting.SpaceAfterSemicolonRule - MAJOR - - - Check that there is at least one space (blank) or whitespace following a semicolon that separates:

-]]>
- convention -
+ + + Checks if the number of parameters in method/constructor exceeds the number of parameters specified by the maxParameters property.

+

Example of violations:

+
+    void someMethod(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6) { // violation
+    }
 
-  
-  
-    org.codenarc.rule.formatting.SpaceAroundOperatorRule
-    MAJOR
-    
-    
-    Check that there is at least one space (blank) or whitespace around each binary operator, including: +, -, *, /, \>\>, \<\<, &&, ||, &, |, ?:, =, "as". 

-

Do not check dot ('.') operator. Do not check unary operators (!, +, -, ++, --, ?.). Do not check array ('[') operator.

-

Known limitations:

-]]>
- convention -
+ @Override + void someMethod(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7) { // no violation if ignoreOverriddenMethods == true + } - - - org.codenarc.rule.formatting.SpaceBeforeOpeningBraceRule - MAJOR - - - Check that there is at least one space (blank) or whitespace before each opening brace ("\{") for method/class/interface declarations, closure expressions and block statements.

-

Known limitations:

-]]>
- convention + class SampleClass { + SampleClass(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7) { // violation + } + } +
]]>
+ bug - checkClosureMapEntryValue - + ignoreOverriddenMethods + true + + maxParameters + + 5 + - + + - org.codenarc.rule.formatting.SpaceAfterOpeningBraceRule + org.codenarc.rule.unnecessary.UnnecessaryBooleanExpressionRule MAJOR - - - Check that there is at least one space (blank) or whitespace after each opening brace ("\{") for method/class/interface declarations, closure expressions and block statements.

- Examples of violations:

+ + + Checks for unnecessary boolean expressions, including ANDing (&&) or ORing (||) with true, false, null, or a Map/List/String/Number literal.

+

This rule also checks for negation (!) of true, false, null, or a Map/List/String/Number literal.

+

Examples of violations include:

-    class MyClass{int count }                   // violation
-
-    interface MyInterface {static final OK = 1 }// violation
-
-    enum MyEnum {OK, BAD }                      // violation
+    result = value && true              // AND or OR with boolean constants
+    if (false || value) { .. }
+    return value && Boolean.FALSE
 
-    def myMethod() {int count }                 // violation
+    result = null && value              // AND or OR with null
 
-    if (ready) {println 9 }                     // violation
+    result = value && "abc"             // AND or OR with String literal
 
-    if (ready) {
-    } else {println 99}                         // violation
+    result = value && 123               // AND or OR with Number literal
+    result = 678.123 || true
 
-    for (int i=0; i<10; i++) {println i }       // violation
+    result = value && [x, y]            // AND or OR with List literal
 
-    for (String name in names) {println name }  // violation
+    result = [a:123] && value           // AND or OR with Map literal
 
-    for (String name: names) {println name }    // violation
+    result = !true                      // Negation of boolean constants
+    result = !false
+    result = !Boolean.TRUE
 
-    while (ready) {println time }               // violation
+    result = !null                      // Negation of null
 
-    try {doStuff()                              // violation
-    } catch(Exception e) {x=77 }                // violation
-    } finally {println 'error' }                // violation
+    result = !"abc"                     // Negation of String literal
 
-    list.each {name -> }                        // violation
+    result = ![a:123]                   // Negation of Map literal
 
-    shouldFail(Exception) {doStuff() }          // violation
-
-]]>
- convention - - checkClosureMapEntryValue - - true - - - ignoreEmptyBlock - - false - + result = ![a,b] // Negation of List literal +]]>
+ clumsy
- - org.codenarc.rule.formatting.SpaceAfterClosingBraceRule + org.codenarc.rule.unnecessary.UnnecessaryIfStatementRule MAJOR - - - Check that there is at least one space (blank) or whitespace after each closing brace ("\{") for method/class/interface declarations, closure expressions and block statements.

-

A closure expression followed by a dot operator (.), a comma, a closing parenthesis, the spread-dot operator (*.), a semicolon or the null-safe operator (?.) does not cause a violation.

- Known limitations:

-]]>
- convention - - checkClosureMapEntryValue - - true - -
+ + + Checks for unnecessary if statements. The entire if statement, or at least the if or else block, are considered unnecessary for the four scenarios described below.

+

(1) When the if and else blocks contain only an explicit return of true and false constants. These cases can be replaced by a simple return statement. Examples of violations include:

+
+    if (someExpression)         // can be replaced by: return someExpression
+        return true
+    else
+        return false
 
-  
-  
-    org.codenarc.rule.formatting.SpaceBeforeClosingBraceRule
-    MAJOR
-    
-    
-    Check that there is at least one space (blank) or whitespace before each closing brace ("\}") for method/class/interface declarations, closure expressions and block statements. 

-

Known limitations:

-]]>
- convention - - checkClosureMapEntryValue - - true - - - ignoreEmptyBlock - - false - -
+ if (someExpression) { // can be replaced by: return !someExpression + return false + } else { + return true + } - - - org.codenarc.rule.formatting.SpaceAfterIfRule - MAJOR - - - Check that there is exactly one space (blank) after the if keyword and before the opening parenthesis.

-

Examples of violations:

+ if (someExpression) { // can be replaced by: return someExpression + return Boolean.TRUE + } else { + return Boolean.FALSE + } +
+

(2) When the if statement is the last statement in a block and the if and else blocks are only true and false expressions. This is an implicit return of true/false. For example, the if statement in the following code can be replaced by someExpression or someExpression as boolean:

-    if(true) { }                            // violation
-    if  (true) { }                          // violation
+    def myMethod() {
+        doSomething()
+        if (someExpression)
+            true
+        else false
+    }
 
-]]>
- convention - - - - - org.codenarc.rule.formatting.SpaceAfterWhileRule - MAJOR - - - Check that there is exactly one space (blank) after the while keyword and before the opening parenthesis.

-

Examples of violations:

+

(3) When the second-to-last statement in a block is an if statement with no else, where the block contains a single return statement, and the last statement in the block is a return statement, and one return statement returns a true expression and the other returns a false expression. This check is disabled by setting checkLastStatementImplicitElse to false. For example, the if statement in the following code can be replaced by return expression1:

-    while(true) { }             // violation
-    while  (true) { }           // violation
+    def myMethod() {
+        doSomething()
+        if (expression1) {
+            return true
+        }
+        return false
+    }
 
-]]>
- convention +

(4) When either the if block or else block of an if statement that is not the last statement in a block contain only a single constant or literal expression. For example, the if statement in the following code has no effect and can be removed:

+
+    def myMethod() {
+        if (someExpression) { 123 }
+        doSomething()
+    }
+
]]> + clumsy
- - org.codenarc.rule.formatting.SpaceAfterForRule + org.codenarc.rule.unnecessary.UnnecessaryTernaryExpressionRule MAJOR - - - Check that there is exactly one space (blank) after the for keyword and before the opening parenthesis.

-

Examples of violations:

+ + + Checks for ternary expressions where the conditional expression always evaluates to a boolean and the true and false expressions are merely returning true and false constants. These cases can be replaced by a simple boolean expression. Examples of violations include:

-    for(name in names) { }                  // violation
-    for  (int i=0; i < 10; i++) { }         // violation
+    x==99 ? true : false                    // can be replaced by: x==99
+    x && y ? true : false                   // can be replaced by: x && y
+    x||y ? false : true                     // can be replaced by: !(x||y)
+    x >= 1 ? true: false                    // can be replaced by: x >= 1
+    x < 99 ? Boolean.TRUE : Boolean.FALSE   // can be replaced by: x < 99
+    !x ? true : false                       // can be replaced by: !x
 
-]]>
- convention +

The rule also checks for ternary expressions where the true and false expressions are the same constant or variable. Examples include:

+
+    x ? '123' : '123'              // can be replaced by: '123'
+    x ? null : null                // can be replaced by: null
+    x ? 23 : 23                    // can be replaced by: 23
+    x ? MAX_VALUE : MAX_VALUE      // can be replaced by: MAX_VALUE
+    ready ? minValue : minValue    // can be replaced by: minValue
+
]]>
+ clumsy
- + - org.codenarc.rule.formatting.SpaceAfterSwitchRule + org.codenarc.rule.unnecessary.UnnecessaryBigDecimalInstantiationRule MAJOR - - - Check that there is exactly one space (blank) after the switch keyword and before the opening parenthesis.

-

Examples of violations:

-
-    switch(x) {                                 // violation
-        case 1: println 'one'
-    }
-    switch  (x) {                               // violation
-        case 1: println 'one'
-    }
-
-]]>
- convention + + + It is unnecessary to instantiate BigDecimal objects. Instead just use the decimal literal or the 'G' identifier to force the type, such as 123.45 or 123.45G.

+

This rule does not produce violations when the parameter evaluates to an integer/long, e.g. new BigDecimal(42), new BigDecimal(42L) or new BigDecimal("42"), because using the "G" suffix on an integer value produces a BigInteger, rather than a BigDecimal, e.g. 45G. So that means there is no way to produce a BigDecimal with exactly that value using a literal.

+

This rule also does not produce violations when the parameter is a double, e.g. new BigDecimal(12.3). That scenario is covered by the BigDecimalInstantiation rule, because that produces an unpredictable (double) value (and so it is unsafe, rather than unnecessary).

]]>
+ clumsy
- + - org.codenarc.rule.formatting.SpaceAfterCatchRule + org.codenarc.rule.unnecessary.UnnecessaryBigIntegerInstantiationRule MAJOR - - - Check that there is exactly one space (blank) after the catch keyword and before the opening parenthesis.

-

Examples of violations:

-
-    try { } catch(Exception e) { }          // violation
-    try { } catch  (Exception e) { }        // violation
-
-]]>
- convention + + + It is unnecessary to instantiate BigInteger objects. Instead just use the literal with the 'G' identifier to force the type, such as 8G or 42G.

]]>
+ clumsy
- + - org.codenarc.rule.formatting.SpaceAroundClosureArrowRule + org.codenarc.rule.unnecessary.UnnecessaryBooleanInstantiationRule MAJOR - - - Checks that there is at least one space (blank) or whitespace around each closure arrow (->) symbol.

-

Known limitations:

-]]>
- convention + + + Checks for direct call to a Boolean constructor. Use Boolean.valueOf() or the Boolean.TRUE and Boolean.FALSE constants instead of calling the Boolean() constructor directly.

+

Also checks for Boolean.valueOf(true) or Boolean.valueOf(false). Use the Boolean.TRUE or Boolean.FALSE constants instead.

+

Here is an example of code that produces a violation:

+
+    def b1 = new Boolean(true)             // violation
+    def b2 = new java.lang.Boolean(false)  // violation
+    def b3 = Boolean.valueOf(true)         // violation
+    def b4 = Boolean.valueOf(false)        // violation
+
]]>
+ clumsy
- + - org.codenarc.rule.formatting.SpaceAroundMapEntryColonRule + org.codenarc.rule.unnecessary.UnnecessaryCallForLastElementRule MAJOR - - - Check for proper formatting of whitespace around colons for literal Map entries. By default, no whitespace is allowed either before or after the Map entry colon, but you can change that through the configuration properties below.

-

Example of violations:

+ + + This rule checks for excessively verbose methods of accessing the last element of an array or list. For instance, it is possible to access the last element of an array by performing array[array.length - 1], in Groovy it is simpler to either call array.last() or array[-1]. The same is true for lists. This violation is triggered whenever a get, getAt, or array-style access is used with an object size check.

+

Code like this all cause violations.

-    Map m1 = [myKey : 12345]            // violation (both before and after the colon)
-    println [a :[1:11, 2:22],           // violation on a (before colon)
-                b:[(Integer): 33]]      // violation on Integer (after colon)
+    def x = [0, 1, 2]
+    def a = x.get(x.size() -1)
+    def b = x.get(x.length -1)
+    def c = x.getAt(x.size() -1)
+    def d = x.getAt(x.length -1)
+    def f = x[(x.size() -1]
+    def d = x[(x.length -1]
 
-]]>
- convention - - characterAfterColonRegex - entry. For example, /\\S/ matches any non-whitespace character and /\\s/ matches any whitespace character (thus requiring a space or whitespace). ]]> - \\S - - - characterBeforeColonRegex - entry. For example, /\\S/ matches any non-whitespace character and /\\s/ matches any whitespace character (thus requiring a space or whitespace). ]]> - \\S - +

All of this code is fine though:

+
+    def x = [0, 1, 2]
+    def a = x.last()
+    def b = x[-1]
+    def c = x.getAt(-1)
+    def d = x.get(z.size() -1)     // different objects
+    def e = x.get(z.length -1)     // different objects
+    def f = x.getAt(z.size() -1)   // different objects
+
]]>
+ clumsy
- + - org.codenarc.rule.formatting.ClosureStatementOnOpeningLineOfMultipleLineClosureRule + org.codenarc.rule.unnecessary.UnnecessaryCatchBlockRule MAJOR - - - Checks for closure logic on first line (after -\) for a multi-line closure. That breaks the symmetry of indentation (if the subsequent statements are indented normally), and that first statement can be easily missed when reading the code.

-

Example of violations:

-
-    def closure = { name -> println name
-        addToCounts()
-        println “done” }
-
-]]>
- convention + + + Violations are triggered when a catch block does nothing but throw the original exception. In this scenario there is usually no need for a catch block, just let the exception be thrown from the original code. This condition frequently occurs when catching an exception for debugging purposes but then forgetting to take the catch statement out.

]]>
+ clumsy
- + - org.codenarc.rule.formatting.ConsecutiveBlankLinesRule + org.codenarc.rule.unnecessary.UnnecessaryCollectCallRule MAJOR - - - Makes sure there are no consecutive lines that are either blank or whitespace only. This reduces the need to scroll further than necessary when reading code, and increases the likelihood that a logical block of code will fit on one screen for easier comprehension.

-

Example of violation:

+ + + Some method calls to Object.collect(Closure) can be replaced with the spread operator. For instance, list.collect { it.multiply(2) } can be replaced by list*.multiply(2).

+

Examples of violations include:

-    def name
+    assert [1, 2, 3].collect { it.multiply(2) }
+    assert [1, 2, 3].collect { x -> x.multiply(2) }
+    ["1", "2", "3"].collect { it.bytes }
+
+

The following code does not produce violations:

+
+    [1, 2, 3].collect { it * it }   // OK, closure parameter is referenced twice
 
+    [1, 2, 3].mapMethod { it.multiply(5) } // OK, method call is not collect
 
-    def value
+    [1, 2, 3].collect(5) // OK, collect parameter is not a closure
 
+    // OK, the closure is not a simple one line statement
+    [1, 2, 3].collect { println it; it.multiply(5) }
 
+    // OK, closure has too many arguments
+    [1, 2, 3].collect { a, b -> a.multiply(b) }
 
-    def id
-
-]]>
- convention -
+ // OK, closure statement references parameter multiple times + [1, 2, 3].collect { it.multiply(it) } - - - org.codenarc.rule.formatting.BlankLineBeforePackageRule - MAJOR - - - Makes sure there are no blank lines before the package declaration of a source code file.

- ]]>
- convention + // OK, it is referenced several times in the closure + [1, 2, 3].collect { it.multiply(2).multiply(it) } + ["1", "2", "3"].collect { it.bytes.foo(it) } + + // OK, chained methods are too complex to analyze at this point + [1, 2, 3].collect { it.multiply(2).multiply(4) } + + // in general the above examples can be rewritten like this: + [1, 2, 3]*.multiply(2) + ["1", "2", "3"]*.bytes +]]> + clumsy
- + - org.codenarc.rule.formatting.FileEndsWithoutNewlineRule + org.codenarc.rule.unnecessary.UnnecessaryCollectionCallRule MAJOR - - - Makes sure each source file ends with a newline character.

- ]]>
- convention + + + Checks for useless calls to collections. For any collection c, calling c.containsAll(c) should always be true, and c.retainAll(c) should have no effect.

]]>
+ clumsy
- + - org.codenarc.rule.formatting.MissingBlankLineAfterImportsRule + org.codenarc.rule.unnecessary.UnnecessaryConstructorRule MAJOR - - - Makes sure there is a blank line after the imports of a source code file.

-

Example of violation:

+ + + This rule detects when a constructor is not necessary; i.e., when there's only one constructor, it's public, has an empty body, and takes no arguments, or else contains only a single call to super().

+

Example of violations:

-    import org.apache.commons.lang.StringUtils
-    class MyClass { }                       // violation
-
-]]>
- convention + class MyClass { + public MyClass() { // violation; constructor is not necessary + } + } + + class MyClass2 extends OtherClass { + MyClass2() { // violation; constructor is not necessary + super() + } + } +]]>
+ clumsy + + ignoreAnnotations + + false +
- + - org.codenarc.rule.formatting.MissingBlankLineAfterPackageRule + org.codenarc.rule.unnecessary.UnnecessaryDoubleInstantiationRule MAJOR - - - Makes sure there is a blank line after the package statement of a source code file.

-

Example of violation:

-
-  package org.codenarc
-  import java.util.Date                     // violation
-
-  class MyClass {
-      void go() { /* ... */ }
-  }
-
-]]>
- convention + + + It is unnecessary to instantiate Double objects. Instead just use the double literal with 'D' identifier to force the type, such as 123.45d or 0.42d.

]]>
+ clumsy
- + - org.codenarc.rule.formatting.TrailingWhitespaceRule + org.codenarc.rule.unnecessary.UnnecessaryFloatInstantiationRule MAJOR - - - Checks that no lines of source code end with whitespace characters.

-]]>
- convention + + + It is unnecessary to instantiate Float objects. Instead just use the float literal with the 'F' identifier to force the type, such as 123.45F or 0.42f.

]]>
+ clumsy
- - - + - org.codenarc.rule.convention.InvertedIfElseRule + org.codenarc.rule.unnecessary.UnnecessaryGetterRule MAJOR - - - An inverted statement is one in which there is a single if statement with a single else branch and the boolean test of the if is negated. For instance if (!x) false else true. It is usually clearer to write this as if (x) true else false.

- ]]>
- bug + + + Checks for explicit calls to getter/accessor methods which can, for the most part, be replaced by property access. A getter is defined as a no-argument method call that matches get[A-Z] but not getClass() or get[A-Z][A-Z] such as getURL().

+

These bits of code produce violations:

+
+    x.getProperty()
+    x.getFirst()
+    x.getFirstName()
+    x.getA()
+
+    x.isFirst()         // Violation if checkIsMethods is true
+    x.isA()             // Violation if checkIsMethods is true
+
+

These bits of code do not:

+
+    x.property
+    x.first
+    x.firstName
+    x.a
+    x.getURL()
+    x.getClass()
+    x.getProperty('key')
+
]]>
+ clumsy + + checkIsMethods + + true + + + ignoreMethodNames + +
- org.codenarc.rule.convention.ConfusingTernaryRule + org.codenarc.rule.unnecessary.UnnecessaryGStringRule MAJOR - - - In a ternary expression avoid negation in the test. For example, rephrase: (x != y) ? diff : same as: (x == y) ? same : diff. Consistent use of this rule makes the code easier to read. Also, this resolves trivial ordering problems, such as "does the error case go first?" or "does the common case go first?".

-

Example:

+ + + String objects should be created with single quotes, and GString objects created with double quotes. Creating normal String objects with double quotes is confusing to readers.

+

Example of violations:

-    (x != y) ? diff : same      // triggers violation
-    (!x) ? diff : same          // triggers violation
+    def a = "I am a string"     // violation
+
+    // violation
+    def b = """
+        I am a string
+    """
 
-    (x == y) ? same : diff      // OK
-    (x) ? same : diff           // OK
+    def c = "I am a ' string"       // OK
 
-    // this is OK, because of GroovyTruth there is no inverse of != null
-    (x != null) ? diff : same
+    def d = """I am a ' string"""   // OK
 
-    // this is OK, because of GroovyTruth there is no inverse of != true
-    (x != true) ? diff : same
+    def e = """I am a ' string"""   // OK
 
-    // this is OK, because of GroovyTruth there is no inverse of != false
-    (x != false) ? diff : same
-
-]]>
- bug + def f = "I am a \$ string" // OK + + // OK + def g = """ + I am a \$ string + """ + + // OK + def h = """ + I am a $string + """ + + def i = 'i am a string' + def j = '''i am a + string + ''' +]]>
+ clumsy
- + - org.codenarc.rule.convention.CouldBeElvisRule + org.codenarc.rule.unnecessary.UnnecessaryInstantiationToGetClassRule MAJOR - - - Catch an if block that could be written as an elvis expression.

-

Example of violations:

+ + + Avoid instantiating an object just to call getClass() on it; use the .class public member instead.

-    if (!x) {                   // violation
-        x = 'some value'
-    }
-
-    if (!x)                     // violation
-        x = "some value"
+    public class Foo {
+     // Replace this
+     Class c = new String().getClass();
 
-    if (!params.max) {          // violation
-      params.max = 10
+     // with this:
+     Class c = String.class;
     }
+
]]>
+ clumsy +
- x ?: 'some value' // OK - -]]> - bug + + + org.codenarc.rule.unnecessary.UnnecessaryIntegerInstantiationRule + MAJOR + + + It is unnecessary to instantiate Integer objects. Instead just use the literal with the 'I' identifier to force the type, such as 8I or 42i.

]]>
+ clumsy
- + - org.codenarc.rule.convention.LongLiteralWithLowerCaseLRule - MINOR - - - In Java and Groovy, you can specify long literals with the L or l character, for instance 55L or 24l. It is best practice to always use an uppercase L and never a lowercase l. This is because 11l rendered in some fonts may look like 111 instead of 11L.

-

Example of violations:

-
-    def x = 1l
-    def y = 55l
-
-]]>
- bug + org.codenarc.rule.unnecessary.UnnecessaryLongInstantiationRule + MAJOR + + + It is unnecessary to instantiate Long objects. Instead just use the literal with the 'L' identifier to force the type, such as 8L or 42L.

]]>
+ clumsy
- + - org.codenarc.rule.convention.ParameterReassignmentRule + org.codenarc.rule.unnecessary.UnnecessaryObjectReferencesRule MAJOR - - - Checks for a method or closure parameter being reassigned to a new value within the body of the method/closure, which is a confusing and questionable practice. Use a temporary variable instead.

-

Example of violations:

+ + + Violations are triggered when an excessive set of consecutive statements all reference the same variable. This can be made more readable by using a with or identity block. By default, 5 references are allowed. You can override this property using the maxReferencesAllowed property on the rule.

+

These two bits of code produce violations:

-    void myMethod(int a, String b) {
-        println a
-        b = 'new value'     // violation
+    def p1 = new Person()
+    p1.firstName = 'Hamlet'
+    p1.lastName = "D'Arcy"
+    p1.employer = 'Canoo'
+    p1.street = 'Kirschgaraten 5'
+    p1.city = 'Basel'
+    p1.zipCode = '4051'
+
+    def p2 = new Person()
+    p2.setFirstName('Hamlet')
+    p2.setLastName("D'Arcy")
+    p2.setEmployer('Canoo')
+    p2.setStreet('Kirschgaraten 5')
+    p2.setCity('Basel')
+    p2.setZipCode('4051')
+
+

However, these two bits of code do not because they use either a with or identity block.

+
+    def p1 = new Person().with {
+        firstName = 'Hamlet'
+        lastName = "D'Arcy"
+        employer = 'Canoo'
+        street = 'Kirschgaraten 5'
+        city = 'Basel'
+        zipCode = '4051'
     }
 
-    def myClosure1 = { int a, b ->
-        a = 123             // violation
+    def p2 = new Person().identity {
+        firstName = 'Hamlet'
+        lastName = "D'Arcy"
+        employer = 'Canoo'
+        street = 'Kirschgaraten 5'
+        city = 'Basel'
+        zipCode = '4051'
     }
-
-]]>
- bug +]]>
+ clumsy
- + - org.codenarc.rule.convention.TernaryCouldBeElvisRule + org.codenarc.rule.unnecessary.UnnecessaryNullCheckRule MAJOR - - - Checks for ternary expressions where the and expressions are the same. These can be simplified to an expression.

-

Example of violations:

+ + + Groovy contains the safe dereference operator. It can be used in boolean conditional statements to safely replace explicit x == null tests. Also, testing the 'this' or 'super' reference for null equality is pointless and can be removed.

+

Examples of violations:

-    x ? x : false               // violation; can simplify to x ?: false
+    if (obj != null && obj.method()) { }
 
-    foo() ? foo() : bar()       // violation; can simplify to foo() ?: bar()
-    foo(1) ? foo(1) : 123       // violation; can simplify to foo(1) ?: 123
+    if (obj != null && obj.prop) { }
 
-    (x == y) ? same : diff      // OK
-    x ? y : z                   // OK
-    x ? x + 1 : x + 2           // OK
-    x ? 1 : 0                   // OK
-    x ? !x : x                  // OK
-    !x ? x : null               // OK
+    // this is pointless and won't avoid NullPointerException
+    if (obj.method() && obj != null ) { }
 
-    foo() ? bar() : 123         // OK
-    foo() ? foo(99) : 123       // OK
-    foo(x) ? foo() : 123        // OK
-    foo(1) ? foo(2) : 123       // OK
-
-]]>
- bug -
+ if (this == null) { } + if (null == this) { } + if (this != null) { } + if (null != this) { } - - - org.codenarc.rule.convention.VectorIsObsoleteRule - MINOR - - - Checks for references to the () obsolete java.util.Vector class. Use the Java Collections Framework classes instead, including ArrayList or Collections.synchronizedList(). See the JDK javadoc.

-

Example of violations:

-
-    def myList = new Vector()           // violation
+    if (super == null) { }
+    if (null == super) { }
+    if (super != null) { }
+    if (null != super) { }
 
-]]>
- bug -
- - - - org.codenarc.rule.convention.HashtableIsObsoleteRule - MINOR - - - Checks for references to the () obsolete java.util.Hashtable class. Use the Java Collections Framework classes instead, including HashMap or ConcurrentHashMap. See the JDK javadoc.

-

Example of violations:

+

Examples of acceptable code:

-    def myMap = new Hashtable()           // violation
-
-]]>
- bug -
+ // null check it OK + if (obj != null) { } - - - org.codenarc.rule.convention.IfStatementCouldBeTernaryRule - MINOR - - - Checks for:

-]]>
- bug + // null safe dereference in if is OK + if (obj?.method()) { } + + // null safe dereference in ternary is OK + (obj?.prop && obj?.prop2) ? x : y + + // obj is reused in a parameter list, so OK + if (obj != null && obj.method() && isValid(obj)) { } + + // rule is not so complex yet... + (obj != null && obj.prop && obj.method()) ? x : y +]]> + clumsy
- + - org.codenarc.rule.convention.NoDefRule + org.codenarc.rule.unnecessary.UnnecessaryNullCheckBeforeInstanceOfRule MAJOR - - - Do not allow using the def keyword in code. Use a specific type instead.

-

NOTE: This rule applies to the text contents of a rather than a specific , so it does not support the and configuration properties.

-]]>
- bug - - excludeRegex - - + + + There is no need to check for null before an instanceof; the instanceof keyword returns false when given a null argument.

+

Example:

+
+    if (x != null && x instanceof MyClass) {
+        // should drop the "x != null" check
+    }
+
+    if (x instanceof MyClass && x != null) {
+        // should drop the "x != null" check
+    }
+
+    // should drop the "x != null" check
+    (x != null && x instanceof MyClass) ? foo : bar
+
+    if (x != null && x instanceof MyClass && x.isValid()) {
+        // this is OK and causes no violation because the x.isValid() requires a non null reference
+    }
+
]]>
+ clumsy
- + - org.codenarc.rule.convention.TrailingCommaRule + org.codenarc.rule.unnecessary.UnnecessaryOverridingMethodRule MAJOR - - - Check whether list and map literals contain optional trailing comma. Rationale: Putting this comma in make is easier to change the order of the elements or add new elements on the end.

-

This is valid code:

-
-  int[] array1 = [] // one line declaration
-  int[] array2 = [ // empty list
-                 ]
-  int[] array3 = [1,2,3] // one line declaration
-  int[] array4 = [1,
-                  2,
-                  3, // contains trailing comma
-                 ]
-
-
-  int[] array2 = [1,
-                  2 // there is no trailing comma
-                 ]
-
-]]>
- bug - - checkList - - true - - - checkMap - - true - + + + Checks for an overriding method that merely calls the same method defined in a superclass. Remove it.

]]>
+ clumsy
- + - org.codenarc.rule.convention.NoTabCharacterRule + org.codenarc.rule.unnecessary.UnnecessaryReturnKeywordRule MAJOR - - - Checks that all source files do not contain the tab character.

-]]>
- bug + + + In Groovy, the return keyword is often optional. If a statement is the last line in a method or closure then you do not need to have the return keyword.

]]>
+ clumsy
- - - + - org.codenarc.rule.groovyism.ExplicitArrayListInstantiationRule - MINOR - - - This rule checks for explicit calls to the no-argument constructor of ArrayList. In Groovy, it is best to write new ArrayList() as [], which creates the same object.

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessaryStringInstantiationRule + MAJOR + + + Checks for direct call to the String constructor that accepts a String literal. In almost all cases, this is unnecessary. Use a String literal (e.g., "...") instead of calling the corresponding String constructor (new String("..")) directly.

+

Here is an example of code that produces a violation:

+
+    def s = new String('abc')
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitCallToAndMethodRule + org.codenarc.rule.unnecessary.AddEmptyStringRule MINOR - - - This rule detects when the and(Object) method is called directly in code instead of using the & operator. A groovier way to express this: a.and(b) is this: a & b. This rule can be configured to ignore this.and(Object) using the property. It defaults to , so even and(x) will not trigger a violation. The default is because and appears commonly in Grails criteria.

-

This rule also ignores all calls to super.and(Object).

- ]]>
- groovyism + + + Finds empty string literals which are being added. This is an inefficient way to convert any type to a String.

+

Examples:

+
+    // do not add empty strings to things
+    def a = '' + 123
+    def b = method('' + property)
+
+    // these examples are OK and do not trigger violations
+    def c = 456.toString()
+    def d = property?.toString() ?: ""
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitCallToCompareToMethodRule + org.codenarc.rule.unnecessary.ConsecutiveLiteralAppendsRule MINOR - - - This rule detects when the compareTo(Object) method is called directly in code instead of using the \<\=\>, \>, \>\=, \<, and \<\= operators. A groovier way to express this: a.compareTo(b) is this: a \<\=\> b, or using the other operators. Here are some other ways to write groovier code:

+ + + Violations occur when method calls to append(Object) are chained together with literals as parameters. The chained calls can be joined into one invocation.

+

Example of violations:

-    a.compareTo(b) == 0               // can be replaced by: a == b
-    a.compareTo(b)                    // can be replaced by: a <=> b
-    a.compareTo(b) > 0                // can be replaced by: a > b
-    a.compareTo(b) >= 0               // can be replaced by: a >= b
-    a.compareTo(b) < 0                // can be replaced by: a < b
-    a.compareTo(b) <= 0               // can be replaced by: a <= b
+    writer.append('foo').append('bar')      // strings can be joined
+    writer.append('foo').append(5)          // string and number can be joined
+    writer.append('Hello').append("$World") // GString can be joined
 
-]]>
- groovyism -
+

Example of passing code:

+
+    // usage not chained invocation
+    writer.append('Hello')
+    writer.append('World')
 
-  
-  
-    org.codenarc.rule.groovyism.ExplicitCallToDivMethodRule
-    MINOR
-    
-    
-    This rule detects when the div(Object) method is called directly in code instead of using the / operator. A groovier way to express this: a.div(b) is this: a / b. This rule can be configured to ignore div.xor(Object) using the  property. It defaults to , so even div(x) will trigger a violation. 

-

This rule also ignores all calls to super.div(Object).

- ]]>
- groovyism -
+ writer.append(null).append(5) // nulls cannot be joined - - - org.codenarc.rule.groovyism.ExplicitCallToEqualsMethodRule - MINOR - - - This rule detects when the equals(Object) method is called directly in code instead of using the == or != operator. A groovier way to express this: a.equals(b) is this: a == b and a groovier way to express : !a.equals(b) is: a != b. This rule can be configured to ignore this.equals(Object) using the property. It defaults to , so even equals(x) will trigger a violation.

-

This rule also ignores all calls to super.equals(Object).

- ]]>
- groovyism + writer.append().append('Hello') // no arg append is unknown + writer.append('a', 'b').append('Hello') // two arg append is unknown +
]]> + clumsy - + - org.codenarc.rule.groovyism.ExplicitCallToGetAtMethodRule - MINOR - - - This rule detects when the getAt(Object) method is called directly in code instead of using the [] index operator. A groovier way to express this: a.getAt(b) is this: a[b]. This rule can be configured to ignore this.getAt(Object) using the property. It defaults to , so even getAt(x) will trigger a violation.

-

This rule also ignores all calls to super.getAt(Object).

- ]]>
- groovyism -
+ org.codenarc.rule.unnecessary.ConsecutiveStringConcatenationRule + MAJOR + + + Catches concatenation of two string literals on the same line. These can safely by joined. In Java, the Java compiler will join two String literals together and place them in the Constant Pool. However, Groovy will not because the plus() method may override the + operator.

+

Examples:

+
+    // Violations
+    def a = 'Hello' + 'World'   // should be 'HelloWorld'
+    def b = "$Hello" + 'World'  // should be "${Hello}World"
+    def c = 'Hello' + "$World"  // should be "Hello${World}"
+    def d = 'Hello' + 5         // should be 'Hello5'
+    def e = 'Hello' + '''
+                        world   // should be joined
+                      '''
+    def f = '''Hello
+                  ''' + 'world'   // should be joined
 
-  
-  
-    org.codenarc.rule.groovyism.ExplicitCallToLeftShiftMethodRule
-    MINOR
-    
-    
-    This rule detects when the leftShift(Object) method is called directly in code instead of using the \<\< operator. A groovier way to express this: a.leftShift(b) is this: a \<\< b. This rule can be configured to ignore this.leftShift(Object) using the  property. It defaults to , so even leftShift(x) will trigger a violation. 

-

This rule also ignores all calls to super.leftShift(Object).

- ]]>
- groovyism -
- - - org.codenarc.rule.groovyism.ExplicitCallToMinusMethodRule - MINOR - - - This rule detects when the minus(Object) method is called directly in code instead of using the - operator. A groovier way to express this: a.minus(b) is this: a - b. This rule can be configured to ignore minus.xor(Object) using the property. It defaults to , so even minus(x) will trigger a violation.

-

This rule also ignores all calls to super.minus(Object).

- ]]>
- groovyism + // Not Violations + def g = 'Hello' + // OK because of line break + 'World' + def h = 'Hello' + null // OK because not a string + def i = 'Hello' + method() // OK because not a string + def j = 'Hello' - "$World" // OK because not + +
]]>
+ clumsy - + - org.codenarc.rule.groovyism.ExplicitCallToMultiplyMethodRule - MINOR - - - This rule detects when the multiply(Object) method is called directly in code instead of using the * operator. A groovier way to express this: a.multiply(b) is this: a * b. This rule can be configured to ignore this.multiply(Object) using the property. It defaults to , so even multiply(x) will trigger a violation.

-

This rule also ignores all calls to super.multiply(Object).

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessaryCallToSubstringRule + MAJOR + + + Calling String.substring(0) always returns the original string. This code is meaningless.

+

Examples:

+
+    string.substring(0)         // violation
+    method().substring(0)       // violation
+
+    prop.substring(1)           // OK, not constant 0
+    prop.substring(0, 1)        // OK, end is specified
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitCallToModMethodRule - MINOR - - - This rule detects when the mod(Object) method is called directly in code instead of using the % operator. A groovier way to express this: a.mod(b) is this: a % b. This rule can be configured to ignore this.mod(Object) using the property. It defaults to , so even mod(x) will trigger a violation.

-

This rule also ignores all calls to super.mod(Object).

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessaryDefInMethodDeclarationRule + MAJOR + + + If a method has a visibility modifier or a type declaration, then the def keyword is unneeded. For instance 'def private method() {}' is redundant and can be simplified to 'private method() {}'.

+

Examples of violations:

+
+    // def and private is redundant
+    def private method1() { return 4 }
+
+    // def and protected is redundant
+    def protected method2() { return 4 }
+
+    // def and public is redundant
+    def public method3() { return 4 }
+
+    // def and static is redundant
+    def static method4() { return 4 }
+
+    // def and type is redundant
+    def Object method5() { return 4 }
+
+    class MyClass {
+        def MyClass() {}    // def is redundant
+    }
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitCallToOrMethodRule - MINOR - - - This rule detects when the or(Object) method is called directly in code instead of using the | operator. A groovier way to express this: a.or(b) is this: a | b. This rule can be configured to ignore this.or(Object) using the property. It defaults to , so even or(x) will not trigger a violation. This is the default because it is commonly used in Grails criteria.

-

This rule also ignores all calls to super.or(Object).

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessaryModOneRule + MAJOR + + + Any expression mod 1 (exp % 1) is guaranteed to always return zero. This code is probably an error, and should be either (exp & 1) or (exp % 2).

+

Examples:

+
+    if (exp % 1) {}         // violation
+    if (method() % 1) {}    // violation
+
+    if (exp & 1) {}     // ok
+    if (exp % 2) {}     // ok
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitCallToPlusMethodRule - MINOR - - - This rule detects when the plus(Object) method is called directly in code instead of using the + operator. A groovier way to express this: a.plus(b) is this: a + b. This rule can be configured to ignore this.plus(Object) using the property. It defaults to , so even plus(x) will trigger a violation.

-

This rule also ignores all calls to super.plus(Object).

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessaryPublicModifierRule + MAJOR + + + The 'public' modifier is not required on methods, constructors or classes.

+

Because of Groovy parsing limitations, this rule ignores methods (and constructors) that include Generic types in the method declaration.

+

Example of violations:

+
+    // violation on class
+    public class MyClass {
+        // violation on constructor
+        public MyClass() {}
+
+        // violation on method
+        public void myMethod() {}
+    }
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitCallToPowerMethodRule - MINOR - - - This rule detects when the power(Object) method is called directly in code instead of using the ** operator. A groovier way to express this: a.power(b) is this: a ** b. This rule can be configured to ignore this.power(Object) using the property. It defaults to , so even power(x) will trigger a violation.

-

This rule also ignores all calls to super.power(Object).

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessarySelfAssignmentRule + MAJOR + + + Method contains a pointless self-assignment to a variable or property. Either the code is pointless or the equals()/get() method has been overridden to have a side effect, which is a terrible way to code getters and violates the contract of equals().

+

Examples:

+
+    x = x               // violation
+    def method(y) {
+        y = y           // violation
+    }
+    a.b.c = a.b.c       // violation
+
+    x = y               // acceptable
+    a.b = a.zz          // acceptable
+    a.b = a().b         // acceptable
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitCallToRightShiftMethodRule - MINOR - - - This rule detects when the rightShift(Object) method is called directly in code instead of using the \>\> operator. A groovier way to express this: a.rightShift(b) is this: a \>\> b. This rule can be configured to ignore this.rightShift(Object) using the property. It defaults to , so even rightShift(x) will trigger a violation.

-

This rule also ignores all calls to super.rightShift(Object).

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessarySemicolonRule + MAJOR + + + Semicolons as line terminators are not required in Groovy: remove them. Do not use a semicolon as a replacement for empty braces on for and while loops; this is a confusing practice.

+

The rule contains a String property called 'excludePattern'. Any source code line matching this pattern will not trigger a violation. The default value is '\\s?\\*.*|/\\*.*|.*//.*|.*\\*/.*' This is to filter out comments. Any source line that even looks like it is a comment is ignored.

+

\s?\*.* == whitespace plus star character plus anything /\*.* == any line that contains the /* sequence .*//.* == any line that contains the // sequence .*\*/.* == any line that contains the */ sequence

+

Example of violations:

+
+    package my.company.server;  // violation
+
+    import java.lang.String;    // violation
+
+    println(value) ;             // violation
+
+    for (def x : list);         // violation
+
+    // this code is OK
+    println(value); println (otherValue)
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitCallToXorMethodRule - MINOR - - - This rule detects when the xor(Object) method is called directly in code instead of using the ^ operator. A groovier way to express this: a.xor(b) is this: a ^ b. This rule can be configured to ignore this.xor(Object) using the property. It defaults to , so even xor(x) will trigger a violation.

-

This rule also ignores all calls to super.xor(Object).

- ]]>
- groovyism -
+ org.codenarc.rule.unnecessary.UnnecessaryTransientModifierRule + MAJOR + + + The field is marked as transient, but the class isn't Serializable, so marking it as transient has no effect. This may be leftover marking from a previous version of the code in which the class was transient, or it may indicate a misunderstanding of how serialization works.

+

Some Java frameworks change the semantics of the transient keyword. For instance, when using Terracotta the transient keyword may have slightly different semantics. You may need to turn this rule off depending on which Java frameworks are in use.

+

Examples:

+
+    class MyClass {
+        // class not serializable, violation occurs
+        transient String property
+    }
 
-  
-  
-    org.codenarc.rule.groovyism.ExplicitHashMapInstantiationRule
-    MINOR
-    
-    
-    This rule checks for explicit calls to the no-argument constructor of HashMap. In Groovy, it is best to replace new HashMap() with [:], which creates (mostly) the same object. [:] is technically a LinkedHashMap but it is very rare that someone absolutely needs an instance of HashMap and not a subclass. 

- ]]>
- groovyism + class MySerializableClass implements Serializable { + // OK, class is serializable + transient String property + } +
]]>
+ clumsy - + - org.codenarc.rule.groovyism.ExplicitHashSetInstantiationRule - MINOR - - - This rule checks for explicit calls to the no-argument constructor of HashSet. In Groovy, it is best to replace new HashSet() with [] as Set, which creates the same object.

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessaryFinalOnPrivateMethodRule + MAJOR + + + A private method is marked final. Private methods cannot be overridden, so marking it final is unnecessary.

+

Example of violations:

+
+    private final method() {}
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitLinkedListInstantiationRule - MINOR - - - This rule checks for explicit calls to the no-argument constructor of LinkedList. In Groovy, it is best to replace new LinkedList() with [] as Queue, which creates the same object.

- ]]>
- groovyism -
+ org.codenarc.rule.unnecessary.UnnecessaryElseStatementRule + MAJOR + + + When an if statement block ends with a return statement, then the else is unnecessary. The logic in the else branch can be run without being in a new scope.

+

Example of violations:

+
+    if(value){
+        println 'Executing if logic...'
+        return true
+    } else {
+        println 'Executing else logic...'
+    }
 
-  
-  
-    org.codenarc.rule.groovyism.ExplicitStackInstantiationRule
-    MINOR
-    
-    
-    This rule checks for explicit calls to the no-argument constructor of Stack. In Groovy, it is best to replace new Stack() with [] as Stack, which creates the same object. 

- ]]>
- groovyism + // can be replaced by: + + if(value){ + println 'Executing if logic...' + return true + } + println 'Executing else logic...' +
]]>
+ clumsy - + - org.codenarc.rule.groovyism.ExplicitTreeSetInstantiationRule - MINOR - - - This rule checks for explicit calls to the no-argument constructor of TreeSet. In Groovy, it is best to replace new TreeSet() with [] as SortedSet, which creates the same object.

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessaryParenthesesForMethodCallWithClosureRule + MAJOR + + + If a method is called and the only parameter to that method is an inline closure then the parentheses of the method call can be omitted.

+

Example of violations:

+
+    [1,2,3].each() { println it }
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.GStringAsMapKeyRule - MINOR - - - A GString should not be used as a map key since its is not guaranteed to be stable. Consider calling key.toString().

-

Here is an example of code that produces a violation:

+ org.codenarc.rule.unnecessary.UnnecessaryPackageReferenceRule + MAJOR + + + Checks for explicit package reference for classes that Groovy imports by default, such as java.lang.String, java.util.Map and groovy.lang.Closure, as well as classes that were explicitly imported.

+

You do not need to specify the package for any classes from java.lang, java.util, java.io, java.net, groovy.lang and groovy.util, as well as the classes java.math.BigDecimal and java.math.BigInteger.

+

Examples of violations include:

-    Map map = ["${someRef}" : 'invalid' ]       // violation
+    // Field types
+    class MyClass {
+        java.math.BigDecimal amount = 42.10                     // violation
+    }
+
+    // Within expressions
+    if (value.class == java.math.BigInteger) { }                // violation
+    println "isClosure=${v instanceof groovy.lang.Closure}"     // violation
+    def p = java.lang.Runtime.availableProcessors()             // violation
+
+    // Constructor calls
+    def url = new java.net.URL('http://abc@example.com')        // violation
+
+    // Variable types
+    void doSomething() {
+        java.math.BigInteger maxValue = 0                       // violation
+        java.net.URI uri                                        // violation
+    }
+
+    // Method return types
+    java.io.Reader getReader() { }                              // violation
+    groovy.util.AntBuilder getAntBuilder() { }                  // violation
+
+    // Method parameter types
+    void writeCount(java.io.Writer writer, int count) { }       // violation
+    void init(String name, groovy.lang.Binding binding) { }     // violation
+
+    // Closure parameter types
+    def writeCount = { java.io.Writer writer, int count -> }    // violation
+
+    // Extends and implements
+    class MyHashMap extends java.util.HashMap { }               // violation
+    class MyList implements java.util.List { }                  // violation
+
+    // Explicitly imported classes
+    import javax.servlet.http.Cookie
+    import javax.sql.DataSource
+
+    class MyClass {
+        void doStuff(javax.servlet.http.Cookie cookie) {        // violation
+            def dataSource = [:] as javax.sql.DataSource        // violation
+        }
+    }
 
-]]>
- groovyism +

Known limitations:

+

* Does not catch class declarations that explicitly extend java.lang.Object. For instance, class MyClass extends java.lang.Object { }. Just don't do that, okay?

+

* Does not catch class declarations that explicitly extend groovy.lang.Script. For instance, class MyScript extends groovy.lang.Script{ }. Don't do that, either!

+

* Does not catch unnecessary package references if they are the types of anonymous inner class definitions, for older versions of Groovy ( 1.7.10?). For instance, def runnable = new java.lang.Runnable() { ... }.

]]>
+ clumsy
- + - org.codenarc.rule.groovyism.GroovyLangImmutableRule - MINOR - - - The groovy.lang.Immutable annotation has been deprecated and replaced by groovy.transform.Immutable. Do not use the Immutable in groovy.lang.

-

Example of violations:

+ org.codenarc.rule.unnecessary.UnnecessaryDefInVariableDeclarationRule + MAJOR + + + If a variable has a visibility modifier or a type declaration, then the def keyword is unneeded. For instance 'def private n = 2' is redundant and can be simplified to 'private n = 2'.

+

Examples of violations:

-    @Immutable
-    class Person { }
-
-    @groovy.lang.Immutable
-    class Person { }
+    // def and private is redundant
+    def private string1 = 'example'
 
-    import groovy.lang.Immutable as Imtl
-    @Imtl
-    class Person { }
+    // def and protected is redundant
+    def protected string2 = 'example'
 
-    // the following code is OK
-    @groovy.transform.Immutable
-    class Person { }
+    // def and public is redundant
+    def public string3 = 'example'
 
-    import groovy.transform.Immutable
-    @Immutable
-    class Person { }
+    // def and static is redundant
+    def static string4 = 'example'
 
-    import groovy.transform.*
-    @Immutable
-    class Person { }
+    // def and final is redundant
+    def final string5 = 'example'
 
-    import groovy.transform.Immutable as Imtl
-    @Imtl
-    class Person { }
-
-]]>
- groovyism + // def and a type is redundant + def String string6 = 'example' +]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ExplicitLinkedHashMapInstantiationRule - MINOR - - - This rule checks for the explicit instantiation of a LinkedHashMap using the no-arg constructor. In Groovy, it is best to replace new LinkedHashMap() with [:], which creates the same object.

- ]]>
- groovyism + org.codenarc.rule.unnecessary.UnnecessaryDotClassRule + MAJOR + + + To make a reference to a class, it is unnecessary to specify the '.class' identifier. For instance String.class can be shortened to String.

+

Example of violations:

+
+    // The '.class' identifier is unnecessary, violation occurs
+    def x = String.class
+
+    // Ok, unnecessary '.class' identifier has been excluded
+    def x = String
+
]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ClosureAsLastMethodParameterRule + org.codenarc.rule.unnecessary.UnnecessaryInstanceOfCheckRule MAJOR - - - If a method is called and the last parameter is an inline closure then it can be declared outside of the method call parentheses.

-

Example of violations:

+ + + This rule finds instanceof checks that cannot possibly evaluate to true. For instance, checking that (!variable instanceof String) will never be true because the result of a not expression is always a boolean.

+

Example of violations:

-    // creates violation: poor Groovy style
-    [1,2,3].each({ println it })
+    if (!variable instanceof String) { ... }    // always false
+    def x = !variable instanceof String         // always false
 
-    // no violation
-    [1,2,3].each { println it }
-
-]]>
- groovyism + if (!variable instanceof Boolean) { ... } // always true + def x = !variable instanceof Boolean // always true + + // this code is OK + if (!(variable instanceof String)) { ... } +]]>
+ clumsy
- org.codenarc.rule.groovyism.AssignCollectionUniqueRule - MINOR - - - The Collections.unique() method mutates the list and returns the list as a value. If you are assigning the result of unique() to a variable, then you probably don't realize that you're also modifying the original list as well. This is frequently the cause of subtle bugs. This violation is triggered when a unique() method call appears as the right hand side of an assignment, or when it appears as the first method call in a series of chained method calls.

-

Example of violations:

+ org.codenarc.rule.unnecessary.UnnecessarySubstringRule + MAJOR + + + This rule finds usages of String.substring(int) and String.substring(int, int) that can be replaced by use of the subscript operator. For instance, var.substring(5) can be replaced with var[5..-1].

+

Note that the String.substring(beginIndex,endIndex) method specifies a range of beginIndex..endIndex-1, while Groovy's String subscript specifies an inclusive range. So, "123456".substring(1, 5) is equivalent to "123456"[1..4].

+

Example of violations:

+
+    myVar.substring(5)          // can use myVar[5..-1] instead
+    myVar.substring(1, 5)       // can use myVar[1..4] instead
+
]]>
+ clumsy +
+ + + + org.codenarc.rule.unnecessary.UnnecessaryDefInFieldDeclarationRule + MAJOR + + + If a field has a visibility modifier or a type declaration, then the def keyword is unneeded. For instance, 'static def constraints = {}' is redundant and can be simplified to 'static constraints = {}.

+

Example of violations:

-  def a = myList.unique()
-  def b = myList.unique() { it }
-  def c = myList.unique().findAll { x < 1 }
+    class MyClass {
+        // def is redundant
+        static def constraints = {  }
+
+        // def and private is redundant
+        def private field1 = { }
+
+        // def and protected is redundant
+        def protected field2 = { }
+
+        // def and public is redundant
+        def public field3 = { }
 
-
-]]>
- groovyism + // def and static is redundant + def static field4 = { } + + // def and type is redundant + def Object field5 = { } + } +]]> + clumsy
- + - org.codenarc.rule.groovyism.AssignCollectionSortRule + org.codenarc.rule.unnecessary.UnnecessaryCastRule MINOR - - - The Collections.sort() method mutates the list and returns the list as a value. If you are assigning the result of sort() to a variable, then you probably don't realize that you're also modifying the original list as well. This is frequently the cause of subtle bugs. This violation is triggered when a sort() method call appears as the right hand side of an assignment, or when it appears as the first method call in a series of chained method calls.

-

Example of violations:

+ + + Checks for unnecessary cast operations.

+

Example of violations:

-  def a = myList.sort()
-  def b = myList.sort() { it }
-  def c = myList.sort().findAll { x < 1 }
-
-]]>
- groovyism + int count = (int)123 // violation + def longValue = (long)123456L // violation + def bigDecimal = (BigDecimal)1234.56 // violation + String name = (String) "Joe" // violation + def list = (List)[1, 2, 3] // violation + def map = (Map)[a:1] // violation +]]>
+ clumsy
- + - org.codenarc.rule.groovyism.ConfusingMultipleReturnsRule + org.codenarc.rule.unnecessary.UnnecessaryToStringRule MINOR - - - Multiple return values can be used to set several variables at once. To use multiple return values, the left hand side of the assignment must be enclosed in parenthesis. If not, then you are not using multiple return values, you're only assigning the last element.

-

Example of violations:

+ + + Checks for unnecessary calls to toString(). This includes:

+

* Calls to toString() on a String literal or expression

+

* Calls to toString() for the value assigned to a String field or variable (if checkAssignments is true).

+

Example of violations:

-def a, b = [1, 2] // bad, b is null
-def c, d, e = [1, 2, 3] // bad, c and d are null
-class MyClass {
-    def a, b, c = [1, 2, 3]  // bad, a and b are null
-}
+    def name = "Joe".toString()                             // violation - string literal
+    def groupId = ((String)row.get('GroupID')).toString()   // violation - string expression
 
-def x = 1              // ok
-def (f, g) = [1, 2]    // ok
-(a, b, c) = [1, 2, 3]  // ok
-
-]]>
- groovyism + class MyClass { + String name = nameNode.toString() // violation - field + String code = account.getCode().toString() // violation - field + + void run() { + String name = nameNode.toString() // violation - variable + String id = account.id.toString() // violation - variable + } + } +]]>
+ clumsy + + checkAssignments + + true +
- + - org.codenarc.rule.groovyism.GetterMethodCouldBePropertyRule + org.codenarc.rule.unnecessary.UnnecessarySafeNavigationOperatorRule MAJOR - - - If a class defines a public method that follows the Java getter notation and that returns a constant, then it is cleaner to provide a Groovy property for the value rather than a Groovy method.

-

Example of violations:

+ + + Check for the safe navigation operator (?.) applied to constants and literals, or this or super, or constructor calls, all of which can never be null.

+

Example of violations:

-    interface Parent {
-        String getSomething()
-        String getSomethingElse()
+    def myMethod() {
+        "abc"?.bytes            // violation
+        [1,2]?.getSize()        // violation
+        [abc:123]?.name         // violation
+        [:]?.toString()         // violation
+        123?.class              // violation
+        123.45?.getClass()      // violation
+        Boolean.FALSE?.class    // violation
+        Boolean.TRUE?.class     // violation
+        this?.class             // violation
+        super?.getClass()       // violation
+        new Long(100)?.class    // violation
     }
+
]]>
+ clumsy +
- class Child extends Parent { - static VALUE = 'value' - - @Override - String getSomething() { - 'something' // this could be simplified - } + + + org.codenarc.rule.unnecessary.UnnecessarySetterRule + MAJOR + + + Checks for explicit calls to setter methods which can, for the most part, be replaced by assignment to property. A setter is defined as a method call that matches set[A-Z] but not set[A-Z][A-Z] such as setURL(). Setters take one method argument. Setter calls within an expression are ignored.

+

These bits of code produce violations:

+
+  x.setProperty(1)
+  x.setProperty(this.getA())
+  x.setProperty([])
+
+

These bits of code do not:

+
+  x.set(1)                              // Nothing after "set"
+  x.setup(2)                            // The letter after "set" must be capitalized
+  x.setURL('')                          // But setters with multiple capital letters after "set" are ignored
+  x.setSomething('arg1', 'arg2')        // Setter must have exactly one argument
+  if (!file.setExecutable(true)) { }    // Set method called within expression
+  def count = x.setCount(92)            // Set method called within expression
+
]]>
+ clumsy +
- @Override - String getSomethingElse() { - VALUE // this could be simplified - } + - int getOtherValue() { - 123 - } + + org.codenarc.rule.unused.UnusedArrayRule + MINOR + + + Checks for array allocations that are not assigned or used, unless it is the last statement within a block (because it may be the intentional return value). Examples include:

+
+    int myMethod() {
+        new String[3]               // unused
+        return -1
+    }
 
-        static String getName() {
-            'MyName'
-        }
+    String[] myMethod() {
+        new String[3]               // OK (last statement in block)
     }
 
-    class Child2 extends Parent {
-        static VALUE = 'value'
-        final String something = 'something'    // this is cleaner
-        final String somethingElse = VALUE      // this is cleaner
-        final int otherValue = 123              // this is cleaner
-        static final String name = 'MyName'     // this is cleaner
+    def closure = {
+        doStuff()
+        new Date[3]                 // unused
+        doOtherStuff()
     }
-
-]]>
- groovyism + + def closure = { new Date[3] } // OK (last statement in block) +]]> + bug
- - org.codenarc.rule.groovyism.UseCollectManyRule + org.codenarc.rule.unused.UnusedObjectRule MINOR - - - In many case collectMany() yields the same result as collect{}.flatten(). It is easier to understand and more clearly conveys the intent.

-

Example of violations:

+ + + Checks for object allocations that are not assigned or used, unless it is the last statement within a block (because it may be the intentional return value). Examples include:

+

By default, this rule does not analyze test files. This rule sets the default value of the doNotApplyToFilesMatching property to ignore file names ending in 'Spec.groovy, ''Test.groovy', 'Tests.groovy' or 'TestCase.groovy'. Invoking constructors without using the result is a common pattern in tests.

-def l = [1, 2, 3, 4]
+    int myMethod() {
+        new BigDecimal("23.45")     // unused
+        return -1
+    }
 
-l.collect{ [it, it*2] }.flatten() // suboptimal
+    BigDecimal myMethod() {
+        new BigDecimal("23.45")     // OK (last statement in block)
+    }
 
-l.collectMany{ [it, it*2] }       // same functionality, better readability
-
-]]>
- groovyism + def closure = { + doStuff() + new Date() // unused + doOtherStuff() + } + + def closure = { new Date() } // OK (last statement in block) +]]>
+ bug
- - org.codenarc.rule.groovyism.CollectAllIsDeprecatedRule + org.codenarc.rule.unused.UnusedPrivateFieldRule MINOR - - - The collectAll method is deprecated since Groovy 1.8.1. Use collectNested instead.

-

Example of violations:

-
-def list = [1, 2, [3, 4, [5, 6]], 7]
-
-list.collectAll { it * 2 }      // deprecated
-
-list.collectNested { it * 2 }   // replacement
-
-]]>
- groovyism + + + Checks for private fields that are not referenced within the same class. Note that the private modifier is not currently "respected" by Groovy code (i.e., Groovy can access private members within other classes).

+

By default, fields named serialVersionUID, and fields annotated with groovy.lang.Delegate are ignored. The rule has a property named ignoreFieldNames, which can be set to ignore other field names as well. For instance, to also ignore fields named 'fieldx', set the property to the 'fieldx, serialVersionUID'

+

Known limitations:

+

* Does not recognize field access when field name is a GString (e.g. this."${fieldName}")

+

* Does not recognize access of private field of another instance (i.e. other than this)

]]>
+ bug + + ignoreFieldNames + + serialVersionUID +
- - org.codenarc.rule.groovyism.UseCollectNestedRule + org.codenarc.rule.unused.UnusedPrivateMethodRule MINOR - - - Instead of nested collect{} calls use collectNested{}.

-

Example of violations:

-
-def list = [1, 2, [3, 4, 5, 6], [7]]
-
-println list.collect { elem ->
-    if (elem instanceof List)
-        elem.collect {it *2} // violation
-    else elem * 2
-}
+    
+    
+    Checks for private methods that are not referenced within the same class. Note that the private modifier is not currently "respected" by Groovy code (i.e., Groovy can access private members within other classes).

+

Known limitations:

+

* Does not recognize method reference through property access (e.g. getName() accessed as x.name)

+

* Does not recognize method invocations when method name is a GString (e.g. this."${methodName}"())

+

* Does not recognize invoking private method of another instance (i.e. other than this)

+

* Does not differentiate between multiple private methods with the same name but different parameters (i.e., overloaded)

+

* Does not check for unused constructors

]]>
+ bug + + ignoreMethodsWithAnnotationNames + + + -println list.collect([8]) { - if (it instanceof List) - it.collect {it *2} // violation - else it * 2 -} + + org.codenarc.rule.unused.UnusedVariableRule.fixed + MINOR + + + Checks for variables that are never referenced. An assignment to the variable is not considered a reference.

+

The rule has a property named ignoreVariableNames, which can be set to ignore some variable names. For instance, to ignore fields named 'unused', set the property to 'unused'.

+

Known limitations:

+

* Incorrectly considers a variable referenced if another variable with the same name is referenced elsewhere (in another scope/block).

]]>
+ bug + + ignoreVariableNames + + +
-println list.collectNested { it * 2 } // same functionality, better readability -
-]]>
- groovyism + + + org.codenarc.rule.unused.UnusedPrivateMethodParameterRule + MINOR + + + Checks for parameters to private methods that are not referenced within the method body. Note that the private modifier is not currently "respected" by Groovy code (i.e., Groovy can access private members within other classes).

+

Known limitations:

+

* Does not recognize parameter references within an inner class. See CodeNarc bug #3155974.

+

* Does not recognize parameter references when parameter name is a GString (e.g. println "${parameterName}")

+

* You can specify an ignore list using the 'ignoreRegex' property. By default, a parameter named 'ignore' or 'ignored' does not trigger a violation (the regex value is 'ignore|ignored'). You can add your own ignore list using this property.

]]>
+ bug
- + - org.codenarc.rule.groovyism.GStringExpressionWithinStringRule + org.codenarc.rule.unused.UnusedMethodParameterRule MINOR - - - Check for regular (single quote) strings containing a GString-type expression (${..}).

-

Example of violations:

+ + + This rule finds instances of method parameters not being used. It does not analyze private methods (that is done by the UnusedPrivateMethodParameter rule) or methods marked @Override.

+

* This rule ignores main() methods. In Groovy, the main() method can either specify a void return type or else omit a return type (be dynamically typed). The main() method must have exactly one parameter. That parameter can either be typed as String[] or else the type can be omitted (be dynamically typed). And the main() method must be static.

+

* You can specify an ignore list of parameter names using the 'ignoreRegex' property. By default, a parameter named 'ignore' or 'ignored' does not trigger a violation (the regex value is 'ignore|ignored'). You can add your own ignore list using this property.

+

* You can specify a class name pattern to ignore using the 'ignoreClassRegex' property. By default classes named '*.Category' are ignored because they are category classes and have unused parameters in static methods.

+

Example of violations:

-    def str1 = 'total: ${count}'                // violation
-    def str2 = 'average: ${total / count}'      // violation
-
-    def str3 = "abc ${count}"                   // ok; GString
-    def str4 = '$123'                           // ok
-    def str5 = 'abc {123}'                      // ok
+    class MyClass {
+        def method(def param) {
+            // param is unused
+        }
+    }
 
-]]>
- groovyism +

Example of code that does not cause violations:

+
+    class MyClass {
+        @Override
+        def otherMethod(def param) {
+            // this is OK because it overrides a super class
+        }
+    }
+
+    class MyCategory {
+        // Category classes are ignored by default
+        void myMethod1(String string, int value) { }
+        void myMethod1(String string, int value, name) { }
+    }
+
+    class MainClass1 {
+        // main() methods are ignored
+        public static void main(String[] args) { }
+    }
+    class MainClass2 {
+        // This is also a valid Groovy main() method
+        static main(args) { }
+    }
+
]]>
+ bug + + ignoreClassRegex + + .*Category + + + ignoreRegex + + ignore|ignored +
diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovyMetricsTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovyMetricsTest.java deleted file mode 100644 index 1c7041c0..00000000 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovyMetricsTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class GroovyMetricsTest { - @Test - public void test_metrics() { - assertThat(new GroovyMetrics().getMetrics()).hasSize(4); - } -} diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovyPluginTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovyPluginTest.java index bf2c1e57..c0653eb9 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovyPluginTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovyPluginTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,20 +19,27 @@ */ package org.sonar.plugins.groovy; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.Test; import org.sonar.api.Plugin; -import org.sonar.api.SonarQubeVersion; - -import static org.assertj.core.api.Assertions.assertThat; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.SonarRuntime; +import org.sonar.api.internal.SonarRuntimeImpl; +import org.sonar.api.utils.Version; public class GroovyPluginTest { + public static final Version VERSION_7_9 = Version.create(7, 9); @Test public void testExtensions() { GroovyPlugin plugin = new GroovyPlugin(); - Plugin.Context context = new Plugin.Context(SonarQubeVersion.V5_6); + + SonarRuntime runtime = + SonarRuntimeImpl.forSonarQube(VERSION_7_9, SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); + Plugin.Context context = new Plugin.Context(runtime); plugin.define(context); - assertThat(context.getExtensions()).hasSize(17); + assertThat(context.getExtensions()).hasSize(14); } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovySensorTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovySensorTest.java index ca086141..97f0667b 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovySensorTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/GroovySensorTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,39 +19,40 @@ */ package org.sonar.plugins.groovy; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import org.junit.Test; -import org.mockito.Matchers; import org.mockito.Mockito; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.FileLinesContextFactory; import org.sonar.plugins.groovy.foundation.Groovy; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class GroovySensorTest { - private Settings settings = new Settings(); + private MapSettings settings = new MapSettings(); private FileLinesContextFactory fileLinesContextFactory = mock(FileLinesContextFactory.class); - private DefaultFileSystem fileSystem = new DefaultFileSystem(new File(".")); + private DefaultFileSystem fileSystem = new DefaultFileSystem(Paths.get(".")); private GroovySensor sensor = new GroovySensor(settings, fileLinesContextFactory, fileSystem); @Test public void do_nothing_when_no_groovy_file() throws IOException { - SensorContextTester context = SensorContextTester.create(new File("")); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); context = Mockito.spy(context); sensor = new GroovySensor(settings, fileLinesContextFactory, context.fileSystem()); sensor.execute(context); @@ -70,92 +71,45 @@ public void compute_metrics_ignoring_header_comment() throws IOException { } private void testMetrics(boolean headerComment, int expectedCommentMetric) throws IOException { - settings.appendProperty(GroovyPlugin.IGNORE_HEADER_COMMENTS, "" + headerComment); - File sourceDir = new File("src/test/resources/org/sonar/plugins/groovy/gmetrics"); - SensorContextTester context = SensorContextTester.create(new File("")); + settings.setProperty(GroovyPlugin.IGNORE_HEADER_COMMENTS, headerComment); + + Path sourceFile = TestUtils.getResource(getClass(), "../gmetrics/Greeting.groovy"); + SensorContextTester context = SensorContextTester.create(sourceFile.getParent()); - File sourceFile = new File(sourceDir, "Greeting.groovy"); fileSystem = context.fileSystem(); - fileSystem.add(new DefaultInputDir("", sourceDir.getPath())); - DefaultInputFile groovyFile = new DefaultInputFile("", sourceFile.getPath()) - .setLanguage(Groovy.KEY) - .initMetadata(new String(Files.readAllBytes(sourceFile.toPath()), "UTF-8")); + InputFile groovyFile = + TestInputFileBuilder.create("", sourceFile.getParent().toFile(), sourceFile.toFile()) + .setLanguage(Groovy.KEY) + .setContents(new String(Files.readAllBytes(sourceFile), StandardCharsets.UTF_8)) + .setCharset(StandardCharsets.UTF_8) + .build(); fileSystem.add(groovyFile); - fileSystem.add(new DefaultInputFile("", "unknownFile.groovy").setLanguage(Groovy.KEY)); FileLinesContext fileLinesContext = mock(FileLinesContext.class); - when(fileLinesContextFactory.createFor(any(DefaultInputFile.class))).thenReturn(fileLinesContext); + when(fileLinesContextFactory.createFor(any(DefaultInputFile.class))) + .thenReturn(fileLinesContext); sensor = new GroovySensor(settings, fileLinesContextFactory, fileSystem); sensor.execute(context); String key = groovyFile.key(); - assertThat(context.measure(key, CoreMetrics.FILES).value()).isEqualTo(1); assertThat(context.measure(key, CoreMetrics.CLASSES).value()).isEqualTo(2); assertThat(context.measure(key, CoreMetrics.FUNCTIONS).value()).isEqualTo(2); - assertThat(context.measure(key, CoreMetrics.LINES).value()).isEqualTo(33); assertThat(context.measure(key, CoreMetrics.NCLOC).value()).isEqualTo(17); - assertThat(context.measure(key, CoreMetrics.COMMENT_LINES).value()).isEqualTo(expectedCommentMetric); + assertThat(context.measure(key, CoreMetrics.COMMENT_LINES).value()) + .isEqualTo(expectedCommentMetric); assertThat(context.measure(key, CoreMetrics.COMPLEXITY).value()).isEqualTo(4); - assertThat(context.measure(key, CoreMetrics.COMPLEXITY_IN_CLASSES).value()).isEqualTo(4); - assertThat(context.measure(key, CoreMetrics.COMPLEXITY_IN_FUNCTIONS).value()).isEqualTo(4); - - assertThat(context.measure(key, CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION).value()).isEqualTo("1=0;2=2;4=0;6=0;8=0;10=0;12=0"); - assertThat(context.measure(key, CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION).value()).isEqualTo("0=1;5=0;10=0;20=0;30=0;60=0;90=0"); // 11 times for comment because we register comment even when ignoring header comment - Mockito.verify(fileLinesContext, Mockito.times(11)).setIntValue(Mockito.eq(CoreMetrics.COMMENT_LINES_DATA_KEY), Matchers.anyInt(), Mockito.eq(1)); - Mockito.verify(fileLinesContext, Mockito.times(17)).setIntValue(Mockito.eq(CoreMetrics.NCLOC_DATA_KEY), Matchers.anyInt(), Mockito.eq(1)); - Mockito.verify(fileLinesContext).setIntValue(CoreMetrics.COMMENT_LINES_DATA_KEY, 18, 1); + Mockito.verify(fileLinesContext, Mockito.times(17)) + .setIntValue(Mockito.eq(CoreMetrics.NCLOC_DATA_KEY), anyInt(), Mockito.eq(1)); Mockito.verify(fileLinesContext).setIntValue(CoreMetrics.NCLOC_DATA_KEY, 18, 1); // Only "Greeting.groovy" is part of the file system. Mockito.verify(fileLinesContext, Mockito.times(1)).save(); } - @Test - public void compute_coupling_metrics() throws IOException { - SensorContextTester context = SensorContextTester.create(new File("")); - - fileSystem = context.fileSystem(); - // package 'org' contains class 'Greeting', used by no other class, but using classes 'Bar' and 'Foo' - DefaultInputDir org = addFileWithParentFolder("src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org", "Greeting.groovy"); - // package 'org.foo' contains class 'Foo', used by class 'Greeting', and using class 'Bar' - DefaultInputDir org_foo = addFileWithParentFolder("src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/foo", "Foo.groovy"); - // package 'org.bar' contains class 'Bar', used by classes 'Greeting' and 'Foo', but using no other class - DefaultInputDir org_bar = addFileWithParentFolder("src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/bar", "Bar.groovy"); - - FileLinesContext fileLinesContext = mock(FileLinesContext.class); - when(fileLinesContextFactory.createFor(any(DefaultInputFile.class))).thenReturn(fileLinesContext); - - sensor = new GroovySensor(settings, fileLinesContextFactory, fileSystem); - sensor.execute(context); - - assertCouplingMeasureAre(context, org.key(), 3, 1.0, 3, 1.0); - assertCouplingMeasureAre(context, org_foo.key(), 1, 1.0, 1, 1.0); - assertCouplingMeasureAre(context, org_bar.key(), 2, 2.0, 0, 0.0); - } - - private static void assertCouplingMeasureAre(SensorContextTester context, String key, Object afferentTot, Object afferentAvg, Object efferentTot, Object efferentAvg) { - assertThat(context.measure(key, GroovyMetrics.AFFERENT_COUPLING_TOTAL.key()).value()).isEqualTo(afferentTot); - assertThat(context.measure(key, GroovyMetrics.AFFERENT_COUPLING_AVERAGE.key()).value()).isEqualTo(afferentAvg); - - assertThat(context.measure(key, GroovyMetrics.EFFERENT_COUPLING_TOTAL.key()).value()).isEqualTo(efferentTot); - assertThat(context.measure(key, GroovyMetrics.EFFERENT_COUPLING_AVERAGE.key()).value()).isEqualTo(efferentAvg); - } - - private DefaultInputDir addFileWithParentFolder(String dirPath, String fileName) throws IOException { - File dir = new File(dirPath); - File file = new File(dir, fileName); - DefaultInputDir inputDir = new DefaultInputDir("", dir.getPath()); - fileSystem.add(inputDir); - fileSystem.add(new DefaultInputFile("", file.getPath()) - .setLanguage(Groovy.KEY) - .initMetadata(new String(Files.readAllBytes(file.toPath()), "UTF-8"))); - return inputDir; - } - @Test public void test_toString() { assertThat(sensor.toString()).isEqualTo("GroovySensor"); @@ -167,5 +121,4 @@ public void test_description() { sensor.describe(defaultSensorDescriptor); assertThat(defaultSensorDescriptor.languages()).containsOnly(Groovy.KEY); } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/TestUtils.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/TestUtils.java index 92a91600..9832a8e8 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/TestUtils.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/TestUtils.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,17 +20,24 @@ package org.sonar.plugins.groovy; import java.io.File; +import java.lang.reflect.Constructor; import java.net.URL; +import java.nio.file.Path; +import java.util.Collection; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.utils.System2; +import org.sonar.plugins.groovy.jacoco.JaCoCoConfiguration; public final class TestUtils { - private TestUtils() { - } + private TestUtils() {} /** - * Search for a test resource in the classpath. For example getResource("org/sonar/MyClass/foo.txt"); + * Search for a test resource in the classpath. For example + * getResource("org/sonar/MyClass/foo.txt"); * * @param path the starting slash is optional * @return the resource. Null if resource not found @@ -48,18 +55,32 @@ public static File getResource(String path) { } /** - * Search for a resource in the classpath. For example calling the method getResource(getClass(), "myTestName/foo.txt") from - * the class org.sonar.Foo loads the file $basedir/src/test/resources/org/sonar/Foo/myTestName/foo.txt + * Search for a resource in the classpath. For example calling the method getResource(getClass(), + * "myTestName/foo.txt") from the class org.sonar.Foo loads the file + * $basedir/src/test/resources/org/sonar/Foo/myTestName/foo.txt * * @return the resource. Null if resource not found */ - public static File getResource(Class baseClass, String path) { + public static Path getResource(Class baseClass, String path) { String resourcePath = StringUtils.replaceChars(baseClass.getCanonicalName(), '.', '/'); if (!path.startsWith("/")) { resourcePath += "/"; } resourcePath += path; - return getResource(resourcePath); + return getResource(resourcePath).toPath(); } + public static MapSettings jacocoDefaultSettings() { + PropertyDefinitions prop = null; + try { + // Legacy declaration, doesn't compile on modern SonarQube + Constructor c = + PropertyDefinitions.class.getConstructor(Collection.class); + prop = c.newInstance(JaCoCoConfiguration.getPropertyDefinitions()); + } catch (ReflectiveOperationException | IllegalArgumentException e) { + prop = + new PropertyDefinitions(System2.INSTANCE, JaCoCoConfiguration.getPropertyDefinitions()); + } + return new MapSettings(prop); + } } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/cobertura/CoberturaSensorTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/cobertura/CoberturaSensorTest.java index 2e4f48ce..6e3abcac 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/cobertura/CoberturaSensorTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/cobertura/CoberturaSensorTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,46 +19,41 @@ */ package org.sonar.plugins.groovy.cobertura; -import java.io.File; -import java.util.HashMap; -import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.nio.file.Paths; import org.junit.Before; import org.junit.Test; -import org.mockito.ArgumentMatchers; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import org.sonar.api.batch.fs.FilePredicate; -import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.batch.sensor.coverage.CoverageType; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; import org.sonar.plugins.groovy.GroovyPlugin; +import org.sonar.plugins.groovy.TestUtils; import org.sonar.plugins.groovy.foundation.Groovy; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class CoberturaSensorTest { - private Settings settings; + private MapSettings settings; private CoberturaSensor sensor; private DefaultFileSystem fileSystem; @Before public void setUp() throws Exception { - settings = new Settings(); - settings.setProperty(GroovyPlugin.COBERTURA_REPORT_PATH, "src/test/resources/org/sonar/plugins/groovy/cobertura/coverage.xml"); - fileSystem = new DefaultFileSystem(new File(".")); + settings = new MapSettings(); + settings.setProperty( + GroovyPlugin.COBERTURA_REPORT_PATH, + TestUtils.getResource(getClass(), "../coverage.xml").toString()); + fileSystem = new DefaultFileSystem(Paths.get(".")); sensor = new CoberturaSensor(settings, fileSystem); } @@ -69,79 +64,38 @@ public void test_description() { assertThat(defaultSensorDescriptor.languages()).containsOnly(Groovy.KEY); } - /** - * See SONARPLUGINS-696 - */ @Test public void should_parse_report() { - FilePredicates fp = mock(FilePredicates.class); - - class CustomFilePredicate implements FilePredicate { - - final String fileName; - - CustomFilePredicate(String fileName) { - this.fileName = fileName; - } - - @Override - public boolean apply(InputFile inputFile) { - return true; - } - } - - when(fp.hasAbsolutePath(ArgumentMatchers.anyString())).thenAnswer(new Answer() { - @Override - public FilePredicate answer(InvocationOnMock invocation) throws Throwable { - return new CustomFilePredicate(invocation.getArgument(0)); - } - }); - - FileSystem mockfileSystem = mock(FileSystem.class); - when(mockfileSystem.predicates()).thenReturn(fp); - when(mockfileSystem.hasFiles(ArgumentMatchers.nullable(FilePredicate.class))).thenReturn(true); - - Map groovyFilesByName = new HashMap<>(); - - when(mockfileSystem.inputFile(any(FilePredicate.class))).thenAnswer(new Answer() { - boolean firstCall = true; - - @Override - public InputFile answer(InvocationOnMock invocation) throws Throwable { - if (firstCall) { - // The first class in the test coverage.xml is a java class and the rest are groovy - firstCall = false; - return new DefaultInputFile("", "fake.java").setLanguage("java"); - } - String fileName = invocation.getArgument(0).fileName; - DefaultInputFile groovyFile; - if (!groovyFilesByName.containsKey(fileName)) { - // store groovy file as default input files - groovyFile = new DefaultInputFile("", fileName).setLanguage(Groovy.KEY).setType(Type.MAIN).setLines(Integer.MAX_VALUE); - groovyFilesByName.put(fileName, groovyFile); - } - return groovyFilesByName.get(fileName); - } - }); - sensor = new CoberturaSensor(settings, mockfileSystem); - - SensorContextTester context = SensorContextTester.create(new File("")); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); + DefaultFileSystem fs = context.fileSystem(); + fs.add( + TestInputFileBuilder.create("", "com/test/web/EmptyResultException.java") + .setLanguage("java") + .build()); + fs.add( + TestInputFileBuilder.create("", "grails-app/domain/AboveEighteenFilters.groovy") + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .setLines(Integer.MAX_VALUE) + .build()); + + sensor = new CoberturaSensor(settings, fs); sensor.execute(context); // random pick groovy file - String filekey = ":/Users/cpicat/myproject/grails-app/domain/AboveEighteenFilters.groovy"; + String filekey = ":grails-app/domain/AboveEighteenFilters.groovy"; int[] lineHits = {2, 6, 7}; int[] lineNoHits = {9, 10, 11}; for (int line : lineHits) { - assertThat(context.lineHits(filekey, CoverageType.UNIT, line)).isEqualTo(1); + assertThat(context.lineHits(filekey, line)).isEqualTo(1); } for (int line : lineNoHits) { - assertThat(context.lineHits(filekey, CoverageType.UNIT, line)).isEqualTo(0); + assertThat(context.lineHits(filekey, line)).isEqualTo(0); } // No value for java file - assertThat(context.lineHits(":/Users/cpicat/myproject/grails-app/domain/com/test/web/EmptyResultException.java", CoverageType.UNIT, 16)).isNull(); + assertThat(context.lineHits(":com/test/web/EmptyResultException.java", 16)).isNull(); } @Test @@ -159,9 +113,9 @@ public void should_not_save_any_measure_if_files_can_not_be_found() { @Test public void should_not_parse_report_if_settings_does_not_contain_report_path() { - DefaultFileSystem fileSystem = new DefaultFileSystem(new File(".")); - fileSystem.add(new DefaultInputFile("", "fake.groovy").setLanguage(Groovy.KEY)); - sensor = new CoberturaSensor(new Settings(), fileSystem); + DefaultFileSystem fileSystem = new DefaultFileSystem(Paths.get(".")); + fileSystem.add(TestInputFileBuilder.create("", "fake.groovy").setLanguage(Groovy.KEY).build()); + sensor = new CoberturaSensor(new MapSettings(), fileSystem); SensorContext context = mock(SensorContext.class); sensor.execute(context); @@ -171,11 +125,12 @@ public void should_not_parse_report_if_settings_does_not_contain_report_path() { @Test public void should_not_parse_report_if_report_does_not_exist() { - Settings settings = new Settings(); - settings.setProperty(GroovyPlugin.COBERTURA_REPORT_PATH, "org/sonar/plugins/groovy/cobertura/fake-coverage.xml"); + MapSettings settings = new MapSettings(); + settings.setProperty( + GroovyPlugin.COBERTURA_REPORT_PATH, "org/sonar/plugins/groovy/cobertura/fake-coverage.xml"); - DefaultFileSystem fileSystem = new DefaultFileSystem(new File(".")); - fileSystem.add(new DefaultInputFile("", "fake.groovy").setLanguage(Groovy.KEY)); + DefaultFileSystem fileSystem = new DefaultFileSystem(Paths.get(".")); + fileSystem.add(TestInputFileBuilder.create("", "fake.groovy").setLanguage(Groovy.KEY).build()); sensor = new CoberturaSensor(settings, fileSystem); @@ -187,11 +142,13 @@ public void should_not_parse_report_if_report_does_not_exist() { @Test public void should_use_relative_path_to_get_report() { - Settings settings = new Settings(); - settings.setProperty(GroovyPlugin.COBERTURA_REPORT_PATH, "//org/sonar/plugins/groovy/cobertura/fake-coverage.xml"); + MapSettings settings = new MapSettings(); + settings.setProperty( + GroovyPlugin.COBERTURA_REPORT_PATH, + "//org/sonar/plugins/groovy/cobertura/fake-coverage.xml"); - DefaultFileSystem fileSystem = new DefaultFileSystem(new File(".")); - fileSystem.add(new DefaultInputFile("", "fake.groovy").setLanguage(Groovy.KEY)); + DefaultFileSystem fileSystem = new DefaultFileSystem(Paths.get(".")); + fileSystem.add(TestInputFileBuilder.create("", "fake.groovy").setLanguage(Groovy.KEY).build()); sensor = new CoberturaSensor(settings, fileSystem); @@ -203,7 +160,7 @@ public void should_use_relative_path_to_get_report() { @Test public void should_execute_on_project() { - fileSystem.add(new DefaultInputFile("", "fake.groovy").setLanguage(Groovy.KEY)); + fileSystem.add(TestInputFileBuilder.create("", "fake.groovy").setLanguage(Groovy.KEY).build()); assertThat(sensor.shouldExecuteOnProject()).isTrue(); } @@ -216,5 +173,4 @@ public void should_not_execute_if_no_groovy_files() { public void test_toString() { assertThat(sensor.toString()).isEqualTo("Groovy CoberturaSensor"); } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/ActiveRulesBuilderWrapper.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/ActiveRulesBuilderWrapper.java new file mode 100644 index 00000000..065f13e4 --- /dev/null +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/ActiveRulesBuilderWrapper.java @@ -0,0 +1,69 @@ +/* + * Sonar Groovy Plugin + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com + * + * 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. + */ +package org.sonar.plugins.groovy.codenarc; + +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.rule.internal.NewActiveRule; +import org.sonar.api.rule.RuleKey; + +public class ActiveRulesBuilderWrapper { + + private ActiveRulesBuilder builder = new ActiveRulesBuilder(); + private NewActiveRule.Builder lastRule; + + boolean newType = false; + + public ActiveRulesBuilderWrapper addRule(String key) { + addLastRule(); + RuleKey ruleKey = RuleKey.of(CodeNarcRulesDefinition.REPOSITORY_KEY, key); + lastRule = new NewActiveRule.Builder(); + lastRule.setRuleKey(ruleKey); + setInternalKey(key); + return this; + } + + public ActiveRulesBuilderWrapper setName(String name) { + lastRule.setName(name); + return this; + } + + public ActiveRulesBuilderWrapper setInternalKey(String key) { + lastRule.setInternalKey(key); + return this; + } + + public ActiveRulesBuilderWrapper addParam(String key, String value) { + lastRule.setParam(key, value); + return this; + } + + private void addLastRule() { + if (lastRule != null) { + builder.addRule(lastRule.build()); + lastRule = null; + } + } + + public ActiveRules build() { + addLastRule(); + return builder.build(); + } +} diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcProfileExporterTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcProfileExporterTest.java index 298fb499..86ef9aab 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcProfileExporterTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcProfileExporterTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,139 +19,152 @@ */ package org.sonar.plugins.groovy.codenarc; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; import org.apache.commons.lang.CharUtils; +import org.assertj.core.api.Fail; import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.XMLUnit; -import org.assertj.core.api.Fail; import org.junit.Assert; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; -import org.mockito.Matchers; import org.mockito.Mockito; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; -import org.sonar.plugins.groovy.foundation.Groovy; import org.sonar.plugins.groovy.TestUtils; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; - -import static org.assertj.core.api.Assertions.assertThat; - public class CodeNarcProfileExporterTest { private StringWriter writer; private CodeNarcProfileExporter exporter; - private RulesProfile profile; @Before public void setUp() { writer = new StringWriter(); exporter = new CodeNarcProfileExporter(writer); - profile = RulesProfile.create("Sonar Groovy way", Groovy.KEY); } @Test public void shouldExportProfile() throws Exception { - Rule rule = Rule.create(CodeNarcRulesDefinition.REPOSITORY_KEY, "org.codenarc.rule.basic.AddEmptyStringRule", "Add Empty String"); - profile.activateRule(rule, RulePriority.MAJOR); - rule = Rule.create(CodeNarcRulesDefinition.REPOSITORY_KEY, "org.codenarc.rule.size.ClassSizeRule", "Class Size"); - profile.activateRule(rule, RulePriority.MAJOR); - exporter.exportProfile(profile); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper() + .addRule("org.codenarc.rule.basic.AddEmptyStringRule") + .setName("Add Empty String") + .addRule("org.codenarc.rule.size.ClassSizeRule") + .setName("Class Size"); + exporter.exportProfile(activeRulesBuilder.build()); assertSimilarXml( - TestUtils.getResource("/org/sonar/plugins/groovy/codenarc/exportProfile/exportProfile.xml"), - writer.toString()); + TestUtils.getResource("/org/sonar/plugins/groovy/codenarc/exportProfile/exportProfile.xml"), + writer.toString()); } @Test public void shouldFailToExport() throws IOException { Writer writer = Mockito.mock(Writer.class); - Mockito.when(writer.append(Matchers.any(CharSequence.class))).thenThrow(new IOException()); + Mockito.when(writer.append(any(CharSequence.class))).thenThrow(new IOException()); exporter = new CodeNarcProfileExporter(writer); try { - exporter.exportProfile(profile); + exporter.exportProfile(new ActiveRulesBuilderWrapper().build()); Fail.fail("Should have failed"); - } catch(IllegalStateException e) { + } catch (IllegalStateException e) { assertThat(e.getMessage()).contains("Fail to export CodeNarc profile"); } } @Test public void shouldExportParameters() throws Exception { - Rule rule = Rule.create(CodeNarcRulesDefinition.REPOSITORY_KEY, "org.codenarc.rule.size.ClassSizeRule", "Class Size"); - rule.createParameter("maxLines"); - profile.activateRule(rule, RulePriority.MAJOR).setParameter("maxLines", "20"); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper() + .addRule("org.codenarc.rule.size.ClassSizeRule") + .setName("Class Size") + .addParam("maxLines", "20"); - exporter.exportProfile(profile); + exporter.exportProfile(activeRulesBuilder.build()); assertSimilarXml( - TestUtils.getResource("/org/sonar/plugins/groovy/codenarc/exportProfile/exportParameters.xml"), - writer.toString()); + TestUtils.getResource( + "/org/sonar/plugins/groovy/codenarc/exportProfile/exportParameters.xml"), + writer.toString()); } @Test public void shouldNotExportUnsetParameters() throws Exception { - Rule rule = Rule.create(CodeNarcRulesDefinition.REPOSITORY_KEY, "org.codenarc.rule.size.ClassSizeRule", "Class Size"); - rule.createParameter("maxLines"); - profile.activateRule(rule, RulePriority.MAJOR).setParameter("maxLines", null); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper() + .addRule("org.codenarc.rule.size.ClassSizeRule") + .setName("Class Size") + .addParam("maxLines", null); - exporter.exportProfile(profile); + exporter.exportProfile(activeRulesBuilder.build()); assertSimilarXml( - TestUtils.getResource("/org/sonar/plugins/groovy/codenarc/exportProfile/exportNullParameters.xml"), - writer.toString()); + TestUtils.getResource( + "/org/sonar/plugins/groovy/codenarc/exportProfile/exportNullParameters.xml"), + writer.toString()); } @Test public void shouldExportFixedRulesCorrectly() throws Exception { - Rule rule = Rule.create(CodeNarcRulesDefinition.REPOSITORY_KEY, "org.codenarc.rule.design.PrivateFieldCouldBeFinalRule.fixed", "Private Field Could Be Final"); - profile.activateRule(rule, RulePriority.MAJOR); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper() + .addRule("org.codenarc.rule.design.PrivateFieldCouldBeFinalRule.fixed") + .setName("Private Field Could Be Final"); - exporter.exportProfile(profile); + exporter.exportProfile(activeRulesBuilder.build()); assertSimilarXml( - TestUtils.getResource("/org/sonar/plugins/groovy/codenarc/exportProfile/exportFixedRules.xml"), - writer.toString()); + TestUtils.getResource( + "/org/sonar/plugins/groovy/codenarc/exportProfile/exportFixedRules.xml"), + writer.toString()); } @Test + @Ignore( + "Is this rule still pertinent, as the rule parameters are kept server-side? I assume defaults will be brought down as params") public void shouldNotExportParametersWithDefaultValue() throws Exception { - Rule rule = Rule.create(CodeNarcRulesDefinition.REPOSITORY_KEY, "org.codenarc.rule.size.ClassSizeRule", "Class Size"); - rule.createParameter("maxLines").setDefaultValue("20"); - profile.activateRule(rule, RulePriority.MAJOR).setParameter("maxLines", "20"); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper() + .addRule("org.codenarc.rule.size.ClassSizeRule") + .setName("Class Size") + .addParam("maxLines", "20"); - exporter.exportProfile(profile); + exporter.exportProfile(activeRulesBuilder.build()); assertSimilarXml( - TestUtils.getResource("/org/sonar/plugins/groovy/codenarc/exportProfile/exportNullParameters.xml"), - writer.toString()); + TestUtils.getResource( + "/org/sonar/plugins/groovy/codenarc/exportProfile/exportNullParameters.xml"), + writer.toString()); } @Test public void shouldEscapeExportedParameters() throws Exception { - Rule rule = Rule.create(CodeNarcRulesDefinition.REPOSITORY_KEY, "org.codenarc.rule.naming.ClassNameRule", "Class Name"); - rule.createParameter("regex").setDefaultValue("([A-Z]\\w*\\$?)*"); - profile.activateRule(rule, RulePriority.MAJOR).setParameter("regex", "[A-Z]+[a-z&&[^bc]]"); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper() + .addRule("org.codenarc.rule.naming.ClassNameRule") + .setName("Class Name") + .addParam("regex", "[A-Z]+[a-z&&[^bc]]"); - exporter.exportProfile(profile); + exporter.exportProfile(activeRulesBuilder.build()); assertSimilarXml( - TestUtils.getResource("/org/sonar/plugins/groovy/codenarc/exportProfile/exportEscapedParameters.xml"), - writer.toString()); + TestUtils.getResource( + "/org/sonar/plugins/groovy/codenarc/exportProfile/exportEscapedParameters.xml"), + writer.toString()); } private void assertSimilarXml(File expectedFile, String xml) throws Exception { XMLUnit.setIgnoreWhitespace(true); Reader reader = new FileReader(expectedFile); Diff diff = XMLUnit.compareXML(reader, xml); - String message = "Diff: " + diff.toString() + CharUtils.LF + "XML: " + xml; + String message = "Diff: " + diff + CharUtils.LF + "XML: " + xml; Assert.assertTrue(message, diff.similar()); } } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcRulesDefinitionTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcRulesDefinitionTest.java index eda52e27..c0d15e47 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcRulesDefinitionTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcRulesDefinitionTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,6 +19,8 @@ */ package org.sonar.plugins.groovy.codenarc; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.LinkedList; import java.util.List; import org.junit.Test; @@ -26,8 +28,6 @@ import org.sonar.api.server.rule.RulesDefinition.Rule; import org.sonar.plugins.groovy.foundation.Groovy; -import static org.assertj.core.api.Assertions.assertThat; - public class CodeNarcRulesDefinitionTest { @Test @@ -35,13 +35,14 @@ public void test() { CodeNarcRulesDefinition definition = new CodeNarcRulesDefinition(); RulesDefinition.Context context = new RulesDefinition.Context(); definition.define(context); - RulesDefinition.Repository repository = context.repository(CodeNarcRulesDefinition.REPOSITORY_KEY); + RulesDefinition.Repository repository = + context.repository(CodeNarcRulesDefinition.REPOSITORY_KEY); assertThat(repository.name()).isEqualTo(CodeNarcRulesDefinition.REPOSITORY_NAME); assertThat(repository.language()).isEqualTo(Groovy.KEY); List rules = repository.rules(); - assertThat(rules).hasSize(347); + assertThat(rules).hasSize(379); List missingDebt = new LinkedList<>(); for (Rule rule : rules) { @@ -53,7 +54,8 @@ public void test() { missingDebt.add(rule.key()); } } - // From SONARGROOV-36, 'org.codenarc.rule.generic.IllegalSubclassRule' does not have debt by purpose + // From SONARGROOV-36, 'org.codenarc.rule.generic.IllegalSubclassRule' does not have debt by + // purpose assertThat(missingDebt).containsOnly("org.codenarc.rule.generic.IllegalSubclassRule.fixed"); Rule rule = repository.rule("org.codenarc.rule.braces.ElseBlockBracesRule"); diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcSensorTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcSensorTest.java index 5f18ff20..af9317e7 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcSensorTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcSensorTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,60 +19,51 @@ */ package org.sonar.plugins.groovy.codenarc; +import static org.assertj.core.api.Assertions.assertThat; + import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.Arrays; +import java.nio.file.Path; import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.config.PropertyDefinitions; -import org.sonar.api.config.Settings; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.ActiveRule; +import org.sonar.api.config.internal.MapSettings; import org.sonar.plugins.groovy.GroovyPlugin; import org.sonar.plugins.groovy.foundation.Groovy; import org.sonar.plugins.groovy.foundation.GroovyFileSystem; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class CodeNarcSensorTest { + @Rule public TemporaryFolder temp = new TemporaryFolder(); - private RulesProfile profile; - private CodeNarcSensor sensor; - private Groovy groovy; private SensorContextTester sensorContextTester; - - @org.junit.Rule - public TemporaryFolder temp = new TemporaryFolder(); + private MapSettings settings = new MapSettings(); @Before public void setUp() throws Exception { - sensorContextTester = SensorContextTester.create(temp.newFolder()); - sensorContextTester.fileSystem().setWorkDir(temp.newFolder()); - - profile = mock(RulesProfile.class); + sensorContextTester.fileSystem().setWorkDir(temp.newFolder().toPath()); - sensorContextTester.setSettings(new Settings(new PropertyDefinitions(GroovyPlugin.class))); - groovy = new Groovy(sensorContextTester.settings()); - sensor = new CodeNarcSensor(profile, new GroovyFileSystem(sensorContextTester.fileSystem())); + sensorContextTester.setSettings(settings); } @Test public void test_description() { + CodeNarcSensor sensor = + new CodeNarcSensor(null, new GroovyFileSystem(sensorContextTester.fileSystem())); DefaultSensorDescriptor defaultSensorDescriptor = new DefaultSensorDescriptor(); sensor.describe(defaultSensorDescriptor); assertThat(defaultSensorDescriptor.languages()).containsOnly(Groovy.KEY); @@ -81,32 +72,38 @@ public void test_description() { @Test public void should_parse() throws Exception { - ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "BooleanInstantiation"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "DuplicateImport"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "EmptyCatchBlock"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "EmptyElseBlock"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "EmptyFinallyBlock"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "EmptyForStatement"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "EmptyIfStatement"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "EmptyTryBlock"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "EmptyWhileStatement"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "ImportFromSamePackage"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "ReturnFromFinallyBlock"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "StringInstantiation"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "ThrowExceptionFromFinallyBlock"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "UnnecessaryGroovyImport"); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "UnusedImport"); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper() + .addRule("BooleanInstantiation") + .addRule("DuplicateImport") + .addRule("EmptyCatchBlock") + .addRule("EmptyElseBlock") + .addRule("EmptyFinallyBlock") + .addRule("EmptyForStatement") + .addRule("EmptyIfStatement") + .addRule("EmptyTryBlock") + .addRule("EmptyWhileStatement") + .addRule("ImportFromSamePackage") + .addRule("ReturnFromFinallyBlock") + .addRule("StringInstantiation") + .addRule("ThrowExceptionFromFinallyBlock") + .addRule("UnnecessaryGroovyImport") + .addRule("UnusedImport"); sensorContextTester.setActiveRules(activeRulesBuilder.build()); - File reportUpdated = getReportWithUpdatedSourceDir(); - sensorContextTester.settings().setProperty(GroovyPlugin.CODENARC_REPORT_PATHS, reportUpdated.getAbsolutePath()); + Path reportUpdated = getReportWithUpdatedSourceDir(); + settings.setProperty( + GroovyPlugin.CODENARC_REPORT_PATHS, reportUpdated.toAbsolutePath().toString()); addFileWithFakeContent("src/org/codenarc/sample/domain/SampleDomain.groovy"); addFileWithFakeContent("src/org/codenarc/sample/service/NewService.groovy"); addFileWithFakeContent("src/org/codenarc/sample/service/OtherService.groovy"); addFileWithFakeContent("src/org/codenarc/sample/service/SampleService.groovy"); + CodeNarcSensor sensor = + new CodeNarcSensor( + sensorContextTester.activeRules(), + new GroovyFileSystem(sensorContextTester.fileSystem())); sensor.execute(sensorContextTester); assertThat(sensorContextTester.allIssues()).hasSize(17); @@ -115,18 +112,23 @@ public void should_parse() throws Exception { @Test public void should_parse_but_not_add_issue_if_rule_not_found() throws Exception { - ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "UnknownRule"); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper().addRule("UnknownRule"); sensorContextTester.setActiveRules(activeRulesBuilder.build()); - File reportUpdated = getReportWithUpdatedSourceDir(); - sensorContextTester.settings().setProperty(GroovyPlugin.CODENARC_REPORT_PATHS, reportUpdated.getAbsolutePath()); + Path reportUpdated = getReportWithUpdatedSourceDir(); + settings.setProperty( + GroovyPlugin.CODENARC_REPORT_PATHS, reportUpdated.toAbsolutePath().toString()); addFileWithFakeContent("src/org/codenarc/sample/domain/SampleDomain.groovy"); addFileWithFakeContent("src/org/codenarc/sample/service/NewService.groovy"); addFileWithFakeContent("src/org/codenarc/sample/service/OtherService.groovy"); addFileWithFakeContent("src/org/codenarc/sample/service/SampleService.groovy"); + CodeNarcSensor sensor = + new CodeNarcSensor( + sensorContextTester.activeRules(), + new GroovyFileSystem(sensorContextTester.fileSystem())); sensor.execute(sensorContextTester); assertThat(sensorContextTester.allIssues()).isEmpty(); @@ -135,15 +137,20 @@ public void should_parse_but_not_add_issue_if_rule_not_found() throws Exception @Test public void should_parse_but_not_add_issue_if_inputFile_not_found() throws Exception { - ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); - activeRulesBuilder = activateFakeRule(activeRulesBuilder, "BooleanInstantiation"); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper().addRule("BooleanInstantiation"); sensorContextTester.setActiveRules(activeRulesBuilder.build()); - File reportUpdated = getReportWithUpdatedSourceDir(); - sensorContextTester.settings().setProperty(GroovyPlugin.CODENARC_REPORT_PATHS, reportUpdated.getAbsolutePath()); + Path reportUpdated = getReportWithUpdatedSourceDir(); + settings.setProperty( + GroovyPlugin.CODENARC_REPORT_PATHS, reportUpdated.toAbsolutePath().toString()); addFileWithFakeContent("src/org/codenarc/sample/domain/Unknown.groovy"); + CodeNarcSensor sensor = + new CodeNarcSensor( + sensorContextTester.activeRules(), + new GroovyFileSystem(sensorContextTester.fileSystem())); sensor.execute(sensorContextTester); assertThat(sensorContextTester.allIssues()).isEmpty(); @@ -154,14 +161,16 @@ public void should_run_code_narc() throws IOException { addFileWithContent("src/sample.groovy", "package source\nclass SourceFile1 {\n}"); - ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); - activeRulesBuilder = activateRule(activeRulesBuilder, "org.codenarc.rule.basic.EmptyClassRule", "EmptyClass"); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper() + .addRule("org.codenarc.rule.basic.EmptyClassRule") + .setInternalKey("EmptyClass"); sensorContextTester.setActiveRules(activeRulesBuilder.build()); - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRuleKey()).thenReturn("org.codenarc.rule.basic.EmptyClassRule"); - when(profile.getActiveRulesByRepository(CodeNarcRulesDefinition.REPOSITORY_KEY)).thenReturn(Arrays.asList(activeRule)); - + CodeNarcSensor sensor = + new CodeNarcSensor( + sensorContextTester.activeRules(), + new GroovyFileSystem(sensorContextTester.fileSystem())); sensor.execute(sensorContextTester); assertThat(sensorContextTester.allIssues()).hasSize(1); @@ -170,14 +179,18 @@ public void should_run_code_narc() throws IOException { @Test public void should_do_nothing_when_can_not_find_report_path() throws Exception { - sensorContextTester.settings().setProperty(GroovyPlugin.CODENARC_REPORT_PATHS, "../missing_file.xml"); + settings.setProperty(GroovyPlugin.CODENARC_REPORT_PATHS, "../missing_file.xml"); addFileWithFakeContent("src/org/codenarc/sample/domain/Unknown.groovy"); - ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); - activeRulesBuilder = activateRule(activeRulesBuilder, "org.codenarc.rule.basic.EmptyClassRule", "EmptyClass"); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper().addRule("org.codenarc.rule.basic.EmptyClassRule"); sensorContextTester.setActiveRules(activeRulesBuilder.build()); + CodeNarcSensor sensor = + new CodeNarcSensor( + sensorContextTester.activeRules(), + new GroovyFileSystem(sensorContextTester.fileSystem())); sensor.execute(sensorContextTester); assertThat(sensorContextTester.allIssues()).isEmpty(); @@ -189,51 +202,61 @@ public void should_run_code_narc_with_multiple_files() throws IOException { addFileWithContent("src/sample.groovy", "package source\nclass SourceFile1 {\n}"); addFileWithContent("src/foo/bar/qix/sample.groovy", "package source\nclass SourceFile1 {\n}"); - ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); - activeRulesBuilder = activateRule(activeRulesBuilder, "org.codenarc.rule.basic.EmptyClassRule", "EmptyClass"); + ActiveRulesBuilderWrapper activeRulesBuilder = + new ActiveRulesBuilderWrapper() + .addRule("org.codenarc.rule.basic.EmptyClassRule") + .setInternalKey("EmptyClass"); sensorContextTester.setActiveRules(activeRulesBuilder.build()); - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRuleKey()).thenReturn("org.codenarc.rule.basic.EmptyClassRule"); - when(profile.getActiveRulesByRepository(CodeNarcRulesDefinition.REPOSITORY_KEY)).thenReturn(Arrays.asList(activeRule)); - + CodeNarcSensor sensor = + new CodeNarcSensor( + sensorContextTester.activeRules(), + new GroovyFileSystem(sensorContextTester.fileSystem())); sensor.execute(sensorContextTester); assertThat(sensorContextTester.allIssues()).hasSize(2); } - private File getReportWithUpdatedSourceDir() throws IOException { - File report = FileUtils.toFile(getClass().getResource("parsing/sample.xml")); - File reportUpdated = temp.newFile(); - String newSourceDir = sensorContextTester.fileSystem().baseDir().toPath().resolve("src").toAbsolutePath().toString().replaceAll("\\\\", "/"); - FileUtils.write(reportUpdated, - FileUtils.readFileToString(report).replaceAll(Pattern.quote("[sourcedir]"), newSourceDir)); + private Path getReportWithUpdatedSourceDir() throws IOException { + Path reportUpdated = temp.newFile().toPath(); + String newSourceDir = + sensorContextTester + .fileSystem() + .baseDirPath() + .resolve("src") + .toAbsolutePath() + .toString() + .replaceAll("\\\\", "/"); + try (InputStream report = getClass().getResourceAsStream("parsing/sample.xml"); + Writer reportWriter = Files.newBufferedWriter(reportUpdated)) { + IOUtils.write( + IOUtils.toString(report, StandardCharsets.UTF_8) + .replaceAll(Pattern.quote("[sourcedir]"), newSourceDir), + reportWriter); + } return reportUpdated; } - private void addFileWithFakeContent(String path) throws UnsupportedEncodingException, IOException { + private void addFileWithFakeContent(String path) + throws UnsupportedEncodingException, IOException { File sampleFile = FileUtils.toFile(getClass().getResource("parsing/Sample.groovy")); - sensorContextTester.fileSystem().add(new DefaultInputFile(sensorContextTester.module().key(), path) - .setLanguage(Groovy.KEY) - .setType(Type.MAIN) - .initMetadata(new String(Files.readAllBytes(sampleFile.toPath()), "UTF-8"))); + sensorContextTester + .fileSystem() + .add( + TestInputFileBuilder.create(sensorContextTester.module().key(), path) + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .setContents(new String(Files.readAllBytes(sampleFile.toPath()), "UTF-8")) + .build()); } - private void addFileWithContent(String path, String content) throws UnsupportedEncodingException, IOException { - DefaultInputFile inputFile = new DefaultInputFile(sensorContextTester.module().key(), path) - .setLanguage(Groovy.KEY) - .setType(Type.MAIN) - .initMetadata(content); + private void addFileWithContent(String path, String content) { + InputFile inputFile = + TestInputFileBuilder.create(sensorContextTester.module().key(), path) + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .setContents(content) + .build(); sensorContextTester.fileSystem().add(inputFile); - FileUtils.write(inputFile.file(), content, StandardCharsets.UTF_8); - } - - private static ActiveRulesBuilder activateFakeRule(ActiveRulesBuilder activeRulesBuilder, String ruleKey) { - return activateRule(activeRulesBuilder, ruleKey, ruleKey); } - - private static ActiveRulesBuilder activateRule(ActiveRulesBuilder activeRulesBuilder, String ruleKey, String internalKey) { - return activeRulesBuilder.create(RuleKey.of(CodeNarcRulesDefinition.REPOSITORY_KEY, ruleKey)).setInternalKey(internalKey).activate(); - } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcXMLParserTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcXMLParserTest.java index 8555eb99..398e73a6 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcXMLParserTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/CodeNarcXMLParserTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,39 +19,42 @@ */ package org.sonar.plugins.groovy.codenarc; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.*; + import java.util.List; import org.apache.commons.io.FileUtils; import org.junit.Test; -import org.mockito.ArgumentMatchers; -import org.mockito.Matchers; import org.mockito.Mockito; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; import org.sonar.plugins.groovy.codenarc.CodeNarcXMLParser.CodeNarcViolation; -import static org.assertj.core.api.Assertions.assertThat; - public class CodeNarcXMLParserTest { @Test public void should_parse_report() throws Exception { FileSystem fileSystem = Mockito.mock(FileSystem.class); Mockito.when(fileSystem.predicates()).thenReturn(Mockito.mock(FilePredicates.class)); - Mockito.when(fileSystem.hasFiles(ArgumentMatchers.nullable(FilePredicate.class))).thenReturn(true); - List violations = CodeNarcXMLParser.parse(FileUtils.toFile(getClass().getResource("parsing/sample.xml")), fileSystem); + Mockito.when(fileSystem.hasFiles(nullable(FilePredicate.class))).thenReturn(true); + List violations = + CodeNarcXMLParser.parse( + FileUtils.toFile(getClass().getResource("parsing/sample.xml")), fileSystem); assertThat(violations.size()).isEqualTo(17); CodeNarcViolation violation = violations.get(0); assertThat(violation.getRuleName()).isEqualTo("EmptyElseBlock"); - assertThat(violation.getFilename()).isEqualTo("[sourcedir]/org/codenarc/sample/domain/SampleDomain.groovy"); + assertThat(violation.getFilename()) + .isEqualTo("[sourcedir]/org/codenarc/sample/domain/SampleDomain.groovy"); assertThat(violation.getLine()).isEqualTo(24); assertThat(violation.getMessage()).isEqualTo(""); violation = violations.get(1); assertThat(violation.getRuleName()).isEqualTo("EmptyIfStatement"); - assertThat(violation.getFilename()).isEqualTo("[sourcedir]/org/codenarc/sample/domain/SampleDomain.groovy"); + assertThat(violation.getFilename()) + .isEqualTo("[sourcedir]/org/codenarc/sample/domain/SampleDomain.groovy"); assertThat(violation.getLine()).isEqualTo(21); assertThat(violation.getMessage()).isEqualTo(""); } @@ -60,8 +63,11 @@ public void should_parse_report() throws Exception { public void should_not_fail_if_line_number_not_specified() throws Exception { FileSystem fileSystem = Mockito.mock(FileSystem.class); Mockito.when(fileSystem.predicates()).thenReturn(Mockito.mock(FilePredicates.class)); - Mockito.when(fileSystem.hasFiles(Matchers.any(FilePredicate.class))).thenReturn(true); - List violations = CodeNarcXMLParser.parse(FileUtils.toFile(getClass().getResource("parsing/line-number-not-specified.xml")), fileSystem); + Mockito.when(fileSystem.hasFiles(any(FilePredicate.class))).thenReturn(true); + List violations = + CodeNarcXMLParser.parse( + FileUtils.toFile(getClass().getResource("parsing/line-number-not-specified.xml")), + fileSystem); assertThat(violations.size()).isEqualTo(1); @@ -69,7 +75,7 @@ public void should_not_fail_if_line_number_not_specified() throws Exception { assertThat(violation.getRuleName()).isEqualTo("CyclomaticComplexity"); assertThat(violation.getFilename()).isEqualTo("org/example/Example.groovy"); assertThat(violation.getLine()).isNull(); - assertThat(violation.getMessage()).isEqualTo("The cyclomatic complexity for class [org.example.Example] is [27.0]"); + assertThat(violation.getMessage()) + .isEqualTo("The cyclomatic complexity for class [org.example.Example] is [27.0]"); } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/SonarWayProfileTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/SonarWayProfileTest.java index e88c01d7..e643d4c4 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/SonarWayProfileTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/codenarc/SonarWayProfileTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,6 +19,11 @@ */ package org.sonar.plugins.groovy.codenarc; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.util.HashMap; import java.util.Map; import org.junit.Test; @@ -34,45 +39,49 @@ import org.sonar.api.utils.ValidationMessages; import org.sonar.plugins.groovy.foundation.Groovy; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class SonarWayProfileTest { @Test public void shouldCreateProfile() { - ProfileDefinition profileDefinition = new SonarWayProfile(new XMLProfileParser(newRuleFinder())); + ProfileDefinition profileDefinition = + new SonarWayProfile(new XMLProfileParser(newRuleFinder())); ValidationMessages messages = ValidationMessages.create(); RulesProfile profile = profileDefinition.createProfile(messages); assertThat(profile.getName()).isEqualTo("Sonar way"); assertThat(profile.getLanguage()).isEqualTo(Groovy.KEY); - assertThat(profile.getActiveRules()).hasSize(59); + assertThat(profile.getActiveRules()).hasSize(58); assertThat(messages.hasErrors()).isFalse(); CodeNarcRulesDefinition definition = new CodeNarcRulesDefinition(); RulesDefinition.Context context = new RulesDefinition.Context(); definition.define(context); - RulesDefinition.Repository repository = context.repository(CodeNarcRulesDefinition.REPOSITORY_KEY); + RulesDefinition.Repository repository = + context.repository(CodeNarcRulesDefinition.REPOSITORY_KEY); Map rules = new HashMap<>(); for (RulesDefinition.Rule rule : repository.rules()) { rules.put(rule.key(), rule); } for (ActiveRule activeRule : profile.getActiveRules()) { - assertThat(rules.containsKey(activeRule.getConfigKey())).as("No such rule: " + activeRule.getConfigKey()).isTrue(); + assertThat(rules.containsKey(activeRule.getConfigKey())) + .as("No such rule: " + activeRule.getConfigKey()) + .isTrue(); } } private RuleFinder newRuleFinder() { RuleFinder ruleFinder = mock(RuleFinder.class); - when(ruleFinder.findByKey(anyString(), anyString())).thenAnswer(new Answer() { - public Rule answer(InvocationOnMock iom) throws Throwable { - return Rule.create((String) iom.getArguments()[0], (String) iom.getArguments()[1], (String) iom.getArguments()[1]); - } - }); + when(ruleFinder.findByKey(anyString(), anyString())) + .thenAnswer( + new Answer() { + public Rule answer(InvocationOnMock iom) throws Throwable { + return Rule.create( + (String) iom.getArguments()[0], + (String) iom.getArguments()[1], + (String) iom.getArguments()[1]); + } + }); return ruleFinder; } } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyFileSystemTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyFileSystemTest.java index ffd82e83..a3ac2686 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyFileSystemTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyFileSystemTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,15 +19,14 @@ */ package org.sonar.plugins.groovy.foundation; +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Paths; import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputFile; - -import java.io.File; - -import static org.assertj.core.api.Assertions.assertThat; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; public class GroovyFileSystemTest { @@ -36,7 +35,7 @@ public class GroovyFileSystemTest { @Before public void setUp() { - fileSystem = new DefaultFileSystem(new File(".")); + fileSystem = new DefaultFileSystem(Paths.get(".")); groovyFileSystem = new GroovyFileSystem(fileSystem); } @@ -44,35 +43,32 @@ public void setUp() { public void isEnabled() { assertThat(groovyFileSystem.hasGroovyFiles()).isFalse(); - fileSystem.add(new DefaultInputFile("", "fake.file")); + fileSystem.add(TestInputFileBuilder.create("", "fake.file").build()); assertThat(groovyFileSystem.hasGroovyFiles()).isFalse(); - fileSystem.add(new DefaultInputFile("", "fake.groovy").setLanguage(Groovy.KEY)); + fileSystem.add(TestInputFileBuilder.create("", "fake.groovy").setLanguage(Groovy.KEY).build()); assertThat(groovyFileSystem.hasGroovyFiles()).isTrue(); } - @Test - public void getSourceFile() { - assertThat(groovyFileSystem.sourceFiles()).isEmpty(); - - fileSystem.add(new DefaultInputFile("", "fake.file")); - assertThat(groovyFileSystem.sourceFiles()).isEmpty(); - - fileSystem.add(new DefaultInputFile("", "fake.groovy").setLanguage(Groovy.KEY)); - assertThat(groovyFileSystem.sourceFiles()).hasSize(1); - } - @Test public void inputFileFromRelativePath() { assertThat(groovyFileSystem.sourceInputFileFromRelativePath(null)).isNull(); - fileSystem.add(new DefaultInputFile("", "fake1.file")); + fileSystem.add(TestInputFileBuilder.create("", "fake1.file").build()); assertThat(groovyFileSystem.sourceInputFileFromRelativePath("fake1.file")).isNull(); - fileSystem.add(new DefaultInputFile("", "fake2.file").setType(Type.MAIN).setLanguage(Groovy.KEY)); + fileSystem.add( + TestInputFileBuilder.create("", "fake2.file") + .setType(Type.MAIN) + .setLanguage(Groovy.KEY) + .build()); assertThat(groovyFileSystem.sourceInputFileFromRelativePath("fake2.file")).isNotNull(); - fileSystem.add(new DefaultInputFile("", "org/sample/foo/fake3.file").setType(Type.MAIN).setLanguage(Groovy.KEY)); + fileSystem.add( + TestInputFileBuilder.create("", "org/sample/foo/fake3.file") + .setType(Type.MAIN) + .setLanguage(Groovy.KEY) + .build()); assertThat(groovyFileSystem.sourceInputFileFromRelativePath("foo/fake3.file")).isNotNull(); } } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyHighlighterAndTokenizerTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyHighlighterAndTokenizerTest.java index 3d660254..2994a84b 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyHighlighterAndTokenizerTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyHighlighterAndTokenizerTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,18 +19,19 @@ */ package org.sonar.plugins.groovy.foundation; +import static org.assertj.core.api.Assertions.assertThat; + import java.io.File; import java.nio.file.Files; import org.junit.Test; import org.mockito.Mockito; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.highlighting.TypeOfText; import org.sonar.api.batch.sensor.internal.SensorContextTester; import org.sonar.plugins.groovy.TestUtils; -import static org.assertj.core.api.Assertions.assertThat; - public class GroovyHighlighterAndTokenizerTest { @Test @@ -38,10 +39,12 @@ public void should_highlight_keywords() throws Exception { File file = TestUtils.getResource("/org/sonar/plugins/groovy/foundation/Greet.groovy"); SensorContextTester context = SensorContextTester.create(file.getParentFile()); - DefaultInputFile inputFile = new DefaultInputFile("", "Greet.groovy") - .setLanguage(Groovy.KEY) - .setType(Type.MAIN) - .initMetadata(new String(Files.readAllBytes(file.toPath()), "UTF-8")); + InputFile inputFile = + TestInputFileBuilder.create("", file.getParentFile(), file) + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .setContents(new String(Files.readAllBytes(file.toPath()), "UTF-8")) + .build(); context.fileSystem().add(inputFile); GroovyHighlighterAndTokenizer highlighter = new GroovyHighlighterAndTokenizer(inputFile); @@ -53,14 +56,21 @@ public void should_highlight_keywords() throws Exception { assertThat(context.highlightingTypeAt(":Greet.groovy", 4, 2)).containsOnly(TypeOfText.KEYWORD); assertThat(context.highlightingTypeAt(":Greet.groovy", 4, 25)).containsOnly(TypeOfText.STRING); assertThat(context.highlightingTypeAt(":Greet.groovy", 4, 32)).containsOnly(TypeOfText.STRING); - assertThat(context.highlightingTypeAt(":Greet.groovy", 7, 0)).containsOnly(TypeOfText.STRUCTURED_COMMENT); - assertThat(context.highlightingTypeAt(":Greet.groovy", 8, 1)).containsOnly(TypeOfText.STRUCTURED_COMMENT); - assertThat(context.highlightingTypeAt(":Greet.groovy", 9, 1)).containsOnly(TypeOfText.STRUCTURED_COMMENT); - assertThat(context.highlightingTypeAt(":Greet.groovy", 10, 0)).containsOnly(TypeOfText.ANNOTATION); - assertThat(context.highlightingTypeAt(":Greet.groovy", 10, 21)).containsOnly(TypeOfText.ANNOTATION); + assertThat(context.highlightingTypeAt(":Greet.groovy", 7, 0)) + .containsOnly(TypeOfText.STRUCTURED_COMMENT); + assertThat(context.highlightingTypeAt(":Greet.groovy", 8, 1)) + .containsOnly(TypeOfText.STRUCTURED_COMMENT); + assertThat(context.highlightingTypeAt(":Greet.groovy", 9, 1)) + .containsOnly(TypeOfText.STRUCTURED_COMMENT); + assertThat(context.highlightingTypeAt(":Greet.groovy", 10, 0)) + .containsOnly(TypeOfText.ANNOTATION); + assertThat(context.highlightingTypeAt(":Greet.groovy", 10, 21)) + .containsOnly(TypeOfText.ANNOTATION); assertThat(context.highlightingTypeAt(":Greet.groovy", 12, 2)).containsOnly(TypeOfText.KEYWORD); - assertThat(context.highlightingTypeAt(":Greet.groovy", 12, 13)).containsOnly(TypeOfText.CONSTANT); - assertThat(context.highlightingTypeAt(":Greet.groovy", 12, 17)).containsOnly(TypeOfText.COMMENT); + assertThat(context.highlightingTypeAt(":Greet.groovy", 12, 13)) + .containsOnly(TypeOfText.CONSTANT); + assertThat(context.highlightingTypeAt(":Greet.groovy", 12, 17)) + .containsOnly(TypeOfText.COMMENT); Mockito.verify(context, Mockito.times(1)).newHighlighting(); } @@ -69,26 +79,31 @@ public void should_tokenize_for_cpd() throws Exception { File file = TestUtils.getResource("/org/sonar/plugins/groovy/foundation/Greet.groovy"); SensorContextTester context = SensorContextTester.create(file.getParentFile()); - DefaultInputFile inputFile = new DefaultInputFile("", "Greet.groovy") - .setLanguage(Groovy.KEY) - .setType(Type.MAIN) - .initMetadata(new String(Files.readAllBytes(file.toPath()), "UTF-8")); + InputFile inputFile = + TestInputFileBuilder.create("", file.getParentFile(), file) + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .initMetadata(new String(Files.readAllBytes(file.toPath()), "UTF-8")) + .build(); context.fileSystem().add(inputFile); GroovyHighlighterAndTokenizer highlighter = new GroovyHighlighterAndTokenizer(inputFile); context = Mockito.spy(context); highlighter.processFile(context); - assertThat(context.cpdTokens(":Greet.groovy")).extracting("value").containsExactly("classGreet{", - "defname", - "Greet(who){name=who}", - "defsalute(){printlnLITERALnameLITERALnameLITERAL}", - "}", - "/** * Javadoc style */", - "@groovy.beans.Bindable", - "classCool{", - "doublex=1.4// Comment", - "}"); + assertThat(context.cpdTokens(":Greet.groovy")) + .extracting("value") + .containsExactly( + "classGreet{", + "defname", + "Greet(who){name=who}", + "defsalute(){printlnLITERALnameLITERALnameLITERAL}", + "}", + "/** * Javadoc style */", + "@groovy.beans.Bindable", + "classCool{", + "doublex=1.4// Comment", + "}"); Mockito.verify(context, Mockito.times(1)).newCpdTokens(); } @@ -97,9 +112,11 @@ public void should_highlight_nothing_if_file_is_missing() throws Exception { File file = TestUtils.getResource("/org/sonar/plugins/groovy/foundation/Greet.groovy"); SensorContextTester context = SensorContextTester.create(file.getParentFile()); - DefaultInputFile inputFile = new DefaultInputFile("", "Greet-fake.groovy") - .setLanguage(Groovy.KEY) - .setType(Type.MAIN); + InputFile inputFile = + TestInputFileBuilder.create("", "Greet-fake.groovy") + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .build(); context.fileSystem().add(inputFile); GroovyHighlighterAndTokenizer highlighter = new GroovyHighlighterAndTokenizer(inputFile); @@ -115,10 +132,12 @@ public void should_highlight_only_partially_if_file_can_not_be_lexed() throws Ex File file = TestUtils.getResource("/org/sonar/plugins/groovy/foundation/Error.groovy"); SensorContextTester context = SensorContextTester.create(file.getParentFile()); - DefaultInputFile inputFile = new DefaultInputFile("", "Error.groovy") - .setLanguage(Groovy.KEY) - .setType(Type.MAIN) - .initMetadata(new String(Files.readAllBytes(file.toPath()), "UTF-8")); + InputFile inputFile = + TestInputFileBuilder.create("", file.getParentFile(), file) + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .initMetadata(new String(Files.readAllBytes(file.toPath()), "UTF-8")) + .build(); context.fileSystem().add(inputFile); GroovyHighlighterAndTokenizer highlighter = new GroovyHighlighterAndTokenizer(inputFile); @@ -131,5 +150,4 @@ public void should_highlight_only_partially_if_file_can_not_be_lexed() throws Ex assertThat(context.highlightingTypeAt(":Error.groovy", 3, 2)).isEmpty(); Mockito.verify(context, Mockito.times(1)).newHighlighting(); } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyTest.java index a1c82dca..6c38dee9 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/foundation/GroovyTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,7 +20,7 @@ package org.sonar.plugins.groovy.foundation; import org.junit.Test; -import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; import org.sonar.plugins.groovy.GroovyPlugin; import static org.assertj.core.api.Assertions.assertThat; @@ -29,7 +29,7 @@ public class GroovyTest { @Test public void test() { - Settings settings = new Settings(); + MapSettings settings = new MapSettings(); Groovy language = new Groovy(settings); assertThat(language.getKey()).isEqualTo("grvy"); assertThat(language.getName()).isEqualTo("Groovy"); diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/ExecutionDataVisitorTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/ExecutionDataVisitorTest.java index b3d381f0..b331f1e7 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/ExecutionDataVisitorTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/ExecutionDataVisitorTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoConfigurationTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoConfigurationTest.java index 3c017460..381abb10 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoConfigurationTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoConfigurationTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,42 +19,41 @@ */ package org.sonar.plugins.groovy.jacoco; +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Paths; import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.config.PropertyDefinitions; -import org.sonar.api.config.Settings; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.plugins.groovy.TestUtils; import org.sonar.plugins.groovy.foundation.Groovy; -import java.io.File; - -import static org.assertj.core.api.Assertions.assertThat; - public class JaCoCoConfigurationTest { - private Settings settings; + private MapSettings settings = TestUtils.jacocoDefaultSettings(); private JaCoCoConfiguration jacocoSettings; private DefaultFileSystem fileSystem; @Before public void setUp() { - settings = new Settings(new PropertyDefinitions().addComponents(JaCoCoConfiguration.getPropertyDefinitions())); - fileSystem = new DefaultFileSystem(new File(".")); + fileSystem = new DefaultFileSystem(Paths.get(".")); jacocoSettings = new JaCoCoConfiguration(settings, fileSystem); } @Test - public void shouldExecuteOnProject() throws Exception { + public void shouldExecuteOnProject() { // no files assertThat(jacocoSettings.shouldExecuteOnProject(true)).isFalse(); assertThat(jacocoSettings.shouldExecuteOnProject(false)).isFalse(); - fileSystem.add(new DefaultInputFile("", "src/foo/bar.java").setLanguage("java")); + fileSystem.add(TestInputFileBuilder.create("", "src/foo/bar.java").setLanguage("java").build()); assertThat(jacocoSettings.shouldExecuteOnProject(true)).isFalse(); assertThat(jacocoSettings.shouldExecuteOnProject(false)).isFalse(); - fileSystem.add(new DefaultInputFile("", "src/foo/bar.groovy").setLanguage(Groovy.KEY)); + fileSystem.add( + TestInputFileBuilder.create("", "src/foo/bar.groovy").setLanguage(Groovy.KEY).build()); assertThat(jacocoSettings.shouldExecuteOnProject(true)).isTrue(); assertThat(jacocoSettings.shouldExecuteOnProject(false)).isFalse(); diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoExtensionsTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoExtensionsTest.java index 1aefffb8..15258e7e 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoExtensionsTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoExtensionsTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,15 +19,14 @@ */ package org.sonar.plugins.groovy.jacoco; -import org.junit.Test; - import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; + public class JaCoCoExtensionsTest { @Test public void testExtensions() { - assertThat(JaCoCoExtensions.getExtensions().size()).isEqualTo(7); + assertThat(JaCoCoExtensions.getExtensions().size()).isEqualTo(5); } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoItSensorTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoItSensorTest.java index f908b024..eec97405 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoItSensorTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoItSensorTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,114 +19,75 @@ */ package org.sonar.plugins.groovy.jacoco; -import java.io.File; -import org.apache.commons.io.FileUtils; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; -import org.mockito.ArgumentMatchers; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.coverage.CoverageType; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; -import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.plugins.groovy.GroovyPlugin; import org.sonar.plugins.groovy.TestUtils; import org.sonar.plugins.groovy.foundation.Groovy; import org.sonar.plugins.groovy.foundation.GroovyFileSystem; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class JaCoCoItSensorTest { - private File jacocoExecutionData; - private DefaultInputFile inputFile; - private JaCoCoConfiguration configuration; - private PathResolver pathResolver; - private JaCoCoItSensor sensor; + @Rule public final TemporaryFolder tmpDir = new TemporaryFolder(); - @Before - public void setUp() throws Exception { - File outputDir = TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/JaCoCoItSensorTests/"); - jacocoExecutionData = new File(outputDir, "jacoco-it.exec"); + private MapSettings settings = TestUtils.jacocoDefaultSettings(); + private JaCoCoSensor sensor; - FileUtils.copyFile(TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/Hello.class.toCopy"), - new File(jacocoExecutionData.getParentFile(), "Hello.class")); - FileUtils.copyFile(TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/Hello$InnerClass.class.toCopy"), - new File(jacocoExecutionData.getParentFile(), "Hello$InnerClass.class")); + @Before + public void setUp() throws IOException { + Path outputDir = tmpDir.newFolder().toPath(); + Files.copy( + TestUtils.getResource(getClass(), "../JaCoCoSensor_0_7_5/jacoco-ut.exec"), + outputDir.resolve("jacoco-it.exec")); + Files.copy( + TestUtils.getResource(getClass(), "../Hello.class.toCopy"), + outputDir.resolve("Hello.class")); + Files.copy( + TestUtils.getResource(getClass(), "../Hello$InnerClass.class.toCopy"), + outputDir.resolve("Hello$InnerClass.class")); - Settings settings = new Settings(); settings.setProperty(GroovyPlugin.SONAR_GROOVY_BINARIES, "."); - - configuration = mock(JaCoCoConfiguration.class); - when(configuration.shouldExecuteOnProject(true)).thenReturn(true); - when(configuration.shouldExecuteOnProject(false)).thenReturn(false); - when(configuration.getItReportPath()).thenReturn(jacocoExecutionData.getPath()); - - DefaultFileSystem fileSystem = new DefaultFileSystem(jacocoExecutionData.getParentFile()); - inputFile = new DefaultInputFile("", "example/Hello.groovy") - .setLanguage(Groovy.KEY) - .setType(Type.MAIN); - inputFile.setLines(50); + settings.setProperty(JaCoCoConfiguration.IT_REPORT_PATH_PROPERTY, "jacoco-it.exec"); + + DefaultFileSystem fileSystem = new DefaultFileSystem(outputDir); + InputFile inputFile = + TestInputFileBuilder.create("", "example/Hello.groovy") + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .setLines(50) + .build(); fileSystem.add(inputFile); - - pathResolver = mock(PathResolver.class); - sensor = new JaCoCoItSensor(configuration, new GroovyFileSystem(fileSystem), pathResolver, settings); + JaCoCoConfiguration configuration = new JaCoCoConfiguration(settings, fileSystem); + + sensor = + new JaCoCoSensor( + configuration, + new GroovyFileSystem(fileSystem), + new PathResolver(), + settings, + mock(AnalysisWarnings.class)); } @Test - public void test_description() { + public void testDescription() { DefaultSensorDescriptor defaultSensorDescriptor = new DefaultSensorDescriptor(); sensor.describe(defaultSensorDescriptor); assertThat(defaultSensorDescriptor.languages()).containsOnly(Groovy.KEY); } - - @Test - public void should_Execute_On_Project_only_if_exec_exists() { - when(configuration.getItReportPath()).thenReturn("it.exec"); - when(pathResolver.relativeFile(any(File.class), eq("it.exec"))).thenReturn(jacocoExecutionData); - assertThat(sensor.shouldExecuteOnProject()).isTrue(); - - when(pathResolver.relativeFile(any(File.class), eq("it.exec"))).thenReturn(jacocoExecutionData.getParentFile()); - assertThat(sensor.shouldExecuteOnProject()).isFalse(); - - File outputDir = TestUtils.getResource(JaCoCoSensorTest.class, "."); - File fakeExecFile = new File(outputDir, "it.not.found.exec"); - when(pathResolver.relativeFile(any(File.class), eq("it.exec"))).thenReturn(fakeExecFile); - assertThat(sensor.shouldExecuteOnProject()).isFalse(); - - when(pathResolver.relativeFile(any(File.class), eq("it.exec"))).thenReturn(fakeExecFile); - when(configuration.shouldExecuteOnProject(false)).thenReturn(true); - assertThat(sensor.shouldExecuteOnProject()).isTrue(); - } - - @Test - public void test_read_execution_data() { - when(pathResolver.relativeFile(any(File.class), ArgumentMatchers.endsWith(".exec"))).thenReturn(jacocoExecutionData); - - SensorContextTester context = SensorContextTester.create(new File("")); - sensor.execute(context); - - int[] oneHitlines = {9, 10, 25}; - int[] zeroHitlines = {14, 15, 17, 21, 29, 30, 32, 33, 38, 42, 47}; - int[] conditionLines = {14, 29, 30}; - - for (int zeroHitline : zeroHitlines) { - assertThat(context.lineHits(":example/Hello.groovy", CoverageType.IT, zeroHitline)).isEqualTo(0); - } - for (int oneHitline : oneHitlines) { - assertThat(context.lineHits(":example/Hello.groovy", CoverageType.IT, oneHitline)).isEqualTo(1); - } - for (int conditionLine : conditionLines) { - assertThat(context.conditions(":example/Hello.groovy", CoverageType.IT, conditionLine)).isEqualTo(2); - assertThat(context.coveredConditions(":example/Hello.groovy", CoverageType.IT, conditionLine)).isEqualTo(0); - } - } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTest.java index 181962f1..cf8c4119 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,180 +19,141 @@ */ package org.sonar.plugins.groovy.jacoco; -import java.io.File; -import org.apache.commons.io.FileUtils; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.ArgumentMatchers.anyString; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.coverage.CoverageType; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.plugins.groovy.GroovyPlugin; import org.sonar.plugins.groovy.TestUtils; import org.sonar.plugins.groovy.foundation.Groovy; import org.sonar.plugins.groovy.foundation.GroovyFileSystem; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class JaCoCoOverallSensorTest { - private JaCoCoConfiguration configuration; - private PathResolver pathResolver; - private JaCoCoOverallSensor sensor; - private File jacocoUTData; - private File jacocoITData; - private File outputDir; - private DefaultInputFile inputFile; - private Settings settings; + @Rule public final TemporaryFolder tmpDir = new TemporaryFolder(); + @Rule + public final MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); + + private JaCoCoSensor sensor; + private InputFile inputFile; + private MapSettings settings = new MapSettings(); private SensorContextTester context; + @Mock + private AnalysisWarnings analysisWarnings; @Before - public void before() throws Exception { - outputDir = TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTests/"); - jacocoUTData = new File(outputDir, "jacoco-ut.exec"); - jacocoITData = new File(outputDir, "jacoco-it.exec"); + public void before() throws IOException { + Path outputDir = tmpDir.newFolder().toPath(); + + Files.copy( + TestUtils.getResource(getClass(), "../JaCoCoSensor_0_7_5/jacoco-ut.exec"), + outputDir.resolve("jacoco-ut.exec")); + Files.copy( + TestUtils.getResource(getClass(), "../JaCoCoSensor_0_7_5/jacoco-ut.exec"), + outputDir.resolve("jacoco-it.exec")); + Files.copy( + TestUtils.getResource(getClass(), "../Hello.class.toCopy"), + outputDir.resolve("Hello.class")); + Files.copy( + TestUtils.getResource(getClass(), "../Hello$InnerClass.class.toCopy"), + outputDir.resolve("Hello$InnerClass.class")); - FileUtils.copyFile(TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/Hello.class.toCopy"), - new File(jacocoUTData.getParentFile(), "Hello.class")); - FileUtils.copyFile(TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/Hello$InnerClass.class.toCopy"), - new File(jacocoUTData.getParentFile(), "Hello$InnerClass.class")); - - settings = new Settings(); settings.setProperty(GroovyPlugin.SONAR_GROOVY_BINARIES, "."); - context = SensorContextTester.create(jacocoUTData.getParentFile()); + context = SensorContextTester.create(outputDir); + context.fileSystem().setWorkDir(tmpDir.newFolder().toPath()); + context.setSettings(settings); - context.fileSystem().setWorkDir(jacocoUTData.getParentFile()); - inputFile = new DefaultInputFile("", "example/Hello.groovy") - .setLanguage(Groovy.KEY) - .setType(Type.MAIN); - inputFile.setLines(50); + inputFile = + TestInputFileBuilder.create("", "example/Hello.groovy") + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .setLines(50) + .build(); context.fileSystem().add(inputFile); - configuration = mock(JaCoCoConfiguration.class); - when(configuration.shouldExecuteOnProject(true)).thenReturn(true); - when(configuration.shouldExecuteOnProject(false)).thenReturn(false); - pathResolver = mock(PathResolver.class); - sensor = new JaCoCoOverallSensor(configuration, new GroovyFileSystem(context.fileSystem()), pathResolver, settings); + JaCoCoConfiguration configuration = new JaCoCoConfiguration(settings, context.fileSystem()); + sensor = + new JaCoCoSensor( + configuration, + new GroovyFileSystem(context.fileSystem()), + new PathResolver(), + settings, + analysisWarnings); } @Test - public void test_description() { + public void testDescription() { DefaultSensorDescriptor defaultSensorDescriptor = new DefaultSensorDescriptor(); sensor.describe(defaultSensorDescriptor); assertThat(defaultSensorDescriptor.languages()).containsOnly(Groovy.KEY); } - @Test - public void should_Execute_On_Project_only_if_at_least_one_exec_exists() { - when(configuration.getItReportPath()).thenReturn("it.exec"); - when(configuration.getReportPath()).thenReturn("ut.exec"); - - when(pathResolver.relativeFile(any(File.class), eq("it.exec"))).thenReturn(jacocoITData); - when(pathResolver.relativeFile(any(File.class), eq("ut.exec"))).thenReturn(fakeExecFile()); - assertThat(sensor.shouldExecuteOnProject()).isTrue(); - - when(pathResolver.relativeFile(any(File.class), eq("it.exec"))).thenReturn(fakeExecFile()); - when(pathResolver.relativeFile(any(File.class), eq("ut.exec"))).thenReturn(jacocoUTData); - assertThat(sensor.shouldExecuteOnProject()).isTrue(); - - when(pathResolver.relativeFile(any(File.class), eq("it.exec"))).thenReturn(fakeExecFile()); - when(pathResolver.relativeFile(any(File.class), eq("ut.exec"))).thenReturn(fakeExecFile()); - assertThat(sensor.shouldExecuteOnProject()).isFalse(); - - when(configuration.shouldExecuteOnProject(false)).thenReturn(true); - assertThat(sensor.shouldExecuteOnProject()).isTrue(); - } @Test - public void test_read_execution_data_with_IT_and_UT() { - setMocks(true, true); - + public void shouldExecuteOnProjectWithItExisting() { + configReports(false, true); sensor.execute(context); - - int[] oneHitlines = {9, 10, 14, 15, 17, 21, 25, 29, 32, 33, 42, 47}; - int[] zeroHitlines = {30, 38}; - int[] conditionLines = {14, 29, 30}; - int[] coveredConditions = {2, 1, 0}; - - verifyOverallMetrics(context, zeroHitlines, oneHitlines, conditionLines, coveredConditions); + assertThat(context.coveredConditions(inputFile.key(), 14), is(equalTo(2))); + verify(analysisWarnings).addUnique(anyString()); } - private void verifyOverallMetrics(SensorContextTester context, int[] zeroHitlines, int[] oneHitlines, int[] conditionLines, int[] coveredConditions) { - for (int zeroHitline : zeroHitlines) { - assertThat(context.lineHits(inputFile.key(), CoverageType.OVERALL, zeroHitline)).isEqualTo(0); - } - for (int oneHitline : oneHitlines) { - assertThat(context.lineHits(inputFile.key(), CoverageType.OVERALL, oneHitline)).isEqualTo(1); - } - - for (int i = 0; i < conditionLines.length; i++) { - int line = conditionLines[i]; - assertThat(context.conditions(inputFile.key(), CoverageType.OVERALL, line)).isEqualTo(2); - assertThat(context.coveredConditions(inputFile.key(), CoverageType.OVERALL, line)).isEqualTo(coveredConditions[i]); - } - } @Test - public void test_read_execution_data_with_IT_and_UT_and_binaryDirs_being_absolute() { - setMocks(true, true); - settings.setProperty(GroovyPlugin.SONAR_GROOVY_BINARIES, jacocoUTData.getParentFile().getAbsolutePath()); - + public void shouldExecuteOnProjectWithUtExisting() { + configReports(true, false); sensor.execute(context); - - int[] oneHitlines = {9, 10, 14, 15, 17, 21, 25, 29, 32, 33, 42, 47}; - int[] zeroHitlines = {30, 38}; - int[] conditionLines = {14, 29, 30}; - int[] coveredConditions = {2, 1, 0}; - - verifyOverallMetrics(context, zeroHitlines, oneHitlines, conditionLines, coveredConditions); + assertThat(context.coveredConditions(inputFile.key(), 14), is(equalTo(2))); + verify(analysisWarnings).addUnique(anyString()); } @Test - public void test_read_execution_data_with_only_UT() { - setMocks(true, false); - + public void shouldNotExecuteOnProjectWithoutExecutionData() { + configReports(false, false); sensor.execute(context); - - int[] oneHitlines = {9, 10, 14, 15, 17, 21, /* 25 not covered in UT */ 29, 32, 33, 42, 47}; - int[] zeroHitlines = {25, 30, 38}; - int[] conditionLines = {14, 29, 30}; - int[] coveredConditions = {2, 1, 0}; - - verifyOverallMetrics(context, zeroHitlines, oneHitlines, conditionLines, coveredConditions); + assertNull(context.coveredConditions(inputFile.key(), 14)); + verify(analysisWarnings, never()).addUnique(anyString()); } + @Test - public void test_read_execution_data_with_only_IT() { - setMocks(false, true); + public void shouldNotExecuteIfJaCoCoXmlConfigured() { + settings.setProperty(JaCoCoSensor.JACOCO_XML_PROPERTY, "report.xml"); + configReports(true, true); sensor.execute(context); - - int[] oneHitlines = {9, 10, 25}; - int[] zeroHitlines = {14, 15, 17, 21, 29, 30, 32, 33, 38, 42, 47}; - int[] conditionLines = {14, 29, 30}; - int[] coveredConditions = {0, 0, 0}; - - verifyOverallMetrics(context, zeroHitlines, oneHitlines, conditionLines, coveredConditions); - } - - private void setMocks(boolean utReport, boolean itReport) { - when(configuration.getReportPath()).thenReturn("ut.exec"); - when(pathResolver.relativeFile(any(File.class), eq("ut.exec"))).thenReturn(utReport ? jacocoUTData : fakeExecFile()); - when(configuration.getItReportPath()).thenReturn("it.exec"); - when(pathResolver.relativeFile(any(File.class), eq("it.exec"))).thenReturn(itReport ? jacocoITData : fakeExecFile()); - File jacocoOverallData = new File(outputDir, "jacoco-overall.exec"); - when(pathResolver.relativeFile(any(File.class), eq(jacocoOverallData.getAbsolutePath()))).thenReturn(jacocoOverallData); + assertNull(context.coveredConditions(inputFile.key(), 14)); + verify(analysisWarnings, never()).addUnique(anyString()); } - private File fakeExecFile() { - return new File("fake.exec"); + private void configReports(boolean utReport, boolean itReport) { + settings.setProperty( + JaCoCoConfiguration.REPORT_PATH_PROPERTY, utReport ? "jacoco-ut.exec" : "notexist-ut.exec"); + settings.setProperty( + JaCoCoConfiguration.IT_REPORT_PATH_PROPERTY, + itReport ? "jacoco-it.exec" : "notexist-it.exec"); } } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportMergerTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportMergerTest.java index c6ae3909..ccc051ae 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportMergerTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportMergerTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,45 +19,46 @@ */ package org.sonar.plugins.groovy.jacoco; +import java.io.File; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.plugins.groovy.TestUtils; -import java.io.File; - public class JaCoCoReportMergerTest { - @Rule - public TemporaryFolder testFolder = new TemporaryFolder(); + @Rule public TemporaryFolder testFolder = new TemporaryFolder(); - @Rule - public ExpectedException exception = ExpectedException.none(); + @Rule public ExpectedException exception = ExpectedException.none(); @Test - public void merge_different_format_should_fail() { - exception.expect(IllegalStateException.class); - exception.expectMessage("You are trying to merge two different JaCoCo binary formats. Please use only one version of JaCoCo."); + public void mergeDifferentFormatShouldFail1() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage(JaCoCoReportReader.INCOMPATIBLE_JACOCO_ERROR); merge("jacoco-0.7.5.exec", "jacoco-it-0.7.4.exec"); } @Test - public void merge_different_format_should_fail_() { - exception.expect(IllegalStateException.class); - exception.expectMessage("You are trying to merge two different JaCoCo binary formats. Please use only one version of JaCoCo."); + public void mergeDifferentFormatShouldFail2() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage(JaCoCoReportReader.INCOMPATIBLE_JACOCO_ERROR); merge("jacoco-0.7.4.exec", "jacoco-it-0.7.5.exec"); } @Test - public void merge_same_format_should_not_fail() throws Exception { + public void merge_same_format_should_not_fail() { merge("jacoco-0.7.5.exec", "jacoco-it-0.7.5.exec"); } private void merge(String file1, String file2) { - File current = TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/JaCoCo_incompatible_merge/" + file1); - File previous = TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/JaCoCo_incompatible_merge/" + file2); - JaCoCoReportMerger.mergeReports(new File(testFolder.getRoot(), "dummy"), current, previous); + File current = + TestUtils.getResource( + "/org/sonar/plugins/groovy/jacoco/JaCoCo_incompatible_merge/" + file1); + File previous = + TestUtils.getResource( + "/org/sonar/plugins/groovy/jacoco/JaCoCo_incompatible_merge/" + file2); + JaCoCoReportMerger.mergeReports( + testFolder.getRoot().toPath().resolve("dummy"), current, previous); } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportReaderTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportReaderTest.java index 495e8dc1..d492469e 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportReaderTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoReportReaderTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -56,19 +56,12 @@ public void reading_file_no_tfound_should_do_nothing() { } @Test - public void not_existing_class_files_should_not_be_analyzed_for_current() { + public void notExistingClassFilesShouldNotBeAnalyzed() { File report = TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/JaCoCo_incompatible_merge/jacoco-0.7.5.exec"); Collection classFile = Arrays.asList(dummy); new JaCoCoReportReader(report).analyzeFiles(null, classFile); } - @Test - public void not_existing_class_files_should_not_be_analyzed_for_previous() { - File report = TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/JaCoCo_incompatible_merge/jacoco-0.7.4.exec"); - Collection classFile = Arrays.asList(dummy); - new JaCoCoReportReader(report).analyzeFiles(null, classFile); - } - @Test public void analyzing_a_deleted_file_should_fail() throws Exception { File report = testFolder.newFile("jacoco.exec"); diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoSensorTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoSensorTest.java index 25251b70..fe67bbf3 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoSensorTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/jacoco/JaCoCoSensorTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,117 +19,105 @@ */ package org.sonar.plugins.groovy.jacoco; -import java.io.File; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + import java.io.IOException; -import org.apache.commons.io.FileUtils; -import org.junit.Before; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Rule; import org.junit.Test; -import org.mockito.ArgumentMatchers; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.coverage.CoverageType; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.plugins.groovy.GroovyPlugin; import org.sonar.plugins.groovy.TestUtils; import org.sonar.plugins.groovy.foundation.Groovy; import org.sonar.plugins.groovy.foundation.GroovyFileSystem; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class JaCoCoSensorTest { - private File jacocoExecutionData; - private DefaultInputFile inputFile; - private JaCoCoConfiguration configuration; - private PathResolver pathResolver; - private JaCoCoSensor sensor; - - @Before - public void setUp() throws Exception { - this.jacocoExecutionData = initWithJaCoCoVersion("JaCoCoSensor_0_7_4"); - } + @Rule public final TemporaryFolder tmpDir = new TemporaryFolder(); - private File initWithJaCoCoVersion(String jacocoVersion) throws IOException { - File outputDir = TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/" + jacocoVersion + "/"); - File jacocoExecutionData = new File(outputDir, "jacoco-ut.exec"); + @Rule public ExpectedException exception = ExpectedException.none(); - FileUtils.copyFile(TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/Hello.class.toCopy"), - new File(jacocoExecutionData.getParentFile(), "Hello.class")); - FileUtils.copyFile(TestUtils.getResource("/org/sonar/plugins/groovy/jacoco/Hello$InnerClass.class.toCopy"), - new File(jacocoExecutionData.getParentFile(), "Hello$InnerClass.class")); + private MapSettings settings = TestUtils.jacocoDefaultSettings(); + private JaCoCoSensor sensor; - Settings settings = new Settings(); - settings.setProperty(GroovyPlugin.SONAR_GROOVY_BINARIES, "."); + private void initWithJaCoCoVersion(String jacocoVersion) throws IOException { + Path outputDir = tmpDir.newFolder().toPath(); - configuration = mock(JaCoCoConfiguration.class); - when(configuration.shouldExecuteOnProject(true)).thenReturn(true); - when(configuration.shouldExecuteOnProject(false)).thenReturn(false); - when(configuration.getReportPath()).thenReturn(jacocoExecutionData.getPath()); + Files.copy( + TestUtils.getResource(getClass(), "../" + jacocoVersion + "/jacoco-ut.exec"), + outputDir.resolve("jacoco-ut.exec")); + Files.copy( + TestUtils.getResource(getClass(), "../Hello.class.toCopy"), + outputDir.resolve("Hello.class")); + Files.copy( + TestUtils.getResource(getClass(), "../Hello$InnerClass.class.toCopy"), + outputDir.resolve("Hello$InnerClass.class")); - DefaultFileSystem fileSystem = new DefaultFileSystem(jacocoExecutionData.getParentFile()); - inputFile = new DefaultInputFile("", "example/Hello.groovy") - .setLanguage(Groovy.KEY) - .setType(Type.MAIN); - inputFile.setLines(50); + settings.setProperty(GroovyPlugin.SONAR_GROOVY_BINARIES, "."); + settings.setProperty(JaCoCoConfiguration.REPORT_PATH_PROPERTY, "jacoco-ut.exec"); + + DefaultFileSystem fileSystem = new DefaultFileSystem(outputDir); + InputFile inputFile = + TestInputFileBuilder.create("", "example/Hello.groovy") + .setLanguage(Groovy.KEY) + .setType(Type.MAIN) + .setLines(50) + .build(); fileSystem.add(inputFile); - - pathResolver = mock(PathResolver.class); - sensor = new JaCoCoSensor(configuration, new GroovyFileSystem(fileSystem), pathResolver, settings); - - return jacocoExecutionData; + JaCoCoConfiguration configuration = new JaCoCoConfiguration(settings, fileSystem); + + sensor = + new JaCoCoSensor( + configuration, + new GroovyFileSystem(fileSystem), + new PathResolver(), + settings, + mock(AnalysisWarnings.class)); } @Test - public void test_description() { + public void testDescription() throws IOException { + initWithJaCoCoVersion("JaCoCoSensor_0_7_5"); DefaultSensorDescriptor defaultSensorDescriptor = new DefaultSensorDescriptor(); sensor.describe(defaultSensorDescriptor); assertThat(defaultSensorDescriptor.languages()).containsOnly(Groovy.KEY); } @Test - public void should_Execute_On_Project_only_if_exec_exists() { - when(configuration.getReportPath()).thenReturn("ut.exec"); - - when(pathResolver.relativeFile(any(File.class), eq("ut.exec"))).thenReturn(jacocoExecutionData); - assertThat(sensor.shouldExecuteOnProject()).isTrue(); + public void testReadExecutionDataWithJacoco074() throws IOException { + initWithJaCoCoVersion("JaCoCoSensor_0_7_4"); - when(pathResolver.relativeFile(any(File.class), eq("ut.exec"))).thenReturn(jacocoExecutionData.getParentFile()); - assertThat(sensor.shouldExecuteOnProject()).isFalse(); - - File outputDir = TestUtils.getResource(JaCoCoSensorTest.class, "."); - File fakeExecFile = new File(outputDir, "ut.not.found.exec"); - when(pathResolver.relativeFile(any(File.class), eq("ut.exec"))).thenReturn(fakeExecFile); - assertThat(sensor.shouldExecuteOnProject()).isFalse(); - - when(pathResolver.relativeFile(any(File.class), eq("ut.exec"))).thenReturn(fakeExecFile); - when(configuration.shouldExecuteOnProject(false)).thenReturn(true); - assertThat(sensor.shouldExecuteOnProject()).isTrue(); - } + Path workDir = tmpDir.newFolder().toPath(); + SensorContextTester context = SensorContextTester.create(workDir); + context.setSettings(settings); + context.fileSystem().setWorkDir(workDir); - @Test - public void test_read_execution_data_with_jacoco_0_7_4() { - when(pathResolver.relativeFile(any(File.class), ArgumentMatchers.endsWith(".exec"))).thenReturn(jacocoExecutionData); - - SensorContextTester context = SensorContextTester.create(new File("")); + exception.expect(IllegalArgumentException.class); + exception.expectMessage(JaCoCoReportReader.INCOMPATIBLE_JACOCO_ERROR); sensor.execute(context); - - verifyMeasures(context); } @Test - public void test_read_execution_data_with_jacoco_0_7_5() throws IOException { - File jacocoExecutionData = initWithJaCoCoVersion("JaCoCoSensor_0_7_5"); - when(pathResolver.relativeFile(any(File.class), ArgumentMatchers.endsWith(".exec"))).thenReturn(jacocoExecutionData); + public void testReadExecutionDataWithJacoco075() throws IOException { + initWithJaCoCoVersion("JaCoCoSensor_0_7_5"); + + Path workDir = tmpDir.newFolder().toPath(); + SensorContextTester context = SensorContextTester.create(workDir); + context.setSettings(settings); + context.fileSystem().setWorkDir(workDir); - SensorContextTester context = SensorContextTester.create(new File("")); sensor.execute(context); verifyMeasures(context); @@ -142,15 +130,16 @@ private void verifyMeasures(SensorContextTester context) { int[] coveredConditions = {2, 1, 0}; for (int zeroHitline : zeroHitlines) { - assertThat(context.lineHits(":example/Hello.groovy", CoverageType.UNIT, zeroHitline)).isEqualTo(0); + assertThat(context.lineHits(":example/Hello.groovy", zeroHitline)).isEqualTo(0); } for (int oneHitline : oneHitlines) { - assertThat(context.lineHits(":example/Hello.groovy", CoverageType.UNIT, oneHitline)).isEqualTo(1); + assertThat(context.lineHits(":example/Hello.groovy", oneHitline)).isEqualTo(1); } for (int i = 0; i < conditionLines.length; i++) { int conditionLine = conditionLines[i]; - assertThat(context.conditions(":example/Hello.groovy", CoverageType.UNIT, conditionLine)).isEqualTo(2); - assertThat(context.coveredConditions(":example/Hello.groovy", CoverageType.UNIT, conditionLine)).isEqualTo(coveredConditions[i]); + assertThat(context.conditions(":example/Hello.groovy", conditionLine)).isEqualTo(2); + assertThat(context.coveredConditions(":example/Hello.groovy", conditionLine)) + .isEqualTo(coveredConditions[i]); } } } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/GroovySurefireParserTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/GroovySurefireParserTest.java index bc806dbd..e411320b 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/GroovySurefireParserTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/GroovySurefireParserTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,157 +19,109 @@ */ package org.sonar.plugins.groovy.surefire; -import java.io.File; -import java.net.URISyntaxException; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import java.nio.file.Paths; import org.junit.Before; import org.junit.Test; -import org.mockito.ArgumentMatcher; -import org.mockito.ArgumentMatchers; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.component.ResourcePerspectives; -import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.test.MutableTestCase; -import org.sonar.api.test.MutableTestPlan; -import org.sonar.api.test.TestCase; import org.sonar.plugins.groovy.GroovyPlugin; import org.sonar.plugins.groovy.foundation.Groovy; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Created by iwarapter - */ public class GroovySurefireParserTest { - private ResourcePerspectives perspectives; - private FileSystem fs; private GroovySurefireParser parser; private Groovy groovy; - private SensorContext context; @Before public void before() { - context = mock(SensorContext.class); - perspectives = mock(ResourcePerspectives.class); - fs = new DefaultFileSystem(new File(".")); + FileSystem fs = new DefaultFileSystem(Paths.get(".")); - Settings settings = mock(Settings.class); - when(settings.getStringArray(GroovyPlugin.FILE_SUFFIXES_KEY)).thenReturn(new String[] {".groovy", "grvy"}); + MapSettings settings = new MapSettings(); + settings.setProperty(GroovyPlugin.FILE_SUFFIXES_KEY, ".groovy,grvy"); groovy = new Groovy(settings); - parser = spy(new GroovySurefireParser(groovy, perspectives, fs)); + parser = spy(new GroovySurefireParser(groovy, fs)); - doAnswer(new Answer() { - @Override - public InputFile answer(InvocationOnMock invocation) throws Throwable { - return new DefaultInputFile("", (String) invocation.getArguments()[0]); - } - }).when(parser).getUnitTestInputFile(anyString()); + doAnswer( + invocation -> + TestInputFileBuilder.create("", (String) invocation.getArguments()[0]).build()) + .when(parser) + .getUnitTestInputFile(anyString()); } @Test - public void should_register_tests() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); - - MutableTestCase testCase = mock(MutableTestCase.class); - when(testCase.setDurationInMs(anyLong())).thenReturn(testCase); - when(testCase.setStatus(any(TestCase.Status.class))).thenReturn(testCase); - when(testCase.setMessage(ArgumentMatchers.nullable(String.class))).thenReturn(testCase); - when(testCase.setStackTrace(anyString())).thenReturn(testCase); - when(testCase.setType(anyString())).thenReturn(testCase); - MutableTestPlan testPlan = mock(MutableTestPlan.class); - when(testPlan.addTestCase(anyString())).thenReturn(testCase); - when(perspectives.as(eq(MutableTestPlan.class), - argThat(inputFileMatcher(":ch.hortis.sonar.mvn.mc.MetricsCollectorRegistryTest")))).thenReturn(testPlan); - - parser.collect(context, getDir("multipleReports")); - - verify(testPlan).addTestCase("testGetUnKnownCollector"); - verify(testPlan).addTestCase("testGetJDependsCollector"); - } - - private static ArgumentMatcher inputFileMatcher(final String fileName) { - return new ArgumentMatcher() { - @Override - public boolean matches(InputFile arg0) { - return fileName.equals(arg0.key()); - } - }; - } - - @Test - public void should_store_zero_tests_when_directory_is_null_or_non_existing_or_a_file() throws Exception { + public void shouldStoreZeroTestsWhenDirectoryIsNull() { + SensorContext context = mock(SensorContext.class); parser.collect(context, null); verify(context, never()).newMeasure(); + } - context = mock(SensorContext.class); + @Test + public void shouldStoreZeroTestsWhenDirectoryIsNonExisting() { + SensorContext context = mock(SensorContext.class); parser.collect(context, getDir("nonExistingReportsDirectory")); verify(context, never()).newMeasure(); + } - context = mock(SensorContext.class); + @Test + public void shouldStoreZeroTestsWhenDirectoryIsAFile() { + SensorContext context = mock(SensorContext.class); parser.collect(context, getDir("file.txt")); verify(context, never()).newMeasure(); } @Test - public void shouldAggregateReports() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + public void shouldAggregateReports() { + SensorContextTester context = SensorContextTester.create(Paths.get(".")); parser.collect(context, getDir("multipleReports")); - // Only 6 tests measures should be stored, no more: the TESTS-AllTests.xml must not be read as there's 1 file result per unit test - // (SONAR-2841). - assertThat(context.measures(":ch.hortis.sonar.mvn.mc.MetricsCollectorRegistryTest")).hasSize(6); - assertThat(context.measures(":ch.hortis.sonar.mvn.mc.CloverCollectorTest")).hasSize(6); - assertThat(context.measures(":ch.hortis.sonar.mvn.mc.CheckstyleCollectorTest")).hasSize(6); - assertThat(context.measures(":ch.hortis.sonar.mvn.SonarMojoTest")).hasSize(6); - assertThat(context.measures(":ch.hortis.sonar.mvn.mc.JDependsCollectorTest")).hasSize(6); - assertThat(context.measures(":ch.hortis.sonar.mvn.mc.JavaNCSSCollectorTest")).hasSize(6); + // Only 5 tests measures should be stored, no more: the TESTS-AllTests.xml must not be read as + // there's 1 file result per unit test (SONAR-2841). + assertThat(context.measures(":ch.hortis.sonar.mvn.mc.MetricsCollectorRegistryTest")).hasSize(5); + assertThat(context.measures(":ch.hortis.sonar.mvn.mc.CloverCollectorTest")).hasSize(5); + assertThat(context.measures(":ch.hortis.sonar.mvn.mc.CheckstyleCollectorTest")).hasSize(5); + assertThat(context.measures(":ch.hortis.sonar.mvn.SonarMojoTest")).hasSize(5); + assertThat(context.measures(":ch.hortis.sonar.mvn.mc.JDependsCollectorTest")).hasSize(5); + assertThat(context.measures(":ch.hortis.sonar.mvn.mc.JavaNCSSCollectorTest")).hasSize(5); } // SONAR-2841: if there's only a test suite report, then it should be read. @Test - public void shouldUseTestSuiteReportIfAlone() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + public void shouldUseTestSuiteReportIfAlone() { + SensorContextTester context = SensorContextTester.create(Paths.get(".")); parser.collect(context, getDir("onlyTestSuiteReport")); - assertThat(context.measures(":org.sonar.SecondTest")).hasSize(6); - assertThat(context.measures(":org.sonar.JavaNCSSCollectorTest")).hasSize(6); + assertThat(context.measures(":org.sonar.SecondTest")).hasSize(5); + assertThat(context.measures(":org.sonar.JavaNCSSCollectorTest")).hasSize(5); } - /** - * See http://jira.codehaus.org/browse/SONAR-2371 - */ + /** See http://jira.codehaus.org/browse/SONAR-2371 */ @Test - public void shouldInsertZeroWhenNoReports() throws URISyntaxException { + public void shouldInsertZeroWhenNoReports() { SensorContext context = mock(SensorContext.class); parser.collect(context, getDir("noReports")); verify(context, never()).newMeasure(); } @Test - public void shouldNotInsertZeroOnFiles() throws URISyntaxException { + public void shouldNotInsertZeroOnFiles() { SensorContext context = mock(SensorContext.class); parser.collect(context, getDir("noTests")); @@ -177,26 +129,45 @@ public void shouldNotInsertZeroOnFiles() throws URISyntaxException { } @Test - public void shouldMergeInnerClasses() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + public void shouldMergeInnerClasses() { + SensorContextTester context = SensorContextTester.create(Paths.get(".")); parser.collect(context, getDir("innerClasses")); - assertThat(context.measure(":org.apache.commons.collections.bidimap.AbstractTestBidiMap", CoreMetrics.TESTS).value()).isEqualTo(7); - assertThat(context.measure(":org.apache.commons.collections.bidimap.AbstractTestBidiMap", CoreMetrics.TEST_ERRORS).value()).isEqualTo(1); - assertThat(context.measures(":org.apache.commons.collections.bidimap.AbstractTestBidiMap$TestBidiMapEntrySet")).isEmpty(); + assertThat( + context + .measure( + ":org.apache.commons.collections.bidimap.AbstractTestBidiMap", + CoreMetrics.TESTS) + .value()) + .isEqualTo(7); + assertThat( + context + .measure( + ":org.apache.commons.collections.bidimap.AbstractTestBidiMap", + CoreMetrics.TEST_ERRORS) + .value()) + .isEqualTo(1); + assertThat( + context.measures( + ":org.apache.commons.collections.bidimap.AbstractTestBidiMap$TestBidiMapEntrySet")) + .isEmpty(); } @Test - public void shouldMergeNestedInnerClasses() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + public void shouldMergeNestedInnerClasses() { + SensorContextTester context = SensorContextTester.create(Paths.get(".")); parser.collect(context, getDir("nestedInnerClasses")); - assertThat(context.measure(":org.sonar.plugins.surefire.NestedInnerTest", CoreMetrics.TESTS).value()).isEqualTo(3); + assertThat( + context + .measure(":org.sonar.plugins.surefire.NestedInnerTest", CoreMetrics.TESTS) + .value()) + .isEqualTo(3); } @Test - public void should_not_count_negative_tests() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + public void shouldNotCountNegativeTests() { + SensorContextTester context = SensorContextTester.create(Paths.get(".")); parser.collect(context, getDir("negativeTestTime")); // Test times : -1.120, 0.644, 0.015 -> computed time : 0.659, ignore negative time. @@ -204,28 +175,35 @@ public void should_not_count_negative_tests() throws URISyntaxException { assertThat(context.measure(":java.Foo", CoreMetrics.TESTS).value()).isEqualTo(6); assertThat(context.measure(":java.Foo", CoreMetrics.TEST_ERRORS).value()).isEqualTo(0); assertThat(context.measure(":java.Foo", CoreMetrics.TEST_FAILURES).value()).isEqualTo(0); - assertThat(context.measure(":java.Foo", CoreMetrics.TEST_EXECUTION_TIME).value()).isEqualTo(659); + assertThat(context.measure(":java.Foo", CoreMetrics.TEST_EXECUTION_TIME).value()) + .isEqualTo(659); } - private java.io.File getDir(String dirname) throws URISyntaxException { - return new java.io.File("src/test/resources/org/sonar/plugins/groovy/surefire/SurefireParserTest/" + dirname); + private java.io.File getDir(String dirname) { + return new java.io.File( + "src/test/resources/org/sonar/plugins/groovy/surefire/SurefireParserTest/" + dirname); } @Test - public void should_generate_correct_predicate() throws URISyntaxException { - DefaultFileSystem fs = new DefaultFileSystem(new File(".")); - DefaultInputFile inputFile = new DefaultInputFile("", "src/test/org/sonar/JavaNCSSCollectorTest.groovy") - .setLanguage(Groovy.KEY) - .setType(Type.TEST); + public void shouldGenerateCorrectPredicate() { + DefaultFileSystem fs = new DefaultFileSystem(Paths.get(".")); + InputFile inputFile = + TestInputFileBuilder.create("", "src/test/org/sonar/JavaNCSSCollectorTest.groovy") + .setLanguage(Groovy.KEY) + .setType(Type.TEST) + .build(); fs.add(inputFile); - parser = new GroovySurefireParser(groovy, perspectives, fs); + parser = new GroovySurefireParser(groovy, fs); - SensorContextTester context = SensorContextTester.create(new File("")); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); context.setFileSystem(fs); parser.collect(context, getDir("onlyTestSuiteReport")); - assertThat(context.measure(":src/test/org/sonar/JavaNCSSCollectorTest.groovy", CoreMetrics.TESTS).value()).isEqualTo(11); + assertThat( + context + .measure(":src/test/org/sonar/JavaNCSSCollectorTest.groovy", CoreMetrics.TESTS) + .value()) + .isEqualTo(11); } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/GroovySurefireSensorTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/GroovySurefireSensorTest.java index dfe5f4bf..3baf303a 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/GroovySurefireSensorTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/GroovySurefireSensorTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,75 +19,60 @@ */ package org.sonar.plugins.groovy.surefire; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import java.io.File; +import java.net.URISyntaxException; +import java.nio.file.Paths; import org.junit.Before; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.component.ResourcePerspectives; -import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.plugins.groovy.GroovyPlugin; import org.sonar.plugins.groovy.foundation.Groovy; import org.sonar.plugins.groovy.surefire.api.SurefireUtils; -import java.io.File; -import java.net.URISyntaxException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -/** - * Created by iwarapter - */ +/** Created by iwarapter */ public class GroovySurefireSensorTest { - private DefaultFileSystem fs = new DefaultFileSystem(new File(".")); - private ResourcePerspectives perspectives; + private DefaultFileSystem fs = new DefaultFileSystem(Paths.get(".")); private GroovySurefireSensor surefireSensor; private PathResolver pathResolver = new PathResolver(); private Groovy groovy; - private SensorContext context; @Before public void before() { - fs = new DefaultFileSystem(new File(".")); - DefaultInputFile groovyFile = new DefaultInputFile("", "src/org/foo/grvy"); - groovyFile.setLanguage(Groovy.KEY); + fs = new DefaultFileSystem(Paths.get(".")); + InputFile groovyFile = + TestInputFileBuilder.create("", "src/org/foo/grvy").setLanguage(Groovy.KEY).build(); fs.add(groovyFile); - perspectives = mock(ResourcePerspectives.class); - context = mock(SensorContext.class); - - Settings settings = mock(Settings.class); - when(settings.getStringArray(GroovyPlugin.FILE_SUFFIXES_KEY)).thenReturn(new String[] {".groovy", "grvy"}); + MapSettings settings = new MapSettings(); + settings.setProperty(GroovyPlugin.FILE_SUFFIXES_KEY, ".groovy,grvy"); groovy = new Groovy(settings); - GroovySurefireParser parser = spy(new GroovySurefireParser(groovy, perspectives, fs)); + GroovySurefireParser parser = spy(new GroovySurefireParser(groovy, fs)); - doAnswer(new Answer() { - @Override - public InputFile answer(InvocationOnMock invocation) throws Throwable { - return inputFile((String) invocation.getArguments()[0]); - } - }).when(parser).getUnitTestInputFile(anyString()); + doAnswer(invocation -> inputFile((String) invocation.getArguments()[0])) + .when(parser) + .getUnitTestInputFile(anyString()); - surefireSensor = new GroovySurefireSensor(parser, mock(Settings.class), fs, pathResolver); + surefireSensor = new GroovySurefireSensor(parser, settings, fs, pathResolver); } @Test - public void test_description() { - surefireSensor = new GroovySurefireSensor(new GroovySurefireParser(groovy, perspectives, fs), mock(Settings.class), fs, pathResolver); + public void testDescription() { DefaultSensorDescriptor defaultSensorDescriptor = new DefaultSensorDescriptor(); surefireSensor.describe(defaultSensorDescriptor); assertThat(defaultSensorDescriptor.languages()).containsOnly(Groovy.KEY); @@ -95,134 +80,231 @@ public void test_description() { @Test public void shouldNotFailIfReportsNotFound() { - Settings settings = mock(Settings.class); - when(settings.getString(SurefireUtils.SUREFIRE_REPORTS_PATH_PROPERTY)).thenReturn("unknown"); + MapSettings settings = new MapSettings(); + settings.setProperty(SurefireUtils.SUREFIRE_REPORTS_PATH_PROPERTY, "unknown"); - GroovySurefireSensor surefireSensor = new GroovySurefireSensor(mock(GroovySurefireParser.class), settings, fs, pathResolver); - surefireSensor.execute(mock(SensorContext.class)); + GroovySurefireSensor localSensor = + new GroovySurefireSensor(mock(GroovySurefireParser.class), settings, fs, pathResolver); + localSensor.execute(mock(SensorContext.class)); } @Test public void shouldHandleTestSuiteDetails() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); - context.fileSystem() - .add(inputFile("org.sonar.core.ExtensionsFinderTest")) - .add(inputFile("org.sonar.core.ExtensionsFinderTest2")) - .add(inputFile("org.sonar.core.ExtensionsFinderTest3")); - surefireSensor.collect(context, new File(getClass().getResource( - "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/shouldHandleTestSuiteDetails/").toURI())); - - // 3 classes, 6 measures by class - assertThat(context.measures(":org.sonar.core.ExtensionsFinderTest")).hasSize(6); - assertThat(context.measures(":org.sonar.core.ExtensionsFinderTest2")).hasSize(6); - assertThat(context.measures(":org.sonar.core.ExtensionsFinderTest3")).hasSize(6); - - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TESTS).value()).isEqualTo(4); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TEST_EXECUTION_TIME).value()).isEqualTo(111); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TEST_FAILURES).value()).isEqualTo(1); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TEST_ERRORS).value()).isEqualTo(1); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.SKIPPED_TESTS).value()).isEqualTo(0); - - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.TESTS).value()).isEqualTo(2); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.TEST_EXECUTION_TIME).value()).isEqualTo(2); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.TEST_FAILURES).value()).isEqualTo(0); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.TEST_ERRORS).value()).isEqualTo(0); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.SKIPPED_TESTS).value()).isEqualTo(0); - - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.TESTS).value()).isEqualTo(1); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.TEST_EXECUTION_TIME).value()).isEqualTo(16); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.TEST_FAILURES).value()).isEqualTo(0); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.TEST_ERRORS).value()).isEqualTo(0); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.SKIPPED_TESTS).value()).isEqualTo(1); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); + context + .fileSystem() + .add(inputFile("org.sonar.core.ExtensionsFinderTest")) + .add(inputFile("org.sonar.core.ExtensionsFinderTest2")) + .add(inputFile("org.sonar.core.ExtensionsFinderTest3")); + surefireSensor.collect( + context, + new File( + getClass() + .getResource( + "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/shouldHandleTestSuiteDetails/") + .toURI())); + + // 3 classes, 5 measures by class + assertThat(context.measures(":org.sonar.core.ExtensionsFinderTest")).hasSize(5); + assertThat(context.measures(":org.sonar.core.ExtensionsFinderTest2")).hasSize(5); + assertThat(context.measures(":org.sonar.core.ExtensionsFinderTest3")).hasSize(5); + + assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TESTS).value()) + .isEqualTo(4); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TEST_EXECUTION_TIME) + .value()) + .isEqualTo(111); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TEST_FAILURES) + .value()) + .isEqualTo(1); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TEST_ERRORS) + .value()) + .isEqualTo(1); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.SKIPPED_TESTS) + .value()) + .isEqualTo(0); + + assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.TESTS).value()) + .isEqualTo(2); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.TEST_EXECUTION_TIME) + .value()) + .isEqualTo(2); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.TEST_FAILURES) + .value()) + .isEqualTo(0); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.TEST_ERRORS) + .value()) + .isEqualTo(0); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest2", CoreMetrics.SKIPPED_TESTS) + .value()) + .isEqualTo(0); + + assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.TESTS).value()) + .isEqualTo(1); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.TEST_EXECUTION_TIME) + .value()) + .isEqualTo(16); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.TEST_FAILURES) + .value()) + .isEqualTo(0); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.TEST_ERRORS) + .value()) + .isEqualTo(0); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest3", CoreMetrics.SKIPPED_TESTS) + .value()) + .isEqualTo(1); } - private static DefaultInputFile inputFile(String key) { - return new DefaultInputFile("", key).setType(InputFile.Type.TEST); + private static InputFile inputFile(String key) { + return TestInputFileBuilder.create("", key).setType(InputFile.Type.TEST).build(); } @Test public void shouldSaveErrorsAndFailuresInXML() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); - context.fileSystem() - .add(inputFile("org.sonar.core.ExtensionsFinderTest")) - .add(inputFile("org.sonar.core.ExtensionsFinderTest2")) - .add(inputFile("org.sonar.core.ExtensionsFinderTest3")); - - surefireSensor.collect(context, new File(getClass().getResource( - "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/shouldSaveErrorsAndFailuresInXML/").toURI())); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); + context + .fileSystem() + .add(inputFile("org.sonar.core.ExtensionsFinderTest")) + .add(inputFile("org.sonar.core.ExtensionsFinderTest2")) + .add(inputFile("org.sonar.core.ExtensionsFinderTest3")); + + surefireSensor.collect( + context, + new File( + getClass() + .getResource( + "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/shouldSaveErrorsAndFailuresInXML/") + .toURI())); // 1 classes, 6 measures by class - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.SKIPPED_TESTS).value()).isEqualTo(1); - assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TESTS).value()).isEqualTo(7); - assertThat(context.measures(":org.sonar.core.ExtensionsFinderTest")).hasSize(6); + assertThat( + context + .measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.SKIPPED_TESTS) + .value()) + .isEqualTo(1); + assertThat(context.measure(":org.sonar.core.ExtensionsFinderTest", CoreMetrics.TESTS).value()) + .isEqualTo(7); + assertThat(context.measures(":org.sonar.core.ExtensionsFinderTest")).hasSize(5); } @Test public void shouldManageClassesWithDefaultPackage() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); context.fileSystem().add(inputFile("NoPackagesTest")); - surefireSensor.collect(context, new File(getClass().getResource( - "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/shouldManageClassesWithDefaultPackage/").toURI())); + surefireSensor.collect( + context, + new File( + getClass() + .getResource( + "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/shouldManageClassesWithDefaultPackage/") + .toURI())); assertThat(context.measure(":NoPackagesTest", CoreMetrics.TESTS).value()).isEqualTo(2); } @Test public void successRatioIsZeroWhenAllTestsFail() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); context.fileSystem().add(inputFile("org.sonar.Foo")); - surefireSensor.collect(context, new File(getClass().getResource( - "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/successRatioIsZeroWhenAllTestsFail/").toURI())); + surefireSensor.collect( + context, + new File( + getClass() + .getResource( + "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/successRatioIsZeroWhenAllTestsFail/") + .toURI())); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TESTS).value()).isEqualTo(2); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TEST_FAILURES).value()).isEqualTo(1); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TEST_ERRORS).value()).isEqualTo(1); - assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TEST_SUCCESS_DENSITY).value()).isEqualTo(0); } @Test public void measuresShouldNotIncludeSkippedTests() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); context.fileSystem().add(inputFile("org.sonar.Foo")); - surefireSensor.collect(context, new File(getClass().getResource( - "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/measuresShouldNotIncludeSkippedTests/").toURI())); + surefireSensor.collect( + context, + new File( + getClass() + .getResource( + "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/measuresShouldNotIncludeSkippedTests/") + .toURI())); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TESTS).value()).isEqualTo(2); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TEST_FAILURES).value()).isEqualTo(1); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TEST_ERRORS).value()).isEqualTo(0); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.SKIPPED_TESTS).value()).isEqualTo(1); - assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TEST_SUCCESS_DENSITY).value()).isEqualTo(50); } @Test public void noSuccessRatioIfNoTests() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); context.fileSystem().add(inputFile("org.sonar.Foo")); - surefireSensor.collect(context, new File(getClass().getResource( - "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/noSuccessRatioIfNoTests/").toURI())); + surefireSensor.collect( + context, + new File( + getClass() + .getResource( + "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/noSuccessRatioIfNoTests/") + .toURI())); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TESTS).value()).isEqualTo(0); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TEST_FAILURES).value()).isEqualTo(0); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TEST_ERRORS).value()).isEqualTo(0); assertThat(context.measure(":org.sonar.Foo", CoreMetrics.SKIPPED_TESTS).value()).isEqualTo(2); - assertThat(context.measure(":org.sonar.Foo", CoreMetrics.TEST_SUCCESS_DENSITY)).isNull(); } @Test public void ignoreSuiteAsInnerClass() throws URISyntaxException { - SensorContextTester context = SensorContextTester.create(new File("")); + SensorContextTester context = SensorContextTester.create(Paths.get(".")); context.fileSystem().add(inputFile("org.sonar.Foo")); - surefireSensor.collect(context, new File(getClass().getResource( - "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/ignoreSuiteAsInnerClass/").toURI())); + surefireSensor.collect( + context, + new File( + getClass() + .getResource( + "/org/sonar/plugins/groovy/surefire/SurefireSensorTest/ignoreSuiteAsInnerClass/") + .toURI())); // ignore TestHandler$Input.xml - assertThat(context.measure(":org.apache.shindig.protocol.TestHandler", CoreMetrics.TESTS).value()).isEqualTo(0); - assertThat(context.measure(":org.apache.shindig.protocol.TestHandler", CoreMetrics.SKIPPED_TESTS).value()).isEqualTo(1); + assertThat( + context.measure(":org.apache.shindig.protocol.TestHandler", CoreMetrics.TESTS).value()) + .isEqualTo(0); + assertThat( + context + .measure(":org.apache.shindig.protocol.TestHandler", CoreMetrics.SKIPPED_TESTS) + .value()) + .isEqualTo(1); } } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/api/SurefireUtilsTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/api/SurefireUtilsTest.java index 89e8e333..77905fc9 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/api/SurefireUtilsTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/api/SurefireUtilsTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,58 +19,48 @@ */ package org.sonar.plugins.groovy.surefire.api; -import org.junit.Before; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; import org.junit.Test; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; import org.sonar.api.scan.filesystem.PathResolver; - -import java.io.File; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.sonar.plugins.groovy.TestUtils; public class SurefireUtilsTest { - private FileSystem fs; - private PathResolver pathResolver; - - @Before - public void setup() { - fs = new DefaultFileSystem(new File("src/test/resources/org/sonar/plugins/groovy/surefire/api/SurefireUtilsTest/shouldGetReportsFromProperty")); - pathResolver = new PathResolver(); - } + private FileSystem fs = + new DefaultFileSystem(TestUtils.getResource(getClass(), "shouldGetReportsFromProperty")); + private PathResolver pathResolver = new PathResolver(); @Test public void should_get_reports_from_property() { - Settings settings = mock(Settings.class); - when(settings.getString("sonar.junit.reportsPath")).thenReturn("target/surefire"); + MapSettings settings = new MapSettings(); + settings.setProperty("sonar.junit.reportsPath", "target/surefire"); assertThat(SurefireUtils.getReportsDirectory(settings, fs, pathResolver).exists()).isTrue(); - assertThat(SurefireUtils.getReportsDirectory(settings, fs, pathResolver).isDirectory()).isTrue(); + assertThat(SurefireUtils.getReportsDirectory(settings, fs, pathResolver).isDirectory()) + .isTrue(); } @Test public void return_default_value_if_property_unset() throws Exception { - File directory = SurefireUtils.getReportsDirectory(mock(Settings.class), fs, pathResolver); - assertThat(directory.getCanonicalPath()).endsWith("target" + File.separator + "surefire-reports"); + File directory = SurefireUtils.getReportsDirectory(new MapSettings(), fs, pathResolver); + assertThat(directory.getCanonicalPath()) + .endsWith("target" + File.separator + "surefire-reports"); assertThat(directory.exists()).isFalse(); assertThat(directory.isDirectory()).isFalse(); } @Test public void return_default_value_if_can_not_read_file() throws Exception { - Settings settings = mock(Settings.class); - when(settings.getString("sonar.junit.reportsPath")).thenReturn("target/surefire"); - PathResolver pathResolver = mock(PathResolver.class); - when(pathResolver.relativeFile(any(File.class), anyString())).thenThrow(new IllegalStateException()); + MapSettings settings = new MapSettings(); + settings.setProperty("sonar.junit.reportsPath", "../target/\u0000:surefire"); File directory = SurefireUtils.getReportsDirectory(settings, fs, pathResolver); - assertThat(directory.getCanonicalPath()).endsWith("target" + File.separator + "surefire-reports"); + assertThat(directory.getCanonicalPath()) + .endsWith("target" + File.separator + "surefire-reports"); assertThat(directory.exists()).isFalse(); assertThat(directory.isDirectory()).isFalse(); } - } diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/data/UnitTestResultTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/data/UnitTestResultTest.java index 18722305..428b9b23 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/data/UnitTestResultTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/surefire/data/UnitTestResultTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/utils/StaxParserTest.java b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/utils/StaxParserTest.java index e6b5f51a..b8ae31c9 100644 --- a/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/utils/StaxParserTest.java +++ b/sonar-groovy-plugin/src/test/java/org/sonar/plugins/groovy/utils/StaxParserTest.java @@ -1,7 +1,7 @@ /* * Sonar Groovy Plugin - * Copyright (C) 2010-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com + * Copyright (C) 2010-2019 SonarSource SA & Community + * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/Greeting.groovy b/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/Greeting.groovy deleted file mode 100644 index 1e95ddbb..00000000 --- a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/Greeting.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package org; - -import org.foo.Foo; -import org.bar.Bar; - -class Greeting { - - def void sayHello() { - new Bar().sayHello() - new Foo().sayHello() - } - -} diff --git a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/bar/Bar.groovy b/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/bar/Bar.groovy deleted file mode 100644 index a919a47e..00000000 --- a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/bar/Bar.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package org.bar; - -class Bar { - - def void sayHello() { - if (true) { - print "Hello world!" - } else { - } - } - -} diff --git a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/foo/Foo.groovy b/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/foo/Foo.groovy deleted file mode 100644 index d1288a7b..00000000 --- a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/gmetricswithcoupling/org/foo/Foo.groovy +++ /dev/null @@ -1,11 +0,0 @@ -package org.foo; - -import org.bar.Bar; - -class Foo { - - def void sayHello() { - new Bar().sayHello() - } - -} diff --git a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/jacoco/JaCoCoItSensorTests/jacoco-it.exec b/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/jacoco/JaCoCoItSensorTests/jacoco-it.exec deleted file mode 100644 index 1b4c570f..00000000 Binary files a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/jacoco/JaCoCoItSensorTests/jacoco-it.exec and /dev/null differ diff --git a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTests/jacoco-it.exec b/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTests/jacoco-it.exec deleted file mode 100644 index 1b4c570f..00000000 Binary files a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTests/jacoco-it.exec and /dev/null differ diff --git a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTests/jacoco-ut.exec b/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTests/jacoco-ut.exec deleted file mode 100644 index 85bf4e1f..00000000 Binary files a/sonar-groovy-plugin/src/test/resources/org/sonar/plugins/groovy/jacoco/JaCoCoOverallSensorTests/jacoco-ut.exec and /dev/null differ diff --git a/tools/sonar-codenarc-converter/pom.xml b/tools/sonar-codenarc-converter/pom.xml deleted file mode 100644 index fb5371ee..00000000 --- a/tools/sonar-codenarc-converter/pom.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - 4.0.0 - - - org.sonarsource.parent - parent - 36 - - - org.sonarsource.sonar-groovy-plugin.sonar-codenarc-converter - sonar-codenarc-converter - 0.1-SNAPSHOT - - Sonar CodeNarc Converter - - 2011 - - SonarSource - http://www.sonarsource.com - - - - GNU LGPL 3 - http://www.gnu.org/licenses/lgpl.txt - repo - - - - - 2.4 - - - - - org.slf4j - slf4j-nop - 1.5.6 - - - com.googlecode.java-diff-utils - diffutils - 1.2.1 - - - org.codehaus.sonar - sonar-testing-harness - 4.5.2 - test - - - org.codehaus.sonar - sonar-plugin-api - ${sonar.version} - provided - - - org.codenarc - CodeNarc - 0.25.2 - - - log4j - log4j - - - ant - ant - - - org.apache.ant - ant - - - org.apache.ant - ant-launcher - - - - - diff --git a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Converter.java b/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Converter.java deleted file mode 100644 index 2303fd21..00000000 --- a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/Converter.java +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy.codenarc; - -import com.google.common.collect.LinkedListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; - -import org.codenarc.rule.AbstractRule; -import org.sonar.plugins.groovy.codenarc.apt.AptParser; -import org.sonar.plugins.groovy.codenarc.apt.AptResult; -import org.sonar.plugins.groovy.codenarc.printer.Printer; -import org.sonar.plugins.groovy.codenarc.printer.XMLPrinter; - -import java.io.File; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -public class Converter { - - /** - * location of the generated file - */ - public static final File RESULTS_FOLDER = new File("target/results"); - - /** - * location of the apt files in the CodeNarc project - */ - private static final String RULES_APT_FILES_LOCATION = "../../../CodeNarc/src/site/apt"; - - private int count = 0; - private Map rulesByVersion = Maps.newHashMap(); - private Map rulesByTags = Maps.newHashMap(); - private Set duplications = new HashSet<>(); - - public static void main(String[] args) throws Exception { - Converter converter = new Converter(); - - process(converter, new XMLPrinter()); - - converter.resultsByCategory(); - converter.resultsByVersion(); - System.out.println(); - System.out.println(converter.count + " rules processed"); - } - - private static void process(Converter converter, Printer printer) throws Exception { - checkResultFolder(); - printer.init(converter).process(Converter.loadRules()).printAll(RESULTS_FOLDER); - } - - private static void checkResultFolder() { - RESULTS_FOLDER.mkdirs(); - } - - public static Multimap loadRules() throws Exception { - Properties props = new Properties(); - props.load(Converter.class.getResourceAsStream("/codenarc-base-messages.properties")); - - Map parametersByRule = retrieveRulesParameters(); - - Multimap rules = LinkedListMultimap.create(); - - insertRules(rules, /* legacy */null, props, parametersByRule, - org.codenarc.rule.unused.UnusedArrayRule.class, - org.codenarc.rule.unused.UnusedObjectRule.class, - org.codenarc.rule.unused.UnusedPrivateFieldRule.class, - org.codenarc.rule.unused.UnusedPrivateMethodRule.class, - org.codenarc.rule.unused.UnusedVariableRule.class, - org.codenarc.rule.unnecessary.UnnecessaryBooleanExpressionRule.class, - org.codenarc.rule.unnecessary.UnnecessaryIfStatementRule.class, - org.codenarc.rule.unnecessary.UnnecessaryTernaryExpressionRule.class, - org.codenarc.rule.size.ClassSizeRule.class, - org.codenarc.rule.size.CyclomaticComplexityRule.class, - org.codenarc.rule.size.MethodCountRule.class, - org.codenarc.rule.size.MethodSizeRule.class, - org.codenarc.rule.size.NestedBlockDepthRule.class, - // org.codenarc.rule.size.AbcComplexityRule.class, - deprecated in 0.18 - org.codenarc.rule.naming.AbstractClassNameRule.class, - org.codenarc.rule.naming.ClassNameRule.class, - org.codenarc.rule.naming.FieldNameRule.class, - org.codenarc.rule.naming.InterfaceNameRule.class, - org.codenarc.rule.naming.MethodNameRule.class, - org.codenarc.rule.naming.PackageNameRule.class, - org.codenarc.rule.naming.ParameterNameRule.class, - org.codenarc.rule.naming.PropertyNameRule.class, - org.codenarc.rule.naming.VariableNameRule.class, - org.codenarc.rule.logging.PrintlnRule.class, - org.codenarc.rule.logging.PrintStackTraceRule.class, - org.codenarc.rule.logging.SystemErrPrintRule.class, - org.codenarc.rule.logging.SystemOutPrintRule.class, - org.codenarc.rule.junit.JUnitAssertAlwaysFailsRule.class, - org.codenarc.rule.junit.JUnitAssertAlwaysSucceedsRule.class, - org.codenarc.rule.junit.JUnitPublicNonTestMethodRule.class, - org.codenarc.rule.junit.JUnitSetUpCallsSuperRule.class, - org.codenarc.rule.junit.JUnitTearDownCallsSuperRule.class, - org.codenarc.rule.junit.JUnitUnnecessarySetUpRule.class, - org.codenarc.rule.junit.JUnitUnnecessaryTearDownRule.class, - org.codenarc.rule.imports.DuplicateImportRule.class, - org.codenarc.rule.imports.ImportFromSamePackageRule.class, - org.codenarc.rule.imports.UnnecessaryGroovyImportRule.class, - org.codenarc.rule.imports.UnusedImportRule.class, - org.codenarc.rule.grails.GrailsPublicControllerMethodRule.class, - org.codenarc.rule.grails.GrailsSessionReferenceRule.class, - org.codenarc.rule.grails.GrailsServletContextReferenceRule.class, - org.codenarc.rule.grails.GrailsStatelessServiceRule.class, - org.codenarc.rule.generic.IllegalRegexRule.class, - org.codenarc.rule.generic.RequiredRegexRule.class, - org.codenarc.rule.generic.RequiredStringRule.class, - org.codenarc.rule.generic.StatelessClassRule.class, - org.codenarc.rule.exceptions.CatchErrorRule.class, - org.codenarc.rule.exceptions.CatchExceptionRule.class, - org.codenarc.rule.exceptions.CatchNullPointerExceptionRule.class, - org.codenarc.rule.exceptions.CatchRuntimeExceptionRule.class, - org.codenarc.rule.exceptions.CatchThrowableRule.class, - org.codenarc.rule.exceptions.ThrowErrorRule.class, - org.codenarc.rule.exceptions.ThrowExceptionRule.class, - org.codenarc.rule.exceptions.ThrowNullPointerExceptionRule.class, - org.codenarc.rule.exceptions.ThrowRuntimeExceptionRule.class, - org.codenarc.rule.exceptions.ThrowThrowableRule.class, - org.codenarc.rule.basic.BigDecimalInstantiationRule.class, - org.codenarc.rule.basic.ConstantIfExpressionRule.class, - org.codenarc.rule.basic.ConstantTernaryExpressionRule.class, - org.codenarc.rule.basic.EmptyCatchBlockRule.class, - org.codenarc.rule.basic.EmptyElseBlockRule.class, - org.codenarc.rule.basic.EmptyFinallyBlockRule.class, - org.codenarc.rule.basic.EmptyForStatementRule.class, - org.codenarc.rule.basic.EmptyIfStatementRule.class, - org.codenarc.rule.basic.EmptySwitchStatementRule.class, - org.codenarc.rule.basic.EmptySynchronizedStatementRule.class, - org.codenarc.rule.basic.EmptyTryBlockRule.class, - org.codenarc.rule.basic.EmptyWhileStatementRule.class, - org.codenarc.rule.basic.EqualsAndHashCodeRule.class, - org.codenarc.rule.basic.ReturnFromFinallyBlockRule.class, - org.codenarc.rule.basic.ThrowExceptionFromFinallyBlockRule.class, - org.codenarc.rule.braces.IfStatementBracesRule.class, - org.codenarc.rule.braces.ElseBlockBracesRule.class, - org.codenarc.rule.braces.ForStatementBracesRule.class, - org.codenarc.rule.braces.WhileStatementBracesRule.class, - org.codenarc.rule.concurrency.NestedSynchronizationRule.class, - org.codenarc.rule.concurrency.SynchronizedMethodRule.class, - org.codenarc.rule.concurrency.SynchronizedOnThisRule.class, - org.codenarc.rule.concurrency.SystemRunFinalizersOnExitRule.class, - org.codenarc.rule.concurrency.ThreadGroupRule.class, - org.codenarc.rule.concurrency.ThreadLocalNotStaticFinalRule.class, - org.codenarc.rule.concurrency.ThreadYieldRule.class, - org.codenarc.rule.concurrency.VolatileLongOrDoubleFieldRule.class, - // moved from basic in 0.16 - org.codenarc.rule.design.CloneableWithoutCloneRule.class, - org.codenarc.rule.design.ImplementationAsTypeRule.class); - - insertRules(rules, "0.11", props, parametersByRule, - org.codenarc.rule.naming.ConfusingMethodNameRule.class, - org.codenarc.rule.naming.ObjectOverrideMisspelledMethodNameRule.class, - org.codenarc.rule.junit.JUnitStyleAssertionsRule.class, - org.codenarc.rule.junit.UseAssertEqualsInsteadOfAssertTrueRule.class, - org.codenarc.rule.junit.UseAssertFalseInsteadOfNegationRule.class, - org.codenarc.rule.junit.UseAssertTrueInsteadOfAssertEqualsRule.class, - org.codenarc.rule.junit.UseAssertNullInsteadOfAssertEqualsRule.class, - org.codenarc.rule.junit.UseAssertSameInsteadOfAssertTrueRule.class, - org.codenarc.rule.junit.JUnitFailWithoutMessageRule.class, - org.codenarc.rule.exceptions.CatchIllegalMonitorStateExceptionRule.class, - org.codenarc.rule.exceptions.ConfusingClassNamedExceptionRule.class, - org.codenarc.rule.exceptions.ReturnNullFromCatchBlockRule.class, - org.codenarc.rule.dry.DuplicateNumberLiteralRule.class, - org.codenarc.rule.dry.DuplicateStringLiteralRule.class, - org.codenarc.rule.basic.DeadCodeRule.class, - org.codenarc.rule.basic.DoubleNegativeRule.class, - org.codenarc.rule.basic.DuplicateCaseStatementRule.class, - org.codenarc.rule.basic.RemoveAllOnSelfRule.class, - // org.codenarc.rule.basic.SerialVersionUIDRule.class - removed in 0.14 - org.codenarc.rule.concurrency.SynchronizedOnGetClassRule.class, - org.codenarc.rule.concurrency.UseOfNotifyMethodRule.class, - // moved from basic in 0.16 - org.codenarc.rule.design.BooleanMethodReturnsNullRule.class, - // moved from basic in 0.16 - org.codenarc.rule.design.ReturnsNullInsteadOfEmptyArrayRule.class, - // moved from basic in 0.16 - org.codenarc.rule.design.ReturnsNullInsteadOfEmptyCollectionRule.class, - org.codenarc.rule.convention.InvertedIfElseRule.class, - org.codenarc.rule.groovyism.ExplicitArrayListInstantiationRule.class, - org.codenarc.rule.groovyism.ExplicitCallToAndMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToCompareToMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToDivMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToEqualsMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToGetAtMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToLeftShiftMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToMinusMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToMultiplyMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToModMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToOrMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToPlusMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToPowerMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToRightShiftMethodRule.class, - org.codenarc.rule.groovyism.ExplicitCallToXorMethodRule.class, - org.codenarc.rule.groovyism.ExplicitHashMapInstantiationRule.class, - org.codenarc.rule.groovyism.ExplicitHashSetInstantiationRule.class, - org.codenarc.rule.groovyism.ExplicitLinkedListInstantiationRule.class, - org.codenarc.rule.groovyism.ExplicitStackInstantiationRule.class, - org.codenarc.rule.groovyism.ExplicitTreeSetInstantiationRule.class, - org.codenarc.rule.groovyism.GStringAsMapKeyRule.class); - - insertRules(rules, "0.12", props, parametersByRule, - org.codenarc.rule.unused.UnusedPrivateMethodParameterRule.class, - org.codenarc.rule.unnecessary.UnnecessaryBigDecimalInstantiationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryBigIntegerInstantiationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryBooleanInstantiationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryCallForLastElementRule.class, - org.codenarc.rule.unnecessary.UnnecessaryCatchBlockRule.class, - org.codenarc.rule.unnecessary.UnnecessaryCollectCallRule.class, - org.codenarc.rule.unnecessary.UnnecessaryCollectionCallRule.class, - org.codenarc.rule.unnecessary.UnnecessaryConstructorRule.class, - org.codenarc.rule.unnecessary.UnnecessaryDoubleInstantiationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryFloatInstantiationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryGetterRule.class, - org.codenarc.rule.unnecessary.UnnecessaryGStringRule.class, - org.codenarc.rule.unnecessary.UnnecessaryInstantiationToGetClassRule.class, - org.codenarc.rule.unnecessary.UnnecessaryIntegerInstantiationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryLongInstantiationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryObjectReferencesRule.class, - org.codenarc.rule.unnecessary.UnnecessaryNullCheckRule.class, - org.codenarc.rule.unnecessary.UnnecessaryNullCheckBeforeInstanceOfRule.class, - org.codenarc.rule.unnecessary.UnnecessaryOverridingMethodRule.class, - org.codenarc.rule.unnecessary.UnnecessaryReturnKeywordRule.class, - org.codenarc.rule.unnecessary.UnnecessaryStringInstantiationRule.class, - org.codenarc.rule.logging.LoggerForDifferentClassRule.class, - org.codenarc.rule.logging.LoggingSwallowsStacktraceRule.class, - org.codenarc.rule.logging.LoggerWithWrongModifiersRule.class, - org.codenarc.rule.logging.MultipleLoggersRule.class, - org.codenarc.rule.junit.UseAssertTrueInsteadOfNegationRule.class, - org.codenarc.rule.junit.JUnitTestMethodWithoutAssertRule.class, - org.codenarc.rule.exceptions.CatchArrayIndexOutOfBoundsExceptionRule.class, - org.codenarc.rule.exceptions.CatchIndexOutOfBoundsExceptionRule.class, - org.codenarc.rule.exceptions.MissingNewInThrowStatementRule.class, - org.codenarc.rule.basic.ExplicitGarbageCollectionRule.class, - // moved from basic in 0.16 - org.codenarc.rule.design.CompareToWithoutComparableRule.class, - org.codenarc.rule.design.SimpleDateFormatMissingLocaleRule.class, - org.codenarc.rule.design.AbstractClassWithoutAbstractMethodRule.class, - org.codenarc.rule.design.CloseWithoutCloseableRule.class, - org.codenarc.rule.design.ConstantsOnlyInterfaceRule.class, - org.codenarc.rule.design.EmptyMethodInAbstractClassRule.class, - org.codenarc.rule.design.FinalClassWithProtectedMemberRule.class, - org.codenarc.rule.convention.ConfusingTernaryRule.class); - - insertRules(rules, "0.13", props, parametersByRule, - // moved from basic in 0.16 - org.codenarc.rule.unnecessary.AddEmptyStringRule.class, - // moved from basic in 0.16 - org.codenarc.rule.unnecessary.ConsecutiveLiteralAppendsRule.class, - // moved from basic in 0.16 - org.codenarc.rule.unnecessary.ConsecutiveStringConcatenationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryCallToSubstringRule.class, - org.codenarc.rule.unnecessary.UnnecessaryDefInMethodDeclarationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryModOneRule.class, - org.codenarc.rule.unnecessary.UnnecessaryPublicModifierRule.class, - org.codenarc.rule.unnecessary.UnnecessarySelfAssignmentRule.class, - org.codenarc.rule.unnecessary.UnnecessarySemicolonRule.class, - org.codenarc.rule.unnecessary.UnnecessaryTransientModifierRule.class, - org.codenarc.rule.junit.ChainedTestRule.class, - org.codenarc.rule.junit.CoupledTestCaseRule.class, - org.codenarc.rule.junit.UnnecessaryFailRule.class, - org.codenarc.rule.exceptions.ExceptionExtendsErrorRule.class, - org.codenarc.rule.basic.AssignmentInConditionalRule.class, - org.codenarc.rule.basic.BooleanGetBooleanRule.class, - org.codenarc.rule.basic.BrokenOddnessCheckRule.class, - org.codenarc.rule.basic.EmptyInstanceInitializerRule.class, - org.codenarc.rule.basic.EmptyMethodRule.class, - org.codenarc.rule.basic.EmptyStaticInitializerRule.class, - org.codenarc.rule.basic.IntegerGetIntegerRule.class, - // org.codenarc.rule.basic.SerializableClassMustDefineSerialVersionUIDRule.class - removed in 0.14 - org.codenarc.rule.concurrency.BusyWaitRule.class, - org.codenarc.rule.concurrency.DoubleCheckedLockingRule.class, - org.codenarc.rule.concurrency.InconsistentPropertyLockingRule.class, - org.codenarc.rule.concurrency.InconsistentPropertySynchronizationRule.class, - org.codenarc.rule.concurrency.StaticCalendarFieldRule.class, - org.codenarc.rule.concurrency.StaticDateFormatFieldRule.class, - org.codenarc.rule.concurrency.StaticMatcherFieldRule.class, - org.codenarc.rule.concurrency.SynchronizedOnBoxedPrimitiveRule.class, - org.codenarc.rule.concurrency.SynchronizedOnStringRule.class, - org.codenarc.rule.concurrency.SynchronizedReadObjectMethodRule.class, - org.codenarc.rule.concurrency.SynchronizedOnReentrantLockRule.class, - org.codenarc.rule.concurrency.VolatileArrayFieldRule.class, - org.codenarc.rule.concurrency.WaitOutsideOfWhileLoopRule.class, - org.codenarc.rule.groovyism.GroovyLangImmutableRule.class); - - insertRules(rules, "0.14", props, parametersByRule, - org.codenarc.rule.security.NonFinalSubclassOfSensitiveInterfaceRule.class, - org.codenarc.rule.security.InsecureRandomRule.class, - org.codenarc.rule.security.FileCreateTempFileRule.class, - org.codenarc.rule.security.SystemExitRule.class, - org.codenarc.rule.security.ObjectFinalizeRule.class, - org.codenarc.rule.security.JavaIoPackageAccessRule.class, - org.codenarc.rule.security.UnsafeArrayDeclarationRule.class, - org.codenarc.rule.security.PublicFinalizeMethodRule.class, - org.codenarc.rule.security.NonFinalPublicFieldRule.class, - org.codenarc.rule.jdbc.DirectConnectionManagementRule.class, - org.codenarc.rule.unnecessary.UnnecessaryFinalOnPrivateMethodRule.class, - org.codenarc.rule.unnecessary.UnnecessaryElseStatementRule.class, - org.codenarc.rule.unnecessary.UnnecessaryParenthesesForMethodCallWithClosureRule.class, - org.codenarc.rule.unnecessary.UnnecessaryPackageReferenceRule.class, - org.codenarc.rule.junit.SpockIgnoreRestUsedRule.class, - org.codenarc.rule.imports.ImportFromSunPackagesRule.class, - org.codenarc.rule.imports.MisorderedStaticImportsRule.class, - org.codenarc.rule.generic.IllegalPackageReferenceRule.class, - org.codenarc.rule.exceptions.SwallowThreadDeathRule.class, - org.codenarc.rule.basic.DuplicateMapKeyRule.class, - org.codenarc.rule.basic.DuplicateSetValueRule.class, - org.codenarc.rule.basic.EqualsOverloadedRule.class, - org.codenarc.rule.basic.ForLoopShouldBeWhileLoopRule.class, - org.codenarc.rule.basic.ClassForNameRule.class, - org.codenarc.rule.basic.ComparisonOfTwoConstantsRule.class, - org.codenarc.rule.basic.ComparisonWithSelfRule.class, - org.codenarc.rule.serialization.SerialVersionUIDRule.class, - org.codenarc.rule.serialization.SerializableClassMustDefineSerialVersionUIDRule.class, - org.codenarc.rule.serialization.SerialPersistentFieldsRule.class, - org.codenarc.rule.concurrency.StaticConnectionRule.class, - org.codenarc.rule.concurrency.StaticSimpleDateFormatFieldRule.class, - org.codenarc.rule.design.PublicInstanceFieldRule.class, - org.codenarc.rule.design.StatelessSingletonRule.class, - org.codenarc.rule.design.AbstractClassWithPublicConstructorRule.class, - org.codenarc.rule.groovyism.ExplicitLinkedHashMapInstantiationRule.class, - org.codenarc.rule.groovyism.ClosureAsLastMethodParameterRule.class); - - insertRules(rules, "0.15", props, parametersByRule, - org.codenarc.rule.jdbc.JdbcConnectionReferenceRule.class, - org.codenarc.rule.jdbc.JdbcResultSetReferenceRule.class, - org.codenarc.rule.jdbc.JdbcStatementReferenceRule.class, - org.codenarc.rule.unnecessary.UnnecessaryDefInVariableDeclarationRule.class, - org.codenarc.rule.unnecessary.UnnecessaryDotClassRule.class, - org.codenarc.rule.unnecessary.UnnecessaryInstanceOfCheckRule.class, - org.codenarc.rule.unnecessary.UnnecessarySubstringRule.class, - org.codenarc.rule.grails.GrailsDomainHasToStringRule.class, - org.codenarc.rule.grails.GrailsDomainHasEqualsRule.class, - org.codenarc.rule.generic.IllegalClassReferenceRule.class, - org.codenarc.rule.basic.BitwiseOperatorInConditionalRule.class, - org.codenarc.rule.basic.HardCodedWindowsFileSeparatorRule.class, - org.codenarc.rule.basic.RandomDoubleCoercedToZeroRule.class, - org.codenarc.rule.basic.HardCodedWindowsRootDirectoryRule.class, - org.codenarc.rule.formatting.BracesForClassRule.class, - org.codenarc.rule.formatting.LineLengthRule.class, - org.codenarc.rule.formatting.BracesForForLoopRule.class, - org.codenarc.rule.formatting.BracesForIfElseRule.class, - org.codenarc.rule.formatting.BracesForMethodRule.class, - org.codenarc.rule.formatting.BracesForTryCatchFinallyRule.class, - org.codenarc.rule.formatting.ClassJavadocRule.class, - org.codenarc.rule.groovyism.AssignCollectionUniqueRule.class); - - insertRules(rules, "0.16", props, parametersByRule, - org.codenarc.rule.unused.UnusedMethodParameterRule.class, - org.codenarc.rule.unnecessary.UnnecessaryDefInFieldDeclarationRule.class, - org.codenarc.rule.naming.FactoryMethodNameRule.class, - org.codenarc.rule.dry.DuplicateMapLiteralRule.class, - org.codenarc.rule.dry.DuplicateListLiteralRule.class, - org.codenarc.rule.design.BuilderMethodWithSideEffectsRule.class, - org.codenarc.rule.convention.CouldBeElvisRule.class, - org.codenarc.rule.convention.LongLiteralWithLowerCaseLRule.class, - org.codenarc.rule.groovyism.AssignCollectionSortRule.class, - org.codenarc.rule.groovyism.ConfusingMultipleReturnsRule.class, - org.codenarc.rule.groovyism.GetterMethodCouldBePropertyRule.class, - org.codenarc.rule.groovyism.UseCollectManyRule.class, - org.codenarc.rule.groovyism.CollectAllIsDeprecatedRule.class, - org.codenarc.rule.groovyism.UseCollectNestedRule.class); - - insertRules(rules, "0.17", props, parametersByRule, - org.codenarc.rule.size.CrapMetricRule.class, - org.codenarc.rule.basic.AssertWithinFinallyBlockRule.class, - org.codenarc.rule.basic.ConstantAssertExpressionRule.class, - org.codenarc.rule.basic.BrokenNullCheckRule.class, - org.codenarc.rule.design.PrivateFieldCouldBeFinalRule.class, - org.codenarc.rule.convention.ParameterReassignmentRule.class, - org.codenarc.rule.convention.TernaryCouldBeElvisRule.class, - org.codenarc.rule.convention.VectorIsObsoleteRule.class, - org.codenarc.rule.convention.HashtableIsObsoleteRule.class); - - insertRules(rules, "0.18", props, parametersByRule, - org.codenarc.rule.size.AbcMetricRule.class, - org.codenarc.rule.junit.JUnitLostTestRule.class, - org.codenarc.rule.junit.JUnitUnnecessaryThrowsExceptionRule.class, - org.codenarc.rule.grails.GrailsDuplicateMappingRule.class, - org.codenarc.rule.grails.GrailsDuplicateConstraintRule.class, - org.codenarc.rule.exceptions.ExceptionNotThrownRule.class, - org.codenarc.rule.formatting.SpaceAfterCommaRule.class, - org.codenarc.rule.formatting.SpaceAfterSemicolonRule.class, - org.codenarc.rule.formatting.SpaceAroundOperatorRule.class, - org.codenarc.rule.formatting.SpaceBeforeOpeningBraceRule.class, - org.codenarc.rule.formatting.SpaceAfterOpeningBraceRule.class, - org.codenarc.rule.formatting.SpaceAfterClosingBraceRule.class, - org.codenarc.rule.formatting.SpaceBeforeClosingBraceRule.class, - org.codenarc.rule.formatting.SpaceAfterIfRule.class, - org.codenarc.rule.formatting.SpaceAfterWhileRule.class, - org.codenarc.rule.formatting.SpaceAfterForRule.class, - org.codenarc.rule.formatting.SpaceAfterSwitchRule.class, - org.codenarc.rule.formatting.SpaceAfterCatchRule.class, - org.codenarc.rule.convention.IfStatementCouldBeTernaryRule.class); - - insertRules(rules, "0.19", props, parametersByRule, - org.codenarc.rule.security.UnsafeImplementationAsMapRule.class, - org.codenarc.rule.naming.ClassNameSameAsFilenameRule.class, - org.codenarc.rule.junit.JUnitPublicFieldRule.class, - org.codenarc.rule.junit.JUnitAssertEqualsConstantActualValueRule.class, - org.codenarc.rule.grails.GrailsDomainReservedSqlKeywordNameRule.class, - org.codenarc.rule.grails.GrailsDomainWithServiceReferenceRule.class, - org.codenarc.rule.generic.IllegalClassMemberRule.class, - org.codenarc.rule.basic.EmptyClassRule.class, - org.codenarc.rule.serialization.EnumCustomSerializationIgnoredRule.class, - org.codenarc.rule.concurrency.ThisReferenceEscapesConstructorRule.class, - org.codenarc.rule.design.CloneWithoutCloneableRule.class, - org.codenarc.rule.formatting.SpaceAroundClosureArrowRule.class, - org.codenarc.rule.groovyism.GStringExpressionWithinStringRule.class); - - insertRules(rules, "0.20", props, parametersByRule, - org.codenarc.rule.generic.IllegalStringRule.class, - org.codenarc.rule.design.LocaleSetDefaultRule.class, - org.codenarc.rule.formatting.SpaceAroundMapEntryColonRule.class, - org.codenarc.rule.formatting.ClosureStatementOnOpeningLineOfMultipleLineClosureRule.class); - - insertRules(rules, "0.21", props, parametersByRule, - org.codenarc.rule.unnecessary.UnnecessaryCastRule.class, - org.codenarc.rule.unnecessary.UnnecessaryToStringRule.class, - org.codenarc.rule.junit.JUnitPublicPropertyRule.class, - org.codenarc.rule.imports.NoWildcardImportsRule.class, - org.codenarc.rule.grails.GrailsMassAssignmentRule.class, - org.codenarc.rule.generic.IllegalSubclassRule.class, - org.codenarc.rule.exceptions.ExceptionExtendsThrowableRule.class, - org.codenarc.rule.basic.MultipleUnaryOperatorsRule.class, - org.codenarc.rule.design.ToStringReturnsNullRule.class, - org.codenarc.rule.formatting.ConsecutiveBlankLinesRule.class, - org.codenarc.rule.formatting.BlankLineBeforePackageRule.class, - org.codenarc.rule.formatting.FileEndsWithoutNewlineRule.class, - org.codenarc.rule.formatting.MissingBlankLineAfterImportsRule.class, - org.codenarc.rule.formatting.MissingBlankLineAfterPackageRule.class, - org.codenarc.rule.formatting.TrailingWhitespaceRule.class); - - insertRules(rules, "0.22", props, parametersByRule, - org.codenarc.rule.unnecessary.UnnecessarySafeNavigationOperatorRule.class, - org.codenarc.rule.naming.PackageNameMatchesFilePathRule.class, - org.codenarc.rule.design.InstanceofRule.class, - org.codenarc.rule.convention.NoDefRule.class); - - insertRules(rules, "0.23", props, parametersByRule, - org.codenarc.rule.size.ParameterCountRule.class, - org.codenarc.rule.design.NestedForLoopRule.class); - - insertRules(rules, "0.24", props, parametersByRule, - org.codenarc.rule.naming.ClassNameSameAsSuperclassRule.class, - org.codenarc.rule.naming.InterfaceNameSameAsSuperInterfaceRule.class, - org.codenarc.rule.design.AssignmentToStaticFieldFromInstanceMethodRule.class); - - insertRules(rules, "0.25", props, parametersByRule, - org.codenarc.rule.convention.TrailingCommaRule.class, - org.codenarc.rule.convention.NoTabCharacterRule.class); - - return rules; - } - - @SafeVarargs - private static void insertRules( - Multimap rules, - String version, - Properties props, - Map parametersByRule, - Class... ruleClasses) throws Exception { - - for (Class ruleClass : ruleClasses) { - rules.put(RuleSet.getCategory(ruleClass), new Rule(ruleClass, version, props, parametersByRule)); - } - } - - private static Map retrieveRulesParameters() throws Exception { - return new AptParser().parse(getRulesAptFile()); - } - - private static List getRulesAptFile() { - File aptDir = new File(RULES_APT_FILES_LOCATION); - List rulesAptFiles = Lists.newArrayList(); - if (aptDir.exists() && aptDir.isDirectory()) { - File[] files = aptDir.listFiles(); - for (File file : files) { - if (file.getName().startsWith("codenarc-rules-")) { - rulesAptFiles.add(file); - } - } - } - return rulesAptFiles; - } - - private void updateCounters(Rule rule) { - count++; - for (String tag : rule.tags) { - Integer nbByTag = rulesByTags.get(tag); - if (nbByTag == null) { - nbByTag = 0; - } - rulesByTags.put(tag, nbByTag + 1); - } - - String version = rule.version == null ? "legacy" : rule.version; - Integer nbByVersion = rulesByVersion.get(version); - if (nbByVersion == null) { - nbByVersion = 0; - } - rulesByVersion.put(version, nbByVersion + 1); - } - - private void resultsByVersion() { - System.out.println("Rules by Version:"); - List versions = Lists.newArrayList(rulesByVersion.keySet()); - Collections.sort(versions); - for (String version : versions) { - System.out.println(" - " + version + " : " + rulesByVersion.get(version)); - } - } - - private void resultsByCategory() { - System.out.println("Rules by category:"); - List categories = Lists.newArrayList(rulesByTags.keySet()); - Collections.sort(categories); - for (String category : categories) { - System.out.println(" - " + category + " : " + rulesByTags.get(category)); - } - } - - public void startPrintingRule(Rule rule) { - if (duplications.contains(rule.key)) { - System.out.println("Duplicated rule " + rule.key); - } else { - duplications.add(rule.key); - } - - updateCounters(rule); - - } -} diff --git a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleParameter.java b/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleParameter.java deleted file mode 100644 index 59cafd0a..00000000 --- a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/RuleParameter.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy.codenarc; - -import org.apache.commons.lang.StringUtils; - -public class RuleParameter implements Comparable { - public String key = ""; - public String description = ""; - public String defaultValue = ""; - - public RuleParameter() { - } - - public RuleParameter(String key) { - this.key = key; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - RuleParameter other = (RuleParameter) obj; - if (defaultValue == null) { - if (other.defaultValue != null) { - return false; - } - } else if (!defaultValue.equals(other.defaultValue)) { - return false; - } - if (description == null) { - if (other.description != null) { - return false; - } - } else if (!description.equals(other.description)) { - return false; - } - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - return true; - } - - public boolean isEmpty() { - return StringUtils.isBlank(key) && StringUtils.isBlank(defaultValue) && StringUtils.isBlank(description); - } - - public boolean hasDefaultValue() { - return StringUtils.isNotBlank(defaultValue); - } - - public void merge(RuleParameter parameter) { - if (key != null && key.equals(parameter.key)) { - description = selectValue(description, parameter.description); - defaultValue = selectValue(defaultValue, parameter.defaultValue); - } - } - - private static String selectValue(String currentValue, String newValue) { - if (StringUtils.isBlank(currentValue) && StringUtils.isNotBlank(newValue)) { - return newValue; - } - return currentValue; - } - - @Override - public String toString() { - String smallDescr = description; - if (description.length() > 30) { - smallDescr = description.substring(0, 30) + "..."; - } - return "RuleParameter [key=" + key + ", defaultValue=" + defaultValue + ", description=" + smallDescr + "]"; - } - - @Override - public int compareTo(RuleParameter o) { - return key.compareTo(o.key); - } - -} diff --git a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/apt/AptResult.java b/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/apt/AptResult.java deleted file mode 100644 index 2cf1b9ac..00000000 --- a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/apt/AptResult.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy.codenarc.apt; - -import com.google.common.collect.Sets; - -import org.apache.commons.lang.StringUtils; -import org.sonar.plugins.groovy.codenarc.RuleParameter; - -import java.util.Set; - -public class AptResult { - String rule; - Set parameters = Sets.newHashSet(); - String description = ""; - - public AptResult(String rule) { - this.rule = rule; - } - - void display(int ruleFileCounter, int ruleTotalCounter, String filename) { - System.out.println("=========================================="); - System.out.println("Rule #" + ruleTotalCounter + " : " + rule + " (" + filename + " #" + ruleFileCounter - + ")"); - if (StringUtils.isNotBlank(description)) { - System.out.println("------------------------------------------"); - System.out.println(description); - } - if (!parameters.isEmpty()) { - System.out.println("------------------------------------------"); - System.out.println("Parameters: "); - for (RuleParameter parameter : parameters) { - System.out.println(" * \"" + parameter.key + "\""); - System.out.println(" - defaultValue: " - + (parameter.defaultValue == null ? "" : parameter.defaultValue)); - System.out.println(" - description: " + parameter.description); - } - } - } - - public String getRule() { - return rule; - } - - public Set getParameters() { - return parameters; - } - - public String getDescription() { - return description; - } - - public boolean hasParameters() { - return parameters != null && !parameters.isEmpty(); - } -} diff --git a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/printer/Printer.java b/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/printer/Printer.java deleted file mode 100644 index ec6f3613..00000000 --- a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/codenarc/printer/Printer.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy.codenarc.printer; - -import com.google.common.collect.Multimap; - -import org.sonar.plugins.groovy.codenarc.Converter; -import org.sonar.plugins.groovy.codenarc.Rule; -import org.sonar.plugins.groovy.codenarc.RuleSet; - -import java.io.File; - -public interface Printer { - - Printer init(Converter converter); - - Printer process(Multimap rulesBySet) throws Exception; - - File printAll(File resultFolder) throws Exception; - -} diff --git a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/sqale/RemediationEffortExtractor.java b/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/sqale/RemediationEffortExtractor.java deleted file mode 100644 index 3db4412a..00000000 --- a/tools/sonar-codenarc-converter/src/main/java/org/sonar/plugins/groovy/sqale/RemediationEffortExtractor.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy.sqale; - -import org.apache.commons.io.Charsets; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class RemediationEffortExtractor { - private static final String SQALE_MODEL_LOCATION = "src/test/files/groovy-model.xml"; - private static final String REMEDIATION_FILE_LOCATION = "target/cost.csv"; - - private List extractedRules = new ArrayList<>(); - - public static void main(String[] args) throws Exception { - RemediationEffortExtractor extractor = new RemediationEffortExtractor(); - extractor.extractFromSqaleFile(SQALE_MODEL_LOCATION); - extractor.toCSV(REMEDIATION_FILE_LOCATION); - } - - public void extractFromSqaleFile(String sqaleModelLocation) throws Exception { - Document sqaleModel = parseXml(new File(sqaleModelLocation)); - Node sqale = sqaleModel.getFirstChild(); - NodeList categories = sqale.getChildNodes(); - handleCategories(categories); - } - - public void toCSV(String remediationFileLocation) { - File file = new File(remediationFileLocation); - if (!file.exists() || file.delete()) { - try { - Path path = Paths.get(file.getAbsolutePath()); - Files.write(path, getLines(), Charsets.UTF_8); - } catch (IOException e) { - throw new IllegalStateException("Unable to create file at this location: " + file.getAbsolutePath(), e); - } - } else { - throw new IllegalStateException("Unable to create file at this location: " + file.getAbsolutePath()); - } - } - - public List extractedRules() { - return extractedRules; - } - - private List getLines() { - List rules = extractedRules.stream().sorted().map(ExtractedRule::toCSV).collect(Collectors.toList()); - rules.add(0, ExtractedRule.csvHeader()); - return rules; - } - - private void handleCategories(NodeList categories) { - for (int c = 0; c < categories.getLength(); c++) { - Node category = categories.item(c); - NodeList childNodes = category.getChildNodes(); - String categoryName = "UNKNOWN"; - for (int i = 0; i < childNodes.getLength(); i++) { - Node child = childNodes.item(i); - String nodeName = child.getNodeName(); - if ("key".equals(nodeName)) { - categoryName = getTextValue(child); - } else if ("chc".equals(nodeName)) { - handleSubCategories(child, categoryName); - } - } - } - } - - private void handleSubCategories(Node subCategory, String categoryName) { - NodeList childNodes = subCategory.getChildNodes(); - String subCategoryName = "UNKNOWN"; - for (int i = 0; i < childNodes.getLength(); i++) { - Node child = childNodes.item(i); - String nodeName = child.getNodeName(); - if ("key".equals(nodeName)) { - subCategoryName = getTextValue(child); - } else if ("chc".equals(nodeName)) { - ExtractedRule extractedRule = extractRule(child, categoryName, subCategoryName); - if (extractedRule != null) { - extractedRules.add(extractedRule); - } - } - } - } - - private static ExtractedRule extractRule(Node rule, String categoryName, String subCategoryName) { - NodeList childNodes = rule.getChildNodes(); - ExtractedRule extractedRule = new ExtractedRule(categoryName, subCategoryName); - for (int i = 0; i < childNodes.getLength(); i++) { - Node child = childNodes.item(i); - String nodeName = child.getNodeName(); - if ("rule-key".equals(nodeName)) { - extractedRule.ruleKey = getTextValue(child); - if (!extractedRule.ruleKey.startsWith("org.codenarc.")) { - // skip common rules - return null; - } - } else if ("prop".equals(nodeName)) { - handleProperty(extractedRule, child); - } - } - return extractedRule; - } - - private static void handleProperty(ExtractedRule extractedRule, Node node) { - NodeList childNodes = node.getChildNodes(); - Property property = Property.OTHER; - for (int i = 0; i < childNodes.getLength(); i++) { - Node child = childNodes.item(i); - String nodeName = child.getNodeName(); - if ("key".equals(nodeName)) { - property = Property.getProp(getTextValue(child)); - } else if ("val".equals(nodeName) || "txt".equals(nodeName)) { - extractedRule.setProperty(property, getTextValue(child)); - } - } - } - - private enum Property { - REMEDIATION_FUNCTION, REMEDIATION_FACTOR, OFFSET, OTHER; - - private static Property getProp(String s) { - if ("remediationFunction".equals(s)) { - return REMEDIATION_FUNCTION; - } else if ("remediationFactor".equals(s)) { - return REMEDIATION_FACTOR; - } else if ("offset".equals(s)) { - return OFFSET; - } - return OTHER; - } - } - - public static class ExtractedRule implements Comparable { - final String category; - final String subCategory; - - String ruleKey; - String remediationFunction = ""; - String remediationFactor = ""; - String offset = ""; - - public ExtractedRule(String category, String subCategory) { - this.category = category; - this.subCategory = subCategory; - } - - public void setProperty(Property property, String textValue) { - switch (property) { - case REMEDIATION_FACTOR: - remediationFactor += cleanText(textValue); - break; - case REMEDIATION_FUNCTION: - remediationFunction += textValue; - break; - case OFFSET: - offset += textValue; - break; - default: - // do nothing - break; - } - } - - private static String cleanText(String textValue) { - String newValue = textValue; - if (textValue.endsWith(".0")) { - newValue = textValue.substring(0, textValue.length() - 2); - } else if ("mn".equals(textValue)) { - newValue = "min"; - } - return newValue; - } - - @Override - public int compareTo(ExtractedRule o) { - return ruleKey.compareTo(o.ruleKey); - } - - public String toCSV() { - return ruleKey + ";" + remediationFunction + ";" + remediationFactor; - } - - public static String csvHeader() { - return "ruleKey;remediationFunction;remediationFactor"; - } - } - - private static String getTextValue(Node node) { - return node.getFirstChild().getTextContent(); - } - - private static Document parseXml(File f) throws Exception { - DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder documentBuilder; - documentBuilder = documentBuilderFactory.newDocumentBuilder(); - InputSource is = new InputSource(); - is.setCharacterStream(new FileReader(f)); - return documentBuilder.parse(is); - } -} diff --git a/tools/sonar-codenarc-converter/src/test/files/groovy-model.xml b/tools/sonar-codenarc-converter/src/test/files/groovy-model.xml deleted file mode 100644 index bcb88fa7..00000000 --- a/tools/sonar-codenarc-converter/src/test/files/groovy-model.xml +++ /dev/null @@ -1,6454 +0,0 @@ - - - REUSABILITY - Reusability - - MODULARITY - Modularity - - grvy - org.codenarc.rule.dry.DuplicateListLiteralRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.dry.DuplicateStringLiteralRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.dry.DuplicateNumberLiteralRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.dry.DuplicateMapLiteralRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - - TRANSPORTABILITY - Transportability - - - - PORTABILITY - Portability - - COMPILER_RELATED_PORTABILITY - Compiler - - - HARDWARE_RELATED_PORTABILITY - Hardware - - - LANGUAGE_RELATED_PORTABILITY - Language - - grvy - org.codenarc.rule.basic.ExplicitGarbageCollectionRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.security.FileCreateTempFileRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.GroovyLangImmutableRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.HashtableIsObsoleteRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.ImplementationAsTypeRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.imports.ImportFromSunPackagesRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.IntegerGetIntegerRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.security.JavaIoPackageAccessRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.jdbc.JdbcConnectionReferenceRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.jdbc.JdbcResultSetReferenceRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.jdbc.JdbcStatementReferenceRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.LocaleSetDefaultRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.security.ObjectFinalizeRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.StaticCalendarFieldRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.VectorIsObsoleteRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - - OS_RELATED_PORTABILITY - OS - - grvy - org.codenarc.rule.basic.HardCodedWindowsFileSeparatorRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.HardCodedWindowsRootDirectoryRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - - SOFTWARE_RELATED_PORTABILITY - Software - - - TIME_ZONE_RELATED_PORTABILITY - Time zone - - - - MAINTAINABILITY - Maintainability - - READABILITY - Readability - - grvy - org.codenarc.rule.convention.TrailingCommaRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.NoTabCharacterRule - - remediationFunction - linear - - - remediationFactor - 2.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.AbstractClassNameRule.fixed - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.AbstractClassWithPublicConstructorRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.AddEmptyStringRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.AssignmentInConditionalRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.BitwiseOperatorInConditionalRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.BracesForClassRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.BracesForForLoopRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.BracesForIfElseRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.BracesForMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.BracesForTryCatchFinallyRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.ClassJavadocRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.ClassNameRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.size.ClassSizeRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.CloneWithoutCloneableRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.CloneableWithoutCloneRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.CloseWithoutCloseableRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ClosureAsLastMethodParameterRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.ClosureStatementOnOpeningLineOfMultipleLineClosureRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.CollectAllIsDeprecatedRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.CompareToWithoutComparableRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.ConfusingMethodNameRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ConfusingMultipleReturnsRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.ConfusingTernaryRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.ConsecutiveLiteralAppendsRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.ConsecutiveStringConcatenationRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.ConstantAssertExpressionRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.ConstantIfExpressionRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.ConstantTernaryExpressionRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.DoubleNegativeRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.imports.DuplicateImportRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.braces.ElseBlockBracesRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyStaticInitializerRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitArrayListInstantiationRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToAndMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToCompareToMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToXorMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToRightShiftMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToPowerMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToPlusMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToOrMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToMultiplyMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToModMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToMinusMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToLeftShiftMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToGetAtMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToEqualsMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitCallToDivMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.FactoryMethodNameRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitTreeSetInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitStackInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitLinkedListInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitLinkedHashMapInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitHashSetInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.ExplicitHashMapInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.FieldNameRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.braces.ForStatementBracesRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.braces.IfStatementBracesRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.IfStatementCouldBeTernaryRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.generic.IllegalClassReferenceRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.generic.IllegalClassMemberRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.generic.IllegalPackageReferenceRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.generic.IllegalRegexRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.generic.IllegalStringRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.imports.ImportFromSamePackageRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.InterfaceNameRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitStyleAssertionsRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitUnnecessarySetUpRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitUnnecessaryTearDownRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitUnnecessaryThrowsExceptionRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.LineLengthRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.LongLiteralWithLowerCaseLRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.MethodNameRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.imports.MisorderedStaticImportsRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.PackageNameRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.ParameterNameRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.logging.PrintlnRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.PropertyNameRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.generic.RequiredRegexRule.fixed - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.generic.RequiredStringRule.fixed - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAfterCatchRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceBeforeOpeningBraceRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceBeforeClosingBraceRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAroundOperatorRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAroundMapEntryColonRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAroundClosureArrowRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAfterWhileRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAfterSwitchRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAfterSemicolonRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAfterOpeningBraceRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAfterIfRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAfterForRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAfterCommaRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.SpaceAfterClosingBraceRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryBigDecimalInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryBooleanInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryBigIntegerInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryCallForLastElementRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryCallToSubstringRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryDotClassRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryDefInVariableDeclarationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryDefInMethodDeclarationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryDefInFieldDeclarationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryConstructorRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryCollectionCallRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryCollectCallRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryCatchBlockRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryIfStatementRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.imports.UnnecessaryGroovyImportRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryGetterRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryGStringRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryFloatInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryFinalOnPrivateMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.UnnecessaryFailRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryElseStatementRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryDoubleInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryLongInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryIntegerInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryModOneRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryPackageReferenceRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryParenthesesForMethodCallWithClosureRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryTransientModifierRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryTernaryExpressionRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessarySubstringRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryStringInstantiationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessarySemicolonRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessarySelfAssignmentRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryReturnKeywordRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryPublicModifierRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.imports.UnusedImportRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.UseAssertTrueInsteadOfNegationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.UseAssertTrueInsteadOfAssertEqualsRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.UseAssertSameInsteadOfAssertTrueRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.UseAssertNullInsteadOfAssertEqualsRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.UseAssertFalseInsteadOfNegationRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.UseAssertEqualsInsteadOfAssertTrueRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.UseCollectManyRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.UseCollectNestedRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.VariableNameRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.braces.WhileStatementBracesRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.BlankLineBeforePackageRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.ConsecutiveBlankLinesRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.FileEndsWithoutNewlineRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.MissingBlankLineAfterImportsRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.MissingBlankLineAfterPackageRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.formatting.TrailingWhitespaceRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.imports.NoWildcardImportsRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - - UNDERSTANDABILITY - Understandability - - grvy - org.codenarc.rule.naming.ClassNameSameAsSuperclassRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryCastRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessarySafeNavigationOperatorRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryToStringRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.MultipleUnaryOperatorsRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.PackageNameMatchesFilePathRule.fixed - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.InterfaceNameSameAsSuperInterfaceRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.NoDefRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.size.AbcMetricRule.fixed - - remediationFunction - linear - - - remediationFactor - 3.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.AbstractClassWithoutAbstractMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.ClassNameSameAsFilenameRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.ComparisonOfTwoConstantsRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.ComparisonWithSelfRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.ConfusingClassNamedExceptionRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.DeadCodeRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.DuplicateCaseStatementRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.InvertedIfElseRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitFailWithoutMessageRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unused.UnusedMethodParameterRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unused.UnusedObjectRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unused.UnusedPrivateFieldRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unused.UnusedPrivateMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unused.UnusedVariableRule.fixed - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unused.UnusedPrivateMethodParameterRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - - - SECURITY - Security - - API_ABUSE - API abuse - - grvy - org.codenarc.rule.security.PublicFinalizeMethodRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - - ERRORS - Errors - - - INPUT_VALIDATION_AND_REPRESENTATION - Input validation and representation - - grvy - org.codenarc.rule.grails.GrailsMassAssignmentRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - - SECURITY_FEATURES - Security features - - - - EFFICIENCY - Efficiency - - MEMORY_EFFICIENCY - Memory use - - grvy - org.codenarc.rule.basic.ClassForNameRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - - CPU_EFFICIENCY - Processor use - - - - CHANGEABILITY - Changeability - - ARCHITECTURE_CHANGEABILITY - Architecture - - grvy - org.codenarc.rule.design.FinalClassWithProtectedMemberRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsDomainWithServiceReferenceRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsDuplicateConstraintRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsDuplicateMappingRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsServletContextReferenceRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsSessionReferenceRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsStatelessServiceRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.security.NonFinalSubclassOfSensitiveInterfaceRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.StatelessSingletonRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - - DATA_CHANGEABILITY - Data - - grvy - org.codenarc.rule.groovyism.GetterMethodCouldBePropertyRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsDomainReservedSqlKeywordNameRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.security.NonFinalPublicFieldRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.ParameterReassignmentRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.PrivateFieldCouldBeFinalRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.serialization.SerialPersistentFieldsRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.generic.StatelessClassRule.fixed - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.security.UnsafeArrayDeclarationRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - - LOGIC_CHANGEABILITY - Logic - - grvy - org.codenarc.rule.size.ParameterCountRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.size.CrapMetricRule.fixed - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - common-grvy - DuplicatedBlocks - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsPublicControllerMethodRule.fixed - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - common-grvy - InsufficientBranchCoverage - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - common-grvy - InsufficientLineCoverage - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.size.MethodCountRule - - remediationFunction - linear - - - remediationFactor - 1.0 - d - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.size.MethodSizeRule.fixed - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.size.NestedBlockDepthRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.TernaryCouldBeElvisRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryNullCheckRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryNullCheckBeforeInstanceOfRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryObjectReferencesRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryOverridingMethodRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - - - RELIABILITY - Reliability - - ARCHITECTURE_RELIABILITY - Architecture - - grvy - org.codenarc.rule.design.InstanceofRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.BuilderMethodWithSideEffectsRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.jdbc.DirectConnectionManagementRule - - remediationFunction - linear - - - remediationFactor - 1.0 - d - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyClassRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyInstanceInitializerRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.EmptyMethodInAbstractClassRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.PublicInstanceFieldRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - - DATA_RELIABILITY - Data - - grvy - org.codenarc.rule.groovyism.AssignCollectionSortRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.AssignCollectionUniqueRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.BrokenOddnessCheckRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.DuplicateSetValueRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.DuplicateMapKeyRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.GStringAsMapKeyRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.groovyism.GStringExpressionWithinStringRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsDomainHasEqualsRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.grails.GrailsDomainHasToStringRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.security.InsecureRandomRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.RandomDoubleCoercedToZeroRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.RemoveAllOnSelfRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.serialization.SerialVersionUIDRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.serialization.SerializableClassMustDefineSerialVersionUIDRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.security.UnsafeImplementationAsMapRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unused.UnusedArrayRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - - EXCEPTION_HANDLING - Exception handling - - grvy - org.codenarc.rule.exceptions.ExceptionExtendsThrowableRule - - remediationFunction - linear - - - remediationFactor - 15.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.AssertWithinFinallyBlockRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.CatchArrayIndexOutOfBoundsExceptionRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.CatchErrorRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.CatchExceptionRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.CatchIndexOutOfBoundsExceptionRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.CatchNullPointerExceptionRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.CatchRuntimeExceptionRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.CatchThrowableRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyCatchBlockRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyFinallyBlockRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyTryBlockRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.ExceptionExtendsErrorRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.ExceptionNotThrownRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.logging.LoggingSwallowsStacktraceRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.MissingNewInThrowStatementRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.logging.PrintStackTraceRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.ReturnFromFinallyBlockRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.ReturnNullFromCatchBlockRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.ThrowErrorRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.ThrowExceptionRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.ThrowExceptionFromFinallyBlockRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.ThrowNullPointerExceptionRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.ThrowRuntimeExceptionRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.ThrowThrowableRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - - FAULT_TOLERANCE - Fault tolerance - - grvy - org.codenarc.rule.basic.BrokenNullCheckRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.ReturnsNullInsteadOfEmptyArrayRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.ReturnsNullInsteadOfEmptyCollectionRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - - INSTRUCTION_RELIABILITY - Instruction - - grvy - org.codenarc.rule.design.ToStringReturnsNullRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.BooleanMethodReturnsNullRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.serialization.EnumCustomSerializationIgnoredRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EqualsOverloadedRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.SystemRunFinalizersOnExitRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.logging.SystemOutPrintRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.security.SystemExitRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.logging.SystemErrPrintRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryInstantiationToGetClassRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - - LOGIC_RELIABILITY - Logic - - grvy - org.codenarc.rule.design.NestedForLoopRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.convention.CouldBeElvisRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.size.CyclomaticComplexityRule.fixed - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyElseBlockRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyForStatementRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyIfStatementRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyMethodRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptySwitchStatementRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptyWhileStatementRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EqualsAndHashCodeRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.ForLoopShouldBeWhileLoopRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - common-grvy - InsufficientCommentDensity - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitAssertAlwaysFailsRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitAssertAlwaysSucceedsRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitAssertEqualsConstantActualValueRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitLostTestRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitPublicFieldRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitPublicNonTestMethodRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitSetUpCallsSuperRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitTearDownCallsSuperRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.JUnitTestMethodWithoutAssertRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.naming.ObjectOverrideMisspelledMethodNameRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.SpockIgnoreRestUsedRule.fixed - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.StaticDateFormatFieldRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.ThisReferenceEscapesConstructorRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryBooleanExpressionRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.unnecessary.UnnecessaryInstanceOfCheckRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - - RESOURCE_RELIABILITY - Resource - - grvy - org.codenarc.rule.basic.BooleanGetBooleanRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.ConstantsOnlyInterfaceRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.logging.LoggerForDifferentClassRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.logging.LoggerWithWrongModifiersRule - - remediationFunction - linear - - - remediationFactor - 5.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.logging.MultipleLoggersRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.design.SimpleDateFormatMissingLocaleRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.StaticMatcherFieldRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.StaticSimpleDateFormatFieldRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - - SYNCHRONIZATION_RELIABILITY - Synchronization - - grvy - org.codenarc.rule.design.AssignmentToStaticFieldFromInstanceMethodRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.BigDecimalInstantiationRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.BusyWaitRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.CatchIllegalMonitorStateExceptionRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.DoubleCheckedLockingRule - - remediationFunction - linear - - - remediationFactor - 1.0 - d - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.basic.EmptySynchronizedStatementRule - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.InconsistentPropertyLockingRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.InconsistentPropertySynchronizationRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.NestedSynchronizationRule - - remediationFunction - linear - - - remediationFactor - 1.0 - d - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.StaticConnectionRule - - remediationFunction - linear - - - remediationFactor - 1.0 - d - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.exceptions.SwallowThreadDeathRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.SynchronizedMethodRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.SynchronizedOnBoxedPrimitiveRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.SynchronizedOnGetClassRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.SynchronizedOnReentrantLockRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.SynchronizedOnStringRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.SynchronizedOnThisRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.SynchronizedReadObjectMethodRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.ThreadYieldRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.ThreadLocalNotStaticFinalRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.ThreadGroupRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.UseOfNotifyMethodRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.VolatileArrayFieldRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.VolatileLongOrDoubleFieldRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.concurrency.WaitOutsideOfWhileLoopRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - - UNIT_TESTS - Unit tests coverage - - - - TESTABILITY - Testability - - INTEGRATION_TESTABILITY - Integration level - - - UNIT_TESTABILITY - Unit level - - grvy - org.codenarc.rule.junit.JUnitPublicPropertyRule.fixed - - remediationFunction - linear - - - remediationFactor - 10.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.ChainedTestRule - - remediationFunction - linear - - - remediationFactor - 20.0 - mn - - - offset - 0.0 - mn - - - - grvy - org.codenarc.rule.junit.CoupledTestCaseRule - - remediationFunction - linear - - - remediationFactor - 1.0 - h - - - offset - 0.0 - mn - - - - - diff --git a/tools/sonar-codenarc-converter/src/test/java/org/sonar/plugins/groovy/sqale/RemediationEffortExtractorTest.java b/tools/sonar-codenarc-converter/src/test/java/org/sonar/plugins/groovy/sqale/RemediationEffortExtractorTest.java deleted file mode 100644 index 97b91949..00000000 --- a/tools/sonar-codenarc-converter/src/test/java/org/sonar/plugins/groovy/sqale/RemediationEffortExtractorTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Sonar CodeNarc Converter - * Copyright (C) 2011-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * 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. - */ -package org.sonar.plugins.groovy.sqale; - -import org.junit.Test; - -import static org.fest.assertions.Assertions.assertThat; - -public class RemediationEffortExtractorTest { - - @Test - public void testExtraction() throws Exception { - RemediationEffortExtractor extractor = new RemediationEffortExtractor(); - extractor.extractFromSqaleFile("src/test/files/groovy-model.xml"); - - // 4 common rules - assertThat(extractor.extractedRules()).hasSize(350 - 4); - } -}