aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine/src/test/java
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2016-03-17 17:05:56 +0100
committerJulien HENRY <julien.henry@sonarsource.com>2016-03-18 09:35:05 +0100
commitcfcbe278f7ced12599d898d50f3fe68bfbf95155 (patch)
tree5b4116ad08a8ba87ffc5bf9f159a431b9609b48f /sonar-scanner-engine/src/test/java
parent38ce80934961773a9a35ec0401994b3d726597dc (diff)
downloadsonarqube-cfcbe278f7ced12599d898d50f3fe68bfbf95155.tar.gz
sonarqube-cfcbe278f7ced12599d898d50f3fe68bfbf95155.zip
Rename batch into scanner
Diffstat (limited to 'sonar-scanner-engine/src/test/java')
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java148
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/ProjectConfiguratorTest.java130
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisTempFolderProviderTest.java66
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java62
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/DefaultAnalysisModeTest.java141
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java412
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java86
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginJarExploderTest.java80
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java103
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java76
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientProviderTest.java76
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java116
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/DroppedPropertyCheckerTest.java62
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java136
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java88
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/FileCacheProviderTest.java65
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalContainerTest.java76
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java89
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalPropertiesTest.java43
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java79
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalTempFolderProviderTest.java142
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java55
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java118
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java73
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/EnvironmentInformationTest.java41
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java76
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java167
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java184
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java91
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cache/GlobalPersistentCacheProviderTest.java83
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java93
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java197
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectPersistentCacheProviderTest.java80
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java52
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java59
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cache/WSLoaderTest.java264
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java32
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdExecutorTest.java239
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java80
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java80
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DuplicationPredicatesTest.java38
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java106
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/deprecated/perspectives/PerspectiveBuilderTest.java44
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/events/BatchStepEventTest.java45
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/events/EventBusTest.java77
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/index/AbstractCachesTest.java77
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/index/BatchComponentCacheTest.java53
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/index/BucketTest.java58
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/index/CacheTest.java250
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesManagerTest.java42
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesTest.java89
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/index/DefaultIndexTest.java163
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultFilterableIssueTest.java86
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java137
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java95
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java78
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueAdapterForFilterTest.java157
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueFilterChainTest.java96
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java57
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssueCacheTest.java98
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java221
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/TrackedIssueAdapterTest.java84
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/EnforceIssuesFilterTest.java149
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/IgnoreIssuesFilterTest.java67
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java141
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueInclusionPatternInitializerTest.java70
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssuePatternTest.java114
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/LineRangeTest.java72
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternDecoderTest.java187
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternMatcherTest.java119
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java157
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java168
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java89
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/RollingFileHashesTest.java43
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/SourceHashHolderTest.java119
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/TrackedIssueTest.java47
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java497
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/LogOutputRecorder.java54
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java155
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java180
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java336
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java134
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java296
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/NoLanguagesPluginsMediumTest.java76
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java168
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java133
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java127
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesIssuesModeMediumTest.java100
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java187
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java113
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnModuleMediumTest.java84
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java131
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java94
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java316
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NoPreviousAnalysisTest.java86
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java89
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java212
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java116
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java216
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java131
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java363
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java116
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tasks/TasksMediumTest.java160
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/CoveragePerTestMediumTest.java160
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/TestExecutionMediumTest.java105
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java60
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/platform/DefaultServerTest.java51
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java87
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/PostJobOptimizerTest.java77
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java410
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/report/ActiveRulesPublisherTest.java70
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java207
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java170
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java116
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java113
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java100
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/report/ReportPublisherTest.java164
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/report/SourcePublisherTest.java119
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoaderTest.java78
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java144
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java116
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java82
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/repository/ProjectRepositoriesProviderTest.java115
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java165
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/repository/user/UserRepositoryLoaderTest.java119
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java97
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java85
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultRulesLoaderTest.java79
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java135
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java101
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RuleFinderCompatibilityTest.java101
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java86
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProviderTest.java66
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java72
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java99
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java162
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java122
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectLockTest.java103
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java683
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java185
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java63
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java142
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/WorkDirectoryCleanerTest.java79
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java45
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java141
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java200
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java77
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java134
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java49
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java118
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java82
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java41
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java213
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializerTest.java92
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java35
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java52
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java231
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java145
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java174
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/RuleNameProviderTest.java67
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/scm/DefaultBlameOutputTest.java94
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java79
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java140
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/SensorOptimizerTest.java136
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/coverage/CoverageExclusionsTest.java143
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/source/CodeColorizersTest.java230
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java55
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java100
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java66
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java58
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java59
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/task/ListTaskTest.java63
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/task/TasksTest.java90
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/util/BatchUtilsTest.java51
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/util/ProgressReportTest.java90
175 files changed, 21341 insertions, 0 deletions
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java
new file mode 100644
index 00000000000..eb622b003fa
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java
@@ -0,0 +1,148 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Matchers;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.PersistenceMode;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+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;
+
+public class DefaultFileLinesContextTest {
+
+ private SonarIndex index;
+ private Resource resource;
+ private DefaultFileLinesContext fileLineMeasures;
+
+ @Before
+ public void setUp() {
+ index = mock(SonarIndex.class);
+ resource = mock(Resource.class);
+ when(resource.getScope()).thenReturn(Scopes.FILE);
+ fileLineMeasures = new DefaultFileLinesContext(index, resource);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldNotAllowCreationForDirectory() {
+ new DefaultFileLinesContext(index, Directory.create("key"));
+ }
+
+ @Test
+ public void shouldSave() {
+ fileLineMeasures.setIntValue("hits", 1, 2);
+ fileLineMeasures.setIntValue("hits", 3, 4);
+ fileLineMeasures.save();
+
+ assertThat(fileLineMeasures.toString()).isEqualTo("DefaultFileLinesContext{map={hits={1=2, 3=4}}}");
+
+ ArgumentCaptor<Measure> measureCaptor = ArgumentCaptor.forClass(Measure.class);
+ verify(index).addMeasure(Matchers.eq(resource), measureCaptor.capture());
+ Measure measure = measureCaptor.getValue();
+ assertThat(measure.getMetricKey(), is("hits"));
+ assertThat(measure.getPersistenceMode(), is(PersistenceMode.DATABASE));
+ assertThat(measure.getData(), is("1=2;3=4"));
+ }
+
+ @Test
+ public void shouldSaveSeveral() {
+ fileLineMeasures.setIntValue("hits", 1, 2);
+ fileLineMeasures.setIntValue("hits", 3, 4);
+ fileLineMeasures.setStringValue("author", 1, "simon");
+ fileLineMeasures.setStringValue("author", 3, "evgeny");
+ fileLineMeasures.save();
+ fileLineMeasures.setIntValue("branches", 1, 2);
+ fileLineMeasures.setIntValue("branches", 3, 4);
+ fileLineMeasures.save();
+
+ verify(index, times(3)).addMeasure(Matchers.eq(resource), Matchers.any(Measure.class));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void shouldNotModifyAfterSave() {
+ fileLineMeasures.setIntValue("hits", 1, 2);
+ fileLineMeasures.save();
+ fileLineMeasures.save();
+ verify(index).addMeasure(Matchers.eq(resource), Matchers.any(Measure.class));
+ fileLineMeasures.setIntValue("hits", 1, 2);
+ }
+
+ @Test
+ public void shouldLoadIntValues() {
+ when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class)))
+ .thenReturn(new Measure("hits").setData("1=2;3=4"));
+
+ assertThat(fileLineMeasures.getIntValue("hits", 1), is(2));
+ assertThat(fileLineMeasures.getIntValue("hits", 3), is(4));
+ assertThat("no measure on line", fileLineMeasures.getIntValue("hits", 5), nullValue());
+ }
+
+ @Test
+ public void shouldLoadStringValues() {
+ when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class)))
+ .thenReturn(new Measure("author").setData("1=simon;3=evgeny"));
+
+ assertThat(fileLineMeasures.getStringValue("author", 1), is("simon"));
+ assertThat(fileLineMeasures.getStringValue("author", 3), is("evgeny"));
+ assertThat("no measure on line", fileLineMeasures.getStringValue("author", 5), nullValue());
+ }
+
+ @Test
+ public void shouldNotSaveAfterLoad() {
+ when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class)))
+ .thenReturn(new Measure("author").setData("1=simon;3=evgeny"));
+
+ fileLineMeasures.getStringValue("author", 1);
+ fileLineMeasures.save();
+
+ verify(index, never()).addMeasure(Matchers.eq(resource), Matchers.any(Measure.class));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void shouldNotModifyAfterLoad() {
+ when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class)))
+ .thenReturn(new Measure("author").setData("1=simon;3=evgeny"));
+
+ fileLineMeasures.getStringValue("author", 1);
+ fileLineMeasures.setStringValue("author", 1, "evgeny");
+ }
+
+ @Test
+ public void shouldNotFailIfNoMeasureInIndex() {
+ assertThat(fileLineMeasures.getIntValue("hits", 1), nullValue());
+ assertThat(fileLineMeasures.getStringValue("author", 1), nullValue());
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/ProjectConfiguratorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/ProjectConfiguratorTest.java
new file mode 100644
index 00000000000..6a30188454c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/ProjectConfiguratorTest.java
@@ -0,0 +1,130 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch;
+
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.System2;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ProjectConfiguratorTest {
+
+ System2 system2;
+
+ @Before
+ public void setUp() {
+ system2 = mock(System2.class);
+ }
+
+ @Test
+ public void analysis_is_today_by_default() {
+ Long now = System.currentTimeMillis();
+ when(system2.now()).thenReturn(now);
+
+ Project project = new Project("key");
+ new ProjectConfigurator(new Settings(), system2).configure(project);
+ assertThat(now - project.getAnalysisDate().getTime()).isLessThan(1000);
+ }
+
+ @Test
+ public void analysis_date_could_be_explicitly_set() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2005-01-30");
+ Project project = new Project("key");
+ new ProjectConfigurator(settings, system2).configure(project);
+
+ assertThat(new SimpleDateFormat("ddMMyyyy").format(project.getAnalysisDate())).isEqualTo("30012005");
+ }
+
+ @Test
+ public void analysis_timestamp_could_be_explicitly_set() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2005-01-30T08:45:10+0000");
+ Project project = new Project("key");
+ new ProjectConfigurator(settings, system2).configure(project);
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat("ddMMyyyy-mmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ assertThat(dateFormat.format(project.getAnalysisDate())).isEqualTo("30012005-4510");
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void fail_if_analyis_date_is_not_valid() {
+ Settings configuration = new Settings();
+ configuration.setProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2005/30/01");
+ Project project = new Project("key");
+ new ProjectConfigurator(configuration, system2).configure(project);
+ }
+
+ @Test
+ public void default_analysis_type_is_dynamic() {
+ Project project = new Project("key");
+ new ProjectConfigurator(new Settings(), system2).configure(project);
+ assertThat(project.getAnalysisType()).isEqualTo(Project.AnalysisType.DYNAMIC);
+ }
+
+ @Test
+ public void explicit_dynamic_analysis() {
+ Settings configuration = new Settings();
+ configuration.setProperty(CoreProperties.DYNAMIC_ANALYSIS_PROPERTY, "true");
+ Project project = new Project("key");
+ new ProjectConfigurator(configuration, system2).configure(project);
+ assertThat(project.getAnalysisType()).isEqualTo(Project.AnalysisType.DYNAMIC);
+ }
+
+ @Test
+ public void explicit_static_analysis() {
+ Settings configuration = new Settings();
+ configuration.setProperty(CoreProperties.DYNAMIC_ANALYSIS_PROPERTY, "false");
+ Project project = new Project("key");
+ new ProjectConfigurator(configuration, system2).configure(project);
+ assertThat(project.getAnalysisType()).isEqualTo(Project.AnalysisType.STATIC);
+ }
+
+ @Test
+ public void explicit_dynamic_analysis_reusing_reports() {
+ Settings configuration = new Settings();
+ configuration.setProperty(CoreProperties.DYNAMIC_ANALYSIS_PROPERTY, "reuseReports");
+ Project project = new Project("key");
+ new ProjectConfigurator(configuration, system2).configure(project);
+ assertThat(project.getAnalysisType()).isEqualTo(Project.AnalysisType.REUSE_REPORTS);
+ }
+
+ @Test
+ public void is_dynamic_analysis() {
+ assertThat(Project.AnalysisType.DYNAMIC.isDynamic(false)).isTrue();
+ assertThat(Project.AnalysisType.DYNAMIC.isDynamic(true)).isTrue();
+
+ assertThat(Project.AnalysisType.STATIC.isDynamic(false)).isFalse();
+ assertThat(Project.AnalysisType.STATIC.isDynamic(true)).isFalse();
+
+ assertThat(Project.AnalysisType.REUSE_REPORTS.isDynamic(false)).isFalse();
+ assertThat(Project.AnalysisType.REUSE_REPORTS.isDynamic(true)).isTrue();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisTempFolderProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisTempFolderProviderTest.java
new file mode 100644
index 00000000000..058ea4fd34c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisTempFolderProviderTest.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.analysis;
+
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.junit.Before;
+import org.sonar.batch.analysis.AnalysisTempFolderProvider;
+import org.sonar.api.utils.TempFolder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mock;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AnalysisTempFolderProviderTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private AnalysisTempFolderProvider tempFolderProvider;
+ private ProjectReactor projectReactor;
+
+ @Before
+ public void setUp() {
+ tempFolderProvider = new AnalysisTempFolderProvider();
+ projectReactor = mock(ProjectReactor.class);
+ ProjectDefinition projectDefinition = mock(ProjectDefinition.class);
+ when(projectReactor.getRoot()).thenReturn(projectDefinition);
+ when(projectDefinition.getWorkDir()).thenReturn(temp.getRoot());
+ }
+
+ @Test
+ public void createTempFolder() throws IOException {
+ File defaultDir = new File(temp.getRoot(), AnalysisTempFolderProvider.TMP_NAME);
+
+ TempFolder tempFolder = tempFolderProvider.provide(projectReactor);
+ tempFolder.newDir();
+ tempFolder.newFile();
+ assertThat(defaultDir).exists();
+ assertThat(defaultDir.list()).hasSize(2);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java
new file mode 100644
index 00000000000..66ab296a063
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.analysis;
+
+import com.google.common.collect.ImmutableMap;
+import org.assertj.core.util.Maps;
+import org.junit.Test;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.batch.bootstrap.BatchWsClient;
+import org.sonar.batch.cache.WSLoader;
+import org.sonar.batch.cache.WSLoader.LoadStrategy;
+import org.sonar.home.cache.PersistentCache;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AnalysisWSLoaderProviderTest {
+
+ PersistentCache cache = mock(PersistentCache.class);
+ BatchWsClient wsClient = mock(BatchWsClient.class);
+ AnalysisMode mode = mock(AnalysisMode.class);
+
+ AnalysisWSLoaderProvider underTest = new AnalysisWSLoaderProvider();
+
+ @Test
+ public void testDefault() {
+ WSLoader loader = underTest.provide(mode, cache, wsClient, new AnalysisProperties(Maps.<String, String>newHashMap()));
+ assertThat(loader.getDefaultStrategy()).isEqualTo(LoadStrategy.SERVER_ONLY);
+ }
+
+ @Test
+ public void no_cache_by_default_in_issues_mode() {
+ when(mode.isIssues()).thenReturn(true);
+ WSLoader loader = underTest.provide(mode, cache, wsClient, new AnalysisProperties(Maps.<String, String>newHashMap()));
+ assertThat(loader.getDefaultStrategy()).isEqualTo(LoadStrategy.SERVER_ONLY);
+ }
+
+ @Test
+ public void enable_cache_in_issues_mode() {
+ when(mode.isIssues()).thenReturn(true);
+ WSLoader loader = underTest.provide(mode, cache, wsClient, new AnalysisProperties(ImmutableMap.of(AnalysisWSLoaderProvider.SONAR_USE_WS_CACHE, "true")));
+ assertThat(loader.getDefaultStrategy()).isEqualTo(LoadStrategy.CACHE_ONLY);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/DefaultAnalysisModeTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/DefaultAnalysisModeTest.java
new file mode 100644
index 00000000000..67734ed62fa
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/analysis/DefaultAnalysisModeTest.java
@@ -0,0 +1,141 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.analysis;
+
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.analysis.AnalysisProperties;
+
+import javax.annotation.Nullable;
+
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DefaultAnalysisModeTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void regular_analysis_by_default() {
+ DefaultAnalysisMode mode = createMode(null, null);
+ assertThat(mode.isPreview()).isFalse();
+ assertThat(mode.isPublish()).isTrue();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void fail_if_inconsistent() {
+ createMode(null, CoreProperties.ANALYSIS_MODE_ISSUES);
+ }
+
+ @Test
+ public void support_publish_mode() {
+ DefaultAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE_PUBLISH);
+
+ assertThat(mode.isPreview()).isFalse();
+ assertThat(mode.isPublish()).isTrue();
+ }
+
+ @Test
+ public void incremental_mode_no_longer_valid() {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("This mode was removed in SonarQube 5.2");
+
+ createMode(CoreProperties.ANALYSIS_MODE_INCREMENTAL);
+ }
+
+ @Test
+ public void invalidate_mode() {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("[preview, publish, issues]");
+
+ createMode("invalid");
+ }
+
+ @Test
+ public void preview_mode_fallback_issues() {
+ DefaultAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE_PREVIEW);
+
+ assertThat(mode.isIssues()).isTrue();
+ assertThat(mode.isPreview()).isFalse();
+ }
+
+ @Test
+ public void scan_all() {
+ Map<String, String> props = new HashMap<>();
+ props.put(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES);
+ GlobalProperties globalProps = new GlobalProperties(props);
+
+ AnalysisProperties analysisProps = new AnalysisProperties(new HashMap<String, String>());
+ DefaultAnalysisMode mode = new DefaultAnalysisMode(globalProps, analysisProps);
+ assertThat(mode.scanAllFiles()).isFalse();
+
+ props.put("sonar.scanAllFiles", "true");
+ analysisProps = new AnalysisProperties(props);
+
+ mode = new DefaultAnalysisMode(globalProps, analysisProps);
+ assertThat(mode.scanAllFiles()).isTrue();
+
+ props.put(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PUBLISH);
+ analysisProps = new AnalysisProperties(props);
+
+ mode = new DefaultAnalysisMode(globalProps, analysisProps);
+ assertThat(mode.scanAllFiles()).isTrue();
+ }
+
+ @Test
+ public void default_publish_mode() {
+ DefaultAnalysisMode mode = createMode(null);
+ assertThat(mode.isPublish()).isTrue();
+ assertThat(mode.scanAllFiles()).isTrue();
+ }
+
+ @Test
+ public void support_issues_mode() {
+ DefaultAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE_ISSUES);
+
+ assertThat(mode.isIssues()).isTrue();
+ assertThat(mode.scanAllFiles()).isFalse();
+ }
+
+ private static DefaultAnalysisMode createMode(@Nullable String mode) {
+ return createMode(mode, mode);
+ }
+
+ private static DefaultAnalysisMode createMode(@Nullable String bootstrapMode, @Nullable String analysisMode) {
+ Map<String, String> bootstrapMap = new HashMap<>();
+ Map<String, String> analysisMap = new HashMap<>();
+
+ if (bootstrapMode != null) {
+ bootstrapMap.put(CoreProperties.ANALYSIS_MODE, bootstrapMode);
+ }
+ if (analysisMode != null) {
+ analysisMap.put(CoreProperties.ANALYSIS_MODE, analysisMode);
+ }
+ return new DefaultAnalysisMode(new GlobalProperties(bootstrapMap), new AnalysisProperties(analysisMap));
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java
new file mode 100644
index 00000000000..77a0021ef31
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java
@@ -0,0 +1,412 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import com.google.common.collect.Lists;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.batch.BuildBreaker;
+import org.sonar.api.batch.CheckProject;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.batch.PostJob;
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.postjob.PostJobContext;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.postjob.PostJobOptimizer;
+import org.sonar.batch.sensor.DefaultSensorContext;
+import org.sonar.batch.sensor.SensorOptimizer;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class BatchExtensionDictionnaryTest {
+
+ private BatchExtensionDictionnary newSelector(Object... extensions) {
+ ComponentContainer iocContainer = new ComponentContainer();
+ for (Object extension : extensions) {
+ iocContainer.addSingleton(extension);
+ }
+ return new BatchExtensionDictionnary(iocContainer, mock(DefaultSensorContext.class), mock(SensorOptimizer.class), mock(PostJobContext.class),
+ mock(PostJobOptimizer.class));
+ }
+
+ @Test
+ public void testGetFilteredExtensionWithExtensionMatcher() {
+ final Sensor sensor1 = new FakeSensor();
+ final Sensor sensor2 = new FakeSensor();
+
+ BatchExtensionDictionnary selector = newSelector(sensor1, sensor2);
+ Collection<Sensor> sensors = selector.select(Sensor.class, null, true, new ExtensionMatcher() {
+ @Override
+ public boolean accept(Object extension) {
+ return extension.equals(sensor1);
+ }
+ });
+
+ assertThat(sensors).contains(sensor1);
+ assertEquals(1, sensors.size());
+ }
+
+ @Test
+ public void testGetFilteredExtensions() {
+ Sensor sensor1 = new FakeSensor();
+ Sensor sensor2 = new FakeSensor();
+ Decorator decorator = mock(Decorator.class);
+
+ BatchExtensionDictionnary selector = newSelector(sensor1, sensor2, decorator);
+ Collection<Sensor> sensors = selector.select(Sensor.class, null, true, null);
+
+ assertThat(sensors).containsOnly(sensor1, sensor2);
+ }
+
+ @Test
+ public void shouldSearchInParentContainers() {
+ Sensor a = new FakeSensor();
+ Sensor b = new FakeSensor();
+ Sensor c = new FakeSensor();
+
+ ComponentContainer grandParent = new ComponentContainer();
+ grandParent.addSingleton(a);
+
+ ComponentContainer parent = grandParent.createChild();
+ parent.addSingleton(b);
+
+ ComponentContainer child = parent.createChild();
+ child.addSingleton(c);
+
+ BatchExtensionDictionnary dictionnary = new BatchExtensionDictionnary(child, mock(DefaultSensorContext.class), mock(SensorOptimizer.class), mock(PostJobContext.class),
+ mock(PostJobOptimizer.class));
+ assertThat(dictionnary.select(Sensor.class, null, true, null)).containsOnly(a, b, c);
+ }
+
+ @Test
+ public void sortExtensionsByDependency() {
+ BatchExtension a = new MethodDependentOf(null);
+ BatchExtension b = new MethodDependentOf(a);
+ BatchExtension c = new MethodDependentOf(b);
+
+ BatchExtensionDictionnary selector = newSelector(b, c, a);
+ List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(3);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+ assertThat(extensions.get(2)).isEqualTo(c);
+ }
+
+ @Test
+ public void useMethodAnnotationsToSortExtensions() {
+ BatchExtension a = new GeneratesSomething("foo");
+ BatchExtension b = new MethodDependentOf("foo");
+
+ BatchExtensionDictionnary selector = newSelector(a, b);
+ List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions.size()).isEqualTo(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+
+ // different initial order
+ selector = newSelector(b, a);
+ extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+ }
+
+ @Test
+ public void methodDependsUponCollection() {
+ BatchExtension a = new GeneratesSomething("foo");
+ BatchExtension b = new MethodDependentOf(Arrays.asList("foo"));
+
+ BatchExtensionDictionnary selector = newSelector(a, b);
+ List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+
+ // different initial order
+ selector = newSelector(b, a);
+ extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+ }
+
+ @Test
+ public void methodDependsUponArray() {
+ BatchExtension a = new GeneratesSomething("foo");
+ BatchExtension b = new MethodDependentOf(new String[] {"foo"});
+
+ BatchExtensionDictionnary selector = newSelector(a, b);
+ List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+
+ // different initial order
+ selector = newSelector(b, a);
+ extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+ }
+
+ @Test
+ public void useClassAnnotationsToSortExtensions() {
+ BatchExtension a = new ClassDependedUpon();
+ BatchExtension b = new ClassDependsUpon();
+
+ BatchExtensionDictionnary selector = newSelector(a, b);
+ List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+
+ // different initial order
+ selector = newSelector(b, a);
+ extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+ }
+
+ @Test
+ public void useClassAnnotationsOnInterfaces() {
+ BatchExtension a = new InterfaceDependedUpon() {
+ };
+ BatchExtension b = new InterfaceDependsUpon() {
+ };
+
+ BatchExtensionDictionnary selector = newSelector(a, b);
+ List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+
+ // different initial order
+ selector = newSelector(b, a);
+ extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+ }
+
+ @Test
+ public void checkProject() {
+ BatchExtension ok = new CheckProjectOK();
+ BatchExtension ko = new CheckProjectKO();
+
+ BatchExtensionDictionnary selector = newSelector(ok, ko);
+ List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, new Project("key"), true, null));
+
+ assertThat(extensions).hasSize(1);
+ assertThat(extensions.get(0)).isInstanceOf(CheckProjectOK.class);
+ }
+
+ @Test
+ public void inheritAnnotations() {
+ BatchExtension a = new SubClass("foo");
+ BatchExtension b = new MethodDependentOf("foo");
+
+ BatchExtensionDictionnary selector = newSelector(b, a);
+ List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+
+ // change initial order
+ selector = newSelector(a, b);
+ extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(2);
+ assertThat(extensions.get(0)).isEqualTo(a);
+ assertThat(extensions.get(1)).isEqualTo(b);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void annotatedMethodsCanNotBePrivate() {
+ BatchExtensionDictionnary selector = newSelector();
+ BatchExtension wrong = new BatchExtension() {
+ @DependsUpon
+ private Object foo() {
+ return "foo";
+ }
+ };
+ selector.evaluateAnnotatedClasses(wrong, DependsUpon.class);
+ }
+
+ @Test
+ public void dependsUponPhase() {
+ BatchExtension pre = new PreSensor();
+ BatchExtension analyze = new GeneratesSomething("something");
+ BatchExtension post = new PostSensor();
+
+ BatchExtensionDictionnary selector = newSelector(analyze, post, pre);
+ List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(3);
+ assertThat(extensions.get(0)).isEqualTo(pre);
+ assertThat(extensions.get(1)).isEqualTo(analyze);
+ assertThat(extensions.get(2)).isEqualTo(post);
+ }
+
+ @Test
+ public void dependsUponInheritedPhase() {
+ BatchExtension pre = new PreSensorSubclass();
+ BatchExtension analyze = new GeneratesSomething("something");
+ BatchExtension post = new PostSensorSubclass();
+
+ BatchExtensionDictionnary selector = newSelector(analyze, post, pre);
+ List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
+
+ assertThat(extensions).hasSize(3);
+ assertThat(extensions.get(0)).isEqualTo(pre);
+ assertThat(extensions.get(1)).isEqualTo(analyze);
+ assertThat(extensions.get(2)).isEqualTo(post);
+ }
+
+ @Test
+ public void buildStatusCheckersAreExecutedAfterOtherPostJobs() {
+ BuildBreaker checker = new BuildBreaker() {
+ public void executeOn(Project project, SensorContext context) {
+ }
+ };
+
+ BatchExtensionDictionnary selector = newSelector(new FakePostJob(), checker, new FakePostJob());
+ List extensions = Lists.newArrayList(selector.select(PostJob.class, null, true, null));
+
+ assertThat(extensions).hasSize(3);
+ assertThat(extensions.get(2)).isEqualTo(checker);
+ }
+
+ class FakeSensor implements Sensor {
+
+ public void analyse(Project project, SensorContext context) {
+
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+ }
+
+ class MethodDependentOf implements BatchExtension {
+ private Object dep;
+
+ MethodDependentOf(Object o) {
+ this.dep = o;
+ }
+
+ @DependsUpon
+ public Object dependsUponObject() {
+ return dep;
+ }
+ }
+
+ @DependsUpon("flag")
+ class ClassDependsUpon implements BatchExtension {
+ }
+
+ @DependedUpon("flag")
+ class ClassDependedUpon implements BatchExtension {
+ }
+
+ @DependsUpon("flag")
+ interface InterfaceDependsUpon extends BatchExtension {
+ }
+
+ @DependedUpon("flag")
+ interface InterfaceDependedUpon extends BatchExtension {
+ }
+
+ class GeneratesSomething implements BatchExtension {
+ private Object gen;
+
+ GeneratesSomething(Object o) {
+ this.gen = o;
+ }
+
+ @DependedUpon
+ public Object generates() {
+ return gen;
+ }
+ }
+
+ class SubClass extends GeneratesSomething {
+ SubClass(Object o) {
+ super(o);
+ }
+ }
+
+ @Phase(name = Phase.Name.PRE)
+ class PreSensor implements BatchExtension {
+
+ }
+
+ class PreSensorSubclass extends PreSensor {
+
+ }
+
+ @Phase(name = Phase.Name.POST)
+ class PostSensor implements BatchExtension {
+
+ }
+
+ class PostSensorSubclass extends PostSensor {
+
+ }
+
+ class CheckProjectOK implements BatchExtension, CheckProject {
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+ }
+
+ class CheckProjectKO implements BatchExtension, CheckProject {
+ public boolean shouldExecuteOnProject(Project project) {
+ return false;
+ }
+ }
+
+ private class FakePostJob implements PostJob {
+ public void executeOn(Project project, SensorContext context) {
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java
new file mode 100644
index 00000000000..95e17ca849a
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import java.io.File;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.cache.WSLoader;
+import org.sonar.batch.cache.WSLoaderResult;
+import org.sonar.core.platform.RemotePlugin;
+import org.sonar.home.cache.FileCache;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BatchPluginInstallerTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ FileCache fileCache = mock(FileCache.class);
+ BatchWsClient wsClient = mock(BatchWsClient.class);
+ BatchPluginPredicate pluginPredicate = mock(BatchPluginPredicate.class);
+
+ @Test
+ public void listRemotePlugins() {
+
+ WSLoader wsLoader = mock(WSLoader.class);
+ when(wsLoader.loadString("/deploy/plugins/index.txt")).thenReturn(new WSLoaderResult<>("checkstyle\nsqale", true));
+ BatchPluginInstaller underTest = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate);
+
+ List<RemotePlugin> remotePlugins = underTest.listRemotePlugins();
+ assertThat(remotePlugins).extracting("key").containsOnly("checkstyle", "sqale");
+ }
+
+ @Test
+ public void should_download_plugin() throws Exception {
+ File pluginJar = temp.newFile();
+ when(fileCache.get(eq("checkstyle-plugin.jar"), eq("fakemd5_1"), any(FileCache.Downloader.class))).thenReturn(pluginJar);
+
+ WSLoader wsLoader = mock(WSLoader.class);
+ BatchPluginInstaller underTest = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate);
+
+ RemotePlugin remote = new RemotePlugin("checkstyle").setFile("checkstyle-plugin.jar", "fakemd5_1");
+ File file = underTest.download(remote);
+
+ assertThat(file).isEqualTo(pluginJar);
+ }
+
+ @Test
+ public void should_fail_to_get_plugin_index() {
+ thrown.expect(IllegalStateException.class);
+
+ WSLoader wsLoader = mock(WSLoader.class);
+ doThrow(new IllegalStateException()).when(wsLoader).loadString("/deploy/plugins/index.txt");
+
+ new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate).installRemotes();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginJarExploderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginJarExploderTest.java
new file mode 100644
index 00000000000..fe991f5d406
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginJarExploderTest.java
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.core.platform.ExplodedPlugin;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.home.cache.FileCache;
+import org.sonar.home.cache.FileCacheBuilder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BatchPluginJarExploderTest {
+
+ @ClassRule
+ public static TemporaryFolder temp = new TemporaryFolder();
+
+ File userHome;
+ BatchPluginJarExploder underTest;
+
+ @Before
+ public void setUp() throws IOException {
+ userHome = temp.newFolder();
+ FileCache fileCache = new FileCacheBuilder(new Slf4jLogger()).setUserHome(userHome).build();
+ underTest = new BatchPluginJarExploder(fileCache);
+ }
+
+ @Test
+ public void copy_and_extract_libs() throws IOException {
+ File fileFromCache = getFileFromCache("sonar-checkstyle-plugin-2.8.jar");
+ ExplodedPlugin exploded = underTest.explode(PluginInfo.create(fileFromCache));
+
+ assertThat(exploded.getKey()).isEqualTo("checkstyle");
+ assertThat(exploded.getMain()).isFile().exists();
+ assertThat(exploded.getLibs()).extracting("name").containsOnly("antlr-2.7.6.jar", "checkstyle-5.1.jar", "commons-cli-1.0.jar");
+ assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar")).exists();
+ assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/META-INF/lib/checkstyle-5.1.jar")).exists();
+ }
+
+ @Test
+ public void extract_only_libs() throws IOException {
+ File fileFromCache = getFileFromCache("sonar-checkstyle-plugin-2.8.jar");
+ underTest.explode(PluginInfo.create(fileFromCache));
+
+ assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar")).exists();
+ assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/META-INF/MANIFEST.MF")).doesNotExist();
+ assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/org/sonar/plugins/checkstyle/CheckstyleVersion.class")).doesNotExist();
+ }
+
+ File getFileFromCache(String filename) throws IOException {
+ File src = FileUtils.toFile(getClass().getResource(this.getClass().getSimpleName() + "/" + filename));
+ File destFile = new File(new File(userHome, "" + filename.hashCode()), filename);
+ FileUtils.copyFile(src, destFile);
+ return destFile;
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java
new file mode 100644
index 00000000000..0e8f29a1609
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java
@@ -0,0 +1,103 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BatchPluginPredicateTest {
+
+ Settings settings = new Settings();
+ GlobalMode mode = mock(GlobalMode.class);
+
+ @Test
+ public void accept_if_no_inclusions_nor_exclusions() {
+ BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode);
+ assertThat(predicate.getWhites()).isEmpty();
+ assertThat(predicate.getBlacks()).isEmpty();
+ assertThat(predicate.apply("pmd")).isTrue();
+ assertThat(predicate.apply("buildbreaker")).isTrue();
+ }
+
+ @Test
+ public void exclude_buildbreaker_in_preview_mode() {
+ when(mode.isPreview()).thenReturn(true);
+ BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode);
+ assertThat(predicate.apply("buildbreaker")).isFalse();
+ }
+
+ @Test
+ public void inclusions_take_precedence_over_exclusions() {
+ when(mode.isPreview()).thenReturn(true);
+ settings
+ .setProperty(CoreProperties.PREVIEW_INCLUDE_PLUGINS, "checkstyle,pmd,findbugs")
+ .setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "cobertura,pmd");
+ BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode);
+ assertThat(predicate.apply("pmd")).isTrue();
+ }
+
+ @Test
+ public void verify_both_inclusions_and_exclusions() {
+ when(mode.isPreview()).thenReturn(true);
+ settings
+ .setProperty(CoreProperties.PREVIEW_INCLUDE_PLUGINS, "checkstyle,pmd,findbugs")
+ .setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "cobertura");
+ BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode);
+ assertThat(predicate.apply("checkstyle")).isTrue();
+ assertThat(predicate.apply("pmd")).isTrue();
+ assertThat(predicate.apply("cobertura")).isFalse();
+ }
+
+ @Test
+ public void verify_both_inclusions_and_exclusions_issues() {
+ when(mode.isIssues()).thenReturn(true);
+ settings
+ .setProperty(CoreProperties.PREVIEW_INCLUDE_PLUGINS, "checkstyle,pmd,findbugs")
+ .setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "cobertura");
+ BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode);
+ assertThat(predicate.apply("checkstyle")).isTrue();
+ assertThat(predicate.apply("pmd")).isTrue();
+ assertThat(predicate.apply("cobertura")).isFalse();
+ }
+
+ @Test
+ public void test_exclusions_without_any_inclusions() {
+ when(mode.isPreview()).thenReturn(true);
+ settings.setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "checkstyle,pmd,findbugs");
+ BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode);
+ assertThat(predicate.apply("checkstyle")).isFalse();
+ assertThat(predicate.apply("pmd")).isFalse();
+ assertThat(predicate.apply("cobertura")).isTrue();
+ }
+
+ @Test
+ public void trim_inclusions_and_exclusions() {
+ settings
+ .setProperty(CoreProperties.PREVIEW_INCLUDE_PLUGINS, "checkstyle, pmd, findbugs")
+ .setProperty(CoreProperties.PREVIEW_EXCLUDE_PLUGINS, "cobertura, pmd");
+ BatchPluginPredicate predicate = new BatchPluginPredicate(settings, mode);
+ assertThat(predicate.apply("pmd")).isTrue();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
new file mode 100644
index 00000000000..0d0fc068dfe
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Test;
+import org.sonar.api.SonarPlugin;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.platform.PluginLoader;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyCollectionOf;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class BatchPluginRepositoryTest {
+
+ PluginInstaller installer = mock(PluginInstaller.class);
+ PluginLoader loader = mock(PluginLoader.class);
+ BatchPluginRepository underTest = new BatchPluginRepository(installer, loader);
+
+ @Test
+ public void install_and_load_plugins() {
+ PluginInfo info = new PluginInfo("squid");
+ ImmutableMap<String, PluginInfo> infos = ImmutableMap.of("squid", info);
+ SonarPlugin instance = mock(SonarPlugin.class);
+ when(loader.load(infos)).thenReturn(ImmutableMap.of("squid", instance));
+ when(installer.installRemotes()).thenReturn(infos);
+
+ underTest.start();
+
+ assertThat(underTest.getPluginInfos()).containsOnly(info);
+ assertThat(underTest.getPluginInfo("squid")).isSameAs(info);
+ assertThat(underTest.getPluginInstance("squid")).isSameAs(instance);
+
+ underTest.stop();
+ verify(loader).unload(anyCollectionOf(SonarPlugin.class));
+ }
+
+ @Test
+ public void fail_if_requesting_missing_plugin() {
+ underTest.start();
+
+ try {
+ underTest.getPluginInfo("unknown");
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("Plugin [unknown] does not exist");
+ }
+ try {
+ underTest.getPluginInstance("unknown");
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("Plugin [unknown] does not exist");
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientProviderTest.java
new file mode 100644
index 00000000000..76fa07167ab
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientProviderTest.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonarqube.ws.client.HttpConnector;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BatchWsClientProviderTest {
+
+ BatchWsClientProvider underTest = new BatchWsClientProvider();
+ EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.3");
+
+ @Test
+ public void provide_client_with_default_settings() {
+ GlobalProperties settings = new GlobalProperties(new HashMap<String, String>());
+
+ BatchWsClient client = underTest.provide(settings, env);
+
+ assertThat(client).isNotNull();
+ assertThat(client.baseUrl()).isEqualTo("http://localhost:9000/");
+ HttpConnector httpConnector = (HttpConnector) client.wsConnector();
+ assertThat(httpConnector.baseUrl()).isEqualTo("http://localhost:9000/");
+ assertThat(httpConnector.okHttpClient().getProxy()).isNull();
+ assertThat(httpConnector.okHttpClient().getConnectTimeout()).isEqualTo(5_000);
+ assertThat(httpConnector.okHttpClient().getReadTimeout()).isEqualTo(60_000);
+ assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3");
+ }
+
+ @Test
+ public void provide_client_with_custom_settings() {
+ Map<String, String> props = new HashMap<>();
+ props.put("sonar.host.url", "https://here/sonarqube");
+ props.put("sonar.login", "theLogin");
+ props.put("sonar.password", "thePassword");
+ props.put("sonar.ws.timeout", "42");
+ GlobalProperties settings = new GlobalProperties(props);
+
+ BatchWsClient client = underTest.provide(settings, env);
+
+ assertThat(client).isNotNull();
+ HttpConnector httpConnector = (HttpConnector) client.wsConnector();
+ assertThat(httpConnector.baseUrl()).isEqualTo("https://here/sonarqube/");
+ assertThat(httpConnector.okHttpClient().getProxy()).isNull();
+ assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3");
+ }
+
+ @Test
+ public void build_singleton() {
+ GlobalProperties settings = new GlobalProperties(new HashMap<String, String>());
+ BatchWsClient first = underTest.provide(settings, env);
+ BatchWsClient second = underTest.provide(settings, env);
+ assertThat(first).isSameAs(second);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java
new file mode 100644
index 00000000000..3689c2d601a
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.Mockito;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.MockWsResponse;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.WsRequest;
+import org.sonarqube.ws.client.WsResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BatchWsClientTest {
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ WsClient wsClient = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS);
+
+ @Test
+ public void log_and_profile_request_if_debug_level() throws Exception {
+ WsRequest request = newRequest();
+ WsResponse response = newResponse().setRequestUrl("https://local/api/issues/search");
+ when(wsClient.wsConnector().call(request)).thenReturn(response);
+
+ logTester.setLevel(LoggerLevel.DEBUG);
+ BatchWsClient underTest = new BatchWsClient(wsClient, false);
+
+ WsResponse result = underTest.call(request);
+
+ // do not fail the execution -> interceptor returns the response
+ assertThat(result).isSameAs(response);
+
+ // check logs
+ List<String> debugLogs = logTester.logs(LoggerLevel.DEBUG);
+ assertThat(debugLogs).hasSize(1);
+ assertThat(debugLogs.get(0)).contains("GET 200 https://local/api/issues/search | time=");
+ }
+
+ @Test
+ public void fail_if_requires_credentials() throws Exception {
+ expectedException.expect(MessageException.class);
+ expectedException
+ .expectMessage("Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties sonar.login and sonar.password.");
+
+ WsRequest request = newRequest();
+ WsResponse response = newResponse().setCode(401);
+ when(wsClient.wsConnector().call(request)).thenReturn(response);
+
+ new BatchWsClient(wsClient, false).call(request);
+ }
+
+ @Test
+ public void fail_if_credentials_are_not_valid() throws Exception {
+ expectedException.expect(MessageException.class);
+ expectedException.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password.");
+
+ WsRequest request = newRequest();
+ WsResponse response = newResponse().setCode(401);
+ when(wsClient.wsConnector().call(request)).thenReturn(response);
+
+ new BatchWsClient(wsClient, /* credentials are configured */true).call(request);
+ }
+
+ @Test
+ public void fail_if_requires_permission() throws Exception {
+ expectedException.expect(MessageException.class);
+ expectedException.expectMessage("missing scan permission, missing another permission");
+
+ WsRequest request = newRequest();
+ WsResponse response = newResponse()
+ .setCode(403)
+ .setContent("{\"errors\":[{\"msg\":\"missing scan permission\"}, {\"msg\":\"missing another permission\"}]}");
+ when(wsClient.wsConnector().call(request)).thenReturn(response);
+
+ new BatchWsClient(wsClient, true).call(request);
+ }
+
+ private MockWsResponse newResponse() {
+ return new MockWsResponse().setRequestUrl("https://local/api/issues/search");
+ }
+
+ private WsRequest newRequest() {
+ return new GetRequest("api/issues/search");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/DroppedPropertyCheckerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/DroppedPropertyCheckerTest.java
new file mode 100644
index 00000000000..e6cd758d253
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/DroppedPropertyCheckerTest.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DroppedPropertyCheckerTest {
+ private static final String SOME_VALUE = "some value";
+ private static final String DROPPED_PROPERTY_1 = "I'm dropped";
+ private static final String DROPPED_PROPERTY_MSG_1 = "blablabla!";
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Test
+ public void no_log_if_no_dropped_property() {
+ new DroppedPropertyChecker(ImmutableMap.of(DROPPED_PROPERTY_1, SOME_VALUE), ImmutableMap.<String, String>of()).checkDroppedProperties();
+
+ assertThat(logTester.logs()).isEmpty();
+ }
+
+ @Test
+ public void no_log_if_settings_does_not_contain_any_dropped_property() {
+ new DroppedPropertyChecker(ImmutableMap.<String, String>of(), ImmutableMap.of(DROPPED_PROPERTY_1, DROPPED_PROPERTY_MSG_1)).checkDroppedProperties();
+
+ assertThat(logTester.logs()).isEmpty();
+ }
+
+ @Test
+ public void warn_log_if_settings_contains_any_dropped_property() {
+ new DroppedPropertyChecker(ImmutableMap.of(DROPPED_PROPERTY_1, SOME_VALUE), ImmutableMap.of(DROPPED_PROPERTY_1, DROPPED_PROPERTY_MSG_1)).checkDroppedProperties();
+
+ assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty();
+ assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Property '" + DROPPED_PROPERTY_1 + "' is not supported any more. " + DROPPED_PROPERTY_MSG_1);
+ assertThat(logTester.logs(LoggerLevel.INFO)).isEmpty();
+ assertThat(logTester.logs(LoggerLevel.DEBUG)).isEmpty();
+ assertThat(logTester.logs(LoggerLevel.TRACE)).isEmpty();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java
new file mode 100644
index 00000000000..c368fb146b6
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java
@@ -0,0 +1,136 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.commons.lang.ClassUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ExtensionProvider;
+import org.sonar.api.SonarPlugin;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.core.platform.PluginInfo;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ExtensionInstallerTest {
+
+ GlobalMode mode;
+ BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class);
+
+ private static SonarPlugin newPluginInstance(final Object... extensions) {
+ return new SonarPlugin() {
+ public List getExtensions() {
+ return Arrays.asList(extensions);
+ }
+ };
+ }
+
+ @Before
+ public void setUp() {
+ mode = mock(GlobalMode.class);
+ }
+
+ @Test
+ public void should_filter_extensions_to_install() {
+ when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo")));
+ when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(Foo.class, Bar.class));
+
+ ComponentContainer container = new ComponentContainer();
+ ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class));
+ installer.install(container, new FooMatcher());
+
+ assertThat(container.getComponentByType(Foo.class)).isNotNull();
+ assertThat(container.getComponentByType(Bar.class)).isNull();
+ }
+
+ @Test
+ public void should_execute_extension_provider() {
+ when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo")));
+ when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(new FooProvider(), new BarProvider()));
+ ComponentContainer container = new ComponentContainer();
+ ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class));
+
+ installer.install(container, new FooMatcher());
+
+ assertThat(container.getComponentByType(Foo.class)).isNotNull();
+ assertThat(container.getComponentByType(Bar.class)).isNull();
+ }
+
+ @Test
+ public void should_provide_list_of_extensions() {
+ when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo")));
+ when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(new FooBarProvider()));
+ ComponentContainer container = new ComponentContainer();
+ ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class));
+
+ installer.install(container, new TrueMatcher());
+
+ assertThat(container.getComponentByType(Foo.class)).isNotNull();
+ assertThat(container.getComponentByType(Bar.class)).isNotNull();
+ }
+
+ private static class FooMatcher implements ExtensionMatcher {
+ public boolean accept(Object extension) {
+ return extension.equals(Foo.class) || ClassUtils.isAssignable(Foo.class, extension.getClass()) || ClassUtils.isAssignable(FooProvider.class, extension.getClass());
+ }
+ }
+
+ private static class TrueMatcher implements ExtensionMatcher {
+ public boolean accept(Object extension) {
+ return true;
+ }
+ }
+
+ public static class Foo implements BatchExtension {
+
+ }
+
+ public static class Bar implements BatchExtension {
+
+ }
+
+ public static class FooProvider extends ExtensionProvider implements BatchExtension {
+ @Override
+ public Object provide() {
+ return new Foo();
+ }
+ }
+
+ public static class BarProvider extends ExtensionProvider implements BatchExtension {
+ @Override
+ public Object provide() {
+ return new Bar();
+ }
+ }
+
+ public static class FooBarProvider extends ExtensionProvider implements BatchExtension {
+ @Override
+ public Object provide() {
+ return Arrays.asList(new Foo(), new Bar());
+ }
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java
new file mode 100644
index 00000000000..f83920847e3
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java
@@ -0,0 +1,88 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import org.junit.Test;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.server.ServerSide;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ExtensionUtilsTest {
+
+ @Test
+ public void shouldBeBatchInstantiationStrategy() {
+ assertThat(ExtensionUtils.isInstantiationStrategy(BatchService.class, InstantiationStrategy.PER_BATCH)).isTrue();
+ assertThat(ExtensionUtils.isInstantiationStrategy(new BatchService(), InstantiationStrategy.PER_BATCH)).isTrue();
+ assertThat(ExtensionUtils.isInstantiationStrategy(ProjectService.class, InstantiationStrategy.PER_BATCH)).isFalse();
+ assertThat(ExtensionUtils.isInstantiationStrategy(new ProjectService(), InstantiationStrategy.PER_BATCH)).isFalse();
+ assertThat(ExtensionUtils.isInstantiationStrategy(DefaultService.class, InstantiationStrategy.PER_BATCH)).isFalse();
+ assertThat(ExtensionUtils.isInstantiationStrategy(new DefaultService(), InstantiationStrategy.PER_BATCH)).isFalse();
+ }
+
+ @Test
+ public void shouldBeProjectInstantiationStrategy() {
+ assertThat(ExtensionUtils.isInstantiationStrategy(BatchService.class, InstantiationStrategy.PER_PROJECT)).isFalse();
+ assertThat(ExtensionUtils.isInstantiationStrategy(new BatchService(), InstantiationStrategy.PER_PROJECT)).isFalse();
+ assertThat(ExtensionUtils.isInstantiationStrategy(ProjectService.class, InstantiationStrategy.PER_PROJECT)).isTrue();
+ assertThat(ExtensionUtils.isInstantiationStrategy(new ProjectService(), InstantiationStrategy.PER_PROJECT)).isTrue();
+ assertThat(ExtensionUtils.isInstantiationStrategy(DefaultService.class, InstantiationStrategy.PER_PROJECT)).isTrue();
+ assertThat(ExtensionUtils.isInstantiationStrategy(new DefaultService(), InstantiationStrategy.PER_PROJECT)).isTrue();
+ }
+
+ @Test
+ public void testIsBatchSide() {
+ assertThat(ExtensionUtils.isBatchSide(BatchService.class)).isTrue();
+ assertThat(ExtensionUtils.isBatchSide(new BatchService())).isTrue();
+ assertThat(ExtensionUtils.isBatchSide(DeprecatedBatchService.class)).isTrue();
+
+ assertThat(ExtensionUtils.isBatchSide(ServerService.class)).isFalse();
+ assertThat(ExtensionUtils.isBatchSide(new ServerService())).isFalse();
+ }
+
+ @BatchSide
+ @InstantiationStrategy(InstantiationStrategy.PER_BATCH)
+ public static class BatchService {
+
+ }
+
+ public static class DeprecatedBatchService implements BatchComponent {
+
+ }
+
+ @BatchSide
+ @InstantiationStrategy(InstantiationStrategy.PER_PROJECT)
+ public static class ProjectService {
+
+ }
+
+ @BatchSide
+ public static class DefaultService {
+
+ }
+
+ @ServerSide
+ public static class ServerService {
+
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/FileCacheProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/FileCacheProviderTest.java
new file mode 100644
index 00000000000..09ce7835e0c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/FileCacheProviderTest.java
@@ -0,0 +1,65 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.home.cache.FileCache;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FileCacheProviderTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void provide() {
+ FileCacheProvider provider = new FileCacheProvider();
+ FileCache cache = provider.provide(new Settings());
+
+ assertThat(cache).isNotNull();
+ assertThat(cache.getDir()).isNotNull().exists();
+ }
+
+ @Test
+ public void keep_singleton_instance() {
+ FileCacheProvider provider = new FileCacheProvider();
+ Settings settings = new Settings();
+ FileCache cache1 = provider.provide(settings);
+ FileCache cache2 = provider.provide(settings);
+
+ assertThat(cache1).isSameAs(cache2);
+ }
+
+ @Test
+ public void honor_sonarUserHome() throws IOException {
+ FileCacheProvider provider = new FileCacheProvider();
+ Settings settings = new Settings();
+ File f = temp.newFolder();
+ settings.appendProperty("sonar.userHome", f.getAbsolutePath());
+ FileCache cache = provider.provide(settings);
+
+ assertThat(cache.getDir()).isEqualTo(new File(f, "cache"));
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalContainerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalContainerTest.java
new file mode 100644
index 00000000000..41152b3ccb5
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalContainerTest.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.utils.TempFolder;
+import org.sonar.core.util.UuidFactory;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class GlobalContainerTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private GlobalContainer createContainer(List<Object> extensions) {
+ Map<String, String> props = ImmutableMap.of(CoreProperties.WORKING_DIRECTORY, temp.getRoot().getAbsolutePath(),
+ CoreProperties.GLOBAL_WORKING_DIRECTORY, temp.getRoot().getAbsolutePath());
+
+ GlobalContainer container = GlobalContainer.create(props, extensions, false);
+ container.doBeforeStart();
+ return container;
+ }
+
+ @Test
+ public void should_add_components() {
+ GlobalContainer container = createContainer(Collections.emptyList());
+
+ assertThat(container.getComponentByType(UuidFactory.class)).isNotNull();
+ assertThat(container.getComponentByType(TempFolder.class)).isNotNull();
+ }
+
+ @Test
+ public void should_add_bootstrap_extensions() {
+ GlobalContainer container = createContainer(Lists.newArrayList(Foo.class, new Bar()));
+
+ assertThat(container.getComponentByType(Foo.class)).isNotNull();
+ assertThat(container.getComponentByType(Bar.class)).isNotNull();
+ }
+
+ @BatchSide
+ public static class Foo {
+
+ }
+
+ @BatchSide
+ public static class Bar {
+
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java
new file mode 100644
index 00000000000..5da1858e33b
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.CoreProperties;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class GlobalModeTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testModeNotSupported() {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("[preview, publish, issues]");
+
+ createMode(CoreProperties.ANALYSIS_MODE, "invalid");
+ }
+
+ @Test
+ public void testOtherProperty() {
+ GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PUBLISH);
+ assertThat(mode.isPreview()).isFalse();
+ assertThat(mode.isIssues()).isFalse();
+ assertThat(mode.isPublish()).isTrue();
+ }
+
+ @Test
+ public void testIssuesMode() {
+ GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES);
+ assertThat(mode.isPreview()).isFalse();
+ assertThat(mode.isIssues()).isTrue();
+ assertThat(mode.isPublish()).isFalse();
+ }
+
+ @Test
+ public void preview_mode_fallback_issues() {
+ GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PREVIEW);
+
+ assertThat(mode.isIssues()).isTrue();
+ assertThat(mode.isPreview()).isFalse();
+ }
+
+ @Test
+ public void testDefault() {
+ GlobalMode mode = createMode(null, null);
+ assertThat(mode.isPreview()).isFalse();
+ assertThat(mode.isIssues()).isFalse();
+ assertThat(mode.isPublish()).isTrue();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testInvalidMode() {
+ createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ANALYSIS);
+ }
+
+ private GlobalMode createMode(String key, String value) {
+ Map<String, String> map = new HashMap<>();
+ if (key != null) {
+ map.put(key, value);
+ }
+ GlobalProperties props = new GlobalProperties(map);
+ return new GlobalMode(props);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalPropertiesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalPropertiesTest.java
new file mode 100644
index 00000000000..4f3fd5cebca
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalPropertiesTest.java
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import com.google.common.collect.Maps;
+import org.junit.Test;
+
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+
+public class GlobalPropertiesTest {
+ @Test
+ public void test_copy_of_properties() {
+ Map<String, String> map = Maps.newHashMap();
+ map.put("foo", "bar");
+
+ GlobalProperties wrapper = new GlobalProperties(map);
+ assertThat(wrapper.properties()).containsOnly(entry("foo", "bar"));
+ assertThat(wrapper.properties()).isNotSameAs(map);
+
+ map.put("put", "after_copy");
+ assertThat(wrapper.properties()).hasSize(1);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java
new file mode 100644
index 00000000000..aa13b14ccb5
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java
@@ -0,0 +1,79 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import java.util.Collections;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.scanner.protocol.input.GlobalRepositories;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class GlobalSettingsTest {
+
+ public static final String SOME_VALUE = "some_value";
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ GlobalRepositories globalRef;
+ GlobalProperties bootstrapProps;
+
+ private GlobalMode mode;
+
+ @Before
+ public void prepare() {
+ globalRef = new GlobalRepositories();
+ bootstrapProps = new GlobalProperties(Collections.<String, String>emptyMap());
+ mode = mock(GlobalMode.class);
+ }
+
+ @Test
+ public void should_load_global_settings() {
+ globalRef.globalSettings().put("sonar.cpd.cross", "true");
+
+ GlobalSettings batchSettings = new GlobalSettings(bootstrapProps, new PropertyDefinitions(), globalRef, mode);
+
+ assertThat(batchSettings.getBoolean("sonar.cpd.cross")).isTrue();
+ }
+
+ @Test
+ public void should_log_warn_msg_for_each_jdbc_property_if_present() {
+ globalRef.globalSettings().put("sonar.jdbc.url", SOME_VALUE);
+ globalRef.globalSettings().put("sonar.jdbc.username", SOME_VALUE);
+ globalRef.globalSettings().put("sonar.jdbc.password", SOME_VALUE);
+
+ new GlobalSettings(bootstrapProps, new PropertyDefinitions(), globalRef, mode);
+
+ assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly(
+ "Property 'sonar.jdbc.url' is not supported any more. It will be ignored. There is no longer any DB connection to the SQ database.",
+ "Property 'sonar.jdbc.username' is not supported any more. It will be ignored. There is no longer any DB connection to the SQ database.",
+ "Property 'sonar.jdbc.password' is not supported any more. It will be ignored. There is no longer any DB connection to the SQ database."
+ );
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalTempFolderProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalTempFolderProviderTest.java
new file mode 100644
index 00000000000..e0d4ee9d3ea
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/GlobalTempFolderProviderTest.java
@@ -0,0 +1,142 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import org.sonar.api.utils.System2;
+import org.apache.commons.io.FileUtils;
+import org.sonar.api.utils.TempFolder;
+import com.google.common.collect.ImmutableMap;
+import org.sonar.api.CoreProperties;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.FileTime;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class GlobalTempFolderProviderTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private GlobalTempFolderProvider tempFolderProvider = new GlobalTempFolderProvider();
+
+ @Test
+ public void createTempFolderProps() throws Exception {
+ File workingDir = temp.newFolder();
+
+ TempFolder tempFolder = tempFolderProvider.provide(new GlobalProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath())));
+ tempFolder.newDir();
+ tempFolder.newFile();
+ assertThat(getCreatedTempDir(workingDir)).exists();
+ assertThat(getCreatedTempDir(workingDir).list()).hasSize(2);
+
+ FileUtils.deleteQuietly(workingDir);
+ }
+
+ @Test
+ public void cleanUpOld() throws IOException {
+ long creationTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(100);
+ File workingDir = temp.newFolder();
+
+ for (int i = 0; i < 3; i++) {
+ File tmp = new File(workingDir, ".sonartmp_" + i);
+ tmp.mkdirs();
+ setFileCreationDate(tmp, creationTime);
+ }
+
+ tempFolderProvider.provide(new GlobalProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath())));
+ // this also checks that all other temps were deleted
+ assertThat(getCreatedTempDir(workingDir)).exists();
+
+ FileUtils.deleteQuietly(workingDir);
+ }
+
+ @Test
+ public void createTempFolderSonarHome() throws Exception {
+ // with sonar home, it will be in {sonar.home}/.sonartmp
+ File sonarHome = temp.newFolder();
+ File workingDir = new File(sonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE).getAbsoluteFile();
+
+ TempFolder tempFolder = tempFolderProvider.provide(new GlobalProperties(ImmutableMap.of("sonar.userHome", sonarHome.getAbsolutePath())));
+ tempFolder.newDir();
+ tempFolder.newFile();
+ assertThat(getCreatedTempDir(workingDir)).exists();
+ assertThat(getCreatedTempDir(workingDir).list()).hasSize(2);
+
+ FileUtils.deleteQuietly(sonarHome);
+ }
+
+ @Test
+ public void createTempFolderDefault() throws Exception {
+ System2 system = mock(System2.class);
+ tempFolderProvider = new GlobalTempFolderProvider(system);
+ File userHome = temp.newFolder();
+
+ when(system.envVariable("SONAR_USER_HOME")).thenReturn(null);
+ when(system.property("user.home")).thenReturn(userHome.getAbsolutePath().toString());
+
+ // if nothing is defined, it will be in {user.home}/.sonar/.sonartmp
+ File defaultSonarHome = new File(userHome.getAbsolutePath(), ".sonar");
+ File workingDir = new File(defaultSonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE).getAbsoluteFile();
+ try {
+ TempFolder tempFolder = tempFolderProvider.provide(new GlobalProperties(Collections.<String, String>emptyMap()));
+ tempFolder.newDir();
+ tempFolder.newFile();
+ assertThat(getCreatedTempDir(workingDir)).exists();
+ assertThat(getCreatedTempDir(workingDir).list()).hasSize(2);
+ } finally {
+ FileUtils.deleteQuietly(workingDir);
+ }
+ }
+
+ @Test
+ public void dotWorkingDir() throws IOException {
+ File sonarHome = temp.getRoot();
+ String globalWorkDir = ".";
+ GlobalProperties globalProperties = new GlobalProperties(ImmutableMap.of("sonar.userHome", sonarHome.getAbsolutePath(),
+ CoreProperties.GLOBAL_WORKING_DIRECTORY, globalWorkDir));
+
+ TempFolder tempFolder = tempFolderProvider.provide(globalProperties);
+ File newFile = tempFolder.newFile();
+ assertThat(newFile.getParentFile().getParentFile().getAbsolutePath()).isEqualTo(sonarHome.getAbsolutePath());
+ assertThat(newFile.getParentFile().getName()).startsWith(".sonartmp_");
+ }
+
+ private File getCreatedTempDir(File workingDir) {
+ assertThat(workingDir).isDirectory();
+ assertThat(workingDir.listFiles()).hasSize(1);
+ return workingDir.listFiles()[0];
+ }
+
+ private void setFileCreationDate(File f, long time) throws IOException {
+ BasicFileAttributeView attributes = Files.getFileAttributeView(f.toPath(), BasicFileAttributeView.class);
+ FileTime creationTime = FileTime.fromMillis(time);
+ attributes.setTimes(creationTime, creationTime, creationTime);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java
new file mode 100644
index 00000000000..8d1ab306b8d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MetricProviderTest.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.Metrics;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MetricProviderTest {
+ @Test
+ public void should_provide_at_least_core_metrics() {
+ MetricProvider provider = new MetricProvider();
+ List<Metric> metrics = provider.provide();
+
+ assertThat(metrics).hasSize(CoreMetrics.getMetrics().size());
+ assertThat(metrics).extracting("key").contains("ncloc");
+ }
+
+ @Test
+ public void should_provide_plugin_metrics() {
+ Metrics factory = new Metrics() {
+ public List<Metric> getMetrics() {
+ return Arrays.<Metric>asList(new Metric.Builder("custom", "Custom", Metric.ValueType.FLOAT).create());
+ }
+ };
+ MetricProvider provider = new MetricProvider(new Metrics[] {factory});
+ List<Metric> metrics = provider.provide();
+
+ assertThat(metrics.size()).isEqualTo(1 + CoreMetrics.getMetrics().size());
+ assertThat(metrics).extracting("key").contains("custom");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java
new file mode 100644
index 00000000000..99f10de2c9e
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java
@@ -0,0 +1,118 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.io.IOUtils;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static org.apache.commons.io.IOUtils.write;
+
+public class MockHttpServer {
+ private Server server;
+ private String responseBody;
+ private String requestBody;
+ private String mockResponseData;
+ private int mockResponseStatus = SC_OK;
+ private List<String> targets = new ArrayList<>();
+
+ public void start() throws Exception {
+ server = new Server(0);
+ server.setHandler(getMockHandler());
+ server.start();
+ }
+
+ public int getNumberRequests() {
+ return targets.size();
+ }
+
+ /**
+ * Creates an {@link org.mortbay.jetty.handler.AbstractHandler handler} returning an arbitrary String as a response.
+ *
+ * @return never <code>null</code>.
+ */
+ public Handler getMockHandler() {
+ Handler handler = new AbstractHandler() {
+
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ targets.add(target);
+ setResponseBody(getMockResponseData());
+ setRequestBody(IOUtils.toString(baseRequest.getInputStream()));
+ response.setStatus(mockResponseStatus);
+ response.setContentType("text/xml;charset=utf-8");
+ write(getResponseBody(), response.getOutputStream());
+ baseRequest.setHandled(true);
+ }
+ };
+ return handler;
+ }
+
+ public void stop() {
+ try {
+ if (server != null) {
+ server.stop();
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to stop HTTP server", e);
+ }
+ }
+
+ public String getResponseBody() {
+ return responseBody;
+ }
+
+ public void setResponseBody(String responseBody) {
+ this.responseBody = responseBody;
+ }
+
+ public String getRequestBody() {
+ return requestBody;
+ }
+
+ public void setRequestBody(String requestBody) {
+ this.requestBody = requestBody;
+ }
+
+ public void setMockResponseStatus(int status) {
+ this.mockResponseStatus = status;
+ }
+
+ public String getMockResponseData() {
+ return mockResponseData;
+ }
+
+ public void setMockResponseData(String mockResponseData) {
+ this.mockResponseData = mockResponseData;
+ }
+
+ public int getPort() {
+ return server.getConnectors()[0].getLocalPort();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java
new file mode 100644
index 00000000000..b9696631f89
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java
@@ -0,0 +1,73 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrapper;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+
+public class BatchTest {
+ @Test
+ public void testBuilder() {
+ Batch batch = newBatch();
+ assertNotNull(batch);
+
+ }
+
+ private Batch newBatch() {
+ return Batch.builder()
+ .setEnvironment(new EnvironmentInformation("Gradle", "1.0"))
+ .addComponent("fake")
+ .build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void shouldFailIfNullComponents() {
+ Batch.builder()
+ .setEnvironment(new EnvironmentInformation("Gradle", "1.0"))
+ .setComponents(null)
+ .build();
+ }
+
+ @Test
+ public void shouldDisableLoggingConfiguration() {
+ Batch batch = Batch.builder()
+ .setEnvironment(new EnvironmentInformation("Gradle", "1.0"))
+ .addComponent("fake")
+ .setEnableLoggingConfiguration(false)
+ .build();
+ assertNull(batch.getLoggingConfiguration());
+ }
+
+ @Test
+ public void loggingConfigurationShouldBeEnabledByDefault() {
+ assertNotNull(newBatch().getLoggingConfiguration());
+ }
+
+ @Test
+ public void shoudSetLogListener() {
+ LogOutput logOutput = mock(LogOutput.class);
+ Batch batch = Batch.builder().setLogOutput(logOutput).build();
+ assertThat(batch.getLoggingConfiguration().getLogOutput()).isEqualTo(logOutput);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/EnvironmentInformationTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/EnvironmentInformationTest.java
new file mode 100644
index 00000000000..71dd0495d49
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/EnvironmentInformationTest.java
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrapper;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EnvironmentInformationTest {
+ @Test
+ public void test_bean() {
+ EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.0");
+
+ assertThat(env.getKey()).isEqualTo("Maven Plugin");
+ assertThat(env.getVersion()).isEqualTo("2.0");
+ }
+
+ @Test
+ public void test_toString() {
+ EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.0");
+
+ assertThat(env.toString()).isEqualTo("Maven Plugin/2.0");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java
new file mode 100644
index 00000000000..6b225306fe0
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrapper;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class LogCallbackAppenderTest {
+ private LogOutput listener;
+ private LogCallbackAppender appender;
+ private ILoggingEvent event;
+
+ @Before
+ public void setUp() {
+ listener = mock(LogOutput.class);
+ appender = new LogCallbackAppender(listener);
+ }
+
+ @Test
+ public void testLevelTranslation() {
+ testMessage("test", Level.INFO, LogOutput.Level.INFO);
+ testMessage("test", Level.DEBUG, LogOutput.Level.DEBUG);
+ testMessage("test", Level.ERROR, LogOutput.Level.ERROR);
+ testMessage("test", Level.TRACE, LogOutput.Level.TRACE);
+ testMessage("test", Level.WARN, LogOutput.Level.WARN);
+
+ // this should never happen
+ testMessage("test", Level.OFF, LogOutput.Level.DEBUG);
+ }
+
+ private void testMessage(String msg, Level level, LogOutput.Level translatedLevel) {
+ reset(listener);
+ event = mock(ILoggingEvent.class);
+ when(event.getFormattedMessage()).thenReturn(msg);
+ when(event.getLevel()).thenReturn(level);
+
+ appender.append(event);
+
+ verify(event).getFormattedMessage();
+ verify(event).getLevel();
+ verify(listener).log(msg, translatedLevel);
+ verifyNoMoreInteractions(event, listener);
+ }
+
+ @Test
+ public void testChangeTarget() {
+ listener = mock(LogOutput.class);
+ appender.setTarget(listener);
+ testLevelTranslation();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java
new file mode 100644
index 00000000000..920af566e93
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java
@@ -0,0 +1,167 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrapper;
+
+import com.google.common.collect.Maps;
+import java.util.Map;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class LoggingConfigurationTest {
+
+ @Test
+ public void testSetVerbose() {
+ assertThat(new LoggingConfiguration(null).setVerbose(true)
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE);
+
+ assertThat(new LoggingConfiguration(null).setVerbose(false)
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT);
+
+ assertThat(new LoggingConfiguration(null).setRootLevel("ERROR")
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("ERROR");
+ }
+
+ @Test
+ public void testSetVerboseAnalysis() {
+ Map<String, String> globalProps = Maps.newHashMap();
+ LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(globalProps);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+
+ Map<String, String> analysisProperties = Maps.newHashMap();
+ analysisProperties.put("sonar.verbose", "true");
+
+ conf.setProperties(analysisProperties, globalProps);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+ }
+
+ @Test
+ public void testOverrideVerbose() {
+ Map<String, String> globalProps = Maps.newHashMap();
+ globalProps.put("sonar.verbose", "true");
+ LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(globalProps);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+
+ Map<String, String> analysisProperties = Maps.newHashMap();
+ analysisProperties.put("sonar.verbose", "false");
+
+ conf.setProperties(analysisProperties, globalProps);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+ }
+
+ @Test
+ public void shouldNotBeVerboseByDefault() {
+ assertThat(new LoggingConfiguration(null)
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT);
+ }
+
+ @Test
+ public void test_log_listener_setter() {
+ LogOutput listener = mock(LogOutput.class);
+ assertThat(new LoggingConfiguration(null).setLogOutput(listener).getLogOutput()).isEqualTo(listener);
+ }
+
+ @Test
+ public void test_deprecated_log_properties() {
+ Map<String, String> properties = Maps.newHashMap();
+ assertThat(new LoggingConfiguration(null).setProperties(properties)
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT);
+
+ properties.put("sonar.verbose", "true");
+ LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(properties);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+
+ properties.put("sonar.verbose", "false");
+ conf = new LoggingConfiguration(null).setProperties(properties);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+
+ properties.put("sonar.verbose", "false");
+ properties.put("sonar.log.profilingLevel", "FULL");
+ conf = new LoggingConfiguration(null).setProperties(properties);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG");
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("TRACE");
+
+ properties.put("sonar.verbose", "false");
+ properties.put("sonar.log.profilingLevel", "BASIC");
+ conf = new LoggingConfiguration(null).setProperties(properties);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG");
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+ }
+
+ @Test
+ public void test_log_level_property() {
+ Map<String, String> properties = Maps.newHashMap();
+ LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(properties);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("INFO");
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+
+ properties.put("sonar.log.level", "INFO");
+ conf = new LoggingConfiguration(null).setProperties(properties);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("INFO");
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+
+ properties.put("sonar.log.level", "DEBUG");
+ conf = new LoggingConfiguration(null).setProperties(properties);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG");
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN");
+
+ properties.put("sonar.log.level", "TRACE");
+ conf = new LoggingConfiguration(null).setProperties(properties);
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG");
+ assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("TRACE");
+ }
+
+ @Test
+ public void testDefaultFormat() {
+ assertThat(new LoggingConfiguration(null)
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT);
+ }
+
+ @Test
+ public void testMavenFormat() {
+ assertThat(new LoggingConfiguration(new EnvironmentInformation("maven", "1.0"))
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_MAVEN);
+ }
+
+ @Test
+ public void testSetFormat() {
+ assertThat(new LoggingConfiguration(null).setFormat("%d %level")
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo("%d %level");
+ }
+
+ @Test
+ public void shouldNotSetBlankFormat() {
+ assertThat(new LoggingConfiguration(null).setFormat(null)
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT);
+
+ assertThat(new LoggingConfiguration(null).setFormat("")
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT);
+
+ assertThat(new LoggingConfiguration(null).setFormat(" ")
+ .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java
new file mode 100644
index 00000000000..2d719e4a657
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java
@@ -0,0 +1,184 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.bootstrapper;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import org.apache.commons.io.IOUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LoggingConfiguratorTest {
+ private static final String DEFAULT_CLASSPATH_CONF = "/org/sonar/batch/bootstrapper/logback.xml";
+ private static final String TEST_STR = "foo";
+ private LoggingConfiguration conf = new LoggingConfiguration();
+ private ByteArrayOutputStream out;
+ private SimpleLogListener listener;
+ @Rule
+ public TemporaryFolder folder = new TemporaryFolder();
+
+ @Before
+ public void setUp() {
+ out = new ByteArrayOutputStream();
+ conf = new LoggingConfiguration();
+ listener = new SimpleLogListener();
+ }
+
+ private class SimpleLogListener implements LogOutput {
+ String msg;
+ LogOutput.Level level;
+
+ @Override
+ public void log(String msg, LogOutput.Level level) {
+ this.msg = msg;
+ this.level = level;
+ }
+ }
+
+ @Test
+ public void testWithFile() throws FileNotFoundException, IOException {
+ InputStream is = this.getClass().getResourceAsStream(DEFAULT_CLASSPATH_CONF);
+ File tmpFolder = folder.getRoot();
+ File testFile = new File(tmpFolder, "test");
+ OutputStream os = new FileOutputStream(testFile);
+ IOUtils.copy(is, os);
+ os.close();
+
+ conf.setLogOutput(listener);
+ LoggingConfigurator.apply(conf, testFile);
+
+ Logger logger = LoggerFactory.getLogger(this.getClass());
+ logger.info(TEST_STR);
+
+ assertThat(listener.msg).endsWith(TEST_STR);
+ assertThat(listener.level).isEqualTo(LogOutput.Level.INFO);
+ }
+
+ @Test
+ public void testCustomAppender() throws UnsupportedEncodingException {
+ conf.setLogOutput(listener);
+ LoggingConfigurator.apply(conf);
+
+ Logger logger = LoggerFactory.getLogger(this.getClass());
+ logger.info(TEST_STR);
+
+ assertThat(listener.msg).endsWith(TEST_STR);
+ assertThat(listener.level).isEqualTo(LogOutput.Level.INFO);
+ }
+
+ @Test
+ public void testNoStdout() throws UnsupportedEncodingException {
+ System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8.name()));
+ conf.setLogOutput(listener);
+ LoggingConfigurator.apply(conf);
+
+ Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ logger.error(TEST_STR);
+ logger.info(TEST_STR);
+ logger.debug(TEST_STR);
+ assertThat(out.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testConfigureMultipleTimes() throws UnsupportedEncodingException {
+ System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8.name()));
+ conf.setLogOutput(listener);
+ LoggingConfigurator.apply(conf);
+
+ Logger logger = LoggerFactory.getLogger(this.getClass());
+ logger.debug("debug");
+ assertThat(listener.msg).isNull();
+
+ conf.setVerbose(true);
+ LoggingConfigurator.apply(conf);
+
+ logger.debug("debug");
+ assertThat(listener.msg).isEqualTo("debug");
+ }
+
+ @Test
+ public void testFormatNoEffect() throws UnsupportedEncodingException {
+ conf.setLogOutput(listener);
+ conf.setFormat("%t");
+
+ LoggingConfigurator.apply(conf);
+ Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ logger.info("info");
+
+ assertThat(listener.msg).isEqualTo("info");
+ }
+
+ @Test
+ public void testSqlClasspath() throws UnsupportedEncodingException {
+ String classpath = "/org/sonar/batch/bootstrapper/logback.xml";
+
+ conf.setLogOutput(listener);
+ conf.setShowSql(true);
+
+ LoggingConfigurator.apply(conf, classpath);
+
+ Logger logger = LoggerFactory.getLogger("java.sql");
+ logger.info("foo");
+
+ assertThat(listener.msg).endsWith(TEST_STR);
+ }
+
+ @Test
+ public void testNoListener() throws UnsupportedEncodingException {
+ System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8.name()));
+ LoggingConfigurator.apply(conf);
+
+ Logger logger = LoggerFactory.getLogger(this.getClass());
+ logger.info("info");
+
+ assertThat(new String(out.toByteArray(), StandardCharsets.UTF_8)).contains("info");
+ }
+
+ @Test
+ public void testNoSqlClasspath() throws UnsupportedEncodingException {
+ String classpath = "/org/sonar/batch/bootstrapper/logback.xml";
+
+ conf.setLogOutput(listener);
+ conf.setShowSql(false);
+
+ LoggingConfigurator.apply(conf, classpath);
+
+ Logger logger = LoggerFactory.getLogger("java.sql");
+ logger.info("foo");
+
+ assertThat(listener.msg).isNull();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java
new file mode 100644
index 00000000000..c8f105de443
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java
@@ -0,0 +1,91 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cache;
+
+import com.google.common.io.Files;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.home.cache.PersistentCache;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultProjectCacheStatusTest {
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ ProjectCacheStatus cacheStatus;
+ PersistentCache cache = mock(PersistentCache.class);
+
+ @Before
+ public void setUp() {
+ when(cache.getDirectory()).thenReturn(tmp.getRoot().toPath());
+ cacheStatus = new DefaultProjectCacheStatus(cache);
+ }
+
+ @Test
+ public void errorSave() throws IOException {
+ when(cache.getDirectory()).thenReturn(tmp.getRoot().toPath().resolve("unexistent_folder"));
+ cacheStatus = new DefaultProjectCacheStatus(cache);
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Failed to write cache sync status");
+ cacheStatus.save();
+ }
+
+ @Test
+ public void errorStatus() throws IOException {
+ Files.write("trash".getBytes(StandardCharsets.UTF_8), new File(tmp.getRoot(), "cache-sync-status"));
+ cacheStatus = new DefaultProjectCacheStatus(cache);
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Failed to read cache sync status");
+ cacheStatus.getSyncStatus();
+ }
+
+ @Test
+ public void testSave() {
+ cacheStatus.save();
+ assertThat(cacheStatus.getSyncStatus()).isNotNull();
+ assertThat(age(cacheStatus.getSyncStatus())).isLessThan(2000);
+ }
+
+ @Test
+ public void testDelete() {
+ cacheStatus.save();
+ cacheStatus.delete();
+ assertThat(cacheStatus.getSyncStatus()).isNull();
+ }
+
+ private long age(Date date) {
+ return (new Date().getTime()) - date.getTime();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/GlobalPersistentCacheProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/GlobalPersistentCacheProviderTest.java
new file mode 100644
index 00000000000..3f019caae02
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/GlobalPersistentCacheProviderTest.java
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cache;
+
+import org.sonar.home.cache.PersistentCache;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+
+import static org.junit.Assert.*;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public class GlobalPersistentCacheProviderTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private GlobalPersistentCacheProvider provider;
+ private GlobalProperties globalProperties;
+
+ @Before
+ public void setUp() {
+ HashMap<String, String> map = new HashMap<>();
+ map.put("sonar.userHome", temp.getRoot().getAbsolutePath());
+ globalProperties = new GlobalProperties(map);
+ provider = new GlobalPersistentCacheProvider();
+ }
+
+ @Test
+ public void test_path() {
+ PersistentCache cache = provider.provide(globalProperties);
+ assertThat(cache.getDirectory()).isEqualTo(temp.getRoot().toPath()
+ .resolve("ws_cache")
+ .resolve("http%3A%2F%2Flocalhost%3A9000")
+ .resolve("global"));
+ }
+
+ @Test
+ public void test_singleton() {
+ assertTrue(provider.provide(globalProperties) == provider.provide(globalProperties));
+ }
+
+ @Test
+ public void test_without_sonar_home() {
+ globalProperties = new GlobalProperties(new HashMap<String, String>());
+ PersistentCache cache = provider.provide(globalProperties);
+ assertThat(cache.getDirectory().toAbsolutePath().toString()).startsWith(findHome().toAbsolutePath().toString());
+
+ }
+
+ private static Path findHome() {
+ String home = System.getenv("SONAR_USER_HOME");
+
+ if (home != null) {
+ return Paths.get(home);
+ }
+
+ home = System.getProperty("user.home");
+ return Paths.get(home, ".sonar");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java
new file mode 100644
index 00000000000..c06bb94ca08
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cache;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Date;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.batch.repository.QualityProfileLoader;
+import org.sonar.batch.rule.ActiveRulesLoader;
+import org.sonar.batch.rule.LoadedActiveRule;
+import org.sonar.batch.rule.RulesLoader;
+import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class NonAssociatedCacheSynchronizerTest {
+ private NonAssociatedCacheSynchronizer synchronizer;
+
+ @Mock
+ private RulesLoader rulesLoader;
+ @Mock
+ private QualityProfileLoader qualityProfileLoader;
+ @Mock
+ private ActiveRulesLoader activeRulesLoader;
+ @Mock
+ private ProjectCacheStatus cacheStatus;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ QualityProfile pf = QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").build();
+ LoadedActiveRule ar = new LoadedActiveRule();
+
+ when(qualityProfileLoader.loadDefault(null, null)).thenReturn(ImmutableList.of(pf));
+ when(activeRulesLoader.load("profile", null)).thenReturn(ImmutableList.of(ar));
+
+ synchronizer = new NonAssociatedCacheSynchronizer(rulesLoader, qualityProfileLoader, activeRulesLoader, cacheStatus);
+ }
+
+ @Test
+ public void dont_sync_if_exists() {
+ when(cacheStatus.getSyncStatus()).thenReturn(new Date());
+ synchronizer.execute(false);
+ verifyZeroInteractions(rulesLoader, qualityProfileLoader, activeRulesLoader);
+ }
+
+ @Test
+ public void always_sync_if_force() {
+ when(cacheStatus.getSyncStatus()).thenReturn(new Date());
+ synchronizer.execute(true);
+ checkSync();
+ }
+
+ @Test
+ public void sync_if_doesnt_exist() {
+ synchronizer.execute(false);
+ checkSync();
+ }
+
+ private void checkSync() {
+ verify(cacheStatus).getSyncStatus();
+ verify(cacheStatus).save();
+ verify(rulesLoader).load(null);
+ verify(qualityProfileLoader).loadDefault(null, null);
+ verify(activeRulesLoader).load("profile", null);
+
+ verifyNoMoreInteractions(qualityProfileLoader, activeRulesLoader);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java
new file mode 100644
index 00000000000..52df42e035d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java
@@ -0,0 +1,197 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cache;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.batch.analysis.AnalysisProperties;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.repository.DefaultProjectRepositoriesLoader;
+import org.sonar.batch.repository.DefaultQualityProfileLoader;
+import org.sonar.batch.repository.DefaultServerIssuesLoader;
+import org.sonar.batch.repository.ProjectRepositories;
+import org.sonar.batch.repository.ProjectRepositoriesLoader;
+import org.sonar.batch.repository.QualityProfileLoader;
+import org.sonar.batch.repository.ServerIssuesLoader;
+import org.sonar.batch.repository.user.UserRepositoryLoader;
+import org.sonar.batch.rule.ActiveRulesLoader;
+import org.sonar.batch.rule.DefaultActiveRulesLoader;
+import org.sonar.batch.rule.LoadedActiveRule;
+import org.sonar.batch.rule.RulesLoader;
+import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class ProjectCacheSynchronizerTest {
+ private static final String PROJECT_KEY = "org.codehaus.sonar-plugins:sonar-scm-git-plugin";
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Mock
+ private ProjectDefinition project;
+ @Mock
+ private ProjectCacheStatus cacheStatus;
+ @Mock
+ private DefaultAnalysisMode analysisMode;
+ @Mock
+ private AnalysisProperties properties;
+ @Mock
+ private RulesLoader rulesLoader;
+
+ private ServerIssuesLoader issuesLoader;
+ private UserRepositoryLoader userRepositoryLoader;
+ private QualityProfileLoader qualityProfileLoader;
+ private ActiveRulesLoader activeRulesLoader;
+ private ProjectRepositoriesLoader projectRepositoriesLoader;
+
+ @Before
+ public void setUp() throws IOException {
+ MockitoAnnotations.initMocks(this);
+
+ when(analysisMode.isIssues()).thenReturn(true);
+ when(properties.properties()).thenReturn(new HashMap<String, String>());
+ }
+
+ private ProjectCacheSynchronizer createMockedLoaders(boolean projectExists, Date lastAnalysisDate) {
+ issuesLoader = mock(DefaultServerIssuesLoader.class);
+ userRepositoryLoader = mock(UserRepositoryLoader.class);
+ qualityProfileLoader = mock(DefaultQualityProfileLoader.class);
+ activeRulesLoader = mock(DefaultActiveRulesLoader.class);
+ projectRepositoriesLoader = mock(DefaultProjectRepositoriesLoader.class);
+
+ QualityProfile pf = QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").build();
+ LoadedActiveRule ar = new LoadedActiveRule();
+ ProjectRepositories repo = mock(ProjectRepositories.class);
+
+ when(qualityProfileLoader.load(PROJECT_KEY, null, null)).thenReturn(ImmutableList.of(pf));
+ when(qualityProfileLoader.loadDefault(null, null)).thenReturn(ImmutableList.of(pf));
+ when(activeRulesLoader.load("profile", null)).thenReturn(ImmutableList.of(ar));
+ when(repo.lastAnalysisDate()).thenReturn(lastAnalysisDate);
+ when(repo.exists()).thenReturn(projectExists);
+ when(projectRepositoriesLoader.load(anyString(), anyBoolean(), any(MutableBoolean.class))).thenReturn(repo);
+
+ return new ProjectCacheSynchronizer(rulesLoader, qualityProfileLoader, projectRepositoriesLoader, activeRulesLoader, issuesLoader, userRepositoryLoader, cacheStatus);
+ }
+
+ @Test
+ public void testLoadersUsage() {
+ ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date());
+ synchronizer.load(PROJECT_KEY, false);
+
+ verify(issuesLoader).load(eq(PROJECT_KEY), any(Function.class));
+ verify(rulesLoader).load(null);
+ verify(qualityProfileLoader).load(PROJECT_KEY, null, null);
+ verify(activeRulesLoader).load("profile", null);
+ verify(projectRepositoriesLoader).load(eq(PROJECT_KEY), eq(true), any(MutableBoolean.class));
+
+ verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectRepositoriesLoader);
+ }
+
+ @Test
+ public void testLoadersUsage_NoLastAnalysis() {
+ ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, null);
+ synchronizer.load(PROJECT_KEY, false);
+
+ verify(projectRepositoriesLoader).load(eq(PROJECT_KEY), eq(true), any(MutableBoolean.class));
+ verify(qualityProfileLoader).load(PROJECT_KEY, null, null);
+ verify(activeRulesLoader).load("profile", null);
+
+ verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectRepositoriesLoader);
+ }
+
+ @Test
+ public void testLoadersUsage_ProjectDoesntExist() {
+ ProjectCacheSynchronizer synchronizer = createMockedLoaders(false, null);
+ synchronizer.load(PROJECT_KEY, false);
+
+ verify(projectRepositoriesLoader).load(eq(PROJECT_KEY), eq(true), any(MutableBoolean.class));
+ verify(qualityProfileLoader).loadDefault(null, null);
+ verify(activeRulesLoader).load("profile", null);
+
+ verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectRepositoriesLoader);
+ }
+
+ @Test
+ public void testLastAnalysisToday() {
+ ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date());
+
+ when(cacheStatus.getSyncStatus()).thenReturn(new Date());
+ synchronizer.load(PROJECT_KEY, false);
+
+ verify(cacheStatus).getSyncStatus();
+ verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectRepositoriesLoader, cacheStatus);
+ }
+
+ @Test
+ public void testLastAnalysisYesterday() {
+ ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date());
+
+ Date d = new Date(new Date().getTime() - 60 * 60 * 24 * 1000);
+ when(cacheStatus.getSyncStatus()).thenReturn(d);
+ synchronizer.load(PROJECT_KEY, false);
+
+ verify(cacheStatus).save();
+ verify(cacheStatus).getSyncStatus();
+ }
+
+ @Test
+ public void testDontFailOnError() {
+ ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date());
+
+ Date d = new Date(new Date().getTime() - 60 * 60 * 24 * 1000);
+ when(cacheStatus.getSyncStatus()).thenReturn(d);
+
+ when(projectRepositoriesLoader.load(anyString(), anyBoolean(), any(MutableBoolean.class))).thenThrow(IllegalStateException.class);
+ synchronizer.load(PROJECT_KEY, false);
+
+ verify(cacheStatus).getSyncStatus();
+ verifyNoMoreInteractions(cacheStatus);
+ }
+
+ @Test
+ public void testForce() {
+ ProjectCacheSynchronizer synchronizer = createMockedLoaders(true, new Date());
+
+ when(cacheStatus.getSyncStatus()).thenReturn(new Date());
+ synchronizer.load(PROJECT_KEY, true);
+
+ verify(cacheStatus).save();
+ verify(cacheStatus).getSyncStatus();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectPersistentCacheProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectPersistentCacheProviderTest.java
new file mode 100644
index 00000000000..69c142556ae
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectPersistentCacheProviderTest.java
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cache;
+
+import org.sonar.api.batch.bootstrap.ProjectKey;
+
+import org.sonar.batch.util.BatchUtils;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.sonar.batch.cache.ProjectPersistentCacheProvider;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Collections;
+
+import static org.mockito.Mockito.mock;
+import org.junit.Before;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Test;
+
+public class ProjectPersistentCacheProviderTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private ProjectPersistentCacheProvider provider = null;
+ private GlobalProperties props = null;
+ private DefaultAnalysisMode mode = null;
+ private ProjectKey key = null;
+
+ @Before
+ public void prepare() {
+ key = new ProjectKeySupplier("proj");
+ props = new GlobalProperties(Collections.<String, String>emptyMap());
+ mode = mock(DefaultAnalysisMode.class);
+ provider = new ProjectPersistentCacheProvider();
+ }
+
+ @Test
+ public void test_singleton() {
+ assertThat(provider.provide(props, mode, key)).isEqualTo(provider.provide(props, mode, key));
+ }
+
+ @Test
+ public void test_cache_dir() {
+ assertThat(provider.provide(props, mode, key).getDirectory().toFile()).exists().isDirectory();
+ }
+
+ @Test
+ public void test_home() {
+ File f = temp.getRoot();
+ props.properties().put("sonar.userHome", f.getAbsolutePath());
+ Path expected = f.toPath()
+ .resolve("ws_cache")
+ .resolve("http%3A%2F%2Flocalhost%3A9000")
+ .resolve( BatchUtils.getServerVersion())
+ .resolve("projects")
+ .resolve("proj");
+
+ assertThat(provider.provide(props, mode, key).getDirectory()).isEqualTo(expected);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java
new file mode 100644
index 00000000000..948f888cd68
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cache;
+
+import java.util.HashMap;
+import org.junit.Test;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.home.cache.PersistentCache;
+import org.sonar.scanner.protocol.input.ProjectRepositories;
+import org.sonarqube.ws.client.WsClient;
+
+import static org.mockito.Mockito.mock;
+
+public class ProjectSyncContainerTest {
+ private ComponentContainer createParentContainer() {
+ PersistentCache cache = mock(PersistentCache.class);
+ WsClient server = mock(WsClient.class);
+
+ GlobalProperties globalProps = new GlobalProperties(new HashMap<String, String>());
+ ComponentContainer parent = new ComponentContainer();
+ parent.add(cache);
+ parent.add(server);
+ parent.add(globalProps);
+ return parent;
+ }
+
+ @Test
+ public void testProjectRepository() {
+ ProjectSyncContainer container = new ProjectSyncContainer(createParentContainer(), "my:project", true);
+ container.doBeforeStart();
+ container.getPicoContainer().start();
+ container.getComponentByType(ProjectRepositories.class);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java
new file mode 100644
index 00000000000..ce9d88a037c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cache;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.batch.bootstrap.BatchWsClient;
+import org.sonar.batch.cache.WSLoader.LoadStrategy;
+import org.sonar.home.cache.PersistentCache;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class StrategyWSLoaderProviderTest {
+ @Mock
+ private PersistentCache cache;
+
+ @Mock
+ private BatchWsClient client;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testStrategy() {
+ StrategyWSLoaderProvider provider = new StrategyWSLoaderProvider(LoadStrategy.CACHE_FIRST);
+ WSLoader wsLoader = provider.provide(cache, client);
+
+ assertThat(wsLoader.getDefaultStrategy()).isEqualTo(LoadStrategy.CACHE_FIRST);
+ }
+
+ @Test
+ public void testSingleton() {
+ StrategyWSLoaderProvider provider = new StrategyWSLoaderProvider(LoadStrategy.CACHE_FIRST);
+ WSLoader wsLoader = provider.provide(cache, client);
+
+ assertThat(provider.provide(null, null)).isEqualTo(wsLoader);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/WSLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/WSLoaderTest.java
new file mode 100644
index 00000000000..ad7bb763d67
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cache/WSLoaderTest.java
@@ -0,0 +1,264 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cache;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.commons.io.IOUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+import org.sonar.batch.bootstrap.BatchWsClient;
+import org.sonar.batch.cache.WSLoader.LoadStrategy;
+import org.sonar.home.cache.PersistentCache;
+import org.sonarqube.ws.client.HttpException;
+import org.sonarqube.ws.client.MockWsResponse;
+import org.sonarqube.ws.client.WsRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class WSLoaderTest {
+ private final static String ID = "dummy";
+ private final static String cacheValue = "cache";
+ private final static String serverValue = "server";
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ BatchWsClient ws = mock(BatchWsClient.class, Mockito.RETURNS_DEEP_STUBS);
+ PersistentCache cache = mock(PersistentCache.class);
+
+ @Test
+ public void dont_retry_server_offline() throws IOException {
+ turnServerOffline();
+ when(cache.getString(ID)).thenReturn(cacheValue);
+ WSLoader underTest = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+
+ assertResult(underTest.loadString(ID), cacheValue, true);
+ assertResult(underTest.loadString(ID), cacheValue, true);
+
+ assertUsedServer(1);
+ assertUsedCache(2);
+ }
+
+ @Test
+ public void get_stream_from_cache() throws IOException {
+ InputStream is = IOUtils.toInputStream("is");
+ when(cache.getStream(ID)).thenReturn(is);
+
+ WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws);
+ WSLoaderResult<InputStream> result = loader.loadStream(ID);
+
+ assertThat(result.get()).isEqualTo(is);
+ verify(cache).getStream(ID);
+ verifyNoMoreInteractions(cache, ws);
+ }
+
+ @Test
+ public void put_stream_in_cache() throws IOException {
+ InputStream input = IOUtils.toInputStream("is");
+
+ when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(input));
+ when(cache.getStream(ID)).thenReturn(input);
+
+ // SERVER_FIRST -> load from server then put to cache
+ WSLoader underTest = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+ WSLoaderResult<InputStream> result = underTest.loadStream(ID);
+ assertThat(result.get()).isEqualTo(input);
+
+ InOrder inOrder = inOrder(ws, cache);
+ inOrder.verify(ws).call(any(WsRequest.class));
+ inOrder.verify(cache).put(eq(ID), any(InputStream.class));
+ inOrder.verify(cache).getStream(ID);
+ verifyNoMoreInteractions(cache, ws);
+ }
+
+ @Test
+ public void test_cache_strategy_fallback() throws IOException {
+ turnCacheEmpty();
+ when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue));
+ WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws);
+
+ assertResult(loader.loadString(ID), serverValue, false);
+
+ InOrder inOrder = inOrder(ws, cache);
+ inOrder.verify(cache).getString(ID);
+ inOrder.verify(ws).call(any(WsRequest.class));
+ }
+
+ @Test
+ public void test_server_strategy_fallback() throws IOException {
+ turnServerOffline();
+ when(cache.getString(ID)).thenReturn(cacheValue);
+ WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+
+ assertResult(loader.loadString(ID), cacheValue, true);
+
+ InOrder inOrder = inOrder(ws, cache);
+ inOrder.verify(ws).call(any(WsRequest.class));
+ inOrder.verify(cache).getString(ID);
+ }
+
+ @Test
+ public void test_put_cache() throws IOException {
+ when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue));
+ WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+ loader.loadString(ID);
+ verify(cache).put(ID, serverValue.getBytes());
+ }
+
+ @Test
+ public void test_throw_cache_exception_fallback() throws IOException {
+ turnServerOffline();
+
+ when(cache.getString(ID)).thenThrow(new NullPointerException());
+ WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+
+ try {
+ loader.loadString(ID);
+ fail("NPE expected");
+ } catch (NullPointerException e) {
+ assertUsedServer(1);
+ assertUsedCache(1);
+ }
+ }
+
+ @Test
+ public void test_throw_cache_exception() throws IOException {
+ when(cache.getString(ID)).thenThrow(new IllegalStateException());
+
+ WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws);
+
+ try {
+ loader.loadString(ID);
+ fail("IllegalStateException expected");
+ } catch (IllegalStateException e) {
+ assertUsedServer(0);
+ assertUsedCache(1);
+ }
+ }
+
+ @Test
+ public void test_throw_http_exceptions() {
+ when(ws.call(any(WsRequest.class))).thenThrow(new HttpException("url", 500));
+
+ WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+
+ try {
+ loader.loadString(ID);
+ fail("IllegalStateException expected");
+ } catch (HttpException e) {
+ // cache should not be used
+ verifyNoMoreInteractions(cache);
+ }
+ }
+
+ @Test
+ public void test_server_only_not_available() {
+ turnServerOffline();
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Server is not available");
+
+ WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, ws);
+ loader.loadString(ID);
+ }
+
+ @Test
+ public void test_server_cache_not_available() throws IOException {
+ turnServerOffline();
+ turnCacheEmpty();
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Server is not accessible and data is not cached");
+
+ WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+ loader.loadString(ID);
+ }
+
+ @Test
+ public void test_cache_only_available() throws IOException {
+ turnCacheEmpty();
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Data is not cached");
+
+ WSLoader loader = new WSLoader(LoadStrategy.CACHE_ONLY, cache, ws);
+ loader.loadString(ID);
+ }
+
+ @Test
+ public void test_server_strategy() throws IOException {
+ when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue));
+ WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+ assertResult(loader.loadString(ID), serverValue, false);
+
+ // should not fetch from cache
+ verify(cache).put(ID, serverValue.getBytes());
+ verifyNoMoreInteractions(cache);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void test_server_only() throws IOException {
+ turnServerOffline();
+ WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, ws);
+ loader.loadString(ID);
+ }
+
+ @Test
+ public void test_string() {
+ when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue));
+ WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+ assertResult(loader.loadString(ID), serverValue, false);
+ }
+
+ private void assertUsedCache(int times) throws IOException {
+ verify(cache, times(times)).getString(ID);
+ }
+
+ private void assertUsedServer(int times) {
+ verify(ws, times(times)).call(any(WsRequest.class));
+ }
+
+ private void assertResult(WSLoaderResult<String> result, String expected, boolean fromCache) {
+ assertThat(result).isNotNull();
+ assertThat(result.get()).isEqualTo(expected);
+ assertThat(result.isFromCache()).isEqualTo(fromCache);
+ }
+
+ private void turnServerOffline() {
+ when(ws.call(any(WsRequest.class))).thenThrow(new IllegalStateException());
+ }
+
+ private void turnCacheEmpty() throws IOException {
+ when(cache.getString(ID)).thenReturn(null);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java
new file mode 100644
index 00000000000..814e7d69d38
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java
@@ -0,0 +1,32 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cpd;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CpdComponentsTest {
+
+ @Test
+ public void getExtensions() {
+ assertThat(CpdComponents.all().size()).isGreaterThan(0);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdExecutorTest.java
new file mode 100644
index 00000000000..6a6c209348f
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdExecutorTest.java
@@ -0,0 +1,239 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cpd;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
+import org.sonar.batch.index.BatchComponent;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.report.ReportPublisher;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.duplications.index.CloneGroup;
+import org.sonar.duplications.index.ClonePart;
+import org.sonar.scanner.protocol.output.ScannerReportReader;
+import org.sonar.scanner.protocol.output.ScannerReportWriter;
+import org.sonar.scanner.protocol.output.ScannerReport.Duplicate;
+import org.sonar.scanner.protocol.output.ScannerReport.Duplication;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class CpdExecutorTest {
+ private CpdExecutor executor;
+ private Settings settings;
+ private SonarCpdBlockIndex index;
+ private ReportPublisher publisher;
+ private BatchComponentCache componentCache;
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ // private AbstractCpdEngine engine;
+
+ private ScannerReportReader reader;
+ private BatchComponent batchComponent1;
+ private BatchComponent batchComponent2;
+ private BatchComponent batchComponent3;
+
+ @Before
+ public void setUp() throws IOException {
+ File outputDir = temp.newFolder();
+
+ settings = new Settings();
+ index = mock(SonarCpdBlockIndex.class);
+ publisher = mock(ReportPublisher.class);
+ when(publisher.getWriter()).thenReturn(new ScannerReportWriter(outputDir));
+ componentCache = new BatchComponentCache();
+ executor = new CpdExecutor(settings, index, publisher, componentCache);
+ reader = new ScannerReportReader(outputDir);
+
+ Project p = new Project("foo");
+ componentCache.add(p, null).setInputComponent(new DefaultInputModule("foo"));
+
+ batchComponent1 = createComponent("src/Foo.php", 5);
+ batchComponent2 = createComponent("src/Foo2.php", 5);
+ batchComponent3 = createComponent("src/Foo3.php", 5);
+ }
+
+ private BatchComponent createComponent(String relativePath, int lines) {
+ org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("relativePath").setEffectiveKey("foo:" + relativePath);
+ return componentCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", relativePath).setLines(lines));
+ }
+
+ @Test
+ public void defaultMinimumTokens() {
+ assertThat(executor.getMinimumTokens("java")).isEqualTo(100);
+ }
+
+ @Test
+ public void minimumTokensByLanguage() {
+ settings.setProperty("sonar.cpd.java.minimumTokens", "42");
+ settings.setProperty("sonar.cpd.php.minimumTokens", "33");
+ assertThat(executor.getMinimumTokens("java")).isEqualTo(42);
+
+ settings.setProperty("sonar.cpd.java.minimumTokens", "42");
+ settings.setProperty("sonar.cpd.php.minimumTokens", "33");
+ assertThat(executor.getMinimumTokens("php")).isEqualTo(33);
+ }
+
+ @Test
+ public void testNothingToSave() {
+ executor.saveDuplications(batchComponent1, Collections.<CloneGroup>emptyList());
+ assertThat(reader.readComponentDuplications(batchComponent1.batchId())).hasSize(0);
+ }
+
+ @Test
+ public void reportOneSimpleDuplicationBetweenTwoFiles() {
+ List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart(batchComponent1.key(), 0, 2, 4), new ClonePart(batchComponent2.key(), 0, 15, 17)));
+
+ executor.saveDuplications(batchComponent1, groups);
+
+ Duplication[] dups = readDuplications(1);
+ assertDuplication(dups[0], 2, 4, batchComponent2.batchId(), 15, 17);
+ }
+
+ @Test
+ public void reportDuplicationOnSameFile() throws Exception {
+ List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart(batchComponent1.key(), 0, 5, 204), new ClonePart(batchComponent1.key(), 0, 215, 414)));
+ executor.saveDuplications(batchComponent1, groups);
+
+ Duplication[] dups = readDuplications(1);
+ assertDuplication(dups[0], 5, 204, null, 215, 414);
+ }
+
+ @Test
+ public void reportTooManyDuplicates() throws Exception {
+ // 1 origin part + 101 duplicates = 102
+ List<ClonePart> parts = new ArrayList<>(CpdExecutor.MAX_CLONE_PART_PER_GROUP + 2);
+ for (int i = 0; i < CpdExecutor.MAX_CLONE_PART_PER_GROUP + 2; i++) {
+ parts.add(new ClonePart(batchComponent1.key(), i, i, i + 1));
+ }
+ List<CloneGroup> groups = Arrays.asList(CloneGroup.builder().setLength(0).setOrigin(parts.get(0)).setParts(parts).build());
+ executor.saveDuplications(batchComponent1, groups);
+
+ Duplication[] dups = readDuplications(1);
+ assertThat(dups[0].getDuplicateList()).hasSize(CpdExecutor.MAX_CLONE_PART_PER_GROUP);
+
+ assertThat(logTester.logs(LoggerLevel.WARN))
+ .contains("Too many duplication references on file " + batchComponent1.inputComponent() + " for block at line 0. Keep only the first "
+ + CpdExecutor.MAX_CLONE_PART_PER_GROUP + " references.");
+ }
+
+ @Test
+ public void reportTooManyDuplications() throws Exception {
+ // 1 origin part + 101 duplicates = 102
+ List<CloneGroup> dups = new ArrayList<>(CpdExecutor.MAX_CLONE_GROUP_PER_FILE + 1);
+ for (int i = 0; i < CpdExecutor.MAX_CLONE_GROUP_PER_FILE + 1; i++) {
+ ClonePart clonePart = new ClonePart(batchComponent1.key(), i, i, i + 1);
+ ClonePart dupPart = new ClonePart(batchComponent1.key(), i + 1, i + 1, i + 2);
+ dups.add(newCloneGroup(clonePart, dupPart));
+ }
+ executor.saveDuplications(batchComponent1, dups);
+
+ assertThat(reader.readComponentDuplications(batchComponent1.batchId())).hasSize(CpdExecutor.MAX_CLONE_GROUP_PER_FILE);
+
+ assertThat(logTester.logs(LoggerLevel.WARN))
+ .contains("Too many duplication groups on file " + batchComponent1.inputComponent() + ". Keep only the first " + CpdExecutor.MAX_CLONE_GROUP_PER_FILE + " groups.");
+ }
+
+ @Test
+ public void reportOneDuplicatedGroupInvolvingMoreThanTwoFiles() throws Exception {
+ List<CloneGroup> groups = Arrays
+ .asList(newCloneGroup(new ClonePart(batchComponent1.key(), 0, 5, 204), new ClonePart(batchComponent2.key(), 0, 15, 214), new ClonePart(batchComponent3.key(), 0, 25, 224)));
+ executor.saveDuplications(batchComponent1, groups);
+
+ Duplication[] dups = readDuplications(1);
+ assertDuplication(dups[0], 5, 204, 2);
+ assertDuplicate(dups[0].getDuplicate(0), batchComponent2.batchId(), 15, 214);
+ assertDuplicate(dups[0].getDuplicate(1), batchComponent3.batchId(), 25, 224);
+ }
+
+ @Test
+ public void reportTwoDuplicatedGroupsInvolvingThreeFiles() throws Exception {
+ List<CloneGroup> groups = Arrays.asList(
+ newCloneGroup(new ClonePart(batchComponent1.key(), 0, 5, 204), new ClonePart(batchComponent2.key(), 0, 15, 214)),
+ newCloneGroup(new ClonePart(batchComponent1.key(), 0, 15, 214), new ClonePart(batchComponent3.key(), 0, 15, 214)));
+ executor.saveDuplications(batchComponent1, groups);
+
+ Duplication[] dups = readDuplications(2);
+ assertDuplication(dups[0], 5, 204, batchComponent2.batchId(), 15, 214);
+ assertDuplication(dups[1], 15, 214, batchComponent3.batchId(), 15, 214);
+ }
+
+ private Duplication[] readDuplications(int expected) {
+ assertThat(reader.readComponentDuplications(batchComponent1.batchId())).hasSize(expected);
+ Duplication[] duplications = new Duplication[expected];
+ CloseableIterator<Duplication> dups = reader.readComponentDuplications(batchComponent1.batchId());
+
+ for(int i = 0; i< expected; i++) {
+ duplications[i] = dups.next();
+ }
+ dups.close();
+ return duplications;
+ }
+
+ private void assertDuplicate(Duplicate d, int otherFileRef, int rangeStartLine, int rangeEndLine) {
+ assertThat(d.getOtherFileRef()).isEqualTo(otherFileRef);
+ assertThat(d.getRange().getStartLine()).isEqualTo(rangeStartLine);
+ assertThat(d.getRange().getEndLine()).isEqualTo(rangeEndLine);
+ }
+
+ private void assertDuplication(Duplication d, int originStartLine, int originEndLine, int numDuplicates) {
+ assertThat(d.getOriginPosition().getStartLine()).isEqualTo(originStartLine);
+ assertThat(d.getOriginPosition().getEndLine()).isEqualTo(originEndLine);
+ assertThat(d.getDuplicateList()).hasSize(numDuplicates);
+ }
+
+ private void assertDuplication(Duplication d, int originStartLine, int originEndLine, Integer otherFileRef, int rangeStartLine, int rangeEndLine) {
+ assertThat(d.getOriginPosition().getStartLine()).isEqualTo(originStartLine);
+ assertThat(d.getOriginPosition().getEndLine()).isEqualTo(originEndLine);
+ assertThat(d.getDuplicateList()).hasSize(1);
+ if(otherFileRef != null) {
+ assertThat(d.getDuplicate(0).getOtherFileRef()).isEqualTo(otherFileRef);
+ } else {
+ assertThat(d.getDuplicate(0).hasOtherFileRef()).isFalse();
+ }
+ assertThat(d.getDuplicate(0).getRange().getStartLine()).isEqualTo(rangeStartLine);
+ assertThat(d.getDuplicate(0).getRange().getEndLine()).isEqualTo(rangeEndLine);
+ }
+
+ private CloneGroup newCloneGroup(ClonePart... parts) {
+ return CloneGroup.builder().setLength(0).setOrigin(parts[0]).setParts(Arrays.asList(parts)).build();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java
new file mode 100644
index 00000000000..ce27f0776c4
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cpd;
+
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Java;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CpdSensorTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ JavaCpdBlockIndexer sonarEngine;
+ DefaultCpdBlockIndexer sonarBridgeEngine;
+ CpdSensor sensor;
+ Settings settings;
+
+ @Before
+ public void setUp() throws IOException {
+ sonarEngine = new JavaCpdBlockIndexer(null, null, null);
+ sonarBridgeEngine = new DefaultCpdBlockIndexer(new CpdMappings(), null, null, null);
+ settings = new Settings(new PropertyDefinitions(CpdComponents.class));
+
+ DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder().toPath());
+ sensor = new CpdSensor(sonarEngine, sonarBridgeEngine, settings, fs);
+ }
+
+ @Test
+ public void test_global_skip() {
+ settings.setProperty("sonar.cpd.skip", true);
+ assertThat(sensor.isSkipped(Java.KEY)).isTrue();
+ }
+
+ @Test
+ public void should_not_skip_by_default() {
+ assertThat(sensor.isSkipped(Java.KEY)).isFalse();
+ }
+
+ @Test
+ public void should_skip_by_language() {
+ settings.setProperty("sonar.cpd.skip", false);
+ settings.setProperty("sonar.cpd.php.skip", true);
+
+ assertThat(sensor.isSkipped("php")).isTrue();
+ assertThat(sensor.isSkipped(Java.KEY)).isFalse();
+ }
+
+ @Test
+ public void test_engine() {
+ assertThat(sensor.getEngine(Java.KEY)).isSameAs(sonarEngine);
+ assertThat(sensor.getEngine("PHP")).isSameAs(sonarBridgeEngine);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java
new file mode 100644
index 00000000000..9fcd03ac940
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cpd;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.sonar.api.config.Settings;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class DefaultCpdBlockIndexerTest {
+
+ private DefaultCpdBlockIndexer engine;
+ private Settings settings;
+
+ @Before
+ public void init() {
+ settings = new Settings();
+ engine = new DefaultCpdBlockIndexer(null, null, settings, null);
+ }
+
+ @Test
+ public void shouldLogExclusions() {
+ Logger logger = mock(Logger.class);
+ engine.logExclusions(new String[0], logger);
+ verify(logger, never()).info(anyString());
+
+ logger = mock(Logger.class);
+ engine.logExclusions(new String[] {"Foo*", "**/Bar*"}, logger);
+
+ String message = "Copy-paste detection exclusions:"
+ + "\n Foo*"
+ + "\n **/Bar*";
+ verify(logger, times(1)).info(message);
+ }
+
+ @Test
+ public void shouldReturnDefaultBlockSize() {
+ assertThat(DefaultCpdBlockIndexer.getDefaultBlockSize("cobol")).isEqualTo(30);
+ assertThat(DefaultCpdBlockIndexer.getDefaultBlockSize("abap")).isEqualTo(20);
+ assertThat(DefaultCpdBlockIndexer.getDefaultBlockSize("other")).isEqualTo(10);
+ }
+
+ @Test
+ public void defaultBlockSize() {
+
+ assertThat(engine.getBlockSize("java")).isEqualTo(10);
+ }
+
+ @Test
+ public void blockSizeForCobol() {
+ settings.setProperty("sonar.cpd.cobol.minimumLines", "42");
+
+ assertThat(engine.getBlockSize("cobol")).isEqualTo(42);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DuplicationPredicatesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DuplicationPredicatesTest.java
new file mode 100644
index 00000000000..6b6fa4fa0e8
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DuplicationPredicatesTest.java
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cpd;
+
+import com.google.common.base.Predicate;
+import org.junit.Test;
+import org.sonar.duplications.index.CloneGroup;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DuplicationPredicatesTest {
+
+ @Test
+ public void testNumberOfUnitsNotLessThan() {
+ Predicate<CloneGroup> predicate = DuplicationPredicates.numberOfUnitsNotLessThan(5);
+ assertThat(predicate.apply(CloneGroup.builder().setLengthInUnits(6).build())).isTrue();
+ assertThat(predicate.apply(CloneGroup.builder().setLengthInUnits(5).build())).isTrue();
+ assertThat(predicate.apply(CloneGroup.builder().setLengthInUnits(4).build())).isFalse();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java
new file mode 100644
index 00000000000..851fb523815
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java
@@ -0,0 +1,106 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.cpd;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.duplications.block.Block;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+public class JavaCpdBlockIndexerTest {
+ private static final String JAVA = "java";
+
+ @Mock
+ private SonarCpdBlockIndex index;
+
+ @Captor
+ private ArgumentCaptor<List<Block>> blockCaptor;
+
+ private Settings settings;
+ private JavaCpdBlockIndexer engine;
+ private DefaultInputFile file;
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws IOException {
+ MockitoAnnotations.initMocks(this);
+
+ File baseDir = temp.newFolder();
+ DefaultFileSystem fs = new DefaultFileSystem(baseDir);
+ file = new DefaultInputFile("foo", "src/ManyStatements.java").setLanguage(JAVA);
+ fs.add(file);
+ BatchComponentCache batchComponentCache = new BatchComponentCache();
+ batchComponentCache.add(org.sonar.api.resources.File.create("src/Foo.java").setEffectiveKey("foo:src/ManyStatements.java"), null).setInputComponent(file);
+ File ioFile = file.file();
+ FileUtils.copyURLToFile(this.getClass().getResource("ManyStatements.java"), ioFile);
+
+ settings = new Settings();
+ engine = new JavaCpdBlockIndexer(fs, settings, index);
+ }
+
+ @Test
+ public void languageSupported() {
+ JavaCpdBlockIndexer engine = new JavaCpdBlockIndexer(mock(FileSystem.class), new Settings(), index);
+ assertThat(engine.isLanguageSupported(JAVA)).isTrue();
+ assertThat(engine.isLanguageSupported("php")).isFalse();
+ }
+
+ @Test
+ public void testExclusions() {
+ settings.setProperty(CoreProperties.CPD_EXCLUSIONS, "**");
+ engine.index(JAVA);
+ verifyZeroInteractions(index);
+ }
+
+ @Test
+ public void testJavaIndexing() throws Exception {
+ engine.index(JAVA);
+
+ verify(index).insert(eq(file), blockCaptor.capture());
+ List<Block> blockList = blockCaptor.getValue();
+
+ assertThat(blockList).hasSize(26);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/deprecated/perspectives/PerspectiveBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/deprecated/perspectives/PerspectiveBuilderTest.java
new file mode 100644
index 00000000000..b07671362ce
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/deprecated/perspectives/PerspectiveBuilderTest.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.deprecated.perspectives;
+
+import org.junit.Test;
+import org.sonar.api.component.Perspective;
+import org.sonar.batch.index.BatchComponent;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PerspectiveBuilderTest {
+ @Test
+ public void testGetPerspectiveClass() throws Exception {
+ PerspectiveBuilder<FakePerspective> builder = new PerspectiveBuilder<FakePerspective>(FakePerspective.class) {
+ @Override
+ public FakePerspective loadPerspective(Class<FakePerspective> perspectiveClass, BatchComponent component) {
+ return null;
+ }
+ };
+
+ assertThat(builder.getPerspectiveClass()).isEqualTo(FakePerspective.class);
+ }
+
+ static interface FakePerspective extends Perspective {
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/events/BatchStepEventTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/events/BatchStepEventTest.java
new file mode 100644
index 00000000000..3f3738b3900
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/events/BatchStepEventTest.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.events;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class BatchStepEventTest {
+
+ private BatchStepEvent batchStepEvent = new BatchStepEvent("foo", true);
+
+ @Test
+ public void testGetType() {
+ assertThat(batchStepEvent.getType()).isEqualTo(BatchStepHandler.class);
+ }
+
+ @Test
+ public void testDispatch() {
+ BatchStepHandler handler = mock(BatchStepHandler.class);
+ batchStepEvent.dispatch(handler);
+
+ verify(handler).onBatchStep(batchStepEvent);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/events/EventBusTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/events/EventBusTest.java
new file mode 100644
index 00000000000..78fcd4926c2
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/events/EventBusTest.java
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.events;
+
+import org.junit.Test;
+import org.sonar.api.batch.events.EventHandler;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class EventBusTest {
+
+ @Test
+ public void shouldNotifyAboutEvent() {
+ FirstHandler firstHandler = mock(FirstHandler.class);
+ SecondHandler secondHandler = mock(SecondHandler.class);
+ EventBus eventBus = new EventBus(new EventHandler[] { firstHandler, secondHandler });
+
+ FirstEvent firstEvent = new FirstEvent();
+ eventBus.fireEvent(firstEvent);
+ SecondEvent secondEvent = new SecondEvent();
+ eventBus.fireEvent(secondEvent);
+
+ verify(firstHandler).onEvent(firstEvent);
+ verify(secondHandler).onEvent(secondEvent);
+ }
+
+ interface FirstHandler extends EventHandler {
+ void onEvent(FirstEvent event);
+ }
+
+ static class FirstEvent extends BatchEvent<FirstHandler> {
+ @Override
+ protected void dispatch(FirstHandler handler) {
+ handler.onEvent(this);
+ }
+
+ @Override
+ public Class getType() {
+ return FirstHandler.class;
+ }
+ }
+
+ interface SecondHandler extends EventHandler {
+ void onEvent(SecondEvent event);
+ }
+
+ static class SecondEvent extends BatchEvent<SecondHandler> {
+ @Override
+ protected void dispatch(SecondHandler handler) {
+ handler.onEvent(this);
+ }
+
+ @Override
+ public Class getType() {
+ return SecondHandler.class;
+ }
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/AbstractCachesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/AbstractCachesTest.java
new file mode 100644
index 00000000000..b3e6592d9d2
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/AbstractCachesTest.java
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.index;
+
+import org.junit.After;
+
+import org.junit.Before;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import com.google.common.collect.ImmutableMap;
+import org.sonar.api.CoreProperties;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.sonar.batch.bootstrap.GlobalTempFolderProvider;
+
+import java.util.Map;
+
+import org.junit.ClassRule;
+import org.junit.rules.TemporaryFolder;
+
+public abstract class AbstractCachesTest {
+ @ClassRule
+ public static TemporaryFolder temp = new TemporaryFolder();
+
+ protected static CachesManager cachesManager;
+ protected Caches caches;
+
+ private static CachesManager createCacheOnTemp() {
+ Map<String, String> props = ImmutableMap.of(CoreProperties.WORKING_DIRECTORY, temp.getRoot().getAbsolutePath(),
+ CoreProperties.GLOBAL_WORKING_DIRECTORY, temp.getRoot().getAbsolutePath());
+
+ return new CachesManager(new GlobalTempFolderProvider().provide(new GlobalProperties(props)));
+ }
+
+ @BeforeClass
+ public static void startClass() {
+ cachesManager = createCacheOnTemp();
+ cachesManager.start();
+ }
+
+ @Before
+ public void start() {
+ caches = new Caches(cachesManager);
+ caches.start();
+ }
+
+ @After
+ public void stop() {
+ if (caches != null) {
+ caches.stop();
+ caches = null;
+ }
+ }
+
+ @AfterClass
+ public static void stopClass() {
+ if (cachesManager != null) {
+ cachesManager.stop();
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BatchComponentCacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BatchComponentCacheTest.java
new file mode 100644
index 00000000000..8d014b67321
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BatchComponentCacheTest.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.index;
+
+import org.junit.Test;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Resource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+public class BatchComponentCacheTest {
+ @Test
+ public void should_cache_resource() {
+ BatchComponentCache cache = new BatchComponentCache();
+ String componentKey = "struts:src/org/struts/Action.java";
+ Resource resource = File.create("org/struts/Action.java").setEffectiveKey(componentKey);
+ cache.add(resource, null);
+
+ assertThat(cache.get(componentKey).resource()).isSameAs(resource);
+ assertThat(cache.get("other")).isNull();
+ }
+
+ @Test
+ public void should_fail_if_missing_component_key() {
+ BatchComponentCache cache = new BatchComponentCache();
+ Resource resource = File.create("org/struts/Action.java").setEffectiveKey(null);
+ try {
+ cache.add(resource, null);
+ fail();
+ } catch (IllegalStateException e) {
+ // success
+ assertThat(e).hasMessage("Missing resource effective key");
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BucketTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BucketTest.java
new file mode 100644
index 00000000000..a6a5f900088
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/BucketTest.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.index;
+
+import org.junit.Test;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class BucketTest {
+
+ Directory directory = Directory.create("org/foo");
+ File javaFile = File.create("org/foo/Bar.java");
+ Metric ncloc = new Metric("ncloc");
+
+ @Test
+ public void shouldManageRelationships() {
+ Bucket packageBucket = new Bucket(directory);
+ Bucket fileBucket = new Bucket(javaFile);
+ fileBucket.setParent(packageBucket);
+
+ assertThat(fileBucket.getParent()).isEqualTo(packageBucket);
+ assertThat(packageBucket.getChildren()).containsExactly(fileBucket);
+ }
+
+ @Test
+ public void shouldBeEquals() {
+ assertEquals(new Bucket(directory), new Bucket(directory));
+ assertEquals(new Bucket(directory).hashCode(), new Bucket(directory).hashCode());
+ }
+
+ @Test
+ public void shouldNotBeEquals() {
+ assertFalse(new Bucket(directory).equals(new Bucket(javaFile)));
+ assertThat(new Bucket(directory).hashCode()).isNotEqualTo(new Bucket(javaFile).hashCode());
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CacheTest.java
new file mode 100644
index 00000000000..95ddcb8b766
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CacheTest.java
@@ -0,0 +1,250 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.index;
+
+import com.google.common.collect.Iterables;
+import org.junit.Test;
+import org.sonar.batch.index.Cache.Entry;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CacheTest extends AbstractCachesTest {
+
+ @Test
+ public void one_part_key() {
+ Cache<String> cache = caches.createCache("capitals");
+
+ assertThat(cache.get("france")).isNull();
+
+ cache.put("france", "paris");
+ cache.put("italy", "rome");
+ assertThat(cache.get("france")).isEqualTo("paris");
+ assertThat(cache.keySet()).containsOnly("france", "italy");
+ assertThat(cache.keySet("france")).isEmpty();
+ Iterable<String> values = cache.values();
+ assertThat(values).containsOnly("paris", "rome");
+ assertThat(values).containsOnly("paris", "rome");
+ assertThat(cache.containsKey("france")).isTrue();
+
+ Iterable<Entry<String>> iterable = cache.entries();
+ Cache.Entry[] entries = Iterables.toArray(iterable, Cache.Entry.class);
+ assertThat(entries).hasSize(2);
+ assertThat(iterable).hasSize(2);
+ assertThat(entries[0].key()[0]).isEqualTo("france");
+ assertThat(entries[0].value()).isEqualTo("paris");
+ assertThat(entries[1].key()[0]).isEqualTo("italy");
+ assertThat(entries[1].value()).isEqualTo("rome");
+
+ cache.remove("france");
+ assertThat(cache.get("france")).isNull();
+ assertThat(cache.get("italy")).isEqualTo("rome");
+ assertThat(cache.keySet()).containsOnly("italy");
+ assertThat(cache.keySet("france")).isEmpty();
+ assertThat(cache.containsKey("france")).isFalse();
+ assertThat(cache.containsKey("italy")).isTrue();
+ assertThat(values).containsOnly("rome");
+
+ cache.clear();
+ assertThat(values).isEmpty();
+ }
+
+ @Test
+ public void test_key_being_prefix_of_another_key() throws Exception {
+ Cache<String> cache = caches.createCache("components");
+
+ cache.put("struts-el:org.apache.strutsel.taglib.html.ELButtonTag", "the Tag");
+ cache.put("struts-el:org.apache.strutsel.taglib.html.ELButtonTagBeanInfo", "the BeanInfo");
+
+ assertThat(cache.get("struts-el:org.apache.strutsel.taglib.html.ELButtonTag")).isEqualTo("the Tag");
+ assertThat(cache.get("struts-el:org.apache.strutsel.taglib.html.ELButtonTagBeanInfo")).isEqualTo("the BeanInfo");
+ }
+
+ @Test
+ public void two_parts_key() {
+ Cache<String> cache = caches.createCache("capitals");
+
+ assertThat(cache.get("europe", "france")).isNull();
+
+ cache.put("europe", "france", "paris");
+ cache.put("europe", "italy", "rome");
+ cache.put("asia", "china", "pekin");
+ assertThat(cache.get("europe")).isNull();
+ assertThat(cache.get("europe", "france")).isEqualTo("paris");
+ assertThat(cache.get("europe", "italy")).isEqualTo("rome");
+ assertThat(cache.get("europe")).isNull();
+ assertThat(cache.keySet("europe")).containsOnly("france", "italy");
+ assertThat(cache.keySet()).containsOnly("europe", "asia");
+ assertThat(cache.containsKey("europe")).isFalse();
+ assertThat(cache.containsKey("europe", "france")).isTrue();
+ assertThat(cache.containsKey("europe", "spain")).isFalse();
+ assertThat(cache.values()).containsOnly("paris", "rome", "pekin");
+ assertThat(cache.values("america")).isEmpty();
+ assertThat(cache.values("europe")).containsOnly("paris", "rome");
+ assertThat(cache.values("oceania")).isEmpty();
+
+ Iterable<Entry<String>> iterable = cache.entries();
+ Cache.Entry[] allEntries = Iterables.toArray(iterable, Cache.Entry.class);
+ assertThat(allEntries).hasSize(3);
+ assertThat(iterable).hasSize(3);
+ assertThat(allEntries[0].key()).isEqualTo(new String[] {"asia", "china"});
+ assertThat(allEntries[0].value()).isEqualTo("pekin");
+ assertThat(allEntries[1].key()).isEqualTo(new String[] {"europe", "france"});
+ assertThat(allEntries[1].value()).isEqualTo("paris");
+ assertThat(allEntries[2].key()).isEqualTo(new String[] {"europe", "italy"});
+ assertThat(allEntries[2].value()).isEqualTo("rome");
+
+ Iterable<Entry<String>> iterable2 = cache.entries("europe");
+ Cache.Entry[] subEntries = Iterables.toArray(iterable2, Cache.Entry.class);
+ assertThat(subEntries).hasSize(2);
+ assertThat(iterable2).hasSize(2);
+ assertThat(subEntries[0].key()).isEqualTo(new String[] {"europe", "france"});
+ assertThat(subEntries[0].value()).isEqualTo("paris");
+ assertThat(subEntries[1].key()).isEqualTo(new String[] {"europe", "italy"});
+ assertThat(subEntries[1].value()).isEqualTo("rome");
+
+ cache.remove("europe", "france");
+ assertThat(cache.values()).containsOnly("rome", "pekin");
+ assertThat(cache.get("europe", "france")).isNull();
+ assertThat(cache.get("europe", "italy")).isEqualTo("rome");
+ assertThat(cache.containsKey("europe", "france")).isFalse();
+ assertThat(cache.keySet("europe")).containsOnly("italy");
+
+ cache.clear("america");
+ assertThat(cache.keySet()).containsOnly("europe", "asia");
+ cache.clear();
+ assertThat(cache.keySet()).isEmpty();
+ }
+
+ @Test
+ public void three_parts_key() {
+ Cache<String> cache = caches.createCache("places");
+ assertThat(cache.get("europe", "france", "paris")).isNull();
+
+ cache.put("europe", "france", "paris", "eiffel tower");
+ cache.put("europe", "france", "annecy", "lake");
+ cache.put("europe", "france", "poitiers", "notre dame");
+ cache.put("europe", "italy", "rome", "colosseum");
+ cache.put("europe2", "ukrania", "kiev", "dunno");
+ cache.put("asia", "china", "pekin", "great wall");
+ cache.put("america", "us", "new york", "empire state building");
+ assertThat(cache.get("europe")).isNull();
+ assertThat(cache.get("europe", "france")).isNull();
+ assertThat(cache.get("europe", "france", "paris")).isEqualTo("eiffel tower");
+ assertThat(cache.get("europe", "france", "annecy")).isEqualTo("lake");
+ assertThat(cache.get("europe", "italy", "rome")).isEqualTo("colosseum");
+ assertThat(cache.keySet()).containsOnly("europe", "asia", "america", "europe2");
+ assertThat(cache.keySet("europe")).containsOnly("france", "italy");
+ assertThat(cache.keySet("europe", "france")).containsOnly("annecy", "paris", "poitiers");
+ assertThat(cache.containsKey("europe")).isFalse();
+ assertThat(cache.containsKey("europe", "france")).isFalse();
+ assertThat(cache.containsKey("europe", "france", "annecy")).isTrue();
+ assertThat(cache.containsKey("europe", "france", "biarritz")).isFalse();
+ assertThat(cache.values()).containsOnly("eiffel tower", "lake", "colosseum", "notre dame", "great wall", "empire state building", "dunno");
+ assertThat(cache.values("europe")).containsOnly("eiffel tower", "lake", "colosseum", "notre dame");
+ assertThat(cache.values("europe", "france")).containsOnly("eiffel tower", "lake", "notre dame");
+
+ Iterable<Entry<String>> iterable = cache.entries();
+ Cache.Entry[] allEntries = Iterables.toArray(iterable, Cache.Entry.class);
+ assertThat(allEntries).hasSize(7);
+ assertThat(iterable).hasSize(7);
+ assertThat(allEntries[0].key()).isEqualTo(new String[] {"america", "us", "new york"});
+ assertThat(allEntries[0].value()).isEqualTo("empire state building");
+ assertThat(allEntries[1].key()).isEqualTo(new String[] {"asia", "china", "pekin"});
+ assertThat(allEntries[1].value()).isEqualTo("great wall");
+ assertThat(allEntries[2].key()).isEqualTo(new String[] {"europe", "france", "annecy"});
+ assertThat(allEntries[2].value()).isEqualTo("lake");
+ assertThat(allEntries[3].key()).isEqualTo(new String[] {"europe", "france", "paris"});
+ assertThat(allEntries[3].value()).isEqualTo("eiffel tower");
+ assertThat(allEntries[4].key()).isEqualTo(new String[] {"europe", "france", "poitiers"});
+ assertThat(allEntries[4].value()).isEqualTo("notre dame");
+ assertThat(allEntries[5].key()).isEqualTo(new String[] {"europe", "italy", "rome"});
+ assertThat(allEntries[5].value()).isEqualTo("colosseum");
+
+ Iterable<Entry<String>> iterable2 = cache.entries("europe");
+ Cache.Entry[] subEntries = Iterables.toArray(iterable2, Cache.Entry.class);
+ assertThat(subEntries).hasSize(4);
+ assertThat(iterable2).hasSize(4);
+ assertThat(subEntries[0].key()).isEqualTo(new String[] {"europe", "france", "annecy"});
+ assertThat(subEntries[0].value()).isEqualTo("lake");
+ assertThat(subEntries[1].key()).isEqualTo(new String[] {"europe", "france", "paris"});
+ assertThat(subEntries[1].value()).isEqualTo("eiffel tower");
+ assertThat(subEntries[2].key()).isEqualTo(new String[] {"europe", "france", "poitiers"});
+ assertThat(subEntries[2].value()).isEqualTo("notre dame");
+ assertThat(subEntries[3].key()).isEqualTo(new String[] {"europe", "italy", "rome"});
+ assertThat(subEntries[3].value()).isEqualTo("colosseum");
+
+ cache.remove("europe", "france", "annecy");
+ assertThat(cache.values()).containsOnly("eiffel tower", "colosseum", "notre dame", "great wall", "empire state building", "dunno");
+ assertThat(cache.values("europe")).containsOnly("eiffel tower", "colosseum", "notre dame");
+ assertThat(cache.values("europe", "france")).containsOnly("eiffel tower", "notre dame");
+ assertThat(cache.get("europe", "france", "annecy")).isNull();
+ assertThat(cache.get("europe", "italy", "rome")).isEqualTo("colosseum");
+ assertThat(cache.containsKey("europe", "france")).isFalse();
+
+ cache.clear("europe", "italy");
+ assertThat(cache.values()).containsOnly("eiffel tower", "notre dame", "great wall", "empire state building", "dunno");
+
+ cache.clear("europe");
+ assertThat(cache.values()).containsOnly("great wall", "empire state building", "dunno");
+
+ cache.clear();
+ assertThat(cache.values()).isEmpty();
+ }
+
+ @Test
+ public void remove_versus_clear() {
+ Cache<String> cache = caches.createCache("capitals");
+ cache.put("europe", "france", "paris");
+ cache.put("europe", "italy", "rome");
+
+ // remove("europe") does not remove sub-keys
+ cache.remove("europe");
+ assertThat(cache.values()).containsOnly("paris", "rome");
+
+ // clear("europe") removes sub-keys
+ cache.clear("europe");
+ assertThat(cache.values()).isEmpty();
+ }
+
+ @Test
+ public void empty_cache() {
+ Cache<String> cache = caches.createCache("empty");
+
+ assertThat(cache.get("foo")).isNull();
+ assertThat(cache.get("foo", "bar")).isNull();
+ assertThat(cache.get("foo", "bar", "baz")).isNull();
+ assertThat(cache.keySet()).isEmpty();
+ assertThat(cache.keySet("foo")).isEmpty();
+ assertThat(cache.containsKey("foo")).isFalse();
+ assertThat(cache.containsKey("foo", "bar")).isFalse();
+ assertThat(cache.containsKey("foo", "bar", "baz")).isFalse();
+ assertThat(cache.values()).isEmpty();
+ assertThat(cache.values("foo")).isEmpty();
+
+ // do not fail
+ cache.remove("foo");
+ cache.remove("foo", "bar");
+ cache.remove("foo", "bar", "baz");
+ cache.clear("foo");
+ cache.clear("foo", "bar");
+ cache.clear("foo", "bar", "baz");
+ cache.clear();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesManagerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesManagerTest.java
new file mode 100644
index 00000000000..177c6f1e357
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesManagerTest.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.index;
+
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CachesManagerTest extends AbstractCachesTest {
+ @Test
+ public void should_stop_and_clean_temp_dir() {
+ File tempDir = cachesManager.tempDir();
+ assertThat(tempDir).isDirectory().exists();
+ assertThat(cachesManager.persistit()).isNotNull();
+ assertThat(cachesManager.persistit().isInitialized()).isTrue();
+
+ cachesManager.stop();
+
+ assertThat(tempDir).doesNotExist();
+ assertThat(cachesManager.tempDir()).isNull();
+ assertThat(cachesManager.persistit()).isNull();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesTest.java
new file mode 100644
index 00000000000..2a01ac38025
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/CachesTest.java
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.index;
+
+import java.io.Serializable;
+
+import com.persistit.exception.PersistitException;
+import org.junit.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+public class CachesTest extends AbstractCachesTest {
+ @Test
+ public void should_create_cache() {
+ Cache<Element> cache = caches.createCache("foo");
+ assertThat(cache).isNotNull();
+ }
+
+ @Test
+ public void should_not_create_cache_twice() {
+ caches.<Element>createCache("foo");
+ try {
+ caches.<Element>createCache("foo");
+ fail();
+ } catch (IllegalStateException e) {
+ // ok
+ }
+ }
+
+ @Test
+ public void should_clean_resources() {
+ Cache<String> c = caches.<String>createCache("test1");
+ for (int i = 0; i < 1_000_000; i++) {
+ c.put("a" + i, "a" + i);
+ }
+
+ caches.stop();
+
+ // manager continues up
+ assertThat(cachesManager.persistit().isInitialized()).isTrue();
+
+ caches = new Caches(cachesManager);
+ caches.start();
+ caches.createCache("test1");
+ }
+
+ @Test
+ public void leak_test() throws PersistitException {
+ caches.stop();
+
+ int len = 1 * 1024 * 1024;
+ StringBuilder sb = new StringBuilder(len);
+ for (int i = 0; i < len; i++) {
+ sb.append("a");
+ }
+
+ for (int i = 0; i < 3; i++) {
+ caches = new Caches(cachesManager);
+ caches.start();
+ Cache<String> c = caches.<String>createCache("test" + i);
+ c.put("key" + i, sb.toString());
+ cachesManager.persistit().flush();
+
+ caches.stop();
+ }
+ }
+
+ private static class Element implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/index/DefaultIndexTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/DefaultIndexTest.java
new file mode 100644
index 00000000000..8dae9e19ad0
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/index/DefaultIndexTest.java
@@ -0,0 +1,163 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.index;
+
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilters;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.scan.filesystem.PathResolver;
+import org.sonar.batch.DefaultProjectTree;
+import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.batch.sensor.DefaultSensorStorage;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultIndexTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ DefaultIndex index = null;
+ Rule rule;
+ RuleFinder ruleFinder;
+ Project project;
+ Project moduleA;
+ Project moduleB;
+ Project moduleB1;
+
+ private java.io.File baseDir;
+
+ @Before
+ public void createIndex() throws IOException {
+ ruleFinder = mock(RuleFinder.class);
+
+ DefaultProjectTree projectTree = mock(DefaultProjectTree.class);
+ BatchComponentCache resourceCache = new BatchComponentCache();
+ index = new DefaultIndex(resourceCache, projectTree, mock(MeasureCache.class), new PathResolver());
+
+ baseDir = temp.newFolder();
+ project = new Project("project");
+ when(projectTree.getProjectDefinition(project)).thenReturn(ProjectDefinition.create().setBaseDir(baseDir));
+ moduleA = new Project("moduleA").setParent(project);
+ when(projectTree.getProjectDefinition(moduleA)).thenReturn(ProjectDefinition.create().setBaseDir(new java.io.File(baseDir, "moduleA")));
+ moduleB = new Project("moduleB").setParent(project);
+ when(projectTree.getProjectDefinition(moduleB)).thenReturn(ProjectDefinition.create().setBaseDir(new java.io.File(baseDir, "moduleB")));
+ moduleB1 = new Project("moduleB1").setParent(moduleB);
+ when(projectTree.getProjectDefinition(moduleB1)).thenReturn(ProjectDefinition.create().setBaseDir(new java.io.File(baseDir, "moduleB/moduleB1")));
+
+ RulesProfile rulesProfile = RulesProfile.create();
+ rule = Rule.create("repoKey", "ruleKey", "Rule");
+ rule.setId(1);
+ rulesProfile.activateRule(rule, null);
+ index.setCurrentProject(project, mock(DefaultSensorStorage.class));
+ index.doStart(project);
+ }
+
+ @Test
+ public void shouldIndexParentOfDeprecatedFiles() {
+ File file = File.create("src/org/foo/Bar.java", null, false);
+ assertThat(index.index(file)).isTrue();
+
+ Directory reference = Directory.create("src/org/foo");
+ assertThat(index.getResource(reference).getName()).isEqualTo("src/org/foo");
+ assertThat(index.isIndexed(reference, true)).isTrue();
+ assertThat(index.isExcluded(reference)).isFalse();
+ assertThat(index.getChildren(reference)).hasSize(1);
+ assertThat(index.getParent(reference)).isInstanceOf(Project.class);
+ }
+
+ @Test
+ public void shouldIndexTreeOfResources() {
+ Directory directory = Directory.create("src/org/foo");
+ File file = File.create("src/org/foo/Bar.java", Java.INSTANCE, false);
+
+ assertThat(index.index(directory)).isTrue();
+ assertThat(index.index(file, directory)).isTrue();
+
+ File fileRef = File.create("src/org/foo/Bar.java", null, false);
+ assertThat(index.getResource(fileRef).getKey()).isEqualTo("src/org/foo/Bar.java");
+ assertThat(index.getResource(fileRef).getLanguage().getKey()).isEqualTo("java");
+ assertThat(index.isIndexed(fileRef, true)).isTrue();
+ assertThat(index.isExcluded(fileRef)).isFalse();
+ assertThat(index.getChildren(fileRef)).isEmpty();
+ assertThat(index.getParent(fileRef)).isInstanceOf(Directory.class);
+ }
+
+ @Test
+ public void shouldGetSource() throws Exception {
+ Directory directory = Directory.create("src/org/foo");
+ File file = File.create("src/org/foo/Bar.java", Java.INSTANCE, false);
+ FileUtils.write(new java.io.File(baseDir, "src/org/foo/Bar.java"), "Foo bar");
+
+ assertThat(index.index(directory)).isTrue();
+ assertThat(index.index(file, directory)).isTrue();
+
+ File fileRef = File.create("src/org/foo/Bar.java", null, false);
+ assertThat(index.getSource(fileRef)).isEqualTo("Foo bar");
+ }
+
+ @Test
+ public void shouldNotIndexResourceIfParentNotIndexed() {
+ Directory directory = Directory.create("src/org/other");
+ File file = File.create("src/org/foo/Bar.java", null, false);
+
+ assertThat(index.index(file, directory)).isFalse();
+
+ File fileRef = File.create("src/org/foo/Bar.java", null, false);
+ assertThat(index.isIndexed(directory, true)).isFalse();
+ assertThat(index.isIndexed(fileRef, true)).isFalse();
+ assertThat(index.isExcluded(fileRef)).isFalse();
+ assertThat(index.getChildren(fileRef)).isEmpty();
+ assertThat(index.getParent(fileRef)).isNull();
+ }
+
+ @Test
+ public void shouldNotIndexResourceWhenAddingMeasure() {
+ Resource dir = Directory.create("src/org/foo");
+ index.addMeasure(dir, new Measure("ncloc").setValue(50.0));
+
+ assertThat(index.isIndexed(dir, true)).isFalse();
+ assertThat(index.getMeasures(dir, MeasuresFilters.metric("ncloc"))).isNull();
+ }
+
+ @Test
+ public void shouldComputePathOfIndexedModules() {
+ assertThat(index.getResource(project).getPath()).isNull();
+ assertThat(index.getResource(moduleA).getPath()).isEqualTo("moduleA");
+ assertThat(index.getResource(moduleB).getPath()).isEqualTo("moduleB");
+ assertThat(index.getResource(moduleB1).getPath()).isEqualTo("moduleB1");
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultFilterableIssueTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultFilterableIssueTest.java
new file mode 100644
index 00000000000..daded262498
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultFilterableIssueTest.java
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+import java.util.Date;
+
+import static org.mockito.Mockito.mock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.resources.Project;
+import org.sonar.scanner.protocol.Constants.Severity;
+import org.sonar.scanner.protocol.output.ScannerReport.Issue;
+
+public class DefaultFilterableIssueTest {
+ private DefaultFilterableIssue issue;
+ private Project mockedProject;
+ private String componentKey;
+ private Issue rawIssue;
+
+ @Before
+ public void setUp() {
+ mockedProject = mock(Project.class);
+ componentKey = "component";
+ }
+
+ private Issue createIssue() {
+ Issue.Builder builder = Issue.newBuilder();
+
+ builder.setGap(3.0);
+ builder.setLine(30);
+ builder.setSeverity(Severity.MAJOR);
+ return builder.build();
+ }
+
+ private Issue createIssueWithoutFields() {
+ Issue.Builder builder = Issue.newBuilder();
+ builder.setSeverity(Severity.MAJOR);
+ return builder.build();
+ }
+
+ @Test
+ public void testRoundTrip() {
+ rawIssue = createIssue();
+ issue = new DefaultFilterableIssue(mockedProject, rawIssue, componentKey);
+
+ when(mockedProject.getAnalysisDate()).thenReturn(new Date(10_000));
+ when(mockedProject.getEffectiveKey()).thenReturn("projectKey");
+
+ assertThat(issue.componentKey()).isEqualTo(componentKey);
+ assertThat(issue.creationDate()).isEqualTo(new Date(10_000));
+ assertThat(issue.line()).isEqualTo(30);
+ assertThat(issue.projectKey()).isEqualTo("projectKey");
+ assertThat(issue.effortToFix()).isEqualTo(3.0);
+ assertThat(issue.severity()).isEqualTo("MAJOR");
+ }
+
+ @Test
+ public void nullValues() {
+ rawIssue = createIssueWithoutFields();
+ issue = new DefaultFilterableIssue(mockedProject, rawIssue, componentKey);
+
+ assertThat(issue.line()).isNull();
+ assertThat(issue.effortToFix()).isNull();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java
new file mode 100644
index 00000000000..7be36bd3bc5
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueCallbackTest.java
@@ -0,0 +1,137 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.sonar.batch.issue.tracking.TrackedIssue;
+
+import org.sonar.api.batch.rule.Rule;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.bootstrapper.IssueListener.Issue;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Mock;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.batch.repository.user.UserRepositoryLoader;
+import org.sonar.scanner.protocol.input.ScannerInput;
+import org.sonar.batch.bootstrapper.IssueListener;
+import org.junit.Before;
+import com.google.common.collect.ImmutableList;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.mockito.Matchers.any;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Test;
+
+public class DefaultIssueCallbackTest {
+ @Mock
+ private IssueCache issueCache;
+ @Mock
+ private UserRepositoryLoader userRepository;
+ @Mock
+ private Rules rules;
+
+ private TrackedIssue issue;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ RuleKey ruleKey = RuleKey.of("repo", "key");
+ issue = new TrackedIssue();
+ issue.setKey("key");
+ issue.setAssignee("user");
+ issue.setRuleKey(ruleKey);
+
+ when(issueCache.all()).thenReturn(ImmutableList.of(issue));
+
+ ScannerInput.User.Builder userBuilder = ScannerInput.User.newBuilder();
+ userBuilder.setLogin("user");
+ userBuilder.setName("name");
+ when(userRepository.load("user")).thenReturn(userBuilder.build());
+
+ Rule r = mock(Rule.class);
+ when(r.name()).thenReturn("rule name");
+ when(rules.find(ruleKey)).thenReturn(r);
+ }
+
+ @Test
+ public void testWithoutListener() {
+ DefaultIssueCallback issueCallback = new DefaultIssueCallback(issueCache, userRepository, rules);
+ issueCallback.execute();
+ }
+
+ @Test
+ public void testWithListener() {
+ final List<IssueListener.Issue> issueList = new LinkedList<>();
+ IssueListener listener = new IssueListener() {
+ @Override
+ public void handle(Issue issue) {
+ issueList.add(issue);
+ }
+ };
+
+ DefaultIssueCallback issueCallback = new DefaultIssueCallback(issueCache, listener, userRepository, rules);
+ issueCallback.execute();
+
+ assertThat(issueList).hasSize(1);
+ Issue callbackIssue = issueList.get(0);
+
+ assertThat(callbackIssue.getAssigneeName()).isEqualTo("name");
+ assertThat(callbackIssue.getRuleName()).isEqualTo("rule name");
+ }
+
+ @Test
+ public void testWithNulls() {
+ final List<IssueListener.Issue> issueList = new LinkedList<>();
+ IssueListener listener = new IssueListener() {
+ @Override
+ public void handle(Issue issue) {
+ issueList.add(issue);
+ }
+ };
+
+ issue.setKey(null);
+ issue.setAssignee(null);
+
+ DefaultIssueCallback issueCallback = new DefaultIssueCallback(issueCache, listener, userRepository, rules);
+ issueCallback.execute();
+ }
+
+ @Test
+ public void testDecorationNotFound() {
+ final List<IssueListener.Issue> issueList = new LinkedList<>();
+ IssueListener listener = new IssueListener() {
+ @Override
+ public void handle(Issue issue) {
+ issueList.add(issue);
+ }
+ };
+
+ when(userRepository.load(any(String.class))).thenReturn(null);
+ when(rules.find(any(RuleKey.class))).thenReturn(null);
+
+ DefaultIssueCallback issueCallback = new DefaultIssueCallback(issueCache, listener, userRepository, rules);
+ issueCallback.execute();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java
new file mode 100644
index 00000000000..041bc8493a2
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java
@@ -0,0 +1,95 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import org.sonar.api.scan.issue.filter.FilterableIssue;
+
+import org.junit.Test;
+import org.sonar.api.scan.issue.filter.IssueFilter;
+import org.sonar.api.scan.issue.filter.IssueFilterChain;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+public class DefaultIssueFilterChainTest {
+ private final FilterableIssue issue = mock(FilterableIssue.class);
+
+ @Test
+ public void should_accept_when_no_filter() {
+ assertThat(new DefaultIssueFilterChain().accept(issue)).isTrue();
+ }
+
+ class PassingFilter implements IssueFilter {
+ @Override
+ public boolean accept(FilterableIssue issue, IssueFilterChain chain) {
+ return chain.accept(issue);
+ }
+ }
+
+ class AcceptingFilter implements IssueFilter {
+ @Override
+ public boolean accept(FilterableIssue issue, IssueFilterChain chain) {
+ return true;
+ }
+ }
+
+ class RefusingFilter implements IssueFilter {
+ @Override
+ public boolean accept(FilterableIssue issue, IssueFilterChain chain) {
+ return false;
+ }
+ }
+
+ class FailingFilter implements IssueFilter {
+ @Override
+ public boolean accept(FilterableIssue issue, IssueFilterChain chain) {
+ fail();
+ return false;
+ }
+
+ }
+
+ @Test
+ public void should_accept_if_all_filters_pass() {
+ assertThat(new DefaultIssueFilterChain(
+ new PassingFilter(),
+ new PassingFilter(),
+ new PassingFilter()
+ ).accept(issue)).isTrue();
+ }
+
+ @Test
+ public void should_accept_and_not_go_further_if_filter_accepts() {
+ assertThat(new DefaultIssueFilterChain(
+ new PassingFilter(),
+ new AcceptingFilter(),
+ new FailingFilter()
+ ).accept(issue)).isTrue();
+ }
+
+ @Test
+ public void should_refuse_and_not_go_further_if_filter_refuses() {
+ assertThat(new DefaultIssueFilterChain(
+ new PassingFilter(),
+ new RefusingFilter(),
+ new FailingFilter()
+ ).accept(issue)).isFalse();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java
new file mode 100644
index 00000000000..9448206b1d6
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import org.sonar.batch.issue.tracking.TrackedIssue;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultProjectIssuesTest {
+
+ static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle");
+
+ IssueCache cache = mock(IssueCache.class);
+ DefaultProjectIssues projectIssues = new DefaultProjectIssues(cache);
+
+ @Test
+ public void should_get_all_issues() {
+ DefaultIssue issueOnModule = new DefaultIssue().setKey("1").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core");
+ DefaultIssue issueInModule = new DefaultIssue().setKey("2").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core:Action");
+ DefaultIssue resolvedIssueInModule = new DefaultIssue().setKey("3").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core:Action")
+ .setResolution(Issue.RESOLUTION_FIXED);
+
+ DefaultIssue issueOnRoot = new DefaultIssue().setKey("4").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts");
+ DefaultIssue issueInRoot = new DefaultIssue().setKey("5").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts:FileInRoot");
+ when(cache.all()).thenReturn(Arrays.<TrackedIssue>asList(
+ toTrackedIssue(issueOnRoot), toTrackedIssue(issueInRoot),
+ toTrackedIssue(issueOnModule), toTrackedIssue(issueInModule), toTrackedIssue(resolvedIssueInModule)
+ ));
+
+ // unresolved issues
+ List<Issue> issues = Lists.newArrayList(projectIssues.issues());
+ assertThat(issues).containsOnly(issueOnRoot, issueInRoot, issueInModule, issueOnModule);
+
+ List<Issue> resolvedIssues = Lists.newArrayList(projectIssues.resolvedIssues());
+ assertThat(resolvedIssues).containsOnly(resolvedIssueInModule);
+ }
+
+ private TrackedIssue toTrackedIssue(DefaultIssue issue) {
+ TrackedIssue trackedIssue = new TrackedIssue();
+
+ trackedIssue.setKey(issue.key());
+ trackedIssue.setRuleKey(issue.ruleKey());
+ trackedIssue.setComponentKey(issue.componentKey());
+ trackedIssue.setSeverity(issue.severity());
+ trackedIssue.setResolution(issue.resolution());
+
+ return trackedIssue;
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueAdapterForFilterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueAdapterForFilterTest.java
new file mode 100644
index 00000000000..b4818adae0e
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueAdapterForFilterTest.java
@@ -0,0 +1,157 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import java.util.Date;
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.resources.Project;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.scanner.protocol.Constants.Severity;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+
+public class DeprecatedIssueAdapterForFilterTest {
+
+ private static final String PROJECT_KEY = "foo";
+ private static final Date ANALYSIS_DATE = new Date();
+ private static final String COMPONENT_KEY = "foo:src/Foo.java";
+
+ @Test
+ public void improve_coverage() {
+ DeprecatedIssueAdapterForFilter issue = new DeprecatedIssueAdapterForFilter(new Project(PROJECT_KEY).setAnalysisDate(ANALYSIS_DATE),
+ org.sonar.scanner.protocol.output.ScannerReport.Issue.newBuilder()
+ .setRuleRepository("repo")
+ .setRuleKey("key")
+ .setSeverity(Severity.BLOCKER)
+ .setMsg("msg")
+ .build(),
+ COMPONENT_KEY);
+ DeprecatedIssueAdapterForFilter issue2 = new DeprecatedIssueAdapterForFilter(new Project(PROJECT_KEY).setAnalysisDate(ANALYSIS_DATE),
+ org.sonar.scanner.protocol.output.ScannerReport.Issue.newBuilder()
+ .setRuleRepository("repo")
+ .setRuleKey("key")
+ .setSeverity(Severity.BLOCKER)
+ .setMsg("msg")
+ .setLine(1)
+ .setGap(2.0)
+ .build(),
+ COMPONENT_KEY);
+
+ try {
+ issue.key();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ assertThat(issue.componentKey()).isEqualTo(COMPONENT_KEY);
+ assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "key"));
+
+ try {
+ issue.language();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ assertThat(issue.severity()).isEqualTo("BLOCKER");
+ assertThat(issue.message()).isEqualTo("msg");
+ assertThat(issue.line()).isNull();
+ assertThat(issue2.line()).isEqualTo(1);
+ assertThat(issue.effortToFix()).isNull();
+ assertThat(issue2.effortToFix()).isEqualTo(2.0);
+ assertThat(issue.status()).isEqualTo(Issue.STATUS_OPEN);
+ assertThat(issue.resolution()).isNull();
+
+ try {
+ issue.reporter();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ assertThat(issue.assignee()).isNull();
+ assertThat(issue.creationDate()).isEqualTo(ANALYSIS_DATE);
+ assertThat(issue.updateDate()).isNull();
+ assertThat(issue.closeDate()).isNull();
+ assertThat(issue.attribute(PROJECT_KEY)).isNull();
+
+ try {
+ issue.authorLogin();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ try {
+ issue.actionPlanKey();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ try {
+ issue.comments();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ try {
+ issue.isNew();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ try {
+ issue.debt();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ assertThat(issue.projectKey()).isEqualTo(PROJECT_KEY);
+
+ try {
+ issue.projectUuid();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ try {
+ issue.componentUuid();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+
+ try {
+ issue.tags();
+ fail("Should be unsupported");
+ } catch (Exception e) {
+ assertThat(e).isExactlyInstanceOf(UnsupportedOperationException.class).hasMessage("Not available for issues filters");
+ }
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueFilterChainTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueFilterChainTest.java
new file mode 100644
index 00000000000..c80b1d64a82
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/DeprecatedIssueFilterChainTest.java
@@ -0,0 +1,96 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.batch.IssueFilter;
+import org.sonar.api.issue.batch.IssueFilterChain;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+public class DeprecatedIssueFilterChainTest {
+
+ private final Issue issue = mock(Issue.class);
+
+ @Test
+ public void should_accept_when_no_filter() {
+ assertThat(new DeprecatedIssueFilterChain().accept(issue)).isTrue();
+ }
+
+ class PassingFilter implements IssueFilter {
+ @Override
+ public boolean accept(Issue issue, IssueFilterChain chain) {
+ return chain.accept(issue);
+ }
+ }
+
+ class AcceptingFilter implements IssueFilter {
+ @Override
+ public boolean accept(Issue issue, IssueFilterChain chain) {
+ return true;
+ }
+ }
+
+ class RefusingFilter implements IssueFilter {
+ @Override
+ public boolean accept(Issue issue, IssueFilterChain chain) {
+ return false;
+ }
+ }
+
+ class FailingFilter implements IssueFilter {
+ @Override
+ public boolean accept(Issue issue, IssueFilterChain chain) {
+ fail();
+ return false;
+ }
+
+ }
+
+ @Test
+ public void should_accept_if_all_filters_pass() {
+ assertThat(new DeprecatedIssueFilterChain(
+ new PassingFilter(),
+ new PassingFilter(),
+ new PassingFilter()
+ ).accept(issue)).isTrue();
+ }
+
+ @Test
+ public void should_accept_and_not_go_further_if_filter_accepts() {
+ assertThat(new DeprecatedIssueFilterChain(
+ new PassingFilter(),
+ new AcceptingFilter(),
+ new FailingFilter()
+ ).accept(issue)).isTrue();
+ }
+
+ @Test
+ public void should_refuse_and_not_go_further_if_filter_refuses() {
+ assertThat(new DeprecatedIssueFilterChain(
+ new PassingFilter(),
+ new RefusingFilter(),
+ new FailingFilter()
+ ).accept(issue)).isFalse();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java
new file mode 100644
index 00000000000..65856e1e808
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java
@@ -0,0 +1,57 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import org.junit.Test;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.DefaultProjectTree;
+import org.sonar.batch.index.BatchComponent;
+import org.sonar.batch.sensor.DefaultSensorContext;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class IssuableFactoryTest {
+
+ ModuleIssues moduleIssues = mock(ModuleIssues.class);
+ DefaultProjectTree projectTree = mock(DefaultProjectTree.class);
+
+ @Test
+ public void file_should_be_issuable() {
+ IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class));
+ BatchComponent component = new BatchComponent(1, File.create("foo/bar.c").setEffectiveKey("foo/bar.c"), null);
+ Issuable issuable = factory.loadPerspective(Issuable.class, component);
+
+ assertThat(issuable).isNotNull();
+ assertThat(issuable.issues()).isEmpty();
+ }
+
+ @Test
+ public void project_should_be_issuable() {
+ IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class));
+ BatchComponent component = new BatchComponent(1, new Project("Foo").setEffectiveKey("foo"), null);
+ Issuable issuable = factory.loadPerspective(Issuable.class, component);
+
+ assertThat(issuable).isNotNull();
+ assertThat(issuable.issues()).isEmpty();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssueCacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssueCacheTest.java
new file mode 100644
index 00000000000..6d9f42a8ede
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/IssueCacheTest.java
@@ -0,0 +1,98 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import org.sonar.batch.issue.tracking.TrackedIssue;
+
+import org.sonar.batch.index.AbstractCachesTest;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.sonar.api.rule.Severity;
+
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssueCacheTest extends AbstractCachesTest {
+
+ @Test
+ public void should_add_new_issue() {
+ IssueCache cache = new IssueCache(caches);
+ TrackedIssue issue1 = createIssue("111", "org.struts.Action", null);
+ TrackedIssue issue2 = createIssue("222", "org.struts.Action", null);
+ TrackedIssue issue3 = createIssue("333", "org.struts.Filter", null);
+ issue3.setAssignee("foo");
+ cache.put(issue1).put(issue2).put(issue3);
+
+ assertThat(issueKeys(cache.byComponent("org.struts.Action"))).containsOnly("111", "222");
+ assertThat(issueKeys(cache.byComponent("org.struts.Filter"))).containsOnly("333");
+ assertThat(cache.byComponent("org.struts.Filter").iterator().next().assignee()).isEqualTo("foo");
+ }
+
+ @Test
+ public void should_update_existing_issue() {
+ IssueCache cache = new IssueCache(caches);
+ TrackedIssue issue = createIssue("111", "org.struts.Action", Severity.BLOCKER);
+ cache.put(issue);
+
+ issue.setSeverity(Severity.MINOR);
+ cache.put(issue);
+
+ List<TrackedIssue> issues = ImmutableList.copyOf(cache.byComponent("org.struts.Action"));
+ assertThat(issues).hasSize(1);
+ TrackedIssue reloaded = issues.iterator().next();
+ assertThat(reloaded.key()).isEqualTo("111");
+ assertThat(reloaded.severity()).isEqualTo(Severity.MINOR);
+ }
+
+ @Test
+ public void should_get_all_issues() {
+ IssueCache cache = new IssueCache(caches);
+ TrackedIssue issue1 = createIssue("111", "org.struts.Action", Severity.BLOCKER);
+ TrackedIssue issue2 = createIssue("222", "org.struts.Filter", Severity.INFO);
+ cache.put(issue1).put(issue2);
+
+ List<TrackedIssue> issues = ImmutableList.copyOf(cache.all());
+ assertThat(issues).containsOnly(issue1, issue2);
+ }
+
+ private Collection<String> issueKeys(Iterable<TrackedIssue> issues) {
+ return Collections2.transform(ImmutableList.copyOf(issues), new Function<TrackedIssue, String>() {
+ @Override
+ public String apply(@Nullable TrackedIssue issue) {
+ return issue.key();
+ }
+ });
+ }
+
+ private TrackedIssue createIssue(String key, String componentKey, String severity) {
+ TrackedIssue issue = new TrackedIssue();
+ issue.setKey(key);
+ issue.setComponentKey(componentKey);
+ issue.setSeverity(severity);
+
+ return issue;
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
new file mode 100644
index 00000000000..af84563a8b8
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
@@ -0,0 +1,221 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import java.io.StringReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.batch.rule.internal.RulesBuilder;
+import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
+import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
+import org.sonar.api.resources.File;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.report.ReportPublisher;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ModuleIssuesTest {
+
+ static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle");
+ static final String SQUID_RULE_NAME = "Avoid Cycle";
+
+ @Mock
+ IssueFilters filters;
+
+ ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder();
+ RulesBuilder ruleBuilder = new RulesBuilder();
+
+ ModuleIssues moduleIssues;
+
+ BatchComponentCache componentCache = new BatchComponentCache();
+ InputFile file = new DefaultInputFile("foo", "src/Foo.php").initMetadata(new FileMetadata().readMetadata(new StringReader("Foo\nBar\nBiz\n")));
+ ReportPublisher reportPublisher = mock(ReportPublisher.class, RETURNS_DEEP_STUBS);
+
+ @Before
+ public void prepare() {
+ componentCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputComponent(file);
+ }
+
+ @Test
+ public void fail_on_unknown_rule() {
+ initModuleIssues();
+ DefaultIssue issue = new DefaultIssue()
+ .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY);
+ try {
+ moduleIssues.initAndAddIssue(issue);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(MessageException.class);
+ }
+
+ verifyZeroInteractions(reportPublisher);
+ }
+
+ @Test
+ public void fail_if_rule_has_no_name_and_issue_has_no_message() {
+ ruleBuilder.add(SQUID_RULE_KEY).setInternalKey(SQUID_RULE_KEY.rule());
+ initModuleIssues();
+ DefaultIssue issue = new DefaultIssue()
+ .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
+ .forRule(SQUID_RULE_KEY);
+ try {
+ moduleIssues.initAndAddIssue(issue);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(MessageException.class);
+ }
+
+ verifyZeroInteractions(reportPublisher);
+ }
+
+ @Test
+ public void ignore_null_active_rule() {
+ ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
+ initModuleIssues();
+ DefaultIssue issue = new DefaultIssue()
+ .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY);
+ boolean added = moduleIssues.initAndAddIssue(issue);
+
+ assertThat(added).isFalse();
+ verifyZeroInteractions(reportPublisher);
+ }
+
+ @Test
+ public void ignore_null_rule_of_active_rule() {
+ ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
+ activeRulesBuilder.create(SQUID_RULE_KEY).activate();
+ initModuleIssues();
+
+ DefaultIssue issue = new DefaultIssue()
+ .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY);
+ boolean added = moduleIssues.initAndAddIssue(issue);
+
+ assertThat(added).isFalse();
+ verifyZeroInteractions(reportPublisher);
+ }
+
+ @Test
+ public void add_issue_to_cache() {
+ ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
+ activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
+ initModuleIssues();
+
+ DefaultIssue issue = new DefaultIssue()
+ .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY)
+ .overrideSeverity(org.sonar.api.batch.rule.Severity.CRITICAL);
+
+ when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true);
+
+ boolean added = moduleIssues.initAndAddIssue(issue);
+
+ assertThat(added).isTrue();
+ ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
+ verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture());
+ assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.CRITICAL);
+ }
+
+ @Test
+ public void use_severity_from_active_rule_if_no_severity_on_issue() {
+ ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
+ activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
+ initModuleIssues();
+
+ DefaultIssue issue = new DefaultIssue()
+ .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY);
+ when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true);
+ moduleIssues.initAndAddIssue(issue);
+
+ ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
+ verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture());
+ assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO);
+ }
+
+ @Test
+ public void use_rule_name_if_no_message() {
+ ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
+ activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).setName(SQUID_RULE_NAME).activate();
+ initModuleIssues();
+
+ DefaultIssue issue = new DefaultIssue()
+ .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
+ .forRule(SQUID_RULE_KEY);
+ when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true);
+
+ boolean added = moduleIssues.initAndAddIssue(issue);
+
+ assertThat(added).isTrue();
+ ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
+ verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture());
+ assertThat(argument.getValue().getMsg()).isEqualTo("Avoid Cycle");
+ }
+
+ @Test
+ public void filter_issue() {
+ ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
+ activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
+ initModuleIssues();
+
+ DefaultIssue issue = new DefaultIssue()
+ .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message(""))
+ .forRule(SQUID_RULE_KEY);
+
+ when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(false);
+
+ boolean added = moduleIssues.initAndAddIssue(issue);
+
+ assertThat(added).isFalse();
+ verifyZeroInteractions(reportPublisher);
+ }
+
+ /**
+ * Every rules and active rules has to be added in builders before creating ModuleIssues
+ */
+ private void initModuleIssues() {
+ moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), filters, reportPublisher, componentCache);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/TrackedIssueAdapterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/TrackedIssueAdapterTest.java
new file mode 100644
index 00000000000..11fc560b318
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/TrackedIssueAdapterTest.java
@@ -0,0 +1,84 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue;
+
+import java.util.Date;
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.issue.tracking.TrackedIssue;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TrackedIssueAdapterTest {
+
+ @Test
+ public void improve_coverage() {
+ Date creationDate = new Date();
+ TrackedIssue trackedIssue = new TrackedIssue()
+ .setKey("XYZ123")
+ .setComponentKey("foo")
+ .setRuleKey(RuleKey.of("repo", "rule"))
+ .setSeverity("MAJOR")
+ .setMessage("msg")
+ .setStartLine(1)
+ .setGap(2.0)
+ .setStatus("RESOLVED")
+ .setResolution("FIXED")
+ .setReporter("toto")
+ .setAssignee("tata")
+ .setNew(true)
+ .setCreationDate(creationDate);
+ Issue issue = new TrackedIssueAdapter(trackedIssue);
+ assertThat(issue.key()).isEqualTo("XYZ123");
+ assertThat(issue.componentKey()).isEqualTo("foo");
+ assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
+ assertThat(issue.severity()).isEqualTo("MAJOR");
+ assertThat(issue.message()).isEqualTo("msg");
+ assertThat(issue.line()).isEqualTo(1);
+ assertThat(issue.effortToFix()).isEqualTo(2.0);
+ assertThat(issue.status()).isEqualTo("RESOLVED");
+ assertThat(issue.resolution()).isEqualTo("FIXED");
+ assertThat(issue.reporter()).isEqualTo("toto");
+ assertThat(issue.assignee()).isEqualTo("tata");
+ assertThat(issue.isNew()).isTrue();
+ assertThat(issue.attribute("foo")).isNull();
+ assertThat(issue.creationDate()).isEqualTo(creationDate);
+ assertThat(issue.language()).isNull();
+ assertThat(issue.updateDate()).isNull();
+ assertThat(issue.closeDate()).isNull();
+ assertThat(issue.authorLogin()).isNull();
+ assertThat(issue.actionPlanKey()).isNull();
+ assertThat(issue.comments()).isEmpty();
+ assertThat(issue.debt()).isNull();
+ assertThat(issue.projectKey()).isNull();
+ assertThat(issue.projectUuid()).isNull();
+ assertThat(issue.componentUuid()).isNull();
+ assertThat(issue.tags()).isEmpty();
+
+ assertThat(issue).isNotEqualTo(null);
+ assertThat(issue).isNotEqualTo("Foo");
+ assertThat(issue).isEqualTo(new TrackedIssueAdapter(trackedIssue));
+ assertThat(issue.hashCode()).isEqualTo(trackedIssue.key().hashCode());
+ assertThat(issue).isNotEqualTo(new TrackedIssueAdapter(new TrackedIssue()
+ .setKey("another")));
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/EnforceIssuesFilterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/EnforceIssuesFilterTest.java
new file mode 100644
index 00000000000..28df728657d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/EnforceIssuesFilterTest.java
@@ -0,0 +1,149 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore;
+
+import org.sonar.api.scan.issue.filter.FilterableIssue;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.scan.issue.filter.IssueFilterChain;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.WildcardPattern;
+import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer;
+import org.sonar.batch.issue.ignore.pattern.IssuePattern;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class EnforceIssuesFilterTest {
+
+ private IssueInclusionPatternInitializer exclusionPatternInitializer;
+ private EnforceIssuesFilter ignoreFilter;
+ private FilterableIssue issue;
+ private IssueFilterChain chain;
+
+ @Before
+ public void init() {
+ exclusionPatternInitializer = mock(IssueInclusionPatternInitializer.class);
+ issue = mock(FilterableIssue.class);
+ chain = mock(IssueFilterChain.class);
+ when(chain.accept(issue)).thenReturn(true);
+
+ ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer);
+ }
+
+ @Test
+ public void shouldPassToChainIfNoConfiguredPatterns() {
+ assertThat(ignoreFilter.accept(issue, chain)).isTrue();
+ verify(chain).accept(issue);
+ }
+
+ @Test
+ public void shouldPassToChainIfRuleDoesNotMatch() {
+ String rule = "rule";
+ RuleKey ruleKey = mock(RuleKey.class);
+ when(ruleKey.toString()).thenReturn(rule);
+ when(issue.ruleKey()).thenReturn(ruleKey);
+
+ IssuePattern matching = mock(IssuePattern.class);
+ WildcardPattern rulePattern = mock(WildcardPattern.class);
+ when(matching.getRulePattern()).thenReturn(rulePattern);
+ when(rulePattern.match(rule)).thenReturn(false);
+ when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching));
+
+ assertThat(ignoreFilter.accept(issue, chain)).isTrue();
+ verify(chain).accept(issue);
+ }
+
+ @Test
+ public void shouldAcceptIssueIfFullyMatched() {
+ String rule = "rule";
+ String path = "org/sonar/api/Issue.java";
+ String componentKey = "org.sonar.api.Issue";
+ RuleKey ruleKey = mock(RuleKey.class);
+ when(ruleKey.toString()).thenReturn(rule);
+ when(issue.ruleKey()).thenReturn(ruleKey);
+ when(issue.componentKey()).thenReturn(componentKey);
+
+ IssuePattern matching = mock(IssuePattern.class);
+ WildcardPattern rulePattern = mock(WildcardPattern.class);
+ when(matching.getRulePattern()).thenReturn(rulePattern);
+ when(rulePattern.match(rule)).thenReturn(true);
+ WildcardPattern pathPattern = mock(WildcardPattern.class);
+ when(matching.getResourcePattern()).thenReturn(pathPattern);
+ when(pathPattern.match(path)).thenReturn(true);
+ when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching));
+ when(exclusionPatternInitializer.getPathForComponent(componentKey)).thenReturn(path);
+
+ assertThat(ignoreFilter.accept(issue, chain)).isTrue();
+ verifyZeroInteractions(chain);
+ }
+
+ @Test
+ public void shouldRefuseIssueIfRuleMatchesButNotPath() {
+ String rule = "rule";
+ String path = "org/sonar/api/Issue.java";
+ String componentKey = "org.sonar.api.Issue";
+ RuleKey ruleKey = mock(RuleKey.class);
+ when(ruleKey.toString()).thenReturn(rule);
+ when(issue.ruleKey()).thenReturn(ruleKey);
+ when(issue.componentKey()).thenReturn(componentKey);
+
+ IssuePattern matching = mock(IssuePattern.class);
+ WildcardPattern rulePattern = mock(WildcardPattern.class);
+ when(matching.getRulePattern()).thenReturn(rulePattern);
+ when(rulePattern.match(rule)).thenReturn(true);
+ WildcardPattern pathPattern = mock(WildcardPattern.class);
+ when(matching.getResourcePattern()).thenReturn(pathPattern);
+ when(pathPattern.match(path)).thenReturn(false);
+ when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching));
+ when(exclusionPatternInitializer.getPathForComponent(componentKey)).thenReturn(path);
+
+ assertThat(ignoreFilter.accept(issue, chain)).isFalse();
+ verifyZeroInteractions(chain);
+ }
+
+ @Test
+ public void shouldRefuseIssueIfRuleMatchesAndPathUnknown() {
+ String rule = "rule";
+ String path = "org/sonar/api/Issue.java";
+ String componentKey = "org.sonar.api.Issue";
+ RuleKey ruleKey = mock(RuleKey.class);
+ when(ruleKey.toString()).thenReturn(rule);
+ when(issue.ruleKey()).thenReturn(ruleKey);
+ when(issue.componentKey()).thenReturn(componentKey);
+
+ IssuePattern matching = mock(IssuePattern.class);
+ WildcardPattern rulePattern = mock(WildcardPattern.class);
+ when(matching.getRulePattern()).thenReturn(rulePattern);
+ when(rulePattern.match(rule)).thenReturn(true);
+ WildcardPattern pathPattern = mock(WildcardPattern.class);
+ when(matching.getResourcePattern()).thenReturn(pathPattern);
+ when(pathPattern.match(path)).thenReturn(false);
+ when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching));
+ when(exclusionPatternInitializer.getPathForComponent(componentKey)).thenReturn(null);
+
+ assertThat(ignoreFilter.accept(issue, chain)).isFalse();
+ verifyZeroInteractions(chain);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/IgnoreIssuesFilterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/IgnoreIssuesFilterTest.java
new file mode 100644
index 00000000000..6cf431e215c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/IgnoreIssuesFilterTest.java
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore;
+
+import org.sonar.api.scan.issue.filter.FilterableIssue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.scan.issue.filter.IssueFilterChain;
+import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer;
+import org.sonar.batch.issue.ignore.pattern.IssuePattern;
+import org.sonar.batch.issue.ignore.pattern.PatternMatcher;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class IgnoreIssuesFilterTest {
+
+ private IssueExclusionPatternInitializer exclusionPatternInitializer;
+ private PatternMatcher exclusionPatternMatcher;
+ private IgnoreIssuesFilter ignoreFilter;
+ private FilterableIssue issue;
+ private IssueFilterChain chain;
+
+ @Before
+ public void init() {
+ exclusionPatternMatcher = mock(PatternMatcher.class);
+ exclusionPatternInitializer = mock(IssueExclusionPatternInitializer.class);
+ when(exclusionPatternInitializer.getPatternMatcher()).thenReturn(exclusionPatternMatcher);
+ issue = mock(FilterableIssue.class);
+ chain = mock(IssueFilterChain.class);
+ when(chain.accept(issue)).thenReturn(true);
+
+ ignoreFilter = new IgnoreIssuesFilter(exclusionPatternInitializer);
+ }
+
+ @Test
+ public void shouldPassToChainIfMatcherHasNoPatternForIssue() {
+ when(exclusionPatternMatcher.getMatchingPattern(issue)).thenReturn(null);
+
+ assertThat(ignoreFilter.accept(issue, chain)).isTrue();
+ }
+
+ @Test
+ public void shouldAcceptOrRefuseIfMatcherHasPatternForIssue() {
+ when(exclusionPatternMatcher.getMatchingPattern(issue)).thenReturn(mock(IssuePattern.class));
+
+ assertThat(ignoreFilter.accept(issue, chain)).isFalse();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java
new file mode 100644
index 00000000000..13e1646bb9d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java
@@ -0,0 +1,141 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore.pattern;
+
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.SonarException;
+import org.sonar.core.config.IssueExclusionProperties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssueExclusionPatternInitializerTest {
+
+ private IssueExclusionPatternInitializer patternsInitializer;
+
+ private Settings settings;
+
+ @Before
+ public void init() {
+ settings = new Settings(new PropertyDefinitions(IssueExclusionProperties.all()));
+ patternsInitializer = new IssueExclusionPatternInitializer(settings);
+ }
+
+ @Test
+ public void testNoConfiguration() {
+ patternsInitializer.initPatterns();
+ assertThat(patternsInitializer.hasConfiguredPatterns()).isFalse();
+ assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0);
+ }
+
+ @Test
+ public void shouldHavePatternsBasedOnMulticriteriaPattern() {
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1,2");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "org/foo/Bar.java");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "*");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".2." + "resourceKey", "org/foo/Hello.java");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".2." + "ruleKey", "checkstyle:MagicNumber");
+ patternsInitializer.initPatterns();
+
+ assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
+ assertThat(patternsInitializer.hasFileContentPattern()).isFalse();
+ assertThat(patternsInitializer.hasMulticriteriaPatterns()).isTrue();
+ assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(2);
+ assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(0);
+ assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(0);
+
+ patternsInitializer.initializePatternsForPath("org/foo/Bar.java", "org.foo.Bar");
+ patternsInitializer.initializePatternsForPath("org/foo/Baz.java", "org.foo.Baz");
+ patternsInitializer.initializePatternsForPath("org/foo/Hello.java", "org.foo.Hello");
+
+ assertThat(patternsInitializer.getPatternMatcher().getPatternsForComponent("org.foo.Bar")).hasSize(1);
+ assertThat(patternsInitializer.getPatternMatcher().getPatternsForComponent("org.foo.Baz")).hasSize(0);
+ assertThat(patternsInitializer.getPatternMatcher().getPatternsForComponent("org.foo.Hello")).hasSize(1);
+
+ }
+
+ @Test(expected = SonarException.class)
+ public void shouldLogInvalidResourceKey() {
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "*");
+ patternsInitializer.initPatterns();
+ }
+
+ @Test(expected = SonarException.class)
+ public void shouldLogInvalidRuleKey() {
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "*");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "");
+ patternsInitializer.initPatterns();
+ }
+
+ @Test
+ public void shouldReturnBlockPattern() {
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY, "1,2,3");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// SONAR-OFF");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.END_BLOCK_REGEXP, "// SONAR-ON");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// FOO-OFF");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionProperties.END_BLOCK_REGEXP, "// FOO-ON");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// IGNORE-TO-EOF");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionProperties.END_BLOCK_REGEXP, "");
+ patternsInitializer.loadFileContentPatterns();
+
+ assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
+ assertThat(patternsInitializer.hasFileContentPattern()).isTrue();
+ assertThat(patternsInitializer.hasMulticriteriaPatterns()).isFalse();
+ assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0);
+ assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(3);
+ assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(0);
+ }
+
+ @Test(expected = SonarException.class)
+ public void shouldLogInvalidStartBlockPattern() {
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY, "1");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.END_BLOCK_REGEXP, "// SONAR-ON");
+ patternsInitializer.loadFileContentPatterns();
+ }
+
+ @Test
+ public void shouldReturnAllFilePattern() {
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY, "1,2");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionProperties.FILE_REGEXP, "@SONAR-IGNORE-ALL");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".2." + IssueExclusionProperties.FILE_REGEXP, "//FOO-IGNORE-ALL");
+ patternsInitializer.loadFileContentPatterns();
+
+ assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
+ assertThat(patternsInitializer.hasFileContentPattern()).isTrue();
+ assertThat(patternsInitializer.hasMulticriteriaPatterns()).isFalse();
+ assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0);
+ assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(0);
+ assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(2);
+ }
+
+ @Test(expected = SonarException.class)
+ public void shouldLogInvalidAllFilePattern() {
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY, "1");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionProperties.FILE_REGEXP, "");
+ patternsInitializer.loadFileContentPatterns();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueInclusionPatternInitializerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueInclusionPatternInitializerTest.java
new file mode 100644
index 00000000000..25c74587522
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueInclusionPatternInitializerTest.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore.pattern;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.Settings;
+import org.sonar.core.config.IssueExclusionProperties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssueInclusionPatternInitializerTest {
+
+ private IssueInclusionPatternInitializer patternsInitializer;
+
+ private Settings settings;
+
+ @Before
+ public void init() {
+ settings = new Settings(new PropertyDefinitions(IssueExclusionProperties.all()));
+ patternsInitializer = new IssueInclusionPatternInitializer(settings);
+ }
+
+ @Test
+ public void testNoConfiguration() {
+ patternsInitializer.initPatterns();
+ assertThat(patternsInitializer.hasConfiguredPatterns()).isFalse();
+ }
+
+ @Test
+ public void shouldHavePatternsBasedOnMulticriteriaPattern() {
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria", "1,2");
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".1." + "resourceKey", "org/foo/Bar.java");
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".1." + "ruleKey", "*");
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".2." + "resourceKey", "org/foo/Hello.java");
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".2." + "ruleKey", "checkstyle:MagicNumber");
+ patternsInitializer.initPatterns();
+
+ assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
+ assertThat(patternsInitializer.hasMulticriteriaPatterns()).isTrue();
+ assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(2);
+
+ patternsInitializer.initializePatternsForPath("org/foo/Bar.java", "org.foo.Bar");
+ patternsInitializer.initializePatternsForPath("org/foo/Baz.java", "org.foo.Baz");
+ patternsInitializer.initializePatternsForPath("org/foo/Hello.java", "org.foo.Hello");
+
+ assertThat(patternsInitializer.getPathForComponent("org.foo.Bar")).isEqualTo("org/foo/Bar.java");
+ assertThat(patternsInitializer.getPathForComponent("org.foo.Baz")).isEqualTo("org/foo/Baz.java");
+ assertThat(patternsInitializer.getPathForComponent("org.foo.Hello")).isEqualTo("org/foo/Hello.java");
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssuePatternTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssuePatternTest.java
new file mode 100644
index 00000000000..e67111d0f9c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/IssuePatternTest.java
@@ -0,0 +1,114 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore.pattern;
+
+import org.sonar.api.scan.issue.filter.FilterableIssue;
+
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class IssuePatternTest {
+
+ @Test
+ public void shouldMatchLines() {
+ IssuePattern pattern = new IssuePattern("*", "*");
+ pattern.addLine(12).addLine(15).addLineRange(20, 25);
+
+ assertThat(pattern.matchLine(3)).isFalse();
+ assertThat(pattern.matchLine(12)).isTrue();
+ assertThat(pattern.matchLine(14)).isFalse();
+ assertThat(pattern.matchLine(21)).isTrue();
+ assertThat(pattern.matchLine(6599)).isFalse();
+ }
+
+ @Test
+ public void shouldMatchJavaFile() {
+ String javaFile = "org.foo.Bar";
+ assertThat(new IssuePattern("org.foo.Bar", "*").matchResource(javaFile)).isTrue();
+ assertThat(new IssuePattern("org.foo.*", "*").matchResource(javaFile)).isTrue();
+ assertThat(new IssuePattern("*Bar", "*").matchResource(javaFile)).isTrue();
+ assertThat(new IssuePattern("*", "*").matchResource(javaFile)).isTrue();
+ assertThat(new IssuePattern("org.*.?ar", "*").matchResource(javaFile)).isTrue();
+
+ assertThat(new IssuePattern("org.other.Hello", "*").matchResource(javaFile)).isFalse();
+ assertThat(new IssuePattern("org.foo.Hello", "*").matchResource(javaFile)).isFalse();
+ assertThat(new IssuePattern("org.*.??ar", "*").matchResource(javaFile)).isFalse();
+ assertThat(new IssuePattern("org.*.??ar", "*").matchResource(null)).isFalse();
+ assertThat(new IssuePattern("org.*.??ar", "*").matchResource("plop")).isFalse();
+ }
+
+ @Test
+ public void shouldMatchRule() {
+ RuleKey rule = Rule.create("checkstyle", "IllegalRegexp", "").ruleKey();
+ assertThat(new IssuePattern("*", "*").matchRule(rule)).isTrue();
+ assertThat(new IssuePattern("*", "checkstyle:*").matchRule(rule)).isTrue();
+ assertThat(new IssuePattern("*", "checkstyle:IllegalRegexp").matchRule(rule)).isTrue();
+ assertThat(new IssuePattern("*", "checkstyle:Illegal*").matchRule(rule)).isTrue();
+ assertThat(new IssuePattern("*", "*:*Illegal*").matchRule(rule)).isTrue();
+
+ assertThat(new IssuePattern("*", "pmd:IllegalRegexp").matchRule(rule)).isFalse();
+ assertThat(new IssuePattern("*", "pmd:*").matchRule(rule)).isFalse();
+ assertThat(new IssuePattern("*", "*:Foo*IllegalRegexp").matchRule(rule)).isFalse();
+ }
+
+ @Test
+ public void shouldMatchViolation() {
+ Rule rule = Rule.create("checkstyle", "IllegalRegexp", "");
+ String javaFile = "org.foo.Bar";
+
+ IssuePattern pattern = new IssuePattern("*", "*");
+ pattern.addLine(12);
+
+ assertThat(pattern.match(create(rule, javaFile, null))).isFalse();
+ assertThat(pattern.match(create(rule, javaFile, 12))).isTrue();
+ assertThat(pattern.match(create((Rule) null, javaFile, 5))).isFalse();
+ assertThat(pattern.match(create(rule, null, null))).isFalse();
+ assertThat(pattern.match(create((Rule) null, null, null))).isFalse();
+ }
+
+ private FilterableIssue create(Rule rule, String component, Integer line) {
+ FilterableIssue mockIssue = mock(FilterableIssue.class);
+ RuleKey ruleKey = null;
+ if (rule != null) {
+ ruleKey = rule.ruleKey();
+ }
+ when(mockIssue.ruleKey()).thenReturn(ruleKey);
+ when(mockIssue.componentKey()).thenReturn(component);
+ when(mockIssue.line()).thenReturn(line);
+ return mockIssue;
+ }
+
+ @Test
+ public void shouldNotMatchNullRule() {
+ assertThat(new IssuePattern("*", "*").matchRule(null)).isFalse();
+ }
+
+ @Test
+ public void shouldPrintPatternToString() {
+ IssuePattern pattern = new IssuePattern("*", "checkstyle:*");
+
+ assertThat(pattern.toString()).isEqualTo(
+ "IssuePattern[resourcePattern=*,rulePattern=checkstyle:*,lines=[],lineRanges=[],beginBlockRegexp=<null>,endBlockRegexp=<null>,allFileRegexp=<null>,checkLines=true]");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/LineRangeTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/LineRangeTest.java
new file mode 100644
index 00000000000..8af95b5d4ad
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/LineRangeTest.java
@@ -0,0 +1,72 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore.pattern;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LineRangeTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void lineRangeShouldBeOrdered() {
+ new LineRange(25, 12);
+ }
+
+ @Test
+ public void shouldConvertLineRangeToLines() {
+ LineRange range = new LineRange(12, 15);
+
+ assertThat(range.toLines()).containsOnly(12, 13, 14, 15);
+ }
+
+ @Test
+ public void shouldTestInclusionInRangeOfLines() {
+ LineRange range = new LineRange(12, 15);
+
+ assertThat(range.in(3)).isFalse();
+ assertThat(range.in(12)).isTrue();
+ assertThat(range.in(13)).isTrue();
+ assertThat(range.in(14)).isTrue();
+ assertThat(range.in(15)).isTrue();
+ assertThat(range.in(16)).isFalse();
+ }
+
+ @Test
+ public void testToString() throws Exception {
+ assertThat(new LineRange(12, 15).toString()).isEqualTo("[12-15]");
+ }
+
+ @Test
+ public void testEquals() throws Exception {
+ LineRange range = new LineRange(12, 15);
+ assertThat(range).isEqualTo(range);
+ assertThat(range).isEqualTo(new LineRange(12, 15));
+ assertThat(range).isNotEqualTo(new LineRange(12, 2000));
+ assertThat(range).isNotEqualTo(new LineRange(1000, 2000));
+ assertThat(range).isNotEqualTo(null);
+ assertThat(range).isNotEqualTo(new StringBuffer());
+ }
+
+ @Test
+ public void testHashCode() throws Exception {
+ assertThat(new LineRange(12, 15).hashCode()).isEqualTo(new LineRange(12, 15).hashCode());
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternDecoderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternDecoderTest.java
new file mode 100644
index 00000000000..3501d1a2326
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternDecoderTest.java
@@ -0,0 +1,187 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore.pattern;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.SonarException;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PatternDecoderTest {
+
+ private PatternDecoder decoder = new PatternDecoder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void shouldReadString() {
+ String patternsList = "# a comment followed by a blank line\n\n" +
+ "# suppress all violations\n" +
+ "*;*;*\n\n" +
+ "# exclude a Java file\n" +
+ "com.foo.Bar;*;*\n\n" +
+ "# exclude a Java package\n" +
+ "com.foo.*;*;*\n\n" +
+ "# exclude a specific rule\n" +
+ "*;checkstyle:IllegalRegexp;*\n\n" +
+ "# exclude a specific rule on a specific file\n" +
+ "com.foo.Bar;checkstyle:IllegalRegexp;*\n";
+ List<IssuePattern> patterns = decoder.decode(patternsList);
+
+ assertThat(patterns).hasSize(5);
+ }
+
+ @Test
+ public void shouldCheckFormatOfResource() {
+ assertThat(PatternDecoder.isResource("")).isFalse();
+ assertThat(PatternDecoder.isResource("*")).isTrue();
+ assertThat(PatternDecoder.isResource("com.foo.*")).isTrue();
+ }
+
+ @Test
+ public void shouldCheckFormatOfRule() {
+ assertThat(PatternDecoder.isRule("")).isFalse();
+ assertThat(PatternDecoder.isRule("*")).isTrue();
+ assertThat(PatternDecoder.isRule("com.foo.*")).isTrue();
+ }
+
+ @Test
+ public void shouldCheckFormatOfLinesRange() {
+ assertThat(PatternDecoder.isLinesRange("")).isFalse();
+ assertThat(PatternDecoder.isLinesRange(" ")).isFalse();
+ assertThat(PatternDecoder.isLinesRange("12")).isFalse();
+ assertThat(PatternDecoder.isLinesRange("12,212")).isFalse();
+
+ assertThat(PatternDecoder.isLinesRange("*")).isTrue();
+ assertThat(PatternDecoder.isLinesRange("[]")).isTrue();
+ assertThat(PatternDecoder.isLinesRange("[13]")).isTrue();
+ assertThat(PatternDecoder.isLinesRange("[13,24]")).isTrue();
+ assertThat(PatternDecoder.isLinesRange("[13,24,25-500]")).isTrue();
+ assertThat(PatternDecoder.isLinesRange("[24-65]")).isTrue();
+ assertThat(PatternDecoder.isLinesRange("[13,24-65,84-89,122]")).isTrue();
+ }
+
+ @Test
+ public void shouldReadStarPatterns() {
+ IssuePattern pattern = decoder.decodeLine("*;*;*");
+
+ assertThat(pattern.getResourcePattern().toString()).isEqualTo("*");
+ assertThat(pattern.getRulePattern().toString()).isEqualTo("*");
+ assertThat(pattern.isCheckLines()).isFalse();
+ }
+
+ @Test
+ public void shouldReadLineIds() {
+ IssuePattern pattern = decoder.decodeLine("*;*;[10,25,98]");
+
+ assertThat(pattern.isCheckLines()).isTrue();
+ assertThat(pattern.getAllLines()).containsOnly(10, 25, 98);
+ }
+
+ @Test
+ public void shouldReadRangeOfLineIds() {
+ IssuePattern pattern = decoder.decodeLine("*;*;[10-12,25,97-100]");
+
+ assertThat(pattern.isCheckLines()).isTrue();
+ assertThat(pattern.getAllLines()).containsOnly(10, 11, 12, 25, 97, 98, 99, 100);
+ }
+
+ @Test
+ public void shouldNotExcludeLines() {
+ // [] is different than *
+ // - all violations are excluded on *
+ // * no violations are excluded on []
+ IssuePattern pattern = decoder.decodeLine("*;*;[]");
+
+ assertThat(pattern.isCheckLines()).isTrue();
+ assertThat(pattern.getAllLines()).isEmpty();
+ }
+
+ @Test
+ public void shouldReadBlockPattern() {
+ IssuePattern pattern = decoder.decodeLine("SONAR-OFF;SONAR-ON");
+
+ assertThat(pattern.getResourcePattern()).isNull();
+ assertThat(pattern.getBeginBlockRegexp()).isEqualTo("SONAR-OFF");
+ assertThat(pattern.getEndBlockRegexp()).isEqualTo("SONAR-ON");
+ }
+
+ @Test
+ public void shouldReadAllFilePattern() {
+ IssuePattern pattern = decoder.decodeLine("SONAR-ALL-OFF");
+
+ assertThat(pattern.getResourcePattern()).isNull();
+ assertThat(pattern.getAllFileRegexp()).isEqualTo("SONAR-ALL-OFF");
+ }
+
+ @Test
+ public void shouldFailToReadUncorrectLine1() {
+ thrown.expect(SonarException.class);
+ thrown.expectMessage("Exclusions > Issues : Invalid format. The following line has more than 3 fields separated by comma");
+
+ decoder.decode(";;;;");
+ }
+
+ @Test
+ public void shouldFailToReadUncorrectLine3() {
+ thrown.expect(SonarException.class);
+ thrown.expectMessage("Exclusions > Issues : Invalid format. The first field does not define a resource pattern");
+
+ decoder.decode(";*;*");
+ }
+
+ @Test
+ public void shouldFailToReadUncorrectLine4() {
+ thrown.expect(SonarException.class);
+ thrown.expectMessage("Exclusions > Issues : Invalid format. The second field does not define a rule pattern");
+
+ decoder.decode("*;;*");
+ }
+
+ @Test
+ public void shouldFailToReadUncorrectLine5() {
+ thrown.expect(SonarException.class);
+ thrown.expectMessage("Exclusions > Issues : Invalid format. The third field does not define a range of lines");
+
+ decoder.decode("*;*;blabla");
+ }
+
+ @Test
+ public void shouldFailToReadUncorrectLine6() {
+ thrown.expect(SonarException.class);
+ thrown.expectMessage("Exclusions > Issues : Invalid format. The first field does not define a regular expression");
+
+ decoder.decode(";ON");
+ }
+
+ @Test
+ public void shouldAcceptEmptyEndBlockRegexp() {
+ IssuePattern pattern = decoder.decodeLine("OFF;");
+
+ assertThat(pattern.getResourcePattern()).isNull();
+ assertThat(pattern.getBeginBlockRegexp()).isEqualTo("OFF");
+ assertThat(pattern.getEndBlockRegexp()).isEmpty();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternMatcherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternMatcherTest.java
new file mode 100644
index 00000000000..1d69a30c371
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/pattern/PatternMatcherTest.java
@@ -0,0 +1,119 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore.pattern;
+
+import org.sonar.api.scan.issue.filter.FilterableIssue;
+
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class PatternMatcherTest {
+
+ public static final Rule CHECKSTYLE_RULE = Rule.create("checkstyle", "MagicNumber", "");
+ public static final String JAVA_FILE = "org.foo.Hello";
+
+ private PatternMatcher patternMatcher;
+
+ @Before
+ public void setUp() {
+ patternMatcher = new PatternMatcher();
+ }
+
+ @Test
+ public void shouldReturnExtraPatternForResource() {
+ String file = "foo";
+ patternMatcher.addPatternToExcludeResource(file);
+
+ IssuePattern extraPattern = patternMatcher.getPatternsForComponent(file).iterator().next();
+ assertThat(extraPattern.matchResource(file)).isTrue();
+ assertThat(extraPattern.isCheckLines()).isFalse();
+ }
+
+ @Test
+ public void shouldReturnExtraPatternForLinesOfResource() {
+ String file = "foo";
+ Set<LineRange> lineRanges = Sets.newHashSet();
+ lineRanges.add(new LineRange(25, 28));
+ patternMatcher.addPatternToExcludeLines(file, lineRanges);
+
+ IssuePattern extraPattern = patternMatcher.getPatternsForComponent(file).iterator().next();
+ assertThat(extraPattern.matchResource(file)).isTrue();
+ assertThat(extraPattern.getAllLines()).isEqualTo(Sets.newHashSet(25, 26, 27, 28));
+ }
+
+ @Test
+ public void shouldHaveNoMatcherIfNoneDefined() {
+ assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, null))).isNull();
+ }
+
+ @Test
+ public void shouldMatchWithStandardPatterns() {
+ patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello;checkstyle:MagicNumber;[15-200]"));
+
+ assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, 150))).isNotNull();
+ }
+
+ @Test
+ public void shouldNotMatchWithStandardPatterns() {
+ patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello;checkstyle:MagicNumber;[15-200]"));
+
+ assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, 5))).isNull();
+ }
+
+ @Test
+ public void shouldMatchWithExtraPattern() {
+ patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello;*;[15-200]"));
+
+ assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, 150))).isNotNull();
+ }
+
+ @Test
+ public void shouldNotMatchWithExtraPattern() {
+ patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello;*;[15-200]"));
+
+ assertThat(patternMatcher.getMatchingPattern(create(CHECKSTYLE_RULE, JAVA_FILE, 5))).isNull();
+ }
+
+ private FilterableIssue create(Rule rule, String component, Integer line) {
+ FilterableIssue mockIssue = mock(FilterableIssue.class);
+ RuleKey ruleKey = null;
+ if (rule != null) {
+ ruleKey = rule.ruleKey();
+ }
+ when(mockIssue.ruleKey()).thenReturn(ruleKey);
+ when(mockIssue.componentKey()).thenReturn(component);
+ when(mockIssue.line()).thenReturn(line);
+ return mockIssue;
+ }
+
+ private IssuePattern createPattern(String line) {
+ return new PatternDecoder().decode(line).get(0);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java
new file mode 100644
index 00000000000..52a0e59cbbe
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java
@@ -0,0 +1,157 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore.scanner;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer;
+import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer;
+import org.sonar.batch.issue.ignore.pattern.PatternMatcher;
+
+import java.io.File;
+import java.io.IOException;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class IssueExclusionsLoaderTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Mock
+ private IssueExclusionsRegexpScanner regexpScanner;
+
+ @Mock
+ private IssueInclusionPatternInitializer inclusionPatternInitializer;
+
+ @Mock
+ private IssueExclusionPatternInitializer exclusionPatternInitializer;
+
+ @Mock
+ private PatternMatcher patternMatcher;
+
+ private DefaultFileSystem fs;
+ private IssueExclusionsLoader scanner;
+ private File baseDir;
+
+ @Before
+ public void before() throws Exception {
+ baseDir = temp.newFolder();
+ fs = new DefaultFileSystem(baseDir.toPath()).setEncoding(UTF_8);
+ MockitoAnnotations.initMocks(this);
+ scanner = new IssueExclusionsLoader(regexpScanner, exclusionPatternInitializer, inclusionPatternInitializer, fs);
+ }
+
+ @Test
+ public void testToString() throws Exception {
+ assertThat(scanner.toString()).isEqualTo("Issues Exclusions - Source Scanner");
+ }
+
+ @Test
+ public void shouldExecute() {
+ when(exclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(true);
+ when(inclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(true);
+ assertThat(scanner.shouldExecuteOnProject(null)).isTrue();
+
+ when(exclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(true);
+ when(inclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(false);
+ assertThat(scanner.shouldExecuteOnProject(null)).isTrue();
+
+ when(exclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(false);
+ when(inclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(true);
+ assertThat(scanner.shouldExecuteOnProject(null)).isTrue();
+
+ when(exclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(false);
+ when(inclusionPatternInitializer.hasConfiguredPatterns()).thenReturn(false);
+ assertThat(scanner.shouldExecuteOnProject(null)).isFalse();
+
+ }
+
+ @Test
+ public void shouldAnalyzeProject() throws IOException {
+ File javaFile1 = new File(baseDir, "src/main/java/Foo.java");
+ fs.add(new DefaultInputFile("polop", "src/main/java/Foo.java")
+ .setType(InputFile.Type.MAIN));
+ File javaTestFile1 = new File(baseDir, "src/test/java/FooTest.java");
+ fs.add(new DefaultInputFile("polop", "src/test/java/FooTest.java")
+ .setType(InputFile.Type.TEST));
+
+ when(exclusionPatternInitializer.hasFileContentPattern()).thenReturn(true);
+
+ scanner.execute();
+
+ verify(inclusionPatternInitializer).initializePatternsForPath("src/main/java/Foo.java", "polop:src/main/java/Foo.java");
+ verify(inclusionPatternInitializer).initializePatternsForPath("src/test/java/FooTest.java", "polop:src/test/java/FooTest.java");
+ verify(exclusionPatternInitializer).initializePatternsForPath("src/main/java/Foo.java", "polop:src/main/java/Foo.java");
+ verify(exclusionPatternInitializer).initializePatternsForPath("src/test/java/FooTest.java", "polop:src/test/java/FooTest.java");
+ verify(regexpScanner).scan("polop:src/main/java/Foo.java", javaFile1, UTF_8);
+ verify(regexpScanner).scan("polop:src/test/java/FooTest.java", javaTestFile1, UTF_8);
+ }
+
+ @Test
+ public void shouldAnalyseFilesOnlyWhenRegexConfigured() {
+ File javaFile1 = new File(baseDir, "src/main/java/Foo.java");
+ fs.add(new DefaultInputFile("polop", "src/main/java/Foo.java")
+ .setType(InputFile.Type.MAIN));
+ File javaTestFile1 = new File(baseDir, "src/test/java/FooTest.java");
+ fs.add(new DefaultInputFile("polop", "src/test/java/FooTest.java")
+ .setType(InputFile.Type.TEST));
+ when(exclusionPatternInitializer.hasFileContentPattern()).thenReturn(false);
+
+ scanner.execute();
+
+ verify(inclusionPatternInitializer).initializePatternsForPath("src/main/java/Foo.java", "polop:src/main/java/Foo.java");
+ verify(inclusionPatternInitializer).initializePatternsForPath("src/test/java/FooTest.java", "polop:src/test/java/FooTest.java");
+ verify(exclusionPatternInitializer).initializePatternsForPath("src/main/java/Foo.java", "polop:src/main/java/Foo.java");
+ verify(exclusionPatternInitializer).initializePatternsForPath("src/test/java/FooTest.java", "polop:src/test/java/FooTest.java");
+ verifyZeroInteractions(regexpScanner);
+ }
+
+ @Test
+ public void shouldReportFailure() throws IOException {
+ File phpFile1 = new File(baseDir, "src/Foo.php");
+ fs.add(new DefaultInputFile("polop", "src/Foo.php")
+ .setType(InputFile.Type.MAIN));
+
+ when(exclusionPatternInitializer.hasFileContentPattern()).thenReturn(true);
+ doThrow(new IOException("BUG")).when(regexpScanner).scan("polop:src/Foo.php", phpFile1, UTF_8);
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Unable to read the source file");
+
+ scanner.execute();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java
new file mode 100644
index 00000000000..f4aa4ebd9f1
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java
@@ -0,0 +1,168 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.ignore.scanner;
+
+import com.google.common.collect.Sets;
+import com.google.common.io.Resources;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer;
+import org.sonar.batch.issue.ignore.pattern.IssuePattern;
+import org.sonar.batch.issue.ignore.pattern.LineRange;
+import org.sonar.batch.issue.ignore.pattern.PatternMatcher;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Set;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class IssueExclusionsRegexpScannerTest {
+
+ private IssueExclusionsRegexpScanner regexpScanner;
+
+ private String javaFile;
+ @Mock
+ private IssueExclusionPatternInitializer patternsInitializer;
+ @Mock
+ private PatternMatcher patternMatcher;
+ @Mock
+ private IssuePattern allFilePattern;
+ @Mock
+ private IssuePattern blockPattern1;
+ @Mock
+ private IssuePattern blockPattern2;
+
+ @Before
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+
+ when(allFilePattern.getAllFileRegexp()).thenReturn("@SONAR-IGNORE-ALL");
+ when(blockPattern1.getBeginBlockRegexp()).thenReturn("// SONAR-OFF");
+ when(blockPattern1.getEndBlockRegexp()).thenReturn("// SONAR-ON");
+ when(blockPattern2.getBeginBlockRegexp()).thenReturn("// FOO-OFF");
+ when(blockPattern2.getEndBlockRegexp()).thenReturn("// FOO-ON");
+ when(patternsInitializer.getAllFilePatterns()).thenReturn(Arrays.asList(allFilePattern));
+ when(patternsInitializer.getBlockPatterns()).thenReturn(Arrays.asList(blockPattern1, blockPattern2));
+ when(patternsInitializer.getPatternMatcher()).thenReturn(patternMatcher);
+
+ regexpScanner = new IssueExclusionsRegexpScanner(patternsInitializer);
+ verify(patternsInitializer, times(1)).getAllFilePatterns();
+ verify(patternsInitializer, times(1)).getBlockPatterns();
+
+ javaFile = "org.sonar.test.MyFile";
+ }
+
+ @Test
+ public void shouldDoNothing() throws Exception {
+ regexpScanner.scan(javaFile, new File(Resources.getResource(
+ "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-no-regexp.txt").toURI()), UTF_8);
+
+ verifyNoMoreInteractions(patternsInitializer);
+ }
+
+ @Test
+ public void shouldAddPatternToExcludeFile() throws Exception {
+ regexpScanner.scan(javaFile, new File(Resources.getResource(
+ "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-single-regexp.txt").toURI()), UTF_8);
+
+ verify(patternsInitializer).getPatternMatcher();
+ verify(patternMatcher, times(1)).addPatternToExcludeResource(javaFile);
+ verifyNoMoreInteractions(patternsInitializer);
+ }
+
+ @Test
+ public void shouldAddPatternToExcludeFileEvenIfAlsoDoubleRegexps() throws Exception {
+ regexpScanner.scan(javaFile, new File(Resources.getResource(
+ "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-single-regexp-and-double-regexp.txt").toURI()), UTF_8);
+
+ verify(patternsInitializer).getPatternMatcher();
+ verify(patternMatcher, times(1)).addPatternToExcludeResource(javaFile);
+ verifyNoMoreInteractions(patternsInitializer);
+ }
+
+ @Test
+ public void shouldAddPatternToExcludeLines() throws Exception {
+ regexpScanner.scan(javaFile, new File(Resources.getResource(
+ "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp.txt").toURI()), UTF_8);
+
+ Set<LineRange> lineRanges = Sets.newHashSet();
+ lineRanges.add(new LineRange(21, 25));
+ verify(patternsInitializer).getPatternMatcher();
+ verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+ verifyNoMoreInteractions(patternsInitializer);
+ }
+
+ @Test
+ public void shouldAddPatternToExcludeLinesTillTheEnd() throws Exception {
+ regexpScanner.scan(javaFile, new File(Resources.getResource(
+ "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp-unfinished.txt").toURI()), UTF_8);
+
+ Set<LineRange> lineRanges = Sets.newHashSet();
+ lineRanges.add(new LineRange(21, 34));
+ verify(patternsInitializer).getPatternMatcher();
+ verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+ verifyNoMoreInteractions(patternsInitializer);
+ }
+
+ @Test
+ public void shouldAddPatternToExcludeSeveralLineRanges() throws Exception {
+ regexpScanner.scan(javaFile, new File(Resources.getResource(
+ "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp-twice.txt").toURI()), UTF_8);
+
+ Set<LineRange> lineRanges = Sets.newHashSet();
+ lineRanges.add(new LineRange(21, 25));
+ lineRanges.add(new LineRange(29, 33));
+ verify(patternsInitializer).getPatternMatcher();
+ verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+ verifyNoMoreInteractions(patternsInitializer);
+ }
+
+ @Test
+ public void shouldAddPatternToExcludeLinesWithWrongOrder() throws Exception {
+ regexpScanner.scan(javaFile, new File(Resources.getResource(
+ "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp-wrong-order.txt").toURI()), UTF_8);
+
+ Set<LineRange> lineRanges = Sets.newHashSet();
+ lineRanges.add(new LineRange(25, 35));
+ verify(patternsInitializer).getPatternMatcher();
+ verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+ verifyNoMoreInteractions(patternsInitializer);
+ }
+
+ @Test
+ public void shouldAddPatternToExcludeLinesWithMess() throws Exception {
+ regexpScanner.scan(javaFile, new File(Resources.getResource(
+ "org/sonar/batch/issue/ignore/scanner/IssueExclusionsRegexpScannerTest/file-with-double-regexp-mess.txt").toURI()), UTF_8);
+
+ Set<LineRange> lineRanges = Sets.newHashSet();
+ lineRanges.add(new LineRange(21, 29));
+ verify(patternsInitializer).getPatternMatcher();
+ verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+ verifyNoMoreInteractions(patternsInitializer);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java
new file mode 100644
index 00000000000..7d8ab1d2319
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.tracking;
+
+import org.sonar.batch.cache.WSLoader.LoadStrategy;
+import org.sonar.batch.cache.WSLoaderResult;
+import org.sonar.batch.cache.WSLoader;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.HttpDownloader;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import static org.mockito.Matchers.any;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DefaultServerLineHashesLoaderTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Before
+ public void before() {
+ }
+
+ @Test
+ public void should_download_source_from_ws_if_preview_mode() {
+ WSLoader wsLoader = mock(WSLoader.class);
+ when(wsLoader.loadString(anyString(), any(LoadStrategy.class))).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true));
+
+ ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(wsLoader);
+
+ String[] hashes = lastSnapshots.getLineHashes("myproject:org/foo/Bar.c", null);
+ assertThat(hashes).containsOnly("ae12", "", "43fb");
+ verify(wsLoader).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FBar.c", LoadStrategy.CACHE_FIRST);
+ }
+
+ @Test
+ public void should_download_source_with_space_from_ws_if_preview_mode() {
+ WSLoader server = mock(WSLoader.class);
+ when(server.loadString(anyString(), any(LoadStrategy.class))).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true));
+
+ ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server);
+
+ MutableBoolean fromCache = new MutableBoolean();
+ String[] hashes = lastSnapshots.getLineHashes("myproject:org/foo/Foo Bar.c", fromCache);
+ assertThat(fromCache.booleanValue()).isTrue();
+ assertThat(hashes).containsOnly("ae12", "", "43fb");
+ verify(server).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FFoo+Bar.c", LoadStrategy.CACHE_FIRST);
+ }
+
+ @Test
+ public void should_fail_to_download_source_from_ws() throws URISyntaxException {
+ WSLoader server = mock(WSLoader.class);
+ when(server.loadString(anyString(), any(LoadStrategy.class))).thenThrow(new HttpDownloader.HttpException(new URI(""), 500));
+
+ ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server);
+
+ thrown.expect(HttpDownloader.HttpException.class);
+ lastSnapshots.getLineHashes("foo", null);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/RollingFileHashesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/RollingFileHashesTest.java
new file mode 100644
index 00000000000..8648fba48d1
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/RollingFileHashesTest.java
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.tracking;
+
+import org.junit.Test;
+
+import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RollingFileHashesTest {
+
+ @Test
+ public void test_equals() {
+ RollingFileHashes a = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line0"), md5Hex("line1"), md5Hex("line2")}), 1);
+ RollingFileHashes b = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line0"), md5Hex("line1"), md5Hex("line2"), md5Hex("line3")}), 1);
+
+ assertThat(a.getHash(1) == b.getHash(1)).isTrue();
+ assertThat(a.getHash(2) == b.getHash(2)).isTrue();
+ assertThat(a.getHash(3) == b.getHash(3)).isFalse();
+
+ RollingFileHashes c = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line-1"), md5Hex("line0"), md5Hex("line1"), md5Hex("line2"), md5Hex("line3")}), 1);
+ assertThat(a.getHash(1) == c.getHash(2)).isFalse();
+ assertThat(a.getHash(2) == c.getHash(3)).isTrue();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/SourceHashHolderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/SourceHashHolderTest.java
new file mode 100644
index 00000000000..9acf02b3577
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/SourceHashHolderTest.java
@@ -0,0 +1,119 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.tracking;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+
+import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SourceHashHolderTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ SourceHashHolder sourceHashHolder;
+
+ ServerLineHashesLoader lastSnapshots;
+ DefaultInputFile file;
+
+ private File ioFile;
+
+ @Before
+ public void setUp() throws Exception {
+ lastSnapshots = mock(ServerLineHashesLoader.class);
+ file = mock(DefaultInputFile.class);
+ ioFile = temp.newFile();
+ when(file.file()).thenReturn(ioFile);
+ when(file.path()).thenReturn(ioFile.toPath());
+ when(file.lines()).thenReturn(1);
+ when(file.charset()).thenReturn(StandardCharsets.UTF_8);
+
+ sourceHashHolder = new SourceHashHolder(file, lastSnapshots);
+ }
+
+ @Test
+ public void should_lazy_load_line_hashes() throws Exception {
+ final String source = "source";
+ FileUtils.write(ioFile, source + "\n", StandardCharsets.UTF_8);
+ when(file.lines()).thenReturn(2);
+
+ assertThat(sourceHashHolder.getHashedSource().getHash(1)).isEqualTo(md5Hex(source));
+ assertThat(sourceHashHolder.getHashedSource().getHash(2)).isEqualTo("");
+ verify(file).key();
+ verify(file).status();
+
+ assertThat(sourceHashHolder.getHashedSource().getHash(1)).isEqualTo(md5Hex(source));
+ }
+
+ @Test
+ public void should_lazy_load_reference_hashes_when_status_changed() throws Exception {
+ final String source = "source";
+ String key = "foo:src/Foo.java";
+ FileUtils.write(ioFile, source, StandardCharsets.UTF_8);
+ when(file.key()).thenReturn(key);
+ when(file.status()).thenReturn(InputFile.Status.CHANGED);
+ when(lastSnapshots.getLineHashes(key, null)).thenReturn(new String[] {md5Hex(source)});
+
+ assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source));
+ verify(lastSnapshots).getLineHashes(key, null);
+
+ assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source));
+ Mockito.verifyNoMoreInteractions(lastSnapshots);
+ }
+
+ @Test
+ public void should_not_load_reference_hashes_when_status_same() throws Exception {
+ final String source = "source";
+ String key = "foo:src/Foo.java";
+ FileUtils.write(ioFile, source, StandardCharsets.UTF_8);
+ when(file.key()).thenReturn(key);
+ when(file.status()).thenReturn(InputFile.Status.SAME);
+
+ assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source));
+ Mockito.verifyNoMoreInteractions(lastSnapshots);
+ }
+
+ @Test
+ public void no_reference_hashes_when_status_added() throws Exception {
+ final String source = "source";
+ String key = "foo:src/Foo.java";
+ FileUtils.write(ioFile, source, StandardCharsets.UTF_8);
+ when(file.key()).thenReturn(key);
+ when(file.status()).thenReturn(InputFile.Status.ADDED);
+
+ assertThat(sourceHashHolder.getHashedReference()).isNull();
+ Mockito.verifyNoMoreInteractions(lastSnapshots);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/TrackedIssueTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/TrackedIssueTest.java
new file mode 100644
index 00000000000..4228f912b94
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/issue/tracking/TrackedIssueTest.java
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.issue.tracking;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Test;
+
+public class TrackedIssueTest {
+ @Test
+ public void round_trip() {
+ TrackedIssue issue = new TrackedIssue();
+ issue.setStartLine(15);
+
+ assertThat(issue.getLine()).isEqualTo(15);
+ assertThat(issue.startLine()).isEqualTo(15);
+ }
+
+ @Test
+ public void hashes() {
+ String[] hashArr = new String[] {
+ "hash1", "hash2", "hash3"
+ };
+
+ FileHashes hashes = FileHashes.create(hashArr);
+ TrackedIssue issue = new TrackedIssue(hashes);
+ issue.setStartLine(1);
+ assertThat(issue.getLineHash()).isEqualTo("hash1");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java
new file mode 100644
index 00000000000..619977ce156
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java
@@ -0,0 +1,497 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest;
+
+import org.sonar.api.rule.RuleKey;
+
+import org.sonar.batch.rule.LoadedActiveRule;
+import org.sonar.batch.repository.FileData;
+import org.sonar.api.utils.DateUtils;
+import com.google.common.collect.Table;
+import com.google.common.collect.HashBasedTable;
+import org.sonar.batch.repository.ProjectRepositories;
+import org.sonar.batch.rule.ActiveRulesLoader;
+import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
+import org.sonar.batch.repository.QualityProfileLoader;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.mutable.MutableBoolean;
+
+import javax.annotation.Nullable;
+
+import org.sonarqube.ws.Rules.ListResponse.Rule;
+import org.sonar.batch.bootstrapper.IssueListener;
+import org.sonar.api.server.rule.RulesDefinition.Repository;
+import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.batch.rule.RulesLoader;
+import org.sonar.scanner.protocol.input.GlobalRepositories;
+import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue;
+import com.google.common.base.Function;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.sonar.api.CoreProperties;
+import org.sonar.api.SonarPlugin;
+import org.sonar.api.batch.debt.internal.DefaultDebtModel;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Metric;
+import org.sonar.batch.bootstrapper.Batch;
+import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.batch.bootstrapper.LogOutput;
+import org.sonar.batch.issue.tracking.ServerLineHashesLoader;
+import org.sonar.batch.report.ReportPublisher;
+import org.sonar.batch.repository.GlobalRepositoriesLoader;
+import org.sonar.batch.repository.ProjectRepositoriesLoader;
+import org.sonar.batch.repository.ServerIssuesLoader;
+
+/**
+ * Main utility class for writing batch medium tests.
+ *
+ */
+public class BatchMediumTester {
+
+ public static final String MEDIUM_TEST_ENABLED = "sonar.mediumTest.enabled";
+ private Batch batch;
+ private static Path workingDir = null;
+ private static Path globalWorkingDir = null;
+
+ private static void createWorkingDirs() throws IOException {
+ destroyWorkingDirs();
+
+ workingDir = java.nio.file.Files.createTempDirectory("mediumtest-working-dir");
+ globalWorkingDir = java.nio.file.Files.createTempDirectory("mediumtest-global-working-dir");
+ }
+
+ private static void destroyWorkingDirs() throws IOException {
+ if (workingDir != null) {
+ FileUtils.deleteDirectory(workingDir.toFile());
+ workingDir = null;
+ }
+
+ if (globalWorkingDir != null) {
+ FileUtils.deleteDirectory(globalWorkingDir.toFile());
+ globalWorkingDir = null;
+ }
+
+ }
+
+ public static BatchMediumTesterBuilder builder() {
+ try {
+ createWorkingDirs();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ BatchMediumTesterBuilder builder = new BatchMediumTesterBuilder().registerCoreMetrics();
+ builder.bootstrapProperties.put(MEDIUM_TEST_ENABLED, "true");
+ builder.bootstrapProperties.put(ReportPublisher.KEEP_REPORT_PROP_KEY, "true");
+ builder.bootstrapProperties.put(CoreProperties.WORKING_DIRECTORY, workingDir.toString());
+ builder.bootstrapProperties.put("sonar.userHome", globalWorkingDir.toString());
+ return builder;
+ }
+
+ public static class BatchMediumTesterBuilder {
+ private final FakeGlobalRepositoriesLoader globalRefProvider = new FakeGlobalRepositoriesLoader();
+ private final FakeProjectRepositoriesLoader projectRefProvider = new FakeProjectRepositoriesLoader();
+ private final FakePluginInstaller pluginInstaller = new FakePluginInstaller();
+ private final FakeServerIssuesLoader serverIssues = new FakeServerIssuesLoader();
+ private final FakeServerLineHashesLoader serverLineHashes = new FakeServerLineHashesLoader();
+ private final Map<String, String> bootstrapProperties = new HashMap<>();
+ private final FakeRulesLoader rulesLoader = new FakeRulesLoader();
+ private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader();
+ private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader();
+ private boolean associated = true;
+ private LogOutput logOutput = null;
+
+ public BatchMediumTester build() {
+ return new BatchMediumTester(this);
+ }
+
+ public BatchMediumTesterBuilder setAssociated(boolean associated) {
+ this.associated = associated;
+ return this;
+ }
+
+ public BatchMediumTesterBuilder setLogOutput(LogOutput logOutput) {
+ this.logOutput = logOutput;
+ return this;
+ }
+
+ public BatchMediumTesterBuilder registerPlugin(String pluginKey, File location) {
+ pluginInstaller.add(pluginKey, location);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder registerPlugin(String pluginKey, SonarPlugin instance) {
+ pluginInstaller.add(pluginKey, instance);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder registerCoreMetrics() {
+ for (Metric<?> m : CoreMetrics.getMetrics()) {
+ registerMetric(m);
+ }
+ return this;
+ }
+
+ public BatchMediumTesterBuilder registerMetric(Metric<?> metric) {
+ globalRefProvider.add(metric);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder addQProfile(String language, String name) {
+ qualityProfiles.add(language, name);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder addRule(Rule rule) {
+ rulesLoader.addRule(rule);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder addRule(String key, String repoKey, String internalKey, String name) {
+ Rule.Builder builder = Rule.newBuilder();
+ builder.setKey(key);
+ builder.setRepository(repoKey);
+ if (internalKey != null) {
+ builder.setInternalKey(internalKey);
+ }
+ builder.setName(name);
+
+ rulesLoader.addRule(builder.build());
+ return this;
+ }
+
+ public BatchMediumTesterBuilder addRules(RulesDefinition rulesDefinition) {
+ RulesDefinition.Context context = new RulesDefinition.Context();
+ rulesDefinition.define(context);
+ List<Repository> repositories = context.repositories();
+ for (Repository repo : repositories) {
+ for (RulesDefinition.Rule rule : repo.rules()) {
+ this.addRule(rule.key(), rule.repository().key(), rule.internalKey(), rule.name());
+ }
+ }
+ return this;
+ }
+
+ public BatchMediumTesterBuilder addDefaultQProfile(String language, String name) {
+ addQProfile(language, name);
+ globalRefProvider.globalSettings().put("sonar.profile." + language, name);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder setPreviousAnalysisDate(Date previousAnalysis) {
+ projectRefProvider.setLastAnalysisDate(previousAnalysis);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder bootstrapProperties(Map<String, String> props) {
+ bootstrapProperties.putAll(props);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder activateRule(LoadedActiveRule activeRule) {
+ activeRules.addActiveRule(activeRule);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder addActiveRule(String repositoryKey, String ruleKey, @Nullable String templateRuleKey, String name, @Nullable String severity,
+ @Nullable String internalKey, @Nullable String languag) {
+ LoadedActiveRule r = new LoadedActiveRule();
+
+ r.setInternalKey(internalKey);
+ r.setRuleKey(RuleKey.of(repositoryKey, ruleKey));
+ r.setName(name);
+ r.setTemplateRuleKey(templateRuleKey);
+ r.setLanguage(languag);
+ r.setSeverity(severity);
+
+ activeRules.addActiveRule(r);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder addFileData(String moduleKey, String path, FileData fileData) {
+ projectRefProvider.addFileData(moduleKey, path, fileData);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder setLastBuildDate(Date d) {
+ projectRefProvider.setLastAnalysisDate(d);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder mockServerIssue(ServerIssue issue) {
+ serverIssues.getServerIssues().add(issue);
+ return this;
+ }
+
+ public BatchMediumTesterBuilder mockLineHashes(String fileKey, String[] lineHashes) {
+ serverLineHashes.byKey.put(fileKey, lineHashes);
+ return this;
+ }
+
+ }
+
+ public void start() {
+ batch.start();
+ }
+
+ public void stop() {
+ batch.stop();
+ try {
+ destroyWorkingDirs();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void syncProject(String projectKey) {
+ batch.syncProject(projectKey);
+ }
+
+ private BatchMediumTester(BatchMediumTesterBuilder builder) {
+ Batch.Builder batchBuilder = Batch.builder()
+ .setEnableLoggingConfiguration(true)
+ .addComponents(
+ new EnvironmentInformation("mediumTest", "1.0"),
+ builder.pluginInstaller,
+ builder.globalRefProvider,
+ builder.qualityProfiles,
+ builder.rulesLoader,
+ builder.projectRefProvider,
+ builder.activeRules,
+ new DefaultDebtModel())
+ .setBootstrapProperties(builder.bootstrapProperties)
+ .setLogOutput(builder.logOutput);
+
+ if (builder.associated) {
+ batchBuilder.addComponents(
+ builder.serverIssues);
+ }
+ batch = batchBuilder.build();
+ }
+
+ public TaskBuilder newTask() {
+ return new TaskBuilder(this);
+ }
+
+ public TaskBuilder newScanTask(File sonarProps) {
+ Properties prop = new Properties();
+ try (Reader reader = new InputStreamReader(new FileInputStream(sonarProps), StandardCharsets.UTF_8)) {
+ prop.load(reader);
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to read configuration file", e);
+ }
+ TaskBuilder builder = new TaskBuilder(this);
+ builder.property("sonar.projectBaseDir", sonarProps.getParentFile().getAbsolutePath());
+ for (Map.Entry<Object, Object> entry : prop.entrySet()) {
+ builder.property(entry.getKey().toString(), entry.getValue().toString());
+ }
+ return builder;
+ }
+
+ public static class TaskBuilder {
+ private final Map<String, String> taskProperties = new HashMap<>();
+ private BatchMediumTester tester;
+ private IssueListener issueListener = null;
+
+ public TaskBuilder(BatchMediumTester tester) {
+ this.tester = tester;
+ }
+
+ public TaskResult start() {
+ TaskResult result = new TaskResult();
+ Map<String, String> props = new HashMap<>();
+ props.putAll(taskProperties);
+ if (issueListener != null) {
+ tester.batch.executeTask(props, result, issueListener);
+ } else {
+ tester.batch.executeTask(props, result);
+ }
+ return result;
+ }
+
+ public TaskBuilder properties(Map<String, String> props) {
+ taskProperties.putAll(props);
+ return this;
+ }
+
+ public TaskBuilder property(String key, String value) {
+ taskProperties.put(key, value);
+ return this;
+ }
+
+ public TaskBuilder setIssueListener(IssueListener issueListener) {
+ this.issueListener = issueListener;
+ return this;
+ }
+ }
+
+ private static class FakeRulesLoader implements RulesLoader {
+ private List<org.sonarqube.ws.Rules.ListResponse.Rule> rules = new LinkedList<>();
+
+ public FakeRulesLoader addRule(Rule rule) {
+ rules.add(rule);
+ return this;
+ }
+
+ @Override
+ public List<Rule> load(@Nullable MutableBoolean fromCache) {
+ return rules;
+ }
+ }
+
+ private static class FakeActiveRulesLoader implements ActiveRulesLoader {
+ private List<LoadedActiveRule> activeRules = new LinkedList<>();
+
+ public void addActiveRule(LoadedActiveRule activeRule) {
+ this.activeRules.add(activeRule);
+ }
+
+ @Override
+ public List<LoadedActiveRule> load(String qualityProfileKey, MutableBoolean fromCache) {
+ return activeRules;
+ }
+ }
+
+ private static class FakeGlobalRepositoriesLoader implements GlobalRepositoriesLoader {
+
+ private int metricId = 1;
+
+ private GlobalRepositories ref = new GlobalRepositories();
+
+ @Override
+ public GlobalRepositories load(@Nullable MutableBoolean fromCache) {
+ return ref;
+ }
+
+ public Map<String, String> globalSettings() {
+ return ref.globalSettings();
+ }
+
+ public FakeGlobalRepositoriesLoader add(Metric<?> metric) {
+ Boolean optimizedBestValue = metric.isOptimizedBestValue();
+ ref.metrics().add(new org.sonar.scanner.protocol.input.Metric(metricId,
+ metric.key(),
+ metric.getType().name(),
+ metric.getDescription(),
+ metric.getDirection(),
+ metric.getName(),
+ metric.getQualitative(),
+ metric.getUserManaged(),
+ metric.getWorstValue(),
+ metric.getBestValue(),
+ optimizedBestValue != null ? optimizedBestValue : false));
+ metricId++;
+ return this;
+ }
+
+ }
+
+ private static class FakeProjectRepositoriesLoader implements ProjectRepositoriesLoader {
+
+ private Table<String, String, FileData> fileDataTable = HashBasedTable.create();
+ private Date lastAnalysisDate;
+
+ @Override
+ public ProjectRepositories load(String projectKey, boolean isIssuesMode, @Nullable MutableBoolean fromCache) {
+ Table<String, String, String> settings = HashBasedTable.create();
+ return new ProjectRepositories(settings, fileDataTable, lastAnalysisDate);
+ }
+
+ public FakeProjectRepositoriesLoader addFileData(String moduleKey, String path, FileData fileData) {
+ fileDataTable.put(moduleKey, path, fileData);
+ return this;
+ }
+
+ public FakeProjectRepositoriesLoader setLastAnalysisDate(Date d) {
+ lastAnalysisDate = d;
+ return this;
+ }
+
+ }
+
+ private static class FakeQualityProfileLoader implements QualityProfileLoader {
+
+ private List<QualityProfile> qualityProfiles = new LinkedList<>();
+
+ public void add(String language, String name) {
+ qualityProfiles.add(QualityProfile.newBuilder()
+ .setLanguage(language)
+ .setKey(name)
+ .setName(name)
+ .setRulesUpdatedAt(DateUtils.formatDateTime(new Date(1234567891212L)))
+ .build());
+ }
+
+ @Override
+ public List<QualityProfile> load(String projectKey, String profileName, MutableBoolean fromCache) {
+ return qualityProfiles;
+ }
+
+ @Override
+ public List<QualityProfile> loadDefault(String profileName, MutableBoolean fromCache) {
+ return qualityProfiles;
+ }
+ }
+
+ private static class FakeServerIssuesLoader implements ServerIssuesLoader {
+
+ private List<ServerIssue> serverIssues = new ArrayList<>();
+
+ public List<ServerIssue> getServerIssues() {
+ return serverIssues;
+ }
+
+ @Override
+ public boolean load(String componentKey, Function<ServerIssue, Void> consumer) {
+ for (ServerIssue serverIssue : serverIssues) {
+ consumer.apply(serverIssue);
+ }
+ return true;
+ }
+ }
+
+ private static class FakeServerLineHashesLoader implements ServerLineHashesLoader {
+ private Map<String, String[]> byKey = new HashMap<>();
+
+ @Override
+ public String[] getLineHashes(String fileKey, @Nullable MutableBoolean fromCache) {
+ if (byKey.containsKey(fileKey)) {
+ return byKey.get(fileKey);
+ } else {
+ throw new IllegalStateException("You forgot to mock line hashes for " + fileKey);
+ }
+ }
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/LogOutputRecorder.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/LogOutputRecorder.java
new file mode 100644
index 00000000000..edae5e47fb1
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/LogOutputRecorder.java
@@ -0,0 +1,54 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.google.common.collect.Multimap;
+import com.google.common.collect.HashMultimap;
+import org.sonar.batch.bootstrapper.LogOutput;
+
+public class LogOutputRecorder implements LogOutput {
+ private Multimap<String, String> recordedByLevel = HashMultimap.create();
+ private List<String> recorded = new LinkedList<>();
+ private StringBuffer asString = new StringBuffer();
+
+ @Override
+ public void log(String formattedMessage, Level level) {
+ recordedByLevel.put(level.toString(), formattedMessage);
+ recorded.add(formattedMessage);
+ asString.append(formattedMessage).append("\n");
+ }
+
+ public Collection<String> getAll() {
+ return recorded;
+ }
+
+ public Collection<String> get(String level) {
+ return recordedByLevel.get(level);
+ }
+
+ public String getAsString() {
+ return asString.toString();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java
new file mode 100644
index 00000000000..4ebf5eb1fa6
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java
@@ -0,0 +1,155 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.cache;
+
+import org.junit.rules.TemporaryFolder;
+
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.batch.mediumtest.BatchMediumTester.TaskBuilder;
+import org.sonar.batch.mediumtest.LogOutputRecorder;
+import org.sonar.batch.repository.FileData;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Date;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.CoreProperties;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+public class CacheSyncTest {
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private BatchMediumTester tester;
+
+ @After
+ public void stop() {
+ if (tester != null) {
+ tester.stop();
+ tester = null;
+ }
+ }
+
+ @Test
+ public void testExecuteTask() {
+ LogOutputRecorder logOutput = new LogOutputRecorder();
+
+ tester = BatchMediumTester.builder()
+ .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES,
+ "sonar.verbose", "true"))
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addQProfile("lang", "name")
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo")
+ .setPreviousAnalysisDate(new Date())
+ .addFileData("test-project", "file1", new FileData("hash", "123456789"))
+ .setLogOutput(logOutput)
+ .build();
+
+ tester.start();
+ executeTask(tester.newTask());
+ assertThat(logOutput.getAsString()).contains("Cache for project [key] not found, synchronizing");
+ }
+
+ @Test
+ public void testSyncFirstTime() {
+ LogOutputRecorder logOutput = new LogOutputRecorder();
+
+ tester = BatchMediumTester.builder()
+ .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES,
+ "sonar.verbose", "true"))
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addQProfile("lang", "name")
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo")
+ .setPreviousAnalysisDate(new Date())
+ .addFileData("test-project", "file1", new FileData("hash", "123456789"))
+ .setLogOutput(logOutput)
+ .build();
+
+ tester.start();
+ tester.syncProject("test-project");
+ assertThat(logOutput.getAsString()).contains("Cache for project [test-project] not found");
+ }
+
+ @Test
+ public void testSyncTwice() {
+ LogOutputRecorder logOutput = new LogOutputRecorder();
+
+ tester = BatchMediumTester.builder()
+ .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES,
+ "sonar.verbose", "true"))
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addQProfile("lang", "name")
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo")
+ .setPreviousAnalysisDate(new Date())
+ .addFileData("test-project", "file1", new FileData("hash", "123456789"))
+ .setLogOutput(logOutput)
+ .build();
+
+ tester.start();
+ tester.syncProject("test-project");
+ tester.syncProject("test-project");
+ assertThat(logOutput.getAsString()).contains("-- Found project [test-project]");
+ assertThat(logOutput.getAsString()).contains("not found, synchronizing data");
+ assertThat(logOutput.getAsString()).contains("], synchronizing data (forced)..");
+ }
+
+ @Test
+ public void testNonAssociated() {
+ LogOutputRecorder logOutput = new LogOutputRecorder();
+
+ tester = BatchMediumTester.builder()
+ .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES))
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addQProfile("lang", "name")
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo")
+ .setPreviousAnalysisDate(new Date())
+ .addFileData("test-project", "file1", new FileData("hash", "123456789"))
+ .setLogOutput(logOutput)
+ .build();
+
+ tester.start();
+ tester.syncProject(null);
+
+ assertThat(logOutput.getAsString()).contains("Cache not found, synchronizing data");
+ }
+
+ private TaskResult executeTask(TaskBuilder builder) {
+ builder.property("sonar.projectKey", "key");
+ builder.property("sonar.projectVersion", "1.0");
+ builder.property("sonar.projectName", "key");
+ builder.property("sonar.projectBaseDir", temp.getRoot().getAbsolutePath());
+ builder.property("sonar.sources", temp.getRoot().getAbsolutePath());
+ return builder.start();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java
new file mode 100644
index 00000000000..e3dac6ed869
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/coverage/CoverageMediumTest.java
@@ -0,0 +1,180 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.coverage;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.xoo.XooPlugin;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class CoverageMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void unitTests() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xooUtCoverageFile = new File(srcDir, "sample.xoo.coverage");
+ FileUtils.write(xooFile, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}");
+ FileUtils.write(xooUtCoverageFile, "2:2:2:1\n3:1");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ InputFile file = result.inputFile("src/sample.xoo");
+ assertThat(result.coverageFor(file, 2).getUtHits()).isTrue();
+ assertThat(result.coverageFor(file, 2).getItHits()).isFalse();
+ assertThat(result.coverageFor(file, 2).getConditions()).isEqualTo(2);
+ assertThat(result.coverageFor(file, 2).getUtCoveredConditions()).isEqualTo(1);
+ assertThat(result.coverageFor(file, 2).getItCoveredConditions()).isEqualTo(0);
+ assertThat(result.coverageFor(file, 2).getOverallCoveredConditions()).isEqualTo(0);
+
+ Map<String, List<org.sonar.scanner.protocol.output.ScannerReport.Measure>> allMeasures = result.allMeasures();
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue")
+ .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 2),
+ tuple(CoreMetrics.UNCOVERED_LINES_KEY, 0),
+ tuple(CoreMetrics.CONDITIONS_TO_COVER_KEY, 2),
+ tuple(CoreMetrics.UNCOVERED_CONDITIONS_KEY, 1));
+ }
+
+ @Test
+ public void exclusions() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xooUtCoverageFile = new File(srcDir, "sample.xoo.coverage");
+ FileUtils.write(xooFile, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}");
+ FileUtils.write(xooUtCoverageFile, "2:2:2:1\n3:1");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.coverage.exclusions", "**/sample.xoo")
+ .build())
+ .start();
+
+ InputFile file = result.inputFile("src/sample.xoo");
+ assertThat(result.coverageFor(file, 2)).isNull();
+
+ Map<String, List<org.sonar.scanner.protocol.output.ScannerReport.Measure>> allMeasures = result.allMeasures();
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey")
+ .doesNotContain(CoreMetrics.LINES_TO_COVER_KEY, CoreMetrics.UNCOVERED_LINES_KEY, CoreMetrics.CONDITIONS_TO_COVER_KEY,
+ CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY);
+ }
+
+ @Test
+ public void fallbackOnExecutableLines() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File measuresFile = new File(srcDir, "sample.xoo.measures");
+ FileUtils.write(xooFile, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}");
+ FileUtils.write(measuresFile, "executable_lines_data:2=1;3=1;4=0");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ InputFile file = result.inputFile("src/sample.xoo");
+ assertThat(result.coverageFor(file, 1)).isNull();
+
+ assertThat(result.coverageFor(file, 2).getUtHits()).isFalse();
+ assertThat(result.coverageFor(file, 2).getItHits()).isFalse();
+ assertThat(result.coverageFor(file, 2).getConditions()).isEqualTo(0);
+ assertThat(result.coverageFor(file, 2).getUtCoveredConditions()).isEqualTo(0);
+ assertThat(result.coverageFor(file, 2).getItCoveredConditions()).isEqualTo(0);
+ assertThat(result.coverageFor(file, 2).getOverallCoveredConditions()).isEqualTo(0);
+
+ assertThat(result.coverageFor(file, 3).getUtHits()).isFalse();
+ assertThat(result.coverageFor(file, 4)).isNull();
+
+ Map<String, List<org.sonar.scanner.protocol.output.ScannerReport.Measure>> allMeasures = result.allMeasures();
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue")
+ .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 2),
+ tuple(CoreMetrics.UNCOVERED_LINES_KEY, 2));
+
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey").doesNotContain(CoreMetrics.CONDITIONS_TO_COVER_KEY, CoreMetrics.UNCOVERED_CONDITIONS_KEY);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
new file mode 100644
index 00000000000..662ed7dbcfd
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
@@ -0,0 +1,336 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.cpd;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.io.FileUtils;
+import org.assertj.core.groups.Tuple;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.Measure;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.lang.CpdTokenizerSensor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(Parameterized.class)
+public class CpdMediumTest {
+
+ @Parameters(name = "new api: {0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {
+ {true}, {false}
+ });
+ }
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .build();
+
+ private File baseDir;
+
+ private ImmutableMap.Builder<String, String> builder;
+
+ private boolean useNewSensorApi;
+
+ public CpdMediumTest(boolean useNewSensorApi) {
+ this.useNewSensorApi = useNewSensorApi;
+ }
+
+ @Before
+ public void prepare() throws IOException {
+ tester.start();
+
+ baseDir = temp.getRoot();
+
+ builder = ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project");
+ if (useNewSensorApi) {
+ builder.put(CpdTokenizerSensor.ENABLE_PROP, "true");
+ }
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testCrossModuleDuplications() throws IOException {
+ builder.put("sonar.modules", "module1,module2")
+ .put("sonar.cpd.xoo.minimumTokens", "10")
+ .put("sonar.verbose", "true");
+
+ // module 1
+ builder.put("module1.sonar.projectKey", "module1");
+ builder.put("module1.sonar.projectName", "Module 1");
+ builder.put("module1.sonar.sources", ".");
+
+ // module2
+ builder.put("module2.sonar.projectKey", "module2");
+ builder.put("module2.sonar.projectName", "Module 2");
+ builder.put("module2.sonar.sources", ".");
+
+ File module1Dir = new File(baseDir, "module1");
+ File module2Dir = new File(baseDir, "module2");
+
+ module1Dir.mkdir();
+ module2Dir.mkdir();
+
+ String duplicatedStuff = "Sample xoo\ncontent\n"
+ + "foo\nbar\ntoto\ntiti\n"
+ + "foo\nbar\ntoto\ntiti\n"
+ + "bar\ntoto\ntiti\n"
+ + "foo\nbar\ntoto\ntiti";
+
+ // create duplicated file in both modules
+ File xooFile1 = new File(module1Dir, "sample1.xoo");
+ FileUtils.write(xooFile1, duplicatedStuff);
+
+ File xooFile2 = new File(module2Dir, "sample2.xoo");
+ FileUtils.write(xooFile2, duplicatedStuff);
+
+ TaskResult result = tester.newTask().properties(builder.build()).start();
+
+ assertThat(result.inputFiles()).hasSize(2);
+
+ InputFile inputFile1 = result.inputFile("sample1.xoo");
+ InputFile inputFile2 = result.inputFile("sample2.xoo");
+
+ // One clone group on each file
+ List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1);
+ assertThat(duplicationGroupsFile1).hasSize(1);
+
+ org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile1 = duplicationGroupsFile1.get(0);
+ assertThat(cloneGroupFile1.getOriginPosition().getStartLine()).isEqualTo(1);
+ assertThat(cloneGroupFile1.getOriginPosition().getEndLine()).isEqualTo(17);
+ assertThat(cloneGroupFile1.getDuplicateList()).hasSize(1);
+ assertThat(cloneGroupFile1.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile2).key()).getRef());
+
+ List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2);
+ assertThat(duplicationGroupsFile2).hasSize(1);
+
+ org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile2 = duplicationGroupsFile2.get(0);
+ assertThat(cloneGroupFile2.getOriginPosition().getStartLine()).isEqualTo(1);
+ assertThat(cloneGroupFile2.getOriginPosition().getEndLine()).isEqualTo(17);
+ assertThat(cloneGroupFile2.getDuplicateList()).hasSize(1);
+ assertThat(cloneGroupFile2.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile1).key()).getRef());
+
+ assertThat(result.duplicationBlocksFor(inputFile1)).isEmpty();
+ }
+
+ @Test
+ public void testCrossFileDuplications() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ String duplicatedStuff = "Sample xoo\ncontent\n"
+ + "foo\nbar\ntoto\ntiti\n"
+ + "foo\nbar\ntoto\ntiti\n"
+ + "bar\ntoto\ntiti\n"
+ + "foo\nbar\ntoto\ntiti";
+
+ File xooFile1 = new File(srcDir, "sample1.xoo");
+ FileUtils.write(xooFile1, duplicatedStuff);
+
+ File xooFile2 = new File(srcDir, "sample2.xoo");
+ FileUtils.write(xooFile2, duplicatedStuff);
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .put("sonar.cpd.xoo.minimumTokens", "10")
+ .put("sonar.verbose", "true")
+ .build())
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(2);
+
+ InputFile inputFile1 = result.inputFile("src/sample1.xoo");
+ InputFile inputFile2 = result.inputFile("src/sample2.xoo");
+
+ // One clone group on each file
+ List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1);
+ assertThat(duplicationGroupsFile1).hasSize(1);
+
+ org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile1 = duplicationGroupsFile1.get(0);
+ assertThat(cloneGroupFile1.getOriginPosition().getStartLine()).isEqualTo(1);
+ assertThat(cloneGroupFile1.getOriginPosition().getEndLine()).isEqualTo(17);
+ assertThat(cloneGroupFile1.getDuplicateList()).hasSize(1);
+ assertThat(cloneGroupFile1.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile2).key()).getRef());
+
+ List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2);
+ assertThat(duplicationGroupsFile2).hasSize(1);
+
+ org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroupFile2 = duplicationGroupsFile2.get(0);
+ assertThat(cloneGroupFile2.getOriginPosition().getStartLine()).isEqualTo(1);
+ assertThat(cloneGroupFile2.getOriginPosition().getEndLine()).isEqualTo(17);
+ assertThat(cloneGroupFile2.getDuplicateList()).hasSize(1);
+ assertThat(cloneGroupFile2.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile1).key()).getRef());
+
+ assertThat(result.duplicationBlocksFor(inputFile1)).isEmpty();
+ }
+
+ @Test
+ public void enableCrossProjectDuplication() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ String duplicatedStuff = "Sample xoo\ncontent\nfoo\nbar\ntoto\ntiti\nfoo";
+
+ File xooFile1 = new File(srcDir, "sample1.xoo");
+ FileUtils.write(xooFile1, duplicatedStuff);
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .put("sonar.cpd.xoo.minimumTokens", "1")
+ .put("sonar.cpd.xoo.minimumLines", "5")
+ .put("sonar.verbose", "true")
+ .put("sonar.cpd.cross_project", "true")
+ .build())
+ .start();
+
+ InputFile inputFile1 = result.inputFile("src/sample1.xoo");
+
+ List<ScannerReport.CpdTextBlock> duplicationBlocks = result.duplicationBlocksFor(inputFile1);
+ assertThat(duplicationBlocks).hasSize(3);
+ assertThat(duplicationBlocks.get(0).getStartLine()).isEqualTo(1);
+ assertThat(duplicationBlocks.get(0).getEndLine()).isEqualTo(5);
+ assertThat(duplicationBlocks.get(0).getStartTokenIndex()).isEqualTo(1);
+ assertThat(duplicationBlocks.get(0).getEndTokenIndex()).isEqualTo(6);
+ assertThat(duplicationBlocks.get(0).getHash()).isNotEmpty();
+
+ assertThat(duplicationBlocks.get(1).getStartLine()).isEqualTo(2);
+ assertThat(duplicationBlocks.get(1).getEndLine()).isEqualTo(6);
+ assertThat(duplicationBlocks.get(1).getStartTokenIndex()).isEqualTo(3);
+ assertThat(duplicationBlocks.get(1).getEndTokenIndex()).isEqualTo(7);
+ assertThat(duplicationBlocks.get(0).getHash()).isNotEmpty();
+
+ assertThat(duplicationBlocks.get(2).getStartLine()).isEqualTo(3);
+ assertThat(duplicationBlocks.get(2).getEndLine()).isEqualTo(7);
+ assertThat(duplicationBlocks.get(2).getStartTokenIndex()).isEqualTo(4);
+ assertThat(duplicationBlocks.get(2).getEndTokenIndex()).isEqualTo(8);
+ assertThat(duplicationBlocks.get(0).getHash()).isNotEmpty();
+ }
+
+ // SONAR-6000
+ @Test
+ public void truncateDuplication() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ String duplicatedStuff = "Sample xoo\n";
+
+ int blockCount = 10000;
+ File xooFile1 = new File(srcDir, "sample.xoo");
+ for (int i = 0; i < blockCount; i++) {
+ FileUtils.write(xooFile1, duplicatedStuff, true);
+ FileUtils.write(xooFile1, "" + i + "\n", true);
+ }
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .put("sonar.cpd.xoo.minimumTokens", "1")
+ .put("sonar.cpd.xoo.minimumLines", "1")
+ .build())
+ .start();
+
+ Map<String, List<Measure>> allMeasures = result.allMeasures();
+
+ assertThat(allMeasures.get("com.foo.project")).extracting("metricKey", "intValue", "doubleValue", "stringValue").containsOnly(
+ Tuple.tuple(CoreMetrics.QUALITY_PROFILES_KEY, 0, 0.0,
+ "[{\"key\":\"Sonar Way\",\"language\":\"xoo\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2009-02-13T23:31:31+0000\"}]"));
+
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue").containsOnly(
+ Tuple.tuple(CoreMetrics.LINES_KEY, blockCount * 2 + 1));
+
+ List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroups = result.duplicationsFor(result.inputFile("src/sample.xoo"));
+ assertThat(duplicationGroups).hasSize(1);
+
+ org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroup = duplicationGroups.get(0);
+ assertThat(cloneGroup.getDuplicateList()).hasSize(100);
+ }
+
+ @Test
+ public void testIntraFileDuplications() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ String content = "Sample xoo\ncontent\nfoo\nbar\nSample xoo\ncontent\n";
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, content);
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .put("sonar.cpd.xoo.minimumTokens", "2")
+ .put("sonar.cpd.xoo.minimumLines", "2")
+ .put("sonar.verbose", "true")
+ .build())
+ .start();
+
+ InputFile inputFile = result.inputFile("src/sample.xoo");
+ // One clone group
+ List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroups = result.duplicationsFor(inputFile);
+ assertThat(duplicationGroups).hasSize(1);
+
+ org.sonar.scanner.protocol.output.ScannerReport.Duplication cloneGroup = duplicationGroups.get(0);
+ assertThat(cloneGroup.getOriginPosition().getStartLine()).isEqualTo(1);
+ assertThat(cloneGroup.getOriginPosition().getEndLine()).isEqualTo(2);
+ assertThat(cloneGroup.getDuplicateList()).hasSize(1);
+ assertThat(cloneGroup.getDuplicate(0).getRange().getStartLine()).isEqualTo(5);
+ assertThat(cloneGroup.getDuplicate(0).getRange().getEndLine()).isEqualTo(6);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java
new file mode 100644
index 00000000000..fa1ff38833d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java
@@ -0,0 +1,134 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.deprecated;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.groups.Tuple.tuple;
+
+public class DeprecatedApiMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addActiveRule("xoo", "DeprecatedResourceApi", null, "One issue per line", "MAJOR", null, "xoo")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testIssueDetails() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFileInRootDir = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFileInRootDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
+
+ File xooFileInAnotherDir = new File(srcDir, "package/sample.xoo");
+ FileUtils.write(xooFileInAnotherDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ assertThat(result.issuesFor(result.inputFile("src/sample.xoo"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0),
+ tuple("Issue created using deprecated API", 1));
+ assertThat(result.issuesFor(result.inputFile("src/package/sample.xoo"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0),
+ tuple("Issue created using deprecated API", 1));
+ assertThat(result.issuesFor(result.inputDir("src"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0));
+ assertThat(result.issuesFor(result.inputDir("src/package"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0));
+
+ }
+
+ @Test
+ public void createIssueOnRootDir() throws IOException {
+
+ File baseDir = temp.getRoot();
+
+ File xooFileInRootDir = new File(baseDir, "sample.xoo");
+ FileUtils.write(xooFileInRootDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
+
+ File xooFileInAnotherDir = new File(baseDir, "package/sample.xoo");
+ FileUtils.write(xooFileInAnotherDir, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", ".")
+ .build())
+ .start();
+
+ assertThat(result.issuesFor(result.inputFile("sample.xoo"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0),
+ tuple("Issue created using deprecated API", 1));
+ assertThat(result.issuesFor(result.inputFile("package/sample.xoo"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0),
+ tuple("Issue created using deprecated API", 1));
+ assertThat(result.issuesFor(result.inputDir(""))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0));
+ assertThat(result.issuesFor(result.inputDir("package"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0));
+
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java
new file mode 100644
index 00000000000..dcb32c1030e
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java
@@ -0,0 +1,296 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.fs;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.System2;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.xoo.XooPlugin;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FileSystemMediumTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .build();
+
+ private File baseDir;
+
+ private ImmutableMap.Builder<String, String> builder;
+
+ @Before
+ public void prepare() throws IOException {
+ tester.start();
+
+ baseDir = temp.getRoot();
+
+ builder = ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project");
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void scanProjectWithSourceDir() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(1);
+ assertThat(result.inputDirs()).hasSize(1);
+ assertThat(result.inputFile("src/sample.xoo").type()).isEqualTo(InputFile.Type.MAIN);
+ assertThat(result.inputFile("src/sample.xoo").relativePath()).isEqualTo("src/sample.xoo");
+ assertThat(result.inputDir("src").relativePath()).isEqualTo("src");
+ }
+
+ @Test
+ public void scanBigProject() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ int nbFiles = 100;
+ int ruleCount = 100000;
+ for (int nb = 1; nb <= nbFiles; nb++) {
+ File xooFile = new File(srcDir, "sample" + nb + ".xoo");
+ FileUtils.write(xooFile, StringUtils.repeat(StringUtils.repeat("a", 100) + "\n", ruleCount / 1000));
+ }
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(100);
+ assertThat(result.inputDirs()).hasSize(1);
+ }
+
+ @Test
+ public void scanProjectWithTestDir() throws IOException {
+ File test = new File(baseDir, "test");
+ test.mkdir();
+
+ File xooFile = new File(test, "sampleTest.xoo");
+ FileUtils.write(xooFile, "Sample test xoo\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "")
+ .put("sonar.tests", "test")
+ .build())
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(1);
+ assertThat(result.inputFile("test/sampleTest.xoo").type()).isEqualTo(InputFile.Type.TEST);
+ }
+
+ /**
+ * SONAR-5419
+ */
+ @Test
+ public void scanProjectWithMixedSourcesAndTests() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+
+ File xooFile2 = new File(baseDir, "another.xoo");
+ FileUtils.write(xooFile2, "Sample xoo 2\ncontent");
+
+ File testDir = new File(baseDir, "test");
+ testDir.mkdir();
+
+ File xooTestFile = new File(baseDir, "sampleTest2.xoo");
+ FileUtils.write(xooTestFile, "Sample test xoo\ncontent");
+
+ File xooTestFile2 = new File(testDir, "sampleTest.xoo");
+ FileUtils.write(xooTestFile2, "Sample test xoo 2\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src,another.xoo")
+ .put("sonar.tests", "test,sampleTest2.xoo")
+ .build())
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(4);
+ }
+
+ @Test
+ public void fileInclusionsExclusions() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+
+ File xooFile2 = new File(baseDir, "another.xoo");
+ FileUtils.write(xooFile2, "Sample xoo 2\ncontent");
+
+ File testDir = new File(baseDir, "test");
+ testDir.mkdir();
+
+ File xooTestFile = new File(baseDir, "sampleTest2.xoo");
+ FileUtils.write(xooTestFile, "Sample test xoo\ncontent");
+
+ File xooTestFile2 = new File(testDir, "sampleTest.xoo");
+ FileUtils.write(xooTestFile2, "Sample test xoo 2\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src,another.xoo")
+ .put("sonar.tests", "test,sampleTest2.xoo")
+ .put("sonar.inclusions", "src/**")
+ .put("sonar.exclusions", "**/another.*")
+ .put("sonar.test.inclusions", "**/sampleTest*.*")
+ .put("sonar.test.exclusions", "**/sampleTest2.xoo")
+ .build())
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(2);
+ }
+
+ @Test
+ public void failForDuplicateInputFile() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("can't be indexed twice. Please check that inclusion/exclusion patterns produce disjoint sets for main and test files");
+ tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src,src/sample.xoo")
+ .build())
+ .start();
+
+ }
+
+ // SONAR-5330
+ @Test
+ public void scanProjectWithSourceSymlink() {
+ if (!System2.INSTANCE.isOsWindows()) {
+ File projectDir = new File("src/test/resources/mediumtest/xoo/sample-with-symlink");
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(3);
+ // check that symlink was not resolved to target
+ assertThat(result.inputFiles()).extractingResultOf("path").toString().startsWith(projectDir.toString());
+ }
+ }
+
+ // SONAR-6719
+ @Test
+ public void scanProjectWithWrongCase() {
+ if (System2.INSTANCE.isOsWindows()) {
+ File projectDir = new File("src/test/resources/mediumtest/xoo/sample");
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .property("sonar.sources", "XOURCES")
+ .property("sonar.tests", "TESTX")
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(3);
+ assertThat(result.inputFiles()).extractingResultOf("relativePath").containsOnly(
+ "xources/hello/HelloJava.xoo",
+ "xources/hello/helloscala.xoo",
+ "testx/ClassOneTest.xoo");
+ }
+ }
+
+ @Test
+ public void indexAnyFile() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+
+ File otherFile = new File(srcDir, "sample.other");
+ FileUtils.write(otherFile, "Sample other\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .put("sonar.import_unknown_files", "true")
+ .build())
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(2);
+ assertThat(result.inputFile("src/sample.other").type()).isEqualTo(InputFile.Type.MAIN);
+ assertThat(result.inputFile("src/sample.other").relativePath()).isEqualTo("src/sample.other");
+ assertThat(result.inputFile("src/sample.other").language()).isNull();
+ }
+
+ @Test
+ public void scanMultiModuleProject() {
+ File projectDir = new File("src/test/resources/mediumtest/xoo/multi-modules-sample");
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(4);
+ assertThat(result.inputDirs()).hasSize(4);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/NoLanguagesPluginsMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/NoLanguagesPluginsMediumTest.java
new file mode 100644
index 00000000000..90502c54ae8
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/NoLanguagesPluginsMediumTest.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.fs;
+
+import org.junit.rules.ExpectedException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.sonar.batch.mediumtest.issuesmode.IssueModeAndReportsMediumTest;
+
+import java.io.File;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+
+public class NoLanguagesPluginsMediumTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .setPreviousAnalysisDate(null)
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testNoLanguagePluginsInstalled() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample");
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("No language plugins are installed");
+
+ tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .start();
+ }
+
+ private File copyProject(String path) throws Exception {
+ File projectDir = temp.newFolder();
+ File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI());
+ FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar")));
+ return projectDir;
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java
new file mode 100644
index 00000000000..21526ddcfd5
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java
@@ -0,0 +1,168 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.fs;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.scanner.protocol.output.ScannerReport.Issue;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectBuilderMediumTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .setPreviousAnalysisDate(new Date())
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testProjectBuilder() throws IOException {
+ File baseDir = prepareProject();
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", ".")
+ .put("sonar.xoo.enableProjectBuilder", "true")
+ .build())
+ .start();
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+ assertThat(issues).hasSize(10);
+
+ boolean foundIssueAtLine1 = false;
+ for (Issue issue : issues) {
+ if (issue.getLine() == 1) {
+ foundIssueAtLine1 = true;
+ assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line");
+ assertThat(issue.hasGap()).isFalse();
+ }
+ }
+ assertThat(foundIssueAtLine1).isTrue();
+
+ }
+
+ @Test
+ // SONAR-6976
+ public void testProjectBuilderWithNewLine() throws IOException {
+ File baseDir = prepareProject();
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("is not a valid branch name");
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.branch", "branch\n")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", ".")
+ .put("sonar.xoo.enableProjectBuilder", "true")
+ .build())
+ .start();
+ }
+
+ @Test
+ public void testProjectBuilderWithBranch() throws IOException {
+ File baseDir = prepareProject();
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.branch", "my-branch")
+ .put("sonar.sources", ".")
+ .put("sonar.xoo.enableProjectBuilder", "true")
+ .build())
+ .start();
+
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+ assertThat(issues).hasSize(10);
+
+ boolean foundIssueAtLine1 = false;
+ for (Issue issue : issues) {
+ if (issue.getLine() == 1) {
+ foundIssueAtLine1 = true;
+ assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line");
+ assertThat(issue.hasGap()).isFalse();
+ }
+ }
+ assertThat(foundIssueAtLine1).isTrue();
+ }
+
+ private File prepareProject() throws IOException {
+ File baseDir = temp.getRoot();
+ File module1Dir = new File(baseDir, "module1");
+ module1Dir.mkdir();
+
+ File srcDir = new File(module1Dir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
+
+ return baseDir;
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java
new file mode 100644
index 00000000000..3c83ed61fcf
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java
@@ -0,0 +1,133 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.highlighting;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.xoo.XooPlugin;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class HighlightingMediumTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void computeSyntaxHighlightingOnTempProject() throws IOException {
+
+ File baseDir = temp.newFolder();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting");
+ FileUtils.write(xooFile, "Sample xoo\ncontent plop");
+ FileUtils.write(xoohighlightingFile, "0:10:s\n11:18:k");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ InputFile file = result.inputFile("src/sample.xoo");
+ assertThat(result.highlightingTypeFor(file, 1, 0)).containsExactly(TypeOfText.STRING);
+ assertThat(result.highlightingTypeFor(file, 1, 9)).containsExactly(TypeOfText.STRING);
+ assertThat(result.highlightingTypeFor(file, 2, 0)).containsExactly(TypeOfText.KEYWORD);
+ assertThat(result.highlightingTypeFor(file, 2, 8)).isEmpty();
+ }
+
+ @Test
+ public void computeInvalidOffsets() throws IOException {
+
+ File baseDir = temp.newFolder();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting");
+ FileUtils.write(xooFile, "Sample xoo\ncontent plop");
+ FileUtils.write(xoohighlightingFile, "0:10:s\n18:18:k");
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Error processing line 2");
+ exception.expectCause(new TypeSafeMatcher<IllegalArgumentException>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Invalid cause");
+ }
+
+ @Override
+ protected boolean matchesSafely(IllegalArgumentException e) {
+ return e.getMessage().contains("Unable to highlight file");
+ }
+ });
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java
new file mode 100644
index 00000000000..804a66a7d4d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java
@@ -0,0 +1,127 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issues;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.batch.rule.LoadedActiveRule;
+import org.sonar.scanner.protocol.output.ScannerReport.Issue;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ChecksMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addRule("TemplateRule_1234", "xoo", "TemplateRule_1234", "A template rule")
+ .addRule("TemplateRule_1235", "xoo", "TemplateRule_1235", "Another template rule")
+ .activateRule(createActiveRuleWithParam("xoo", "TemplateRule_1234", "TemplateRule", "A template rule", "MAJOR", null, "xoo", "line", "1"))
+ .activateRule(createActiveRuleWithParam("xoo", "TemplateRule_1235", "TemplateRule", "Another template rule", "MAJOR", null, "xoo", "line", "2"))
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testCheckWithTemplate() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "foo\nbar");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+ assertThat(issues).hasSize(2);
+
+ boolean foundIssueAtLine1 = false;
+ boolean foundIssueAtLine2 = false;
+ for (Issue issue : issues) {
+ if (issue.getLine() == 1) {
+ foundIssueAtLine1 = true;
+ assertThat(issue.getMsg()).isEqualTo("A template rule");
+ }
+ if (issue.getLine() == 2) {
+ foundIssueAtLine2 = true;
+ assertThat(issue.getMsg()).isEqualTo("Another template rule");
+ }
+ }
+ assertThat(foundIssueAtLine1).isTrue();
+ assertThat(foundIssueAtLine2).isTrue();
+ }
+
+ private LoadedActiveRule createActiveRuleWithParam(String repositoryKey, String ruleKey, @Nullable String templateRuleKey, String name, @Nullable String severity,
+ @Nullable String internalKey, @Nullable String languag, String paramKey, String paramValue) {
+ LoadedActiveRule r = new LoadedActiveRule();
+
+ r.setInternalKey(internalKey);
+ r.setRuleKey(RuleKey.of(repositoryKey, ruleKey));
+ r.setName(name);
+ r.setTemplateRuleKey(templateRuleKey);
+ r.setLanguage(languag);
+ r.setSeverity(severity);
+
+ Map<String, String> params = new HashMap<>();
+ params.put(paramKey, paramValue);
+ r.setParams(params);
+ return r;
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesIssuesModeMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesIssuesModeMediumTest.java
new file mode 100644
index 00000000000..cae361869cb
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesIssuesModeMediumTest.java
@@ -0,0 +1,100 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issues;
+
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.bootstrapper.IssueListener;
+import org.junit.After;
+import org.junit.Before;
+import com.google.common.collect.ImmutableMap;
+import org.sonar.api.CoreProperties;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+import org.apache.commons.io.FileUtils;
+import org.junit.Test;
+import org.sonar.batch.mediumtest.TaskResult;
+
+import java.io.File;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssuesIssuesModeMediumTest {
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester testerPreview = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addRules(new XooRulesDefinition())
+ .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES))
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo")
+ .setLastBuildDate(new Date())
+ .build();
+
+ @Before
+ public void prepare() {
+ testerPreview.start();
+ }
+
+ @After
+ public void stop() {
+ testerPreview.stop();
+ }
+
+ @Test
+ public void testIssueCallback() throws Exception {
+ File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File tmpDir = temp.getRoot();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+ IssueRecorder issueListener = new IssueRecorder();
+
+ TaskResult result1 = testerPreview
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .setIssueListener(issueListener)
+ .property(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)
+ .start();
+
+ assertThat(result1.trackedIssues()).hasSize(14);
+ assertThat(issueListener.issueList).hasSize(14);
+ issueListener = new IssueRecorder();
+
+ TaskResult result2 = testerPreview
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .setIssueListener(issueListener)
+ .property(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)
+ .start();
+
+ assertThat(result2.trackedIssues()).hasSize(14);
+ assertThat(issueListener.issueList).hasSize(14);
+ }
+
+ private class IssueRecorder implements IssueListener {
+ List<Issue> issueList = new LinkedList<>();
+
+ @Override
+ public void handle(Issue issue) {
+ issueList.add(issue);
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
new file mode 100644
index 00000000000..50823ff4f48
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
@@ -0,0 +1,187 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issues;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.bootstrapper.IssueListener;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.scanner.protocol.output.ScannerReport.Issue;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssuesMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addRules(new XooRulesDefinition())
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testNoIssueCallbackInNonPreview() throws Exception {
+ File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File tmpDir = temp.getRoot();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+ IssueRecorder issueListener = new IssueRecorder();
+
+ TaskResult result = tester
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .setIssueListener(issueListener)
+ .start();
+
+ assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(8);
+ assertThat(issueListener.issueList).hasSize(0);
+ }
+
+ @Test
+ public void testOneIssuePerLine() throws Exception {
+ File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File tmpDir = temp.newFolder();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+
+ TaskResult result = tester
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .start();
+
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+ assertThat(issues).hasSize(8 /* lines */);
+
+ Issue issue = issues.get(0);
+ assertThat(issue.getTextRange().getStartLine()).isEqualTo(issue.getLine());
+ assertThat(issue.getTextRange().getEndLine()).isEqualTo(issue.getLine());
+ }
+
+ @Test
+ public void findActiveRuleByInternalKey() throws Exception {
+ File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File tmpDir = temp.newFolder();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+
+ TaskResult result = tester
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .property("sonar.xoo.internalKey", "OneIssuePerLine.internal")
+ .start();
+
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+ assertThat(issues).hasSize(8 /* lines */ + 1 /* file */);
+ }
+
+ @Test
+ public void testOverrideQProfileSeverity() throws Exception {
+ File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File tmpDir = temp.newFolder();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+
+ TaskResult result = tester
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .property("sonar.oneIssuePerLine.forceSeverity", "CRITICAL")
+ .start();
+
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+ assertThat(issues.get(0).getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.CRITICAL);
+ }
+
+ @Test
+ public void testIssueExclusion() throws Exception {
+ File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File tmpDir = temp.newFolder();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+
+ TaskResult result = tester
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .property("sonar.issue.ignore.allfile", "1")
+ .property("sonar.issue.ignore.allfile.1.fileRegexp", "object")
+ .start();
+
+ assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(8 /* lines */);
+ assertThat(result.issuesFor(result.inputFile("xources/hello/helloscala.xoo"))).isEmpty();
+ }
+
+ @Test
+ public void testIssueDetails() throws IOException {
+
+ File baseDir = temp.newFolder();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+ assertThat(issues).hasSize(10);
+
+ boolean foundIssueAtLine1 = false;
+ for (Issue issue : issues) {
+ if (issue.getLine() == 1) {
+ foundIssueAtLine1 = true;
+ assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line");
+ assertThat(issue.hasGap()).isFalse();
+ }
+ }
+ assertThat(foundIssueAtLine1).isTrue();
+ }
+
+ private class IssueRecorder implements IssueListener {
+ List<Issue> issueList = new LinkedList<>();
+
+ @Override
+ public void handle(Issue issue) {
+ issueList.add(issue);
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
new file mode 100644
index 00000000000..aae11ca0e39
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
@@ -0,0 +1,113 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issues;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssuesOnDirMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addRules(new XooRulesDefinition())
+ .addActiveRule("xoo", "OneIssueOnDirPerFile", null, "One issue per line", "MINOR", "xoo", "xoo")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void scanTempProject() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile1 = new File(srcDir, "sample1.xoo");
+ FileUtils.write(xooFile1, "Sample1 xoo\ncontent");
+
+ File xooFile2 = new File(srcDir, "sample2.xoo");
+ FileUtils.write(xooFile2, "Sample2 xoo\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ assertThat(result.issuesFor(result.inputDir("src"))).hasSize(2);
+ }
+
+ @Test
+ public void issueOnRootFolder() throws IOException {
+
+ File baseDir = temp.getRoot();
+
+ File xooFile1 = new File(baseDir, "sample1.xoo");
+ FileUtils.write(xooFile1, "Sample1 xoo\ncontent");
+
+ File xooFile2 = new File(baseDir, "sample2.xoo");
+ FileUtils.write(xooFile2, "Sample2 xoo\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", ".")
+ .build())
+ .start();
+
+ assertThat(result.issuesFor(result.inputDir(""))).hasSize(2);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnModuleMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnModuleMediumTest.java
new file mode 100644
index 00000000000..73434ed872d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnModuleMediumTest.java
@@ -0,0 +1,84 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issues;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssuesOnModuleMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addRules(new XooRulesDefinition())
+ .addActiveRule("xoo", "OneIssuePerModule", null, "One issue per module", "MINOR", "xoo", "xoo")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void scanTempProject() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile1 = new File(srcDir, "sample1.xoo");
+ FileUtils.write(xooFile1, "Sample1 xoo\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ assertThat(result.issuesFor(result.getReportComponent("com.foo.project"))).hasSize(1);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java
new file mode 100644
index 00000000000..7ec0fe90535
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java
@@ -0,0 +1,131 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issues;
+
+import java.io.File;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.scanner.protocol.output.ScannerReport.Flow;
+import org.sonar.scanner.protocol.output.ScannerReport.Issue;
+import org.sonar.scanner.protocol.output.ScannerReport.IssueLocation;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MultilineIssuesMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addActiveRule("xoo", "MultilineIssue", null, "Multinile Issue", "MAJOR", null, "xoo")
+ .build();
+
+ private TaskResult result;
+
+ @Before
+ public void prepare() throws Exception {
+ tester.start();
+
+ File projectDir = new File(MultilineIssuesMediumTest.class.getResource("/mediumtest/xoo/sample-multiline").toURI());
+ File tmpDir = temp.getRoot();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+
+ result = tester
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testIssueRange() throws Exception {
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Single.xoo"));
+ assertThat(issues).hasSize(1);
+ Issue issue = issues.get(0);
+ assertThat(issue.getLine()).isEqualTo(6);
+ assertThat(issue.getMsg()).isEqualTo("Primary location");
+ assertThat(issue.getTextRange().getStartLine()).isEqualTo(6);
+ assertThat(issue.getTextRange().getStartOffset()).isEqualTo(23);
+ assertThat(issue.getTextRange().getEndLine()).isEqualTo(6);
+ assertThat(issue.getTextRange().getEndOffset()).isEqualTo(50);
+ }
+
+ @Test
+ public void testMultilineIssueRange() throws Exception {
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Multiline.xoo"));
+ assertThat(issues).hasSize(1);
+ Issue issue = issues.get(0);
+ assertThat(issue.getLine()).isEqualTo(6);
+ assertThat(issue.getMsg()).isEqualTo("Primary location");
+ assertThat(issue.getTextRange().getStartLine()).isEqualTo(6);
+ assertThat(issue.getTextRange().getStartOffset()).isEqualTo(23);
+ assertThat(issue.getTextRange().getEndLine()).isEqualTo(7);
+ assertThat(issue.getTextRange().getEndOffset()).isEqualTo(23);
+ }
+
+ @Test
+ public void testFlowWithSingleLocation() throws Exception {
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Multiple.xoo"));
+ assertThat(issues).hasSize(1);
+ Issue issue = issues.get(0);
+ assertThat(issue.getLine()).isEqualTo(6);
+ assertThat(issue.getMsg()).isEqualTo("Primary location");
+ assertThat(issue.getTextRange().getStartLine()).isEqualTo(6);
+ assertThat(issue.getTextRange().getStartOffset()).isEqualTo(23);
+ assertThat(issue.getTextRange().getEndLine()).isEqualTo(6);
+ assertThat(issue.getTextRange().getEndOffset()).isEqualTo(50);
+
+ assertThat(issue.getFlowList()).hasSize(1);
+ Flow flow = issue.getFlow(0);
+ assertThat(flow.getLocationList()).hasSize(1);
+ IssueLocation additionalLocation = flow.getLocation(0);
+ assertThat(additionalLocation.getMsg()).isEqualTo("Flow step #1");
+ assertThat(additionalLocation.getTextRange().getStartLine()).isEqualTo(7);
+ assertThat(additionalLocation.getTextRange().getStartOffset()).isEqualTo(26);
+ assertThat(additionalLocation.getTextRange().getEndLine()).isEqualTo(7);
+ assertThat(additionalLocation.getTextRange().getEndOffset()).isEqualTo(53);
+ }
+
+ @Test
+ public void testFlowsWithMultipleElements() throws Exception {
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/WithFlow.xoo"));
+ assertThat(issues).hasSize(1);
+ Issue issue = issues.get(0);
+ assertThat(issue.getFlowList()).hasSize(1);
+
+ Flow flow = issue.getFlow(0);
+ assertThat(flow.getLocationList()).hasSize(2);
+ // TODO more assertions
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java
new file mode 100644
index 00000000000..22eb34b4c22
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/EmptyFileTest.java
@@ -0,0 +1,94 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issuesmode;
+
+import org.sonar.batch.issue.tracking.TrackedIssue;
+
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.apache.commons.io.FileUtils;
+import org.sonar.xoo.rule.XooRulesDefinition;
+import com.google.common.collect.ImmutableMap;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.xoo.XooPlugin;
+
+import java.io.File;
+import java.util.Date;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EmptyFileTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES))
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo")
+ .setPreviousAnalysisDate(new Date())
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testIssueTrackingWithIssueOnEmptyFile() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample-with-empty-file");
+
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .property("sonar.xoo.internalKey", "my/internal/key")
+ .start();
+
+ for(TrackedIssue i : result.trackedIssues()) {
+ System.out.println(i.startLine() + " " + i.getMessage());
+ }
+
+ assertThat(result.trackedIssues()).hasSize(11);
+ }
+
+ private File copyProject(String path) throws Exception {
+ File projectDir = temp.newFolder();
+ File originalProjectDir = new File(EmptyFileTest.class.getResource(path).toURI());
+ FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar")));
+ return projectDir;
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java
new file mode 100644
index 00000000000..e02c84ec71a
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/IssueModeAndReportsMediumTest.java
@@ -0,0 +1,316 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issuesmode;
+
+import org.apache.commons.lang.StringUtils;
+
+import org.sonar.api.utils.log.LoggerLevel;
+import org.assertj.core.api.Condition;
+import org.sonar.batch.issue.tracking.TrackedIssue;
+import com.google.common.collect.ImmutableMap;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.bootstrapper.IssueListener;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.batch.scan.report.ConsoleReport;
+import org.sonar.scanner.protocol.Constants.Severity;
+import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssueModeAndReportsMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @org.junit.Rule
+ public LogTester logTester = new LogTester();
+
+ private static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
+
+ private static Long date(String date) {
+ try {
+ return sdf.parse(date).getTime();
+ } catch (ParseException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES))
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addRules(new XooRulesDefinition())
+ .addRule("manual:MyManualIssue", "manual", "MyManualIssue", "My manual issue")
+ .addRule("manual:MyManualIssueDup", "manual", "MyManualIssue", "My manual issue")
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo")
+ .addActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo")
+ .addActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo")
+ .addActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null)
+ .setPreviousAnalysisDate(new Date())
+ // Existing issue that is still detected
+ .mockServerIssue(ServerIssue.newBuilder().setKey("xyz")
+ .setModuleKey("sample")
+ .setPath("xources/hello/HelloJava.xoo")
+ .setRuleRepository("xoo")
+ .setRuleKey("OneIssuePerLine")
+ .setLine(1)
+ .setSeverity(Severity.MAJOR)
+ .setCreationDate(date("14/03/2004"))
+ .setChecksum(DigestUtils.md5Hex("packagehello;"))
+ .setStatus("OPEN")
+ .build())
+ // Existing issue that is no more detected (will be closed)
+ .mockServerIssue(ServerIssue.newBuilder().setKey("resolved")
+ .setModuleKey("sample")
+ .setPath("xources/hello/HelloJava.xoo")
+ .setRuleRepository("xoo")
+ .setRuleKey("OneIssuePerLine")
+ .setLine(1)
+ .setSeverity(Severity.MAJOR)
+ .setCreationDate(date("14/03/2004"))
+ .setChecksum(DigestUtils.md5Hex("dontexist"))
+ .setStatus("OPEN")
+ .build())
+ // Existing issue on project that is still detected
+ .mockServerIssue(ServerIssue.newBuilder().setKey("resolved-on-project")
+ .setModuleKey("sample")
+ .setRuleRepository("xoo")
+ .setRuleKey("OneIssuePerModule")
+ .setSeverity(Severity.CRITICAL)
+ .setCreationDate(date("14/03/2004"))
+ .setStatus("OPEN")
+ .build())
+ // Manual issue
+ .mockServerIssue(ServerIssue.newBuilder().setKey("manual")
+ .setModuleKey("sample")
+ .setPath("xources/hello/HelloJava.xoo")
+ .setRuleRepository("manual")
+ .setRuleKey("MyManualIssue")
+ .setLine(1)
+ .setSeverity(Severity.MAJOR)
+ .setCreationDate(date("14/03/2004"))
+ .setChecksum(DigestUtils.md5Hex("packagehello;"))
+ .setStatus("OPEN")
+ .build())
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ private File copyProject(String path) throws Exception {
+ File projectDir = temp.newFolder();
+ File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI());
+ FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar")));
+ return projectDir;
+ }
+
+ @Test
+ public void testIssueTracking() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample");
+
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .start();
+
+ int newIssues = 0;
+ int openIssues = 0;
+ int resolvedIssue = 0;
+ for (TrackedIssue issue : result.trackedIssues()) {
+ System.out
+ .println(issue.getMessage() + " " + issue.key() + " " + issue.getRuleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " "
+ + issue.startLine());
+ if (issue.isNew()) {
+ newIssues++;
+ } else if (issue.resolution() != null) {
+ resolvedIssue++;
+ } else {
+ openIssues++;
+ }
+ }
+ System.out.println("new: " + newIssues + " open: " + openIssues + " resolved " + resolvedIssue);
+ assertThat(newIssues).isEqualTo(16);
+ assertThat(openIssues).isEqualTo(3);
+ assertThat(resolvedIssue).isEqualTo(1);
+
+ // progress report
+ String logs = StringUtils.join(logTester.logs(LoggerLevel.INFO), "\n");
+
+ assertThat(logs).contains("Performing issue tracking");
+ assertThat(logs).contains("6/6 components tracked");
+
+ // assert that original fields of a matched issue are kept
+ assertThat(result.trackedIssues()).haveExactly(1, new Condition<TrackedIssue>() {
+ @Override
+ public boolean matches(TrackedIssue value) {
+ return value.isNew() == false
+ && "resolved-on-project".equals(value.key())
+ && "OPEN".equals(value.status())
+ && new Date(date("14/03/2004")).equals(value.creationDate());
+ }
+ });
+ }
+
+ @Test
+ public void testConsoleReport() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample");
+
+ tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .property("sonar.issuesReport.console.enable", "true")
+ .start();
+
+ assertThat(getReportLog()).contains("+16 issues", "+16 major");
+ }
+
+ @Test
+ public void testPostJob() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample");
+
+ tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .property("sonar.xoo.enablePostJob", "true")
+ .start();
+
+ assertThat(logTester.logs()).contains("Resolved issues: 1", "Open issues: 19");
+ }
+
+ private String getReportLog() {
+ for (String log : logTester.logs()) {
+ if (log.contains(ConsoleReport.HEADER)) {
+ return log;
+ }
+ }
+ throw new IllegalStateException("No console report");
+ }
+
+ @Test
+ public void testHtmlReport() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample");
+
+ tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .property("sonar.issuesReport.html.enable", "true")
+ .start();
+
+ assertThat(new File(projectDir, ".sonar/issues-report/issues-report.html")).exists();
+ assertThat(new File(projectDir, ".sonar/issues-report/issues-report-light.html")).exists();
+ }
+
+ @Test
+ public void testHtmlReportNoFile() throws Exception {
+ File baseDir = temp.newFolder();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "sample")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.issuesReport.html.enable", "true")
+ .build())
+ .start();
+
+ assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report.html"))).contains("No file analyzed");
+ assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report-light.html"))).contains("No file analyzed");
+ }
+
+ @Test
+ public void testIssueCallback() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample");
+ IssueRecorder issueListener = new IssueRecorder();
+
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .setIssueListener(issueListener)
+ .start();
+
+ assertThat(result.trackedIssues()).hasSize(20);
+ assertThat(issueListener.issueList).hasSize(20);
+ }
+
+ private class IssueRecorder implements IssueListener {
+ List<Issue> issueList = new LinkedList<>();
+
+ @Override
+ public void handle(Issue issue) {
+ issueList.add(issue);
+ }
+ }
+
+ @Test
+ public void noSyntaxHighlightingInIssuesMode() throws IOException {
+
+ File baseDir = temp.newFolder();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting");
+ FileUtils.write(xooFile, "Sample xoo\ncontent plop");
+ FileUtils.write(xoohighlightingFile, "0:10:s\n11:18:k");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ assertThat(result.getReportReader().hasSyntaxHighlighting(1)).isFalse();
+ assertThat(result.getReportReader().hasSyntaxHighlighting(2)).isFalse();
+ assertThat(result.getReportReader().hasSyntaxHighlighting(3)).isFalse();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NoPreviousAnalysisTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NoPreviousAnalysisTest.java
new file mode 100644
index 00000000000..fa133979562
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NoPreviousAnalysisTest.java
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issuesmode;
+
+import org.sonar.batch.mediumtest.TaskResult;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+public class NoPreviousAnalysisTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES))
+ .registerPlugin("xoo", new XooPlugin())
+ .addRules(new XooRulesDefinition())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo")
+ .setPreviousAnalysisDate(null)
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testIssueTrackingWithIssueOnEmptyFile() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample");
+
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .start();
+
+ assertThat(result.trackedIssues()).hasSize(14);
+
+ }
+
+ private File copyProject(String path) throws Exception {
+ File projectDir = temp.newFolder();
+ File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI());
+ FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar")));
+ return projectDir;
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java
new file mode 100644
index 00000000000..32c66f2689f
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issuesmode;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import java.io.File;
+import java.io.IOException;
+
+public class NonAssociatedProject {
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @org.junit.Rule
+ public LogTester logTester = new LogTester();
+
+ public BatchMediumTester tester;
+
+ @Before
+ public void prepare() throws IOException {
+ tester = BatchMediumTester.builder()
+ .bootstrapProperties(ImmutableMap.of(
+ CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES,
+ CoreProperties.GLOBAL_WORKING_DIRECTORY, temp.newFolder().getAbsolutePath()))
+ .registerPlugin("xoo", new XooPlugin())
+ .addQProfile("xoo", "Sonar Way")
+ .addRules(new XooRulesDefinition())
+ .addRule("manual:MyManualIssue", "manual", "MyManualIssue", "My manual issue")
+ .addRule("manual:MyManualIssueDup", "manual", "MyManualIssue", "My manual issue")
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo")
+ .addActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo")
+ .addActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo")
+ .addActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null)
+ .setAssociated(false)
+ .build();
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ private File copyProject(String path) throws Exception {
+ File projectDir = temp.newFolder();
+ File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI());
+ FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar")));
+ return projectDir;
+ }
+
+ @Test
+ public void testNonAssociated() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/multi-modules-sample-not-associated");
+
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .start();
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java
new file mode 100644
index 00000000000..eb5850b0809
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/issuesmode/ScanOnlyChangedTest.java
@@ -0,0 +1,212 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issuesmode;
+
+import org.sonar.batch.issue.tracking.TrackedIssue;
+
+import org.assertj.core.api.Condition;
+import com.google.common.io.Resources;
+import org.sonar.batch.repository.FileData;
+import org.sonar.scanner.protocol.Constants.Severity;
+import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue;
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.sonar.api.CoreProperties;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.utils.log.LogTester;
+import org.junit.Test;
+import org.sonar.batch.mediumtest.TaskResult;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ScanOnlyChangedTest {
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @org.junit.Rule
+ public LogTester logTester = new LogTester();
+
+ private static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
+
+ private BatchMediumTester tester;
+
+ private static Long date(String date) {
+ try {
+ return sdf.parse(date).getTime();
+ } catch (ParseException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Before
+ public void prepare() throws IOException {
+ String filePath = "xources/hello/HelloJava.xoo";
+ String md5sum = DigestUtils.md5Hex(FileUtils.readFileToString(new File(
+ Resources.getResource("mediumtest/xoo/sample/" + filePath).getPath())));
+
+ tester = BatchMediumTester.builder()
+ .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES))
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addRules(new XooRulesDefinition())
+ .addRule("manual:MyManualIssue", "manual", "MyManualIssue", "My manual issue")
+ .addRule("manual:MyManualIssueDup", "manual", "MyManualIssue", "My manual issue")
+ .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo")
+ .addActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo")
+ .addActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo")
+ .addActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null)
+ // this will cause the file to have status==SAME
+ .addFileData("sample", filePath, new FileData(md5sum, null))
+ .setPreviousAnalysisDate(new Date())
+ // Existing issue that is copied
+ .mockServerIssue(ServerIssue.newBuilder().setKey("xyz")
+ .setModuleKey("sample")
+ .setMsg("One issue per Line copied")
+ .setPath("xources/hello/HelloJava.xoo")
+ .setRuleRepository("xoo")
+ .setRuleKey("OneIssuePerLine")
+ .setLine(1)
+ .setSeverity(Severity.MAJOR)
+ .setCreationDate(date("14/03/2004"))
+ .setChecksum(DigestUtils.md5Hex("packagehello;"))
+ .setStatus("OPEN")
+ .build())
+ // Existing issue on project that is still detected
+ .mockServerIssue(ServerIssue.newBuilder().setKey("resolved-on-project")
+ .setModuleKey("sample")
+ .setRuleRepository("xoo")
+ .setRuleKey("OneIssuePerModule")
+ .setSeverity(Severity.CRITICAL)
+ .setCreationDate(date("14/03/2004"))
+ .setStatus("OPEN")
+ .build())
+ // Manual issue
+ .mockServerIssue(ServerIssue.newBuilder().setKey("manual")
+ .setModuleKey("sample")
+ .setPath("xources/hello/HelloJava.xoo")
+ .setRuleRepository("manual")
+ .setRuleKey("MyManualIssue")
+ .setLine(1)
+ .setSeverity(Severity.MAJOR)
+ .setCreationDate(date("14/03/2004"))
+ .setChecksum(DigestUtils.md5Hex("packagehello;"))
+ .setStatus("OPEN")
+ .build())
+ .build();
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ private File copyProject(String path) throws Exception {
+ File projectDir = temp.newFolder();
+ File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI());
+ FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar")));
+ return projectDir;
+ }
+
+ @Test
+ public void testScanAll() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample");
+
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .property("sonar.scanAllFiles", "true")
+ .start();
+
+ assertNumberIssues(result, 16, 3, 0);
+
+ /*
+ * 8 new per line
+ * 1 manual
+ */
+ assertNumberIssuesOnFile(result, "HelloJava.xoo", 9);
+ }
+
+ @Test
+ public void testScanOnlyChangedFiles() throws Exception {
+ File projectDir = copyProject("/mediumtest/xoo/sample");
+
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .start();
+
+ /*
+ * We have:
+ * 6 new issues per line (open) in helloscala.xoo
+ * 2 new issues per file in helloscala.xoo / ClassOneTest.xoo
+ * 1 server issue (open, not new) copied from server in HelloJava.xoo (this file is unchanged)
+ * 1 manual issue (open, not new) in HelloJava.xoo
+ * 1 existing issue on the project (open, not new)
+ */
+ assertNumberIssues(result, 8, 3, 0);
+
+ // should only have server issues (HelloJava.xoo should not have been analyzed)
+ assertNumberIssuesOnFile(result, "HelloJava.xoo", 2);
+ }
+
+ private static void assertNumberIssuesOnFile(TaskResult result, final String fileNameEndsWith, int issues) {
+ assertThat(result.trackedIssues()).haveExactly(issues, new Condition<TrackedIssue>() {
+ @Override
+ public boolean matches(TrackedIssue value) {
+ return value.componentKey().endsWith(fileNameEndsWith);
+ }
+ });
+ }
+
+ private static void assertNumberIssues(TaskResult result, int expectedNew, int expectedOpen, int expectedResolved) {
+ int newIssues = 0;
+ int openIssues = 0;
+ int resolvedIssue = 0;
+ for (TrackedIssue issue : result.trackedIssues()) {
+ System.out
+ .println(issue.getMessage() + " " + issue.key() + " " + issue.getRuleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " "
+ + issue.startLine());
+ if (issue.isNew()) {
+ newIssues++;
+ } else if (issue.resolution() != null) {
+ resolvedIssue++;
+ } else {
+ openIssues++;
+ }
+ }
+
+ System.out.println("new: " + newIssues + " open: " + openIssues + " resolved " + resolvedIssue);
+ assertThat(newIssues).isEqualTo(expectedNew);
+ assertThat(openIssues).isEqualTo(expectedOpen);
+ assertThat(resolvedIssue).isEqualTo(expectedResolved);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java
new file mode 100644
index 00000000000..db1a2dcd30e
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.log;
+
+import java.util.Collections;
+
+import org.hamcrest.Matchers;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.BeforeClass;
+import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.api.utils.MessageException;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.sonar.batch.repository.GlobalRepositoriesLoader;
+import org.sonar.scanner.protocol.input.GlobalRepositories;
+import org.sonar.batch.bootstrapper.Batch;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class ExceptionHandlingMediumTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private Batch batch;
+ private static ErrorGlobalRepositoriesLoader loader;
+
+ @BeforeClass
+ public static void beforeClass() {
+ loader = new ErrorGlobalRepositoriesLoader();
+ }
+
+ public void setUp(boolean verbose) {
+ Batch.Builder builder = Batch.builder()
+ .setEnableLoggingConfiguration(true)
+ .addComponents(
+ loader,
+ new EnvironmentInformation("mediumTest", "1.0"));
+
+ if (verbose) {
+ builder.setBootstrapProperties(Collections.singletonMap("sonar.verbose", "true"));
+ }
+ batch = builder.build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ setUp(false);
+ loader.withCause = false;
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("Error loading repository");
+ thrown.expectCause(Matchers.nullValue(Throwable.class));
+
+ batch.start();
+ }
+
+ @Test
+ public void testWithCause() throws Exception {
+ setUp(false);
+ loader.withCause = true;
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("Error loading repository");
+ thrown.expectCause(new TypeSafeMatcher<Throwable>() {
+ @Override
+ public void describeTo(Description description) {
+ }
+
+ @Override
+ protected boolean matchesSafely(Throwable item) {
+ return item instanceof IllegalStateException && item.getMessage().equals("Code 401");
+ }
+ });
+
+ batch.start();
+ }
+
+ @Test
+ public void testWithVerbose() throws Exception {
+ setUp(true);
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Unable to load component class");
+ batch.start();
+ }
+
+ private static class ErrorGlobalRepositoriesLoader implements GlobalRepositoriesLoader {
+ boolean withCause = false;
+
+ @Override
+ public GlobalRepositories load(MutableBoolean fromCache) {
+ if (withCause) {
+ IllegalStateException cause = new IllegalStateException("Code 401");
+ throw MessageException.of("Error loading repository", cause);
+ } else {
+ throw MessageException.of("Error loading repository");
+ }
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java
new file mode 100644
index 00000000000..f360dca56ac
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java
@@ -0,0 +1,216 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.log;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.bootstrapper.LogOutput;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.xoo.XooPlugin;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LogListenerTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private Pattern simpleTimePattern = Pattern.compile("\\d{2}:\\d{2}:\\d{2}");
+ private List<LogEvent> logOutput;
+ private StringBuilder logOutputStr;
+ private ByteArrayOutputStream stdOutTarget = new ByteArrayOutputStream();
+ private ByteArrayOutputStream stdErrTarget = new ByteArrayOutputStream();
+ private static PrintStream savedStdOut;
+ private static PrintStream savedStdErr;
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .setLogOutput(new SimpleLogListener())
+ .build();
+
+ private File baseDir;
+
+ private ImmutableMap.Builder<String, String> builder;
+
+ @BeforeClass
+ public static void backupStdStreams() {
+ savedStdOut = System.out;
+ savedStdErr = System.err;
+ }
+
+ @AfterClass
+ public static void resumeStdStreams() {
+ if (savedStdOut != null) {
+ System.setOut(savedStdOut);
+ }
+ if (savedStdErr != null) {
+ System.setErr(savedStdErr);
+ }
+ }
+
+ @Before
+ public void prepare() throws IOException {
+ System.setOut(new PrintStream(stdOutTarget));
+ System.setErr(new PrintStream(stdErrTarget));
+ // logger from the batch might write to it asynchronously
+ logOutput = Collections.synchronizedList(new LinkedList<LogEvent>());
+ logOutputStr = new StringBuilder();
+ tester.start();
+
+ baseDir = temp.getRoot();
+
+ builder = ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project");
+ }
+
+ private void assertNoStdOutput() {
+ assertThat(stdOutTarget.toByteArray()).isEmpty();
+ assertThat(stdErrTarget.toByteArray()).isEmpty();
+ }
+
+ /**
+ * Check that log message is not formatted, i.e. has no log level and timestamp.
+ */
+ private void assertMsgClean(String msg) {
+ for (LogOutput.Level l : LogOutput.Level.values()) {
+ assertThat(msg).doesNotContain(l.toString());
+ }
+
+ Matcher matcher = simpleTimePattern.matcher(msg);
+ assertThat(matcher.find()).isFalse();
+ }
+
+ @Test
+ public void testChangeLogForAnalysis() throws IOException, InterruptedException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+
+ tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .put("sonar.verbose", "true")
+ .build())
+ .start();
+
+ tester.stop();
+ for (LogEvent e : logOutput) {
+ savedStdOut.println("[captured]" + e.level + " " + e.msg);
+ }
+
+ // only done in DEBUG during analysis
+ assertThat(logOutputStr.toString()).contains("Post-jobs : ");
+ }
+
+ @Test
+ public void testNoStdLog() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+
+ tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+ tester.stop();
+
+ assertNoStdOutput();
+ assertThat(logOutput).isNotEmpty();
+
+ synchronized (logOutput) {
+ for (LogEvent e : logOutput) {
+ savedStdOut.println("[captured]" + e.level + " " + e.msg);
+ }
+ }
+ }
+
+ @Test
+ public void testNoFormattedMsgs() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+
+ tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+ tester.stop();
+
+ assertNoStdOutput();
+
+ synchronized (logOutput) {
+ for (LogEvent e : logOutput) {
+ assertMsgClean(e.msg);
+ savedStdOut.println("[captured]" + e.level + " " + e.msg);
+ }
+ }
+ }
+
+ private class SimpleLogListener implements LogOutput {
+ @Override
+ public void log(String msg, Level level) {
+ logOutput.add(new LogEvent(msg, level));
+ logOutputStr.append(msg).append("\n");
+ }
+ }
+
+ private static class LogEvent {
+ String msg;
+ LogOutput.Level level;
+
+ LogEvent(String msg, LogOutput.Level level) {
+ this.msg = msg;
+ this.level = level;
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java
new file mode 100644
index 00000000000..e9db30c49f4
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java
@@ -0,0 +1,131 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.measures;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.io.FileUtils;
+import org.assertj.core.groups.Tuple;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.scanner.protocol.output.ScannerReport.Measure;
+import org.sonar.xoo.XooPlugin;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class MeasuresMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private File baseDir;
+ private File srcDir;
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Before
+ public void setUp() {
+ baseDir = temp.getRoot();
+ srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+ }
+
+ @Test
+ public void computeMeasuresOnTempProject() throws IOException {
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xooMeasureFile = new File(srcDir, "sample.xoo.measures");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+ FileUtils.write(xooMeasureFile, "lines:20");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.cpd.xoo.skip", "true")
+ .build())
+ .start();
+
+ Map<String, List<Measure>> allMeasures = result.allMeasures();
+
+ assertThat(allMeasures.get("com.foo.project")).extracting("metricKey", "intValue", "doubleValue", "stringValue").containsOnly(
+ Tuple.tuple(CoreMetrics.QUALITY_PROFILES_KEY, 0, 0.0,
+ "[{\"key\":\"Sonar Way\",\"language\":\"xoo\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2009-02-13T23:31:31+0000\"}]"));
+
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue").containsOnly(
+ Tuple.tuple(CoreMetrics.LINES_KEY, 2));
+ }
+
+ @Test
+ public void computeLinesOnAllFiles() throws IOException {
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\n\ncontent");
+
+ File otherFile = new File(srcDir, "sample.other");
+ FileUtils.write(otherFile, "Sample other\ncontent\n");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.import_unknown_files", "true")
+ .build())
+ .start();
+
+ Map<String, List<Measure>> allMeasures = result.allMeasures();
+
+ assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue")
+ .contains(tuple("lines", 3));
+ assertThat(allMeasures.get("com.foo.project:src/sample.other")).extracting("metricKey", "intValue")
+ .contains(tuple("lines", 3), tuple("ncloc", 2));
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java
new file mode 100644
index 00000000000..37c8da1ef01
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java
@@ -0,0 +1,363 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.scm;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.utils.PathUtils;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.BatchMediumTester.TaskBuilder;
+import org.sonar.batch.repository.FileData;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReportReader;
+import org.sonar.scanner.protocol.output.ScannerReport.Component;
+import org.sonar.scanner.protocol.output.ScannerReport.Changesets.Changeset;
+import org.sonar.xoo.XooPlugin;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ScmMediumTest {
+
+ private static final String MISSING_BLAME_INFORMATION_FOR_THE_FOLLOWING_FILES = "Missing blame information for the following files:";
+ private static final String CHANGED_CONTENT_SCM_ON_SERVER_XOO = "src/changed_content_scm_on_server.xoo";
+ private static final String SAME_CONTENT_SCM_ON_SERVER_XOO = "src/same_content_scm_on_server.xoo";
+ private static final String SAME_CONTENT_NO_SCM_ON_SERVER_XOO = "src/same_content_no_scm_on_server.xoo";
+ private static final String SAMPLE_XOO_CONTENT = "Sample xoo\ncontent";
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .addFileData("com.foo.project", CHANGED_CONTENT_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), null))
+ .addFileData("com.foo.project", SAME_CONTENT_NO_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), null))
+ .addFileData("com.foo.project", SAME_CONTENT_SCM_ON_SERVER_XOO, new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), "1.1"))
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testScmMeasure() throws IOException {
+
+ File baseDir = prepareProject();
+
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.scm.provider", "xoo")
+ .build())
+ .start();
+
+ ScannerReport.Changesets fileScm = getChangesets(baseDir, "src/sample.xoo");
+
+ assertThat(fileScm.getChangesetIndexByLineList()).hasSize(5);
+
+ Changeset changesetLine1 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(0));
+ assertThat(changesetLine1.hasAuthor()).isFalse();
+
+ Changeset changesetLine2 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(1));
+ assertThat(changesetLine2.getAuthor()).isEqualTo("julien");
+
+ Changeset changesetLine3 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(2));
+ assertThat(changesetLine3.getAuthor()).isEqualTo("julien");
+
+ Changeset changesetLine4 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(3));
+ assertThat(changesetLine4.getAuthor()).isEqualTo("julien");
+
+ Changeset changesetLine5 = fileScm.getChangeset(fileScm.getChangesetIndexByLine(4));
+ assertThat(changesetLine5.getAuthor()).isEqualTo("simon");
+ }
+
+ private ScannerReport.Changesets getChangesets(File baseDir, String path) {
+ File reportDir = new File(baseDir, ".sonar/batch-report");
+ ScannerReportReader reader = new ScannerReportReader(reportDir);
+
+ Component project = reader.readComponent(reader.readMetadata().getRootComponentRef());
+ Component dir = reader.readComponent(project.getChildRef(0));
+ for (Integer fileRef : dir.getChildRefList()) {
+ Component file = reader.readComponent(fileRef);
+ if (file.getPath().equals(path)) {
+ return reader.readChangesets(file.getRef());
+ }
+ }
+ return null;
+ }
+
+ @Test
+ public void noScmOnEmptyFile() throws IOException {
+
+ File baseDir = prepareProject();
+
+ // Clear file content
+ FileUtils.write(new File(baseDir, "src/sample.xoo"), "");
+
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.scm.provider", "xoo")
+ .build())
+ .start();
+
+ ScannerReport.Changesets changesets = getChangesets(baseDir, "src/sample.xoo");
+
+ assertThat(changesets).isNull();
+ }
+
+ @Test
+ public void log_files_with_missing_blame() throws IOException {
+
+ File baseDir = prepareProject();
+ File xooFileWithoutBlame = new File(baseDir, "src/sample_no_blame.xoo");
+ FileUtils.write(xooFileWithoutBlame, "Sample xoo\ncontent\n3\n4\n5");
+
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.scm.provider", "xoo")
+ .build())
+ .start();
+
+ ScannerReport.Changesets file1Scm = getChangesets(baseDir, "src/sample.xoo");
+ assertThat(file1Scm).isNotNull();
+
+ ScannerReport.Changesets fileWithoutBlameScm = getChangesets(baseDir, "src/sample_no_blame.xoo");
+ assertThat(fileWithoutBlameScm).isNull();
+
+ assertThat(logTester.logs()).containsSubsequence("2 files to be analyzed", "1/2 files analyzed", MISSING_BLAME_INFORMATION_FOR_THE_FOLLOWING_FILES,
+ " * " + PathUtils.sanitize(xooFileWithoutBlame.toPath().toString()));
+ }
+
+ // SONAR-6397
+ @Test
+ public void optimize_blame() throws IOException {
+
+ File baseDir = prepareProject();
+ File changedContentScmOnServer = new File(baseDir, CHANGED_CONTENT_SCM_ON_SERVER_XOO);
+ FileUtils.write(changedContentScmOnServer, SAMPLE_XOO_CONTENT + "\nchanged");
+ File xooScmFile = new File(baseDir, CHANGED_CONTENT_SCM_ON_SERVER_XOO + ".scm");
+ FileUtils.write(xooScmFile,
+ // revision,author,dateTime
+ "1,foo,2013-01-04\n" +
+ "1,bar,2013-01-04\n" +
+ "2,biz,2014-01-04\n");
+
+ File sameContentScmOnServer = new File(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO);
+ FileUtils.write(sameContentScmOnServer, SAMPLE_XOO_CONTENT);
+ // No need to write .scm file since this file should not be blamed
+
+ File sameContentNoScmOnServer = new File(baseDir, SAME_CONTENT_NO_SCM_ON_SERVER_XOO);
+ FileUtils.write(sameContentNoScmOnServer, SAMPLE_XOO_CONTENT);
+ xooScmFile = new File(baseDir, SAME_CONTENT_NO_SCM_ON_SERVER_XOO + ".scm");
+ FileUtils.write(xooScmFile,
+ // revision,author,dateTime
+ "1,foo,2013-01-04\n" +
+ "1,bar,2013-01-04\n");
+
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.scm.provider", "xoo")
+ .build())
+ .start();
+
+ assertThat(getChangesets(baseDir, "src/sample.xoo")).isNotNull();
+
+ assertThat(getChangesets(baseDir, CHANGED_CONTENT_SCM_ON_SERVER_XOO)).isNotNull();
+
+ assertThat(getChangesets(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO)).isNull();
+
+ assertThat(getChangesets(baseDir, SAME_CONTENT_NO_SCM_ON_SERVER_XOO)).isNotNull();
+
+ assertThat(logTester.logs()).containsSubsequence("3 files to be analyzed", "3/3 files analyzed");
+ assertThat(logTester.logs()).doesNotContain(MISSING_BLAME_INFORMATION_FOR_THE_FOLLOWING_FILES);
+ }
+
+ @Test
+ public void forceReload() throws IOException {
+
+ File baseDir = prepareProject();
+ File xooFileNoScm = new File(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO);
+ FileUtils.write(xooFileNoScm, SAMPLE_XOO_CONTENT);
+ File xooScmFile = new File(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO + ".scm");
+ FileUtils.write(xooScmFile,
+ // revision,author,dateTime
+ "1,foo,2013-01-04\n" +
+ "1,bar,2013-01-04\n");
+
+ TaskBuilder taskBuilder = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.scm.provider", "xoo")
+ // Force reload
+ .put("sonar.scm.forceReloadAll", "true")
+ .build());
+
+ taskBuilder.start();
+
+ ScannerReport.Changesets file1Scm = getChangesets(baseDir, "src/sample.xoo");
+ assertThat(file1Scm).isNotNull();
+
+ ScannerReport.Changesets file2Scm = getChangesets(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO);
+ assertThat(file2Scm).isNotNull();
+ }
+
+ @Test
+ public void configureUsingScmURL() throws IOException {
+
+ File baseDir = prepareProject();
+
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.links.scm_dev", "scm:xoo:foobar")
+ .build())
+ .start();
+
+ ScannerReport.Changesets file1Scm = getChangesets(baseDir, "src/sample.xoo");
+ assertThat(file1Scm).isNotNull();
+ }
+
+ @Test
+ public void testAutoDetection() throws IOException {
+
+ File baseDir = prepareProject();
+ new File(baseDir, ".xoo").createNewFile();
+
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ ScannerReport.Changesets file1Scm = getChangesets(baseDir, "src/sample.xoo");
+ assertThat(file1Scm).isNotNull();
+ }
+
+ private File prepareProject() throws IOException {
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile1 = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile1, "Sample xoo\ncontent\n3\n4\n5");
+ File xooScmFile1 = new File(srcDir, "sample.xoo.scm");
+ FileUtils.write(xooScmFile1,
+ // revision,author,dateTime
+ "1,,2013-01-04\n" +
+ "2,julien,2013-01-04\n" +
+ "3,julien,2013-02-03\n" +
+ "3,julien,2013-02-03\n" +
+ "4,simon,2013-03-04\n");
+
+ return baseDir;
+ }
+
+ @Test
+ public void testDisableScmSensor() throws IOException {
+
+ File baseDir = prepareProject();
+
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.scm.disabled", "true")
+ .put("sonar.scm.provider", "xoo")
+ .put("sonar.cpd.xoo.skip", "true")
+ .build())
+ .start();
+
+ ScannerReport.Changesets changesets = getChangesets(baseDir, "src/sample.xoo");
+ assertThat(changesets).isNull();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java
new file mode 100644
index 00000000000..8d9836f994d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.symbol;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.xoo.XooPlugin;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SymbolMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void computeSymbolReferencesOnTempProject() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xooSymbolFile = new File(srcDir, "sample.xoo.symbol");
+ FileUtils.write(xooFile, "Sample xoo\ncontent\nanother xoo");
+ // Highlight xoo symbol
+ FileUtils.write(xooSymbolFile, "7:10,27");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ InputFile file = result.inputFile("src/sample.xoo");
+ assertThat(result.symbolReferencesFor(file, 1, 7)).containsOnly(ScannerReport.TextRange.newBuilder().setStartLine(3).setStartOffset(8).setEndLine(3).setEndOffset(11).build());
+ }
+
+ @Test
+ public void computeSymbolReferencesWithVariableLength() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ File xooSymbolFile = new File(srcDir, "sample.xoo.symbol");
+ FileUtils.write(xooFile, "Sample xoo\ncontent\nanother xoo\nyet another");
+ // Highlight xoo symbol
+ FileUtils.write(xooSymbolFile, "7:10,27:32");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .build())
+ .start();
+
+ InputFile file = result.inputFile("src/sample.xoo");
+ assertThat(result.symbolReferencesFor(file, 1, 7)).containsOnly(ScannerReport.TextRange.newBuilder().setStartLine(3).setStartOffset(8).setEndLine(4).setEndOffset(1).build());
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tasks/TasksMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tasks/TasksMediumTest.java
new file mode 100644
index 00000000000..766e9371898
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tasks/TasksMediumTest.java
@@ -0,0 +1,160 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.tasks;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.List;
+import org.assertj.core.api.Condition;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.SonarPlugin;
+import org.sonar.api.issue.action.Actions;
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.bootstrap.MockHttpServer;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TasksMediumTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("faketask", new FakeTaskPlugin())
+ .build();
+
+ private MockHttpServer server = null;
+
+ @After
+ public void stopServer() {
+ if (server != null) {
+ server.stop();
+ }
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void listTasksIncludingBroken() throws Exception {
+ tester.start();
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "list").build())
+ .start();
+
+ assertThat(logTester.logs()).haveExactly(1, new Condition<String>() {
+
+ @Override
+ public boolean matches(String value) {
+ return value.contains("Available tasks:") && value.contains("fake: Fake description") && value.contains("broken: Broken description");
+ }
+ });
+ }
+
+ @Test
+ public void runBroken() throws Exception {
+ tester.start();
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage(
+ "Unable to load component class org.sonar.batch.mediumtest.tasks.TasksMediumTest$BrokenTask");
+
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "broken").build())
+ .start();
+ }
+
+ @Test(expected = MessageException.class)
+ public void unsupportedTask() throws Exception {
+ tester = BatchMediumTester.builder()
+ .build();
+ tester.start();
+ tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "foo").build())
+ .start();
+ }
+
+ private void startServer(Integer responseStatus, String responseData) throws Exception {
+ server = new MockHttpServer();
+ server.start();
+
+ if (responseStatus != null) {
+ server.setMockResponseStatus(responseStatus);
+ }
+ if (responseData != null) {
+ server.setMockResponseData(responseData);
+ }
+ }
+
+ private static class FakeTaskPlugin extends SonarPlugin {
+
+ @Override
+ public List getExtensions() {
+ return Arrays.asList(FakeTask.DEF, FakeTask.class, BrokenTask.DEF, BrokenTask.class);
+ }
+
+ }
+
+ private static class FakeTask implements Task {
+
+ public static final TaskDefinition DEF = TaskDefinition.builder().key("fake").description("Fake description").taskClass(FakeTask.class).build();
+
+ @Override
+ public void execute() {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ private static class BrokenTask implements Task {
+
+ public static final TaskDefinition DEF = TaskDefinition.builder().key("broken").description("Broken description").taskClass(BrokenTask.class).build();
+ private final Actions serverSideComponent;
+
+ public BrokenTask(Actions serverSideComponent) {
+ this.serverSideComponent = serverSideComponent;
+ }
+
+ @Override
+ public void execute() {
+ System.out.println(serverSideComponent.list());
+ ;
+
+ }
+
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/CoveragePerTestMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/CoveragePerTestMediumTest.java
new file mode 100644
index 00000000000..d62d978ecaf
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/CoveragePerTestMediumTest.java
@@ -0,0 +1,160 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.tests;
+
+import org.hamcrest.Description;
+
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.xoo.XooPlugin;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CoveragePerTestMediumTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ // SONAR-6183
+ public void invalidCoverage() throws IOException {
+ File baseDir = createTestFiles();
+ File srcDir = new File(baseDir, "src");
+
+ File coverageFile = new File(srcDir, "sample.xoo.coverage");
+ FileUtils.write(coverageFile, "0:2\n");
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Error processing line 1 of file");
+ exception.expectCause(new TypeSafeMatcher<Throwable>() {
+
+ @Override
+ public void describeTo(Description description) {
+ // nothing to do
+ }
+
+ @Override
+ protected boolean matchesSafely(Throwable item) {
+ return item.getMessage().contains("Line number must be strictly positive");
+ }
+ });
+ runTask(baseDir);
+
+ }
+
+ @Test
+ public void coveragePerTestInReport() throws IOException {
+ File baseDir = createTestFiles();
+ File testDir = new File(baseDir, "test");
+
+ File xooTestExecutionFile = new File(testDir, "sampleTest.xoo.test");
+ FileUtils.write(xooTestExecutionFile, "some test:4:::OK:UNIT\n" +
+ "another test:10:::OK:UNIT\n" +
+ "test without coverage:10:::OK:UNIT\n");
+
+ File xooCoveragePerTestFile = new File(testDir, "sampleTest.xoo.testcoverage");
+ FileUtils.write(xooCoveragePerTestFile, "some test;src/sample.xoo,10,11;src/sample2.xoo,1,2\n" +
+ "another test;src/sample.xoo,10,20\n");
+
+ TaskResult result = runTask(baseDir);
+
+ InputFile file = result.inputFile("test/sampleTest.xoo");
+ org.sonar.scanner.protocol.output.ScannerReport.CoverageDetail someTest = result.coveragePerTestFor(file, "some test");
+ assertThat(someTest.getCoveredFileList()).hasSize(2);
+ assertThat(someTest.getCoveredFile(0).getFileRef()).isGreaterThan(0);
+ assertThat(someTest.getCoveredFile(0).getCoveredLineList()).containsExactly(10, 11);
+ assertThat(someTest.getCoveredFile(1).getFileRef()).isGreaterThan(0);
+ assertThat(someTest.getCoveredFile(1).getCoveredLineList()).containsExactly(1, 2);
+
+ org.sonar.scanner.protocol.output.ScannerReport.CoverageDetail anotherTest = result.coveragePerTestFor(file, "another test");
+ assertThat(anotherTest.getCoveredFileList()).hasSize(1);
+ assertThat(anotherTest.getCoveredFile(0).getFileRef()).isGreaterThan(0);
+ assertThat(anotherTest.getCoveredFile(0).getCoveredLineList()).containsExactly(10, 20);
+ }
+
+ private TaskResult runTask(File baseDir) {
+ return tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.tests", "test")
+ .build())
+ .start();
+ }
+
+ private File createTestFiles() throws IOException {
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+ File testDir = new File(baseDir, "test");
+ testDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "foo");
+
+ File xooFile2 = new File(srcDir, "sample2.xoo");
+ FileUtils.write(xooFile2, "foo");
+
+ File xooTestFile = new File(testDir, "sampleTest.xoo");
+ FileUtils.write(xooTestFile, "failure\nerror\nok\nskipped");
+
+ File xooTestFile2 = new File(testDir, "sample2Test.xoo");
+ FileUtils.write(xooTestFile2, "test file tests");
+
+ return baseDir;
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/TestExecutionMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/TestExecutionMediumTest.java
new file mode 100644
index 00000000000..cae559f16c5
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/tests/TestExecutionMediumTest.java
@@ -0,0 +1,105 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.tests;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.scanner.protocol.Constants.TestStatus;
+import org.sonar.xoo.XooPlugin;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TestExecutionMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void unitTests() throws IOException {
+
+ File baseDir = temp.getRoot();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+ File testDir = new File(baseDir, "test");
+ testDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "foo");
+
+ File xooTestFile = new File(testDir, "sampleTest.xoo");
+ FileUtils.write(xooTestFile, "failure\nerror\nok\nskipped");
+
+ File xooTestExecutionFile = new File(testDir, "sampleTest.xoo.test");
+ FileUtils.write(xooTestExecutionFile, "skipped::::SKIPPED:UNIT\n" +
+ "failure:2:Failure::FAILURE:UNIT\n" +
+ "error:2:Error:The stack:ERROR:UNIT\n" +
+ "success:4:::OK:INTEGRATION");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project")
+ .put("sonar.sources", "src")
+ .put("sonar.tests", "test")
+ .build())
+ .start();
+
+ InputFile file = result.inputFile("test/sampleTest.xoo");
+ org.sonar.scanner.protocol.output.ScannerReport.Test success = result.testExecutionFor(file, "success");
+ assertThat(success.getDurationInMs()).isEqualTo(4);
+ assertThat(success.getStatus()).isEqualTo(TestStatus.OK);
+
+ org.sonar.scanner.protocol.output.ScannerReport.Test error = result.testExecutionFor(file, "error");
+ assertThat(error.getDurationInMs()).isEqualTo(2);
+ assertThat(error.getStatus()).isEqualTo(TestStatus.ERROR);
+ assertThat(error.getMsg()).isEqualTo("Error");
+ assertThat(error.getStacktrace()).isEqualTo("The stack");
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java
new file mode 100644
index 00000000000..f9fda97b21c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.phases;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.batch.PostJob;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.bootstrap.BatchExtensionDictionnary;
+import org.sonar.batch.events.EventBus;
+
+import java.util.Arrays;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class PostJobsExecutorTest {
+ PostJobsExecutor executor;
+
+ Project project = new Project("project");
+ BatchExtensionDictionnary selector = mock(BatchExtensionDictionnary.class);
+ PostJob job1 = mock(PostJob.class);
+ PostJob job2 = mock(PostJob.class);
+ SensorContext context = mock(SensorContext.class);
+
+ @Before
+ public void setUp() {
+ executor = new PostJobsExecutor(selector, project, mock(EventBus.class));
+ }
+
+ @Test
+ public void should_execute_post_jobs() {
+ when(selector.select(PostJob.class, project, true, null)).thenReturn(Arrays.asList(job1, job2));
+
+ executor.execute(context);
+
+ verify(job1).executeOn(project, context);
+ verify(job2).executeOn(project, context);
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/platform/DefaultServerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/platform/DefaultServerTest.java
new file mode 100644
index 00000000000..801848354fc
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/platform/DefaultServerTest.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.platform;
+
+import org.sonar.batch.bootstrap.GlobalProperties;
+
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultServerTest {
+
+ @Test
+ public void shouldLoadServerProperties() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.SERVER_ID, "123");
+ settings.setProperty(CoreProperties.SERVER_VERSION, "2.2");
+ settings.setProperty(CoreProperties.SERVER_STARTTIME, "2010-05-18T17:59:00+0000");
+ settings.setProperty(CoreProperties.PERMANENT_SERVER_ID, "abcde");
+ GlobalProperties props = mock(GlobalProperties.class);
+ when(props.property("sonar.host.url")).thenReturn("http://foo.com");
+
+ DefaultServer metadata = new DefaultServer(settings, props);
+
+ assertThat(metadata.getId()).isEqualTo("123");
+ assertThat(metadata.getVersion()).isEqualTo("2.2");
+ assertThat(metadata.getStartedAt()).isNotNull();
+ assertThat(metadata.getURL()).isEqualTo("http://foo.com");
+ assertThat(metadata.getPermanentServerId()).isEqualTo("abcde");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java
new file mode 100644
index 00000000000..12f00a55d90
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/DefaultPostJobContextTest.java
@@ -0,0 +1,87 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.postjob;
+
+import org.sonar.batch.issue.tracking.TrackedIssue;
+
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.postjob.issue.Issue;
+import org.sonar.api.batch.rule.Severity;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.File;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.issue.IssueCache;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultPostJobContextTest {
+
+ private IssueCache issueCache;
+ private BatchComponentCache resourceCache;
+ private AnalysisMode analysisMode;
+ private DefaultPostJobContext context;
+ private Settings settings;
+
+ @Before
+ public void prepare() {
+ issueCache = mock(IssueCache.class);
+ resourceCache = new BatchComponentCache();
+ analysisMode = mock(AnalysisMode.class);
+ settings = new Settings();
+ context = new DefaultPostJobContext(settings, analysisMode, issueCache, resourceCache);
+ }
+
+ @Test
+ public void test() {
+ assertThat(context.settings()).isSameAs(settings);
+ assertThat(context.analysisMode()).isSameAs(analysisMode);
+
+ TrackedIssue defaultIssue = new TrackedIssue();
+ defaultIssue.setComponentKey("foo:src/Foo.php");
+ defaultIssue.setGap(2.0);
+ defaultIssue.setNew(true);
+ defaultIssue.setKey("xyz");
+ defaultIssue.setStartLine(1);
+ defaultIssue.setMessage("msg");
+ defaultIssue.setSeverity("BLOCKER");
+ when(issueCache.all()).thenReturn(Arrays.asList(defaultIssue));
+
+ Issue issue = context.issues().iterator().next();
+ assertThat(issue.componentKey()).isEqualTo("foo:src/Foo.php");
+ assertThat(issue.effortToFix()).isEqualTo(2.0);
+ assertThat(issue.isNew()).isTrue();
+ assertThat(issue.key()).isEqualTo("xyz");
+ assertThat(issue.line()).isEqualTo(1);
+ assertThat(issue.message()).isEqualTo("msg");
+ assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
+ assertThat(issue.inputComponent()).isNull();
+
+ InputFile inputPath = mock(InputFile.class);
+ resourceCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputComponent(inputPath);
+ assertThat(issue.inputComponent()).isEqualTo(inputPath);
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/PostJobOptimizerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/PostJobOptimizerTest.java
new file mode 100644
index 00000000000..e5d30f1dfa3
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/postjob/PostJobOptimizerTest.java
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.postjob;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.postjob.internal.DefaultPostJobDescriptor;
+import org.sonar.api.config.Settings;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class PostJobOptimizerTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private PostJobOptimizer optimizer;
+ private Settings settings;
+ private AnalysisMode analysisMode;
+
+ @Before
+ public void prepare() {
+ settings = new Settings();
+ analysisMode = mock(AnalysisMode.class);
+ optimizer = new PostJobOptimizer(settings, analysisMode);
+ }
+
+ @Test
+ public void should_run_analyzer_with_no_metadata() {
+ DefaultPostJobDescriptor descriptor = new DefaultPostJobDescriptor();
+
+ assertThat(optimizer.shouldExecute(descriptor)).isTrue();
+ }
+
+ @Test
+ public void should_optimize_on_settings() {
+ DefaultPostJobDescriptor descriptor = new DefaultPostJobDescriptor()
+ .requireProperty("sonar.foo.reportPath");
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+
+ settings.setProperty("sonar.foo.reportPath", "foo");
+ assertThat(optimizer.shouldExecute(descriptor)).isTrue();
+ }
+
+ @Test
+ public void should_disabled_in_issues_mode() {
+ DefaultPostJobDescriptor descriptor = new DefaultPostJobDescriptor()
+ .disabledInIssues();
+ assertThat(optimizer.shouldExecute(descriptor)).isTrue();
+
+ when(analysisMode.isIssues()).thenReturn(true);
+
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java
new file mode 100644
index 00000000000..9c8e4ed8d56
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java
@@ -0,0 +1,410 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.profiling;
+
+import com.google.common.collect.Maps;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.Initializer;
+import org.sonar.api.batch.PostJob;
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.events.DecoratorExecutionHandler;
+import org.sonar.api.batch.events.DecoratorsPhaseHandler;
+import org.sonar.api.batch.events.InitializerExecutionHandler;
+import org.sonar.api.batch.events.InitializersPhaseHandler;
+import org.sonar.api.batch.events.PostJobExecutionHandler;
+import org.sonar.api.batch.events.PostJobsPhaseHandler;
+import org.sonar.api.batch.events.ProjectAnalysisHandler;
+import org.sonar.api.batch.events.ProjectAnalysisHandler.ProjectAnalysisEvent;
+import org.sonar.api.batch.events.SensorExecutionHandler;
+import org.sonar.api.batch.events.SensorExecutionHandler.SensorExecutionEvent;
+import org.sonar.api.batch.events.SensorsPhaseHandler;
+import org.sonar.api.batch.events.SensorsPhaseHandler.SensorsPhaseEvent;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.System2;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.sonar.batch.events.BatchStepEvent;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+public class PhasesSumUpTimeProfilerTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private MockedSystem clock;
+ private PhasesSumUpTimeProfiler profiler;
+
+ @Before
+ public void prepare() throws Exception {
+ clock = new MockedSystem();
+ Map<String, String> props = Maps.newHashMap();
+ props.put(CoreProperties.WORKING_DIRECTORY, temp.newFolder().getAbsolutePath());
+ profiler = new PhasesSumUpTimeProfiler(clock, new GlobalProperties(props));
+ }
+
+ @Test
+ public void testSimpleProject() throws InterruptedException {
+
+ final Project project = mockProject("my:project", true);
+ when(project.getModules()).thenReturn(Collections.<Project>emptyList());
+
+ fakeAnalysis(profiler, project);
+
+ assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(7L);
+ assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(10L);
+ assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(30L);
+ assertThat(profiler.currentModuleProfiling.getProfilingPerBatchStep("Free memory").totalTime()).isEqualTo(9L);
+ }
+
+ @Test
+ public void testMultimoduleProject() throws InterruptedException {
+ final Project project = mockProject("project root", true);
+ final Project moduleA = mockProject("moduleA", false);
+ final Project moduleB = mockProject("moduleB", false);
+ when(project.getModules()).thenReturn(Arrays.asList(moduleA, moduleB));
+
+ fakeAnalysis(profiler, moduleA);
+ fakeAnalysis(profiler, moduleB);
+ fakeAnalysis(profiler, project);
+
+ assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(7L);
+ assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(10L);
+ assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(30L);
+
+ assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(21L);
+ assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(30L);
+ assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(90L);
+ }
+
+ @Test
+ public void testDisplayTimings() {
+ AbstractTimeProfiling profiling = new AbstractTimeProfiling(System2.INSTANCE) {
+ };
+
+ profiling.setTotalTime(5);
+ assertThat(profiling.totalTimeAsString()).isEqualTo("5ms");
+
+ profiling.setTotalTime(5 * 1000 + 12);
+ assertThat(profiling.totalTimeAsString()).isEqualTo("5s");
+
+ profiling.setTotalTime(5 * 60 * 1000 + 12 * 1000);
+ assertThat(profiling.totalTimeAsString()).isEqualTo("5min 12s");
+
+ profiling.setTotalTime(5 * 60 * 1000);
+ assertThat(profiling.totalTimeAsString()).isEqualTo("5min");
+ }
+
+ private class MockedSystem extends System2 {
+ private long now = 0;
+
+ @Override
+ public long now() {
+ return now;
+ }
+
+ public void sleep(long duration) {
+ now += duration;
+ }
+ }
+
+ private Project mockProject(String name, boolean isRoot) {
+ final Project project = spy(new Project("myProject"));
+ when(project.isRoot()).thenReturn(isRoot);
+ when(project.getName()).thenReturn(name);
+ return project;
+ }
+
+ private void fakeAnalysis(PhasesSumUpTimeProfiler profiler, final Project module) {
+ // Start of moduleA
+ profiler.onProjectAnalysis(projectEvent(module, true));
+ initializerPhase(profiler);
+ sensorPhase(profiler);
+ postJobPhase(profiler);
+ batchStep(profiler);
+ // End of moduleA
+ profiler.onProjectAnalysis(projectEvent(module, false));
+ }
+
+ private void batchStep(PhasesSumUpTimeProfiler profiler) {
+ // Start of batch step
+ profiler.onBatchStep(new BatchStepEvent("Free memory", true));
+ clock.sleep(9);
+ // End of batch step
+ profiler.onBatchStep(new BatchStepEvent("Free memory", false));
+ }
+
+ private void initializerPhase(PhasesSumUpTimeProfiler profiler) {
+ Initializer initializer = new FakeInitializer();
+ // Start of initializer phase
+ profiler.onInitializersPhase(initializersEvent(true));
+ // Start of an initializer
+ profiler.onInitializerExecution(initializerEvent(initializer, true));
+ clock.sleep(7);
+ // End of an initializer
+ profiler.onInitializerExecution(initializerEvent(initializer, false));
+ // End of initializer phase
+ profiler.onInitializersPhase(initializersEvent(false));
+ }
+
+ private void sensorPhase(PhasesSumUpTimeProfiler profiler) {
+ Sensor sensor = new FakeSensor();
+ // Start of sensor phase
+ profiler.onSensorsPhase(sensorsEvent(true));
+ // Start of a Sensor
+ profiler.onSensorExecution(sensorEvent(sensor, true));
+ clock.sleep(10);
+ // End of a Sensor
+ profiler.onSensorExecution(sensorEvent(sensor, false));
+ // End of sensor phase
+ profiler.onSensorsPhase(sensorsEvent(false));
+ }
+
+ private void postJobPhase(PhasesSumUpTimeProfiler profiler) {
+ PostJob postJob = new FakePostJob();
+ // Start of sensor phase
+ profiler.onPostJobsPhase(postJobsEvent(true));
+ // Start of a Sensor
+ profiler.onPostJobExecution(postJobEvent(postJob, true));
+ clock.sleep(30);
+ // End of a Sensor
+ profiler.onPostJobExecution(postJobEvent(postJob, false));
+ // End of sensor phase
+ profiler.onPostJobsPhase(postJobsEvent(false));
+ }
+
+ private SensorExecutionEvent sensorEvent(final Sensor sensor, final boolean start) {
+ return new SensorExecutionHandler.SensorExecutionEvent() {
+
+ @Override
+ public boolean isStart() {
+ return start;
+ }
+
+ @Override
+ public boolean isEnd() {
+ return !start;
+ }
+
+ @Override
+ public Sensor getSensor() {
+ return sensor;
+ }
+ };
+ }
+
+ private InitializerExecutionHandler.InitializerExecutionEvent initializerEvent(final Initializer initializer, final boolean start) {
+ return new InitializerExecutionHandler.InitializerExecutionEvent() {
+
+ @Override
+ public boolean isStart() {
+ return start;
+ }
+
+ @Override
+ public boolean isEnd() {
+ return !start;
+ }
+
+ @Override
+ public Initializer getInitializer() {
+ return initializer;
+ }
+ };
+ }
+
+ private DecoratorExecutionHandler.DecoratorExecutionEvent decoratorEvent(final Decorator decorator, final boolean start) {
+ return new DecoratorExecutionHandler.DecoratorExecutionEvent() {
+
+ @Override
+ public boolean isStart() {
+ return start;
+ }
+
+ @Override
+ public boolean isEnd() {
+ return !start;
+ }
+
+ @Override
+ public Decorator getDecorator() {
+ return decorator;
+ }
+ };
+ }
+
+ private PostJobExecutionHandler.PostJobExecutionEvent postJobEvent(final PostJob postJob, final boolean start) {
+ return new PostJobExecutionHandler.PostJobExecutionEvent() {
+
+ @Override
+ public boolean isStart() {
+ return start;
+ }
+
+ @Override
+ public boolean isEnd() {
+ return !start;
+ }
+
+ @Override
+ public PostJob getPostJob() {
+ return postJob;
+ }
+ };
+ }
+
+ private SensorsPhaseEvent sensorsEvent(final boolean start) {
+ return new SensorsPhaseHandler.SensorsPhaseEvent() {
+
+ @Override
+ public boolean isStart() {
+ return start;
+ }
+
+ @Override
+ public boolean isEnd() {
+ return !start;
+ }
+
+ @Override
+ public List<Sensor> getSensors() {
+ return null;
+ }
+ };
+ }
+
+ private InitializersPhaseHandler.InitializersPhaseEvent initializersEvent(final boolean start) {
+ return new InitializersPhaseHandler.InitializersPhaseEvent() {
+
+ @Override
+ public boolean isStart() {
+ return start;
+ }
+
+ @Override
+ public boolean isEnd() {
+ return !start;
+ }
+
+ @Override
+ public List<Initializer> getInitializers() {
+ return null;
+ }
+ };
+ }
+
+ private PostJobsPhaseHandler.PostJobsPhaseEvent postJobsEvent(final boolean start) {
+ return new PostJobsPhaseHandler.PostJobsPhaseEvent() {
+
+ @Override
+ public boolean isStart() {
+ return start;
+ }
+
+ @Override
+ public boolean isEnd() {
+ return !start;
+ }
+
+ @Override
+ public List<PostJob> getPostJobs() {
+ return null;
+ }
+ };
+ }
+
+ private DecoratorsPhaseHandler.DecoratorsPhaseEvent decoratorsEvent(final boolean start) {
+ return new DecoratorsPhaseHandler.DecoratorsPhaseEvent() {
+
+ @Override
+ public boolean isStart() {
+ return start;
+ }
+
+ @Override
+ public boolean isEnd() {
+ return !start;
+ }
+
+ @Override
+ public List<Decorator> getDecorators() {
+ return null;
+ }
+ };
+ }
+
+ private ProjectAnalysisEvent projectEvent(final Project project, final boolean start) {
+ return new ProjectAnalysisHandler.ProjectAnalysisEvent() {
+ @Override
+ public boolean isStart() {
+ return start;
+ }
+
+ @Override
+ public boolean isEnd() {
+ return !start;
+ }
+
+ @Override
+ public Project getProject() {
+ return project;
+ }
+ };
+ }
+
+ public class FakeSensor implements Sensor {
+ @Override
+ public void analyse(Project project, SensorContext context) {
+ }
+
+ @Override
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+ }
+
+ public class FakeInitializer extends Initializer {
+ @Override
+ public void execute(Project project) {
+ }
+
+ @Override
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+ }
+
+ public class FakePostJob implements PostJob {
+ @Override
+ public void executeOn(Project project, SensorContext context) {
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ActiveRulesPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ActiveRulesPublisherTest.java
new file mode 100644
index 00000000000..4b912b22d7b
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ActiveRulesPublisherTest.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.report;
+
+import java.io.File;
+import java.util.Arrays;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.Severity;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.batch.rule.internal.DefaultActiveRules;
+import org.sonar.api.batch.rule.internal.NewActiveRule;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.scanner.protocol.Constants;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReportReader;
+import org.sonar.scanner.protocol.output.ScannerReportWriter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ActiveRulesPublisherTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void write() throws Exception {
+ File outputDir = temp.newFolder();
+ ScannerReportWriter writer = new ScannerReportWriter(outputDir);
+
+ NewActiveRule ar = new ActiveRulesBuilder().create(RuleKey.of("java", "S001")).setSeverity("BLOCKER").setParam("p1", "v1");
+ ActiveRules activeRules = new DefaultActiveRules(Arrays.asList(ar));
+
+ ActiveRulesPublisher underTest = new ActiveRulesPublisher(activeRules);
+ underTest.publish(writer);
+
+ ScannerReportReader reader = new ScannerReportReader(outputDir);
+ try (CloseableIterator<ScannerReport.ActiveRule> readIt = reader.readActiveRules()) {
+ ScannerReport.ActiveRule reportAr = readIt.next();
+ assertThat(reportAr.getRuleRepository()).isEqualTo("java");
+ assertThat(reportAr.getRuleKey()).isEqualTo("S001");
+ assertThat(reportAr.getSeverity()).isEqualTo(Constants.Severity.BLOCKER);
+ assertThat(reportAr.getParamCount()).isEqualTo(1);
+ assertThat(reportAr.getParam(0).getKey()).isEqualTo("p1");
+ assertThat(reportAr.getParam(0).getValue()).isEqualTo("v1");
+
+ assertThat(readIt.hasNext()).isFalse();
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java
new file mode 100644
index 00000000000..320941119b3
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/AnalysisContextReportPublisherTest.java
@@ -0,0 +1,207 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.report;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.batch.bootstrap.BatchPluginRepository;
+import org.sonar.batch.repository.ProjectRepositories;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.scanner.protocol.output.ScannerReportWriter;
+import org.sonar.updatecenter.common.Version;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class AnalysisContextReportPublisherTest {
+
+ private static final String BIZ = "BIZ";
+ private static final String FOO = "FOO";
+ private static final String SONAR_SKIP = "sonar.skip";
+ private static final String COM_FOO = "com.foo";
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private BatchPluginRepository pluginRepo = mock(BatchPluginRepository.class);
+ private AnalysisContextReportPublisher publisher;
+ private AnalysisMode analysisMode = mock(AnalysisMode.class);
+ private System2 system2;
+ private ProjectRepositories projectRepos;
+
+ @Before
+ public void prepare() throws Exception {
+ logTester.setLevel(LoggerLevel.INFO);
+ system2 = mock(System2.class);
+ when(system2.properties()).thenReturn(new Properties());
+ projectRepos = mock(ProjectRepositories.class);
+ publisher = new AnalysisContextReportPublisher(analysisMode, pluginRepo, system2, projectRepos);
+ }
+
+ @Test
+ public void shouldOnlyDumpPluginsByDefault() throws Exception {
+ when(pluginRepo.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("xoo").setName("Xoo").setVersion(Version.create("1.0"))));
+
+ ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder());
+ publisher.init(writer);
+
+ assertThat(writer.getFileStructure().analysisLog()).exists();
+ assertThat(FileUtils.readFileToString(writer.getFileStructure().analysisLog())).contains("Xoo 1.0 (xoo)");
+
+ verifyZeroInteractions(system2);
+ }
+
+ @Test
+ public void shouldNotDumpInIssuesMode() throws Exception {
+ when(analysisMode.isIssues()).thenReturn(true);
+
+ ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder());
+ publisher.init(writer);
+ publisher.dumpSettings(ProjectDefinition.create().setProperty("sonar.projectKey", "foo"));
+
+ assertThat(writer.getFileStructure().analysisLog()).doesNotExist();
+ }
+
+ @Test
+ public void dumpServerSideProps() throws Exception {
+ logTester.setLevel(LoggerLevel.DEBUG);
+ ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder());
+ publisher.init(writer);
+
+ when(projectRepos.moduleExists("foo")).thenReturn(true);
+ when(projectRepos.settings("foo")).thenReturn(ImmutableMap.of(COM_FOO, "bar", SONAR_SKIP, "true"));
+
+ publisher.dumpSettings(ProjectDefinition.create()
+ .setProperty("sonar.projectKey", "foo"));
+
+ String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog());
+ assertThat(content).doesNotContain(COM_FOO);
+ assertThat(content).containsOnlyOnce(SONAR_SKIP);
+ }
+
+ @Test
+ public void shouldNotDumpSQPropsInSystemProps() throws Exception {
+ logTester.setLevel(LoggerLevel.DEBUG);
+ ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder());
+ Properties props = new Properties();
+ props.setProperty(COM_FOO, "bar");
+ props.setProperty(SONAR_SKIP, "true");
+ when(system2.properties()).thenReturn(props);
+ publisher.init(writer);
+
+ String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog());
+ assertThat(content).containsOnlyOnce(COM_FOO);
+ assertThat(content).doesNotContain(SONAR_SKIP);
+
+ publisher.dumpSettings(ProjectDefinition.create()
+ .setProperty("sonar.projectKey", "foo")
+ .setProperty(COM_FOO, "bar")
+ .setProperty(SONAR_SKIP, "true"));
+
+ content = FileUtils.readFileToString(writer.getFileStructure().analysisLog());
+ assertThat(content).containsOnlyOnce(COM_FOO);
+ assertThat(content).containsOnlyOnce(SONAR_SKIP);
+ }
+
+ @Test
+ public void shouldNotDumpEnvTwice() throws Exception {
+ logTester.setLevel(LoggerLevel.DEBUG);
+ ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder());
+
+ Map<String, String> env = new HashMap<>();
+ env.put(FOO, "BAR");
+ env.put(BIZ, "BAZ");
+ when(system2.envVariables()).thenReturn(env);
+ publisher.init(writer);
+
+ String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog());
+ assertThat(content).containsOnlyOnce(FOO);
+ assertThat(content).containsOnlyOnce(BIZ);
+ assertThat(content).containsSequence(BIZ, FOO);
+
+ publisher.dumpSettings(ProjectDefinition.create()
+ .setProperty("sonar.projectKey", "foo")
+ .setProperty("env." + FOO, "BAR"));
+
+ content = FileUtils.readFileToString(writer.getFileStructure().analysisLog());
+ assertThat(content).containsOnlyOnce(FOO);
+ assertThat(content).containsOnlyOnce(BIZ);
+ assertThat(content).doesNotContain("env." + FOO);
+ }
+
+ @Test
+ public void shouldNotDumpSensitiveProperties() throws Exception {
+ ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder());
+ publisher.init(writer);
+
+ assertThat(writer.getFileStructure().analysisLog()).exists();
+
+ publisher.dumpSettings(ProjectDefinition.create()
+ .setProperty("sonar.projectKey", "foo")
+ .setProperty("sonar.projectKey", "foo")
+ .setProperty("sonar.password", "azerty")
+ .setProperty("sonar.cpp.license.secured", "AZERTY"));
+
+ assertThat(FileUtils.readFileToString(writer.getFileStructure().analysisLog())).containsSequence(
+ "sonar.cpp.license.secured=******",
+ "sonar.password=******",
+ "sonar.projectKey=foo");
+ }
+
+ // SONAR-7371
+ @Test
+ public void dontDumpParentProps() throws Exception {
+ logTester.setLevel(LoggerLevel.DEBUG);
+ ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder());
+ publisher.init(writer);
+
+ ProjectDefinition module = ProjectDefinition.create()
+ .setProperty("sonar.projectKey", "foo")
+ .setProperty(SONAR_SKIP, "true");
+
+ ProjectDefinition.create()
+ .setProperty("sonar.projectKey", "parent")
+ .setProperty(SONAR_SKIP, "true")
+ .addSubProject(module);
+
+ publisher.dumpSettings(module);
+
+ String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog());
+ assertThat(content).doesNotContain(SONAR_SKIP);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java
new file mode 100644
index 00000000000..0a4ed8d69cd
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java
@@ -0,0 +1,170 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.report;
+
+import java.io.File;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.scan.ImmutableProjectReactor;
+import org.sonar.scanner.protocol.Constants.ComponentLinkType;
+import org.sonar.scanner.protocol.output.ScannerReportReader;
+import org.sonar.scanner.protocol.output.ScannerReportWriter;
+import org.sonar.scanner.protocol.output.FileStructure;
+import org.sonar.scanner.protocol.output.ScannerReport.Component;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ComponentsPublisherTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ BatchComponentCache resourceCache = new BatchComponentCache();
+
+ @Test
+ public void add_components_to_report() throws Exception {
+
+ ProjectDefinition rootDef = ProjectDefinition.create().setKey("foo");
+ rootDef.properties().put(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0");
+ Project root = new Project("foo").setName("Root project").setDescription("Root description")
+ .setAnalysisDate(DateUtils.parseDate(("2012-12-12")));
+ root.setId(1).setUuid("PROJECT_UUID");
+ resourceCache.add(root, null).setInputComponent(new DefaultInputModule("foo"));
+
+ Project module1 = new Project("module1").setName("Module1").setDescription("Module description");
+ module1.setParent(root);
+ module1.setId(2).setUuid("MODULE_UUID");
+ resourceCache.add(module1, root).setInputComponent(new DefaultInputModule("module1"));
+ rootDef.addSubProject(ProjectDefinition.create().setKey("module1"));
+
+ Directory dir = Directory.create("src");
+ dir.setEffectiveKey("module1:src");
+ dir.setId(3).setUuid("DIR_UUID");
+ resourceCache.add(dir, module1).setInputComponent(new DefaultInputDir("foo", "src"));
+
+ org.sonar.api.resources.File file = org.sonar.api.resources.File.create("src/Foo.java", Java.INSTANCE, false);
+ file.setEffectiveKey("module1:src/Foo.java");
+ file.setId(4).setUuid("FILE_UUID");
+ resourceCache.add(file, dir).setInputComponent(new DefaultInputFile("module1", "src/Foo.java").setLines(2));
+
+ org.sonar.api.resources.File fileWithoutLang = org.sonar.api.resources.File.create("src/make", null, false);
+ fileWithoutLang.setEffectiveKey("module1:src/make");
+ fileWithoutLang.setId(5).setUuid("FILE_WITHOUT_LANG_UUID");
+ resourceCache.add(fileWithoutLang, dir).setInputComponent(new DefaultInputFile("module1", "src/make").setLines(10));
+
+ org.sonar.api.resources.File testFile = org.sonar.api.resources.File.create("test/FooTest.java", Java.INSTANCE, true);
+ testFile.setEffectiveKey("module1:test/FooTest.java");
+ testFile.setId(6).setUuid("TEST_FILE_UUID");
+ resourceCache.add(testFile, dir).setInputComponent(new DefaultInputFile("module1", "test/FooTest.java").setLines(4));
+
+ ImmutableProjectReactor reactor = new ImmutableProjectReactor(rootDef);
+
+ ComponentsPublisher publisher = new ComponentsPublisher(reactor, resourceCache);
+
+ File outputDir = temp.newFolder();
+ ScannerReportWriter writer = new ScannerReportWriter(outputDir);
+ publisher.publish(writer);
+
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 1)).isTrue();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 2)).isTrue();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 3)).isTrue();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 4)).isTrue();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 5)).isTrue();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 6)).isTrue();
+
+ // no such reference
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 7)).isFalse();
+
+ ScannerReportReader reader = new ScannerReportReader(outputDir);
+ Component rootProtobuf = reader.readComponent(1);
+ assertThat(rootProtobuf.getKey()).isEqualTo("foo");
+ assertThat(rootProtobuf.getDescription()).isEqualTo("Root description");
+ assertThat(rootProtobuf.getVersion()).isEqualTo("1.0");
+ assertThat(rootProtobuf.getLinkCount()).isEqualTo(0);
+
+ Component module1Protobuf = reader.readComponent(2);
+ assertThat(module1Protobuf.getKey()).isEqualTo("module1");
+ assertThat(module1Protobuf.getDescription()).isEqualTo("Module description");
+ assertThat(module1Protobuf.getVersion()).isEqualTo("1.0");
+ }
+
+ @Test
+ public void add_components_with_links_and_branch() throws Exception {
+ // inputs
+ ProjectDefinition rootDef = ProjectDefinition.create().setKey("foo");
+ rootDef.properties().put(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0");
+ Project root = new Project("foo:my_branch").setName("Root project")
+ .setAnalysisDate(DateUtils.parseDate(("2012-12-12")));
+ root.setId(1).setUuid("PROJECT_UUID");
+ resourceCache.add(root, null).setInputComponent(new DefaultInputModule("foo"));
+ rootDef.properties().put(CoreProperties.LINKS_HOME_PAGE, "http://home");
+ rootDef.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "my_branch");
+
+ Project module1 = new Project("module1:my_branch").setName("Module1");
+ module1.setParent(root);
+ module1.setId(2).setUuid("MODULE_UUID");
+ resourceCache.add(module1, root).setInputComponent(new DefaultInputModule("module1"));
+ ProjectDefinition moduleDef = ProjectDefinition.create().setKey("module1");
+ moduleDef.properties().put(CoreProperties.LINKS_CI, "http://ci");
+ rootDef.addSubProject(moduleDef);
+
+ Directory dir = Directory.create("src");
+ dir.setEffectiveKey("module1:my_branch:my_branch:src");
+ dir.setId(3).setUuid("DIR_UUID");
+ resourceCache.add(dir, module1).setInputComponent(new DefaultInputDir("foo", "src"));
+
+ org.sonar.api.resources.File file = org.sonar.api.resources.File.create("src/Foo.java", Java.INSTANCE, false);
+ file.setEffectiveKey("module1:my_branch:my_branch:src/Foo.java");
+ file.setId(4).setUuid("FILE_UUID");
+ resourceCache.add(file, dir).setInputComponent(new DefaultInputFile("module1", "src/Foo.java").setLines(2));
+
+ ImmutableProjectReactor reactor = new ImmutableProjectReactor(rootDef);
+
+ ComponentsPublisher publisher = new ComponentsPublisher(reactor, resourceCache);
+
+ File outputDir = temp.newFolder();
+ ScannerReportWriter writer = new ScannerReportWriter(outputDir);
+ publisher.publish(writer);
+
+ ScannerReportReader reader = new ScannerReportReader(outputDir);
+ Component rootProtobuf = reader.readComponent(1);
+ assertThat(rootProtobuf.getVersion()).isEqualTo("1.0");
+ assertThat(rootProtobuf.getLinkCount()).isEqualTo(1);
+ assertThat(rootProtobuf.getLink(0).getType()).isEqualTo(ComponentLinkType.HOME);
+ assertThat(rootProtobuf.getLink(0).getHref()).isEqualTo("http://home");
+
+ Component module1Protobuf = reader.readComponent(2);
+ assertThat(module1Protobuf.getVersion()).isEqualTo("1.0");
+ assertThat(module1Protobuf.getLinkCount()).isEqualTo(1);
+ assertThat(module1Protobuf.getLink(0).getType()).isEqualTo(ComponentLinkType.CI);
+ assertThat(module1Protobuf.getLink(0).getHref()).isEqualTo("http://ci");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java
new file mode 100644
index 00000000000..72c31cf4928
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/CoveragePublisherTest.java
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.report;
+
+import java.io.File;
+import java.util.Date;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.scanner.protocol.output.ScannerReportReader;
+import org.sonar.scanner.protocol.output.ScannerReportWriter;
+import org.sonar.scanner.protocol.output.ScannerReport.Coverage;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class CoveragePublisherTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private MeasureCache measureCache;
+ private CoveragePublisher publisher;
+
+ private org.sonar.api.resources.Resource sampleFile;
+
+ @Before
+ public void prepare() {
+ Project p = new Project("foo").setAnalysisDate(new Date(1234567L));
+ BatchComponentCache resourceCache = new BatchComponentCache();
+ sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
+ resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo"));
+ resourceCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", "src/Foo.php").setLines(5));
+ measureCache = mock(MeasureCache.class);
+ when(measureCache.byMetric(anyString(), anyString())).thenReturn(null);
+ publisher = new CoveragePublisher(resourceCache, measureCache);
+ }
+
+ @Test
+ public void publishCoverage() throws Exception {
+
+ Measure utLineHits = new Measure<>(CoreMetrics.COVERAGE_LINE_HITS_DATA).setData("2=1;3=1;5=0;6=3");
+ when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY)).thenReturn(utLineHits);
+
+ Measure conditionsByLine = new Measure<>(CoreMetrics.CONDITIONS_BY_LINE).setData("3=4");
+ when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.CONDITIONS_BY_LINE_KEY)).thenReturn(conditionsByLine);
+
+ Measure coveredConditionsByUts = new Measure<>(CoreMetrics.COVERED_CONDITIONS_BY_LINE).setData("3=2");
+ when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(coveredConditionsByUts);
+
+ Measure itLineHits = new Measure<>(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA).setData("2=0;3=0;5=1");
+ when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY)).thenReturn(itLineHits);
+
+ Measure coveredConditionsByIts = new Measure<>(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE).setData("3=1");
+ when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(coveredConditionsByIts);
+
+ Measure overallCoveredConditions = new Measure<>(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE).setData("3=2");
+ when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(overallCoveredConditions);
+
+ File outputDir = temp.newFolder();
+ ScannerReportWriter writer = new ScannerReportWriter(outputDir);
+
+ publisher.publish(writer);
+
+ try (CloseableIterator<Coverage> it = new ScannerReportReader(outputDir).readComponentCoverage(2)) {
+ assertThat(it.next()).isEqualTo(Coverage.newBuilder()
+ .setLine(2)
+ .setUtHits(true)
+ .setItHits(false)
+ .build());
+ assertThat(it.next()).isEqualTo(Coverage.newBuilder()
+ .setLine(3)
+ .setUtHits(true)
+ .setItHits(false)
+ .setConditions(4)
+ .setUtCoveredConditions(2)
+ .setItCoveredConditions(1)
+ .setOverallCoveredConditions(2)
+ .build());
+ assertThat(it.next()).isEqualTo(Coverage.newBuilder()
+ .setLine(5)
+ .setUtHits(false)
+ .setItHits(true)
+ .build());
+ }
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java
new file mode 100644
index 00000000000..0c7a29dd377
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java
@@ -0,0 +1,113 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.report;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Date;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.core.metric.BatchMetrics;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReportReader;
+import org.sonar.scanner.protocol.output.ScannerReportWriter;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class MeasuresPublisherTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private MeasureCache measureCache;
+ private MeasuresPublisher publisher;
+
+ private org.sonar.api.resources.Resource sampleFile;
+
+ @Before
+ public void prepare() {
+ Project p = new Project("foo").setAnalysisDate(new Date(1234567L));
+ BatchComponentCache resourceCache = new BatchComponentCache();
+ sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
+ resourceCache.add(p, null);
+ resourceCache.add(sampleFile, null);
+ measureCache = mock(MeasureCache.class);
+ when(measureCache.byResource(any(Resource.class))).thenReturn(Collections.<Measure>emptyList());
+ publisher = new MeasuresPublisher(resourceCache, measureCache, new BatchMetrics());
+ }
+
+ @Test
+ public void publishMeasures() throws Exception {
+ Measure measure = new Measure<>(CoreMetrics.LINES_TO_COVER)
+ .setValue(2.0);
+ // String value
+ Measure stringMeasure = new Measure<>(CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION)
+ .setData("foo bar");
+ when(measureCache.byResource(sampleFile)).thenReturn(asList(measure, stringMeasure));
+
+ File outputDir = temp.newFolder();
+ ScannerReportWriter writer = new ScannerReportWriter(outputDir);
+
+ publisher.publish(writer);
+
+ ScannerReportReader reader = new ScannerReportReader(outputDir);
+
+ assertThat(reader.readComponentMeasures(1)).hasSize(0);
+ try (CloseableIterator<ScannerReport.Measure> componentMeasures = reader.readComponentMeasures(2)) {
+ assertThat(componentMeasures).hasSize(2);
+ }
+ }
+
+ @Test
+ public void fail_with_IAE_when_measure_has_no_value() throws Exception {
+ Measure measure = new Measure<>(CoreMetrics.LINES_TO_COVER);
+ when(measureCache.byResource(sampleFile)).thenReturn(Collections.singletonList(measure));
+
+ File outputDir = temp.newFolder();
+ ScannerReportWriter writer = new ScannerReportWriter(outputDir);
+
+ try {
+ publisher.publish(writer);
+ fail();
+ } catch (RuntimeException e) {
+ assertThat(ExceptionUtils.getFullStackTrace(e)).contains("Measure on metric 'lines_to_cover' and component 'foo:src/Foo.php' has no value, but it's not allowed");
+ }
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java
new file mode 100644
index 00000000000..7069845e9cf
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java
@@ -0,0 +1,100 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.report;
+
+import java.io.File;
+import java.util.Date;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.scan.ImmutableProjectReactor;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReportReader;
+import org.sonar.scanner.protocol.output.ScannerReportWriter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MetadataPublisherTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private ProjectDefinition projectDef;
+ private Project project;
+ private MetadataPublisher underTest;
+ private Settings settings;
+
+ @Before
+ public void prepare() {
+ projectDef = ProjectDefinition.create().setKey("foo");
+ project = new Project("foo").setAnalysisDate(new Date(1234567L));
+ BatchComponentCache componentCache = new BatchComponentCache();
+ org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
+ componentCache.add(project, null);
+ componentCache.add(sampleFile, project);
+ settings = new Settings();
+ underTest = new MetadataPublisher(componentCache, new ImmutableProjectReactor(projectDef), settings);
+ }
+
+ @Test
+ public void write_metadata() throws Exception {
+ settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
+ File outputDir = temp.newFolder();
+ ScannerReportWriter writer = new ScannerReportWriter(outputDir);
+
+ underTest.publish(writer);
+
+ ScannerReportReader reader = new ScannerReportReader(outputDir);
+ ScannerReport.Metadata metadata = reader.readMetadata();
+ assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L);
+ assertThat(metadata.getProjectKey()).isEqualTo("foo");
+ assertThat(metadata.getProjectKey()).isEqualTo("foo");
+ assertThat(metadata.getCrossProjectDuplicationActivated()).isTrue();
+ }
+
+ @Test
+ public void write_project_branch() throws Exception {
+ settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
+ settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch");
+ projectDef.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch");
+ project.setKey("foo:myBranch");
+ project.setEffectiveKey("foo:myBranch");
+
+ File outputDir = temp.newFolder();
+ ScannerReportWriter writer = new ScannerReportWriter(outputDir);
+
+ underTest.publish(writer);
+
+ ScannerReportReader reader = new ScannerReportReader(outputDir);
+ ScannerReport.Metadata metadata = reader.readMetadata();
+ assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L);
+ assertThat(metadata.getProjectKey()).isEqualTo("foo");
+ assertThat(metadata.getBranch()).isEqualTo("myBranch");
+ // Cross project duplication disabled on branches
+ assertThat(metadata.getCrossProjectDuplicationActivated()).isFalse();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ReportPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ReportPublisherTest.java
new file mode 100644
index 00000000000..bb54bfb9113
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/ReportPublisherTest.java
@@ -0,0 +1,164 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.report;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.TempFolder;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.bootstrap.BatchWsClient;
+import org.sonar.batch.scan.ImmutableProjectReactor;
+import org.sonar.core.config.CorePropertyDefinitions;
+
+import static org.apache.commons.io.FileUtils.readFileToString;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ReportPublisherTest {
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ DefaultAnalysisMode mode = mock(DefaultAnalysisMode.class);
+ Settings settings = new Settings(new PropertyDefinitions(CorePropertyDefinitions.all()));
+ BatchWsClient wsClient = mock(BatchWsClient.class, Mockito.RETURNS_DEEP_STUBS);
+ ImmutableProjectReactor reactor = mock(ImmutableProjectReactor.class);
+ ProjectDefinition root;
+ AnalysisContextReportPublisher contextPublisher = mock(AnalysisContextReportPublisher.class);
+
+ @Before
+ public void setUp() {
+ root = ProjectDefinition.create().setKey("struts").setWorkDir(temp.getRoot());
+ when(reactor.getRoot()).thenReturn(root);
+ when(wsClient.baseUrl()).thenReturn("https://localhost/");
+ }
+
+ @Test
+ public void log_and_dump_information_about_report_uploading() throws IOException {
+ ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+ underTest.logSuccess("TASK-123");
+
+ assertThat(logTester.logs(LoggerLevel.INFO))
+ .contains("ANALYSIS SUCCESSFUL, you can browse https://localhost/dashboard/index/struts")
+ .contains("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report")
+ .contains("More about the report processing at https://localhost/api/ce/task?id=TASK-123");
+
+ File detailsFile = new File(temp.getRoot(), "report-task.txt");
+ assertThat(readFileToString(detailsFile)).isEqualTo(
+ "projectKey=struts\n" +
+ "serverUrl=https://localhost\n" +
+ "dashboardUrl=https://localhost/dashboard/index/struts\n" +
+ "ceTaskId=TASK-123\n" +
+ "ceTaskUrl=https://localhost/api/ce/task?id=TASK-123\n"
+ );
+ }
+
+ @Test
+ public void log_public_url_if_defined() throws IOException {
+ settings.setProperty(CoreProperties.SERVER_BASE_URL, "https://publicserver/sonarqube");
+ ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+ underTest.logSuccess("TASK-123");
+
+ assertThat(logTester.logs(LoggerLevel.INFO))
+ .contains("ANALYSIS SUCCESSFUL, you can browse https://publicserver/sonarqube/dashboard/index/struts")
+ .contains("More about the report processing at https://publicserver/sonarqube/api/ce/task?id=TASK-123");
+
+ File detailsFile = new File(temp.getRoot(), "report-task.txt");
+ assertThat(readFileToString(detailsFile)).isEqualTo(
+ "projectKey=struts\n" +
+ "serverUrl=https://publicserver/sonarqube\n" +
+ "dashboardUrl=https://publicserver/sonarqube/dashboard/index/struts\n" +
+ "ceTaskId=TASK-123\n" +
+ "ceTaskUrl=https://publicserver/sonarqube/api/ce/task?id=TASK-123\n"
+ );
+ }
+
+ @Test
+ public void fail_if_public_url_malformed() throws IOException {
+ settings.setProperty(CoreProperties.SERVER_BASE_URL, "invalid");
+ ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("Failed to parse public URL set in SonarQube server: invalid");
+ underTest.start();
+ }
+
+ @Test
+ public void log_but_not_dump_information_when_report_is_not_uploaded() {
+ ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+ underTest.logSuccess(/* report not uploaded, no server task */null);
+
+ assertThat(logTester.logs(LoggerLevel.INFO))
+ .contains("ANALYSIS SUCCESSFUL")
+ .doesNotContain("dashboard/index");
+
+ File detailsFile = new File(temp.getRoot(), ReportPublisher.METADATA_DUMP_FILENAME);
+ assertThat(detailsFile).doesNotExist();
+ }
+
+ @Test
+ public void should_not_delete_report_if_property_is_set() throws IOException {
+ settings.setProperty("sonar.batch.keepReport", true);
+ Path reportDir = temp.getRoot().toPath().resolve("batch-report");
+ Files.createDirectory(reportDir);
+ ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+ underTest.start();
+ underTest.stop();
+ assertThat(reportDir).isDirectory();
+ }
+
+ @Test
+ public void should_delete_report_by_default() throws IOException {
+ Path reportDir = temp.getRoot().toPath().resolve("batch-report");
+ Files.createDirectory(reportDir);
+ ReportPublisher job = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+ job.start();
+ job.stop();
+ assertThat(reportDir).doesNotExist();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/SourcePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/SourcePublisherTest.java
new file mode 100644
index 00000000000..683d6fecb8a
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/SourcePublisherTest.java
@@ -0,0 +1,119 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.report;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.scanner.protocol.output.ScannerReportWriter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SourcePublisherTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private SourcePublisher publisher;
+
+ private File sourceFile;
+
+ private ScannerReportWriter writer;
+
+ private org.sonar.api.resources.File sampleFile;
+
+ @Before
+ public void prepare() throws IOException {
+ Project p = new Project("foo").setAnalysisDate(new Date(1234567L));
+ BatchComponentCache resourceCache = new BatchComponentCache();
+ sampleFile = org.sonar.api.resources.File.create("src/Foo.php");
+ sampleFile.setEffectiveKey("foo:src/Foo.php");
+ resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo"));
+ File baseDir = temp.newFolder();
+ sourceFile = new File(baseDir, "src/Foo.php");
+ resourceCache.add(sampleFile, null).setInputComponent(
+ new DefaultInputFile("foo", "src/Foo.php").setLines(5).setModuleBaseDir(baseDir.toPath()).setCharset(StandardCharsets.ISO_8859_1));
+ publisher = new SourcePublisher(resourceCache);
+ File outputDir = temp.newFolder();
+ writer = new ScannerReportWriter(outputDir);
+ }
+
+ @Test
+ public void publishEmptySource() throws Exception {
+ FileUtils.write(sourceFile, "", StandardCharsets.ISO_8859_1);
+
+ publisher.publish(writer);
+
+ File out = writer.getSourceFile(2);
+ assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo("");
+ }
+
+ @Test
+ public void publishSourceWithLastEmptyLine() throws Exception {
+ FileUtils.write(sourceFile, "1\n2\n3\n4\n", StandardCharsets.ISO_8859_1);
+
+ publisher.publish(writer);
+
+ File out = writer.getSourceFile(2);
+ assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo("1\n2\n3\n4\n");
+ }
+
+ @Test
+ public void publishTestSource() throws Exception {
+ FileUtils.write(sourceFile, "1\n2\n3\n4\n", StandardCharsets.ISO_8859_1);
+ sampleFile.setQualifier(Qualifiers.UNIT_TEST_FILE);
+
+ publisher.publish(writer);
+
+ File out = writer.getSourceFile(2);
+ assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo("1\n2\n3\n4\n");
+ }
+
+ @Test
+ public void publishSourceWithLastLineNotEmpty() throws Exception {
+ FileUtils.write(sourceFile, "1\n2\n3\n4\n5", StandardCharsets.ISO_8859_1);
+
+ publisher.publish(writer);
+
+ File out = writer.getSourceFile(2);
+ assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo("1\n2\n3\n4\n5");
+ }
+
+ @Test
+ public void cleanLineEnds() throws Exception {
+ FileUtils.write(sourceFile, "\n2\r\n3\n4\r5", StandardCharsets.ISO_8859_1);
+
+ publisher.publish(writer);
+
+ File out = writer.getSourceFile(2);
+ assertThat(FileUtils.readFileToString(out, StandardCharsets.UTF_8)).isEqualTo("\n2\n3\n4\n5");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoaderTest.java
new file mode 100644
index 00000000000..cb3781e5103
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoaderTest.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.repository;
+
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.batch.cache.WSLoader;
+import org.sonar.batch.cache.WSLoaderResult;
+import org.sonar.scanner.protocol.input.GlobalRepositories;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class DefaultGlobalRepositoriesLoaderTest {
+ private static final String BATCH_GLOBAL_URL = "/batch/global";
+ private WSLoader wsLoader;
+ private WSLoaderResult<String> result;
+ private DefaultGlobalRepositoriesLoader globalRepositoryLoader;
+
+ @Before
+ public void setUp() {
+ wsLoader = mock(WSLoader.class);
+ result = new WSLoaderResult<>(new GlobalRepositories().toJson(), true);
+ when(wsLoader.loadString(BATCH_GLOBAL_URL)).thenReturn(result);
+
+ globalRepositoryLoader = new DefaultGlobalRepositoriesLoader(wsLoader);
+ }
+
+ @Test
+ public void test() {
+ MutableBoolean fromCache = new MutableBoolean();
+ globalRepositoryLoader.load(fromCache);
+
+ assertThat(fromCache.booleanValue()).isTrue();
+ verify(wsLoader).loadString(BATCH_GLOBAL_URL);
+ verifyNoMoreInteractions(wsLoader);
+ }
+
+ @Test
+ public void testFromServer() {
+ result = new WSLoaderResult<>(new GlobalRepositories().toJson(), false);
+ when(wsLoader.loadString(BATCH_GLOBAL_URL)).thenReturn(result);
+ MutableBoolean fromCache = new MutableBoolean();
+ globalRepositoryLoader.load(fromCache);
+
+ assertThat(fromCache.booleanValue()).isFalse();
+ verify(wsLoader).loadString(BATCH_GLOBAL_URL);
+ verifyNoMoreInteractions(wsLoader);
+ }
+
+ public void testWithoutArg() {
+ globalRepositoryLoader.load(null);
+
+ verify(wsLoader).loadString(BATCH_GLOBAL_URL);
+ verifyNoMoreInteractions(wsLoader);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java
new file mode 100644
index 00000000000..d633820ca83
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java
@@ -0,0 +1,144 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.repository;
+
+import com.google.common.io.Resources;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.cache.WSLoader;
+import org.sonar.batch.cache.WSLoaderResult;
+import org.sonarqube.ws.WsBatch.WsProjectResponse;
+import org.sonarqube.ws.client.HttpException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DefaultProjectRepositoriesLoaderTest {
+ private final static String PROJECT_KEY = "foo?";
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private DefaultProjectRepositoriesLoader loader;
+ private WSLoader wsLoader;
+
+ @Before
+ public void prepare() throws IOException {
+ wsLoader = mock(WSLoader.class);
+ InputStream is = mockData();
+ when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true));
+ loader = new DefaultProjectRepositoriesLoader(wsLoader);
+ }
+
+ @Test
+ public void continueOnError() {
+ when(wsLoader.loadStream(anyString())).thenThrow(IllegalStateException.class);
+ ProjectRepositories proj = loader.load(PROJECT_KEY, false, null);
+ assertThat(proj.exists()).isEqualTo(false);
+ }
+
+ @Test
+ public void parsingError() throws IOException {
+ InputStream is = mock(InputStream.class);
+ when(is.read()).thenThrow(IOException.class);
+
+ when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, false));
+ loader.load(PROJECT_KEY, false, null);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void failFastHttpError() {
+ HttpException http = new HttpException("url", 403);
+ IllegalStateException e = new IllegalStateException("http error", http);
+ when(wsLoader.loadStream(anyString())).thenThrow(e);
+ loader.load(PROJECT_KEY, false, null);
+ }
+
+ @Test
+ public void failFastHttpErrorMessageException() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("http error");
+
+ HttpException http = new HttpException("uri", 403);
+ MessageException e = MessageException.of("http error", http);
+ when(wsLoader.loadStream(anyString())).thenThrow(e);
+ loader.load(PROJECT_KEY, false, null);
+ }
+
+ @Test
+ public void passIssuesModeParameter() {
+ loader.load(PROJECT_KEY, false, null);
+ verify(wsLoader).loadStream("/batch/project.protobuf?key=foo%3F");
+
+ loader.load(PROJECT_KEY, true, null);
+ verify(wsLoader).loadStream("/batch/project.protobuf?key=foo%3F&issues_mode=true");
+ }
+
+ @Test
+ public void deserializeResponse() throws IOException {
+ MutableBoolean fromCache = new MutableBoolean();
+ loader.load(PROJECT_KEY, false, fromCache);
+ assertThat(fromCache.booleanValue()).isTrue();
+ }
+
+ @Test
+ public void passAndEncodeProjectKeyParameter() {
+ loader.load(PROJECT_KEY, false, null);
+ verify(wsLoader).loadStream("/batch/project.protobuf?key=foo%3F");
+ }
+
+ private InputStream mockData() throws IOException {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ WsProjectResponse.Builder projectResponseBuilder = WsProjectResponse.newBuilder();
+ WsProjectResponse response = projectResponseBuilder.build();
+ response.writeTo(os);
+
+ return new ByteArrayInputStream(os.toByteArray());
+ }
+
+ @Test
+ public void readRealResponse() throws IOException {
+ InputStream is = getTestResource("project.protobuf");
+ when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true));
+
+ ProjectRepositories proj = loader.load("org.sonarsource.github:sonar-github-plugin", true, null);
+ FileData fd = proj.fileData("org.sonarsource.github:sonar-github-plugin",
+ "src/test/java/org/sonar/plugins/github/PullRequestIssuePostJobTest.java");
+
+ assertThat(fd.revision()).isEqualTo("27bf2c54633d05c5df402bbe09471fe43bd9e2e5");
+ assertThat(fd.hash()).isEqualTo("edb6b3b9ab92d8dc53ba90ab86cd422e");
+ }
+
+ private InputStream getTestResource(String name) throws IOException {
+ return Resources.asByteSource(this.getClass().getResource(this.getClass().getSimpleName() + "/" + name))
+ .openBufferedStream();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java
new file mode 100644
index 00000000000..d3b4ce6b87d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.repository;
+
+import org.sonar.api.utils.MessageException;
+
+import org.sonarqube.ws.QualityProfiles;
+import com.google.common.io.Resources;
+import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
+import org.sonar.batch.cache.WSLoaderResult;
+import org.sonar.batch.cache.WSLoader;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultQualityProfileLoaderTest {
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ private DefaultQualityProfileLoader qpLoader;
+ private WSLoader ws;
+ private InputStream is;
+
+ @Before
+ public void setUp() throws IOException {
+ ws = mock(WSLoader.class);
+ is = mock(InputStream.class);
+ when(is.read()).thenReturn(-1);
+ WSLoaderResult<InputStream> result = new WSLoaderResult<>(is, false);
+ when(ws.loadStream(anyString())).thenReturn(result);
+ qpLoader = new DefaultQualityProfileLoader(ws);
+ }
+
+ @Test
+ public void testEncoding() throws IOException {
+ WSLoaderResult<InputStream> result = new WSLoaderResult<>(createEncodedQP("qp"), false);
+ when(ws.loadStream(anyString())).thenReturn(result);
+
+ List<QualityProfile> loaded = qpLoader.load("foo#2", "my-profile#2", null);
+ verify(ws).loadStream("/api/qualityprofiles/search.protobuf?projectKey=foo%232&profileName=my-profile%232");
+ verifyNoMoreInteractions(ws);
+ assertThat(loaded).hasSize(1);
+ }
+
+ @Test
+ public void testNoProfile() throws IOException {
+ InputStream is = createEncodedQP();
+ when(ws.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, false));
+
+ exception.expect(MessageException.class);
+ exception.expectMessage("No quality profiles");
+
+ qpLoader.load("project", null, null);
+ verifyNoMoreInteractions(ws);
+ }
+
+ @Test
+ public void use_real_response() throws IOException {
+ InputStream is = getTestResource("quality_profile_search_default");
+ when(ws.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, false));
+
+ List<QualityProfile> loaded = qpLoader.loadDefault(null, null);
+ verify(ws).loadStream("/api/qualityprofiles/search.protobuf?defaults=true");
+ verifyNoMoreInteractions(ws);
+ assertThat(loaded).hasSize(1);
+ }
+
+ private InputStream getTestResource(String name) throws IOException {
+ return Resources.asByteSource(this.getClass().getResource(this.getClass().getSimpleName() + "/" + name))
+ .openBufferedStream();
+ }
+
+ private static InputStream createEncodedQP(String... names) throws IOException {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ QualityProfiles.SearchWsResponse.Builder responseBuilder = QualityProfiles.SearchWsResponse.newBuilder();
+
+ for (String n : names) {
+ QualityProfile qp = QualityProfile.newBuilder().setKey(n).setName(n).setLanguage("lang").build();
+ responseBuilder.addProfiles(qp);
+ }
+
+ responseBuilder.build().writeTo(os);
+ return new ByteArrayInputStream(os.toByteArray());
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java
new file mode 100644
index 00000000000..22416368f3f
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.repository;
+
+import org.sonar.batch.cache.WSLoaderResult;
+import org.sonar.scanner.protocol.input.ScannerInput;
+import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue;
+import org.sonar.batch.cache.WSLoader;
+import com.google.common.base.Function;
+import org.junit.Before;
+import org.junit.Test;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultServerIssuesLoaderTest {
+ private DefaultServerIssuesLoader loader;
+ private WSLoader wsLoader;
+
+ @Before
+ public void prepare() {
+ wsLoader = mock(WSLoader.class);
+ loader = new DefaultServerIssuesLoader(wsLoader);
+ }
+
+ @Test
+ public void loadFromWs() throws Exception {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ ServerIssue.newBuilder().setKey("ab1").build()
+ .writeDelimitedTo(bos);
+ ServerIssue.newBuilder().setKey("ab2").build()
+ .writeDelimitedTo(bos);
+
+ InputStream is = new ByteArrayInputStream(bos.toByteArray());
+ when(wsLoader.loadStream("/batch/issues.protobuf?key=foo")).thenReturn(new WSLoaderResult<>(is, true));
+
+ final List<ServerIssue> result = new ArrayList<>();
+ loader.load("foo", new Function<ScannerInput.ServerIssue, Void>() {
+
+ @Override
+ public Void apply(ServerIssue input) {
+ result.add(input);
+ return null;
+ }
+ });
+
+ assertThat(result).extracting("key").containsExactly("ab1", "ab2");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testError() throws IOException {
+ InputStream is = mock(InputStream.class);
+ when(is.read()).thenThrow(IOException.class);
+ when(wsLoader.loadStream("/batch/issues.protobuf?key=foo")).thenReturn(new WSLoaderResult<>(is, true));
+ loader.load("foo", mock(Function.class));
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/ProjectRepositoriesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/ProjectRepositoriesProviderTest.java
new file mode 100644
index 00000000000..781b48a2a59
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/ProjectRepositoriesProviderTest.java
@@ -0,0 +1,115 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.repository;
+
+import java.util.Date;
+
+import org.sonar.batch.repository.FileData;
+import com.google.common.collect.Table;
+import com.google.common.collect.HashBasedTable;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.batch.bootstrap.ProjectKey;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class ProjectRepositoriesProviderTest {
+ private ProjectRepositoriesProvider provider;
+ private ProjectRepositories project;
+
+ @Mock
+ private ProjectRepositoriesLoader loader;
+ @Mock
+ private ProjectKey projectKey;
+ @Mock
+ private DefaultAnalysisMode mode;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Table<String, String, String> t1 = HashBasedTable.create();
+ Table<String, String, FileData> t2 = HashBasedTable.create();
+
+ project = new ProjectRepositories(t1, t2, new Date());
+ provider = new ProjectRepositoriesProvider();
+
+ when(projectKey.get()).thenReturn("key");
+ }
+
+ @Test
+ public void testNonAssociated() {
+ when(mode.isNotAssociated()).thenReturn(true);
+ ProjectRepositories repo = provider.provide(loader, projectKey, mode);
+
+ assertThat(repo.exists()).isEqualTo(false);
+ verify(mode).isNotAssociated();
+ verifyNoMoreInteractions(loader, projectKey, mode);
+ }
+
+ @Test
+ public void singleton() {
+ when(mode.isNotAssociated()).thenReturn(true);
+ ProjectRepositories repo = provider.provide(loader, projectKey, mode);
+
+ assertThat(repo.exists()).isEqualTo(false);
+ verify(mode).isNotAssociated();
+ verifyNoMoreInteractions(loader, projectKey, mode);
+
+ repo = provider.provide(loader, projectKey, mode);
+ verifyNoMoreInteractions(loader, projectKey, mode);
+ }
+
+ @Test
+ public void testValidation() {
+ when(mode.isNotAssociated()).thenReturn(false);
+ when(mode.isIssues()).thenReturn(true);
+ when(loader.load(eq("key"), eq(true), any(MutableBoolean.class))).thenReturn(project);
+
+ provider.provide(loader, projectKey, mode);
+ }
+
+ @Test
+ public void testAssociated() {
+ when(mode.isNotAssociated()).thenReturn(false);
+ when(mode.isIssues()).thenReturn(false);
+ when(loader.load(eq("key"), eq(false), any(MutableBoolean.class))).thenReturn(project);
+
+ ProjectRepositories repo = provider.provide(loader, projectKey, mode);
+
+ assertThat(repo.exists()).isEqualTo(true);
+ assertThat(repo.lastAnalysisDate()).isNotNull();
+
+ verify(mode).isNotAssociated();
+ verify(mode, times(2)).isIssues();
+ verify(projectKey).get();
+ verify(loader).load(eq("key"), eq(false), any(MutableBoolean.class));
+ verifyNoMoreInteractions(loader, projectKey, mode);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java
new file mode 100644
index 00000000000..0cd1683b9c9
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java
@@ -0,0 +1,165 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.repository;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.batch.bootstrap.ProjectKey;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.batch.analysis.AnalysisProperties;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class QualityProfileProviderTest {
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private QualityProfileProvider qualityProfileProvider;
+
+ @Mock
+ private QualityProfileLoader loader;
+ @Mock
+ private DefaultAnalysisMode mode;
+ @Mock
+ private AnalysisProperties props;
+ @Mock
+ private ProjectKey key;
+ @Mock
+ private ProjectRepositories projectRepo;
+
+ private List<QualityProfile> response;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ qualityProfileProvider = new QualityProfileProvider();
+
+ when(key.get()).thenReturn("project");
+ when(projectRepo.exists()).thenReturn(true);
+
+ response = new ArrayList<>(1);
+ response.add(QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").build());
+ }
+
+ @Test
+ public void testProvide() {
+ when(mode.isNotAssociated()).thenReturn(false);
+ when(loader.load(eq("project"), isNull(String.class), any(MutableBoolean.class))).thenReturn(response);
+ ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode);
+ assertResponse(qps);
+
+ verify(loader).load(eq("project"), isNull(String.class), any(MutableBoolean.class));
+ verifyNoMoreInteractions(loader);
+ }
+
+ @Test
+ public void testNonAssociated() {
+ when(mode.isNotAssociated()).thenReturn(true);
+ when(loader.loadDefault(anyString(), any(MutableBoolean.class))).thenReturn(response);
+ ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode);
+ assertResponse(qps);
+
+ verify(loader).loadDefault(anyString(), any(MutableBoolean.class));
+ verifyNoMoreInteractions(loader);
+ }
+
+ @Test
+ public void testProjectDoesntExist() {
+ when(mode.isNotAssociated()).thenReturn(false);
+ when(projectRepo.exists()).thenReturn(false);
+ when(loader.loadDefault(anyString(), any(MutableBoolean.class))).thenReturn(response);
+ ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode);
+ assertResponse(qps);
+
+ verify(loader).loadDefault(anyString(), any(MutableBoolean.class));
+ verifyNoMoreInteractions(loader);
+ }
+
+ @Test
+ public void testProfileProp() {
+ when(mode.isNotAssociated()).thenReturn(false);
+ when(loader.load(eq("project"), eq("custom"), any(MutableBoolean.class))).thenReturn(response);
+ when(props.property(ModuleQProfiles.SONAR_PROFILE_PROP)).thenReturn("custom");
+ when(props.properties()).thenReturn(ImmutableMap.of(ModuleQProfiles.SONAR_PROFILE_PROP, "custom"));
+
+ ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode);
+ assertResponse(qps);
+
+ verify(loader).load(eq("project"), eq("custom"), any(MutableBoolean.class));
+ verifyNoMoreInteractions(loader);
+ assertThat(logTester.logs(LoggerLevel.WARN)).contains("Ability to set quality profile from command line using '" + ModuleQProfiles.SONAR_PROFILE_PROP
+ + "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server.");
+ }
+
+ @Test
+ public void testIgnoreSonarProfileIssuesMode() {
+ when(mode.isNotAssociated()).thenReturn(false);
+ when(mode.isIssues()).thenReturn(true);
+ when(loader.load(eq("project"), (String) eq(null), any(MutableBoolean.class))).thenReturn(response);
+ when(props.property(ModuleQProfiles.SONAR_PROFILE_PROP)).thenReturn("custom");
+
+ ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode);
+ assertResponse(qps);
+
+ verify(loader).load(eq("project"), (String) eq(null), any(MutableBoolean.class));
+ verifyNoMoreInteractions(loader);
+ }
+
+ @Test
+ public void testProfilePropDefault() {
+ when(mode.isNotAssociated()).thenReturn(true);
+ when(loader.loadDefault(eq("custom"), any(MutableBoolean.class))).thenReturn(response);
+ when(props.property(ModuleQProfiles.SONAR_PROFILE_PROP)).thenReturn("custom");
+ when(props.properties()).thenReturn(ImmutableMap.of(ModuleQProfiles.SONAR_PROFILE_PROP, "custom"));
+
+ ModuleQProfiles qps = qualityProfileProvider.provide(key, loader, projectRepo, props, mode);
+ assertResponse(qps);
+
+ verify(loader).loadDefault(eq("custom"), any(MutableBoolean.class));
+ verifyNoMoreInteractions(loader);
+ assertThat(logTester.logs(LoggerLevel.WARN)).contains("Ability to set quality profile from command line using '" + ModuleQProfiles.SONAR_PROFILE_PROP
+ + "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server.");
+ }
+
+ private void assertResponse(ModuleQProfiles qps) {
+ assertThat(qps.findAll()).hasSize(1);
+ assertThat(qps.findAll()).extracting("key").containsExactly("profile");
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/user/UserRepositoryLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/user/UserRepositoryLoaderTest.java
new file mode 100644
index 00000000000..870afd5737e
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/repository/user/UserRepositoryLoaderTest.java
@@ -0,0 +1,119 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.repository.user;
+
+import org.assertj.core.util.Lists;
+
+import org.sonar.batch.cache.WSLoaderResult;
+import org.sonar.scanner.protocol.input.ScannerInput;
+import org.sonar.batch.cache.WSLoader;
+import org.junit.Before;
+import com.google.common.collect.ImmutableList;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import com.google.common.collect.ImmutableMap;
+import org.junit.rules.ExpectedException;
+import org.junit.Rule;
+import org.mockito.Mockito;
+import org.junit.Test;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Map;
+
+import static org.mockito.Matchers.anyString;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class UserRepositoryLoaderTest {
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
+
+ private WSLoader wsLoader;
+ private UserRepositoryLoader userRepo;
+
+ @Before
+ public void setUp() {
+ wsLoader = mock(WSLoader.class);
+ userRepo = new UserRepositoryLoader(wsLoader);
+ }
+
+ @Test
+ public void testLoadEmptyList() {
+ assertThat(userRepo.load(Lists.<String>emptyList())).isEmpty();
+ }
+
+ @Test
+ public void testLoad() throws IOException {
+ Map<String, String> userMap = ImmutableMap.of("fmallet", "Freddy Mallet", "sbrandhof", "Simon");
+ WSLoaderResult<InputStream> res = new WSLoaderResult<>(createUsersMock(userMap), true);
+ when(wsLoader.loadStream("/batch/users?logins=fmallet,sbrandhof")).thenReturn(res);
+
+ assertThat(userRepo.load(Arrays.asList("fmallet", "sbrandhof"))).extracting("login", "name").containsOnly(tuple("fmallet", "Freddy Mallet"), tuple("sbrandhof", "Simon"));
+ }
+
+ @Test
+ public void testFromCache() throws IOException {
+ WSLoaderResult<InputStream> res = new WSLoaderResult<>(createUsersMock(ImmutableMap.of("fmallet", "Freddy Mallet")), true);
+ when(wsLoader.loadStream(anyString())).thenReturn(res);
+ MutableBoolean fromCache = new MutableBoolean();
+ userRepo.load("", fromCache);
+ assertThat(fromCache.booleanValue()).isTrue();
+
+ fromCache.setValue(false);
+ userRepo.load(ImmutableList.of("user"), fromCache);
+ assertThat(fromCache.booleanValue()).isTrue();
+ }
+
+ @Test
+ public void testLoadSingleUser() throws IOException {
+ WSLoaderResult<InputStream> res = new WSLoaderResult<>(createUsersMock(ImmutableMap.of("fmallet", "Freddy Mallet")), true);
+ when(wsLoader.loadStream("/batch/users?logins=fmallet")).thenReturn(res);
+
+ assertThat(userRepo.load("fmallet").getName()).isEqualTo("Freddy Mallet");
+ }
+
+ private InputStream createUsersMock(Map<String, String> users) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ for (Map.Entry<String, String> user : users.entrySet()) {
+ ScannerInput.User.Builder builder = ScannerInput.User.newBuilder();
+ builder.setLogin(user.getKey()).setName(user.getValue()).build().writeDelimitedTo(out);
+ }
+ return new ByteArrayInputStream(out.toByteArray());
+ }
+
+ @Test
+ public void testInputStreamError() throws IOException {
+ InputStream is = mock(InputStream.class);
+ Mockito.doThrow(IOException.class).when(is).read();
+ WSLoaderResult<InputStream> res = new WSLoaderResult<>(is, true);
+
+ when(wsLoader.loadStream("/batch/users?logins=fmallet,sbrandhof")).thenReturn(res);
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Unable to get user details from server");
+
+ userRepo.load(Arrays.asList("fmallet", "sbrandhof"));
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java
new file mode 100644
index 00000000000..1e1b1f0fc3c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java
@@ -0,0 +1,97 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.rule;
+
+import com.google.common.collect.ImmutableList;
+import java.util.LinkedList;
+import java.util.List;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.rule.RuleKey;
+import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class ActiveRulesProviderTest {
+ private ActiveRulesProvider provider;
+
+ @Mock
+ private DefaultActiveRulesLoader loader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ provider = new ActiveRulesProvider();
+ }
+
+ @Test
+ public void testCombinationOfRules() {
+ LoadedActiveRule r1 = mockRule("rule1");
+ LoadedActiveRule r2 = mockRule("rule2");
+ LoadedActiveRule r3 = mockRule("rule3");
+
+ List<LoadedActiveRule> qp1Rules = ImmutableList.of(r1, r2);
+ List<LoadedActiveRule> qp2Rules = ImmutableList.of(r2, r3);
+ List<LoadedActiveRule> qp3Rules = ImmutableList.of(r1, r3);
+
+ when(loader.load(eq("qp1"), any(MutableBoolean.class))).thenReturn(qp1Rules);
+ when(loader.load(eq("qp2"), any(MutableBoolean.class))).thenReturn(qp2Rules);
+ when(loader.load(eq("qp3"), any(MutableBoolean.class))).thenReturn(qp3Rules);
+
+ ModuleQProfiles profiles = mockProfiles("qp1", "qp2", "qp3");
+ ActiveRules activeRules = provider.provide(loader, profiles);
+
+ assertThat(activeRules.findAll()).hasSize(3);
+ assertThat(activeRules.findAll()).extracting("ruleKey").containsOnly(
+ RuleKey.of("rule1", "rule1"), RuleKey.of("rule2", "rule2"), RuleKey.of("rule3", "rule3"));
+
+ verify(loader).load(eq("qp1"), any(MutableBoolean.class));
+ verify(loader).load(eq("qp2"), any(MutableBoolean.class));
+ verify(loader).load(eq("qp3"), any(MutableBoolean.class));
+ verifyNoMoreInteractions(loader);
+ }
+
+ private static ModuleQProfiles mockProfiles(String... keys) {
+ List<QualityProfile> profiles = new LinkedList<>();
+
+ for (String k : keys) {
+ QualityProfile p = QualityProfile.newBuilder().setKey(k).setLanguage(k).build();
+ profiles.add(p);
+ }
+
+ return new ModuleQProfiles(profiles);
+ }
+
+ private static LoadedActiveRule mockRule(String name) {
+ LoadedActiveRule r = new LoadedActiveRule();
+ r.setName(name);
+ r.setRuleKey(RuleKey.of(name, name));
+ return r;
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java
new file mode 100644
index 00000000000..434c84dda93
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java
@@ -0,0 +1,85 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.rule;
+
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.cache.WSLoaderResult;
+import org.sonar.batch.cache.WSLoader;
+import com.google.common.io.Resources;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+
+import static org.mockito.Mockito.verify;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import org.junit.Before;
+
+public class DefaultActiveRulesLoaderTest {
+ private DefaultActiveRulesLoader loader;
+ private WSLoader ws;
+
+ @Before
+ public void setUp() {
+ ws = mock(WSLoader.class);
+ loader = new DefaultActiveRulesLoader(ws);
+ }
+
+ @Test
+ public void feed_real_response_encode_qp() throws IOException {
+ InputStream response1 = loadResource("active_rule_search1.protobuf");
+ InputStream response2 = loadResource("active_rule_search2.protobuf");
+
+ String req1 = "/api/rules/search.protobuf?f=repo,name,severity,lang,internalKey,templateKey,params,actives&activation=true&qprofile=c%2B-test_c%2B-values-17445&p=1&ps=500";
+ String req2 = "/api/rules/search.protobuf?f=repo,name,severity,lang,internalKey,templateKey,params,actives&activation=true&qprofile=c%2B-test_c%2B-values-17445&p=2&ps=500";
+ when(ws.loadStream(req1)).thenReturn(new WSLoaderResult<>(response1, false));
+ when(ws.loadStream(req2)).thenReturn(new WSLoaderResult<>(response2, false));
+
+ Collection<LoadedActiveRule> activeRules = loader.load("c+-test_c+-values-17445", null);
+ assertThat(activeRules).hasSize(226);
+ assertActiveRule(activeRules);
+
+ verify(ws).loadStream(req1);
+ verify(ws).loadStream(req2);
+ verifyNoMoreInteractions(ws);
+ }
+
+ private static void assertActiveRule(Collection<LoadedActiveRule> activeRules) {
+ RuleKey key = RuleKey.of("squid", "S3008");
+ for (LoadedActiveRule r : activeRules) {
+ if (!r.getRuleKey().equals(key)) {
+ continue;
+ }
+
+ assertThat(r.getParams().get("format")).isEqualTo("^[a-z][a-zA-Z0-9]*$");
+ assertThat(r.getSeverity()).isEqualTo("MINOR");
+ }
+ }
+
+ private InputStream loadResource(String name) throws IOException {
+ return Resources.asByteSource(this.getClass().getResource("DefaultActiveRulesLoaderTest/" + name))
+ .openBufferedStream();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultRulesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultRulesLoaderTest.java
new file mode 100644
index 00000000000..a5a461c4e9f
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/DefaultRulesLoaderTest.java
@@ -0,0 +1,79 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.rule;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import org.junit.rules.ExpectedException;
+import org.sonar.batch.cache.WSLoaderResult;
+import org.sonar.batch.cache.WSLoader;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.sonarqube.ws.Rules.ListResponse.Rule;
+import com.google.common.io.ByteSource;
+import com.google.common.io.Resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import static org.mockito.Matchers.anyString;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Test;
+
+public class DefaultRulesLoaderTest {
+ @org.junit.Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Test
+ public void testParseServerResponse() throws IOException {
+ WSLoader wsLoader = mock(WSLoader.class);
+ InputStream is = Resources.asByteSource(this.getClass().getResource("DefaultRulesLoader/response.protobuf")).openBufferedStream();
+ when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true));
+ DefaultRulesLoader loader = new DefaultRulesLoader(wsLoader);
+ List<Rule> ruleList = loader.load(null);
+ assertThat(ruleList).hasSize(318);
+ }
+
+ @Test
+ public void testLoadedFromCache() throws IOException {
+ WSLoader wsLoader = mock(WSLoader.class);
+ InputStream is = Resources.asByteSource(this.getClass().getResource("DefaultRulesLoader/response.protobuf")).openBufferedStream();
+ when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true));
+ DefaultRulesLoader loader = new DefaultRulesLoader(wsLoader);
+ MutableBoolean fromCache = new MutableBoolean();
+ loader.load(fromCache);
+
+ assertThat(fromCache.booleanValue()).isTrue();
+ }
+
+ @Test
+ public void testError() throws IOException {
+ WSLoader wsLoader = mock(WSLoader.class);
+ InputStream is = ByteSource.wrap(new String("trash").getBytes()).openBufferedStream();
+ when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true));
+ DefaultRulesLoader loader = new DefaultRulesLoader(wsLoader);
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Unable to get rules");
+
+ loader.load(null);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java
new file mode 100644
index 00000000000..4801f246c55
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java
@@ -0,0 +1,135 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.rule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Project;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.core.util.UtcDateUtils;
+
+import java.util.Collections;
+import java.util.Date;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class QProfileSensorTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ static final Date DATE = UtcDateUtils.parseDateTime("2014-01-15T12:00:00+0000");
+ static final QProfile JAVA_PROFILE = new QProfile().setKey("java-two").setName("Java Two").setLanguage("java")
+ .setRulesUpdatedAt(DATE);
+ static final QProfile PHP_PROFILE = new QProfile().setKey("php-one").setName("Php One").setLanguage("php")
+ .setRulesUpdatedAt(DATE);
+
+ ModuleQProfiles moduleQProfiles = mock(ModuleQProfiles.class);
+ Project project = mock(Project.class);
+ SensorContext sensorContext = mock(SensorContext.class);
+ DefaultFileSystem fs;
+
+ @Before
+ public void prepare() throws Exception {
+ fs = new DefaultFileSystem(temp.newFolder().toPath());
+ }
+
+ @Test
+ public void to_string() {
+ QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
+ assertThat(sensor.toString()).isEqualTo("QProfileSensor");
+ }
+
+ @Test
+ public void no_execution_in_issues_mode() {
+ AnalysisMode analysisMode = mock(AnalysisMode.class);
+ when(analysisMode.isIssues()).thenReturn(true);
+ QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, analysisMode);
+ assertThat(sensor.shouldExecuteOnProject(project)).isFalse();
+
+ }
+
+ @Test
+ public void no_qprofiles() {
+ when(moduleQProfiles.findAll()).thenReturn(Collections.<QProfile>emptyList());
+
+ QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
+ assertThat(sensor.shouldExecuteOnProject(project)).isTrue();
+ sensor.analyse(project, sensorContext);
+
+ // measures are not saved
+ verify(sensorContext).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES, "[]")));
+ }
+
+ @Test
+ public void mark_profiles_as_used() {
+ when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE);
+ when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE);
+ when(moduleQProfiles.findByLanguage("abap")).thenReturn(null);
+ fs.addLanguages("java", "php", "abap");
+
+ QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
+ assertThat(sensor.shouldExecuteOnProject(project)).isTrue();
+ sensor.analyse(project, sensorContext);
+ }
+
+ @Test
+ public void store_measures_on_single_lang_module() {
+ when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE);
+ when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE);
+ when(moduleQProfiles.findByLanguage("abap")).thenReturn(null);
+ fs.addLanguages("java");
+
+ QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
+ assertThat(sensor.shouldExecuteOnProject(project)).isTrue();
+ sensor.analyse(project, sensorContext);
+
+ verify(sensorContext).saveMeasure(
+ argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES,
+ "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}]")));
+ }
+
+ @Test
+ public void store_measures_on_multi_lang_module() {
+ when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE);
+ when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE);
+ when(moduleQProfiles.findByLanguage("abap")).thenReturn(null);
+ fs.addLanguages("java", "php");
+
+ QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
+ assertThat(sensor.shouldExecuteOnProject(project)).isTrue();
+ sensor.analyse(project, sensorContext);
+
+ verify(sensorContext).saveMeasure(
+ argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES,
+ "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}," +
+ "{\"key\":\"php-one\",\"language\":\"php\",\"name\":\"Php One\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}]")));
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java
new file mode 100644
index 00000000000..9df8cd75491
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java
@@ -0,0 +1,101 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.rule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.MessageException;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class QProfileVerifierTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private DefaultFileSystem fs;
+ private ModuleQProfiles profiles;
+ private Settings settings = new Settings();
+
+ @Before
+ public void before() throws Exception {
+ fs = new DefaultFileSystem(temp.newFolder().toPath());
+ profiles = mock(ModuleQProfiles.class);
+ QProfile javaProfile = new QProfile().setKey("p1").setName("My Java profile").setLanguage("java");
+ when(profiles.findByLanguage("java")).thenReturn(javaProfile);
+ QProfile cobolProfile = new QProfile().setKey("p2").setName("My Cobol profile").setLanguage("cobol");
+ when(profiles.findByLanguage("cobol")).thenReturn(cobolProfile);
+ }
+
+ @Test
+ public void should_log_all_used_profiles() {
+ fs.addLanguages("java", "cobol");
+ QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
+ Logger logger = mock(Logger.class);
+ profileLogger.execute(logger);
+
+ verify(logger).info("Quality profile for {}: {}", "java", "My Java profile");
+ verify(logger).info("Quality profile for {}: {}", "cobol", "My Cobol profile");
+ }
+
+ @Test
+ public void should_fail_if_default_profile_not_used() {
+ fs.addLanguages("java", "cobol");
+ settings.setProperty("sonar.profile", "Unknown");
+
+ QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("sonar.profile was set to 'Unknown' but didn't match any profile for any language. Please check your configuration.");
+
+ profileLogger.execute();
+ }
+
+ @Test
+ public void should_not_fail_if_no_language_on_project() {
+ settings.setProperty("sonar.profile", "Unknown");
+
+ QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
+
+ profileLogger.execute();
+
+ }
+
+ @Test
+ public void should_not_fail_if_default_profile_used_at_least_once() {
+ fs.addLanguages("java", "cobol");
+ settings.setProperty("sonar.profile", "My Java profile");
+
+ QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
+
+ profileLogger.execute();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RuleFinderCompatibilityTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RuleFinderCompatibilityTest.java
new file mode 100644
index 00000000000..abc5f5dfd75
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RuleFinderCompatibilityTest.java
@@ -0,0 +1,101 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.rule;
+
+import org.sonar.api.batch.rule.internal.RulesBuilder;
+
+import org.sonar.api.batch.rule.Rules;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleQuery;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RuleFinderCompatibilityTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private Rules rules;
+ private RuleFinderCompatibility ruleFinder;
+
+ @Before
+ public void prepare() {
+ RulesBuilder builder = new RulesBuilder();
+ builder.add(RuleKey.of("repo1", "rule1"));
+ builder.add(RuleKey.of("repo1", "rule2")).setInternalKey("rule2_internal");
+ builder.add(RuleKey.of("repo2", "rule1"));
+ rules = builder.build();
+
+ ruleFinder = new RuleFinderCompatibility(rules);
+ }
+
+ @Test
+ public void testByInternalKey() {
+ assertThat(ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1").withConfigKey("rule2_internal")).getKey()).isEqualTo("rule2");
+ assertThat(ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1").withConfigKey("rule2_internal2"))).isNull();
+ }
+
+ @Test
+ public void testByKey() {
+ assertThat(ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1").withKey("rule2")).getKey()).isEqualTo("rule2");
+ assertThat(ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1").withKey("rule3"))).isNull();
+ assertThat(ruleFinder.findByKey("repo1", "rule2").getKey()).isEqualTo("rule2");
+ }
+
+ @Test
+ public void duplicateResult() {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Non unique result for rule query: RuleQuery[repositoryKey=repo1,key=<null>,configKey=<null>]");
+ ruleFinder.find(RuleQuery.create().withRepositoryKey("repo1"));
+ }
+
+ @Test
+ public void unsupportedById() {
+ thrown.expect(UnsupportedOperationException.class);
+ ruleFinder.findById(1);
+ }
+
+ @Test
+ public void unsupportedByInternalKeyWithoutRepo() {
+ thrown.expect(UnsupportedOperationException.class);
+ ruleFinder.find(RuleQuery.create().withConfigKey("config"));
+ }
+
+ @Test
+ public void unsupportedByKeyWithoutRepo() {
+ thrown.expect(UnsupportedOperationException.class);
+ ruleFinder.find(RuleQuery.create().withKey("key"));
+ }
+
+ @Test
+ public void unsupportedByKeyAndInternalKey() {
+ thrown.expect(UnsupportedOperationException.class);
+ ruleFinder.find(RuleQuery.create().withRepositoryKey("repo").withKey("key").withConfigKey("config"));
+ }
+
+ @Test
+ public void unsupportedByKeyAndInternalKeyWithoutRepo() {
+ thrown.expect(UnsupportedOperationException.class);
+ ruleFinder.find(RuleQuery.create().withKey("key").withConfigKey("config"));
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java
new file mode 100644
index 00000000000..208a187a966
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.rule;
+
+import java.util.Arrays;
+import org.junit.Test;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.config.Settings;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.rule.RuleKey;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RulesProfileProviderTest {
+
+ ModuleQProfiles qProfiles = mock(ModuleQProfiles.class);
+ Settings settings = new Settings();
+ RulesProfileProvider provider = new RulesProfileProvider();
+
+ @Test
+ public void merge_profiles() {
+ QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java");
+ when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile));
+
+ RulesProfile profile = provider.provide(qProfiles, new ActiveRulesBuilder().build(), settings);
+
+ // merge of all profiles
+ assertThat(profile).isNotNull().isInstanceOf(RulesProfileWrapper.class);
+ assertThat(profile.getLanguage()).isEqualTo("");
+ assertThat(profile.getName()).isEqualTo("SonarQube");
+ assertThat(profile.getActiveRules()).isEmpty();
+ try {
+ profile.getId();
+ fail();
+ } catch (IllegalStateException e) {
+ // id must not be used at all
+ }
+ }
+
+ @Test
+ public void keep_compatibility_with_single_language_projects() {
+ settings.setProperty("sonar.language", "java");
+
+ QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java");
+ when(qProfiles.findByLanguage("java")).thenReturn(qProfile);
+
+ RulesProfile profile = provider.provide(qProfiles, new ActiveRulesBuilder().build(), settings);
+
+ // no merge, directly the old hibernate profile
+ assertThat(profile).isNotNull();
+ assertThat(profile.getLanguage()).isEqualTo("java");
+ assertThat(profile.getName()).isEqualTo("Sonar way");
+ }
+
+ @Test
+ public void support_rule_templates() {
+ QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java");
+ when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile));
+ ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder();
+ activeRulesBuilder.create(RuleKey.of("java", "S001")).setTemplateRuleKey("T001").setLanguage("java").activate();
+
+ RulesProfile profile = provider.provide(qProfiles, activeRulesBuilder.build(), settings);
+
+ assertThat(profile.getActiveRule("java", "S001").getRule().getTemplate().getKey()).isEqualTo("T001");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProviderTest.java
new file mode 100644
index 00000000000..2df29eaa61a
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/RulesProviderTest.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.rule;
+
+import static org.mockito.Matchers.any;
+
+import org.apache.commons.lang.mutable.MutableBoolean;
+
+import com.google.common.collect.Lists;
+import org.sonar.api.batch.rule.Rules;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mock;
+import org.sonarqube.ws.Rules.ListResponse.Rule;
+import org.junit.Test;
+
+public class RulesProviderTest {
+ @Test
+ public void testRuleTranslation() {
+ RulesLoader loader = mock(RulesLoader.class);
+ when(loader.load(any(MutableBoolean.class))).thenReturn(Lists.newArrayList(getTestRule()));
+
+ RulesProvider provider = new RulesProvider();
+
+ Rules rules = provider.provide(loader);
+
+ assertThat(rules.findAll()).hasSize(1);
+ assertRule(rules.findAll().iterator().next());
+ }
+
+ private static void assertRule(org.sonar.api.batch.rule.Rule r) {
+ Rule testRule = getTestRule();
+
+ assertThat(r.name()).isEqualTo(testRule.getName());
+ assertThat(r.internalKey()).isEqualTo(testRule.getInternalKey());
+ assertThat(r.key().rule()).isEqualTo(testRule.getKey());
+ assertThat(r.key().repository()).isEqualTo(testRule.getRepository());
+ }
+
+ private static Rule getTestRule() {
+ Rule.Builder ruleBuilder = Rule.newBuilder();
+ ruleBuilder.setKey("key1");
+ ruleBuilder.setRepository("repo1");
+ ruleBuilder.setName("name");
+ ruleBuilder.setInternalKey("key1");
+ return ruleBuilder.build();
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java
new file mode 100644
index 00000000000..dd46501e68e
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java
@@ -0,0 +1,72 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.rule;
+
+import org.junit.Test;
+import org.sonar.core.util.UtcDateUtils;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UsedQProfilesTest {
+
+ static final String JAVA_JSON = "{\"key\":\"p1\",\"language\":\"java\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2014-01-15T00:00:00+0000\"}";
+ static final String PHP_JSON = "{\"key\":\"p2\",\"language\":\"php\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2014-02-20T00:00:00+0000\"}";
+
+ @Test
+ public void from_and_to_json() {
+ QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java")
+ .setRulesUpdatedAt(UtcDateUtils.parseDateTime("2014-01-15T00:00:00+0000"));
+ QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php")
+ .setRulesUpdatedAt(UtcDateUtils.parseDateTime("2014-02-20T00:00:00+0000"));
+
+ UsedQProfiles used = new UsedQProfiles().add(java).add(php);
+ String json = "[" + JAVA_JSON + "," + PHP_JSON + "]";
+ assertThat(used.toJson()).isEqualTo(json);
+
+ used = UsedQProfiles.fromJson(json);
+ assertThat(used.profiles()).hasSize(2);
+ assertThat(used.profiles().first().getKey()).isEqualTo("p1");
+ assertThat(used.profiles().last().getKey()).isEqualTo("p2");
+ }
+
+ @Test
+ public void do_not_duplicate_profiles() {
+ QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java");
+ QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php");
+
+ UsedQProfiles used = new UsedQProfiles().addAll(Arrays.asList(java, java, php));
+ assertThat(used.profiles()).hasSize(2);
+ }
+
+ @Test
+ public void group_profiles_by_key() {
+ QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java");
+ QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php");
+
+ UsedQProfiles used = new UsedQProfiles().addAll(Arrays.asList(java, java, php));
+ Map<String, QProfile> map = used.profilesByKey();
+ assertThat(map).hasSize(2);
+ assertThat(map.get("p1")).isSameAs(java);
+ assertThat(map.get("p2")).isSameAs(php);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java
new file mode 100644
index 00000000000..22a4f21091f
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java
@@ -0,0 +1,99 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Languages;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.repository.language.DefaultLanguagesRepository;
+import org.sonar.batch.repository.language.LanguagesRepository;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LanguageVerifierTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private Settings settings = new Settings();
+ private LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(Java.INSTANCE));
+ private DefaultFileSystem fs;
+
+ @Before
+ public void prepare() throws Exception {
+ fs = new DefaultFileSystem(temp.newFolder().toPath());
+ }
+
+ @Test
+ public void language_is_not_set() {
+ LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs);
+ verifier.start();
+
+ // no failure and no language is forced
+ assertThat(fs.languages()).isEmpty();
+
+ verifier.stop();
+ }
+
+ @Test
+ public void language_is_empty() {
+ settings.setProperty("sonar.language", "");
+ LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs);
+ verifier.start();
+
+ // no failure and no language is forced
+ assertThat(fs.languages()).isEmpty();
+
+ verifier.stop();
+ }
+
+ @Test
+ public void language_is_valid() {
+ settings.setProperty("sonar.language", "java");
+
+ LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs);
+ verifier.start();
+
+ // no failure and language is hardly registered
+ assertThat(fs.languages()).contains("java");
+
+ verifier.stop();
+ }
+
+ @Test
+ public void language_is_not_valid() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("You must install a plugin that supports the language 'php'");
+
+ settings.setProperty("sonar.language", "php");
+ LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs);
+ verifier.start();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java
new file mode 100644
index 00000000000..88fb18f06a4
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java
@@ -0,0 +1,162 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.bootstrap.GlobalSettings;
+import org.sonar.batch.report.AnalysisContextReportPublisher;
+import org.sonar.batch.repository.FileData;
+import org.sonar.batch.repository.ProjectRepositories;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ModuleSettingsTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private DefaultAnalysisMode mode;
+
+ @Before
+ public void before() {
+ mode = mock(DefaultAnalysisMode.class);
+ }
+
+ private ProjectRepositories createSettings(String module, Map<String, String> settingsMap) {
+ Table<String, String, FileData> fileData = ImmutableTable.of();
+ Table<String, String, String> settings = HashBasedTable.create();
+
+ for (Map.Entry<String, String> e : settingsMap.entrySet()) {
+ settings.put(module, e.getKey(), e.getValue());
+ }
+ return new ProjectRepositories(settings, fileData, null);
+ }
+
+ @Test
+ public void testOrderedProjects() {
+ ProjectDefinition grandParent = ProjectDefinition.create();
+ ProjectDefinition parent = ProjectDefinition.create();
+ ProjectDefinition child = ProjectDefinition.create();
+ grandParent.addSubProject(parent);
+ parent.addSubProject(child);
+
+ List<ProjectDefinition> hierarchy = ModuleSettings.getTopDownParentProjects(child);
+ assertThat(hierarchy.get(0)).isEqualTo(grandParent);
+ assertThat(hierarchy.get(1)).isEqualTo(parent);
+ assertThat(hierarchy.get(2)).isEqualTo(child);
+ }
+
+ @Test
+ public void test_loading_of_module_settings() {
+ GlobalSettings globalSettings = mock(GlobalSettings.class);
+ when(globalSettings.getDefinitions()).thenReturn(new PropertyDefinitions());
+ when(globalSettings.getProperties()).thenReturn(ImmutableMap.of(
+ "overridding", "batch",
+ "on-batch", "true"));
+
+ ProjectRepositories projRepos = createSettings("struts-core", ImmutableMap.of("on-module", "true", "overridding", "module"));
+
+ ProjectDefinition module = ProjectDefinition.create().setKey("struts-core");
+
+ ModuleSettings moduleSettings = new ModuleSettings(globalSettings, module, projRepos, mode, mock(AnalysisContextReportPublisher.class));
+
+ assertThat(moduleSettings.getString("overridding")).isEqualTo("module");
+ assertThat(moduleSettings.getString("on-batch")).isEqualTo("true");
+ assertThat(moduleSettings.getString("on-module")).isEqualTo("true");
+
+ }
+
+ // SONAR-6386
+ @Test
+ public void test_loading_of_parent_module_settings_for_new_module() {
+ GlobalSettings globalSettings = mock(GlobalSettings.class);
+ when(globalSettings.getDefinitions()).thenReturn(new PropertyDefinitions());
+ when(globalSettings.getProperties()).thenReturn(ImmutableMap.of(
+ "overridding", "batch",
+ "on-batch", "true"));
+
+ ProjectRepositories projRepos = createSettings("struts", ImmutableMap.of("on-module", "true", "overridding", "module"));
+
+ ProjectDefinition module = ProjectDefinition.create().setKey("struts-core");
+ ProjectDefinition.create().setKey("struts").addSubProject(module);
+
+ ModuleSettings moduleSettings = new ModuleSettings(globalSettings, module, projRepos, mode, mock(AnalysisContextReportPublisher.class));
+
+ assertThat(moduleSettings.getString("overridding")).isEqualTo("module");
+ assertThat(moduleSettings.getString("on-batch")).isEqualTo("true");
+ assertThat(moduleSettings.getString("on-module")).isEqualTo("true");
+ }
+
+ @Test
+ public void should_not_fail_when_accessing_secured_properties() {
+ GlobalSettings batchSettings = mock(GlobalSettings.class);
+ when(batchSettings.getDefinitions()).thenReturn(new PropertyDefinitions());
+ when(batchSettings.getProperties()).thenReturn(ImmutableMap.of(
+ "sonar.foo.secured", "bar"));
+
+ ProjectRepositories projSettingsRepo = createSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2"));
+
+ ProjectDefinition module = ProjectDefinition.create().setKey("struts-core");
+
+ ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode, mock(AnalysisContextReportPublisher.class));
+
+ assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2");
+ assertThat(moduleSettings.getString("sonar.foo.secured")).isEqualTo("bar");
+ }
+
+ @Test
+ public void should_fail_when_accessing_secured_properties_in_issues() {
+ GlobalSettings batchSettings = mock(GlobalSettings.class);
+ when(batchSettings.getDefinitions()).thenReturn(new PropertyDefinitions());
+ when(batchSettings.getProperties()).thenReturn(ImmutableMap.of(
+ "sonar.foo.secured", "bar"));
+
+ ProjectRepositories projSettingsRepo = createSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2"));
+
+ when(mode.isIssues()).thenReturn(true);
+
+ ProjectDefinition module = ProjectDefinition.create().setKey("struts-core");
+
+ ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode, mock(AnalysisContextReportPublisher.class));
+
+ assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2");
+
+ thrown.expect(MessageException.class);
+ thrown
+ .expectMessage(
+ "Access to the secured property 'sonar.foo.secured' is not possible in issues mode. The SonarQube plugin which requires this property must be deactivated in issues mode.");
+ moduleSettings.getString("sonar.foo.secured");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java
new file mode 100644
index 00000000000..4f9b403a94e
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java
@@ -0,0 +1,122 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan;
+
+import org.junit.Test;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.config.Settings;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectExclusionsTest {
+
+ ProjectReactor newReactor(String rootKey, String... moduleKeys) {
+ ProjectDefinition root = ProjectDefinition.create().setKey(rootKey);
+ for (String moduleKey : moduleKeys) {
+ ProjectDefinition module = ProjectDefinition.create().setKey(moduleKey);
+ root.addSubProject(module);
+ }
+ return new ProjectReactor(root);
+ }
+
+ @Test
+ public void testSkippedModules() {
+ Settings settings = new Settings();
+ settings.setProperty("sonar.skippedModules", "sub1,sub3");
+
+ ProjectReactor reactor = newReactor("root", "sub1", "sub2");
+
+ ProjectExclusions exclusions = new ProjectExclusions(settings);
+ exclusions.apply(reactor);
+
+ assertThat(reactor.getProject("root")).isNotNull();
+ assertThat(reactor.getProject("sub1")).isNull();
+ assertThat(reactor.getProject("sub2")).isNotNull();
+ }
+
+ @Test
+ public void testNoSkippedModules() {
+ Settings settings = new Settings();
+ ProjectReactor reactor = newReactor("root", "sub1", "sub2");
+ ProjectExclusions exclusions = new ProjectExclusions(settings);
+ exclusions.apply(reactor);
+
+ assertThat(reactor.getProject("root")).isNotNull();
+ assertThat(reactor.getProject("sub1")).isNotNull();
+ assertThat(reactor.getProject("sub2")).isNotNull();
+ }
+
+ @Test
+ public void testIncludedModules() {
+ Settings settings = new Settings();
+ settings.setProperty("sonar.includedModules", "sub1");
+ ProjectReactor reactor = newReactor("root", "sub1", "sub2");
+ ProjectExclusions exclusions = new ProjectExclusions(settings);
+ exclusions.apply(reactor);
+
+ assertThat(reactor.getProject("root")).isNotNull();
+ assertThat(reactor.getProject("sub1")).isNotNull();
+ assertThat(reactor.getProject("sub2")).isNull();
+ }
+
+ @Test
+ public void shouldBeExcludedIfParentIsExcluded() {
+ ProjectDefinition sub11 = ProjectDefinition.create().setKey("sub11");
+ ProjectDefinition sub1 = ProjectDefinition.create().setKey("sub1").addSubProject(sub11);
+ ProjectDefinition root = ProjectDefinition.create().setKey("root").addSubProject(sub1);
+
+ Settings settings = new Settings();
+ settings.setProperty("sonar.skippedModules", "sub1");
+
+ ProjectReactor reactor = new ProjectReactor(root);
+ ProjectExclusions exclusions = new ProjectExclusions(settings);
+ exclusions.apply(reactor);
+
+ assertThat(reactor.getProject("root")).isNotNull();
+ assertThat(reactor.getProject("sub1")).isNull();
+ assertThat(reactor.getProject("sub11")).isNull();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldFailIfExcludingRoot() {
+ Settings settings = new Settings();
+ settings.setProperty("sonar.skippedModules", "sub1,root");
+
+ ProjectReactor reactor = newReactor("root", "sub1", "sub2");
+ ProjectExclusions exclusions = new ProjectExclusions(settings);
+ exclusions.apply(reactor);
+ }
+
+ @Test
+ public void shouldIgnoreMavenGroupId() {
+ ProjectReactor reactor = newReactor("org.apache.struts:struts", "org.apache.struts:struts-core", "org.apache.struts:struts-taglib");
+
+ Settings settings = new Settings();
+ settings.setProperty("sonar.skippedModules", "struts-taglib");
+
+ ProjectExclusions exclusions = new ProjectExclusions(settings);
+ exclusions.apply(reactor);
+
+ assertThat(reactor.getProject("org.apache.struts:struts")).isNotNull();
+ assertThat(reactor.getProject("org.apache.struts:struts-core")).isNotNull();
+ assertThat(reactor.getProject("org.apache.struts:struts-taglib")).isNull();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectLockTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectLockTest.java
new file mode 100644
index 00000000000..c03240b7341
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectLockTest.java
@@ -0,0 +1,103 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan;
+
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.home.cache.DirectoryLock;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public class ProjectLockTest {
+ @Rule
+ public TemporaryFolder tempFolder = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+ private ProjectLock lock;
+
+ @Before
+ public void setUp() {
+ lock = setUpTest(tempFolder.getRoot());
+ }
+
+ private ProjectLock setUpTest(File file) {
+ ProjectReactor projectReactor = mock(ProjectReactor.class);
+ ProjectDefinition projectDefinition = mock(ProjectDefinition.class);
+ when(projectReactor.getRoot()).thenReturn(projectDefinition);
+ when(projectDefinition.getWorkDir()).thenReturn(file);
+
+ return new ProjectLock(projectReactor);
+ }
+
+ @Test
+ public void tryLock() {
+ Path lockFilePath = tempFolder.getRoot().toPath().resolve(DirectoryLock.LOCK_FILE_NAME);
+ lock.tryLock();
+ assertThat(Files.exists(lockFilePath)).isTrue();
+ assertThat(Files.isRegularFile(lockFilePath)).isTrue();
+
+ lock.stop();
+ assertThat(Files.exists(lockFilePath)).isTrue();
+ }
+
+ @Test
+ public void tryLockConcurrently() {
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Another SonarQube analysis is already in progress for this project");
+ lock.tryLock();
+ lock.tryLock();
+ }
+
+ @Test
+ /**
+ * If there is an error starting up the scan, we'll still try to unlock even if the lock
+ * was never done
+ */
+ public void stopWithoutStarting() {
+ lock.stop();
+ lock.stop();
+ }
+
+ @Test
+ public void tryLockTwice() {
+ lock.tryLock();
+ lock.stop();
+ lock.tryLock();
+ lock.stop();
+ }
+
+ @Test
+ public void unLockWithNoLock() {
+ lock.stop();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java
new file mode 100644
index 00000000000..d1a61853ef2
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java
@@ -0,0 +1,683 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan;
+
+import com.google.common.collect.Maps;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.batch.analysis.AnalysisProperties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ProjectReactorBuilderTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private AnalysisMode mode;
+
+ @Before
+ public void setUp() {
+ mode = mock(AnalysisMode.class);
+ }
+
+ @Test
+ public void shouldDefineSimpleProject() {
+ ProjectDefinition projectDefinition = loadProjectDefinition("simple-project");
+
+ assertThat(projectDefinition.getKey()).isEqualTo("com.foo.project");
+ assertThat(projectDefinition.getName()).isEqualTo("Foo Project");
+ assertThat(projectDefinition.getVersion()).isEqualTo("1.0-SNAPSHOT");
+ assertThat(projectDefinition.getDescription()).isEqualTo("Description of Foo Project");
+ assertThat(projectDefinition.getSourceDirs()).contains("sources");
+ }
+
+ @Test
+ public void shouldFailIfUnexistingSourceDirectory() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("The folder 'unexisting-source-dir' does not exist for 'com.foo.project' (base directory = "
+ + getResource(this.getClass(), "simple-project-with-unexisting-source-dir") + ")");
+
+ loadProjectDefinition("simple-project-with-unexisting-source-dir");
+ }
+
+ @Test
+ public void fail_if_sources_not_set() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("You must define the following mandatory properties for 'com.foo.project': sonar.sources");
+ loadProjectDefinition("simple-project-with-missing-source-dir");
+ }
+
+ @Test
+ public void shouldNotFailIfBlankSourceDirectory() {
+ loadProjectDefinition("simple-project-with-blank-source-dir");
+ }
+
+ @Test
+ public void modulesDuplicateIds() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("Two modules have the same id: 'module1'. Each module must have a unique id.");
+
+ loadProjectDefinition("multi-module-duplicate-id");
+ }
+
+ @Test
+ public void modulesRepeatedIds() {
+ ProjectDefinition rootProject = loadProjectDefinition("multi-module-repeated-id");
+
+ List<ProjectDefinition> modules = rootProject.getSubProjects();
+ assertThat(modules.size()).isEqualTo(1);
+ // Module 1
+ ProjectDefinition module1 = modules.get(0);
+ assertThat(module1.getKey()).isEqualTo("com.foo.project:module1");
+ assertThat(module1.getName()).isEqualTo("Foo Module 1");
+
+ // Module 1 -> Module 1
+ ProjectDefinition module1_module1 = module1.getSubProjects().get(0);
+ assertThat(module1_module1.getKey()).isEqualTo("com.foo.project:module1:module1");
+ assertThat(module1_module1.getName()).isEqualTo("Foo Sub Module 1");
+ }
+
+ @Test
+ public void shouldDefineMultiModuleProjectWithDefinitionsAllInRootProject() throws IOException {
+ ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-all-in-root");
+
+ // CHECK ROOT
+ assertThat(rootProject.getKey()).isEqualTo("com.foo.project");
+ assertThat(rootProject.getName()).isEqualTo("Foo Project");
+ assertThat(rootProject.getVersion()).isEqualTo("1.0-SNAPSHOT");
+ assertThat(rootProject.getDescription()).isEqualTo("Description of Foo Project");
+ // root project must not contain some properties - even if they are defined in the root properties file
+ assertThat(rootProject.getSourceDirs().contains("sources")).isFalse();
+ assertThat(rootProject.getTestDirs().contains("tests")).isFalse();
+ // and module properties must have been cleaned
+ assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull();
+ assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull();
+ // Check baseDir and workDir
+ assertThat(rootProject.getBaseDir().getCanonicalFile())
+ .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root"));
+ assertThat(rootProject.getWorkDir().getCanonicalFile())
+ .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-all-in-root"), ".sonar"));
+
+ // CHECK MODULES
+ List<ProjectDefinition> modules = rootProject.getSubProjects();
+ assertThat(modules.size()).isEqualTo(2);
+
+ // Module 1
+ ProjectDefinition module1 = modules.get(0);
+ assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module1"));
+ assertThat(module1.getKey()).isEqualTo("com.foo.project:module1");
+ assertThat(module1.getName()).isEqualTo("module1");
+ assertThat(module1.getVersion()).isEqualTo("1.0-SNAPSHOT");
+ // Description should not be inherited from parent if not set
+ assertThat(module1.getDescription()).isNull();
+ assertThat(module1.getSourceDirs()).contains("sources");
+ assertThat(module1.getTestDirs()).contains("tests");
+ assertThat(module1.getBinaries()).contains("target/classes");
+ // and module properties must have been cleaned
+ assertThat(module1.properties().get("module1.sonar.projectKey")).isNull();
+ assertThat(module1.properties().get("module2.sonar.projectKey")).isNull();
+ // Check baseDir and workDir
+ assertThat(module1.getBaseDir().getCanonicalFile())
+ .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module1"));
+ assertThat(module1.getWorkDir().getCanonicalFile())
+ .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-all-in-root"), ".sonar/com.foo.project_module1"));
+
+ // Module 2
+ ProjectDefinition module2 = modules.get(1);
+ assertThat(module2.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module2"));
+ assertThat(module2.getKey()).isEqualTo("com.foo.project:com.foo.project.module2");
+ assertThat(module2.getName()).isEqualTo("Foo Module 2");
+ assertThat(module2.getVersion()).isEqualTo("1.0-SNAPSHOT");
+ assertThat(module2.getDescription()).isEqualTo("Description of Module 2");
+ assertThat(module2.getSourceDirs()).contains("src");
+ assertThat(module2.getTestDirs()).contains("tests");
+ assertThat(module2.getBinaries()).contains("target/classes");
+ // and module properties must have been cleaned
+ assertThat(module2.properties().get("module1.sonar.projectKey")).isNull();
+ assertThat(module2.properties().get("module2.sonar.projectKey")).isNull();
+ // Check baseDir and workDir
+ assertThat(module2.getBaseDir().getCanonicalFile())
+ .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module2"));
+ assertThat(module2.getWorkDir().getCanonicalFile())
+ .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-all-in-root"), ".sonar/com.foo.project_com.foo.project.module2"));
+ }
+
+ // SONAR-4876
+ @Test
+ public void shouldDefineMultiModuleProjectWithModuleKey() {
+ ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-moduleKey");
+
+ // CHECK ROOT
+ // module properties must have been cleaned
+ assertThat(rootProject.properties().get("module1.sonar.moduleKey")).isNull();
+ assertThat(rootProject.properties().get("module2.sonar.moduleKey")).isNull();
+
+ // CHECK MODULES
+ List<ProjectDefinition> modules = rootProject.getSubProjects();
+ assertThat(modules.size()).isEqualTo(2);
+
+ // Module 2
+ ProjectDefinition module2 = modules.get(1);
+ assertThat(module2.getKey()).isEqualTo("com.foo.project.module2");
+ }
+
+ // SONARPLUGINS-2421
+ @Test
+ public void shouldDefineMultiLanguageProjectWithDefinitionsAllInRootProject() throws IOException {
+ ProjectDefinition rootProject = loadProjectDefinition("multi-language-definitions-all-in-root");
+
+ // CHECK ROOT
+ assertThat(rootProject.getKey()).isEqualTo("example");
+ assertThat(rootProject.getName()).isEqualTo("Example");
+ assertThat(rootProject.getVersion()).isEqualTo("1.0");
+
+ // CHECK MODULES
+ List<ProjectDefinition> modules = rootProject.getSubProjects();
+ assertThat(modules.size()).isEqualTo(2);
+
+ // Module 1
+ ProjectDefinition module1 = modules.get(0);
+ assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-language-definitions-all-in-root"));
+ assertThat(module1.getSourceDirs()).contains("src/main/java");
+ // and module properties must have been cleaned
+ assertThat(module1.getWorkDir().getCanonicalFile())
+ .isEqualTo(new File(getResource(this.getClass(), "multi-language-definitions-all-in-root"), ".sonar/example_java-module"));
+
+ // Module 2
+ ProjectDefinition module2 = modules.get(1);
+ assertThat(module2.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-language-definitions-all-in-root"));
+ assertThat(module2.getSourceDirs()).contains("src/main/groovy");
+ // and module properties must have been cleaned
+ assertThat(module2.getWorkDir().getCanonicalFile())
+ .isEqualTo(new File(getResource(this.getClass(), "multi-language-definitions-all-in-root"), ".sonar/example_groovy-module"));
+ }
+
+ @Test
+ public void shouldDefineMultiModuleProjectWithBaseDir() {
+ ProjectDefinition rootProject = loadProjectDefinition("multi-module-with-basedir");
+ List<ProjectDefinition> modules = rootProject.getSubProjects();
+ assertThat(modules.size()).isEqualTo(1);
+ assertThat(modules.get(0).getKey()).isEqualTo("com.foo.project:com.foo.project.module1");
+ }
+
+ @Test
+ public void shouldFailIfUnexistingModuleBaseDir() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("The base directory of the module 'module1' does not exist: "
+ + getResource(this.getClass(), "multi-module-with-unexisting-basedir").getAbsolutePath() + File.separator + "module1");
+
+ loadProjectDefinition("multi-module-with-unexisting-basedir");
+ }
+
+ @Test
+ public void shouldFailIfUnexistingSourceFolderInheritedInMultimodule() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("The folder 'unexisting-source-dir' does not exist for 'com.foo.project:module1' (base directory = "
+ + getResource(this.getClass(), "multi-module-with-unexisting-source-dir").getAbsolutePath() + File.separator + "module1)");
+
+ loadProjectDefinition("multi-module-with-unexisting-source-dir");
+ }
+
+ @Test
+ public void shouldFailIfExplicitUnexistingTestFolder() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("The folder 'tests' does not exist for 'com.foo.project' (base directory = "
+ + getResource(this.getClass(), "simple-project-with-unexisting-test-dir").getAbsolutePath());
+
+ loadProjectDefinition("simple-project-with-unexisting-test-dir");
+ }
+
+ @Test
+ public void shouldFailIfExplicitUnexistingTestFolderOnModule() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("The folder 'tests' does not exist for 'module1' (base directory = "
+ + getResource(this.getClass(), "multi-module-with-explicit-unexisting-test-dir").getAbsolutePath() + File.separator + "module1)");
+
+ loadProjectDefinition("multi-module-with-explicit-unexisting-test-dir");
+ }
+
+ @Test
+ public void multiModuleProperties() {
+ ProjectDefinition projectDefinition = loadProjectDefinition("big-multi-module-definitions-all-in-root");
+
+ assertThat(projectDefinition.properties().get("module11.property")).isNull();
+ assertThat(projectDefinition.properties().get("sonar.profile")).isEqualTo("Foo");
+ ProjectDefinition module1 = null;
+ ProjectDefinition module2 = null;
+ for (ProjectDefinition prj : projectDefinition.getSubProjects()) {
+ if (prj.getKey().equals("com.foo.project:module1")) {
+ module1 = prj;
+ } else if (prj.getKey().equals("com.foo.project:module2")) {
+ module2 = prj;
+ }
+ }
+ assertThat(module1.properties().get("module11.property")).isNull();
+ assertThat(module1.properties().get("property")).isNull();
+ assertThat(module1.properties().get("sonar.profile")).isEqualTo("Foo");
+ assertThat(module2.properties().get("module11.property")).isNull();
+ assertThat(module2.properties().get("property")).isNull();
+ assertThat(module2.properties().get("sonar.profile")).isEqualTo("Foo");
+
+ ProjectDefinition module11 = null;
+ ProjectDefinition module12 = null;
+ for (ProjectDefinition prj : module1.getSubProjects()) {
+ if (prj.getKey().equals("com.foo.project:module1:module11")) {
+ module11 = prj;
+ } else if (prj.getKey().equals("com.foo.project:module1:module12")) {
+ module12 = prj;
+ }
+ }
+ assertThat(module11.properties().get("module1.module11.property")).isNull();
+ assertThat(module11.properties().get("module11.property")).isNull();
+ assertThat(module11.properties().get("property")).isEqualTo("My module11 property");
+ assertThat(module11.properties().get("sonar.profile")).isEqualTo("Foo");
+ assertThat(module12.properties().get("module11.property")).isNull();
+ assertThat(module12.properties().get("property")).isNull();
+ assertThat(module12.properties().get("sonar.profile")).isEqualTo("Foo");
+ }
+
+ @Test
+ public void shouldRemoveModulePropertiesFromTaskProperties() {
+ Map<String, String> props = loadProps("big-multi-module-definitions-all-in-root");
+
+ AnalysisProperties taskProperties = new AnalysisProperties(props, null);
+ assertThat(taskProperties.property("module1.module11.property")).isEqualTo("My module11 property");
+
+ new ProjectReactorBuilder(taskProperties, mode).execute();
+
+ assertThat(taskProperties.property("module1.module11.property")).isNull();
+ }
+
+ @Test
+ public void shouldFailIfMandatoryPropertiesAreNotPresent() {
+ Map<String, String> props = new HashMap<>();
+ props.put("foo1", "bla");
+ props.put("foo4", "bla");
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("You must define the following mandatory properties for 'Unknown': foo2, foo3");
+
+ ProjectReactorBuilder.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"});
+ }
+
+ @Test
+ public void shouldFailIfMandatoryPropertiesAreNotPresentButWithProjectKey() {
+ Map<String, String> props = new HashMap<>();
+ props.put("foo1", "bla");
+ props.put("sonar.projectKey", "my-project");
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("You must define the following mandatory properties for 'my-project': foo2, foo3");
+
+ ProjectReactorBuilder.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"});
+ }
+
+ @Test
+ public void shouldNotFailIfMandatoryPropertiesArePresent() {
+ Map<String, String> props = new HashMap<>();
+ props.put("foo1", "bla");
+ props.put("foo4", "bla");
+
+ ProjectReactorBuilder.checkMandatoryProperties(props, new String[] {"foo1"});
+
+ // No exception should be thrown
+ }
+
+ @Test
+ public void shouldGetRelativeFile() {
+ assertThat(ProjectReactorBuilder.resolvePath(getResource(this.getClass(), "/"), "shouldGetFile/foo.properties"))
+ .isEqualTo(getResource(this.getClass(), "shouldGetFile/foo.properties"));
+ }
+
+ @Test
+ public void shouldGetAbsoluteFile() {
+ File file = getResource(this.getClass(), "shouldGetFile/foo.properties");
+
+ assertThat(ProjectReactorBuilder.resolvePath(getResource(this.getClass(), "/"), file.getAbsolutePath()))
+ .isEqualTo(file);
+ }
+
+ @Test
+ public void shouldMergeParentProperties() {
+ // Use a random value to avoid VM optimization that would create constant String and make s1 and s2 the same object
+ int i = (int) Math.random() * 10;
+ String s1 = "value" + i;
+ String s2 = "value" + i;
+ Map<String, String> parentProps = new HashMap<>();
+ parentProps.put("toBeMergeProps", "fooParent");
+ parentProps.put("existingChildProp", "barParent");
+ parentProps.put("duplicatedProp", s1);
+ parentProps.put("sonar.projectDescription", "Desc from Parent");
+
+ Map<String, String> childProps = new HashMap<>();
+ childProps.put("existingChildProp", "barChild");
+ childProps.put("otherProp", "tutuChild");
+ childProps.put("duplicatedProp", s2);
+
+ ProjectReactorBuilder.mergeParentProperties(childProps, parentProps);
+
+ assertThat(childProps).hasSize(4);
+ assertThat(childProps.get("toBeMergeProps")).isEqualTo("fooParent");
+ assertThat(childProps.get("existingChildProp")).isEqualTo("barChild");
+ assertThat(childProps.get("otherProp")).isEqualTo("tutuChild");
+ assertThat(childProps.get("sonar.projectDescription")).isNull();
+ assertThat(childProps.get("duplicatedProp")).isSameAs(parentProps.get("duplicatedProp"));
+ }
+
+ @Test
+ public void shouldInitRootWorkDir() {
+ ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(Maps.<String, String>newHashMap(), null), mode);
+ File baseDir = new File("target/tmp/baseDir");
+
+ File workDir = builder.initRootProjectWorkDir(baseDir, Maps.<String, String>newHashMap());
+
+ assertThat(workDir).isEqualTo(new File(baseDir, ".sonar"));
+ }
+
+ @Test
+ public void nonAssociatedMode() {
+ when(mode.isIssues()).thenReturn(true);
+ ProjectDefinition project = loadProjectDefinition("multi-module-with-basedir-not-associated");
+
+ assertThat(project.getKey()).isEqualTo("project");
+ }
+
+ @Test
+ public void shouldInitWorkDirWithCustomRelativeFolder() {
+ Map<String, String> props = Maps.<String, String>newHashMap();
+ props.put("sonar.working.directory", ".foo");
+ ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode);
+ File baseDir = new File("target/tmp/baseDir");
+
+ File workDir = builder.initRootProjectWorkDir(baseDir, props);
+
+ assertThat(workDir).isEqualTo(new File(baseDir, ".foo"));
+ }
+
+ @Test
+ public void shouldInitRootWorkDirWithCustomAbsoluteFolder() {
+ Map<String, String> props = Maps.<String, String>newHashMap();
+ props.put("sonar.working.directory", new File("src").getAbsolutePath());
+ ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode);
+ File baseDir = new File("target/tmp/baseDir");
+
+ File workDir = builder.initRootProjectWorkDir(baseDir, props);
+
+ assertThat(workDir).isEqualTo(new File("src").getAbsoluteFile());
+ }
+
+ @Test
+ public void shouldFailIf2ModulesWithSameKey() {
+ Map<String, String> props = new HashMap<>();
+ props.put("sonar.projectKey", "root");
+ ProjectDefinition root = ProjectDefinition.create().setProperties(props);
+
+ Map<String, String> props1 = new HashMap<>();
+ props1.put("sonar.projectKey", "mod1");
+ root.addSubProject(ProjectDefinition.create().setProperties(props1));
+
+ // Check uniqueness of a new module: OK
+ Map<String, String> props2 = new HashMap<>();
+ props2.put("sonar.projectKey", "mod2");
+ ProjectDefinition mod2 = ProjectDefinition.create().setProperties(props2);
+ ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);
+
+ // Now, add it and check again
+ root.addSubProject(mod2);
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("Project 'root' can't have 2 modules with the following key: mod2");
+
+ ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);
+ }
+
+ @Test
+ public void shouldSetModuleKeyIfNotPresent() {
+ Map<String, String> props = new HashMap<>();
+ props.put("sonar.projectVersion", "1.0");
+
+ // should be set
+ ProjectReactorBuilder.setModuleKeyAndNameIfNotDefined(props, "foo", "parent");
+ assertThat(props.get("sonar.moduleKey")).isEqualTo("parent:foo");
+ assertThat(props.get("sonar.projectName")).isEqualTo("foo");
+
+ // but not this 2nd time
+ ProjectReactorBuilder.setModuleKeyAndNameIfNotDefined(props, "bar", "parent");
+ assertThat(props.get("sonar.moduleKey")).isEqualTo("parent:foo");
+ assertThat(props.get("sonar.projectName")).isEqualTo("foo");
+ }
+
+ private ProjectDefinition loadProjectDefinition(String projectFolder) {
+ Map<String, String> props = loadProps(projectFolder);
+ AnalysisProperties bootstrapProps = new AnalysisProperties(props, null);
+ ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps, mode).execute();
+ return projectReactor.getRoot();
+ }
+
+ protected static Properties toProperties(File propertyFile) {
+ Properties propsFromFile = new Properties();
+ try (FileInputStream fileInputStream = new FileInputStream(propertyFile)) {
+ propsFromFile.load(fileInputStream);
+ } catch (IOException e) {
+ throw new IllegalStateException("Impossible to read the property file: " + propertyFile.getAbsolutePath(), e);
+ }
+ // Trim properties
+ for (String propKey : propsFromFile.stringPropertyNames()) {
+ propsFromFile.setProperty(propKey, StringUtils.trim(propsFromFile.getProperty(propKey)));
+ }
+ return propsFromFile;
+ }
+
+ private Map<String, String> loadProps(String projectFolder) {
+ Map<String, String> props = Maps.<String, String>newHashMap();
+ Properties runnerProps = toProperties(getResource(this.getClass(), projectFolder + "/sonar-project.properties"));
+ for (final String name : runnerProps.stringPropertyNames()) {
+ props.put(name, runnerProps.getProperty(name));
+ }
+ props.put("sonar.projectBaseDir", getResource(this.getClass(), projectFolder).getAbsolutePath());
+ return props;
+ }
+
+ public Map<String, String> toMap(Properties props) {
+ Map<String, String> result = new HashMap<>();
+ for (Map.Entry<Object, Object> entry : props.entrySet()) {
+ result.put(entry.getKey().toString(), entry.getValue().toString());
+ }
+ return result;
+ }
+
+ @Test
+ public void shouldGetList() {
+ Map<String, String> props = new HashMap<>();
+
+ props.put("prop", " foo ,, bar , \n\ntoto,tutu");
+ assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu");
+ }
+
+ @Test
+ public void shouldGetEmptyList() {
+ Map<String, String> props = new HashMap<>();
+
+ props.put("prop", "");
+ assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).isEmpty();
+ }
+
+ @Test
+ public void shouldGetListFromFile() throws IOException {
+ String filePath = "shouldGetList/foo.properties";
+ Map<String, String> props = loadPropsFromFile(filePath);
+
+ assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu");
+ }
+
+ @Test
+ public void shouldDefineProjectWithBuildDir() {
+ ProjectDefinition rootProject = loadProjectDefinition("simple-project-with-build-dir");
+ File buildDir = rootProject.getBuildDir();
+ assertThat(buildDir).isDirectory().exists();
+ assertThat(new File(buildDir, "report.txt")).isFile().exists();
+ assertThat(buildDir.getName()).isEqualTo("build");
+ }
+
+ @Test
+ public void doNotMixPropertiesWhenModuleKeyIsPrefixOfAnother() throws IOException {
+ ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-same-prefix");
+
+ // CHECK ROOT
+ assertThat(rootProject.getKey()).isEqualTo("com.foo.project");
+ assertThat(rootProject.getName()).isEqualTo("Foo Project");
+ assertThat(rootProject.getVersion()).isEqualTo("1.0-SNAPSHOT");
+ assertThat(rootProject.getDescription()).isEqualTo("Description of Foo Project");
+ // root project must not contain some properties - even if they are defined in the root properties file
+ assertThat(rootProject.getSourceDirs().contains("sources")).isFalse();
+ assertThat(rootProject.getTestDirs().contains("tests")).isFalse();
+ // and module properties must have been cleaned
+ assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull();
+ assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull();
+ // Check baseDir and workDir
+ assertThat(rootProject.getBaseDir().getCanonicalFile())
+ .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix"));
+ assertThat(rootProject.getWorkDir().getCanonicalFile())
+ .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar"));
+
+ // CHECK MODULES
+ List<ProjectDefinition> modules = rootProject.getSubProjects();
+ assertThat(modules.size()).isEqualTo(2);
+
+ // Module 1
+ ProjectDefinition module1 = modules.get(0);
+ assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1"));
+ assertThat(module1.getKey()).isEqualTo("com.foo.project:module1");
+ assertThat(module1.getName()).isEqualTo("module1");
+ assertThat(module1.getVersion()).isEqualTo("1.0-SNAPSHOT");
+ // Description should not be inherited from parent if not set
+ assertThat(module1.getDescription()).isNull();
+ assertThat(module1.getSourceDirs()).contains("sources");
+ assertThat(module1.getTestDirs()).contains("tests");
+ assertThat(module1.getBinaries()).contains("target/classes");
+ // and module properties must have been cleaned
+ assertThat(module1.properties().get("module1.sonar.projectKey")).isNull();
+ assertThat(module1.properties().get("module2.sonar.projectKey")).isNull();
+ // Check baseDir and workDir
+ assertThat(module1.getBaseDir().getCanonicalFile())
+ .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1"));
+ assertThat(module1.getWorkDir().getCanonicalFile())
+ .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar/com.foo.project_module1"));
+
+ // Module 1 Feature
+ ProjectDefinition module1Feature = modules.get(1);
+ assertThat(module1Feature.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature"));
+ assertThat(module1Feature.getKey()).isEqualTo("com.foo.project:com.foo.project.module1.feature");
+ assertThat(module1Feature.getName()).isEqualTo("Foo Module 1 Feature");
+ assertThat(module1Feature.getVersion()).isEqualTo("1.0-SNAPSHOT");
+ assertThat(module1Feature.getDescription()).isEqualTo("Description of Module 1 Feature");
+ assertThat(module1Feature.getSourceDirs()).contains("src");
+ assertThat(module1Feature.getTestDirs()).contains("tests");
+ assertThat(module1Feature.getBinaries()).contains("target/classes");
+ // and module properties must have been cleaned
+ assertThat(module1Feature.properties().get("module1.sonar.projectKey")).isNull();
+ assertThat(module1Feature.properties().get("module2.sonar.projectKey")).isNull();
+ // Check baseDir and workDir
+ assertThat(module1Feature.getBaseDir().getCanonicalFile())
+ .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature"));
+ assertThat(module1Feature.getWorkDir().getCanonicalFile())
+ .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar/com.foo.project_com.foo.project.module1.feature"));
+ }
+
+ @Test
+ public void should_log_a_warning_when_a_dropped_property_is_present() {
+ Map<String, String> props = loadProps("simple-project");
+ props.put("sonar.qualitygate", "somevalue");
+ AnalysisProperties bootstrapProps = new AnalysisProperties(props, null);
+ new ProjectReactorBuilder(bootstrapProps, mode).execute();
+
+ assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Property 'sonar.qualitygate' is not supported any more. It will be ignored.");
+ }
+
+ private Map<String, String> loadPropsFromFile(String filePath) throws IOException {
+ Properties props = new Properties();
+ try (FileInputStream fileInputStream = new FileInputStream(getResource(this.getClass(), filePath))) {
+ props.load(fileInputStream);
+ }
+ Map<String, String> result = new HashMap<>();
+ for (Map.Entry<Object, Object> entry : props.entrySet()) {
+ result.put(entry.getKey().toString(), entry.getValue().toString());
+ }
+ return result;
+ }
+
+ /**
+ * Search for a test resource in the classpath. For example getResource("org/sonar/MyClass/foo.txt");
+ *
+ * @param path the starting slash is optional
+ * @return the resource. Null if resource not found
+ */
+ public static File getResource(String path) {
+ String resourcePath = path;
+ if (!resourcePath.startsWith("/")) {
+ resourcePath = "/" + resourcePath;
+ }
+ URL url = ProjectReactorBuilderTest.class.getResource(resourcePath);
+ if (url != null) {
+ return FileUtils.toFile(url);
+ }
+ return null;
+ }
+
+ /**
+ * Search for a resource in the classpath. For example calling the method getResource(getClass(), "myTestName/foo.txt") from
+ * the class org.sonar.Foo loads the file $basedir/src/test/resources/org/sonar/Foo/myTestName/foo.txt
+ *
+ * @return the resource. Null if resource not found
+ */
+ public static File getResource(Class baseClass, String path) {
+ String resourcePath = StringUtils.replaceChars(baseClass.getCanonicalName(), '.', '/');
+ if (!path.startsWith("/")) {
+ resourcePath += "/";
+ }
+ resourcePath += path;
+ return getResource(resourcePath);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java
new file mode 100644
index 00000000000..fd5882467b5
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java
@@ -0,0 +1,185 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan;
+
+import static org.mockito.Mockito.when;
+
+import org.sonar.api.utils.MessageException;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import static org.mockito.Mockito.mock;
+
+public class ProjectReactorValidatorTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private ProjectReactorValidator validator;
+ private Settings settings;
+ private DefaultAnalysisMode mode;
+
+ @Before
+ public void prepare() {
+ mode = mock(DefaultAnalysisMode.class);
+ settings = new Settings();
+ validator = new ProjectReactorValidator(settings, mode);
+ }
+
+ @Test
+ public void not_fail_with_valid_key() {
+ validator.validate(createProjectReactor("foo"));
+ validator.validate(createProjectReactor("123foo"));
+ validator.validate(createProjectReactor("foo123"));
+ validator.validate(createProjectReactor("1Z3"));
+ validator.validate(createProjectReactor("a123"));
+ validator.validate(createProjectReactor("123a"));
+ validator.validate(createProjectReactor("1:2"));
+ validator.validate(createProjectReactor("3-3"));
+ validator.validate(createProjectReactor("-:"));
+ }
+
+ @Test
+ public void allow_slash_issues_mode() {
+ when(mode.isIssues()).thenReturn(true);
+ validator.validate(createProjectReactor("project/key"));
+
+ when(mode.isIssues()).thenReturn(false);
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("is not a valid project or module key");
+ validator.validate(createProjectReactor("project/key"));
+ }
+
+ @Test
+ public void not_fail_with_alphanumeric_key() {
+ ProjectReactor reactor = createProjectReactor("Foobar2");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void should_not_fail_with_dot_key() {
+ ProjectReactor reactor = createProjectReactor("foo.bar");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void not_fail_with_dash_key() {
+ ProjectReactor reactor = createProjectReactor("foo-bar");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void not_fail_with_colon_key() {
+ ProjectReactor reactor = createProjectReactor("foo:bar");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void not_fail_with_underscore_key() {
+ ProjectReactor reactor = createProjectReactor("foo_bar");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void fail_with_invalid_key() {
+ ProjectReactor reactor = createProjectReactor("foo$bar");
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("\"foo$bar\" is not a valid project or module key");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void fail_with_backslash_in_key() {
+ ProjectReactor reactor = createProjectReactor("foo\\bar");
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("\"foo\\bar\" is not a valid project or module key");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void not_fail_with_valid_branch() {
+ validator.validate(createProjectReactor("foo", "branch"));
+ validator.validate(createProjectReactor("foo", "Branch2"));
+ validator.validate(createProjectReactor("foo", "bra.nch"));
+ validator.validate(createProjectReactor("foo", "bra-nch"));
+ validator.validate(createProjectReactor("foo", "1"));
+ validator.validate(createProjectReactor("foo", "bra_nch"));
+ }
+
+ @Test
+ public void fail_with_invalid_branch() {
+ ProjectReactor reactor = createProjectReactor("foo", "bran#ch");
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("\"bran#ch\" is not a valid branch name");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void fail_with_colon_in_branch() {
+ ProjectReactor reactor = createProjectReactor("foo", "bran:ch");
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("\"bran:ch\" is not a valid branch name");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void fail_with_only_digits() {
+ ProjectReactor reactor = createProjectReactor("12345");
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("\"12345\" is not a valid project or module key");
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void fail_with_deprecated_sonar_phase() {
+ ProjectReactor reactor = createProjectReactor("foo");
+ settings.setProperty("sonar.phase", "phase");
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("\"sonar.phase\" is deprecated");
+ validator.validate(reactor);
+ }
+
+ private ProjectReactor createProjectReactor(String projectKey) {
+ ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey);
+ ProjectReactor reactor = new ProjectReactor(def);
+ return reactor;
+ }
+
+ private ProjectReactor createProjectReactor(String projectKey, String branch) {
+ ProjectDefinition def = ProjectDefinition.create()
+ .setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey)
+ .setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, branch);
+ ProjectReactor reactor = new ProjectReactor(def);
+ settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, branch);
+ return reactor;
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java
new file mode 100644
index 00000000000..512462923a1
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan;
+
+import org.junit.Test;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ServerExtension;
+import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.task.TaskExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectScanContainerTest {
+
+ @Test
+ public void should_add_only_batch_extensions() {
+ ProjectScanContainer.BatchExtensionFilter filter = new ProjectScanContainer.BatchExtensionFilter();
+
+ assertThat(filter.accept(new MyBatchExtension())).isTrue();
+ assertThat(filter.accept(MyBatchExtension.class)).isTrue();
+
+ assertThat(filter.accept(new MyProjectExtension())).isFalse();
+ assertThat(filter.accept(MyProjectExtension.class)).isFalse();
+ assertThat(filter.accept(new MyServerExtension())).isFalse();
+ assertThat(filter.accept(MyServerExtension.class)).isFalse();
+ assertThat(filter.accept(new MyTaskExtension())).isFalse();
+ assertThat(filter.accept(MyTaskExtension.class)).isFalse();
+ }
+
+ @InstantiationStrategy(InstantiationStrategy.PER_BATCH)
+ static class MyBatchExtension implements BatchExtension {
+
+ }
+
+ static class MyProjectExtension implements BatchExtension {
+
+ }
+
+ static class MyServerExtension implements ServerExtension {
+
+ }
+
+ static class MyTaskExtension implements TaskExtension {
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java
new file mode 100644
index 00000000000..7f9c6eecc4c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java
@@ -0,0 +1,142 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.bootstrap.GlobalMode;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.sonar.batch.bootstrap.GlobalSettings;
+import org.sonar.batch.repository.FileData;
+import org.sonar.batch.repository.ProjectRepositories;
+import org.sonar.scanner.protocol.input.GlobalRepositories;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ProjectSettingsTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private ProjectRepositories projectRef;
+ private ProjectDefinition project;
+ private GlobalSettings bootstrapProps;
+ private Table<String, String, FileData> emptyFileData;
+ private Table<String, String, String> emptySettings;
+
+ private GlobalMode globalMode;
+ private DefaultAnalysisMode mode;
+
+ @Before
+ public void prepare() {
+ emptyFileData = ImmutableTable.of();
+ emptySettings = ImmutableTable.of();
+ project = ProjectDefinition.create().setKey("struts");
+ globalMode = mock(GlobalMode.class);
+ mode = mock(DefaultAnalysisMode.class);
+ bootstrapProps = new GlobalSettings(new GlobalProperties(Collections.<String, String>emptyMap()), new PropertyDefinitions(), new GlobalRepositories(), globalMode);
+ }
+
+ @Test
+ public void should_load_project_props() {
+ project.setProperty("project.prop", "project");
+
+ projectRef = new ProjectRepositories(emptySettings, emptyFileData, null);
+ ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
+
+ assertThat(batchSettings.getString("project.prop")).isEqualTo("project");
+ }
+
+ @Test
+ public void should_load_project_root_settings() {
+ Table<String, String, String> settings = HashBasedTable.create();
+ settings.put("struts", "sonar.cpd.cross", "true");
+ settings.put("struts", "sonar.java.coveragePlugin", "jacoco");
+
+ projectRef = new ProjectRepositories(settings, emptyFileData, null);
+ ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
+ assertThat(batchSettings.getString("sonar.java.coveragePlugin")).isEqualTo("jacoco");
+ }
+
+ @Test
+ public void should_load_project_root_settings_on_branch() {
+ project.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "mybranch");
+
+ Table<String, String, String> settings = HashBasedTable.create();
+ settings.put("struts:mybranch", "sonar.cpd.cross", "true");
+ settings.put("struts:mybranch", "sonar.java.coveragePlugin", "jacoco");
+
+ projectRef = new ProjectRepositories(settings, emptyFileData, null);
+
+ ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
+
+ assertThat(batchSettings.getString("sonar.java.coveragePlugin")).isEqualTo("jacoco");
+ }
+
+ @Test
+ public void should_not_fail_when_accessing_secured_properties() {
+ Table<String, String, String> settings = HashBasedTable.create();
+ settings.put("struts", "sonar.foo.secured", "bar");
+ settings.put("struts", "sonar.foo.license.secured", "bar2");
+
+ projectRef = new ProjectRepositories(settings, emptyFileData, null);
+ ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
+
+ assertThat(batchSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2");
+ assertThat(batchSettings.getString("sonar.foo.secured")).isEqualTo("bar");
+ }
+
+ @Test
+ public void should_fail_when_accessing_secured_properties_in_issues_mode() {
+ Table<String, String, String> settings = HashBasedTable.create();
+ settings.put("struts", "sonar.foo.secured", "bar");
+ settings.put("struts", "sonar.foo.license.secured", "bar2");
+
+ when(mode.isIssues()).thenReturn(true);
+
+ projectRef = new ProjectRepositories(settings, emptyFileData, null);
+ ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
+
+ assertThat(batchSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2");
+ thrown.expect(MessageException.class);
+ thrown
+ .expectMessage(
+ "Access to the secured property 'sonar.foo.secured' is not possible in issues mode. The SonarQube plugin which requires this property must be deactivated in issues mode.");
+ batchSettings.getString("sonar.foo.secured");
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/WorkDirectoryCleanerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/WorkDirectoryCleanerTest.java
new file mode 100644
index 00000000000..8bd3d5c4e3a
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/WorkDirectoryCleanerTest.java
@@ -0,0 +1,79 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.home.cache.DirectoryLock;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class WorkDirectoryCleanerTest {
+ private WorkDirectoryCleaner cleaner;
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws IOException {
+ // create files to clean
+ temp.newFile();
+ File newFolder = temp.newFolder();
+ File fileInFolder = new File(newFolder, "test");
+ fileInFolder.createNewFile();
+
+ File lock = new File(temp.getRoot(), DirectoryLock.LOCK_FILE_NAME);
+ lock.createNewFile();
+
+ // mock project
+ ProjectReactor projectReactor = mock(ProjectReactor.class);
+ ProjectDefinition projectDefinition = mock(ProjectDefinition.class);
+ when(projectReactor.getRoot()).thenReturn(projectDefinition);
+ when(projectDefinition.getWorkDir()).thenReturn(temp.getRoot());
+
+ assertThat(temp.getRoot().list().length).isGreaterThan(1);
+ cleaner = new WorkDirectoryCleaner(projectReactor);
+ }
+
+ @Test
+ public void testNonExisting() {
+ temp.delete();
+ cleaner.execute();
+ }
+
+ @Test
+ public void testClean() {
+ File lock = new File(temp.getRoot(), DirectoryLock.LOCK_FILE_NAME);
+ cleaner.execute();
+
+ assertThat(temp.getRoot()).exists();
+ assertThat(lock).exists();
+ assertThat(temp.getRoot().list()).containsOnly(DirectoryLock.LOCK_FILE_NAME);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java
new file mode 100644
index 00000000000..c0f9fd3c062
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicatesTest.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AdditionalFilePredicatesTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void key() {
+ FilePredicate predicate = new AdditionalFilePredicates.KeyPredicate("struts:Action.java");
+
+ DefaultInputFile inputFile = new DefaultInputFile("struts", "Action.java");
+ assertThat(predicate.apply(inputFile)).isTrue();
+
+ inputFile = new DefaultInputFile("struts", "Filter.java");
+ assertThat(predicate.apply(inputFile)).isFalse();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java
new file mode 100644
index 00000000000..554f175695f
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java
@@ -0,0 +1,141 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.sonar.api.batch.fs.InputFile.Status;
+
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.resources.AbstractLanguage;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Languages;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Resource;
+import org.sonar.batch.index.BatchComponent;
+import org.sonar.batch.index.BatchComponentCache;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class ComponentIndexerTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+ private File baseDir;
+ private DefaultFileSystem fs;
+ private SonarIndex sonarIndex;
+ private AbstractLanguage cobolLanguage;
+ private Project project;
+ private ModuleFileSystemInitializer initializer;
+ private DefaultAnalysisMode mode;
+
+ @Before
+ public void prepare() throws IOException {
+ baseDir = temp.newFolder();
+ fs = new DefaultFileSystem(baseDir.toPath());
+ sonarIndex = mock(SonarIndex.class);
+ project = new Project("myProject");
+ initializer = mock(ModuleFileSystemInitializer.class);
+ mode = mock(DefaultAnalysisMode.class);
+ when(initializer.baseDir()).thenReturn(baseDir);
+ when(initializer.workingDir()).thenReturn(temp.newFolder());
+ cobolLanguage = new AbstractLanguage("cobol") {
+ @Override
+ public String[] getFileSuffixes() {
+ return new String[] {"cbl"};
+ }
+ };
+ }
+
+ @Test
+ public void should_index_java_files() throws IOException {
+ Languages languages = new Languages(Java.INSTANCE);
+ ComponentIndexer indexer = createIndexer(languages);
+ DefaultModuleFileSystem fs = new DefaultModuleFileSystem(project, null, mock(FileIndexer.class), initializer, indexer, mode);
+ fs.add(newInputFile("src/main/java/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false, Status.ADDED));
+ fs.add(newInputFile("src/main/java2/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false, Status.ADDED));
+ // should index even if filter is applied
+ fs.add(newInputFile("src/test/java/foo/bar/FooTest.java", "", "foo/bar/FooTest.java", "java", true, Status.SAME));
+
+ fs.index();
+
+ verify(sonarIndex).index(org.sonar.api.resources.File.create("src/main/java/foo/bar/Foo.java", Java.INSTANCE, false));
+ verify(sonarIndex).index(org.sonar.api.resources.File.create("src/main/java2/foo/bar/Foo.java", Java.INSTANCE, false));
+ verify(sonarIndex).index(argThat(new ArgumentMatcher<org.sonar.api.resources.File>() {
+ @Override
+ public boolean matches(Object arg0) {
+ org.sonar.api.resources.File javaFile = (org.sonar.api.resources.File) arg0;
+ return javaFile.getKey().equals("src/test/java/foo/bar/FooTest.java")
+ && javaFile.getPath().equals("src/test/java/foo/bar/FooTest.java")
+ && javaFile.getQualifier().equals(Qualifiers.UNIT_TEST_FILE);
+ }
+ }));
+ }
+
+ private ComponentIndexer createIndexer(Languages languages) {
+ BatchComponentCache resourceCache = mock(BatchComponentCache.class);
+ when(resourceCache.get(any(Resource.class)))
+ .thenReturn(new BatchComponent(2, org.sonar.api.resources.File.create("foo.php"), new BatchComponent(1, Directory.create("src"), null)));
+ return new ComponentIndexer(project, languages, sonarIndex, resourceCache);
+ }
+
+ @Test
+ public void should_index_cobol_files() throws IOException {
+ Languages languages = new Languages(cobolLanguage);
+ ComponentIndexer indexer = createIndexer(languages);
+ DefaultModuleFileSystem fs = new DefaultModuleFileSystem(project, null, mock(FileIndexer.class), initializer, indexer, mode);
+ fs.add(newInputFile("src/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false, Status.ADDED));
+ fs.add(newInputFile("src2/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false, Status.ADDED));
+ fs.add(newInputFile("src/test/foo/bar/FooTest.cbl", "", "foo/bar/FooTest.cbl", "cobol", true, Status.ADDED));
+
+ fs.index();
+
+ verify(sonarIndex).index(org.sonar.api.resources.File.create("/src/foo/bar/Foo.cbl", cobolLanguage, false));
+ verify(sonarIndex).index(org.sonar.api.resources.File.create("/src2/foo/bar/Foo.cbl", cobolLanguage, false));
+ verify(sonarIndex).index(org.sonar.api.resources.File.create("/src/test/foo/bar/FooTest.cbl", cobolLanguage, true));
+ }
+
+ private DefaultInputFile newInputFile(String path, String content, String sourceRelativePath, String languageKey, boolean unitTest, InputFile.Status status) throws IOException {
+ File file = new File(baseDir, path);
+ FileUtils.write(file, content);
+ return new DefaultInputFile("foo", path)
+ .setLanguage(languageKey)
+ .setType(unitTest ? InputFile.Type.TEST : InputFile.Type.MAIN)
+ .setStatus(status);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java
new file mode 100644
index 00000000000..46b70a17522
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java
@@ -0,0 +1,200 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.sonar.api.batch.fs.InputFile.Status;
+
+import org.junit.Before;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import com.google.common.collect.Lists;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Project;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class DefaultModuleFileSystemTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private Settings settings;
+ private FileIndexer fileIndexer;
+ private ModuleFileSystemInitializer initializer;
+ private ComponentIndexer componentIndexer;
+ private ModuleInputFileCache moduleInputFileCache;
+ private DefaultAnalysisMode mode;
+
+ @Before
+ public void setUp() {
+ settings = new Settings();
+ fileIndexer = mock(FileIndexer.class);
+ initializer = mock(ModuleFileSystemInitializer.class, Mockito.RETURNS_DEEP_STUBS);
+ componentIndexer = mock(ComponentIndexer.class);
+ moduleInputFileCache = mock(ModuleInputFileCache.class);
+ mode = mock(DefaultAnalysisMode.class);
+ }
+
+ @Test
+ public void test_equals_and_hashCode() throws Exception {
+ DefaultModuleFileSystem foo1 = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode);
+ DefaultModuleFileSystem foo2 = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode);
+ DefaultModuleFileSystem bar = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("bar"), settings, fileIndexer, initializer, componentIndexer, mode);
+ DefaultModuleFileSystem branch = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("bar", "branch", "My project"), settings, fileIndexer, initializer, componentIndexer, mode);
+
+ assertThat(foo1.moduleKey()).isEqualTo("foo");
+ assertThat(branch.moduleKey()).isEqualTo("bar:branch");
+ assertThat(foo1.equals(foo1)).isTrue();
+ assertThat(foo1.equals(foo2)).isTrue();
+ assertThat(foo1.equals(bar)).isFalse();
+ assertThat(foo1.equals("foo")).isFalse();
+ assertThat(foo1.hashCode()).isEqualTo(foo1.hashCode());
+ assertThat(foo1.hashCode()).isEqualTo(foo2.hashCode());
+ }
+
+ @Test
+ public void default_source_encoding() {
+ DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode);
+
+ assertThat(fs.sourceCharset()).isEqualTo(Charset.defaultCharset());
+ assertThat(fs.isDefaultJvmEncoding()).isTrue();
+ }
+
+ @Test
+ public void source_encoding_is_set() {
+ settings.setProperty(CoreProperties.ENCODING_PROPERTY, "Cp1124");
+ DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode);
+
+ assertThat(fs.encoding()).isEqualTo(Charset.forName("Cp1124"));
+ assertThat(fs.sourceCharset()).isEqualTo(Charset.forName("Cp1124"));
+
+ // This test fails when default Java encoding is "IBM AIX Ukraine". Sorry for that.
+ assertThat(fs.isDefaultJvmEncoding()).isFalse();
+ }
+
+ @Test
+ public void default_predicate_scan_only_changed() throws IOException {
+ when(mode.scanAllFiles()).thenReturn(false);
+
+ DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode);
+
+ File baseDir = temp.newFile();
+ InputFile mainInput = new DefaultInputFile("foo", "Main.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.MAIN);
+ InputFile testInput = new DefaultInputFile("foo", "Test.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.TEST);
+ InputFile mainSameInput = new DefaultInputFile("foo", "MainSame.java").setModuleBaseDir(baseDir.toPath())
+ .setType(InputFile.Type.TEST).setStatus(Status.SAME);
+ when(moduleInputFileCache.inputFiles()).thenReturn(Lists.newArrayList(mainInput, testInput, mainSameInput));
+
+ fs.index();
+ Iterable<InputFile> inputFiles = fs.inputFiles(fs.predicates().all());
+ assertThat(inputFiles).containsOnly(mainInput, testInput);
+
+ Iterable<InputFile> allInputFiles = fs.inputFiles();
+ assertThat(allInputFiles).containsOnly(mainInput, mainSameInput, testInput);
+ }
+
+ @Test
+ public void test_dirs() throws IOException {
+ File basedir = temp.newFolder("base");
+ File buildDir = temp.newFolder("build");
+ File workingDir = temp.newFolder("work");
+ File additionalFile = temp.newFile("Main.java");
+ File additionalTest = temp.newFile("Test.java");
+ when(initializer.baseDir()).thenReturn(basedir);
+ when(initializer.buildDir()).thenReturn(buildDir);
+ when(initializer.workingDir()).thenReturn(workingDir);
+ when(initializer.binaryDirs()).thenReturn(Arrays.asList(new File(basedir, "target/classes")));
+ File javaSrc = new File(basedir, "src/main/java");
+ javaSrc.mkdirs();
+ File groovySrc = new File(basedir, "src/main/groovy");
+ groovySrc.mkdirs();
+ when(initializer.sources()).thenReturn(Arrays.asList(javaSrc, groovySrc, additionalFile));
+ File javaTest = new File(basedir, "src/test/java");
+ javaTest.mkdirs();
+ when(initializer.tests()).thenReturn(Arrays.asList(javaTest, additionalTest));
+
+ DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode);
+
+ assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(basedir.getCanonicalPath());
+ assertThat(fs.workDir().getCanonicalPath()).isEqualTo(workingDir.getCanonicalPath());
+ assertThat(fs.buildDir().getCanonicalPath()).isEqualTo(buildDir.getCanonicalPath());
+ assertThat(fs.sourceDirs()).hasSize(2);
+ assertThat(fs.testDirs()).hasSize(1);
+ assertThat(fs.binaryDirs()).hasSize(1);
+ }
+
+ @Test
+ public void should_search_input_files() throws Exception {
+ DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode);
+
+ File baseDir = temp.newFile();
+ InputFile mainInput = new DefaultInputFile("foo", "Main.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.MAIN);
+ InputFile testInput = new DefaultInputFile("foo", "Test.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.TEST);
+ when(moduleInputFileCache.inputFiles()).thenReturn(Lists.newArrayList(mainInput, testInput));
+
+ fs.index();
+ Iterable<InputFile> inputFiles = fs.inputFiles(fs.predicates().hasType(InputFile.Type.MAIN));
+ assertThat(inputFiles).containsOnly(mainInput);
+
+ Iterable<File> files = fs.files(fs.predicates().hasType(InputFile.Type.MAIN));
+ assertThat(files).containsOnly(new File(baseDir, "Main.java"));
+ }
+
+ @Test
+ public void should_index() {
+ DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache,
+ new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode);
+
+ verifyZeroInteractions(fileIndexer);
+
+ fs.index();
+ verify(fileIndexer).index(fs);
+ verify(componentIndexer).execute(fs);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java
new file mode 100644
index 00000000000..e1905a12a9e
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileFiltersTest.java
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.apache.commons.io.FilenameUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.scan.filesystem.FileSystemFilter;
+import org.sonar.api.scan.filesystem.FileType;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DeprecatedFileFiltersTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ FileSystemFilter filter = mock(FileSystemFilter.class);
+
+ @Test
+ public void no_filters() {
+ DeprecatedFileFilters filters = new DeprecatedFileFilters();
+
+ InputFile inputFile = new DefaultInputFile("foo", "src/main/java/Foo.java");
+ assertThat(filters.accept(inputFile)).isTrue();
+ }
+
+ @Test
+ public void at_least_one_filter() throws Exception {
+ DeprecatedFileFilters filters = new DeprecatedFileFilters(new FileSystemFilter[] {filter});
+
+ File basedir = temp.newFolder();
+ File file = new File(basedir, "src/main/java/Foo.java");
+ InputFile inputFile = new DefaultInputFile("foo", "src/main/java/Foo.java")
+ .setModuleBaseDir(basedir.toPath())
+ .setType(InputFile.Type.MAIN);
+ when(filter.accept(eq(file), any(DeprecatedFileFilters.DeprecatedContext.class))).thenReturn(false);
+
+ assertThat(filters.accept(inputFile)).isFalse();
+
+ ArgumentCaptor<DeprecatedFileFilters.DeprecatedContext> argument = ArgumentCaptor.forClass(DeprecatedFileFilters.DeprecatedContext.class);
+ verify(filter).accept(eq(file), argument.capture());
+
+ DeprecatedFileFilters.DeprecatedContext context = argument.getValue();
+ assertThat(context.canonicalPath()).isEqualTo(FilenameUtils.separatorsToUnix(file.getAbsolutePath()));
+ assertThat(context.relativePath()).isEqualTo("src/main/java/Foo.java");
+ assertThat(context.type()).isEqualTo(FileType.MAIN);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java
new file mode 100644
index 00000000000..c7b4e6eaa5e
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java
@@ -0,0 +1,134 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.api.scan.filesystem.FileExclusions;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ExclusionFiltersTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void no_inclusions_nor_exclusions() throws IOException {
+ ExclusionFilters filter = new ExclusionFilters(new FileExclusions(new Settings()));
+ filter.prepare();
+
+ java.io.File file = temp.newFile();
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/FooDao.java").setModuleBaseDir(temp.newFolder().toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue();
+ assertThat(filter.accept(inputFile, InputFile.Type.TEST)).isTrue();
+ }
+
+ @Test
+ public void match_inclusion() throws IOException {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java");
+ ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings));
+ filter.prepare();
+
+ java.io.File file = temp.newFile();
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/FooDao.java").setModuleBaseDir(temp.newFolder().toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue();
+
+ inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/Foo.java").setModuleBaseDir(temp.newFolder().toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isFalse();
+ }
+
+ @Test
+ public void match_at_least_one_inclusion() throws IOException {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java,**/*Dto.java");
+ ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings));
+
+ filter.prepare();
+
+ java.io.File file = temp.newFile();
+
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/Foo.java").setModuleBaseDir(temp.newFolder().toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isFalse();
+
+ inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/FooDto.java").setModuleBaseDir(temp.newFolder().toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue();
+ }
+
+ @Test
+ public void match_exclusions() throws IOException {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "src/main/java/**/*");
+ settings.setProperty(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY, "src/test/java/**/*");
+ settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "**/*Dao.java");
+ ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings));
+
+ filter.prepare();
+
+ java.io.File file = temp.newFile();
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/FooDao.java").setModuleBaseDir(temp.newFolder().toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isFalse();
+
+ inputFile = new DefaultInputFile("foo", "src/main/java/com/mycompany/Foo.java").setModuleBaseDir(temp.newFolder().toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue();
+
+ // source exclusions do not apply to tests
+ inputFile = new DefaultInputFile("foo", "src/test/java/com/mycompany/FooDao.java").setModuleBaseDir(temp.newFolder().toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.TEST)).isTrue();
+ }
+
+ @Test
+ public void match_exclusion_by_absolute_path() throws IOException {
+ File baseDir = temp.newFile();
+ File excludedFile = new File(baseDir, "src/main/java/org/bar/Bar.java");
+
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "src/main/java/**/*");
+ settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "file:" + excludedFile.getCanonicalPath());
+ ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings));
+
+ filter.prepare();
+
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/main/java/org/bar/Foo.java").setModuleBaseDir(baseDir.toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isTrue();
+
+ inputFile = new DefaultInputFile("foo", "src/main/java/org/bar/Bar.java").setModuleBaseDir(baseDir.toPath());
+ assertThat(filter.accept(inputFile, InputFile.Type.MAIN)).isFalse();
+ }
+
+ @Test
+ public void trim_pattern() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, " **/*Dao.java ");
+ ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings));
+
+ assertThat(filter.prepareMainExclusions()[0].toString()).isEqualTo("**/*Dao.java");
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java
new file mode 100644
index 00000000000..b9c627e1ea8
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.config.Settings;
+import org.sonar.api.scan.filesystem.PathResolver;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class InputFileBuilderFactoryTest {
+ @Test
+ public void create_builder() {
+ PathResolver pathResolver = new PathResolver();
+ LanguageDetectionFactory langDetectionFactory = mock(LanguageDetectionFactory.class, Mockito.RETURNS_MOCKS);
+ StatusDetectionFactory statusDetectionFactory = mock(StatusDetectionFactory.class, Mockito.RETURNS_MOCKS);
+ DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class);
+
+ InputFileBuilderFactory factory = new InputFileBuilderFactory(ProjectDefinition.create().setKey("struts"), pathResolver, langDetectionFactory,
+ statusDetectionFactory, new Settings(), new FileMetadata());
+ InputFileBuilder builder = factory.create(fs);
+
+ assertThat(builder.langDetection()).isNotNull();
+ assertThat(builder.statusDetection()).isNotNull();
+ assertThat(builder.pathResolver()).isSameAs(pathResolver);
+ assertThat(builder.fs()).isSameAs(fs);
+ assertThat(builder.moduleKey()).isEqualTo("struts");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java
new file mode 100644
index 00000000000..eaec22ca37a
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java
@@ -0,0 +1,118 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.config.Settings;
+import org.sonar.api.scan.filesystem.PathResolver;
+import org.sonar.api.utils.PathUtils;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class InputFileBuilderTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ LanguageDetection langDetection = mock(LanguageDetection.class);
+ StatusDetection statusDetection = mock(StatusDetection.class);
+ DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class);
+
+ @Test
+ public void complete_input_file() throws Exception {
+ // file system
+ File basedir = temp.newFolder();
+ File srcFile = new File(basedir, "src/main/java/foo/Bar.java");
+ FileUtils.touch(srcFile);
+ FileUtils.write(srcFile, "single line");
+ when(fs.baseDir()).thenReturn(basedir);
+ when(fs.encoding()).thenReturn(StandardCharsets.UTF_8);
+
+ // lang
+ when(langDetection.language(any(InputFile.class))).thenReturn("java");
+
+ // status
+ when(statusDetection.status("foo", "src/main/java/foo/Bar.java", "6c1d64c0b3555892fe7273e954f6fb5a"))
+ .thenReturn(InputFile.Status.ADDED);
+
+ InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(),
+ langDetection, statusDetection, fs, new Settings(), new FileMetadata());
+ DefaultInputFile inputFile = builder.create(srcFile);
+ builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN);
+
+ assertThat(inputFile.type()).isEqualTo(InputFile.Type.MAIN);
+ assertThat(inputFile.file()).isEqualTo(srcFile.getAbsoluteFile());
+ assertThat(inputFile.absolutePath()).isEqualTo(PathUtils.sanitize(srcFile.getAbsolutePath()));
+ assertThat(inputFile.language()).isEqualTo("java");
+ assertThat(inputFile.key()).isEqualTo("struts:src/main/java/foo/Bar.java");
+ assertThat(inputFile.relativePath()).isEqualTo("src/main/java/foo/Bar.java");
+ assertThat(inputFile.lines()).isEqualTo(1);
+ }
+
+ @Test
+ public void return_null_if_file_outside_basedir() throws Exception {
+ // file system
+ File basedir = temp.newFolder();
+ File otherDir = temp.newFolder();
+ File srcFile = new File(otherDir, "src/main/java/foo/Bar.java");
+ FileUtils.touch(srcFile);
+ when(fs.baseDir()).thenReturn(basedir);
+
+ InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(),
+ langDetection, statusDetection, fs, new Settings(), new FileMetadata());
+ DefaultInputFile inputFile = builder.create(srcFile);
+
+ assertThat(inputFile).isNull();
+ }
+
+ @Test
+ public void return_null_if_language_not_detected() throws Exception {
+ // file system
+ File basedir = temp.newFolder();
+ File srcFile = new File(basedir, "src/main/java/foo/Bar.java");
+ FileUtils.touch(srcFile);
+ FileUtils.write(srcFile, "single line");
+ when(fs.baseDir()).thenReturn(basedir);
+ when(fs.encoding()).thenReturn(StandardCharsets.UTF_8);
+
+ // lang
+ when(langDetection.language(any(InputFile.class))).thenReturn(null);
+
+ InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(),
+ langDetection, statusDetection, fs, new Settings(), new FileMetadata());
+ DefaultInputFile inputFile = builder.create(srcFile);
+ inputFile = builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN);
+
+ assertThat(inputFile).isNull();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java
new file mode 100644
index 00000000000..23269eef7d3
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile.Status;
+import org.sonar.api.batch.fs.InputFile.Type;
+import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+
+import java.nio.charset.StandardCharsets;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class InputPathCacheTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Before
+ public void start() {
+ }
+
+ @After
+ public void stop() {
+ }
+
+ @Test
+ public void should_add_input_file() throws Exception {
+ InputPathCache cache = new InputPathCache();
+ DefaultInputFile fooFile = new DefaultInputFile("foo", "src/main/java/Foo.java").setModuleBaseDir(temp.newFolder().toPath());
+ cache.put("struts", fooFile);
+ cache.put("struts-core", new DefaultInputFile("foo", "src/main/java/Bar.java")
+ .setLanguage("bla")
+ .setType(Type.MAIN)
+ .setStatus(Status.ADDED)
+ .setLines(2)
+ .setCharset(StandardCharsets.UTF_8)
+ .setModuleBaseDir(temp.newFolder().toPath()));
+
+ DefaultInputFile loadedFile = (DefaultInputFile) cache.getFile("struts-core", "src/main/java/Bar.java");
+ assertThat(loadedFile.relativePath()).isEqualTo("src/main/java/Bar.java");
+ assertThat(loadedFile.charset()).isEqualTo(StandardCharsets.UTF_8);
+
+ assertThat(cache.filesByModule("struts")).hasSize(1);
+ assertThat(cache.filesByModule("struts-core")).hasSize(1);
+ assertThat(cache.allFiles()).hasSize(2);
+ for (InputPath inputPath : cache.allFiles()) {
+ assertThat(inputPath.relativePath()).startsWith("src/main/java/");
+ }
+
+ cache.remove("struts", fooFile);
+ assertThat(cache.allFiles()).hasSize(1);
+
+ cache.removeModule("struts");
+ assertThat(cache.filesByModule("struts")).hasSize(0);
+ assertThat(cache.filesByModule("struts-core")).hasSize(1);
+ assertThat(cache.allFiles()).hasSize(1);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java
new file mode 100644
index 00000000000..b30bb73036c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionFactoryTest.java
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Languages;
+import org.sonar.batch.repository.language.DefaultLanguagesRepository;
+import org.sonar.batch.repository.language.LanguagesRepository;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LanguageDetectionFactoryTest {
+ @Test
+ public void testCreate() throws Exception {
+ LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(Java.INSTANCE));
+ LanguageDetectionFactory factory = new LanguageDetectionFactory(new Settings(), languages);
+ LanguageDetection languageDetection = factory.create();
+ assertThat(languageDetection).isNotNull();
+ assertThat(languageDetection.patternsByLanguage()).hasSize(1);
+ assertThat(languageDetection.patternsByLanguage().containsKey("java")).isTrue();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java
new file mode 100644
index 00000000000..01a6a8aef3c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/LanguageDetectionTest.java
@@ -0,0 +1,213 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.Languages;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.repository.language.DefaultLanguagesRepository;
+import org.sonar.batch.repository.language.LanguagesRepository;
+
+import java.io.File;
+import java.io.IOException;
+
+import static junit.framework.Assert.fail;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.spy;
+
+public class LanguageDetectionTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void test_sanitizeExtension() throws Exception {
+ assertThat(LanguageDetection.sanitizeExtension(".cbl")).isEqualTo("cbl");
+ assertThat(LanguageDetection.sanitizeExtension(".CBL")).isEqualTo("cbl");
+ assertThat(LanguageDetection.sanitizeExtension("CBL")).isEqualTo("cbl");
+ assertThat(LanguageDetection.sanitizeExtension("cbl")).isEqualTo("cbl");
+ }
+
+ @Test
+ public void search_by_file_extension() throws Exception {
+ LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java", "jav"), new MockLanguage("cobol", "cbl", "cob")));
+ LanguageDetection detection = new LanguageDetection(new Settings(), languages);
+
+ assertThat(detection.language(newInputFile("Foo.java"))).isEqualTo("java");
+ assertThat(detection.language(newInputFile("src/Foo.java"))).isEqualTo("java");
+ assertThat(detection.language(newInputFile("Foo.JAVA"))).isEqualTo("java");
+ assertThat(detection.language(newInputFile("Foo.jav"))).isEqualTo("java");
+ assertThat(detection.language(newInputFile("Foo.Jav"))).isEqualTo("java");
+
+ assertThat(detection.language(newInputFile("abc.cbl"))).isEqualTo("cobol");
+ assertThat(detection.language(newInputFile("abc.CBL"))).isEqualTo("cobol");
+
+ assertThat(detection.language(newInputFile("abc.php"))).isNull();
+ assertThat(detection.language(newInputFile("abc"))).isNull();
+ }
+
+ @Test
+ public void should_not_fail_if_no_language() throws Exception {
+ LanguageDetection detection = spy(new LanguageDetection(new Settings(), new DefaultLanguagesRepository(new Languages())));
+ assertThat(detection.language(newInputFile("Foo.java"))).isNull();
+ }
+
+ @Test
+ public void plugin_can_declare_a_file_extension_twice_for_case_sensitivity() throws Exception {
+ LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("abap", "abap", "ABAP")));
+
+ LanguageDetection detection = new LanguageDetection(new Settings(), languages);
+ assertThat(detection.language(newInputFile("abc.abap"))).isEqualTo("abap");
+ }
+
+ @Test
+ public void language_with_no_extension() throws Exception {
+ // abap does not declare any file extensions.
+ // When analyzing an ABAP project, then all source files must be parsed.
+ LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java"), new MockLanguage("abap")));
+
+ // No side-effect on non-ABAP projects
+ LanguageDetection detection = new LanguageDetection(new Settings(), languages);
+ assertThat(detection.language(newInputFile("abc"))).isNull();
+ assertThat(detection.language(newInputFile("abc.abap"))).isNull();
+ assertThat(detection.language(newInputFile("abc.java"))).isEqualTo("java");
+
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "abap");
+ detection = new LanguageDetection(settings, languages);
+ assertThat(detection.language(newInputFile("abc"))).isEqualTo("abap");
+ assertThat(detection.language(newInputFile("abc.txt"))).isEqualTo("abap");
+ assertThat(detection.language(newInputFile("abc.java"))).isEqualTo("abap");
+ }
+
+ @Test
+ public void force_language_using_deprecated_property() throws Exception {
+ LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java"), new MockLanguage("php", "php")));
+
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "java");
+ LanguageDetection detection = new LanguageDetection(settings, languages);
+ assertThat(detection.language(newInputFile("abc"))).isNull();
+ assertThat(detection.language(newInputFile("abc.php"))).isNull();
+ assertThat(detection.language(newInputFile("abc.java"))).isEqualTo("java");
+ assertThat(detection.language(newInputFile("src/abc.java"))).isEqualTo("java");
+ }
+
+ @Test
+ public void fail_if_invalid_language() {
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("No language is installed with key 'unknown'. Please update property 'sonar.language'");
+
+ LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java"), new MockLanguage("php", "php")));
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "unknown");
+ new LanguageDetection(settings, languages);
+ }
+
+ @Test
+ public void fail_if_conflicting_language_suffix() throws Exception {
+ LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml")));
+ LanguageDetection detection = new LanguageDetection(new Settings(), languages);
+ try {
+ detection.language(newInputFile("abc.xhtml"));
+ fail();
+ } catch (MessageException e) {
+ assertThat(e.getMessage())
+ .contains("Language of file 'abc.xhtml' can not be decided as the file matches patterns of both ")
+ .contains("sonar.lang.patterns.web : **/*.xhtml")
+ .contains("sonar.lang.patterns.xml : **/*.xhtml");
+ }
+ }
+
+ @Test
+ public void solve_conflict_using_filepattern() throws Exception {
+ LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml")));
+
+ Settings settings = new Settings();
+ settings.setProperty("sonar.lang.patterns.xml", "xml/**");
+ settings.setProperty("sonar.lang.patterns.web", "web/**");
+ LanguageDetection detection = new LanguageDetection(settings, languages);
+ assertThat(detection.language(newInputFile("xml/abc.xhtml"))).isEqualTo("xml");
+ assertThat(detection.language(newInputFile("web/abc.xhtml"))).isEqualTo("web");
+ }
+
+ @Test
+ public void fail_if_conflicting_filepattern() throws Exception {
+ LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("abap", "abap"), new MockLanguage("cobol", "cobol")));
+ Settings settings = new Settings();
+ settings.setProperty("sonar.lang.patterns.abap", "*.abap,*.txt");
+ settings.setProperty("sonar.lang.patterns.cobol", "*.cobol,*.txt");
+
+ LanguageDetection detection = new LanguageDetection(settings, languages);
+
+ assertThat(detection.language(newInputFile("abc.abap"))).isEqualTo("abap");
+ assertThat(detection.language(newInputFile("abc.cobol"))).isEqualTo("cobol");
+ try {
+ detection.language(newInputFile("abc.txt"));
+ fail();
+ } catch (MessageException e) {
+ assertThat(e.getMessage())
+ .contains("Language of file 'abc.txt' can not be decided as the file matches patterns of both ")
+ .contains("sonar.lang.patterns.abap : *.abap,*.txt")
+ .contains("sonar.lang.patterns.cobol : *.cobol,*.txt");
+ }
+ }
+
+ private InputFile newInputFile(String path) throws IOException {
+ File basedir = temp.newFolder();
+ return new DefaultInputFile("foo", path).setModuleBaseDir(basedir.toPath());
+ }
+
+ static class MockLanguage implements Language {
+ private final String key;
+ private final String[] extensions;
+
+ MockLanguage(String key, String... extensions) {
+ this.key = key;
+ this.extensions = extensions;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public String getName() {
+ return key;
+ }
+
+ @Override
+ public String[] getFileSuffixes() {
+ return extensions;
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializerTest.java
new file mode 100644
index 00000000000..08dcf9197e5
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializerTest.java
@@ -0,0 +1,92 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.scan.filesystem.PathResolver;
+import org.sonar.api.utils.TempFolder;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class ModuleFileSystemInitializerTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ PathResolver pathResolver = new PathResolver();
+
+ @Test
+ public void test_default_directories() throws Exception {
+ File baseDir = temp.newFolder("base");
+ File workDir = temp.newFolder("work");
+ ProjectDefinition module = ProjectDefinition.create().setBaseDir(baseDir).setWorkDir(workDir);
+
+ ModuleFileSystemInitializer initializer = new ModuleFileSystemInitializer(module, mock(TempFolder.class), pathResolver);
+
+ assertThat(initializer.baseDir().getCanonicalPath()).isEqualTo(baseDir.getCanonicalPath());
+ assertThat(initializer.workingDir().getCanonicalPath()).isEqualTo(workDir.getCanonicalPath());
+ assertThat(initializer.sources()).isEmpty();
+ assertThat(initializer.tests()).isEmpty();
+ }
+
+ @Test
+ public void should_init_directories() throws IOException {
+ File baseDir = temp.newFolder("base");
+ File buildDir = temp.newFolder("build");
+ File sourceDir = new File(baseDir, "src/main/java");
+ FileUtils.forceMkdir(sourceDir);
+ File testDir = new File(baseDir, "src/test/java");
+ FileUtils.forceMkdir(testDir);
+ File binaryDir = new File(baseDir, "target/classes");
+ FileUtils.forceMkdir(binaryDir);
+
+ ProjectDefinition project = ProjectDefinition.create()
+ .setBaseDir(baseDir)
+ .setBuildDir(buildDir)
+ .addSourceDirs("src/main/java", "src/main/unknown")
+ .addTestDirs("src/test/java", "src/test/unknown")
+ .addBinaryDir("target/classes");
+
+ ModuleFileSystemInitializer initializer = new ModuleFileSystemInitializer(project, mock(TempFolder.class), pathResolver);
+
+ assertThat(initializer.baseDir().getCanonicalPath()).isEqualTo(baseDir.getCanonicalPath());
+ assertThat(initializer.buildDir().getCanonicalPath()).isEqualTo(buildDir.getCanonicalPath());
+ assertThat(initializer.sources()).hasSize(1);
+ assertThat(path(initializer.sources().get(0))).endsWith("src/main/java");
+ assertThat(initializer.tests()).hasSize(1);
+ assertThat(path(initializer.tests().get(0))).endsWith("src/test/java");
+ assertThat(initializer.binaryDirs()).hasSize(1);
+ assertThat(path(initializer.binaryDirs().get(0))).endsWith("target/classes");
+ }
+
+ private String path(File f) throws IOException {
+ return FilenameUtils.separatorsToUnix(f.getCanonicalPath());
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java
new file mode 100644
index 00000000000..b741f1eb344
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.sonar.batch.repository.ProjectRepositories;
+
+import org.junit.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class StatusDetectionFactoryTest {
+ @Test
+ public void testCreate() throws Exception {
+ StatusDetectionFactory factory = new StatusDetectionFactory(mock(ProjectRepositories.class));
+ StatusDetection detection = factory.create();
+ assertThat(detection).isNotNull();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java
new file mode 100644
index 00000000000..0601fa26b3b
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.sonar.batch.repository.FileData;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import org.junit.Test;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.batch.repository.ProjectRepositories;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class StatusDetectionTest {
+ @Test
+ public void detect_status() {
+ Table<String, String, String> t = ImmutableTable.of();
+ ProjectRepositories ref = new ProjectRepositories(t, createTable(), null);
+ StatusDetection statusDetection = new StatusDetection(ref);
+
+ assertThat(statusDetection.status("foo", "src/Foo.java", "ABCDE")).isEqualTo(InputFile.Status.SAME);
+ assertThat(statusDetection.status("foo", "src/Foo.java", "XXXXX")).isEqualTo(InputFile.Status.CHANGED);
+ assertThat(statusDetection.status("foo", "src/Other.java", "QWERT")).isEqualTo(InputFile.Status.ADDED);
+ }
+
+ private static Table<String, String, FileData> createTable() {
+ Table<String, String, FileData> t = HashBasedTable.create();
+
+ t.put("foo", "src/Foo.java", new FileData("ABCDE", "12345789"));
+ t.put("foo", "src/Bar.java", new FileData("FGHIJ", "123456789"));
+
+ return t;
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
new file mode 100644
index 00000000000..61f21529547
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
@@ -0,0 +1,231 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.measure;
+
+import java.util.Date;
+import java.util.Iterator;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.measure.MetricFinder;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric.Level;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.batch.index.AbstractCachesTest;
+import org.sonar.batch.index.Cache.Entry;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class MeasureCacheTest extends AbstractCachesTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private MetricFinder metricFinder;
+
+ private MeasureCache measureCache;
+
+ @Before
+ public void start() {
+ super.start();
+ metricFinder = mock(MetricFinder.class);
+ when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
+ measureCache = new MeasureCache(caches, metricFinder);
+ }
+
+ @Test
+ public void should_add_measure() {
+ Project p = new Project("struts");
+
+ assertThat(measureCache.entries()).hasSize(0);
+ assertThat(measureCache.byResource(p)).hasSize(0);
+
+ Measure m = new Measure(CoreMetrics.NCLOC, 1.0);
+ measureCache.put(p, m);
+
+ assertThat(measureCache.contains(p, m)).isTrue();
+ assertThat(measureCache.entries()).hasSize(1);
+ Iterator<Entry<Measure>> iterator = measureCache.entries().iterator();
+ iterator.hasNext();
+ Entry<Measure> next = iterator.next();
+ assertThat(next.value()).isEqualTo(m);
+ assertThat(next.key()[0]).isEqualTo("struts");
+
+ assertThat(measureCache.byResource(p)).hasSize(1);
+ assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m);
+ }
+
+ @Test
+ public void should_add_measure_with_big_data() {
+ Project p = new Project("struts");
+
+ assertThat(measureCache.entries()).hasSize(0);
+
+ assertThat(measureCache.byResource(p)).hasSize(0);
+
+ Measure m = new Measure(CoreMetrics.NCLOC, 1.0).setDate(new Date());
+ m.setAlertText("foooooooooooooooooooooooooooooooooooo");
+ StringBuilder data = new StringBuilder();
+ for (int i = 0; i < 1_048_575; i++) {
+ data.append("a");
+ }
+
+ m.setData(data.toString());
+
+ measureCache.put(p, m);
+
+ assertThat(measureCache.contains(p, m)).isTrue();
+ assertThat(measureCache.entries()).hasSize(1);
+ Iterator<Entry<Measure>> iterator = measureCache.entries().iterator();
+ iterator.hasNext();
+ Entry<Measure> next = iterator.next();
+ assertThat(next.value()).isEqualTo(m);
+ assertThat(next.key()[0]).isEqualTo("struts");
+
+ assertThat(measureCache.byResource(p)).hasSize(1);
+ assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m);
+ }
+
+ /**
+ * This test fails with stock PersisitIt.
+ */
+ @Test
+ public void should_add_measure_with_too_big_data_for_persistit_pre_patch() {
+ Project p = new Project("struts");
+
+ assertThat(measureCache.entries()).hasSize(0);
+
+ assertThat(measureCache.byResource(p)).hasSize(0);
+
+ Measure m = new Measure(CoreMetrics.NCLOC, 1.0).setDate(new Date());
+ StringBuilder data = new StringBuilder();
+ for (int i = 0; i < 500000; i++) {
+ data.append("some data");
+ }
+ m.setData(data.toString());
+
+ measureCache.put(p, m);
+
+ assertThat(measureCache.contains(p, m)).isTrue();
+ assertThat(measureCache.entries()).hasSize(1);
+ Iterator<Entry<Measure>> iterator = measureCache.entries().iterator();
+ iterator.hasNext();
+ Entry<Measure> next = iterator.next();
+ assertThat(next.value()).isEqualTo(m);
+ assertThat(next.key()[0]).isEqualTo("struts");
+
+ assertThat(measureCache.byResource(p)).hasSize(1);
+ assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m);
+
+ }
+
+ @Test
+ public void should_add_measure_with_too_big_data_for_persistit() {
+ Project p = new Project("struts");
+
+ assertThat(measureCache.entries()).hasSize(0);
+
+ assertThat(measureCache.byResource(p)).hasSize(0);
+
+ Measure m = new Measure(CoreMetrics.NCLOC, 1.0).setDate(new Date());
+ StringBuilder data = new StringBuilder(64 * 1024 * 1024 + 1);
+ // Limit is 64Mo
+ for (int i = 0; i < (64 * 1024 * 1024 + 1); i++) {
+ data.append('a');
+ }
+ m.setData(data.toString());
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Fail to put element in the cache measures");
+
+ measureCache.put(p, m);
+ }
+
+ @Test
+ public void should_get_measures() {
+ Project p = new Project("struts");
+ Resource dir = Directory.create("foo/bar").setEffectiveKey("struts:foo/bar");
+ Resource file1 = Directory.create("foo/bar/File1.txt").setEffectiveKey("struts:foo/bar/File1.txt");
+ Resource file2 = Directory.create("foo/bar/File2.txt").setEffectiveKey("struts:foo/bar/File2.txt");
+
+ assertThat(measureCache.entries()).hasSize(0);
+
+ assertThat(measureCache.byResource(p)).hasSize(0);
+ assertThat(measureCache.byResource(dir)).hasSize(0);
+
+ Measure mFile1 = new Measure(CoreMetrics.NCLOC, 1.0);
+ measureCache.put(file1, mFile1);
+ Measure mFile2 = new Measure(CoreMetrics.NCLOC, 3.0);
+ measureCache.put(file2, mFile2);
+
+ assertThat(measureCache.entries()).hasSize(2);
+ assertThat(measureCache.byResource(p)).hasSize(0);
+ assertThat(measureCache.byResource(dir)).hasSize(0);
+
+ Measure mDir = new Measure(CoreMetrics.NCLOC, 4.0);
+ measureCache.put(dir, mDir);
+
+ assertThat(measureCache.entries()).hasSize(3);
+ assertThat(measureCache.byResource(p)).hasSize(0);
+ assertThat(measureCache.byResource(dir)).hasSize(1);
+ assertThat(measureCache.byResource(dir).iterator().next()).isEqualTo(mDir);
+
+ Measure mProj = new Measure(CoreMetrics.NCLOC, 4.0);
+ measureCache.put(p, mProj);
+
+ assertThat(measureCache.entries()).hasSize(4);
+ assertThat(measureCache.byResource(p)).hasSize(1);
+ assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(mProj);
+ assertThat(measureCache.byResource(dir)).hasSize(1);
+ assertThat(measureCache.byResource(dir).iterator().next()).isEqualTo(mDir);
+ }
+
+ @Test
+ public void test_measure_coder() throws Exception {
+ Resource file1 = File.create("foo/bar/File1.txt").setEffectiveKey("struts:foo/bar/File1.txt");
+
+ Measure measure = new Measure(CoreMetrics.NCLOC, 3.14);
+ measure.setData("data");
+ measure.setAlertStatus(Level.ERROR);
+ measure.setAlertText("alert");
+ measure.setDate(new Date());
+ measure.setDescription("description");
+ measure.setPersistenceMode(null);
+ measure.setPersonId(3);
+ measure.setUrl("http://foo");
+ measure.setVariation1(11.0);
+ measure.setVariation2(12.0);
+ measure.setVariation3(13.0);
+ measure.setVariation4(14.0);
+ measure.setVariation5(15.0);
+ measureCache.put(file1, measure);
+
+ Measure savedMeasure = measureCache.byResource(file1).iterator().next();
+ assertThat(EqualsBuilder.reflectionEquals(measure, savedMeasure)).isTrue();
+
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java
new file mode 100644
index 00000000000..1a49bb94731
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java
@@ -0,0 +1,145 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.report;
+
+import javax.annotation.Nullable;
+
+import org.sonar.batch.issue.tracking.TrackedIssue;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.issue.IssueCache;
+import org.sonar.batch.scan.filesystem.InputPathCache;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ConsoleReportTest {
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private Settings settings;
+ private IssueCache issueCache;
+ private InputPathCache inputPathCache;
+ private ConsoleReport report;
+
+ @Before
+ public void prepare() {
+ settings = new Settings();
+ issueCache = mock(IssueCache.class);
+ inputPathCache = mock(InputPathCache.class);
+ report = new ConsoleReport(settings, issueCache, inputPathCache);
+ }
+
+ @Test
+ public void dontExecuteByDefault() {
+ report.execute();
+ for (String log : logTester.logs()) {
+ assertThat(log).doesNotContain(ConsoleReport.HEADER);
+ }
+ }
+
+ @Test
+ public void testNoFile() {
+ settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true");
+ when(inputPathCache.allFiles()).thenReturn(Collections.<InputFile>emptyList());
+ when(issueCache.all()).thenReturn(Collections.<TrackedIssue>emptyList());
+ report.execute();
+ assertThat(getReportLog()).isEqualTo(
+ "\n\n------------- Issues Report -------------\n\n" +
+ " No file analyzed\n" +
+ "\n-------------------------------------------\n\n");
+ }
+
+ @Test
+ public void testNoNewIssue() {
+ settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true");
+ when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php")));
+ when(issueCache.all()).thenReturn(Arrays.asList(createIssue(false, null)));
+ report.execute();
+ assertThat(getReportLog()).isEqualTo(
+ "\n\n------------- Issues Report -------------\n\n" +
+ " No new issue\n" +
+ "\n-------------------------------------------\n\n");
+ }
+
+ @Test
+ public void testOneNewIssue() {
+ settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true");
+ when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php")));
+ when(issueCache.all()).thenReturn(Arrays.asList(createIssue(true, Severity.BLOCKER)));
+ report.execute();
+ assertThat(getReportLog()).isEqualTo(
+ "\n\n------------- Issues Report -------------\n\n" +
+ " +1 issue\n\n" +
+ " +1 blocker\n" +
+ "\n-------------------------------------------\n\n");
+ }
+
+ @Test
+ public void testOneNewIssuePerSeverity() {
+ settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true");
+ when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php")));
+ when(issueCache.all()).thenReturn(Arrays.asList(
+ createIssue(true, Severity.BLOCKER),
+ createIssue(true, Severity.CRITICAL),
+ createIssue(true, Severity.MAJOR),
+ createIssue(true, Severity.MINOR),
+ createIssue(true, Severity.INFO)));
+ report.execute();
+ assertThat(getReportLog()).isEqualTo(
+ "\n\n------------- Issues Report -------------\n\n" +
+ " +5 issues\n\n" +
+ " +1 blocker\n" +
+ " +1 critical\n" +
+ " +1 major\n" +
+ " +1 minor\n" +
+ " +1 info\n" +
+ "\n-------------------------------------------\n\n");
+ }
+
+ private String getReportLog() {
+ for (String log : logTester.logs()) {
+ if (log.contains(ConsoleReport.HEADER)) {
+ return log;
+ }
+ }
+ throw new IllegalStateException("No console report");
+ }
+
+ private TrackedIssue createIssue(boolean isNew, @Nullable String severity) {
+ TrackedIssue issue = new TrackedIssue();
+ issue.setNew(isNew);
+ issue.setSeverity(severity);
+
+ return issue;
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java
new file mode 100644
index 00000000000..da798a2dbf1
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java
@@ -0,0 +1,174 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.report;
+
+import com.google.common.collect.Lists;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.TimeZone;
+import org.apache.commons.io.IOUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputDir;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.batch.rule.internal.RulesBuilder;
+import org.sonar.api.config.Settings;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.platform.Server;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.issue.IssueCache;
+import org.sonar.batch.issue.tracking.TrackedIssue;
+import org.sonar.batch.repository.user.UserRepositoryLoader;
+import org.sonar.batch.scan.filesystem.InputPathCache;
+import org.sonar.scanner.protocol.input.ScannerInput;
+
+import static net.javacrumbs.jsonunit.assertj.JsonAssert.assertThatJson;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class JSONReportTest {
+
+ private SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ JSONReport jsonReport;
+ Resource resource = mock(Resource.class);
+ DefaultFileSystem fs;
+ Server server = mock(Server.class);
+ Rules rules = mock(Rules.class);
+ Settings settings = new Settings();
+ IssueCache issueCache = mock(IssueCache.class);
+ private UserRepositoryLoader userRepository;
+
+ @Before
+ public void before() throws Exception {
+ fs = new DefaultFileSystem(temp.newFolder().toPath());
+ SIMPLE_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+02:00"));
+ when(resource.getEffectiveKey()).thenReturn("Action.java");
+ when(server.getVersion()).thenReturn("3.6");
+ userRepository = mock(UserRepositoryLoader.class);
+ DefaultInputDir inputDir = new DefaultInputDir("struts", "src/main/java/org/apache/struts");
+ DefaultInputFile inputFile = new DefaultInputFile("struts", "src/main/java/org/apache/struts/Action.java");
+ inputFile.setStatus(InputFile.Status.CHANGED);
+ InputPathCache fileCache = mock(InputPathCache.class);
+ when(fileCache.allFiles()).thenReturn(Arrays.<InputFile>asList(inputFile));
+ when(fileCache.allDirs()).thenReturn(Arrays.<InputDir>asList(inputDir));
+ Project rootModule = new Project("struts");
+ Project moduleA = new Project("struts-core");
+ moduleA.setParent(rootModule).setPath("core");
+ Project moduleB = new Project("struts-ui");
+ moduleB.setParent(rootModule).setPath("ui");
+
+ RulesBuilder builder = new RulesBuilder();
+ builder.add(RuleKey.of("squid", "AvoidCycles")).setName("Avoid Cycles");
+ rules = builder.build();
+ jsonReport = new JSONReport(settings, fs, server, rules, issueCache, rootModule, fileCache, userRepository);
+ }
+
+ @Test
+ public void should_write_json() throws Exception {
+ TrackedIssue issue = new TrackedIssue();
+ issue.setKey("200");
+ issue.setComponentKey("struts:src/main/java/org/apache/struts/Action.java");
+ issue.setRuleKey(RuleKey.of("squid", "AvoidCycles"));
+ issue.setMessage("There are 2 cycles");
+ issue.setSeverity("MINOR");
+ issue.setStatus(Issue.STATUS_OPEN);
+ issue.setResolution(null);
+ issue.setStartLine(1);
+ issue.setEndLine(2);
+ issue.setStartLineOffset(3);
+ issue.setEndLineOffset(4);
+ issue.setGap(3.14);
+ issue.setReporter("julien");
+ issue.setAssignee("simon");
+ issue.setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24"));
+ issue.setNew(false);
+ when(issueCache.all()).thenReturn(Lists.newArrayList(issue));
+ ScannerInput.User user1 = ScannerInput.User.newBuilder().setLogin("julien").setName("Julien").build();
+ ScannerInput.User user2 = ScannerInput.User.newBuilder().setLogin("simon").setName("Simon").build();
+ when(userRepository.load("julien")).thenReturn(user1);
+ when(userRepository.load("simon")).thenReturn(user2);
+
+ StringWriter writer = new StringWriter();
+ jsonReport.writeJson(writer);
+
+ assertThatJson(writer.toString()).isEqualTo(IOUtils.toString(this.getClass().getResource(this.getClass().getSimpleName() + "/report.json")));
+ }
+
+ @Test
+ public void should_exclude_resolved_issues() throws Exception {
+ RuleKey ruleKey = RuleKey.of("squid", "AvoidCycles");
+ TrackedIssue issue = new TrackedIssue();
+ issue.setKey("200");
+ issue.setComponentKey("struts:src/main/java/org/apache/struts/Action.java");
+ issue.setRuleKey(ruleKey);
+ issue.setStatus(Issue.STATUS_CLOSED);
+ issue.setResolution(Issue.RESOLUTION_FIXED);
+ issue.setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24"));
+ issue.setNew(false);
+ when(issueCache.all()).thenReturn(Lists.newArrayList(issue));
+
+ StringWriter writer = new StringWriter();
+ jsonReport.writeJson(writer);
+
+ assertThatJson(writer.toString()).isEqualTo(IOUtils.toString(this.getClass().getResource(this.getClass().getSimpleName() + "/report-without-resolved-issues.json")));
+ }
+
+ @Test
+ public void should_not_export_by_default() throws IOException {
+ File workDir = temp.newFolder("sonar");
+ fs.setWorkDir(workDir);
+
+ jsonReport.execute();
+
+ verifyZeroInteractions(issueCache);
+ }
+
+ @Test
+ public void should_export_issues_to_file() throws IOException {
+ File workDir = temp.newFolder("sonar");
+ fs.setWorkDir(workDir);
+
+ when(issueCache.all()).thenReturn(Collections.<TrackedIssue>emptyList());
+
+ settings.setProperty("sonar.report.export.path", "output.json");
+
+ jsonReport.execute();
+
+ assertThat(new File(workDir, "output.json")).exists();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/RuleNameProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/RuleNameProviderTest.java
new file mode 100644
index 00000000000..3c3af0e689c
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scan/report/RuleNameProviderTest.java
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.report;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import static org.mockito.Matchers.any;
+
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.batch.rule.Rule;
+import org.junit.Test;
+import org.junit.Before;
+import org.sonar.api.batch.rule.Rules;
+
+public class RuleNameProviderTest {
+ RuleNameProvider provider;
+ Rules rules;
+ Rule rule;
+ RuleKey ruleKey;
+
+ @Before
+ public void setUp() {
+ ruleKey = mock(RuleKey.class);
+ rule = mock(Rule.class);
+ rules = mock(Rules.class);
+ provider = new RuleNameProvider(rules);
+
+ when(ruleKey.rule()).thenReturn("ruleKey");
+ when(ruleKey.repository()).thenReturn("repoKey");
+
+ when(rule.name()).thenReturn("name");
+ when(rule.key()).thenReturn(ruleKey);
+
+ when(rules.find(any(RuleKey.class))).thenReturn(rule);
+ }
+
+ @Test
+ public void testNameForHTML() {
+ assertThat(provider.nameForHTML(rule)).isEqualTo(rule.name());
+ assertThat(provider.nameForHTML(ruleKey)).isEqualTo(rule.name());
+ }
+
+ @Test
+ public void testNameForJS() {
+ assertThat(provider.nameForJS("repoKey:ruleKey")).isEqualTo(rule.name());
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/scm/DefaultBlameOutputTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/scm/DefaultBlameOutputTest.java
new file mode 100644
index 00000000000..743f9175ba4
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/scm/DefaultBlameOutputTest.java
@@ -0,0 +1,94 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scm;
+
+import java.util.Arrays;
+import java.util.Date;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.fs.InputComponent;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.scm.BlameLine;
+import org.sonar.batch.index.BatchComponent;
+import org.sonar.batch.index.BatchComponentCache;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultBlameOutputTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private BatchComponentCache componentCache;
+
+ @Before
+ public void prepare() {
+ componentCache = mock(BatchComponentCache.class);
+ BatchComponent component = mock(BatchComponent.class);
+ when(component.batchId()).thenReturn(1);
+ when(componentCache.get(any(InputComponent.class))).thenReturn(component);
+ }
+
+ @Test
+ public void shouldNotFailIfNotSameNumberOfLines() {
+ InputFile file = new DefaultInputFile("foo", "src/main/java/Foo.java").setLines(10);
+
+ new DefaultBlameOutput(null, null, Arrays.asList(file)).blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy")));
+ }
+
+ @Test
+ public void shouldFailIfNotExpectedFile() {
+ InputFile file = new DefaultInputFile("foo", "src/main/java/Foo.java").setLines(1);
+
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("It was not expected to blame file src/main/java/Foo.java");
+
+ new DefaultBlameOutput(null, null, Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/main/java/Foo2.java")))
+ .blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy")));
+ }
+
+ @Test
+ public void shouldFailIfNullDate() {
+ InputFile file = new DefaultInputFile("foo", "src/main/java/Foo.java").setLines(1);
+
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Blame date is null for file src/main/java/Foo.java at line 1");
+
+ new DefaultBlameOutput(null, componentCache, Arrays.<InputFile>asList(file))
+ .blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy")));
+ }
+
+ @Test
+ public void shouldFailIfNullRevision() {
+ InputFile file = new DefaultInputFile("foo", "src/main/java/Foo.java").setLines(1);
+
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Blame revision is blank for file src/main/java/Foo.java at line 1");
+
+ new DefaultBlameOutput(null, componentCache, Arrays.<InputFile>asList(file))
+ .blameResult(file, Arrays.asList(new BlameLine().date(new Date()).author("guy")));
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java
new file mode 100644
index 00000000000..ba40858840d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java
@@ -0,0 +1,79 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.sensor;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.measure.MetricFinder;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.api.config.Settings;
+import org.sonar.api.measures.CoreMetrics;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultSensorContextTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private ActiveRules activeRules;
+ private DefaultFileSystem fs;
+ private DefaultSensorContext adaptor;
+ private Settings settings;
+ private SensorStorage sensorStorage;
+ private AnalysisMode analysisMode;
+
+ @Before
+ public void prepare() throws Exception {
+ activeRules = new ActiveRulesBuilder().build();
+ fs = new DefaultFileSystem(temp.newFolder().toPath());
+ MetricFinder metricFinder = mock(MetricFinder.class);
+ when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
+ when(metricFinder.findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
+ settings = new Settings();
+ sensorStorage = mock(SensorStorage.class);
+ analysisMode = mock(AnalysisMode.class);
+ adaptor = new DefaultSensorContext(mock(InputModule.class), settings, fs, activeRules, analysisMode, sensorStorage);
+ }
+
+ @Test
+ public void shouldProvideComponents() {
+ assertThat(adaptor.activeRules()).isEqualTo(activeRules);
+ assertThat(adaptor.fileSystem()).isEqualTo(fs);
+ assertThat(adaptor.settings()).isEqualTo(settings);
+
+ assertThat(adaptor.newIssue()).isNotNull();
+ assertThat(adaptor.newMeasure()).isNotNull();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
new file mode 100644
index 00000000000..caa16b11a19
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
@@ -0,0 +1,140 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.sensor;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.measure.MetricFinder;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
+import org.sonar.api.config.Settings;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.issue.ModuleIssues;
+import org.sonar.batch.report.ReportPublisher;
+import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.batch.sensor.coverage.CoverageExclusions;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultSensorStorageTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private ActiveRules activeRules;
+ private DefaultFileSystem fs;
+ private DefaultSensorStorage sensorStorage;
+ private Settings settings;
+ private ModuleIssues moduleIssues;
+ private Project project;
+ private MeasureCache measureCache;
+
+ private BatchComponentCache resourceCache;
+
+ @Before
+ public void prepare() throws Exception {
+ activeRules = new ActiveRulesBuilder().build();
+ fs = new DefaultFileSystem(temp.newFolder().toPath());
+ MetricFinder metricFinder = mock(MetricFinder.class);
+ when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
+ when(metricFinder.findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
+ settings = new Settings();
+ moduleIssues = mock(ModuleIssues.class);
+ project = new Project("myProject");
+ measureCache = mock(MeasureCache.class);
+ CoverageExclusions coverageExclusions = mock(CoverageExclusions.class);
+ when(coverageExclusions.accept(any(Resource.class), any(Measure.class))).thenReturn(true);
+ resourceCache = new BatchComponentCache();
+ sensorStorage = new DefaultSensorStorage(metricFinder,
+ moduleIssues, settings, fs, activeRules, coverageExclusions, resourceCache, mock(ReportPublisher.class), measureCache, mock(SonarCpdBlockIndex.class));
+ }
+
+ @Test
+ public void shouldFailIfUnknowMetric() {
+ InputFile file = new DefaultInputFile("foo", "src/Foo.php");
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Unknow metric with key: lines");
+
+ sensorStorage.store(new DefaultMeasure()
+ .on(file)
+ .forMetric(CoreMetrics.LINES)
+ .withValue(10));
+ }
+
+ @Test
+ public void shouldSaveFileMeasureToSensorContext() {
+ InputFile file = new DefaultInputFile("foo", "src/Foo.php");
+
+ ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class);
+ Resource sonarFile = File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
+ resourceCache.add(sonarFile, null).setInputComponent(file);
+ when(measureCache.put(eq(sonarFile), argumentCaptor.capture())).thenReturn(null);
+ sensorStorage.store(new DefaultMeasure()
+ .on(file)
+ .forMetric(CoreMetrics.NCLOC)
+ .withValue(10));
+
+ org.sonar.api.measures.Measure m = argumentCaptor.getValue();
+ assertThat(m.getValue()).isEqualTo(10.0);
+ assertThat(m.getMetric()).isEqualTo(CoreMetrics.NCLOC);
+ }
+
+ @Test
+ public void shouldSaveProjectMeasureToSensorContext() {
+ DefaultInputModule module = new DefaultInputModule(project.getEffectiveKey());
+ resourceCache.add(project, null).setInputComponent(module);
+
+ ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class);
+ when(measureCache.put(eq(project), argumentCaptor.capture())).thenReturn(null);
+
+ sensorStorage.store(new DefaultMeasure()
+ .on(module)
+ .forMetric(CoreMetrics.NCLOC)
+ .withValue(10));
+
+ org.sonar.api.measures.Measure m = argumentCaptor.getValue();
+ assertThat(m.getValue()).isEqualTo(10.0);
+ assertThat(m.getMetric()).isEqualTo(CoreMetrics.NCLOC);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/SensorOptimizerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/SensorOptimizerTest.java
new file mode 100644
index 00000000000..0fac9a72139
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/SensorOptimizerTest.java
@@ -0,0 +1,136 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.sensor;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
+import org.sonar.api.config.Settings;
+import org.sonar.api.rule.RuleKey;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SensorOptimizerTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private DefaultFileSystem fs;
+ private SensorOptimizer optimizer;
+ private Settings settings;
+
+ @Before
+ public void prepare() throws Exception {
+ fs = new DefaultFileSystem(temp.newFolder().toPath());
+ settings = new Settings();
+ optimizer = new SensorOptimizer(fs, new ActiveRulesBuilder().build(), settings);
+ }
+
+ @Test
+ public void should_run_analyzer_with_no_metadata() {
+ DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
+
+ assertThat(optimizer.shouldExecute(descriptor)).isTrue();
+ }
+
+ @Test
+ public void should_optimize_on_language() {
+ DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor()
+ .onlyOnLanguages("java", "php");
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+
+ fs.add(new DefaultInputFile("foo", "src/Foo.java").setLanguage("java"));
+ assertThat(optimizer.shouldExecute(descriptor)).isTrue();
+ }
+
+ @Test
+ public void should_optimize_on_type() {
+ DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor()
+ .onlyOnFileType(InputFile.Type.MAIN);
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+
+ fs.add(new DefaultInputFile("foo", "tests/FooTest.java").setType(InputFile.Type.TEST));
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+
+ fs.add(new DefaultInputFile("foo", "src/Foo.java").setType(InputFile.Type.MAIN));
+ assertThat(optimizer.shouldExecute(descriptor)).isTrue();
+ }
+
+ @Test
+ public void should_optimize_on_both_type_and_language() {
+ DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor()
+ .onlyOnLanguages("java", "php")
+ .onlyOnFileType(InputFile.Type.MAIN);
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+
+ fs.add(new DefaultInputFile("foo", "tests/FooTest.java").setLanguage("java").setType(InputFile.Type.TEST));
+ fs.add(new DefaultInputFile("foo", "src/Foo.cbl").setLanguage("cobol").setType(InputFile.Type.MAIN));
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+
+ fs.add(new DefaultInputFile("foo", "src/Foo.java").setLanguage("java").setType(InputFile.Type.MAIN));
+ assertThat(optimizer.shouldExecute(descriptor)).isTrue();
+ }
+
+ @Test
+ public void should_optimize_on_repository() {
+ DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor()
+ .createIssuesForRuleRepositories("squid");
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+
+ ActiveRules activeRules = new ActiveRulesBuilder()
+ .create(RuleKey.of("repo1", "foo"))
+ .activate()
+ .build();
+ optimizer = new SensorOptimizer(fs, activeRules, settings);
+
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+
+ activeRules = new ActiveRulesBuilder()
+ .create(RuleKey.of("repo1", "foo"))
+ .activate()
+ .create(RuleKey.of("squid", "rule"))
+ .activate()
+ .build();
+ optimizer = new SensorOptimizer(fs, activeRules, settings);
+ assertThat(optimizer.shouldExecute(descriptor)).isTrue();
+ }
+
+ @Test
+ public void should_optimize_on_settings() {
+ DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor()
+ .requireProperty("sonar.foo.reportPath");
+ assertThat(optimizer.shouldExecute(descriptor)).isFalse();
+
+ settings.setProperty("sonar.foo.reportPath", "foo");
+ assertThat(optimizer.shouldExecute(descriptor)).isTrue();
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/coverage/CoverageExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/coverage/CoverageExclusionsTest.java
new file mode 100644
index 00000000000..284226f9af0
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/sensor/coverage/CoverageExclusionsTest.java
@@ -0,0 +1,143 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.sensor.coverage;
+
+import org.junit.rules.TemporaryFolder;
+
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import com.google.common.collect.ImmutableMap;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.Settings;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.core.config.ExclusionProperties;
+
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class CoverageExclusionsTest {
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private Settings settings;
+ private DefaultFileSystem fs;
+
+ private CoverageExclusions filter;
+
+ @Before
+ public void createFilter() {
+ settings = new Settings(new PropertyDefinitions(ExclusionProperties.all()));
+ fs = new DefaultFileSystem(temp.getRoot());
+ filter = new CoverageExclusions(settings, fs);
+ }
+
+ @Test
+ public void shouldValidateStrictlyPositiveLine() {
+ DefaultInputFile file = new DefaultInputFile("module", "testfile");
+ Measure measure = mock(Measure.class);
+ Map<Integer, Integer> map = ImmutableMap.of(0, 3);
+
+ String data = KeyValueFormat.format(map);
+ when(measure.getMetric()).thenReturn(CoreMetrics.IT_CONDITIONS_BY_LINE);
+ when(measure.getData()).thenReturn(data);
+
+ fs.add(file);
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("must be > 0");
+ filter.validate(measure, "testfile");
+ }
+
+ @Test
+ public void shouldValidateFileExists() {
+ DefaultInputFile file = new DefaultInputFile("module", "testfile");
+ Measure measure = mock(Measure.class);
+ Map<Integer, Integer> map = ImmutableMap.of(0, 3);
+
+ String data = KeyValueFormat.format(map);
+ when(measure.getMetric()).thenReturn(CoreMetrics.IT_CONDITIONS_BY_LINE);
+ when(measure.getData()).thenReturn(data);
+
+ fs.add(file);
+
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("resource is not indexed as a file");
+ filter.validate(measure, "dummy");
+ }
+
+ @Test
+ public void shouldValidateMaxLine() {
+ DefaultInputFile file = new DefaultInputFile("module", "testfile");
+ file.setLines(10);
+ Measure measure = mock(Measure.class);
+ Map<Integer, Integer> map = ImmutableMap.of(11, 3);
+
+ String data = KeyValueFormat.format(map);
+ when(measure.getMetric()).thenReturn(CoreMetrics.COVERED_CONDITIONS_BY_LINE);
+ when(measure.getData()).thenReturn(data);
+
+ exception.expect(IllegalStateException.class);
+ filter.validate(measure, file);
+ }
+
+ @Test
+ public void shouldNotFilterNonCoverageMetrics() {
+ Measure otherMeasure = mock(Measure.class);
+ when(otherMeasure.getMetric()).thenReturn(CoreMetrics.LINES);
+ assertThat(filter.accept(mock(Resource.class), otherMeasure)).isTrue();
+ }
+
+ @Test
+ public void shouldFilterFileBasedOnPattern() {
+ Resource resource = File.create("src/org/polop/File.php", null, false);
+ Measure coverageMeasure = mock(Measure.class);
+ when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.LINES_TO_COVER);
+
+ settings.setProperty("sonar.coverage.exclusions", "src/org/polop/*");
+ filter.initPatterns();
+ assertThat(filter.accept(resource, coverageMeasure)).isFalse();
+ }
+
+ @Test
+ public void shouldNotFilterFileBasedOnPattern() {
+ Resource resource = File.create("src/org/polop/File.php", null, false);
+ Measure coverageMeasure = mock(Measure.class);
+ when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.COVERAGE);
+
+ settings.setProperty("sonar.coverage.exclusions", "src/org/other/*");
+ filter.initPatterns();
+ assertThat(filter.accept(resource, coverageMeasure)).isTrue();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/CodeColorizersTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/CodeColorizersTest.java
new file mode 100644
index 00000000000..2a9530e1142
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/CodeColorizersTest.java
@@ -0,0 +1,230 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.source;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+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.MultilinesDocTokenizer;
+import org.sonar.colorizer.RegexpTokenizer;
+import org.sonar.colorizer.StringTokenizer;
+import org.sonar.colorizer.Tokenizer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class CodeColorizersTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void testConvertToHighlighting() throws Exception {
+ CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList(new JavaScriptColorizerFormat(), new WebCodeColorizerFormat()));
+ File jsFile = new File(this.getClass().getResource("CodeColorizersTest/Person.js").toURI());
+ NewHighlighting highlighting = mock(NewHighlighting.class);
+
+ codeColorizers.toSyntaxHighlighting(jsFile, StandardCharsets.UTF_8, "js", highlighting);
+
+ verifyForJs(highlighting);
+ }
+
+ private void verifyForJs(NewHighlighting highlighting) {
+ verify(highlighting).highlight(0, 4, TypeOfText.CPP_DOC);
+ verify(highlighting).highlight(5, 11, TypeOfText.CPP_DOC);
+ verify(highlighting).highlight(12, 15, TypeOfText.CPP_DOC);
+ verify(highlighting).highlight(16, 19, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(29, 37, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(65, 69, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(85, 93, TypeOfText.COMMENT);
+ verify(highlighting).highlight(98, 102, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(112, 114, TypeOfText.STRING);
+ verify(highlighting).highlight(120, 124, TypeOfText.KEYWORD);
+ }
+
+ @Test
+ public void testConvertToHighlightingIgnoreBOM() throws Exception {
+ CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList(new JavaScriptColorizerFormat(), new WebCodeColorizerFormat()));
+
+ File fileWithBom = temp.newFile();
+ FileUtils.write(fileWithBom, "\uFEFF", "UTF-8");
+ File jsFile = new File(this.getClass().getResource("CodeColorizersTest/Person.js").toURI());
+ FileUtils.write(fileWithBom, FileUtils.readFileToString(jsFile), "UTF-8", true);
+
+ NewHighlighting highlighting = mock(NewHighlighting.class);
+ codeColorizers.toSyntaxHighlighting(fileWithBom, StandardCharsets.UTF_8, "js", highlighting);
+
+ verifyForJs(highlighting);
+ }
+
+ @Test
+ public void shouldSupportJavaIfNotProvidedByJavaPluginForBackwardCompatibility() throws Exception {
+ CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList());
+
+ File javaFile = new File(this.getClass().getResource("CodeColorizersTest/Person.java").toURI());
+
+ NewHighlighting highlighting = mock(NewHighlighting.class);
+ codeColorizers.toSyntaxHighlighting(javaFile, StandardCharsets.UTF_8, "java", highlighting);
+
+ verify(highlighting).highlight(0, 4, TypeOfText.STRUCTURED_COMMENT);
+ verify(highlighting).highlight(5, 11, TypeOfText.STRUCTURED_COMMENT);
+ verify(highlighting).highlight(12, 15, TypeOfText.STRUCTURED_COMMENT);
+ verify(highlighting).highlight(16, 22, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(23, 28, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(43, 50, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(51, 54, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(67, 78, TypeOfText.ANNOTATION);
+ verify(highlighting).highlight(81, 87, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(88, 92, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(97, 100, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(142, 146, TypeOfText.KEYWORD);
+ verify(highlighting).highlight(162, 170, TypeOfText.COMMENT);
+ }
+
+ @Test
+ public void testConvertHtmlToHighlightingWithMacEoL() throws Exception {
+ CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList(new JavaScriptColorizerFormat(), new WebCodeColorizerFormat()));
+ File htmlFile = new File(this.getClass().getResource("CodeColorizersTest/package.html").toURI());
+ SensorStorage sensorStorage = mock(SensorStorage.class);
+ DefaultHighlighting highlighting = new DefaultHighlighting(sensorStorage);
+ highlighting.onFile(new DefaultInputFile("FOO", "package.html")
+ .initMetadata(new FileMetadata().readMetadata(htmlFile, StandardCharsets.UTF_8)));
+
+ codeColorizers.toSyntaxHighlighting(htmlFile, StandardCharsets.UTF_8, "web", highlighting);
+
+ assertThat(highlighting.getSyntaxHighlightingRuleSet()).extracting("range.start.line", "range.start.lineOffset", "range.end.line", "range.end.lineOffset", "textType")
+ .containsExactly(
+ tuple(1, 0, 1, 132, TypeOfText.STRUCTURED_COMMENT),
+ tuple(2, 0, 2, 6, TypeOfText.KEYWORD),
+ tuple(3, 0, 3, 3, TypeOfText.KEYWORD),
+ tuple(4, 0, 4, 3, TypeOfText.KEYWORD),
+ // SONARWEB-26
+ tuple(5, 42, 12, 0, TypeOfText.STRING));
+ }
+
+ public static class JavaScriptColorizerFormat extends CodeColorizerFormat {
+
+ public JavaScriptColorizerFormat() {
+ super("js");
+ }
+
+ @Override
+ public List<Tokenizer> getTokenizers() {
+ return ImmutableList.<Tokenizer>of(
+ new StringTokenizer("<span class=\"s\">", "</span>"),
+ new CDocTokenizer("<span class=\"cd\">", "</span>"),
+ new JavadocTokenizer("<span class=\"cppd\">", "</span>"),
+ new CppDocTokenizer("<span class=\"cppd\">", "</span>"),
+ new KeywordsTokenizer("<span class=\"k\">", "</span>", "null",
+ "true",
+ "false",
+ "break",
+ "case",
+ "catch",
+ "class",
+ "continue",
+ "debugger",
+ "default",
+ "delete",
+ "do",
+ "extends",
+ "else",
+ "finally",
+ "for",
+ "function",
+ "if",
+ "import",
+ "in",
+ "instanceof",
+ "new",
+ "return",
+ "super",
+ "switch",
+ "this",
+ "throw",
+ "try",
+ "typeof",
+ "var",
+ "void",
+ "while",
+ "with",
+ "yield",
+ "const",
+ "enum",
+ "export"));
+ }
+
+ }
+
+ public class WebCodeColorizerFormat extends CodeColorizerFormat {
+
+ private final List<Tokenizer> tokenizers = new ArrayList<>();
+
+ public WebCodeColorizerFormat() {
+ super("web");
+ String tagAfter = "</span>";
+
+ // == tags ==
+ tokenizers.add(new RegexpTokenizer("<span class=\"k\">", tagAfter, "</?[:\\w]+>?"));
+ tokenizers.add(new RegexpTokenizer("<span class=\"k\">", tagAfter, ">"));
+
+ // == doctype ==
+ tokenizers.add(new RegexpTokenizer("<span class=\"j\">", tagAfter, "<!DOCTYPE.*>"));
+
+ // == comments ==
+ tokenizers.add(new MultilinesDocTokenizer("<!--", "-->", "<span class=\"j\">", tagAfter));
+ tokenizers.add(new MultilinesDocTokenizer("<%--", "--%>", "<span class=\"j\">", tagAfter));
+
+ // == expressions ==
+ tokenizers.add(new MultilinesDocTokenizer("<%@", "%>", "<span class=\"a\">", tagAfter));
+ tokenizers.add(new MultilinesDocTokenizer("<%", "%>", "<span class=\"a\">", tagAfter));
+
+ // == tag properties ==
+ tokenizers.add(new StringTokenizer("<span class=\"s\">", tagAfter));
+ }
+
+ @Override
+ public List<Tokenizer> getTokenizers() {
+ return tokenizers;
+ }
+
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java
new file mode 100644
index 00000000000..3a791783b1d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.source;
+
+import java.io.StringReader;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class DefaultHighlightableTest {
+
+ @Rule
+ public ExpectedException throwable = ExpectedException.none();
+
+ @Test
+ public void should_store_highlighting_rules() {
+ SensorStorage sensorStorage = mock(SensorStorage.class);
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php")
+ .initMetadata(new FileMetadata().readMetadata(new StringReader("azerty\nbla bla")));
+ DefaultHighlightable highlightablePerspective = new DefaultHighlightable(inputFile, sensorStorage, mock(AnalysisMode.class));
+ highlightablePerspective.newHighlighting().highlight(0, 6, "k").highlight(7, 10, "cppd").done();
+
+ ArgumentCaptor<DefaultHighlighting> argCaptor = ArgumentCaptor.forClass(DefaultHighlighting.class);
+ verify(sensorStorage).store(argCaptor.capture());
+ assertThat(argCaptor.getValue().getSyntaxHighlightingRuleSet()).hasSize(2);
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java
new file mode 100644
index 00000000000..86c7b7b316d
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java
@@ -0,0 +1,100 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.source;
+
+import org.sonar.api.batch.fs.TextRange;
+import com.google.common.base.Strings;
+
+import java.io.StringReader;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.source.Symbol;
+import org.sonar.api.source.Symbolizable;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DefaultSymbolTableTest {
+
+ @Rule
+ public ExpectedException throwable = ExpectedException.none();
+ private DefaultInputFile inputFile;
+
+ @Before
+ public void prepare() {
+ inputFile = new DefaultInputFile("foo", "src/Foo.php")
+ .initMetadata(new FileMetadata().readMetadata(new StringReader(Strings.repeat("azerty\n", 20))));
+ }
+
+ @Test
+ public void should_order_symbol_and_references() {
+
+ Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile);
+ Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20);
+ symbolTableBuilder.newReference(firstSymbol, 32);
+ Symbol secondSymbol = symbolTableBuilder.newSymbol(84, 92);
+ symbolTableBuilder.newReference(secondSymbol, 124);
+ Symbol thirdSymbol = symbolTableBuilder.newSymbol(55, 62);
+ symbolTableBuilder.newReference(thirdSymbol, 70);
+ Symbolizable.SymbolTable symbolTable = symbolTableBuilder.build();
+
+ assertThat(symbolTable.symbols()).containsExactly(firstSymbol, secondSymbol, thirdSymbol);
+ }
+
+ @Test
+ public void variable_length_references() {
+ Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile);
+ Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20);
+ symbolTableBuilder.newReference(firstSymbol, 32);
+ symbolTableBuilder.newReference(firstSymbol, 44, 47);
+
+ DefaultSymbolTable symbolTable = (DefaultSymbolTable) symbolTableBuilder.build();
+
+ assertThat(symbolTable.symbols()).containsExactly(firstSymbol);
+
+ Set<TextRange> references = symbolTable.getReferencesBySymbol().get(firstSymbol);
+ assertThat(references).containsExactly(range(32, 42), range(44, 47));
+ }
+
+ private TextRange range(int start, int end) {
+ return inputFile.newRange(start, end);
+ }
+
+ @Test
+ public void should_reject_reference_conflicting_with_declaration() {
+ throwable.expect(UnsupportedOperationException.class);
+
+ Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile);
+ Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
+ symbolTableBuilder.newReference(symbol, 15);
+ }
+
+ @Test
+ public void test_toString() throws Exception {
+ Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile);
+ Symbol symbol = symbolTableBuilder.newSymbol(10, 20);
+
+ assertThat(symbol.toString()).isEqualTo("Symbol{range=Range[from [line=2, lineOffset=3] to [line=3, lineOffset=6]]}");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java
new file mode 100644
index 00000000000..f19a4b04099
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.source;
+
+import com.google.common.base.Strings;
+import java.io.StringReader;
+import java.util.Map;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
+import org.sonar.api.source.Symbol;
+import org.sonar.api.source.Symbolizable;
+import org.sonar.batch.sensor.DefaultSensorStorage;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class DefaultSymbolizableTest {
+
+ @Test
+ public void should_update_cache_when_done() {
+
+ DefaultSensorStorage sensorStorage = mock(DefaultSensorStorage.class);
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php")
+ .initMetadata(new FileMetadata().readMetadata(new StringReader(Strings.repeat("azerty\n", 20))));
+
+ DefaultSymbolizable symbolPerspective = new DefaultSymbolizable(inputFile, sensorStorage, mock(AnalysisMode.class));
+ Symbolizable.SymbolTableBuilder symbolTableBuilder = symbolPerspective.newSymbolTableBuilder();
+ Symbol firstSymbol = symbolTableBuilder.newSymbol(4, 8);
+ symbolTableBuilder.newReference(firstSymbol, 12);
+ symbolTableBuilder.newReference(firstSymbol, 70);
+ Symbol otherSymbol = symbolTableBuilder.newSymbol(25, 33);
+ symbolTableBuilder.newReference(otherSymbol, 44);
+ symbolTableBuilder.newReference(otherSymbol, 60);
+ symbolTableBuilder.newReference(otherSymbol, 108);
+ Symbolizable.SymbolTable symbolTable = symbolTableBuilder.build();
+
+ symbolPerspective.setSymbolTable(symbolTable);
+
+ ArgumentCaptor<Map> argCaptor = ArgumentCaptor.forClass(Map.class);
+ verify(sensorStorage).store(eq(inputFile), argCaptor.capture());
+ // Map<Symbol, Set<TextRange>>
+ assertThat(argCaptor.getValue().keySet()).hasSize(2);
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java
new file mode 100644
index 00000000000..07ca60048b3
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.source;
+
+import org.junit.Test;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.source.Highlightable;
+import org.sonar.batch.index.BatchComponent;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class HighlightableBuilderTest {
+
+ @Test
+ public void should_load_default_perspective() {
+ Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c");
+ BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c"));
+
+ HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class), mock(AnalysisMode.class));
+ Highlightable perspective = builder.loadPerspective(Highlightable.class, component);
+
+ assertThat(perspective).isNotNull().isInstanceOf(DefaultHighlightable.class);
+ }
+
+ @Test
+ public void project_should_not_be_highlightable() {
+ BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts"));
+
+ HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class), mock(AnalysisMode.class));
+ Highlightable perspective = builder.loadPerspective(Highlightable.class, component);
+
+ assertThat(perspective).isNull();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java
new file mode 100644
index 00000000000..5cc7fe2d06b
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.source;
+
+import org.junit.Test;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.component.Perspective;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.source.Symbolizable;
+import org.sonar.batch.index.BatchComponent;
+import org.sonar.batch.sensor.DefaultSensorStorage;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class SymbolizableBuilderTest {
+
+ @Test
+ public void should_load_perspective() {
+ Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c");
+ BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c"));
+
+ SymbolizableBuilder perspectiveBuilder = new SymbolizableBuilder(mock(DefaultSensorStorage.class), mock(AnalysisMode.class));
+ Perspective perspective = perspectiveBuilder.loadPerspective(Symbolizable.class, component);
+
+ assertThat(perspective).isInstanceOf(Symbolizable.class);
+ }
+
+ @Test
+ public void project_should_not_be_highlightable() {
+ BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts"));
+
+ SymbolizableBuilder builder = new SymbolizableBuilder(mock(DefaultSensorStorage.class), mock(AnalysisMode.class));
+ Perspective perspective = builder.loadPerspective(Symbolizable.class, component);
+
+ assertThat(perspective).isNull();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/task/ListTaskTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/task/ListTaskTest.java
new file mode 100644
index 00000000000..0bf7ef0afc2
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/task/ListTaskTest.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import java.util.Arrays;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ListTaskTest {
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Test
+ public void should_list_available_tasks() {
+ Tasks tasks = mock(Tasks.class);
+ when(tasks.definitions()).thenReturn(Arrays.asList(
+ TaskDefinition.builder().key("foo").description("Foo").taskClass(FooTask.class).build(),
+ TaskDefinition.builder().key("purge").description("Purge database").taskClass(FakePurgeTask.class).build()));
+
+ ListTask task = new ListTask(tasks);
+
+ task.execute();
+
+ assertThat(logTester.logs(LoggerLevel.INFO)).hasSize(1);
+ assertThat(logTester.logs(LoggerLevel.INFO).get(0)).contains("Available tasks:", " - foo: Foo", " - purge: Purge database");
+ }
+
+ private static class FakePurgeTask implements Task {
+ public void execute() {
+ }
+ }
+
+ private static class FooTask implements Task {
+ public void execute() {
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/task/TasksTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/task/TasksTest.java
new file mode 100644
index 00000000000..b2d52b253f2
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/task/TasksTest.java
@@ -0,0 +1,90 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.task;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TasksTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void should_get_definitions() {
+ Tasks tasks = new Tasks(new TaskDefinition[] {ScanTask.DEFINITION, ListTask.DEFINITION});
+ assertThat(tasks.definitions()).hasSize(2);
+ }
+
+ @Test
+ public void should_get_definition_by_key() {
+ Tasks tasks = new Tasks(new TaskDefinition[] {ScanTask.DEFINITION, ListTask.DEFINITION});
+ tasks.start();
+ assertThat(tasks.definition(ListTask.DEFINITION.key())).isEqualTo(ListTask.DEFINITION);
+ }
+
+ @Test
+ public void should_return_null_if_task_not_found() {
+ Tasks tasks = new Tasks(new TaskDefinition[] {ScanTask.DEFINITION, ListTask.DEFINITION});
+
+ assertThat(tasks.definition("not-exists")).isNull();
+ }
+
+ @Test
+ public void should_fail_on_duplicated_keys() {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Task 'foo' is declared twice");
+
+ new Tasks(new TaskDefinition[] {
+ TaskDefinition.builder().key("foo").taskClass(FakeTask1.class).description("foo1").build(),
+ TaskDefinition.builder().key("foo").taskClass(FakeTask2.class).description("foo2").build()
+ });
+ }
+
+ @Test
+ public void should_fail_on_duplicated_class() {
+ Tasks tasks = new Tasks(new TaskDefinition[] {
+ TaskDefinition.builder().key("foo1").taskClass(FakeTask1.class).description("foo1").build(),
+ TaskDefinition.builder().key("foo2").taskClass(FakeTask1.class).description("foo1").build()
+ });
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Task 'org.sonar.batch.task.TasksTest$FakeTask1' is defined twice: first by 'foo1' and then by 'foo2'");
+
+ tasks.start();
+ }
+
+ private static class FakeTask1 implements Task {
+ public void execute() {
+ }
+ }
+
+ private static class FakeTask2 implements Task {
+ public void execute() {
+ }
+
+ }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/util/BatchUtilsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/util/BatchUtilsTest.java
new file mode 100644
index 00000000000..8de6036b5e3
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/util/BatchUtilsTest.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.util;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BatchUtilsTest {
+
+ @Test
+ public void encodeForUrl() throws Exception {
+ assertThat(BatchUtils.encodeForUrl(null)).isEqualTo("");
+ assertThat(BatchUtils.encodeForUrl("")).isEqualTo("");
+ assertThat(BatchUtils.encodeForUrl("foo")).isEqualTo("foo");
+ assertThat(BatchUtils.encodeForUrl("foo&bar")).isEqualTo("foo%26bar");
+ }
+
+ @Test
+
+ public void testDescribe() {
+ Object withToString = new Object() {
+ @Override
+ public String toString() {
+ return "desc";
+ }
+ };
+
+ Object withoutToString = new Object();
+
+ assertThat(BatchUtils.describe(withToString)).isEqualTo(("desc"));
+ assertThat(BatchUtils.describe(withoutToString)).isEqualTo("java.lang.Object");
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/util/ProgressReportTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/util/ProgressReportTest.java
new file mode 100644
index 00000000000..b21ad736fde
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/util/ProgressReportTest.java
@@ -0,0 +1,90 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.util;
+
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.log.LogTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProgressReportTest {
+ private static final String THREAD_NAME = "progress";
+ private ProgressReport progressReport;
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Before
+ public void setUp() {
+ progressReport = new ProgressReport(THREAD_NAME, 100);
+ }
+
+ @Test
+ public void die_on_stop() {
+ progressReport.start("start");
+ assertThat(isThreadAlive(THREAD_NAME)).isTrue();
+ progressReport.stop("stop");
+ assertThat(isThreadAlive(THREAD_NAME)).isFalse();
+ }
+
+ @Test
+ public void do_not_block_app() {
+ progressReport.start("start");
+ assertThat(isDaemon(THREAD_NAME)).isTrue();
+ progressReport.stop("stop");
+ }
+
+ @Test
+ public void do_log() {
+ progressReport.start("start");
+ progressReport.message("Some message");
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ progressReport.stop("stop");
+ assertThat(logTester.logs()).contains("Some message");
+ }
+
+ private static boolean isDaemon(String name) {
+ Thread t = getThread(name);
+ return (t != null) && t.isDaemon();
+ }
+
+ private static boolean isThreadAlive(String name) {
+ Thread t = getThread(name);
+ return (t != null) && t.isAlive();
+ }
+
+ private static Thread getThread(String name) {
+ Set<Thread> threads = Thread.getAllStackTraces().keySet();
+
+ for (Thread t : threads) {
+ if (t.getName().equals(name)) {
+ return t;
+ }
+ }
+ return null;
+ }
+}