From 5ba12b00229aca77c742480b5264734160780ac0 Mon Sep 17 00:00:00 2001 From: Mete Balci Date: Thu, 19 Jul 2012 10:44:33 -0700 Subject: [PATCH 001/107] work started on lexer/parser --- .../objectivec/ObjectiveCAstScanner.java | 5 ++++ .../objectivec/lexer/ObjectiveCLexer.java | 28 ++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java b/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java index ef77ec41..154809ee 100644 --- a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java +++ b/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java @@ -38,6 +38,7 @@ import com.sonar.sslr.squid.metrics.CommentsVisitor; import com.sonar.sslr.squid.metrics.LinesOfCodeVisitor; import com.sonar.sslr.squid.metrics.LinesVisitor; +import com.sonar.sslr.squid.metrics.CounterVisitor; public class ObjectiveCAstScanner { @@ -99,6 +100,10 @@ public String getContents(String comment) { .withNoSonar(true) .withIgnoreHeaderComment(conf.getIgnoreHeaderComments()) .build()); + builder.withSquidAstVisitor(CounterVisitor. builder() + .setMetricDef(ObjectiveCMetric.STATEMENTS) + .subscribeTo(parser.getGrammar().statement) + .build()); return builder.build(); } diff --git a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java b/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java index 0c5fe95d..f93f6e33 100644 --- a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java +++ b/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java @@ -19,11 +19,12 @@ */ package org.sonar.objectivec.lexer; -import static com.sonar.sslr.api.GenericTokenType.LITERAL; import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.commentRegexp; import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.regexp; import org.sonar.objectivec.ObjectiveCConfiguration; +import org.sonar.objectivec.api.ObjectiveCKeyword; +import org.sonar.objectivec.api.ObjectiveCTokenType; import com.sonar.sslr.impl.Lexer; import com.sonar.sslr.impl.channel.BlackHoleChannel; @@ -41,15 +42,34 @@ public static Lexer create(ObjectiveCConfiguration conf) { return Lexer.builder() .withCharset(conf.getCharset()) - .withFailIfNoChannelToConsumeOneCharacter(false) + .withFailIfNoChannelToConsumeOneCharacter(true) // Comments .withChannel(commentRegexp("//[^\\n\\r]*+")) .withChannel(commentRegexp("/\\*[\\s\\S]*?\\*/")) - // All other tokens - .withChannel(regexp(LITERAL, "[^\r\n\\s/]+")) + // string literals + .withChannel(regexp(ObjectiveCTokenType.STRING_LITERAL, "\"([^\"\\\\]*+(\\\\[\\s\\S])?+)*+\"")) + // numeric literals + // integer/long + // decimal + .withChannel(regexp(ObjectiveCTokenType.NUMERIC_LITERAL, "[0-9]++[lL]?+")) + // hex + .withChannel(regexp(ObjectiveCTokenType.NUMERIC_LITERAL, "0[xX][0-9A-Fa-f]++[lL]?+")) + // float/double + // decimal + .withChannel(regexp(ObjectiveCTokenType.NUMERIC_LITERAL, "[0-9]++[fFdD]")) + + // identifiers and keywords + // identifiers starts with a non digit and underscore and continues with either one of these or with digits + // case sensitive = true + .withChannel(new IdentifierAndKeywordChannel(and("[a-zA-Z_]", o2n("\\w")), true, ObjectiveCKeyword.values())) + + // punctuators/operators + .withChannel(new PunctuatorChannel(ObjecticeCPunctuator.values())) + + // skip all whitespace chars .withChannel(new BlackHoleChannel("[\\s]")) .build(); From e952c02f9ad901c849c9041280e718fa7ee955dc Mon Sep 17 00:00:00 2001 From: Mete Balci Date: Thu, 19 Jul 2012 13:33:17 -0700 Subject: [PATCH 002/107] simple send message expression grammar rule is added and it seems to be working, older tests are either removed if they are wrong or commented out --- .../SendMessageExpressionTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/test/java/org/sonar/objectivec/parser/expressions/SendMessageExpressionTest.java diff --git a/src/test/java/org/sonar/objectivec/parser/expressions/SendMessageExpressionTest.java b/src/test/java/org/sonar/objectivec/parser/expressions/SendMessageExpressionTest.java new file mode 100644 index 00000000..350da231 --- /dev/null +++ b/src/test/java/org/sonar/objectivec/parser/expressions/SendMessageExpressionTest.java @@ -0,0 +1,46 @@ + /* + * Sonar Objective-C Plugin + * Copyright (C) 2012 François Helg, Cyril Picat and 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.javascript.parser.grammar.expressions; + +import com.sonar.sslr.impl.Parser; +import org.junit.Before; +import org.junit.Test; +import org.sonar.objectivec.api.ObjectiveCGrammar; +import org.sonar.objectivec.parser.ObjectiveCParser; + +import static com.sonar.sslr.test.parser.ParserMatchers.parse; +import static org.junit.Assert.assertThat; + +public class SendMessageExpressionTest { + + Parser p = ObjectiveCParser.create(); + ObjectiveCGrammar g = p.getGrammar(); + + @Before + public void init() { + p.setRootRule(g.sendMessageExpression); + } + + @Test + public void ok() { + assertThat(p, parse("[receiver message]")); + } + +} From 2cba5b8225a0343f76dc3684bcea2b202cf58ffd Mon Sep 17 00:00:00 2001 From: Mete Balci Date: Thu, 19 Jul 2012 22:27:34 -0700 Subject: [PATCH 003/107] more --- .../objectivec/api/ObjectiveCGrammar.java | 11 ++++++++-- .../objectivec/api/ObjectiveCTokenType.java | 1 + .../objectivec/lexer/ObjectiveCLexer.java | 7 ++++++- .../parser/ObjectiveCGrammarImpl.java | 19 ++++++++++++++++-- .../objectivec/ObjectiveCAstScannerTest.java | 3 ++- .../objectivec/lexer/ObjectiveCLexerTest.java | 20 +++++++++---------- 6 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java index 64d68ef9..5e43d5e3 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java +++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java @@ -27,16 +27,23 @@ public class ObjectiveCGrammar extends Grammar { public Rule identifierName; // A.1 Lexical - public Rule literal; public Rule nullLiteral; public Rule booleanLiteral; public Rule stringLiteral; - public Rule program; + public Rule messageReceiver; + public Rule messageSent; + + // Expressions + public Rule sendMessageExpression; + + // Statements + public Rule statement; public Rule sourceElements; public Rule sourceElement; + public Rule program; @Override public Rule getRootRule() { diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java index fdbb9fe8..24bdbe9f 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java +++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java @@ -24,6 +24,7 @@ public enum ObjectiveCTokenType implements TokenType { + STRING_LITERAL, NUMERIC_LITERAL; public String getName() { diff --git a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java b/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java index f93f6e33..32ab0f3f 100644 --- a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java +++ b/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java @@ -21,13 +21,18 @@ import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.commentRegexp; import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.regexp; +import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.o2n; +import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.and; import org.sonar.objectivec.ObjectiveCConfiguration; import org.sonar.objectivec.api.ObjectiveCKeyword; import org.sonar.objectivec.api.ObjectiveCTokenType; +import org.sonar.objectivec.api.ObjectiveCPunctuator; import com.sonar.sslr.impl.Lexer; import com.sonar.sslr.impl.channel.BlackHoleChannel; +import com.sonar.sslr.impl.channel.PunctuatorChannel; +import com.sonar.sslr.impl.channel.IdentifierAndKeywordChannel; public class ObjectiveCLexer { @@ -67,7 +72,7 @@ public static Lexer create(ObjectiveCConfiguration conf) { .withChannel(new IdentifierAndKeywordChannel(and("[a-zA-Z_]", o2n("\\w")), true, ObjectiveCKeyword.values())) // punctuators/operators - .withChannel(new PunctuatorChannel(ObjecticeCPunctuator.values())) + .withChannel(new PunctuatorChannel(ObjectiveCPunctuator.values())) // skip all whitespace chars .withChannel(new BlackHoleChannel("[\\s]")) diff --git a/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java b/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java index 6b4cdcc8..b4beb49f 100644 --- a/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java +++ b/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java @@ -20,16 +20,31 @@ package org.sonar.objectivec.parser; import static com.sonar.sslr.api.GenericTokenType.EOF; -import static com.sonar.sslr.api.GenericTokenType.LITERAL; +import static com.sonar.sslr.api.GenericTokenType.IDENTIFIER; import static com.sonar.sslr.impl.matcher.GrammarFunctions.Standard.o2n; +import static com.sonar.sslr.impl.matcher.GrammarFunctions.Standard.or; +import static com.sonar.sslr.impl.matcher.GrammarFunctions.Standard.and; import org.sonar.objectivec.api.ObjectiveCGrammar; +import static org.sonar.objectivec.api.ObjectiveCPunctuator.LBRACKET; +import static org.sonar.objectivec.api.ObjectiveCPunctuator.RBRACKET; + +import static org.sonar.objectivec.api.ObjectiveCTokenType.STRING_LITERAL; +import static org.sonar.objectivec.api.ObjectiveCTokenType.NUMERIC_LITERAL; + public class ObjectiveCGrammarImpl extends ObjectiveCGrammar { public ObjectiveCGrammarImpl() { - program.is(o2n(LITERAL), EOF); + messageReceiver.is(IDENTIFIER); + messageSent.is(IDENTIFIER); + + sendMessageExpression.is(and(LBRACKET, messageReceiver, messageSent, RBRACKET)); + + statement.is(or(sendMessageExpression)); + + program.is(statement); } diff --git a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java b/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java index 06f4cba8..471358cb 100644 --- a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java +++ b/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java @@ -30,7 +30,7 @@ import org.sonar.squid.api.SourceFile; public class ObjectiveCAstScannerTest { - + /* @Test public void lines() { SourceFile file = ObjectiveCAstScanner.scanSingleFile(new File("src/test/resources/objcSample.h")); @@ -51,4 +51,5 @@ public void comments() { assertThat(file.getNoSonarTagLines(), hasItem(10)); assertThat(file.getNoSonarTagLines().size(), is(1)); } + */ } diff --git a/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java b/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java index 3f1f8903..cbf00c7b 100644 --- a/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java +++ b/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java @@ -34,6 +34,9 @@ import com.sonar.sslr.api.Token; import com.sonar.sslr.impl.Lexer; +import org.sonar.objectivec.api.ObjectiveCTokenType; +import org.sonar.objectivec.api.ObjectiveCKeyword; + public class ObjectiveCLexerTest { private static Lexer lexer; @@ -61,23 +64,18 @@ public void lexEndOflineComment() { assertThat(lexer.lex("[self init]; //"), hasComment("//")); } - @Test - public void lexLineOfCode() { - assertThat(lexer.lex("[self init];"), hasToken("[self", GenericTokenType.LITERAL)); - } - @Test public void lexEmptyLine() { List tokens = lexer.lex("\n"); assertThat(tokens.size(), equalTo(1)); - assertThat(tokens, hasToken(GenericTokenType.EOF)); + assertThat(tokens, hasToken(GenericTokenType.EOF)); } - @Test - public void lexSampleFile() { - List tokens = lexer.lex(new File("src/test/resources/objcSample.h")); - assertThat(tokens.size(), equalTo(16)); - assertThat(tokens, hasToken(GenericTokenType.EOF)); + public void lexInclude() { + List tokens = lexer.lex("#include \"Test.h\""); + assertThat(tokens.size(), equalTo(2)); + assertThat(tokens, hasToken(ObjectiveCKeyword.HASH_INCLUDE)); + assertThat(tokens, hasToken(ObjectiveCTokenType.STRING_LITERAL)); } } From 8f891db4511c56931d9693487a88986645daaa9e Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 12 Feb 2015 23:39:10 +0100 Subject: [PATCH 004/107] Added FauxPas Sensor --- pom.xml | 6 + .../plugins/objectivec/ObjectiveCPlugin.java | 28 +- .../violations/fauxpas/FauxPasProfile.java | 62 ++ .../fauxpas/FauxPasProfileImporter.java | 60 ++ .../fauxpas/FauxPasReportParser.java | 119 +++ .../violations/fauxpas/FauxPasRuleParser.java | 69 ++ .../fauxpas/FauxPasRuleRepository.java | 65 ++ .../violations/fauxpas/FauxPasSensor.java | 90 +++ .../violations/{ => oclint}/OCLintParser.java | 2 +- .../{ => oclint}/OCLintProfile.java | 2 +- .../{ => oclint}/OCLintProfileImporter.java | 9 +- .../{ => oclint}/OCLintRuleParser.java | 2 +- .../{ => oclint}/OCLintRuleRepository.java | 2 +- .../violations/{ => oclint}/OCLintSensor.java | 2 +- .../{ => oclint}/OCLintXMLStreamHandler.java | 2 +- .../sonar/plugins/fauxpas/profile-fauxpas.xml | 415 ++++++++++ .../org/sonar/plugins/fauxpas/rules.json | 716 ++++++++++++++++++ .../fauxpas/FauxPasRuleParserTest.java | 55 ++ .../violations/fauxpas/FauxPasSensorTest.java | 77 ++ .../{ => oclint}/OCLintParserTest.java | 3 +- .../{ => oclint}/OCLintSensorTest.java | 3 +- .../OCLintXMLStreamHandlerTest.java | 5 +- .../{ => oclint}/ProjectBuilder.java | 2 +- updateFauxPasRules.groovy | 92 +++ ...teRules.groovy => updateOCLintRules.groovy | 20 +- 25 files changed, 1873 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java rename src/main/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintParser.java (97%) rename src/main/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintProfile.java (97%) rename src/main/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintProfileImporter.java (89%) rename src/main/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintRuleParser.java (98%) rename src/main/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintRuleRepository.java (97%) rename src/main/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintSensor.java (98%) rename src/main/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintXMLStreamHandler.java (98%) create mode 100644 src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml create mode 100644 src/main/resources/org/sonar/plugins/fauxpas/rules.json create mode 100644 src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java create mode 100644 src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java rename src/test/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintParserTest.java (96%) rename src/test/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintSensorTest.java (95%) rename src/test/java/org/sonar/plugins/objectivec/violations/{ => oclint}/OCLintXMLStreamHandlerTest.java (96%) rename src/test/java/org/sonar/plugins/objectivec/violations/{ => oclint}/ProjectBuilder.java (96%) create mode 100644 updateFauxPasRules.groovy rename updateRules.groovy => updateOCLintRules.groovy (85%) diff --git a/pom.xml b/pom.xml index 140dd4eb..5883e30c 100644 --- a/pom.xml +++ b/pom.xml @@ -152,6 +152,12 @@ 1.6 + + com.googlecode.json-simple + json-simple + 1.1.1 + + junit junit diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java index 8fa3b673..01bd773c 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java @@ -34,14 +34,18 @@ 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; -import org.sonar.plugins.objectivec.violations.OCLintRuleRepository; -import org.sonar.plugins.objectivec.violations.OCLintSensor; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfile; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfileImporter; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasRuleRepository; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasSensor; +import org.sonar.plugins.objectivec.violations.oclint.OCLintProfile; +import org.sonar.plugins.objectivec.violations.oclint.OCLintProfileImporter; +import org.sonar.plugins.objectivec.violations.oclint.OCLintRuleRepository; +import org.sonar.plugins.objectivec.violations.oclint.OCLintSensor; @Properties({ @Property(key = CoberturaSensor.REPORT_PATTERN_KEY, defaultValue = CoberturaSensor.DEFAULT_REPORT_PATTERN, name = "Path to unit test coverage report(s)", description = "Relative to projects' root. Ant patterns are accepted", global = false, project = true), - @Property(key = OCLintSensor.REPORT_PATH_KEY, defaultValue = OCLintSensor.DEFAULT_REPORT_PATH, name = "Path to oclint pmd formatted report", description = "Relative to projects' root.", global = false, project = true) + @Property(key = OCLintSensor.REPORT_PATH_KEY, defaultValue = OCLintSensor.DEFAULT_REPORT_PATH, name = "Path to fauxpas pmd formatted report", description = "Relative to projects' root.", global = false, project = true) }) public class ObjectiveCPlugin extends SonarPlugin { @@ -55,9 +59,16 @@ public List> getExtensions() { ObjectiveCProfile.class, SurefireSensor.class, CoberturaSensor.class, + OCLintRuleRepository.class, - OCLintSensor.class, OCLintProfile.class, - OCLintProfileImporter.class + OCLintSensor.class, + OCLintProfile.class, + OCLintProfileImporter.class, + + FauxPasSensor.class, + FauxPasRuleRepository.class, + FauxPasProfile.class, + FauxPasProfileImporter.class ); } @@ -69,8 +80,7 @@ public List> getExtensions() { public static final String PROPERTY_PREFIX = "sonar.objectivec"; - public static final String TEST_FRAMEWORK_KEY = PROPERTY_PREFIX - + ".testframework"; + public static final String TEST_FRAMEWORK_KEY = PROPERTY_PREFIX + ".testframework"; public static final String TEST_FRAMEWORK_DEFAULT = "ghunit"; } diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java new file mode 100644 index 00000000..70ae682f --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java @@ -0,0 +1,62 @@ +/* + * 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.violations.fauxpas; + +import com.google.common.io.Closeables; +import org.slf4j.LoggerFactory; +import org.sonar.api.profiles.ProfileDefinition; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.plugins.objectivec.core.ObjectiveC; +import org.sonar.plugins.objectivec.violations.oclint.OCLintRuleRepository; + +import java.io.InputStreamReader; +import java.io.Reader; + +/** + * Created by gillesgrousset on 12/02/15. + */ +public class FauxPasProfile extends ProfileDefinition { + + private static final String DEFAULT_PROFILE = "/org/sonar/plugins/fauxpas/profile-fauxpas.xml"; + private final FauxPasProfileImporter profileImporter; + + public FauxPasProfile(final FauxPasProfileImporter importer) { + profileImporter = importer; + } + + @Override + public RulesProfile createProfile(ValidationMessages messages) { + LoggerFactory.getLogger(getClass()).info("Creating FauxPas Profile"); + Reader config = null; + + try { + config = new InputStreamReader(getClass().getResourceAsStream( + DEFAULT_PROFILE)); + final RulesProfile profile = profileImporter.importProfile(config, messages); + profile.setName(FauxPasRuleRepository.REPOSITORY_KEY); + profile.setLanguage(ObjectiveC.KEY); + + return profile; + } finally { + Closeables.closeQuietly(config); + } + } +} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java new file mode 100644 index 00000000..bdb1cc17 --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java @@ -0,0 +1,60 @@ +/* + * 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.violations.fauxpas; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.profiles.ProfileImporter; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.profiles.XMLProfileParser; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.plugins.objectivec.core.ObjectiveC; + +import java.io.Reader; + +/** + * Created by gillesgrousset on 12/02/15. + */ +public class FauxPasProfileImporter extends ProfileImporter { + + private static final String UNABLE_TO_LOAD_DEFAULT_PROFILE = "Unable to load default FauxPas profile"; + private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasProfileImporter.class); + + private final XMLProfileParser profileParser; + + public FauxPasProfileImporter(final XMLProfileParser xmlProfileParser) { + super(FauxPasRuleRepository.REPOSITORY_KEY, FauxPasRuleRepository.REPOSITORY_KEY); + setSupportedLanguages(ObjectiveC.KEY); + profileParser = xmlProfileParser; + } + + @Override + public RulesProfile importProfile(Reader reader, ValidationMessages messages) { + + final RulesProfile profile = profileParser.parse(reader, messages); + + if (null == profile) { + messages.addErrorText(UNABLE_TO_LOAD_DEFAULT_PROFILE); + LOGGER.error(UNABLE_TO_LOAD_DEFAULT_PROFILE); + } + + return profile; + } +} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java new file mode 100644 index 00000000..e4b97dfa --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -0,0 +1,119 @@ +/* + * 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.violations.fauxpas; + +import org.apache.commons.io.IOUtils; +import org.codehaus.staxmate.in.SMInputCursor; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.resources.Project; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.rules.Violation; +import org.sonar.plugins.objectivec.violations.oclint.OCLintRuleRepository; + +import javax.xml.stream.XMLStreamException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.Collection; + +/** + * Created by gillesgrousset on 12/02/15. + */ +public class FauxPasReportParser { + + private final Project project; + private final SensorContext context; + + private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasReportParser.class); + + public FauxPasReportParser(final Project p, final SensorContext c) { + project = p; + context = c; + } + + public Collection parseReport(File reportFile) { + + final Collection violations = new ArrayList(); + + try { + // Read and parse report + FileReader fr = new FileReader(reportFile); + Object reportObj = JSONValue.parse(fr); + IOUtils.closeQuietly(fr); + + // Record violations + if (reportObj != null) { + + JSONObject reportJson = (JSONObject)reportObj; + JSONArray diagnosticsJson = (JSONArray)reportJson.get("diagnostics"); + + for (Object obj : diagnosticsJson) { + recordViolation((JSONObject)obj, violations); + + } + } + + } catch (FileNotFoundException e) { + LOGGER.error("Failed to parse FauxPas report file", e); + } + + + return violations; + } + + private void recordViolation(final JSONObject diagnosticJson, Collection violations) { + + String filePath = (String)diagnosticJson.get("file"); + + if (filePath != null) { + + org.sonar.api.resources.File resource = org.sonar.api.resources.File.fromIOFile(new File(filePath), project); + + final Rule rule = Rule.create(); + final Violation violation = Violation.create(rule, resource); + + rule.setRepositoryKey(FauxPasRuleRepository.REPOSITORY_KEY); + rule.setKey((String)diagnosticJson.get("ruleShortName")); + + violation.setMessage((String)diagnosticJson.get("info")); + + + JSONObject extent = (JSONObject)diagnosticJson.get("extent"); + JSONObject start = (JSONObject)extent.get("start"); + + int lineNumber = Integer.parseInt(start.get("line").toString()); + if (lineNumber > 0) { + violation.setLineId(Integer.parseInt(start.get("line").toString())); + } + + violations.add(violation); + + } + + } + +} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java new file mode 100644 index 00000000..a86016f3 --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java @@ -0,0 +1,69 @@ +/* + * 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.violations.fauxpas; + +import org.apache.commons.io.IOUtils; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.ServerComponent; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RulePriority; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by gillesgrousset on 12/02/15. + */ +public class FauxPasRuleParser implements ServerComponent { + + private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasRuleParser.class); + + public List parse(final BufferedReader reader) throws IOException { + final List rules = new ArrayList(); + + String jsonString = IOUtils.toString(reader); + + Object rulesObj = JSONValue.parse(jsonString); + + if (rulesObj != null) { + JSONArray fpRules = (JSONArray)rulesObj; + for (Object obj : fpRules) { + JSONObject fpRule = (JSONObject)obj; + + Rule rule = Rule.create(); + rules.add(rule); + rule.setName((String) fpRule.get("name")); + rule.setKey((String) fpRule.get("key")); + rule.setDescription((String) fpRule.get("description")); + rule.setSeverity(RulePriority.valueOf((String) fpRule.get("severity"))); + } + } + + + return rules; + } +} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java new file mode 100644 index 00000000..78a3220b --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java @@ -0,0 +1,65 @@ +/* + * 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.violations.fauxpas; + +import com.google.common.io.Closeables; +import org.apache.commons.lang.CharEncoding; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleRepository; +import org.sonar.api.utils.SonarException; +import org.sonar.plugins.objectivec.core.ObjectiveC; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.List; + +/** + * Created by gillesgrousset on 12/02/15. + */ +public class FauxPasRuleRepository extends RuleRepository { + + public static final String REPOSITORY_KEY = "FauxPas"; + public static final String REPOSITORY_NAME = REPOSITORY_KEY; + + private static final String RULES_FILE = "/org/sonar/plugins/fauxpas/rules.json"; + + private final FauxPasRuleParser ruleParser = new FauxPasRuleParser(); + + public FauxPasRuleRepository() { + super(FauxPasRuleRepository.REPOSITORY_KEY, ObjectiveC.KEY); + setName(FauxPasRuleRepository.REPOSITORY_NAME); + } + + @Override + public List createRules() { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(getClass() + .getResourceAsStream(RULES_FILE), CharEncoding.UTF_8)); + return ruleParser.parse(reader); + } catch (final IOException e) { + throw new SonarException("Fail to load the default FauxPas rules.", + e); + } finally { + Closeables.closeQuietly(reader); + } + } +} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java new file mode 100644 index 00000000..05883ae3 --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java @@ -0,0 +1,90 @@ +/* + * 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.violations.fauxpas; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Project; +import org.sonar.api.rules.Violation; +import org.sonar.plugins.objectivec.ObjectiveCPlugin; +import org.sonar.plugins.objectivec.core.ObjectiveC; + +import java.io.File; +import java.util.Collection; + +/** + * Created by gillesgrousset on 12/02/15. + */ +public class FauxPasSensor implements Sensor { + + public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX + + ".fauxpas.report"; + public static final String DEFAULT_REPORT_PATH = "sonar-reports/fauxpas.json"; + + private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasSensor.class); + + private final Settings conf; + private final FileSystem fileSystem; + + public FauxPasSensor(final FileSystem moduleFileSystem, final Settings config) { + this.conf = config; + this.fileSystem = moduleFileSystem; + } + + @Override + public boolean shouldExecuteOnProject(final Project project) { + + return project.isRoot() && fileSystem.languages().contains(ObjectiveC.KEY); + } + @Override + public void analyse(Project module, SensorContext context) { + + final String projectBaseDir = module.getFileSystem().getBasedir().getPath(); + + FauxPasReportParser parser = new FauxPasReportParser(module, context); + saveViolations(parseReportIn(projectBaseDir, parser), context); + } + + private void saveViolations(final Collection violations, final SensorContext context) { + for (final Violation violation : violations) { + context.saveViolation(violation); + } + } + + private Collection parseReportIn(final String baseDir, final FauxPasReportParser parser) { + final StringBuilder reportFileName = new StringBuilder(baseDir); + reportFileName.append("/").append(reportPath()); + LOGGER.info("Processing FauxPas report {}", reportFileName); + return parser.parseReport(new File(reportFileName.toString())); + } + + private String reportPath() { + String reportPath = conf.getString(REPORT_PATH_KEY); + if (reportPath == null) { + reportPath = DEFAULT_REPORT_PATH; + } + return reportPath; + } + +} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java similarity index 97% rename from src/main/java/org/sonar/plugins/objectivec/violations/OCLintParser.java rename to src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java index a16b9278..b13b7207 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import java.io.File; import java.io.FileInputStream; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java similarity index 97% rename from src/main/java/org/sonar/plugins/objectivec/violations/OCLintProfile.java rename to src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java index 1e5684d8..7604b265 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import java.io.InputStreamReader; import java.io.Reader; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintProfileImporter.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java similarity index 89% rename from src/main/java/org/sonar/plugins/objectivec/violations/OCLintProfileImporter.java rename to src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java index ba174e58..9703ac1e 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintProfileImporter.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java @@ -17,10 +17,11 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import java.io.Reader; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.profiles.ProfileImporter; import org.sonar.api.profiles.RulesProfile; @@ -29,7 +30,10 @@ import org.sonar.plugins.objectivec.core.ObjectiveC; public final class OCLintProfileImporter extends ProfileImporter { + private static final String UNABLE_TO_LOAD_DEFAULT_PROFILE = "Unable to load default OCLint profile"; + private static final Logger LOGGER = LoggerFactory.getLogger(OCLintProfileImporter.class); + private final XMLProfileParser profileParser; public OCLintProfileImporter(final XMLProfileParser xmlProfileParser) { @@ -46,8 +50,7 @@ public RulesProfile importProfile(final Reader reader, if (null == profile) { messages.addErrorText(UNABLE_TO_LOAD_DEFAULT_PROFILE); - LoggerFactory.getLogger(OCLintProfileImporter.class).error( - UNABLE_TO_LOAD_DEFAULT_PROFILE); + LOGGER.error(UNABLE_TO_LOAD_DEFAULT_PROFILE); } return profile; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java similarity index 98% rename from src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleParser.java rename to src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java index 427090c0..19b67b10 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import java.io.BufferedReader; import java.io.IOException; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleRepository.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java similarity index 97% rename from src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleRepository.java rename to src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java index 47920cea..ee958508 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintRuleRepository.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import java.io.BufferedReader; import java.io.IOException; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java similarity index 98% rename from src/main/java/org/sonar/plugins/objectivec/violations/OCLintSensor.java rename to src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java index 4847905f..8787cad6 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import java.io.File; import java.util.Collection; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandler.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java similarity index 98% rename from src/main/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandler.java rename to src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java index 0847046b..2bfa20fb 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandler.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import java.io.File; import java.util.Collection; diff --git a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml new file mode 100644 index 00000000..c32547c7 --- /dev/null +++ b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml @@ -0,0 +1,415 @@ + + + FauxPas + objc + + + FauxPas + UndetachedDelegate + + + FauxPas + MallocWithoutSizeof + + + FauxPas + AssertionSideEffects + + + FauxPas + LoadMethodWithoutAutoreleasePool + + + FauxPas + InitializeSuperInvocation + + + FauxPas + MacroBasedIncludeGuard + + + FauxPas + RetainingImmutableProperty + + + FauxPas + OldVerboseObjCSyntax + + + FauxPas + StrongDelegate + + + FauxPas + ConstructorReturnType + + + FauxPas + SetterInvocationInInitOrDealloc + + + FauxPas + ArgumentModification + + + FauxPas + FastEnumElementOutside + + + FauxPas + UnusedErrorValue + + + FauxPas + CopyingMutableProperty + + + FauxPas + InstanceMethodWritesToStaticVariable + + + FauxPas + PrefixHeaderIncludeSuggestion + + + FauxPas + TerminatingApp + + + FauxPas + PrivateCategory + + + FauxPas + IBOutletsInPublicInterface + + + FauxPas + InitializeMethodCategoryOverride + + + FauxPas + ImplicitAtomicProperty + + + FauxPas + AssigningDelegate + + + FauxPas + Swizzling + + + FauxPas + DiscardedOpaqueNotificationObserver + + + FauxPas + ErrorConditionCheck + + + FauxPas + RedundantInclude + + + FauxPas + NSLogUsed + + + FauxPas + UnprefixedCategoryMethod + + + FauxPas + UnidiomaticAccessorNaming + + + FauxPas + ViewControllerInitWithNibName + + + FauxPas + RetinaImagesResolution + + + FauxPas + SuspiciousResources + + + FauxPas + UnknownResourceCodeReference + + + FauxPas + UnusedResource + + + FauxPas + XIBUnknownClassReference + + + FauxPas + XIBRuntimeAttributeMismatch + + + FauxPas + MissingDeviceTypeResource + + + FauxPas + MissingImageResolutionVariant + + + FauxPas + WeakReferenceToTopLevelXIBObject + + + FauxPas + UnknownResourceXIBReference + + + FauxPas + GlobalAndLocalizedResource + + + FauxPas + UnknownResourceModifier + + + FauxPas + SuspiciousMissingResources + + + FauxPas + DuplicateResource + + + FauxPas + BuildSettingsSetInGUI + + + FauxPas + CompilerWarnings + + + FauxPas + MissingAPIUsageDescription + + + FauxPas + BasicProjectSettings + + + FauxPas + ImplicitBundleId + + + FauxPas + ReleaseBuildCompilerArgs + + + FauxPas + XcconfigOverwrites + + + FauxPas + BuildSettingSelfReference + + + FauxPas + BuildSettingPlacement + + + FauxPas + AbsPathInBuildSetting + + + FauxPas + FileRefWithAbsPath + + + FauxPas + ReleaseBuildConfig + + + FauxPas + DylibInstallName + + + FauxPas + InvalidStringsFile + + + FauxPas + UnusedTranslation + + + FauxPas + StringsdictWithoutStrings + + + FauxPas + HardcodedUIString + + + FauxPas + MissingTranslation + + + FauxPas + DuplicateTranslation + + + FauxPas + TranslationPunctuation + + + FauxPas + TranslationFormatMismatch + + + FauxPas + UncommentedLocalizedString + + + FauxPas + UIKitKVO + + + FauxPas + APIAvailability + + + FauxPas + RestrictedDirectMethodCall + + + FauxPas + RestrictedMethodOverride + + + FauxPas + IsEqualAndHash + + + FauxPas + LiteralStringKeyPath + + + FauxPas + BlockAPIRetainCycle + + + FauxPas + MissingNotificationCenterDetachment + + + FauxPas + StringsFileEncoding + + + FauxPas + RecommendedVCSIgnores + + + FauxPas + FileRefOutsideVCS + + + FauxPas + FileRefIgnoredInVCS + + + FauxPas + NullCoalescingOp + + + FauxPas + StrongInsteadOfRetain + + + FauxPas + OrderedComparisonOpDirection + + + FauxPas + ThrowingObjCException + + + FauxPas + NewInitializer + + + FauxPas + NonTypedefBlockDeclaration + + + FauxPas + UnprefixedClass + + + FauxPas + DotSyntax + + + FauxPas + IdentifierNaming + + + FauxPas + MacroLiteral + + + FauxPas + UnnecessaryNullCheck + + + FauxPas + UnusedMethod + + + FauxPas + ReservedIdentifierNaming + + + FauxPas + MallocCast + + + FauxPas + ReservedPrefix + + + FauxPas + UsedVariableMarkedUnused + + + FauxPas + UnsupportedWeakReference + + + FauxPas + ThreadUnsafeInstanceCaching + + + FauxPas + UnnecessaryNibMethod + + + FauxPas + OrderedPointerToZeroComparison + + + FauxPas + FixedFormatDateFormatter + + + FauxPas + ZeroAssignmentToPointer + + + FauxPas + CategoryMethodConflict + + + \ No newline at end of file diff --git a/src/main/resources/org/sonar/plugins/fauxpas/rules.json b/src/main/resources/org/sonar/plugins/fauxpas/rules.json new file mode 100644 index 00000000..98d4cdda --- /dev/null +++ b/src/main/resources/org/sonar/plugins/fauxpas/rules.json @@ -0,0 +1,716 @@ +[ + { + "category": "BestPractice", + "key": "UndetachedDelegate", + "name": "Undetached delegate or data source UndetachedDelegate", + "description": "When an object sets itself as the delegate or data source of one of its members, it must detach that reference in its -[NSObject dealloc] method.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "MallocWithoutSizeof", + "name": "Memory allocation without using sizeofMallocWithoutSizeof", + "description": "The sizeof operator should be used to obtain the correct size for any allocated structure or variable \u2014 the sizes of some data types vary between the 32-bit and 64-bit runtimes.This rule is na\u00EFve and may produce false positives in many cases.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "AssertionSideEffects", + "name": "Potential assertion side effects AssertionSideEffects", + "description": "Warns if assertion macro bodies contain invocations of non-getter methods or functions.Assertions should not have side effects because they are normally disabled in release builds \u2014 the program\u2019s behavior should not be dependent on whether assertions are enabled or not.This rule considers only Foundation assertion macros.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "LoadMethodWithoutAutoreleasePool", + "name": "+[NSObject load] method without an @autoreleasepoolLoadMethodWithoutAutoreleasePool", + "description": "When the +[NSObject load] method is executed, there may not be an autorelease pool in place, so you must set one up yourself.This only applies to iOS 5 (and earlier) and OS X 10.7 (and earlier).", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "InitializeSuperInvocation", + "name": "Invocation of superclass implementation of +[NSObject initialize]InitializeSuperInvocation", + "description": "Implementations of +[NSObject initialize] should not invoke the superclass implementation: this method is special in that the runtime invokes it separately for every subclass.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "MacroBasedIncludeGuard", + "name": "Macro-based include guard MacroBasedIncludeGuard", + "description": "Simplify the header by replacing the macro definition check -based include guard with the \u2018once\u2019 pragma.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "RetainingImmutableProperty", + "name": "Non-copying property of immutable NSCopying type RetainingImmutableProperty", + "description": "Warns if \u201Cretaining\u201D semantics are specified in a @property declaration for a common immutable NSCopying class type that also has a mutable subclass variant (for example NSString).This rule helps avoid situations where the value of a property could change without the setter being invoked (i.e. the object gets mutated).", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "OldVerboseObjCSyntax", + "name": "Old, verbose Objective-C syntax OldVerboseObjCSyntax", + "description": "Warns if Objective-C literals, boxed expressions and subscript notation are not used whenever possible.Note that Xcode can automatically fix your code to do this (see the Edit \u2192 Refactor \u2192 Convert To Modern Objective-C Syntax\u2026 menu item).", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "StrongDelegate", + "name": "Retaining or copying delegate StrongDelegate", + "description": "To avoid retain cycles, delegate setter methods should not retain or copy the delegate object.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "ConstructorReturnType", + "name": "Constructor return type ConstructorReturnType", + "description": "Warns if an init or factory method return type is anything other than instancetype.Properly specifying the return type for factory methods (e.g. +[NSArray arrayWithObject:]) allows the compiler to perform more type checking, which helps catch bugs. Even though the compiler automatically determines the return types of init methods (even if they are declared as id) it is still good to be explicit and use instancetype.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "SetterInvocationInInitOrDealloc", + "name": "Setter invocation in init or dealloc method SetterInvocationInInitOrDealloc", + "description": "It is recommended to avoid invoking setter methods on self in init methods, or in -[NSObject dealloc]. Doing so might trigger KVC notifications, which others might observe, and expect the object to be in a valid, consistent state.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "ArgumentModification", + "name": "Modifying the value of an argument variable ArgumentModification", + "description": "Argument variable values should not be modified directly \u2014 code is easier to follow if you can always trust that argument variable values do not change.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "FastEnumElementOutside", + "name": "Element variable of fast enumeration loop declared outside the loop FastEnumElementOutside", + "description": "The element variable of a fast enumeration loop should not be used for anything other than holding the loop iteration element, so it follows that there is no reason for the variable to be visible outside the loop.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "UnusedErrorValue", + "name": "Unused error value UnusedErrorValue", + "description": "Warns when NSError or CFErrorRef variable values are never used.If you are not interested in the error return value of a method or function, pass NULL for the error argument.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "CopyingMutableProperty", + "name": "Copying property of mutable NSCopying type CopyingMutableProperty", + "description": "Warns if \u201Ccopying\u201D semantics are specified in a @property declaration for a common mutable NSCopying class type that has an immutable superclass (for example NSMutableArray).Invoking -[NSCopying copy] for such objects will in many cases return an immutable copy. This means that after assigning a mutable object value to such a property, it may in fact get an immutable copy of the assigned value (which will lead to crashes as soon as any attempt is made to mutate this immutable object).", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "InstanceMethodWritesToStaticVariable", + "name": "Instance method writes to static variable InstanceMethodWritesToStaticVariable", + "description": "Writing to static variables (which is essentially global state) in instance methods is generally considered bad practice, because it easily leads to undesireable behavior when multiple instances are being manipulated.Warnings are suppressed for assignments occurring inside dispatch_once() blocks.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "PrefixHeaderIncludeSuggestion", + "name": "Moving common inclusions into prefix header PrefixHeaderIncludeSuggestion", + "description": "Moving commonly used header inclusion directives into a precompiled prefix header improves compilation time.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "TerminatingApp", + "name": "Terminating the app in a release build TerminatingApp", + "description": "The iOS Human Interface Guidelines say that you should never quit an iOS app programmatically.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "PrivateCategory", + "name": "Category used for \u201Cprivate\u201D declarations PrivateCategory", + "description": "A class extension should be used instead, because they enable compiler warnings for detecting unimplemented methods.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "IBOutletsInPublicInterface", + "name": "IBOutlets in public interface IBOutletsInPublicInterface", + "description": "IBOutlets are often private implementation details, and should thus be in a private class extension.This rule warns only about cases where the class is the \u201CFile\u2019s Owner\u201D of a XIB whose basename is the same as the class name, and that contains a connection to the outlet.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "InitializeMethodCategoryOverride", + "name": "Overridden +[NSObject initialize] method in a category InitializeMethodCategoryOverride", + "description": "The +[NSObject initialize] method should not be overridden in categories, because this will prevent the original implementation from being executed. In many cases, +[NSObject load] may be used instead \u2014 it will be executed separately for classes and categories.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "ImplicitAtomicProperty", + "name": "Implicitly atomic property ImplicitAtomicProperty", + "description": "Warns for implicitly atomic properties.If neither the atomic keyword nor the nonatomic keyword is specified, a property is synthesized as atomic by default.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "AssigningDelegate", + "name": "Assigning delegate property AssigningDelegate", + "description": "Delegate properties should be declared weak instead of assign. Weak references are safer because they become nil when the referenced object is deallocated.This rule only applies to code compiled with Automatic Reference Counting (ARC).", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "Swizzling", + "name": "Method swizzling Swizzling", + "description": "Warns whenever there are calls to Objective-C runtime API functions that are used to change method implementations.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "DiscardedOpaqueNotificationObserver", + "name": "Discarded opaque NSNotificationCenter observer DiscardedOpaqueNotificationObserver", + "description": "Warns if the return value of -[NSNotificationCenter addObserverForName:object:queue:usingBlock:] is not stored anywhere. This method returns an opaque observer object that is needed in order to stop observing.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "ErrorConditionCheck", + "name": "Fragile error condition check ErrorConditionCheck", + "description": "Methods that pass back NSError objects by reference return NO or nil to indicate an error condition. This rule warns if the NSError pointer is checked against nil without checking the return value.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "RedundantInclude", + "name": "Redundant inclusion directive RedundantInclude", + "description": "If a header is included in the project\u2019s prefix header, it needn\u2019t be included anywhere else.This rule does not check header files that are themselves included in the precompiled prefix header.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "NSLogUsed", + "name": "NSLog() used in release build NSLogUsed", + "description": "Logging should be disabled in release builds (except perhaps for fatal errors).", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "UnprefixedCategoryMethod", + "name": "Unprefixed category method UnprefixedCategoryMethod", + "description": "Category methods on system classes must be prefixed in order to avoid name collisions.By default, this rule considers a method name prefixed if it begins with at least three lowercase characters (or the lowercase version of the \u2018Class Prefix\u2019 configured for the project), followed by an underscore.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "UnidiomaticAccessorNaming", + "name": "Unidiomatic accessor naming UnidiomaticAccessorNaming", + "description": "Warns if getter methods are named in the form getSomething instead of something.", + "severity": "MAJOR" + }, + { + "category": "BestPractice", + "key": "ViewControllerInitWithNibName", + "name": "Using -[UIViewController initWithNibName:bundle:] outside the UIViewController implementation ViewControllerInitWithNibName", + "description": "It is not advisable to use -[UIViewController initWithNibName:bundle:] to instantiate UIViewController subclasses outside of the subclass implementation itself. This breaks encapsulation (the subclass should be the one to decide which NIB to use) and makes it more likely that a typo in the NIB name string will crash the app during runtime.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "RetinaImagesResolution", + "name": "Unexpected retina image resolution RetinaImagesResolution", + "description": "The retina (\u2018@2x\u2019 or \u2018@3x\u2019) version of an image resource must be exactly double or triple the size of the corresponding low-resolution image (assuming the point size of the presentation context remains the same regardless of screen resolution). Otherwise the effective resolution of the image will not match the effective resolution of its presentation context, which will lead to a blurry image, empty space at the edges, or overflow.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "SuspiciousResources", + "name": "Suspicious bundle resources SuspiciousResources", + "description": "Some files that are part of the project but only needed at build/development time (e.g. Xcode build configuration (.xcconfig) files) may be added as bundle resources by mistake.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "UnknownResourceCodeReference", + "name": "Code refers to unknown resource UnknownResourceCodeReference", + "description": "Resources loaded from disk at runtime should be included in the bundle resources under the correct path. Remember that unlike most Macs (and thus iOS simulators run on those Macs), iOS devices have a case sensitive filesystem.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "UnusedResource", + "name": "Possibly unused resource UnusedResource", + "description": "Warns about resource files that do not seem to be referred to in code or XIBs.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "XIBUnknownClassReference", + "name": "XIB Reference to unknown class XIBUnknownClassReference", + "description": "Warns if an Interface Builder file refers to an unknown Objective-C class.This rule will warn about all references to classes that are not declared in the source code files that Faux Pas checks, or exported in linked libraries.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "XIBRuntimeAttributeMismatch", + "name": "XIB User-defined runtime attribute mismatch XIBRuntimeAttributeMismatch", + "description": "Warns when a XIB specifies user-defined runtime attributes that don\u2019t match the source code of the associated class.A key path that doesn\u2019t match any of the class properties or setter methods yields a diagnostic, as does a mismatch in the type of the assigned value and the value type declared for the property or setter.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "MissingDeviceTypeResource", + "name": "Missing device type resource MissingDeviceTypeResource", + "description": "Warns if a device type is not covered by the variants of a resource file. E.g. if image~iphone.png exists but image~ipad.png does not.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "MissingImageResolutionVariant", + "name": "Missing image resolution variant MissingImageResolutionVariant", + "description": "Warns when either the high-resolution (@2x or @3x) or the low-resolution variant of an image resource is missing.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "WeakReferenceToTopLevelXIBObject", + "name": "Weak reference to top-level XIB object WeakReferenceToTopLevelXIBObject", + "description": "IBOutlet references to top-level objects in XIBs should be strong, so as to ensure that the object is not deallocated prematurely.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "UnknownResourceXIBReference", + "name": "XIB refers to unknown resource UnknownResourceXIBReference", + "description": "Resources loaded from disk at runtime should be included in the bundle resources under the correct path. Remember that unlike most Macs (and thus iOS simulators run on those Macs), iOS devices have a case sensitive filesystem.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "GlobalAndLocalizedResource", + "name": "Global and localized resource GlobalAndLocalizedResource", + "description": "There should never be both a global and localized version of a given resource, because global resources take precedence over language-specific resources. If a global version of a resource exists, language-specific versions of the same resource are never returned by the platform APIs.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "UnknownResourceModifier", + "name": "Unknown resource file name modifier UnknownResourceModifier", + "description": "Device type modifiers for device-specific resource files (like ~iphone or ~ipad), as well as the resolution modifier @2x, must be spelled correctly (and case sensitively, due to the case-sensitive filesystem on iOS devices).", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "SuspiciousMissingResources", + "name": "Suspicious missing bundle resources SuspiciousMissingResources", + "description": "Warns when the project contains certain types of files that are not included into any target as resources, but probably should be.", + "severity": "MAJOR" + }, + { + "category": "Resources", + "key": "DuplicateResource", + "name": "Duplicate resource DuplicateResource", + "description": "Warns if two resource files are equal.", + "severity": "MAJOR" + }, + { + "category": "Config", + "key": "BuildSettingsSetInGUI", + "name": "Build settings set in Xcode GUI BuildSettingsSetInGUI", + "description": "Enable this rule if you want to set all of your build configuration values in .xcconfig files, and get warnings whenever something gets accidentally set in the GUI.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "CompilerWarnings", + "name": "Recommended compiler warning options CompilerWarnings", + "description": "Some compiler warnings and warning groups are so useful that they should be enabled at all times.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "MissingAPIUsageDescription", + "name": "Missing API usage description MissingAPIUsageDescription", + "description": "Some APIs (that access e.g. contacts or calendars) require a usage description in the application metadata. This will be shown to the user when the system asks them to allow the application access to the related user data or system resource. This rule warns when some such APIs are used, but the associated usage description is missing.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "BasicProjectSettings", + "name": "Recommended project settings BasicProjectSettings", + "description": "The \u2018Organization\u2019 and \u2018Class Prefix\u2019 settings should always be set. The expected values for other settings are configurable.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "ImplicitBundleId", + "name": "Implicit bundle identifier ImplicitBundleId", + "description": "It is recommended to choose an explicit value for the bundle identifier: an explicit, matching value must be chosen in iTunes Connect before publishing in the App Store (Xcode by default makes the bundle ID dependent on the PRODUCT_NAME variable).", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "ReleaseBuildCompilerArgs", + "name": "Problematic release build compiler arguments ReleaseBuildCompilerArgs", + "description": "For release builds, warns when some expected arguments are missing, or some unexpected arguments are present.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "XcconfigOverwrites", + "name": "Xcode build configuration file overwrites previously set value XcconfigOverwrites", + "description": "Overwriting a previously set value in a build configuration file is often a sign of a mistake somewhere.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "BuildSettingSelfReference", + "name": "Xcode build setting refers to itself BuildSettingSelfReference", + "description": "Build settings should not refer to themselves. The $(inherited) keyword should be used to append something to an inherited value, and custom intermediary build setting keys should be used to compose values from multiple definitions in an .xcconfig file.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "BuildSettingPlacement", + "name": "Incorrect placement of build setting value BuildSettingPlacement", + "description": "Warns if preprocessor definitions are defined explicitly using the -D argument instead of defining them in the GCC_PREPROCESSOR_DEFINITIONS (\u201CPreprocessor macros\u201D) build setting.Also warns if non-warning flags are defined in WARNING_CFLAGS (instead of OTHER_CFLAGS).", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "AbsPathInBuildSetting", + "name": "Absolute path in build setting value AbsPathInBuildSetting", + "description": "The project build configurations should not contain absolute path references. This will become a problem as soon as the project is moved to another developer\u2019s computer.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "FileRefWithAbsPath", + "name": "Project reference to file using absolute path FileRefWithAbsPath", + "description": "The project should not contain references to files using absolute paths. This will become a problem as soon as the project is moved to another developer\u2019s computer.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "ReleaseBuildConfig", + "name": "Problematic release build settings ReleaseBuildConfig", + "description": "Warns when certain problematic build setting values are used for release builds.For example, if program flow instrumentation is enabled, the compiler optimization level is too low, or if the static analyzer is not run.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "DylibInstallName", + "name": "Dynamic library install name DylibInstallName", + "description": "Warns if the install names of dynamic libraries are absolute (@loader_path, @executable_path, or @rpath should be used instead.)You can use install_name_tool to change dynamic library install names.", + "severity": "MINOR" + }, + { + "category": "Localization", + "key": "InvalidStringsFile", + "name": "Invalid string resource file InvalidStringsFile", + "description": "String resource files should be valid dictionary-format property lists.", + "severity": "MAJOR" + }, + { + "category": "Localization", + "key": "UnusedTranslation", + "name": "Unused translation UnusedTranslation", + "description": "Warns about entries in string resource files that are not directly loaded in code (using NSLocalizedString and its variants).", + "severity": "MAJOR" + }, + { + "category": "Localization", + "key": "StringsdictWithoutStrings", + "name": "Stringsdict file without matching string resource file StringsdictWithoutStrings", + "description": "If a .stringsdict file is included as a resource, a .strings file with the same name also needs to be included, even if empty \u2014 otherwise translations will not be read from the .stringsdict file.", + "severity": "MAJOR" + }, + { + "category": "Localization", + "key": "HardcodedUIString", + "name": "UI String not localized HardcodedUIString", + "description": "All strings that are given to APIs that display them in the user interface should be routed through NSLocalizedString() and friends.", + "severity": "MAJOR" + }, + { + "category": "Localization", + "key": "MissingTranslation", + "name": "Missing translation MissingTranslation", + "description": "Warns if a translation is missing for a key that is used as argument to NSLocalizedString or its variants.", + "severity": "MAJOR" + }, + { + "category": "Localization", + "key": "DuplicateTranslation", + "name": "Duplicate translation DuplicateTranslation", + "description": "Warns if a string resource file contains multiple entries with the same key.", + "severity": "MAJOR" + }, + { + "category": "Localization", + "key": "TranslationPunctuation", + "name": "Translation border punctuation mismatch TranslationPunctuation", + "description": "Warns if the punctuation at the start or end of translation strings differs between languages.", + "severity": "MAJOR" + }, + { + "category": "Localization", + "key": "TranslationFormatMismatch", + "name": "Translation format mismatch TranslationFormatMismatch", + "description": "Warns when different language translations for the same key contain mismatched format specifiers.", + "severity": "MAJOR" + }, + { + "category": "Localization", + "key": "UncommentedLocalizedString", + "name": "Uncommented localized string UncommentedLocalizedString", + "description": "Warns if NSLocalizedString() or one of its variants is used without a non-empty comment argument.", + "severity": "MAJOR" + }, + { + "category": "APIUsage", + "key": "UIKitKVO", + "name": "Observing UIKit object using KVO UIKitKVO", + "description": "The classes of the UIKit framework generally do not support Key-Value Observing (KVO). If KVO does happen to work for some properties, it is undefined behavior and thus not guaranteed to work in future versions.", + "severity": "CRITICAL" + }, + { + "category": "APIUsage", + "key": "APIAvailability", + "name": "Use of API not available in the minimum deployment target APIAvailability", + "description": "Warns about usages of system APIs that are not available in the minimum deployment target that your project target is configured to support.", + "severity": "CRITICAL" + }, + { + "category": "APIUsage", + "key": "RestrictedDirectMethodCall", + "name": "Restricted direct method call RestrictedDirectMethodCall", + "description": "Some methods of system classes should not be called directly.This rule will not warn about overriding methods that call the superclass implementation.", + "severity": "CRITICAL" + }, + { + "category": "APIUsage", + "key": "RestrictedMethodOverride", + "name": "Restricted method override RestrictedMethodOverride", + "description": "Some methods of system classes should not be overridden.", + "severity": "CRITICAL" + }, + { + "category": "APIUsage", + "key": "IsEqualAndHash", + "name": "Class implements -isEqual: but not -hashIsEqualAndHash", + "description": "If -isEqual: determines that two objects are equal, they must have the same hash value.", + "severity": "CRITICAL" + }, + { + "category": "APIUsage", + "key": "LiteralStringKeyPath", + "name": "Literal string for key path LiteralStringKeyPath", + "description": "Warns when literal strings are used to specify key paths.Something like NSStringFromSelector(@selector(foo)) is safer because it makes the compiler aware of the selector being specified \u2014 this helps find typos at compile time and allows automatic refactoring tools to make appropriate changes.", + "severity": "CRITICAL" + }, + { + "category": "APIUsage", + "key": "BlockAPIRetainCycle", + "name": "Retain cycle in block API usage BlockAPIRetainCycle", + "description": "Warns if self is referenced in a block that is used in a context where it is known to cause a retain cycle.The Clang compiler\u2019s -Warc-retain-cycles warning implements generic retain cycle detection \u2014 this rule only considers specific known system framework APIs that the compiler warning does not warn about.", + "severity": "CRITICAL" + }, + { + "category": "APIUsage", + "key": "MissingNotificationCenterDetachment", + "name": "Missing NSNotificationCenter observer detachment MissingNotificationCenterDetachment", + "description": "You must invoke -[NSNotificationCenter removeObserver:] or -[NSNotificationCenter removeObserver:name:object:] before the observer object is deallocated.This rule only considers cases where the observer reference is self, or an instance variable or an Objective-C property in self.", + "severity": "CRITICAL" + }, + { + "category": "VCS", + "key": "StringsFileEncoding", + "name": "String resource file is not UTF-8 StringsFileEncoding", + "description": "Git prefers UTF-8 encoding, and doesn\u2019t handle some other encodings (e.g. UTF-16) very well. It thinks UTF-16 -encoded files are binary, and will thus not display line diffs for them.", + "severity": "INFO" + }, + { + "category": "VCS", + "key": "RecommendedVCSIgnores", + "name": "Recommended VCS ignores RecommendedVCSIgnores", + "description": "Warns whenever some files that are recommended to be ignored by the version control system are not ignored, or vice versa.", + "severity": "INFO" + }, + { + "category": "VCS", + "key": "FileRefOutsideVCS", + "name": "Project reference to file outside version control root FileRefOutsideVCS", + "description": "The project should not contain references to files that are outside the version control working copy root.", + "severity": "INFO" + }, + { + "category": "VCS", + "key": "FileRefIgnoredInVCS", + "name": "Project reference to file ignored in the VCS FileRefIgnoredInVCS", + "description": "The project should not contain references to files that are ignored by the version control system.", + "severity": "INFO" + }, + { + "category": "Style", + "key": "NullCoalescingOp", + "name": "Null coalescing operator usage NullCoalescingOp", + "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "StrongInsteadOfRetain", + "name": "Usage of retain in ARC code StrongInsteadOfRetain", + "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "OrderedComparisonOpDirection", + "name": "Direction of ordered comparison operators OrderedComparisonOpDirection", + "description": "Warns if the > or >= operators are used (the < or <= operators should be used instead). This is based on the idea that it is easier to read comparisons where the smaller values are on the left.", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "ThrowingObjCException", + "name": "Throwing an Objective-C exception ThrowingObjCException", + "description": "Throwing exceptions in Objective-C is not very idiomatic, and should only be reserved for truly exceptional cases (if that,) and never for flow control.This rule can help you enforce a policy to never throw exceptions.", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "NewInitializer", + "name": "Shortcut initializer NewInitializer", + "description": "Some people prefer [[NSObject alloc] init] instead of [NSObject new], or vice versa.", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "NonTypedefBlockDeclaration", + "name": "Block-typed declaration without typedef NonTypedefBlockDeclaration", + "description": "It is recommended that typedef be used for all block-typed declarations, for readability.", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "UnprefixedClass", + "name": "Unprefixed Objective-C class UnprefixedClass", + "description": "Warns when an Objective-C class name has no prefix (e.g. Thing instead of FPXThing).", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "DotSyntax", + "name": "Dot syntax usage DotSyntax", + "description": "Warns about [obj foo] or [obj setFoo:x] instead of obj.foo or obj.foo = x.Can also be configured to enforce dot syntax only for accessors declared with the @property syntax.", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "IdentifierNaming", + "name": "Identifier naming IdentifierNaming", + "description": "This rule allows enforcing custom naming guidelines for different kinds of identifiers via regular expressions.", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "MacroLiteral", + "name": "Macro definition for literal value MacroLiteral", + "description": "Instead of using a macro definition for a literal value that is used as a constant, define it as an actual constant.This makes the scope of the constant more explicit (it\u2019s not available in all imported files until undefined) and it cannot be redefined or undefined in some later part of the code. Macro definitions are also not available in the debugger.", + "severity": "MAJOR" + }, + { + "category": "Pedantic", + "key": "UnnecessaryNullCheck", + "name": "Unnecessary NULL check before free()UnnecessaryNullCheck", + "description": "Passing NULL to free() is a no-op.", + "severity": "MINOR" + }, + { + "category": "Pedantic", + "key": "UnusedMethod", + "name": "Possibly unused Objective-C method UnusedMethod", + "description": "Warns about Objective-C methods to which no references are found.This rule will not warn about IBAction methods, initializer methods, or public methods (methods with a declaration in a header file, excluding header files named in the form *+Private.h). It also won\u2019t warn about unused getter methods whose setter is used, and vice versa.", + "severity": "MINOR" + }, + { + "category": "Pedantic", + "key": "ReservedIdentifierNaming", + "name": "Reserved identifier name ReservedIdentifierNaming", + "description": "Warns when identifiers are named using conventions reserved by the C standard or POSIX.", + "severity": "MINOR" + }, + { + "category": "Pedantic", + "key": "MallocCast", + "name": "Casting the return value of malloc()MallocCast", + "description": "In C, it is not recommended to explicitly cast the return value of malloc() (and other related memory allocation functions.)This rule does not warn about casts that occur in compilation units compiled in C++ (or Objective-C++) mode.", + "severity": "MINOR" + }, + { + "category": "Pedantic", + "key": "ReservedPrefix", + "name": "Reserved symbol prefix ReservedPrefix", + "description": "Two-character prefixes (such as NS) are reserved for Apple\u2019s system frameworks. Your own code should use prefixes that are three characters long.", + "severity": "MINOR" + }, + { + "category": "Pedantic", + "key": "UsedVariableMarkedUnused", + "name": "Using a variable marked unused UsedVariableMarkedUnused", + "description": "Warns when a variable is annotated with the \u201Cunused\u201D attribute, but is actually used. This rule does not warn about cases where a variable is marked unused via a pragma directive.", + "severity": "MINOR" + }, + { + "category": "Miscellaneous", + "key": "UnsupportedWeakReference", + "name": "Assignment of weak-unavailable object to a weak property UnsupportedWeakReference", + "description": "In OS X, you cannot create weak references to instances of some classes (see the full list from Apple\u2019s documentation.)The Clang compiler should warn about assignments of such objects to weak variables.", + "severity": "MINOR" + }, + { + "category": "Miscellaneous", + "key": "ThreadUnsafeInstanceCaching", + "name": "Globally caching a thread-unsafe class instance ThreadUnsafeInstanceCaching", + "description": "Warns about variables with global storage whose type is an object pointer to a known thread-unsafe class.-[NSThread threadDictionary] can be used to cache instances of thread-unsafe classes \u2014 one instance for each thread.This rule does not warn about code occuring in subclasses of (or categories on) UIKit or AppKit classes, because it can be assumed that these are always accessed solely from the main thread.", + "severity": "MINOR" + }, + { + "category": "Miscellaneous", + "key": "UnnecessaryNibMethod", + "name": "Unnecessary Nib method UnnecessaryNibMethod", + "description": "Warns if -[NSObject awakeFromNib] is implemented in a class that is not archived in any known XIB.", + "severity": "MINOR" + }, + { + "category": "Miscellaneous", + "key": "OrderedPointerToZeroComparison", + "name": "Ordered comparison of pointer and zero OrderedPointerToZeroComparison", + "description": "Warns whenever a pointer value is compared to zero using an ordered comparison operator.The Clang compiler should warn about similar comparisons to values other than zero.", + "severity": "MINOR" + }, + { + "category": "Miscellaneous", + "key": "FixedFormatDateFormatter", + "name": "Fixed-format NSDateFormatter not using invariant (POSIX) locale FixedFormatDateFormatter", + "description": "Warns when an NSDateFormatter is used with fixed-format dates without using the invariant en_US_POSIX locale. If any other locale is used, the date format string may be overwritten, depending on system date and time settings.When working with user-visible dates, date and time styles should be used instead of setting a date format.", + "severity": "MINOR" + }, + { + "category": "Miscellaneous", + "key": "ZeroAssignmentToPointer", + "name": "Assignment of literal zero to pointer ZeroAssignmentToPointer", + "description": "Assigning the literal zero to a pointer value might be an indication of a mistake where the programmer actually wanted to assign to an integer-typed member of the pointer type value. Assigning NULL, nil, or Nil instead suppresses warnings from this rule.", + "severity": "MINOR" + }, + { + "category": "Miscellaneous", + "key": "CategoryMethodConflict", + "name": "Conflicting category methods CategoryMethodConflict", + "description": "If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.", + "severity": "MINOR" + } +] \ No newline at end of file diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java new file mode 100644 index 00000000..5e6a6f6d --- /dev/null +++ b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java @@ -0,0 +1,55 @@ +/* + * 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.violations.fauxpas; + +import org.junit.Test; +import org.sonar.api.rules.Rule; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Created by gillesgrousset on 12/02/15. + */ +public class FauxPasRuleParserTest { + + private static final String VALID_JSON_RULES = "[{\"category\":\"Localization\",\"key\":\"HardcodedUIString\",\"name\":\"UI String not localized\",\"description\":\"All strings that are given to APIs that display them in the user interface should be routed through NSLocalizedString() and friends.\",\"severity\":\"MAJOR\"}]"; + + @Test + public void parseShouldReturnAnEmptyListIfNoValidRulesFile() throws IOException { + FauxPasRuleParser parser = new FauxPasRuleParser(); + List rules = parser.parse(new BufferedReader(new StringReader(""))); + + assertTrue(rules.isEmpty()); + } + + @Test + public void parseShouldReturnAListOfRulesIfValideFile() throws IOException { + + FauxPasRuleParser parser = new FauxPasRuleParser(); + List rules = parser.parse(new BufferedReader(new StringReader(VALID_JSON_RULES))); + + assertEquals(1, rules.size()); + } +} diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java new file mode 100644 index 00000000..bc5bb0b6 --- /dev/null +++ b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java @@ -0,0 +1,77 @@ +/* + * 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.violations.fauxpas; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Project; +import org.sonar.plugins.objectivec.core.ObjectiveC; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasSensor; + +import java.util.SortedSet; +import java.util.TreeSet; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Created by gillesgrousset on 12/02/15. + */ +public class FauxPasSensorTest { + + private Settings settings; + + @Before + public void setUp() { + settings = new Settings(); + } + + @Test + public void shouldExecuteOnProjectShouldBeTrueWhenProjectIsObjc() { + final Project project = new Project("Test"); + + FileSystem fileSystem = mock(FileSystem.class); + SortedSet languages = new TreeSet(); + languages.add(ObjectiveC.KEY); + when(fileSystem.languages()).thenReturn(languages); + + final FauxPasSensor testedSensor = new FauxPasSensor(fileSystem, settings); + + assertTrue(testedSensor.shouldExecuteOnProject(project)); + } + + @Test + public void shouldExecuteOnProjectShouldBeFalseWhenProjectIsSomethingElse() { + final Project project = new Project("Test"); + + FileSystem fileSystem = mock(FileSystem.class); + SortedSet languages = new TreeSet(); + languages.add("Test"); + when(fileSystem.languages()).thenReturn(languages); + + final FauxPasSensor testedSensor = new FauxPasSensor(fileSystem, settings); + + assertFalse(testedSensor.shouldExecuteOnProject(project)); + } +} diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintParserTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java similarity index 96% rename from src/test/java/org/sonar/plugins/objectivec/violations/OCLintParserTest.java rename to src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java index 12730013..5aa6cdcd 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintParserTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -37,6 +37,7 @@ import org.sonar.api.resources.ProjectFileSystem; import org.sonar.api.resources.Resource; import org.sonar.api.rules.Violation; +import org.sonar.plugins.objectivec.violations.oclint.OCLintParser; public class OCLintParserTest { private final String VALID_REPORT = "An operation on an Immutable object (String, BigDecimal or BigInteger) won't change the object itself"; diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java similarity index 95% rename from src/test/java/org/sonar/plugins/objectivec/violations/OCLintSensorTest.java rename to src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java index 52bd2ec8..019de2f0 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintSensorTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -30,6 +30,7 @@ import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; import org.sonar.plugins.objectivec.core.ObjectiveC; +import org.sonar.plugins.objectivec.violations.oclint.OCLintSensor; import java.util.SortedSet; import java.util.TreeSet; diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandlerTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java similarity index 96% rename from src/test/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandlerTest.java rename to src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java index 888aeba1..c0c410df 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/OCLintXMLStreamHandlerTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -29,18 +29,15 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; -import java.util.List; import javax.xml.stream.XMLStreamException; 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; import org.sonar.api.utils.StaxParser; diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/ProjectBuilder.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java similarity index 96% rename from src/test/java/org/sonar/plugins/objectivec/violations/ProjectBuilder.java rename to src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java index 9f6bbebb..b7d4994d 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/ProjectBuilder.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java @@ -17,7 +17,7 @@ * 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.violations; +package org.sonar.plugins.objectivec.violations.oclint; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/updateFauxPasRules.groovy b/updateFauxPasRules.groovy new file mode 100644 index 00000000..4983fe00 --- /dev/null +++ b/updateFauxPasRules.groovy @@ -0,0 +1,92 @@ +// Update profile-fauxpas.xml from Faux Pas online rules 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 +import groovy.json.JsonBuilder + +def parseRules(url, catMapping) { + + def result = [] + + def http = new HTTPBuilder(url) + http.contentEncoding = ContentEncoding.Type.GZIP + def html = http.get(contentType : 'text/html;charset=UTF-8') + + def categories = html."**".findAll {it.@class.toString().contains('tag-section')} + categories.each {cat -> + + def rules = cat."**".findAll {it.@class.toString().contains('rule')} + rules.each {r -> + + def rule = [ + category: cat.H2.text(), + key: r."**".find {it.@class.toString().contains("short-name")}.text(), + name: r.H3.text().trim().replaceAll('\\n', ' '), + description: r."**".find {it.@class.toString().contains("description")}.text().trim().replaceAll('\\n', ' '), + severity: catMapping[cat.H2.text()] + ] + + result.add(rule) + } + } + + + + return result +} + +def writeProfileFauxPas(rls, file) { + def writer = new StringWriter() + def xml = new MarkupBuilder(writer) + xml.profile() { + name "FauxPas" + language "objc" + rules { + rls.each {rl -> + rule { + repositoryKey "FauxPas" + key rl.key + } + } + } + } + + file.text = "\n" + writer.toString() + +} + +def writeRules(rls, file) { + + def builder = new JsonBuilder() + builder(rls) + + file.text = builder.toPrettyString() + +} + +// Files +File rulesJson = new File('src/main/resources/org/sonar/plugins/fauxpas/rules.json') +File profileXml = new File('src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml') + +// Parse online documentation +def rules = parseRules('http://fauxpasapp.com/rules/', [ + BestPractice: 'MAJOR', + Resources: 'MAJOR', + Config: 'MINOR', + Localization: 'MAJOR', + APIUsage: 'CRITICAL', + VCS: 'INFO', + Style: 'MAJOR', + Pedantic: 'MINOR', + Miscellaneous: 'MINOR' +]) + +// Write profile +writeProfileFauxPas(rules, profileXml) + +// Write rules +writeRules(rules, rulesJson) \ No newline at end of file diff --git a/updateRules.groovy b/updateOCLintRules.groovy similarity index 85% rename from updateRules.groovy rename to updateOCLintRules.groovy index f3a86fda..2812a8a2 100644 --- a/updateRules.groovy +++ b/updateOCLintRules.groovy @@ -189,21 +189,21 @@ def mergeRules(existingRules, freshRules) { } // 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') +File rulesTxt = new File('src/main/resources/org/sonar/plugins/fauxpas/rules.txt') +File profileXml = new File('src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.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) +rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/basic.html", "basic", 3) +rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/convention.html", "convention", 2) +rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/empty.html", "empty", 3) +rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/migration.html", "migration", 1) +rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/naming.html", "naming", 2) +rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/redundant.html", "redundant", 1) +rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/size.html", "size", 3) +rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/unused.html", "unused", 0) println "${rules.size()} rules found" From efc80bb23e56ccc270c794096acb77589372c125 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 16 Feb 2015 20:52:09 +0100 Subject: [PATCH 005/107] Added global 'Objective-C' profile with OCLint and FauxPas repos --- .../plugins/objectivec/ObjectiveCPlugin.java | 4 +- .../plugins/objectivec/ObjectiveCProfile.java | 42 ---------- .../violations/ObjectiveCProfile.java | 81 +++++++++++++++++++ .../violations/fauxpas/FauxPasProfile.java | 10 ++- .../violations/oclint/OCLintProfile.java | 10 ++- 5 files changed, 96 insertions(+), 51 deletions(-) delete mode 100644 src/main/java/org/sonar/plugins/objectivec/ObjectiveCProfile.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java index 01bd773c..27530277 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java @@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableList; import org.sonar.plugins.objectivec.tests.SurefireSensor; +import org.sonar.plugins.objectivec.violations.ObjectiveCProfile; import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfile; import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfileImporter; import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasRuleRepository; @@ -45,7 +46,8 @@ @Properties({ @Property(key = CoberturaSensor.REPORT_PATTERN_KEY, defaultValue = CoberturaSensor.DEFAULT_REPORT_PATTERN, name = "Path to unit test coverage report(s)", description = "Relative to projects' root. Ant patterns are accepted", global = false, project = true), - @Property(key = OCLintSensor.REPORT_PATH_KEY, defaultValue = OCLintSensor.DEFAULT_REPORT_PATH, name = "Path to fauxpas pmd formatted report", description = "Relative to projects' root.", global = false, project = true) + @Property(key = OCLintSensor.REPORT_PATH_KEY, defaultValue = OCLintSensor.DEFAULT_REPORT_PATH, name = "Path to oclint pmd formatted report", description = "Relative to projects' root.", global = false, project = true), + @Property(key = FauxPasSensor.REPORT_PATH_KEY, defaultValue = FauxPasSensor.DEFAULT_REPORT_PATH, name = "Path to fauxpas json formatted report", description = "Relative to projects' root.", global = false, project = true) }) public class ObjectiveCPlugin extends SonarPlugin { diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCProfile.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCProfile.java deleted file mode 100644 index e223a48c..00000000 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCProfile.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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; - -import org.sonar.api.profiles.AnnotationProfileParser; -import org.sonar.api.profiles.ProfileDefinition; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.utils.ValidationMessages; -import org.sonar.objectivec.checks.CheckList; -import org.sonar.plugins.objectivec.core.ObjectiveC; - -public class ObjectiveCProfile extends ProfileDefinition { - - private final AnnotationProfileParser annotationProfileParser; - - public ObjectiveCProfile(AnnotationProfileParser annotationProfileParser) { - this.annotationProfileParser = annotationProfileParser; - } - - @Override - public RulesProfile createProfile(ValidationMessages validation) { - return annotationProfileParser.parse(CheckList.REPOSITORY_KEY, CheckList.SONAR_WAY_PROFILE, ObjectiveC.KEY, CheckList.getChecks(), validation); - } - -} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java new file mode 100644 index 00000000..82688740 --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java @@ -0,0 +1,81 @@ +/* + * 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.violations; + +import com.google.common.io.Closeables; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.profiles.ProfileDefinition; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.plugins.objectivec.core.ObjectiveC; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfile; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfileImporter; +import org.sonar.plugins.objectivec.violations.oclint.OCLintProfile; +import org.sonar.plugins.objectivec.violations.oclint.OCLintProfileImporter; + +import java.io.InputStreamReader; +import java.io.Reader; + +public class ObjectiveCProfile extends ProfileDefinition { + + private static final Logger LOGGER = LoggerFactory.getLogger(ObjectiveCProfile.class); + + private final OCLintProfileImporter ocLintProfileImporter; + private final FauxPasProfileImporter fauxPasProfileImporter; + + public ObjectiveCProfile(final OCLintProfileImporter ocLintProfileImporter, final FauxPasProfileImporter fauxPasProfileImporter) { + this.ocLintProfileImporter = ocLintProfileImporter; + this.fauxPasProfileImporter = fauxPasProfileImporter; + } + + @Override + public RulesProfile createProfile(ValidationMessages messages) { + + + LOGGER.info("Creating Objective-C Profile"); + + Reader config = null; + final RulesProfile profile = RulesProfile.create("Objective-C", ObjectiveC.KEY); + profile.setDefaultProfile(true); + + try { + config = new InputStreamReader(getClass().getResourceAsStream(OCLintProfile.PROFILE_PATH)); + RulesProfile ocLintRulesProfile = ocLintProfileImporter.importProfile(config, messages); + for (ActiveRule rule : ocLintRulesProfile.getActiveRules()) { + profile.addActiveRule(rule); + } + + config = new InputStreamReader(getClass().getResourceAsStream(FauxPasProfile.PROFILE_PATH)); + RulesProfile fauxPasRulesProfile = fauxPasProfileImporter.importProfile(config, messages); + for (ActiveRule rule : fauxPasRulesProfile.getActiveRules()) { + profile.addActiveRule(rule); + } + + + return profile; + } finally { + + Closeables.closeQuietly(config); + } + } + +} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java index 70ae682f..4bd070b6 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java @@ -20,12 +20,12 @@ package org.sonar.plugins.objectivec.violations.fauxpas; import com.google.common.io.Closeables; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.profiles.ProfileDefinition; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.utils.ValidationMessages; import org.sonar.plugins.objectivec.core.ObjectiveC; -import org.sonar.plugins.objectivec.violations.oclint.OCLintRuleRepository; import java.io.InputStreamReader; import java.io.Reader; @@ -35,7 +35,9 @@ */ public class FauxPasProfile extends ProfileDefinition { - private static final String DEFAULT_PROFILE = "/org/sonar/plugins/fauxpas/profile-fauxpas.xml"; + public static final String PROFILE_PATH = "/org/sonar/plugins/fauxpas/profile-fauxpas.xml"; + private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasProfile.class); + private final FauxPasProfileImporter profileImporter; public FauxPasProfile(final FauxPasProfileImporter importer) { @@ -44,12 +46,12 @@ public FauxPasProfile(final FauxPasProfileImporter importer) { @Override public RulesProfile createProfile(ValidationMessages messages) { - LoggerFactory.getLogger(getClass()).info("Creating FauxPas Profile"); + LOGGER.info("Creating FauxPas Profile"); Reader config = null; try { config = new InputStreamReader(getClass().getResourceAsStream( - DEFAULT_PROFILE)); + PROFILE_PATH)); final RulesProfile profile = profileImporter.importProfile(config, messages); profile.setName(FauxPasRuleRepository.REPOSITORY_KEY); profile.setLanguage(ObjectiveC.KEY); diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java index 7604b265..e5b482f4 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java @@ -22,6 +22,7 @@ import java.io.InputStreamReader; import java.io.Reader; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.profiles.ProfileDefinition; import org.sonar.api.profiles.RulesProfile; @@ -32,7 +33,9 @@ public final class OCLintProfile extends ProfileDefinition { - private static final String DEFAULT_PROFILE = "/org/sonar/plugins/oclint/profile-oclint.xml"; + public static final String PROFILE_PATH = "/org/sonar/plugins/oclint/profile-oclint.xml"; + private static final Logger LOGGER = LoggerFactory.getLogger(OCLintProfile.class); + private final OCLintProfileImporter profileImporter; public OCLintProfile(final OCLintProfileImporter importer) { @@ -41,17 +44,16 @@ public OCLintProfile(final OCLintProfileImporter importer) { @Override public RulesProfile createProfile(final ValidationMessages messages) { - LoggerFactory.getLogger(getClass()).info("Creating OCLint Profile"); + LOGGER.info("Creating OCLint Profile"); Reader config = null; try { config = new InputStreamReader(getClass().getResourceAsStream( - DEFAULT_PROFILE)); + PROFILE_PATH)); final RulesProfile profile = profileImporter.importProfile(config, messages); profile.setName(OCLintRuleRepository.REPOSITORY_KEY); profile.setLanguage(ObjectiveC.KEY); - profile.setDefaultProfile(true); return profile; } finally { From 1db7260c261614b17fc4402842a8e0bd71ff9aba Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 17 Feb 2015 14:46:19 +0100 Subject: [PATCH 006/107] Run fauxpas command line in run-sonar.sh if installed --- .../violations/fauxpas/FauxPasReportParser.java | 7 +++++-- src/main/shell/run-sonar.sh | 11 +++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java index e4b97dfa..f2c25663 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -106,9 +106,12 @@ private void recordViolation(final JSONObject diagnosticJson, Collection 0) { - violation.setLineId(Integer.parseInt(start.get("line").toString())); + + // Avoid line 0 + if (lineNumber == 0) { + lineNumber++; } + violation.setLineId(Integer.parseInt(start.get("line").toString())); violations.add(violation); diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index fef70fd4..1c12546d 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -297,6 +297,17 @@ else echo 'Skipping OCLint (test purposes only!)' fi +hash fauxpas 2>/dev/null +if [ $? -eq 0 ]; then + + #FauxPas + echo -n 'Running FauxPas...' + fauxpas -t $appScheme -o json check $projectFile > sonar-reports/fauxpas.json + +else + echo 'Skipping FauxPas (not installed)' +fi + # SonarQube echo -n 'Running SonarQube using SonarQube Runner' runCommand /dev/stdout sonar-runner From ca85f2c74a9ad83a312663cbb7a46af0b0ce5590 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 17 Feb 2015 14:50:01 +0100 Subject: [PATCH 007/107] Removed key from rule name updateFauxPasRules.groovy --- .../org/sonar/plugins/fauxpas/rules.json | 204 +++++++++--------- updateFauxPasRules.groovy | 6 +- 2 files changed, 106 insertions(+), 104 deletions(-) diff --git a/src/main/resources/org/sonar/plugins/fauxpas/rules.json b/src/main/resources/org/sonar/plugins/fauxpas/rules.json index 98d4cdda..da6b4a0b 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/rules.json +++ b/src/main/resources/org/sonar/plugins/fauxpas/rules.json @@ -2,714 +2,714 @@ { "category": "BestPractice", "key": "UndetachedDelegate", - "name": "Undetached delegate or data source UndetachedDelegate", + "name": "Undetached delegate or data source", "description": "When an object sets itself as the delegate or data source of one of its members, it must detach that reference in its -[NSObject dealloc] method.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "MallocWithoutSizeof", - "name": "Memory allocation without using sizeofMallocWithoutSizeof", + "name": "Memory allocation without using sizeof", "description": "The sizeof operator should be used to obtain the correct size for any allocated structure or variable \u2014 the sizes of some data types vary between the 32-bit and 64-bit runtimes.This rule is na\u00EFve and may produce false positives in many cases.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "AssertionSideEffects", - "name": "Potential assertion side effects AssertionSideEffects", + "name": "Potential assertion side effects", "description": "Warns if assertion macro bodies contain invocations of non-getter methods or functions.Assertions should not have side effects because they are normally disabled in release builds \u2014 the program\u2019s behavior should not be dependent on whether assertions are enabled or not.This rule considers only Foundation assertion macros.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "LoadMethodWithoutAutoreleasePool", - "name": "+[NSObject load] method without an @autoreleasepoolLoadMethodWithoutAutoreleasePool", + "name": "+[NSObject load] method without an @autoreleasepool", "description": "When the +[NSObject load] method is executed, there may not be an autorelease pool in place, so you must set one up yourself.This only applies to iOS 5 (and earlier) and OS X 10.7 (and earlier).", "severity": "MAJOR" }, { "category": "BestPractice", "key": "InitializeSuperInvocation", - "name": "Invocation of superclass implementation of +[NSObject initialize]InitializeSuperInvocation", + "name": "Invocation of superclass implementation of +[NSObject initialize]", "description": "Implementations of +[NSObject initialize] should not invoke the superclass implementation: this method is special in that the runtime invokes it separately for every subclass.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "MacroBasedIncludeGuard", - "name": "Macro-based include guard MacroBasedIncludeGuard", + "name": "Macro-based include guard", "description": "Simplify the header by replacing the macro definition check -based include guard with the \u2018once\u2019 pragma.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "RetainingImmutableProperty", - "name": "Non-copying property of immutable NSCopying type RetainingImmutableProperty", + "name": "Non-copying property of immutable NSCopying type", "description": "Warns if \u201Cretaining\u201D semantics are specified in a @property declaration for a common immutable NSCopying class type that also has a mutable subclass variant (for example NSString).This rule helps avoid situations where the value of a property could change without the setter being invoked (i.e. the object gets mutated).", "severity": "MAJOR" }, { "category": "BestPractice", "key": "OldVerboseObjCSyntax", - "name": "Old, verbose Objective-C syntax OldVerboseObjCSyntax", + "name": "Old, verbose Objective-C syntax", "description": "Warns if Objective-C literals, boxed expressions and subscript notation are not used whenever possible.Note that Xcode can automatically fix your code to do this (see the Edit \u2192 Refactor \u2192 Convert To Modern Objective-C Syntax\u2026 menu item).", "severity": "MAJOR" }, { "category": "BestPractice", "key": "StrongDelegate", - "name": "Retaining or copying delegate StrongDelegate", + "name": "Retaining or copying delegate", "description": "To avoid retain cycles, delegate setter methods should not retain or copy the delegate object.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "ConstructorReturnType", - "name": "Constructor return type ConstructorReturnType", + "name": "Constructor return type", "description": "Warns if an init or factory method return type is anything other than instancetype.Properly specifying the return type for factory methods (e.g. +[NSArray arrayWithObject:]) allows the compiler to perform more type checking, which helps catch bugs. Even though the compiler automatically determines the return types of init methods (even if they are declared as id) it is still good to be explicit and use instancetype.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "SetterInvocationInInitOrDealloc", - "name": "Setter invocation in init or dealloc method SetterInvocationInInitOrDealloc", + "name": "Setter invocation in init or dealloc method", "description": "It is recommended to avoid invoking setter methods on self in init methods, or in -[NSObject dealloc]. Doing so might trigger KVC notifications, which others might observe, and expect the object to be in a valid, consistent state.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "ArgumentModification", - "name": "Modifying the value of an argument variable ArgumentModification", + "name": "Modifying the value of an argument variable", "description": "Argument variable values should not be modified directly \u2014 code is easier to follow if you can always trust that argument variable values do not change.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "FastEnumElementOutside", - "name": "Element variable of fast enumeration loop declared outside the loop FastEnumElementOutside", + "name": "Element variable of fast enumeration loop declared outside the loop", "description": "The element variable of a fast enumeration loop should not be used for anything other than holding the loop iteration element, so it follows that there is no reason for the variable to be visible outside the loop.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "UnusedErrorValue", - "name": "Unused error value UnusedErrorValue", + "name": "Unused error value", "description": "Warns when NSError or CFErrorRef variable values are never used.If you are not interested in the error return value of a method or function, pass NULL for the error argument.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "CopyingMutableProperty", - "name": "Copying property of mutable NSCopying type CopyingMutableProperty", + "name": "Copying property of mutable NSCopying type", "description": "Warns if \u201Ccopying\u201D semantics are specified in a @property declaration for a common mutable NSCopying class type that has an immutable superclass (for example NSMutableArray).Invoking -[NSCopying copy] for such objects will in many cases return an immutable copy. This means that after assigning a mutable object value to such a property, it may in fact get an immutable copy of the assigned value (which will lead to crashes as soon as any attempt is made to mutate this immutable object).", "severity": "MAJOR" }, { "category": "BestPractice", "key": "InstanceMethodWritesToStaticVariable", - "name": "Instance method writes to static variable InstanceMethodWritesToStaticVariable", + "name": "Instance method writes to static variable", "description": "Writing to static variables (which is essentially global state) in instance methods is generally considered bad practice, because it easily leads to undesireable behavior when multiple instances are being manipulated.Warnings are suppressed for assignments occurring inside dispatch_once() blocks.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "PrefixHeaderIncludeSuggestion", - "name": "Moving common inclusions into prefix header PrefixHeaderIncludeSuggestion", + "name": "Moving common inclusions into prefix header", "description": "Moving commonly used header inclusion directives into a precompiled prefix header improves compilation time.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "TerminatingApp", - "name": "Terminating the app in a release build TerminatingApp", + "name": "Terminating the app in a release build", "description": "The iOS Human Interface Guidelines say that you should never quit an iOS app programmatically.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "PrivateCategory", - "name": "Category used for \u201Cprivate\u201D declarations PrivateCategory", + "name": "Category used for \u201Cprivate\u201D declarations", "description": "A class extension should be used instead, because they enable compiler warnings for detecting unimplemented methods.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "IBOutletsInPublicInterface", - "name": "IBOutlets in public interface IBOutletsInPublicInterface", + "name": "IBOutlets in public interface", "description": "IBOutlets are often private implementation details, and should thus be in a private class extension.This rule warns only about cases where the class is the \u201CFile\u2019s Owner\u201D of a XIB whose basename is the same as the class name, and that contains a connection to the outlet.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "InitializeMethodCategoryOverride", - "name": "Overridden +[NSObject initialize] method in a category InitializeMethodCategoryOverride", + "name": "Overridden +[NSObject initialize] method in a category", "description": "The +[NSObject initialize] method should not be overridden in categories, because this will prevent the original implementation from being executed. In many cases, +[NSObject load] may be used instead \u2014 it will be executed separately for classes and categories.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "ImplicitAtomicProperty", - "name": "Implicitly atomic property ImplicitAtomicProperty", + "name": "Implicitly atomic property", "description": "Warns for implicitly atomic properties.If neither the atomic keyword nor the nonatomic keyword is specified, a property is synthesized as atomic by default.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "AssigningDelegate", - "name": "Assigning delegate property AssigningDelegate", + "name": "Assigning delegate property", "description": "Delegate properties should be declared weak instead of assign. Weak references are safer because they become nil when the referenced object is deallocated.This rule only applies to code compiled with Automatic Reference Counting (ARC).", "severity": "MAJOR" }, { "category": "BestPractice", "key": "Swizzling", - "name": "Method swizzling Swizzling", + "name": "Method swizzling", "description": "Warns whenever there are calls to Objective-C runtime API functions that are used to change method implementations.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "DiscardedOpaqueNotificationObserver", - "name": "Discarded opaque NSNotificationCenter observer DiscardedOpaqueNotificationObserver", + "name": "Discarded opaque NSNotificationCenter observer", "description": "Warns if the return value of -[NSNotificationCenter addObserverForName:object:queue:usingBlock:] is not stored anywhere. This method returns an opaque observer object that is needed in order to stop observing.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "ErrorConditionCheck", - "name": "Fragile error condition check ErrorConditionCheck", + "name": "Fragile error condition check", "description": "Methods that pass back NSError objects by reference return NO or nil to indicate an error condition. This rule warns if the NSError pointer is checked against nil without checking the return value.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "RedundantInclude", - "name": "Redundant inclusion directive RedundantInclude", + "name": "Redundant inclusion directive", "description": "If a header is included in the project\u2019s prefix header, it needn\u2019t be included anywhere else.This rule does not check header files that are themselves included in the precompiled prefix header.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "NSLogUsed", - "name": "NSLog() used in release build NSLogUsed", + "name": "NSLog() used in release build", "description": "Logging should be disabled in release builds (except perhaps for fatal errors).", "severity": "MAJOR" }, { "category": "BestPractice", "key": "UnprefixedCategoryMethod", - "name": "Unprefixed category method UnprefixedCategoryMethod", + "name": "Unprefixed category method", "description": "Category methods on system classes must be prefixed in order to avoid name collisions.By default, this rule considers a method name prefixed if it begins with at least three lowercase characters (or the lowercase version of the \u2018Class Prefix\u2019 configured for the project), followed by an underscore.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "UnidiomaticAccessorNaming", - "name": "Unidiomatic accessor naming UnidiomaticAccessorNaming", + "name": "Unidiomatic accessor naming", "description": "Warns if getter methods are named in the form getSomething instead of something.", "severity": "MAJOR" }, { "category": "BestPractice", "key": "ViewControllerInitWithNibName", - "name": "Using -[UIViewController initWithNibName:bundle:] outside the UIViewController implementation ViewControllerInitWithNibName", + "name": "Using -[UIViewController initWithNibName:bundle:] outside the UIViewController implementation", "description": "It is not advisable to use -[UIViewController initWithNibName:bundle:] to instantiate UIViewController subclasses outside of the subclass implementation itself. This breaks encapsulation (the subclass should be the one to decide which NIB to use) and makes it more likely that a typo in the NIB name string will crash the app during runtime.", "severity": "MAJOR" }, { "category": "Resources", "key": "RetinaImagesResolution", - "name": "Unexpected retina image resolution RetinaImagesResolution", + "name": "Unexpected retina image resolution", "description": "The retina (\u2018@2x\u2019 or \u2018@3x\u2019) version of an image resource must be exactly double or triple the size of the corresponding low-resolution image (assuming the point size of the presentation context remains the same regardless of screen resolution). Otherwise the effective resolution of the image will not match the effective resolution of its presentation context, which will lead to a blurry image, empty space at the edges, or overflow.", "severity": "MAJOR" }, { "category": "Resources", "key": "SuspiciousResources", - "name": "Suspicious bundle resources SuspiciousResources", + "name": "Suspicious bundle resources", "description": "Some files that are part of the project but only needed at build/development time (e.g. Xcode build configuration (.xcconfig) files) may be added as bundle resources by mistake.", "severity": "MAJOR" }, { "category": "Resources", "key": "UnknownResourceCodeReference", - "name": "Code refers to unknown resource UnknownResourceCodeReference", + "name": "Code refers to unknown resource", "description": "Resources loaded from disk at runtime should be included in the bundle resources under the correct path. Remember that unlike most Macs (and thus iOS simulators run on those Macs), iOS devices have a case sensitive filesystem.", "severity": "MAJOR" }, { "category": "Resources", "key": "UnusedResource", - "name": "Possibly unused resource UnusedResource", + "name": "Possibly unused resource", "description": "Warns about resource files that do not seem to be referred to in code or XIBs.", "severity": "MAJOR" }, { "category": "Resources", "key": "XIBUnknownClassReference", - "name": "XIB Reference to unknown class XIBUnknownClassReference", + "name": "XIB Reference to unknown class", "description": "Warns if an Interface Builder file refers to an unknown Objective-C class.This rule will warn about all references to classes that are not declared in the source code files that Faux Pas checks, or exported in linked libraries.", "severity": "MAJOR" }, { "category": "Resources", "key": "XIBRuntimeAttributeMismatch", - "name": "XIB User-defined runtime attribute mismatch XIBRuntimeAttributeMismatch", + "name": "XIB User-defined runtime attribute mismatch", "description": "Warns when a XIB specifies user-defined runtime attributes that don\u2019t match the source code of the associated class.A key path that doesn\u2019t match any of the class properties or setter methods yields a diagnostic, as does a mismatch in the type of the assigned value and the value type declared for the property or setter.", "severity": "MAJOR" }, { "category": "Resources", "key": "MissingDeviceTypeResource", - "name": "Missing device type resource MissingDeviceTypeResource", + "name": "Missing device type resource", "description": "Warns if a device type is not covered by the variants of a resource file. E.g. if image~iphone.png exists but image~ipad.png does not.", "severity": "MAJOR" }, { "category": "Resources", "key": "MissingImageResolutionVariant", - "name": "Missing image resolution variant MissingImageResolutionVariant", + "name": "Missing image resolution variant", "description": "Warns when either the high-resolution (@2x or @3x) or the low-resolution variant of an image resource is missing.", "severity": "MAJOR" }, { "category": "Resources", "key": "WeakReferenceToTopLevelXIBObject", - "name": "Weak reference to top-level XIB object WeakReferenceToTopLevelXIBObject", + "name": "Weak reference to top-level XIB object", "description": "IBOutlet references to top-level objects in XIBs should be strong, so as to ensure that the object is not deallocated prematurely.", "severity": "MAJOR" }, { "category": "Resources", "key": "UnknownResourceXIBReference", - "name": "XIB refers to unknown resource UnknownResourceXIBReference", + "name": "XIB refers to unknown resource", "description": "Resources loaded from disk at runtime should be included in the bundle resources under the correct path. Remember that unlike most Macs (and thus iOS simulators run on those Macs), iOS devices have a case sensitive filesystem.", "severity": "MAJOR" }, { "category": "Resources", "key": "GlobalAndLocalizedResource", - "name": "Global and localized resource GlobalAndLocalizedResource", + "name": "Global and localized resource", "description": "There should never be both a global and localized version of a given resource, because global resources take precedence over language-specific resources. If a global version of a resource exists, language-specific versions of the same resource are never returned by the platform APIs.", "severity": "MAJOR" }, { "category": "Resources", "key": "UnknownResourceModifier", - "name": "Unknown resource file name modifier UnknownResourceModifier", + "name": "Unknown resource file name modifier", "description": "Device type modifiers for device-specific resource files (like ~iphone or ~ipad), as well as the resolution modifier @2x, must be spelled correctly (and case sensitively, due to the case-sensitive filesystem on iOS devices).", "severity": "MAJOR" }, { "category": "Resources", "key": "SuspiciousMissingResources", - "name": "Suspicious missing bundle resources SuspiciousMissingResources", + "name": "Suspicious missing bundle resources", "description": "Warns when the project contains certain types of files that are not included into any target as resources, but probably should be.", "severity": "MAJOR" }, { "category": "Resources", "key": "DuplicateResource", - "name": "Duplicate resource DuplicateResource", + "name": "Duplicate resource", "description": "Warns if two resource files are equal.", "severity": "MAJOR" }, { "category": "Config", "key": "BuildSettingsSetInGUI", - "name": "Build settings set in Xcode GUI BuildSettingsSetInGUI", + "name": "Build settings set in Xcode GUI", "description": "Enable this rule if you want to set all of your build configuration values in .xcconfig files, and get warnings whenever something gets accidentally set in the GUI.", "severity": "MINOR" }, { "category": "Config", "key": "CompilerWarnings", - "name": "Recommended compiler warning options CompilerWarnings", + "name": "Recommended compiler warning options", "description": "Some compiler warnings and warning groups are so useful that they should be enabled at all times.", "severity": "MINOR" }, { "category": "Config", "key": "MissingAPIUsageDescription", - "name": "Missing API usage description MissingAPIUsageDescription", + "name": "Missing API usage description", "description": "Some APIs (that access e.g. contacts or calendars) require a usage description in the application metadata. This will be shown to the user when the system asks them to allow the application access to the related user data or system resource. This rule warns when some such APIs are used, but the associated usage description is missing.", "severity": "MINOR" }, { "category": "Config", "key": "BasicProjectSettings", - "name": "Recommended project settings BasicProjectSettings", + "name": "Recommended project settings", "description": "The \u2018Organization\u2019 and \u2018Class Prefix\u2019 settings should always be set. The expected values for other settings are configurable.", "severity": "MINOR" }, { "category": "Config", "key": "ImplicitBundleId", - "name": "Implicit bundle identifier ImplicitBundleId", + "name": "Implicit bundle identifier", "description": "It is recommended to choose an explicit value for the bundle identifier: an explicit, matching value must be chosen in iTunes Connect before publishing in the App Store (Xcode by default makes the bundle ID dependent on the PRODUCT_NAME variable).", "severity": "MINOR" }, { "category": "Config", "key": "ReleaseBuildCompilerArgs", - "name": "Problematic release build compiler arguments ReleaseBuildCompilerArgs", + "name": "Problematic release build compiler arguments", "description": "For release builds, warns when some expected arguments are missing, or some unexpected arguments are present.", "severity": "MINOR" }, { "category": "Config", "key": "XcconfigOverwrites", - "name": "Xcode build configuration file overwrites previously set value XcconfigOverwrites", + "name": "Xcode build configuration file overwrites previously set value", "description": "Overwriting a previously set value in a build configuration file is often a sign of a mistake somewhere.", "severity": "MINOR" }, { "category": "Config", "key": "BuildSettingSelfReference", - "name": "Xcode build setting refers to itself BuildSettingSelfReference", + "name": "Xcode build setting refers to itself", "description": "Build settings should not refer to themselves. The $(inherited) keyword should be used to append something to an inherited value, and custom intermediary build setting keys should be used to compose values from multiple definitions in an .xcconfig file.", "severity": "MINOR" }, { "category": "Config", "key": "BuildSettingPlacement", - "name": "Incorrect placement of build setting value BuildSettingPlacement", + "name": "Incorrect placement of build setting value", "description": "Warns if preprocessor definitions are defined explicitly using the -D argument instead of defining them in the GCC_PREPROCESSOR_DEFINITIONS (\u201CPreprocessor macros\u201D) build setting.Also warns if non-warning flags are defined in WARNING_CFLAGS (instead of OTHER_CFLAGS).", "severity": "MINOR" }, { "category": "Config", "key": "AbsPathInBuildSetting", - "name": "Absolute path in build setting value AbsPathInBuildSetting", + "name": "Absolute path in build setting value", "description": "The project build configurations should not contain absolute path references. This will become a problem as soon as the project is moved to another developer\u2019s computer.", "severity": "MINOR" }, { "category": "Config", "key": "FileRefWithAbsPath", - "name": "Project reference to file using absolute path FileRefWithAbsPath", + "name": "Project reference to file using absolute path", "description": "The project should not contain references to files using absolute paths. This will become a problem as soon as the project is moved to another developer\u2019s computer.", "severity": "MINOR" }, { "category": "Config", "key": "ReleaseBuildConfig", - "name": "Problematic release build settings ReleaseBuildConfig", + "name": "Problematic release build settings", "description": "Warns when certain problematic build setting values are used for release builds.For example, if program flow instrumentation is enabled, the compiler optimization level is too low, or if the static analyzer is not run.", "severity": "MINOR" }, { "category": "Config", "key": "DylibInstallName", - "name": "Dynamic library install name DylibInstallName", + "name": "Dynamic library install name", "description": "Warns if the install names of dynamic libraries are absolute (@loader_path, @executable_path, or @rpath should be used instead.)You can use install_name_tool to change dynamic library install names.", "severity": "MINOR" }, { "category": "Localization", "key": "InvalidStringsFile", - "name": "Invalid string resource file InvalidStringsFile", + "name": "Invalid string resource file", "description": "String resource files should be valid dictionary-format property lists.", "severity": "MAJOR" }, { "category": "Localization", "key": "UnusedTranslation", - "name": "Unused translation UnusedTranslation", + "name": "Unused translation", "description": "Warns about entries in string resource files that are not directly loaded in code (using NSLocalizedString and its variants).", "severity": "MAJOR" }, { "category": "Localization", "key": "StringsdictWithoutStrings", - "name": "Stringsdict file without matching string resource file StringsdictWithoutStrings", + "name": "Stringsdict file without matching string resource file", "description": "If a .stringsdict file is included as a resource, a .strings file with the same name also needs to be included, even if empty \u2014 otherwise translations will not be read from the .stringsdict file.", "severity": "MAJOR" }, { "category": "Localization", "key": "HardcodedUIString", - "name": "UI String not localized HardcodedUIString", + "name": "UI String not localized", "description": "All strings that are given to APIs that display them in the user interface should be routed through NSLocalizedString() and friends.", "severity": "MAJOR" }, { "category": "Localization", "key": "MissingTranslation", - "name": "Missing translation MissingTranslation", + "name": "Missing translation", "description": "Warns if a translation is missing for a key that is used as argument to NSLocalizedString or its variants.", "severity": "MAJOR" }, { "category": "Localization", "key": "DuplicateTranslation", - "name": "Duplicate translation DuplicateTranslation", + "name": "Duplicate translation", "description": "Warns if a string resource file contains multiple entries with the same key.", "severity": "MAJOR" }, { "category": "Localization", "key": "TranslationPunctuation", - "name": "Translation border punctuation mismatch TranslationPunctuation", + "name": "Translation border punctuation mismatch", "description": "Warns if the punctuation at the start or end of translation strings differs between languages.", "severity": "MAJOR" }, { "category": "Localization", "key": "TranslationFormatMismatch", - "name": "Translation format mismatch TranslationFormatMismatch", + "name": "Translation format mismatch", "description": "Warns when different language translations for the same key contain mismatched format specifiers.", "severity": "MAJOR" }, { "category": "Localization", "key": "UncommentedLocalizedString", - "name": "Uncommented localized string UncommentedLocalizedString", + "name": "Uncommented localized string", "description": "Warns if NSLocalizedString() or one of its variants is used without a non-empty comment argument.", "severity": "MAJOR" }, { "category": "APIUsage", "key": "UIKitKVO", - "name": "Observing UIKit object using KVO UIKitKVO", + "name": "Observing UIKit object using KVO", "description": "The classes of the UIKit framework generally do not support Key-Value Observing (KVO). If KVO does happen to work for some properties, it is undefined behavior and thus not guaranteed to work in future versions.", "severity": "CRITICAL" }, { "category": "APIUsage", "key": "APIAvailability", - "name": "Use of API not available in the minimum deployment target APIAvailability", + "name": "Use of API not available in the minimum deployment target", "description": "Warns about usages of system APIs that are not available in the minimum deployment target that your project target is configured to support.", "severity": "CRITICAL" }, { "category": "APIUsage", "key": "RestrictedDirectMethodCall", - "name": "Restricted direct method call RestrictedDirectMethodCall", + "name": "Restricted direct method call", "description": "Some methods of system classes should not be called directly.This rule will not warn about overriding methods that call the superclass implementation.", "severity": "CRITICAL" }, { "category": "APIUsage", "key": "RestrictedMethodOverride", - "name": "Restricted method override RestrictedMethodOverride", + "name": "Restricted method override", "description": "Some methods of system classes should not be overridden.", "severity": "CRITICAL" }, { "category": "APIUsage", "key": "IsEqualAndHash", - "name": "Class implements -isEqual: but not -hashIsEqualAndHash", + "name": "Class implements -isEqual: but not -hash", "description": "If -isEqual: determines that two objects are equal, they must have the same hash value.", "severity": "CRITICAL" }, { "category": "APIUsage", "key": "LiteralStringKeyPath", - "name": "Literal string for key path LiteralStringKeyPath", + "name": "Literal string for key path", "description": "Warns when literal strings are used to specify key paths.Something like NSStringFromSelector(@selector(foo)) is safer because it makes the compiler aware of the selector being specified \u2014 this helps find typos at compile time and allows automatic refactoring tools to make appropriate changes.", "severity": "CRITICAL" }, { "category": "APIUsage", "key": "BlockAPIRetainCycle", - "name": "Retain cycle in block API usage BlockAPIRetainCycle", + "name": "Retain cycle in block API usage", "description": "Warns if self is referenced in a block that is used in a context where it is known to cause a retain cycle.The Clang compiler\u2019s -Warc-retain-cycles warning implements generic retain cycle detection \u2014 this rule only considers specific known system framework APIs that the compiler warning does not warn about.", "severity": "CRITICAL" }, { "category": "APIUsage", "key": "MissingNotificationCenterDetachment", - "name": "Missing NSNotificationCenter observer detachment MissingNotificationCenterDetachment", + "name": "Missing NSNotificationCenter observer detachment", "description": "You must invoke -[NSNotificationCenter removeObserver:] or -[NSNotificationCenter removeObserver:name:object:] before the observer object is deallocated.This rule only considers cases where the observer reference is self, or an instance variable or an Objective-C property in self.", "severity": "CRITICAL" }, { "category": "VCS", "key": "StringsFileEncoding", - "name": "String resource file is not UTF-8 StringsFileEncoding", + "name": "String resource file is not UTF-8", "description": "Git prefers UTF-8 encoding, and doesn\u2019t handle some other encodings (e.g. UTF-16) very well. It thinks UTF-16 -encoded files are binary, and will thus not display line diffs for them.", "severity": "INFO" }, { "category": "VCS", "key": "RecommendedVCSIgnores", - "name": "Recommended VCS ignores RecommendedVCSIgnores", + "name": "Recommended VCS ignores", "description": "Warns whenever some files that are recommended to be ignored by the version control system are not ignored, or vice versa.", "severity": "INFO" }, { "category": "VCS", "key": "FileRefOutsideVCS", - "name": "Project reference to file outside version control root FileRefOutsideVCS", + "name": "Project reference to file outside version control root", "description": "The project should not contain references to files that are outside the version control working copy root.", "severity": "INFO" }, { "category": "VCS", "key": "FileRefIgnoredInVCS", - "name": "Project reference to file ignored in the VCS FileRefIgnoredInVCS", + "name": "Project reference to file ignored in the VCS", "description": "The project should not contain references to files that are ignored by the version control system.", "severity": "INFO" }, { "category": "Style", "key": "NullCoalescingOp", - "name": "Null coalescing operator usage NullCoalescingOp", + "name": "Null coalescing operator usage", "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", "severity": "MAJOR" }, { "category": "Style", "key": "StrongInsteadOfRetain", - "name": "Usage of retain in ARC code StrongInsteadOfRetain", + "name": "Usage of retain in ARC code", "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", "severity": "MAJOR" }, { "category": "Style", "key": "OrderedComparisonOpDirection", - "name": "Direction of ordered comparison operators OrderedComparisonOpDirection", + "name": "Direction of ordered comparison operators", "description": "Warns if the > or >= operators are used (the < or <= operators should be used instead). This is based on the idea that it is easier to read comparisons where the smaller values are on the left.", "severity": "MAJOR" }, { "category": "Style", "key": "ThrowingObjCException", - "name": "Throwing an Objective-C exception ThrowingObjCException", + "name": "Throwing an Objective-C exception", "description": "Throwing exceptions in Objective-C is not very idiomatic, and should only be reserved for truly exceptional cases (if that,) and never for flow control.This rule can help you enforce a policy to never throw exceptions.", "severity": "MAJOR" }, { "category": "Style", "key": "NewInitializer", - "name": "Shortcut initializer NewInitializer", + "name": "Shortcut initializer", "description": "Some people prefer [[NSObject alloc] init] instead of [NSObject new], or vice versa.", "severity": "MAJOR" }, { "category": "Style", "key": "NonTypedefBlockDeclaration", - "name": "Block-typed declaration without typedef NonTypedefBlockDeclaration", + "name": "Block-typed declaration without typedef", "description": "It is recommended that typedef be used for all block-typed declarations, for readability.", "severity": "MAJOR" }, { "category": "Style", "key": "UnprefixedClass", - "name": "Unprefixed Objective-C class UnprefixedClass", + "name": "Unprefixed Objective-C class", "description": "Warns when an Objective-C class name has no prefix (e.g. Thing instead of FPXThing).", "severity": "MAJOR" }, { "category": "Style", "key": "DotSyntax", - "name": "Dot syntax usage DotSyntax", + "name": "Dot syntax usage", "description": "Warns about [obj foo] or [obj setFoo:x] instead of obj.foo or obj.foo = x.Can also be configured to enforce dot syntax only for accessors declared with the @property syntax.", "severity": "MAJOR" }, { "category": "Style", "key": "IdentifierNaming", - "name": "Identifier naming IdentifierNaming", + "name": "Identifier naming", "description": "This rule allows enforcing custom naming guidelines for different kinds of identifiers via regular expressions.", "severity": "MAJOR" }, { "category": "Style", "key": "MacroLiteral", - "name": "Macro definition for literal value MacroLiteral", + "name": "Macro definition for literal value", "description": "Instead of using a macro definition for a literal value that is used as a constant, define it as an actual constant.This makes the scope of the constant more explicit (it\u2019s not available in all imported files until undefined) and it cannot be redefined or undefined in some later part of the code. Macro definitions are also not available in the debugger.", "severity": "MAJOR" }, { "category": "Pedantic", "key": "UnnecessaryNullCheck", - "name": "Unnecessary NULL check before free()UnnecessaryNullCheck", + "name": "Unnecessary NULL check before free()", "description": "Passing NULL to free() is a no-op.", "severity": "MINOR" }, { "category": "Pedantic", "key": "UnusedMethod", - "name": "Possibly unused Objective-C method UnusedMethod", + "name": "Possibly unused Objective-C method", "description": "Warns about Objective-C methods to which no references are found.This rule will not warn about IBAction methods, initializer methods, or public methods (methods with a declaration in a header file, excluding header files named in the form *+Private.h). It also won\u2019t warn about unused getter methods whose setter is used, and vice versa.", "severity": "MINOR" }, { "category": "Pedantic", "key": "ReservedIdentifierNaming", - "name": "Reserved identifier name ReservedIdentifierNaming", + "name": "Reserved identifier name", "description": "Warns when identifiers are named using conventions reserved by the C standard or POSIX.", "severity": "MINOR" }, { "category": "Pedantic", "key": "MallocCast", - "name": "Casting the return value of malloc()MallocCast", + "name": "Casting the return value of malloc()", "description": "In C, it is not recommended to explicitly cast the return value of malloc() (and other related memory allocation functions.)This rule does not warn about casts that occur in compilation units compiled in C++ (or Objective-C++) mode.", "severity": "MINOR" }, { "category": "Pedantic", "key": "ReservedPrefix", - "name": "Reserved symbol prefix ReservedPrefix", + "name": "Reserved symbol prefix", "description": "Two-character prefixes (such as NS) are reserved for Apple\u2019s system frameworks. Your own code should use prefixes that are three characters long.", "severity": "MINOR" }, { "category": "Pedantic", "key": "UsedVariableMarkedUnused", - "name": "Using a variable marked unused UsedVariableMarkedUnused", + "name": "Using a variable marked unused", "description": "Warns when a variable is annotated with the \u201Cunused\u201D attribute, but is actually used. This rule does not warn about cases where a variable is marked unused via a pragma directive.", "severity": "MINOR" }, { "category": "Miscellaneous", "key": "UnsupportedWeakReference", - "name": "Assignment of weak-unavailable object to a weak property UnsupportedWeakReference", + "name": "Assignment of weak-unavailable object to a weak property", "description": "In OS X, you cannot create weak references to instances of some classes (see the full list from Apple\u2019s documentation.)The Clang compiler should warn about assignments of such objects to weak variables.", "severity": "MINOR" }, { "category": "Miscellaneous", "key": "ThreadUnsafeInstanceCaching", - "name": "Globally caching a thread-unsafe class instance ThreadUnsafeInstanceCaching", + "name": "Globally caching a thread-unsafe class instance", "description": "Warns about variables with global storage whose type is an object pointer to a known thread-unsafe class.-[NSThread threadDictionary] can be used to cache instances of thread-unsafe classes \u2014 one instance for each thread.This rule does not warn about code occuring in subclasses of (or categories on) UIKit or AppKit classes, because it can be assumed that these are always accessed solely from the main thread.", "severity": "MINOR" }, { "category": "Miscellaneous", "key": "UnnecessaryNibMethod", - "name": "Unnecessary Nib method UnnecessaryNibMethod", + "name": "Unnecessary Nib method", "description": "Warns if -[NSObject awakeFromNib] is implemented in a class that is not archived in any known XIB.", "severity": "MINOR" }, { "category": "Miscellaneous", "key": "OrderedPointerToZeroComparison", - "name": "Ordered comparison of pointer and zero OrderedPointerToZeroComparison", + "name": "Ordered comparison of pointer and zero", "description": "Warns whenever a pointer value is compared to zero using an ordered comparison operator.The Clang compiler should warn about similar comparisons to values other than zero.", "severity": "MINOR" }, { "category": "Miscellaneous", "key": "FixedFormatDateFormatter", - "name": "Fixed-format NSDateFormatter not using invariant (POSIX) locale FixedFormatDateFormatter", + "name": "Fixed-format NSDateFormatter not using invariant (POSIX) locale", "description": "Warns when an NSDateFormatter is used with fixed-format dates without using the invariant en_US_POSIX locale. If any other locale is used, the date format string may be overwritten, depending on system date and time settings.When working with user-visible dates, date and time styles should be used instead of setting a date format.", "severity": "MINOR" }, { "category": "Miscellaneous", "key": "ZeroAssignmentToPointer", - "name": "Assignment of literal zero to pointer ZeroAssignmentToPointer", + "name": "Assignment of literal zero to pointer", "description": "Assigning the literal zero to a pointer value might be an indication of a mistake where the programmer actually wanted to assign to an integer-typed member of the pointer type value. Assigning NULL, nil, or Nil instead suppresses warnings from this rule.", "severity": "MINOR" }, { "category": "Miscellaneous", "key": "CategoryMethodConflict", - "name": "Conflicting category methods CategoryMethodConflict", + "name": "Conflicting category methods", "description": "If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.", "severity": "MINOR" } diff --git a/updateFauxPasRules.groovy b/updateFauxPasRules.groovy index 4983fe00..0406af47 100644 --- a/updateFauxPasRules.groovy +++ b/updateFauxPasRules.groovy @@ -22,10 +22,12 @@ def parseRules(url, catMapping) { def rules = cat."**".findAll {it.@class.toString().contains('rule')} rules.each {r -> + def k = r."**".find {it.@class.toString().contains("short-name")}.text() + def rule = [ category: cat.H2.text(), - key: r."**".find {it.@class.toString().contains("short-name")}.text(), - name: r.H3.text().trim().replaceAll('\\n', ' '), + key: k, + name: (r.H3.text().trim().replaceAll('\\n', ' ') - k).trim(), description: r."**".find {it.@class.toString().contains("description")}.text().trim().replaceAll('\\n', ' '), severity: catMapping[cat.H2.text()] ] From 8e124d5bc0a49c08304242c3900551d8e6ad1342 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 17 Feb 2015 15:12:00 +0100 Subject: [PATCH 008/107] Moved OCLint long line threshold to 250 --- src/main/shell/run-sonar.sh | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 1c12546d..d3de4e1f 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -117,12 +117,14 @@ function runCommand() { vflag="" nflag="" oclint="on" +fauxpas="on" while [ $# -gt 0 ] do case "$1" in -v) vflag=on;; -n) nflag=on;; - -nooclint) oclint="";; + -nooclint) oclint="";; + -nofauxpas) fauxpas="";; --) shift; break;; -*) echo >&2 "Usage: $0 [-v]" @@ -292,20 +294,25 @@ if [ "$oclint" = "on" ]; then # Run OCLint with the right set of compiler options 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 + longLineThreshold=250 + runCommand no oclint-json-compilation-database $includedCommandLineFlags -- -rc LONG_LINE=$longLineThreshold -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 -hash fauxpas 2>/dev/null -if [ $? -eq 0 ]; then +if [ "$fauxpas" = "on" ]; then + hash fauxpas 2>/dev/null + if [ $? -eq 0 ]; then - #FauxPas - echo -n 'Running FauxPas...' - fauxpas -t $appScheme -o json check $projectFile > sonar-reports/fauxpas.json + #FauxPas + echo -n 'Running FauxPas...' + fauxpas -t $appScheme -o json check $projectFile > sonar-reports/fauxpas.json + else + echo 'Skipping FauxPas (not installed)' + fi else - echo 'Skipping FauxPas (not installed)' + echo 'Skipping FauxPas' fi # SonarQube From cdecbf2552164a20e207a023e15dc0e9df630946 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 18 Feb 2015 22:34:57 +0100 Subject: [PATCH 009/107] Added some FauxPas rules to SQALE model (to be continued) --- .../com/sonar/sqale/objectivec-model.xml | 1030 ++++++++++++++++- 1 file changed, 1001 insertions(+), 29 deletions(-) diff --git a/src/main/resources/com/sonar/sqale/objectivec-model.xml b/src/main/resources/com/sonar/sqale/objectivec-model.xml index 42c209fb..4918c934 100644 --- a/src/main/resources/com/sonar/sqale/objectivec-model.xml +++ b/src/main/resources/com/sonar/sqale/objectivec-model.xml @@ -45,6 +45,132 @@ READABILITY Readability + + FauxPas + UnidiomaticAccessorNaming + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + IBOutletsInPublicInterface + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + PrivateCategory + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + PrefixHeaderIncludeSuggestion + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + UnusedErrorValue + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + ArgumentModification + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + MacroBasedIncludeGuard + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + OCLint unused method parameter @@ -459,6 +585,24 @@ d + + FauxPas + OldVerboseObjCSyntax + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + OCLint replace with object subscripting @@ -481,6 +625,24 @@ UNDERSTANDABILITY Understandability + + FauxPas + MissingAPIUsageDescription + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + OCLint useless parentheses @@ -833,10 +995,100 @@ MEMORY_EFFICIENCY Memory use + + FauxPas + StrongDelegate + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + LoadMethodWithoutAutoreleasePool + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + UndetachedDelegate + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + MallocWithoutSizeof + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + CPU_EFFICIENCY Processor use + + FauxPas + NSLogUsed + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + @@ -968,8 +1220,8 @@ INSTRUCTION_RELIABILITY Instruction - OCLint - must override hash with isEqual + FauxPas + BuildSettingPlacement remediationFunction linear @@ -986,15 +1238,15 @@ - OCLint - parameter reassignment + FauxPas + BuildSettingSelfReference remediationFunction linear remediationFactor - 30 + 10 mn @@ -1004,8 +1256,8 @@ - OCLint - ivar assignment outside accessors or init + FauxPas + XcconfigOverwrites remediationFunction linear @@ -1021,13 +1273,9 @@ d - - - LOGIC_RELIABILITY - Logic - OCLint - missing break in switch statement + FauxPas + ReleaseBuildCompilerArgs remediationFunction linear @@ -1044,15 +1292,15 @@ - OCLint - avoid branching statement as last in loop + FauxPas + ImplicitBundleId remediationFunction linear remediationFactor - 30 + 10 mn @@ -1062,8 +1310,8 @@ - OCLint - switch statements should have default + FauxPas + BasicProjectSettings remediationFunction linear @@ -1080,8 +1328,8 @@ - OCLint - jumbled incrementer + FauxPas + CompilerWarnings remediationFunction linear @@ -1098,8 +1346,8 @@ - OCLint - broken null check + FauxPas + BuildSettingsSetInGUI remediationFunction linear @@ -1116,15 +1364,15 @@ - OCLint - broken nil check + FauxPas + ViewControllerInitWithNibName remediationFunction linear remediationFactor - 10 + 30 mn @@ -1134,8 +1382,8 @@ - OCLint - broken oddness check + FauxPas + UnprefixedCategoryMethod remediationFunction linear @@ -1152,8 +1400,480 @@ - OCLint - misplaced nil check + FauxPas + RedundantInclude + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + ErrorConditionCheck + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + DiscardedOpaqueNotificationObserver + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + Swizzling + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + AssigningDelegate + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + ImplicitAtomicProperty + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + InitializeMethodCategoryOverride + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + TerminatingApp + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + InstanceMethodWritesToStaticVariable + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + FastEnumElementOutside + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + SetterInvocationInInitOrDealloc + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + CopyingMutableProperty + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + ConstructorReturnType + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + RetainingImmutableProperty + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + InitializeSuperInvocation + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + AssertionSideEffects + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + must override hash with isEqual + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + parameter reassignment + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + OCLint + ivar assignment outside accessors or init + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + + LOGIC_RELIABILITY + Logic + + OCLint + missing break in switch statement + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + avoid branching statement as last in loop + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + OCLint + switch statements should have default + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + jumbled incrementer + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + broken null check + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + broken nil check + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + broken oddness check + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + misplaced nil check remediationFunction linear @@ -1263,6 +1983,258 @@ RESOURCE_RELIABILITY Resource + + FauxPas + RetinaImagesResolution + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + SuspiciousResources + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + UnknownResourceCodeReference + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + UnusedResource + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + XIBUnknownClassReference + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + XIBRuntimeAttributeMismatch + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + MissingDeviceTypeResource + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + MissingImageResolutionVariant + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + WeakReferenceToTopLevelXIBObject + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + UnknownResourceXIBReference + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + GlobalAndLocalizedResource + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + UnknownResourceModifier + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + SuspiciousMissingResources + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + DuplicateResource + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + SYNCHRONIZATION_RELIABILITY From 9e309a0fa2a7d717c410bcaccc49403d5a527fdf Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 18 Feb 2015 23:25:39 +0100 Subject: [PATCH 010/107] Added remaining FauxPas rules to SQALE model --- .../com/sonar/sqale/objectivec-model.xml | 986 ++++++++++++++++-- 1 file changed, 925 insertions(+), 61 deletions(-) diff --git a/src/main/resources/com/sonar/sqale/objectivec-model.xml b/src/main/resources/com/sonar/sqale/objectivec-model.xml index 4918c934..0d1de0ed 100644 --- a/src/main/resources/com/sonar/sqale/objectivec-model.xml +++ b/src/main/resources/com/sonar/sqale/objectivec-model.xml @@ -9,6 +9,96 @@ TRANSPORTABILITY Transportability + + FauxPas + AbsPathInBuildSetting + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + FileRefWithAbsPath + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + StringsFileEncoding + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + FileRefOutsideVCS + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + FileRefIgnoredInVCS + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + @@ -25,10 +115,64 @@ LANGUAGE_RELATED_PORTABILITY Language + + FauxPas + HardcodedUIString + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + FixedFormatDateFormatter + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + OS_RELATED_PORTABILITY OS + + FauxPas + APIAvailability + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + SOFTWARE_RELATED_PORTABILITY @@ -45,6 +189,24 @@ READABILITY Readability + + FauxPas + RecommendedVCSIgnores + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + FauxPas UnidiomaticAccessorNaming @@ -1067,6 +1229,24 @@ d + + FauxPas + BlockAPIRetainCycle + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + CPU_EFFICIENCY @@ -1221,7 +1401,7 @@ Instruction FauxPas - BuildSettingPlacement + CategoryMethodConflict remediationFunction linear @@ -1239,7 +1419,7 @@ FauxPas - BuildSettingSelfReference + ZeroAssignmentToPointer remediationFunction linear @@ -1257,7 +1437,7 @@ FauxPas - XcconfigOverwrites + OrderedPointerToZeroComparison remediationFunction linear @@ -1275,7 +1455,7 @@ FauxPas - ReleaseBuildCompilerArgs + UnnecessaryNibMethod remediationFunction linear @@ -1293,7 +1473,7 @@ FauxPas - ImplicitBundleId + UnsupportedWeakReference remediationFunction linear @@ -1311,7 +1491,7 @@ FauxPas - BasicProjectSettings + UsedVariableMarkedUnused remediationFunction linear @@ -1329,7 +1509,7 @@ FauxPas - CompilerWarnings + ReservedPrefix remediationFunction linear @@ -1347,7 +1527,7 @@ FauxPas - BuildSettingsSetInGUI + MallocCast remediationFunction linear @@ -1365,14 +1545,14 @@ FauxPas - ViewControllerInitWithNibName + ReservedIdentifierNaming remediationFunction linear remediationFactor - 30 + 10 mn @@ -1383,7 +1563,7 @@ FauxPas - UnprefixedCategoryMethod + UnusedMethod remediationFunction linear @@ -1401,7 +1581,7 @@ FauxPas - RedundantInclude + UnnecessaryNullCheck remediationFunction linear @@ -1419,7 +1599,7 @@ FauxPas - ErrorConditionCheck + MacroLiteral remediationFunction linear @@ -1437,7 +1617,7 @@ FauxPas - DiscardedOpaqueNotificationObserver + IdentifierNaming remediationFunction linear @@ -1455,14 +1635,14 @@ FauxPas - Swizzling + DotSyntax remediationFunction linear remediationFactor - 30 + 10 mn @@ -1473,7 +1653,7 @@ FauxPas - AssigningDelegate + UnprefixedClass remediationFunction linear @@ -1491,7 +1671,7 @@ FauxPas - ImplicitAtomicProperty + NonTypedefBlockDeclaration remediationFunction linear @@ -1509,7 +1689,7 @@ FauxPas - InitializeMethodCategoryOverride + NewInitializer remediationFunction linear @@ -1527,14 +1707,14 @@ FauxPas - TerminatingApp + ThrowingObjCException remediationFunction linear remediationFactor - 10 + 30 mn @@ -1545,14 +1725,14 @@ FauxPas - InstanceMethodWritesToStaticVariable + OrderedComparisonOpDirection remediationFunction linear remediationFactor - 30 + 10 mn @@ -1563,7 +1743,7 @@ FauxPas - FastEnumElementOutside + StrongInsteadOfRetain remediationFunction linear @@ -1581,7 +1761,7 @@ FauxPas - SetterInvocationInInitOrDealloc + NullCoalescingOp remediationFunction linear @@ -1599,7 +1779,7 @@ FauxPas - CopyingMutableProperty + MissingNotificationCenterDetachment remediationFunction linear @@ -1617,7 +1797,7 @@ FauxPas - ConstructorReturnType + LiteralStringKeyPath remediationFunction linear @@ -1635,7 +1815,7 @@ FauxPas - RetainingImmutableProperty + IsEqualAndHash remediationFunction linear @@ -1653,7 +1833,7 @@ FauxPas - InitializeSuperInvocation + RestrictedMethodOverride remediationFunction linear @@ -1671,7 +1851,7 @@ FauxPas - AssertionSideEffects + RestrictedDirectMethodCall remediationFunction linear @@ -1688,8 +1868,8 @@ - OCLint - must override hash with isEqual + FauxPas + UIKitKVO remediationFunction linear @@ -1706,15 +1886,15 @@ - OCLint - parameter reassignment + FauxPas + DylibInstallName remediationFunction linear remediationFactor - 30 + 10 mn @@ -1724,8 +1904,8 @@ - OCLint - ivar assignment outside accessors or init + FauxPas + ReleaseBuildConfig remediationFunction linear @@ -1741,13 +1921,9 @@ d - - - LOGIC_RELIABILITY - Logic - OCLint - missing break in switch statement + FauxPas + BuildSettingPlacement remediationFunction linear @@ -1764,15 +1940,15 @@ - OCLint - avoid branching statement as last in loop + FauxPas + BuildSettingSelfReference remediationFunction linear remediationFactor - 30 + 10 mn @@ -1782,8 +1958,8 @@ - OCLint - switch statements should have default + FauxPas + XcconfigOverwrites remediationFunction linear @@ -1800,8 +1976,8 @@ - OCLint - jumbled incrementer + FauxPas + ReleaseBuildCompilerArgs remediationFunction linear @@ -1818,8 +1994,8 @@ - OCLint - broken null check + FauxPas + ImplicitBundleId remediationFunction linear @@ -1836,8 +2012,8 @@ - OCLint - broken nil check + FauxPas + BasicProjectSettings remediationFunction linear @@ -1854,8 +2030,8 @@ - OCLint - broken oddness check + FauxPas + CompilerWarnings remediationFunction linear @@ -1872,8 +2048,8 @@ - OCLint - misplaced nil check + FauxPas + BuildSettingsSetInGUI remediationFunction linear @@ -1890,9 +2066,535 @@ - OCLint - misplaced null check - + FauxPas + ViewControllerInitWithNibName + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + UnprefixedCategoryMethod + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + RedundantInclude + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + ErrorConditionCheck + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + DiscardedOpaqueNotificationObserver + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + Swizzling + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + AssigningDelegate + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + ImplicitAtomicProperty + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + InitializeMethodCategoryOverride + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + TerminatingApp + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + InstanceMethodWritesToStaticVariable + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + FastEnumElementOutside + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + SetterInvocationInInitOrDealloc + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + CopyingMutableProperty + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + ConstructorReturnType + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + RetainingImmutableProperty + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + InitializeSuperInvocation + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + AssertionSideEffects + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + must override hash with isEqual + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + parameter reassignment + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + OCLint + ivar assignment outside accessors or init + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + + LOGIC_RELIABILITY + Logic + + OCLint + missing break in switch statement + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + avoid branching statement as last in loop + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + OCLint + switch statements should have default + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + jumbled incrementer + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + broken null check + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + broken nil check + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + broken oddness check + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + misplaced nil check + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + OCLint + misplaced null check + remediationFunction linear @@ -1983,6 +2685,150 @@ RESOURCE_RELIABILITY Resource + + FauxPas + UncommentedLocalizedString + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + TranslationFormatMismatch + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + TranslationPunctuation + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + DuplicateTranslation + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + MissingTranslation + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + StringsdictWithoutStrings + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + UnusedTranslation + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + InvalidStringsFile + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + FauxPas RetinaImagesResolution @@ -2239,6 +3085,24 @@ SYNCHRONIZATION_RELIABILITY Synchronization + + FauxPas + ThreadUnsafeInstanceCaching + + remediationFunction + linear + + + remediationFactor + 1 + h + + + offset + 0.0 + d + + UNIT_TESTS From d2b25bd20c9f12178f3671b3a644fd54aea557ed Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 19 Feb 2015 22:44:21 +0100 Subject: [PATCH 011/107] Documentation update --- README.md | 71 +++++++++------------ sample/screen shot SonarQube dashboard.png | Bin 97620 -> 388068 bytes 2 files changed, 29 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index b621a656..d1249129 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -SonarQube Plugin for Objective C -================================ - -This repository hosts the Objective-C plugin for [SonarQube](http://www.sonarqube.org/). This plugin enables to analyze and track the quality of iOS (iPhone, iPad) and MacOS developments. +

+ +

-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. +SonarQube Plugin for Objective-C +================================ -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. +This repository is a fork of the open source [SonarQube Plugin for Objective-C](https://github.com/octo-technology/sonar-objective-c). It provides modifications and extra features needed for our internal use. -Find below an example of an iOS SonarQube dashboard: +A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://github.com/semweb/GreatReader):

- Example iOS SonarQube dashboard + Example iOS SonarQube dashboard

###Features @@ -18,42 +18,41 @@ Find below an example of an iOS SonarQube dashboard: - [ ] Design - [x] Documentation - [x] Duplications -- [x] Issues +- [x] Issues (OCLint: 63 rules, Faux Pas: 102 rules) - [x] Size - [x] Tests -For more details, see the list of [SonarQube metrics](https://github.com/octo-technology/sonar-objective-c/wiki/Features) implemented or pending. - ###Compatibility -- Use 0.3.x releases for SonarQube 3.x -- Use 0.4.x releases for SonarQube 4.x +Releases available from this repository are compliant with SonarQube 4.3.x and above. + +###Faux Pas support + +[Faux Pas](http://fauxpasapp.com/) is a wonderful tool to analyse iOS or Mac applications source code, however it is not free. A 30 trial version is available [here](http://fauxpasapp.com/try/). + +The plugin runs fine even if Faux Pas is not installed (Faux Pas analysis will be skipped). ###Download -The latest version is the 0.4.x is not yet released and is available as a snapshot [here](https://rfelden.ci.cloudbees.com/job/sonar-objective-c/lastSuccessfulBuild/artifact/target/). -The latest SonarQube 3.x release is the 0.3.1, and it's available [here](http://bit.ly/1fSwd5I). - -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 with all snapshots and releases is available here: http://repository-rfelden.forge.cloudbees.com/ +Binary packages will be available soon... ###Prerequisites -- a Mac with Xcode... +- 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 ([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```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. - [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 +- [Faux Pas](http://fauxpasapp.com/) command line tools installed (optional) ###Installation (once for all your Objective-C projects) -- Install [the plugin](http://bit.ly/1fSwd5I) through the Update Center (of SonarQube) or download it into the $SONARQUBE_HOME/extensions/plugins directory -- Copy [run-sonar.sh](https://rawgithub.com/octo-technology/sonar-objective-c/master/src/main/shell/run-sonar.sh) somewhere in your PATH +- Download the plugin binary into the $SONARQUBE_HOME/extensions/plugins directory +- Copy [run-sonar.sh](https://rawgithub.com/Backelite/sonar-objective-c/master/src/main/shell/run-sonar.sh) somewhere in your PATH - Restart the SonarQube server. ###Configuration (once per project) -- Copy [sonar-project.properties](https://rawgithub.com/octo-technology/sonar-objective-c/master/sample/sonar-project.properties) in your Xcode project root folder (along your .xcodeproj file) -- Edit the *sonar-project.properties* file to match your Xcode iOS/MacOS project +- Copy [sonar-project.properties](https://rawgithub.com/Backelite/sonar-objective-c/master/sample/sonar-project.properties) in your Xcode project root folder (along your .xcodeproj file) +- Edit the ```sonar-project.properties``` file to match your Xcode iOS/MacOS project **The good news is that you don't have to modify your Xcode project to enable SonarQube!**. Ok, there might be one needed modification if you don't have a specific scheme for your test target, but that's all. @@ -62,27 +61,15 @@ In the worst case, the Maven repository with all snapshots and releases is avail - Enjoy or file an issue! ###Update (once per plugin update) -- Install the [latest plugin](http://bit.ly/1fSwd5I) version -- Copy [run-sonar.sh](https://rawgithub.com/octo-technology/sonar-objective-c/master/src/main/shell/run-sonar.sh) somewhere in your PATH +- Install the lastest plugin version +- Copy ```run-sonar.sh``` somewhere in your PATH If you still have *run-sonar.sh* file in each of your project (not recommended), you will need to update all those files. -###Credits -* **Cyril Picat** -* **Denis Bregeon** -* **Gilles Grousset** -* **François Helg** -* **Romain Felden** -* **Mete Balci** - -###History -- v0.4.0 (SOON): 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 -- v0.0.1 (2012/09): v0 with basic metrics such as nb lines of code, nb lines of comment, nb of files, duplications +###Contributing + +Feel free to contribute to this plugin by issuing pull requests to this repository or to the [original one](https://github.com/octo-technology/sonar-objective-c). ###License -SonarQube Plugin for Objective C is released under the GNU LGPL 3 license: -http://www.gnu.org/licenses/lgpl.txt +SonarQube Plugin for Objective-C is released under the [GNU LGPL 3 license](http://www.gnu.org/licenses/lgpl.txt). diff --git a/sample/screen shot SonarQube dashboard.png b/sample/screen shot SonarQube dashboard.png index c2b72a7c37459beaa81522a31628646a7f563fef..62f8ecf34c15b76a75d27ed882696df441b1739d 100644 GIT binary patch literal 388068 zcmbTd1yEdFmo-cRL4t?i?(P!YHCTWI3GNWw8)+cH-4a{^BuH?##@*dr8;8akYxp|v z%s=1MykFH{^H7(n?tAaiz0W#p@3T7MgPH>7Ym(Op2nd);in5vr2(P&i5MEiJBE#=E zNA}1eAYc&L%E)|Bl98eM;Ob;$Yj25wpcwHZ1?6MXAVEKvl{q+=AUyEauAG<5Qhl^i*v zVnxrDLzT$8Bx-#|9&eg2avgW5vXEW70iq<5OK-tw#dvCmx)kKpVPf9vxd1C zlQpy@dHPk9p4W}S!kfin``6jUuSfl{UlEyX+Cxan{WQ!Llt%SbL3QWPnX%7?JiM~o z!2PcGs`$vV;r$rkGGU-mWooYH&RYp)K8)%&JHw)}J;ozYO}Q77=1+*n_D+Jkobsl^0R3F!E+eg?G2Vr4q8dbq97LCduHOC6>(otV2Z@Cn+tp7(Ay`Z zzV7_UThrekqF1T+2J=t6n5$;~%U@NxWbZ^cEDT7)j<#8616>*)_qj)j3=EBM&8tGb zMW1sAlzz+Dq@>QWK34Q}+yU6@U>TT9d3R+0s`}RT`@YIlN)7P+BWY|9&IYC*0$#X@ z>dQ+>l8riSK8%ilDFTU(m(uYs8$0|)cqnjLZ8y8E=>`x3q_(~tM+kB%u@de`Kct|Q zT-jnJ(w8+QhP$6*xH6Q-wrp|$Q2l&5zQl-ZscpNc+(C>Y+GHus5GLbncyfaxyTOI2glwwu03*4pnB5{VDYNnWh5m~(?RRYJgsf^lPT7&P2 z&e)~%*va=cV;*$pKHup3KgF%-nZFKBgkrijqPzBxcKK$TIu5Yv z6;oO3Hn&h-dA^swXzW>5pkipIb+aM6-{FM|HhU`}W7s{9q%RulSa8R$wD<-5~ z6U2p=^+ARvoJGiC9U2n|YF{rm-ZUZ$cd$ECmdA*nzrpxs%Jq#36)l(AT^4gv9s`HB zhr~s81;>~UDME59L6+{-CM}Hxcl8@)`S2pG8M|HvIY!yxToSkQsTKp z@8y1IsLtSIMGEI-+i;PDjLBEDOU~z7+R*#plqrno&rj&vVBfzX??PaR;^rbB zA#>{iRdKPsQ0@y}&@4q|?PaMFE=6lQBGttf2n6;-2$EZG?KuOHHzoy1$GcLt>>wuq zextaCjd$n$w^UC*Bx!AMMo`CsC8f!zL%4E2C^FF>GiFj51dIHM<5Hn}Lo7?1hq#gf z>EYX=+4{Vtwq?2{WX4#PA}C9fFr#Qir=v*}AvZ*gpID!Ol{l8ztO)#tZJ}2!u$X?V zphk5$c(3BfT3UKoW?5JdTv((GCl692w6Z9Q$(k%(cU`Yt4P7gCEO#(>#CE|0;K7t3 z%A|G{TRtVXL@?dyFaD%M1`k!A3SqrjnI9#efBEQMCm%BM7o=BwZeEhF zL#_)uY-}#R$GE;b#6KjQMPXo7_e>kfHsHJ4-|;0W3N4lo?xTrmC2=5;N&lSwl>Tzm zA)Sy9rIzS3#b?{k8Fgbe*LKr&tF_)s8Fk}do(Dgg`tb0kT8G0N0%J z57VGcblOk(lKGkn1Q<~`QGQX#Q94@Jg$ydIf^{B;I?5u$7{kD1 zXxaDDZ>1My>7{GB8J}y)hRd9mChQ;WlNb4xK=u4JyGz;&YgPHnstx*<^Y+l*wN4`& zW9#*^pJ(y=mEF0^8jIX3E@j)6jNg({rg;jr)F`t~vfMJ`gr85$P7_aEJ9c~d@@-da z9}2FU51XqEpp7?;+^cU^JzK6Dj~Z)MSXXlZ``*r?H@>1!K8VVt=>FZ%@RahtN7qqn zLk;xJnVWcOT)TRLdP_y3Q|+1Ox`KbRKeKanhqPorplPZ#8lBlJ|2!0jn@YGS99yhS%rAwp7*QM{vR(W-@k9Swms+M~A$f5tZ57p#2i=^7#D(m}Vi)DD z^4^mE%PmqoMKo@-2fR?L`>_?C8mHvd(k{IQYtm68u7@GAYawDcwecT0!|A?k3GqZ3 z04fMCYxJiSG5_brh%b7DY@3mI7l(kR* zYoHU*yzL9ymZ`PEu-`hrAza=~;tYzmjY_!_E6m16?6KqB; zM5PlRu^Cm}Lf(wi`zF&UCnXDTPU~w|mO3>(UmW6p$3I~8A8#1p91LjS@wC+XYSnr1 zZ7t_9;1BzE*0%(n6|Q=9cKmJW4b8xq(R2s9HUgK(asqvV6oLSwVI$vb>yn&de+t7C zP&u#_b{~n^gZYTr@UGdZltY7Uko91d52yvR+S|(;J*yvCb^Se4?>?_Rq&+#8T^(QD z=WxF)VE+IT^LQw8t+?nG;qxqAg!nUBTkC`<$gOgW8uqq`pX{Bq)$|9b1Am)Z9vE${ zt+bBc^d8&6dLb$4u)~KaXh89n&h9O9Kwp?g*ij6*8o6rLwD7ol zlUG*zrrY+^Xh+7pW60Veh#dH!1}#0!7ReOhJhfT@0y&{;1F&6eRP0C!7Ew0fFG$e^ zc9H}ZbOD;_e|*_^qO_Cr(0aU%YKO!u=huw=jW!_n@D|Zej zeC=v~`@94hx}Alzi#<7#Mw1@+d+XjLB=0biEXTEZ7KnDTp{P$C>aeNhAxpj3E$_A;g~o0(c&!7L3cf%5EB) z?4jqT?pI|D2)`H*+E5h~6g*>N{TESs*IsQHBwwvDa>xSu*2@?Q>KzSG9zQ(2vL>?= zOpPvPfrkoc&Wd_&2nYo9fB#=7Y0{k|AiT)2{iy4%tEwVw?&QE>YT;yN$pLV1hQEz~ zAPNwMe{`^PH>CnN*gLuj1H@?l^@cF~^WWQ?G*th3#obPfMpyL%m5h_CB^5sh9|sqW z_-iUEDp6MpD`8Dp`Tr~q|0YIbS(%*pBH<;CH}%i-i|&B-k!B*e+Z!^y+L4u6B) z&D+u46u|E2M*AO?{D1YxTDqCL+B&=2IyqAPt=H7d$-`ZYhURZW|MmAD{j>zw{@<1y z-Tre}@BwoEeZtAj!NvJsb;FB_{=F;w!4_a?uPbZoVCm=vZ$q3%fJ;#HUj_c{(f@7o zzZKQ{zeTxu1-brv(f{`7KZ}ZT{vE=98`6Kw*T3$sAv2s&-f+yZkeW$BEm$E^lm$X4s5}!h)mXube z`i>+M*nvWKwdQ-qkG$fr;`@UQco{7UZk=5_ICW=KzIcY|wY99KEv8v}SzB8_U8*hm zQi6<)jf|bH@$t2_jjeWft?E21we{MkwC7Uvy?^VaK~^1DSsR*{d|2JSYA09J4t;j> zJv7YZDJgM-l=W$AYk$@&Q|>=u_R{}ixb)-4aYcLk?6e#>aSpI_Q~;)l__v}P>l|Imv<(X;#~B<< z(E?M{r!E#<+6^hbpy?Rf{@`f8=0_^FV+tB>m=%(@bPNc|*F@y(f58!JU52i+mzu1j zx$(Zl?Gb3iRD)=6CNR_De9dYHYdY^|aPZP)ExB4|#s-gODV7wKxeqq-8Tuctx(qm` ziPW+JQ&H2BQF%MrjqZm7^4>rP%Qt_j>>Q5T!PGSGTZJfW?Nn;Fv<(Lb~EF`o8q zUwfDh`1M44O<7E-35!1*FUVig7MYCR~w*Y`s@=c}6 zKP@ha^!a-rQZ##{J__CoT%3P&1;Q(6#`=f-hj$?JZwF!xP~QHd*5Q4Q57d|sL=FFE zmt+)Pkv)X=Fz_E(kzH-$X4%OJV*anvCs7@yVdUB~F9#9LHC$M@|KW7vd^JEFKs4w0 z{#O2fZPg{TuMYK#1zF!^d*Sn9!~D{z^;%mAiyk|gzVnZ)+&}fQYWNg$lainOV;5mU zdx1uz{Owaji=uA z{O8*ENryyd!iJ_z&+<(|!79J#a|v*y@cs*^ZOAp(oTFPt!X7&s4HD}-#=Uob2_YnyxUaBh21N^BBFe5a9mwdCFaGq zc)W#pu6cv+Rv(UQ;<7yE^wPe4X+aH_{Q7quaWeD%_f7iJf6&*r^ICBUTlKpf)z;CO zFG|&SZ2Fz$e}5ogF%U2Rur!R^lin%pc}&!HsD&i^GaP9sDm6Lr&A)lgKtcyc%dy$R zX-kDeRWE^1-v_S$pA~OI{L*c2q$&toe4#IzIEl%Kj_|DQp`6_Jxa#J-8@oZJ!2K;Q z&R0(*$(Qn8pO048E1&OL9o%o&wHJRY7%?^vBQGx%U7prH`R2syfa=K_*D3lNdDbmg z1yw+*Q;KRCn0%6MLl~k%{r}hyOxS7S%dkz@ zyC%Lw-PctBN8!dL_q|!PcIXIv!Jas-c?Bz4ua{FZY>_}+BM5l9%vqlLtqjx~9vV7s z+>WiuaBLoQY=?QaYtKXTY57yc}llC9kW@E=pC*#BT@Sq_>7&W{SI>0|3I zHifaeHGR7Ik|w{LzUQ%%ps@7(IR2~)6w{n{FDWXjq*qQ}u&ZdTmq$)~xH>QoLdN8| znEb7ray4?Glffvt9d{`LymxJMTdBJZptAoH~v5 zor~rnswIXvJ5Hz03-R+ho z;#Dr;-$x_+bZvJ48W zewXdgU92ngAZ^MbyrH)&cnEhWU7yEl5UP-{na*kCW>$=HV%4WtE2&VXICtWaLEEZl z*sh&KxL&iiBpvm8u}H!H2&&rvb=U&s(>Y4Z<(LS%;CoXItMP1yinjYdn3Uox z&g-B8%yB8c{lANa1d~K>6A5sz2FD)Q@TAXCnf3bJm>0;apg@z3!qk@QB2kT2^JQN_ zQQ7JPZVSYfEQ`9%6VJBgH~4AYzGk%A^UpcR1euw4CsuF^Rln9oWAe3umX=m+?UJwb z%w#QL9&|MlpB03FlS`%i`mAx7dRhCj-ixTLnlfqFU)}* zTel&mjdjBjke;NDZvCK?Ldex(ZVdMnk;o0p|8CegnT~Stjo9^!Rx1Fvz1 z45wFC#?SZjCt(tgxxm}aFga%Y;UWs3{oI?Uhcn-CbEOD@>$RZ;c%;^9-5rWqb9d?H z^(V=l&4HIx%%>I(W}Z*Nf*XN|>Fg#L*?W!IJVLLT1fT`?{;!3Mv|#liztV=UjTM{k zdTbA#_WUm9D7tC(Fk@frgQFq|WxUgz)eRper7{yyQ3SLTWGm^Jl1k*?n3SX7P*@tA5XbYrta^5`@qvsizkyOgee% z|8QbWF5;;oM=eUBgC^t8jBfc$ZBsBUx|Zt-X9he&TDb)G5t!_m3Z&K$NtR0FYa%Mq zk|M6(H-US@c_=_}gyht)It{cQbTScPk`ymp-HV40z^iBumoO8^U1PA}&XTgX&>$TQ**=8t4-Qo=)X9 zkH(5DQUGBJEOfNpjXL>EKJ<|~9d}a!lIz%BWTyZ+ERy-bharSUmBx=-gv?HX0O?)& z^pvBLingU#@q3$R*R|@V5tt3UdFvb?kNko+I4kDJVXjnL&id~zT-j4`{zz<{|^g;D&ckASR%J-vZuD7@-JH-U(z}J z&u#y!r&=JS2v1G06B@wN2{*Z;@-^hVe5AS{++jbcBV-udeGC= z7|Y=Ur$Q(H&Tp@5w`U)%m=Dz@V6_@=W)=Ys1_7{Z(vxoPu1`9Kvo<26;n04Gr%8bo z=kA%O-YCLl+rq59SJ9%3I3(gvx7+>_%~*WoQXPUGhaW{QM|d$cyjM_%Z(m+3CE}*I zkZT`AFH`s)Z!#Jtg~DS>Dv6i$t#*fB?CgK*$2NIxl9d>8EkCnAt$5oKU-D+$?5_jW zM6ii15uq>*|0Zo4TwO#Yz(QU}zuf^lyysXpUn`^_Q5_Q-5` zyfSA9F1q;+%R6d>YIA3>s^iaYI(YmdN|6YxY%SaJCdeGyu#w7vQ@kgGUt`>W^aM#`*t8m~ThBwzkjNqsmmQ+6TEJKp(L+zy<9r z=Bk&Pk2y5N>)akD?Bm3ae6TVl+41(o%|t~d>Bh}mN3qDD{8@TxlyNNm&}ob@?u6OC z_6*Y+BbuL{*qm64F(K;QFFgX5~UE%dc{gTTXO-03f%J+OYX|R6F848ei zg2YX07t4-})VxTbp`Z=|B%B|c?-E7N!eJQ~pL6RMGm-gKDJbou(P0{)u^+bvNb!*| z$8X9F)%F0+ZK=Fo(nH|>xe0EA{QU9h<5Hj#nL}&+Qnyw6BV|e2J2CDr4VVoLV@Ld` zUWYn!3ti)DwZD_Z)i6i}l0RwdkF$F={8>YUF@U5vZg1uEx$Rt06L0kz%!*4|g>}bA z+pT7SVYghvhM7%gqS>>`7UV{ZSsuh~KRn3W>Ym%Ce)B`G9~^e4dY`-so{74S>vk#T zTsoY)ed)W#?FXbdUFK{D z`sRQ;SlVZx78lU&bbpe?knU>`Sf_0S&?k)35#!|t1}cm2kCDq zB{AHgScn=3$}8reaNQ~Lkep*IZ1Yg&=XOwS!x?hIThY20dMuZOXcS2+-A8Eqb6pfi zGgF;}A2k`aD+yw-r6+Ms=D_7&yWy~150<_|=5KL7*lO5K&-wV$ZZM_jHa?V+a|aH6 zo_$jRyR62L;cGcWS-JSh6Tj!acWs*~;@Du*t)7UdN2}(!@SyOEqWstkaLRgPkNk-5 zxES=DHy`&b)jU?oFsR4eIYTeOW!q<0JWa^00Cr^j9C>!rZoBU)0fU_dsJDc5!6df@ zQy$82knOgwGsf?tvbn6Z9BL=!i-nQm0s%Jfd6Fqin%s>P^UTU|2(TR=&f4377lVwR z&oF4%sBKP2=D755vgd}t&GvlzQ{74PQTdY`I{+U`&SnfoI5lviH*i@bmPIQok-u6h zaohyXzN8?4#VrKP#ED9%3m?!KSM*zB(jqTerWk@!MpbGeyj`s8O{d&ayERg%eZZL} zquvBzi>GX1WYIq90=UX%<|Ek_#Ar*ecOtAYp<>xmws96LC?~J1KG4XIxSxfY@7)hp z6-VJTCFFDj-sYBjUyi;5X4^=_5OH^nFM#&a?MUjf!j(}3hu=?V=OM;mMg9Vlf-#-k zO&OT&0c)F>iC9S=#HZ{Jg;L8(d&V193m@z)@)z%V0UYE3kB~OQ=y}!Pl1yvlkJWW` ziDM20b~Gsh0OnKPa?Yg!gR{k@f|0<2r@Tw-itDsBG1s!VMhV*^?(-fI6ey>%jOnB= z(-_whg=rqX!~9>K3^B27&v$8TsHroZqB#uvH?}Mph4WM5mGAvQp~Fg}OGN))hdR;0 zUVxLwGKDyb2-5{3Aj-(q+Q0)7P*w>he6`Cho-*!hSMQH3-VYh5jQG`t+gaZT9i3k`L@O4XPijIoXdgVnhc()SGoLA@`2FR zdETw3{Ks}UUy$Z{9)!4&yN&#A7h-pT=1*`#4f=_;`|aY|72t`|EK_Hoc_~v*Yv1bx zz|OBYu8c`WpUn`$dx%9BZuWh+9Z@IEJTHxOU(KSL?mdi1p^w*=>-0BmtY8^57L$n;;y&%a|u0F(XF@TdH5zt1cs##&agBFIGi)E!x z}1pA~!1hpKwj zEOHib#w~fn*d1DB#_BE(XTMl)U9aAm=!<)$t2XZXoy{gVXdKm@ymRQtmZ+@0h}^e2 zg3ih~Og4JiZ=IC8d`Q_T&Rx5=Z)hK$ZqqhOGg>U?y+4hIsn)kgll@aXG4u9?aB9eqc6^F>cYt>`TSKFO( zT`4CeuQLecfxdkE0as}%5c_nx+n*$6gv7+Vp)r9Ic}Bwh}9q*a7G2D3oK+5VM-QE*+J@1~K`4^rl+8&OTmLM!c@%F`RBW zsn0me{3S!br18G+y}JwLQJ_8OQ#?XS76zChU5ilfK3qOmw+o5hR<9~ z*fa-Eu2D*8NQ_y)L%bV!#CJbxKnSqGh~=L%^fIgf{>+JmI3F_gVeP-`AHLqKVs?Hr+BwDpA~N( zztv`}#TlQo${gwsWUdR(OW^tJWG1F#K4-*$5&9(YY9S|4v(rI)SiApskNvs+IoOSQ zsnw*YzA!h?x$vPV%i+8eb8SXj@GOZp8OM-b0@ZH)yz-dwjmXgwUKYh~&ThoYdRl_=Ee8C$P@ z3sVIDa9wLrzjNgzd1!{`!KnX`U6mz0o>|ruE{qg9R#pG`a&!`$n>D{WA14%s1W}ycuDgLKGu>O!@M{Y0*;lM#5Ya zYjL6%;W@8l(=kM80z2^Nc%#2f+E^oGq3~h4BZZ3&KxhU7MQfI9iY7Lwa*h=$I}p=h zJCIiBS`9p{cSo$A8~rP;0W_i55k(@6Ktc|%qL%3CZ{e zJ8pZRv z(2mR$Yr!tLIUWZ+CE_V+@S1d_8~3^QQ_*>Qy$RZ{rj*;VaI<^EhrJ==)_Pgar4zY< zjhWV{TkOJ@sg^3<2Z}v7d>U==q~=;f9Dt&Ofg?C?o7SCUdXA}p+Z3r>R6Vtdn2l>& zC}h(A9v&)#&iR(w@=t;XaLlzC#*LN&omVW9R6*x4_2lD{Ly^?q`rY>6X=RvO(xoh; zaNZnX5p8_r#7_mPhgfE|Zk;wJ)u_ESPQNszW90qYsn8kAH~{7m5W`)PdK7tszJ}2F zoaa!c(jVa%XN1>O+o|l5Gl@%%9Cq%r@V)9$nPIp=+-#4xe~&oy-KzzyI1>n{BOPnV zIcJ;rhbJdFxYp2{QA&~Ap@m>6bnv6N1L41&r!bMfK%z%Qad`@C=%gx$piJ=UF46&2 zN~}ZW@BItZbFvTTb18rL^Y>WfuCwGowbvlt6*189P=B#|TrU444p?w>|3t&^0=2nq zxPP8}TIxW53)cbfO;8wbu!qVNhPh%cHfN6m9J&D#K`3MgVdkQVRmuz+v}&VtCcm5z z;~cIG-=tK@1Zzc`I*Wxsc<(W{eTo~HV0ncr^;Le$9!oH=*Rqf26mc^Pt;?C99G zr%|O3hUvy9eWJtzo^W1l{R%(gV|fC?w<}HWz62>8&d`{ZuoY6Noq_A1Dl<~p0TGl8 zO#dY})`E1NXdZwg80!2>${4t^Pm2*cAs9~BEpVX@B)vbDk#JrZXY`PRS8%c~gwvr( zK;%MJu8;;lWIIKgD+)}opBu3g6$w6RZII|dKfa(`@X9Lk2Ont5!e$M1l&tZ+r2D7o+c>5*rRiRVeBIjz>lE8Bi*dZ7?B)EWl z>QM@G>DDU-?#YHDw$E;`znt7|%*fJpn_Z*F6R&(V_OP`PH0Em7nc(kjJ49C6qMuC+ zp&2%dYdu6N(LYUY^@QgFd6NMSK$cZ&msX?v?!LW;sgmIH5^qL5(mhWek4Y$>mmX}TE4=yL0R1Vz5<=PDX&%GZd1NXLfrEc6l@A6MvH z^hVJHXnaM%6pfZA;hq168>fa?EHh48?Ub2-zs&SFJ+j1ruN^`^X6 z21|(UQ$xJdf>ytC8O;{jwVK<|$AP;YK*;wpp*#D7#t}_ro?tPy)ea%EdV?vR1|>*( zP1yuk!&g+noduGI`CyQ}JGzzHshr9qs#y)5HLXFn^}=>tjN8)4RqK^;&xT*ford0cq;6$?z4f0?*=?6RGX zMIJgLV7cdp!%SlkMDKwICdFc-{R%PIiX&cdF4#^J_{gmSUHlShcQ0<9h)E;2hPE#n z>q+L8Z9{uW_KQ}mwoV%?4s~j({}=YzDvDje4;{eSo4NzihqTB>x7)z4YFfg4wmNjP zg)3e&?6vvcVXsfLPBoTtRXEIlMtS|J%yd_7Ff~)Wf+yephZchh`(-=q#Hm#aqlfxx znuy-Msv~($rKIrh~5+D zo%LKPopY!J5E?ynMKXd=a=iD_{OQ?)gBss4KVJtWSeV={@gKa$FYadvbB zY;LX{%+Gg8(0$$3F(2*4G9AS!oX^^gzpI$HgO~m1i=xr90=dHp9!_1u@eH(oZI!1= zbVm_dx8F#<2c<3@fY`>oul=8ErhW1?GxCkkdj#*+9XpnQePv0PetN6EhG%6iLjg}K zOvckWWah(}I=yryhRp*hb$w`}M_D+0UovZ6QMT^fw49*z$Amh-6Pc0RIK(6^rnb8P zA;7IXX{V_KClRVQSC6R%gk~8)RP&aV7vfOYgIh;^mG8cmK+aQz3vzhhq4jkwQ zb-LB@-va^E zfI@DEvJGx7VP$~FX$F+N7}F^7fz7>*es#Vn`KQn|?wKsnxX7B!A+E5NZD_=6@3r#Y?>COC%yRmE(%$7G`+E^VB0EWYooT?Sy~7HsIoV zFmHR<`ole)=YF4vGdeD9GC(1R5YC3!(L)gvD_@eBCrg2dZni(XeDNW%ZWd~)?a_Un zO(Y0ge}?5y4ogq;FGwX$8i#H2bBH@eEyVJ1jY%=ks=%28mkJR!a7pDpIN`h-b?1HRVNz#a$zS`xp)s3Ac;)8BzRu**(HL zV$+PVzD2&Sb;F$PD{zs=jX_XDy0ZZy;H@P;RbK|qCxs)LQ`X?<&V7YAk~K73%FuQ$ zah$H;HbejE{;}8o;jv11xjYnVlV-Av+Kg!UGCmOXQweXmXst;nTpKYYk`=7_?8jfO zJl$x!x5F0vmsWR%*X4;3pI0w>pRCujuxpoI@oLdX3eYe;exonsCF*MXog`sfn?lQ$ zaTDTk*xvS|X*VIM)JmheFyE4wLAZ0rpyYUI*;{L+qj-We`OE1s2;U6OiKTq%YDv_nbHe=1$OJgNXjDr@X?(6owP8AN5kFq|SghlFV8to# z0=f*YMRn=*VZH#;CNwdEQeOdl$K$w5-g4WRh3s@J#y*qJ1fVN0}tpg5xj>Aa)3;S27@NUr4cyXfkzwqdIZFQOvsCKnZO z+f(7BGn@Os6&ph8fvm%nkiC!O}UE9OM;AXbLlE)E>3iO zJKXBU}m)(R&^~ zFpQzBfI=%iYQAsJj@Z`%*WzYwK~x$%$P%1rbR1G`PNN!L%Bes>^77sgD?rVJrtofpIwJrB&m z9IdW&7v|L>H)mipW4q7_hstFu0b&ZoyA;E~B*TJ8GE_eB+AqfFBRBEdg7*zS6`56)#l;m$&!ZWlkO&#roTkP|GNnc$l} zbL)P+Sxl2PiFq^y>r3(o^y!zG^+JV>9* z0=eI|4~LhP&FkFM$HFoJmDNK|<;HsEJaes}UzmtZn}U<$ z_e=Os)-jiXY0Mw>ESojOd#syIydBdHvNM3QoV=^n53ongS!Kq6FK+AJAdM#(Q2iA# z`R$enVjP=@AY3f6K7qQ1mNfJ>#}tlf@-#8pNQPye&^N~O4_Q7L1wOr>UuNV7%mS8hvFGAI3H2s3t7HrY-?r4HTQ+1GY_vF zR?01^YMKxcXt|1wBB++H$XjZN;X7)cEQz;(d(TP3f$v3l|MJTYz(wm;A+l3Qy zh;R3K7v#PN1lysIIR_f^;JskSTx_(?13XFxSU0aLR^TB2ZuUoKH&*Ut-Ff6T$2Dbn zDQq;gkF+74Bj}PRb}>LJXI$MFe~H#EJv67e66HcBJC3?uJm2)r+grY4_(4W^~S zlfNSmn@;q}a7B1+HKH{B5*%G5xcIK#61ROIUh}a~*X@?Ew^vythzZVAlvM#+X+QWj zX_@`G8}J{*(LIQvS~EV-(-V=6q|=b!838khG;*6sP8cf?I0dC&vQ;mA+`3 z|Hna|5mGi_3vC{@_{na7)O*w=n1JyH;1$Y!MxvZNH7wSt32-}HfXCPu=eYBPf#tgpt4qxI zA(|S=d?cY++&JG+&JMS)uu0njuAMIn>mVWILe|J5{iIU36&!p-=@sE|_-@3hFi(19 z{+AGEWR9`hA3U*BJD{g%#~+`YhH}S6>!plZS52!d2;YI5{& zVFQMlllBK;M19 ziSv=5Nw9~Jm(26Lbr{UyV5Umo;4~`BdSryAwP4CP>A`m36rSRzAC|_!wGu`^ybU3W z;op+)O__8TT<2%|B+(z=-0NrMDYSIriQ&?WeWWS$MV8!LJGyZ;#*<1&lho} z`^q%g=o2N7qEvr0v3VG|XKIdI2w~q}b*JygV0tjSZJ!RA6`;6FRZRKO$_LF#B4)%S zoX!P>!bngtOx_UgFBEwnd+bw;e2NKGco3u0EFZa96BoGw{cN%zlGubZ>s*ZY9mx$`j?{dg)pZk=m6{5@{2HtQY%^3i?I zmM_3yc-a9CR-ZPhIcyo6`24Ecz?Y(1sV5M!R}~iSW9Nir)=7f2$Sn1SevsaOiElmy z&`NGq6yJju?obap1BnL#1{TPI9?0ah0N3tBE7q{aMR9L=jWMd5aRPyLVD8%BT!LJa zv^Ai3>66mk9MFonY9k#qeojJ??64&|Y1+V02Hzad@Mnl?H}mC}Nh=68TM^a6{vLeu zs&%k<0DCTRDj-VV-d~J*B4g#vB|4<;yIjLnZvP`N{waubKIHLN#@Is?)uly=*Ya1w z{O)r^emt~qYp2@1n2IQ}Tr%+}VQK1ti$XwVow_8N`%Y-7L22D^Kf~mkZ7LVJZe`B- z?|om)ew&jVyXLdj3GRM_CV^Q>{ryy5j{b3mf31e0Az)9Bx9sE!FB>dNsUtN4^V+_$ z<*aQv4*#8pKPqLP=Is+)MIo#T8mOmB;97R$zK;pY)mL=Ab%QG+e8I|Bluvu_+9BJv z)Uo35t^B=$NenuXujAs+Y9ns&aIGem*p`kFNcYT&TP5ILJL7T*Xq(q9^^jvF)RXWt30zMucG0)@Uc)oC@A`1uNo0MI%Fx4bFIqd z6uab9V#G&;+jg8CTB#N!zZ8zg(eP~K;@4+cgDz&}8%~(T{NrQxMhG)NROxk6ZOrbH zKU`+bc=Dw-hLDtmN_F?QMvth##DLIYQNCaF-xGZFbezzOu1-c?7U!z0ie9%OG?Hx_orOk^ieVd|II9 z!4$Jv zISCR5R}FAKHTMD~vgA;SAO@s{KK!WPE-WW9F9)k$GwZi`8{lmQEeIYggeyb#-zyNj z%t(I2wRb5Rxz{3=l|OIfJBmIDd9S>>^j)XT%=e2qa~74+&a3D9ql$*V^4Jhk?3T&( zVsiSYt`Sw)6H)c^8RbIMdB&3j=}VavmAv131dP8(ks_52Tw;SzJ~F(j{eB?M%Q^?t z-giSA&z1rW$YNTdD2Tn7DENYQ)&Q|9*M$^ zBXS0shHi({=KF-dLZ8aYoP6iq`HE0BY2Zj?Jebt6v9tfL4kt!wAy*lRK32@pbn5ZpotE`_@U4;~zX1&1JoQ+R?q!QCN1 zaCZo9!M(8HE`=3P$hUL8x4YkaPxtLU{XG0YHc#!n_FQwVIp!E+J?>=`?b>ykt{N8O zf^pxkjx5XNY3falg)U-&6j~9HTw8JXxxx5o^&g7TLN?n}`9{9nLsut&lVd zM4EP4*8DvN2gFr|WQaIw zEmee86gy4EE0TjJ$QpZk^jwRlc)`)Q%yMPcGF#m&Zu_+tq)|xe(R3I3f#!z)p%*$pFams1zXVinUumtdrtcN30_P3 zDWP5+?WJ)I-at==+u6D1L}etml#OY!t z=r@0!)V`0xVpXCxri{~|yLrTiHp)!MdR(-aYBvX9gKx#PiO#e5$Libt-%G|@QOxdd1N)s(wr)(yAqKl$+Z_wJi%eTVEHz}na*vOzI7`fukUXxEsr`yZo1ZOdx{8% z!|o`$j78}>a##%bKGN6W^J_&a@pp6cSLjR4|H^HH_5pnmdwgme^aa*W_#@{jYqD&; zotuazqYS4ZAC2yJ>k5ygG}T;YAkG_FdEUzPBg-LA;L8?X@VT8IlJgFW6 z_{+R!$EVi5wLd{UadoOSo_Y2 z>HtAUU@)jlho+fB4-h@n0?SWNj_b^PI|J^Fcll_+#$zL23@%iF#-o#R)kupOg>Evc zJOl1Cv%F+HyQv^~Q>70(!LI|ToLo*A_83x3j5{oVP-c5`68>sh-r83GsbGeGJQI*K z?gWR0WAk6a^(jVjF5DupQUI!9FB#mW^)#*6=hk6Fa0cCdYa|1#2sDp)B<|QzGa*-s z-v==T&)$=Bf2eyArNAF%_7JYPaQw8ForerWR{C{WUNE7v@+7Fu=fQqIO$Ouod#Xq= zDYTxG2F9`&Fj{c;#mE$xybyCHv}^`>b9Fz_t5)w)61@7U@kQ?AWvHUu(cdT|oM02`v@H z@w7JhN;>B(Y9$<$@_6gj*xe{l@8tBNt!a%}Tb}UaWbSzTq79x>6Ap_<7c;eBvU3IF zyzD8~lJO`^Ohf1}3_3taX7<^|AQRN6Su z@kwdHy>8j7>Bzxo|0SrtSQN(!U^-c@|5#s-QB*jMbpx2SkU-w9Hh@8@Y}f@f>}KF4 z%KB2AIA?V)B4GEqtdDxi#4;GKUPZQNFL!c{o86d^5oiD>WeSE2>t;T{a9m|`-bY9y{4{+pV--~p$qZV8ga2$Lb?KD-8l7P9HY6Y z@vPBAnZNm6Rpat|?PVr0ouw>3Ykpz%Mw56;yphxFzAuP0sJZu%b=!uxtLMVS8C4ST zW5$nSYGn^=4h zblmH2{w(Z%-%R+_5UI;wfYyOEzRajWo2)eGNlrRu@W@Z3c&rEzKS_N3ylILxQ7{I! zG9dowDCWHHyzSk8^2E(`n~1SaPY*4Sbgk(&j50#8lLeI!?d;}sWWK{6-Lv^utB|;SkibyD%$sCew34xN(|k%8o#+B z8I*b#g@J|lngOr)5MLcc0w0itVB4w)zo2IREH?U%C@2u%F#-*m94W*Ek*9^3*k$-z z7J4ii5abdgEs-KsF*oU+lL`sF|I|vXSQ^5I3}I&3tKcMZdeI@C4i-RJvt~>$Yfs@| zx6FpXpD;NDA2u8R zR+*6pO{E?8#Ti4cp5E%B8u+xLa3!p-{GKSBCg%M+s zUa`)o*AYI6?AC#ZvG}DxY_oCicfzZ;@MUt44D^VF&I3>v%AG4>y^=f5_2v-L>!0m< zh(hu;UcntPmf8vjgcUjvT((Vu@uBHuo$su0+x?nzoL(7#iuf}qszkv}ZMs6}pocM}Ja4+nAIb^BR*%J`B8&ki({Lq;+akk3+M2CSN zUXD$pw<#X-ZkecfAb8p5vfOlqgxGt+^*57u_CR2S^}pw~Df!*6O5h}CdDemw*<4RD`t2>0*#dEZXM8Wzxa-DmqI z?hMd{T<1RF{j9YiKKdo@gP}*bRd14=pz!H<66ZzYX-{Wq7-cw{v(TEPHrt(N(eC0h z-v#+fcQl1g;P7I{%p}cL-h%729eJ>1Yv10r)O|aaiH+m5eC@BCgyQ1w(8lmtYNx~K z*CQS)&uMrTyL;y^Q?;%8b1|*w&d>tz2ZqHYuV^`K==K50N6JgjlBIxT@2VlLQ?fuT zyvlVFvSh6IR~IWQ1B~JI?;_#;k|?C^l_CcJU-xdB`E zhQR2rj?k5IgU)74@GyE+KUUCV)(K#-BaJ>`x3KM%&pB&89X_psV`bP0b`hIX2t_qK zAqQ}8Z$`Z^?1x@=S6g0)tKd_*NS!>Khx zF#5GaA-8yLcQ(J9pI@+}7*{Fz6bgt7Tsp4AGG)?Ua`L^$dWVB4!oHlu+0BX7Hk=K6 z%KOLWVqyAPaH55L2(*#G=ky(afOYWYMZ1jMeUxb{u-EX2s5jja*f7c1OH*JI7y2#V zZS_Q4o8*Ws2Fz^a2BYV~fKKq-W(xSQrD1GmfOo?yc)BxnX4}f(P`XPp$OWL23f+@@ zJN=AccNFsH5$Mc`-+_MGrAnTT*nq;P;Q=f!K z(HZ%boIr9godP0~(jc@GmKFXuf3zpt(+`#3akbn-^6ZX&`y;R9xM%$H?*wqItPv`| zU)5FS2$p`ohq!#DqVxRg96CiS+!;xSRpyD8@NdvxW5<3>J)V-ua&88%U*aTOuDNY_ zNqNZW_OJ_AA6ak<0V3+L;!7^ktn)E|41Ta2UVqQra;7{&kS=GRg+sQ+e@-GB4D$q_ z9QZlo7R971z~7i@0A#D4sX(0}RQa3k4=Bw4_m{a65|8#T&j`CmJ-LvhHPTcU{p$s+ zHorUO9;(&h7f)Tmxu2W&*tH^pyCK*)!l&LNS_Bsv9GT>ov{Z?Uof+4!D!uob;zy@5l9WzW@b1Qfz?R zryL=B5?GwgI%!WX(#fR+g3GcyB93!i{K+vVpg`=y4M=&JHBMi~Y zTWdJI4tZH*M@G=A=j6kbbW(^I3v}*tv4c76l zE3$lt!#4L`@U*+>;FNgyY?+~nuS{mR(`b^1#;Nq#A!QDegC0R|_*K#rv(~SH_a*?Y zchDW3+63s|EF6|Sp}i&A(BqF!(XLKz&ZTONE47!V)edz7Y-~Jhmq$YJu?v|95Xs@U zgVH;L27!GgmpCi0@S4>&Qq;(PXfDia3Z(t^3h&6s4N$H`(2!oJ} z8&M1P4aTHkmcMd>3+^zgWSa1!JNLywN;MOuNPlYPp54?*k3 za(TlU-*HZKZbh)VR+{S2Ei0p@7Pn#@bNYGP-@z9v;E6el-eoY3=?Y-0>Np@vjsT!r z62xjK2$0dFN`*syCa!35&~|e6RrIX^t^U;d8)}$2#9nw5LKXSU4+jOT&Tjhic*d;R zqhSer#g-{|aC^OR7oknD73qeeC>2yufDYM_BEjkkWyB43>8WQ4wnPJ`PihdP(W5ps z45Je3iFbHn4_Q+DCWlBw&V6V@5egEn($efnj1b`al>H<{^icTFfw6Fy9uQaPjbMji zNv1(og9F}T=ji2~hVP~0ufGpucYP&>AZlO;L22b#(fn;dm&= z#x#EB6YTF8|Cx)6EFry8X`Le|as!#G>XwGiObWg%-FZI)^DvSWUX>UoN3b&)yd-$? z4Yp?1l+e+dOQkHDeqsR!P(@*BnjImAvj$`psA;MhvUAXf2B`+>4EICcqc6#x)nKd% z2_^vTBtk=UxJH685hEc1*B;X9s3u#wwRJ#-v zau`_$GFjp{UGTf)Yqn>Gw$Fyi)0+xu3UNdQiFXBdnTOS?OIOI~b1`rRauxrNMEE-? zeu=02;ZBaThK2q5RjzPoEAtY@PW}ov5-|y%cmmdQUdHv7sCq!?lK|fyak*aI)(7Ml zUM(LR07BdDBUFn~IL^Tg`utVyQLxM6MV(pzz1^M-?9oy$#wlFV5LCx>X zD7@pI9@nqajloTrgHg)NCKns@4^H*3P1iW;86a9YbNWjS~J1=br4^iT#`@ zz=23UfurLnw>1NcW`c*^vqiK1+YKgMzYjDcv+(o)=jX;XO`G(LS>Gc#Sue==XB3D1 z4w%{TXA9kQ)-y{4jgQ1NRMgvBWwo0YB<>SN#qRv>MsBs+oaFjF+DMDcqMntJKXFe# zkEElrb46s%8GuP8Q@`INatP1(Am3|QfrXpasOi)e@Z}sT;b7gpPsdB%5;D; z$MNyoSo!*(M9ZR*+LLVL=6I7zLHNQ1=3zw!p+S&}s4tHS3DMb4(N&Xx9X-M;H z(qo zf>HNYarOrRbfI|!*CWyPZ@aLsk_Ekx)-5-1K)t_*J$Sq@r3Ax%BTmCD3n$Ie@ogRM zTC!wrz{a%)gVP*P7~$9){w|Dy`C?6q%_k-TvV7|bofX|mPw(SoiJMj7S|7zr3pEmH z9Qm$&N zvV(uJL~!l9jXrRePB?D8+fq|_gIg)woG;aDf9%oQWvKpTO;@0Qx)#1-zPtTaNRbuC0=_2@F_4CdZ<3yl}%QV zlJPwzp~m^45$n@J4)IJuoR1&B5~BR%iiUSwczjdi`r}r=)Pz5X!PtgKJoAIE)o+bo zbUmt=0la(;%#^CS#5}YzE>zXy@|>_oJ*OLo1MMmQL^cC#@7{YL@l*n-lct$lQeC2N z6D#jhN+aZVFE|RYsVadL(<;6m0i0I2LdtZ2+UN~7&ql0DvG@Jm(%v|*4NH3| zKL2<47E1fY_c>o5h~l;b9aL=zd!4@(!ccm=DiLpLApIc;wff~lK(a6Kp%?rA+=l)C zTQXYCKRj`~f~WovUB5tH?FJe*fCYk9HqQ*Mj!c?H9_SxWp#->rNm^_-p;kXc%8`n! z9b|6$8PBY{n{a1BR}J8bvy_nn<4^3%82|2Awl4N7jD5I|29olW|KpDbMQGOR`2FJ7(0}IuyM4Cw6o{pbDRH6*Rm3M*y01*XD4VWlc@ZbeX;| z0|*3%kO8%U`)*Z}sV9f%iAtm7DR&^z%m8vsf|9;ISZ`q%5LV$IzW?-R-#MccGldhmX8F58~U%zpb{Dc@ZF59>m$HuW3hKkM5#v3t67$za#u7 z&VOIBab^J*m)kCba4I~~k{2((IE(x?+kCdDcKeumCws zqPwu`A7k-0tMVY8${p8Oe^MCl8aI|+nW)p*J z|LbvxO$5>!)sy1&KlAkd!^a}R0RoK`$NIkrjQ>e;tiO#EpK11o{)eybPnU5}0|~{5 z8v6Plx$r+6zyG=Hz2R>oMS}9^KLdaN@6i7HU(Mw*tpG^oJe=Dy^lypoPxtw6-%@PE zX91S@h!SPIf3_O@{loqr-}Jo&Lh9MaYOO!@*#9^L6bo^~ZNUEL3_}>oZ*DpXw z5&kf*`ftbK?G9k*$ZYZZ4!)v)xxRKIFD@?r=^e04VdLR`NqVvOXQ|15@Nm%A4_!Z6 zg__B`{+EOMg-hWr8NvJ|n(Dq~afydIIXMY6ZEO^A_K5sU zd|6f#Cx7+d%+fF4P>a>2IB}H`zZpO>4-8)WNce^Pw*E62jCZ9l(ka&*m!q}&Z}O-) z1#s81qM1$v*g-g`WVoHqUNh6vP+-lr0_-ybY%5%jmZobgHSPudFf;57j4nyXC-VQB zxT-b8nPxA(MV_zzS+-216*9(=`1mnqSDgZfq|(ydL+qU+?nsPV*Y=& zaQj!Y&O8s9U{AlnK977EoTsH)`+I_As^MssgnXKsQ{5E*c<>6VD>w*Wqgc}b@XCZ4 zc9RLZK`1CYB#49)1z1Z-XZ&RTEPVJ^(KG6XCYQ!HRaVObSbH256r69W^8R7}H!Ypn zjT{D2Oavzg3-_Bcm0I8hx9alE5;kE}qy=1`TZA38HWD! zcmki3dnbbo(%G6=@q?idw8MoKg>tLOD5$G$>4(+iD;NI+e}S z!>t>0#n1?+Mwd#@IwI5gZ~}9typ<1?*#g1pvbgjnQsnG zC}oOQ?b}0EEv<{n^D~nPQp= z>!-_M;UoF;hHw_mhRR{*CG)RN`(M4Tmoqp9X%)HLw+}$YMGkcCLojz}-$TKw$GU5X z@BAEihNUn$3AP`P2+@~b5R;9b0}i=cbyca3wjf==Hy0>8h6ZtR6vglVLO^{+eVO*a zu)Dqn4-_`+8SN?HRLkTf+G7R0;I(4!kzwbOiOu6?kyD# zH1dq;%g%32Y|URLowhg>G6j?x-8Q-m^P;~KDD^xMBtHF#628i)rME_s9e(N^M4-4} z6pDxA-^1=pd(@b}Bx`+lQA<1vcOf* z9}*QiZ-66_mGgzp9}6mvQs;5iR)S21)2w|%zKatg3@_LaYwT+Wr&_YQ=b9vICxl~l zF}IFqEW}IsMs_cTz7Cvb$W=I`(hCBWmt)cgUE}jA-3_~W~&P`%Eb8mB8zwU%SNbs z0q1)Lj0waw^-pp+_jI$bY!2bX>2AxHoy>e5+{7(AUsGdl26K*PnMS;0pSHpAI<36( zOSI~(8W!)yy$=@Aipm13;LF3URh@mRv+*~TwmVPxe~r60LvWRLbeAH?6zU3bHVS2-kOEz2@(OX}v@2;Bl&YLMTYs!hCN+;*u*7S+|ZTn{JexBkY$9i|nipJ=Yxg?PrwG z=*(4^k$py4RLRDctt=yGdcLYNU%e;zD=Ye!1vDNr1HwmZ{uO|cm%7pzU#kAWepjgZ z+g0ZQJ#DCxw`U(7QxVK9VP5a1%ZtTr0#fdLPMl?0K0H;_7HFL$L^GrpORaaa7NvhK zD;ap)1a3@U+~VNCUpoE1?AYB2zS?M*@k@|SEqy%VlcqyBQ%e=?FeT~lFf%P=7Z6ax zt^5Aa#psKf71RfJy5al6-L0^;W*}pBgGY3kmKGRg`6fNeneL#yuJss~vzMU8hMeCi zqeOYN+jb#6t>_>!(0eMPVH3N}qT}adgM_L2yky}-j(PMkz<12h*WIP2cbOQh&jSi7 zNVS_F>iQ~vZGK|qzSE{+op^e3*+1D(+xg^?)%?n%@s8XVF8cAM1vMJI3RQ~vLp4Kz zXBC*#BNe(_B*rO?M@lJm#trh0SIKpb=g@O-WQsvcDt`Yt!ijqTtHy*`SGC_xvCt1O z<>Fi$V*^=0$=DQ6k?!d{n2V$)L>k4dQMlUE898k2|H=^=34>%PW|)Dz_@=HM_ly}* zv8o3IIh+^Gb@#@2s{(s5N$2wy?cAI!`tCKPFn}G-H?_5i)CZ@*aB9sqvNY>#2YG>F zFgIeifFfML>~6-cJIy}sl#BP1Ex2=NKV|J3Vma`jIm+of{o(;miTR~MXTX!^T$a;} z+JG^0MKMPY+r(G^=4!9G2l>0dmOnXC0N?fD9Tj=`9!FdCF8WYQD7sPKuc!IC(v@FY z8fU4N$|1IVF4OOCb~l6)8) zTSaLUZJ@>}HDTN26d4ye4l;eMwzdj!$I*4H%_@sitB&HS_Z4h5ke0F>% zcw>jhq-~JFC;x)0BdzGNVhp|IW|_`L4Vt8`rDXFcwKl-$+{rE7h5F|iQGt1LLukSr zzKw;ABfaz%$4=^&LwFQ<1OjR{p>y35Zt|m{$Jw!CVVg6)h>L!}Y0b$!zZ4s@>$Vq` zjxetD+%KgGv$t+*e>+caFL*^-bSKtS{c|Uki)eC?DO7J`M3|4cnm=_}kc)V?v?5-g zSunY`_PE9IX|Y;f8$6LIWcVS)`mOM6<@Qbw4hcRsWqRd|I-y$s!a6ER!wNnxyr~#x zM82}vG%t;7lkTX*b};Mq<}qStlJ|~fuZc%h5BE@1YTf06Y2-SEDH%m)WEBI}h;vbS zS1K9=jUkt-KD|Hco;;Z{SQbwATGrgW{%+KIxMpjV=6cHB>Vs*}w>$#&Zoh9nF4sNS zODVy3=DeDV<-2K!q?vcUcWYH@y}S8A?0nriKfBVD>;6+`71^h)c};?5AvGGZpUV@w zg9&floZQRE>u%VMssm1*s!lsZft9+S2sXizFjt#Ani(SJI$PPAGnS=hPmetMK@CIp zb2*ET9>M5(8U$^duqALUlA2(n)P+!g*yDbNRvB|h>a>&=R70bM6L<%iGDN%?Z` ze735;t=6u4AseI>@k83@_c9b6IyxFtYnV}2hU<3-w`MKXa~L($o2@j+n*#B%1Jaa8 z1H_ErX%1gYCbCp!b-y}q8f|UwiEsIZs)?`RO%2}+x+XMVSGbJ(+#JAMZjS`H$v?I6 zJ0?_nj***x5lS6@FNy4#6d&+Sju>NqPlweR!xYy2loAB6ns&Z{ho#rx`tBcMDfs=mD&v^7Zsgi@(iy1}fPb*dl zxdpA+@j0B!1Dt8LJ$MwDp#|n%Zq7UAm^Wu{ix{;JXs!1XPWQX^$WModJw!BaQ7q#P z+cxV3Q)6l(3Uzh7dvy^7_n!6IQ}AFo4adMkGDhl23bouH8IiAc$!D(II79;ZtMP-~ zwM*uwyejA2Y84VzzpFKvFFQTe3wvtb_4&z>i}c3Z7SABHVmDAVIj`53-gZx$u*nXgq z78mgP&+r={Oait|7&l=0EAmE<2&aLykE)yc^*4cm=yuZ%WG|E)P!L7# z1>)VIvHnyQ_>5Xn>ezwDE2oMI;;u7A_ZY&S8*C=Sue}-Rk5#}P9X7n@+FTvMvK7D7 zS1!$ck0%p{@c2OiRd~$j zA^|+NGO9oDA_FO_7E@h%`0_>C5oA5Awrl}g-5<;K${iczwKvXBq7losZI#SS36TRd^M9?I}~~XyC6*16fshNN#J=MckAW&J=u}^ zyx^~B_k%AEm@+!@TtO=RX2LFHSdoV7r!@B-)J6j2w1eK0R3bNWX&h$5Bp`8}O5uxw)J__=fKpnyDPnsymgDJv z*}0cd4yPVI?!9Yczc}T#rO%qhhZh7{v%1eW1m5az#jy~?*|40rw|ncM2_JB5S;KlTC%_Fgj0@8-&c?Lgi*Dm}u2m){o82l9nlh|{ls zINQq_sU!{Qlyk^OkqHj(Wq9wz%}8%OopQhe^{CAzEZjHVB5Eqq$Ica7!T6*VmfuNM z&|j;^ZtcO3b{5HE5)8EqNa~l$x52ea4Iihbro86NTUB=cC}f6GwupH}SX-!xdDdkw z?t!z~QpqH)dZna~TB5$9qfsF5WIYj0$Ua+YQWZ%K%0vx$th95Zv_snx@Vdn%`jz0@ zcc@>NaK?S_uZ9<^xa;bU77?v32@F?)a98mB{{Fs;DR4@g7b5I00An1~NB}3r79B5^ z-|727^~6@>TQ)Agu-ohNP>rc;4=8UO=>rQSJv4cvcC2)nhdZFLV~F4y|7m%OQ*{0VRQI>f>8%v(3k2_DgU+0Es4 zQw_KZ=xsC_x!4oO#)eRnUrf*`y2z%;URlUe4 zr6L`qZ1_30ZOQn|K=2ZBUgyX`vZs2SgrpY{DlUo~GIpsuS7U4WPvo)p=uo`*9Q}w_ zoJ2?>9q+~>V?SubtMy1mT))N!tx}z4KdODUfjW{w1F>VglAR;n86-j)7OhdT)3c9A zSkTu?CqTNbLy2>%H8$VPDMLi2umL;nqjSv}fveJ{j~PR3N+fH6_`+U+=SWBWLIOU0 zkM$l5dXYoy!28Wi>^@;}-zW@mh$xW5fRjBiI?Mv9@}E;YTa_fqq%af*cX6d=X3=Ha zFwoIS8`1~}Pf$@2@7jGEMh=7Mb=e3)3if8d+dO9npe2_{UGm4ENQM*&{{$J3wN97T zC(8he)~j6I8_3p1`s;*iB$+%PC23vnh2j49V+Y!Gw%$Tk+I9E(* zQMl=g-fQ(>e5b2-(Q12x4XO-(dJ(QjgQ8Ix#43E0I!BSVL6cBC`1!;czsc-DKXz z6qA+BJ3S{%wwZn^<6=`90rxdp8*`xn|8;Xw~Pk0G4wTcQknR%W5uV|;Ex5A_ zukLWbr^2p8&P;~u7>CcQ2@ak;uL>d;%QXYI0MdhMUVTe~%2V$J3=bPn)42Oj9oQ1DA z@_eZpy31X9%45t7zZ6{8hbFxG)-Mf)s3G#P;bft&;L(p{yF;yxCcl~3_SKfnfGNbg za5e~=the7uSv}kGTvgbkDROi9wC7cIAAgw2ulICs>B{hf5>n3_z<+4bFDS}je+-R6 zp$je9)!aCQ1zR3lMLdj0ymO++pF*gXlcR{(BuiM9n5z-oJJ5+ggXed_q=Bw9SMiBK zFO&vve~$0l`dYUh6#iP9RLazOZo!;m5B1D>*l!BCfe#Y)pS&u& z?7`h-^A-}Z+UjIatxiH?K)6zKrJ($B6rR!1*3A#7+Pf1 zWV~ehu6MRSk6U-z$zld7fAU(Mxfj=qcS*xdkk`GalR?01LD|KycQgd);%m*o1;2 zag|?Q|Mi(J38sE3?%+rZyyKyuOd4(Xd{s5GzMC$(-bT4R%7t?apZcWAuhiW>>wrdC zau0UDs~?zG#F&(%>XP>%W5Z$e#M24p&!L6~GTAi|dXCPqE)RM}C&duX!4c}0jGZE< z8ev%oS9hJl;k>+=XVx14XKntW&@J^d5BCLCmow)*YBF*(^pROK{Jqi9GxKFGR04VJ zj%!1V$atgtpHG`|=H^^mIO!#(1jSxy(Y?oJs2m^|D_%Qgnts{-;`TX|_DrnCBBwXu zIgWg4ptWH2_}4Ya)WVp%i8xI_5NvSglz~^IilTGP(aBol(Bs*GGOKAa#R*|JmgpXE zn89Pa*L$NW*iV?Sg{LX3#KQyu+@Hn65pYZHoQwb?X6?li)m0}s{a$irayVKq)0=$~ zO44y#`SvKc_g&qA*7wc1vHG)@$B)VgEZU3GBGNeK7jHTO4>uHBWdvTRH>spQgw}jI zRG2lm{dN9T=4s7o@m1(To8IjkogmeS`V`OY{gzZ#BHrW8*8j|O)p^*jM zGVd1wEu}UY(p9p8?#c!ia54KWBUu4$oRAlH)hQW6W_53#;kz)iTE2M4{+F89;mMza z31}44N&MbAIk5@4ooUjocJGe2tzllHl-XH2T4oS($nI4xgdz#qz;YUx&vJ?tlJ=yQ&sH>Q^ZURpubcvA9vj?xXl;+WZSW2c&>K-u+>tL) z9YHpX;9X7n^{!qVQ;lpSy-9KF^?1{V6TEhgz3(b57}PTpBWPNz(s9O^SAa9Ij+Sh$ z&vtB})fOVL02D01oE#LDl?!VO`fLaEnrr~soU>TKGdt_~S`4J#09w8ERH~11TOQS6 zp=x1r5xQZ@n0E(#2R_Qg6pJ?!vWxxPLZtLX-zcad34#OL93sB|K!}f z(S1^}KVdiBV-Jt8UU;beTp_~Dj*Qn(*JXPu@-coJL#y`6QU=eWb)8L{Ej!U@OAawV zo%W;!V!m2_P?`I?suzkL%)G$QTH9$tC~e+<&DMZfKPn?PnHa58ZOeguMbvtwb0*); zMzc6OB0-wcpul`{Cb015$**!#)0C!T^zo1m@bc)_Jx$59bAz|~FUW3}bk0IHxnjJ=)=|n|R)dkY3 zU5FIb<_j@rC=)Ckwk_|z%2By$ok4~LZsgFs_CnWRwEN`X&X<-)Ml&dpUE^Bpu|{;> zt?IHX@6Ss+$bI;|V}o8Xj8=O&Krd+Q1HaSmIRvd&nmJ1gdZAS^Ay#{*b;5AW0O%1WFA-RqiAtQ%@A_oil5 zy}aE$R98Q6p{f3Q7H3+Yl)IbF<;Ami3Wxqov)enR=yB;$L`RbBNJf!FPO3|5Y}#9q z_zH=WBVHnzcgKv}ci8a-Q>}KV3*CU96C6H)AFhMH7V%CFAaw3*sqD-c>8)!&(0|$S zjbrL1BXGa+Xu7Rl^h__M73`f9Dp=$lvArv_K5q#6I7W}Psd0Ixg0|8~G*ea|bTD0# zf*f|9B+Xa*AHU_dF|R)3P~fnR3+ro71HH=8GJS2>1`Z}4hQN!HA`ERj*hik_-JhvE z8&5UDoJ~8rvYDv}|5_kf?MnVB zw(ye{1%gsOVL>@{q7W^7%ott$*8@C@vMo#73*IfU#Zdbap~HMVm2TUOAx~Z}vxS!~ zo7SK=^Ok%#=yn>+S2rp)$rKkq+UGu4mTXWfq!Rc|7mGF5Ltv{c)f~^qxaN#wetBY7 z=H+ZkhN3f5OJj>{lX{YHQ@wi@<+4@R1bobs>V@z zui0#%)T+6w6(-}IY`ADD$f4|J!;U~LgsMlNq^z=XEM^gIsrlEYB@B5x)U19Qy6u}^ z)}~RW$DrG+c{8`QROKGX)ld5k}@*uOEJDUD7H<`I8Y0mFZHdu`1 zU2fbGzM+SH;A?Wcaye`XW{*CvkR9i2jMwNx{`hV)<@R`diqJpVATG2*Q=0s!ZRdwx z|EfN@JI%AQv|`$nE5{QO{r))h#%K3(CgCg4I+gi&lPnn;cq`08S!Di6X*agm^=x(% ztO3ptEOX?&S7qEZ^sB)>=3(AW&gu=K>S+F(v5d*-q2Ept_w<|hf0$7)(%7{r&*m3go%VM*9u2RLM z&$cJ~;6(3u(%a>xKWktI$}3|wNKD&{zS-PEc# z(RqbQ-t2}NqUX=i9WZ+G>FE8C)#a}xt|sOD?N3k6XwN5m&5K;xxN19K(pyy~NUcwa zZ$#(Wq)fMygy4ohXGcXnkDVGe>6X-|ZtO<}0{HdX^x;pl6~^s#Iqa=VAy3C7Z-05#J{bCACoc zu+>i%9Dx?K3t0It0237k2)ckJTs|7>*`mil_{g>B4*McHjQR=*vAqpi@bV_rGN@no z=*`JT;yDN;riR_`9%?MM>ECt@>DEumU*N-H?$dr<+zpByc6keI^vE-*S@eo&EFbT? z9||8uxMW;C{e`FBe{jAPNFQ`JZeuThE|>RZ^vDwwmMR2RvcK^#HKg(+@w$Y1`d~#l zscbPi2I?aq1&P1Tg162C1DEWjuALGDPn@wxqiYWjWWmFJZR-I3kso8$#NGV#9{5ld zgG8&-#&-+am^etK|&krQSEn&Cw92&Z=4vc|wcnSi^%nab-0>sXQ61YRHpxlO$-3DfNs39)J2_VCbbMHCU-=kt#;sF*nW-?n z?Ki+4_)kPZI_#d!zj*=t`LF-;n=i>Y=1T(Z^}j?7ZKjLwBz+m*q+R(*?AZ?t6TcA| zLCnY=M!EDffLrj}F&Bs zqR-2=?KIj2wE0GXZn~5Rj!_dy-L2b_?8idNGO=2s=xw#bQ!zRH!@xvI+hFGH>XdXi zG;PO#8`&{M(xRqby=T&_kl$m^DOwEJrlTB!BEwH8%b9JBn%T>ZO1;^n&0h44lrN{J zJbS0kT70^fq43o0dsI$O!=o~fYM*p^G|eHod@d#(bm0)Di*`+F~Tg zeY8c;#{PvEdYN!Y34!%X%H_jWgB{neZMi9@-{gc3BE5IzN8SYTSma}TvgWyXGmc~` zr{PB<*%hYS=xBF~fV%)_8oEkAJl9Al#E3&s`C(u|LZ*CvKry9a&@JKA?aez#%zLGy zwyO_^<0#9|40zgfY~1Y>n|^3qlipSoatxnkj}MZ2((DHY!Oj^5bTKvfKF&SW8PK!; zW$OaOxgTIsbTb~X)vL?RXczRpM49sEz%+6XkX0+TBznI0p1xk~2B7AEr~UM_;{(mW z$&CL2hx31~$=pQ7dYHsrZ)l|5=vdg9Vq{eAQN!$R-Kf55{ItyP#?a`^@G?1@K+wYK zxz;HFNCvGXxPvz(OjSs)lU$dSX2%FWr0Y(D?D%gpz3ux}EXxi{-FFwuZ&s!BuU0bK zo{IJjKA)#Xi{bQrwpQ6G389#;66p&}f0LkRhH#p(m69YQ9xKWmsyN>Em7_H7?Vy<9 zlsftaz0OJ<`aV&tTDQ2`@6rhSOlC8K@udF##>HcHL+@xVTHng{-JV%M*(paI+yrc5?Uo_o zFS2K2T5zhXt@*1ne&cp$yXC1HR*W;WbxGJS%U|fe-w(!aU*(#&!s!Y@B^pAV(Bu%u zH7Rg@lC1vf^XTCcJKR<~zQL)ad!NsLNM2{8 zdPPHg`;0pvN_d%HDT(sApjo8++A9OE10mgoTeG8$f=O29lgsQS@6h(cao;)xzif)H z_kr~%C!KXp2LlTe-vr+uUixw_Sf}f@*&J9W7!M5nx+I-;xaYCkdS6(P71yi?ZF? zh80mjz!^ko7(zOR?hr`{!Jwo;N?K}=k{pndkdTr#=oo6~fssbKafX)ehVSD3zGv_K zzVCkb^W6UV{l&oV2VB>AuC>l}u63;A@PZrIBP}h0PFop1?sL_>-Sid7sIbK+-E5Vl zO2$JfS_Z~$+!xl$)$<>{=Ep-Z5o_=%f$LmDz>iK(F_qlYGJm7&X>Jeo$2}rCwt7@< zZ1aM2@mDH@Wlg>+4hf_n(xbrQYf(h$X0gvp-!(S{o5`N?9J^#;>lrMwk>hi^Z{qg% zI^G^z4@lo~qQ-DlesX9MRBx1G zDb7Tbg<(!G{q42?`+m>>%u3!>ecYPsKuYG8dwgVN;u@_Z1u3J~0jl$6-9Z#~s#82F z*YoGVg6WvWP#w&`Zd0ofcB_DRE}%tW?OE|W^G4K68dt0C5`T!FV$N7gUCjDOgWC*` z%D7{u3<$e1z|)wbP$W42GFpEonUDZ!f{!b$rZ9Yq&Uy|?Lo<=_8I(T5+!7{}z zB2A5CVZ`@3nyL>RX+um3MnNI#M5o-k5sDU%`+YxWudK8MY1ur)modM<(wlwfDahqa zZ?}0RHNimI-i9e#%9Fyg_htR#Ui;k~`p&SF;xOU&yBWeBPq)^7IFrO-F{KQj z#GZr?CRFmw%z&4Kf?WM;0yG^Wi9o(L72_yV%N%b1?!NZ*BT@d;bmf!6Lf@G4-xHd( zyItt5cwM4FvqL8Jwz94z?;T?EM{=b6Wsp_Phvg0(vCIy@kc2~f1f?u;tk!Ln*Uojc z$S{yow`DB)9D8IZJzK$+`Zp5m(1#W{l^Cf~VeFUGvXIg|aG|QCHWCjEL z*i=jGcGqpk)!WHraf;!MaB>~tSFNU$Z!=;yXk%Y7-mc$o1GVsD2Zg-cRS<0gZkcUi zyTa1iZo5K16)Sg`HpsHgzwxNMmD2ivCm3jQOe1Z^mXqMP5q!iw;SZ(m@{%wRr zN|p31(?`NeZGk!?80x;Z3}=K=XQ$uBkJBxs6Htf$&nFJ72XMcULJ+TqIYc>LRy&zK zxkDtU4!Qg`)+lp2$-FV=Xn8WrbvT2v@h6E&ds(H+%?h>u0>LrvG5B@oXp+gHtMa7U z9)3W~l*u2vHaxS7&EhYPkvd!cB1D2W(w8i>p8c%&Gjj`-E$wqFoc09Y>R-%VjV?ed z+{18UsK+#vdJfQ>u(rchduP2OtN@_I0h%s|fhLpDylK9El>-l?FB~M`~ezj4>LX+%w#eUfIj>v#Dx=S$oWE~^HGKpb>U2J{q};S zN7MS|sArTjS2Dh!I}Sd@x_^ji_VN2{puHwr&DIwlDdq9e_ur(<|Mc8{b!z{1#U@GC z_LVC)2J;|nb)J47D6>!<1=IuSP2_(lU;5cuU(;5&Zr=Uwc+Oq7kGApr?aJ^=hika* zlTPnWUoUzecH@ywmo4PV-PJD}b3bvQuIaVcALme4_&kE|?Fd|h#1uakkMnIs9VI3< zxSBWUfhGAyxDadg+sIJZ_8Q6gYIm3UK}@AK?&~M?T{oCOyrJaz>M3MXbvsUhhYED> z8UVr`C(Q#nG5g;(;phb0bpE#g+uXpv{drSKKhQxSpr^>H1a7)T0D1LeKOe22F>;1% zV%h1h&-3ZM3tngB)!JWREo8x?K$mFrQFejwdpn~N*jN4EN;7Rh0fVuN%8hYVt!eDb zSGH}&lDqgxTm5%}uf@0#l5sf!0}lis$?6nDv9NZ*moE+?%~@^cU1S+~f2+pbM*7e@ z^hOpCNyQbPocWT-EoEI#lJIaCn?1P<*N8c|8n*P$Qvybx@)Gj_F+{~CK+A1;OTWRp z25?Jfu)vi=!OR{2H8K+RPntgu$`=&fCsd05h6(?FW!ff=C!fbTd@sa(NUV8r&0@@e zf~Q<9SWxB5m`Ry3!QaRv{>#+9C;*cwBaCVa%s9bdWkOOtgxjL~L_*l8B=>~4KF@s8 zZO*^Zvi!%g5(hUOQmZ_QzXPnyGn`UhzI1ZP|Jd~Z>)8OuuvWpH_b+8%$m`90O2X@G zHj$mhJ=rmT+iFtie?B7fPY3gFR|bZtsLhMmd;amLfA2&8kE_8=roM;FjoYNWpm2AV zm4)9UlkVz&Ff=9qnUCZHUVD4IzRLH%Zwr6b=wEJ2pYVYJ$`obA--3VR4)rg;f3*tM!VXi_?eRu`{1Eb=3BbTovU~^= zzxgM2c4caIxD6tH7iX8lmRX=izek}PEwrOwdaUm!UhT~gmH(`i1Ru%`?aN$Ame2S% z^0EIiXSN7n+>WeWHSA-)j<(HdRF$+gtf#FX=)0Mw#QUEqfIG5Z1_niM< zPO-|XN!OR}zOenF(F2USOn~E=BJaKYL%);%Om?-9gy8$Zf4rHdSJSo;iI4tx_IU^g zSV-IJSy}(v7X8n^1-t?`B6$0m^&i{QOL|}-xgWl`_s5$VOaj<6uhDJpKei`AU?B@` zhQ5FF$D7Fu*!2Gof`hd-q4O)n?K&$~%c4=0zqhAfqMGtEd|w)Z|EXoF+ktxvL}&0_ zDITx=W!u*uf9!2L44PJS6uV;8E&m!j3uCd%ojIIJ zEGMr!oykb|xO-q+T(>$~qlJI@JJDr|+PJsRYqp)rY0!6lT9`Q($C1J!#ihN=sf4jGY%8G(uqTDg6nf74GYLT9&4uS=2D5qw7$diyj}-(BEDy^QQ&?m z;=0^VUu`7Ri(E-(K0XHYJnJscH~or$wvj`MJ;IqAz-iR776|@o5S(W zLJ@%IDAM}=j98Xb+A!_YUnbDRq4@bJ0forQWbgSK_}!&SZ!!Dv_m?%!0_1hJMyC@# zQXY?coj7ei53 ze&BM=(PssKf3Au}MiJ*}`heJ0tF+dJrV=$t^)x`A6SYTgLc{+mRNuaG`1i7Pn?juv z<`EjCfdF2Rn0S=nB_9z{dr4ZXxaCf6VhZvHO*cTeF;oE@0`hP?I@o9KMImP6P(oQ9 zJ7~j^8JKUQ@$$k`Y7KR&Eh6)Ji0yE~wzx(ioUA5SHIb#+Ga1!tk-*?v570cdNN*op zY5X{#LP;>>#eG82U?DQ-!RD+?d^s1%6hMRcNIWc@y!@)0ty59ia1o~=O>i9WP6PoQ zrW5uh&9^qtG8fLw*QZk)8&<~Bg$H~AWf(2+Te9aNV49t>PhHi*PsA4Pjn=p<8OXMr zBnw!DzyR~mVK<1TlbVWAqSM24DR;6(-ic4HkOFkk9S)~GJn%Jl`6Te@I?cTR4)W^%FiP-v-{;`3hWPe=^eRXe!vF{?6T%M)sG<4K(f8yN z1}$u9apt}f1e1NCl1}5)biL?#{y@x=@9$UEt7hGS6ZTG&!SY-dloFa-_KEcKWq%PV zZZPNIvUxrv`a;B5uzaY9qT@987vZgobw4yudvs9~y|=K{V& zM21@a-eq|2x(QI}%ZkGDh3SS?Ct+*~{fP)OQHr%~2jmKQP}V<|X^cF0@KD6B$I zGXBl&ANY<9yz@6_I+gh>z6f_pn>958(Y|9}-3Yr9hi6Ac3Hyu$bRMR*(U1%S+t3#z z^`9^?_Z>qeP0T|G4p7*QV-pr^p!V{ zv++h%%=tSXjR@%si^aqESoejFud_DLbB?syYN5YI+g~wDi$Xhz8HYm_wW_3(mXrR^ z-0hz~`M~n9u@z7(OEIFISfb=x1=!r&m(o1aC%t!jID_@{IA=VU(&UlK`UhY0)2U80 zDVhnwPlc)UCoek9bf=wfW3*?HD}i(}zTV49kF2{wU#hn)lY6a|qGJa$XKTgL!h3#( ze&_pPCXr|3Zli{@KGA^Tgce7_8W#CX@$3ZgJ<9X~NSg&7eJpNKuMZ>N?yv5XJ`F09 zJ?VMQic^JYg)i;>wkb`fh{PkG8eQVYjj8Td6s_LvmC%7T_f%@w7c40RFck51%Fywy zGpFpf5i)aaA*vS=^g|Mz5-5LD%^{zNVsJ+RHU#>iRyl?MC0suw%CWSKjKP4fRVNh-x5>8I@mAIwNevH zTNJ#w-V#aVT#&R6gyX4x*XN=`{G@2DF~z?hE&lrpJ=x+|sAmU+GyLl^^&Vq@6mIA- zm8Unb@hq+1sH?mW9hWgjE#SF@beE6+DFnU4fAQd99ly_AOQNUm&2LI4SyA_rS_@pZ zLN0#!0y1h#G^Sk##oyV@gv!hNq$T$5kMj?PLmS30l#!ru^QWpbin|{PcwbX7nV2s2 zl)d>9Qnd(>HWFvO+)0(bCXY~Oo=p*fy1lR*A`uv{`efudnd}P)dxgP%j(j&pDO+>k zL`FZ%zRw#_E7#T8;kVpMxiPmgTdV4!Hz;&^aTwsyUXT33r`j~u33}{vW?4V|j;GKE zuXvaCvRB=>WJv0)G}`?2aY^oqong5uXVvW6X~W_J?pEI}owGjm{nPW)>8}nu$Q{h1 z!PCND*Jh%oE2^lc>eaz|Wt&Qa4Q^FP0cKic%Z}Nm&zo~;`|0#mR~JL1PRIS(^S+)l z_hc{kDZ9f1on6i#btp}>EU2pE_j>Ergq=QeP2#XKs3utr>qEFuzB*wz^{%&&#>%tL zYjEmq{X|XL=M3hJ7Yp9H{JABWM8%2_#Ea9ZpTb>+lLY_btjXC zFom=YeSJQiXzWXQV4lqIdDkZt(7m8*GWRz=Nz&p*x5ygFcS}H?Cv3;VG2G=E8PZw` zTK&SB#jF=P&7C=uG>%)QFfTo|rhGIM=3Mxc?)Lsm zrB5~5yq%{2iNyLnB&M{RkfnXU#h1d<5ts!6j%Xh^TZ}8oOpBgToHQW_ zQY1tK&1NxrmCR5>GVq4DHFk9EBN-nvfRu5#RG1fZLvn>Y}K@2>(rm9 zBUSll5pAzG5&}n@(~vNz@ByvSz|ZrOuiIibKj`moE+ZhieD$_&wzLB$d->(lZpAv% zi=Q0|r=SR^^Yp}v-o;F*$!f?61Hy0nNE9y0Z{M~xK8)7Ewxucaow&VT8Cm%$j2Wu+ zJT9s5B3@sNq8i~Gw?xmBXh>ACF>xadpOaWG{64{?G`Y`qCVK7NA*uKytHe{iBW%fgEOszst(jEzQxp81F&L1yLhMi=@@ApiD$sHIRV@A#TfIk)QJ#^j-a*aVfv-oxr#>*q-6bj8R)cUEvJ6a8@9?*N z&xh;g^V9=*znt{1^8r-)%I=l;*}(TtLvOSxUq56068E1PZvHp~x|alXm@P&!Xjzo- zDUhS`M0^(X+?hw&Jg!ZngfmCrDpE_?uLa&BzHNViiV;3p$5)cUX%Uz>VTG|iSnT$1 zAUi4VK2r7rIG;PVe&&Qpb7M0htta)%ai9GH-K4w@DyHMuCn=iW&w)c4C{KKs&0iT0 zm^~7W3JWBR3fASH?u)VY5+@d&Z~BVkL%$dI2|rHWc?wuZdQpdWdSRP$nQ7_f+7=aV zapZihqLMy7qE0`sCX?9RPB$JhRv&iJ-gkH_tZ5~x5~-zI!rVbs>#c$i9#L`zsgYu3 zy3qVM?<^1Y z);5F%iig&QN?ZK~!;;Ihk0^$F;w*nZh>E^JjU323EiA|WNVG*dW?IDUvi6z0H>ckT zvULXjd6A}LJK7}u{D(%xop^F0EAd4N=7JwbI?B@yb>y*|sqkS7(Y9vFyPkbgw+h4b zO7}HSQp3nuAJ5y%RdseELejC4gCYZ7PXW1Fl0b4rzsA+6TF)|8ED~~OBb6)Ps#?ln zO^PhN&R*tliM&xoI72GSI8;C{=96h7?JYO=-NXVXk$g!G#;?m$;vx?63FK@2vuOvK z3eC~9jAYj)#Peueto(Rfka(?r(d_%7ZEsQGi5uemm0Z*+DQ-5LD^ml8RhuRrz1bOc zdW4CrDd7v{-Cn^c<-uAh&bMnVt&E8Jr8s6IBI@(Tkr~cBHC!rz>nQoD?wU*_(3p1s zBqBBFw$?F$yK|Z0)QyxPUssD8rMJ7=10T>eZ^fg1@d-Sad5hmnIe8{krWCdX9tzR* zlWZiJyr&jDy-R)o2n^f;*)8VwV9QcuOhsAb%G!%oJax+wZy@V2&09cLriL5sJ~(&1 z0~J2ys!spo1i0Nxti}1RV3I0ra%KnabnVGWbZsqI6iY`!b0ZW+$OYJ8xuVn;YDuo^~n)=?ne8awWc|@Q(`co$VLAo2vmPtRY2~pIZa=u zxo!+vrgIEP&l4$QT~n%~stLlAtz;J?Z|+)#KDGa{Rj^K0^o<=Y-1jAv4Fdb+uQ`Hg zk)xd<^~@~p6h#pCrQYZ1l_?@D$jLz&IyK+aYi`}QU6O+(nK+9vZ1M(ub+5Pbu^F$l z9R)}Eky({xh4t$Cyck}2DiQ-7x*5+TV04%5&;V};lHtAc7{(qz3ynB1ldHkx1{AxE zhy0MVwBH_&Czmnmw2sG|WXpc5ZND=)nl~>5?WB=Nqa!qbj%6Vl+13qPo<6C1!^{SCaC>Kb2?fprRReGp}=Z=g1by!8dGj9GX)6W^J{!M zP@)My^)hkMug3#9PK_5}G4@*gx#zK_X>$m(6WV1zQyE;JJ-;gnuMX+%e#A+*@fqVD zXPH`~iMQRrIUoZUt8RLaO;S0w-T3lkG_(GrFYZX24{qv`0=Z%Q9$|3Wxy-=36 zR5)28Z5(VxLxoVd95LwI#kXvpIZchn5iN586TN5O6p?Dt{+61yXM``;=fb1$)R4lB z#|Y9l2zfJMlOODoNqg+*cd9?2s7AGHTk39=+Jy4K1?mvSzaD$jz=hWeTuSGDlD}_F ztQd^EZnQ>rsHX=@A?utx=z@o*$yoe|@J!0t4M);zfD`f}eWi4ht%d6A8MvP>*0h7~ zSQ6}vC1}V%eMAl7;-f1pKGs*;(EuuD$x)_ka^yxZ(}6^rZN_->AIAl*n6vr4L5<=H z(6K8Z!||dM`upyQi4kz$`+ydiftlaDvt~8gNIRRSMB^+EwnFxD7)tkCYBa^c}}Bt%8P*+GES)zWd*ena>xu5m#E4+p5%D zx4%x>Uq0HAYJl_`D?zAzOehvQaUfd|}TLx(8 ze!cauCP6ZmIRLj!dl8XA!ix8eA&DA^*);9u@=Ui= z9=!{hzp7>7fTYI$TZGH$u{px_DTm7PV5SALMoh=`^X>wLu4$i5b&#A4Q{KE}XyUAc z@6jS2Q17PM9pXOK216Tm_q{!8$juMzW~V^YQ+*hlyQ^4*kN9MMhii$ZHM-KKOGAyp z>6c2+8;Mt_rM|xS>D_)jS_0Qdv68K&%o2_|nD z)kj3DAIAd1k|da0-GsNXq@+N?-9$*HXyD$;=)+=C0aKG~=Lh{_xh9Y60!WkwX9f|t zgdbYAdph}=w?{YwTKDB_vFwjj9L|vR^q-C!FHakJEg(T0oO8`%be-5;mAhjPEq}1? zkOi`X$Pye>8CSmv$Uj6@^G^wb%23;J8D|fWTxC$_ttNZ)1W@n|NPLK3u=Y7#NNg3R zI~cc2mnY~04+^^qfu@i{W|E?43(>z3iBoo=nF z5B4qW1UdRcn^bMx+ACpg_jQ7bi3d%ALftAo(Gz0+q?OVUe8#ZrbSQ3FIJ5BneDyZnb?x~6Zc z@7$G7Hh3rA*+6?N_UeBw!S(#`*HbI>cA6_G*AG~t%--)tyHazl_phl!o+SKKJk6Im zXWz@;mUW**+)umNcG{1&^m*LvqBc=wPit!9qJSi&UeB%&Lfd>U9FB;Y=*Cvt85XVc z$|nT{-Y12YfqMDuGJqTZ^t9jQIhht%X}~HJ^X`Tzag=ab(CZ)uo)hhrx}51GVe?)} zCK)|=Getf*zCUj-d7nE)fXazJuPXWOb`AFESGPc40sTWJs5N<&@AHGl`h3VRCS#t8 zOMOZeNQ!r*3;lUTD;h^!{w;6&GUt_8CtZkqm2au5_kMkMjnn#1zsDEAg4t$jusk7O zbyf^;_Js{Ic~AThkq;c1wZuf?Jx8_e`J}WGa8lDSsXD3B;P%bc%h#K{fxJvRP8 zxg=ZP?B?%GmlL=;aKfPs*?*iWz4Pg8t`_mO4O_uJ+RL7w8awZDq8rbdI+b&3W9OIh zCMq6YZP)6*mvd@i2gz%5;zrm}MOVOLTs0jBK#G1bVi41M%E@w~7|40$GGF)m!pZOs zkGh1xbG=2i{IR#o>0TR;E9e1S`C~Eq3{&U*VI8=y#z`{uw?h7U7)%s3vOdg0!uHI- zahFjZ6+R}lsqW$tl}r}$(2`P$5l5M8CVFA%O!H?bd8ve=25}au)~!UPKoqF z^G?m4B^kcn>7#>JhsP0Z8_giAqs)v(cZ(>(^T;+Cw#p8&tsX;UOWICTzARGNz|V7q z^Kgee7j0FOroqanX!kw#PLbczGk?aeP;wHsH)qiLl3lw^XXloJH^t_whYm|4dvY@7 zK;x-}b&cknsXkw7&qj2^@x0Vjt@dpNr%a(hPz z_Qy@QK2!K=e0|CE@A*wJc_|K`QfY7`PF2F;S%8wGjHi=qcc_jM5ueK0wN`P5c5m8t z!rP#I$*r*)GL!3yXvw10?Uxo=tPa|GinSyarjlFqBcfqj^Eq>}70MK+ zwPA_x4LzoXP)bt=pUJcfu$Mm^U5@S3hs_>90cE#mme~E{Zgh)!XH;0ph=EJb?ErT9 zqJ&1INleD#D+sP{@ta+fFu)RfQEMlM&+G6HE z3aB{8!T7iz7;eemvm7O8ZQhMF_{hMEfNL9_Jr1WFy%MI_^gU{YXoDTc-PLzl0S&kv zGc!x#2Qb|E+DsBo2NZ)0NNoOtCS-RD;&nLpz3+%nZ6~drgB1FvaJ*#LPOgb!mX(Wm zJN|7P`Ahjiic#gY>BFmQYl{@8IRXkzWHHJyMMscB8cHHsnSMpIh2u{ZratrA3UC~i z-ftALlLTUq|BVPU00IperA=);UdqS_C9i2%pJ~_m>h74$!<+qNepS)y@uIKgA#{U@ z2xE3g+8cbdUhDsqYast;1o{6v$6KI+yUdn3$CLpvwfJWh`X5lD5)Sa^_5KqGR{C9m zOMdaY7}q1c4Du{?QYho+I!p-ll+dWb+}4SeePczgk>|okS>D+DP#-`1Y-{7T)6B}* zfnFP&hbI?f4n{4?BB%5?4;&KATg3t?*Grwf*vG*|Eax4!j z6vo#&$E3!6(j2kdW3guC!(6eXvBgA}{PTjY$^(s~p88GK7XxNl z$8W-xu+LDbpD8t7vuDX6JxI_X8_8?i;Rz!2A@P{;v~sF8U3z#)vZ7G&>YNHFcp1d{ z(oFRqM2qwQDyTf$UkE^oBz9kJX&{66Rmc=CGxv-2Kdrz^0SE5l>a(j;H^(DFJvM3y zJQIA%?-R7wWkKoI*5`Xr?zAp>^ea%-&Kd(&d)B}^pq4D$dV|q|j)7r@xM#UF^i(-uGOLbOhm8J>zy(`ce5>{z%_*UgD@V+%=G7CgcY^-Rv z8~%zv6P}DJQLE}fa}<)@VQQ(0i(;(uc|$()v;KH=Nw}Da(A})0+WIkXjk_2t@Gop? zps<0eG2cFT;m2>y-(9m82UQGEc6jL=c;lwC4`qHXL$?mH9ANkucBWGFsc$-8Oy1tW zTCr4TNbMwhoeT~@xz8tS4JU(ww~e-}&t2r+^$= z=Xnhg?$hnGpeSb(Jc9HoMtn#J@bqLR@|PxaIUj*@sN}q~BSh@BF5A^UWx#YQ+^Qlz zVYik;OE-biQmbUENJ?Vsf)Onxh?^GY$d5@TIT@cd#VdF5*G4XyJ0ZHw_W1xcNVVsL z7^yih5Ny3WyZNN@SBLA6Nbc>uSHYI@pGofi^6$ho#TTt6aJnIoxQCplXB3!QXhI2e zGl^o_9c4(hN$@~VOP!yWlXL4k8iPu^dHW^Lwzisy=}ajCzz#2_kZ&lBNcm838Ybb9 zww50->Rn0s{5eZV`H^YU$Z*>|BcV#5wd1_iv&R6oJx4%;ny^K{%QK`4P(y#Jb?+mh6a*#GUL*l8 zy~UKId9ExnP%){OnvAvNXG}>5D%aK}@=V7y2t_w%{&SaSCwR;or`gw+1fbJKM3DAR1d-!D+<2|Ttz z^v14*xbi0eX=2HH@X?hF@L@99UExjT`|A6uhwJmv&>@B+QOG(+^tUOQ%yfnN((2B!;kFp)yHWf=5P1CRXpQ=diRw66`y19 zp2n59`^_{DE%f`UpLuuU(%dK5MYS@aF$klH)E+P?1Ad!{VhaEHdkP=k5uJP5IT!W- zw5fxN^cRT?3})abDPPJ^7feG5M!gLNSE;>+=dSszVHZA zo_51iMcn11X~-sGCy*jBCXOn+c|62J$tmnc!dL+$#3di{Kxa}W0$dy8r$p!ZYQV4W zjrX+0ez6l0=#Y=wFP##K-I-#a6#hnLA;eQ`pdarU)D5&lbz-6TR)BVL;|K4Sxx#zq zGq_!-$ajS~IS=fzjha%j4XaEo#$}NFQB*=4Tt8<6miu=om&lc9-Cm3-o<)@DeEg|o zrzn#cDUYt~Q1;V17YQn6AAQutf|fg?8BMwBG%`!AY^rF-_wfEEUufL{Ej_^3(5b1+Z>VoC7$X?!>lyT;ZA4D~aeaVuMiVEy;&|2>rNoGy=pywep>c+s z?>Of(IgYKCAraEfyY5BVhe_5W-QmY-uh6xdo&1!1GhEPzuas@p@aiXOPos24eb%LJ zJxQt`t3O#V@Q6Ds(EV7rNT}qNz$w2g`qU!kL*C=vgzD=SGo)UXj+BU`s>ft`&@Wt{ zUY&oRTdFk=+23i^NxV}^>t)9k0&;Fo_&5VQb?w_3TnWc&nbPs)0wpMk>ka{-=}tEz zzu;VF1l^HrGMnP<_PXEYKKLq~T9JYAYXKfV-J_cAlwUW0h7u@&b#|EF`&72@m3Pzm z9)Dt6wFoue>W@C;?Byd%wa8^(rEsg~AyX&wD){=Gldv#6l5^A-Nx%6EZok+?%BA`s z-gee|6ul4$<;52FVMVccWONCzW(1x=XJP#oBT8Uf3+b1+8-E`aey|g2H85sNwOts2 z$AOz7xy%Sgz`Z5NL!v@%*r*eRa;p`wKa%$%!AOFUS{BG4hBf{_x|CvxA=J7E5ntMvQ| zJ!bt4cSP4bvS06m!!Pv=XkS*)ziWLqeS7#0@zhqe%BEpSJte>La)9SGFyWgatRKV( z-0f^dZ)KkmQSdei!`TCvY|AT&D-s#8*~BA?ohnKqtN+CXP$v9@@cECS6Xr4a!3$FS zmozld!W#v`#)Qwi<1bLv8gNl-4`F8t`?fueoo1xxklY8jE+Jwsza=ltA5&o61cOYCW1 zvhKi$0C$M3dMC@i<;!C5Xo_e&qvN3IfSOo7pdu--!peGCVzzo$7P0BrJlSs8mohGz`o0-Iw7t#R!|9QN9PM}5_)P3oE!?9j zk+v(fcT>X`!*lwRezZ`X9bswoLKoW>cm_y0boA^6wBSi6r4v0)b3_Uez~Kr45*fXP z-|0RLv?SUHw3@-oGRySXfP^8%R=irj1`n~B<+^mcc%QL9i2#^H({1ia%FHIo+oBI# zIC<%BF9~C!>VB(BN;0L{(dPg}y%BQTgQUY6V?ntnwl6fUK z1UHzo2f-5#9_8T^uB%c_M#D)+kH%_0JrTPFnvvYvdDO)Rv&L`=?u74X#=*m2#&iar zzj`^$B~eax5#$1$+(wk1TXUHHgRsUooCj0va#gTC7gY52%>e)#_KaPevo5Ib?e)_2 zkM3M%3fg*>v``uRoN8Y-ont|ot>M_Pr>1Zn^tXdKW@?a&wgcsBabsvA5_eq4=2!={ zVw-r+-_`T8GY%*ezvZj3IZ3f$IX(rTFhFY*Z!K9O91p%hF7MqD zN!Y+v9Nj=rS(uVir}~5d&jOF(A~pn4mo5f!QPnu#1@I;zd*uza#k*>u)=Ri}zsYeU z1SiJ!`JJ&Sa8Pj!NheD2Twcn4#Wcke{wn*C$&x)$=%@)E!65{tZl4Pza|Y`DJ5FW&NU8)X0n~EJ#6b zeS4ctZI3gDTh)~eo-FP2c3=il41F~-kv@)AOy+oXY1TSGXXkUD3~$MHsT+|20MQU% z2jUSs)x=V??}Tkngpa>uTu`0Bmv7|yq0_%+eqloL}3qW$$5;KO@=l<5g+EL=b67=UDLOX#XNR-d@`Px!f$fj3Px& zbl}h3wU?;o>y|#YdQ1M*ASL36Cy8$Amh0xL0}ILf&XnqhPY1{Yoc7W@W(cq^!&z-r zE<^Ui>F=o>F1=cBt)&#Oe2~)5IRECQ9Q_HMi=M%)6^SQ=D`QaUQCG>}H-(SKE5sk9 z-O^W-iP3^k8xx}AeVI;9zlea<4XX}zh8uhYCHq` zW{WhLf|f@53p;L8oO;--Ou#k+DDE~U6RN^pXTIeGN&sTq5wui1TXLd(hA+e;1OVJjL?Dp1?my9#qerPaKw>)kY)B3Sfvc=Ru2Wls2T0Y}oGCH#HO{am!P!Dve6aPA= zC1!V+fZsq^n^c-1fa8MuB^h(Ud#8y9IO_h_vhD`4Na8az@Ls13h}|1i+P)!`B{Zd? zW1$C9b%xO?Rx|9WcqH7qaFb1O+8kC5lD0Ua-8zSSFDjqintw zd(u6tW*9W>lEaIb!;|S{C;Kt4!fHfg+ZtzHbTqdg>dVz=C$&-q=RAGXiMY=nDN#rQ zi9H;qMB>9w?NG{#3&b&6XZugwwqM!j6HNmxnlPPbz1%(<2*`10O#TX71B+3;Cy)@% zhiy`qSnvkmMaA?Fn4Waq*kR*Qs)-6-5|uZh7{yCJ!P>IbLl#Ih;@zhi5S^^iTy(#w zu~wG_i7^B-`rq_QBkcUtRS7h8^1HH2F>P@B`1^!%v?$)*HVWuH@2BX|kd+;!Gw=08 zi*2`zN+~0B*Zqydni0a%-<@%3jng-5oRl-@cb|JF z=xqX{Z%QAF(u4)uJ0pjC^y^-`VcytBpH>$5G3kue8fkbrX`M5z%;|V+;BPG#GlEu5 zON2ouui!+KcT+9s-Lrj?qNfT$1buAs*rJOM{t$ zC!JQaV^0;_FohSLxA6q(X28$2waD`hp&_=02|Zj4CgMHYWoaUB@p|i6+`KZ6>GGLu zrFqDMhvZPfcqS*q#f=vyz@I+%JyVRLp$zJdP!5&*5b|1GyQz9fd^$B!F(Tcxz2zAw z0)Q!`hmyyLNnUiQ9Y%|EwSkC8eAZ_I<9PKb4~zMp)5_SL%BD?6&75NU)9vGY6p5CX z>CUY~PR&NJ1=Fi`X<_$Sd-T^r_N-~62D9|IO1WfQQ^+n%Gu0~a;bFdPb_K#mL81H? z+`2C}+}O;^wZk69F*Zd;7e8|9qnu4O*mjQqITxM0 z=7@_LE|Yf$DjsgtVE0Qg;>+Dq_;l)^b17XS7Uh3!5x{HV8=<*q%MMkXp6KuF1d|(;JQ!Uz4Jhtp^Nd{b; z&_ZvOJW>mL8loO?Py62XCZJ_byzt(TM2^mTt68ioTL`BqFfmT|m+G+~0|!Wd#idik z24xN{jXyjhd`vg7PNALm?R_F9FXQd+ui&7Ht1nuBhp7%gbnVi}W35itJ)}Gs6Ij)Yv7`?Z`a=EWhR%u}8 zYB4t>pzO!V{dCSOtuLN?I7uzKoVmWO_)D!ZGWnXDcq{WbSlOLN(7jCk|KcntSC%i? zo5V3$mHGK%^lGM})(EEC;q|T-l{d;8_oeuZL$h&OMibiBN4L?~;Wo%2L^ngh?es-e znlxAybjRr82ra{s)xXzJG3~xFC01YC!kG6K&p(Ts@ng-cD-YK%@p_zE#lg#V|T2L-|t>h-Mq=*_P^=rt|HnF`FeTDYR@@hRK>lTtS?zT@=8WTU9vvJY>6U^AnWATRMdMelfAVRK88g@LYo z!x&J7_v@5s`mY_ioR+ja9(0>zB~Ce?Qt* zKeA4SJ?acU@Aci=uZ496I(oL}s9JAQR$q5$ zRFemJE5W@Z_|&3Ew899Niu5{N77T{K({VV{E1J;j3lsuHON#DjbpyREGJ|B7)>qZf zg6u+}W~N$Xgbro+Envu-z`2F@#ZO@mA9XixeWn1BOZG3^eY3R?DYZBE`s{Pyoj1%g zx%_54xUmPkp>^Bd5bLr5ed<+M7`7Rt4J$Hdvn0x-HIotriczhy)otO#W@NntOzXtW zoaE^|&MtjjTp-m{!bdc8JiJD~!CUuiC8y>V!2Vo3^_$gmcd9lUPH(YD?mce+*D!2^ zwUu}8_P^#CW``aK#pMQ~b57OyPUhTk*O{HESOCU>!DU*9PyQzI*>&qb-J(5K3m<66XO3K7G#ycXK z6H8b`SvGe_9=WLL^$=)g=+`W^9RW7a#qrFREi0;I$DD*jW*--A23zztRS%;%`+6Gl;zr&!C^>tE5nAi?P%VTIWK$TjMr6{70%TmAPt0GTgwR_##DaV6_IKnFx=a(*hS3z_; z;^z~MItlyKy-Yp}F=3R8<*}$jNlB$xq*MjNLFez3ZgD#M0*a+JaL-=f&=3G~g4|%e zyBFLf^TarFd(ct-lDLKDB&D0889&=GL7H>|xgvr3ffo|TgWA$nJoS=3f3kf%-&KdG zadOBZ?~4=V$}E1@V5B(v%VX%NQt-tWU=V}evzHd1B zM0MV@_Z4N+dEbi9VD-D5C4t;ZU}fzc_3IW{W3}~3-hFLCwmuB|U^A7IpDWR=nYU|k zeD|~lN8w8fz0~#ip2``nautf)CaG09WlAzZA*V&7^;4%=`M$Gwx#=xG-ociab(_$L zNNvZZJG0f;t$_kGm=M9{vLf09^zvuC$>po|&;|hurq==ltrkqFf(3h-8rUEKBI4?ApBS}K zst?fg_N?}$wWr5(bFqfE+-&cMEI!e;R>Y+eyWa5Pkzjj&#u(5&Pah1YYY{T=aB;yb z#t03uF_r_tPDGN+CA-f8_7deLAi`$!SuKktQ{dcHX+% zr25E^3i>!9&j8Ip1hP(r8iSMG{X{;&5<^GD=-+=;73q*%TQJ8zQH-S0!dP@2%r%FZ zM$W-sPihYf_C!K`ifR@={c4NA`^Z^Toi(sgjNExY7tSnT)ZHD&whC)nV&JitTw~y! zr(qMRK|Rh1gR=_kL?uRXQA@%~B(|Dvkjf79h|JRL#PD}O5x^kS&Fxpn-o;&ZIfL)s zK4=}bphjS*$L{b|C*L>DP1s2U@wQ3AcG=Z9%S~sd0C@olBcN1>&^7HO1TiWYA=E-6 zOa>YnFbeVFxEOM%bE2WUUhui+%4dfk7S5hwOD@5qC%*~$ulXN7H{$&Qx29Ka1i$i> zjx=ASLNPH$+8`gsR^bQtfb;p~#ST`DFDJI$Y%(pOy+eEesLCsNdT{h*aO6!cU9ldw zJ1fN>N$%@2ks6Cy5pVii{~YKQx|$i27$V1at%m;ux7Tm{V2XD21JLMluXk! zdvR6sr6Rt*!R-A*&YZ1Xa`n}}>qUJd`5XzBy=PxUF|%wfQz5K2=l?LuS1bada+10X z!N|Qi$$_C+qQ+Kk*q-~5(r?M2&ehbG|w5RP97|)GEWazV#?Bh0tcFifyzge0!QhDfZIpRpihsOO-l~+pz-Kxrz zgNu{$YNN&0h8|x0+;j$#0OlwPWipG~N(@?hQ%b6rEZsfderHth2CMZv#y!m`_BwGc zJE0}PA5@BLB|F(!xeV*NoBd`S-`4phnD-4&>W$+trffk%7rR&^2wLtqQ?0*B6+*l} z4e~o`eSm)+3BZUaHHk(~I8sEnLT^sxF!d6y?&cXm^BkZovytXSHl_PupO_Ky5K9~@ zh~-f9p1RfWDOp~oT37KvNNkqmAV$b(qo3HTR%Q*6S7&$VZ~AsscD4G(2$URpci5J6 zvDQi^B@I=}IB@o>eyi5!R-FrCeW_^1miuruO~pV$rs7RP{r<3|)hT0>gx~4i!|$}G zb#nD4JBbnnm^;(eR%MgVAX|-b5bDMrD@8Y^#+gonO^ZZUM;XH2Hs9Se z?It8I{Yscn^K-Ua1Hs325Q#_R%xX6?nTE80c$`by)6Zu+@B={T!)RdYS!NumVRDR~zEwLyna&bNE69Ws`QaJ}Zi(Ogh7$BXlnaL6 z_11dcG^JQ*4by4FSQcbP^wkqUc4@tRh#iys!uYBj8iU=V#|?{Ce_36V8!bzLT-y#v zv8Wn ze}+MX!_695ek^XqQL*JojrHxk1vN(H^dkkki#JIZI?@rO=hY)uksaF9{GRNGTTR+ZTo>Vqo+dd(>q)u$u&i(P0NS73e^C;@+6_J698aUT$ zk7zF(9;tqWbS>wEKSm_>C2paYn{P(AyAezswj^E3LdbFh!k*Kh$dMvW>(UoIWWE>L z;|-emrZODd)85UVc6yjiwkO`Ug~%+xU}|5lUOitvr=bwfaNO3)Q@Z z7|@f_hvV^@7Qw(CdrPs>4Bx)EE_|uwAU`eT)c!Eo&28Kn>Rt9u4QuIXwG4G9kDJzW zBQtLLf+oM?@b>H7U&^~(vE$@c_lV=ADV38Iva+W_lvFTk!dsL{S+VBeC?nzvXL+dq(9lZyY5Fo(*u|504RW@6 z+<H*;Hf09!F+_k2HAworjNWtCv5| zraARIUYcI?PeoyA$PJhE*1goy7F+h&Z|REOi8eL%cGaEZWWj=0`@ECteCV7#8nX^~ zd(VV$YunGm9mWhtR*JUHA(M;86&+PJCz_g8gRd>Ay*1fgG}{x(BZKj`UFuRss$y?S z;(Yd{dQMby`?lxUEm0hkF8RAYyCG zrittZFK+tMtwoKFXvt+o!ZvxHZR&5mdGVWk4rauiJ%n-G_r8pl#tNEF|Gv@T!9W>p zNV_?d&zpJx+ZU;8tSRG|TUL*WT|(8I=^?WmQ^a8&&1^j-v|a zI=1x6UB%LIi*n~ts0TpH(qCJ9x264HZ$UC_8|)H@c-^|+LLLrwZm|HSu2mZvvX4*4 zeYdL-KF{OL>|NS=R>NTSOg{Y8AuKAgEZyceH)>xQv?5#EjvSM>^uDl?auRQReD~WY=JvDs?v}z@4KfHCa?j!7FmAV`{Ys!9+ z>4#uQ`r38{J~G|y`Ip_ZmFU8zuK-a3Y6HeOfZ`Pp6a?rN{LfAWbuvWiIXBe(1#AhW zUQc%pLwQ5J)?3VcQJ2*RA6IAU799)oZ||H#124tC(;L2`3Jk4m;BNQXY5iim-m(Tw z?G$r&CQM*@|GwhbazX^nZH(Ed7Qoz;Dj3On%qRCM)Sk~R)IVYG-S0~@dlsKiXjGOb zVt|Uzc?uL7wvw#UH$>SDFE9A*Ry(<$-Ic%fyV$pYYVP@uEQj$`n~4oDb`Cf-dcKyb ztC90wUcvE;k32W`znm+@C(j!VCVDbbR>wk{^0n1U7b#f?ArLn-PIdQ4wK>4PxFdk& zV4t}n>qE%>XV?cn=|$Vq<&|L1GVbEy-+`W&dvV{ySc}*Yxj6T*A=h@DHC7`$HhMqJcbIUZ`?g+yf>5mCn@;P4NP533zte%c%I^-ZR@f=5 zo~lX8Vm(%W!2EWS<4ygioJrG{d7Q6*$wfYV{j*I*>B=~4@mUN54s=W%Q#IKdgW8;MMR;MMB;?pn^f4qjSNQR;u}y*&o1ROTYr;zuAAhR z>Jhf-c*OFAa`@UUJ+f7bYXh!ti1ULs=Y+37yc%>dy4EKTA%ed%CROtu{LzG?pjvUqq`w%P&ybbyM zbjO09YXic2E&`CS88vx|HBneGl|0w*<$HAd!|r%=GraF%8*LGn7Avs6G@756n)fZp zAXjvKHZJ(b;^n@yPOxm_ zv%bpq{zHpKiVgC0&*eedK_WlqGdmnQDU$pydVfI^{_A7dyFO!y%A@@ z)gAi6wU@Or4rlY>mjGO6oneR8c&r0PDa(kR#h|qw-WiM7!(U#>$L`#osf~AkqL*HF z)=NBjeH3(Kyic{^XyAy;zF~1$DVlIeneRU0sy3*9i$!BBlZYK%r8cgqZLmg%*AY!H zvhl)ySxna|-jp@k&+}=Nh1QJ~3KAun-aX1Y+DI+c_Ukvl#FFEyOKMcqxGUG>+VN-E zs1tY3s||PODN<^c?y8vu2$j#JN(`N=zjfezyw_T3wU&%DPe1I(rc=>V&taEFl$L$` ziy1pZ!$S_{%&LcMQkPI@h>uFDT{FQj5KcK>fvO?kAA&-l2_a$nJ8Q;u#W$L2p=p3CiIVWT`wHh18m&~6=hiuH$qI1&Iqq7}tp;^UY9PIu? z3Xj~Wi{{I_HFETmzBj31^v@n*xXB1^q^PNTO#f!#%A0g29k*y8O?cuJMvdEl$k_Pb zXE0vrsu&VjXvftIF4hY-Zk@DbP?pTx;8gBSEJXW$XwZ4M8j`>l_ztxYM+R5x0yMmi zCxnfg?ywiV+Zaq_8O|l{4VD!v3@`dB^MTeLNy5=zt&UCeWNI6YA8M;n6JCMkAR~fWBZ2Mn@%OrW6DsvW~bCtM!JP*+skTgb)}v*V*_&iOKcj(I_g2auCzaI zxggj;7OHuG7fyE{sk_gkEpm^~ukn}Ikw{`Fdph6T#v{7GGzMtjajM3k$;dguwM|EF znODn#RbY|XT&(_0g&xV&JPcy6l96?8e&pUYu#%i~)C*hCy}a4*(ytN_b4r!MJils0 z`%+J6?zy$6qPscoj?S%=Ug^@>*sZh*>q{)T7k=d?@#sjaz04$ zjWQ#RK#!>lPfv<`?jbXmea|auvz9=#f@cYACOXcixWR46$oz9Iqix&6)yp5l)&iUv z)*eOv!Myly%1})gl6TZpbBnuOv=etcwhsF?IAz|VCgF(7?!q(d&R5UF4ogU4BiGZq z`Dn=vgY2fpOGWBT-rIVZiI&cKh0ADe<}ug!5}b>4?xgSfv^~Ddj%2R(p2Q>#mp$GL zGTPVaGZ3W4w&5W9@!H1ROU3DYCf%0AY))G^-MXPV-C|8^%qx-3o-%-Ob!kr3uWW^ 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%0AQCjmNuZC;)Yz_GUa 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 z�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 }ctgbzc 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 literal 97620 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

`z_j=wQrI z5EWLhq7s+Scr>hbz;9Nnv(xXdhXO6!hwO=-mLmj)Sul01qnlN@4#zkV|7u!~&uJJE zh)RdYsK4Q<<_8I>f^Zos83A3nn6FyW)7w1$?wRLs-dL#=p#Hfgi=>-}-nKI3c?Z>M zQpT?LApkhpxQ>iyHxZPCCiXzK>Z`;$cntA0Vt`q97s^wemog*e1Xtr}ct4(Loia6S z(pQ(W%Z^!aG4M#2$+5NqO0E^?9i)CSW*S5j)6j23lTs)@50v=BehRt%99SG0S8w`h z@zx&xD_U-55)>loy0&sv` zZ_jSv;3;&ay6jW$Xkw1JlR6V(YLf3f$319b+K?xjE5?Kf5n!GsC94@5{oCk1*n~fs z(FO7f$pn=3pfXE-VK=|8f<_s!?D;pJXefSB+#J0#%sPD$^LXS$!8jWcPGVeAEI~mr zjN{g?(-`=ZBJ??N+WKz=i$g3Yp+@JV`-pzTwOg`S*oXlVbRq{u{h}zRFsr4V)+OLLeJxH$~AB9VaD%g zpRa^o?;mwfERiZ-x0rW(lTq@RtOM+n;tRZCwG_U+?G2;+XlFOe2q7DW zpIQe&EW(J^e8P~Gttt{FFk?wE-dzbfBx0kH1un}wlC5rTU2yvpl^LktBLs>Ote4o8gyi5t$9k+CMWQ*_m zE>sm8k^wFOvlPWt@%kZwbkuPA`r!e?iqJ3O{@SI)Hf+z3w1&5xL3gWlkIi;DY2SRD zLLF93@oCzBkB#l*U@V=>$|PcVxDcp4OLkb-l9Y)qS;)jzu&Ag@K(&;%25U2CV2e!< zu(OiZ?a${F=8exD4F&`y$R{T&iFs(kyYPcFZ)sQIy;zYJsH->k1SNq{S64n*K{Phc zy&!%7Bp*`|LbfMc%OgR2C!Akdy0A6-!A_DjRd2F7PlI@5`Dr00%3*T<7Y_ z6w`73<@bu4l>nHg6*g*fhpj(O8aN0DoK$00xM7^atUl*aM_}-hW}?ab1$ezGrh&+H z*88#v!WmAX2+Q}aS*iwJsxtq>yVERw4}mOYF-Nxu_*)MGD8pHBx&8M$qq`Daq%`G$YYsdSW*aHA(F?P755>tYXT;jYwF(AxvY3v_s9CJL3TAVJY1CTbIdL%D67`@ zO;prpu)Amq!}5n~ij+Jy+-iF6x#z)LWe|TPgfv?6*&gHQgT^j&rcG-$L9)5%U`qIf0MEi*Q9Yxq_GWj*P*Zt8~$#6NIaUM{o znfICblq=WDbyh~4h!YXl-oJfX`RPeDaVC$KQ?Gv|t3dCwrKP0syK@nX70k-Op6Cgj z2)~txstyh6FQrYSpSn%N3u)RVfe{@&%T4rOM8)v0p>DvZ`;8)INl6iHbQrN7t9 zBdM@+3WGkIL0V(LP7vp1pmG0sghbgs1?-FWx(_uqKU{MB0$BQFhP5-Ee1XsB`$N4RpLJ|OlJ+?7c)VftQFcN ze)-EhwN~3=tpsE3uPq}%DIFHtDJ|NpoBw7-hmIkE z<)IB^0%g_$D5r?zNGE{5W-9L;hWNnrow&8S7jEPv+`kWM9UtwV=#k`T( zPj~C~c`o+LI*C-L3kZn`xt&>aLQ0kRGn89yJgpICqWv5GbV0XL8fL6%BT2<1B3;Nn zjiaVx{wm^k8ZQ8JIiUp>drx5+5pjDL^dQ-72gAuRr^3zkPglz2BsQ?9weyKF$WVa5 z$7`tWK;QZr%@}i9o;4`YbzigUCn?}{d+r-}Z4V>!c!^<2ewmJ3%znAL0e5@#9!@#B2W2&0oR+ODt0bKS3zoyZSP6adK*B@5jrZm0&{i*6+kygnvPUNfUx`1Q-=G74&kKg)k!LS zy?q{@(GNZ8OK}prtOlRmHKVLSskFoMrfa9m^MT08lK4RKwRlqIJ(}r;ZDx$M3mR*8 zXC%+tXP|UD7HJ}uVTTPe-Eg9+s*Zx?6OfNG!5#cY|7`f`6iFgsFQ-ysobA+1Ps64) zNE#qjzwjsUw~S0#VV{NDV5aD% z^irLe*-p9a#C~wuwVZRqs3vA5jBLJ3(4=ha>D6{W;#wEvemwoNrS6UNpSrTrFw+=1 zz`anD#YInzWB>pRe;<$sekW&s4k~gS1yN0sr4rReiVq@U%$Z^Pmwg7SJf>7v8UN`p z0H&0 z43&aJTeaF<{G*_Zg!@#Dij4+(MPB51n6S=gFSDe1Tbb4mU)4hkj&3)=;&!wUHj9ls z;JeLIhXb`JP2?h-bWAGILKBGtDVK{)_j$omO?ndaSW@VPG`go*S(LJs9Yt*ukrTTC zyHles>FuP7{I~0tlFo1*GoCtX&vmi8jVd>X%FS{|y}}+H566GV56d@c&zd@p^OCK> zp4PQwNzJe5TyeYpB|EuQZC<0x5vbgQV;Z`m5B}9S8ebLr$D8lVDiUN}k9#?%hbQK+ z-cFw44YHdf-xnD&fPI{EMOjVl`1*RMP5OGtk_AB+{QA3AXByV<=yXOs%<()IR#691 zgDpVmC-NWjk^~_e>5v?V{I!J7=A3<2q!YM*riK$>YIq82kj&CSI{O-)buQlm;o{1} zjb|9DHxKTnYgG&xEoc6)ee?&?uIOlMYHnoCEuY(Ibivuz|E1YagI0nvS38oRa=*{yazTl? zRz|#pdV2b5iKL<499UDPNg=Ct-_OK^+%~>{n4zfSF1iMN)nD3ZZ%(?e+SNnfC5%f- zsY#|RFX3wcR;>KHnUzWA4?+KC;*_^YZr#sxUx&8Bb#`nb=@5%G%#Sxg_Z2KU$$G1d zCF#ToLWeqFHxI+JrWaSHRqDuV!tdEd@<1}rA2N7+&R!gG=9g&w&|9hzw)5X=<3D}% zeF^$ct|wu>6^xC(!dTRfVOmPoTtSv4iESUOKya$37C46I*jvHsF)N%DSz|7^)(H;e zvj(8ohr2&nefG(aPS61Tt3uvk*z!<74_H+Bd*^@;^EU+;01}tRW`UzD+W(#^c|X3FoAyW+kA=4T0|=jX`6<&cM=)%F0NLNy?S`)40~M^P#GG zCkZhLg&UsKPf`LBIV{>7Oe$a#Gbkenpsu$iCQ^IvFEm+midv9o#6uvw_iHF7l}QVw z6e(0%g%V??U;V$QJ2-l`pJzUcMU(tKF0A zE8L0p?-@AjerBF=dsEf0hOR0Z>Vc>JZ{!^CCGF379E{QF>{C!&_r$J1^{`T7K}Y3R zwp!BV9Ke*lqpf#*%7@3eg-sy_HgUOb(+?F+O&!Ig{2FNpJ#LtOB3RKOzu9{dST9bN zJEu)cWv+%%+m~IEILCS$k?o&eUysqz)gesC=e)4Z2>Yu?EJ)xoWUCbn9H&qSr0Y5_ z$w1Ed%V%?sr=B=$!mo4{POn<(S@v#)M1VV4%-d z1x)AibZFhVDYs5uOzzCtzNI_TuG}en^P1vhI>|vjbo*8Fc6vbkxd2$=*iy}o z`fW?n$n;FR#$uiQG)zSk{*u}Za?pq@-ov2Flv8El4c(%J#VW6v-_OX`DE~ zOE7p`L~^LoZ1%>II^jeN0I>Hh$$G$?;3xCoZu><{O<#-ktUwip&Nzq_Fty0qobMH!A zpdj7#I?aUHY%6r9bO#w+Os*w$b4X_))QIV7-Ai6=;G=cr2(oDy`87OM&DA<)^GqoN z{l7~5|6tw#U)lVY?V4rapz1n_9|~vNxy~Z4N{-s2cX!Pw%*^KPYmk_7!_`@$|b;C&v8pIH>#zFBmC_e{DG8+bEerAO_=@E1z3jO)w zx-)qZ4MV6owWbN4V@!~pAV`RxW_~rWDW+bMR;D3(LYV_51 zPB56Di45?sM<_*BPEyOpo$|iv0*lq1^;@Q9dZJii)nJ zXTo7b5F06_mr(Q}ER&frzeAO^k)&#w34H(P;w0*vJ9NEq;OiBVxrK$pN=xsVJ&ldc z?jLNlnDc6+WHG>Hu917W?0vquu9&VC!aQ9> zG_3=*7q)a2@Srw5K*}8poZxhBs|xrhbGV2>nOx!Epc}CMIlSwq0d0Oqwzy6UNbBTa z^Vt=QFZN<$##eds^#QIUC}N>{@g|u>1^_DUV*C|L%}vei9c@#{GjW-GJ2?pIByi9j zCE!K>;6?I(ikQgtl=&DNDv%-K>>hP_{*Jb9Nj5Q1uHd0gO;^`C2P_7)0 zU9$8slGx8cX4A}M;O4FInwVXqB{DHi0^#7IJX<@jG9aDcgE8O1NQA5@?eNb zcA!U`SEW2Ow%MNbOK#A0}pN2iY;1- zxTX@95}KuQ3<&u-I$q^BvJL~0rix06qQ2sxbp!n|Q<{A_hGa1=SfMtl zsKi7O!?J<2;19wNIG%3P4}^y8N&#jT;#Wg1r1gw@cR+uC#qpj z8H)`E03L(Xk};^oD45(2^Fn4yK{@dew-q$yrnV3RnMDc`yV*kfV8X|e!JKw@Gw4X1 z$H`D+v=U-s(`CKFS`J-kHP9<|0(lFvJ)S@Ef- z$S;k{<7}FoX3h@YDxScuAYNO2b75)iyHFWw{ZzpM1(nOm#7u9GKId%iYap1PSE#_A zJK%cE4X4drFzlZmezM%xpe>6mwN?k-DvK{VW z&GljxQ(Jr~Wh#uDU^{{Mx@&S+HaDCXS=wg-^#yeDFeUd&j5m}td; zQZ2*Grq9pL)oQdocGSGSknU!O?_*Mgz{T*hJNL*Rx#FOGy1%Ta##3DQKEVt_UA8(T zBs%O+U8aeZNaq>JB%Fpo6cbWNlrmx=8$@mO4Sap6x2}uW+3kZG z*R2hzhv!P=uvi}Z))I!~qH`>JL${}|sG^t#e+mcMZ*!POs7UO^prTPApo0}M+UBRm zoPu4eH!O~)NBz_9e+=Q`B2tNgd?x!7Ft3`o)i*9XXH=F}*5Sl5F>0z*8T8{~Q59-v zh@e7eT1wtA{WT*`c6;#9PoZP&kg4eExk(E3XwZ;}xx&MnLIrpE*Q`W|+0(|dih z+Wx`ocbrgno%?c|7Zm!@@o{b^`RwD6r$6xgi0z9?|B}^3t*ZrL$fH4B{hP5ub0lg~ z9nq$p{VxQGS@F+Rkd!|H6;yhewcO-ppPg$B$23z47U%a&jMV4Yk!dA;$zk;rqz0e7 zRdw+WNB=Ue*=h?~XBGQ=DBE@AX^2t<}AVUDOG^dmg53tX!IMYBopcV&Vk^@|{I+VQaz)}dsG;1pu0?C-nI z-O*ED9HZZ8+qm^qyj6vwq_Q${G0>H%lBq|d5<-W9?raf~@yWRfSlHF7%7rD?4PCz) z&Nft7(|=fkc`tyb-2id^%3WNd8mk8RF=9dB|HW|y8* zaW2nJm}vL=Les3mfEUW9t@d7(Qwu#iLx;XoUp4IIJ@ubfVM4;d(ZW{98>*Nx0ost> z;lT@?u{p%K3DzPRgCMt^+5DsWkVKs-3Ik(!NVlcG5FYVdgjLt{5DfIq+E#}GdTQ!FslCNPK-D@tVF z^i~{ja&^A6n!58HHDe8Mll+$HTm#bH~dpl!J`5;+ciE{7V%(s2U)}pRP zRDD>FLvDX28qGRx_Ps$%=iC|Y&+4*iTT@*brJ$1L60wgyjPpA#xYnR9khLE@jU>ZH zbr#WfEq1mmozt-ML&`C78mV`(szrl(a04msQ2zA6G3P^SO#*8yZ-|sSgHQn__gsS> zxIB2<&nd?92q50m-cOG9NI`Bc-a>akY4J>GjCt9?wYfj`DlFdg-VHc9NM?;0cKXY; zxf;@*ceeKZ!j}a6vVscLC_rR<^mhSHMWyF)z2cl3OXT?;WyP+w4Ho*wdE&-WJFC>Xx4%*kyq##1l0M(xS z&U#|}*r5Jub9)dAkvoH&i1N8gD$=4J{fW)zJ{rY8i8k@m`|g&Sv4KbQG0_Qb2P5l+ z-yD|kSTc@Tgj?Tnhz=k47-ybd2_xfvhbF6+$C69R%KfqVa5{?_frLdxWtg8=AV2@F z-SpaBPOU6$SivDQ(f(0-p5a*`su%`EFq40gUzLqKMNAHVBgAIHffY*{#xV}$nd3c` z$V5N8FtJw8^eKq_m-s`9xbR3 zj1EHb286qGL~t?2%SghbP6*c>6&->Q{dI3UO-z35&WO-9xut?Mfsezy)`EndUS4Lk zcfZTQW|G>2tWdQ~+ZDms#Ec+mBek#y_y-X7KPckTMS8g8=~$_AcN@;P|>>Jyp3W!1to`?P~?i+GZ~%s(kl- z>|f`L!5?OK06=)h9j=E@4|8tX4|sVbS}3FHTv|>y9x`*tDr?0HrX2B@ zdYQ-?w~DIk~o?9 zT)X+>SQ1)WPco5TYu~WKajtn5|o3xM*iGJG}bTKaF_-!jL#_E5;mm)H4$ni2i z6=$dJ?5J8k9w7~6!lN^PZ#>7{Rb@SOwe4ZDzIsetr+xni@9X>r?|*KDMzynjGmo5{ zX+1FVDo*l#wu~F`{a_%xIwoKjMtH@b@!c=6RTZ*Fn{>%;WXt0W_fd*XtNH7%U5BMfRgn1=bP&lC*gKH$ttO`~&{^oFa+69puX}sVrrJGB)Q5 zr2+I@x_iPYb$|Me_I@->=cK!Y=;H5aqymTuC3*Y#EQ7^E3%LScRo%#Ro|jpJ<>4OY zxX1h0sGwt?tc2rzkpM%kao{qND>lLA1fHdge z7thge+5%_i^QP8w7yP*AnfdbDA?tbAf&~k!WqL2OW|SS6!Ni2&Qj_CPU_Il2=}CQH zy|Ft7mrLDL$iMtpG)5!J{IR-T8P=y06s*H%V^W418(v8Fs8(oY_k@DM(Qv0OD16dT);YoM9fgT*tqo#Wah%cS| zQ>!p*8Tw68&+t8vS>Yt8&$>axg1pakv6lg#^oQqA1~Gq6`w(%fphgsC$0(hRwEcXA zTixhy{q|kh0w95Azz%PWOMGL8ofP!bX&Ge_sh|5BW_fA#yMk{;csL;KN9boPmZ<2d z>_LiND6RE$WI^u6O-CtI@L=@(&=GFuQD+lcXovE$w_Wn3nVEtlT;2#-=qpr3e&w{5 z3z)dyZN?x|OK_5;#BX|PBNa-YrV@s_kOXW-MUmEzP$@H3D{0X&E?Q+p3#(4-Re1ZWnJcd*&y&RS?{Y6T zr(VL&CT0>CoXGg{H*G4--lrsHGD-N;txd# z<;&l<`|0}u*7nZ?YPPgDb@mfw{e4h~%U^Wr_}yO1SXk}L7YU1emPaFTXf#XXiK?c|*WnjRwSR ziLXS6ZA;F!exy%-suhz7%-YnWO^moV0DV>G?vm^_8-pP_8K2iRGdpniN$q3x!ig+L z<}t6Q$@ZtU19(y(k=`9&_YlJK^dH+Mi`;P%>kli&=(R@j1zLUYb0fCOrqiEn21U3U zwwkIe@sKDnokHB~i!?n#?Fuyi5_A}DkQ-X&BUfKDVkc3E^&qWSv;1jx!yG0sf_&UW zrbG$=OYHS0v|stWpB|elsb2j@{74_E8667ZYWWTeZQqdK?Du8226_GEw*()|{dQ)w z`XY(#H=oE3au-N1{ zG@~Z#^C|G=#j=z47tW#8UQk{cY~g-&glr(HWj$#Gfm6X(!QpNAapLaMszvo|)bHC# zL`-xh9QgD5dAXSBG3^{4-MVTvVdRfkOUt=#b;q)++&fU(lTKn@1})`glPBCX9n4J) zR#@2oR-fYioxnUjzHoE{w(w&g)hrv=8R?;yUi?QzY>vv0p4hB*uw;B}D!@Ep#>@-{DHMGV>oE8L^+r?Eox3*9!h-gv*kY_ToFJr?5=>@t zZ^ajbLlv4m3ZkD{D=PDuP;)h(u2h6OT9~3?`c}0~{Lhu(HtC#5yKf<)FD}1^k^Wa_ z{qw~?3duewg`5-9^Z$2yyZtFYCG#2HNOEZQs@$nVvRROE&&<9&J+wFN*2>|$D%G;Q zD!R#fKb_Dwrh{;?71q(o>FM!1W>o)gtMqO);=k5z9VR5M|Blqs|Bv#Lc0A)1^={*N z1i^$uz4wr6wyh%8H3a(je{SSI|E(8@d{zt)j3$fwe|t8Y3<$A?hKA;5%&^X0!L6k3 zHv!U#-#@J1Z%2c#zO}hN8%z{Tv-4F&i7t&At1*}wCE~-Ii(4;WVX0zwr~yrA5lN4a zZ(w2qJG~i)Ru9GUcHCL8eYQ+RMTHXlk36s1KX_V#3GEGFHV4!l$-238pjjw8zLM&u zL#EoCH1S~SPFZ_2u3fJDsa<(%nvKx-w`Wf>fm#oDlNxL-M|b;df|r)p&z1{Ta zz~cYp!o3tdB@mWT?Z}N$L?i}7ykX%OPIW&*B|M7E7TPHwQlS2SR%8pJZ?(rp=lLN(uDbX6K$EAi!lI`{x9h4xL9E0as6Obf!YF4RQ?f-oIt9os)F>}C@!lm zsx|;&0J>jW*ZjIQD+9uw$FN)#OaHNbvuM~`eiffQMKO5jYI;Uw{>K|0Tb;_>#I>*+ zJgA-PvJAe=K_K5_XY?ny+caGwFuEVad!;r4;;RVE_HVDKzj!YqaEzs%ZfId}`@&^| z(__(Ywwy$wbvUqJ{%`lCpKXcJqv0S59bky0*HqC9@>Mvr3gi0=cM$}v3WH0FM*&W> zC}!e`1Ff0>jmz8g!_#2Rl8@XO3GUMW>lgy<=-BBKC&$*jB|W}(i*VOm?Yhv-jyJD1 zqpQX3$=IYW(7r?+$2aG}rt~bwx8ni={J$y#a)1UKJ389>45weUn$fB?_Cz;3ySmV6 zGzYPm!&TclTB2O3M+gzNAiq3hZV6=?U)RM+L632edKwx))f!3o*oId&OSL}#)D~08 zD2~I)Z8dRX5pk(S7~GkcG!nqk$vLdhBcEtp^RZ=xWcbSP=c2<;dnj9@+^=)}l~+!~ za__tunggg0cd&7xZYx%hgDaY3kh*Lndegr*y#>J%FG%gbMe7NS6V5xv-UcT_NQ-D?vRLy4T)o1E^oEVr~6u1 zML5$vWf5}^$X;OYoDL@L9$UAxTB`kch&n9T_<48Y$1n1b7%?k9qLe}pf;!*Off&QzI_W}bC z1lr$_9dy!YHFu_dOf}Xu!G2e%e7Ed7d~fcMqoJViM{`O4<}$xjzD-V7C!9mYFJ}{< zh>D7!r;-#Wmp8X8)lMfWQ-w;LXHqm8Sc;5%NVbtLXe%rYKt|A(=kR)?FCCGSjZctdZgYL*G*KspoPr=am@L`INJpn}(g`AhRUW<`6fAnYrZ$5#&Z)%VXnF=OC7chkVW_72 zc}asY&OeEV{c@&?U)vaOII6+o{QIODhvnuy>sYI~NQjtZ`=KGSI2lUgB{^Z%$RBOC# zG?bF27Zw_Rko8gyxfN2lLXT~#mPhFJlbS_QJ11Z|eL(MFMU&YCWlo?~Z#PBArV0l` zF{fBO07`(NOI0C~C}p#G(?wqlNP2HWduJge^U&zk8rmg`$QD~QksSp=UOqUkK;2^f zeSWACYvlp7STtc1>&V}_ojHZ&$>n`saV&B@PN07kOHj;Gj3cEOr|!_sa%L@ThmCjD z7;rx9$OQ81_LK`$=gg`d(`b_!s58owcX1Ndr7}783=`=nC6Un)x06d-TU&?z$mUHj zmJZ30%(4W)b+nrx>jWXGo_j5Ftp`;j14>RT!$-oTH29KrOKip3i_{It4?P|%FCnE zfyK$9?=!4j%OtaV=mMbJA(bkrzkPj5Anbmz?|oY&`JQ})-NF*Vb%|3_!m*syRq;su zu~01!+ii2$PY@Fm6{wcE_MQX*MSmrQV!lF^I4>WDHtUSXW0Dd>^M-dv?bDY^y2l>G z?ae{N93lN=Gn6Sd$Wm02RnrdodjKa{li$UoBwXcnkWlO*z1!8c3$^FS%^Vcdb8wT| z4Sid0(@7#?Xd325%XYTcrGSU+WcWW803f7RT#%bTPR&^xktx(+xG&xt?t1tw%U0XE zc)wT!^xXvc9&D;M&(pp+oE(pS*Q=$`yc2M3+<-LOx~)UX@ryx=msQrME{aFcv&0WD z3`_P+Z8Qw+7i!OrAw$T;NkCUm(=gCX8qFmPaSRas1i_WVN2}6in?UZr2Ssqzmqbb8 z_ebUB0$CIh=PZs;!X~qpJfl*LzVNpnt z?BPxz>MB;f__Fil2dTnY8WGtC#LJ|V{og(}n%km;uw$z;)_>_ZXkTuB(`389Z z?7;{OK+L9dB{EYV8j6dvKDXh`q~9-+{X~(r3}XDBT`_n|7#w4zGdPxB_j)Z>M#P$J zw>$%$IHJXFOkObyr{%YW!UbD!CeM0H!7&n4e<~}_E%v=4pFJ_dHOz>4yo;yzlAsx* zXS=Oq)I>kl9puM5Y#oq%_0bhto#0;ymKtRIB-pLT)H(@v0RCRx7G<^GSRQ}-Dr{K5 zH_Q=luPOHaIN zZs(pn+?)yn?6%=9Du*l@*2U-rvu=b#M50>saHXYMVVsU?JDOT zaP4reY-wkeH}R?X3v9$%9-Walk`eD4`Vjwgsy;JVv8)u4Qi<(ar^i}B5oHLeROE7D z1-_Kp_xscX(~aF6{J1bUpZv#-*dv_{p^`a<46H)^#81?VI$=y6Hu{X5V4vAN`wpFE zoAX9hzS{?OgH|>SQLlH%)Q&$ruND))ZdsxWSN3ZgRsrSoHAG)D&yl7sL#}uuGc~Ju zU*{+8g)9y*qTl8>jTooel&s+mYR3c7cxhXww~RMG@?@hy^5z!9;l;ta!89H-I~j$f z+SPhe7x8?;2727+gE2@$Et5Wpt!1eTxPBJb`veLNUsjwP5MA==5* zG*nDiFti?q8XbvJ)vOg2;T7MBG5)i`Lze=2QU*mmQ>j?o{tnr5OiTu|nm(O(nuZA5yDy_p zpnZL%OsjsQ`53-#Jgn@_ z(_kJ|kABe;pUf=*%9_RA4tlFi-G{d(ty{45ph?g8Z{15Fac{0i0mrRjUN60g!wV+P zrIL9gy81<=;S$rSFUrL%I}!a|jK#sGK3nILt=M2bfzLIIT`oz?BPv#M#49X#-;!Y@ zbJSHV{cl0z3F5!V<;Y20bwNQvo2BlZW{>i=NEkO}GyU=JMf^yAlmn!nVB$zdpZtaHKm*a_laV#FJ4%d z3_67cLp3HW&{HH9m?hl=Fj1eQA>II^Zmmey7wi&!?X$W=v90aaHwI8BZyB&o=_XaK z&C5Xf-y13!Jo8-kLw?w*V_kl|F% z7noc&rT?94&}$BPbILo2*Pm3ODy0YsV~KlydST3sS23+Po0^BVBM6U@?JsI$JOO?$ zsqEoHqQedR&XAes@n&n#pYm?X*%>B6qo#^@m(WgsUpRnFayeI72>s z9bENa<>!DyJS}NJ;|EKn6>@2T@Se8DFv9%tEuw;+P1$na%>1%ipcv9>N&aT5nrdmy zxE%gEM$2c)*;%tLM#Qm6IaS57{c_aI_O`lNJv$G*@Kn<19d)^+;biD@Tc(u@7Z)Z1 zl4Sh^Gb+bv_T^v89aBb)c5YujxpW0Zl?*!2YlOj-72RqJ!bIti!G+a%C=ZWaERorw z8ru+=@}WB<^Sn_v1kLG5B?^<=9Ih%nKDvhd@^X^#PpZqnnT-+l9BfNC zE0_Lv=v;c54r#4~Br<|*Dv55+1syYbFL&-b>!$AFc-9ch@P(IE{#M2p4_=T0O=(`L zz569@M_<>&r`YiX5qfk0Nsc1&e|i+EUAe0(>8pmtWGi6~#A?e)ZVi7|hjb83oLMuk- zsRbn+(a{#^9B5qi`uox@b{4EJ{6ZO>k>ccr2?*VV)VQsWpwmaLZh`3KEUU_4xHld- zXv~=dmKR9KI!q-p-P7or#i9fw*498e$E-+EGiv%HoS3}zel15d)NL<6gyl5ELx$dN zwX($4#p`TcC%EpisDD2jI>(?zA}vi#UV$v?PDCtxWqTa!7}Ha8m81kbdn@U0qSGCEyc7yO57U8 zB{clJ;J7}WwScZ^Xea;9T-}KFjIGK3wZZCTZBTi1z9e;t3jMO~iFoJm2&mxsz<+_# zTd3U^4{z3VCq%H$>LAy!BuGUP#kE(ST~X!1SUF*wkw`KkNY6j(y6_XZxWYYXlYH5#7XJAt{+EX3-M;FJee|a zAGYbsaPI_o^i_KB%M2aAOY=C6rlzM!OvxQISuCjmxw@|$il+!Q^3rTvy)@H?CDkP= zO-(9w*yMQD#Whgjh4EPnxk`$r)}VZ@xxmZ%#Twip*)G!LhzYXf3I}EanvwB3GI9-V z1j^&5v4xC>mUR+;gI|IB_+SMBZrJpan~DSN>Q*K3*GGGUKYj~TD$i;kXz>Lk$cIu*1Jl6u_A;wG&3p{Orn5+fN*wB#SgP)-MpuX#SjbrP?;*`mp}w`i0Lh#48D#5`)Uk%H<<-)!gzUZDb_Ta3Q@K z?h1V&w%l4Ex}C~eS>{)Dj0<3*PYLFBUZ;|SrepL zx^RE|=n4}jE17*S7|~Rl+OV?w=~;Ysb>gTk@KnR`K;-PiFMPp7Ty7BQ+QBN=n5 ziL&U)a@w~@#@N(p8X+I0pi+mIh*caK@25O6Oy_cTF>uth zN~fGUxLPt#?b4Cg+jL{Zl)#SogCUW}kD7q!7SKIN$8s9&IbeTra8-d$L{02k1~zE^ zG-Gi&SFT=ed~8=nU58eI=n9NnEu)Rst-N-KjAX$waYXm|9Lhwhqum(vicS>Q|3KE) z+p3?ER#HWkGOui@o)$3Xj0wbZPuGPo^=ZH~&=}Q+7jB~oWwhTta;qQako&{197Oty zrflUvVZ^#i)d{;Rd(Z@;i@BVgB7T|el(95q2=iDlWjgt1O%K>SjSK__7h5jV^x;rw zC=zE&_RN%J1mnHI7uP|rcbf4Vssgy&uY+hOT~Prg8MrCfU`WDd{`UGnm(Qb9>xM*_ zwqVw=HB`LIxa2TwZY>|cf&+|HiPbYB2UKah?W{ZhV9|Lh!SF4B=Es@B*;9>yr0WHE zpHkWuYA#G4w8wjUNBaewtp!ml-6==j4 zw2#Q>>4{$*Gq^GzGR#1Ee{h8GKZ9YuhJ|5$Jd7N47}oDf13BlDc>mhbv!NB6W8+6j zP%NSG5kNY2m%nHNbEE059w~CV_P%B=zYI(kIp?EtG_YLts?)w9ffadOPZ*Sjv(&lvB%NPdy1o+%`n&BA$pHA90DR0`f zjWAW)<2Qg2+&CiSPQiRyx@W}7QMIL46PXF%jx(KvL%4BSNe1GJb2f!&5VJn#b|?;&eetKkeN@={3kqF3OI;?$baW zSUQ~>Y?e2VyXTPo6zY6HbyPOtXA6Z6#^byWl^SBwZ;=8^!v4w5{{H?Y#&oBvf~*No zpNmxb)A6kiChO&4%^!dLfw|KceiR@f5E*mRaz1XUzr}~RK%6_y_xQ&e>DJEdjS(q9 zH-m!(NQbN2@+Arwgd}my@Gp*HPa05D zYQ$R}NcO8)=t#YG89+`C#5!g!q*nbIgH`+M!&V*oPofh#0^-3h^enA1pxoDFkWlJW z{$Lv}BG7LA(N8*p9QjvBQ114`U`a9b0AhT$m>ic?JhjJK#alh1L4+9bNF^a|0z^^< z&S;?3ZT)DT*Mp=yPu>h(v$I{bS&f1GEZt6v$cPUY8w8Q900Fi#v9XZmCC3n+X6 za$yR)0_80(HEJ1Xjqo~X^wWg))WG2LWJwYTat=Lg9>t^ecCT=CE3_GD7HRrd~|e=#O^t8d|V$!Gr>xFp+oenm{9pX zyqJDV6YJ!^g+cxv?~%=xT^cexhw&y;jbGAB{o(~Li{>B)gof6zs0jwNx>A* zrWjMt)k~c-vW~i$A1K@V$~B=lpjSXR+oe8%e%{z2?*1m3$QlOd$5nZKJ~Mg$V!2qp%ANWtCIu_SYxnp^@h)6U zudkRe2>Xd$+lL2?RltsMS%#|gjrrTD(w<|-v1D|Y+AnBNs!!L?DN@ZTqNqH!e(heR z+f$zf(C!%}W#;PoTSBGkNr=|xGwP;SvwrD7WqF3}AEsA}8GSB%8=>5X=fV~#i)pzK z=w`r%cLMZn$d>)~nJ8XNpkgz15ZT1Sg9m)GesP6#*&wlbX$T>yqgwF&MZN!&*1AG) zYtQ*egba#(WJd!@hm1~huAXKc9F8L%xNfkFPvM9{zBFH2(p5w;4|xJ}TlMzxf_;jk zm(|b=rQu;s;k3)9cUyIJ^tztc5J$zT2tE@K_O8``w$*dt7|q$;oH}ZIC#r5eW8mA$ zAT$pzQP!#}=QR)3dTM|*TT&-iM2@99VfwW}S4T?mdrS57+%XG^{p*B!jPKQu=jq_i zh?PRe=y_V1#F*c&o}wKv4>(MnuT-UI@rdTC+F~|L?3~dLxR^iXRe^b`87!y6uTEV_ zB}UJ#^7C>qxHt`=B@jh@3cGvSLpjIBRG~DMgaFS@gkeU0@Ud(;hPX2uMz63f`I8&0 zRc>b}*B|I2UHgXGQc-GNbKR)9;_iNMeCMMl00 zqHLvs*5o*nC{wUde)MhpO!2HUt^@9|mA|l^FU6%1c;WZfD)HhI&$83m>xZuKt$(}-g|m2;agTmtlhm{@w}2~@PnN4>F5rUbWS666l-kEt@@R^?c3M; zmr|d#s+U#HeSBY{Y)h2ToxG74bi?S?ROLWayNcTBa@jq9jBF9}oK@G^54+`sjN2E1 zA#QuN5YGuEa=R3B7L0L5NaS~nda<2DH=7S9@Jr5f6b7RM!OA>A5gw9_U(CNhW9dqp z>RyDnFHxoHs8&$2jh^ALa(x2tjVe^IHhuo77W;L`9e^vDG{)u0d9Y2{B_Z(04L-FU z8vdXk?zd61#a4cE1wSkngRy)o)j;N17V!cqo4)W;xh7&Par!WJdm0m5_Fk-i{P|d> z{g}J6{Z!+2FhF|i-18>?hV5nQ`T_?3IKgpvWeq^W%)^p{L>!;|t)NQmjA{yd=;s8= z`3DVr4*OKgj}3ja)#jnJ51Sv{Xh00CzRvOxy1u!FNvg6bYy6-Fi{-;5tF+&rtmf1w zInghxmB#L#!mh5VZ7m63!Au%KJZ{{Fr_yeR+j-}={JPpv1k{;T722t|9KMuqGTlaT zD{*9&zJbAzh_64z!oevv{fIo^xUQ#Fil%4npQ}J6Uzw}m7D8HLMm@iY{lZ{^@20u zEKV_3T{rI-H)f^9d{#a53!3es@2Yrkae2==zepvjDI{60N+q!v^U0{lJb$QO!l^`E z)dx!iK8%?O^Ko+-ol|?;_}>l#mOYr&?3P$c+oh9OnB&;bzW|`IGXiExnCB~0H1lEb_`@@;0B*=BGx=;_Q*s(ViL~X%I&njEXmnrpT0gb z?RNw!0M(}7zUK2VAje1$Hfnekx4o6RdJ_hFw|K>Ms5yG+(@PxBFwK~pK>0Uo=uITWwrLwp{QS9JC6q&S{fOWH zvJ+gwF3hHoVU|%SKc<~yM8|~>-p%5UsqbiM7D9?-ERSL;sc?m_cc{F`yj1pcl?h_T zgb6i**iiu{EA7xy34ZpPu}mg;*FqOhFN}uy>6QZ=4rd; zsiNjJ>~oLeUh?k_NaU_=o7l`0uVZ|L90{QLSVIC{V3f#&rWQ!iZh)M znyzy_ipynnb?MsnOkZBi!C&TD(-%p1e*cb*EM{CA7IQDSmJ3g2?{$mq_1?5O^=TaT z=!N4bUcS?$`OCmgOr!5d(n3Ke$E)ksaqPX|AzYcGv_0}ItJI;VIpsyvdMng|9l-#QZJ_YAETQ7Up_61|9zr$1XotyXSx zI!(;X-44HI9h~gXaV) z>rE?}Slwt0JRp~;e}%{^mR4H5^Et#Q${$~U^z^|nNrC!IORqR{tqv1KQCPhsm#{Yz zAr^x7l(2tDz3a+Z%g;yF!|3Yu9riTHv@v?-8oQ>JC^hM<#^RF$igp^eR%CSAc#}hZ zFrpjnY}-h5m#V~-9-vksfA{2J0i!=+T-MY1rkw{aK*4xyd(CC5rL3N=K}pp{00|T4 zk$XaE&5U&uc64#y$$SLkq$1ZNNh;A#mUnk_6|tt?&u{7N2~(v z4Qg{ST`N9)&$nM?RhwyyZ>EANLUw005>bK%nb@*(J z${)GbzmSm5J1!tjjAb^ll+|J-q-~EY=`%<@O3Kv z(&{AgL`OV}@1~G!Lu+>AF!ZnxG%69iMC30&I{b;yOY-jE9vAnHE-1$+{`ge&#L@jA zxN@J8`J=Ac;7xcmGN6xAp~1rD17@_~7Pqf{)fc)_&oKj?R1S+XP)Z@CA$R(@7v!f1 zAB}Znd%+&T>6lg`ja`Fd z3;s-j_Ez~>)gN=mixSR*TRBstxz+$hn z%g#_NMmY&TsK444Unfe8SSYUlWJ0V5imT`aX)Y$%t~fDYqox2g>8u1^1VR) zmx4P8^_;UgF~cFP&4`w!^LkN237c+mLEko3^c~EA(3mgcA+Z*hhL3GfSs|f@kjJ}H zSEDbilM5$T;xRnS&2Ow$+;x?0D?+KcU9>+~s~l#~c*)p-GkAGLp)L}GX)UnYlLU#O zi`1XQZ0)s>+_<{*a0zQG*%>%)<)7f--&NnG=VXn)EK7yiN7pi@TLrM{)9pcpKjj{7 zoQ>|Cs;tUbKW!vOnD7a7@Yc_9-+a4XQk%O{FD3t+d7dsW`1UGhE51|v_x12gCmWyk zZFm*6z&D>F=cQ6txS%BKv;1n8+Wu@S6g3k{tXlNS*@N3J4#(?J? zfa;cq)9h`r&TD5%H1sAu4j`XPXkb15v(sL=uHndYt~tx{3*t)W>m#W79J+h>oT=I)R7 z(Fw;8eGX44fFr;Y?;Q#!__8Clm+a%?8KTR}^(!$5IkRtI-gjs%w$E2ZF|D^Lu;^;Lw`E z7r=yh-SW%g^);j|ZVFC2D_N?C~qeH;Vf3eJs`AE)hK*&SjbQxEks=YAbHj>cRqQ} zTylBc!$hmOiHr?Cu36>el)|Yq84Ul<%|Ga@;y}H&ErvEltx(e5(UN*9QE}TDxQR%XVjASc0`+?{rizC~ z(tR(Ks-O2n71Imv&N6cf^m~lnANQxJMNxCk7i`&~H<7WJ$yej8+%6`d6w)-I;YqQA zd$r@=C~kot19%PXSq6CQcj5MCaCrl^rCC4BsZdT1L`!PM2so<5Xp7w&xX zkt12iSdtjylKZU6nd^R&hSGVc-+RY?fhILHS+r`KqY9aHi=sgwdd1U;Yik!)sEWpH z#t-I{rfK7=Ul_Kbyc~P~@DQk$YmQ`G9LiGSEJr)weZ+0!rcU|E-ShegmQTL4R3cMs z3Rgd2su$hW9O=kiqDdMIzU1nB&f4y?K-u2lqrJiSKU_mj8}PUk7| z;gxTtbZjU+Kr&>G3S*!C8)0WbsTI2Si zL0~N^H>D$6NyYr>F}VS9jvK;CJDN;1lCF|+0UzBX}ed8qb~pOjvRS! z5XOZ2l2oIDfyLQvu7QTFKzf&n`JscIZx5(7eZ}lNnN8{LuY*nGLy^U@u1A0+1kORx)tSBuflQE5|U`bP3JK4u^KgOuC5_8Sa~*bsC|&QiE0e( zc_a4lPdCTfoNTI5XW6%XjX4>GnLOM)j`d|aMxXZ4 zIF^hmy@Dyl#HM$(xaMb_cKK$3aP(=ZX&$Gao+d~b{CRUIUD(w?GR}si@HTdrqZ%IM%=Bk9@V&QBoz{B;;w?wQV+ zipN8}%)Quj1TN1_hPtC24Dj|eZfNp;;hTOj+#nOaEa!vIG6Osx-@&9IX&TKRkXzeQ3O2|jWUw{ ztxM37l^(Q=^_#7a0^*sOg>BlfwsVa2vJ^V-K=XNrMwDGr7qQUB!()0*$3sFpx?0Qe znj0P;G3LA!uJ}&|oS1tGwn(YHY-)UhNXk zu!6^pU2R_JTyD{XasiXu65KoCQDS%bgp~Htd$-<)lye7WZ4 z8Zn=C*sPN1mFD6c?{*q(^BDA!TTE)L3?e79ZkAe&Jl-pYgA?2oCdZ=j_^Y1>h12)z zE^oR?$i4xW$S)#7wEjUTC`1IKTGd?jJv(+j7<8Nnbip<|Xo@!4SY~>HB6_l%n;J>Y zPKZhfyKrzZFha*WH9ty5R%Z z7UT=;8#+5*zdq5QFc)&M&8`7APs$XYZ|96a6=JL}V!m@s9XM;oKf|8jPsg0zZE09{ z1v{wvd4U26pupdIy-wlG9_RAD!<(odDP1i6>Z7MS#S)jf;k)vx8gj-@N?x3*aU3B+ zM?;}xA%bd{C-5gkOAN>%5>@draUZr=ll3gaJB}C<>-mc%B8ucHi|Q;;TJfjU*NdIX%ixC+Y0axt;f7yvB@g z`S3M{_`WF*->&M&g0T@mm?bTqkPsPY)Pmj@cZmHbqQ$Vu2%Y1Ho>=N=>86y-D@6P) z2ni%&^ly_H9u?Kl(VUywp@JFnyihL+IppNoP>^HN!s})$u1`WV(k2#ggv56poSqIlXYOlSapGXlIP`%2Dy8kXy?uqG9%F;`N?V z9nXfLR(LuZwq#~~N_=JC8KY_2Q*NS`$c-QAm5LE@IUNcWv;Xd#_s3NN4bH%N zdi$uy;kE-%Ok=mrYG1P-WwFMfcATDHXZWx~&vlH$4Qq zrb(BMi3bwl^AL7Oe*aUAKC432n(Ejj-QLg;Z52m09FJ-rlBa1nK}*ErM$k7LlZO;P zIuD-5d3<4GVQoNs{rwD>yB)tBlGXod?{&qM#$@NJ^)dSEE3i0uDjF7+-)g8t?P@NO z^(18tu{nF1-G5M2x8&GTJvrHtrL>L>TWD8Du}P zmNb(AOxlB7`z(k{&JMo-(H{S#G_T8}fbJUQ1i=ZVqc)n z(@BkUHI5%)4SDq*B%4)U6m7B$6#**VK z%*>*1Tn3kiTXbM5Whomw0aQyTy)1*0{FY6T*Zs$NT)EfR)5#kh=N5JQ^*jHhr$obb z<~gWVF1`lWo$L^q#nr>bi%Tw&17+dW_khRby1Duq>!QYp{=t@u&e2<5ukm9RX(eeQXaksN?2Y%Lj+8@qGB>ie=mtoa<`m z3-sKco?hy4)?UCZysW%j4_n819tbdBo%seIpLSBYc#qqyPurp##sC?+0j~8Z(R^z< z6L&YZ6ZXq>�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 U`RIDES4+3QeU;qFB From 60616904abc1f3b6b9af3da867cc1a0b10ba055a Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 19 Feb 2015 22:47:57 +0100 Subject: [PATCH 012/107] Mentioned Backelite in new files --- .../objectivec/violations/fauxpas/FauxPasProfile.java | 6 +----- .../violations/fauxpas/FauxPasProfileImporter.java | 6 +----- .../objectivec/violations/fauxpas/FauxPasReportParser.java | 6 +----- .../objectivec/violations/fauxpas/FauxPasRuleParser.java | 6 +----- .../violations/fauxpas/FauxPasRuleRepository.java | 6 +----- .../objectivec/violations/fauxpas/FauxPasSensor.java | 6 +----- 6 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java index 4bd070b6..d9749ba6 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java @@ -1,7 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology - * dev@sonar.codehaus.org + * Copyright (C) 2012-2015 OCTO Technology, Backelite * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,9 +29,6 @@ import java.io.InputStreamReader; import java.io.Reader; -/** - * Created by gillesgrousset on 12/02/15. - */ public class FauxPasProfile extends ProfileDefinition { public static final String PROFILE_PATH = "/org/sonar/plugins/fauxpas/profile-fauxpas.xml"; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java index bdb1cc17..9780190d 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java @@ -1,7 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology - * dev@sonar.codehaus.org + * Copyright (C) 2012-2015 OCTO Technology, Backelite * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,9 +28,6 @@ import java.io.Reader; -/** - * Created by gillesgrousset on 12/02/15. - */ public class FauxPasProfileImporter extends ProfileImporter { private static final String UNABLE_TO_LOAD_DEFAULT_PROFILE = "Unable to load default FauxPas profile"; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java index f2c25663..eb984b08 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -1,7 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology - * dev@sonar.codehaus.org + * Copyright (C) 2012-2015 OCTO Technology, Backelite * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -40,9 +39,6 @@ import java.util.ArrayList; import java.util.Collection; -/** - * Created by gillesgrousset on 12/02/15. - */ public class FauxPasReportParser { private final Project project; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java index a86016f3..a3489a4a 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java @@ -1,7 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology - * dev@sonar.codehaus.org + * Copyright (C) 2012-2015 OCTO Technology, Backelite * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -35,9 +34,6 @@ import java.util.ArrayList; import java.util.List; -/** - * Created by gillesgrousset on 12/02/15. - */ public class FauxPasRuleParser implements ServerComponent { private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasRuleParser.class); diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java index 78a3220b..ebd6d9a5 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java @@ -1,7 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology - * dev@sonar.codehaus.org + * Copyright (C) 2012-2015 OCTO Technology, Backelite * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -31,9 +30,6 @@ import java.io.InputStreamReader; import java.util.List; -/** - * Created by gillesgrousset on 12/02/15. - */ public class FauxPasRuleRepository extends RuleRepository { public static final String REPOSITORY_KEY = "FauxPas"; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java index 05883ae3..ea6d80bb 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java @@ -1,7 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology - * dev@sonar.codehaus.org +* Copyright (C) 2012-2015 OCTO Technology, Backelite * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -33,9 +32,6 @@ import java.io.File; import java.util.Collection; -/** - * Created by gillesgrousset on 12/02/15. - */ public class FauxPasSensor implements Sensor { public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX From 3300a5c1e5f991e02d5b9604efe4efe0e1dcab60 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 19 Feb 2015 23:38:03 +0100 Subject: [PATCH 013/107] Tried to restore broken files --- .../objectivec/ObjectiveCAstScanner.java | 25 ---------- .../objectivec/api/ObjectiveCGrammar.java | 23 ---------- .../objectivec/api/ObjectiveCTokenType.java | 9 ---- .../objectivec/lexer/ObjectiveCLexer.java | 38 +-------------- .../parser/ObjectiveCGrammarImpl.java | 21 +-------- .../objectivec/ObjectiveCAstScannerTest.java | 25 ---------- .../objectivec/lexer/ObjectiveCLexerTest.java | 17 ------- .../SendMessageExpressionTest.java | 46 ------------------- 8 files changed, 2 insertions(+), 202 deletions(-) delete mode 100644 src/test/java/org/sonar/objectivec/parser/expressions/SendMessageExpressionTest.java diff --git a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java b/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java index d78e8e6f..ae3b590e 100644 --- a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java +++ b/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java @@ -38,16 +38,6 @@ import org.sonar.squidbridge.metrics.LinesVisitor; import com.sonar.sslr.impl.Parser; -<<<<<<< HEAD -======= -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; -import com.sonar.sslr.squid.metrics.CounterVisitor; ->>>>>>> FETCH_HEAD public class ObjectiveCAstScanner { @@ -101,7 +91,6 @@ public String getContents(String comment) { /* Files */ builder.setFilesMetric(ObjectiveCMetric.FILES); -<<<<<<< HEAD /* Metrics */ builder.withSquidAstVisitor(new LinesVisitor(ObjectiveCMetric.LINES)); builder.withSquidAstVisitor(new LinesOfCodeVisitor(ObjectiveCMetric.LINES_OF_CODE)); @@ -109,20 +98,6 @@ public String getContents(String comment) { .withNoSonar(true) .withIgnoreHeaderComment(conf.getIgnoreHeaderComments()) .build()); -======= - /* 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(CounterVisitor. builder() - .setMetricDef(ObjectiveCMetric.STATEMENTS) - .subscribeTo(parser.getGrammar().statement) - .build()); ->>>>>>> FETCH_HEAD return builder.build(); } diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java index bb4e813c..f582eea1 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java +++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java @@ -24,7 +24,6 @@ public class ObjectiveCGrammar extends Grammar { -<<<<<<< HEAD public Rule identifierName; // A.1 Lexical @@ -33,28 +32,6 @@ public class ObjectiveCGrammar extends Grammar { public Rule nullLiteral; public Rule booleanLiteral; public Rule stringLiteral; -======= - public Rule identifierName; - - // A.1 Lexical - public Rule literal; - public Rule nullLiteral; - public Rule booleanLiteral; - public Rule stringLiteral; - - public Rule messageReceiver; - public Rule messageSent; - - // Expressions - public Rule sendMessageExpression; - - // Statements - public Rule statement; - - public Rule sourceElements; - public Rule sourceElement; - public Rule program; ->>>>>>> FETCH_HEAD public Rule program; diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java index 960ecea7..553b7129 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java +++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java @@ -24,16 +24,7 @@ public enum ObjectiveCTokenType implements TokenType { -<<<<<<< HEAD NUMERIC_LITERAL; -======= - STRING_LITERAL, - NUMERIC_LITERAL; - - public String getName() { - return name(); - } ->>>>>>> FETCH_HEAD public String getName() { return name(); diff --git a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java b/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java index 3a4589c3..c0f72700 100644 --- a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java +++ b/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java @@ -19,20 +19,14 @@ */ package org.sonar.objectivec.lexer; +import static com.sonar.sslr.api.GenericTokenType.LITERAL; import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.commentRegexp; import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.regexp; -import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.o2n; -import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.and; import org.sonar.objectivec.ObjectiveCConfiguration; -import org.sonar.objectivec.api.ObjectiveCKeyword; -import org.sonar.objectivec.api.ObjectiveCTokenType; -import org.sonar.objectivec.api.ObjectiveCPunctuator; import com.sonar.sslr.impl.Lexer; import com.sonar.sslr.impl.channel.BlackHoleChannel; -import com.sonar.sslr.impl.channel.PunctuatorChannel; -import com.sonar.sslr.impl.channel.IdentifierAndKeywordChannel; public class ObjectiveCLexer { @@ -47,17 +41,12 @@ public static Lexer create(ObjectiveCConfiguration conf) { return Lexer.builder() .withCharset(conf.getCharset()) -<<<<<<< HEAD .withFailIfNoChannelToConsumeOneCharacter(false) -======= - .withFailIfNoChannelToConsumeOneCharacter(true) ->>>>>>> FETCH_HEAD // Comments .withChannel(commentRegexp("//[^\\n\\r]*+")) .withChannel(commentRegexp("/\\*[\\s\\S]*?\\*/")) -<<<<<<< HEAD // All other tokens .withChannel(regexp(LITERAL, "[^\r\n\\s/]+")) @@ -65,30 +54,5 @@ public static Lexer create(ObjectiveCConfiguration conf) { .build(); } -======= - // string literals - .withChannel(regexp(ObjectiveCTokenType.STRING_LITERAL, "\"([^\"\\\\]*+(\\\\[\\s\\S])?+)*+\"")) - - // numeric literals - // integer/long - // decimal - .withChannel(regexp(ObjectiveCTokenType.NUMERIC_LITERAL, "[0-9]++[lL]?+")) - // hex - .withChannel(regexp(ObjectiveCTokenType.NUMERIC_LITERAL, "0[xX][0-9A-Fa-f]++[lL]?+")) - // float/double - // decimal - .withChannel(regexp(ObjectiveCTokenType.NUMERIC_LITERAL, "[0-9]++[fFdD]")) - - // identifiers and keywords - // identifiers starts with a non digit and underscore and continues with either one of these or with digits - // case sensitive = true - .withChannel(new IdentifierAndKeywordChannel(and("[a-zA-Z_]", o2n("\\w")), true, ObjectiveCKeyword.values())) - - // punctuators/operators - .withChannel(new PunctuatorChannel(ObjectiveCPunctuator.values())) - - // skip all whitespace chars - .withChannel(new BlackHoleChannel("[\\s]")) ->>>>>>> FETCH_HEAD } diff --git a/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java b/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java index 8fa51ab4..14bb779f 100644 --- a/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java +++ b/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java @@ -20,36 +20,17 @@ package org.sonar.objectivec.parser; import static com.sonar.sslr.api.GenericTokenType.EOF; -import static com.sonar.sslr.api.GenericTokenType.IDENTIFIER; +import static com.sonar.sslr.api.GenericTokenType.LITERAL; import static com.sonar.sslr.impl.matcher.GrammarFunctions.Standard.o2n; -import static com.sonar.sslr.impl.matcher.GrammarFunctions.Standard.or; -import static com.sonar.sslr.impl.matcher.GrammarFunctions.Standard.and; import org.sonar.objectivec.api.ObjectiveCGrammar; -import static org.sonar.objectivec.api.ObjectiveCPunctuator.LBRACKET; -import static org.sonar.objectivec.api.ObjectiveCPunctuator.RBRACKET; - -import static org.sonar.objectivec.api.ObjectiveCTokenType.STRING_LITERAL; -import static org.sonar.objectivec.api.ObjectiveCTokenType.NUMERIC_LITERAL; - public class ObjectiveCGrammarImpl extends ObjectiveCGrammar { public ObjectiveCGrammarImpl() { -<<<<<<< HEAD program.is(o2n(LITERAL), EOF); } -======= - messageReceiver.is(IDENTIFIER); - messageSent.is(IDENTIFIER); - - sendMessageExpression.is(and(LBRACKET, messageReceiver, messageSent, RBRACKET)); - - statement.is(or(sendMessageExpression)); - - program.is(statement); ->>>>>>> FETCH_HEAD } diff --git a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java b/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java index 6e2c63cc..ea40f1f6 100644 --- a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java +++ b/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java @@ -30,7 +30,6 @@ import org.sonar.squidbridge.api.SourceFile; public class ObjectiveCAstScannerTest { -<<<<<<< HEAD @Test public void lines() { @@ -52,28 +51,4 @@ public void comments() { assertThat(file.getNoSonarTagLines().size(), is(1)); } -======= - /* - @Test - public void lines() { - SourceFile file = ObjectiveCAstScanner.scanSingleFile(new File("src/test/resources/objcSample.h")); - assertThat(file.getInt(ObjectiveCMetric.LINES), is(18)); - } - - @Test - public void lines_of_code() { - SourceFile file = ObjectiveCAstScanner.scanSingleFile(new File("src/test/resources/objcSample.h")); - assertThat(file.getInt(ObjectiveCMetric.LINES_OF_CODE), is(5)); - } - - @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)); - } - */ ->>>>>>> FETCH_HEAD } diff --git a/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java b/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java index 78ce4b32..080c41cf 100644 --- a/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java +++ b/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java @@ -34,9 +34,6 @@ import com.sonar.sslr.api.Token; import com.sonar.sslr.impl.Lexer; -import org.sonar.objectivec.api.ObjectiveCTokenType; -import org.sonar.objectivec.api.ObjectiveCKeyword; - public class ObjectiveCLexerTest { private static Lexer lexer; @@ -65,7 +62,6 @@ public void lexEndOflineComment() { } @Test -<<<<<<< HEAD public void lexLineOfCode() { assertThat(lexer.lex("[self init];"), hasToken("[self", GenericTokenType.LITERAL)); } @@ -82,19 +78,6 @@ public void lexSampleFile() { List tokens = lexer.lex(new File("src/test/resources/objcSample.h")); assertThat(tokens.size(), equalTo(16)); assertThat(tokens, hasToken(GenericTokenType.EOF)); -======= - public void lexEmptyLine() { - List tokens = lexer.lex("\n"); - assertThat(tokens.size(), equalTo(1)); - assertThat(tokens, hasToken(GenericTokenType.EOF)); - } - - public void lexInclude() { - List tokens = lexer.lex("#include \"Test.h\""); - assertThat(tokens.size(), equalTo(2)); - assertThat(tokens, hasToken(ObjectiveCKeyword.HASH_INCLUDE)); - assertThat(tokens, hasToken(ObjectiveCTokenType.STRING_LITERAL)); ->>>>>>> FETCH_HEAD } } diff --git a/src/test/java/org/sonar/objectivec/parser/expressions/SendMessageExpressionTest.java b/src/test/java/org/sonar/objectivec/parser/expressions/SendMessageExpressionTest.java deleted file mode 100644 index 350da231..00000000 --- a/src/test/java/org/sonar/objectivec/parser/expressions/SendMessageExpressionTest.java +++ /dev/null @@ -1,46 +0,0 @@ - /* - * Sonar Objective-C Plugin - * Copyright (C) 2012 François Helg, Cyril Picat and 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.javascript.parser.grammar.expressions; - -import com.sonar.sslr.impl.Parser; -import org.junit.Before; -import org.junit.Test; -import org.sonar.objectivec.api.ObjectiveCGrammar; -import org.sonar.objectivec.parser.ObjectiveCParser; - -import static com.sonar.sslr.test.parser.ParserMatchers.parse; -import static org.junit.Assert.assertThat; - -public class SendMessageExpressionTest { - - Parser p = ObjectiveCParser.create(); - ObjectiveCGrammar g = p.getGrammar(); - - @Before - public void init() { - p.setRootRule(g.sendMessageExpression); - } - - @Test - public void ok() { - assertThat(p, parse("[receiver message]")); - } - -} From 1f34ef4c3f10f341cff79001518db788896be324 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 19 Feb 2015 23:49:59 +0100 Subject: [PATCH 014/107] Restored original headers --- .../objectivec/coverage/CoberturaSensor.old | 74 ------------------- .../violations/fauxpas/FauxPasProfile.java | 3 +- .../fauxpas/FauxPasProfileImporter.java | 3 +- .../fauxpas/FauxPasReportParser.java | 3 +- .../violations/fauxpas/FauxPasRuleParser.java | 3 +- .../fauxpas/FauxPasRuleRepository.java | 3 +- .../violations/fauxpas/FauxPasSensor.java | 3 +- 7 files changed, 12 insertions(+), 80 deletions(-) delete mode 100644 src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.old diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.old b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.old deleted file mode 100644 index 1cdb1303..00000000 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.old +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.coverage; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.AbstractCoverageExtension; -import org.sonar.api.batch.CoverageExtension; -import org.sonar.api.batch.Sensor; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.plugins.cobertura.api.AbstractCoberturaParser; -import org.sonar.plugins.cobertura.api.CoberturaUtils; -import org.sonar.plugins.objectivec.core.ObjectiveC; - -import java.io.File; - -public class CoberturaSensor implements Sensor { - - private static final Logger LOG = LoggerFactory.getLogger(CoberturaSensor.class); - - public CoberturaSensor() { - } - - public boolean shouldExecuteOnProject(Project project) { - return ObjectiveC.KEY.equals(project.getLanguageKey()); - } - - public void analyse(Project project, SensorContext context) { - File report = CoberturaUtils.getReport(project); - if (report != null) { - parseReport(report, context); - } - } - - protected void parseReport(File xmlFile, final SensorContext context) { - LOG.info("parsing {}", xmlFile); - COBERTURA_PARSER.parseReport(xmlFile, context); - } - - private static final AbstractCoberturaParser COBERTURA_PARSER = new AbstractCoberturaParser() { - @Override - protected Resource getResource(String fileName) { - LOG.info("Analyzing {}", fileName); - fileName = fileName.replace(".", "/") + ".m"; - return new org.sonar.api.resources.File(fileName); - } - }; - - @Override - public String toString() { - return "Objective-C CoberturaSensor"; - } - -} \ No newline at end of file diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java index d9749ba6..ae7c52ee 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java @@ -1,6 +1,7 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012-2015 OCTO Technology, Backelite + * 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 diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java index 9780190d..43087de4 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java @@ -1,6 +1,7 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012-2015 OCTO Technology, Backelite + * 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 diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java index eb984b08..04550463 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -1,6 +1,7 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012-2015 OCTO Technology, Backelite + * 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 diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java index a3489a4a..45a0a3e7 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java @@ -1,6 +1,7 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012-2015 OCTO Technology, Backelite + * 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 diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java index ebd6d9a5..8327a04d 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java @@ -1,6 +1,7 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012-2015 OCTO Technology, Backelite + * 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 diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java index ea6d80bb..da374360 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java @@ -1,6 +1,7 @@ /* * Sonar Objective-C Plugin -* Copyright (C) 2012-2015 OCTO Technology, Backelite + * 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 From a599ae5d79bd5c1aeef884b13fb6249f8c143691 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 20 Feb 2015 00:07:49 +0100 Subject: [PATCH 015/107] Documentation updated - Added travis widget on README - Updated pom to match new repo --- README.md | 5 +++++ pom.xml | 12 ++++++------ .../org/sonar/objectivec/ObjectiveCAstScanner.java | 2 +- .../sonar/objectivec/ObjectiveCConfiguration.java | 2 +- .../org/sonar/objectivec/api/ObjectiveCGrammar.java | 2 +- .../org/sonar/objectivec/api/ObjectiveCKeyword.java | 2 +- .../org/sonar/objectivec/api/ObjectiveCMetric.java | 2 +- .../sonar/objectivec/api/ObjectiveCPunctuator.java | 2 +- .../sonar/objectivec/api/ObjectiveCTokenType.java | 2 +- .../java/org/sonar/objectivec/checks/CheckList.java | 2 +- .../org/sonar/objectivec/lexer/ObjectiveCLexer.java | 2 +- .../objectivec/parser/ObjectiveCGrammarImpl.java | 2 +- .../sonar/objectivec/parser/ObjectiveCParser.java | 2 +- .../sonar/plugins/objectivec/ObjectiveCPlugin.java | 2 +- .../plugins/objectivec/ObjectiveCSquidSensor.java | 2 +- .../colorizer/ObjectiveCColorizerFormat.java | 2 +- .../sonar/plugins/objectivec/core/ObjectiveC.java | 2 +- .../objectivec/core/ObjectiveCSourceImporter.java | 2 +- .../plugins/objectivec/coverage/CoberturaParser.java | 2 +- .../plugins/objectivec/coverage/CoberturaSensor.java | 2 +- .../coverage/CoberturaXMLStreamHandler.java | 2 +- .../coverage/CoverageMeasuresPersistor.java | 2 +- .../objectivec/coverage/ReportFilesFinder.java | 2 +- .../plugins/objectivec/cpd/ObjectiveCCpdMapping.java | 2 +- .../plugins/objectivec/cpd/ObjectiveCTokenizer.java | 2 +- .../plugins/objectivec/tests/SurefireParser.java | 3 +-- .../plugins/objectivec/tests/SurefireSensor.java | 3 +-- .../objectivec/violations/ObjectiveCProfile.java | 2 +- .../violations/fauxpas/FauxPasProfile.java | 2 +- .../violations/fauxpas/FauxPasProfileImporter.java | 2 +- .../violations/fauxpas/FauxPasReportParser.java | 2 +- .../violations/fauxpas/FauxPasRuleParser.java | 2 +- .../violations/fauxpas/FauxPasRuleRepository.java | 2 +- .../objectivec/violations/fauxpas/FauxPasSensor.java | 2 +- .../objectivec/violations/oclint/OCLintParser.java | 2 +- .../objectivec/violations/oclint/OCLintProfile.java | 2 +- .../violations/oclint/OCLintProfileImporter.java | 2 +- .../violations/oclint/OCLintRuleParser.java | 2 +- .../violations/oclint/OCLintRuleRepository.java | 2 +- .../objectivec/violations/oclint/OCLintSensor.java | 2 +- .../violations/oclint/OCLintXMLStreamHandler.java | 2 +- .../sonar/objectivec/ObjectiveCAstScannerTest.java | 2 +- .../objectivec/api/ObjectiveCPunctuatorTest.java | 2 +- .../sonar/objectivec/lexer/ObjectiveCLexerTest.java | 2 +- .../coverage/CoberturaMeasuresPersistorTest.java | 2 +- .../objectivec/coverage/CoberturaParserTest.java | 2 +- .../objectivec/coverage/CoberturaSensorTest.java | 2 +- .../coverage/CoberturaXMLStreamHandlerTest.java | 2 +- .../violations/fauxpas/FauxPasRuleParserTest.java | 2 +- .../violations/fauxpas/FauxPasSensorTest.java | 2 +- .../violations/oclint/OCLintParserTest.java | 2 +- .../violations/oclint/OCLintSensorTest.java | 2 +- .../oclint/OCLintXMLStreamHandlerTest.java | 2 +- .../objectivec/violations/oclint/ProjectBuilder.java | 2 +- 54 files changed, 63 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index d1249129..7a329fa0 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@

+| Branch | Status | +|----------|:------------------------------------------------------------------------------------------------------------------------------------------:| +| master | [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=master)](https://travis-ci.org/Backelite/sonar-objective-c) | +| develop| [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=develop)](https://travis-ci.org/Backelite/sonar-objective-c) | + SonarQube Plugin for Objective-C ================================ diff --git a/pom.xml b/pom.xml index 5883e30c..e5f79225 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ Objective-C Sonar Plugin Enables analysis of Objective-C projects into Sonar. - https://github.com/octo-technology/sonar-objective-c + https://github.com/Backelite/sonar-objective-c 2012 @@ -59,7 +59,7 @@ zippy1978 Gilles Grousset - https://github.com/zippy1978 + Backelite fhelg @@ -79,9 +79,9 @@ - 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 + scm:git:git@github.com:Backelite/sonar-objective-c.git + scm:git:git@github.com:Backelite/sonar-objective-c.git + https://github.com/Backelite/sonar-objective-c @@ -90,7 +90,7 @@ - OCTO Technology + OCTO Technology, Backelite Sonar Objective-C Plugin true diff --git a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java b/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java index ae3b590e..b3f5f576 100644 --- a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java +++ b/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/ObjectiveCConfiguration.java b/src/main/java/org/sonar/objectivec/ObjectiveCConfiguration.java index cee56859..7832d7a0 100644 --- a/src/main/java/org/sonar/objectivec/ObjectiveCConfiguration.java +++ b/src/main/java/org/sonar/objectivec/ObjectiveCConfiguration.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java index f582eea1..633fc744 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java +++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCKeyword.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCKeyword.java index df2c1a81..68c436bd 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCKeyword.java +++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCKeyword.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java index 23058102..d42074e9 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java +++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCPunctuator.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCPunctuator.java index d6bba1ad..c00d2418 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCPunctuator.java +++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCPunctuator.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java b/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java index 553b7129..96010a48 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java +++ b/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/checks/CheckList.java b/src/main/java/org/sonar/objectivec/checks/CheckList.java index be5d018f..dd898a09 100644 --- a/src/main/java/org/sonar/objectivec/checks/CheckList.java +++ b/src/main/java/org/sonar/objectivec/checks/CheckList.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java b/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java index c0f72700..dd5d41dc 100644 --- a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java +++ b/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java b/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java index 14bb779f..e3a6b699 100644 --- a/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java +++ b/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java b/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java index 6f0d2c19..5e9f0bb3 100644 --- a/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java +++ b/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java index 27530277..06272db7 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java index 43bbb5e9..ddabf32c 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java b/src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java index 3f5ea9f8..9f5e2d14 100644 --- a/src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java +++ b/src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or 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 25e3904d..0eca295f 100644 --- a/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java +++ b/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveCSourceImporter.java b/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveCSourceImporter.java index f8776a20..8398f430 100644 --- a/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveCSourceImporter.java +++ b/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveCSourceImporter.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaParser.java b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaParser.java index cd333d32..98723097 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaParser.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or 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 0a900b2b..8e7bf719 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java index 44501a4c..8a6b5575 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or 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 d46ee0fa..6c261733 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java b/src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java index 831327a9..9527cf04 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java b/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java index 4cf0fc5b..8c3adac5 100644 --- a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java +++ b/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java b/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java index bf180f7c..8c884bb9 100644 --- a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java +++ b/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java index b566a92a..09ad0a4b 100644 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or @@ -17,7 +17,6 @@ * 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 com.sun.swing.internal.plaf.metal.resources.metal_sv; 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 207a430f..431a3054 100644 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or @@ -17,7 +17,6 @@ * 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.slf4j.Logger; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java index 82688740..d877872b 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java index ae7c52ee..6408947d 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java index 43087de4..b9239d4f 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java index 04550463..9a8c8c30 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java index 45a0a3e7..02fb61b6 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java index 8327a04d..a833e79b 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java index da374360..07dbd29b 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java index b13b7207..546b0411 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java index e5b482f4..de337ef0 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java index 9703ac1e..fc6896b7 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java index 19b67b10..0dd51c17 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java index ee958508..d16d78e5 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java index 8787cad6..59a1d0ee 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java index 2bfa20fb..e5d77dec 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java b/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java index ea40f1f6..474828c6 100644 --- a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java +++ b/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/objectivec/api/ObjectiveCPunctuatorTest.java b/src/test/java/org/sonar/objectivec/api/ObjectiveCPunctuatorTest.java index 052b19c2..5b1df7a2 100644 --- a/src/test/java/org/sonar/objectivec/api/ObjectiveCPunctuatorTest.java +++ b/src/test/java/org/sonar/objectivec/api/ObjectiveCPunctuatorTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java b/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java index 080c41cf..4e912c02 100644 --- a/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java +++ b/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or 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 8ac0339b..fb687e6d 100644 --- a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaParserTest.java b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaParserTest.java index f3d6be1e..35f0d042 100644 --- a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaParserTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaParserTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaSensorTest.java index 84922956..81136f6b 100644 --- a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaSensorTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaSensorTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandlerTest.java b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandlerTest.java index 1b9f3e6a..4175a539 100644 --- a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandlerTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandlerTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java index 5e6a6f6d..3050382e 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java index bc5bb0b6..02ecbf63 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java index 5aa6cdcd..8790896b 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java index 019de2f0..d78c742e 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java index c0c410df..ffde0c59 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java index b7d4994d..5de09a7b 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java @@ -1,6 +1,6 @@ /* * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology + * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or From e233b20048a1656ca0611da0d1cfedf2236cc6db Mon Sep 17 00:00:00 2001 From: Philippe Bernery Date: Fri, 20 Feb 2015 09:47:11 +0100 Subject: [PATCH 016/107] Add the `plain` reporter at build step to get more information in case of build failure. --- src/main/shell/run-sonar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index d3de4e1f..6b170ed1 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -221,7 +221,7 @@ fi # Extracting project information needed later echo -n 'Extracting Xcode project information' runCommand /dev/stdout $xctoolCmdPrefix -scheme "$appScheme" clean -runCommand /dev/stdout $xctoolCmdPrefix -scheme "$appScheme" -reporter json-compilation-database:compile_commands.json build +runCommand /dev/stdout $xctoolCmdPrefix -scheme "$appScheme" -reporter plain -reporter json-compilation-database:compile_commands.json build # Unit tests and coverage if [ "$testScheme" = "" ]; then From 12d864b4811a088f26dd64f8fe2cac39dc0b892f Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 23 Feb 2015 13:44:53 +0100 Subject: [PATCH 017/107] Updated sample sonar-project.properties --- sample/sonar-project.properties | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sample/sonar-project.properties b/sample/sonar-project.properties index 41ed559f..5cdfde10 100644 --- a/sample/sonar-project.properties +++ b/sample/sonar-project.properties @@ -48,9 +48,13 @@ sonar.sourceEncoding=UTF-8 # Change it only if you generate the file on your own # sonar.objectivec.oclint.report=sonar-reports/oclint.xml +# FauxPas report generated by run-sonar.sh is stored in sonar-reports/fauxpas.json +# Change it only if you generate the file on your own +# sonar.objectivec.fauxpas.report=sonar-reports/fauxpas.json + # Paths to exclude from coverage report (tests, 3rd party libraries etc.) # sonar.objectivec.excludedPathsFromCoverage=pattern1,pattern2 -sonar.objectivec.excludedPathsFromCoverage=.*Tests.* +sonar.objectivec.excludedPathsFromCoverage=.*Tests.*,.*Specs.* # Project SCM settings # sonar.scm.enabled=true From ea952d7be97d78ab279c76d938bc3c699f468e7a Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 27 Feb 2015 22:43:34 +0100 Subject: [PATCH 018/107] Capitalized OCLint rule names --- .../objectivec/violations/oclint/OCLintRuleParser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java index 0dd51c17..3b994b0f 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java @@ -25,6 +25,7 @@ import java.util.List; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.ServerComponent; @@ -69,7 +70,8 @@ public List parse(final BufferedReader reader) throws IOException { rule = Rule.create(); rules.add(rule); - rule.setName(previousLine); + rule.setName(StringUtils.capitalize(previousLine)); + System.out.println(">>> " + rule.getName()); rule.setKey(previousLine); } else if (line.matches("Summary:.*")) { inDescription = true; From 2d37a17bc7d7f17f00c1d2167a9264714efe150c Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 27 Feb 2015 22:45:45 +0100 Subject: [PATCH 019/107] Cleared System.out --- .../plugins/objectivec/violations/oclint/OCLintRuleParser.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java index 3b994b0f..02bdca28 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java @@ -71,7 +71,6 @@ public List parse(final BufferedReader reader) throws IOException { rule = Rule.create(); rules.add(rule); rule.setName(StringUtils.capitalize(previousLine)); - System.out.println(">>> " + rule.getName()); rule.setKey(previousLine); } else if (line.matches("Summary:.*")) { inDescription = true; From ff088788387987838613272fce4aa58570375c34 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 27 Feb 2015 22:48:28 +0100 Subject: [PATCH 020/107] Documentation update --- README.md | 2 ++ pom.xml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a329fa0..a75a9c09 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://g Releases available from this repository are compliant with SonarQube 4.3.x and above. +In order not to break compatibility with the original plugin, plugin releases are numbered with 4 digits. + ###Faux Pas support [Faux Pas](http://fauxpasapp.com/) is a wonderful tool to analyse iOS or Mac applications source code, however it is not free. A 30 trial version is available [here](http://fauxpasapp.com/try/). diff --git a/pom.xml b/pom.xml index e5f79225..7180cce3 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec sonar-objective-c-plugin - 0.4.0-SNAPSHOT + 0.4.0.1 sonar-plugin From 09c18e3bf720903c073d3dd152aafe479686de37 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 27 Feb 2015 22:59:36 +0100 Subject: [PATCH 021/107] Documentation update --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a75a9c09..a2939472 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,17 @@ The plugin runs fine even if Faux Pas is not installed (Faux Pas analysis will b ###Download -Binary packages will be available soon... +Binary packages are available in the release section. + + +###Release history + +####0.4.0.1 (based on 0.4.0) +- Faux Pas support +- Moved OCLint long line threshold to 250 +- Add the `plain` reporter at build step to get more information in case of build failure +- Capitalized OCLint rule names + ###Prerequisites From b69ba3ba7f34cf89b369f16f8d071bfffe3e3e80 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 2 Mar 2015 16:22:06 +0100 Subject: [PATCH 022/107] Added workspace switch to faux pas command --- src/main/shell/run-sonar.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index d3de4e1f..7ed8a689 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -306,7 +306,8 @@ if [ "$fauxpas" = "on" ]; then #FauxPas echo -n 'Running FauxPas...' - fauxpas -t $appScheme -o json check $projectFile > sonar-reports/fauxpas.json + fauxpas -t $appScheme -o json check $projectFile --workspace $workspaceFile --scheme $appScheme > sonar-reports/fauxpas.json + else echo 'Skipping FauxPas (not installed)' From 0ac8b7bfdb941b0c15fe830334bf83c617ccabd6 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 22 Jun 2015 17:18:30 +0200 Subject: [PATCH 023/107] Added default description for FauxPas violation when not available from report --- .../objectivec/violations/fauxpas/FauxPasReportParser.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java index 9a8c8c30..9ee0d9df 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -96,7 +96,11 @@ private void recordViolation(final JSONObject diagnosticJson, Collection Date: Mon, 6 Jul 2015 14:03:39 +0200 Subject: [PATCH 024/107] Faux Pas support for multiple projects - Multiple projects support - New Faux Pas rules available --- .../violations/fauxpas/FauxPasSensor.java | 24 ++++++++++--- .../com/sonar/sqale/objectivec-model.xml | 36 +++++++++++++++++++ .../sonar/plugins/fauxpas/profile-fauxpas.xml | 12 +++++-- .../org/sonar/plugins/fauxpas/rules.json | 26 ++++++++++---- src/main/shell/run-sonar.sh | 12 +++++-- 5 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java index 07dbd29b..bf4d0f9d 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java @@ -19,6 +19,7 @@ */ package org.sonar.plugins.objectivec.violations.fauxpas; +import org.apache.tools.ant.DirectoryScanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.Sensor; @@ -31,13 +32,14 @@ import org.sonar.plugins.objectivec.core.ObjectiveC; import java.io.File; +import java.util.ArrayList; import java.util.Collection; public class FauxPasSensor implements Sensor { public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX + ".fauxpas.report"; - public static final String DEFAULT_REPORT_PATH = "sonar-reports/fauxpas.json"; + public static final String DEFAULT_REPORT_PATH = "sonar-reports/*fauxpas.json"; private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasSensor.class); @@ -70,10 +72,22 @@ private void saveViolations(final Collection violations, final Sensor } private Collection parseReportIn(final String baseDir, final FauxPasReportParser parser) { - final StringBuilder reportFileName = new StringBuilder(baseDir); - reportFileName.append("/").append(reportPath()); - LOGGER.info("Processing FauxPas report {}", reportFileName); - return parser.parseReport(new File(reportFileName.toString())); + + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setIncludes(new String[]{reportPath()}); + scanner.setBasedir(baseDir); + scanner.setCaseSensitive(false); + scanner.scan(); + String[] files = scanner.getIncludedFiles(); + + Collection result = new ArrayList(); + for(String filename : files) { + LOGGER.info("Processing FauxPas report {}", filename); + result.addAll(parser.parseReport(new File(filename))); + } + + return result; + } private String reportPath() { diff --git a/src/main/resources/com/sonar/sqale/objectivec-model.xml b/src/main/resources/com/sonar/sqale/objectivec-model.xml index 0d1de0ed..29a7a2a3 100644 --- a/src/main/resources/com/sonar/sqale/objectivec-model.xml +++ b/src/main/resources/com/sonar/sqale/objectivec-model.xml @@ -189,6 +189,24 @@ READABILITY Readability + + FauxPas + SuspiciousDateTimeFormat + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + FauxPas RecommendedVCSIgnores @@ -2681,6 +2699,24 @@ d + + FauxPas + DefaultInExhaustiveSwitch + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + RESOURCE_RELIABILITY diff --git a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml index c32547c7..bdf9a5d6 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml +++ b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml @@ -295,6 +295,10 @@ FauxPas LiteralStringKeyPath + + FauxPas + SuspiciousDateTimeFormat + FauxPas BlockAPIRetainCycle @@ -321,11 +325,11 @@ FauxPas - NullCoalescingOp + StrongInsteadOfRetain FauxPas - StrongInsteadOfRetain + NullCoalescingOp FauxPas @@ -371,6 +375,10 @@ FauxPas ReservedIdentifierNaming + + FauxPas + DefaultInExhaustiveSwitch + FauxPas MallocCast diff --git a/src/main/resources/org/sonar/plugins/fauxpas/rules.json b/src/main/resources/org/sonar/plugins/fauxpas/rules.json index da6b4a0b..291cb11e 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/rules.json +++ b/src/main/resources/org/sonar/plugins/fauxpas/rules.json @@ -510,6 +510,13 @@ "description": "Warns when literal strings are used to specify key paths.Something like NSStringFromSelector(@selector(foo)) is safer because it makes the compiler aware of the selector being specified \u2014 this helps find typos at compile time and allows automatic refactoring tools to make appropriate changes.", "severity": "CRITICAL" }, + { + "category": "APIUsage", + "key": "SuspiciousDateTimeFormat", + "name": "Suspicious date-time format", + "description": "Warns about date-time formats that are likely to have unintentional behavior, e.g. \u201Cweek-year\u201D specifiers (uppercase Y) instead of normal calendar year specifiers (lowercase y).", + "severity": "CRITICAL" + }, { "category": "APIUsage", "key": "BlockAPIRetainCycle", @@ -554,16 +561,16 @@ }, { "category": "Style", - "key": "NullCoalescingOp", - "name": "Null coalescing operator usage", - "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", + "key": "StrongInsteadOfRetain", + "name": "Usage of retain in ARC code", + "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", "severity": "MAJOR" }, { "category": "Style", - "key": "StrongInsteadOfRetain", - "name": "Usage of retain in ARC code", - "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", + "key": "NullCoalescingOp", + "name": "Null coalescing operator usage", + "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", "severity": "MAJOR" }, { @@ -643,6 +650,13 @@ "description": "Warns when identifiers are named using conventions reserved by the C standard or POSIX.", "severity": "MINOR" }, + { + "category": "Pedantic", + "key": "DefaultInExhaustiveSwitch", + "name": "Unnecessary default case in exhaustive switch statement", + "description": "If a switch statement explicitly handles all possible values, then the default case is unnecessary dead code, and might even be misleading to people reading the code.If the intended purpose of the default case is to safeguard against enum fields that are added in the future but accidentally left unhandled in the switch, the compiler warning -Wswitch-enum is recommended for that purpose instead.", + "severity": "MINOR" + }, { "category": "Pedantic", "key": "MallocCast", diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 34686c69..8d9bd23d 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -246,7 +246,7 @@ else echo $projectFile | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh while read projectName; do - projectName=$(basename $projectFile .xcodeproj) + projectName=$(basename $projectName .xcodeproj) coverageFilesPath="build/$projectName.build/Debug-iphonesimulator/$appScheme.build/Objects-normal/i386" if [ "$vflag" = "on" ]; then echo @@ -305,9 +305,15 @@ if [ "$fauxpas" = "on" ]; then if [ $? -eq 0 ]; then #FauxPas - echo -n 'Running FauxPas...' - fauxpas -t $appScheme -o json check $projectFile --workspace $workspaceFile --scheme $appScheme > sonar-reports/fauxpas.json + echo $projectFile | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh + while read projectName; do + echo -n 'Running FauxPas...' + echo 'fauxpas -t $appScheme -o json check $projectName --workspace $workspaceFile --scheme $appScheme > sonar-reports/$projectName-fauxpas.json' + fauxpas -t $appScheme -o json check $projectName --workspace $workspaceFile --scheme $appScheme > sonar-reports/$(basename $projectName .xcodeproj)-fauxpas.json + + done < tmpFileRunSonarSh + rm -rf tmpFileRunSonarSh else echo 'Skipping FauxPas (not installed)' From a38947664a7988e1ad5260f8bee40fda784bf5b7 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 6 Jul 2015 14:39:12 +0200 Subject: [PATCH 025/107] Dynamic coverage file path --- src/main/shell/run-sonar.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 8d9bd23d..7f27e5a4 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -247,7 +247,7 @@ else while read projectName; do projectName=$(basename $projectName .xcodeproj) - coverageFilesPath="build/$projectName.build/Debug-iphonesimulator/$appScheme.build/Objects-normal/i386" + coverageFilesPath=$(grep 'command' compile_commands.json | sed 's#^.*-o \\/#\/#;s#",##' | grep "/${projectName%%.*}" | awk 'NR<2' | sed 's/\\\//\//g' | sed 's/\\\\//g' | xargs -0 dirname) if [ "$vflag" = "on" ]; then echo echo "Path for .gcno/.gcda coverage files is: $coverageFilesPath" @@ -295,6 +295,7 @@ if [ "$oclint" = "on" ]; then # Run OCLint with the right set of compiler options maxPriority=10000 longLineThreshold=250 + echo "oclint-json-compilation-database $includedCommandLineFlags -- -rc LONG_LINE=$longLineThreshold -max-priority-1 $maxPriority -max-priority-2 $maxPriority -max-priority-3 $maxPriority -report-type pmd -o sonar-reports/oclint.xml" runCommand no oclint-json-compilation-database $includedCommandLineFlags -- -rc LONG_LINE=$longLineThreshold -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!)' @@ -309,7 +310,6 @@ if [ "$fauxpas" = "on" ]; then while read projectName; do echo -n 'Running FauxPas...' - echo 'fauxpas -t $appScheme -o json check $projectName --workspace $workspaceFile --scheme $appScheme > sonar-reports/$projectName-fauxpas.json' fauxpas -t $appScheme -o json check $projectName --workspace $workspaceFile --scheme $appScheme > sonar-reports/$(basename $projectName .xcodeproj)-fauxpas.json done < tmpFileRunSonarSh From 744954ed32e47a0388de297a05e2646bb8bbac40 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 7 Jul 2015 16:57:12 +0200 Subject: [PATCH 026/107] Multiple OCLint report support - Each source folder generate a separate OCLint report - Fixed OCLint analysis by removing OBJROOT from xctool command line --- .../violations/oclint/OCLintSensor.java | 23 ++++++++--- src/main/shell/run-sonar.sh | 38 +++++++++++-------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java index 59a1d0ee..576d13a8 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java @@ -20,8 +20,10 @@ package org.sonar.plugins.objectivec.violations.oclint; import java.io.File; +import java.util.ArrayList; import java.util.Collection; +import org.apache.tools.ant.DirectoryScanner; import org.slf4j.LoggerFactory; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; @@ -35,7 +37,7 @@ public final class OCLintSensor implements Sensor { public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX + ".oclint.report"; - public static final String DEFAULT_REPORT_PATH = "sonar-reports/oclint.xml"; + public static final String DEFAULT_REPORT_PATH = "sonar-reports/*oclint.xml"; private final Settings conf; private final FileSystem fileSystem; @@ -68,12 +70,21 @@ private void saveViolations(final Collection violations, private Collection parseReportIn(final String baseDir, final OCLintParser parser) { - final StringBuilder reportFileName = new StringBuilder(baseDir); - reportFileName.append("/").append(reportPath()); - LoggerFactory.getLogger(getClass()).info("Processing OCLint report {}", - reportFileName); - return parser.parseReport(new File(reportFileName.toString())); + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setIncludes(new String[]{reportPath()}); + scanner.setBasedir(baseDir); + scanner.setCaseSensitive(false); + scanner.scan(); + String[] files = scanner.getIncludedFiles(); + + Collection result = new ArrayList(); + for(String filename : files) { + LoggerFactory.getLogger(getClass()).info("Processing OCLint report {}", filename); + result.addAll(parser.parseReport(new File(filename))); + } + + return result; } private String reportPath() { diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 7f27e5a4..4051ea3c 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -155,9 +155,9 @@ fi workspaceFile=''; readParameter workspaceFile 'sonar.objectivec.workspace' projectFile=''; readParameter projectFile 'sonar.objectivec.project' if [[ "$workspaceFile" != "" ]] ; then - xctoolCmdPrefix="xctool -workspace $workspaceFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO OBJROOT=./build" + xctoolCmdPrefix="xctool -workspace $workspaceFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" else - xctoolCmdPrefix="xctool -project $projectFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO OBJROOT=./build" + xctoolCmdPrefix="xctool -project $projectFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" fi # Source directories for .h/.m files @@ -216,6 +216,11 @@ if [[ ! (-d "sonar-reports") && ("$nflag" != "on") ]]; then stopProgress exit $? fi +else + if [ "$vflag" = "on" ]; then + echo 'Clearing sonar-reports/' + fi + rm -rf sonar-reports/* fi # Extracting project information needed later @@ -278,25 +283,28 @@ if [ "$oclint" = "on" ]; then # OCLint echo -n 'Running OCLint...' - + + # Options + maxPriority=10000 + longLineThreshold=250 + # Build the --include flags currentDirectory=${PWD##*/} - includedCommandLineFlags="" echo "$srcDirs" | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh while read word; do - includedCommandLineFlags+=" --include .*/${currentDirectory}/${word}" + + includedCommandLineFlags=" --include .*/${currentDirectory}/${word}" + if [ "$vflag" = "on" ]; then + echo + echo -n "Path included in oclint analysis is:$includedCommandLineFlags" + fi + # Run OCLint with the right set of compiler options + runCommand no oclint-json-compilation-database -v $includedCommandLineFlags -- -rc LONG_LINE=$longLineThreshold -max-priority-1 $maxPriority -max-priority-2 $maxPriority -max-priority-3 $maxPriority -report-type pmd -o sonar-reports/$(echo $word | sed 's/\//_/g')-oclint.xml + done < tmpFileRunSonarSh rm -rf tmpFileRunSonarSh - if [ "$vflag" = "on" ]; then - echo - echo -n "Path included in oclint analysis is:$includedCommandLineFlags" - fi - - # Run OCLint with the right set of compiler options - maxPriority=10000 - longLineThreshold=250 - echo "oclint-json-compilation-database $includedCommandLineFlags -- -rc LONG_LINE=$longLineThreshold -max-priority-1 $maxPriority -max-priority-2 $maxPriority -max-priority-3 $maxPriority -report-type pmd -o sonar-reports/oclint.xml" - runCommand no oclint-json-compilation-database $includedCommandLineFlags -- -rc LONG_LINE=$longLineThreshold -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 From 822b9651c28b635b49085d217e2e976b0c534a4d Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 7 Jul 2015 21:46:23 +0200 Subject: [PATCH 027/107] Fixed gcovr path in run-script.sh --- src/main/shell/run-sonar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 4051ea3c..a6cec316 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -252,7 +252,7 @@ else while read projectName; do projectName=$(basename $projectName .xcodeproj) - coverageFilesPath=$(grep 'command' compile_commands.json | sed 's#^.*-o \\/#\/#;s#",##' | grep "/${projectName%%.*}" | awk 'NR<2' | sed 's/\\\//\//g' | sed 's/\\\\//g' | xargs -0 dirname) + coverageFilesPath=$(grep 'command' compile_commands.json | sed 's#^.*-o \\/#\/#;s#",##' | grep "${projectName%%.*}.build" | awk 'NR<2' | sed 's/\\\//\//g' | sed 's/\\\\//g' | xargs -0 dirname) if [ "$vflag" = "on" ]; then echo echo "Path for .gcno/.gcda coverage files is: $coverageFilesPath" From 966dfd6877b6a00542364001ca1c5b03a0e62562 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 7 Jul 2015 22:57:10 +0200 Subject: [PATCH 028/107] Created symlinks for build files to avoid gcovr object-directory bug --- src/main/shell/run-sonar.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index a6cec316..12f17a05 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -207,11 +207,12 @@ if [ "$vflag" = "" -a "$nflag" = "" ]; then fi # Create sonar-reports/ for reports output -if [[ ! (-d "sonar-reports") && ("$nflag" != "on") ]]; then +if [[ ! (-d "sonar-reports/build") && ("$nflag" != "on") ]]; then if [ "$vflag" = "on" ]; then echo 'Creating directory sonar-reports/' fi mkdir sonar-reports + mkdir sonar-reports/build if [[ $? != 0 ]] ; then stopProgress exit $? @@ -258,6 +259,9 @@ else echo "Path for .gcno/.gcda coverage files is: $coverageFilesPath" fi + # Create symlink to avoid gcovr bug with --object-directory + ln -s $coverageFilesPath sonar-reports/build/$projectName + # Build the --exclude flags excludedCommandLineFlags="" if [ ! -z "$excludedPathsFromCoverage" -a "$excludedPathsFromCoverage" != " " ]; then @@ -272,7 +276,7 @@ else fi # Run gcovr with the right options - runCommand "sonar-reports/coverage-${projectName%%.*}.xml" gcovr -r . --object-directory "$coverageFilesPath" $excludedCommandLineFlags --xml + runCommand "sonar-reports/coverage-${projectName%%.*}.xml" gcovr -r . $excludedCommandLineFlags --xml done < tmpFileRunSonarSh rm -rf tmpFileRunSonarSh From 22e04140bf5c24e46bbb61a58dacfcaa1700e69f Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 7 Jul 2015 23:09:01 +0200 Subject: [PATCH 029/107] glover optimization (runs once only) --- src/main/shell/run-sonar.sh | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 12f17a05..1fb9202c 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -262,24 +262,24 @@ else # Create symlink to avoid gcovr bug with --object-directory ln -s $coverageFilesPath sonar-reports/build/$projectName - # Build the --exclude flags - excludedCommandLineFlags="" - if [ ! -z "$excludedPathsFromCoverage" -a "$excludedPathsFromCoverage" != " " ]; then - echo $excludedPathsFromCoverage | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh2 - while read word; do - excludedCommandLineFlags+=" --exclude $word" - done < tmpFileRunSonarSh2 - rm -rf tmpFileRunSonarSh2 - fi - if [ "$vflag" = "on" ]; then - echo "Command line exclusion flags for gcovr is:$excludedCommandLineFlags" - fi - - # Run gcovr with the right options - runCommand "sonar-reports/coverage-${projectName%%.*}.xml" gcovr -r . $excludedCommandLineFlags --xml - done < tmpFileRunSonarSh rm -rf tmpFileRunSonarSh + + # Build the --exclude flags + excludedCommandLineFlags="" + if [ ! -z "$excludedPathsFromCoverage" -a "$excludedPathsFromCoverage" != " " ]; then + echo $excludedPathsFromCoverage | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh2 + while read word; do + excludedCommandLineFlags+=" --exclude $word" + done < tmpFileRunSonarSh2 + rm -rf tmpFileRunSonarSh2 + fi + if [ "$vflag" = "on" ]; then + echo "Command line exclusion flags for gcovr is:$excludedCommandLineFlags" + fi + + # Run gcovr with the right options + runCommand "sonar-reports/coverage.xml" gcovr -r . $excludedCommandLineFlags --xml fi From 5a0e88e4c280ed1b680c59a522661a1427ef8999 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 8 Jul 2015 10:58:09 +0200 Subject: [PATCH 030/107] Fixed wrong coverage report --- src/main/shell/run-sonar.sh | 94 +++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 1fb9202c..a8d47ce6 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -158,7 +158,13 @@ if [[ "$workspaceFile" != "" ]] ; then xctoolCmdPrefix="xctool -workspace $workspaceFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" else xctoolCmdPrefix="xctool -project $projectFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" -fi +fi + +# Count projects +projectCount=$(echo $projectFile | sed -n 1'p' | tr ',' '\n' | wc -l | tr -d '[[:space:]]') +if [ "$vflag" = "on" ]; then + echo "Project count is [$projectCount]" +fi # Source directories for .h/.m files srcDirs=''; readParameter srcDirs 'sonar.sources' @@ -207,22 +213,11 @@ if [ "$vflag" = "" -a "$nflag" = "" ]; then fi # Create sonar-reports/ for reports output -if [[ ! (-d "sonar-reports/build") && ("$nflag" != "on") ]]; then - if [ "$vflag" = "on" ]; then - echo 'Creating directory sonar-reports/' - fi - mkdir sonar-reports - mkdir sonar-reports/build - if [[ $? != 0 ]] ; then - stopProgress - exit $? - fi -else - if [ "$vflag" = "on" ]; then - echo 'Clearing sonar-reports/' - fi - rm -rf sonar-reports/* +if [ "$vflag" = "on" ]; then + echo 'Creating directory sonar-reports/' fi +rm -rf sonar-reports +mkdir sonar-reports # Extracting project information needed later echo -n 'Extracting Xcode project information' @@ -246,25 +241,6 @@ else echo -n 'Computing coverage report' - # We do it for every xcodeproject (in case of workspaces) - - # Extract the path to the .gcno/.gcda coverage files - echo $projectFile | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh - while read projectName; do - - projectName=$(basename $projectName .xcodeproj) - coverageFilesPath=$(grep 'command' compile_commands.json | sed 's#^.*-o \\/#\/#;s#",##' | grep "${projectName%%.*}.build" | awk 'NR<2' | sed 's/\\\//\//g' | sed 's/\\\\//g' | xargs -0 dirname) - if [ "$vflag" = "on" ]; then - echo - echo "Path for .gcno/.gcda coverage files is: $coverageFilesPath" - fi - - # Create symlink to avoid gcovr bug with --object-directory - ln -s $coverageFilesPath sonar-reports/build/$projectName - - done < tmpFileRunSonarSh - rm -rf tmpFileRunSonarSh - # Build the --exclude flags excludedCommandLineFlags="" if [ ! -z "$excludedPathsFromCoverage" -a "$excludedPathsFromCoverage" != " " ]; then @@ -278,8 +254,15 @@ else echo "Command line exclusion flags for gcovr is:$excludedCommandLineFlags" fi + # Create symlink on the build directory to enable its access from the workspace + coverageFilesPath=$(grep 'command' compile_commands.json | sed 's#^.*-o \\/#\/#;s#",##' | grep "${projectName%%.*}.build" | awk 'NR<2' | sed 's/\\\//\//g' | sed 's/\\\\//g' | xargs -0 dirname) + splitIndex=$(awk -v a="$coverageFilesPath" -v b="/Intermediates" 'BEGIN{print index(a,b)}') + coverageFilesPath=$(echo ${coverageFilesPath:0:$splitIndex}Intermediates) + ln -s $coverageFilesPath sonar-reports/build + # Run gcovr with the right options runCommand "sonar-reports/coverage.xml" gcovr -r . $excludedCommandLineFlags --xml + fi @@ -318,14 +301,43 @@ if [ "$fauxpas" = "on" ]; then if [ $? -eq 0 ]; then #FauxPas - echo $projectFile | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh - while read projectName; do + echo -n 'Running FauxPas...' + + if [ "$projectCount" = "1" ] + then + + fauxpas -o json check $projectFile --workspace $workspaceFile --scheme $appScheme > sonar-reports/fauxpas.json + + + else + + echo $projectFile | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh + while read projectName; do + + xcodebuild -list -project $projectName | sed -n '/Schemes/,$p' | while read scheme + do + + if [ "$scheme" = "" ] + then + exit + fi + + if [ "$scheme" == "${scheme/Schemes/}" ] + then + if [ "$scheme" != "$testScheme" ] + then + projectBaseDir=$(dirname $projectName) + workspaceRelativePath=$(python -c "import os.path; print os.path.relpath('$workspaceFile', '$projectBaseDir')") + fauxpas -o json check $projectName --workspace $workspaceRelativePath --scheme $scheme > sonar-reports/$(basename $projectName .xcodeproj)-$scheme-fauxpas.json + fi + fi + + done - echo -n 'Running FauxPas...' - fauxpas -t $appScheme -o json check $projectName --workspace $workspaceFile --scheme $appScheme > sonar-reports/$(basename $projectName .xcodeproj)-fauxpas.json + done < tmpFileRunSonarSh + rm -rf tmpFileRunSonarSh - done < tmpFileRunSonarSh - rm -rf tmpFileRunSonarSh + fi else echo 'Skipping FauxPas (not installed)' From 5ab1dcf2b7f0eda0e929711a14e01ec89fe4efd8 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 22 Sep 2015 12:40:54 +0200 Subject: [PATCH 031/107] Fix for high "hits" value in coverage report --- .../plugins/objectivec/coverage/CoberturaXMLStreamHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java index 8a6b5575..18efc286 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java @@ -72,7 +72,7 @@ private void recordCoverageFor(final SMInputCursor line, final CoverageMeasuresBuilder builder) throws XMLStreamException { final int lineId = Integer.parseInt(line.getAttrValue("number")); final int noHits = (int) Math.min( - Long.parseLong(line.getAttrValue("hits")), Integer.MAX_VALUE); + Double.parseDouble(line.getAttrValue("hits")), Integer.MAX_VALUE); final String isBranch = line.getAttrValue("branch"); final String conditionText = line.getAttrValue("condition-coverage"); From 551ab25a2e1604cee9e489c4f26d921e11412399 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 22 Sep 2015 22:57:04 +0200 Subject: [PATCH 032/107] Added FauxPas 1.5 new rules to repository --- .../com/sonar/sqale/objectivec-model.xml | 18 ++++++++++ .../sonar/plugins/fauxpas/profile-fauxpas.xml | 16 +++++---- .../org/sonar/plugins/fauxpas/rules.json | 35 +++++++++++-------- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/main/resources/com/sonar/sqale/objectivec-model.xml b/src/main/resources/com/sonar/sqale/objectivec-model.xml index 29a7a2a3..c1199114 100644 --- a/src/main/resources/com/sonar/sqale/objectivec-model.xml +++ b/src/main/resources/com/sonar/sqale/objectivec-model.xml @@ -1417,6 +1417,24 @@ INSTRUCTION_RELIABILITY Instruction + + FauxPas + HardcodedSelfClass + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + FauxPas CategoryMethodConflict diff --git a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml index bdf9a5d6..4bdfbb69 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml +++ b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml @@ -23,6 +23,10 @@ FauxPas InitializeSuperInvocation + + FauxPas + HardcodedSelfClass + FauxPas MacroBasedIncludeGuard @@ -295,6 +299,10 @@ FauxPas LiteralStringKeyPath + + FauxPas + FixedFormatDateFormatter + FauxPas SuspiciousDateTimeFormat @@ -325,11 +333,11 @@ FauxPas - StrongInsteadOfRetain + NullCoalescingOp FauxPas - NullCoalescingOp + StrongInsteadOfRetain FauxPas @@ -407,10 +415,6 @@ FauxPas OrderedPointerToZeroComparison - - FauxPas - FixedFormatDateFormatter - FauxPas ZeroAssignmentToPointer diff --git a/src/main/resources/org/sonar/plugins/fauxpas/rules.json b/src/main/resources/org/sonar/plugins/fauxpas/rules.json index 291cb11e..5916ad7f 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/rules.json +++ b/src/main/resources/org/sonar/plugins/fauxpas/rules.json @@ -34,6 +34,13 @@ "description": "Implementations of +[NSObject initialize] should not invoke the superclass implementation: this method is special in that the runtime invokes it separately for every subclass.", "severity": "MAJOR" }, + { + "category": "BestPractice", + "key": "HardcodedSelfClass", + "name": "Hardcoded self class reference", + "description": "Warns if an Objective-C class (or an instance of a class) sends a message to its own class object using a hardcoded reference to the class, e.g. [FOOThing alloc].It is recommended to use self instead (e.g. [self alloc] or [[self class] alloc]) so that the concrete class would receive the message, and subclassing behavior would not be impeded.", + "severity": "MAJOR" + }, { "category": "BestPractice", "key": "MacroBasedIncludeGuard", @@ -360,7 +367,7 @@ "category": "Config", "key": "XcconfigOverwrites", "name": "Xcode build configuration file overwrites previously set value", - "description": "Overwriting a previously set value in a build configuration file is often a sign of a mistake somewhere.", + "description": "Overwriting a previously set value in a build configuration file is often a sign of a mistake somewhere.It is recommended to set custom intermediary settings in \u201Cparent\u201D configuration files and then compose final values for actual build settings in leaf configuration files.", "severity": "MINOR" }, { @@ -510,6 +517,13 @@ "description": "Warns when literal strings are used to specify key paths.Something like NSStringFromSelector(@selector(foo)) is safer because it makes the compiler aware of the selector being specified \u2014 this helps find typos at compile time and allows automatic refactoring tools to make appropriate changes.", "severity": "CRITICAL" }, + { + "category": "APIUsage", + "key": "FixedFormatDateFormatter", + "name": "Fixed-format NSDateFormatter not using invariant (POSIX) locale", + "description": "Warns when an NSDateFormatter is used with fixed-format dates without using the invariant en_US_POSIX locale. If any other locale is used, the date format string may be overwritten, depending on system date and time settings.When working with user-visible dates, date and time styles should be used instead of setting a date format.", + "severity": "CRITICAL" + }, { "category": "APIUsage", "key": "SuspiciousDateTimeFormat", @@ -561,16 +575,16 @@ }, { "category": "Style", - "key": "StrongInsteadOfRetain", - "name": "Usage of retain in ARC code", - "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", + "key": "NullCoalescingOp", + "name": "Null coalescing operator usage", + "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", "severity": "MAJOR" }, { "category": "Style", - "key": "NullCoalescingOp", - "name": "Null coalescing operator usage", - "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", + "key": "StrongInsteadOfRetain", + "name": "Usage of retain in ARC code", + "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", "severity": "MAJOR" }, { @@ -706,13 +720,6 @@ "description": "Warns whenever a pointer value is compared to zero using an ordered comparison operator.The Clang compiler should warn about similar comparisons to values other than zero.", "severity": "MINOR" }, - { - "category": "Miscellaneous", - "key": "FixedFormatDateFormatter", - "name": "Fixed-format NSDateFormatter not using invariant (POSIX) locale", - "description": "Warns when an NSDateFormatter is used with fixed-format dates without using the invariant en_US_POSIX locale. If any other locale is used, the date format string may be overwritten, depending on system date and time settings.When working with user-visible dates, date and time styles should be used instead of setting a date format.", - "severity": "MINOR" - }, { "category": "Miscellaneous", "key": "ZeroAssignmentToPointer", From 7c5b2a8dc463711d88f568df099a3f3ce34a0ce9 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 19 Oct 2015 18:32:55 +0200 Subject: [PATCH 033/107] Updated Faux Pas rules for 1.6 support --- .../com/sonar/sqale/objectivec-model.xml | 36 +++++++++++++++++++ .../sonar/plugins/fauxpas/profile-fauxpas.xml | 12 +++++-- .../org/sonar/plugins/fauxpas/rules.json | 28 +++++++++++---- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/main/resources/com/sonar/sqale/objectivec-model.xml b/src/main/resources/com/sonar/sqale/objectivec-model.xml index c1199114..0ded82ce 100644 --- a/src/main/resources/com/sonar/sqale/objectivec-model.xml +++ b/src/main/resources/com/sonar/sqale/objectivec-model.xml @@ -1417,6 +1417,42 @@ INSTRUCTION_RELIABILITY Instruction + + FauxPas + CompleteNotificationCenterDetachment + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + + + FauxPas + AssociatedObjectOnValueType + + remediationFunction + linear + + + remediationFactor + 10 + mn + + + offset + 0.0 + d + + FauxPas HardcodedSelfClass diff --git a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml index 4bdfbb69..67742775 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml +++ b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml @@ -35,6 +35,10 @@ FauxPas RetainingImmutableProperty + + FauxPas + CompleteNotificationCenterDetachment + FauxPas OldVerboseObjCSyntax @@ -71,6 +75,10 @@ FauxPas InstanceMethodWritesToStaticVariable + + FauxPas + AssociatedObjectOnValueType + FauxPas PrefixHeaderIncludeSuggestion @@ -333,11 +341,11 @@ FauxPas - NullCoalescingOp + StrongInsteadOfRetain FauxPas - StrongInsteadOfRetain + NullCoalescingOp FauxPas diff --git a/src/main/resources/org/sonar/plugins/fauxpas/rules.json b/src/main/resources/org/sonar/plugins/fauxpas/rules.json index 5916ad7f..e67aea38 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/rules.json +++ b/src/main/resources/org/sonar/plugins/fauxpas/rules.json @@ -55,6 +55,13 @@ "description": "Warns if \u201Cretaining\u201D semantics are specified in a @property declaration for a common immutable NSCopying class type that also has a mutable subclass variant (for example NSString).This rule helps avoid situations where the value of a property could change without the setter being invoked (i.e. the object gets mutated).", "severity": "MAJOR" }, + { + "category": "BestPractice", + "key": "CompleteNotificationCenterDetachment", + "name": "Complete NSNotificationCenter detachment", + "description": "Warns when an object removes itself as an observer to all notifications (by invoking -[NSNotificationCenter removeObserver:] with self).This can be a problem if a superclass or a subclass wants to keep observing some notifications. The recommended way to handle this is to remove self as an observer only for the specific notifications that you have previously begun observing.This rule does not warn about such removals occuring in -dealloc.", + "severity": "MAJOR" + }, { "category": "BestPractice", "key": "OldVerboseObjCSyntax", @@ -118,6 +125,13 @@ "description": "Writing to static variables (which is essentially global state) in instance methods is generally considered bad practice, because it easily leads to undesireable behavior when multiple instances are being manipulated.Warnings are suppressed for assignments occurring inside dispatch_once() blocks.", "severity": "MAJOR" }, + { + "category": "BestPractice", + "key": "AssociatedObjectOnValueType", + "name": "Associated object on value-like type", + "description": "Warns when associated objects are attached to value-like types such as NSNumber or UIFont.Instances of these classes may be shared and/or immortal due to de-duplication and tagged pointers.", + "severity": "MAJOR" + }, { "category": "BestPractice", "key": "PrefixHeaderIncludeSuggestion", @@ -542,7 +556,7 @@ "category": "APIUsage", "key": "MissingNotificationCenterDetachment", "name": "Missing NSNotificationCenter observer detachment", - "description": "You must invoke -[NSNotificationCenter removeObserver:] or -[NSNotificationCenter removeObserver:name:object:] before the observer object is deallocated.This rule only considers cases where the observer reference is self, or an instance variable or an Objective-C property in self.", + "description": "You must invoke -[NSNotificationCenter removeObserver:] or -[NSNotificationCenter removeObserver:name:object:] before the observer object is deallocated.This rule only considers cases where the observer reference is self, or an instance variable or an Objective-C property in self.This only applies to iOS 8 (and earlier) and OS X 10.10 (and earlier).", "severity": "CRITICAL" }, { @@ -575,16 +589,16 @@ }, { "category": "Style", - "key": "NullCoalescingOp", - "name": "Null coalescing operator usage", - "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", + "key": "StrongInsteadOfRetain", + "name": "Usage of retain in ARC code", + "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", "severity": "MAJOR" }, { "category": "Style", - "key": "StrongInsteadOfRetain", - "name": "Usage of retain in ARC code", - "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", + "key": "NullCoalescingOp", + "name": "Null coalescing operator usage", + "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", "severity": "MAJOR" }, { From 6772f61c15f43977e8564e58efd337ecf06598b6 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 12 Nov 2015 12:55:10 +0100 Subject: [PATCH 034/107] Refactored SQALE model --- .../com/sonar/sqale/objectivec-model.xml | 1833 +++++------------ 1 file changed, 499 insertions(+), 1334 deletions(-) diff --git a/src/main/resources/com/sonar/sqale/objectivec-model.xml b/src/main/resources/com/sonar/sqale/objectivec-model.xml index 0ded82ce..690122a8 100644 --- a/src/main/resources/com/sonar/sqale/objectivec-model.xml +++ b/src/main/resources/com/sonar/sqale/objectivec-model.xml @@ -50,17 +50,12 @@ StringsFileEncoding remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min
@@ -68,17 +63,12 @@ FileRefOutsideVCS remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -86,17 +76,12 @@ FileRefIgnoredInVCS remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min
@@ -120,17 +105,12 @@ HardcodedUIString remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min
@@ -138,17 +118,12 @@ FixedFormatDateFormatter remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min
@@ -194,17 +169,12 @@ SuspiciousDateTimeFormat remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min
@@ -212,17 +182,12 @@ RecommendedVCSIgnores remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -230,17 +195,12 @@ UnidiomaticAccessorNaming remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -248,17 +208,12 @@ IBOutletsInPublicInterface remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -266,17 +221,12 @@ PrivateCategory remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -284,17 +234,12 @@ PrefixHeaderIncludeSuggestion remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -302,17 +247,12 @@ UnusedErrorValue remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -320,17 +260,12 @@ ArgumentModification remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -338,17 +273,12 @@ MacroBasedIncludeGuard remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -356,17 +286,12 @@ unused method parameter remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -374,17 +299,12 @@ unused local variable remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -392,17 +312,12 @@ replace with number literal remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -410,17 +325,12 @@ replace with boxed expression remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -428,17 +338,12 @@ unnecessary else statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -446,17 +351,12 @@ redundant local variable remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -464,17 +364,12 @@ redundant if statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -482,17 +377,12 @@ redundant conditional operator remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -500,17 +390,12 @@ replace with container literal remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -518,17 +403,12 @@ long line remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -536,17 +416,12 @@ long method remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -554,17 +429,12 @@ for loop should be while loop remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -572,71 +442,51 @@ too many methods remediationFunction - linear + CONSTANT_ISSUE - remediationFactor + offset 1 h - - offset - 0.0 - d - OCLint long class remediationFunction - linear + CONSTANT_ISSUE - remediationFactor + offset 1 h - - offset - 0.0 - d - OCLint too many fields remediationFunction - linear + CONSTANT_ISSUE - remediationFactor + offset 1 h - - offset - 0.0 - d - OCLint collapsible if statements remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -644,17 +494,12 @@ deep nested block remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -662,17 +507,12 @@ high cyclomatic complexity remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -680,17 +520,12 @@ too few branches in switch statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -698,17 +533,12 @@ non case label in switch statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -716,17 +546,12 @@ short variable name remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -734,17 +559,12 @@ long variable name remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -752,17 +572,12 @@ default label not last in switch statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -770,17 +585,12 @@ OldVerboseObjCSyntax remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -788,17 +598,12 @@ replace with object subscripting remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min
@@ -810,17 +615,12 @@ MissingAPIUsageDescription remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min
@@ -828,17 +628,12 @@ useless parentheses remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -846,17 +641,12 @@ use early exits and continue remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -864,17 +654,12 @@ high npath complexity remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -882,17 +667,12 @@ inverted logic remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -900,17 +680,12 @@ multiple unary operator remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -918,17 +693,12 @@ redundant nil check remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -936,17 +706,12 @@ high ncss method remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -954,17 +719,12 @@ dead code remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -972,17 +732,12 @@ double negative remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -990,17 +745,12 @@ bitwise operator in conditional remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1008,17 +758,12 @@ empty if statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1026,17 +771,12 @@ empty while statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1044,17 +784,12 @@ empty else block remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1062,17 +797,12 @@ empty for statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1080,17 +810,12 @@ empty do/while statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1098,17 +823,12 @@ empty switch statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1116,17 +836,12 @@ too many parameters remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -1134,17 +849,12 @@ switch statements don't need default when fully covered remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min
@@ -1180,17 +890,12 @@ StrongDelegate remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1198,17 +903,12 @@ LoadMethodWithoutAutoreleasePool remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1216,17 +916,12 @@ UndetachedDelegate remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1234,17 +929,12 @@ MallocWithoutSizeof remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1252,17 +942,12 @@ BlockAPIRetainCycle remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1274,17 +959,12 @@ NSLogUsed remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1324,17 +1004,12 @@ empty finally statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1342,17 +1017,12 @@ return from finally block remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1360,17 +1030,12 @@ empty catch statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1378,17 +1043,12 @@ empty try statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1396,17 +1056,12 @@ throw exception from finally block remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1422,17 +1077,12 @@ CompleteNotificationCenterDetachment remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1440,17 +1090,12 @@ AssociatedObjectOnValueType remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1458,17 +1103,12 @@ HardcodedSelfClass remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1476,17 +1116,12 @@ CategoryMethodConflict remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1494,17 +1129,12 @@ ZeroAssignmentToPointer remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1512,17 +1142,12 @@ OrderedPointerToZeroComparison remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1530,17 +1155,12 @@ UnnecessaryNibMethod remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1548,17 +1168,12 @@ UnsupportedWeakReference remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1566,17 +1181,12 @@ UsedVariableMarkedUnused remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1584,17 +1194,12 @@ ReservedPrefix remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1602,17 +1207,12 @@ MallocCast remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1620,17 +1220,12 @@ ReservedIdentifierNaming remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1638,17 +1233,12 @@ UnusedMethod remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1656,17 +1246,12 @@ UnnecessaryNullCheck remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1674,17 +1259,12 @@ MacroLiteral remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1692,17 +1272,12 @@ IdentifierNaming remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1710,17 +1285,12 @@ DotSyntax remediationFunction - linear + CONSTANT_ISSUE - remediationFactor + offset 10 - mn - - - offset - 0.0 - d + min @@ -1728,17 +1298,12 @@ UnprefixedClass remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1746,17 +1311,12 @@ NonTypedefBlockDeclaration remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1764,17 +1324,12 @@ NewInitializer remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1782,17 +1337,12 @@ ThrowingObjCException remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -1800,17 +1350,12 @@ OrderedComparisonOpDirection remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1818,17 +1363,12 @@ StrongInsteadOfRetain remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1836,17 +1376,12 @@ NullCoalescingOp remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1854,17 +1389,12 @@ MissingNotificationCenterDetachment remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1872,17 +1402,12 @@ LiteralStringKeyPath remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1890,17 +1415,12 @@ IsEqualAndHash remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1908,17 +1428,12 @@ RestrictedMethodOverride remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1926,17 +1441,12 @@ RestrictedDirectMethodCall remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1944,17 +1454,12 @@ UIKitKVO remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1962,17 +1467,12 @@ DylibInstallName remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1980,17 +1480,12 @@ ReleaseBuildConfig remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -1998,17 +1493,12 @@ BuildSettingPlacement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2016,17 +1506,12 @@ BuildSettingSelfReference remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2034,17 +1519,12 @@ XcconfigOverwrites remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2052,17 +1532,12 @@ ReleaseBuildCompilerArgs remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2070,17 +1545,12 @@ ImplicitBundleId remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2088,17 +1558,12 @@ BasicProjectSettings remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2106,17 +1571,12 @@ CompilerWarnings remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2124,17 +1584,12 @@ BuildSettingsSetInGUI remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2142,17 +1597,12 @@ ViewControllerInitWithNibName remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -2160,17 +1610,12 @@ UnprefixedCategoryMethod remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2178,17 +1623,12 @@ RedundantInclude remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2196,17 +1636,12 @@ ErrorConditionCheck remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2214,17 +1649,12 @@ DiscardedOpaqueNotificationObserver remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2232,17 +1662,12 @@ Swizzling remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -2250,17 +1675,12 @@ AssigningDelegate remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2268,17 +1688,12 @@ ImplicitAtomicProperty remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2286,17 +1701,12 @@ InitializeMethodCategoryOverride remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2304,17 +1714,12 @@ TerminatingApp remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2322,17 +1727,12 @@ InstanceMethodWritesToStaticVariable remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -2340,17 +1740,12 @@ FastEnumElementOutside remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2358,17 +1753,12 @@ SetterInvocationInInitOrDealloc remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2376,17 +1766,12 @@ CopyingMutableProperty remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2394,17 +1779,12 @@ ConstructorReturnType remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2412,17 +1792,12 @@ RetainingImmutableProperty remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2430,17 +1805,12 @@ InitializeSuperInvocation remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2448,17 +1818,12 @@ AssertionSideEffects remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2466,17 +1831,12 @@ must override hash with isEqual remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2484,17 +1844,12 @@ parameter reassignment remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -2502,17 +1857,12 @@ ivar assignment outside accessors or init remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2524,17 +1874,12 @@ missing break in switch statement remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2542,17 +1887,12 @@ avoid branching statement as last in loop remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -2560,17 +1900,12 @@ switch statements should have default remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2578,17 +1913,12 @@ jumbled incrementer remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2596,17 +1926,12 @@ broken null check remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2614,17 +1939,12 @@ broken nil check remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2632,17 +1952,12 @@ broken oddness check remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2650,35 +1965,25 @@ misplaced nil check remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min OCLint misplaced null check - - remediationFunction - linear - - - remediationFactor - 10 - mn + + remediationFunction + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2686,17 +1991,12 @@ goto statement remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -2704,17 +2004,12 @@ goto statement remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -2722,17 +2017,12 @@ constant if expression remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2740,17 +2030,12 @@ constant conditional operator remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2758,17 +2043,12 @@ DefaultInExhaustiveSwitch remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2780,17 +2060,12 @@ UncommentedLocalizedString remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2798,17 +2073,12 @@ TranslationFormatMismatch remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2816,17 +2086,12 @@ TranslationPunctuation remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2834,17 +2099,12 @@ DuplicateTranslation remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2852,17 +2112,12 @@ MissingTranslation remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2870,17 +2125,12 @@ StringsdictWithoutStrings remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2888,17 +2138,12 @@ UnusedTranslation remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2906,17 +2151,12 @@ InvalidStringsFile remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -2924,17 +2164,12 @@ RetinaImagesResolution remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2942,17 +2177,12 @@ SuspiciousResources remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -2960,17 +2190,12 @@ UnknownResourceCodeReference remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2978,17 +2203,12 @@ UnusedResource remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -2996,17 +2216,12 @@ XIBUnknownClassReference remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -3014,17 +2229,12 @@ XIBRuntimeAttributeMismatch remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -3032,17 +2242,12 @@ MissingDeviceTypeResource remediationFunction - linear - - - remediationFactor - 30 - mn + CONSTANT_ISSUE offset - 0.0 - d + 30 + min @@ -3050,17 +2255,12 @@ MissingImageResolutionVariant remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -3068,17 +2268,12 @@ WeakReferenceToTopLevelXIBObject remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -3086,17 +2281,12 @@ UnknownResourceXIBReference remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -3104,17 +2294,12 @@ GlobalAndLocalizedResource remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -3122,17 +2307,12 @@ UnknownResourceModifier remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -3140,17 +2320,12 @@ SuspiciousMissingResources remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -3158,17 +2333,12 @@ DuplicateResource remediationFunction - linear - - - remediationFactor - 10 - mn + CONSTANT_ISSUE offset - 0.0 - d + 10 + min @@ -3180,18 +2350,13 @@ ThreadUnsafeInstanceCaching remediationFunction - linear + CONSTANT_ISSUE - remediationFactor + offset 1 h - - offset - 0.0 - d - From cc45ea77971c59c519f23d47d6f777329f3cf1b5 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 12 Nov 2015 13:13:30 +0100 Subject: [PATCH 035/107] Build script fix --- build-and-deploy.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build-and-deploy.sh b/build-and-deploy.sh index f2e7c47b..9a2a58fb 100755 --- a/build-and-deploy.sh +++ b/build-and-deploy.sh @@ -19,6 +19,7 @@ fi cp target/*.jar $SONARQUBE_HOME/extensions/plugins # Stop/start Sonar +unset GEM_PATH GEM_HOME $SONARQUBE_HOME/bin/macosx-universal-64/sonar.sh stop $SONARQUBE_HOME/bin/macosx-universal-64/sonar.sh start From f9b08f021353d834a98b45be665c35836e115497 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 12 Nov 2015 13:16:36 +0100 Subject: [PATCH 036/107] Version bumped to 0.4.0.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7180cce3..5b8a964e 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec sonar-objective-c-plugin - 0.4.0.1 + 0.4.0.2 sonar-plugin From 481ba0c59d657f493a04aea029f86ca56b5931cb Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 12 Nov 2015 13:24:58 +0100 Subject: [PATCH 037/107] README update --- README.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a2939472..6cc555d8 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,17 @@ A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://g ###Features -- [ ] Complexity -- [ ] Design -- [x] Documentation -- [x] Duplications -- [x] Issues (OCLint: 63 rules, Faux Pas: 102 rules) -- [x] Size -- [x] Tests +| Feature | Supported | Details | +|---------------|----------|:-----------:| +| Complexity |NO | | +| Design |NO | | +| Documentation |YES | | +| Duplications |YES | | +| Issues |YES | Uses [OCLint](http://docs.oclint.org/en/dev/intro/installation.html): 63 rules, and [Faux Pas](http://fauxpasapp.com/): 102 rules| +| Size |YES | | +| Tests |YES | Uses [xctool](https://github.com/facebook/xctool), probably to xcodebuild + xcpretty [xcpretty](https://github.com/supermarin/xcpretty) soon | +| Code coverage |YES | With [gcovr](http://gcovr.com), probably [slather](https://github.com/venmo/slather) soon| + ###Compatibility @@ -46,6 +50,10 @@ Binary packages are available in the release section. ###Release history +####0.4.0.2 (based on 0.4.0) +- Faux Pas support for release 1.5 and 1.6 +- Support for multiple projects in a same workspace + ####0.4.0.1 (based on 0.4.0) - Faux Pas support - Moved OCLint long line threshold to 250 From 2737db61da6c6fb1f3b64c72a19a6df6cf7b0161 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 12 Nov 2015 13:57:42 +0100 Subject: [PATCH 038/107] README fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6cc555d8..a9b441ac 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://g | Duplications |YES | | | Issues |YES | Uses [OCLint](http://docs.oclint.org/en/dev/intro/installation.html): 63 rules, and [Faux Pas](http://fauxpasapp.com/): 102 rules| | Size |YES | | -| Tests |YES | Uses [xctool](https://github.com/facebook/xctool), probably to xcodebuild + xcpretty [xcpretty](https://github.com/supermarin/xcpretty) soon | +| Tests |YES | Uses [xctool](https://github.com/facebook/xctool), will probably switch to xcodebuild + [xcpretty](https://github.com/supermarin/xcpretty) soon | | Code coverage |YES | With [gcovr](http://gcovr.com), probably [slather](https://github.com/venmo/slather) soon| From 4c5480a166deb6f99f4b00c0718d44831ef9b48a Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 15 Dec 2015 18:30:21 +0100 Subject: [PATCH 039/107] profdata support --- README.md | 28 +++++++++- sample/sonar-project.properties | 10 ++++ src/main/shell/run-sonar.sh | 98 +++++++++++++++++++++++---------- 3 files changed, 104 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 7a329fa0..d2d3811c 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,33 @@ Binary packages will be available soon... - 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 ([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```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. -- [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 +- [xcpretty](https://github.com/supermarin/xcpretty) (```gem install xcpretty```) +- [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.10.1 recommended. +- [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) +- [slather](https://github.com/venmo/slather) with profdata support (see instructions below) for Xcode 7 and above. - [Faux Pas](http://fauxpasapp.com/) command line tools installed (optional) +###Installation of slather with profdata support + +At the time, slather does not support profdata. A special version of slather needs t be installed. + +To install slather with profdata support, follow those steps : + + git clone https://github.com/mattdelves/slather.git + cd slather + git checkout feature-profdata + gem build slather.gemspec + gem install --both slather-1.8.1.gem + + +###Code coverage data format + +Since Xcode 7, Apple changed its coverage data format to a new format called 'profdata'. +By default this format will be used by the plugin, except if you explicitly force it to legacy mode (for Xcode 6 and below) in your *sonar-project.properties* with this line: + + sonar.objectivec.coverageType=legacy + + ###Installation (once for all your Objective-C projects) - Download the plugin binary into the $SONARQUBE_HOME/extensions/plugins directory - Copy [run-sonar.sh](https://rawgithub.com/Backelite/sonar-objective-c/master/src/main/shell/run-sonar.sh) somewhere in your PATH diff --git a/sample/sonar-project.properties b/sample/sonar-project.properties index 5cdfde10..b72ae5a0 100644 --- a/sample/sonar-project.properties +++ b/sample/sonar-project.properties @@ -15,6 +15,16 @@ sonar.sources=srcDir1,srcDir2 # Path to test directories (comment if no test) sonar.tests=testSrcDir +# Coverage type to expect from project +# can be 'legacy' (pre Xcode 7) or 'profdata' +# If not set : defaults to profdata +#sonar.objectivec.coverageType=profdata + +# Destination Simulator to run tests +# As string expected in destination argument of xcodebuild command +# Example = sonar.swift.simulator=platform=iOS Simulator,name=iPhone 6,OS=9.2 +sonar.objectivec.simulator=platform=iOS Simulator,name=iPhone 6,OS=9.2 + # Xcode project configuration (.xcodeproj or .xcworkspace) # -> If you have a project: configure only sonar.objectivec.project diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index a8d47ce6..4ba715a5 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -5,6 +5,10 @@ ## WARNING: edit your project parameters in sonar-project.properties rather than modifying this script # +# Global parameters +SLATHER_CMD=slather +XCPRETTY_CMD=xcpretty + trap "echo 'Script interrupted by Ctrl+C'; stopProgress; exit 1" SIGHUP SIGINT SIGTERM function startProgress() { @@ -175,6 +179,11 @@ appScheme=''; readParameter appScheme 'sonar.objectivec.appScheme' testScheme=''; readParameter testScheme 'sonar.objectivec.testScheme' # The file patterns to exclude from coverage report excludedPathsFromCoverage=''; readParameter excludedPathsFromCoverage 'sonar.objectivec.excludedPathsFromCoverage' +# Read coverage type +coverageType=''; readParameter coverageType 'sonar.objectivec.coverageType' +# Read destination simulator +destinationSimulator=''; readParameter destinationSimulator 'sonar.objectivec.simulator' + # Check for mandatory parameters if [ -z "$projectFile" -o "$projectFile" = " " ]; then @@ -194,6 +203,10 @@ if [ -z "$appScheme" -o "$appScheme" = " " ]; then echo >&2 "ERROR - sonar.objectivec.appScheme parameter is missing in sonar-project.properties. You must specify which scheme is used to build your application." exit 1 fi +if [ -z "$destinationSimulator" -o "$destinationSimulator" = " " ]; then + echo >&2 "ERROR - sonar.objectivec.simulator parameter is missing in sonar-project.properties. You must specify which simulator to use." + exit 1 +fi if [ "$vflag" = "on" ]; then echo "Xcode workspace file is: $workspaceFile" @@ -219,11 +232,6 @@ fi rm -rf sonar-reports mkdir sonar-reports -# Extracting project information needed later -echo -n 'Extracting Xcode project information' -runCommand /dev/stdout $xctoolCmdPrefix -scheme "$appScheme" clean -runCommand /dev/stdout $xctoolCmdPrefix -scheme "$appScheme" -reporter plain -reporter json-compilation-database:compile_commands.json build - # Unit tests and coverage if [ "$testScheme" = "" ]; then echo 'Skipping tests as no test scheme has been provided!' @@ -233,35 +241,67 @@ if [ "$testScheme" = "" ]; then echo "" > sonar-reports/coverage.xml else - echo -n 'Running tests using xctool' - # Not using runCommand function because xctool may return 1, even if everything is fine (maybe a xctool bug ?) - #runCommand /dev/null $xctoolCmdPrefix -scheme "$testScheme" GCC_PRECOMPILE_PREFIX_HEADER=NO GCC_GENERATE_TEST_COVERAGE_FILES=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES -reporter junit:sonar-reports/TEST-report.xml -reporter plain clean test - $xctoolCmdPrefix -scheme "$testScheme" GCC_PRECOMPILE_PREFIX_HEADER=NO GCC_GENERATE_TEST_COVERAGE_FILES=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES -reporter junit:sonar-reports/TEST-report.xml -reporter plain clean test - + echo -n 'Running tests' + runCommand /dev/stdout xcodebuild clean -workspace $workspaceFile -scheme $appScheme + buildCmd=(xcodebuild test -workspace $workspaceFile -scheme $appScheme -sdk iphonesimulator -configuration Debug -enableCodeCoverage YES) + if [[ ! -z "$destinationSimulator" ]]; then + buildCmd+=(-destination "$destinationSimulator" -destination-timeout 60) + fi + runCommand sonar-reports/xcodebuild.log "${buildCmd[@]}" + cat sonar-reports/xcodebuild.log | $XCPRETTY_CMD -t --report junit + mv build/reports/junit.xml sonar-reports/TEST-report.xml echo -n 'Computing coverage report' - # Build the --exclude flags - excludedCommandLineFlags="" - if [ ! -z "$excludedPathsFromCoverage" -a "$excludedPathsFromCoverage" != " " ]; then - echo $excludedPathsFromCoverage | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh2 - while read word; do - excludedCommandLineFlags+=" --exclude $word" - done < tmpFileRunSonarSh2 - rm -rf tmpFileRunSonarSh2 - fi - if [ "$vflag" = "on" ]; then - echo "Command line exclusion flags for gcovr is:$excludedCommandLineFlags" - fi + if [ "$coverageType" = "profdata" -o "$coverageType" = "" ]; then + + # profdata = use slather + + echo 'Using profdata' + + # Build the --exclude flags + excludedCommandLineFlags="" + if [ ! -z "$excludedPathsFromCoverage" -a "$excludedPathsFromCoverage" != " " ]; then + echo $excludedPathsFromCoverage | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh2 + while read word; do + excludedCommandLineFlags+=" -i $word" + done < tmpFileRunSonarSh2 + rm -rf tmpFileRunSonarSh2 + fi + if [ "$vflag" = "on" ]; then + echo "Command line exclusion flags for slather is:$excludedCommandLineFlags" + fi + + runCommand /dev/stdout $SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports --scheme $appScheme $projectFile + mv sonar-reports/cobertura.xml sonar-reports/coverage.xml + + else + + # Legacy mode = use gcovr - # Create symlink on the build directory to enable its access from the workspace - coverageFilesPath=$(grep 'command' compile_commands.json | sed 's#^.*-o \\/#\/#;s#",##' | grep "${projectName%%.*}.build" | awk 'NR<2' | sed 's/\\\//\//g' | sed 's/\\\\//g' | xargs -0 dirname) - splitIndex=$(awk -v a="$coverageFilesPath" -v b="/Intermediates" 'BEGIN{print index(a,b)}') - coverageFilesPath=$(echo ${coverageFilesPath:0:$splitIndex}Intermediates) - ln -s $coverageFilesPath sonar-reports/build + # Build the --exclude flags + excludedCommandLineFlags="" + if [ ! -z "$excludedPathsFromCoverage" -a "$excludedPathsFromCoverage" != " " ]; then + echo $excludedPathsFromCoverage | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh2 + while read word; do + excludedCommandLineFlags+=" --exclude $word" + done < tmpFileRunSonarSh2 + rm -rf tmpFileRunSonarSh2 + fi + if [ "$vflag" = "on" ]; then + echo "Command line exclusion flags for gcovr is:$excludedCommandLineFlags" + fi - # Run gcovr with the right options - runCommand "sonar-reports/coverage.xml" gcovr -r . $excludedCommandLineFlags --xml + # Create symlink on the build directory to enable its access from the workspace + coverageFilesPath=$(grep 'command' compile_commands.json | sed 's#^.*-o \\/#\/#;s#",##' | grep "${projectName%%.*}.build" | awk 'NR<2' | sed 's/\\\//\//g' | sed 's/\\\\//g' | xargs -0 dirname) + splitIndex=$(awk -v a="$coverageFilesPath" -v b="/Intermediates" 'BEGIN{print index(a,b)}') + coverageFilesPath=$(echo ${coverageFilesPath:0:$splitIndex}Intermediates) + ln -s $coverageFilesPath sonar-reports/build + + # Run gcovr with the right options + runCommand "sonar-reports/coverage.xml" gcovr -r . $excludedCommandLineFlags --xml + + fi fi From 8001e89330b6115dd7d129080b5b1d612ef6cd4a Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 16 Dec 2015 11:33:46 +0100 Subject: [PATCH 040/107] Fixed legacy coverage xcodebuild command support --- src/main/shell/run-sonar.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 4ba715a5..91551ce4 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -243,7 +243,15 @@ else echo -n 'Running tests' runCommand /dev/stdout xcodebuild clean -workspace $workspaceFile -scheme $appScheme - buildCmd=(xcodebuild test -workspace $workspaceFile -scheme $appScheme -sdk iphonesimulator -configuration Debug -enableCodeCoverage YES) + + if [ "$coverageType" = "profdata" -o "$coverageType" = "" ]; then + # profdata + buildCmd=(xcodebuild test -workspace $workspaceFile -scheme $appScheme -sdk iphonesimulator -configuration Debug -enableCodeCoverage YES) + else + # Legacy coverage + buildCmd=(xcodebuild test -workspace $workspaceFile -scheme $appScheme -sdk iphonesimulator -configuration Debug) + fi + if [[ ! -z "$destinationSimulator" ]]; then buildCmd+=(-destination "$destinationSimulator" -destination-timeout 60) fi From 292b1291c6543b6716643321943ebff754653af1 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 16 Dec 2015 11:52:43 +0100 Subject: [PATCH 041/107] Added back xctools to required (mandatory for OCLint) --- README.md | 1 + src/main/shell/run-sonar.sh | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d2d3811c..f5a08222 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Binary packages will be available soon... - 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 ([HomeBrew](http://brew.sh) installed and ```brew install sonar-runner```) - [xcpretty](https://github.com/supermarin/xcpretty) (```gem install xcpretty```) +- [xctool](https://github.com/facebook/xctool) ([HomeBrew](http://brew.sh) installed and ```brew install xctool```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. - [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.10.1 recommended. - [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) - [slather](https://github.com/venmo/slather) with profdata support (see instructions below) for Xcode 7 and above. diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 91551ce4..e1212d6c 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -6,6 +6,7 @@ # # Global parameters +XCTOOL_CMD=xctool SLATHER_CMD=slather XCPRETTY_CMD=xcpretty @@ -159,9 +160,9 @@ fi workspaceFile=''; readParameter workspaceFile 'sonar.objectivec.workspace' projectFile=''; readParameter projectFile 'sonar.objectivec.project' if [[ "$workspaceFile" != "" ]] ; then - xctoolCmdPrefix="xctool -workspace $workspaceFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" + xctoolCmdPrefix="$XCTOOL_CMD -workspace $workspaceFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" else - xctoolCmdPrefix="xctool -project $projectFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" + xctoolCmdPrefix="$XCTOOL_CMD -project $projectFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" fi # Count projects @@ -232,6 +233,11 @@ fi rm -rf sonar-reports mkdir sonar-reports +# Extracting project information needed later +echo -n 'Extracting Xcode project information' +runCommand /dev/stdout $xctoolCmdPrefix -scheme "$appScheme" clean +runCommand /dev/stdout $xctoolCmdPrefix -scheme "$appScheme" -reporter plain -reporter json-compilation-database:compile_commands.json build + # Unit tests and coverage if [ "$testScheme" = "" ]; then echo 'Skipping tests as no test scheme has been provided!' From 780e65c04cf7d38546e16899e48e9c91deddbd51 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 12 Jan 2016 16:17:18 +0100 Subject: [PATCH 042/107] Version and README update --- README.md | 5 ++++- pom.xml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dd1a4d33..2fa72360 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://g | Issues |YES | Uses [OCLint](http://docs.oclint.org/en/dev/intro/installation.html): 63 rules, and [Faux Pas](http://fauxpasapp.com/): 102 rules| | Size |YES | | | Tests |YES | Uses [xctool](https://github.com/facebook/xctool), will probably switch to xcodebuild + [xcpretty](https://github.com/supermarin/xcpretty) soon | -| Code coverage |YES | With [gcovr](http://gcovr.com), probably [slather](https://github.com/venmo/slather) soon| +| Code coverage |YES | With [gcovr](http://gcovr.com) for project before Xcode 7, otherwise [slather](https://github.com/venmo/slather)| ###Compatibility @@ -50,6 +50,9 @@ Binary packages are available in the release section. ###Release history +####0.4.0.3 (based on 0.4.0) +- Xcode 7 coverage support (profdata) + ####0.4.0.2 (based on 0.4.0) - Faux Pas support for release 1.5 and 1.6 - Support for multiple projects in a same workspace diff --git a/pom.xml b/pom.xml index 5b8a964e..5c72f5d3 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec sonar-objective-c-plugin - 0.4.0.2 + 0.4.0.3 sonar-plugin From 8a5719bb828831696add888986a61a012d0ed33c Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 25 Jan 2016 18:15:22 +0100 Subject: [PATCH 043/107] run-sonar.sh fixes - Avoid test timeout - watchOS support --- src/main/shell/run-sonar.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index e1212d6c..71e96a46 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -252,14 +252,14 @@ else if [ "$coverageType" = "profdata" -o "$coverageType" = "" ]; then # profdata - buildCmd=(xcodebuild test -workspace $workspaceFile -scheme $appScheme -sdk iphonesimulator -configuration Debug -enableCodeCoverage YES) + buildCmd=(xcodebuild build test -workspace $workspaceFile -scheme $appScheme -configuration Debug -enableCodeCoverage YES) else # Legacy coverage - buildCmd=(xcodebuild test -workspace $workspaceFile -scheme $appScheme -sdk iphonesimulator -configuration Debug) + buildCmd=(xcodebuild build test -workspace $workspaceFile -scheme $appScheme -configuration Debug) fi if [[ ! -z "$destinationSimulator" ]]; then - buildCmd+=(-destination "$destinationSimulator" -destination-timeout 60) + buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) fi runCommand sonar-reports/xcodebuild.log "${buildCmd[@]}" cat sonar-reports/xcodebuild.log | $XCPRETTY_CMD -t --report junit From f032f091cbb519d96505eec3cacfa61f042e39e7 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 25 Jan 2016 20:52:00 +0100 Subject: [PATCH 044/107] Updated run script --- src/main/shell/run-sonar.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 71e96a46..7c3180dd 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -160,9 +160,9 @@ fi workspaceFile=''; readParameter workspaceFile 'sonar.objectivec.workspace' projectFile=''; readParameter projectFile 'sonar.objectivec.project' if [[ "$workspaceFile" != "" ]] ; then - xctoolCmdPrefix="$XCTOOL_CMD -workspace $workspaceFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" + xctoolCmdPrefix="$XCTOOL_CMD -workspace $workspaceFile ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" else - xctoolCmdPrefix="$XCTOOL_CMD -project $projectFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" + xctoolCmdPrefix="$XCTOOL_CMD -project $projectFile ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" fi # Count projects From f52392b218f4379f4f5828d9f2eac28b911c5b85 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 25 Jan 2016 21:01:09 +0100 Subject: [PATCH 045/107] Script fix --- src/main/shell/run-sonar.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 7c3180dd..bcbb7cca 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -160,9 +160,9 @@ fi workspaceFile=''; readParameter workspaceFile 'sonar.objectivec.workspace' projectFile=''; readParameter projectFile 'sonar.objectivec.project' if [[ "$workspaceFile" != "" ]] ; then - xctoolCmdPrefix="$XCTOOL_CMD -workspace $workspaceFile ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" + xctoolCmdPrefix="$XCTOOL_CMD -workspace $workspaceFile" else - xctoolCmdPrefix="$XCTOOL_CMD -project $projectFile ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" + xctoolCmdPrefix="$XCTOOL_CMD -project $projectFile" fi # Count projects From 110ad3992b921870da5ffa20ae19e8fb288108b7 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 25 Jan 2016 21:26:23 +0100 Subject: [PATCH 046/107] Run script fix (again) --- src/main/shell/run-sonar.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index bcbb7cca..71e96a46 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -160,9 +160,9 @@ fi workspaceFile=''; readParameter workspaceFile 'sonar.objectivec.workspace' projectFile=''; readParameter projectFile 'sonar.objectivec.project' if [[ "$workspaceFile" != "" ]] ; then - xctoolCmdPrefix="$XCTOOL_CMD -workspace $workspaceFile" + xctoolCmdPrefix="$XCTOOL_CMD -workspace $workspaceFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" else - xctoolCmdPrefix="$XCTOOL_CMD -project $projectFile" + xctoolCmdPrefix="$XCTOOL_CMD -project $projectFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" fi # Count projects From b81c74a5d610786419166ee1936c086d815f5f34 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 26 Jan 2016 11:20:02 +0100 Subject: [PATCH 047/107] Run script optim --- src/main/shell/run-sonar.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 71e96a46..add1edd2 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -248,14 +248,13 @@ if [ "$testScheme" = "" ]; then else echo -n 'Running tests' - runCommand /dev/stdout xcodebuild clean -workspace $workspaceFile -scheme $appScheme if [ "$coverageType" = "profdata" -o "$coverageType" = "" ]; then # profdata - buildCmd=(xcodebuild build test -workspace $workspaceFile -scheme $appScheme -configuration Debug -enableCodeCoverage YES) + buildCmd=(xcodebuild clean build test -workspace $workspaceFile -scheme $appScheme -configuration Debug -enableCodeCoverage YES) else # Legacy coverage - buildCmd=(xcodebuild build test -workspace $workspaceFile -scheme $appScheme -configuration Debug) + buildCmd=(xcodebuild clean build test -workspace $workspaceFile -scheme $appScheme -configuration Debug) fi if [[ ! -z "$destinationSimulator" ]]; then From 3ad174d686702e20842d632e9eb3a582588268db Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 26 Jan 2016 17:13:04 +0100 Subject: [PATCH 048/107] Run script update --- .../objectivec/tests/SurefireParser.java | 5 ++ src/main/shell/run-sonar.sh | 70 ++++++++++--------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java index 09ad0a4b..9ddd9c57 100644 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java @@ -22,6 +22,8 @@ import com.sun.swing.internal.plaf.metal.resources.metal_sv; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; @@ -48,6 +50,8 @@ */ public class SurefireParser { + private static final Logger LOG = LoggerFactory.getLogger(SurefireParser.class); + public void collect(Project project, SensorContext context, File reportsDir) { File[] xmlFiles = getReports(reportsDir); @@ -113,6 +117,7 @@ private void parseFiles(SensorContext context, File[] reports) { } } catch (Exception e) { + LOG.error("Can not parse surefire reports", e); throw new XmlParserException("Can not parse surefire reports", e); } } diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index add1edd2..148c793d 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -1,3 +1,4 @@ +#### run-sonar.sh #### #!/bin/bash ## INSTALLATION: script to copy in your Xcode project in the same directory as the .xcodeproj file ## USAGE: ./run-sonar.sh @@ -6,7 +7,7 @@ # # Global parameters -XCTOOL_CMD=xctool +XCTOOL_CMD=xcodebuild SLATHER_CMD=slather XCPRETTY_CMD=xcpretty @@ -35,7 +36,7 @@ function testIsInstalled() { } function readParameter() { - + variable=$1 shift parameter=$1 @@ -47,7 +48,7 @@ function readParameter() { # Run a set of commands with logging and error handling function runCommand() { - # 1st arg: redirect stdout + # 1st arg: redirect stdout # 2nd arg: command to run # 3rd..nth arg: args redirect=$1 @@ -55,11 +56,11 @@ function runCommand() { command=$1 shift - + if [ "$nflag" = "on" ]; then # don't execute command, just echo it echo - if [ "$redirect" = "/dev/stdout" ]; then + if [ "$redirect" = "/dev/stdout" ]; then if [ "$vflag" = "on" ]; then echo "+" $command "$@" else @@ -70,35 +71,35 @@ function runCommand() { else echo "+" $command "$@" fi - + elif [ "$vflag" = "on" ]; then echo - if [ "$redirect" = "/dev/stdout" ]; then + if [ "$redirect" = "/dev/stdout" ]; then set -x #echo on $command "$@" - returnValue=$? - set +x #echo off + returnValue=$? + set +x #echo off elif [ "$redirect" != "no" ]; then set -x #echo on $command "$@" > $redirect - returnValue=$? - set +x #echo off + returnValue=$? + set +x #echo off else set -x #echo on $command "$@" - returnValue=$? - set +x #echo off + returnValue=$? + set +x #echo off fi - + if [[ $returnValue != 0 && $returnValue != 5 ]] ; then stopProgress echo "ERROR - Command '$command $@' failed with error code: $returnValue" exit $returnValue fi else - - if [ "$redirect" = "/dev/stdout" ]; then + + if [ "$redirect" = "/dev/stdout" ]; then $command "$@" > /dev/null elif [ "$redirect" != "no" ]; then $command "$@" > $redirect @@ -113,9 +114,9 @@ function runCommand() { exit $? fi - - echo - fi + + echo + fi } ## COMMAND LINE OPTIONS @@ -145,9 +146,10 @@ echo "Running run-sonar.sh..." ## CHECK PREREQUISITES # xctool, gcovr and oclint installed -testIsInstalled xctool +testIsInstalled xcodebuild testIsInstalled gcovr testIsInstalled oclint +testIsInstalled oclint-xcodebuild # sonar-project.properties in current directory if [ ! -f sonar-project.properties ]; then @@ -156,14 +158,12 @@ fi ## READ PARAMETERS from sonar-project.properties +# Read destination simulator +destinationSimulator=''; readParameter destinationSimulator 'sonar.objectivec.simulator' + # Your .xcworkspace/.xcodeproj filename workspaceFile=''; readParameter workspaceFile 'sonar.objectivec.workspace' projectFile=''; readParameter projectFile 'sonar.objectivec.project' -if [[ "$workspaceFile" != "" ]] ; then - xctoolCmdPrefix="$XCTOOL_CMD -workspace $workspaceFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" -else - xctoolCmdPrefix="$XCTOOL_CMD -project $projectFile -sdk iphonesimulator ARCHS=i386 VALID_ARCHS=i386 CURRENT_ARCH=i386 ONLY_ACTIVE_ARCH=NO" -fi # Count projects projectCount=$(echo $projectFile | sed -n 1'p' | tr ',' '\n' | wc -l | tr -d '[[:space:]]') @@ -182,8 +182,6 @@ testScheme=''; readParameter testScheme 'sonar.objectivec.testScheme' excludedPathsFromCoverage=''; readParameter excludedPathsFromCoverage 'sonar.objectivec.excludedPathsFromCoverage' # Read coverage type coverageType=''; readParameter coverageType 'sonar.objectivec.coverageType' -# Read destination simulator -destinationSimulator=''; readParameter destinationSimulator 'sonar.objectivec.simulator' # Check for mandatory parameters @@ -214,7 +212,7 @@ if [ "$vflag" = "on" ]; then echo "Xcode project file is: $projectFile" echo "Xcode application scheme is: $appScheme" echo "Xcode test scheme is: $testScheme" - echo "Excluded paths from coverage are: $excludedPathsFromCoverage" + echo "Excluded paths from coverage are: $excludedPathsFromCoverage" fi ## SCRIPT @@ -235,8 +233,13 @@ mkdir sonar-reports # Extracting project information needed later echo -n 'Extracting Xcode project information' -runCommand /dev/stdout $xctoolCmdPrefix -scheme "$appScheme" clean -runCommand /dev/stdout $xctoolCmdPrefix -scheme "$appScheme" -reporter plain -reporter json-compilation-database:compile_commands.json build +buildCmd=(xcodebuild clean build -workspace $workspaceFile -scheme $appScheme) +if [[ ! -z "$destinationSimulator" ]]; then + buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) +fi +runCommand xcodebuild.log "${buildCmd[@]}" +oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.json file + # Unit tests and coverage if [ "$testScheme" = "" ]; then @@ -260,8 +263,7 @@ else if [[ ! -z "$destinationSimulator" ]]; then buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) fi - runCommand sonar-reports/xcodebuild.log "${buildCmd[@]}" - cat sonar-reports/xcodebuild.log | $XCPRETTY_CMD -t --report junit + "${buildCmd[@]}" | $XCPRETTY_CMD -t --report junit mv build/reports/junit.xml sonar-reports/TEST-report.xml echo -n 'Computing coverage report' @@ -316,8 +318,8 @@ else fi - -fi + +fi if [ "$oclint" = "on" ]; then From 63ca2513673fc78dc142ca4433136fdf1c92c624 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 2 Feb 2016 13:20:28 +0100 Subject: [PATCH 049/107] Coverage computed on first project if multiple provided --- src/main/shell/run-sonar.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 148c793d..f8b7ed0b 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -287,7 +287,10 @@ else echo "Command line exclusion flags for slather is:$excludedCommandLineFlags" fi - runCommand /dev/stdout $SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports --scheme $appScheme $projectFile + + projectArray=(${projectFile//,/ }) + firstProject=${projectArray[0]} + runCommand /dev/stdout $SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports --scheme $appScheme $firstProject mv sonar-reports/cobertura.xml sonar-reports/coverage.xml else From 0ca1a87508394349af03c7b6757b0db751b0004e Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 8 Feb 2016 11:21:04 +0100 Subject: [PATCH 050/107] Added instructions for xcpretty fixed install --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fa72360..909d76a1 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Binary packages are available in the release section. - 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 ([HomeBrew](http://brew.sh) installed and ```brew install sonar-runner```) -- [xcpretty](https://github.com/supermarin/xcpretty) (```gem install xcpretty```) +- [xcpretty](https://github.com/supermarin/xcpretty) (see instructions below) - [xctool](https://github.com/facebook/xctool) ([HomeBrew](http://brew.sh) installed and ```brew install xctool```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. - [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.10.1 recommended. - [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) @@ -87,6 +87,17 @@ To install slather with profdata support, follow those steps : gem build slather.gemspec gem install --both slather-1.8.1.gem +###Installation of xcpretty with JUnit reports fix + +At the time, xcpretty needs to be fixed to work with SonarQube. + +To install the fixed version, follow those steps : + + git clone https://github.com/Backelite/xcpretty.git + cd xcpretty + git checkout fix/duration_of_failed_tests_workaround + gem build xcpretty.gemspec + sudo gem install --both xcpretty-0.2.2.gem ###Code coverage data format From a04a88e2ff9417e3d780cce3ee7d3b0cc628ff65 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 19 Feb 2016 15:40:10 +0100 Subject: [PATCH 051/107] Refactoring of deprecated APIs part 1 --- build-and-deploy.sh | 8 +- pom.xml | 2 +- .../objectivec/parser/ObjectiveCParser.java | 8 +- .../plugins/objectivec/ObjectiveCPlugin.java | 11 +- .../objectivec/coverage/CoberturaSensor.java | 5 +- .../coverage/CoverageMeasuresPersistor.java | 7 +- .../objectivec/cpd/ObjectiveCCpdMapping.java | 5 +- .../violations/fauxpas/FauxPasProfile.java | 2 +- .../fauxpas/FauxPasProfileImporter.java | 2 +- .../fauxpas/FauxPasReportParser.java | 66 +- .../violations/fauxpas/FauxPasRuleParser.java | 66 -- .../fauxpas/FauxPasRuleRepository.java | 62 - .../fauxpas/FauxPasRulesDefinition.java | 97 ++ .../violations/fauxpas/FauxPasSensor.java | 23 +- .../violations/oclint/OCLintParser.java | 27 +- .../violations/oclint/OCLintProfile.java | 2 +- .../oclint/OCLintProfileImporter.java | 3 +- .../oclint/OCLintRuleRepository.java | 62 - .../oclint/OCLintRuleSeverity.java} | 40 +- ...Parser.java => OCLintRulesDefinition.java} | 105 +- .../violations/oclint/OCLintSensor.java | 32 +- .../oclint/OCLintXMLStreamHandler.java | 70 +- ...objectivec-model.xml => fauxpas-model.xml} | 835 +------------ .../com/sonar/sqale/oclint-model.xml | 1044 +++++++++++++++++ .../CoberturaMeasuresPersistorTest.java | 95 -- .../fauxpas/FauxPasRuleParserTest.java | 55 - .../violations/fauxpas/FauxPasSensorTest.java | 7 +- .../violations/oclint/OCLintParserTest.java | 82 -- .../violations/oclint/OCLintSensorTest.java | 7 +- .../oclint/OCLintXMLStreamHandlerTest.java | 91 -- 30 files changed, 1369 insertions(+), 1552 deletions(-) delete mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParser.java delete mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java delete mode 100644 src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java rename src/{test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java => main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java} (53%) rename src/main/java/org/sonar/plugins/objectivec/violations/oclint/{OCLintRuleParser.java => OCLintRulesDefinition.java} (51%) rename src/main/resources/com/sonar/sqale/{objectivec-model.xml => fauxpas-model.xml} (64%) create mode 100644 src/main/resources/com/sonar/sqale/oclint-model.xml delete mode 100644 src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java delete mode 100644 src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java delete mode 100644 src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java delete mode 100644 src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java diff --git a/build-and-deploy.sh b/build-and-deploy.sh index 9a2a58fb..2e449e75 100755 --- a/build-and-deploy.sh +++ b/build-and-deploy.sh @@ -8,14 +8,8 @@ if [ "$?" != 0 ]; then exit $? fi -# Run shell tests -#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 +rm -rf $SONARQUBE_HOME/extensions/plugins/sonar-objective-c-* cp target/*.jar $SONARQUBE_HOME/extensions/plugins # Stop/start Sonar diff --git a/pom.xml b/pom.xml index 5c72f5d3..0a355044 100644 --- a/pom.xml +++ b/pom.xml @@ -144,7 +144,7 @@ org.codehaus.sonar.sslr-squid-bridge sslr-squid-bridge - 2.4 + 2.5.3 ant diff --git a/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java b/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java index 5e9f0bb3..52e76bb9 100644 --- a/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java +++ b/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java @@ -31,14 +31,14 @@ public class ObjectiveCParser { private ObjectiveCParser() { } - public static Parser create(ParsingEventListener... parsingEventListeners) { - return create(new ObjectiveCConfiguration(), parsingEventListeners); + public static Parser create() { + return create(new ObjectiveCConfiguration()); } - public static Parser create(ObjectiveCConfiguration conf, ParsingEventListener... parsingEventListeners) { + public static Parser create(ObjectiveCConfiguration conf) { return Parser.builder((ObjectiveCGrammar) new ObjectiveCGrammarImpl()) .withLexer(ObjectiveCLexer.create(conf)) - .setParsingEventListeners(parsingEventListeners).build(); + .build(); } } diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java index 06272db7..e77f88f9 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java @@ -37,12 +37,9 @@ import org.sonar.plugins.objectivec.violations.ObjectiveCProfile; import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfile; import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfileImporter; -import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasRuleRepository; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasRulesDefinition; import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasSensor; -import org.sonar.plugins.objectivec.violations.oclint.OCLintProfile; -import org.sonar.plugins.objectivec.violations.oclint.OCLintProfileImporter; -import org.sonar.plugins.objectivec.violations.oclint.OCLintRuleRepository; -import org.sonar.plugins.objectivec.violations.oclint.OCLintSensor; +import org.sonar.plugins.objectivec.violations.oclint.*; @Properties({ @Property(key = CoberturaSensor.REPORT_PATTERN_KEY, defaultValue = CoberturaSensor.DEFAULT_REPORT_PATTERN, name = "Path to unit test coverage report(s)", description = "Relative to projects' root. Ant patterns are accepted", global = false, project = true), @@ -62,13 +59,13 @@ public List> getExtensions() { SurefireSensor.class, CoberturaSensor.class, - OCLintRuleRepository.class, + OCLintRulesDefinition.class, OCLintSensor.class, OCLintProfile.class, OCLintProfileImporter.class, FauxPasSensor.class, - FauxPasRuleRepository.class, + FauxPasRulesDefinition.class, FauxPasProfile.class, FauxPasProfileImporter.class ); 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 8e7bf719..5c7e0430 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java @@ -60,9 +60,8 @@ public boolean shouldExecuteOnProject(final Project project) { } public void analyse(final Project project, final SensorContext context) { - final CoverageMeasuresPersistor measuresPersistor = new CoverageMeasuresPersistor( - project, context); - final String projectBaseDir = project.getFileSystem().getBasedir() + final CoverageMeasuresPersistor measuresPersistor = new CoverageMeasuresPersistor(project, context, fileSystem); + final String projectBaseDir = fileSystem.baseDir() .getPath(); measuresPersistor.saveMeasures(parseReportsIn(projectBaseDir)); 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 6c261733..1e128632 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java @@ -27,6 +27,7 @@ import com.google.common.collect.Lists; import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.measures.CoverageMeasuresBuilder; import org.sonar.api.measures.Measure; import org.sonar.api.resources.Project; @@ -36,10 +37,12 @@ final class CoverageMeasuresPersistor { private final Project project; private final SensorContext context; + private final FileSystem fileSystem; - public CoverageMeasuresPersistor(final Project p, final SensorContext c) { + public CoverageMeasuresPersistor(final Project p, final SensorContext c, final FileSystem fileSystem) { project = p; context = c; + this.fileSystem = fileSystem; } public void saveMeasures(final Map coverageMeasures) { @@ -52,7 +55,7 @@ public void saveMeasures(final Map coverageMeas 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); + final org.sonar.api.resources.File objcfile = org.sonar.api.resources.File.fromIOFile(new File(fileSystem.baseDir(), filePath), project); if (fileExists(context, objcfile)) { LoggerFactory.getLogger(getClass()).debug( diff --git a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java b/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java index 8c3adac5..69202e65 100644 --- a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java +++ b/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java @@ -24,6 +24,7 @@ import net.sourceforge.pmd.cpd.Tokenizer; import org.sonar.api.batch.AbstractCpdMapping; +import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.resources.Language; import org.sonar.api.resources.ProjectFileSystem; import org.sonar.plugins.objectivec.core.ObjectiveC; @@ -33,9 +34,9 @@ public class ObjectiveCCpdMapping extends AbstractCpdMapping { private final ObjectiveC language; private final Charset charset; - public ObjectiveCCpdMapping(ObjectiveC language, ProjectFileSystem fs) { + public ObjectiveCCpdMapping(ObjectiveC language, FileSystem fs) { this.language = language; - this.charset = fs.getSourceCharset(); + this.charset = fs.encoding(); } public Tokenizer getTokenizer() { diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java index 6408947d..212afdf3 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java @@ -50,7 +50,7 @@ public RulesProfile createProfile(ValidationMessages messages) { config = new InputStreamReader(getClass().getResourceAsStream( PROFILE_PATH)); final RulesProfile profile = profileImporter.importProfile(config, messages); - profile.setName(FauxPasRuleRepository.REPOSITORY_KEY); + profile.setName(FauxPasRulesDefinition.REPOSITORY_KEY); profile.setLanguage(ObjectiveC.KEY); return profile; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java index b9239d4f..dd4c84bf 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java @@ -37,7 +37,7 @@ public class FauxPasProfileImporter extends ProfileImporter { private final XMLProfileParser profileParser; public FauxPasProfileImporter(final XMLProfileParser xmlProfileParser) { - super(FauxPasRuleRepository.REPOSITORY_KEY, FauxPasRuleRepository.REPOSITORY_KEY); + super(FauxPasRulesDefinition.REPOSITORY_KEY, FauxPasRulesDefinition.REPOSITORY_KEY); setSupportedLanguages(ObjectiveC.KEY); profileParser = xmlProfileParser; } diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java index 9ee0d9df..3a8cf30f 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -20,41 +20,37 @@ package org.sonar.plugins.objectivec.violations.fauxpas; import org.apache.commons.io.IOUtils; -import org.codehaus.staxmate.in.SMInputCursor; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; +import org.sonar.api.issue.Issue; import org.sonar.api.resources.Project; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.rules.Violation; -import org.sonar.plugins.objectivec.violations.oclint.OCLintRuleRepository; +import org.sonar.api.rule.RuleKey; -import javax.xml.stream.XMLStreamException; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; -import java.util.ArrayList; -import java.util.Collection; public class FauxPasReportParser { private final Project project; private final SensorContext context; + private final ResourcePerspectives resourcePerspectives; private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasReportParser.class); - public FauxPasReportParser(final Project p, final SensorContext c) { + public FauxPasReportParser(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives) { project = p; context = c; + this.resourcePerspectives = resourcePerspectives; } - public Collection parseReport(File reportFile) { - - final Collection violations = new ArrayList(); + public void parseReport(File reportFile) { try { // Read and parse report @@ -69,7 +65,7 @@ public Collection parseReport(File reportFile) { JSONArray diagnosticsJson = (JSONArray)reportJson.get("diagnostics"); for (Object obj : diagnosticsJson) { - recordViolation((JSONObject)obj, violations); + recordIssue((JSONObject) obj); } } @@ -77,12 +73,9 @@ public Collection parseReport(File reportFile) { } catch (FileNotFoundException e) { LOGGER.error("Failed to parse FauxPas report file", e); } - - - return violations; } - private void recordViolation(final JSONObject diagnosticJson, Collection violations) { + private void recordIssue(final JSONObject diagnosticJson) { String filePath = (String)diagnosticJson.get("file"); @@ -90,31 +83,34 @@ private void recordViolation(final JSONObject diagnosticJson, Collection parse(final BufferedReader reader) throws IOException { - final List rules = new ArrayList(); - - String jsonString = IOUtils.toString(reader); - - Object rulesObj = JSONValue.parse(jsonString); - - if (rulesObj != null) { - JSONArray fpRules = (JSONArray)rulesObj; - for (Object obj : fpRules) { - JSONObject fpRule = (JSONObject)obj; - - Rule rule = Rule.create(); - rules.add(rule); - rule.setName((String) fpRule.get("name")); - rule.setKey((String) fpRule.get("key")); - rule.setDescription((String) fpRule.get("description")); - rule.setSeverity(RulePriority.valueOf((String) fpRule.get("severity"))); - } - } - - - return rules; - } -} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java deleted file mode 100644 index a833e79b..00000000 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleRepository.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.violations.fauxpas; - -import com.google.common.io.Closeables; -import org.apache.commons.lang.CharEncoding; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RuleRepository; -import org.sonar.api.utils.SonarException; -import org.sonar.plugins.objectivec.core.ObjectiveC; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.List; - -public class FauxPasRuleRepository extends RuleRepository { - - public static final String REPOSITORY_KEY = "FauxPas"; - public static final String REPOSITORY_NAME = REPOSITORY_KEY; - - private static final String RULES_FILE = "/org/sonar/plugins/fauxpas/rules.json"; - - private final FauxPasRuleParser ruleParser = new FauxPasRuleParser(); - - public FauxPasRuleRepository() { - super(FauxPasRuleRepository.REPOSITORY_KEY, ObjectiveC.KEY); - setName(FauxPasRuleRepository.REPOSITORY_NAME); - } - - @Override - public List createRules() { - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(getClass() - .getResourceAsStream(RULES_FILE), CharEncoding.UTF_8)); - return ruleParser.parse(reader); - } catch (final IOException e) { - throw new SonarException("Fail to load the default FauxPas rules.", - e); - } finally { - Closeables.closeQuietly(reader); - } - } -} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java new file mode 100644 index 00000000..e7dfb786 --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java @@ -0,0 +1,97 @@ +/* + * Sonar Objective-C Plugin + * Copyright (C) 2012 OCTO Technology, Backelite + * 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.violations.fauxpas; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.CharEncoding; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.plugins.objectivec.core.ObjectiveC; +import org.sonar.squidbridge.rules.SqaleXmlLoader; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by gillesgrousset on 18/02/2016. + */ +public class FauxPasRulesDefinition implements RulesDefinition { + + private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasRulesDefinition.class); + + public static final String REPOSITORY_KEY = "FauxPas"; + public static final String REPOSITORY_NAME = REPOSITORY_KEY; + + private static final String RULES_FILE = "/org/sonar/plugins/fauxpas/rules.json"; + + @Override + public void define(Context context) { + + NewRepository repository = context + .createRepository(REPOSITORY_KEY, ObjectiveC.KEY) + .setName(REPOSITORY_NAME); + + try { + loadRules(repository); + } catch (IOException e) { + LOGGER.error("Failed to load FauxPas rules", e); + } + + SqaleXmlLoader.load(repository, "/com/sonar/sqale/fauxpas-model.xml"); + + repository.done(); + + } + + private void loadRules(NewRepository repository) throws IOException { + + Reader reader = new BufferedReader(new InputStreamReader(getClass() + .getResourceAsStream(RULES_FILE), CharEncoding.UTF_8)); + + String jsonString = IOUtils.toString(reader); + + Object rulesObj = JSONValue.parse(jsonString); + + if (rulesObj != null) { + JSONArray slRules = (JSONArray)rulesObj; + for (Object obj : slRules) { + JSONObject fpRule = (JSONObject)obj; + + RulesDefinition.NewRule rule = repository.createRule((String)fpRule.get("key")); + rule.setName((String) fpRule.get("name")); + rule.setSeverity((String)fpRule.get("severity")); + rule.setHtmlDescription((String) fpRule.get("description")); + + } + } + + } +} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java index bf4d0f9d..7bdc2a88 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java @@ -25,6 +25,7 @@ import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; import org.sonar.api.rules.Violation; @@ -45,10 +46,12 @@ public class FauxPasSensor implements Sensor { private final Settings conf; private final FileSystem fileSystem; + private final ResourcePerspectives resourcePerspectives; - public FauxPasSensor(final FileSystem moduleFileSystem, final Settings config) { + public FauxPasSensor(final FileSystem moduleFileSystem, final Settings config, final ResourcePerspectives resourcePerspectives) { this.conf = config; this.fileSystem = moduleFileSystem; + this.resourcePerspectives = resourcePerspectives; } @Override @@ -59,19 +62,14 @@ public boolean shouldExecuteOnProject(final Project project) { @Override public void analyse(Project module, SensorContext context) { - final String projectBaseDir = module.getFileSystem().getBasedir().getPath(); + final String projectBaseDir = fileSystem.baseDir().getPath(); - FauxPasReportParser parser = new FauxPasReportParser(module, context); - saveViolations(parseReportIn(projectBaseDir, parser), context); + FauxPasReportParser parser = new FauxPasReportParser(module, context, resourcePerspectives); + parseReportIn(projectBaseDir, parser); } - private void saveViolations(final Collection violations, final SensorContext context) { - for (final Violation violation : violations) { - context.saveViolation(violation); - } - } - private Collection parseReportIn(final String baseDir, final FauxPasReportParser parser) { + private void parseReportIn(final String baseDir, final FauxPasReportParser parser) { DirectoryScanner scanner = new DirectoryScanner(); scanner.setIncludes(new String[]{reportPath()}); @@ -80,14 +78,11 @@ private Collection parseReportIn(final String baseDir, final FauxPasR scanner.scan(); String[] files = scanner.getIncludedFiles(); - Collection result = new ArrayList(); for(String filename : files) { LOGGER.info("Processing FauxPas report {}", filename); - result.addAll(parser.parseReport(new File(filename))); + parser.parseReport(new File(filename)); } - return result; - } private String reportPath() { diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java index 546b0411..bd0d34ae 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java @@ -30,46 +30,45 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.resources.Project; import org.sonar.api.rules.Violation; import org.sonar.api.utils.StaxParser; final class OCLintParser { + private final Project project; private final SensorContext context; + private final ResourcePerspectives resourcePerspectives; - public OCLintParser(final Project p, final SensorContext c) { + public OCLintParser(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives) { project = p; context = c; + this.resourcePerspectives = resourcePerspectives; } - public Collection parseReport(final File file) { - Collection result; + public void parseReport(final File file) { + try { final InputStream reportStream = new FileInputStream(file); - result = parseReport(reportStream); + parseReport(reportStream); reportStream.close(); } catch (final IOException e) { - LoggerFactory.getLogger(getClass()).error( - "Error processing file named {}", file, e); - result = new ArrayList(); + LoggerFactory.getLogger(getClass()).error("Error processing file named {}", file, e); } - return result; + } - public Collection parseReport(final InputStream inputStream) { - final Collection violations = new ArrayList(); + public void parseReport(final InputStream inputStream) { + try { final StaxParser parser = new StaxParser( - new OCLintXMLStreamHandler(violations, project, context)); + new OCLintXMLStreamHandler(project, context, resourcePerspectives)); parser.parse(inputStream); - LoggerFactory.getLogger(getClass()).error( - "Reporting {} violations.", violations.size()); } catch (final XMLStreamException e) { LoggerFactory.getLogger(getClass()).error( "Error while parsing XML stream.", e); } - return violations; } } diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java index de337ef0..8d6f5f84 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java @@ -52,7 +52,7 @@ public RulesProfile createProfile(final ValidationMessages messages) { PROFILE_PATH)); final RulesProfile profile = profileImporter.importProfile(config, messages); - profile.setName(OCLintRuleRepository.REPOSITORY_KEY); + profile.setName(OCLintRulesDefinition.REPOSITORY_KEY); profile.setLanguage(ObjectiveC.KEY); return profile; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java index fc6896b7..99d550d3 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java @@ -37,8 +37,7 @@ public final class OCLintProfileImporter extends ProfileImporter { private final XMLProfileParser profileParser; public OCLintProfileImporter(final XMLProfileParser xmlProfileParser) { - super(OCLintRuleRepository.REPOSITORY_KEY, - OCLintRuleRepository.REPOSITORY_KEY); + super(OCLintRulesDefinition.REPOSITORY_KEY, OCLintRulesDefinition.REPOSITORY_KEY); setSupportedLanguages(ObjectiveC.KEY); profileParser = xmlProfileParser; } diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java deleted file mode 100644 index d16d78e5..00000000 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleRepository.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.violations.oclint; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.List; - -import org.apache.commons.lang.CharEncoding; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RuleRepository; -import org.sonar.api.utils.SonarException; -import org.sonar.plugins.objectivec.core.ObjectiveC; - -import com.google.common.io.Closeables; - -public final class OCLintRuleRepository extends RuleRepository { - public static final String REPOSITORY_KEY = "OCLint"; - public static final String REPOSITORY_NAME = REPOSITORY_KEY; - - private static final String RULES_FILE = "/org/sonar/plugins/oclint/rules.txt"; - - private final OCLintRuleParser ocLintRuleParser = new OCLintRuleParser(); - - public OCLintRuleRepository() { - super(OCLintRuleRepository.REPOSITORY_KEY, ObjectiveC.KEY); - setName(OCLintRuleRepository.REPOSITORY_NAME); - } - - @Override - public List createRules() { - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(getClass() - .getResourceAsStream(RULES_FILE), CharEncoding.UTF_8)); - return ocLintRuleParser.parse(reader); - } catch (final IOException e) { - throw new SonarException("Fail to load the default OCLint rules.", - e); - } finally { - Closeables.closeQuietly(reader); - } - } -} diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java similarity index 53% rename from src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java rename to src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java index 5de09a7b..025ddfc5 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/ProjectBuilder.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java @@ -17,34 +17,24 @@ * 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.violations.oclint; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.ProjectFileSystem; +package org.sonar.plugins.objectivec.violations.oclint; -final class ProjectBuilder { - private final Project project = new Project("Test"); - private final ProjectFileSystem fileSystem = mock(ProjectFileSystem.class); - private final List sourceDirs = new ArrayList(); +/** + * Created by gillesgrousset on 18/02/2016. + */ +public enum OCLintRuleSeverity { - public ProjectBuilder() { - project.setFileSystem(fileSystem); - when(fileSystem.getSourceDirs()).thenReturn(sourceDirs); - when(fileSystem.getBasedir()).thenReturn(new File(".")); - } + // Rules are priority + // INFO = 0, MINOR = 1, MAJOR = 2, CRITICAL = 3, BLOCKER = 4 - public Project project() { - return project; - } + INFO, + MINOR, + MAJOR, + CRITICAL, + BLOCKER; - public void containingSourceDirectory(final String d) { - sourceDirs.add(new File(d)); - } + public static OCLintRuleSeverity valueOfInt(int ordinal) { + return values()[ordinal]; + } } diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java similarity index 51% rename from src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java rename to src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java index 02bdca28..506c569b 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java @@ -17,80 +17,119 @@ * 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.violations.oclint; -import java.io.BufferedReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +package org.sonar.plugins.objectivec.violations.oclint; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.CharEncoding; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.ServerComponent; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.plugins.objectivec.core.ObjectiveC; +import org.sonar.squidbridge.rules.SqaleXmlLoader; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** - * Largely copied from AndroidLint's equivalent class whose authors are Stephane - * Nicolas and Jerome Van Der Linden according to the class Javadoc. - * + * Created by gillesgrousset on 18/02/2016. */ -final class OCLintRuleParser implements ServerComponent { +public class OCLintRulesDefinition implements RulesDefinition { + + private static final Logger LOGGER = LoggerFactory.getLogger(OCLintRulesDefinition.class); + + public static final String REPOSITORY_KEY = "OCLint"; + public static final String REPOSITORY_NAME = REPOSITORY_KEY; - private static final int OCLINT_MINIMUM_PRIORITY = 4; - private static final Logger LOGGER = LoggerFactory - .getLogger(OCLintRuleParser.class); + private static final String RULES_FILE = "/org/sonar/plugins/oclint/rules.txt"; + + @Override + public void define(Context context) { + + NewRepository repository = context + .createRepository(REPOSITORY_KEY, ObjectiveC.KEY) + .setName(REPOSITORY_NAME); + + try { + loadRules(repository); + } catch (IOException e) { + LOGGER.error("Failed to load OCLint rules", e); + } - public List parse(final BufferedReader reader) throws IOException { - final List rules = new ArrayList(); + SqaleXmlLoader.load(repository, "/com/sonar/sqale/oclint-model.xml"); + + repository.done(); + + } + + private void loadRules(NewRepository repository) throws IOException { + + Reader reader = new BufferedReader(new InputStreamReader(getClass() + .getResourceAsStream(RULES_FILE), CharEncoding.UTF_8)); final List listLines = IOUtils.readLines(reader); String previousLine = null; - Rule rule = null; + Map rule = new HashMap(); boolean inDescription = false; for (String line : listLines) { + if (isLineIgnored(line)) { inDescription = false; + } else if (line.matches("[\\-]{4,}.*")) { LOGGER.debug("Rule found : {}", previousLine); - // remove the rule name from the description of the previous + // Remove the rule name from the description of the previous // rule - if (rule != null) { - final int index = rule.getDescription().lastIndexOf( - previousLine); + if (rule.get("description") != null) { + String description = rule.get("description").toString(); + final int index = description.lastIndexOf(previousLine); if (index > 0) { - rule.setDescription(rule.getDescription().substring(0, - index)); + rule.put("description", description.substring(0, index)); } + } - rule = Rule.create(); - rules.add(rule); - rule.setName(StringUtils.capitalize(previousLine)); - rule.setKey(previousLine); + rule.clear(); + + rule.put("name", StringUtils.capitalize(previousLine)); + rule.put("key", previousLine); + + } else if (line.matches("Summary:.*")) { inDescription = true; - rule.setDescription(line.substring(line.indexOf(':') + 1)); + rule.put("description", line.substring(line.indexOf(':') + 1)); } else if (line.matches("Category:.*")) { inDescription = true; + + // Create rule when last filed found + RulesDefinition.NewRule newRule = repository.createRule(rule.get("key").toString()); + newRule.setName(rule.get("name").toString()); + newRule.setSeverity(rule.get("severity").toString()); + newRule.setHtmlDescription(rule.get("description").toString()); + } else if (line.matches("Severity:.*")) { inDescription = false; final String severity = line.substring("Severity: ".length()); - // Rules are priority 1, 2 or 3 in OCLint files. - rule.setSeverity(RulePriority.values()[Integer.valueOf(severity)]); + rule.put("severity", OCLintRuleSeverity.valueOfInt(Integer.valueOf(severity)).name()); } else { if (inDescription) { line = ruleDescriptionLink(line); - rule.setDescription(rule.getDescription() + "
" + line); + String description = (String)rule.get("description"); + rule.put("description", description + "
" + line); } } + previousLine = line; } - return rules; + } private boolean isLineIgnored(String line) { diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java index 576d13a8..4134f09c 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java @@ -28,23 +28,26 @@ import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; 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.fauxpas.FauxPasReportParser; public final class OCLintSensor implements Sensor { - public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX - + ".oclint.report"; + public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX + ".oclint.report"; public static final String DEFAULT_REPORT_PATH = "sonar-reports/*oclint.xml"; private final Settings conf; private final FileSystem fileSystem; + private final ResourcePerspectives resourcePerspectives; - public OCLintSensor(final FileSystem moduleFileSystem, final Settings config) { + public OCLintSensor(final FileSystem fileSystem, final Settings config, final ResourcePerspectives resourcePerspectives) { this.conf = config; - this.fileSystem = moduleFileSystem; + this.fileSystem = fileSystem; + this.resourcePerspectives = resourcePerspectives; } public boolean shouldExecuteOnProject(final Project project) { @@ -54,22 +57,14 @@ public boolean shouldExecuteOnProject(final Project project) { } public void analyse(final Project project, final SensorContext context) { - final String projectBaseDir = project.getFileSystem().getBasedir() - .getPath(); - final OCLintParser parser = new OCLintParser(project, context); - saveViolations(parseReportIn(projectBaseDir, parser), context); + final String projectBaseDir = fileSystem.baseDir().getPath(); + final OCLintParser parser = new OCLintParser(project, context, resourcePerspectives); - } + parseReportIn(projectBaseDir, parser); - private void saveViolations(final Collection violations, - final SensorContext context) { - for (final Violation violation : violations) { - context.saveViolation(violation); - } } - private Collection parseReportIn(final String baseDir, - final OCLintParser parser) { + private void parseReportIn(final String baseDir, final OCLintParser parser) { DirectoryScanner scanner = new DirectoryScanner(); scanner.setIncludes(new String[]{reportPath()}); @@ -78,13 +73,10 @@ private Collection parseReportIn(final String baseDir, scanner.scan(); String[] files = scanner.getIncludedFiles(); - Collection result = new ArrayList(); for(String filename : files) { LoggerFactory.getLogger(getClass()).info("Processing OCLint report {}", filename); - result.addAll(parser.parseReport(new File(filename))); + parser.parseReport(new File(filename)); } - - return result; } private String reportPath() { diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java index e5d77dec..b31b02cc 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java @@ -20,64 +20,58 @@ package org.sonar.plugins.objectivec.violations.oclint; import java.io.File; -import java.util.Collection; import javax.xml.stream.XMLStreamException; import org.codehaus.staxmate.in.SMHierarchicCursor; import org.codehaus.staxmate.in.SMInputCursor; +import org.json.simple.JSONObject; import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; +import org.sonar.api.issue.Issue; import org.sonar.api.resources.Project; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RulePriority; import org.sonar.api.rules.Violation; import org.sonar.api.utils.StaxParser.XmlStreamHandler; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasRulesDefinition; final class OCLintXMLStreamHandler implements XmlStreamHandler { private static final int PMD_MINIMUM_PRIORITY = 5; - private final Collection foundViolations; private final Project project; private final SensorContext context; + private final ResourcePerspectives resourcePerspectives; - public OCLintXMLStreamHandler(final Collection violations, - final Project p, final SensorContext c) { - foundViolations = violations; + public OCLintXMLStreamHandler(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives) { project = p; context = c; + this.resourcePerspectives = resourcePerspectives; } - public void stream(final SMHierarchicCursor rootCursor) - throws XMLStreamException { - final SMInputCursor file = rootCursor.advance().childElementCursor( - "file"); + public void stream(final SMHierarchicCursor rootCursor) throws XMLStreamException { + final SMInputCursor file = rootCursor.advance().childElementCursor("file"); while (null != file.getNext()) { - collectViolationsFor(file); + collectIssuesFor(file); } } - private void collectViolationsFor(final SMInputCursor file) - throws XMLStreamException { + private void collectIssuesFor(final SMInputCursor file) throws XMLStreamException { + final String filePath = file.getAttrValue("name"); - LoggerFactory.getLogger(getClass()).debug( - "Collection violations for {}", filePath); + LoggerFactory.getLogger(getClass()).debug("Collection violations for {}", filePath); final org.sonar.api.resources.File resource = findResource(filePath); if (fileExists(resource)) { - LoggerFactory.getLogger(getClass()).debug( - "File {} was found in the project.", filePath); - collectFileViolations(resource, file); + LoggerFactory.getLogger(getClass()).debug("File {} was found in the project.", filePath); + collectFileIssues(resource, file); } } - private org.sonar.api.resources.File findResource(final String filePath) { - return org.sonar.api.resources.File.fromIOFile(new File(filePath), - project); - } + private void collectFileIssues(final org.sonar.api.resources.File resource, final SMInputCursor file) throws XMLStreamException { - private void collectFileViolations( - final org.sonar.api.resources.File resource, - final SMInputCursor file) throws XMLStreamException { final SMInputCursor line = file.childElementCursor("violation"); while (null != line.getNext()) { @@ -85,22 +79,26 @@ private void collectFileViolations( } } - private void recordViolation(final org.sonar.api.resources.File resource, - final SMInputCursor line) throws XMLStreamException { - final Rule rule = Rule.create(); - final Violation violation = Violation.create(rule, resource); + private org.sonar.api.resources.File findResource(final String filePath) { + return org.sonar.api.resources.File.fromIOFile(new File(filePath), project); + } - // PMD Priorities are 1, 2, 3, 4, 5 RulePriority[0] is INFO - rule.setSeverity(RulePriority.values()[PMD_MINIMUM_PRIORITY - - Integer.valueOf(line.getAttrValue("priority"))]); - rule.setKey(line.getAttrValue("rule")); - rule.setRepositoryKey(OCLintRuleRepository.REPOSITORY_KEY); + private void recordViolation(final org.sonar.api.resources.File resource, final SMInputCursor line) throws XMLStreamException { - violation.setLineId(Integer.valueOf(line.getAttrValue("beginline"))); + Issuable issuable = resourcePerspectives.as(Issuable.class, resource); - violation.setMessage(line.getElemStringValue()); + if (issuable != null) { - foundViolations.add(violation); + Issue issue = issuable.newIssueBuilder() + .ruleKey(RuleKey.of(FauxPasRulesDefinition.REPOSITORY_KEY, line.getAttrValue("rule"))) + .line(Integer.valueOf(line.getAttrValue("beginline"))) + .message(line.getElemStringValue()) + .build(); + + issuable.addIssue(issue); + + + } } private boolean fileExists(final org.sonar.api.resources.File file) { diff --git a/src/main/resources/com/sonar/sqale/objectivec-model.xml b/src/main/resources/com/sonar/sqale/fauxpas-model.xml similarity index 64% rename from src/main/resources/com/sonar/sqale/objectivec-model.xml rename to src/main/resources/com/sonar/sqale/fauxpas-model.xml index 690122a8..bb42e286 100644 --- a/src/main/resources/com/sonar/sqale/objectivec-model.xml +++ b/src/main/resources/com/sonar/sqale/fauxpas-model.xml @@ -282,545 +282,8 @@
- OCLint - unused method parameter - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - unused local variable - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - replace with number literal - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - replace with boxed expression - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - unnecessary else statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - redundant local variable - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - redundant if statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - redundant conditional operator - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - replace with container literal - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - long line - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - long method - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - for loop should be while loop - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - too many methods - - remediationFunction - CONSTANT_ISSUE - - - offset - 1 - h - - - - OCLint - long class - - remediationFunction - CONSTANT_ISSUE - - - offset - 1 - h - - - - OCLint - too many fields - - remediationFunction - CONSTANT_ISSUE - - - offset - 1 - h - - - - OCLint - collapsible if statements - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - deep nested block - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - high cyclomatic complexity - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - too few branches in switch statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - non case label in switch statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - short variable name - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - long variable name - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - default label not last in switch statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - FauxPas - OldVerboseObjCSyntax - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - replace with object subscripting - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - - UNDERSTANDABILITY - Understandability - - FauxPas - MissingAPIUsageDescription - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - useless parentheses - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - use early exits and continue - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - high npath complexity - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - inverted logic - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - multiple unary operator - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - redundant nil check - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - high ncss method - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - dead code - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - double negative - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - bitwise operator in conditional - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - empty if statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - empty while statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - empty else block - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - empty for statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - empty do/while statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - empty switch statement + FauxPas + OldVerboseObjCSyntax remediationFunction CONSTANT_ISSUE @@ -831,22 +294,13 @@ min + + + UNDERSTANDABILITY + Understandability - OCLint - too many parameters - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - switch statements don't need default when fully covered + FauxPas + MissingAPIUsageDescription remediationFunction CONSTANT_ISSUE @@ -999,71 +453,6 @@ EXCEPTION_HANDLING Exception handling - - OCLint - empty finally statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - return from finally block - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - empty catch statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - empty try statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - throw exception from finally block - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - FAULT_TOLERANCE @@ -1826,218 +1215,10 @@ min - - OCLint - must override hash with isEqual - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - parameter reassignment - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - ivar assignment outside accessors or init - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - LOGIC_RELIABILITY Logic - - OCLint - missing break in switch statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - avoid branching statement as last in loop - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - switch statements should have default - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - jumbled incrementer - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - broken null check - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - broken nil check - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - broken oddness check - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - misplaced nil check - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - misplaced null check - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - goto statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - goto statement - - remediationFunction - CONSTANT_ISSUE - - - offset - 30 - min - - - - OCLint - constant if expression - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - OCLint - constant conditional operator - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - FauxPas DefaultInExhaustiveSwitch diff --git a/src/main/resources/com/sonar/sqale/oclint-model.xml b/src/main/resources/com/sonar/sqale/oclint-model.xml new file mode 100644 index 00000000..891b4275 --- /dev/null +++ b/src/main/resources/com/sonar/sqale/oclint-model.xml @@ -0,0 +1,1044 @@ + + + REUSABILITY + Reusability + + MODULARITY + Modularity + + + TRANSPORTABILITY + Transportability + + FauxPas + AbsPathInBuildSetting + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + FileRefWithAbsPath + + remediationFunction + linear + + + remediationFactor + 30 + mn + + + offset + 0.0 + d + + + + FauxPas + StringsFileEncoding + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + FauxPas + FileRefOutsideVCS + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + FauxPas + FileRefIgnoredInVCS + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + + + PORTABILITY + Portability + + COMPILER_RELATED_PORTABILITY + Compiler + + + HARDWARE_RELATED_PORTABILITY + Hardware + + + LANGUAGE_RELATED_PORTABILITY + Language + + + OS_RELATED_PORTABILITY + OS + + + SOFTWARE_RELATED_PORTABILITY + Software + + + TIME_ZONE_RELATED_PORTABILITY + Time zone + + + + MAINTAINABILITY + Maintainability + + READABILITY + Readability + + OCLint + unused method parameter + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + unused local variable + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + replace with number literal + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + replace with boxed expression + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + unnecessary else statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + redundant local variable + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + redundant if statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + redundant conditional operator + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + replace with container literal + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + long line + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + long method + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + for loop should be while loop + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + too many methods + + remediationFunction + CONSTANT_ISSUE + + + offset + 1 + h + + + + OCLint + long class + + remediationFunction + CONSTANT_ISSUE + + + offset + 1 + h + + + + OCLint + too many fields + + remediationFunction + CONSTANT_ISSUE + + + offset + 1 + h + + + + OCLint + collapsible if statements + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + deep nested block + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + high cyclomatic complexity + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + too few branches in switch statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + non case label in switch statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + short variable name + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + long variable name + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + default label not last in switch statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + replace with object subscripting + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + + UNDERSTANDABILITY + Understandability + + OCLint + useless parentheses + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + use early exits and continue + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + high npath complexity + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + inverted logic + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + multiple unary operator + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + redundant nil check + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + high ncss method + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + dead code + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + double negative + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + bitwise operator in conditional + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + empty if statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + empty while statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + empty else block + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + empty for statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + empty do/while statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + empty switch statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + too many parameters + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + switch statements don't need default when fully covered + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + + + SECURITY + Security + + API_ABUSE + API abuse + + + ERRORS + Errors + + + INPUT_VALIDATION_AND_REPRESENTATION + Input validation and representation + + + SECURITY_FEATURES + Security features + + + + EFFICIENCY + Efficiency + + MEMORY_EFFICIENCY + Memory use + + + CPU_EFFICIENCY + Processor use + + + + CHANGEABILITY + Changeability + + ARCHITECTURE_CHANGEABILITY + Architecture + + + DATA_CHANGEABILITY + Data + + + LOGIC_CHANGEABILITY + Logic + + + + RELIABILITY + Reliability + + ARCHITECTURE_RELIABILITY + Architecture + + + DATA_RELIABILITY + Data + + + EXCEPTION_HANDLING + Exception handling + + OCLint + empty finally statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + return from finally block + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + empty catch statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + empty try statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + throw exception from finally block + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + + FAULT_TOLERANCE + Fault tolerance + + + INSTRUCTION_RELIABILITY + Instruction + + OCLint + must override hash with isEqual + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + parameter reassignment + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + ivar assignment outside accessors or init + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + + LOGIC_RELIABILITY + Logic + + OCLint + missing break in switch statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + avoid branching statement as last in loop + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + switch statements should have default + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + jumbled incrementer + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + broken null check + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + broken nil check + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + broken oddness check + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + misplaced nil check + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + misplaced null check + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + goto statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + goto statement + + remediationFunction + CONSTANT_ISSUE + + + offset + 30 + min + + + + OCLint + constant if expression + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + constant conditional operator + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + + RESOURCE_RELIABILITY + Resource + + UNIT_TESTS + Unit tests coverage + + + + + TESTABILITY + Testability + + INTEGRATION_TESTABILITY + Integration level + + + UNIT_TESTABILITY + Unit level + + + \ No newline at end of file diff --git a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java deleted file mode 100644 index fb687e6d..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaMeasuresPersistorTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.coverage; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; -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.resources.ProjectFileSystem; -import org.sonar.api.resources.Resource; - -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()); - - - when(fileSystem.getBasedir()).thenReturn(new File(".")); - - project.setFileSystem(fileSystem); - - final CoverageMeasuresPersistor testedPersistor = new CoverageMeasuresPersistor(project, context); - testedPersistor.saveMeasures(measures); - - verify(context, never()).saveMeasure(any(Resource.class), any(Measure.class)); - } - - @Test - public void shouldPersistMeasuresForKnownFiles() { - final Project project = new Project("Test"); - 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 Map measures = new HashMap(); - final CoverageMeasuresBuilder measureBuilder = CoverageMeasuresBuilder.create(); - - sourceDirs.add(new File("/dummy")); - measures.put("/dummy/test", measureBuilder); - measureBuilder.setHits(99, 99); - measureBuilder.setConditions(99, 99, 1); - - when(fileSystem.getSourceDirs()).thenReturn(sourceDirs); - when(context.getResource(any(Resource.class))).thenReturn(dummyFile); - when(fileSystem.getBasedir()).thenReturn(new File(".")); - - project.setFileSystem(fileSystem); - - final CoverageMeasuresPersistor testedPersistor = new CoverageMeasuresPersistor(project, context); - testedPersistor.saveMeasures(measures); - - for (final Measure measure : measureBuilder.createMeasures()) { - 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/fauxpas/FauxPasRuleParserTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java deleted file mode 100644 index 3050382e..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRuleParserTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.violations.fauxpas; - -import org.junit.Test; -import org.sonar.api.rules.Rule; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.StringReader; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -/** - * Created by gillesgrousset on 12/02/15. - */ -public class FauxPasRuleParserTest { - - private static final String VALID_JSON_RULES = "[{\"category\":\"Localization\",\"key\":\"HardcodedUIString\",\"name\":\"UI String not localized\",\"description\":\"All strings that are given to APIs that display them in the user interface should be routed through NSLocalizedString() and friends.\",\"severity\":\"MAJOR\"}]"; - - @Test - public void parseShouldReturnAnEmptyListIfNoValidRulesFile() throws IOException { - FauxPasRuleParser parser = new FauxPasRuleParser(); - List rules = parser.parse(new BufferedReader(new StringReader(""))); - - assertTrue(rules.isEmpty()); - } - - @Test - public void parseShouldReturnAListOfRulesIfValideFile() throws IOException { - - FauxPasRuleParser parser = new FauxPasRuleParser(); - List rules = parser.parse(new BufferedReader(new StringReader(VALID_JSON_RULES))); - - assertEquals(1, rules.size()); - } -} diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java index 02ecbf63..410d9d0d 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java @@ -22,6 +22,7 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; import org.sonar.plugins.objectivec.core.ObjectiveC; @@ -52,11 +53,12 @@ public void shouldExecuteOnProjectShouldBeTrueWhenProjectIsObjc() { final Project project = new Project("Test"); FileSystem fileSystem = mock(FileSystem.class); + ResourcePerspectives resourcePerspectives = mock(ResourcePerspectives.class); SortedSet languages = new TreeSet(); languages.add(ObjectiveC.KEY); when(fileSystem.languages()).thenReturn(languages); - final FauxPasSensor testedSensor = new FauxPasSensor(fileSystem, settings); + final FauxPasSensor testedSensor = new FauxPasSensor(fileSystem, settings, resourcePerspectives); assertTrue(testedSensor.shouldExecuteOnProject(project)); } @@ -66,11 +68,12 @@ public void shouldExecuteOnProjectShouldBeFalseWhenProjectIsSomethingElse() { final Project project = new Project("Test"); FileSystem fileSystem = mock(FileSystem.class); + ResourcePerspectives resourcePerspectives = mock(ResourcePerspectives.class); SortedSet languages = new TreeSet(); languages.add("Test"); when(fileSystem.languages()).thenReturn(languages); - final FauxPasSensor testedSensor = new FauxPasSensor(fileSystem, settings); + final FauxPasSensor testedSensor = new FauxPasSensor(fileSystem, settings, resourcePerspectives); assertFalse(testedSensor.shouldExecuteOnProject(project)); } diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java deleted file mode 100644 index 8790896b..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParserTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.violations.oclint; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -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; - -import org.apache.tools.ant.filters.StringInputStream; -import org.junit.Test; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.ProjectFileSystem; -import org.sonar.api.resources.Resource; -import org.sonar.api.rules.Violation; -import org.sonar.plugins.objectivec.violations.oclint.OCLintParser; - -public class OCLintParserTest { - private final String VALID_REPORT = "An operation on an Immutable object (String, BigDecimal or BigInteger) won't change the object itself"; - - @Test - public void parseReportShouldReturnAnEmptyCollectionWhenTheReportIsInvalid() { - final OCLintParser testedParser = new OCLintParser(null, null); - final Collection violations = testedParser.parseReport(new StringInputStream("")); - - assertTrue(violations.isEmpty()); - } - - @Test - public void parseReportShouldReturnAnEmptyMapWhenTheFileIsInvalid() { - final OCLintParser testedParser = new OCLintParser(null, null); - final Collection violations = testedParser.parseReport(new File("")); - - assertTrue(violations.isEmpty()); - } - - @Test - public void parseReportShouldReturnACollectionOfViolationsWhenTheReportIsNotEmpty() { - final Project project = new Project("Test"); - 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 OCLintParser testedParser = new OCLintParser(project, context); - - 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); - - final Collection violations = testedParser.parseReport(new StringInputStream(VALID_REPORT)); - assertFalse(violations.isEmpty()); - } - - -} diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java index d78c742e..62aed770 100644 --- a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java +++ b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java @@ -27,6 +27,7 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; import org.sonar.plugins.objectivec.core.ObjectiveC; @@ -48,12 +49,13 @@ public void setUp() { public void shouldExecuteOnProjectShouldBeTrueWhenProjectIsObjc() { final Project project = new Project("Test"); + ResourcePerspectives resourcePerspectives = mock(ResourcePerspectives.class); FileSystem fileSystem = mock(FileSystem.class); SortedSet languages = new TreeSet(); languages.add(ObjectiveC.KEY); when(fileSystem.languages()).thenReturn(languages); - final OCLintSensor testedSensor = new OCLintSensor(fileSystem, settings); + final OCLintSensor testedSensor = new OCLintSensor(fileSystem, settings, resourcePerspectives); assertTrue(testedSensor.shouldExecuteOnProject(project)); } @@ -62,12 +64,13 @@ public void shouldExecuteOnProjectShouldBeTrueWhenProjectIsObjc() { public void shouldExecuteOnProjectShouldBeFalseWhenProjectIsSomethingElse() { final Project project = new Project("Test"); + ResourcePerspectives resourcePerspectives = mock(ResourcePerspectives.class); FileSystem fileSystem = mock(FileSystem.class); SortedSet languages = new TreeSet(); languages.add("Test"); when(fileSystem.languages()).thenReturn(languages); - final OCLintSensor testedSensor = new OCLintSensor(fileSystem, settings); + final OCLintSensor testedSensor = new OCLintSensor(fileSystem, settings, resourcePerspectives); assertFalse(testedSensor.shouldExecuteOnProject(project)); } diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java deleted file mode 100644 index ffde0c59..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandlerTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.violations.oclint; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -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 javax.xml.stream.XMLStreamException; - -import org.apache.tools.ant.filters.StringInputStream; -import org.junit.Test; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.ProjectFileSystem; -import org.sonar.api.resources.Resource; -import org.sonar.api.rules.Violation; -import org.sonar.api.utils.StaxParser; - -public class OCLintXMLStreamHandlerTest { - private static final String EMPTY_REPORT = ""; - private static final String DESCRIPTION = "TEST DESCRIPTION"; - private static final Integer VIOLATION_LINE = Integer.valueOf(99); - private static final String RULE_KEY = "TEST RULE"; - private static final String VALID_REPORT = "" + DESCRIPTION + ""; - private ProjectBuilder projectBuilder; - - @Test - public void streamLeavesTheCollectionEmptyWhenNoLinesAreFound() throws XMLStreamException { - final Collection parseResults = new ArrayList(); - final StaxParser parser = new StaxParser(new OCLintXMLStreamHandler(parseResults, null, null)); - - parser.parse(new StringInputStream(EMPTY_REPORT)); - - assertTrue(parseResults.isEmpty()); - } - - @Test - public void streamAddAviolationForALineInTheReport() 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 Collection 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)); - - assertFalse(parseResults.isEmpty()); - } - - private Project project() { - Project project = givenAProject().project(); - ProjectFileSystem fileSystem = mock(ProjectFileSystem.class); - project.setFileSystem(fileSystem); - when(fileSystem.getBasedir()).thenReturn(new File(".")); - return project; - } - - private ProjectBuilder givenAProject() { - projectBuilder = new ProjectBuilder(); - return projectBuilder; - } - -} From 6bc5e90f8cd24ff85579abcfe187acf670c61cd7 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 29 Feb 2016 18:42:41 +0100 Subject: [PATCH 052/107] Finished deprecated API refactoring --- build-and-deploy.sh | 1 + .../plugins/objectivec/ObjectiveCPlugin.java | 2 - .../objectivec/ObjectiveCSquidSensor.java | 106 +++++++++++------- .../core/ObjectiveCSourceImporter.java | 41 ------- .../objectivec/tests/SurefireParser.java | 70 ++++++++++-- .../objectivec/tests/SurefireSensor.java | 25 +++-- .../violations/oclint/OCLintSensor.java | 2 - .../oclint/OCLintXMLStreamHandler.java | 7 +- .../com/sonar/sqale/oclint-model.xml | 75 ------------- .../sonar/plugins/fauxpas/profile-fauxpas.xml | 4 +- .../org/sonar/plugins/fauxpas/rules.json | 12 +- 11 files changed, 152 insertions(+), 193 deletions(-) delete mode 100644 src/main/java/org/sonar/plugins/objectivec/core/ObjectiveCSourceImporter.java diff --git a/build-and-deploy.sh b/build-and-deploy.sh index 2e449e75..77d5f6a4 100755 --- a/build-and-deploy.sh +++ b/build-and-deploy.sh @@ -11,6 +11,7 @@ fi # Deploy new verion of plugin in Sonar dir rm -rf $SONARQUBE_HOME/extensions/plugins/sonar-objective-c-* cp target/*.jar $SONARQUBE_HOME/extensions/plugins +rm $SONARQUBE_HOME/extensions/plugins/*sources.jar # Stop/start Sonar unset GEM_PATH GEM_HOME diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java index e77f88f9..fd7e8a95 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java @@ -28,7 +28,6 @@ import org.sonar.plugins.objectivec.coverage.CoberturaSensor; import org.sonar.plugins.objectivec.colorizer.ObjectiveCColorizerFormat; import org.sonar.plugins.objectivec.core.ObjectiveC; -import org.sonar.plugins.objectivec.core.ObjectiveCSourceImporter; import org.sonar.plugins.objectivec.cpd.ObjectiveCCpdMapping; import com.google.common.collect.ImmutableList; @@ -50,7 +49,6 @@ public class ObjectiveCPlugin extends SonarPlugin { public List> getExtensions() { return ImmutableList.of(ObjectiveC.class, - ObjectiveCSourceImporter.class, ObjectiveCColorizerFormat.class, ObjectiveCCpdMapping.class, diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java index ddabf32c..c320d1b1 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java @@ -22,17 +22,27 @@ import java.util.Collection; import java.util.Locale; +import com.google.common.collect.ImmutableList; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FilePredicate; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.CheckFactory; +import org.sonar.api.batch.rule.Checks; import org.sonar.api.checks.AnnotationCheckFactory; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.PersistenceMode; import org.sonar.api.measures.RangeDistributionBuilder; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.File; -import org.sonar.api.resources.InputFileUtils; import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Violation; +import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.objectivec.ObjectiveCAstScanner; import org.sonar.objectivec.ObjectiveCConfiguration; import org.sonar.objectivec.api.ObjectiveCGrammar; @@ -47,7 +57,6 @@ import org.sonar.squidbridge.checks.SquidCheck; import org.sonar.squidbridge.indexer.QueryByParent; import org.sonar.squidbridge.indexer.QueryByType; -import org.sonar.squidbridge.measures.Metric; public class ObjectiveCSquidSensor implements Sensor { @@ -56,17 +65,30 @@ public class ObjectiveCSquidSensor implements Sensor { private final Number[] FILES_DISTRIB_BOTTOM_LIMITS = {0, 5, 10, 20, 30, 60, 90}; private final AnnotationCheckFactory annotationCheckFactory; + private final FileSystem fileSystem; + private final PathResolver pathResolver; + private final ResourcePerspectives resourcePerspectives; + private final Checks> checks; + private final FilePredicate mainFilePredicates; + private Project project; private SensorContext context; private AstScanner scanner; - public ObjectiveCSquidSensor(RulesProfile profile) { + public ObjectiveCSquidSensor(RulesProfile profile, FileSystem fileSystem, PathResolver pathResolver, ResourcePerspectives resourcePerspectives, CheckFactory checkFactory) { this.annotationCheckFactory = AnnotationCheckFactory.create(profile, CheckList.REPOSITORY_KEY, CheckList.getChecks()); + this.fileSystem = fileSystem; + this.pathResolver = pathResolver; + this.resourcePerspectives = resourcePerspectives; + this.checks = checkFactory.>create(CheckList.REPOSITORY_KEY).addAnnotatedChecks(CheckList.getChecks()); + this.mainFilePredicates = fileSystem.predicates().and(fileSystem.predicates().hasLanguage(ObjectiveC.KEY), fileSystem.predicates().hasType(InputFile.Type.MAIN)); } public boolean shouldExecuteOnProject(Project project) { - return ObjectiveC.KEY.equals(project.getLanguageKey()); + + return project.isRoot() && fileSystem.hasFiles(fileSystem.predicates().hasLanguage(ObjectiveC.KEY)); + } public void analyse(Project project, SensorContext context) { @@ -74,63 +96,65 @@ public void analyse(Project project, SensorContext context) { this.context = context; Collection squidChecks = annotationCheckFactory.getChecks(); - this.scanner = ObjectiveCAstScanner.create(createConfiguration(project), squidChecks.toArray(new SquidCheck[squidChecks.size()])); - scanner.scanFiles(InputFileUtils.toFiles(project.getFileSystem().mainFiles(ObjectiveC.KEY))); + this.scanner = ObjectiveCAstScanner.create(createConfiguration(), squidChecks.toArray(new SquidCheck[squidChecks.size()])); + scanner.scanFiles(ImmutableList.copyOf(fileSystem.files(mainFilePredicates))); Collection squidSourceFiles = scanner.getIndex().search(new QueryByType(SourceFile.class)); save(squidSourceFiles); } - private ObjectiveCConfiguration createConfiguration(Project project) { - return new ObjectiveCConfiguration(project.getFileSystem().getSourceCharset()); + private ObjectiveCConfiguration createConfiguration() { + + return new ObjectiveCConfiguration(fileSystem.encoding()); } private void save(Collection squidSourceFiles) { + for (SourceCode squidSourceFile : squidSourceFiles) { SourceFile squidFile = (SourceFile) squidSourceFile; - File sonarFile = File.fromIOFile(new java.io.File(squidFile.getKey()), project); + String relativePath = pathResolver.relativePath(fileSystem.baseDir(), new java.io.File(squidFile.getKey())); + InputFile inputFile = fileSystem.inputFile(fileSystem.predicates().hasRelativePath(relativePath)); - saveFilesComplexityDistribution(sonarFile, squidFile); - saveFunctionsComplexityDistribution(sonarFile, squidFile); - saveMeasures(sonarFile, squidFile); - saveViolations(sonarFile, squidFile); + saveMeasures(inputFile, squidFile); + saveIssues(inputFile, squidFile); } } - private void saveMeasures(File sonarFile, SourceFile squidFile) { - context.saveMeasure(sonarFile, CoreMetrics.FILES, squidFile.getDouble(ObjectiveCMetric.FILES)); - context.saveMeasure(sonarFile, CoreMetrics.LINES, squidFile.getDouble(ObjectiveCMetric.LINES)); - context.saveMeasure(sonarFile, CoreMetrics.NCLOC, squidFile.getDouble(ObjectiveCMetric.LINES_OF_CODE)); - 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_LINES, squidFile.getDouble(ObjectiveCMetric.COMMENT_LINES)); + private void saveMeasures(InputFile inputFile, SourceFile squidFile) { + context.saveMeasure(inputFile, CoreMetrics.FILES, squidFile.getDouble(ObjectiveCMetric.FILES)); + context.saveMeasure(inputFile, CoreMetrics.LINES, squidFile.getDouble(ObjectiveCMetric.LINES)); + context.saveMeasure(inputFile, CoreMetrics.NCLOC, squidFile.getDouble(ObjectiveCMetric.LINES_OF_CODE)); + context.saveMeasure(inputFile, CoreMetrics.FUNCTIONS, squidFile.getDouble(ObjectiveCMetric.FUNCTIONS)); + context.saveMeasure(inputFile, CoreMetrics.STATEMENTS, squidFile.getDouble(ObjectiveCMetric.STATEMENTS)); + context.saveMeasure(inputFile, CoreMetrics.COMPLEXITY, squidFile.getDouble(ObjectiveCMetric.COMPLEXITY)); + context.saveMeasure(inputFile, CoreMetrics.COMMENT_LINES, squidFile.getDouble(ObjectiveCMetric.COMMENT_LINES)); } - private void saveFunctionsComplexityDistribution(File sonarFile, SourceFile squidFile) { - Collection squidFunctionsInFile = scanner.getIndex().search(new QueryByParent(squidFile), new QueryByType(SourceFunction.class)); - RangeDistributionBuilder complexityDistribution = new RangeDistributionBuilder(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, FUNCTIONS_DISTRIB_BOTTOM_LIMITS); - for (SourceCode squidFunction : squidFunctionsInFile) { - complexityDistribution.add(squidFunction.getDouble(ObjectiveCMetric.COMPLEXITY)); - } - context.saveMeasure(sonarFile, complexityDistribution.build().setPersistenceMode(PersistenceMode.MEMORY)); - } + private void saveIssues(InputFile inputFile, SourceFile squidFile) { - private void saveFilesComplexityDistribution(File sonarFile, SourceFile squidFile) { - RangeDistributionBuilder complexityDistribution = new RangeDistributionBuilder(CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION, FILES_DISTRIB_BOTTOM_LIMITS); - complexityDistribution.add(squidFile.getDouble(ObjectiveCMetric.COMPLEXITY)); - context.saveMeasure(sonarFile, complexityDistribution.build().setPersistenceMode(PersistenceMode.MEMORY)); - } - - private void saveViolations(File sonarFile, SourceFile squidFile) { Collection messages = squidFile.getCheckMessages(); - if (messages != null) { + + Resource resource = context.getResource(org.sonar.api.resources.File.fromIOFile(inputFile.file(), project)); + + if (messages != null && resource != null) { for (CheckMessage message : messages) { - Violation violation = Violation.create(annotationCheckFactory.getActiveRule(message.getChecker()), sonarFile) - .setLineId(message.getLine()) - .setMessage(message.getText(Locale.ENGLISH)); - context.saveViolation(violation); + RuleKey ruleKey = checks.ruleKey((SquidCheck) message.getCheck()); + Issuable issuable = resourcePerspectives.as(Issuable.class, resource); + + if (issuable != null) { + Issuable.IssueBuilder issueBuilder = issuable.newIssueBuilder() + .ruleKey(ruleKey) + .line(message.getLine()) + .message(message.getText(Locale.ENGLISH)); + + if (message.getCost() != null) { + issueBuilder.effortToFix(message.getCost()); + } + + issuable.addIssue(issueBuilder.build()); + } + } } } diff --git a/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveCSourceImporter.java b/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveCSourceImporter.java deleted file mode 100644 index 8398f430..00000000 --- a/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveCSourceImporter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.core; - -import org.sonar.api.batch.AbstractSourceImporter; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.InputFileUtils; -import org.sonar.api.resources.ProjectFileSystem; - -public class ObjectiveCSourceImporter extends AbstractSourceImporter { - - public ObjectiveCSourceImporter(ObjectiveC objectivec) { - super(objectivec); - } - - protected void analyse(ProjectFileSystem fileSystem, SensorContext context) { - parseDirs(context, InputFileUtils.toFiles(fileSystem.mainFiles(ObjectiveC.KEY)), fileSystem.getSourceDirs(), false, fileSystem.getSourceCharset()); - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } -} diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java index 9ddd9c57..d15e41bf 100644 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java @@ -19,12 +19,16 @@ */ package org.sonar.plugins.objectivec.tests; +import com.google.common.collect.ImmutableList; import com.sun.swing.internal.plaf.metal.resources.metal_sv; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; @@ -52,7 +56,20 @@ public class SurefireParser { private static final Logger LOG = LoggerFactory.getLogger(SurefireParser.class); - public void collect(Project project, SensorContext context, File reportsDir) { + private final Project project; + private final FileSystem fileSystem; + private final ResourcePerspectives resourcePerspectives; + private final SensorContext context; + + public SurefireParser(Project project, FileSystem fileSystem, ResourcePerspectives resourcePerspectives, SensorContext context) { + this.project = project; + this.fileSystem = fileSystem; + this.resourcePerspectives = resourcePerspectives; + this.context = context; + } + + public void collect(File reportsDir) { + File[] xmlFiles = getReports(reportsDir); if (xmlFiles.length == 0) { @@ -81,9 +98,8 @@ public boolean accept(File dir, String name) { } private void insertZeroWhenNoReports(Project pom, SensorContext context) { - if ( !StringUtils.equalsIgnoreCase("pom", pom.getPackaging())) { - context.saveMeasure(CoreMetrics.TESTS, 0.0); - } + + context.saveMeasure(CoreMetrics.TESTS, 0.0); } private void parseFiles(SensorContext context, File[] reports) { @@ -146,8 +162,15 @@ private void saveTestsDetails(SensorContext context, TestSuiteReport fileReport) } private void saveClassMeasure(SensorContext context, TestSuiteReport fileReport, Metric metric, double value) { + if ( !Double.isNaN(value)) { + context.saveMeasure(getUnitTestResource(fileReport.getClassKey()), metric, value); + + } + + /*if ( !Double.isNaN(value)) { + String basename = fileReport.getClassKey().replace('.', '/'); // .m file @@ -159,13 +182,46 @@ private void saveClassMeasure(SensorContext context, TestSuiteReport fileReport, } catch (Exception e) { // Nothing : File was probably already registered successfully } - } + }*/ } - public Resource getUnitTestResource(String filename) { + public Resource getUnitTestResource(String classname) { + + String fileName = classname.replace('.', '/') + ".m"; + + File file = new File(fileName); + if (!file.isAbsolute()) { + file = new File(fileSystem.baseDir(), fileName); + } + + // Category naming case + if (!file.isFile() || !file.exists()) { + file = new File(fileSystem.baseDir(), fileName.replace("_", "+")); + } - org.sonar.api.resources.File sonarFile = new org.sonar.api.resources.File(filename); + /* + * Most xcodebuild JUnit parsers don't include the path to the class in the class field, so search for it if it + * wasn't found in the root. + */ + if (!file.isFile() || !file.exists()) { + List files = ImmutableList.copyOf(fileSystem.files(fileSystem.predicates().and( + fileSystem.predicates().hasType(InputFile.Type.TEST), + fileSystem.predicates().matchesPathPattern("**/" + fileName)))); + + if (files.isEmpty()) { + LOG.info("Unable to locate test source file {}", fileName); + } else { + /* + * Lazily get the first file, since we wouldn't be able to determine the correct one from just the + * test class name in the event that there are multiple matches. + */ + file = files.get(0); + } + } + + org.sonar.api.resources.File sonarFile = org.sonar.api.resources.File.fromIOFile(file, project); 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 431a3054..9396593f 100644 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java @@ -25,6 +25,8 @@ import org.sonar.api.batch.DependsUpon; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; import org.sonar.plugins.objectivec.core.ObjectiveC; @@ -36,14 +38,15 @@ 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; - public SurefireSensor() { - this(null); - } + private final Settings settings; + private final FileSystem fileSystem; + private final ResourcePerspectives resourcePerspectives; - public SurefireSensor(final Settings config) { - conf = config; + public SurefireSensor(final FileSystem fileSystem, final Settings config, final ResourcePerspectives resourcePerspectives) { + this.settings = config; + this.fileSystem = fileSystem; + this.resourcePerspectives = resourcePerspectives; } @DependsUpon @@ -52,7 +55,8 @@ public Class dependsUponCoverageSensors() { } public boolean shouldExecuteOnProject(Project project) { - return ObjectiveC.KEY.equals(project.getLanguageKey()); + + return project.isRoot() && fileSystem.hasFiles(fileSystem.predicates().hasLanguage(ObjectiveC.KEY)); } public void analyse(Project project, SensorContext context) { @@ -78,18 +82,17 @@ that is very different (and does not contain a matching method). protected void collect(Project project, SensorContext context, File reportsDir) { LOG.info("parsing {}", reportsDir); - SUREFIRE_PARSER.collect(project, context, reportsDir); + SurefireParser parser = new SurefireParser(project, fileSystem, resourcePerspectives, context); + parser.collect(reportsDir); } - private static final SurefireParser SUREFIRE_PARSER = new SurefireParser(); - @Override public String toString() { return "Objective-C SurefireSensor"; } private String reportPath() { - String reportPath = conf.getString(REPORT_PATH_KEY); + String reportPath = settings.getString(REPORT_PATH_KEY); if (reportPath == null) { reportPath = DEFAULT_REPORT_PATH; } diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java index 4134f09c..d7546def 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java @@ -31,10 +31,8 @@ import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; -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.fauxpas.FauxPasReportParser; public final class OCLintSensor implements Sensor { public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX + ".oclint.report"; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java index b31b02cc..162f9c7f 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java @@ -25,7 +25,6 @@ import org.codehaus.staxmate.in.SMHierarchicCursor; import org.codehaus.staxmate.in.SMInputCursor; -import org.json.simple.JSONObject; import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; import org.sonar.api.component.ResourcePerspectives; @@ -33,11 +32,7 @@ import org.sonar.api.issue.Issue; import org.sonar.api.resources.Project; import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.rules.Violation; import org.sonar.api.utils.StaxParser.XmlStreamHandler; -import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasRulesDefinition; final class OCLintXMLStreamHandler implements XmlStreamHandler { private static final int PMD_MINIMUM_PRIORITY = 5; @@ -90,7 +85,7 @@ private void recordViolation(final org.sonar.api.resources.File resource, final if (issuable != null) { Issue issue = issuable.newIssueBuilder() - .ruleKey(RuleKey.of(FauxPasRulesDefinition.REPOSITORY_KEY, line.getAttrValue("rule"))) + .ruleKey(RuleKey.of(OCLintRulesDefinition.REPOSITORY_KEY, line.getAttrValue("rule"))) .line(Integer.valueOf(line.getAttrValue("beginline"))) .message(line.getElemStringValue()) .build(); diff --git a/src/main/resources/com/sonar/sqale/oclint-model.xml b/src/main/resources/com/sonar/sqale/oclint-model.xml index 891b4275..e20af078 100644 --- a/src/main/resources/com/sonar/sqale/oclint-model.xml +++ b/src/main/resources/com/sonar/sqale/oclint-model.xml @@ -9,81 +9,6 @@ TRANSPORTABILITY Transportability - - FauxPas - AbsPathInBuildSetting - - remediationFunction - linear - - - remediationFactor - 30 - mn - - - offset - 0.0 - d - - - - FauxPas - FileRefWithAbsPath - - remediationFunction - linear - - - remediationFactor - 30 - mn - - - offset - 0.0 - d - - - - FauxPas - StringsFileEncoding - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - FauxPas - FileRefOutsideVCS - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - - - FauxPas - FileRefIgnoredInVCS - - remediationFunction - CONSTANT_ISSUE - - - offset - 10 - min - - diff --git a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml index 67742775..9bec912b 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml +++ b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml @@ -341,11 +341,11 @@
FauxPas - StrongInsteadOfRetain + NullCoalescingOp FauxPas - NullCoalescingOp + StrongInsteadOfRetain FauxPas diff --git a/src/main/resources/org/sonar/plugins/fauxpas/rules.json b/src/main/resources/org/sonar/plugins/fauxpas/rules.json index e67aea38..b47b5efd 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/rules.json +++ b/src/main/resources/org/sonar/plugins/fauxpas/rules.json @@ -589,16 +589,16 @@ }, { "category": "Style", - "key": "StrongInsteadOfRetain", - "name": "Usage of retain in ARC code", - "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", + "key": "NullCoalescingOp", + "name": "Null coalescing operator usage", + "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", "severity": "MAJOR" }, { "category": "Style", - "key": "NullCoalescingOp", - "name": "Null coalescing operator usage", - "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", + "key": "StrongInsteadOfRetain", + "name": "Usage of retain in ARC code", + "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", "severity": "MAJOR" }, { From 13b62389b2f6aaf0662a532149f8c3c5f9efcbe6 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 29 Feb 2016 18:46:20 +0100 Subject: [PATCH 053/107] Version update --- README.md | 4 ++++ pom.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 909d76a1..792b5212 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,10 @@ Binary packages are available in the release section. ###Release history +####0.5 (detached from octo project) +- Detached from octo project (to hard to maintain compatibility) +- Removed deprecated API usages for Sonarube 5.3 support + ####0.4.0.3 (based on 0.4.0) - Xcode 7 coverage support (profdata) diff --git a/pom.xml b/pom.xml index 0a355044..91de82f0 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec sonar-objective-c-plugin - 0.4.0.3 + 0.5 sonar-plugin From 12b2a7a1a40c839bbcef370b5bd2c29b2097a075 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 29 Feb 2016 18:47:39 +0100 Subject: [PATCH 054/107] Version update --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 792b5212..1a57852d 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Binary packages are available in the release section. ###Release history -####0.5 (detached from octo project) +####0.5.0 (detached from octo project) - Detached from octo project (to hard to maintain compatibility) - Removed deprecated API usages for Sonarube 5.3 support diff --git a/pom.xml b/pom.xml index 91de82f0..7fd89898 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec sonar-objective-c-plugin - 0.5 + 0.5.0 sonar-plugin From 097b37adc9bbd4d80c98068362ed3c045634cdb1 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 3 Mar 2016 02:04:40 +0100 Subject: [PATCH 055/107] Complexity with Lizard --- README.md | 8 +- build-and-deploy.sh | 2 +- pom.xml | 6 +- .../plugins/objectivec/ObjectiveCPlugin.java | 8 +- .../objectivec/ObjectiveCSquidSensor.java | 2 - .../complexity/LizardMeasurePersistor.java | 79 ++++++ .../complexity/LizardReportParser.java | 254 ++++++++++++++++++ .../objectivec/complexity/LizardSensor.java | 107 ++++++++ src/main/shell/run-sonar.sh | 14 + .../complexity/LizardReportParserTest.java | 206 ++++++++++++++ .../complexity/LizardSensorTest.java | 84 ++++++ 11 files changed, 760 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java create mode 100644 src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java create mode 100644 src/test/java/org/sonar/plugins/objectivec/complexity/LizardSensorTest.java diff --git a/README.md b/README.md index 792b5212..66f3b76d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://g | Feature | Supported | Details | |---------------|----------|:-----------:| -| Complexity |NO | | +| Complexity |NO | Uses [Lizard](https://github.com/terryyin/lizard) | | Design |NO | | | Documentation |YES | | | Duplications |YES | | @@ -50,7 +50,11 @@ Binary packages are available in the release section. ###Release history -####0.5 (detached from octo project) +####0.5.1 +- Complexity with Lizard ! + + +####0.5.0 (detached from octo project) - Detached from octo project (to hard to maintain compatibility) - Removed deprecated API usages for Sonarube 5.3 support diff --git a/build-and-deploy.sh b/build-and-deploy.sh index 77d5f6a4..bb1ebcd3 100755 --- a/build-and-deploy.sh +++ b/build-and-deploy.sh @@ -9,7 +9,7 @@ if [ "$?" != 0 ]; then fi # Deploy new verion of plugin in Sonar dir -rm -rf $SONARQUBE_HOME/extensions/plugins/sonar-objective-c-* +rm -rf $SONARQUBE_HOME/extensions/plugins/*sonar-objective-c-* cp target/*.jar $SONARQUBE_HOME/extensions/plugins rm $SONARQUBE_HOME/extensions/plugins/*sources.jar diff --git a/pom.xml b/pom.xml index 91de82f0..2100026b 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.codehaus.sonar-plugin.objectivec - sonar-objective-c-plugin + backelite-sonar-objective-c-plugin 0.5 sonar-plugin @@ -60,7 +60,7 @@ zippy1978 Gilles Grousset Backelite - + fhelg François Helg @@ -100,7 +100,7 @@ org.sonar.plugins.objectivec.ObjectiveCPlugin - ObjectiveC + Objective-C (Backelite) diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java index fd7e8a95..0f0be8b0 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java @@ -25,6 +25,7 @@ import org.sonar.api.Properties; import org.sonar.api.Property; import org.sonar.api.SonarPlugin; +import org.sonar.plugins.objectivec.complexity.LizardSensor; import org.sonar.plugins.objectivec.coverage.CoberturaSensor; import org.sonar.plugins.objectivec.colorizer.ObjectiveCColorizerFormat; import org.sonar.plugins.objectivec.core.ObjectiveC; @@ -43,7 +44,8 @@ @Properties({ @Property(key = CoberturaSensor.REPORT_PATTERN_KEY, defaultValue = CoberturaSensor.DEFAULT_REPORT_PATTERN, name = "Path to unit test coverage report(s)", description = "Relative to projects' root. Ant patterns are accepted", global = false, project = true), @Property(key = OCLintSensor.REPORT_PATH_KEY, defaultValue = OCLintSensor.DEFAULT_REPORT_PATH, name = "Path to oclint pmd formatted report", description = "Relative to projects' root.", global = false, project = true), - @Property(key = FauxPasSensor.REPORT_PATH_KEY, defaultValue = FauxPasSensor.DEFAULT_REPORT_PATH, name = "Path to fauxpas json formatted report", description = "Relative to projects' root.", global = false, project = true) + @Property(key = FauxPasSensor.REPORT_PATH_KEY, defaultValue = FauxPasSensor.DEFAULT_REPORT_PATH, name = "Path to fauxpas json formatted report", description = "Relative to projects' root.", global = false, project = true), + @Property(key = LizardSensor.REPORT_PATH_KEY, defaultValue = LizardSensor.DEFAULT_REPORT_PATH, name = "Path to lizard report", description = "Relative to projects' root.", global = false, project = true) }) public class ObjectiveCPlugin extends SonarPlugin { @@ -65,7 +67,9 @@ public List> getExtensions() { FauxPasSensor.class, FauxPasRulesDefinition.class, FauxPasProfile.class, - FauxPasProfileImporter.class + FauxPasProfileImporter.class, + + LizardSensor.class ); } diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java index c320d1b1..77ccb9e3 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java @@ -125,9 +125,7 @@ private void saveMeasures(InputFile inputFile, SourceFile squidFile) { context.saveMeasure(inputFile, CoreMetrics.FILES, squidFile.getDouble(ObjectiveCMetric.FILES)); context.saveMeasure(inputFile, CoreMetrics.LINES, squidFile.getDouble(ObjectiveCMetric.LINES)); context.saveMeasure(inputFile, CoreMetrics.NCLOC, squidFile.getDouble(ObjectiveCMetric.LINES_OF_CODE)); - context.saveMeasure(inputFile, CoreMetrics.FUNCTIONS, squidFile.getDouble(ObjectiveCMetric.FUNCTIONS)); context.saveMeasure(inputFile, CoreMetrics.STATEMENTS, squidFile.getDouble(ObjectiveCMetric.STATEMENTS)); - context.saveMeasure(inputFile, CoreMetrics.COMPLEXITY, squidFile.getDouble(ObjectiveCMetric.COMPLEXITY)); context.saveMeasure(inputFile, CoreMetrics.COMMENT_LINES, squidFile.getDouble(ObjectiveCMetric.COMMENT_LINES)); } diff --git a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java b/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java new file mode 100644 index 00000000..02144bee --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java @@ -0,0 +1,79 @@ +/* + * Sonar Objective-C Plugin + * Copyright (C) 2012 OCTO Technology, Backelite + * 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.complexity; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Project; + +import java.io.File; +import java.util.List; +import java.util.Map; + +/** + * This class is used to save the measures created by the lizardReportParser in the sonar database + * + * @author Andres Gil Herrera + * @since 28/05/15. + */ +public class LizardMeasurePersistor { + + private static final Logger LOGGER = LoggerFactory.getLogger(LizardMeasurePersistor.class); + + private final Project project; + private final SensorContext sensorContext; + private final FileSystem fileSystem; + + public LizardMeasurePersistor(final Project project, final SensorContext sensorContext, final FileSystem fileSystem) { + this.project = project; + this.sensorContext = sensorContext; + this.fileSystem = fileSystem; + } + + /** + * + * @param measures Map containing as key the name of the file and as value a list containing the measures for that file + */ + public void saveMeasures(final Map> measures) { + + if (measures == null) { + return; + } + + for (Map.Entry> entry : measures.entrySet()) { + final org.sonar.api.resources.File file = org.sonar.api.resources.File.fromIOFile(new File(fileSystem.baseDir(), entry.getKey()), project); + + if (sensorContext.getResource(file) != null) { + for (Measure measure : entry.getValue()) { + try { + LOGGER.debug("Save measure {} for file {}", measure.getMetric().getName(), file); + sensorContext.saveMeasure(file, measure); + } catch (Exception e) { + LOGGER.error(" Exception -> {} -> {}", entry.getKey(), measure.getMetric().getName(), e); + } + } + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java b/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java new file mode 100644 index 00000000..fbf74b8b --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java @@ -0,0 +1,254 @@ +/* + * Sonar Objective-C Plugin + * Copyright (C) 2012 OCTO Technology, Backelite + * 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.complexity; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.measures.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class parses xml Reports form the tool Lizard in order to extract this measures: + * COMPLEXITY, FUNCTIONS, FUNCTION_COMPLEXITY, FUNCTION_COMPLEXITY_DISTRIBUTION, + * FILE_COMPLEXITY, FUNCTION_COMPLEXITY_DISTRIBUTION, COMPLEXITY_IN_FUNCTIONS + * + * @author Andres Gil Herrera + * @since 28/05/15 + */ +public class LizardReportParser { + + private static final Logger LOGGER = LoggerFactory.getLogger(LizardReportParser.class); + + private final Number[] FUNCTIONS_DISTRIB_BOTTOM_LIMITS = {1, 2, 4, 6, 8, 10, 12, 20, 30}; + private final Number[] FILES_DISTRIB_BOTTOM_LIMITS = {0, 5, 10, 20, 30, 60, 90}; + + private static final String MEASURE = "measure"; + private static final String MEASURE_TYPE = "type"; + private static final String MEASURE_ITEM = "item"; + private static final String FILE_MEASURE = "file"; + private static final String FUNCTION_MEASURE = "Function"; + private static final String NAME = "name"; + private static final String VALUE = "value"; + private static final int CYCLOMATIC_COMPLEXITY_INDEX = 2; + private static final int FUNCTIONS_INDEX = 3; + + /** + * + * @param xmlFile lizard xml report + * @return Map containing as key the name of the file and as value a list containing the measures for that file + */ + public Map> parseReport(final File xmlFile) { + Map> result = null; + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + try { + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(xmlFile); + result = parseFile(document); + } catch (final FileNotFoundException e){ + LOGGER.error("Lizard Report not found {}", xmlFile, e); + } catch (final IOException e) { + LOGGER.error("Error processing file named {}", xmlFile, e); + } catch (final ParserConfigurationException e) { + LOGGER.error("Error parsing file named {}", xmlFile, e); + } catch (final SAXException e) { + LOGGER.error("Error processing file named {}", xmlFile, e); + } + + return result; + } + + /** + * + * @param document Document object representing the lizard report + * @return Map containing as key the name of the file and as value a list containing the measures for that file + */ + private Map> parseFile(Document document) { + final Map> reportMeasures = new HashMap>(); + final List functions = new ArrayList(); + + NodeList nodeList = document.getElementsByTagName(MEASURE); + + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) node; + if (element.getAttribute(MEASURE_TYPE).equalsIgnoreCase(FILE_MEASURE)) { + NodeList itemList = element.getElementsByTagName(MEASURE_ITEM); + addComplexityFileMeasures(itemList, reportMeasures); + } else if(element.getAttribute(MEASURE_TYPE).equalsIgnoreCase(FUNCTION_MEASURE)) { + NodeList itemList = element.getElementsByTagName(MEASURE_ITEM); + collectFunctions(itemList, functions); + } + } + } + + addComplexityFunctionMeasures(reportMeasures, functions); + + return reportMeasures; + } + + /** + * This method extracts the values for COMPLEXITY, FUNCTIONS, FILE_COMPLEXITY + * + * @param itemList list of all items from a + * @param reportMeasures map to save the measures for each file + */ + private void addComplexityFileMeasures(NodeList itemList, Map> reportMeasures){ + for (int i = 0; i < itemList.getLength(); i++) { + Node item = itemList.item(i); + + if (item.getNodeType() == Node.ELEMENT_NODE) { + Element itemElement = (Element) item; + String fileName = itemElement.getAttribute(NAME); + NodeList values = itemElement.getElementsByTagName(VALUE); + int complexity = Integer.parseInt(values.item(CYCLOMATIC_COMPLEXITY_INDEX).getTextContent()); + double fileComplexity = Double.parseDouble(values.item(CYCLOMATIC_COMPLEXITY_INDEX).getTextContent()); + int numberOfFunctions = Integer.parseInt(values.item(FUNCTIONS_INDEX).getTextContent()); + + reportMeasures.put(fileName, buildMeasureList(complexity, fileComplexity, numberOfFunctions)); + } + } + } + + /** + * + * @param complexity overall complexity of the file + * @param fileComplexity file complexity + * @param numberOfFunctions number of functions in the file + * @return returns a list of tree measures COMPLEXITY, FUNCTIONS, FILE_COMPLEXITY with the values specified + */ + private List buildMeasureList(int complexity, double fileComplexity, int numberOfFunctions){ + List list = new ArrayList(); + list.add(new Measure(CoreMetrics.COMPLEXITY).setIntValue(complexity)); + list.add(new Measure(CoreMetrics.FUNCTIONS).setIntValue(numberOfFunctions)); + list.add(new Measure(CoreMetrics.FILE_COMPLEXITY, fileComplexity)); + RangeDistributionBuilder complexityDistribution = new RangeDistributionBuilder(CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION, FILES_DISTRIB_BOTTOM_LIMITS); + complexityDistribution.add(fileComplexity); + list.add(complexityDistribution.build().setPersistenceMode(PersistenceMode.MEMORY)); + return list; + } + + /** + * + * @param itemList NodeList of all items in a tag + * @param functions list to save the functions in the NodeList as ObjCFunction objects. + */ + private void collectFunctions(NodeList itemList, List functions) { + for (int i = 0; i < itemList.getLength(); i++) { + Node item = itemList.item(i); + if (item.getNodeType() == Node.ELEMENT_NODE) { + Element itemElement = (Element) item; + String name = itemElement.getAttribute(NAME); + String measure = itemElement.getElementsByTagName(VALUE).item(CYCLOMATIC_COMPLEXITY_INDEX).getTextContent(); + functions.add(new ObjCFunction(name, Integer.parseInt(measure))); + } + } + } + + /** + * + * @param reportMeasures map to save the measures for the different files + * @param functions list of ObjCFunction to extract the information needed to create + * FUNCTION_COMPLEXITY_DISTRIBUTION, FUNCTION_COMPLEXITY, COMPLEXITY_IN_FUNCTIONS + */ + private void addComplexityFunctionMeasures(Map> reportMeasures, List functions){ + for (Map.Entry> entry : reportMeasures.entrySet()) { + + RangeDistributionBuilder complexityDistribution = new RangeDistributionBuilder(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, FUNCTIONS_DISTRIB_BOTTOM_LIMITS); + int count = 0; + int complexityInFunctions = 0; + + for (ObjCFunction func : functions) { + if (func.getName().contains(entry.getKey())) { + complexityDistribution.add(func.getCyclomaticComplexity()); + count++; + complexityInFunctions += func.getCyclomaticComplexity(); + } + } + + if (count != 0) { + double complex = 0; + for (Measure m : entry.getValue()){ + if (m.getMetric().getKey().equalsIgnoreCase(CoreMetrics.FILE_COMPLEXITY.getKey())){ + complex = m.getValue(); + break; + } + } + + double complexMean = complex/(double)count; + entry.getValue().addAll(buildFunctionMeasuresList(complexMean, complexityInFunctions, complexityDistribution)); + } + } + } + + /** + * + * @param complexMean average complexity per function in a file + * @param complexityInFunctions Entire complexity in functions + * @param builder Builder ready to build FUNCTION_COMPLEXITY_DISTRIBUTION + * @return list of Measures containing FUNCTION_COMPLEXITY_DISTRIBUTION, FUNCTION_COMPLEXITY and COMPLEXITY_IN_FUNCTIONS + */ + public List buildFunctionMeasuresList(double complexMean, int complexityInFunctions, RangeDistributionBuilder builder){ + List list = new ArrayList(); + list.add(new Measure(CoreMetrics.FUNCTION_COMPLEXITY, complexMean)); + list.add(new Measure(CoreMetrics.COMPLEXITY_IN_FUNCTIONS).setIntValue(complexityInFunctions)); + list.add(builder.build().setPersistenceMode(PersistenceMode.MEMORY)); + return list; + } + + /** + * helper class to process the information the functions contained in a Lizard report + */ + private class ObjCFunction { + private String name; + private int cyclomaticComplexity; + + public ObjCFunction(String name, int cyclomaticComplexity) { + this.name = name; + this.cyclomaticComplexity = cyclomaticComplexity; + } + + public String getName() { + return name; + } + + public int getCyclomaticComplexity() { + return cyclomaticComplexity; + } + + } +} \ No newline at end of file diff --git a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java b/src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java new file mode 100644 index 00000000..302a3cae --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java @@ -0,0 +1,107 @@ +/* + * Sonar Objective-C Plugin + * Copyright (C) 2012 OCTO Technology, Backelite + * 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.complexity; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.config.Settings; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Project; +import org.sonar.plugins.objectivec.ObjectiveCPlugin; +import org.sonar.plugins.objectivec.core.ObjectiveC; + +import java.io.File; +import java.util.List; +import java.util.Map; + +/** + * This sensor searches for the report generated from the tool Lizard + * in order to save complexity metrics. + * + * @author Andres Gil Herrera + * @since 28/05/15 + */ +public class LizardSensor implements Sensor { + + private static final Logger LOGGER = LoggerFactory.getLogger(LizardSensor.class); + + public static final String REPORT_PATH_KEY = ObjectiveCPlugin.PROPERTY_PREFIX + ".lizard.report"; + public static final String DEFAULT_REPORT_PATH = "sonar-reports/lizard-report.xml"; + + private final Settings conf; + private final FileSystem fileSystem; + + public LizardSensor(final FileSystem moduleFileSystem, final Settings config) { + this.conf = config; + this.fileSystem = moduleFileSystem; + } + + /** + * + * @param project + * @return true if the project is root the root project and uses Objective-C + */ + @Override + public boolean shouldExecuteOnProject(Project project) { + return project.isRoot() && fileSystem.languages().contains(ObjectiveC.KEY); + } + + /** + * + * @param project + * @param sensorContext + */ + @Override + public void analyse(Project project, SensorContext sensorContext) { + final String projectBaseDir = fileSystem.baseDir().getPath(); + Map> measures = parseReportsIn(projectBaseDir, new LizardReportParser()); + LOGGER.info("Saving results of complexity analysis"); + new LizardMeasurePersistor(project, sensorContext, fileSystem).saveMeasures(measures); + } + + /** + * + * @param baseDir base directory of the project to search the report + * @param parser LizardReportParser to parse the report + * @return Map containing as key the name of the file and as value a list containing the measures for that file + */ + private Map> parseReportsIn(final String baseDir, LizardReportParser parser) { + final StringBuilder reportFileName = new StringBuilder(baseDir); + reportFileName.append("/").append(reportPath()); + LOGGER.info("Processing complexity report "); + return parser.parseReport(new File(reportFileName.toString())); + } + + /** + * + * @return the default report path or the one specified in the sonar-project.properties + */ + 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/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index f8b7ed0b..d1094407 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -10,6 +10,7 @@ XCTOOL_CMD=xcodebuild SLATHER_CMD=slather XCPRETTY_CMD=xcpretty +LIZARD_CMD=lizard trap "echo 'Script interrupted by Ctrl+C'; stopProgress; exit 1" SIGHUP SIGINT SIGTERM @@ -124,6 +125,7 @@ vflag="" nflag="" oclint="on" fauxpas="on" +lizard="on" while [ $# -gt 0 ] do case "$1" in @@ -404,6 +406,18 @@ else echo 'Skipping FauxPas' fi +# Lizard Complexity +if [ "$lizard" = "on" ]; then + if hash $LIZARD_CMD 2>/dev/null; then + echo -n 'Running Lizard...' + $LIZARD_CMD --xml "$srcDirs" > sonar-reports/lizard-report.xml + else + echo 'Skipping Lizard (not installed!)' + fi +else + echo 'Skipping Lizard (test purposes only!)' +fi + # SonarQube echo -n 'Running SonarQube using SonarQube Runner' runCommand /dev/stdout sonar-runner diff --git a/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java b/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java new file mode 100644 index 00000000..88a48428 --- /dev/null +++ b/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java @@ -0,0 +1,206 @@ +/* + * Sonar Objective-C Plugin + * Copyright (C) 2012 OCTO Technology, Backelite + * 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.complexity; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * @author Andres Gil Herrera + * @since 03/06/15. + */ +public class LizardReportParserTest { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + private File correctFile; + private File incorrectFile; + + @Before + public void setup() throws IOException { + correctFile = createCorrectFile(); + incorrectFile = createIncorrectFile(); + } + + /** + * + * @return dummy lizard xml report to test the parser + * @throws IOException + */ + public File createCorrectFile() throws IOException { + File xmlFile = folder.newFile("correctFile.xml"); + BufferedWriter out = new BufferedWriter(new FileWriter(xmlFile)); + //header + out.write(""); + out.write(""); + //root object and measure + out.write(""); + //items for function + out.write(""); + out.write("2151"); + out.write(""); + out.write("3205"); + //average and close funciton measure + out.write(""); + out.write(""); + out.write(""); + //open file measure and add the labels + out.write(""); + //items for file + out.write(""); + out.write("1200"); + out.write(""); + out.write("286862"); + //add averages + out.write(""); + //add sum + out.write(""); + //close measures and root object + out.write(""); + + out.close(); + + return xmlFile; + } + + /** + * + * @return corrupted dummy lizard report to test the parser + * @throws IOException + */ + public File createIncorrectFile() throws IOException { + File xmlFile = folder.newFile("incorrectFile.xml"); + BufferedWriter out = new BufferedWriter(new FileWriter(xmlFile)); + //header + out.write(""); + out.write(""); + //root object and measure + out.write(""); + //items for function + out.write(""); + out.write("2151"); + out.write(""); + out.write("3205"); + //average and close funciton measure + out.write(""); + out.write(""); + out.write(""); + //open file measure and add the labels + out.write(""); + //items for file 3th value tag has no closing tag + out.write(""); + out.write("1200"); + out.write(""); + out.write("286862"); + //add averages + out.write(""); + //add sum + out.write(""); + //close measures and root object no close tag for measure + out.write(""); + + out.close(); + + return xmlFile; + } + + /** + * this test case test that the parser extract all measures right + */ + @Test + public void parseReportShouldReturnMapWhenXMLFileIsCorrect() { + LizardReportParser parser = new LizardReportParser(); + + assertNotNull("correct file is null", correctFile); + + Map> report = parser.parseReport(correctFile); + + assertNotNull("report is null", report); + + assertTrue("Key is not there", report.containsKey("App/Controller/Accelerate/AccelerationViewController.h")); + List list1 = report.get("App/Controller/Accelerate/AccelerationViewController.h"); + assertEquals(4, list1.size()); + + for (Measure measure : list1) { + String s = measure.getMetric().getKey(); + + if (s.equals(CoreMetrics.FUNCTIONS_KEY)) { + assertEquals("Header Functions has a wrong value", 0, measure.getIntValue().intValue()); + } else if (s.equals(CoreMetrics.COMPLEXITY_KEY)) { + assertEquals("Header Complexity has a wrong value", 0, measure.getIntValue().intValue()); + } else if (s.equals(CoreMetrics.FILE_COMPLEXITY_KEY)) { + assertEquals("Header File Complexity has a wrong value", 0.0d, measure.getValue().doubleValue(), 0.0d); + } else if (s.equals(CoreMetrics.COMPLEXITY_IN_FUNCTIONS_KEY)) { + assertEquals("Header Complexity in Functions has a wrong value", 0, measure.getIntValue().intValue()); + } else if (s.equals(CoreMetrics.FUNCTION_COMPLEXITY_KEY)) { + assertEquals("Header Functions Complexity has a wrong value", 0.0d, measure.getValue().doubleValue(), 0.0d); + } + } + + assertTrue("Key is not there", report.containsKey("App/Controller/Accelerate/AccelerationViewController.m")); + + List list2 = report.get("App/Controller/Accelerate/AccelerationViewController.m"); + assertEquals(7, list2.size()); + for (Measure measure : list2) { + String s = measure.getMetric().getKey(); + + if (s.equals(CoreMetrics.FUNCTIONS_KEY)) { + assertEquals("MFile Functions has a wrong value", 2, measure.getIntValue().intValue()); + } else if (s.equals(CoreMetrics.COMPLEXITY_KEY)) { + assertEquals("MFile Complexity has a wrong value", 6, measure.getIntValue().intValue()); + } else if (s.equals(CoreMetrics.FILE_COMPLEXITY_KEY)) { + assertEquals("MFile File Complexity has a wrong value", 6.0d, measure.getValue().doubleValue(), 0.0d); + } else if (s.equals(CoreMetrics.COMPLEXITY_IN_FUNCTIONS_KEY)) { + assertEquals("MFile Complexity in Functions has a wrong value", 6, measure.getIntValue().intValue()); + } else if (s.equals(CoreMetrics.FUNCTION_COMPLEXITY_KEY)) { + assertEquals("MFile Functions Complexity has a wrong value", 3.0d, measure.getValue().doubleValue(), 0.0d); + } + } + } + + /** + * this method test that the parser shoud not return anything if the xml report is corrupted + */ + @Test + public void parseReportShouldReturnNullWhenXMLFileIsIncorrect() { + LizardReportParser parser = new LizardReportParser(); + + assertNotNull("correct file is null", incorrectFile); + + Map> report = parser.parseReport(incorrectFile); + assertNull("report is not null", report); + + } + +} \ No newline at end of file diff --git a/src/test/java/org/sonar/plugins/objectivec/complexity/LizardSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/complexity/LizardSensorTest.java new file mode 100644 index 00000000..84cf3edd --- /dev/null +++ b/src/test/java/org/sonar/plugins/objectivec/complexity/LizardSensorTest.java @@ -0,0 +1,84 @@ +/* + * Sonar Objective-C Plugin + * Copyright (C) 2012 OCTO Technology, Backelite + * 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.complexity; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Project; +import org.sonar.plugins.objectivec.core.ObjectiveC; + +import java.util.SortedSet; +import java.util.TreeSet; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Andres Gil Herrera + * @since 03/06/15. + */ +public class LizardSensorTest { + + private Settings settings; + + @Before + public void setUp() { + settings = new Settings(); + } + + /** + * this method tests that the sensor should be executed when a project is a root project and uses objective c + */ + @Test + public void shouldExecuteOnProjectShouldBeTrueWhenProjectIsObjc() { + final Project project = new Project("Test"); + + FileSystem fileSystem = mock(FileSystem.class); + SortedSet languages = new TreeSet(); + languages.add(ObjectiveC.KEY); + when(fileSystem.languages()).thenReturn(languages); + + final LizardSensor testedSensor = new LizardSensor(fileSystem, settings); + + assertTrue(testedSensor.shouldExecuteOnProject(project)); + } + + /** + * this method tests that the sensor does not get executed when a project dont uses objective c + */ + @Test + public void shouldExecuteOnProjectShouldBeFalseWhenProjectIsSomethingElse() { + final Project project = new Project("Test"); + + FileSystem fileSystem = mock(FileSystem.class); + SortedSet languages = new TreeSet(); + languages.add("Test"); + when(fileSystem.languages()).thenReturn(languages); + + final LizardSensor testedSensor = new LizardSensor(fileSystem, settings); + + assertFalse(testedSensor.shouldExecuteOnProject(project)); + } + +} \ No newline at end of file From f98522e07f03b4e73f0e20edd70b357424f9a18f Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 3 Mar 2016 02:11:52 +0100 Subject: [PATCH 056/107] Install guide update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d07f9afb..0a08a708 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Binary packages are available in the release section. - [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.10.1 recommended. - [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) - [slather](https://github.com/venmo/slather) with profdata support (see instructions below) for Xcode 7 and above. +- [lizard](https://github.com/terryyin/lizard) ([PIP](https://pip.pypa.io/en/stable/installing/) installed and ```sudo pip install lizard```) - [Faux Pas](http://fauxpasapp.com/) command line tools installed (optional) ###Installation of slather with profdata support From 2a6b4cdfedb440621032016b62b6ca1dde28b90b Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 7 Mar 2016 10:46:21 +0100 Subject: [PATCH 057/107] README fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a08a708..487d8c45 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://g | Feature | Supported | Details | |---------------|----------|:-----------:| -| Complexity |NO | Uses [Lizard](https://github.com/terryyin/lizard) | +| Complexity |YES | Uses [Lizard](https://github.com/terryyin/lizard) | | Design |NO | | | Documentation |YES | | | Duplications |YES | | From 6681be98ff386868a53e624d457e4c91e66393e6 Mon Sep 17 00:00:00 2001 From: Mika Ristimaki Date: Thu, 17 Mar 2016 11:54:39 +0200 Subject: [PATCH 058/107] Fixing issues in run-sonar.sh 1. Fixing handling of sonar.objectivec.testScheme parameter 2. Fixing handling of sonar.objectivec.project parameter 3. Making it possible to read the sonar.projectVersion parameter from the app bundle --- src/main/shell/run-sonar.sh | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index d1094407..4dae6d28 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -166,6 +166,7 @@ destinationSimulator=''; readParameter destinationSimulator 'sonar.objectivec.si # Your .xcworkspace/.xcodeproj filename workspaceFile=''; readParameter workspaceFile 'sonar.objectivec.workspace' projectFile=''; readParameter projectFile 'sonar.objectivec.project' +projectVersion=''; readParameter projectVersion 'sonar.projectVersion' # Count projects projectCount=$(echo $projectFile | sed -n 1'p' | tr ',' '\n' | wc -l | tr -d '[[:space:]]') @@ -209,6 +210,11 @@ if [ -z "$destinationSimulator" -o "$destinationSimulator" = " " ]; then exit 1 fi +# If project version is not set in the sonar-project.properties, read it from the project bundle +if [[ "$projectVersion" = "" ]] ; then + projectVersion=$(agvtool what-marketing-version -terse1) +fi + if [ "$vflag" = "on" ]; then echo "Xcode workspace file is: $workspaceFile" echo "Xcode project file is: $projectFile" @@ -235,7 +241,12 @@ mkdir sonar-reports # Extracting project information needed later echo -n 'Extracting Xcode project information' -buildCmd=(xcodebuild clean build -workspace $workspaceFile -scheme $appScheme) +if [[ "$workspaceFile" != "" ]] ; then + buildCmdPrefix="-workspace $workspaceFile" +else + buildCmdPrefix="-project $projectFile" +fi +buildCmd=(xcodebuild clean build $buildCmdPrefix -scheme $appScheme) if [[ ! -z "$destinationSimulator" ]]; then buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) fi @@ -246,7 +257,7 @@ oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.jso # Unit tests and coverage if [ "$testScheme" = "" ]; then echo 'Skipping tests as no test scheme has been provided!' - + # Put default xml files with no tests and no coverage... echo "" > sonar-reports/TEST-report.xml echo "" > sonar-reports/coverage.xml @@ -256,10 +267,10 @@ else if [ "$coverageType" = "profdata" -o "$coverageType" = "" ]; then # profdata - buildCmd=(xcodebuild clean build test -workspace $workspaceFile -scheme $appScheme -configuration Debug -enableCodeCoverage YES) + buildCmd=(xcodebuild test $buildCmdPrefix -scheme "$testScheme" -configuration Debug -enableCodeCoverage YES) else # Legacy coverage - buildCmd=(xcodebuild clean build test -workspace $workspaceFile -scheme $appScheme -configuration Debug) + buildCmd=(xcodebuild test $buildCmdPrefix -scheme "$testScheme" -configuration Debug) fi if [[ ! -z "$destinationSimulator" ]]; then @@ -292,7 +303,7 @@ else projectArray=(${projectFile//,/ }) firstProject=${projectArray[0]} - runCommand /dev/stdout $SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports --scheme $appScheme $firstProject + runCommand /dev/stdout $SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports --scheme "$testScheme" $firstProject mv sonar-reports/cobertura.xml sonar-reports/coverage.xml else @@ -420,8 +431,8 @@ fi # SonarQube echo -n 'Running SonarQube using SonarQube Runner' -runCommand /dev/stdout sonar-runner - +runCommand /dev/stdout sonar-runner -Dsonar.projectVersion=$projectVersion + # Kill progress indicator stopProgress From adaa151e022ce0ba8fb2fe40d73c7578f0b30ced Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 6 Apr 2016 09:51:33 +0200 Subject: [PATCH 059/107] Coverage support for Xcode 7.3 with new slather --- src/main/shell/run-sonar.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index d1094407..580fb82e 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -292,8 +292,17 @@ else projectArray=(${projectFile//,/ }) firstProject=${projectArray[0]} - runCommand /dev/stdout $SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports --scheme $appScheme $firstProject - mv sonar-reports/cobertura.xml sonar-reports/coverage.xml + + slatherCmd=($SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports) + if [[ ! -z "$workspaceFile" ]]; then + slatherCmd+=( --workspace $workspaceFile) + fi + slatherCmd+=( --scheme "$appScheme" $firstProject) + + runCommand sonar-reports/xcodebuild.log "${buildCmd[@]}" + + runCommand /dev/stdout "${slatherCmd[@]}" + mv sonar-reports/cobertura.xml sonar-reports/coverage.xml else From 077a4a3a56c332498862cd0f71475bb0a87bbada Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 6 Apr 2016 09:57:36 +0200 Subject: [PATCH 060/107] Xcode 7.3 coverage fix + README update --- README.md | 13 ++++++------- src/main/shell/run-sonar.sh | 2 -- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0a08a708..6539fd9f 100644 --- a/README.md +++ b/README.md @@ -79,21 +79,20 @@ Binary packages are available in the release section. - [xctool](https://github.com/facebook/xctool) ([HomeBrew](http://brew.sh) installed and ```brew install xctool```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. - [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.10.1 recommended. - [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) -- [slather](https://github.com/venmo/slather) with profdata support (see instructions below) for Xcode 7 and above. +- [slather](https://github.com/SlatherOrg/slather) (see instructions below) for Xcode 7 and above. - [lizard](https://github.com/terryyin/lizard) ([PIP](https://pip.pypa.io/en/stable/installing/) installed and ```sudo pip install lizard```) - [Faux Pas](http://fauxpasapp.com/) command line tools installed (optional) -###Installation of slather with profdata support +###Installation of slather from master repository -At the time, slather does not support profdata. A special version of slather needs t be installed. +At the time, no Xcode 7.3 compliant slather version is released. You need to install it from the master. -To install slather with profdata support, follow those steps : +Follow those steps : - git clone https://github.com/mattdelves/slather.git + git clone https://github.com/SlatherOrg/slather.git cd slather - git checkout feature-profdata gem build slather.gemspec - gem install --both slather-1.8.1.gem + gem install --both slather-2.0.2.gem ###Installation of xcpretty with JUnit reports fix diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 580fb82e..69a3d7dd 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -299,8 +299,6 @@ else fi slatherCmd+=( --scheme "$appScheme" $firstProject) - runCommand sonar-reports/xcodebuild.log "${buildCmd[@]}" - runCommand /dev/stdout "${slatherCmd[@]}" mv sonar-reports/cobertura.xml sonar-reports/coverage.xml From 7fa892d1d22dccdcf03dfdf03dccf7ea8094cd3a Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 6 Apr 2016 09:51:33 +0200 Subject: [PATCH 061/107] Coverage support for Xcode 7.3 with new slather --- src/main/shell/run-sonar.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index d1094407..580fb82e 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -292,8 +292,17 @@ else projectArray=(${projectFile//,/ }) firstProject=${projectArray[0]} - runCommand /dev/stdout $SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports --scheme $appScheme $firstProject - mv sonar-reports/cobertura.xml sonar-reports/coverage.xml + + slatherCmd=($SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports) + if [[ ! -z "$workspaceFile" ]]; then + slatherCmd+=( --workspace $workspaceFile) + fi + slatherCmd+=( --scheme "$appScheme" $firstProject) + + runCommand sonar-reports/xcodebuild.log "${buildCmd[@]}" + + runCommand /dev/stdout "${slatherCmd[@]}" + mv sonar-reports/cobertura.xml sonar-reports/coverage.xml else From 06ff90268bd204181de134a75dcfe45a31da8233 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 6 Apr 2016 09:57:36 +0200 Subject: [PATCH 062/107] Xcode 7.3 coverage fix + README update --- README.md | 13 ++++++------- src/main/shell/run-sonar.sh | 2 -- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 487d8c45..742b45b5 100644 --- a/README.md +++ b/README.md @@ -79,21 +79,20 @@ Binary packages are available in the release section. - [xctool](https://github.com/facebook/xctool) ([HomeBrew](http://brew.sh) installed and ```brew install xctool```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. - [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.10.1 recommended. - [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) -- [slather](https://github.com/venmo/slather) with profdata support (see instructions below) for Xcode 7 and above. +- [slather](https://github.com/SlatherOrg/slather) (see instructions below) for Xcode 7 and above. - [lizard](https://github.com/terryyin/lizard) ([PIP](https://pip.pypa.io/en/stable/installing/) installed and ```sudo pip install lizard```) - [Faux Pas](http://fauxpasapp.com/) command line tools installed (optional) -###Installation of slather with profdata support +###Installation of slather from master repository -At the time, slather does not support profdata. A special version of slather needs t be installed. +At the time, no Xcode 7.3 compliant slather version is released. You need to install it from the master. -To install slather with profdata support, follow those steps : +Follow those steps : - git clone https://github.com/mattdelves/slather.git + git clone https://github.com/SlatherOrg/slather.git cd slather - git checkout feature-profdata gem build slather.gemspec - gem install --both slather-1.8.1.gem + gem install --both slather-2.0.2.gem ###Installation of xcpretty with JUnit reports fix diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 580fb82e..69a3d7dd 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -299,8 +299,6 @@ else fi slatherCmd+=( --scheme "$appScheme" $firstProject) - runCommand sonar-reports/xcodebuild.log "${buildCmd[@]}" - runCommand /dev/stdout "${slatherCmd[@]}" mv sonar-reports/cobertura.xml sonar-reports/coverage.xml From 17496bf3c6f224f1627f8d366fb10766817a687c Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 19 Apr 2016 17:28:43 +0200 Subject: [PATCH 063/107] README.md update --- README.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/README.md b/README.md index 6539fd9f..25be67ad 100644 --- a/README.md +++ b/README.md @@ -79,21 +79,10 @@ Binary packages are available in the release section. - [xctool](https://github.com/facebook/xctool) ([HomeBrew](http://brew.sh) installed and ```brew install xctool```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. - [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.10.1 recommended. - [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) -- [slather](https://github.com/SlatherOrg/slather) (see instructions below) for Xcode 7 and above. +- [slather](https://github.com/SlatherOrg/slather) (```gem install slather```). Version 2.1.0 or above. - [lizard](https://github.com/terryyin/lizard) ([PIP](https://pip.pypa.io/en/stable/installing/) installed and ```sudo pip install lizard```) - [Faux Pas](http://fauxpasapp.com/) command line tools installed (optional) -###Installation of slather from master repository - -At the time, no Xcode 7.3 compliant slather version is released. You need to install it from the master. - -Follow those steps : - - git clone https://github.com/SlatherOrg/slather.git - cd slather - gem build slather.gemspec - gem install --both slather-2.0.2.gem - ###Installation of xcpretty with JUnit reports fix At the time, xcpretty needs to be fixed to work with SonarQube. From 141aac83c608f8a7d770a3ecd17074303092f19e Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 19 Apr 2016 17:32:52 +0200 Subject: [PATCH 064/107] README.md update --- README.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/README.md b/README.md index 742b45b5..a2084f95 100644 --- a/README.md +++ b/README.md @@ -79,21 +79,10 @@ Binary packages are available in the release section. - [xctool](https://github.com/facebook/xctool) ([HomeBrew](http://brew.sh) installed and ```brew install xctool```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. - [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.10.1 recommended. - [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) -- [slather](https://github.com/SlatherOrg/slather) (see instructions below) for Xcode 7 and above. +- [slather](https://github.com/SlatherOrg/slather) (```gem install slather```). Version 2.1.0 or above. - [lizard](https://github.com/terryyin/lizard) ([PIP](https://pip.pypa.io/en/stable/installing/) installed and ```sudo pip install lizard```) - [Faux Pas](http://fauxpasapp.com/) command line tools installed (optional) -###Installation of slather from master repository - -At the time, no Xcode 7.3 compliant slather version is released. You need to install it from the master. - -Follow those steps : - - git clone https://github.com/SlatherOrg/slather.git - cd slather - gem build slather.gemspec - gem install --both slather-2.0.2.gem - ###Installation of xcpretty with JUnit reports fix At the time, xcpretty needs to be fixed to work with SonarQube. From 994944fa6dcbbd6817bc0afbe8b43c4a106754fa Mon Sep 17 00:00:00 2001 From: Mihai Parv Date: Wed, 11 May 2016 17:43:24 +0300 Subject: [PATCH 065/107] Included category naming case when searching for test source files which are not in the root. --- .../org/sonar/plugins/objectivec/tests/SurefireParser.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java index d15e41bf..be92ce3c 100644 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java @@ -206,7 +206,8 @@ public Resource getUnitTestResource(String classname) { if (!file.isFile() || !file.exists()) { List files = ImmutableList.copyOf(fileSystem.files(fileSystem.predicates().and( fileSystem.predicates().hasType(InputFile.Type.TEST), - fileSystem.predicates().matchesPathPattern("**/" + fileName)))); + fileSystem.predicates().or(fileSystem.predicates().matchesPathPattern("**/" + fileName), + fileSystem.predicates().matchesPathPattern("**/" + fileName.replace("_", "+")))))); if (files.isEmpty()) { LOG.info("Unable to locate test source file {}", fileName); From 078f54867730a67f3bec240d51540a79aa2618ab Mon Sep 17 00:00:00 2001 From: Nicolas Berthet Date: Wed, 3 Aug 2016 15:52:51 +0800 Subject: [PATCH 066/107] Fix paths to OCLint online documentation Fixed what seemed to be an unfortunate search & replace issue in order to refresh the OCLint rules. --- updateOCLintRules.groovy | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/updateOCLintRules.groovy b/updateOCLintRules.groovy index 2812a8a2..45d665e0 100644 --- a/updateOCLintRules.groovy +++ b/updateOCLintRules.groovy @@ -189,21 +189,21 @@ def mergeRules(existingRules, freshRules) { } // Files -File rulesTxt = new File('src/main/resources/org/sonar/plugins/fauxpas/rules.txt') -File profileXml = new File('src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml') +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.fauxpas.org/en/dev/rules/basic.html", "basic", 3) -rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/convention.html", "convention", 2) -rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/empty.html", "empty", 3) -rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/migration.html", "migration", 1) -rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/naming.html", "naming", 2) -rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/redundant.html", "redundant", 1) -rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/size.html", "size", 3) -rules.addAll parseCategory("http://docs.fauxpas.org/en/dev/rules/unused.html", "unused", 0) +rules.addAll parseCategory("http://docs.oclint.org/en/stable/rules/basic.html", "basic", 3) +rules.addAll parseCategory("http://docs.oclint.org/en/stable/rules/convention.html", "convention", 2) +rules.addAll parseCategory("http://docs.oclint.org/en/stable/rules/empty.html", "empty", 3) +rules.addAll parseCategory("http://docs.oclint.org/en/stable/rules/migration.html", "migration", 1) +rules.addAll parseCategory("http://docs.oclint.org/en/stable/rules/naming.html", "naming", 2) +rules.addAll parseCategory("http://docs.oclint.org/en/stable/rules/redundant.html", "redundant", 1) +rules.addAll parseCategory("http://docs.oclint.org/en/stable/rules/size.html", "size", 3) +rules.addAll parseCategory("http://docs.oclint.org/en/stable/rules/unused.html", "unused", 0) println "${rules.size()} rules found" From 8561c7c8e46d940d6d10fe888c76b8ca7cfc1297 Mon Sep 17 00:00:00 2001 From: Nicolas Berthet Date: Wed, 3 Aug 2016 15:53:57 +0800 Subject: [PATCH 067/107] Update rules to support OCLint 0.10.3 Updated the rules to support latest OCLint as a couple of rules changed. --- .../sonar/plugins/oclint/profile-oclint.xml | 12 +++ .../org/sonar/plugins/oclint/rules.txt | 78 ++++++++++++------- 2 files changed, 63 insertions(+), 27 deletions(-) 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 35fa35d4..0c29d998 100644 --- a/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml +++ b/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml @@ -255,5 +255,17 @@ OCLint ivar assignment outside accessors or init + + OCLint + covered switch statements dont need default + + + OCLint + class + + + OCLint + unnecessary null check for dealloc + \ 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 beb11d6b..7b34c0a5 100644 --- a/src/main/resources/org/sonar/plugins/oclint/rules.txt +++ b/src/main/resources/org/sonar/plugins/oclint/rules.txt @@ -6,7 +6,7 @@ OCLint use early exits and continue ---------- -Summary: +Summary: Early exits can reduce the indentation of a block of code, so that reader do not have to remember all the previous decisions, therefore, makes it easier to understand the code. Severity: 2 Category: OCLint @@ -22,7 +22,7 @@ Category: OCLint bitwise operator in conditional ---------- -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. +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. Severity: 3 Category: OCLint @@ -30,9 +30,9 @@ Category: OCLint broken null check ---------- -Summary: The broken nil check in Objective-C in some cases returns just the opposite result. +Summary: The broken null check itself will crash the program. -Severity: 4 +Severity: 3 Category: OCLint broken nil check @@ -46,7 +46,7 @@ Category: OCLint broken oddness check ---------- -Summary: Checking oddness by x%2==1 won’t work for negative numbers. Use x&1==1, or x%2!=0 instead. +Summary: Checking oddness by x%2==1 won?t work for negative numbers. Use x&1==1, or x%2!=0 instead. Severity: 3 Category: OCLint @@ -54,7 +54,7 @@ Category: OCLint collapsible if statements ---------- -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. +Summary: This rule detects instances where the conditions of two consecutive if statements can be combined into one in order to increase code cleanness and readability. Severity: 3 Category: OCLint @@ -86,7 +86,7 @@ Category: OCLint dead code ---------- -Summary: Code after return, break, continue, and throw statements are unreachable and will never be executed. +Summary: Code after return, break, continue, and throw statements is unreachable and will never be executed. Severity: 3 Category: OCLint @@ -118,7 +118,7 @@ Category: OCLint empty do/while statement ---------- -Summary: This rule detects instances where a do-while statement does nothing. +Summary: This rule detects instances where do-while statement does nothing. Severity: 3 Category: OCLint @@ -126,9 +126,9 @@ Category: OCLint empty else block ---------- -Summary: +Summary: This rule detects instances where a else statement does nothing. -Severity: 2 +Severity: 3 Category: OCLint empty finally statement @@ -214,15 +214,15 @@ Category: OCLint jumbled incrementer ---------- -Summary: +Summary: Jumbled incrementers are usually typos. If it?s done on purpose, it?s very confusing for code readers. -Severity: 2 +Severity: 3 Category: OCLint long class ---------- -Summary: Long class generally indicates that this class tries to so many things. Each class should do one thing and one thing well. +Summary: Long class generally indicates that this class tries to do many things. Each class should do one thing and that one thing well. Severity: 3 Category: OCLint @@ -230,15 +230,15 @@ Category: OCLint long line ---------- -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. +Summary: When the number of characters for one line of code is very high, it largely harms the readability. Break long lines of code into multiple lines. -Severity: 2 +Severity: 3 Category: OCLint long method ---------- -Summary: Long method generally indicates that this method tries to so many things. Each method should do one thing and one thing well. +Summary: Long method generally indicates that this method tries to do many things. Each method should do one thing and that one thing well. Severity: 3 Category: OCLint @@ -254,7 +254,7 @@ Category: OCLint misplaced null check ---------- -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. +Summary: The null check is misplaced. In C and C++, sending a message to a null pointer could crash the app. When null is misplaced, either the check is useless or it?s incorrect. Severity: 3 Category: OCLint @@ -270,7 +270,7 @@ Category: OCLint missing break in switch statement ---------- -Summary: +Summary: A switch statement without a break statement has a very large chance to contribute a bug. Severity: 2 Category: OCLint @@ -294,7 +294,7 @@ Category: OCLint high ncss method ---------- -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. +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, a statement that is broken into multiple lines contribute only one count. Severity: 3 Category: OCLint @@ -310,7 +310,7 @@ Category: OCLint non case label in switch statement ---------- -Summary: It is very confusing when default label is not the last label in a switch statement. +Summary: It is very confusing when label becomes part of the switch statement. Severity: 2 Category: OCLint @@ -382,7 +382,7 @@ Category: OCLint redundant local variable ---------- -Summary: This rule detects cases where a variable declaration immediately followed by a return of that variable. +Summary: This rule detects cases where a variable declaration is immediately followed by a return of that variable. Severity: 1 Category: OCLint @@ -390,9 +390,9 @@ Category: OCLint redundant nil check ---------- -Summary: +Summary: C/C++-style null check in Objective-C like foo!=nil&&[foobar] is redundant, since sending a message to a nil object in this case simply returns a false-y value. -Severity: 3 +Severity: 1 Category: OCLint return from finally block @@ -422,7 +422,7 @@ Category: OCLint switch statements should have default ---------- -Summary: Switch statements should a default statement. +Summary: Switch statements should have a default statement. Severity: 2 Category: OCLint @@ -446,7 +446,7 @@ Category: OCLint too many fields ---------- -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. +Summary: A class with too many fields indicates it does too many things and lacks proper abstraction. It can be redesigned to have fewer fields. Severity: 3 Category: OCLint @@ -454,7 +454,7 @@ Category: OCLint too many methods ---------- -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. +Summary: A class with too many methods indicates it does too many things and is hard to read and understand. It usually contains complicated code, and should be refactored. Severity: 3 Category: OCLint @@ -502,8 +502,32 @@ Category: OCLint ivar assignment outside accessors or init ---------- -Summary: +Summary: This rule prevents assigning an ivar outside of getters, setters, and init method. Severity: 2 Category: OCLint +covered switch statements dont need default +---------- + +Summary: When a switch statement covers all possible cases, a default label is not needed and should be removed. If the switch is not fully covered, the SwitchStatementsShouldHaveDefault rule will report. + +Severity: 2 +Category: OCLint + +class +---------- + +Summary: This rule enforces the destructor of a virtual class must be virtual. + +Severity: 2 +Category: OCLint + +unnecessary null check for dealloc +---------- + +Summary: char*p=0;deletep; is valid. This rule locates unnecessary if(p) checks. + +Severity: 1 +Category: OCLint + From a514ec189904bbe84fd285013ab0ce67ea7da528 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 9 Aug 2016 16:40:51 +0200 Subject: [PATCH 068/107] Updated SQLAE model for OCLint new rules --- .../com/sonar/sqale/oclint-model.xml | 26 ++++++++++++++++ .../sonar/plugins/fauxpas/profile-fauxpas.xml | 4 +-- .../org/sonar/plugins/fauxpas/rules.json | 30 +++++++++---------- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/main/resources/com/sonar/sqale/oclint-model.xml b/src/main/resources/com/sonar/sqale/oclint-model.xml index e20af078..956ce221 100644 --- a/src/main/resources/com/sonar/sqale/oclint-model.xml +++ b/src/main/resources/com/sonar/sqale/oclint-model.xml @@ -110,6 +110,19 @@ min + + OCLint + unnecessary null check for dealloc + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + OCLint redundant local variable @@ -344,6 +357,19 @@ min + + OCLint + covered switch statements dont need default + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + OCLint replace with object subscripting diff --git a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml index 9bec912b..67742775 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml +++ b/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml @@ -341,11 +341,11 @@
FauxPas - NullCoalescingOp + StrongInsteadOfRetain FauxPas - StrongInsteadOfRetain + NullCoalescingOp FauxPas diff --git a/src/main/resources/org/sonar/plugins/fauxpas/rules.json b/src/main/resources/org/sonar/plugins/fauxpas/rules.json index b47b5efd..3563460e 100644 --- a/src/main/resources/org/sonar/plugins/fauxpas/rules.json +++ b/src/main/resources/org/sonar/plugins/fauxpas/rules.json @@ -10,7 +10,7 @@ "category": "BestPractice", "key": "MallocWithoutSizeof", "name": "Memory allocation without using sizeof", - "description": "The sizeof operator should be used to obtain the correct size for any allocated structure or variable \u2014 the sizes of some data types vary between the 32-bit and 64-bit runtimes.This rule is na\u00EFve and may produce false positives in many cases.", + "description": "The sizeof operator should be used to obtain the correct size for any allocated structure or variable \u2014 the sizes of some data types vary between the 32-bit and 64-bit runtimes.This rule is na\u00efve and may produce false positives in many cases.", "severity": "MAJOR" }, { @@ -52,7 +52,7 @@ "category": "BestPractice", "key": "RetainingImmutableProperty", "name": "Non-copying property of immutable NSCopying type", - "description": "Warns if \u201Cretaining\u201D semantics are specified in a @property declaration for a common immutable NSCopying class type that also has a mutable subclass variant (for example NSString).This rule helps avoid situations where the value of a property could change without the setter being invoked (i.e. the object gets mutated).", + "description": "Warns if \u201cretaining\u201d semantics are specified in a @property declaration for a common immutable NSCopying class type that also has a mutable subclass variant (for example NSString).This rule helps avoid situations where the value of a property could change without the setter being invoked (i.e. the object gets mutated).", "severity": "MAJOR" }, { @@ -115,7 +115,7 @@ "category": "BestPractice", "key": "CopyingMutableProperty", "name": "Copying property of mutable NSCopying type", - "description": "Warns if \u201Ccopying\u201D semantics are specified in a @property declaration for a common mutable NSCopying class type that has an immutable superclass (for example NSMutableArray).Invoking -[NSCopying copy] for such objects will in many cases return an immutable copy. This means that after assigning a mutable object value to such a property, it may in fact get an immutable copy of the assigned value (which will lead to crashes as soon as any attempt is made to mutate this immutable object).", + "description": "Warns if \u201ccopying\u201d semantics are specified in a @property declaration for a common mutable NSCopying class type that has an immutable superclass (for example NSMutableArray).Invoking -[NSCopying copy] for such objects will in many cases return an immutable copy. This means that after assigning a mutable object value to such a property, it may in fact get an immutable copy of the assigned value (which will lead to crashes as soon as any attempt is made to mutate this immutable object).", "severity": "MAJOR" }, { @@ -149,7 +149,7 @@ { "category": "BestPractice", "key": "PrivateCategory", - "name": "Category used for \u201Cprivate\u201D declarations", + "name": "Category used for \u201cprivate\u201d declarations", "description": "A class extension should be used instead, because they enable compiler warnings for detecting unimplemented methods.", "severity": "MAJOR" }, @@ -157,7 +157,7 @@ "category": "BestPractice", "key": "IBOutletsInPublicInterface", "name": "IBOutlets in public interface", - "description": "IBOutlets are often private implementation details, and should thus be in a private class extension.This rule warns only about cases where the class is the \u201CFile\u2019s Owner\u201D of a XIB whose basename is the same as the class name, and that contains a connection to the outlet.", + "description": "IBOutlets are often private implementation details, and should thus be in a private class extension.This rule warns only about cases where the class is the \u201cFile\u2019s Owner\u201d of a XIB whose basename is the same as the class name, and that contains a connection to the outlet.", "severity": "MAJOR" }, { @@ -381,7 +381,7 @@ "category": "Config", "key": "XcconfigOverwrites", "name": "Xcode build configuration file overwrites previously set value", - "description": "Overwriting a previously set value in a build configuration file is often a sign of a mistake somewhere.It is recommended to set custom intermediary settings in \u201Cparent\u201D configuration files and then compose final values for actual build settings in leaf configuration files.", + "description": "Overwriting a previously set value in a build configuration file is often a sign of a mistake somewhere.It is recommended to set custom intermediary settings in \u201cparent\u201d configuration files and then compose final values for actual build settings in leaf configuration files.", "severity": "MINOR" }, { @@ -395,7 +395,7 @@ "category": "Config", "key": "BuildSettingPlacement", "name": "Incorrect placement of build setting value", - "description": "Warns if preprocessor definitions are defined explicitly using the -D argument instead of defining them in the GCC_PREPROCESSOR_DEFINITIONS (\u201CPreprocessor macros\u201D) build setting.Also warns if non-warning flags are defined in WARNING_CFLAGS (instead of OTHER_CFLAGS).", + "description": "Warns if preprocessor definitions are defined explicitly using the -D argument instead of defining them in the GCC_PREPROCESSOR_DEFINITIONS (\u201cPreprocessor macros\u201d) build setting.Also warns if non-warning flags are defined in WARNING_CFLAGS (instead of OTHER_CFLAGS).", "severity": "MINOR" }, { @@ -542,7 +542,7 @@ "category": "APIUsage", "key": "SuspiciousDateTimeFormat", "name": "Suspicious date-time format", - "description": "Warns about date-time formats that are likely to have unintentional behavior, e.g. \u201Cweek-year\u201D specifiers (uppercase Y) instead of normal calendar year specifiers (lowercase y).", + "description": "Warns about date-time formats that are likely to have unintentional behavior, e.g. \u201cweek-year\u201d specifiers (uppercase Y) instead of normal calendar year specifiers (lowercase y).", "severity": "CRITICAL" }, { @@ -589,16 +589,16 @@ }, { "category": "Style", - "key": "NullCoalescingOp", - "name": "Null coalescing operator usage", - "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", + "key": "StrongInsteadOfRetain", + "name": "Usage of retain in ARC code", + "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", "severity": "MAJOR" }, { "category": "Style", - "key": "StrongInsteadOfRetain", - "name": "Usage of retain in ARC code", - "description": "In files compiled with ARC, warns if the retain property attribute is used. The strong and retain attributes are functionally equivalent, but the former could be considered more idiomatic with ARC.", + "key": "NullCoalescingOp", + "name": "Null coalescing operator usage", + "description": "Expressions of the form (obj ? obj : other) should be written as obj ?: other.", "severity": "MAJOR" }, { @@ -703,7 +703,7 @@ "category": "Pedantic", "key": "UsedVariableMarkedUnused", "name": "Using a variable marked unused", - "description": "Warns when a variable is annotated with the \u201Cunused\u201D attribute, but is actually used. This rule does not warn about cases where a variable is marked unused via a pragma directive.", + "description": "Warns when a variable is annotated with the \u201cunused\u201d attribute, but is actually used. This rule does not warn about cases where a variable is marked unused via a pragma directive.", "severity": "MINOR" }, { From 96351af05f92117d28b1606057094df6f5914290 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 9 Aug 2016 17:19:23 +0200 Subject: [PATCH 069/107] Updated version and release notes --- README.md | 3 +++ pom.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 25be67ad..b4c90437 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ Binary packages are available in the release section. ###Release history +###0.5.2 +- OCLint 0.10.3 support + ####0.5.1 - Complexity with Lizard ! diff --git a/pom.xml b/pom.xml index 15fc0d2e..8fb34afa 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec backelite-sonar-objective-c-plugin - 0.5.1 + 0.5.2-SNAPSHOT sonar-plugin From f2d15ae9df33211c308e003aec2393a663e49d5c Mon Sep 17 00:00:00 2001 From: Julien Curro Date: Wed, 21 Sep 2016 10:10:24 +0200 Subject: [PATCH 070/107] Do not check for gcovr as it is not available if there's only Xcode 8 installed --- src/main/shell/run-sonar.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 69a3d7dd..de2cae07 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -149,7 +149,6 @@ echo "Running run-sonar.sh..." # xctool, gcovr and oclint installed testIsInstalled xcodebuild -testIsInstalled gcovr testIsInstalled oclint testIsInstalled oclint-xcodebuild From 7a701cae088e5241b4e075bcda7001ecb34bf80e Mon Sep 17 00:00:00 2001 From: Jerome Morissard Date: Tue, 15 Nov 2016 17:48:09 +0100 Subject: [PATCH 071/107] Upgrade rules compatibility to OClint to 0.11 --- README.md | 3 + pom.xml | 2 +- .../com/sonar/sqale/oclint-model.xml | 72 +++++- .../sonar/plugins/oclint/profile-oclint.xml | 56 ++++- .../org/sonar/plugins/oclint/rules.txt | 237 ++++++++++-------- 5 files changed, 251 insertions(+), 119 deletions(-) mode change 100644 => 100755 README.md mode change 100644 => 100755 pom.xml mode change 100644 => 100755 src/main/resources/com/sonar/sqale/oclint-model.xml mode change 100644 => 100755 src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml mode change 100644 => 100755 src/main/resources/org/sonar/plugins/oclint/rules.txt diff --git a/README.md b/README.md old mode 100644 new mode 100755 index b4c90437..19242854 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ Binary packages are available in the release section. ###Release history +###0.5.3-SNAPSHOT +- OCLint 0.11.0 support + ###0.5.2 - OCLint 0.10.3 support diff --git a/pom.xml b/pom.xml old mode 100644 new mode 100755 index 8fb34afa..69283ecd --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec backelite-sonar-objective-c-plugin - 0.5.2-SNAPSHOT + 0.5.3-SNAPSHOT sonar-plugin diff --git a/src/main/resources/com/sonar/sqale/oclint-model.xml b/src/main/resources/com/sonar/sqale/oclint-model.xml old mode 100644 new mode 100755 index 956ce221..58e6bf86 --- a/src/main/resources/com/sonar/sqale/oclint-model.xml +++ b/src/main/resources/com/sonar/sqale/oclint-model.xml @@ -73,7 +73,7 @@ OCLint - replace with number literal + use number literal remediationFunction CONSTANT_ISSUE @@ -86,7 +86,7 @@ OCLint - replace with boxed expression + use boxed expression remediationFunction CONSTANT_ISSUE @@ -164,7 +164,7 @@ OCLint - replace with container literal + use container literal remediationFunction CONSTANT_ISSUE @@ -346,7 +346,7 @@ OCLint - default label not last in switch statement + ill-placed default label in switch statement remediationFunction CONSTANT_ISSUE @@ -359,7 +359,7 @@ OCLint - covered switch statements dont need default + unnecessary default statement in covered switch statement remediationFunction CONSTANT_ISSUE @@ -372,7 +372,7 @@ OCLint - replace with object subscripting + use object subscripting remediationFunction CONSTANT_ISSUE @@ -402,7 +402,7 @@ OCLint - use early exits and continue + prefer early exits and continue remediationFunction CONSTANT_ISSUE @@ -629,6 +629,19 @@ API_ABUSE API abuse + + OCLint + calling prohibited method + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + ERRORS @@ -758,9 +771,35 @@ INSTRUCTION_RELIABILITY Instruction + + OCLint + calling protected method + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + + OCLint + missing abstract method implementation + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + OCLint - must override hash with isEqual + missing hash method remediationFunction CONSTANT_ISSUE @@ -771,6 +810,19 @@ min + + OCLint + missing call to base method + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + OCLint parameter reassignment @@ -829,7 +881,7 @@ OCLint - switch statements should have default + missing default in switch statements remediationFunction CONSTANT_ISSUE @@ -992,4 +1044,4 @@ Unit level - \ No newline at end of file + diff --git a/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml b/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml old mode 100644 new mode 100755 index 0c29d998..13c9f14e --- a/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml +++ b/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml @@ -143,10 +143,6 @@ OCLint multiple unary operator - - OCLint - must override hash with isEqual - OCLint high ncss method @@ -261,11 +257,59 @@ OCLint - class + class OCLint unnecessary null check for dealloc + + OCLint + unnecessary default statement in covered switch statement + + + OCLint + ill-placed default label in switch statement + + + OCLint + prefer early exits and continue + + + OCLint + missing default in switch statements + + + OCLint + use object subscripting + + + OCLint + use boxed expression + + + OCLint + use container literal + + + OCLint + use number literal + + + OCLint + missing hash method + + + OCLint + missing call to base method + + + OCLint + calling prohibited method + + + OCLint + missing abstract method implementation + - \ 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 old mode 100644 new mode 100755 index 7b34c0a5..c430108b --- a/src/main/resources/org/sonar/plugins/oclint/rules.txt +++ b/src/main/resources/org/sonar/plugins/oclint/rules.txt @@ -3,18 +3,10 @@ Available issues: OCLint ====== -use early exits and continue ----------- - -Summary: Early exits can reduce the indentation of a block of code, so that reader do not have to remember all the previous decisions, therefore, makes it easier to understand the code. - -Severity: 2 -Category: OCLint - avoid branching statement as last in loop ---------- -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. +Summary: Name: avoid branching statement as last in loop Severity: 2 Category: OCLint @@ -22,7 +14,7 @@ Category: OCLint bitwise operator in conditional ---------- -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. +Summary: Name: bitwise operator in conditional Severity: 3 Category: OCLint @@ -30,7 +22,7 @@ Category: OCLint broken null check ---------- -Summary: The broken null check itself will crash the program. +Summary: Name: broken null check Severity: 3 Category: OCLint @@ -46,7 +38,7 @@ Category: OCLint broken oddness check ---------- -Summary: Checking oddness by x%2==1 won?t work for negative numbers. Use x&1==1, or x%2!=0 instead. +Summary: Name: broken oddness check Severity: 3 Category: OCLint @@ -54,7 +46,7 @@ Category: OCLint collapsible if statements ---------- -Summary: This rule detects instances where the conditions of two consecutive if statements can be combined into one in order to increase code cleanness and readability. +Summary: Name: collapsible if statements Severity: 3 Category: OCLint @@ -62,7 +54,7 @@ Category: OCLint constant conditional operator ---------- -Summary: conditionaloperator whose conditionals are always true or always false are confusing. +Summary: Name: constant conditional operator Severity: 3 Category: OCLint @@ -70,7 +62,7 @@ Category: OCLint constant if expression ---------- -Summary: if statements whose conditionals are always true or always false are confusing. +Summary: Name: constant if expression Severity: 3 Category: OCLint @@ -86,23 +78,15 @@ Category: OCLint dead code ---------- -Summary: Code after return, break, continue, and throw statements is unreachable and will never be executed. +Summary: Name: dead code Severity: 3 Category: OCLint -default label not last in switch statement ----------- - -Summary: It is very confusing when default label is not the last label in a switch statement. - -Severity: 2 -Category: OCLint - double negative ---------- -Summary: There is no point in using a double negative, it is always positive. +Summary: Name: double negative Severity: 3 Category: OCLint @@ -110,7 +94,7 @@ Category: OCLint empty catch statement ---------- -Summary: This rule detects instances where an exception is caught, but nothing is done about it. +Summary: Name: empty catch statement Severity: 3 Category: OCLint @@ -118,7 +102,7 @@ Category: OCLint empty do/while statement ---------- -Summary: This rule detects instances where do-while statement does nothing. +Summary: Name: empty do/while statement Severity: 3 Category: OCLint @@ -126,7 +110,7 @@ Category: OCLint empty else block ---------- -Summary: This rule detects instances where a else statement does nothing. +Summary: Name: empty else block Severity: 3 Category: OCLint @@ -134,7 +118,7 @@ Category: OCLint empty finally statement ---------- -Summary: This rule detects instances where a finally statement does nothing. +Summary: Name: empty finally statement Severity: 3 Category: OCLint @@ -142,7 +126,7 @@ Category: OCLint empty for statement ---------- -Summary: This rule detects instances where a for statement does nothing. +Summary: Name: empty for statement Severity: 3 Category: OCLint @@ -150,7 +134,7 @@ Category: OCLint empty if statement ---------- -Summary: This rule detects instances where a condition is checked, but nothing is done about it. +Summary: Name: empty if statement Severity: 3 Category: OCLint @@ -158,7 +142,7 @@ Category: OCLint empty switch statement ---------- -Summary: This rule detects instances where a switch statement does nothing. +Summary: Name: empty switch statement Severity: 3 Category: OCLint @@ -166,7 +150,7 @@ Category: OCLint empty try statement ---------- -Summary: This rule detects instances where a try statement is empty. +Summary: Name: empty try statement Severity: 3 Category: OCLint @@ -190,7 +174,7 @@ Category: OCLint for loop should be while loop ---------- -Summary: Under certain circumstances, some for loops can be simplified to while loops to make code more concise. +Summary: Name: for loop should be while loop Severity: 3 Category: OCLint @@ -206,7 +190,7 @@ Category: OCLint inverted logic ---------- -Summary: An inverted logic is hard to understand. +Summary: Name: inverted logic Severity: 2 Category: OCLint @@ -214,7 +198,7 @@ Category: OCLint jumbled incrementer ---------- -Summary: Jumbled incrementers are usually typos. If it?s done on purpose, it?s very confusing for code readers. +Summary: Name: jumbled incrementer Severity: 3 Category: OCLint @@ -222,7 +206,7 @@ Category: OCLint long class ---------- -Summary: Long class generally indicates that this class tries to do many things. Each class should do one thing and that one thing well. +Summary: Name: long class Severity: 3 Category: OCLint @@ -230,7 +214,7 @@ Category: OCLint long line ---------- -Summary: When the number of characters for one line of code is very high, it largely harms the readability. Break long lines of code into multiple lines. +Summary: Name: long line Severity: 3 Category: OCLint @@ -238,7 +222,7 @@ Category: OCLint long method ---------- -Summary: Long method generally indicates that this method tries to do many things. Each method should do one thing and that one thing well. +Summary: Name: long method Severity: 3 Category: OCLint @@ -246,7 +230,7 @@ Category: OCLint long variable name ---------- -Summary: Variables with long names harm readability. +Summary: Name: long variable name Severity: 2 Category: OCLint @@ -254,7 +238,7 @@ Category: OCLint misplaced null check ---------- -Summary: The null check is misplaced. In C and C++, sending a message to a null pointer could crash the app. When null is misplaced, either the check is useless or it?s incorrect. +Summary: Name: misplaced null check Severity: 3 Category: OCLint @@ -270,7 +254,7 @@ Category: OCLint missing break in switch statement ---------- -Summary: A switch statement without a break statement has a very large chance to contribute a bug. +Summary: Name: missing break in switch statement Severity: 2 Category: OCLint @@ -278,7 +262,7 @@ Category: OCLint multiple unary operator ---------- -Summary: Multiple unary operator can always be confusing and should be simplified. +Summary: Name: multiple unary operator Severity: 3 Category: OCLint @@ -294,7 +278,7 @@ Category: OCLint high ncss method ---------- -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, a statement that is broken into multiple lines contribute only one count. +Summary: Name: high ncss method Severity: 3 Category: OCLint @@ -302,7 +286,7 @@ Category: OCLint deep nested block ---------- -Summary: This rule indicates blocks nested more deeply than the upper limit. +Summary: Name: deep nested block Severity: 3 Category: OCLint @@ -310,7 +294,7 @@ Category: OCLint non case label in switch statement ---------- -Summary: It is very confusing when label becomes part of the switch statement. +Summary: Name: non case label in switch statement Severity: 2 Category: OCLint @@ -323,42 +307,10 @@ Summary: Severity: 2 Category: OCLint -replace with boxed expression ----------- - -Summary: This rule locates the places that can be migrated to the new Objective-C literals with boxed expressions. - -Severity: 1 -Category: OCLint - -replace with container literal ----------- - -Summary: This rule locates the places that can be migrated to the new Objective-C literals with container literals. - -Severity: 1 -Category: OCLint - -replace with number literal ----------- - -Summary: This rule locates the places that can be migrated to the new Objective-C literals with number literals. - -Severity: 1 -Category: OCLint - -replace with object subscripting ----------- - -Summary: - -Severity: 1 -Category: OCLint - parameter reassignment ---------- -Summary: Reassigning values to parameters is very problematic in most cases. +Summary: Name: parameter reassignment Severity: 2 Category: OCLint @@ -366,7 +318,7 @@ Category: OCLint redundant conditional operator ---------- -Summary: This rule detects three types of redundant conditional operators: +Summary: Name: redundant conditional operator Severity: 1 Category: OCLint @@ -374,7 +326,7 @@ Category: OCLint redundant if statement ---------- -Summary: This rule detects unnecessary if statements. +Summary: Name: redundant if statement Severity: 1 Category: OCLint @@ -382,7 +334,7 @@ Category: OCLint redundant local variable ---------- -Summary: This rule detects cases where a variable declaration is immediately followed by a return of that variable. +Summary: Name: redundant local variable Severity: 1 Category: OCLint @@ -390,7 +342,7 @@ Category: OCLint redundant nil check ---------- -Summary: C/C++-style null check in Objective-C like foo!=nil&&[foobar] is redundant, since sending a message to a nil object in this case simply returns a false-y value. +Summary: Name: redundant nil check Severity: 1 Category: OCLint @@ -398,7 +350,7 @@ Category: OCLint return from finally block ---------- -Summary: Returning from a finally block is not recommended. +Summary: Name: return from finally block Severity: 3 Category: OCLint @@ -419,14 +371,6 @@ Summary: Severity: 3 Category: OCLint -switch statements should have default ----------- - -Summary: Switch statements should have a default statement. - -Severity: 2 -Category: OCLint - throw exception from finally block ---------- @@ -446,7 +390,7 @@ Category: OCLint too many fields ---------- -Summary: A class with too many fields indicates it does too many things and lacks proper abstraction. It can be redesigned to have fewer fields. +Summary: Name: too many fields Severity: 3 Category: OCLint @@ -454,7 +398,7 @@ Category: OCLint too many methods ---------- -Summary: A class with too many methods indicates it does too many things and is hard to read and understand. It usually contains complicated code, and should be refactored. +Summary: Name: too many methods Severity: 3 Category: OCLint @@ -470,7 +414,7 @@ Category: OCLint unnecessary else statement ---------- -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. +Summary: Name: unnecessary else statement Severity: 1 Category: OCLint @@ -478,7 +422,7 @@ Category: OCLint unused local variable ---------- -Summary: This rule detects local variables that are declared, but not used. +Summary: Name: unused local variable Severity: 0 Category: OCLint @@ -502,12 +446,28 @@ Category: OCLint ivar assignment outside accessors or init ---------- -Summary: This rule prevents assigning an ivar outside of getters, setters, and init method. +Summary: Name: ivar assignment outside accessors or init + +Severity: 2 +Category: OCLint + +class +---------- + +Summary: Name: destructor of virtual class Severity: 2 Category: OCLint -covered switch statements dont need default +unnecessary null check for dealloc +---------- + +Summary: Name: unnecessary null check for dealloc + +Severity: 1 +Category: OCLint + +unnecessary default statement in covered switch statement ---------- Summary: When a switch statement covers all possible cases, a default label is not needed and should be removed. If the switch is not fully covered, the SwitchStatementsShouldHaveDefault rule will report. @@ -515,19 +475,92 @@ Summary: When a switch statement covers all possible cases, a default label is n Severity: 2 Category: OCLint -class +ill-placed default label in switch statement ---------- -Summary: This rule enforces the destructor of a virtual class must be virtual. +Summary: It is very confusing when default label is not the last label in a switch statement. Severity: 2 Category: OCLint -unnecessary null check for dealloc +prefer early exits and continue +---------- + +Summary: Early exits can reduce the indentation of a block of code, so that reader do not have to remember all the previous decisions, therefore, makes it easier to understand the code. + +Severity: 2 +Category: OCLint + +missing default in switch statements +---------- + +Summary: Switch statements should have a default statement. + +Severity: 2 +Category: OCLint + +use boxed expression +---------- + +Summary: This rule locates the places that can be migrated to the new Objective-C literals with boxed expressions. + +Severity: 1 +Category: OCLint + +use container literal +---------- + +Summary: This rule locates the places that can be migrated to the new Objective-C literals with container literals. + +Severity: 1 +Category: OCLint + +use number literal +---------- + +Summary: This rule locates the places that can be migrated to the new Objective-C literals with number literals. + +Severity: 1 +Category: OCLint + +use object subscripting +---------- + +Summary: Name: use object subscripting + +Severity: 1 +Category: OCLint + +missing hash method ---------- -Summary: char*p=0;deletep; is valid. This rule locates unnecessary if(p) checks. +Summary: Name: missing hash method Severity: 1 Category: OCLint +missing call to base method +---------- + +Summary: Name: missing call to base method + +Severity: 1 +Category: OCLint + +calling prohibited method +---------- + +Summary: Name: calling prohibited method + +Severity: 1 +Category: OCLint + +missing abstract method implementation +---------- + +Summary: Name: missing abstract method implementation + +Severity: 1 +Category: OCLint + + From f6da0bd3d4a91a960c3fd69e77d6f797c4de96ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Morissard?= Date: Tue, 15 Nov 2016 17:56:30 +0100 Subject: [PATCH 072/107] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 19242854..f5a7f7d6 100755 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ | Branch | Status | |----------|:------------------------------------------------------------------------------------------------------------------------------------------:| -| master | [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=master)](https://travis-ci.org/Backelite/sonar-objective-c) | -| develop| [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=develop)](https://travis-ci.org/Backelite/sonar-objective-c) | +| master | [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=master)](https://travis-ci.org/leverdeterre/sonar-objective-c) | +| develop| [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=develop)](https://travis-ci.org/leverdeterre/sonar-objective-c) | SonarQube Plugin for Objective-C ================================ From 6e042b0f98656deb4d6018634fa91d927ad50412 Mon Sep 17 00:00:00 2001 From: Jerome Morissard Date: Tue, 15 Nov 2016 18:04:27 +0100 Subject: [PATCH 073/107] Update OClint documentation link + number of rules --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f5a7f7d6..becf30a0 100755 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://g | Design |NO | | | Documentation |YES | | | Duplications |YES | | -| Issues |YES | Uses [OCLint](http://docs.oclint.org/en/dev/intro/installation.html): 63 rules, and [Faux Pas](http://fauxpasapp.com/): 102 rules| +| Issues |YES | Uses [OCLint](http://docs.oclint.org/en/dev/intro/installation.html): 71 rules, and [Faux Pas](http://fauxpasapp.com/): 102 rules| | Size |YES | | | Tests |YES | Uses [xctool](https://github.com/facebook/xctool), will probably switch to xcodebuild + [xcpretty](https://github.com/supermarin/xcpretty) soon | | Code coverage |YES | With [gcovr](http://gcovr.com) for project before Xcode 7, otherwise [slather](https://github.com/venmo/slather)| @@ -83,7 +83,7 @@ Binary packages are available in the release section. - [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```) - [xcpretty](https://github.com/supermarin/xcpretty) (see instructions below) - [xctool](https://github.com/facebook/xctool) ([HomeBrew](http://brew.sh) installed and ```brew install xctool```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. -- [OCLint](http://docs.oclint.org/en/dev/intro/installation.html) installed. Version 0.10.1 recommended. +- [OCLint](http://oclint-docs.readthedocs.io/en/stable/) installed. Version 0.11.0 recommended. - [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) - [slather](https://github.com/SlatherOrg/slather) (```gem install slather```). Version 2.1.0 or above. - [lizard](https://github.com/terryyin/lizard) ([PIP](https://pip.pypa.io/en/stable/installing/) installed and ```sudo pip install lizard```) From f161bb81086a3cfe47b0b170fb9884936a383f66 Mon Sep 17 00:00:00 2001 From: Jerome Morissard Date: Tue, 15 Nov 2016 18:05:14 +0100 Subject: [PATCH 074/107] Restore Backelite ci-travis account --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index becf30a0..55233bc7 100755 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ | Branch | Status | |----------|:------------------------------------------------------------------------------------------------------------------------------------------:| -| master | [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=master)](https://travis-ci.org/leverdeterre/sonar-objective-c) | -| develop| [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=develop)](https://travis-ci.org/leverdeterre/sonar-objective-c) | +| master | [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=master)](https://travis-ci.org/Backelite/sonar-objective-c) | +| develop| [![Build Status](https://travis-ci.org/Backelite/sonar-objective-c.svg?branch=develop)](https://travis-ci.org/Backelite/sonar-objective-c) | SonarQube Plugin for Objective-C ================================ From 638671b65b17e8c56b2903704ec9c715274095f3 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 1 Dec 2016 02:09:25 +0100 Subject: [PATCH 075/107] README update --- README.md | 6 ++---- pom.xml | 2 +- src/main/shell/run-sonar.sh | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 79dca752..79fd714a 100755 --- a/README.md +++ b/README.md @@ -50,11 +50,9 @@ Binary packages are available in the release section. ###Release history -###0.5.3-SNAPSHOT -- OCLint 0.11.0 support - ###0.5.2 -- OCLint 0.10.3 support +- OCLint 0.11.0 support (see https://github.com/Backelite/sonar-objective-c/pull/13) +- Removed required gcovr check in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/10) ####0.5.1 - Complexity with Lizard ! diff --git a/pom.xml b/pom.xml index 69283ecd..290aa386 100755 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec backelite-sonar-objective-c-plugin - 0.5.3-SNAPSHOT + 0.5.2 sonar-plugin diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 69a3d7dd..dfd05ce0 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -147,9 +147,8 @@ echo "Running run-sonar.sh..." ## CHECK PREREQUISITES -# xctool, gcovr and oclint installed +# xctool, oclint installed testIsInstalled xcodebuild -testIsInstalled gcovr testIsInstalled oclint testIsInstalled oclint-xcodebuild From 203427249e31e585711e3a27add49d306812824f Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 1 Dec 2016 02:23:21 +0100 Subject: [PATCH 076/107] Updated README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 79fd714a..c5ca5148 100755 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Binary packages are available in the release section. ###0.5.2 - OCLint 0.11.0 support (see https://github.com/Backelite/sonar-objective-c/pull/13) - Removed required gcovr check in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/10) +- Fixed issued in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/2) ####0.5.1 - Complexity with Lizard ! From 214eabc5314a57011c390d89c6ee79f154990a74 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 1 Dec 2016 02:28:06 +0100 Subject: [PATCH 077/107] README update --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c5ca5148..de3e36e0 100755 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

| Branch | Status | @@ -54,6 +54,7 @@ Binary packages are available in the release section. - OCLint 0.11.0 support (see https://github.com/Backelite/sonar-objective-c/pull/13) - Removed required gcovr check in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/10) - Fixed issued in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/2) +- Better test file detection pattern (see https://github.com/Backelite/sonar-objective-c/pull/3/files) ####0.5.1 - Complexity with Lizard ! From fe098d3fd8ea2ad839f2a4b6763cf0d4854b2a51 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 6 Dec 2016 01:35:22 +0100 Subject: [PATCH 078/107] Support for SonarQube 6 --- README.md | 3 + pom.xml | 4 +- .../objectivec/ObjectiveCSquidSensor.java | 27 ++-- .../complexity/LizardMeasurePersistor.java | 16 +- .../objectivec/coverage/CoberturaParser.java | 69 --------- .../coverage/CoberturaReportParser.java | 143 ++++++++++++++++++ .../objectivec/coverage/CoberturaSensor.java | 37 +++-- .../coverage/CoberturaXMLStreamHandler.java | 100 ------------ .../coverage/CoverageMeasuresPersistor.java | 80 ---------- .../objectivec/tests/SurefireParser.java | 39 ++--- .../fauxpas/FauxPasReportParser.java | 10 +- .../violations/fauxpas/FauxPasSensor.java | 2 +- .../violations/oclint/OCLintParser.java | 7 +- .../violations/oclint/OCLintSensor.java | 2 +- .../oclint/OCLintXMLStreamHandler.java | 29 ++-- src/main/shell/run-sonar.sh | 2 +- .../coverage/CoberturaParserTest.java | 60 -------- .../coverage/CoberturaSensorTest.java | 76 ---------- .../CoberturaXMLStreamHandlerTest.java | 114 -------------- 19 files changed, 234 insertions(+), 586 deletions(-) delete mode 100644 src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaParser.java create mode 100644 src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java delete mode 100644 src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java delete mode 100644 src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java delete mode 100644 src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaParserTest.java delete mode 100644 src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaSensorTest.java delete mode 100644 src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandlerTest.java diff --git a/README.md b/README.md index de3e36e0..a41682b9 100755 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ Binary packages are available in the release section. ###Release history +###0.6.0 +- SonarQube 6 support. Important : will work with SonarQube 5.x and above only. Will not work anymore with SonarQube 4.5.x anymore. + ###0.5.2 - OCLint 0.11.0 support (see https://github.com/Backelite/sonar-objective-c/pull/13) - Removed required gcovr check in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/10) diff --git a/pom.xml b/pom.xml index 290aa386..518121f3 100755 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ sonar-plugin Objective-C Sonar Plugin - Enables analysis of Objective-C projects into Sonar. + Enables analysis of Objective-C projects into SonarQube. https://github.com/Backelite/sonar-objective-c @@ -95,7 +95,7 @@ true - 4.3 + 5.0 1.20 diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java index 77ccb9e3..5e7cf5cc 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java @@ -19,10 +19,8 @@ */ package org.sonar.plugins.objectivec; -import java.util.Collection; -import java.util.Locale; - import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.FilePredicate; @@ -30,18 +28,13 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.rule.CheckFactory; import org.sonar.api.batch.rule.Checks; -import org.sonar.api.checks.AnnotationCheckFactory; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.issue.Issuable; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.PersistenceMode; -import org.sonar.api.measures.RangeDistributionBuilder; import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.Violation; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.objectivec.ObjectiveCAstScanner; import org.sonar.objectivec.ObjectiveCConfiguration; @@ -50,21 +43,23 @@ import org.sonar.objectivec.checks.CheckList; import org.sonar.plugins.objectivec.core.ObjectiveC; import org.sonar.squidbridge.AstScanner; +import org.sonar.squidbridge.SquidAstVisitor; 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 java.util.Collection; +import java.util.List; +import java.util.Locale; + public class ObjectiveCSquidSensor implements Sensor { private final Number[] FUNCTIONS_DISTRIB_BOTTOM_LIMITS = {1, 2, 4, 6, 8, 10, 12, 20, 30}; private final Number[] FILES_DISTRIB_BOTTOM_LIMITS = {0, 5, 10, 20, 30, 60, 90}; - private final AnnotationCheckFactory annotationCheckFactory; private final FileSystem fileSystem; private final PathResolver pathResolver; private final ResourcePerspectives resourcePerspectives; @@ -77,7 +72,7 @@ public class ObjectiveCSquidSensor implements Sensor { private AstScanner scanner; public ObjectiveCSquidSensor(RulesProfile profile, FileSystem fileSystem, PathResolver pathResolver, ResourcePerspectives resourcePerspectives, CheckFactory checkFactory) { - this.annotationCheckFactory = AnnotationCheckFactory.create(profile, CheckList.REPOSITORY_KEY, CheckList.getChecks()); + this.fileSystem = fileSystem; this.pathResolver = pathResolver; this.resourcePerspectives = resourcePerspectives; @@ -95,8 +90,10 @@ public void analyse(Project project, SensorContext context) { this.project = project; this.context = context; - Collection squidChecks = annotationCheckFactory.getChecks(); - this.scanner = ObjectiveCAstScanner.create(createConfiguration(), squidChecks.toArray(new SquidCheck[squidChecks.size()])); + List> visitors = Lists.>newArrayList(checks.all()); + AstScanner scanner = ObjectiveCAstScanner.create(createConfiguration(), visitors.toArray(new SquidAstVisitor[visitors.size()])); + + scanner.scanFiles(ImmutableList.copyOf(fileSystem.files(mainFilePredicates))); Collection squidSourceFiles = scanner.getIndex().search(new QueryByType(SourceFile.class)); @@ -133,7 +130,7 @@ private void saveIssues(InputFile inputFile, SourceFile squidFile) { Collection messages = squidFile.getCheckMessages(); - Resource resource = context.getResource(org.sonar.api.resources.File.fromIOFile(inputFile.file(), project)); + Resource resource = context.getResource(inputFile); if (messages != null && resource != null) { for (CheckMessage message : messages) { diff --git a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java b/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java index 02144bee..9dc277bf 100644 --- a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java +++ b/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java @@ -23,8 +23,10 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.measures.Measure; import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; import java.io.File; import java.util.List; @@ -61,13 +63,21 @@ public void saveMeasures(final Map> measures) { } for (Map.Entry> entry : measures.entrySet()) { - final org.sonar.api.resources.File file = org.sonar.api.resources.File.fromIOFile(new File(fileSystem.baseDir(), entry.getKey()), project); + File file = new File(fileSystem.baseDir(), entry.getKey()); + InputFile inputFile = fileSystem.inputFile(fileSystem.predicates().hasAbsolutePath(file.getAbsolutePath())); - if (sensorContext.getResource(file) != null) { + if (inputFile == null) { + LOGGER.warn("file not included in sonar {}", entry.getKey()); + continue; + } + + Resource resource = sensorContext.getResource(inputFile); + + if (resource != null) { for (Measure measure : entry.getValue()) { try { LOGGER.debug("Save measure {} for file {}", measure.getMetric().getName(), file); - sensorContext.saveMeasure(file, measure); + sensorContext.saveMeasure(resource, measure); } catch (Exception e) { LOGGER.error(" Exception -> {} -> {}", entry.getKey(), measure.getMetric().getName(), e); } diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaParser.java b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaParser.java deleted file mode 100644 index 98723097..00000000 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaParser.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.coverage; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import javax.xml.stream.XMLStreamException; - -import org.slf4j.LoggerFactory; -import org.sonar.api.measures.CoverageMeasuresBuilder; -import org.sonar.api.utils.StaxParser; - -final class CoberturaParser { - public Map parseReport(final File xmlFile) { - Map result = null; - try { - final InputStream reportStream = new FileInputStream(xmlFile); - result = parseReport(reportStream); - reportStream.close(); - } catch (final IOException e) { - LoggerFactory.getLogger(getClass()).error( - "Error processing file named {}", xmlFile, e); - result = new HashMap(); - } - return result; - } - - public Map parseReport( - final InputStream xmlFile) { - - final Map measuresForReport = new HashMap(); - try { - final StaxParser parser = new StaxParser( - new CoberturaXMLStreamHandler(measuresForReport)); - parser.parse(xmlFile); - } catch (final XMLStreamException e) { - LoggerFactory.getLogger(getClass()).error( - "Error while parsing XML stream.", e); - } - return measuresForReport; - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } -} diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java new file mode 100644 index 00000000..7de3798d --- /dev/null +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java @@ -0,0 +1,143 @@ +/* + * Sonar Objective-C Plugin + * Copyright (C) 2012 OCTO Technology, Backelite + * 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.coverage; + + +import com.google.common.collect.Maps; +import org.apache.commons.lang.StringUtils; +import org.codehaus.staxmate.in.SMHierarchicCursor; +import org.codehaus.staxmate.in.SMInputCursor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.measures.CoverageMeasuresBuilder; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Project; +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 javax.xml.stream.XMLStreamException; +import java.io.File; +import java.text.ParseException; +import java.util.Locale; +import java.util.Map; + +final class CoberturaReportParser { + + private static final Logger LOGGER = LoggerFactory.getLogger(CoberturaReportParser.class); + + private final FileSystem fileSystem; + private final Project project; + private final SensorContext context; + + private CoberturaReportParser(FileSystem fileSystem, Project project, SensorContext context) { + this.fileSystem = fileSystem; + this.project = project; + this.context = context; + } + + /** + * Parse a Cobertura xml report and create measures accordingly + */ + public static void parseReport(File xmlFile, FileSystem fileSystem, Project project, SensorContext context) { + new CoberturaReportParser(fileSystem, project, context).parse(xmlFile); + } + + private void parse(File xmlFile) { + try { + StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() { + + @Override + public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException { + rootCursor.advance(); + collectPackageMeasures(rootCursor.descendantElementCursor("package")); + } + }); + parser.parse(xmlFile); + } catch (XMLStreamException e) { + throw new XmlParserException(e); + } + } + + private void collectPackageMeasures(SMInputCursor pack) throws XMLStreamException { + while (pack.getNext() != null) { + Map builderByFilename = Maps.newHashMap(); + collectFileMeasures(pack.descendantElementCursor("class"), builderByFilename); + for (Map.Entry entry : builderByFilename.entrySet()) { + String filePath = entry.getKey(); + File file = new File(fileSystem.baseDir(), filePath); + InputFile inputFile = fileSystem.inputFile(fileSystem.predicates().hasAbsolutePath(file.getAbsolutePath())); + + if (inputFile == null) { + LOGGER.warn("file not included in sonar {}", filePath); + continue; + } + + Resource resource = context.getResource(inputFile); + if (resourceExists(resource)) { + for (Measure measure : entry.getValue().createMeasures()) { + context.saveMeasure(resource, measure); + } + } + } + } + } + + private boolean resourceExists(Resource file) { + return context.getResource(file) != null; + } + + private static void collectFileMeasures(SMInputCursor clazz, + Map builderByFilename) throws XMLStreamException { + while (clazz.getNext() != null) { + String fileName = clazz.getAttrValue("filename"); + CoverageMeasuresBuilder builder = builderByFilename.get(fileName); + if (builder == null) { + builder = CoverageMeasuresBuilder.create(); + builderByFilename.put(fileName, builder); + } + collectFileData(clazz, builder); + } + } + + private static void collectFileData(SMInputCursor clazz, + CoverageMeasuresBuilder builder) throws XMLStreamException { + SMInputCursor line = clazz.childElementCursor("lines").advance().childElementCursor("line"); + while (line.getNext() != null) { + int lineId = Integer.parseInt(line.getAttrValue("number")); + try { + builder.setHits(lineId, (int) ParsingUtils.parseNumber(line.getAttrValue("hits"), Locale.ENGLISH)); + } catch (ParseException e) { + throw new XmlParserException(e); + } + + String isBranch = line.getAttrValue("branch"); + String text = line.getAttrValue("condition-coverage"); + if (StringUtils.equals(isBranch, "true") && StringUtils.isNotBlank(text)) { + String[] conditions = StringUtils.split(StringUtils.substringBetween(text, "(", ")"), "/"); + builder.setConditions(lineId, Integer.parseInt(conditions[1]), Integer.parseInt(conditions[0])); + } + } + } +} 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 5c7e0430..07d9bc48 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Map; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; @@ -30,54 +31,52 @@ import org.sonar.api.config.Settings; import org.sonar.api.measures.CoverageMeasuresBuilder; import org.sonar.api.resources.Project; +import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.plugins.objectivec.ObjectiveCPlugin; import org.sonar.plugins.objectivec.core.ObjectiveC; public final class CoberturaSensor implements Sensor { + private static final Logger LOGGER = LoggerFactory.getLogger(CoberturaSensor.class); + public static final String REPORT_PATTERN_KEY = ObjectiveCPlugin.PROPERTY_PREFIX + ".coverage.reportPattern"; public static final String DEFAULT_REPORT_PATTERN = "sonar-reports/coverage*.xml"; private final ReportFilesFinder reportFilesFinder; - private final CoberturaParser parser = new CoberturaParser(); - private final Settings conf; + private final Settings settings; private final FileSystem fileSystem; + private final PathResolver pathResolver; + private Project project; - public CoberturaSensor(final FileSystem fileSystem, final Settings config) { + public CoberturaSensor(final FileSystem fileSystem, final PathResolver pathResolver, final Settings settings) { - this.conf = config; + this.settings = settings; this.fileSystem = fileSystem; + this.pathResolver = pathResolver; - reportFilesFinder = new ReportFilesFinder(config, REPORT_PATTERN_KEY, DEFAULT_REPORT_PATTERN); + reportFilesFinder = new ReportFilesFinder(settings, REPORT_PATTERN_KEY, DEFAULT_REPORT_PATTERN); } public boolean shouldExecuteOnProject(final Project project) { + this.project = project; + return project.isRoot() && fileSystem.languages().contains(ObjectiveC.KEY); } public void analyse(final Project project, final SensorContext context) { - final CoverageMeasuresPersistor measuresPersistor = new CoverageMeasuresPersistor(project, context, fileSystem); - final String projectBaseDir = fileSystem.baseDir() - .getPath(); - measuresPersistor.saveMeasures(parseReportsIn(projectBaseDir)); - } + final String projectBaseDir = fileSystem.baseDir().getPath(); - private Map parseReportsIn( - final String baseDir) { - final Map measuresTotal = new HashMap(); - - for (final File report : reportFilesFinder.reportsIn(baseDir)) { - LoggerFactory.getLogger(getClass()).info( - "Processing coverage report {}", report); - measuresTotal.putAll(parser.parseReport(report)); + for (final File report : reportFilesFinder.reportsIn(projectBaseDir)) { + LOGGER.info("Processing coverage report {}", report); + CoberturaReportParser.parseReport(report, fileSystem, project, context); } - return measuresTotal; } + } diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java b/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java deleted file mode 100644 index 18efc286..00000000 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.coverage; - -import java.util.Map; - -import javax.xml.stream.XMLStreamException; - -import org.apache.commons.lang.StringUtils; -import org.codehaus.staxmate.in.SMHierarchicCursor; -import org.codehaus.staxmate.in.SMInputCursor; -import org.sonar.api.measures.CoverageMeasuresBuilder; -import org.sonar.api.utils.StaxParser; - -class CoberturaXMLStreamHandler implements StaxParser.XmlStreamHandler { - private final Map measuresForReport; - - public CoberturaXMLStreamHandler( - final Map data) { - measuresForReport = data; - } - - public void stream(final SMHierarchicCursor rootCursor) - throws XMLStreamException { - rootCursor.advance(); - collectPackageMeasures(rootCursor.descendantElementCursor("package")); - } - - private void collectPackageMeasures(final SMInputCursor pack) - throws XMLStreamException { - while (pack.getNext() != null) { - collectFileMeasures(pack.descendantElementCursor("class")); - } - } - - private void collectFileMeasures(final SMInputCursor clazz) - throws XMLStreamException { - while (clazz.getNext() != null) { - collectFileData(clazz); - } - } - - private void collectFileData(final SMInputCursor clazz) - throws XMLStreamException { - final CoverageMeasuresBuilder builder = builderFor(clazz); - final SMInputCursor line = clazz.childElementCursor("lines").advance() - .childElementCursor("line"); - - while (null != line.getNext()) { - recordCoverageFor(line, builder); - } - } - - private void recordCoverageFor(final SMInputCursor line, - final CoverageMeasuresBuilder builder) throws XMLStreamException { - final int lineId = Integer.parseInt(line.getAttrValue("number")); - final int noHits = (int) Math.min( - Double.parseDouble(line.getAttrValue("hits")), Integer.MAX_VALUE); - final String isBranch = line.getAttrValue("branch"); - final String conditionText = line.getAttrValue("condition-coverage"); - - builder.setHits(lineId, noHits); - - if (StringUtils.equals(isBranch, "true") - && StringUtils.isNotBlank(conditionText)) { - final String[] conditions = StringUtils.split( - StringUtils.substringBetween(conditionText, "(", ")"), "/"); - builder.setConditions(lineId, Integer.parseInt(conditions[1]), - Integer.parseInt(conditions[0])); - } - } - - private CoverageMeasuresBuilder builderFor(final SMInputCursor clazz) - throws XMLStreamException { - final String fileName = clazz.getAttrValue("filename"); - CoverageMeasuresBuilder builder = measuresForReport.get(fileName); - if (builder == null) { - builder = CoverageMeasuresBuilder.create(); - measuresForReport.put(fileName, builder); - } - return builder; - } -} \ No newline at end of file diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java b/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java deleted file mode 100644 index 1e128632..00000000 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoverageMeasuresPersistor.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.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.batch.fs.FileSystem; -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; - private final SensorContext context; - private final FileSystem fileSystem; - - public CoverageMeasuresPersistor(final Project p, final SensorContext c, final FileSystem fileSystem) { - project = p; - context = c; - this.fileSystem = fileSystem; - } - - 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(fileSystem.baseDir(), filePath), project); - - if (fileExists(context, objcfile)) { - LoggerFactory.getLogger(getClass()).debug( - "File {} was found in the project.", filePath); - saveMeasures(measureBuilder, objcfile); - } - } - - private void saveMeasures(final CoverageMeasuresBuilder measureBuilder, - final org.sonar.api.resources.File objcfile) { - for (final Measure measure : measureBuilder.createMeasures()) { - LoggerFactory.getLogger(getClass()).debug("Measure {}", - measure.getMetric().getName()); - context.saveMeasure(objcfile, measure); - } - } - - private boolean fileExists(final SensorContext context, - final org.sonar.api.resources.File file) { - return context.getResource(file) != null; - } -} diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java index be92ce3c..5a4ac53f 100644 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java @@ -189,40 +189,21 @@ public Resource getUnitTestResource(String classname) { String fileName = classname.replace('.', '/') + ".m"; - File file = new File(fileName); - if (!file.isAbsolute()) { - file = new File(fileSystem.baseDir(), fileName); + InputFile inputFile = fileSystem.inputFile(fileSystem.predicates().or(fileSystem.predicates().matchesPathPattern("**/" + fileName), + fileSystem.predicates().matchesPathPattern("**/" + fileName.replace("_", "+")))); + if (inputFile == null) { + return null; } - // Category naming case - if (!file.isFile() || !file.exists()) { - file = new File(fileSystem.baseDir(), fileName.replace("_", "+")); - } + Resource resource = context.getResource(inputFile); - /* - * Most xcodebuild JUnit parsers don't include the path to the class in the class field, so search for it if it - * wasn't found in the root. - */ - if (!file.isFile() || !file.exists()) { - List files = ImmutableList.copyOf(fileSystem.files(fileSystem.predicates().and( - fileSystem.predicates().hasType(InputFile.Type.TEST), - fileSystem.predicates().or(fileSystem.predicates().matchesPathPattern("**/" + fileName), - fileSystem.predicates().matchesPathPattern("**/" + fileName.replace("_", "+")))))); - - if (files.isEmpty()) { - LOG.info("Unable to locate test source file {}", fileName); - } else { - /* - * Lazily get the first file, since we wouldn't be able to determine the correct one from just the - * test class name in the event that there are multiple matches. - */ - file = files.get(0); - } + if(resource instanceof org.sonar.api.resources.File) { + org.sonar.api.resources.File sonarFile = (org.sonar.api.resources.File) resource; + sonarFile.setQualifier(Qualifiers.UNIT_TEST_FILE); } - org.sonar.api.resources.File sonarFile = org.sonar.api.resources.File.fromIOFile(file, project); - sonarFile.setQualifier(Qualifiers.UNIT_TEST_FILE); - return sonarFile; + + return resource; } } diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java index 3a8cf30f..cb1042fd 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -26,6 +26,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; @@ -41,13 +43,15 @@ public class FauxPasReportParser { private final Project project; private final SensorContext context; private final ResourcePerspectives resourcePerspectives; + private final FileSystem fileSystem; private static final Logger LOGGER = LoggerFactory.getLogger(FauxPasReportParser.class); - public FauxPasReportParser(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives) { + public FauxPasReportParser(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives, final FileSystem fileSystem) { project = p; context = c; this.resourcePerspectives = resourcePerspectives; + this.fileSystem = fileSystem; } public void parseReport(File reportFile) { @@ -81,9 +85,9 @@ private void recordIssue(final JSONObject diagnosticJson) { if (filePath != null) { - org.sonar.api.resources.File resource = org.sonar.api.resources.File.fromIOFile(new File(filePath), project); - Issuable issuable = resourcePerspectives.as(Issuable.class, resource); + InputFile inputFile = fileSystem.inputFile(fileSystem.predicates().hasAbsolutePath(filePath)); + Issuable issuable = resourcePerspectives.as(Issuable.class, inputFile); if (issuable != null) { diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java index 7bdc2a88..6cd20e28 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java @@ -64,7 +64,7 @@ public void analyse(Project module, SensorContext context) { final String projectBaseDir = fileSystem.baseDir().getPath(); - FauxPasReportParser parser = new FauxPasReportParser(module, context, resourcePerspectives); + FauxPasReportParser parser = new FauxPasReportParser(module, context, resourcePerspectives, fileSystem); parseReportIn(projectBaseDir, parser); } diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java index bd0d34ae..f10bac72 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java @@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.resources.Project; import org.sonar.api.rules.Violation; @@ -40,11 +41,13 @@ final class OCLintParser { private final Project project; private final SensorContext context; private final ResourcePerspectives resourcePerspectives; + private final FileSystem fileSystem; - public OCLintParser(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives) { + public OCLintParser(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives, final FileSystem fileSystem) { project = p; context = c; this.resourcePerspectives = resourcePerspectives; + this.fileSystem = fileSystem; } public void parseReport(final File file) { @@ -63,7 +66,7 @@ public void parseReport(final InputStream inputStream) { try { final StaxParser parser = new StaxParser( - new OCLintXMLStreamHandler(project, context, resourcePerspectives)); + new OCLintXMLStreamHandler(project, context, resourcePerspectives, fileSystem)); parser.parse(inputStream); } catch (final XMLStreamException e) { LoggerFactory.getLogger(getClass()).error( diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java index d7546def..b4a28dbc 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java @@ -56,7 +56,7 @@ public boolean shouldExecuteOnProject(final Project project) { public void analyse(final Project project, final SensorContext context) { final String projectBaseDir = fileSystem.baseDir().getPath(); - final OCLintParser parser = new OCLintParser(project, context, resourcePerspectives); + final OCLintParser parser = new OCLintParser(project, context, resourcePerspectives, fileSystem); parseReportIn(projectBaseDir, parser); diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java index 162f9c7f..b80b95a3 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java @@ -27,6 +27,8 @@ import org.codehaus.staxmate.in.SMInputCursor; import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; @@ -39,11 +41,13 @@ final class OCLintXMLStreamHandler implements XmlStreamHandler { private final Project project; private final SensorContext context; private final ResourcePerspectives resourcePerspectives; + private final FileSystem fileSystem; - public OCLintXMLStreamHandler(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives) { + public OCLintXMLStreamHandler(final Project p, final SensorContext c, final ResourcePerspectives resourcePerspectives, final FileSystem fileSystem) { project = p; context = c; this.resourcePerspectives = resourcePerspectives; + this.fileSystem = fileSystem; } public void stream(final SMHierarchicCursor rootCursor) throws XMLStreamException { @@ -58,29 +62,32 @@ private void collectIssuesFor(final SMInputCursor file) throws XMLStreamExceptio final String filePath = file.getAttrValue("name"); LoggerFactory.getLogger(getClass()).debug("Collection violations for {}", filePath); - final org.sonar.api.resources.File resource = findResource(filePath); - if (fileExists(resource)) { + final InputFile inputFile = findResource(filePath); + if (fileExists(inputFile)) { LoggerFactory.getLogger(getClass()).debug("File {} was found in the project.", filePath); - collectFileIssues(resource, file); + collectFileIssues(inputFile, file); } } - private void collectFileIssues(final org.sonar.api.resources.File resource, final SMInputCursor file) throws XMLStreamException { + private void collectFileIssues(final InputFile inputFile, final SMInputCursor file) throws XMLStreamException { final SMInputCursor line = file.childElementCursor("violation"); while (null != line.getNext()) { - recordViolation(resource, line); + recordViolation(inputFile, line); } } - private org.sonar.api.resources.File findResource(final String filePath) { - return org.sonar.api.resources.File.fromIOFile(new File(filePath), project); + private InputFile findResource(final String filePath) { + + File file = new File(filePath); + return fileSystem.inputFile(fileSystem.predicates().hasAbsolutePath(file.getAbsolutePath())); + } - private void recordViolation(final org.sonar.api.resources.File resource, final SMInputCursor line) throws XMLStreamException { + private void recordViolation(InputFile inputFile, final SMInputCursor line) throws XMLStreamException { - Issuable issuable = resourcePerspectives.as(Issuable.class, resource); + Issuable issuable = resourcePerspectives.as(Issuable.class, inputFile); if (issuable != null) { @@ -96,7 +103,7 @@ private void recordViolation(final org.sonar.api.resources.File resource, final } } - private boolean fileExists(final org.sonar.api.resources.File file) { + private boolean fileExists(InputFile file) { return context.getResource(file) != null; } diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index b9eb8496..c2df8ed3 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -431,7 +431,7 @@ fi # SonarQube echo -n 'Running SonarQube using SonarQube Runner' -runCommand /dev/stdout sonar-runner -Dsonar.projectVersion=$projectVersion +runCommand /dev/stdout sonar-runner # Kill progress indicator stopProgress diff --git a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaParserTest.java b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaParserTest.java deleted file mode 100644 index 35f0d042..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaParserTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.coverage; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.util.Map; - -import org.apache.tools.ant.filters.StringInputStream; -import org.junit.Test; -import org.sonar.api.measures.CoverageMeasuresBuilder; - -public final class CoberturaParserTest { - private final String VALID_REPORT_FILE_PATH = "FILEPATH"; - private final String VALID_REPORT = "."; - - @Test - public void parseReportShouldReturnAnEmptyMapWhenTheReportIsInvalid() { - final CoberturaParser coberturaParser = new CoberturaParser(); - final Map measures = coberturaParser.parseReport(new StringInputStream("")); - - assertTrue(measures.isEmpty()); - } - - @Test - public void parseReportShouldReturnAnEmptyMapWhenTheFileIsInvalid() { - final CoberturaParser coberturaParser = new CoberturaParser(); - final Map measures = coberturaParser.parseReport(new File("")); - - assertTrue(measures.isEmpty()); - } - - @Test - public void parseReportShouldReturnAMapOfFileToMeasuresWhenTheReportIsValid() { - final CoberturaParser coberturaParser = new CoberturaParser(); - final Map measures = coberturaParser.parseReport(new StringInputStream(VALID_REPORT)); - - assertNotNull(measures.get(VALID_REPORT_FILE_PATH)); - } - -} diff --git a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaSensorTest.java deleted file mode 100644 index 81136f6b..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaSensorTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.coverage; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.config.Settings; -import org.sonar.api.resources.Project; -import org.sonar.api.scan.filesystem.ModuleFileSystem; -import org.sonar.plugins.objectivec.core.ObjectiveC; - -import java.util.SortedSet; -import java.util.TreeSet; - -public final class CoberturaSensorTest { - - private Settings settings; - - @Before - public void setUp() { - settings = new Settings(); - } - - @Test - public void shouldExecuteOnProjectShouldBeTrueWhenProjectIsObjc() { - final Project project = new Project("Test"); - - FileSystem fileSystem = mock(FileSystem.class); - SortedSet languages = new TreeSet(); - languages.add(ObjectiveC.KEY); - when(fileSystem.languages()).thenReturn(languages); - - final CoberturaSensor testedSensor = new CoberturaSensor(fileSystem, settings); - - assertTrue(testedSensor.shouldExecuteOnProject(project)); - } - - @Test - public void shouldExecuteOnProjectShouldBeFalseWhenProjectIsSomethingElse() { - final Project project = new Project("Test"); - settings.setProperty("sonar.language", "Test"); - - FileSystem fileSystem = mock(FileSystem.class); - SortedSet languages = new TreeSet(); - languages.add("Test"); - when(fileSystem.languages()).thenReturn(languages); - - final CoberturaSensor testedSensor = new CoberturaSensor(fileSystem, settings); - - assertFalse(testedSensor.shouldExecuteOnProject(project)); - } - -} diff --git a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandlerTest.java b/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandlerTest.java deleted file mode 100644 index 4175a539..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/coverage/CoberturaXMLStreamHandlerTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.coverage; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.HashMap; -import java.util.Map; - -import javax.xml.stream.XMLStreamException; - -import org.apache.tools.ant.filters.StringInputStream; -import org.junit.Test; -import org.sonar.api.measures.CoverageMeasuresBuilder; -import org.sonar.api.utils.StaxParser; - -public final class CoberturaXMLStreamHandlerTest { - private final String EMPTY_REPORT = "."; - private final String VALID_REPORT = "."; - private final String FILE_PATH = "FILEPATH"; - private final int NO_HIT_LINE = 25; - private final int NO_BRANCH_LINE = 29; - private final int BRANCH_LINE = 35; - - @Test - public void streamLeavesTheMapEmptyWhenNoLinesAreFound() throws XMLStreamException { - final Map parseResults = new HashMap(); - final StaxParser parser = new StaxParser(new CoberturaXMLStreamHandler(parseResults)); - - parser.parse(new StringInputStream(EMPTY_REPORT)); - - assertTrue(parseResults.isEmpty()); - } - - @Test - public void streamAddsACoverageMeasureBuilderForClassesInTheReport() throws XMLStreamException { - final Map parseResults = new HashMap(); - final StaxParser parser = new StaxParser(new CoberturaXMLStreamHandler(parseResults)); - - parser.parse(new StringInputStream(VALID_REPORT)); - - assertNotNull(parseResults.get(FILE_PATH)); - } - - @Test - public void streamRecords0HitsForLinesWithNoHits() throws XMLStreamException { - final Map parseResults = new HashMap(); - final StaxParser parser = new StaxParser(new CoberturaXMLStreamHandler(parseResults)); - - parser.parse(new StringInputStream(VALID_REPORT)); - - assertEquals(Integer.valueOf(0), parseResults.get(FILE_PATH).getHitsByLine().get(NO_HIT_LINE)); - } - - @Test - public void streamRecordsHitsForLinesWithNoBranch() throws XMLStreamException { - final Map parseResults = new HashMap(); - final StaxParser parser = new StaxParser(new CoberturaXMLStreamHandler(parseResults)); - - parser.parse(new StringInputStream(VALID_REPORT)); - - assertEquals(Integer.valueOf(1), parseResults.get(FILE_PATH).getHitsByLine().get(NO_BRANCH_LINE)); - } - - @Test - public void streamRecordsHitsForLinesWithBranch() throws XMLStreamException { - final Map parseResults = new HashMap(); - final StaxParser parser = new StaxParser(new CoberturaXMLStreamHandler(parseResults)); - - parser.parse(new StringInputStream(VALID_REPORT)); - - assertEquals(Integer.valueOf(10), parseResults.get(FILE_PATH).getHitsByLine().get(BRANCH_LINE)); - } - - @Test - public void streamRecordsConditionsForLinesWithBranch() throws XMLStreamException { - final Map parseResults = new HashMap(); - final StaxParser parser = new StaxParser(new CoberturaXMLStreamHandler(parseResults)); - - parser.parse(new StringInputStream(VALID_REPORT)); - - assertEquals(Integer.valueOf(2), parseResults.get(FILE_PATH).getConditionsByLine().get(BRANCH_LINE)); - } - - @Test - public void streamRecordsConditionsHitsForLinesWithBranch() throws XMLStreamException { - final Map parseResults = new HashMap(); - final StaxParser parser = new StaxParser(new CoberturaXMLStreamHandler(parseResults)); - - parser.parse(new StringInputStream(VALID_REPORT)); - - assertEquals(Integer.valueOf(1), parseResults.get(FILE_PATH).getCoveredConditionsByLine().get(BRANCH_LINE)); - } - -} From 387a96e68627cfb548dadb842aafe80fe93b2411 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 22 Feb 2017 01:09:54 +0100 Subject: [PATCH 079/107] Fixed 'test_data' issue with SonarQube 6.2 --- README.md | 5 ++++- pom.xml | 2 +- .../org/sonar/plugins/objectivec/tests/SurefireParser.java | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index de3e36e0..263cf6bb 100755 --- a/README.md +++ b/README.md @@ -50,7 +50,10 @@ Binary packages are available in the release section. ###Release history -###0.5.2 +####0.5.3 +- + +####0.5.2 - OCLint 0.11.0 support (see https://github.com/Backelite/sonar-objective-c/pull/13) - Removed required gcovr check in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/10) - Fixed issued in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/2) diff --git a/pom.xml b/pom.xml index 290aa386..3acc0c61 100755 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec backelite-sonar-objective-c-plugin - 0.5.2 + 0.5.3 sonar-plugin diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java index be92ce3c..be956baa 100644 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java @@ -158,7 +158,6 @@ private void saveTestsDetails(SensorContext context, TestSuiteReport fileReport) } } 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) { From 57f2e4d628b359dba03c6f200373a1728cbbb9a9 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 22 Feb 2017 01:14:24 +0100 Subject: [PATCH 080/107] pom.xml version bump --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 23e95d77..2cd33bec 100755 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec backelite-sonar-objective-c-plugin - 0.5.3 + 0.6.0 sonar-plugin From 5a8509cefcc91ab6da2c29969c74c3ff200bcfd3 Mon Sep 17 00:00:00 2001 From: Mihai Costea Date: Tue, 7 Mar 2017 11:02:39 +0200 Subject: [PATCH 081/107] Sonar 6 fix for Faux Pas Issuable is created even if the inputFile is null Check if inputFile is null before continuing execution --- .../objectivec/violations/fauxpas/FauxPasReportParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java index cb1042fd..1656feb5 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -89,7 +89,7 @@ private void recordIssue(final JSONObject diagnosticJson) { InputFile inputFile = fileSystem.inputFile(fileSystem.predicates().hasAbsolutePath(filePath)); Issuable issuable = resourcePerspectives.as(Issuable.class, inputFile); - if (issuable != null) { + if (issuable != null && inputFile != null) { JSONObject extent = (JSONObject)diagnosticJson.get("extent"); JSONObject start = (JSONObject)extent.get("start"); From e0c2b5dd0c01df59360f4753d859bcb4c3fb1d30 Mon Sep 17 00:00:00 2001 From: Mihai Costea Date: Tue, 7 Mar 2017 16:46:39 +0200 Subject: [PATCH 082/107] Sonar 6 - OCLint compatibility Added extra null check --- .../objectivec/violations/oclint/OCLintXMLStreamHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java index b80b95a3..133366af 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java +++ b/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java @@ -104,6 +104,10 @@ private void recordViolation(InputFile inputFile, final SMInputCursor line) thro } private boolean fileExists(InputFile file) { + if (file == null) { + return false; + } + return context.getResource(file) != null; } From 5dd74602a2a5187d21296d8466f9ee5bcc08423b Mon Sep 17 00:00:00 2001 From: Mihai Costea Date: Wed, 8 Mar 2017 11:45:07 +0200 Subject: [PATCH 083/107] Add support for Objective-C++ (.mm) files --- .../java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java index 0f0be8b0..f7aa85c1 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java +++ b/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java @@ -77,7 +77,7 @@ public List> getExtensions() { public static final String FALSE = "false"; public static final String FILE_SUFFIXES_KEY = "sonar.objectivec.file.suffixes"; - public static final String FILE_SUFFIXES_DEFVALUE = "h,m"; + public static final String FILE_SUFFIXES_DEFVALUE = "h,m,mm"; public static final String PROPERTY_PREFIX = "sonar.objectivec"; From 17b311c9980275d930160c306a1c763c36665842 Mon Sep 17 00:00:00 2001 From: David Yang Date: Fri, 17 Mar 2017 10:02:48 +0100 Subject: [PATCH 084/107] Add -nounittests and -usesonarscanner parameters to run-sonar.sh script --- src/main/shell/run-sonar.sh | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index c2df8ed3..a48c66a5 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -123,16 +123,21 @@ function runCommand() { ## COMMAND LINE OPTIONS vflag="" nflag="" +unittests="on" oclint="on" fauxpas="on" lizard="on" +sonarscanner="" + while [ $# -gt 0 ] do case "$1" in -v) vflag=on;; -n) nflag=on;; - -nooclint) oclint="";; + -nounittests) unittests="";; + -nooclint) oclint="";; -nofauxpas) fauxpas="";; + -usesonarscanner) sonarscanner="on";; --) shift; break;; -*) echo >&2 "Usage: $0 [-v]" @@ -248,7 +253,7 @@ oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.jso # Unit tests and coverage -if [ "$testScheme" = "" ]; then +if [ "$testScheme" = "" ] || [ "$unittests" = "" ]; then echo 'Skipping tests as no test scheme has been provided!' # Put default xml files with no tests and no coverage... @@ -430,8 +435,21 @@ else fi # SonarQube -echo -n 'Running SonarQube using SonarQube Runner' -runCommand /dev/stdout sonar-runner +if [ "$sonarscanner" = "on" ]; then + echo -n 'Running SonarQube using SonarQube Scanner' + if hash /dev/stdout sonar-scanner 2>/dev/null; then + runCommand /dev/stdout sonar-scanner + else + echo 'Skipping sonar-scanner (not installed!)' + fi +else + echo -n 'Running SonarQube using SonarQube Runner' + if hash /dev/stdout sonar-runner 2>/dev/null; then + runCommand /dev/stdout sonar-runner + else + runCommand /dev/stdout sonar-scanner + fi +fi # Kill progress indicator stopProgress From c27b7748630fca02c253690e052de7ab0131e218 Mon Sep 17 00:00:00 2001 From: David Yang Date: Fri, 17 Mar 2017 10:43:02 +0100 Subject: [PATCH 085/107] Update run-sonar.sh --- src/main/shell/run-sonar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index a48c66a5..fe8d2d16 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -254,7 +254,7 @@ oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.jso # Unit tests and coverage if [ "$testScheme" = "" ] || [ "$unittests" = "" ]; then - echo 'Skipping tests as no test scheme has been provided!' + echo 'Skipping tests!' # Put default xml files with no tests and no coverage... echo "" > sonar-reports/TEST-report.xml From 394837391a7fd7add122d271f1b80e3066698fdc Mon Sep 17 00:00:00 2001 From: David Yang Date: Tue, 28 Mar 2017 14:19:52 +0200 Subject: [PATCH 086/107] Replace oclint-xcodebuild by xcpretty oclint-xcodebuild is no longer maintained. Replaced by "xcpretty -r json-compilation-database" http://oclint-docs.readthedocs.io/en/stable/guide/xcpretty.html --- src/main/shell/run-sonar.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index c2df8ed3..da804ce8 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -150,7 +150,6 @@ echo "Running run-sonar.sh..." # xctool, oclint installed testIsInstalled xcodebuild testIsInstalled oclint -testIsInstalled oclint-xcodebuild # sonar-project.properties in current directory if [ ! -f sonar-project.properties ]; then @@ -244,8 +243,8 @@ if [[ ! -z "$destinationSimulator" ]]; then buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) fi runCommand xcodebuild.log "${buildCmd[@]}" -oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.json file - +#oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.json file +cat xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json # Unit tests and coverage if [ "$testScheme" = "" ]; then From e84077e93176fc759a23cc183a42c5617459c9e6 Mon Sep 17 00:00:00 2001 From: David Yang Date: Tue, 28 Mar 2017 14:38:29 +0200 Subject: [PATCH 087/107] fix : XCPRETTY_CMD usage --- src/main/shell/run-sonar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index da804ce8..511a0a3b 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -244,7 +244,7 @@ if [[ ! -z "$destinationSimulator" ]]; then fi runCommand xcodebuild.log "${buildCmd[@]}" #oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.json file -cat xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json +cat xcodebuild.log | XCPRETTY_CMD -r json-compilation-database -o compile_commands.json # Unit tests and coverage if [ "$testScheme" = "" ]; then From bc54b5ada3505d020f3547a47d9ce4dd8574ed14 Mon Sep 17 00:00:00 2001 From: David Yang Date: Tue, 28 Mar 2017 14:56:00 +0200 Subject: [PATCH 088/107] Fix XCPRETTY_CMD usage --- src/main/shell/run-sonar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 511a0a3b..b651afe1 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -244,7 +244,7 @@ if [[ ! -z "$destinationSimulator" ]]; then fi runCommand xcodebuild.log "${buildCmd[@]}" #oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.json file -cat xcodebuild.log | XCPRETTY_CMD -r json-compilation-database -o compile_commands.json +cat xcodebuild.log | $XCPRETTY_CMD -r json-compilation-database -o compile_commands.json # Unit tests and coverage if [ "$testScheme" = "" ]; then From c1c65b2e5a57fbaf222e30d03857c1e756ff69db Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 21 Apr 2017 16:25:27 +0200 Subject: [PATCH 089/107] Prepared README for next release --- README.md | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index a41682b9..f2d86533 100755 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://g Example iOS SonarQube dashboard

-###Features +### Features | Feature | Supported | Details | |---------------|----------|:-----------:| @@ -31,56 +31,59 @@ A SonarQube 5.0 dashboard of the iOS open source project [GreatReader](https://g | Code coverage |YES | With [gcovr](http://gcovr.com) for project before Xcode 7, otherwise [slather](https://github.com/venmo/slather)| -###Compatibility +### Compatibility Releases available from this repository are compliant with SonarQube 4.3.x and above. In order not to break compatibility with the original plugin, plugin releases are numbered with 4 digits. -###Faux Pas support +### Faux Pas support [Faux Pas](http://fauxpasapp.com/) is a wonderful tool to analyse iOS or Mac applications source code, however it is not free. A 30 trial version is available [here](http://fauxpasapp.com/try/). The plugin runs fine even if Faux Pas is not installed (Faux Pas analysis will be skipped). -###Download +### Download Binary packages are available in the release section. -###Release history +### Release history -###0.6.0 +### 0.6.1 + + +### 0.6.0 - SonarQube 6 support. Important : will work with SonarQube 5.x and above only. Will not work anymore with SonarQube 4.5.x anymore. -###0.5.2 +### 0.5.2 - OCLint 0.11.0 support (see https://github.com/Backelite/sonar-objective-c/pull/13) - Removed required gcovr check in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/10) - Fixed issued in run-sonar.sh (see https://github.com/Backelite/sonar-objective-c/pull/2) - Better test file detection pattern (see https://github.com/Backelite/sonar-objective-c/pull/3/files) -####0.5.1 +#### 0.5.1 - Complexity with Lizard ! -####0.5.0 (detached from octo project) +#### 0.5.0 (detached from octo project) - Detached from octo project (to hard to maintain compatibility) - Removed deprecated API usages for Sonarube 5.3 support -####0.4.0.3 (based on 0.4.0) +#### 0.4.0.3 (based on 0.4.0) - Xcode 7 coverage support (profdata) -####0.4.0.2 (based on 0.4.0) +#### 0.4.0.2 (based on 0.4.0) - Faux Pas support for release 1.5 and 1.6 - Support for multiple projects in a same workspace -####0.4.0.1 (based on 0.4.0) +#### 0.4.0.1 (based on 0.4.0) - Faux Pas support - Moved OCLint long line threshold to 250 - Add the `plain` reporter at build step to get more information in case of build failure - Capitalized OCLint rule names -###Prerequisites +### 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 ([HomeBrew](http://brew.sh) installed and ```brew install sonar-runner```) @@ -92,7 +95,7 @@ Binary packages are available in the release section. - [lizard](https://github.com/terryyin/lizard) ([PIP](https://pip.pypa.io/en/stable/installing/) installed and ```sudo pip install lizard```) - [Faux Pas](http://fauxpasapp.com/) command line tools installed (optional) -###Installation of xcpretty with JUnit reports fix +### Installation of xcpretty with JUnit reports fix At the time, xcpretty needs to be fixed to work with SonarQube. @@ -104,7 +107,7 @@ To install the fixed version, follow those steps : gem build xcpretty.gemspec sudo gem install --both xcpretty-0.2.2.gem -###Code coverage data format +### Code coverage data format Since Xcode 7, Apple changed its coverage data format to a new format called 'profdata'. By default this format will be used by the plugin, except if you explicitly force it to legacy mode (for Xcode 6 and below) in your *sonar-project.properties* with this line: @@ -112,31 +115,31 @@ By default this format will be used by the plugin, except if you explicitly forc sonar.objectivec.coverageType=legacy -###Installation (once for all your Objective-C projects) +### Installation (once for all your Objective-C projects) - Download the plugin binary into the $SONARQUBE_HOME/extensions/plugins directory - Copy [run-sonar.sh](https://rawgithub.com/Backelite/sonar-objective-c/master/src/main/shell/run-sonar.sh) somewhere in your PATH - Restart the SonarQube server. -###Configuration (once per project) +### Configuration (once per project) - Copy [sonar-project.properties](https://rawgithub.com/Backelite/sonar-objective-c/master/sample/sonar-project.properties) in your Xcode project root folder (along your .xcodeproj file) - Edit the ```sonar-project.properties``` file to match your Xcode iOS/MacOS project **The good news is that you don't have to modify your Xcode project to enable SonarQube!**. Ok, there might be one needed modification if you don't have a specific scheme for your test target, but that's all. -###Analysis +### Analysis - Run the script ```run-sonar.sh``` in your Xcode project root folder - Enjoy or file an issue! -###Update (once per plugin update) +### Update (once per plugin update) - Install the lastest plugin version - Copy ```run-sonar.sh``` somewhere in your PATH If you still have *run-sonar.sh* file in each of your project (not recommended), you will need to update all those files. -###Contributing +### Contributing Feel free to contribute to this plugin by issuing pull requests to this repository or to the [original one](https://github.com/octo-technology/sonar-objective-c). -###License +### License SonarQube Plugin for Objective-C is released under the [GNU LGPL 3 license](http://www.gnu.org/licenses/lgpl.txt). From 300cf2980af57b517a9d040603761d4aa03641df Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 21 Apr 2017 16:35:08 +0200 Subject: [PATCH 090/107] Updated README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f2d86533..3858d471 100755 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ Binary packages are available in the release section. ### Release history ### 0.6.1 +- Replaced oclint-xcodebuild by xcpretty (see https://github.com/Backelite/sonar-objective-c/pull/25). +- Added -nounittests and -usesonarscanner parameters to run-sonar.sh script (see https://github.com/Backelite/sonar-objective-c/pull/23). ### 0.6.0 From 755a45fca4bb4dc11d361958264e69bfa346b0f9 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 21 Apr 2017 16:41:21 +0200 Subject: [PATCH 091/107] README update --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3858d471..071d9abb 100755 --- a/README.md +++ b/README.md @@ -51,9 +51,9 @@ Binary packages are available in the release section. ### Release history ### 0.6.1 -- Replaced oclint-xcodebuild by xcpretty (see https://github.com/Backelite/sonar-objective-c/pull/25). -- Added -nounittests and -usesonarscanner parameters to run-sonar.sh script (see https://github.com/Backelite/sonar-objective-c/pull/23). - +- Replaced oclint-xcodebuild by xcpretty (see https://github.com/Backelite/sonar-objective-c/pull/25 thanks to [davidy4ng](https://github.com/davidy4ng)). +- Added -nounittests and -usesonarscanner parameters to run-sonar.sh script (see https://github.com/Backelite/sonar-objective-c/pull/23 thanks to [davidy4ng](https://github.com/davidy4ng)). +- Sonar 6 fix for Faux Pas (see https://github.com/Backelite/sonar-objective-c/pull/20 thanks to [macostea](https://github.com/macostea)). ### 0.6.0 - SonarQube 6 support. Important : will work with SonarQube 5.x and above only. Will not work anymore with SonarQube 4.5.x anymore. From d3e52e86671db3af6895961655e7c6e9814cc3a4 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Fri, 21 Apr 2017 17:01:05 +0200 Subject: [PATCH 092/107] Version bump --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2cd33bec..a855afaa 100755 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec backelite-sonar-objective-c-plugin - 0.6.0 + 0.6.1 sonar-plugin From 8efcf45dc5bafc825d24c9776b3a63d114326bfa Mon Sep 17 00:00:00 2001 From: David Yang Date: Wed, 3 May 2017 11:33:26 +0200 Subject: [PATCH 093/107] Update run-sonar.sh : xcodebuild optimization - replace "xcodebuild" occurences by $XCODEBUILD_CMD - usage of "build-for-testing" followed by "test-without-building" instead of "test" (only when Xcode 8+ is available) : solves some Simulator boot error --- src/main/shell/run-sonar.sh | 39 ++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 97f0eac0..49591e69 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -7,7 +7,7 @@ # # Global parameters -XCTOOL_CMD=xcodebuild +XCODEBUILD_CMD=xcodebuild SLATHER_CMD=slather XCPRETTY_CMD=xcpretty LIZARD_CMD=lizard @@ -36,6 +36,15 @@ function testIsInstalled() { fi } +function testIsXcodeMinMajorVersionAvailable() { + XCODE_VERSION="$($XCODEBUILD_CMD -version | grep -a -A 1 "Xcode" | head -n1 | sed "s/Xcode \([0-9]*\)\..*/\1/")" + if (( "$1" <= "$XCODE_VERSION" )); then + return 0 + else + return 1 + fi +} + function readParameter() { variable=$1 @@ -153,7 +162,7 @@ echo "Running run-sonar.sh..." ## CHECK PREREQUISITES # xctool, oclint installed -testIsInstalled xcodebuild +testIsInstalled $XCODEBUILD_CMD testIsInstalled oclint # sonar-project.properties in current directory @@ -243,7 +252,7 @@ if [[ "$workspaceFile" != "" ]] ; then else buildCmdPrefix="-project $projectFile" fi -buildCmd=(xcodebuild clean build $buildCmdPrefix -scheme $appScheme) +buildCmd=($XCODEBUILD_CMD clean build $buildCmdPrefix -scheme $appScheme) if [[ ! -z "$destinationSimulator" ]]; then buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) fi @@ -264,16 +273,32 @@ else if [ "$coverageType" = "profdata" -o "$coverageType" = "" ]; then # profdata - buildCmd=(xcodebuild test $buildCmdPrefix -scheme "$testScheme" -configuration Debug -enableCodeCoverage YES) + buildCmd=($XCODEBUILD_CMD test $buildCmdPrefix -scheme "$testScheme" -configuration Debug -enableCodeCoverage YES) + xcode8BuildForTestingCmd=($XCODEBUILD_CMD build-for-testing $buildCmdPrefix -scheme "$testScheme" -configuration Debug -enableCodeCoverage YES) + xcode8TestCmd=($XCODEBUILD_CMD test-without-building $buildCmdPrefix -scheme "$testScheme" -configuration Debug -enableCodeCoverage YES) else # Legacy coverage - buildCmd=(xcodebuild test $buildCmdPrefix -scheme "$testScheme" -configuration Debug) + buildCmd=($XCODEBUILD_CMD test $buildCmdPrefix -scheme "$testScheme" -configuration Debug) + xcode8BuildForTestingCmd=($XCODEBUILD_CMD build-for-testing $buildCmdPrefix -scheme "$testScheme" -configuration Debug) + xcode8TestCmd=($XCODEBUILD_CMD test-without-building $buildCmdPrefix -scheme "$testScheme" -configuration Debug) fi if [[ ! -z "$destinationSimulator" ]]; then buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) + xcode8BuildForTestingCmd+=(-destination "$destinationSimulator" -destination-timeout 360) + xcode8TestCmd+=(-destination "$destinationSimulator" -destination-timeout 360) + fi + + if testIsXcodeMinMajorVersionAvailable 8 ; then + echo "Running build-for-testing" + "${xcode8BuildForTestingCmd[@]}" | $XCPRETTY_CMD + echo "Running test-without-building" + "${xcode8TestCmd[@]}" | $XCPRETTY_CMD -t --report junit + else + echo "Testing" + "${buildCmd[@]}" | $XCPRETTY_CMD -t --report junit fi - "${buildCmd[@]}" | $XCPRETTY_CMD -t --report junit + mv build/reports/junit.xml sonar-reports/TEST-report.xml echo -n 'Computing coverage report' @@ -389,7 +414,7 @@ if [ "$fauxpas" = "on" ]; then echo $projectFile | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh while read projectName; do - xcodebuild -list -project $projectName | sed -n '/Schemes/,$p' | while read scheme + $XCODEBUILD_CMD -list -project $projectName | sed -n '/Schemes/,$p' | while read scheme do if [ "$scheme" = "" ] From 6d1d56ecbe644429d50962d03879b212d072f2ef Mon Sep 17 00:00:00 2001 From: Thomas Lanquetin Date: Mon, 22 May 2017 16:59:10 +0200 Subject: [PATCH 094/107] Fix for properties with space --- src/main/shell/run-sonar.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 97f0eac0..30c99246 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -238,12 +238,13 @@ mkdir sonar-reports # Extracting project information needed later echo -n 'Extracting Xcode project information' +buildCmd=(xcodebuild clean build) if [[ "$workspaceFile" != "" ]] ; then - buildCmdPrefix="-workspace $workspaceFile" + buildCmd+=(-workspace "$workspaceFile") else - buildCmdPrefix="-project $projectFile" + buildCmd+=(-project "$projectFile") fi -buildCmd=(xcodebuild clean build $buildCmdPrefix -scheme $appScheme) +buildCmd+=( -scheme "$appScheme") if [[ ! -z "$destinationSimulator" ]]; then buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) fi @@ -298,14 +299,13 @@ else fi - projectArray=(${projectFile//,/ }) - firstProject=${projectArray[0]} + firstProject=$(echo $projectFile | sed -n 1'p' | tr ',' '\n' | head -n 1) slatherCmd=($SLATHER_CMD coverage --input-format profdata $excludedCommandLineFlags --cobertura-xml --output-directory sonar-reports) if [[ ! -z "$workspaceFile" ]]; then - slatherCmd+=( --workspace $workspaceFile) + slatherCmd+=( --workspace "$workspaceFile") fi - slatherCmd+=( --scheme "$appScheme" $firstProject) + slatherCmd+=( --scheme "$appScheme" "$firstProject") runCommand /dev/stdout "${slatherCmd[@]}" mv sonar-reports/cobertura.xml sonar-reports/coverage.xml From 4ee2c1028dd2f499f415a910d2ad3d52f273f72a Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 4 Dec 2017 11:28:07 +0100 Subject: [PATCH 095/107] Version bump --- README.md | 4 ++++ pom.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 071d9abb..5a3c06f3 100755 --- a/README.md +++ b/README.md @@ -50,6 +50,10 @@ Binary packages are available in the release section. ### Release history +### 0.6.2 +- Update run-sonar.sh : xcodebuild optimization (see https://github.com/Backelite/sonar-objective-c/pull/26 thanks to [davidy4ng](https://github.com/davidy4ng)). + + ### 0.6.1 - Replaced oclint-xcodebuild by xcpretty (see https://github.com/Backelite/sonar-objective-c/pull/25 thanks to [davidy4ng](https://github.com/davidy4ng)). - Added -nounittests and -usesonarscanner parameters to run-sonar.sh script (see https://github.com/Backelite/sonar-objective-c/pull/23 thanks to [davidy4ng](https://github.com/davidy4ng)). diff --git a/pom.xml b/pom.xml index 2cd33bec..a11a110d 100755 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.codehaus.sonar-plugin.objectivec backelite-sonar-objective-c-plugin - 0.6.0 + 0.6.2 sonar-plugin From 61b23f6b8e0c03a632384aaad1c81004fe12fba4 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Mon, 4 Dec 2017 12:05:45 +0100 Subject: [PATCH 096/107] Xcode 9 fix with OCLint --- README.md | 5 ++-- src/main/shell/run-sonar.sh | 46 ++++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 5a3c06f3..8dc0a1fd 100755 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ Binary packages are available in the release section. ### 0.6.2 - Update run-sonar.sh : xcodebuild optimization (see https://github.com/Backelite/sonar-objective-c/pull/26 thanks to [davidy4ng](https://github.com/davidy4ng)). +- Fix for properties with space (see https://github.com/Backelite/sonar-objective-c/pull/29 thanks to [Branlute](https://github.com/Branlute)) ### 0.6.1 @@ -95,9 +96,9 @@ Binary packages are available in the release section. - [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```) - [xcpretty](https://github.com/supermarin/xcpretty) (see instructions below) - [xctool](https://github.com/facebook/xctool) ([HomeBrew](http://brew.sh) installed and ```brew install xctool```). If you are using Xcode 6, make sure to update xctool (```brew upgrade xctool```) to a version > 0.2.2. -- [OCLint](http://oclint-docs.readthedocs.io/en/stable/) installed. Version 0.11.0 recommended. +- [OCLint](http://oclint-docs.readthedocs.io/en/stable/) installed. Version 0.11.0 recommended (0.13.0 since Xcode 9). - [gcovr](http://gcovr.com) installed for legacy (pre Xcode 7 coverage) -- [slather](https://github.com/SlatherOrg/slather) (```gem install slather```). Version 2.1.0 or above. +- [slather](https://github.com/SlatherOrg/slather) (```gem install slather```). Version 2.1.0 or above (2.4.4 since Xcode 9). - [lizard](https://github.com/terryyin/lizard) ([PIP](https://pip.pypa.io/en/stable/installing/) installed and ```sudo pip install lizard```) - [Faux Pas](http://fauxpasapp.com/) command line tools installed (optional) diff --git a/src/main/shell/run-sonar.sh b/src/main/shell/run-sonar.sh index 30c99246..af447134 100755 --- a/src/main/shell/run-sonar.sh +++ b/src/main/shell/run-sonar.sh @@ -7,7 +7,7 @@ # # Global parameters -XCTOOL_CMD=xcodebuild +XCODEBUILD_CMD=xcodebuild SLATHER_CMD=slather XCPRETTY_CMD=xcpretty LIZARD_CMD=lizard @@ -36,6 +36,15 @@ function testIsInstalled() { fi } +function testIsXcodeMinMajorVersionAvailable() { + XCODE_VERSION="$($XCODEBUILD_CMD -version | grep -a -A 1 "Xcode" | head -n1 | sed "s/Xcode \([0-9]*\)\..*/\1/")" + if (( "$1" <= "$XCODE_VERSION" )); then + return 0 + else + return 1 + fi +} + function readParameter() { variable=$1 @@ -153,7 +162,7 @@ echo "Running run-sonar.sh..." ## CHECK PREREQUISITES # xctool, oclint installed -testIsInstalled xcodebuild +testIsInstalled $XCODEBUILD_CMD testIsInstalled oclint # sonar-project.properties in current directory @@ -238,15 +247,14 @@ mkdir sonar-reports # Extracting project information needed later echo -n 'Extracting Xcode project information' -buildCmd=(xcodebuild clean build) if [[ "$workspaceFile" != "" ]] ; then - buildCmd+=(-workspace "$workspaceFile") + buildCmdPrefix="-workspace $workspaceFile" else - buildCmd+=(-project "$projectFile") + buildCmdPrefix="-project $projectFile" fi -buildCmd+=( -scheme "$appScheme") +buildCmd=(xcodebuild clean build $buildCmdPrefix -scheme $appScheme) if [[ ! -z "$destinationSimulator" ]]; then - buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) + buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360 COMPILER_INDEX_STORE_ENABLE=NO) fi runCommand xcodebuild.log "${buildCmd[@]}" #oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.json file @@ -265,16 +273,32 @@ else if [ "$coverageType" = "profdata" -o "$coverageType" = "" ]; then # profdata - buildCmd=(xcodebuild test $buildCmdPrefix -scheme "$testScheme" -configuration Debug -enableCodeCoverage YES) + buildCmd=($XCODEBUILD_CMD test $buildCmdPrefix -scheme "$testScheme" -configuration Debug -enableCodeCoverage YES) + xcode8BuildForTestingCmd=($XCODEBUILD_CMD build-for-testing $buildCmdPrefix -scheme "$testScheme" -configuration Debug -enableCodeCoverage YES) + xcode8TestCmd=($XCODEBUILD_CMD test-without-building $buildCmdPrefix -scheme "$testScheme" -configuration Debug -enableCodeCoverage YES) else # Legacy coverage - buildCmd=(xcodebuild test $buildCmdPrefix -scheme "$testScheme" -configuration Debug) + buildCmd=($XCODEBUILD_CMD test $buildCmdPrefix -scheme "$testScheme" -configuration Debug) + xcode8BuildForTestingCmd=($XCODEBUILD_CMD build-for-testing $buildCmdPrefix -scheme "$testScheme" -configuration Debug) + xcode8TestCmd=($XCODEBUILD_CMD test-without-building $buildCmdPrefix -scheme "$testScheme" -configuration Debug) fi if [[ ! -z "$destinationSimulator" ]]; then buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) + xcode8BuildForTestingCmd+=(-destination "$destinationSimulator" -destination-timeout 360) + xcode8TestCmd+=(-destination "$destinationSimulator" -destination-timeout 360) fi - "${buildCmd[@]}" | $XCPRETTY_CMD -t --report junit + + if testIsXcodeMinMajorVersionAvailable 8 ; then + echo "Running build-for-testing" + "${xcode8BuildForTestingCmd[@]}" | $XCPRETTY_CMD + echo "Running test-without-building" + "${xcode8TestCmd[@]}" | $XCPRETTY_CMD -t --report junit + else + echo "Testing" + "${buildCmd[@]}" | $XCPRETTY_CMD -t --report junit + fi + mv build/reports/junit.xml sonar-reports/TEST-report.xml echo -n 'Computing coverage report' @@ -389,7 +413,7 @@ if [ "$fauxpas" = "on" ]; then echo $projectFile | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh while read projectName; do - xcodebuild -list -project $projectName | sed -n '/Schemes/,$p' | while read scheme + $XCODEBUILD_CMD -list -project $projectName | sed -n '/Schemes/,$p' | while read scheme do if [ "$scheme" = "" ] From 38a74c58db9f5cfd17a1cf06c809b50b67716c73 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 5 Dec 2017 17:32:52 +0100 Subject: [PATCH 097/107] SonarQube 6.5 support --- README.md | 1 + build-and-deploy.sh | 2 +- pom.xml | 253 ++++++++++++++---- sample/sonar-project.properties | 6 +- sonar-objective-c-plugin/pom.xml | 39 +++ .../objectivec/ObjectiveCAstScanner.java | 32 ++- .../objectivec/ObjectiveCConfiguration.java | 28 +- .../objectivec/api/ObjectiveCGrammar.java | 26 +- .../objectivec/api/ObjectiveCKeyword.java | 26 +- .../objectivec/api/ObjectiveCMetric.java | 26 +- .../objectivec/api/ObjectiveCPunctuator.java | 26 +- .../objectivec/api/ObjectiveCTokenType.java | 26 +- .../sonar/objectivec/checks/CheckList.java | 26 +- .../objectivec/lexer/ObjectiveCLexer.java | 28 +- .../parser/ObjectiveCGrammarImpl.java | 34 +++ .../objectivec/parser/ObjectiveCParser.java | 41 +++ .../plugins/objectivec/ObjectiveCPlugin.java | 44 +-- .../objectivec/ObjectiveCSquidSensor.java | 34 ++- .../colorizer/ObjectiveCColorizerFormat.java | 38 ++- .../complexity/LizardMeasurePersistor.java | 24 +- .../complexity/LizardReportParser.java | 24 +- .../objectivec/complexity/LizardSensor.java | 27 +- .../plugins/objectivec/core/ObjectiveC.java | 24 +- .../coverage/CoberturaReportParser.java | 24 +- .../objectivec/coverage/CoberturaSensor.java | 27 +- .../coverage/ReportFilesFinder.java | 24 +- .../objectivec/cpd/ObjectiveCCpdMapping.java | 25 +- .../objectivec/cpd/ObjectiveCTokenizer.java | 28 +- .../objectivec/surefire/SurefireParser.java | 208 ++++++++++++++ .../objectivec/surefire}/SurefireSensor.java | 26 +- .../surefire/data/SurefireStaxHandler.java | 139 ++++++++++ .../surefire/data/UnitTestClassReport.java | 100 +++++++ .../surefire/data/UnitTestIndex.java | 78 ++++++ .../surefire/data/UnitTestResult.java | 84 ++++++ .../violations/ObjectiveCProfile.java | 26 +- .../violations/fauxpas/FauxPasProfile.java | 24 +- .../fauxpas/FauxPasProfileImporter.java | 24 +- .../fauxpas/FauxPasReportParser.java | 24 +- .../fauxpas/FauxPasRulesDefinition.java | 29 +- .../violations/fauxpas/FauxPasSensor.java | 27 +- .../violations/oclint/OCLintParser.java | 27 +- .../violations/oclint/OCLintProfile.java | 24 +- .../oclint/OCLintProfileImporter.java | 24 +- .../violations/oclint/OCLintRuleSeverity.java | 25 +- .../oclint/OCLintRulesDefinition.java | 25 +- .../violations/oclint/OCLintSensor.java | 26 +- .../oclint/OCLintXMLStreamHandler.java | 24 +- .../com/sonar/sqale/fauxpas-model.xml | 0 .../com/sonar/sqale/oclint-model.xml | 0 .../sonar/plugins/fauxpas/profile-fauxpas.xml | 0 .../org/sonar/plugins/fauxpas/rules.json | 0 .../sonar/plugins/oclint/profile-oclint.xml | 0 .../org/sonar/plugins/oclint/rules.txt | 0 .../src}/main/shell/run-sonar.sh | 26 +- .../objectivec/ObjectiveCAstScannerTest.java | 35 ++- .../api/ObjectiveCPunctuatorTest.java | 34 +++ .../objectivec/lexer/ObjectiveCLexerTest.java | 84 ++++++ .../complexity/LizardReportParserTest.java | 58 ++-- .../src/test/resources/Profile.m | 39 +++ .../src/test/resources/objcSample.h | 17 ++ .../parser/ObjectiveCGrammarImpl.java | 36 --- .../objectivec/parser/ObjectiveCParser.java | 44 --- .../objectivec/tests/SurefireParser.java | 208 -------------- .../api/ObjectiveCPunctuatorTest.java | 34 --- .../objectivec/lexer/ObjectiveCLexerTest.java | 83 ------ .../complexity/LizardSensorTest.java | 84 ------ .../violations/fauxpas/FauxPasSensorTest.java | 80 ------ .../violations/oclint/OCLintSensorTest.java | 78 ------ updateFauxPasRules.groovy | 23 +- updateOCLintRules.groovy | 23 +- 70 files changed, 1652 insertions(+), 1261 deletions(-) create mode 100644 sonar-objective-c-plugin/pom.xml rename {src/main/java/org => sonar-objective-c-plugin/src/main/java/com}/sonar/objectivec/ObjectiveCAstScanner.java (83%) rename {src/main/java/org => sonar-objective-c-plugin/src/main/java/com}/sonar/objectivec/ObjectiveCConfiguration.java (54%) rename {src/main/java/org => sonar-objective-c-plugin/src/main/java/com}/sonar/objectivec/api/ObjectiveCGrammar.java (55%) rename {src/main/java/org => sonar-objective-c-plugin/src/main/java/com}/sonar/objectivec/api/ObjectiveCKeyword.java (86%) rename {src/main/java/org => sonar-objective-c-plugin/src/main/java/com}/sonar/objectivec/api/ObjectiveCMetric.java (60%) rename {src/main/java/org => sonar-objective-c-plugin/src/main/java/com}/sonar/objectivec/api/ObjectiveCPunctuator.java (74%) rename {src/main/java/org => sonar-objective-c-plugin/src/main/java/com}/sonar/objectivec/api/ObjectiveCTokenType.java (51%) rename {src/main/java/org => sonar-objective-c-plugin/src/main/java/com}/sonar/objectivec/checks/CheckList.java (52%) rename {src/main/java/org => sonar-objective-c-plugin/src/main/java/com}/sonar/objectivec/lexer/ObjectiveCLexer.java (66%) create mode 100644 sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/parser/ObjectiveCGrammarImpl.java create mode 100644 sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/parser/ObjectiveCParser.java rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java (77%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java (88%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java (54%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java (82%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java (94%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java (84%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java (73%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java (90%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java (77%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java (75%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java (62%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java (68%) create mode 100644 sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/SurefireParser.java rename {src/main/java/org/sonar/plugins/objectivec/tests => sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire}/SurefireSensor.java (85%) create mode 100644 sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/SurefireStaxHandler.java create mode 100644 sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestClassReport.java create mode 100644 sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestIndex.java create mode 100644 sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestResult.java rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java (82%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java (75%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java (74%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java (86%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java (79%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java (80%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java (75%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java (75%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java (74%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java (54%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java (89%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java (81%) rename {src => sonar-objective-c-plugin/src}/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java (86%) rename {src => sonar-objective-c-plugin/src}/main/resources/com/sonar/sqale/fauxpas-model.xml (100%) rename {src => sonar-objective-c-plugin/src}/main/resources/com/sonar/sqale/oclint-model.xml (100%) rename {src => sonar-objective-c-plugin/src}/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml (100%) rename {src => sonar-objective-c-plugin/src}/main/resources/org/sonar/plugins/fauxpas/rules.json (100%) rename {src => sonar-objective-c-plugin/src}/main/resources/org/sonar/plugins/oclint/profile-oclint.xml (100%) rename {src => sonar-objective-c-plugin/src}/main/resources/org/sonar/plugins/oclint/rules.txt (100%) rename {src => sonar-objective-c-plugin/src}/main/shell/run-sonar.sh (93%) rename {src/test/java/org => sonar-objective-c-plugin/src/test/java/com}/sonar/objectivec/ObjectiveCAstScannerTest.java (52%) create mode 100644 sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/api/ObjectiveCPunctuatorTest.java create mode 100644 sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/lexer/ObjectiveCLexerTest.java rename {src => sonar-objective-c-plugin/src}/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java (75%) create mode 100644 sonar-objective-c-plugin/src/test/resources/Profile.m create mode 100644 sonar-objective-c-plugin/src/test/resources/objcSample.h delete mode 100644 src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java delete mode 100644 src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java delete mode 100644 src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java delete mode 100644 src/test/java/org/sonar/objectivec/api/ObjectiveCPunctuatorTest.java delete mode 100644 src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java delete mode 100644 src/test/java/org/sonar/plugins/objectivec/complexity/LizardSensorTest.java delete mode 100644 src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java delete mode 100644 src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java diff --git a/README.md b/README.md index 8dc0a1fd..4fee0b77 100755 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Binary packages are available in the release section. ### Release history ### 0.6.2 +- SonarQube 6.5 support - Update run-sonar.sh : xcodebuild optimization (see https://github.com/Backelite/sonar-objective-c/pull/26 thanks to [davidy4ng](https://github.com/davidy4ng)). - Fix for properties with space (see https://github.com/Backelite/sonar-objective-c/pull/29 thanks to [Branlute](https://github.com/Branlute)) diff --git a/build-and-deploy.sh b/build-and-deploy.sh index bb1ebcd3..687a5a60 100755 --- a/build-and-deploy.sh +++ b/build-and-deploy.sh @@ -10,7 +10,7 @@ fi # Deploy new verion of plugin in Sonar dir rm -rf $SONARQUBE_HOME/extensions/plugins/*sonar-objective-c-* -cp target/*.jar $SONARQUBE_HOME/extensions/plugins +cp sonar-objective-c-plugin/target/*.jar $SONARQUBE_HOME/extensions/plugins rm $SONARQUBE_HOME/extensions/plugins/*sources.jar # Stop/start Sonar diff --git a/pom.xml b/pom.xml index a11a110d..dc76dd4b 100755 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,30 @@ + + 4.0.0 + + sonar-objective-c-plugin + @@ -16,17 +40,11 @@ - - org.codehaus.sonar-plugins - parent - 18 - - - org.codehaus.sonar-plugin.objectivec - backelite-sonar-objective-c-plugin + com.backelite.sonarqube + backelite-objective-c 0.6.2 - sonar-plugin + pom Objective-C Sonar Plugin Enables analysis of Objective-C projects into SonarQube. @@ -91,65 +109,99 @@ OCTO Technology, Backelite + contact@backelite.com Sonar Objective-C Plugin true - 5.0 - 1.20 + 5.6 - - org.sonar.plugins.objectivec.ObjectiveCPlugin - ObjectiveC + 3.5.2 + 2.5 + 2.6 + 17.0 + 4.10 + 1.0.13 + 1.9.0 + 1.7.21 + 5.6 + 3.13 + 2.5.0.36 + 1.22 + 2.6.1 + 2.6.2 + 1.6 + + - org.codehaus.sonar - sonar-plugin-api - ${sonar.version} - - - org.codehaus.sonar - sonar-testing-harness - ${sonar.version} + org.sonarsource.sslr + sslr-core + ${sslr.version} - org.codehaus.sonar - sonar-deprecated - ${sonar.version} + org.sonarsource.sslr-squid-bridge + sslr-squid-bridge + ${sslr-squid-bridge.version} + + + org.codehaus.sonar.sslr + sslr-core + + + org.codehaus.sonar.sslr + sslr-xpath + + + org.codehaus.sonar + sonar-plugin-api + + + org.picocontainer + picocontainer + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + - - org.codehaus.sonar.sslr - sslr-core - ${sslr.version} + commons-lang + commons-lang + ${commons-lang.version} - org.codehaus.sonar.sslr - sslr-xpath - ${sslr.version} + commons-io + commons-io + ${commons-io.version} - org.codehaus.sonar.sslr - sslr-toolkit - ${sslr.version} + ch.qos.logback + logback-classic + ${logback.version} - org.codehaus.sonar.sslr - sslr-testing-harness - ${sslr.version} + com.google.guava + guava + ${guava.version} - org.codehaus.sonar.sslr-squid-bridge - sslr-squid-bridge - 2.5.3 + com.google.code.gson + gson + ${gson.version} ant ant - 1.6 + ${ant.version} @@ -158,37 +210,126 @@ 1.1.1 + junit junit - 4.10 + ${junit.version} + test org.mockito mockito-all - 1.9.0 + ${mockito.version} + test - org.hamcrest - hamcrest-all - 1.1 + org.assertj + assertj-core + ${assertj.version} + test - org.easytesting - fest-assert - 1.4 + org.sonarsource.sonarqube + sonar-testing-harness + ${sonar.version} + test - ch.qos.logback - logback-classic - 0.9.30 + org.sonarsource.sslr + sslr-testing-harness + ${sslr.version} + test - org.codehaus.sonar.plugins - sonar-surefire-plugin - 2.7 + org.sonarsource.sonarlint.core + sonarlint-core + ${sonarlint.version} + test + + + org.sonarsource.orchestrator + sonar-orchestrator + ${sonar-orchestrator.version} + test + + + com.oracle + ojdbc6 + + + + + + + org.sonarsource.sonarqube + sonar-plugin-api + ${sonar.version} + provided + + + org.slf4j + slf4j-api + ${slf4j.version} + provided + + + + + org.sonarsource.sonar-packaging-maven-plugin + sonar-packaging-maven-plugin + 1.16 + true + + org.sonar.plugins.objectivec.ObjectiveCPlugin + ObjectiveC (Backelite) + + + + com.mycila + license-maven-plugin + 3.0 + +
com/mycila/maven/plugin/license/templates/LGPL-3.txt
+ + ${license.owner} + ${license.email} + ${license.title} + + + **/README + **/*/sh + src/test/resources/** + src/test/shell/** + src/main/resources/** + *.groovy + *.sh + **/*.properties + **/pom.xml + +
+ + + + check + + + +
+ + org.apache.maven.plugins + maven-compiler-plugin + 3.6.2 + + 1.8 + 1.8 + + +
+
+
diff --git a/sample/sonar-project.properties b/sample/sonar-project.properties index b72ae5a0..8e16e64a 100644 --- a/sample/sonar-project.properties +++ b/sample/sonar-project.properties @@ -20,7 +20,7 @@ sonar.tests=testSrcDir # If not set : defaults to profdata #sonar.objectivec.coverageType=profdata -# Destination Simulator to run tests +# Destination Simulator to run surefire # As string expected in destination argument of xcodebuild command # Example = sonar.swift.simulator=platform=iOS Simulator,name=iPhone 6,OS=9.2 sonar.objectivec.simulator=platform=iOS Simulator,name=iPhone 6,OS=9.2 @@ -35,7 +35,7 @@ sonar.objectivec.project=myApplication.xcodeproj # Scheme to build your application sonar.objectivec.appScheme=myApplication -# Scheme to build and run your tests (comment following line of you don't have any tests) +# Scheme to build and run your surefire (comment following line of you don't have any surefire) sonar.objectivec.testScheme=myApplicationTests ########################## @@ -62,7 +62,7 @@ sonar.sourceEncoding=UTF-8 # Change it only if you generate the file on your own # sonar.objectivec.fauxpas.report=sonar-reports/fauxpas.json -# Paths to exclude from coverage report (tests, 3rd party libraries etc.) +# Paths to exclude from coverage report (surefire, 3rd party libraries etc.) # sonar.objectivec.excludedPathsFromCoverage=pattern1,pattern2 sonar.objectivec.excludedPathsFromCoverage=.*Tests.*,.*Specs.* diff --git a/sonar-objective-c-plugin/pom.xml b/sonar-objective-c-plugin/pom.xml new file mode 100644 index 00000000..be48d0aa --- /dev/null +++ b/sonar-objective-c-plugin/pom.xml @@ -0,0 +1,39 @@ + + + + + com.backelite.sonarqube + backelite-objective-c + 0.6.2 + + 4.0.0 + + com.backelite.sonarqube + backelite-sonar-objective-c-plugin + 0.6.2 + + sonar-plugin + + + \ No newline at end of file diff --git a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/ObjectiveCAstScanner.java similarity index 83% rename from src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java rename to sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/ObjectiveCAstScanner.java index b3f5f576..9ae727ba 100644 --- a/src/main/java/org/sonar/objectivec/ObjectiveCAstScanner.java +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/ObjectiveCAstScanner.java @@ -1,30 +1,28 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.objectivec; +package com.sonar.objectivec; import java.io.File; import java.util.Collection; -import org.sonar.objectivec.api.ObjectiveCGrammar; -import org.sonar.objectivec.api.ObjectiveCMetric; -import org.sonar.objectivec.parser.ObjectiveCParser; +import com.sonar.objectivec.api.ObjectiveCGrammar; +import com.sonar.objectivec.api.ObjectiveCMetric; +import com.sonar.objectivec.parser.ObjectiveCParser; import org.sonar.squidbridge.AstScanner; import org.sonar.squidbridge.CommentAnalyser; import org.sonar.squidbridge.SquidAstVisitor; diff --git a/src/main/java/org/sonar/objectivec/ObjectiveCConfiguration.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/ObjectiveCConfiguration.java similarity index 54% rename from src/main/java/org/sonar/objectivec/ObjectiveCConfiguration.java rename to sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/ObjectiveCConfiguration.java index 7832d7a0..d525f0b2 100644 --- a/src/main/java/org/sonar/objectivec/ObjectiveCConfiguration.java +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/ObjectiveCConfiguration.java @@ -1,27 +1,25 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.objectivec; +package com.sonar.objectivec; import java.nio.charset.Charset; -import org.sonar.squid.api.SquidConfiguration; +import org.sonar.squidbridge.api.SquidConfiguration; public class ObjectiveCConfiguration extends SquidConfiguration { diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCGrammar.java similarity index 55% rename from src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java rename to sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCGrammar.java index 633fc744..95429be5 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCGrammar.java +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCGrammar.java @@ -1,23 +1,21 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.objectivec.api; +package com.sonar.objectivec.api; import com.sonar.sslr.api.Grammar; import com.sonar.sslr.api.Rule; diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCKeyword.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCKeyword.java similarity index 86% rename from src/main/java/org/sonar/objectivec/api/ObjectiveCKeyword.java rename to sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCKeyword.java index 68c436bd..f86138d5 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCKeyword.java +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCKeyword.java @@ -1,23 +1,21 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.objectivec.api; +package com.sonar.objectivec.api; import com.sonar.sslr.api.AstNode; import com.sonar.sslr.api.TokenType; diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCMetric.java similarity index 60% rename from src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java rename to sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCMetric.java index d42074e9..20ddc812 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCMetric.java +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCMetric.java @@ -1,23 +1,21 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.objectivec.api; +package com.sonar.objectivec.api; import org.sonar.squidbridge.measures.CalculatedMetricFormula; import org.sonar.squidbridge.measures.MetricDef; diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCPunctuator.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCPunctuator.java similarity index 74% rename from src/main/java/org/sonar/objectivec/api/ObjectiveCPunctuator.java rename to sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCPunctuator.java index c00d2418..2e7c7597 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCPunctuator.java +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCPunctuator.java @@ -1,23 +1,21 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.objectivec.api; +package com.sonar.objectivec.api; import com.sonar.sslr.api.AstNode; import com.sonar.sslr.api.TokenType; diff --git a/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCTokenType.java similarity index 51% rename from src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java rename to sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCTokenType.java index 96010a48..7cd60730 100644 --- a/src/main/java/org/sonar/objectivec/api/ObjectiveCTokenType.java +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/api/ObjectiveCTokenType.java @@ -1,23 +1,21 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.objectivec.api; +package com.sonar.objectivec.api; import com.sonar.sslr.api.AstNode; import com.sonar.sslr.api.TokenType; diff --git a/src/main/java/org/sonar/objectivec/checks/CheckList.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/checks/CheckList.java similarity index 52% rename from src/main/java/org/sonar/objectivec/checks/CheckList.java rename to sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/checks/CheckList.java index dd898a09..eb5a51f0 100644 --- a/src/main/java/org/sonar/objectivec/checks/CheckList.java +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/checks/CheckList.java @@ -1,23 +1,21 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.objectivec.checks; +package com.sonar.objectivec.checks; import java.util.List; diff --git a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/lexer/ObjectiveCLexer.java similarity index 66% rename from src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java rename to sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/lexer/ObjectiveCLexer.java index dd5d41dc..b90b1aa6 100644 --- a/src/main/java/org/sonar/objectivec/lexer/ObjectiveCLexer.java +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/lexer/ObjectiveCLexer.java @@ -1,29 +1,27 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.objectivec.lexer; +package com.sonar.objectivec.lexer; import static com.sonar.sslr.api.GenericTokenType.LITERAL; import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.commentRegexp; import static com.sonar.sslr.impl.channel.RegexpChannelBuilder.regexp; -import org.sonar.objectivec.ObjectiveCConfiguration; +import com.sonar.objectivec.ObjectiveCConfiguration; import com.sonar.sslr.impl.Lexer; import com.sonar.sslr.impl.channel.BlackHoleChannel; diff --git a/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/parser/ObjectiveCGrammarImpl.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/parser/ObjectiveCGrammarImpl.java new file mode 100644 index 00000000..bf89b323 --- /dev/null +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/parser/ObjectiveCGrammarImpl.java @@ -0,0 +1,34 @@ +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package com.sonar.objectivec.parser; + +import static com.sonar.sslr.api.GenericTokenType.EOF; +import static com.sonar.sslr.api.GenericTokenType.LITERAL; +import static com.sonar.sslr.impl.matcher.GrammarFunctions.Standard.o2n; + +import com.sonar.objectivec.api.ObjectiveCGrammar; + +public class ObjectiveCGrammarImpl extends ObjectiveCGrammar { + + public ObjectiveCGrammarImpl() { + + program.is(o2n(LITERAL), EOF); + + } + +} diff --git a/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/parser/ObjectiveCParser.java b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/parser/ObjectiveCParser.java new file mode 100644 index 00000000..880c2c90 --- /dev/null +++ b/sonar-objective-c-plugin/src/main/java/com/sonar/objectivec/parser/ObjectiveCParser.java @@ -0,0 +1,41 @@ +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package com.sonar.objectivec.parser; + +import com.sonar.objectivec.ObjectiveCConfiguration; +import com.sonar.objectivec.api.ObjectiveCGrammar; +import com.sonar.objectivec.lexer.ObjectiveCLexer; + +import com.sonar.sslr.impl.Parser; + +public class ObjectiveCParser { + + private ObjectiveCParser() { + } + + public static Parser create() { + return create(new ObjectiveCConfiguration()); + } + + public static Parser create(ObjectiveCConfiguration conf) { + return Parser.builder((ObjectiveCGrammar) new ObjectiveCGrammarImpl()) + .withLexer(ObjectiveCLexer.create(conf)) + .build(); + } + +} diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java similarity index 77% rename from src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java index f7aa85c1..a8fc9ac6 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/ObjectiveCPlugin.java @@ -1,31 +1,36 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec; import java.util.List; -import org.sonar.api.Extension; import org.sonar.api.Properties; import org.sonar.api.Property; import org.sonar.api.SonarPlugin; import org.sonar.plugins.objectivec.complexity.LizardSensor; +import org.sonar.plugins.objectivec.surefire.SurefireSensor; +import org.sonar.plugins.objectivec.violations.ObjectiveCProfile; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfile; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasRulesDefinition; +import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasSensor; +import org.sonar.plugins.objectivec.violations.oclint.OCLintProfile; +import org.sonar.plugins.objectivec.violations.oclint.OCLintRulesDefinition; +import org.sonar.plugins.objectivec.violations.oclint.OCLintSensor; import org.sonar.plugins.objectivec.coverage.CoberturaSensor; import org.sonar.plugins.objectivec.colorizer.ObjectiveCColorizerFormat; import org.sonar.plugins.objectivec.core.ObjectiveC; @@ -33,13 +38,8 @@ import com.google.common.collect.ImmutableList; -import org.sonar.plugins.objectivec.tests.SurefireSensor; -import org.sonar.plugins.objectivec.violations.ObjectiveCProfile; -import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfile; import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfileImporter; -import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasRulesDefinition; -import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasSensor; -import org.sonar.plugins.objectivec.violations.oclint.*; +import org.sonar.plugins.objectivec.violations.oclint.OCLintProfileImporter; @Properties({ @Property(key = CoberturaSensor.REPORT_PATTERN_KEY, defaultValue = CoberturaSensor.DEFAULT_REPORT_PATTERN, name = "Path to unit test coverage report(s)", description = "Relative to projects' root. Ant patterns are accepted", global = false, project = true), @@ -49,9 +49,9 @@ }) public class ObjectiveCPlugin extends SonarPlugin { - public List> getExtensions() { + public List getExtensions() { return ImmutableList.of(ObjectiveC.class, - ObjectiveCColorizerFormat.class, + ObjectiveCCpdMapping.class, ObjectiveCSquidSensor.class, diff --git a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java similarity index 88% rename from src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java index 5e7cf5cc..750dcd19 100644 --- a/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/ObjectiveCSquidSensor.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec; @@ -36,11 +34,11 @@ import org.sonar.api.resources.Resource; import org.sonar.api.rule.RuleKey; import org.sonar.api.scan.filesystem.PathResolver; -import org.sonar.objectivec.ObjectiveCAstScanner; -import org.sonar.objectivec.ObjectiveCConfiguration; -import org.sonar.objectivec.api.ObjectiveCGrammar; -import org.sonar.objectivec.api.ObjectiveCMetric; -import org.sonar.objectivec.checks.CheckList; +import com.sonar.objectivec.ObjectiveCAstScanner; +import com.sonar.objectivec.ObjectiveCConfiguration; +import com.sonar.objectivec.api.ObjectiveCGrammar; +import com.sonar.objectivec.api.ObjectiveCMetric; +import com.sonar.objectivec.checks.CheckList; import org.sonar.plugins.objectivec.core.ObjectiveC; import org.sonar.squidbridge.AstScanner; import org.sonar.squidbridge.SquidAstVisitor; diff --git a/src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java similarity index 54% rename from src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java index 9f5e2d14..69faded3 100644 --- a/src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/colorizer/ObjectiveCColorizerFormat.java @@ -1,36 +1,29 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.colorizer; +import java.util.ArrayList; import java.util.List; import org.sonar.api.web.CodeColorizerFormat; -import org.sonar.colorizer.CDocTokenizer; -import org.sonar.colorizer.CppDocTokenizer; -import org.sonar.colorizer.JavadocTokenizer; -import org.sonar.colorizer.KeywordsTokenizer; -import org.sonar.colorizer.StringTokenizer; import org.sonar.colorizer.Tokenizer; -import org.sonar.objectivec.api.ObjectiveCKeyword; +import com.sonar.objectivec.api.ObjectiveCKeyword; import org.sonar.plugins.objectivec.core.ObjectiveC; - import com.google.common.collect.ImmutableList; public class ObjectiveCColorizerFormat extends CodeColorizerFormat { @@ -41,12 +34,13 @@ public ObjectiveCColorizerFormat() { @Override public List getTokenizers() { - return ImmutableList.of( + /*return ImmutableList.of( new StringTokenizer("", ""), new CDocTokenizer("", ""), new JavadocTokenizer("", ""), new CppDocTokenizer("", ""), - new KeywordsTokenizer("", "", ObjectiveCKeyword.keywordValues())); + new KeywordsTokenizer("", "", ObjectiveCKeyword.keywordValues()));*/ + return new ArrayList(); } } diff --git a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java similarity index 82% rename from src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java index 9dc277bf..692d4d1c 100644 --- a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardMeasurePersistor.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.complexity; diff --git a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java similarity index 94% rename from src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java index fbf74b8b..efc35d1d 100644 --- a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.complexity; diff --git a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java similarity index 84% rename from src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java index 302a3cae..ea1c6f4b 100644 --- a/src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardSensor.java @@ -1,23 +1,20 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ - package org.sonar.plugins.objectivec.complexity; import org.slf4j.Logger; @@ -62,7 +59,6 @@ public LizardSensor(final FileSystem moduleFileSystem, final Settings config) { * @param project * @return true if the project is root the root project and uses Objective-C */ - @Override public boolean shouldExecuteOnProject(Project project) { return project.isRoot() && fileSystem.languages().contains(ObjectiveC.KEY); } @@ -72,7 +68,6 @@ public boolean shouldExecuteOnProject(Project project) { * @param project * @param sensorContext */ - @Override public void analyse(Project project, SensorContext sensorContext) { final String projectBaseDir = fileSystem.baseDir().getPath(); Map> measures = parseReportsIn(projectBaseDir, new LizardReportParser()); diff --git a/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java similarity index 73% rename from src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java index 0eca295f..d5f901b7 100644 --- a/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/core/ObjectiveC.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.core; diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java similarity index 90% rename from src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java index 7de3798d..e9e02eb1 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaReportParser.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.coverage; diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java similarity index 77% rename from src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java index 07d9bc48..6122a318 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/coverage/CoberturaSensor.java @@ -1,27 +1,23 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.coverage; import java.io.File; -import java.util.HashMap; -import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +25,6 @@ import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.config.Settings; -import org.sonar.api.measures.CoverageMeasuresBuilder; import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.plugins.objectivec.ObjectiveCPlugin; diff --git a/src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java similarity index 75% rename from src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java index 9527cf04..8a509bd5 100644 --- a/src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/coverage/ReportFilesFinder.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.coverage; diff --git a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java similarity index 62% rename from src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java index 69202e65..04927ee0 100644 --- a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCCpdMapping.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.cpd; @@ -26,7 +24,6 @@ import org.sonar.api.batch.AbstractCpdMapping; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.resources.Language; -import org.sonar.api.resources.ProjectFileSystem; import org.sonar.plugins.objectivec.core.ObjectiveC; public class ObjectiveCCpdMapping extends AbstractCpdMapping { diff --git a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java similarity index 68% rename from src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java index 8c884bb9..ef19ec60 100644 --- a/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/cpd/ObjectiveCTokenizer.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.cpd; @@ -29,8 +27,8 @@ import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.Tokens; -import org.sonar.objectivec.ObjectiveCConfiguration; -import org.sonar.objectivec.lexer.ObjectiveCLexer; +import com.sonar.objectivec.ObjectiveCConfiguration; +import com.sonar.objectivec.lexer.ObjectiveCLexer; import com.sonar.sslr.api.Token; import com.sonar.sslr.impl.Lexer; diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/SurefireParser.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/SurefireParser.java new file mode 100644 index 00000000..a31508e6 --- /dev/null +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/SurefireParser.java @@ -0,0 +1,208 @@ +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package org.sonar.plugins.objectivec.surefire; + +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.measures.CoreMetrics; +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.test.MutableTestPlan; +import org.sonar.api.test.TestCase; +import org.sonar.api.utils.ParsingUtils; +import org.sonar.api.utils.StaxParser; +import org.sonar.api.utils.XmlParserException; +import org.sonar.plugins.objectivec.surefire.data.SurefireStaxHandler; +import org.sonar.plugins.objectivec.surefire.data.UnitTestClassReport; +import org.sonar.plugins.objectivec.surefire.data.UnitTestIndex; +import org.sonar.plugins.objectivec.surefire.data.UnitTestResult; + +import javax.annotation.Nullable; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.TransformerException; +import java.io.File; +import java.io.FilenameFilter; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by gillesgrousset on 06/01/15. + */ +public class SurefireParser { + + private static final Logger LOGGER = LoggerFactory.getLogger(SurefireParser.class); + + private final Project project; + private final FileSystem fileSystem; + private final ResourcePerspectives perspectives; + private final SensorContext context; + + public SurefireParser(Project project, FileSystem fileSystem, ResourcePerspectives resourcePerspectives, SensorContext context) { + this.project = project; + this.fileSystem = fileSystem; + this.perspectives = resourcePerspectives; + this.context = context; + } + + public void collect(File reportsDir) { + + + File[] xmlFiles = getReports(reportsDir); + + if (xmlFiles.length == 0) { + insertZeroWhenNoReports(); + } else { + parseFiles(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() { + context.saveMeasure(CoreMetrics.TESTS, 0.0); + } + + private void parseFiles(File[] reports) { + UnitTestIndex index = new UnitTestIndex(); + parseFiles(reports, index); + save(index); + } + + private static void parseFiles(File[] reports, UnitTestIndex index) { + SurefireStaxHandler staxParser = new SurefireStaxHandler(index); + StaxParser parser = new StaxParser(staxParser, false); + for (File report : reports) { + try { + parser.parse(report); + } catch (XMLStreamException e) { + throw new IllegalStateException("Fail to parse the Surefire report: " + report, e); + } + } + } + + private void save(UnitTestIndex index) { + long negativeTimeTestNumber = 0; + + for (Map.Entry entry : index.getIndexByClassname().entrySet()) { + UnitTestClassReport report = entry.getValue(); + if (report.getTests() > 0) { + negativeTimeTestNumber += report.getNegativeTimeTestNumber(); + Resource resource = getUnitTestResource(entry.getKey()); + if (resource != null) { + save(report, resource); + } else { + LOGGER.warn("Resource not found: {}", entry.getKey()); + } + } + } + if (negativeTimeTestNumber > 0) { + LOGGER.warn("There is {} test(s) reported with negative time by data, total duration may not be accurate.", negativeTimeTestNumber); + } + } + + private void save(UnitTestClassReport report, Resource resource) { + double testsCount = report.getTests() - report.getSkipped(); + saveMeasure(resource, CoreMetrics.SKIPPED_TESTS, report.getSkipped()); + saveMeasure(resource, CoreMetrics.TESTS, testsCount); + saveMeasure(resource, CoreMetrics.TEST_ERRORS, report.getErrors()); + saveMeasure(resource, CoreMetrics.TEST_FAILURES, report.getFailures()); + saveMeasure(resource, CoreMetrics.TEST_EXECUTION_TIME, report.getDurationMilliseconds()); + double passedTests = testsCount - report.getErrors() - report.getFailures(); + if (testsCount > 0) { + double percentage = passedTests * 100d / testsCount; + saveMeasure(resource, CoreMetrics.TEST_SUCCESS_DENSITY, ParsingUtils.scaleValue(percentage)); + } + saveResults(resource, report); + } + + protected void saveResults(Resource 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()); + } + } + } + + @Nullable + public Resource getUnitTestResource(String classname) { + + String fileName = classname.replace('.', '/') + ".m"; + + InputFile inputFile = fileSystem.inputFile(fileSystem.predicates().hasPath(fileName)); + + /* + * Most xcodebuild JUnit parsers don't include the path to the class in the class field, so search for it if it + * wasn't found in the root. + */ + if (inputFile == null) { + List files = ImmutableList.copyOf(fileSystem.inputFiles(fileSystem.predicates().and( + fileSystem.predicates().hasType(InputFile.Type.TEST), + fileSystem.predicates().matchesPathPattern("**/" + fileName.replace("_", "+"))))); + + if (files.isEmpty()) { + LOGGER.info("Unable to locate test source file {}", fileName); + } else { + /* + * Lazily get the first file, since we wouldn't be able to determine the correct one from just the + * test class name in the event that there are multiple matches. + */ + inputFile = files.get(0); + } + } + + return inputFile == null ? null : context.getResource(inputFile); + } + + private void saveMeasure(Resource resource, Metric metric, double value) { + if (!Double.isNaN(value)) { + context.saveMeasure(resource, metric, value); + } + } +} diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/SurefireSensor.java similarity index 85% rename from src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/SurefireSensor.java index 9396593f..80e4e55e 100644 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireSensor.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/SurefireSensor.java @@ -1,23 +1,21 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ -package org.sonar.plugins.objectivec.tests; +package org.sonar.plugins.objectivec.surefire; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/SurefireStaxHandler.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/SurefireStaxHandler.java new file mode 100644 index 00000000..4dea8147 --- /dev/null +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/SurefireStaxHandler.java @@ -0,0 +1,139 @@ +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package org.sonar.plugins.objectivec.surefire.data; + +import org.apache.commons.lang.StringUtils; +import org.codehaus.staxmate.in.ElementFilter; +import org.codehaus.staxmate.in.SMEvent; +import org.codehaus.staxmate.in.SMHierarchicCursor; +import org.codehaus.staxmate.in.SMInputCursor; +import org.sonar.api.utils.ParsingUtils; +import org.sonar.api.utils.StaxParser.XmlStreamHandler; + +import javax.xml.stream.XMLStreamException; +import java.text.ParseException; +import java.util.Locale; + +public class SurefireStaxHandler implements XmlStreamHandler { + + private final UnitTestIndex index; + + public SurefireStaxHandler(UnitTestIndex index) { + this.index = index; + } + + public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException { + SMInputCursor testSuite = rootCursor.constructDescendantCursor(new ElementFilter("testsuite")); + SMEvent testSuiteEvent; + for (testSuiteEvent = testSuite.getNext(); testSuiteEvent != null; testSuiteEvent = testSuite.getNext()) { + if (testSuiteEvent.compareTo(SMEvent.START_ELEMENT) == 0) { + String testSuiteClassName = testSuite.getAttrValue("name"); + if (StringUtils.contains(testSuiteClassName, "$")) { + // test suites for inner classes are ignored + return; + } + SMInputCursor testCase = testSuite.childCursor(new ElementFilter("testcase")); + SMEvent event; + for (event = testCase.getNext(); event != null; event = testCase.getNext()) { + if (event.compareTo(SMEvent.START_ELEMENT) == 0) { + String testClassName = getClassname(testCase, testSuiteClassName); + UnitTestClassReport classReport = index.index(testClassName); + parseTestCase(testCase, classReport); + } + } + } + } + } + + private static String getClassname(SMInputCursor testCaseCursor, + String defaultClassname) throws XMLStreamException { + String testClassName = testCaseCursor.getAttrValue("classname"); + if (StringUtils.isNotBlank(testClassName) && testClassName.endsWith(")")) { + testClassName = testClassName.substring(0, testClassName.indexOf("(")); + } + return StringUtils.defaultIfBlank(testClassName, defaultClassname); + } + + private static void parseTestCase(SMInputCursor testCaseCursor, + UnitTestClassReport report) throws XMLStreamException { + report.add(parseTestResult(testCaseCursor)); + } + + private static void setStackAndMessage(UnitTestResult result, + SMInputCursor stackAndMessageCursor) throws XMLStreamException { + result.setMessage(stackAndMessageCursor.getAttrValue("message")); + String stack = stackAndMessageCursor.collectDescendantText(); + result.setStackTrace(stack); + } + + private static UnitTestResult parseTestResult(SMInputCursor testCaseCursor) throws XMLStreamException { + UnitTestResult detail = new UnitTestResult(); + String name = getTestCaseName(testCaseCursor); + detail.setName(name); + + String status = UnitTestResult.STATUS_OK; + String time = testCaseCursor.getAttrValue("time"); + Long duration = null; + + SMInputCursor childNode = testCaseCursor.descendantElementCursor(); + if (childNode.getNext() != null) { + String elementName = childNode.getLocalName(); + if ("skipped".equals(elementName)) { + status = UnitTestResult.STATUS_SKIPPED; + // bug with data reporting wrong time for skipped surefire + duration = 0L; + + } else if ("failure".equals(elementName)) { + status = UnitTestResult.STATUS_FAILURE; + setStackAndMessage(detail, childNode); + + } else if ("error".equals(elementName)) { + status = UnitTestResult.STATUS_ERROR; + setStackAndMessage(detail, childNode); + } + } + while (childNode.getNext() != null) { + // make sure we loop till the end of the elements cursor + } + if (duration == null) { + duration = getTimeAttributeInMS(time); + } + detail.setDurationMilliseconds(duration); + detail.setStatus(status); + return detail; + } + + private static long getTimeAttributeInMS(String value) throws XMLStreamException { + // hardcoded to Locale.ENGLISH see http://jira.codehaus.org/browse/SONAR-602 + try { + Double time = ParsingUtils.parseNumber(value, Locale.ENGLISH); + return !Double.isNaN(time) ? (long) ParsingUtils.scaleValue(time * 1000, 3) : 0L; + } catch (ParseException e) { + throw new XMLStreamException(e); + } + } + + private static String getTestCaseName(SMInputCursor testCaseCursor) throws XMLStreamException { + String classname = testCaseCursor.getAttrValue("classname"); + String name = testCaseCursor.getAttrValue("name"); + if (StringUtils.contains(classname, "$")) { + return StringUtils.substringAfter(classname, "$") + "/" + name; + } + return name; + } +} diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestClassReport.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestClassReport.java new file mode 100644 index 00000000..a4f886fd --- /dev/null +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestClassReport.java @@ -0,0 +1,100 @@ +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package org.sonar.plugins.objectivec.surefire.data; + +import com.google.common.collect.Lists; + +import java.util.Collections; +import java.util.List; + +public final class UnitTestClassReport { + private long errors = 0L; + private long failures = 0L; + private long skipped = 0L; + private long tests = 0L; + private long durationMilliseconds = 0L; + + + private long negativeTimeTestNumber = 0L; + private List results = null; + + public UnitTestClassReport add(UnitTestClassReport other) { + for (UnitTestResult otherResult : other.getResults()) { + add(otherResult); + } + return this; + } + + public UnitTestClassReport add(UnitTestResult result) { + initResults(); + results.add(result); + if (result.getStatus().equals(UnitTestResult.STATUS_SKIPPED)) { + skipped += 1; + + } else if (result.getStatus().equals(UnitTestResult.STATUS_FAILURE)) { + failures += 1; + + } else if (result.getStatus().equals(UnitTestResult.STATUS_ERROR)) { + errors += 1; + } + tests += 1; + if (result.getDurationMilliseconds() < 0) { + negativeTimeTestNumber += 1; + } else { + durationMilliseconds += result.getDurationMilliseconds(); + } + return this; + } + + private void initResults() { + if (results == null) { + results = Lists.newArrayList(); + } + } + + public long getErrors() { + return errors; + } + + public long getFailures() { + return failures; + } + + public long getSkipped() { + return skipped; + } + + public long getTests() { + return tests; + } + + public long getDurationMilliseconds() { + return durationMilliseconds; + } + + public long getNegativeTimeTestNumber() { + return negativeTimeTestNumber; + } + + public List getResults() { + if (results == null) { + return Collections.emptyList(); + } + return results; + } +} diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestIndex.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestIndex.java new file mode 100644 index 00000000..563cc05f --- /dev/null +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestIndex.java @@ -0,0 +1,78 @@ +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package org.sonar.plugins.objectivec.surefire.data; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import java.util.Map; +import java.util.Set; + +/** + * @since 2.8 + */ +public class UnitTestIndex { + + private Map indexByClassname; + + public UnitTestIndex() { + this.indexByClassname = Maps.newHashMap(); + } + + public UnitTestClassReport index(String classname) { + UnitTestClassReport classReport = indexByClassname.get(classname); + if (classReport == null) { + classReport = new UnitTestClassReport(); + indexByClassname.put(classname, classReport); + } + return classReport; + } + + public UnitTestClassReport get(String classname) { + return indexByClassname.get(classname); + } + + public Set getClassnames() { + return Sets.newHashSet(indexByClassname.keySet()); + } + + public Map getIndexByClassname() { + return indexByClassname; + } + + public int size() { + return indexByClassname.size(); + } + + public UnitTestClassReport merge(String classname, String intoClassname) { + UnitTestClassReport from = indexByClassname.get(classname); + if (from!=null) { + UnitTestClassReport to = index(intoClassname); + to.add(from); + indexByClassname.remove(classname); + return to; + } + return null; + } + + public void remove(String classname) { + indexByClassname.remove(classname); + } + + +} \ No newline at end of file diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestResult.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestResult.java new file mode 100644 index 00000000..501a4f21 --- /dev/null +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/surefire/data/UnitTestResult.java @@ -0,0 +1,84 @@ +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package org.sonar.plugins.objectivec.surefire.data; + +public final class UnitTestResult { + public static final String STATUS_OK = "ok"; + public static final String STATUS_ERROR = "error"; + public static final String STATUS_FAILURE = "failure"; + public static final String STATUS_SKIPPED = "skipped"; + + private String name; + private String status; + private String stackTrace; + private String message; + private long durationMilliseconds = 0L; + + public String getName() { + return name; + } + + public UnitTestResult setName(String name) { + this.name = name; + return this; + } + + public String getStatus() { + return status; + } + + public UnitTestResult setStatus(String status) { + this.status = status; + return this; + } + + public String getStackTrace() { + return stackTrace; + } + + public UnitTestResult setStackTrace(String stackTrace) { + this.stackTrace = stackTrace; + return this; + } + + public String getMessage() { + return message; + } + + public UnitTestResult setMessage(String message) { + this.message = message; + return this; + } + + public long getDurationMilliseconds() { + return durationMilliseconds; + } + + public UnitTestResult setDurationMilliseconds(long l) { + this.durationMilliseconds = l; + return this; + } + + public boolean isErrorOrFailure() { + return STATUS_ERROR.equals(status) || STATUS_FAILURE.equals(status); + } + + public boolean isError() { + return STATUS_ERROR.equals(status); + } +} diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java similarity index 82% rename from src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java index d877872b..43411c0c 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/ObjectiveCProfile.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations; @@ -26,10 +24,10 @@ import org.sonar.api.profiles.RulesProfile; import org.sonar.api.rules.ActiveRule; import org.sonar.api.utils.ValidationMessages; +import org.sonar.plugins.objectivec.violations.oclint.OCLintProfile; import org.sonar.plugins.objectivec.core.ObjectiveC; import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfile; import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasProfileImporter; -import org.sonar.plugins.objectivec.violations.oclint.OCLintProfile; import org.sonar.plugins.objectivec.violations.oclint.OCLintProfileImporter; import java.io.InputStreamReader; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java similarity index 75% rename from src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java index 212afdf3..f4a1b1b9 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfile.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations.fauxpas; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java similarity index 74% rename from src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java index dd4c84bf..27665f53 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasProfileImporter.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations.fauxpas; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java similarity index 86% rename from src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java index 1656feb5..fc70fbd2 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasReportParser.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations.fauxpas; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java similarity index 79% rename from src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java index e7dfb786..948df6a1 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasRulesDefinition.java @@ -1,23 +1,20 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ - package org.sonar.plugins.objectivec.violations.fauxpas; import org.apache.commons.io.IOUtils; @@ -27,8 +24,6 @@ import org.json.simple.JSONValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; import org.sonar.api.server.rule.RulesDefinition; import org.sonar.plugins.objectivec.core.ObjectiveC; import org.sonar.squidbridge.rules.SqaleXmlLoader; @@ -37,8 +32,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; -import java.util.ArrayList; -import java.util.List; /** * Created by gillesgrousset on 18/02/2016. diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java similarity index 80% rename from src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java index 6cd20e28..ba68d92e 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensor.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations.fauxpas; @@ -28,13 +26,10 @@ import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; -import org.sonar.api.rules.Violation; import org.sonar.plugins.objectivec.ObjectiveCPlugin; import org.sonar.plugins.objectivec.core.ObjectiveC; import java.io.File; -import java.util.ArrayList; -import java.util.Collection; public class FauxPasSensor implements Sensor { diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java similarity index 75% rename from src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java index f10bac72..f7d5500f 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintParser.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations.oclint; @@ -23,8 +21,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; import javax.xml.stream.XMLStreamException; @@ -33,7 +29,6 @@ import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.resources.Project; -import org.sonar.api.rules.Violation; import org.sonar.api.utils.StaxParser; final class OCLintParser { diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java similarity index 75% rename from src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java index 8d6f5f84..f2f67cc7 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfile.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations.oclint; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java similarity index 74% rename from src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java index 99d550d3..742d127c 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintProfileImporter.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations.oclint; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java similarity index 54% rename from src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java index 025ddfc5..16d9fa21 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRuleSeverity.java @@ -1,23 +1,20 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ - package org.sonar.plugins.objectivec.violations.oclint; /** diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java similarity index 89% rename from src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java index 506c569b..f9cd192c 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintRulesDefinition.java @@ -1,23 +1,20 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ - package org.sonar.plugins.objectivec.violations.oclint; import org.apache.commons.io.IOUtils; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java similarity index 81% rename from src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java index b4a28dbc..13bb0af9 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensor.java @@ -1,27 +1,23 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations.oclint; import java.io.File; -import java.util.ArrayList; -import java.util.Collection; import org.apache.tools.ant.DirectoryScanner; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java similarity index 86% rename from src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java rename to sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java index 133366af..28c51030 100644 --- a/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/violations/oclint/OCLintXMLStreamHandler.java @@ -1,21 +1,19 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * dev@sonar.codehaus.org +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) * - * 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 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. + * 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 + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ package org.sonar.plugins.objectivec.violations.oclint; diff --git a/src/main/resources/com/sonar/sqale/fauxpas-model.xml b/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/fauxpas-model.xml similarity index 100% rename from src/main/resources/com/sonar/sqale/fauxpas-model.xml rename to sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/fauxpas-model.xml diff --git a/src/main/resources/com/sonar/sqale/oclint-model.xml b/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/oclint-model.xml similarity index 100% rename from src/main/resources/com/sonar/sqale/oclint-model.xml rename to sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/oclint-model.xml diff --git a/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml similarity index 100% rename from src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml rename to sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml diff --git a/src/main/resources/org/sonar/plugins/fauxpas/rules.json b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/rules.json similarity index 100% rename from src/main/resources/org/sonar/plugins/fauxpas/rules.json rename to sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/rules.json diff --git a/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml similarity index 100% rename from src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml rename to sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml diff --git a/src/main/resources/org/sonar/plugins/oclint/rules.txt b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/rules.txt similarity index 100% rename from src/main/resources/org/sonar/plugins/oclint/rules.txt rename to sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/rules.txt diff --git a/src/main/shell/run-sonar.sh b/sonar-objective-c-plugin/src/main/shell/run-sonar.sh similarity index 93% rename from src/main/shell/run-sonar.sh rename to sonar-objective-c-plugin/src/main/shell/run-sonar.sh index af447134..0288df7a 100755 --- a/src/main/shell/run-sonar.sh +++ b/sonar-objective-c-plugin/src/main/shell/run-sonar.sh @@ -1,5 +1,23 @@ #### run-sonar.sh #### #!/bin/bash +# +# backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. +# Copyright © 2012 OCTO Technology, Backelite (${email}) +# +# 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, see . +# + ## INSTALLATION: script to copy in your Xcode project in the same directory as the .xcodeproj file ## USAGE: ./run-sonar.sh ## DEBUG: ./run-sonar.sh -v @@ -260,16 +278,16 @@ runCommand xcodebuild.log "${buildCmd[@]}" #oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.json file cat xcodebuild.log | $XCPRETTY_CMD -r json-compilation-database -o compile_commands.json -# Unit tests and coverage +# Unit surefire and coverage if [ "$testScheme" = "" ] || [ "$unittests" = "" ]; then - echo 'Skipping tests!' + echo 'Skipping surefire!' - # Put default xml files with no tests and no coverage... + # Put default xml files with no surefire and no coverage... echo "" > sonar-reports/TEST-report.xml echo "" > sonar-reports/coverage.xml else - echo -n 'Running tests' + echo -n 'Running surefire' if [ "$coverageType" = "profdata" -o "$coverageType" = "" ]; then # profdata diff --git a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java b/sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/ObjectiveCAstScannerTest.java similarity index 52% rename from src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java rename to sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/ObjectiveCAstScannerTest.java index 474828c6..b4dfcd25 100644 --- a/src/test/java/org/sonar/objectivec/ObjectiveCAstScannerTest.java +++ b/sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/ObjectiveCAstScannerTest.java @@ -1,4 +1,21 @@ -/* +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package com.sonar.objectivec;/* * Sonar Objective-C Plugin * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org @@ -17,7 +34,6 @@ * 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.objectivec; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; @@ -25,8 +41,11 @@ import java.io.File; +import com.sonar.objectivec.ObjectiveCAstScanner; +import org.hamcrest.Matchers; +import org.junit.Assert; import org.junit.Test; -import org.sonar.objectivec.api.ObjectiveCMetric; +import com.sonar.objectivec.api.ObjectiveCMetric; import org.sonar.squidbridge.api.SourceFile; public class ObjectiveCAstScannerTest { @@ -34,21 +53,21 @@ public class ObjectiveCAstScannerTest { @Test public void lines() { SourceFile file = ObjectiveCAstScanner.scanSingleFile(new File("src/test/resources/objcSample.h")); - assertThat(file.getInt(ObjectiveCMetric.LINES), is(18)); + Assert.assertThat(file.getInt(ObjectiveCMetric.LINES), Matchers.is(17)); } @Test public void lines_of_code() { SourceFile file = ObjectiveCAstScanner.scanSingleFile(new File("src/test/resources/objcSample.h")); - assertThat(file.getInt(ObjectiveCMetric.LINES_OF_CODE), is(5)); + Assert.assertThat(file.getInt(ObjectiveCMetric.LINES_OF_CODE), Matchers.is(5)); } @Test public void comments() { SourceFile file = ObjectiveCAstScanner.scanSingleFile(new File("src/test/resources/objcSample.h")); - assertThat(file.getInt(ObjectiveCMetric.COMMENT_LINES), is(4)); - assertThat(file.getNoSonarTagLines(), hasItem(10)); - assertThat(file.getNoSonarTagLines().size(), is(1)); + Assert.assertThat(file.getInt(ObjectiveCMetric.COMMENT_LINES), Matchers.is(4)); + Assert.assertThat(file.getNoSonarTagLines(), Matchers.hasItem(10)); + Assert.assertThat(file.getNoSonarTagLines().size(), Matchers.is(1)); } } diff --git a/sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/api/ObjectiveCPunctuatorTest.java b/sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/api/ObjectiveCPunctuatorTest.java new file mode 100644 index 00000000..50eb80c8 --- /dev/null +++ b/sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/api/ObjectiveCPunctuatorTest.java @@ -0,0 +1,34 @@ +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package com.sonar.objectivec.api; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class ObjectiveCPunctuatorTest { + + @Test + public void test() { + Assert.assertThat(ObjectiveCPunctuator.values().length, Matchers.is(48)); + } + +} diff --git a/sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/lexer/ObjectiveCLexerTest.java b/sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/lexer/ObjectiveCLexerTest.java new file mode 100644 index 00000000..aa42c20f --- /dev/null +++ b/sonar-objective-c-plugin/src/test/java/com/sonar/objectivec/lexer/ObjectiveCLexerTest.java @@ -0,0 +1,84 @@ +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package com.sonar.objectivec.lexer; + +import static com.sonar.sslr.test.lexer.LexerMatchers.hasComment; +import static com.sonar.sslr.test.lexer.LexerMatchers.hasToken; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.util.List; + +import com.sonar.sslr.test.lexer.LexerMatchers; +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.sonar.sslr.api.GenericTokenType; +import com.sonar.sslr.api.Token; +import com.sonar.sslr.impl.Lexer; + +public class ObjectiveCLexerTest { + + private static Lexer lexer; + + @BeforeClass + public static void init() { + lexer = ObjectiveCLexer.create(); + } + + @Test + public void lexMultiLinesComment() { + Assert.assertThat(lexer.lex("/* My Comment \n*/"), LexerMatchers.hasComment("/* My Comment \n*/")); + Assert.assertThat(lexer.lex("/**/"), LexerMatchers.hasComment("/**/")); + } + + @Test + public void lexInlineComment() { + Assert.assertThat(lexer.lex("// My Comment \n new line"), LexerMatchers.hasComment("// My Comment ")); + Assert.assertThat(lexer.lex("//"), LexerMatchers.hasComment("//")); + } + + @Test + public void lexEndOflineComment() { + Assert.assertThat(lexer.lex("[self init]; // My Comment end of line"), LexerMatchers.hasComment("// My Comment end of line")); + Assert.assertThat(lexer.lex("[self init]; //"), LexerMatchers.hasComment("//")); + } + + @Test + public void lexLineOfCode() { + Assert.assertThat(lexer.lex("[self init];"), LexerMatchers.hasToken("[self", GenericTokenType.LITERAL)); + } + + @Test + public void lexEmptyLine() { + List tokens = lexer.lex("\n"); + Assert.assertThat(tokens.size(), Matchers.equalTo(1)); + Assert.assertThat(tokens, LexerMatchers.hasToken(GenericTokenType.EOF)); + } + + @Test + public void lexSampleFile() { + List tokens = lexer.lex(new File("src/test/resources/objcSample.h")); + Assert.assertThat(tokens.size(), Matchers.equalTo(16)); + Assert.assertThat(tokens, LexerMatchers.hasToken(GenericTokenType.EOF)); + } + +} diff --git a/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java b/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java similarity index 75% rename from src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java rename to sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java index 88a48428..a832b4ab 100644 --- a/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java +++ b/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java @@ -1,4 +1,21 @@ -/* +/** + * backelite-sonar-objective-c-plugin - Enables analysis of Objective-C projects into SonarQube. + * Copyright © 2012 OCTO Technology, Backelite (${email}) + * + * 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, see . + */ +package org.sonar.plugins.objectivec.complexity;/* * Sonar Objective-C Plugin * Copyright (C) 2012 OCTO Technology, Backelite * dev@sonar.codehaus.org @@ -17,14 +34,15 @@ * 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.complexity; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; +import org.sonar.plugins.objectivec.complexity.LizardReportParser; import java.io.BufferedWriter; import java.io.File; @@ -142,49 +160,49 @@ public File createIncorrectFile() throws IOException { public void parseReportShouldReturnMapWhenXMLFileIsCorrect() { LizardReportParser parser = new LizardReportParser(); - assertNotNull("correct file is null", correctFile); + Assert.assertNotNull("correct file is null", correctFile); Map> report = parser.parseReport(correctFile); - assertNotNull("report is null", report); + Assert.assertNotNull("report is null", report); - assertTrue("Key is not there", report.containsKey("App/Controller/Accelerate/AccelerationViewController.h")); + Assert.assertTrue("Key is not there", report.containsKey("App/Controller/Accelerate/AccelerationViewController.h")); List list1 = report.get("App/Controller/Accelerate/AccelerationViewController.h"); - assertEquals(4, list1.size()); + Assert.assertEquals(4, list1.size()); for (Measure measure : list1) { String s = measure.getMetric().getKey(); if (s.equals(CoreMetrics.FUNCTIONS_KEY)) { - assertEquals("Header Functions has a wrong value", 0, measure.getIntValue().intValue()); + Assert.assertEquals("Header Functions has a wrong value", 0, measure.getIntValue().intValue()); } else if (s.equals(CoreMetrics.COMPLEXITY_KEY)) { - assertEquals("Header Complexity has a wrong value", 0, measure.getIntValue().intValue()); + Assert.assertEquals("Header Complexity has a wrong value", 0, measure.getIntValue().intValue()); } else if (s.equals(CoreMetrics.FILE_COMPLEXITY_KEY)) { - assertEquals("Header File Complexity has a wrong value", 0.0d, measure.getValue().doubleValue(), 0.0d); + Assert.assertEquals("Header File Complexity has a wrong value", 0.0d, measure.getValue().doubleValue(), 0.0d); } else if (s.equals(CoreMetrics.COMPLEXITY_IN_FUNCTIONS_KEY)) { - assertEquals("Header Complexity in Functions has a wrong value", 0, measure.getIntValue().intValue()); + Assert.assertEquals("Header Complexity in Functions has a wrong value", 0, measure.getIntValue().intValue()); } else if (s.equals(CoreMetrics.FUNCTION_COMPLEXITY_KEY)) { - assertEquals("Header Functions Complexity has a wrong value", 0.0d, measure.getValue().doubleValue(), 0.0d); + Assert.assertEquals("Header Functions Complexity has a wrong value", 0.0d, measure.getValue().doubleValue(), 0.0d); } } - assertTrue("Key is not there", report.containsKey("App/Controller/Accelerate/AccelerationViewController.m")); + Assert.assertTrue("Key is not there", report.containsKey("App/Controller/Accelerate/AccelerationViewController.m")); List list2 = report.get("App/Controller/Accelerate/AccelerationViewController.m"); - assertEquals(7, list2.size()); + Assert.assertEquals(7, list2.size()); for (Measure measure : list2) { String s = measure.getMetric().getKey(); if (s.equals(CoreMetrics.FUNCTIONS_KEY)) { - assertEquals("MFile Functions has a wrong value", 2, measure.getIntValue().intValue()); + Assert.assertEquals("MFile Functions has a wrong value", 2, measure.getIntValue().intValue()); } else if (s.equals(CoreMetrics.COMPLEXITY_KEY)) { - assertEquals("MFile Complexity has a wrong value", 6, measure.getIntValue().intValue()); + Assert.assertEquals("MFile Complexity has a wrong value", 6, measure.getIntValue().intValue()); } else if (s.equals(CoreMetrics.FILE_COMPLEXITY_KEY)) { - assertEquals("MFile File Complexity has a wrong value", 6.0d, measure.getValue().doubleValue(), 0.0d); + Assert.assertEquals("MFile File Complexity has a wrong value", 6.0d, measure.getValue().doubleValue(), 0.0d); } else if (s.equals(CoreMetrics.COMPLEXITY_IN_FUNCTIONS_KEY)) { - assertEquals("MFile Complexity in Functions has a wrong value", 6, measure.getIntValue().intValue()); + Assert.assertEquals("MFile Complexity in Functions has a wrong value", 6, measure.getIntValue().intValue()); } else if (s.equals(CoreMetrics.FUNCTION_COMPLEXITY_KEY)) { - assertEquals("MFile Functions Complexity has a wrong value", 3.0d, measure.getValue().doubleValue(), 0.0d); + Assert.assertEquals("MFile Functions Complexity has a wrong value", 3.0d, measure.getValue().doubleValue(), 0.0d); } } } @@ -196,10 +214,10 @@ public void parseReportShouldReturnMapWhenXMLFileIsCorrect() { public void parseReportShouldReturnNullWhenXMLFileIsIncorrect() { LizardReportParser parser = new LizardReportParser(); - assertNotNull("correct file is null", incorrectFile); + Assert.assertNotNull("correct file is null", incorrectFile); Map> report = parser.parseReport(incorrectFile); - assertNull("report is not null", report); + Assert.assertNull("report is not null", report); } diff --git a/sonar-objective-c-plugin/src/test/resources/Profile.m b/sonar-objective-c-plugin/src/test/resources/Profile.m new file mode 100644 index 00000000..8ae3d092 --- /dev/null +++ b/sonar-objective-c-plugin/src/test/resources/Profile.m @@ -0,0 +1,39 @@ +// +// Profile.m +// Stop-tabac +// +// Created by François HELG on 5/29/12. +// Copyright (c) 2012 Université de Genève. All rights reserved. +// + +#import "Profile.h" +#import "Purchase.h" + + +@implementation Profile + +@dynamic cigaretteCounter; +@dynamic dailyCost; +@dynamic facebookOff; +@dynamic firstCigaretteAfterWakeUp; +@dynamic godfatherId; +@dynamic godfatherName; +@dynamic hasQuitSmoking; +@dynamic lifeCounter; +@dynamic nbOfSmokedCigarettesPerDay; +@dynamic nextCoachMessageToDisplay; +@dynamic nextFailureMessageToDisplay; +@dynamic nextCravingMessageToDisplay; +@dynamic quitSmokingDate; +@dynamic substitute; +@dynamic purchase; +@dynamic dependencyLevel; +@dynamic deliverSubstituteMessages; +@dynamic lastAchievementDayFilename; +@dynamic lastAchievementAssiduityFilename; +@dynamic lastAchievementWillingnessFilename; +@dynamic numberOfConnexions; +@dynamic numberOfAdvicesTaken; +@dynamic numberOfFailures; + +@end \ No newline at end of file diff --git a/sonar-objective-c-plugin/src/test/resources/objcSample.h b/sonar-objective-c-plugin/src/test/resources/objcSample.h new file mode 100644 index 00000000..cb3ce09b --- /dev/null +++ b/sonar-objective-c-plugin/src/test/resources/objcSample.h @@ -0,0 +1,17 @@ +// +// STWelcomeViewController.h +// Stop-tabac +// +// Created by François HELG on 5/7/12. +// Copyright (c) 2012 Université de Genève. All rights reserved. +// + +#import +// NOSONAR +@class Profile; + +@interface StillSmokingViewController : UIViewController + +@property (strong, nonatomic) Profile *userProfile; + +@end \ No newline at end of file diff --git a/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java b/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java deleted file mode 100644 index e3a6b699..00000000 --- a/src/main/java/org/sonar/objectivec/parser/ObjectiveCGrammarImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.objectivec.parser; - -import static com.sonar.sslr.api.GenericTokenType.EOF; -import static com.sonar.sslr.api.GenericTokenType.LITERAL; -import static com.sonar.sslr.impl.matcher.GrammarFunctions.Standard.o2n; - -import org.sonar.objectivec.api.ObjectiveCGrammar; - -public class ObjectiveCGrammarImpl extends ObjectiveCGrammar { - - public ObjectiveCGrammarImpl() { - - program.is(o2n(LITERAL), EOF); - - } - -} diff --git a/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java b/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java deleted file mode 100644 index 52e76bb9..00000000 --- a/src/main/java/org/sonar/objectivec/parser/ObjectiveCParser.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.objectivec.parser; - -import org.sonar.objectivec.ObjectiveCConfiguration; -import org.sonar.objectivec.api.ObjectiveCGrammar; -import org.sonar.objectivec.lexer.ObjectiveCLexer; - -import com.sonar.sslr.impl.Parser; -import com.sonar.sslr.impl.events.ParsingEventListener; - -public class ObjectiveCParser { - - private ObjectiveCParser() { - } - - public static Parser create() { - return create(new ObjectiveCConfiguration()); - } - - public static Parser create(ObjectiveCConfiguration conf) { - return Parser.builder((ObjectiveCGrammar) new ObjectiveCGrammarImpl()) - .withLexer(ObjectiveCLexer.create(conf)) - .build(); - } - -} diff --git a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java b/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java deleted file mode 100644 index 5a48524a..00000000 --- a/src/main/java/org/sonar/plugins/objectivec/tests/SurefireParser.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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 com.google.common.collect.ImmutableList; -import com.sun.swing.internal.plaf.metal.resources.metal_sv; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.component.ResourcePerspectives; -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 { - - private static final Logger LOG = LoggerFactory.getLogger(SurefireParser.class); - - private final Project project; - private final FileSystem fileSystem; - private final ResourcePerspectives resourcePerspectives; - private final SensorContext context; - - public SurefireParser(Project project, FileSystem fileSystem, ResourcePerspectives resourcePerspectives, SensorContext context) { - this.project = project; - this.fileSystem = fileSystem; - this.resourcePerspectives = resourcePerspectives; - this.context = context; - } - - public void collect(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) { - - 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) { - LOG.error("Can not parse surefire reports", 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(""); - } - - private void saveClassMeasure(SensorContext context, TestSuiteReport fileReport, Metric metric, double value) { - - if ( !Double.isNaN(value)) { - - context.saveMeasure(getUnitTestResource(fileReport.getClassKey()), metric, value); - - } - - /*if ( !Double.isNaN(value)) { - - String basename = fileReport.getClassKey().replace('.', '/'); - - // .m file - context.saveMeasure(getUnitTestResource(basename + ".m"), metric, value); - - // Try .m file with + in name - try { - context.saveMeasure(getUnitTestResource(basename.replace('_', '+') + ".m"), metric, value); - } catch (Exception e) { - // Nothing : File was probably already registered successfully - } - }*/ - } - - public Resource getUnitTestResource(String classname) { - - String fileName = classname.replace('.', '/') + ".m"; - - InputFile inputFile = fileSystem.inputFile(fileSystem.predicates().or(fileSystem.predicates().matchesPathPattern("**/" + fileName), - fileSystem.predicates().matchesPathPattern("**/" + fileName.replace("_", "+")))); - if (inputFile == null) { - return null; - } - - Resource resource = context.getResource(inputFile); - - if(resource instanceof org.sonar.api.resources.File) { - org.sonar.api.resources.File sonarFile = (org.sonar.api.resources.File) resource; - sonarFile.setQualifier(Qualifiers.UNIT_TEST_FILE); - } - - - return resource; - - } -} diff --git a/src/test/java/org/sonar/objectivec/api/ObjectiveCPunctuatorTest.java b/src/test/java/org/sonar/objectivec/api/ObjectiveCPunctuatorTest.java deleted file mode 100644 index 5b1df7a2..00000000 --- a/src/test/java/org/sonar/objectivec/api/ObjectiveCPunctuatorTest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.objectivec.api; - -import org.junit.Test; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class ObjectiveCPunctuatorTest { - - @Test - public void test() { - assertThat(ObjectiveCPunctuator.values().length, is(48)); - } - -} diff --git a/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java b/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java deleted file mode 100644 index 4e912c02..00000000 --- a/src/test/java/org/sonar/objectivec/lexer/ObjectiveCLexerTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.objectivec.lexer; - -import static com.sonar.sslr.test.lexer.LexerMatchers.hasComment; -import static com.sonar.sslr.test.lexer.LexerMatchers.hasToken; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; - -import java.io.File; -import java.util.List; - -import org.junit.BeforeClass; -import org.junit.Test; - -import com.sonar.sslr.api.GenericTokenType; -import com.sonar.sslr.api.Token; -import com.sonar.sslr.impl.Lexer; - -public class ObjectiveCLexerTest { - - private static Lexer lexer; - - @BeforeClass - public static void init() { - lexer = ObjectiveCLexer.create(); - } - - @Test - public void lexMultiLinesComment() { - assertThat(lexer.lex("/* My Comment \n*/"), hasComment("/* My Comment \n*/")); - assertThat(lexer.lex("/**/"), hasComment("/**/")); - } - - @Test - public void lexInlineComment() { - assertThat(lexer.lex("// My Comment \n new line"), hasComment("// My Comment ")); - assertThat(lexer.lex("//"), hasComment("//")); - } - - @Test - public void lexEndOflineComment() { - assertThat(lexer.lex("[self init]; // My Comment end of line"), hasComment("// My Comment end of line")); - assertThat(lexer.lex("[self init]; //"), hasComment("//")); - } - - @Test - public void lexLineOfCode() { - assertThat(lexer.lex("[self init];"), hasToken("[self", GenericTokenType.LITERAL)); - } - - @Test - public void lexEmptyLine() { - List tokens = lexer.lex("\n"); - assertThat(tokens.size(), equalTo(1)); - assertThat(tokens, hasToken(GenericTokenType.EOF)); - } - - @Test - public void lexSampleFile() { - List tokens = lexer.lex(new File("src/test/resources/objcSample.h")); - assertThat(tokens.size(), equalTo(16)); - assertThat(tokens, hasToken(GenericTokenType.EOF)); - } - -} diff --git a/src/test/java/org/sonar/plugins/objectivec/complexity/LizardSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/complexity/LizardSensorTest.java deleted file mode 100644 index 84cf3edd..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/complexity/LizardSensorTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.complexity; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.config.Settings; -import org.sonar.api.resources.Project; -import org.sonar.plugins.objectivec.core.ObjectiveC; - -import java.util.SortedSet; -import java.util.TreeSet; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -/** - * @author Andres Gil Herrera - * @since 03/06/15. - */ -public class LizardSensorTest { - - private Settings settings; - - @Before - public void setUp() { - settings = new Settings(); - } - - /** - * this method tests that the sensor should be executed when a project is a root project and uses objective c - */ - @Test - public void shouldExecuteOnProjectShouldBeTrueWhenProjectIsObjc() { - final Project project = new Project("Test"); - - FileSystem fileSystem = mock(FileSystem.class); - SortedSet languages = new TreeSet(); - languages.add(ObjectiveC.KEY); - when(fileSystem.languages()).thenReturn(languages); - - final LizardSensor testedSensor = new LizardSensor(fileSystem, settings); - - assertTrue(testedSensor.shouldExecuteOnProject(project)); - } - - /** - * this method tests that the sensor does not get executed when a project dont uses objective c - */ - @Test - public void shouldExecuteOnProjectShouldBeFalseWhenProjectIsSomethingElse() { - final Project project = new Project("Test"); - - FileSystem fileSystem = mock(FileSystem.class); - SortedSet languages = new TreeSet(); - languages.add("Test"); - when(fileSystem.languages()).thenReturn(languages); - - final LizardSensor testedSensor = new LizardSensor(fileSystem, settings); - - assertFalse(testedSensor.shouldExecuteOnProject(project)); - } - -} \ No newline at end of file diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java deleted file mode 100644 index 410d9d0d..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/violations/fauxpas/FauxPasSensorTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.violations.fauxpas; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.component.ResourcePerspectives; -import org.sonar.api.config.Settings; -import org.sonar.api.resources.Project; -import org.sonar.plugins.objectivec.core.ObjectiveC; -import org.sonar.plugins.objectivec.violations.fauxpas.FauxPasSensor; - -import java.util.SortedSet; -import java.util.TreeSet; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * Created by gillesgrousset on 12/02/15. - */ -public class FauxPasSensorTest { - - private Settings settings; - - @Before - public void setUp() { - settings = new Settings(); - } - - @Test - public void shouldExecuteOnProjectShouldBeTrueWhenProjectIsObjc() { - final Project project = new Project("Test"); - - FileSystem fileSystem = mock(FileSystem.class); - ResourcePerspectives resourcePerspectives = mock(ResourcePerspectives.class); - SortedSet languages = new TreeSet(); - languages.add(ObjectiveC.KEY); - when(fileSystem.languages()).thenReturn(languages); - - final FauxPasSensor testedSensor = new FauxPasSensor(fileSystem, settings, resourcePerspectives); - - assertTrue(testedSensor.shouldExecuteOnProject(project)); - } - - @Test - public void shouldExecuteOnProjectShouldBeFalseWhenProjectIsSomethingElse() { - final Project project = new Project("Test"); - - FileSystem fileSystem = mock(FileSystem.class); - ResourcePerspectives resourcePerspectives = mock(ResourcePerspectives.class); - SortedSet languages = new TreeSet(); - languages.add("Test"); - when(fileSystem.languages()).thenReturn(languages); - - final FauxPasSensor testedSensor = new FauxPasSensor(fileSystem, settings, resourcePerspectives); - - assertFalse(testedSensor.shouldExecuteOnProject(project)); - } -} diff --git a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java b/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java deleted file mode 100644 index 62aed770..00000000 --- a/src/test/java/org/sonar/plugins/objectivec/violations/oclint/OCLintSensorTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Sonar Objective-C Plugin - * Copyright (C) 2012 OCTO Technology, Backelite - * 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.violations.oclint; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.component.ResourcePerspectives; -import org.sonar.api.config.Settings; -import org.sonar.api.resources.Project; -import org.sonar.plugins.objectivec.core.ObjectiveC; -import org.sonar.plugins.objectivec.violations.oclint.OCLintSensor; - -import java.util.SortedSet; -import java.util.TreeSet; - -public final class OCLintSensorTest { - - private Settings settings; - - @Before - public void setUp() { - settings = new Settings(); - } - - @Test - public void shouldExecuteOnProjectShouldBeTrueWhenProjectIsObjc() { - final Project project = new Project("Test"); - - ResourcePerspectives resourcePerspectives = mock(ResourcePerspectives.class); - FileSystem fileSystem = mock(FileSystem.class); - SortedSet languages = new TreeSet(); - languages.add(ObjectiveC.KEY); - when(fileSystem.languages()).thenReturn(languages); - - final OCLintSensor testedSensor = new OCLintSensor(fileSystem, settings, resourcePerspectives); - - assertTrue(testedSensor.shouldExecuteOnProject(project)); - } - - @Test - public void shouldExecuteOnProjectShouldBeFalseWhenProjectIsSomethingElse() { - final Project project = new Project("Test"); - - ResourcePerspectives resourcePerspectives = mock(ResourcePerspectives.class); - FileSystem fileSystem = mock(FileSystem.class); - SortedSet languages = new TreeSet(); - languages.add("Test"); - when(fileSystem.languages()).thenReturn(languages); - - final OCLintSensor testedSensor = new OCLintSensor(fileSystem, settings, resourcePerspectives); - - assertFalse(testedSensor.shouldExecuteOnProject(project)); - } - -} diff --git a/updateFauxPasRules.groovy b/updateFauxPasRules.groovy index 0406af47..c5774f71 100644 --- a/updateFauxPasRules.groovy +++ b/updateFauxPasRules.groovy @@ -1,3 +1,22 @@ +/** + * Sonar Objective-C Plugin + * Copyright (C) 2012 OCTO Technology, Backelite + * 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 + */ // Update profile-fauxpas.xml from Faux Pas online rules documentation // Severity is determined from the category @@ -71,8 +90,8 @@ def writeRules(rls, file) { } // Files -File rulesJson = new File('src/main/resources/org/sonar/plugins/fauxpas/rules.json') -File profileXml = new File('src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml') +File rulesJson = new File('sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/rules.json') +File profileXml = new File('sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml') // Parse online documentation def rules = parseRules('http://fauxpasapp.com/rules/', [ diff --git a/updateOCLintRules.groovy b/updateOCLintRules.groovy index 45d665e0..bd5f4181 100644 --- a/updateOCLintRules.groovy +++ b/updateOCLintRules.groovy @@ -1,3 +1,22 @@ +/** + * Sonar Objective-C Plugin + * Copyright (C) 2012 OCTO Technology, Backelite + * 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 + */ // Update rules.txt and profile-clint.xml from OCLint documentation // Severity is determined from the category @@ -189,8 +208,8 @@ def mergeRules(existingRules, freshRules) { } // 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') +File rulesTxt = new File('sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/rules.txt') +File profileXml = new File('sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml') // Parse OCLint online documentation From 76b64ca25570621ecaa9ff5a0f89e21c2d01aa06 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 5 Dec 2017 17:47:43 +0100 Subject: [PATCH 098/107] Added FauxPas new rules --- .../com/sonar/sqale/fauxpas-model.xml | 53 +++++++++++++++++++ .../sonar/plugins/fauxpas/profile-fauxpas.xml | 16 ++++++ .../org/sonar/plugins/fauxpas/rules.json | 30 ++++++++++- 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/fauxpas-model.xml b/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/fauxpas-model.xml index bb42e286..0531ccea 100644 --- a/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/fauxpas-model.xml +++ b/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/fauxpas-model.xml @@ -164,6 +164,32 @@ READABILITY Readability + + FauxPas + SourceFileHeaderComment + + remediationFunction + CONSTANT_ISSUE + + + offset + 5 + min + + + + FauxPas + Spelling + + remediationFunction + CONSTANT_ISSUE + + + offset + 5 + min + + FauxPas SuspiciousDateTimeFormat @@ -298,6 +324,19 @@ UNDERSTANDABILITY Understandability + + FauxPas + CyclomaticComplexity + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + FauxPas MissingAPIUsageDescription @@ -461,6 +500,20 @@ INSTRUCTION_RELIABILITY Instruction + + FauxPas + ViewLayoutInXIB + + remediationFunction + CONSTANT_ISSUE + + + offset + 10 + min + + + FauxPas CompleteNotificationCenterDetachment diff --git a/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml index 67742775..e5b16df1 100644 --- a/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml +++ b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/profile-fauxpas.xml @@ -207,6 +207,10 @@ FauxPas MissingAPIUsageDescription
+ + FauxPas + ViewLayoutInXIB + FauxPas BasicProjectSettings @@ -363,6 +367,14 @@ FauxPas NonTypedefBlockDeclaration + + FauxPas + Spelling + + + FauxPas + SourceFileHeaderComment + FauxPas UnprefixedClass @@ -391,6 +403,10 @@ FauxPas ReservedIdentifierNaming + + FauxPas + CyclomaticComplexity + FauxPas DefaultInExhaustiveSwitch diff --git a/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/rules.json b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/rules.json index 3563460e..b0329645 100644 --- a/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/rules.json +++ b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/fauxpas/rules.json @@ -353,7 +353,14 @@ "category": "Config", "key": "MissingAPIUsageDescription", "name": "Missing API usage description", - "description": "Some APIs (that access e.g. contacts or calendars) require a usage description in the application metadata. This will be shown to the user when the system asks them to allow the application access to the related user data or system resource. This rule warns when some such APIs are used, but the associated usage description is missing.", + "description": "Some APIs (that access e.g. contacts or calendars) require a usage description (\u201cpurpose string\u201d) in the application metadata. This will be shown to the user when the system asks them to allow the application access to the related user data or system resource. This rule warns when some such APIs are used, but the associated usage description is missing.", + "severity": "MINOR" + }, + { + "category": "Config", + "key": "ViewLayoutInXIB", + "name": "Erroneous view layout in XIB", + "description": "Warns about views that are marked as \u2018misplaced\u2019 or \u2018ambiguous\u2019 in XIBs or storyboards that use autolayout.This rule relies on XIB annotations added by Xcode, which means that no diagnostics will be emitted for misplaced or ambiguous views that have not (yet) been annotated by Xcode.", "severity": "MINOR" }, { @@ -629,6 +636,20 @@ "description": "It is recommended that typedef be used for all block-typed declarations, for readability.", "severity": "MAJOR" }, + { + "category": "Style", + "key": "Spelling", + "name": "error Spelling", + "description": "Warns if some words in code symbol names are spelled incorrectly.This rule expects code symbol names to be in English (US and/or GB).", + "severity": "MAJOR" + }, + { + "category": "Style", + "key": "SourceFileHeaderComment", + "name": "Source file header comment format", + "description": "Warns about errors in source file header comments (that are formatted using the default Xcode template), for example if the file name in the comment does not match the actual file name, or if the placeholder __MyCompanyName__ is present.", + "severity": "MAJOR" + }, { "category": "Style", "key": "UnprefixedClass", @@ -678,6 +699,13 @@ "description": "Warns when identifiers are named using conventions reserved by the C standard or POSIX.", "severity": "MINOR" }, + { + "category": "Pedantic", + "key": "CyclomaticComplexity", + "name": "Cyclomatic complexity", + "description": "Warns if a routine has high cyclomatic complexity (that is, the number of linearly independent paths through the routine\u2019s source code).", + "severity": "MINOR" + }, { "category": "Pedantic", "key": "DefaultInExhaustiveSwitch", From c933413945ba30f27315a14e304cdf0807157065 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 5 Dec 2017 17:54:54 +0100 Subject: [PATCH 099/107] OCLint rules update --- README.md | 2 + .../com/sonar/sqale/oclint-model.xml | 13 ++++ .../sonar/plugins/oclint/profile-oclint.xml | 76 ++++++------------- .../org/sonar/plugins/oclint/rules.txt | 17 ++--- 4 files changed, 47 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 4fee0b77..3cf16241 100755 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ Binary packages are available in the release section. ### 0.6.2 - SonarQube 6.5 support +- FauxPas 1.7.2 support (4 new rules added) +- OCLint 0.13 support (1 new rule added) - Update run-sonar.sh : xcodebuild optimization (see https://github.com/Backelite/sonar-objective-c/pull/26 thanks to [davidy4ng](https://github.com/davidy4ng)). - Fix for properties with space (see https://github.com/Backelite/sonar-objective-c/pull/29 thanks to [Branlute](https://github.com/Branlute)) diff --git a/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/oclint-model.xml b/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/oclint-model.xml index 58e6bf86..59ec3e95 100755 --- a/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/oclint-model.xml +++ b/sonar-objective-c-plugin/src/main/resources/com/sonar/sqale/oclint-model.xml @@ -771,6 +771,19 @@ INSTRUCTION_RELIABILITY Instruction + + OCLint + must override hash with isEqual + + remediationFunction + CONSTANT_ISSUE + + + offset + 5 + min + + OCLint calling protected method diff --git a/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml index 13c9f14e..5c9c3b76 100755 --- a/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml +++ b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/profile-oclint.xml @@ -3,10 +3,6 @@ OCLint objc - - OCLint - use early exits and continue - OCLint avoid branching statement as last in loop @@ -47,10 +43,6 @@ OCLint dead code - - OCLint - default label not last in switch statement - OCLint double negative @@ -143,6 +135,10 @@ OCLint multiple unary operator + + OCLint + must override hash with isEqual + OCLint high ncss method @@ -159,22 +155,6 @@ 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 @@ -207,10 +187,6 @@ OCLint switch statements don't need default when fully covered - - OCLint - switch statements should have default - OCLint throw exception from finally block @@ -251,10 +227,6 @@ OCLint ivar assignment outside accessors or init - - OCLint - covered switch statements dont need default - OCLint class @@ -279,37 +251,37 @@ OCLint missing default in switch statements + + OCLint + use boxed expression + + + OCLint + use container literal + + + OCLint + use number literal + OCLint use object subscripting OCLint - use boxed expression + missing hash method OCLint - use container literal + missing call to base method OCLint - use number literal + calling prohibited method + + + OCLint + missing abstract method implementation - - OCLint - missing hash method - - - OCLint - missing call to base method - - - OCLint - calling prohibited method - - - OCLint - missing abstract method implementation - - + \ No newline at end of file diff --git a/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/rules.txt b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/rules.txt index c430108b..3304844d 100755 --- a/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/rules.txt +++ b/sonar-objective-c-plugin/src/main/resources/org/sonar/plugins/oclint/rules.txt @@ -470,7 +470,7 @@ Category: OCLint unnecessary default statement in covered switch statement ---------- -Summary: When a switch statement covers all possible cases, a default label is not needed and should be removed. If the switch is not fully covered, the SwitchStatementsShouldHaveDefault rule will report. +Summary: Name: unnecessary default statement in covered switch statement Severity: 2 Category: OCLint @@ -478,7 +478,7 @@ Category: OCLint ill-placed default label in switch statement ---------- -Summary: It is very confusing when default label is not the last label in a switch statement. +Summary: Name: ill-placed default label in switch statement Severity: 2 Category: OCLint @@ -486,7 +486,7 @@ Category: OCLint prefer early exits and continue ---------- -Summary: Early exits can reduce the indentation of a block of code, so that reader do not have to remember all the previous decisions, therefore, makes it easier to understand the code. +Summary: Name: prefer early exits and continue Severity: 2 Category: OCLint @@ -494,7 +494,7 @@ Category: OCLint missing default in switch statements ---------- -Summary: Switch statements should have a default statement. +Summary: Name: missing default in switch statements Severity: 2 Category: OCLint @@ -502,7 +502,7 @@ Category: OCLint use boxed expression ---------- -Summary: This rule locates the places that can be migrated to the new Objective-C literals with boxed expressions. +Summary: Name: use boxed expression Severity: 1 Category: OCLint @@ -510,7 +510,7 @@ Category: OCLint use container literal ---------- -Summary: This rule locates the places that can be migrated to the new Objective-C literals with container literals. +Summary: Name: use container literal Severity: 1 Category: OCLint @@ -518,7 +518,7 @@ Category: OCLint use number literal ---------- -Summary: This rule locates the places that can be migrated to the new Objective-C literals with number literals. +Summary: Name: use number literal Severity: 1 Category: OCLint @@ -526,7 +526,7 @@ Category: OCLint use object subscripting ---------- -Summary: Name: use object subscripting +Summary: Severity: 1 Category: OCLint @@ -563,4 +563,3 @@ Summary: Name: missing abstract method implementation Severity: 1 Category: OCLint - From 66ac34eb46d99abb598256a203d20d8fb566d76a Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 6 Dec 2017 09:41:10 +0100 Subject: [PATCH 100/107] Fixed run-sonar.sh conflit --- sonar-objective-c-plugin/src/main/shell/run-sonar.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sonar-objective-c-plugin/src/main/shell/run-sonar.sh b/sonar-objective-c-plugin/src/main/shell/run-sonar.sh index 58ed848d..7dd3d043 100755 --- a/sonar-objective-c-plugin/src/main/shell/run-sonar.sh +++ b/sonar-objective-c-plugin/src/main/shell/run-sonar.sh @@ -54,11 +54,8 @@ function testIsInstalled() { fi } -<<<<<<< HEAD:src/main/shell/run-sonar.sh function testIsXcodeMinMajorVersionAvailable() { -======= -function testIsXcodeMinMajorVersionAvailable() { ->>>>>>> develop:sonar-objective-c-plugin/src/main/shell/run-sonar.sh + XCODE_VERSION="$($XCODEBUILD_CMD -version | grep -a -A 1 "Xcode" | head -n1 | sed "s/Xcode \([0-9]*\)\..*/\1/")" if (( "$1" <= "$XCODE_VERSION" )); then return 0 From a9608305b643feaf3c415d1beb1e9fd8975a596c Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 6 Dec 2017 09:43:41 +0100 Subject: [PATCH 101/107] Fixed second run-script.sh conflict --- .../src/main/shell/run-sonar.sh | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/sonar-objective-c-plugin/src/main/shell/run-sonar.sh b/sonar-objective-c-plugin/src/main/shell/run-sonar.sh index 7dd3d043..a97ac336 100755 --- a/sonar-objective-c-plugin/src/main/shell/run-sonar.sh +++ b/sonar-objective-c-plugin/src/main/shell/run-sonar.sh @@ -306,7 +306,6 @@ else buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360) xcode8BuildForTestingCmd+=(-destination "$destinationSimulator" -destination-timeout 360) xcode8TestCmd+=(-destination "$destinationSimulator" -destination-timeout 360) -<<<<<<< HEAD:src/main/shell/run-sonar.sh fi if testIsXcodeMinMajorVersionAvailable 8 ; then @@ -318,21 +317,7 @@ else echo "Testing" "${buildCmd[@]}" | $XCPRETTY_CMD -t --report junit fi - -======= - fi - - if testIsXcodeMinMajorVersionAvailable 8 ; then - echo "Running build-for-testing" - "${xcode8BuildForTestingCmd[@]}" | $XCPRETTY_CMD - echo "Running test-without-building" - "${xcode8TestCmd[@]}" | $XCPRETTY_CMD -t --report junit - else - echo "Testing" - "${buildCmd[@]}" | $XCPRETTY_CMD -t --report junit - fi ->>>>>>> develop:sonar-objective-c-plugin/src/main/shell/run-sonar.sh mv build/reports/junit.xml sonar-reports/TEST-report.xml echo -n 'Computing coverage report' From 90b516124b813833e9b8542d8a79d804b09a2022 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 6 Dec 2017 09:47:46 +0100 Subject: [PATCH 102/107] Updated README file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cf16241..d7f65828 100755 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Binary packages are available in the release section. ### Release history ### 0.6.2 -- SonarQube 6.5 support +- SonarQube 6.7 support - FauxPas 1.7.2 support (4 new rules added) - OCLint 0.13 support (1 new rule added) - Update run-sonar.sh : xcodebuild optimization (see https://github.com/Backelite/sonar-objective-c/pull/26 thanks to [davidy4ng](https://github.com/davidy4ng)). From 37daaa3ec39738bda6c31da8728f379464581814 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Wed, 6 Dec 2017 11:13:21 +0100 Subject: [PATCH 103/107] Removed JDK under 8 in Travis --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 122387cf..9bcf9994 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,3 @@ language: java jdk: - oraclejdk8 - - oraclejdk7 - - openjdk7 - - openjdk6 \ No newline at end of file From 1e264ed16e2e84a22997facab96592bee765efa1 Mon Sep 17 00:00:00 2001 From: Pavel Machala Date: Thu, 8 Mar 2018 11:19:11 -0800 Subject: [PATCH 104/107] PersistanceMode dropped by sonarqube 7.0 --- .../plugins/objectivec/complexity/LizardReportParser.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java index efc35d1d..eb7e2a71 100644 --- a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java @@ -156,7 +156,7 @@ private List buildMeasureList(int complexity, double fileComplexity, in list.add(new Measure(CoreMetrics.FILE_COMPLEXITY, fileComplexity)); RangeDistributionBuilder complexityDistribution = new RangeDistributionBuilder(CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION, FILES_DISTRIB_BOTTOM_LIMITS); complexityDistribution.add(fileComplexity); - list.add(complexityDistribution.build().setPersistenceMode(PersistenceMode.MEMORY)); + list.add(complexityDistribution.build()); return list; } @@ -224,7 +224,7 @@ public List buildFunctionMeasuresList(double complexMean, int complexit List list = new ArrayList(); list.add(new Measure(CoreMetrics.FUNCTION_COMPLEXITY, complexMean)); list.add(new Measure(CoreMetrics.COMPLEXITY_IN_FUNCTIONS).setIntValue(complexityInFunctions)); - list.add(builder.build().setPersistenceMode(PersistenceMode.MEMORY)); + list.add(builder.build()); return list; } @@ -249,4 +249,4 @@ public int getCyclomaticComplexity() { } } -} \ No newline at end of file +} From 96d4fdd2284122f3b9019d0d02b81ae3793d0d55 Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 23 Aug 2018 11:56:33 +0200 Subject: [PATCH 105/107] SonarQube 7.0 support --- README.md | 3 +++ pom.xml | 2 +- sonar-objective-c-plugin/pom.xml | 2 +- .../plugins/objectivec/complexity/LizardReportParser.java | 2 -- .../plugins/objectivec/complexity/LizardReportParserTest.java | 4 ++-- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d7f65828..ce0656a9 100755 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ Binary packages are available in the release section. ### Release history +### 0.6.3 +- SonarQube 7.0 support + ### 0.6.2 - SonarQube 6.7 support - FauxPas 1.7.2 support (4 new rules added) diff --git a/pom.xml b/pom.xml index f36cad37..a7ff4153 100755 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ com.backelite.sonarqube backelite-objective-c - 0.6.2 + 0.6.3 pom diff --git a/sonar-objective-c-plugin/pom.xml b/sonar-objective-c-plugin/pom.xml index be48d0aa..5064e5a1 100644 --- a/sonar-objective-c-plugin/pom.xml +++ b/sonar-objective-c-plugin/pom.xml @@ -25,7 +25,7 @@ com.backelite.sonarqube backelite-objective-c - 0.6.2 + 0.6.3 4.0.0 diff --git a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java index efc35d1d..07b40d22 100644 --- a/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java +++ b/sonar-objective-c-plugin/src/main/java/org/sonar/plugins/objectivec/complexity/LizardReportParser.java @@ -156,7 +156,6 @@ private List buildMeasureList(int complexity, double fileComplexity, in list.add(new Measure(CoreMetrics.FILE_COMPLEXITY, fileComplexity)); RangeDistributionBuilder complexityDistribution = new RangeDistributionBuilder(CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION, FILES_DISTRIB_BOTTOM_LIMITS); complexityDistribution.add(fileComplexity); - list.add(complexityDistribution.build().setPersistenceMode(PersistenceMode.MEMORY)); return list; } @@ -224,7 +223,6 @@ public List buildFunctionMeasuresList(double complexMean, int complexit List list = new ArrayList(); list.add(new Measure(CoreMetrics.FUNCTION_COMPLEXITY, complexMean)); list.add(new Measure(CoreMetrics.COMPLEXITY_IN_FUNCTIONS).setIntValue(complexityInFunctions)); - list.add(builder.build().setPersistenceMode(PersistenceMode.MEMORY)); return list; } diff --git a/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java b/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java index a832b4ab..1efec585 100644 --- a/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java +++ b/sonar-objective-c-plugin/src/test/java/org/sonar/plugins/objectivec/complexity/LizardReportParserTest.java @@ -168,7 +168,7 @@ public void parseReportShouldReturnMapWhenXMLFileIsCorrect() { Assert.assertTrue("Key is not there", report.containsKey("App/Controller/Accelerate/AccelerationViewController.h")); List list1 = report.get("App/Controller/Accelerate/AccelerationViewController.h"); - Assert.assertEquals(4, list1.size()); + Assert.assertEquals(3, list1.size()); for (Measure measure : list1) { String s = measure.getMetric().getKey(); @@ -189,7 +189,7 @@ public void parseReportShouldReturnMapWhenXMLFileIsCorrect() { Assert.assertTrue("Key is not there", report.containsKey("App/Controller/Accelerate/AccelerationViewController.m")); List list2 = report.get("App/Controller/Accelerate/AccelerationViewController.m"); - Assert.assertEquals(7, list2.size()); + Assert.assertEquals(5, list2.size()); for (Measure measure : list2) { String s = measure.getMetric().getKey(); From 4e8e7157acf87c90fa4a69f6fe4665db6dc0bd8f Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Thu, 23 Aug 2018 12:11:03 +0200 Subject: [PATCH 106/107] Fixed release number --- sonar-objective-c-plugin/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-objective-c-plugin/pom.xml b/sonar-objective-c-plugin/pom.xml index 5064e5a1..d9fb4ef6 100644 --- a/sonar-objective-c-plugin/pom.xml +++ b/sonar-objective-c-plugin/pom.xml @@ -31,7 +31,7 @@ com.backelite.sonarqube backelite-sonar-objective-c-plugin - 0.6.2 + 0.6.3 sonar-plugin From 57539f121d268904a27bbbe81b8eb002fd4b736e Mon Sep 17 00:00:00 2001 From: Gilles Grousset Date: Tue, 28 Aug 2018 11:48:46 +0200 Subject: [PATCH 107/107] README.md : maintenance stopped --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ce0656a9..f48319ae 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# Important : this plugin is not maintained anymore. Please switch to our (Swift plugin)[https://github.com/Backelite/sonar-swift] which now supports Swift and Objective-C + + +