diff --git a/README.md b/README.md
index a80e1ee7..7ae5b0f5 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,7 @@ This repository hosts the Objective-C plugin for [SonarQube](http://www.sonarqub
This plugin is not supported by SonarSource. SonarSource offers a [commercial SonarSource Objective-C plugin](http://www.sonarsource.com/products/plugins/languages/objective-c/) as well. Both plugins do not offer the same functionalities/support.
-###Disclaimer
-This plugin is not compatible today with SonarQube 4.X+ (only 3.5). If you're using SonarQube 4.X+, your only option is to use the commercial plugin.
-
-The development of this plugin is frozen at this time. All contributions are welcomed, at least to support SonarQube 4. If you wish to contribute, check the [Contributing](https://github.com/octo-technology/sonar-objective-c/wiki/Contributing) wiki page.
+The development of this plugin has always been done thanks to the community. If you wish to contribute, check the [Contributing](https://github.com/octo-technology/sonar-objective-c/wiki/Contributing) wiki page.
Find below an example of an iOS SonarQube dashboard:
@@ -27,20 +24,17 @@ Find below an example of an iOS SonarQube dashboard:
For more details, see the list of [SonarQube metrics](https://github.com/octo-technology/sonar-objective-c/wiki/Features) implemented or pending.
-###Download
-
-The latest release is the 0.3.1, and it's available [here](http://bit.ly/1fSwd5I).
+###Compatibility
-You can also download the latest build of the plugin from [Cloudbees](https://rfelden.ci.cloudbees.com/job/sonar-objective-c/lastSuccessfulBuild/artifact/target/).
-
-In the worst case, the Maven repository is available here: http://repository-rfelden.forge.cloudbees.com/
+- Use 0.3.x releases for SonarQube 3.x
+- Use 0.4.x releases for SonarQube 4.x
###Prerequisites
- a Mac with Xcode...
-- [SonarQube](http://docs.codehaus.org/display/SONAR/Setup+and+Upgrade) and [SonarQube Runner](http://docs.codehaus.org/display/SONAR/Installing+and+Configuring+SonarQube+Runner) installed (<4.0, see disclaimer)
+- [SonarQube](http://docs.codehaus.org/display/SONAR/Setup+and+Upgrade) and [SonarQube Runner](http://docs.codehaus.org/display/SONAR/Installing+and+Configuring+SonarQube+Runner) installed ([HomeBrew](http://brew.sh) installed and ```brew install sonar-runner```)
- [xctool](https://github.com/facebook/xctool) ([HomeBrew](http://brew.sh) installed and ```brew install xctool```)
-- [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. In my case the 0.7 version is failing on my project, so I recommend installing the 0.8.dev version (at least it works with oclint-0.8.dev.2888e0f).
+- [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.8.1 recommended ([HomeBrew](http://brew.sh) installed and ```brew install https://gist.githubusercontent.com/TonyAnhTran/e1522b93853c5a456b74/raw/157549c7a77261e906fb88bc5606afd8bd727a73/oclint.rb```).
- [gcovr](http://gcovr.com) installed
###Installation (once for all your Objective-C projects)
@@ -58,17 +52,16 @@ In the worst case, the Maven repository is available here: http://repository-rfe
- Run the script ```run-sonar.sh``` in your Xcode project root folder
- Enjoy or file an issue!
-###Build Status
-[![Build Status](https://rfelden.ci.cloudbees.com/job/sonar-objective-c/badge/icon)](https://rfelden.ci.cloudbees.com/job/sonar-objective-c/)
-
###Credits
* **Cyril Picat**
* **Denis Bregeon**
* **Romain Felden**
* **François Helg**
* **Mete Balci**
+* **Gilles Grousset**
###History
+- v0.4 (2014/01): support for SonarQube 4.x
- v0.3.1 (2013/10): fix release
- v0.3 (2013/10): added support for OCUnit tests and test coverage
- v0.2 (2013/10): added OCLint checks as SonarQube violations
diff --git a/build-and-deploy.sh b/build-and-deploy.sh
index c21fbfff..f2e7c47b 100755
--- a/build-and-deploy.sh
+++ b/build-and-deploy.sh
@@ -9,14 +9,14 @@ if [ "$?" != 0 ]; then
fi
# Run shell tests
-shelltest src/test/shell --execdir --diff
-if [ "$?" != 0 ]; then
- echo "ERROR - Shell tests failed!" 1>&2
- exit $?
-fi
+#shelltest src/test/shell --execdir --diff
+#if [ "$?" != 0 ]; then
+# echo "ERROR - Shell tests failed!" 1>&2
+# exit $?
+#fi
# Deploy new verion of plugin in Sonar dir
-cp target/sonar-objective-c-plugin-0.3.2-SNAPSHOT.jar $SONARQUBE_HOME/extensions/plugins
+cp target/*.jar $SONARQUBE_HOME/extensions/plugins
# Stop/start Sonar
$SONARQUBE_HOME/bin/macosx-universal-64/sonar.sh stop
diff --git a/pom.xml b/pom.xml
index cad8a9fc..0167fc58 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,165 +1,189 @@
-
- 4.0.0
-
-
- org.codehaus.sonar-plugins
- parent
- 17
- ../parent
-
-
- org.codehaus.sonar-plugin.objectivec
- sonar-objective-c-plugin
- 0.3.2-SNAPSHOT
-
- sonar-plugin
-
- Objective-C Sonar Plugin
- Enables analysis of Objective-C projects into Sonar.
- https://github.com/octo-technology/sonar-objective-c
-
-
- 2012
-
- OCTO Technology
-
-
-
- GNU LGPL 3
- http://www.gnu.org/licenses/lgpl.txt
- repo
-
-
-
-
-
- cyrilpicat
- Cyril Picat
- OCTO Technology
-
-
- dbregeon
- Denis Bregeon
- Incept5 LLC
-
-
- rfelden
- Romain Felden
- OCTO Technology
-
-
- metebalci
- Mete Balci
- https://github.com/metebalci
-
-
- fhelg
- François Helg
- OCTO Technology
- https://github.com/fhelg
-
-
-
-
- scm:git:git@github.com:octo-technology/sonar-objective-c.git
- scm:git:git@github.com:octo-technology/sonar-objective-c.git
+
+ 4.0.0
+
+
+
+ sonar
+ http://repository.sonarsource.org/content/repositories/sonar
+
+
+
+
+
+ sonar
+ http://repository.sonarsource.org/content/repositories/sonar
+
+
+
+
+ org.codehaus.sonar-plugins
+ parent
+ 18
+
+
+ org.codehaus.sonar-plugin.objectivec
+ sonar-objective-c-plugin
+ 0.4-SNAPSHOT
+
+ sonar-plugin
+
+ Objective-C Sonar Plugin
+ Enables analysis of Objective-C projects into Sonar.
https://github.com/octo-technology/sonar-objective-c
-
-
-
- Cloudbees
- https://rfelden.ci.cloudbees.com/job/sonar-objective-c/
-
-
-
- OCTO Technology
- Sonar Objective-C Plugin
-
- true
-
- 3.3
- 1.16
-
-
- org.sonar.plugins.objectivec.ObjectiveCPlugin
- ObjectiveC
-
-
-
-
-
- org.codehaus.sonar
- sonar-plugin-api
- ${sonar.version}
-
-
- org.codehaus.sonar
- sonar-testing-harness
- ${sonar.version}
-
-
-
- org.codehaus.sonar.sslr
- sslr-core
- ${sslr.version}
-
-
- org.codehaus.sonar.sslr
- sslr-xpath
- ${sslr.version}
-
-
- org.codehaus.sonar.sslr
- sslr-toolkit
- ${sslr.version}
-
-
- org.codehaus.sonar.sslr
- sslr-testing-harness
- ${sslr.version}
-
-
- org.codehaus.sonar.sslr-squid-bridge
- sslr-squid-bridge
- 2.2
-
-
- ant
- ant
- 1.6
-
-
-
- junit
- junit
- 4.10
-
-
- org.mockito
- mockito-all
- 1.9.0
-
-
- org.hamcrest
- hamcrest-all
- 1.1
-
-
- org.easytesting
- fest-assert
- 1.4
-
-
- ch.qos.logback
- logback-classic
- 0.9.15
-
-
- org.codehaus.sonar.plugins
- sonar-surefire-plugin
- 2.7
-
-
-
+
+
+ 2012
+
+ OCTO Technology
+
+
+
+ GNU LGPL 3
+ http://www.gnu.org/licenses/lgpl.txt
+ repo
+
+
+
+
+
+ cyrilpicat
+ Cyril Picat
+ OCTO Technology
+
+
+ dbregeon
+ Denis Bregeon
+ Incept5 LLC
+
+
+ rfelden
+ Romain Felden
+ OCTO Technology
+
+
+ metebalci
+ Mete Balci
+ https://github.com/metebalci
+
+
+ fhelg
+ François Helg
+ OCTO Technology
+ https://github.com/fhelg
+
+
+ zippy1978
+ Gilles Grousset
+ https://github.com/zippy1978
+
+
+
+
+ scm:git:git@github.com:octo-technology/sonar-objective-c.git
+ scm:git:git@github.com:octo-technology/sonar-objective-c.git
+ https://github.com/octo-technology/sonar-objective-c
+
+
+
+ Cloudbees
+ https://rfelden.ci.cloudbees.com/job/sonar-objective-c/
+
+
+
+ OCTO Technology
+ Sonar Objective-C Plugin
+
+ true
+
+ 4.3
+ 1.20
+
+
+ org.sonar.plugins.objectivec.ObjectiveCPlugin
+ ObjectiveC
+
+
+
+
+
+ org.codehaus.sonar
+ sonar-plugin-api
+ ${sonar.version}
+
+
+ org.codehaus.sonar
+ sonar-testing-harness
+ ${sonar.version}
+
+
+ org.codehaus.sonar
+ sonar-deprecated
+ ${sonar.version}
+
+
+
+ org.codehaus.sonar.sslr
+ sslr-core
+ ${sslr.version}
+
+
+ org.codehaus.sonar.sslr
+ sslr-xpath
+ ${sslr.version}
+
+
+ org.codehaus.sonar.sslr
+ sslr-toolkit
+ ${sslr.version}
+
+
+ org.codehaus.sonar.sslr
+ sslr-testing-harness
+ ${sslr.version}
+
+
+ org.codehaus.sonar.sslr-squid-bridge
+ sslr-squid-bridge
+ 2.4
+
+
+ ant
+ ant
+ 1.6
+
+
+
+ junit
+ junit
+ 4.10
+
+
+ org.mockito
+ mockito-all
+ 1.9.0
+
+
+ org.hamcrest
+ hamcrest-all
+ 1.1
+
+
+ org.easytesting
+ fest-assert
+ 1.4
+
+
+ ch.qos.logback
+ logback-classic
+ 0.9.30
+
+
+ org.codehaus.sonar.plugins
+ sonar-surefire-plugin
+ 2.7
+
+
+
diff --git a/sample/sonar-project.properties b/sample/sonar-project.properties
index 68adcb9e..41ed559f 100644
--- a/sample/sonar-project.properties
+++ b/sample/sonar-project.properties
@@ -12,6 +12,9 @@ sonar.projectDescription=Fake description
# Path to source directories
sonar.sources=srcDir1,srcDir2
+# Path to test directories (comment if no test)
+sonar.tests=testSrcDir
+
# Xcode project configuration (.xcodeproj or .xcworkspace)
# -> If you have a project: configure only sonar.objectivec.project
@@ -48,3 +51,8 @@ sonar.sourceEncoding=UTF-8
# Paths to exclude from coverage report (tests, 3rd party libraries etc.)
# sonar.objectivec.excludedPathsFromCoverage=pattern1,pattern2
sonar.objectivec.excludedPathsFromCoverage=.*Tests.*
+
+# Project SCM settings
+# sonar.scm.enabled=true
+# sonar.scm.url=scm:git:https://...
+
diff --git a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java b/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java
index 6c53cbf0..ae3b590e 100644
--- a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java
+++ b/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java
@@ -25,19 +25,19 @@
import org.sonar.objectivec.api.ObjectiveCGrammar;
import org.sonar.objectivec.api.ObjectiveCMetric;
import org.sonar.objectivec.parser.ObjectiveCParser;
-import org.sonar.squid.api.SourceCode;
-import org.sonar.squid.api.SourceFile;
-import org.sonar.squid.api.SourceProject;
-import org.sonar.squid.indexer.QueryByType;
+import org.sonar.squidbridge.AstScanner;
+import org.sonar.squidbridge.CommentAnalyser;
+import org.sonar.squidbridge.SquidAstVisitor;
+import org.sonar.squidbridge.SquidAstVisitorContextImpl;
+import org.sonar.squidbridge.api.SourceCode;
+import org.sonar.squidbridge.api.SourceFile;
+import org.sonar.squidbridge.api.SourceProject;
+import org.sonar.squidbridge.indexer.QueryByType;
+import org.sonar.squidbridge.metrics.CommentsVisitor;
+import org.sonar.squidbridge.metrics.LinesOfCodeVisitor;
+import org.sonar.squidbridge.metrics.LinesVisitor;
-import com.sonar.sslr.api.CommentAnalyser;
import com.sonar.sslr.impl.Parser;
-import com.sonar.sslr.squid.AstScanner;
-import com.sonar.sslr.squid.SquidAstVisitor;
-import com.sonar.sslr.squid.SquidAstVisitorContextImpl;
-import com.sonar.sslr.squid.metrics.CommentsVisitor;
-import com.sonar.sslr.squid.metrics.LinesOfCodeVisitor;
-import com.sonar.sslr.squid.metrics.LinesVisitor;
public class ObjectiveCAstScanner {
@@ -89,16 +89,15 @@ public String getContents(String comment) {
});
/* Files */
- builder.setFilesMetric(ObjectiveCMetric.FILES);
+ builder.setFilesMetric(ObjectiveCMetric.FILES);
/* Metrics */
- builder.withSquidAstVisitor(new LinesVisitor(ObjectiveCMetric.LINES));
- builder.withSquidAstVisitor(new LinesOfCodeVisitor(ObjectiveCMetric.LINES_OF_CODE));
- builder.withSquidAstVisitor(CommentsVisitor. builder().withCommentMetric(ObjectiveCMetric.COMMENT_LINES)
- .withBlankCommentMetric(ObjectiveCMetric.COMMENT_BLANK_LINES)
- .withNoSonar(true)
- .withIgnoreHeaderComment(conf.getIgnoreHeaderComments())
- .build());
+ builder.withSquidAstVisitor(new LinesVisitor(ObjectiveCMetric.LINES));
+ builder.withSquidAstVisitor(new LinesOfCodeVisitor(ObjectiveCMetric.LINES_OF_CODE));
+ builder.withSquidAstVisitor(CommentsVisitor. builder().withCommentMetric(ObjectiveCMetric.COMMENT_LINES)
+ .withNoSonar(true)
+ .withIgnoreHeaderComment(conf.getIgnoreHeaderComments())
+ .build());
return builder.build();
}
diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java
index abbe0a21..23058102 100644
--- a/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java
+++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java
@@ -19,15 +19,14 @@
*/
package org.sonar.objectivec.api;
-import org.sonar.squid.measures.CalculatedMetricFormula;
-import org.sonar.squid.measures.MetricDef;
+import org.sonar.squidbridge.measures.CalculatedMetricFormula;
+import org.sonar.squidbridge.measures.MetricDef;
public enum ObjectiveCMetric implements MetricDef {
FILES,
LINES,
LINES_OF_CODE,
COMMENT_LINES,
- COMMENT_BLANK_LINES,
STATEMENTS,
COMPLEXITY,
FUNCTIONS;
diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java
index 6e013034..8fa3b673 100644
--- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java
+++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java
@@ -32,6 +32,7 @@
import org.sonar.plugins.objectivec.cpd.ObjectiveCCpdMapping;
import com.google.common.collect.ImmutableList;
+
import org.sonar.plugins.objectivec.tests.SurefireSensor;
import org.sonar.plugins.objectivec.violations.OCLintProfile;
import org.sonar.plugins.objectivec.violations.OCLintProfileImporter;
@@ -47,24 +48,16 @@ public class ObjectiveCPlugin extends SonarPlugin {
public List> getExtensions() {
return ImmutableList.of(ObjectiveC.class,
ObjectiveCSourceImporter.class,
- ObjectiveCColorizerFormat.class, ObjectiveCCpdMapping.class,
+ ObjectiveCColorizerFormat.class,
+ ObjectiveCCpdMapping.class,
- ObjectiveCSquidSensor.class, ObjectiveCProfile.class,
+ ObjectiveCSquidSensor.class,
+ ObjectiveCProfile.class,
SurefireSensor.class,
CoberturaSensor.class,
OCLintRuleRepository.class,
OCLintSensor.class, OCLintProfile.class,
OCLintProfileImporter.class
- // ObjectiveCRuleRepository.class,
- // ObjectiveCProfile.class,
- //
- // OCTestDriverSurefireSensor.class,
- // OCTestDriverCoverageSensor.class,
- //
- // OCTestMavenInitializer.class,
- // OCTestMavenPluginHandler.class,
- // OCTestCoverageSensor.class,
- // OCTestSurefireSensor.class
);
}
diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java
index 19d3dcfe..43bbb5e9 100644
--- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java
+++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java
@@ -39,15 +39,16 @@
import org.sonar.objectivec.api.ObjectiveCMetric;
import org.sonar.objectivec.checks.CheckList;
import org.sonar.plugins.objectivec.core.ObjectiveC;
-import org.sonar.squid.api.CheckMessage;
-import org.sonar.squid.api.SourceCode;
-import org.sonar.squid.api.SourceFile;
-import org.sonar.squid.api.SourceFunction;
-import org.sonar.squid.indexer.QueryByParent;
-import org.sonar.squid.indexer.QueryByType;
+import org.sonar.squidbridge.AstScanner;
+import org.sonar.squidbridge.api.CheckMessage;
+import org.sonar.squidbridge.api.SourceCode;
+import org.sonar.squidbridge.api.SourceFile;
+import org.sonar.squidbridge.api.SourceFunction;
+import org.sonar.squidbridge.checks.SquidCheck;
+import org.sonar.squidbridge.indexer.QueryByParent;
+import org.sonar.squidbridge.indexer.QueryByType;
+import org.sonar.squidbridge.measures.Metric;
-import com.sonar.sslr.squid.AstScanner;
-import com.sonar.sslr.squid.checks.SquidCheck;
public class ObjectiveCSquidSensor implements Sensor {
@@ -104,7 +105,6 @@ private void saveMeasures(File sonarFile, SourceFile squidFile) {
context.saveMeasure(sonarFile, CoreMetrics.FUNCTIONS, squidFile.getDouble(ObjectiveCMetric.FUNCTIONS));
context.saveMeasure(sonarFile, CoreMetrics.STATEMENTS, squidFile.getDouble(ObjectiveCMetric.STATEMENTS));
context.saveMeasure(sonarFile, CoreMetrics.COMPLEXITY, squidFile.getDouble(ObjectiveCMetric.COMPLEXITY));
- context.saveMeasure(sonarFile, CoreMetrics.COMMENT_BLANK_LINES, squidFile.getDouble(ObjectiveCMetric.COMMENT_BLANK_LINES));
context.saveMeasure(sonarFile, CoreMetrics.COMMENT_LINES, squidFile.getDouble(ObjectiveCMetric.COMMENT_LINES));
}
diff --git a/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java b/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java
index fa985d39..a5aa5025 100644
--- a/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java
+++ b/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java
@@ -19,11 +19,15 @@
*/
package org.sonar.plugins.objectivec.core;
+import java.util.List;
+
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.resources.AbstractLanguage;
import org.sonar.plugins.objectivec.ObjectiveCPlugin;
+import com.google.common.collect.Lists;
+
public class ObjectiveC extends AbstractLanguage {
public static final String KEY = "objc";
@@ -36,11 +40,20 @@ public ObjectiveC(Configuration configuration) {
}
public String[] getFileSuffixes() {
- String[] suffixes = configuration.getStringArray(ObjectiveCPlugin.FILE_SUFFIXES_KEY);
+ String[] suffixes = filterEmptyStrings(configuration.getStringArray(ObjectiveCPlugin.FILE_SUFFIXES_KEY));
if (suffixes == null || suffixes.length == 0) {
suffixes = StringUtils.split(ObjectiveCPlugin.FILE_SUFFIXES_DEFVALUE, ",");
}
return suffixes;
}
+ private String[] filterEmptyStrings(String[] stringArray) {
+ List nonEmptyStrings = Lists.newArrayList();
+ for (String string : stringArray) {
+ if (StringUtils.isNotBlank(string.trim())) {
+ nonEmptyStrings.add(string.trim());
+ }
+ }
+ return nonEmptyStrings.toArray(new String[nonEmptyStrings.size()]);
+ }
}
diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java
index 84db58b3..314a607a 100644
--- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java
+++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java
@@ -32,6 +32,7 @@
import org.sonar.plugins.objectivec.ObjectiveCPlugin;
import org.sonar.plugins.objectivec.core.ObjectiveC;
+
public final class CoberturaSensor implements Sensor {
public static final String REPORT_PATTERN_KEY = ObjectiveCPlugin.PROPERTY_PREFIX
diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java b/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java
index 70594815..d46ee0fa 100644
--- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java
+++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java
@@ -20,13 +20,18 @@
package org.sonar.plugins.objectivec.coverage;
import java.io.File;
+import java.util.List;
import java.util.Map;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.measures.CoverageMeasuresBuilder;
import org.sonar.api.measures.Measure;
import org.sonar.api.resources.Project;
+import org.sonar.api.scan.filesystem.PathResolver;
+import org.sonar.api.utils.PathUtils;
final class CoverageMeasuresPersistor {
private final Project project;
@@ -37,20 +42,18 @@ public CoverageMeasuresPersistor(final Project p, final SensorContext c) {
context = c;
}
- public void saveMeasures(
- final Map coverageMeasures) {
- for (final Map.Entry entry : coverageMeasures
- .entrySet()) {
+ public void saveMeasures(final Map coverageMeasures) {
+
+ for (final Map.Entry entry : coverageMeasures.entrySet()) {
saveMeasuresForFile(entry.getValue(), entry.getKey());
}
}
- private void saveMeasuresForFile(
- final CoverageMeasuresBuilder measureBuilder, final String filePath) {
- LoggerFactory.getLogger(getClass()).debug("Saving measures for {}",
- filePath);
- final org.sonar.api.resources.File objcfile = org.sonar.api.resources.File
- .fromIOFile(new File(filePath), project);
+ private void saveMeasuresForFile(final CoverageMeasuresBuilder measureBuilder, final String filePath) {
+
+ LoggerFactory.getLogger(getClass()).debug("Saving measures for {}", filePath);
+ final org.sonar.api.resources.File objcfile = org.sonar.api.resources.File.fromIOFile(new File(project.getFileSystem().getBasedir(), filePath), project);
+
if (fileExists(context, objcfile)) {
LoggerFactory.getLogger(getClass()).debug(
"File {} was found in the project.", filePath);
diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java
new file mode 100644
index 00000000..7b1ee793
--- /dev/null
+++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java
@@ -0,0 +1,156 @@
+/*
+ * Sonar Objective-C Plugin
+ * Copyright (C) 2012 OCTO Technology
+ * dev@sonar.codehaus.org
+ *
+ * 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 02
+ */
+
+package org.sonar.plugins.objectivec.tests;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.ParsingUtils;
+import org.sonar.api.utils.StaxParser;
+import org.sonar.api.utils.XmlParserException;
+import org.sonar.plugins.surefire.TestCaseDetails;
+import org.sonar.plugins.surefire.TestSuiteParser;
+import org.sonar.plugins.surefire.TestSuiteReport;
+
+import javax.xml.transform.TransformerException;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Created by gillesgrousset on 06/01/15.
+ */
+public class SurefireParser {
+
+ public void collect(Project project, SensorContext context, File reportsDir) {
+ File[] xmlFiles = getReports(reportsDir);
+
+ if (xmlFiles.length == 0) {
+ insertZeroWhenNoReports(project, context);
+ } else {
+ parseFiles(context, xmlFiles);
+ }
+ }
+
+ private File[] getReports(File dir) {
+ if (dir == null || !dir.isDirectory() || !dir.exists()) {
+ return new File[0];
+ }
+
+ File[] list = dir.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.startsWith("TEST") && name.endsWith(".xml");
+ }
+ });
+
+ return dir.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.startsWith("TEST") && name.endsWith(".xml");
+ }
+ });
+ }
+
+ private void insertZeroWhenNoReports(Project pom, SensorContext context) {
+ if ( !StringUtils.equalsIgnoreCase("pom", pom.getPackaging())) {
+ context.saveMeasure(CoreMetrics.TESTS, 0.0);
+ }
+ }
+
+ private void parseFiles(SensorContext context, File[] reports) {
+ Set analyzedReports = new HashSet();
+ try {
+ for (File report : reports) {
+ TestSuiteParser parserHandler = new TestSuiteParser();
+ StaxParser parser = new StaxParser(parserHandler, false);
+ parser.parse(report);
+
+ for (TestSuiteReport fileReport : parserHandler.getParsedReports()) {
+ if ( !fileReport.isValid() || analyzedReports.contains(fileReport)) {
+ continue;
+ }
+ if (fileReport.getTests() > 0) {
+ double testsCount = fileReport.getTests() - fileReport.getSkipped();
+ saveClassMeasure(context, fileReport, CoreMetrics.SKIPPED_TESTS, fileReport.getSkipped());
+ saveClassMeasure(context, fileReport, CoreMetrics.TESTS, testsCount);
+ saveClassMeasure(context, fileReport, CoreMetrics.TEST_ERRORS, fileReport.getErrors());
+ saveClassMeasure(context, fileReport, CoreMetrics.TEST_FAILURES, fileReport.getFailures());
+ saveClassMeasure(context, fileReport, CoreMetrics.TEST_EXECUTION_TIME, fileReport.getTimeMS());
+ double passedTests = testsCount - fileReport.getErrors() - fileReport.getFailures();
+ if (testsCount > 0) {
+ double percentage = passedTests * 100d / testsCount;
+ saveClassMeasure(context, fileReport, CoreMetrics.TEST_SUCCESS_DENSITY, ParsingUtils.scaleValue(percentage));
+ }
+ saveTestsDetails(context, fileReport);
+ analyzedReports.add(fileReport);
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ throw new XmlParserException("Can not parse surefire reports", e);
+ }
+ }
+
+ private void saveTestsDetails(SensorContext context, TestSuiteReport fileReport) throws TransformerException {
+ StringBuilder testCaseDetails = new StringBuilder(256);
+ testCaseDetails.append("");
+ List details = fileReport.getDetails();
+ for (TestCaseDetails detail : details) {
+ testCaseDetails.append("")
+ .append(isError ? "")
+ .append("")
+ .append(isError ? "" : "").append("");
+ } else {
+ testCaseDetails.append("/>");
+ }
+ }
+ testCaseDetails.append("");
+ context.saveMeasure(getUnitTestResource(fileReport.getClassKey()), new Measure(CoreMetrics.TEST_DATA, testCaseDetails.toString()));
+ }
+
+ private void saveClassMeasure(SensorContext context, TestSuiteReport fileReport, Metric metric, double value) {
+ if ( !Double.isNaN(value)) {
+ context.saveMeasure(getUnitTestResource(fileReport.getClassKey()), metric, value);
+ }
+ }
+
+ public Resource getUnitTestResource(String classKey) {
+
+ String filename = classKey.replace('.', '/') + ".m";
+ org.sonar.api.resources.File sonarFile = new org.sonar.api.resources.File(filename);
+ sonarFile.setQualifier(Qualifiers.UNIT_TEST_FILE);
+ return sonarFile;
+ }
+}
diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java
index 24b8b29b..207a430f 100644
--- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java
+++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java
@@ -28,38 +28,35 @@
import org.sonar.api.batch.SensorContext;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.Resource;
import org.sonar.plugins.objectivec.core.ObjectiveC;
-import org.sonar.plugins.surefire.api.AbstractSurefireParser;
import java.io.File;
public class SurefireSensor implements Sensor {
- private static final Logger LOG = LoggerFactory.getLogger(SurefireSensor.class);
- public static final String REPORT_PATH_KEY = "sonar.junit.reportsPath";
- public static final String DEFAULT_REPORT_PATH = "sonar-reports/";
- private final Settings conf;
+ private static final Logger LOG = LoggerFactory.getLogger(SurefireSensor.class);
+ public static final String REPORT_PATH_KEY = "sonar.junit.reportsPath";
+ public static final String DEFAULT_REPORT_PATH = "sonar-reports/";
+ private final Settings conf;
- public SurefireSensor() {
- this(null);
- }
+ public SurefireSensor() {
+ this(null);
+ }
- public SurefireSensor(final Settings config) {
- conf = config;
- }
+ public SurefireSensor(final Settings config) {
+ conf = config;
+ }
- @DependsUpon
- public Class> dependsUponCoverageSensors() {
- return CoverageExtension.class;
- }
+ @DependsUpon
+ public Class> dependsUponCoverageSensors() {
+ return CoverageExtension.class;
+ }
- public boolean shouldExecuteOnProject(Project project) {
- return ObjectiveC.KEY.equals(project.getLanguageKey());
- }
+ public boolean shouldExecuteOnProject(Project project) {
+ return ObjectiveC.KEY.equals(project.getLanguageKey());
+ }
- public void analyse(Project project, SensorContext context) {
+ public void analyse(Project project, SensorContext context) {
/*
GitHub Issue #50
@@ -77,35 +74,27 @@ that is very different (and does not contain a matching method).
So the implementation here reaches into the project properties and pulls the path out by itself.
*/
- collect(project, context, new File(reportPath()));
- }
+ collect(project, context, new File(reportPath()));
+ }
- protected void collect(Project project, SensorContext context, File reportsDir) {
- LOG.info("parsing {}", reportsDir);
- SUREFIRE_PARSER.collect(project, context, reportsDir);
- }
+ protected void collect(Project project, SensorContext context, File reportsDir) {
+ LOG.info("parsing {}", reportsDir);
+ SUREFIRE_PARSER.collect(project, context, reportsDir);
+ }
+
+ private static final SurefireParser SUREFIRE_PARSER = new SurefireParser();
- private static final AbstractSurefireParser SUREFIRE_PARSER = new AbstractSurefireParser() {
@Override
- protected Resource> getUnitTestResource(String classKey) {
- String filename = classKey.replace('.', '/') + ".m";
- org.sonar.api.resources.File sonarFile = new org.sonar.api.resources.File(filename);
- sonarFile.setQualifier(Qualifiers.UNIT_TEST_FILE);
- return sonarFile;
+ public String toString() {
+ return "Objective-C SurefireSensor";
+ }
+
+ private String reportPath() {
+ String reportPath = conf.getString(REPORT_PATH_KEY);
+ if (reportPath == null) {
+ reportPath = DEFAULT_REPORT_PATH;
+ }
+ return reportPath;
}
- };
-
- @Override
- public String toString() {
- return "Objective-C SurefireSensor";
- }
-
- private String reportPath() {
- String reportPath = conf.getString(REPORT_PATH_KEY);
- if (reportPath == null) {
- reportPath = DEFAULT_REPORT_PATH;
- }
- return reportPath;
- }
}
\ No newline at end of file
diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleParser.java
index 6ef4806e..427090c0 100644
--- a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleParser.java
+++ b/src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleParser.java
@@ -38,7 +38,7 @@
*/
final class OCLintRuleParser implements ServerComponent {
- private static final int OCLINT_MINIMUM_PRIORITY = 3;
+ private static final int OCLINT_MINIMUM_PRIORITY = 4;
private static final Logger LOGGER = LoggerFactory
.getLogger(OCLintRuleParser.class);
@@ -80,8 +80,7 @@ public List parse(final BufferedReader reader) throws IOException {
inDescription = false;
final String severity = line.substring("Severity: ".length());
// Rules are priority 1, 2 or 3 in OCLint files.
- rule.setSeverity(RulePriority.values()[OCLINT_MINIMUM_PRIORITY
- - Integer.valueOf(severity)]);
+ rule.setSeverity(RulePriority.values()[Integer.valueOf(severity)]);
} else {
if (inDescription) {
line = ruleDescriptionLink(line);
diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/OCLintSensor.java
index 3857c7b8..58a0704e 100644
--- a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintSensor.java
+++ b/src/main/java/org/sonar/plugins/objectivec/violations/OCLintSensor.java
@@ -30,6 +30,7 @@
import org.sonar.api.rules.Violation;
import org.sonar.plugins.objectivec.ObjectiveCPlugin;
import org.sonar.plugins.objectivec.core.ObjectiveC;
+import org.sonar.plugins.objectivec.violations.OCLintParser;
public final class OCLintSensor implements Sensor {
public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX
diff --git a/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml b/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml
index e75a1bed..35fa35d4 100644
--- a/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml
+++ b/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml
@@ -1,239 +1,259 @@
- OCLint
- objc
-
-
- OCLint
- avoid branching statement as last in loop
-
-
- OCLint
- bitwise operator in conditional
-
-
- OCLint
- broken null check
-
-
- OCLint
- broken nil check
-
-
- OCLint
- broken oddness check
-
-
- OCLint
- collapsible if statements
-
-
- OCLint
- constant conditional operator
-
-
- OCLint
- constant if expression
-
-
- OCLint
- high cyclomatic complexity
-
-
- OCLint
- dead code
-
-
- OCLint
- default label not last in switch statement
-
-
- OCLint
- double negative
-
-
- OCLint
- empty catch statement
-
-
- OCLint
- empty do/while statement
-
-
- OCLint
- empty else block
-
-
- OCLint
- empty finally statement
-
-
- OCLint
- empty for statement
-
-
- OCLint
- empty if statement
-
-
- OCLint
- empty switch statement
-
-
- OCLint
- empty try statement
-
-
- OCLint
- empty while statement
-
-
- OCLint
- for loop should be while loop
-
-
- OCLint
- goto statement
-
-
- OCLint
- inverted logic
-
-
- OCLint
- jumbled incrementer
-
-
- OCLint
- long class
-
-
- OCLint
- long line
-
-
- OCLint
- long method
-
-
- OCLint
- long variable name
-
-
- OCLint
- misplaced null check
-
-
- OCLint
- misplaced nil check
-
-
- OCLint
- missing break in switch statement
-
-
- OCLint
- multiple unary operator
-
-
- OCLint
- high ncss method
-
-
- OCLint
- deep nested block
-
-
- OCLint
- non case label in switch statement
-
-
- OCLint
- high npath complexity
-
-
- OCLint
- replace with boxed expression
-
-
- OCLint
- replace with container literal
-
-
- OCLint
- replace with number literal
-
-
- OCLint
- replace with object subscripting
-
-
- OCLint
- parameter reassignment
-
-
- OCLint
- redundant conditional operator
-
-
- OCLint
- redundant if statement
-
-
- OCLint
- redundant local variable
-
-
- OCLint
- redundant nil check
-
-
- OCLint
- return from finally block
-
-
- OCLint
- short variable name
-
-
- OCLint
- switch statements should have default
-
-
- OCLint
- throw exception from finally block
-
-
- OCLint
- too few branches in switch statement
-
-
- OCLint
- too many fields
-
-
- OCLint
- too many methods
-
-
- OCLint
- too many parameters
-
-
- OCLint
- unnecessary else statement
-
-
- OCLint
- unused local variable
-
-
- OCLint
- unused method parameter
-
-
- OCLint
- useless parentheses
-
-
+ OCLint
+ objc
+
+
+ OCLint
+ use early exits and continue
+
+
+ OCLint
+ avoid branching statement as last in loop
+
+
+ OCLint
+ bitwise operator in conditional
+
+
+ OCLint
+ broken null check
+
+
+ OCLint
+ broken nil check
+
+
+ OCLint
+ broken oddness check
+
+
+ OCLint
+ collapsible if statements
+
+
+ OCLint
+ constant conditional operator
+
+
+ OCLint
+ constant if expression
+
+
+ OCLint
+ high cyclomatic complexity
+
+
+ OCLint
+ dead code
+
+
+ OCLint
+ default label not last in switch statement
+
+
+ OCLint
+ double negative
+
+
+ OCLint
+ empty catch statement
+
+
+ OCLint
+ empty do/while statement
+
+
+ OCLint
+ empty else block
+
+
+ OCLint
+ empty finally statement
+
+
+ OCLint
+ empty for statement
+
+
+ OCLint
+ empty if statement
+
+
+ OCLint
+ empty switch statement
+
+
+ OCLint
+ empty try statement
+
+
+ OCLint
+ empty while statement
+
+
+ OCLint
+ feature envy
+
+
+ OCLint
+ for loop should be while loop
+
+
+ OCLint
+ goto statement
+
+
+ OCLint
+ inverted logic
+
+
+ OCLint
+ jumbled incrementer
+
+
+ OCLint
+ long class
+
+
+ OCLint
+ long line
+
+
+ OCLint
+ long method
+
+
+ OCLint
+ long variable name
+
+
+ OCLint
+ misplaced null check
+
+
+ OCLint
+ misplaced nil check
+
+
+ OCLint
+ missing break in switch statement
+
+
+ OCLint
+ multiple unary operator
+
+
+ OCLint
+ must override hash with isEqual
+
+
+ OCLint
+ high ncss method
+
+
+ OCLint
+ deep nested block
+
+
+ OCLint
+ non case label in switch statement
+
+
+ OCLint
+ high npath complexity
+
+
+ OCLint
+ replace with boxed expression
+
+
+ OCLint
+ replace with container literal
+
+
+ OCLint
+ replace with number literal
+
+
+ OCLint
+ replace with object subscripting
+
+
+ OCLint
+ parameter reassignment
+
+
+ OCLint
+ redundant conditional operator
+
+
+ OCLint
+ redundant if statement
+
+
+ OCLint
+ redundant local variable
+
+
+ OCLint
+ redundant nil check
+
+
+ OCLint
+ return from finally block
+
+
+ OCLint
+ short variable name
+
+
+ OCLint
+ switch statements don't need default when fully covered
+
+
+ OCLint
+ switch statements should have default
+
+
+ OCLint
+ throw exception from finally block
+
+
+ OCLint
+ too few branches in switch statement
+
+
+ OCLint
+ too many fields
+
+
+ OCLint
+ too many methods
+
+
+ OCLint
+ too many parameters
+
+
+ OCLint
+ unnecessary else statement
+
+
+ OCLint
+ unused local variable
+
+
+ OCLint
+ unused method parameter
+
+
+ OCLint
+ useless parentheses
+
+
+ OCLint
+ ivar assignment outside accessors or init
+
+
\ No newline at end of file
diff --git a/src/main/resources/org/sonar/plugins/oclint/rules.txt b/src/main/resources/org/sonar/plugins/oclint/rules.txt
index e97b7107..beb11d6b 100644
--- a/src/main/resources/org/sonar/plugins/oclint/rules.txt
+++ b/src/main/resources/org/sonar/plugins/oclint/rules.txt
@@ -3,525 +3,507 @@ Available issues:
OCLint
======
+use early exits and continue
+----------
+
+Summary:
+
+Severity: 2
+Category: OCLint
+
avoid branching statement as last in loop
----------
-Summary:
+Summary: Having branching statement as the last statement inside a loop is very confusing, and could largely be forgetting of something and turning into a bug.
-Priority: 2
Severity: 2
Category: OCLint
bitwise operator in conditional
----------
-Summary:
+Summary: Checks for bitwise operations in conditionals. Although being written on purpose in some rare cases, bitwise operations are considered to be too “smart”. Smart code is not easy to understand.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
broken null check
----------
-Summary:
+Summary: The broken nil check in Objective-C in some cases returns just the opposite result.
-Priority: 1
-Severity: 1
+Severity: 4
Category: OCLint
broken nil check
----------
-Summary:
+Summary:
-Priority: 2
-Severity: 2
+Severity: 4
Category: OCLint
broken oddness check
----------
-Summary:
+Summary: Checking oddness by x%2==1 won’t work for negative numbers. Use x&1==1, or x%2!=0 instead.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
collapsible if statements
----------
-Summary:
+Summary: This rule detects instances where the conditions of two consecutive if statements can combined into one in order to increase code cleanness and readability.
-Priority: 3
Severity: 3
Category: OCLint
constant conditional operator
----------
-Summary:
+Summary: conditionaloperator whose conditionals are always true or always false are confusing.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
constant if expression
----------
-Summary:
+Summary: if statements whose conditionals are always true or always false are confusing.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
high cyclomatic complexity
----------
-Summary:
+Summary:
-Priority: 2
Severity: 2
Category: OCLint
dead code
----------
-Summary:
+Summary: Code after return, break, continue, and throw statements are unreachable and will never be executed.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
default label not last in switch statement
----------
-Summary:
+Summary: It is very confusing when default label is not the last label in a switch statement.
-Priority: 3
-Severity: 3
+Severity: 2
Category: OCLint
double negative
----------
-Summary:
+Summary: There is no point in using a double negative, it is always positive.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
empty catch statement
----------
-Summary:
+Summary: This rule detects instances where an exception is caught, but nothing is done about it.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
empty do/while statement
----------
-Summary:
+Summary: This rule detects instances where a do-while statement does nothing.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
empty else block
----------
-Summary:
+Summary:
-Priority: 2
Severity: 2
Category: OCLint
empty finally statement
----------
-Summary:
+Summary: This rule detects instances where a finally statement does nothing.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
empty for statement
----------
-Summary:
+Summary: This rule detects instances where a for statement does nothing.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
empty if statement
----------
-Summary:
+Summary: This rule detects instances where a condition is checked, but nothing is done about it.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
empty switch statement
----------
-Summary:
+Summary: This rule detects instances where a switch statement does nothing.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
empty try statement
----------
-Summary:
+Summary: This rule detects instances where a try statement is empty.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
empty while statement
----------
-Summary:
+Summary:
-Priority: 2
-Severity: 2
+Severity: 3
+Category: OCLint
+
+feature envy
+----------
+
+Summary:
+
+Severity: 3
Category: OCLint
for loop should be while loop
----------
-Summary:
+Summary: Under certain circumstances, some for loops can be simplified to while loops to make code more concise.
-Priority: 3
Severity: 3
Category: OCLint
goto statement
----------
-Summary:
+Summary:
-Priority: 3
Severity: 3
Category: OCLint
inverted logic
----------
-Summary:
+Summary: An inverted logic is hard to understand.
-Priority: 3
-Severity: 3
+Severity: 2
Category: OCLint
jumbled incrementer
----------
-Summary:
+Summary:
-Priority: 2
Severity: 2
Category: OCLint
long class
----------
-Summary:
+Summary: Long class generally indicates that this class tries to so many things. Each class should do one thing and one thing well.
-Priority: 3
Severity: 3
Category: OCLint
long line
----------
-Summary:
+Summary: When number of characters for one line of code is very long, it largely harm the readability. Break long line of code into multiple lines.
-Priority: 3
-Severity: 3
+Severity: 2
Category: OCLint
long method
----------
-Summary:
+Summary: Long method generally indicates that this method tries to so many things. Each method should do one thing and one thing well.
-Priority: 3
Severity: 3
Category: OCLint
long variable name
----------
-Summary:
+Summary: Variables with long names harm readability.
-Priority: 3
-Severity: 3
+Severity: 2
Category: OCLint
misplaced null check
----------
-Summary:
+Summary: The nil check is misplaced. In Objective-C, sending a message to a nil pointer simply does nothing. But code readers may be confused about the misplaced nil check.
-Priority: 1
-Severity: 1
+Severity: 3
Category: OCLint
misplaced nil check
----------
-Summary:
+Summary:
-Priority: 3
-Severity: 3
+Severity: 4
Category: OCLint
missing break in switch statement
----------
-Summary:
+Summary:
-Priority: 2
Severity: 2
Category: OCLint
multiple unary operator
----------
-Summary:
+Summary: Multiple unary operator can always be confusing and should be simplified.
-Priority: 2
-Severity: 2
+Severity: 3
+Category: OCLint
+
+must override hash with isEqual
+----------
+
+Summary:
+
+Severity: 1
Category: OCLint
high ncss method
----------
-Summary:
+Summary: This rule counts number of lines for a method by counting Non Commenting Source Statements (NCSS). NCSS only takes actual statements into consideration, in other words, ignores empty statements, empty blocks, closing brackets or semicolons after closing brackets. Meanwhile, statement that is break into multiple lines contribute only one count.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
deep nested block
----------
-Summary:
+Summary: This rule indicates blocks nested more deeply than the upper limit.
-Priority: 3
Severity: 3
Category: OCLint
non case label in switch statement
----------
-Summary:
+Summary: It is very confusing when default label is not the last label in a switch statement.
-Priority: 3
-Severity: 3
+Severity: 2
Category: OCLint
high npath complexity
----------
-Summary:
+Summary:
-Priority: 2
Severity: 2
Category: OCLint
replace with boxed expression
----------
-Summary:
+Summary: This rule locates the places that can be migrated to the new Objective-C literals with boxed expressions.
-Priority: 3
-Severity: 3
+Severity: 1
Category: OCLint
replace with container literal
----------
-Summary:
+Summary: This rule locates the places that can be migrated to the new Objective-C literals with container literals.
-Priority: 3
-Severity: 3
+Severity: 1
Category: OCLint
replace with number literal
----------
-Summary:
+Summary: This rule locates the places that can be migrated to the new Objective-C literals with number literals.
-Priority: 3
-Severity: 3
+Severity: 1
Category: OCLint
replace with object subscripting
----------
-Summary:
+Summary:
-Priority: 3
-Severity: 3
+Severity: 1
Category: OCLint
parameter reassignment
----------
-Summary:
+Summary: Reassigning values to parameters is very problematic in most cases.
-Priority: 3
-Severity: 3
+Severity: 2
Category: OCLint
redundant conditional operator
----------
-Summary:
+Summary: This rule detects three types of redundant conditional operators:
-Priority: 3
-Severity: 3
+Severity: 1
Category: OCLint
redundant if statement
----------
-Summary:
+Summary: This rule detects unnecessary if statements.
-Priority: 3
-Severity: 3
+Severity: 1
Category: OCLint
redundant local variable
----------
-Summary:
+Summary: This rule detects cases where a variable declaration immediately followed by a return of that variable.
-Priority: 3
-Severity: 3
+Severity: 1
Category: OCLint
redundant nil check
----------
-Summary:
+Summary:
-Priority: 3
Severity: 3
Category: OCLint
return from finally block
----------
-Summary:
+Summary: Returning from a finally block is not recommended.
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
short variable name
----------
-Summary:
+Summary:
+
+Severity: 2
+Category: OCLint
+
+switch statements don't need default when fully covered
+----------
+
+Summary:
-Priority: 3
Severity: 3
Category: OCLint
switch statements should have default
----------
-Summary:
+Summary: Switch statements should a default statement.
-Priority: 3
-Severity: 3
+Severity: 2
Category: OCLint
throw exception from finally block
----------
-Summary:
+Summary:
-Priority: 2
-Severity: 2
+Severity: 3
Category: OCLint
too few branches in switch statement
----------
-Summary:
+Summary:
-Priority: 3
-Severity: 3
+Severity: 2
Category: OCLint
too many fields
----------
-Summary:
+Summary: A class with too many fields indicates it does too many things and is lack of proper abstraction. It can be resigned to have fewer fields.
-Priority: 3
Severity: 3
Category: OCLint
too many methods
----------
-Summary:
+Summary: A class with too many methods indicates it does too many things and hard to read and understand. It usually contains complicated code, and should be refactored.
-Priority: 3
Severity: 3
Category: OCLint
too many parameters
----------
-Summary:
+Summary:
-Priority: 3
Severity: 3
Category: OCLint
unnecessary else statement
----------
-Summary:
+Summary: When an if statement block ends with a return statement, or all branches in the if statement block end with return statements, then the else statement is unnecessary. The code in the else statement can be run without being in the block.
-Priority: 3
-Severity: 3
+Severity: 1
Category: OCLint
unused local variable
----------
-Summary:
+Summary: This rule detects local variables that are declared, but not used.
-Priority: 3
-Severity: 3
+Severity: 0
Category: OCLint
unused method parameter
----------
-Summary:
+Summary:
-Priority: 3
-Severity: 3
+Severity: 0
Category: OCLint
useless parentheses
----------
-Summary:
+Summary:
-Priority: 3
-Severity: 3
+Severity: 1
+Category: OCLint
+
+ivar assignment outside accessors or init
+----------
+
+Summary:
+
+Severity: 2
Category: OCLint
diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh
index f6011029..e6753271 100755
--- a/src/main/shell/run-sonar.sh
+++ b/src/main/shell/run-sonar.sh
@@ -101,7 +101,8 @@ function runCommand() {
$command "$@"
fi
- if [[ $? != 0 && $? != 5 ]] ; then
+ returnValue=$?
+ if [[ $returnValue != 0 && $returnValue != 5 ]] ; then
stopProgress
echo "ERROR - Command '$command $@' failed with error code: $returnValue"
exit $?
@@ -286,7 +287,8 @@ if [ "$oclint" = "on" ]; then
fi
# Run OCLint with the right set of compiler options
- runCommand no oclint-json-compilation-database $includedCommandLineFlags -- -report-type pmd -o sonar-reports/oclint.xml
+ maxPriority=10000
+ runCommand no oclint-json-compilation-database $includedCommandLineFlags -- -max-priority-1 $maxPriority -max-priority-2 $maxPriority -max-priority-3 $maxPriority -report-type pmd -o sonar-reports/oclint.xml
else
echo 'Skipping OCLint (test purposes only!)'
fi
diff --git a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java b/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java
index 11bcdcfc..ea40f1f6 100644
--- a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java
+++ b/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java
@@ -27,7 +27,7 @@
import org.junit.Test;
import org.sonar.objectivec.api.ObjectiveCMetric;
-import org.sonar.squid.api.SourceFile;
+import org.sonar.squidbridge.api.SourceFile;
public class ObjectiveCAstScannerTest {
@@ -46,7 +46,6 @@ public void lines_of_code() {
@Test
public void comments() {
SourceFile file = ObjectiveCAstScanner.scanSingleFile(new File("src/test/resources/objcSample.h"));
- assertThat(file.getInt(ObjectiveCMetric.COMMENT_BLANK_LINES), is(3));
assertThat(file.getInt(ObjectiveCMetric.COMMENT_LINES), is(4));
assertThat(file.getNoSonarTagLines(), hasItem(10));
assertThat(file.getNoSonarTagLines().size(), is(1));
diff --git a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java
index 7210452a..8ac0339b 100644
--- a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java
+++ b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java
@@ -46,11 +46,16 @@ public final class CoberturaMeasuresPersistorTest {
@Test
public void shouldNotPersistMeasuresForUnknownFiles() {
final Project project = new Project("Test");
+
final SensorContext context = mock(SensorContext.class);
+ final ProjectFileSystem fileSystem = mock(ProjectFileSystem.class);
final Map measures = new HashMap();
measures.put("DummyResource", CoverageMeasuresBuilder.create());
- project.setFileSystem(mock(ProjectFileSystem.class));
+
+ when(fileSystem.getBasedir()).thenReturn(new File("."));
+
+ project.setFileSystem(fileSystem);
final CoverageMeasuresPersistor testedPersistor = new CoverageMeasuresPersistor(project, context);
testedPersistor.saveMeasures(measures);
@@ -64,7 +69,7 @@ public void shouldPersistMeasuresForKnownFiles() {
final org.sonar.api.resources.File dummyFile = new org.sonar.api.resources.File("dummy/test");
final SensorContext context = mock(SensorContext.class);
final ProjectFileSystem fileSystem = mock(ProjectFileSystem.class);
- final List sourceDirs = new ArrayList();
+ final List sourceDirs = new ArrayList();
final Map measures = new HashMap();
final CoverageMeasuresBuilder measureBuilder = CoverageMeasuresBuilder.create();
@@ -75,6 +80,7 @@ public void shouldPersistMeasuresForKnownFiles() {
when(fileSystem.getSourceDirs()).thenReturn(sourceDirs);
when(context.getResource(any(Resource.class))).thenReturn(dummyFile);
+ when(fileSystem.getBasedir()).thenReturn(new File("."));
project.setFileSystem(fileSystem);
@@ -82,7 +88,7 @@ public void shouldPersistMeasuresForKnownFiles() {
testedPersistor.saveMeasures(measures);
for (final Measure measure : measureBuilder.createMeasures()) {
- verify(context, times(1)).saveMeasure(eq(new org.sonar.api.resources.File("test")), eq(measure));
+ verify(context, times(1)).saveMeasure(eq(org.sonar.api.resources.File.fromIOFile(new File(project.getFileSystem().getBasedir(), "dummy/test"), project)), eq(measure));
}
}
diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintParserTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/OCLintParserTest.java
index f4e65ec7..12730013 100644
--- a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintParserTest.java
+++ b/src/test/java/org/sonar/plugins/objectivec/violations/OCLintParserTest.java
@@ -69,6 +69,7 @@ public void parseReportShouldReturnACollectionOfViolationsWhenTheReportIsNotEmpt
sourceDirs.add(new File("/dummy"));
when(fileSystem.getSourceDirs()).thenReturn(sourceDirs);
+ when(fileSystem.getBasedir()).thenReturn(new File("."));
when(context.getResource(any(Resource.class))).thenReturn(dummyFile);
project.setFileSystem(fileSystem);
diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandlerTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandlerTest.java
index 739c5185..888aeba1 100644
--- a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandlerTest.java
+++ b/src/test/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandlerTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -35,7 +36,9 @@
import org.apache.tools.ant.filters.StringInputStream;
import org.junit.Test;
import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.resources.Project;
+import org.sonar.api.resources.ProjectFileSystem;
import org.sonar.api.resources.Resource;
import org.sonar.api.rules.RulePriority;
import org.sonar.api.rules.Violation;
@@ -75,88 +78,12 @@ public void streamAddAviolationForALineInTheReport() throws XMLStreamException {
assertFalse(parseResults.isEmpty());
}
- @Test
- public void violationContainsFileResource() throws XMLStreamException {
- final org.sonar.api.resources.File dummyFile = new org.sonar.api.resources.File("test");
- givenAProject().containingSourceDirectory("dummy");
- final SensorContext context = mock(SensorContext.class);
-
- final List parseResults = new ArrayList();
- final StaxParser parser = new StaxParser(new OCLintXMLStreamHandler(parseResults, project(), context));
-
- when(context.getResource(any(Resource.class))).thenReturn(dummyFile);
-
- parser.parse(new StringInputStream(VALID_REPORT));
-
- assertEquals(dummyFile, parseResults.get(0).getResource());
- }
-
- @Test
- public void violationContainsTheMessageFromTheReport() throws XMLStreamException {
- final org.sonar.api.resources.File dummyFile = new org.sonar.api.resources.File("test");
- givenAProject().containingSourceDirectory("dummy");
- final SensorContext context = mock(SensorContext.class);
-
- final List parseResults = new ArrayList();
- final StaxParser parser = new StaxParser(new OCLintXMLStreamHandler(parseResults, project(), context));
-
- when(context.getResource(any(Resource.class))).thenReturn(dummyFile);
-
- parser.parse(new StringInputStream(VALID_REPORT));
-
- assertEquals(DESCRIPTION, parseResults.get(0).getMessage());
- }
-
- @Test
- public void violationContainsTheLineFromTheReport() throws XMLStreamException {
- final org.sonar.api.resources.File dummyFile = new org.sonar.api.resources.File("test");
- givenAProject().containingSourceDirectory("dummy");
- final SensorContext context = mock(SensorContext.class);
-
- final List parseResults = new ArrayList();
- final StaxParser parser = new StaxParser(new OCLintXMLStreamHandler(parseResults, project(), context));
-
- when(context.getResource(any(Resource.class))).thenReturn(dummyFile);
-
- parser.parse(new StringInputStream(VALID_REPORT));
-
- assertEquals(VIOLATION_LINE, parseResults.get(0).getLineId());
- }
-
- @Test
- public void violationRuleSeverityContainsThePriorityFromTheReport() throws XMLStreamException {
- final org.sonar.api.resources.File dummyFile = new org.sonar.api.resources.File("test");
- givenAProject().containingSourceDirectory("dummy");
- final SensorContext context = mock(SensorContext.class);
-
- final List parseResults = new ArrayList();
- final StaxParser parser = new StaxParser(new OCLintXMLStreamHandler(parseResults, project(), context));
-
- when(context.getResource(any(Resource.class))).thenReturn(dummyFile);
-
- parser.parse(new StringInputStream(VALID_REPORT));
-
- assertEquals(RulePriority.MAJOR, parseResults.get(0).getRule().getSeverity());
- }
-
- @Test
- public void violationRuleKeyContainsThePriorityFromTheReport() throws XMLStreamException {
- final org.sonar.api.resources.File dummyFile = new org.sonar.api.resources.File("test");
- givenAProject().containingSourceDirectory("dummy");
- final SensorContext context = mock(SensorContext.class);
-
- final List parseResults = new ArrayList();
- final StaxParser parser = new StaxParser(new OCLintXMLStreamHandler(parseResults, project(), context));
-
- when(context.getResource(any(Resource.class))).thenReturn(dummyFile);
-
- parser.parse(new StringInputStream(VALID_REPORT));
-
- assertEquals(RULE_KEY, parseResults.get(0).getRule().getKey());
- }
-
private Project project() {
- return projectBuilder.project();
+ Project project = givenAProject().project();
+ ProjectFileSystem fileSystem = mock(ProjectFileSystem.class);
+ project.setFileSystem(fileSystem);
+ when(fileSystem.getBasedir()).thenReturn(new File("."));
+ return project;
}
private ProjectBuilder givenAProject() {
diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/ProjectBuilder.java b/src/test/java/org/sonar/plugins/objectivec/violations/ProjectBuilder.java
index 46ab23ff..9f6bbebb 100644
--- a/src/test/java/org/sonar/plugins/objectivec/violations/ProjectBuilder.java
+++ b/src/test/java/org/sonar/plugins/objectivec/violations/ProjectBuilder.java
@@ -37,6 +37,7 @@ final class ProjectBuilder {
public ProjectBuilder() {
project.setFileSystem(fileSystem);
when(fileSystem.getSourceDirs()).thenReturn(sourceDirs);
+ when(fileSystem.getBasedir()).thenReturn(new File("."));
}
public Project project() {
diff --git a/updateRules.groovy b/updateRules.groovy
new file mode 100644
index 00000000..f3a86fda
--- /dev/null
+++ b/updateRules.groovy
@@ -0,0 +1,217 @@
+// Update rules.txt and profile-clint.xml from OCLint documentation
+// Severity is determined from the category
+
+@Grab(group='org.codehaus.groovy.modules.http-builder',
+ module='http-builder', version='0.7')
+
+import groovyx.net.http.*
+import groovy.xml.MarkupBuilder
+
+def splitCamelCase(value) {
+ value.replaceAll(
+ String.format("%s|%s|%s",
+ "(?<=[A-Z])(?=[A-Z][a-z])",
+ "(?<=[^A-Z])(?=[A-Z])",
+ "(?<=[A-Za-z])(?=[^A-Za-z])"
+ ),
+ " "
+ ).toLowerCase()
+}
+
+
+def parseCategory(url, name, severity) {
+
+ def result = []
+
+ def http = new HTTPBuilder(url)
+ def html = http.get([:])
+
+ def root = html."**".find { it.@id.toString().contains(name)}
+ root."**".findAll { it.@class.toString() == 'section'}.each {rule ->
+
+ def entry = [:]
+
+
+ def ruleName = splitCamelCase(rule.H2.text() - '¶').capitalize()
+
+ // Original name
+ entry.originalName = null
+ try {
+ def sourceHttp = new HTTPBuilder(rule."**".findAll {it.name() == 'A'}.last().@href)
+ def sourceHtml = sourceHttp.get[:]
+
+ def found = sourceHtml."**".find {it.name() == "TR" && it.text().contains("return\"")}.text()
+ def match = found =~ /"([^"]*)"/
+ entry.originalName = match[0][1]
+
+ } catch (Exception e) {
+
+ }
+
+ if (entry.originalName) {
+
+ // Name
+ entry.name = ruleName
+
+
+ println "Retrieving rule $entry.originalName"
+
+ // Summary
+ entry.summary = rule.P[1].text()
+
+ // Severity
+ entry.severity = severity
+
+ result.add entry
+ } else {
+ println "Unable to retrieve rule with name $entry.name"
+ }
+ }
+
+ result
+}
+
+def writeRulesTxt(rules, file) {
+
+ def text = "Available issues:\n" +
+ "\n" +
+ "OCLint\n" +
+ "======\n\n"
+
+ rules.each {rule ->
+ if (rule.name != '') {
+ text += rule.originalName + '\n'
+ text += '----------\n'
+ text += '\n'
+
+ // Summary
+ text += "Summary: $rule.summary\n"
+ text += '\n'
+
+ text += "Severity: $rule.severity\n"
+ text += "Category: OCLint\n"
+
+ text += '\n'
+ }
+ }
+
+ file.text = text
+}
+
+def readRulesTxt(file) {
+
+ def result = []
+
+ def previousLine = ''
+ def rule = null
+ file.eachLine {line ->
+
+ if (line.startsWith('--')) {
+ rule = [:]
+ rule.originalName = previousLine.trim()
+ rule.name = rule.originalName
+ }
+
+ if (line.startsWith('Summary:') && rule) {
+ rule.summary = (line - 'Summary:').trim()
+ }
+
+ if (line.startsWith('Severity:') && rule) {
+ rule.severity = Integer.parseInt((line - 'Severity:').trim())
+ }
+
+ if (line.startsWith('Category:') && rule) {
+ rule.category = (line - 'Category:').trim()
+ result.add rule
+ rule = null
+ }
+
+ previousLine = line
+ }
+
+ result
+
+}
+
+def writeProfileOCLint(rls, file) {
+ def writer = new StringWriter()
+ def xml = new MarkupBuilder(writer)
+ xml.profile() {
+ name "OCLint"
+ language "objc"
+ rules {
+ rls.each {rl ->
+ rule {
+ repositoryKey "OCLint"
+ key rl.originalName
+ }
+ }
+ }
+ }
+
+ file.text = "\n" + writer.toString()
+
+}
+
+def mergeRules(existingRules, freshRules) {
+
+ def result = []
+
+ // Update existing rules
+ existingRules.each {rule ->
+
+ def freshRule = freshRules.find {it.originalName?.trim() == rule.originalName?.trim()}
+ if (freshRule) {
+
+ println "Updating rule [$rule.originalName]"
+ rule.severity = freshRule.severity
+ rule.category = freshRule.category
+ rule.summary = freshRule.summary
+ }
+
+ if (!result.find {it.originalName?.trim() == rule.originalName?.trim()}) {
+ result.add rule
+ } else {
+ println "Skipping rule [$rule.originalName]"
+ }
+ }
+
+ // Add new rules (if any)
+ freshRules.each {rule ->
+
+ def existingRule = existingRules.find {it.originalName?.trim() == rule.originalName?.trim()}
+ if (!existingRule) {
+ result.add rule
+ }
+ }
+
+ result
+}
+
+// Files
+File rulesTxt = new File('src/main/resources/org/sonar/plugins/oclint/rules.txt')
+File profileXml = new File('src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml')
+
+
+// Parse OCLint online documentation
+def rules = []
+
+rules.addAll parseCategory("http://docs.oclint.org/en/dev/rules/basic.html", "basic", 3)
+rules.addAll parseCategory("http://docs.oclint.org/en/dev/rules/convention.html", "convention", 2)
+rules.addAll parseCategory("http://docs.oclint.org/en/dev/rules/empty.html", "empty", 3)
+rules.addAll parseCategory("http://docs.oclint.org/en/dev/rules/migration.html", "migration", 1)
+rules.addAll parseCategory("http://docs.oclint.org/en/dev/rules/naming.html", "naming", 2)
+rules.addAll parseCategory("http://docs.oclint.org/en/dev/rules/redundant.html", "redundant", 1)
+rules.addAll parseCategory("http://docs.oclint.org/en/dev/rules/size.html", "size", 3)
+rules.addAll parseCategory("http://docs.oclint.org/en/dev/rules/unused.html", "unused", 0)
+println "${rules.size()} rules found"
+
+
+// Read existing rules
+def existingRules = readRulesTxt(rulesTxt)
+
+// Update existing rules with fresh rules
+def finalRules = mergeRules(existingRules, rules)
+
+writeRulesTxt(finalRules, rulesTxt)
+writeProfileOCLint(finalRules, profileXml)
\ No newline at end of file