From 4be016e61f4d1e4fed297dd7eb3b3ccb286f0f81 Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Tue, 8 Sep 2015 15:24:50 +0200 Subject: [PATCH] SONAR-6817 Issues mode should support analysis of projects not associated --- .../sonar-project.properties | 5 + .../src/main/xoo/sample/Sample.xoo | 16 ++ .../src/main/xoo/sample/Sample.xoo.measures | 11 ++ .../src/test/java/batch/IssuesModeTest.java | 43 +++-- .../protocol/input/ProjectRepositories.java | 4 + .../batch/analysis/DefaultAnalysisMode.java | 14 ++ .../batch/bootstrap/GlobalContainer.java | 16 +- .../sonar/batch/bootstrap/ServerClient.java | 22 ++- .../org/sonar/batch/bootstrapper/Batch.java | 6 +- .../cache/DefaultProjectCacheStatus.java | 16 +- .../cache/NonAssociatedCacheSynchronizer.java | 92 +++++++++++ .../batch/cache/PersistentCacheProvider.java | 4 +- .../sonar/batch/cache/ProjectCacheStatus.java | 8 +- .../batch/cache/ProjectCacheSynchronizer.java | 147 ++++++----------- .../batch/cache/ProjectSyncContainer.java | 62 +++++--- .../java/org/sonar/batch/cache/WSLoader.java | 21 ++- .../DefaultServerLineHashesLoader.java | 5 +- .../issue/tracking/LocalIssueTracking.java | 11 +- ...=> DefaultProjectRepositoriesFactory.java} | 46 ++++-- .../DefaultProjectRepositoriesLoader.java | 11 +- .../DefaultProjectSettingsLoader.java | 56 +++++++ .../DefaultQualityProfileLoader.java | 50 ++++++ .../ProjectRepositoriesFactory.java | 28 ++++ .../ProjectRepositoriesFactoryProvider.java | 41 +++++ .../repository/ProjectRepositoriesLoader.java | 4 +- .../repository/ProjectSettingsLoader.java | 28 ++++ .../repository/ProjectSettingsProvider.java | 64 ++++++++ .../batch/repository/ProjectSettingsRepo.java | 66 ++++++++ .../repository/QualityProfileLoader.java | 30 ++++ .../repository/QualityProfileProvider.java | 48 ++++++ .../SyncProjectRepositoriesFactory.java | 74 +++++++++ .../sonar/batch/rule/ActiveRulesLoader.java | 28 ++++ .../sonar/batch/rule/ActiveRulesProvider.java | 23 ++- .../batch/rule/DefaultActiveRulesLoader.java | 43 +++++ .../org/sonar/batch/rule/ModuleQProfiles.java | 5 +- .../sonar/batch/scan/ModuleScanContainer.java | 1 - .../org/sonar/batch/scan/ModuleSettings.java | 14 +- .../org/sonar/batch/scan/ProjectLock.java | 1 - .../batch/scan/ProjectReactorBuilder.java | 21 ++- .../batch/scan/ProjectReactorValidator.java | 3 +- .../batch/scan/ProjectScanContainer.java | 26 ++- .../org/sonar/batch/scan/ProjectSettings.java | 8 +- .../scan/filesystem/StatusDetection.java | 11 +- .../filesystem/StatusDetectionFactory.java | 7 +- .../java/org/sonar/batch/scm/ScmSensor.java | 12 +- .../cache/DefaultProjectCacheStatusTest.java | 13 +- .../NonAssociatedCacheSynchronizerTest.java | 91 +++++++++++ .../cache/PersistentCacheProviderTest.java | 12 +- .../cache/ProjectCacheSynchronizerTest.java | 130 +++++++++++---- .../batch/cache/ProjectSyncContainerTest.java | 26 +-- .../DefaultServerLineHashesLoaderTest.java | 14 +- .../batch/mediumtest/BatchMediumTester.java | 67 ++++++-- .../batch/mediumtest/cache/CacheSyncTest.java | 53 ++++++- .../issuesmode/NonAssociatedProject.java | 90 +++++++++++ .../DefaultProjectRepositoriesLoaderTest.java | 18 +-- .../DefaultProjectSettingsLoaderTest.java | 77 +++++++++ .../DefaultQualityProfileLoaderTest.java | 77 +++++++++ .../QualityProfileProviderTest.java | 94 +++++++++++ .../rule/DefaultActiveRulesLoaderTest.java | 70 ++++++++ .../sonar/batch/scan/ModuleSettingsTest.java | 36 +++-- .../batch/scan/ProjectReactorBuilderTest.java | 98 +++++++----- .../scan/ProjectReactorValidatorTest.java | 13 +- .../sonar/batch/scan/ProjectSettingsTest.java | 48 ++++-- .../StatusDetectionFactoryTest.java | 6 +- .../scan/filesystem/StatusDetectionTest.java | 22 ++- .../sonar/it/samples/modules/a1/HelloA1.xoo | 16 ++ .../sonar/it/samples/modules/a2/HelloA2.xoo | 12 ++ .../sonar/it/samples/modules/b1/HelloB1.xoo | 12 ++ .../sonar/it/samples/modules/b2/HelloB2.xoo | 12 ++ .../sonar-project.properties | 31 ++++ .../api_sources_hash_GitScmProvider.text | 49 ------ .../api_sources_hash_JGitBlameCommand.text | 149 ------------------ .../modules/module1/sources/Fake.java | 1 + .../sonar-project.properties | 14 ++ .../java/org/sonar/home/cache/FileCache.java | 2 +- .../java/org/sonar/home/cache/FileHashes.java | 2 +- .../org/sonar/home/cache/PersistentCache.java | 67 +++++++- 77 files changed, 2076 insertions(+), 598 deletions(-) create mode 100644 it/it-projects/shared/xoo-sample-non-associated/sonar-project.properties create mode 100644 it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo create mode 100644 it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo.measures create mode 100644 sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java rename sonar-batch/src/main/java/org/sonar/batch/repository/{ProjectRepositoriesProvider.java => DefaultProjectRepositoriesFactory.java} (57%) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectSettingsLoaderTest.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java create mode 100644 sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a1/src/main/xoo/com/sonar/it/samples/modules/a1/HelloA1.xoo create mode 100644 sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a2/src/main/xoo/com/sonar/it/samples/modules/a2/HelloA2.xoo create mode 100644 sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b1/src/main/xoo/com/sonar/it/samples/modules/b1/HelloB1.xoo create mode 100644 sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b2/src/main/xoo/com/sonar/it/samples/modules/b2/HelloB2.xoo create mode 100644 sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/sonar-project.properties delete mode 100644 sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_GitScmProvider.text delete mode 100644 sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_JGitBlameCommand.text create mode 100644 sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/modules/module1/sources/Fake.java create mode 100644 sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/sonar-project.properties diff --git a/it/it-projects/shared/xoo-sample-non-associated/sonar-project.properties b/it/it-projects/shared/xoo-sample-non-associated/sonar-project.properties new file mode 100644 index 000000000000..71b83e48b9f9 --- /dev/null +++ b/it/it-projects/shared/xoo-sample-non-associated/sonar-project.properties @@ -0,0 +1,5 @@ +#sonar.projectKey=sample +sonar.projectName=Sample +sonar.projectVersion=1.0-SNAPSHOT +sonar.sources=src/main/xoo +sonar.language=xoo diff --git a/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo b/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo new file mode 100644 index 000000000000..41871e123a3f --- /dev/null +++ b/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo @@ -0,0 +1,16 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + if (foo == bar) { + return "hello"; + } else { + throw new IllegalStateException(); + } + } +} diff --git a/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo.measures b/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo.measures new file mode 100644 index 000000000000..3f73ea8f695b --- /dev/null +++ b/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo.measures @@ -0,0 +1,11 @@ +ncloc:13 +#Used by dashboard/widgets tests +complexity:3 +complexity_in_classes:3 +classes:1 +comment_lines:3 +public_api:5 +public_undocumented_api:2 +duplicated_files:1 +duplicated_blocks:2 +duplicated_lines:3 diff --git a/it/it-tests/src/test/java/batch/IssuesModeTest.java b/it/it-tests/src/test/java/batch/IssuesModeTest.java index 651546c05350..5e3c0660b669 100644 --- a/it/it-tests/src/test/java/batch/IssuesModeTest.java +++ b/it/it-tests/src/test/java/batch/IssuesModeTest.java @@ -69,28 +69,45 @@ public void deleteData() throws IOException { } @Test - public void issuesAnalysisOnNewProject() throws IOException { + public void issues_analysis_on_new_project() throws IOException { restoreProfile("one-issue-per-line.xml"); orchestrator.getServer().provisionProject("sample", "xoo-sample"); orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line"); SonarRunner runner = configureRunnerIssues("shared/xoo-sample"); - orchestrator.executeBuild(runner); + BuildResult result = orchestrator.executeBuild(runner); + assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17); } - + @Test - public void invalidIncrementalMode() throws IOException { + public void invalid_incremental_mode() throws IOException { restoreProfile("one-issue-per-line.xml"); orchestrator.getServer().provisionProject("sample", "xoo-sample"); orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line"); SonarRunner runner = configureRunner("shared/xoo-sample"); runner.setProperty("sonar.analysis.mode", "incremental"); - + thrown.expect(BuildFailureException.class); BuildResult res = orchestrator.executeBuild(runner); - + assertThat(res.getLogs()).contains("Invalid analysis mode: incremental. This mode was removed in SonarQube 5.2"); } + @Test + public void non_associated_mode() throws IOException { + restoreProfile("one-issue-per-line.xml"); + setDefaultQualityProfile("xoo", "one-issue-per-line"); + SonarRunner runner = configureRunnerIssues("shared/xoo-sample-non-associated"); + BuildResult result = orchestrator.executeBuild(runner); + + assertThat(result.getLogs()).contains("is not associated"); + assertThat(result.getLogs()).contains("Cache not found, synchronizing data"); + assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17); + + result = orchestrator.executeBuild(runner); + assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17); + assertThat(result.getLogs()).contains("Found cache"); + } + // SONAR-5715 @Test public void test_issues_mode_on_project_with_space_in_filename() throws IOException { @@ -329,16 +346,16 @@ public BuildResult call() throws Exception { boolean expectedError = false; for (Future result : executorService.invokeAll(tasks)) { try { - result.get(); - } catch(ExecutionException e) { - if(e.getCause() instanceof BuildFailureException) { + result.get(); + } catch (ExecutionException e) { + if (e.getCause() instanceof BuildFailureException) { BuildFailureException bfe = (BuildFailureException) e.getCause(); assertThat(bfe.getResult().getLogs()).contains("Another SonarQube analysis is already in progress for this project"); expectedError = true; } } } - if(!expectedError) { + if (!expectedError) { fail("At least one of the threads should have failed"); } } @@ -370,4 +387,10 @@ private SonarRunner configureRunner(String projectDir, String... props) throws I return runner; } + private void setDefaultQualityProfile(String languageKey, String profileName) { + orchestrator.getServer().adminWsClient().post("api/qualityprofiles/set_default", + "language", languageKey, + "profileName", profileName); + } + } diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectRepositories.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectRepositories.java index 8e59ccd1be77..862a21152d8c 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectRepositories.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectRepositories.java @@ -45,6 +45,10 @@ public class ProjectRepositories { public Map settings(String moduleKey) { return settingsByModule.containsKey(moduleKey) ? settingsByModule.get(moduleKey) : Collections.emptyMap(); } + + public Map> settings() { + return settingsByModule; + } public ProjectRepositories addSettings(String moduleKey, Map settings) { Map existingSettings = settingsByModule.get(moduleKey); diff --git a/sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java b/sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java index c49b4f483275..a01928f35291 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java +++ b/sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java @@ -38,6 +38,7 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi private static final Logger LOG = LoggerFactory.getLogger(DefaultAnalysisMode.class); private boolean mediumTestMode; + private boolean notAssociated; public DefaultAnalysisMode(GlobalProperties globalProps, AnalysisProperties props) { init(globalProps.properties(), props.properties()); @@ -47,6 +48,10 @@ public boolean isMediumTest() { return mediumTestMode; } + public boolean isNotAssociated() { + return notAssociated; + } + private void init(Map globalProps, Map analysisProps) { // make sure analysis is consistent with global properties boolean globalPreview = isIssues(globalProps); @@ -64,6 +69,7 @@ private void load(Map globalProps, Map analysisP validate(mode); issues = CoreProperties.ANALYSIS_MODE_ISSUES.equals(mode) || CoreProperties.ANALYSIS_MODE_PREVIEW.equals(mode); mediumTestMode = "true".equals(getPropertyWithFallback(analysisProps, globalProps, FakePluginInstaller.MEDIUM_TEST_ENABLED)); + notAssociated = issues && rootProjectKeyMissing(analysisProps); } public void printMode() { @@ -77,6 +83,9 @@ public void printMode() { if (mediumTestMode) { LOG.info("Medium test mode"); } + if (notAssociated) { + LOG.info("Project is not associated with the server"); + } } private static String getPropertyWithFallback(Map props1, Map props2, String key) { @@ -93,4 +102,9 @@ private static boolean isIssues(Map props) { return CoreProperties.ANALYSIS_MODE_ISSUES.equals(mode); } + private static boolean rootProjectKeyMissing(Map props) { + // ProjectReactorBuilder depends on this class, so it will only create this property later + return !props.containsKey(CoreProperties.PROJECT_KEY_PROPERTY); + } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java index 67e5843c8ee2..5469343c55da 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java @@ -123,14 +123,22 @@ private void installPlugins() { public void executeAnalysis(Map analysisProperties, Object... components) { AnalysisProperties props = new AnalysisProperties(analysisProperties, this.getComponentByType(GlobalProperties.class).property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)); if (isIssuesMode(props)) { - new ProjectSyncContainer(this, props, false).execute(); + String projectKey = getProjectKeyWithBranch(props); + new ProjectSyncContainer(this, projectKey, false).execute(); } new ProjectScanContainer(this, props, components).execute(); } - public void syncProject(Map analysisProperties, boolean force) { - AnalysisProperties props = new AnalysisProperties(analysisProperties, this.getComponentByType(GlobalProperties.class).property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)); - new ProjectSyncContainer(this, props, force).execute(); + private static String getProjectKeyWithBranch(AnalysisProperties props) { + String projectKey = props.property(CoreProperties.PROJECT_KEY_PROPERTY); + if (projectKey != null && props.property(CoreProperties.PROJECT_BRANCH_PROPERTY) != null) { + projectKey = projectKey + ":" + props.property(CoreProperties.PROJECT_BRANCH_PROPERTY); + } + return projectKey; + } + + public void syncProject(String projectKey, boolean force) { + new ProjectSyncContainer(this, projectKey, force).execute(); } private boolean isIssuesMode(AnalysisProperties props) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java index 0faef0826277..901e2d2cac22 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java @@ -19,6 +19,9 @@ */ package org.sonar.batch.bootstrap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Strings; @@ -27,10 +30,13 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; @@ -55,8 +61,8 @@ */ @BatchSide public class ServerClient { - private static final String GET = "GET"; + private static final Logger LOG = LoggerFactory.getLogger(ServerClient.class); private GlobalProperties props; private DefaultHttpDownloader.BaseHttpDownloader downloader; @@ -68,6 +74,20 @@ public ServerClient(GlobalProperties settings, EnvironmentInformation env) { public String getURL() { return StringUtils.removeEnd(StringUtils.defaultIfBlank(props.property("sonar.host.url"), "http://localhost:9000"), "/"); } + + public String getServerVersion() { + InputStream is = this.getClass().getClassLoader().getResourceAsStream("sq-version.txt"); + if (is == null) { + LOG.warn("Failed to get SQ version"); + return null; + } + try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + return br.readLine(); + } catch (IOException e) { + LOG.warn("Failed to get SQ version", e); + return null; + } + } public URI getURI(String pathStartingWithSlash) { Preconditions.checkArgument(pathStartingWithSlash.startsWith("/"), "Path must start with slash /: " + pathStartingWithSlash); diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java index 14070c17d216..e98e6366f7a1 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java @@ -19,9 +19,6 @@ */ package org.sonar.batch.bootstrapper; -import com.google.common.collect.ImmutableMap; - -import org.sonar.api.CoreProperties; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -134,8 +131,7 @@ private void checkStarted() { */ public Batch syncProject(String projectKey) { checkStarted(); - Map props = ImmutableMap.of(CoreProperties.PROJECT_KEY_PROPERTY, projectKey); - bootstrapContainer.syncProject(props, true); + bootstrapContainer.syncProject(projectKey, true); return this; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java b/sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java index 3308098c6a06..248d2b8b3506 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.cache; +import javax.annotation.Nullable; + import org.sonar.batch.bootstrap.ServerClient; import org.sonar.home.cache.PersistentCache; @@ -40,7 +42,7 @@ public DefaultProjectCacheStatus(PersistentCache cache, ServerClient client) { } @Override - public void save(String projectKey) { + public void save(@Nullable String projectKey) { Date now = new Date(); try { @@ -55,7 +57,7 @@ public void save(String projectKey) { } @Override - public void delete(String projectKey) { + public void delete(@Nullable String projectKey) { try { cache.put(getKey(projectKey), new byte[0]); } catch (IOException e) { @@ -64,7 +66,7 @@ public void delete(String projectKey) { } @Override - public Date getSyncStatus(String projectKey) { + public Date getSyncStatus(@Nullable String projectKey) { try { byte[] status = cache.get(getKey(projectKey), null); if (status == null || status.length == 0) { @@ -79,7 +81,11 @@ public Date getSyncStatus(String projectKey) { } } - private String getKey(String projectKey) { - return STATUS_PREFIX + client.getURL() + "-" + projectKey; + private String getKey(@Nullable String projectKey) { + if (projectKey != null) { + return STATUS_PREFIX + client.getURL() + "-" + client.getServerVersion() + "-" + projectKey; + } else { + return STATUS_PREFIX + client.getURL() + "-" + client.getServerVersion(); + } } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java b/sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java new file mode 100644 index 000000000000..e21d14206947 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java @@ -0,0 +1,92 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.batch.rule.ActiveRulesLoader; +import org.sonar.batch.repository.QualityProfileLoader; +import org.sonar.batch.protocol.input.QProfile; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +public class NonAssociatedCacheSynchronizer { + private static final Logger LOG = LoggerFactory.getLogger(NonAssociatedCacheSynchronizer.class); + + private ProjectCacheStatus cacheStatus; + private QualityProfileLoader qualityProfileLoader; + private ActiveRulesLoader activeRulesLoader; + + public NonAssociatedCacheSynchronizer(QualityProfileLoader qualityProfileLoader, ActiveRulesLoader activeRulesLoader, ProjectCacheStatus cacheStatus) { + this.qualityProfileLoader = qualityProfileLoader; + this.activeRulesLoader = activeRulesLoader; + this.cacheStatus = cacheStatus; + } + + public void execute(boolean force) { + Date lastSync = cacheStatus.getSyncStatus(null); + + if (lastSync != null) { + if (!force) { + LOG.info("Found cache [{}]", lastSync); + return; + } else { + LOG.info("-- Found cache [{}], synchronizing data..", lastSync); + } + cacheStatus.delete(null); + } else { + LOG.info("-- Cache not found, synchronizing data.."); + } + + loadData(); + saveStatus(); + } + + private static Collection getKeys(Collection qProfiles) { + List list = new ArrayList<>(qProfiles.size()); + for (QProfile qp : qProfiles) { + list.add(qp.key()); + } + + return list; + } + + private void saveStatus() { + cacheStatus.save(null); + LOG.info("-- Succesfully synchronized cache"); + } + + private void loadData() { + Profiler profiler = Profiler.create(Loggers.get(ProjectCacheSynchronizer.class)); + + profiler.startInfo("Load default quality profiles"); + Collection qProfiles = qualityProfileLoader.load(null, null); + profiler.stopInfo(); + + profiler.startInfo("Load default active rules"); + activeRulesLoader.load(getKeys(qProfiles), null); + profiler.stopInfo(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java b/sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java index 878554d24993..99040ba9927b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java @@ -49,14 +49,14 @@ public PersistentCache provide(UserProperties props) { builder.setSonarHome(Paths.get(home)); } - builder.setVersion(getVersion()); + builder.setVersion(getServerVersion()); cache = builder.build(); } return cache; } - private String getVersion() { + private String getServerVersion() { InputStream is = this.getClass().getClassLoader().getResourceAsStream("sq-version.txt"); if (is == null) { LOG.warn("Failed to get SQ version"); diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java index 281cd60f3443..a5f97349a4b0 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java @@ -19,12 +19,14 @@ */ package org.sonar.batch.cache; +import javax.annotation.Nullable; + import java.util.Date; public interface ProjectCacheStatus { - void save(String projectKey); + void save(@Nullable String projectKey); - void delete(String projectKey); + void delete(@Nullable String projectKey); - Date getSyncStatus(String projectKey); + Date getSyncStatus(@Nullable String projectKey); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java index 6e6bccc9572e..d2d6cbcf2dbc 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java @@ -19,100 +19,98 @@ */ package org.sonar.batch.cache; -import org.sonar.batch.analysis.AnalysisProperties; +import com.google.common.base.Function; import org.apache.commons.lang.StringUtils; -import org.sonar.api.utils.log.Loggers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; -import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.batch.protocol.input.BatchInput.ServerIssue; -import com.google.common.base.Function; -import org.sonar.batch.protocol.input.FileData; +import org.sonar.batch.protocol.input.QProfile; +import org.sonar.batch.repository.ProjectSettingsLoader; +import org.sonar.batch.repository.ProjectSettingsRepo; +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 java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; +import java.util.List; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import org.sonar.batch.protocol.input.ProjectRepositories; -import org.sonar.api.batch.bootstrap.ProjectDefinition; -import org.sonar.batch.repository.user.UserRepositoryLoader; -import org.sonar.batch.issue.tracking.ServerLineHashesLoader; -import org.sonar.batch.repository.ServerIssuesLoader; -import org.sonar.batch.repository.ProjectRepositoriesLoader; public class ProjectCacheSynchronizer { private static final Logger LOG = LoggerFactory.getLogger(ProjectCacheSynchronizer.class); - private static final int NUM_THREAD = 2; - private final ProjectDefinition project; - private final AnalysisProperties properties; - private final ProjectRepositoriesLoader projectRepositoryLoader; private final ServerIssuesLoader issuesLoader; - private final ServerLineHashesLoader lineHashesLoader; private final UserRepositoryLoader userRepository; private final ProjectCacheStatus cacheStatus; - - public ProjectCacheSynchronizer(ProjectReactor project, ProjectRepositoriesLoader projectRepositoryLoader, AnalysisProperties properties, - ServerIssuesLoader issuesLoader, ServerLineHashesLoader lineHashesLoader, UserRepositoryLoader userRepository, ProjectCacheStatus cacheStatus) { - this.project = project.getRoot(); - this.projectRepositoryLoader = projectRepositoryLoader; - this.properties = properties; + private final QualityProfileLoader qualityProfileLoader; + private final ProjectSettingsLoader projectSettingsLoader; + private final ActiveRulesLoader activeRulesLoader; + + public ProjectCacheSynchronizer(QualityProfileLoader qualityProfileLoader, ProjectSettingsLoader projectSettingsLoader, + ActiveRulesLoader activeRulesLoader, ServerIssuesLoader issuesLoader, + UserRepositoryLoader userRepository, ProjectCacheStatus cacheStatus) { + this.qualityProfileLoader = qualityProfileLoader; + this.projectSettingsLoader = projectSettingsLoader; + this.activeRulesLoader = activeRulesLoader; this.issuesLoader = issuesLoader; - this.lineHashesLoader = lineHashesLoader; this.userRepository = userRepository; this.cacheStatus = cacheStatus; } - public void load(boolean force) { - Date lastSync = cacheStatus.getSyncStatus(project.getKeyWithBranch()); + public void load(String projectKey, boolean force) { + Date lastSync = cacheStatus.getSyncStatus(projectKey); if (lastSync != null) { if (!force) { - LOG.info("Found project [{}] cache [{}]", project.getKeyWithBranch(), lastSync); + LOG.info("Found project [{}] cache [{}]", projectKey, lastSync); return; } else { - LOG.info("-- Found project [{}] cache [{}], synchronizing data..", project.getKeyWithBranch(), lastSync); + LOG.info("-- Found project [{}] cache [{}], synchronizing data..", projectKey, lastSync); } - cacheStatus.delete(project.getKeyWithBranch()); + cacheStatus.delete(projectKey); } else { - LOG.info("-- Cache for project [{}] not found, synchronizing data..", project.getKeyWithBranch()); + LOG.info("-- Cache for project [{}] not found, synchronizing data..", projectKey); } - loadData(); - saveStatus(); + loadData(projectKey); + saveStatus(projectKey); } - private void saveStatus() { - cacheStatus.save(project.getKeyWithBranch()); + private void saveStatus(String projectKey) { + cacheStatus.save(projectKey); LOG.info("-- Succesfully synchronized project cache"); } - private static String getComponentKey(String moduleKey, String filePath) { - return moduleKey + ":" + filePath; - } - - private void loadData() { + private void loadData(String projectKey) { Profiler profiler = Profiler.create(Loggers.get(ProjectCacheSynchronizer.class)); - profiler.startInfo("Load project repository"); - ProjectRepositories projectRepo = projectRepositoryLoader.load(project, properties, null); + profiler.startInfo("Load project settings"); + ProjectSettingsRepo settings = projectSettingsLoader.load(projectKey, null); profiler.stopInfo(); - if (projectRepo.lastAnalysisDate() == null) { + if (settings.lastAnalysisDate() == null) { LOG.debug("No previous analysis found"); return; } + profiler.startInfo("Load project quality profiles"); + Collection qProfiles = qualityProfileLoader.load(projectKey, null); + profiler.stopInfo(); + + Collection profileKeys = getKeys(qProfiles); + + profiler.startInfo("Load project active rules"); + activeRulesLoader.load(profileKeys, projectKey); + profiler.stopInfo(); + profiler.startInfo("Load server issues"); UserLoginAccumulator consumer = new UserLoginAccumulator(); - issuesLoader.load(project.getKeyWithBranch(), consumer); + issuesLoader.load(projectKey, consumer); profiler.stopInfo(); profiler.startInfo("Load user information (" + consumer.loginSet.size() + " users)"); @@ -120,56 +118,15 @@ private void loadData() { userRepository.load(login, null); } profiler.stopInfo("Load user information"); - - loadLineHashes(projectRepo.fileDataByModuleAndPath(), profiler); } - private void loadLineHashes(Map> fileDataByModuleAndPath, Profiler profiler) { - ExecutorService executor = Executors.newFixedThreadPool(NUM_THREAD); - int numFiles = 0; - - for (Map fileDataByPath : fileDataByModuleAndPath.values()) { - numFiles += fileDataByPath.size(); - } - profiler.startInfo("Load line file hashes (" + numFiles + " files)"); - - for (Entry> e1 : fileDataByModuleAndPath.entrySet()) { - String moduleKey = e1.getKey(); - - for (Entry e2 : e1.getValue().entrySet()) { - String filePath = e2.getKey(); - executor.submit(new LineHashLoadWorker(getComponentKey(moduleKey, filePath))); - } - } - - executor.shutdown(); - - try { - boolean done = executor.awaitTermination(30, TimeUnit.MINUTES); - if (!done) { - executor.shutdownNow(); - throw new IllegalStateException("Timeout while fetching line hashes"); - } - } catch (InterruptedException e) { - executor.shutdownNow(); - throw new IllegalStateException("Interrupted while fetching line hashes", e); + private static Collection getKeys(Collection qProfiles) { + List list = new ArrayList<>(qProfiles.size()); + for (QProfile qp : qProfiles) { + list.add(qp.key()); } - profiler.stopInfo("Load line file hashes (done)"); - } - - private class LineHashLoadWorker implements Callable { - private String fileKey; - - LineHashLoadWorker(String fileKey) { - this.fileKey = fileKey; - } - - @Override - public Void call() throws Exception { - lineHashesLoader.getLineHashes(fileKey, null); - return null; - } + return list; } private static class UserLoginAccumulator implements Function { diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java index 0f625f72ca05..0bd67f66261c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java @@ -19,59 +19,73 @@ */ package org.sonar.batch.cache; -import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.batch.repository.ProjectRepositoriesFactoryProvider; + import org.sonar.batch.analysis.DefaultAnalysisMode; -import org.sonar.batch.cache.WSLoader.LoadStrategy; +import org.sonar.api.CoreProperties; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + import org.sonar.batch.analysis.AnalysisProperties; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.batch.bootstrap.GlobalProperties; +import org.sonar.batch.repository.ProjectSettingsLoader; +import org.sonar.batch.repository.DefaultProjectSettingsLoader; +import org.sonar.batch.rule.ActiveRulesLoader; +import org.sonar.batch.rule.DefaultActiveRulesLoader; +import org.sonar.batch.repository.QualityProfileLoader; +import org.sonar.batch.repository.DefaultQualityProfileLoader; +import org.sonar.batch.cache.WSLoader.LoadStrategy; import org.sonar.batch.repository.user.UserRepositoryLoader; -import org.sonar.batch.issue.tracking.ServerLineHashesLoader; import org.sonar.batch.repository.DefaultProjectRepositoriesLoader; import org.sonar.batch.repository.DefaultServerIssuesLoader; import org.sonar.batch.repository.ProjectRepositoriesLoader; import org.sonar.batch.repository.ServerIssuesLoader; -import org.sonar.batch.issue.tracking.DefaultServerLineHashesLoader; import org.sonar.core.platform.ComponentContainer; public class ProjectSyncContainer extends ComponentContainer { private final boolean force; - private final AnalysisProperties properties; + private final String projectKey; - public ProjectSyncContainer(ComponentContainer globalContainer, AnalysisProperties analysisProperties, boolean force) { + public ProjectSyncContainer(ComponentContainer globalContainer, String projectKey, boolean force) { super(globalContainer); - this.properties = analysisProperties; + this.projectKey = projectKey; this.force = force; } @Override public void doBeforeStart() { - ProjectReactor projectReactor = createProjectReactor(); - add(projectReactor); addComponents(); } - private ProjectReactor createProjectReactor() { - ProjectDefinition rootProjectDefinition = ProjectDefinition.create(); - rootProjectDefinition.setProperties(properties.properties()); - return new ProjectReactor(rootProjectDefinition); - } - @Override public void doAfterStart() { - getComponentByType(ProjectCacheSynchronizer.class).load(force); + if (projectKey != null) { + getComponentByType(ProjectCacheSynchronizer.class).load(projectKey, force); + } else { + getComponentByType(NonAssociatedCacheSynchronizer.class).execute(force); + } + } + + private static DefaultAnalysisMode createIssuesAnalisysMode() { + Map props = ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES); + GlobalProperties globalProps = new GlobalProperties(props); + AnalysisProperties analysisProps = new AnalysisProperties(props); + return new DefaultAnalysisMode(globalProps, analysisProps); } private void addComponents() { - add(new StrategyWSLoaderProvider(LoadStrategy.SERVER_FIRST), - properties, - DefaultAnalysisMode.class, - ProjectCacheSynchronizer.class, - UserRepositoryLoader.class); + add(new StrategyWSLoaderProvider(LoadStrategy.SERVER_ONLY), + projectKey != null ? ProjectCacheSynchronizer.class : NonAssociatedCacheSynchronizer.class, + UserRepositoryLoader.class, + new ProjectRepositoriesFactoryProvider(projectKey), + createIssuesAnalisysMode()); addIfMissing(DefaultProjectCacheStatus.class, ProjectCacheStatus.class); addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class); addIfMissing(DefaultServerIssuesLoader.class, ServerIssuesLoader.class); - addIfMissing(DefaultServerLineHashesLoader.class, ServerLineHashesLoader.class); + addIfMissing(DefaultQualityProfileLoader.class, QualityProfileLoader.class); + addIfMissing(DefaultActiveRulesLoader.class, ActiveRulesLoader.class); + addIfMissing(DefaultProjectSettingsLoader.class, ProjectSettingsLoader.class); } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java b/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java index 092bab08b0b1..d6966e975481 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java @@ -51,13 +51,13 @@ public enum LoadStrategy { SERVER_FIRST, CACHE_FIRST, SERVER_ONLY, CACHE_ONLY; } - private final LoadStrategy loadStrategy; + private final LoadStrategy defautLoadStrategy; private ServerStatus serverStatus; private final ServerClient client; private final PersistentCache cache; public WSLoader(LoadStrategy strategy, PersistentCache cache, ServerClient client) { - this.loadStrategy = strategy; + this.defautLoadStrategy = strategy; this.serverStatus = UNKNOWN; this.cache = cache; this.client = client; @@ -65,19 +65,28 @@ public WSLoader(LoadStrategy strategy, PersistentCache cache, ServerClient clien @Nonnull public WSLoaderResult loadSource(String id) { - WSLoaderResult byteResult = load(id); + WSLoaderResult byteResult = load(id, defautLoadStrategy); return new WSLoaderResult(ByteSource.wrap(byteResult.get()), byteResult.isFromCache()); } @Nonnull public WSLoaderResult loadString(String id) { - WSLoaderResult byteResult = load(id); + return loadString(id, defautLoadStrategy); + } + @Nonnull + public WSLoaderResult loadString(String id, WSLoader.LoadStrategy strategy) { + WSLoaderResult byteResult = load(id, strategy); return new WSLoaderResult(new String(byteResult.get(), StandardCharsets.UTF_8), byteResult.isFromCache()); } @Nonnull public WSLoaderResult load(String id) { - switch (loadStrategy) { + return load(id, defautLoadStrategy); + } + + @Nonnull + public WSLoaderResult load(String id, WSLoader.LoadStrategy strategy) { + switch (strategy) { case CACHE_FIRST: return loadFromCacheFirst(id, true); case CACHE_ONLY: @@ -91,7 +100,7 @@ public WSLoaderResult load(String id) { } public LoadStrategy getStrategy() { - return this.loadStrategy; + return this.defautLoadStrategy; } private void switchToOffline() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java index 6577835f1cb0..e291a05f15ae 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.issue.tracking; -import org.sonar.batch.cache.WSLoaderResult; +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; @@ -50,7 +51,7 @@ private String loadHashesFromWs(String fileKey, @Nullable MutableBoolean fromCac Profiler profiler = Profiler.createIfDebug(Loggers.get(getClass())) .addContext("file", fileKey) .startDebug("Load line hashes"); - WSLoaderResult result = wsLoader.loadString("/api/sources/hash?key=" + BatchUtils.encodeForUrl(fileKey)); + WSLoaderResult result = wsLoader.loadString("/api/sources/hash?key=" + BatchUtils.encodeForUrl(fileKey), LoadStrategy.CACHE_FIRST); try { if (fromCache != null) { fromCache.setValue(result.isFromCache()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java index f202e5526138..e0d5cc547c1e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java @@ -19,16 +19,22 @@ */ package org.sonar.batch.issue.tracking; +import org.sonar.batch.analysis.DefaultAnalysisMode; + +import org.sonar.batch.repository.ProjectSettingsRepo; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.google.common.collect.Sets; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; + import javax.annotation.CheckForNull; + import org.sonar.api.batch.BatchSide; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.rule.ActiveRule; @@ -41,7 +47,6 @@ import org.sonar.batch.index.BatchComponent; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.issue.IssueCache; -import org.sonar.batch.protocol.input.ProjectRepositories; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.batch.protocol.output.BatchReportReader; import org.sonar.batch.report.ReportPublisher; @@ -72,7 +77,7 @@ public class LocalIssueTracking { public LocalIssueTracking(BatchComponentCache resourceCache, IssueCache issueCache, IssueTracking tracking, ServerLineHashesLoader lastLineHashes, IssueWorkflow workflow, IssueUpdater updater, ActiveRules activeRules, ServerIssueRepository serverIssueRepository, - ProjectRepositories projectRepositories, ReportPublisher reportPublisher) { + ProjectSettingsRepo projectRepositories, ReportPublisher reportPublisher, DefaultAnalysisMode mode) { this.componentCache = resourceCache; this.issueCache = issueCache; this.tracking = tracking; @@ -84,7 +89,7 @@ public LocalIssueTracking(BatchComponentCache resourceCache, IssueCache issueCac this.analysisDate = ((Project) resourceCache.getRoot().resource()).getAnalysisDate(); this.changeContext = IssueChangeContext.createScan(analysisDate); this.activeRules = activeRules; - this.hasServerAnalysis = projectRepositories.lastAnalysisDate() != null; + this.hasServerAnalysis = !mode.isNotAssociated() && projectRepositories.lastAnalysisDate() != null; } public void execute() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesFactory.java similarity index 57% rename from sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesProvider.java rename to sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesFactory.java index b2c86b9e2c06..3861684da2e8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesFactory.java @@ -19,29 +19,42 @@ */ package org.sonar.batch.repository; -import org.sonar.batch.analysis.AnalysisProperties; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.batch.rule.ModuleQProfiles; +import org.sonar.batch.analysis.AnalysisProperties; +import org.sonar.batch.analysis.DefaultAnalysisMode; import org.apache.commons.lang.mutable.MutableBoolean; -import org.picocontainer.injectors.ProviderAdapter; -import org.sonar.api.batch.AnalysisMode; -import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.batch.protocol.input.ProjectRepositories; -public class ProjectRepositoriesProvider extends ProviderAdapter { - +public class DefaultProjectRepositoriesFactory implements ProjectRepositoriesFactory { private static final String LOG_MSG = "Load project repositories"; - private static final Logger LOG = Loggers.get(ProjectRepositoriesProvider.class); + private static final Logger LOG = Loggers.get(DefaultProjectRepositoriesFactory.class); + private static final String NON_EXISTING = "non1-existing2-project3-key"; + + private final DefaultAnalysisMode analysisMode; + private final ProjectRepositoriesLoader loader; + private final AnalysisProperties props; + private final ProjectReactor projectReactor; private ProjectRepositories projectReferentials; - public ProjectRepositories provide(ProjectRepositoriesLoader loader, ProjectReactor reactor, AnalysisProperties taskProps, AnalysisMode analysisMode) { + public DefaultProjectRepositoriesFactory(ProjectReactor projectReactor, DefaultAnalysisMode analysisMode, ProjectRepositoriesLoader loader, AnalysisProperties props) { + this.projectReactor = projectReactor; + this.analysisMode = analysisMode; + this.loader = loader; + this.props = props; + } + + @Override + public ProjectRepositories create() { if (projectReferentials == null) { Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); MutableBoolean fromCache = new MutableBoolean(); - projectReferentials = loader.load(reactor.getRoot(), taskProps, fromCache); + projectReferentials = loader.load(getProjectKey(), getSonarProfile(), fromCache); profiler.stopInfo(fromCache.booleanValue()); if (analysisMode.isIssues() && projectReferentials.lastAnalysisDate() == null) { @@ -50,4 +63,19 @@ public ProjectRepositories provide(ProjectRepositoriesLoader loader, ProjectReac } return projectReferentials; } + + private String getProjectKey() { + if (analysisMode.isNotAssociated()) { + return NON_EXISTING; + } + return projectReactor.getRoot().getKeyWithBranch(); + } + + private String getSonarProfile() { + String profile = null; + if (!analysisMode.isIssues()) { + profile = props.property(ModuleQProfiles.SONAR_PROFILE_PROP); + } + return profile; + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java index 583838113671..a3b38ee4ec54 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java @@ -23,12 +23,10 @@ import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.cache.WSLoader; -import org.sonar.batch.analysis.AnalysisProperties; import javax.annotation.Nullable; import org.apache.commons.lang.mutable.MutableBoolean; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.utils.MessageException; @@ -50,13 +48,12 @@ public DefaultProjectRepositoriesLoader(WSLoader wsLoader, DefaultAnalysisMode a } @Override - public ProjectRepositories load(ProjectDefinition projectDefinition, AnalysisProperties taskProperties, @Nullable MutableBoolean fromCache) { - String projectKey = projectDefinition.getKeyWithBranch(); - String url = BATCH_PROJECT_URL + "?key=" + BatchUtils.encodeForUrl(projectKey); - if (taskProperties.properties().containsKey(ModuleQProfiles.SONAR_PROFILE_PROP)) { + public ProjectRepositories load(String projectKeyWithBranch, @Nullable String sonarProfile, @Nullable MutableBoolean fromCache) { + String url = BATCH_PROJECT_URL + "?key=" + BatchUtils.encodeForUrl(projectKeyWithBranch); + if (sonarProfile != null) { LOG.warn("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."); - url += "&profile=" + BatchUtils.encodeForUrl(taskProperties.properties().get(ModuleQProfiles.SONAR_PROFILE_PROP)); + url += "&profile=" + BatchUtils.encodeForUrl(sonarProfile); } url += "&preview=" + analysisMode.isIssues(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java new file mode 100644 index 000000000000..acb238236867 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java @@ -0,0 +1,56 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 javax.annotation.Nullable; + +import java.util.Map; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import org.sonar.batch.protocol.input.ProjectRepositories; + +public class DefaultProjectSettingsLoader implements ProjectSettingsLoader { + private ProjectRepositoriesFactory projectRepositoryFactory; + + public DefaultProjectSettingsLoader(ProjectRepositoriesFactory projectRepositoryFactory) { + this.projectRepositoryFactory = projectRepositoryFactory; + } + + @Override + public ProjectSettingsRepo load(String projectKey, @Nullable MutableBoolean fromCache) { + ProjectRepositories pr = projectRepositoryFactory.create(); + return new ProjectSettingsRepo(toTable(pr.settings()), toTable(pr.fileDataByModuleAndPath()), pr.lastAnalysisDate()); + } + + private static Table toTable(Map> map) { + Table table = HashBasedTable.create(); + + for (Map.Entry> e1 : map.entrySet()) { + for (Map.Entry e2 : e1.getValue().entrySet()) { + table.put(e1.getKey(), e2.getKey(), e2.getValue()); + } + } + + return table; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java new file mode 100644 index 000000000000..ab86309f3d5d --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java @@ -0,0 +1,50 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 javax.annotation.Nullable; + +import org.sonar.batch.protocol.input.ProjectRepositories; +import org.sonar.batch.protocol.input.QProfile; + +import java.util.Collection; + +public class DefaultQualityProfileLoader implements QualityProfileLoader { + + private ProjectRepositoriesFactory projectRepositoriesFactory; + + public DefaultQualityProfileLoader(ProjectRepositoriesFactory projectRepositoriesFactory) { + this.projectRepositoriesFactory = projectRepositoriesFactory; + } + + @Override + public Collection load(@Nullable String projectKey, @Nullable String sonarProfile) { + ProjectRepositories pr = projectRepositoriesFactory.create(); + validate(pr.qProfiles()); + return pr.qProfiles(); + } + + private static void validate(Collection profiles) { + if (profiles == null || profiles.isEmpty()) { + throw new IllegalStateException("No quality profiles has been found this project, you probably don't have any language plugin suitable for this analysis."); + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java new file mode 100644 index 000000000000..13ce299d8f96 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java @@ -0,0 +1,28 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.protocol.input.ProjectRepositories; + +public interface ProjectRepositoriesFactory { + + ProjectRepositories create(); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java new file mode 100644 index 000000000000..609d7a73f5d4 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java @@ -0,0 +1,41 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.picocontainer.injectors.ProviderAdapter; + +public class ProjectRepositoriesFactoryProvider extends ProviderAdapter { + + private final String projectKey; + private SyncProjectRepositoriesFactory factory; + + public ProjectRepositoriesFactoryProvider(String projectKey) { + this.projectKey = projectKey; + this.factory = null; + } + + public ProjectRepositoriesFactory provide(ProjectRepositoriesLoader loader) { + if (factory == null) { + factory = new SyncProjectRepositoriesFactory(projectKey, loader); + } + + return factory; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java index 444af1a3b629..d066b488940e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java @@ -19,16 +19,14 @@ */ package org.sonar.batch.repository; -import org.sonar.batch.analysis.AnalysisProperties; import javax.annotation.Nullable; import org.apache.commons.lang.mutable.MutableBoolean; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.batch.protocol.input.ProjectRepositories; public interface ProjectRepositoriesLoader { - ProjectRepositories load(ProjectDefinition projectDefinition, AnalysisProperties taskProperties, @Nullable MutableBoolean fromCache); + ProjectRepositories load(String projectKeyWithBranch, @Nullable String sonarProfile, @Nullable MutableBoolean fromCache); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java new file mode 100644 index 000000000000..dc0efbcc4e47 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java @@ -0,0 +1,28 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 javax.annotation.Nullable; + +public interface ProjectSettingsLoader { + ProjectSettingsRepo load(String projectKey, @Nullable MutableBoolean fromCache); +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java new file mode 100644 index 000000000000..5fa0db433b52 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java @@ -0,0 +1,64 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.log.Logger; +import org.sonar.api.utils.log.Loggers; + +import javax.annotation.Nullable; + +import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.protocol.input.FileData; +import com.google.common.collect.Table; +import com.google.common.collect.ImmutableTable; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.picocontainer.injectors.ProviderAdapter; + +public class ProjectSettingsProvider extends ProviderAdapter { + private static final Logger LOG = Loggers.get(ProjectSettingsProvider.class); + private ProjectSettingsRepo settings = null; + + public ProjectSettingsRepo provide(@Nullable ProjectSettingsLoader loader, ProjectReactor projectReactor, DefaultAnalysisMode mode) { + if (settings == null) { + if (mode.isNotAssociated()) { + settings = createNonAssociatedProjectSettings(); + } else { + MutableBoolean fromCache = new MutableBoolean(); + settings = loader.load(projectReactor.getRoot().getKeyWithBranch(), fromCache); + checkProject(mode); + } + } + + return settings; + } + + private void checkProject(DefaultAnalysisMode mode) { + if (mode.isIssues() && settings.lastAnalysisDate() == null) { + LOG.warn("No analysis has been found on the server for this project. All issues will be marked as 'new'."); + } + } + + private static ProjectSettingsRepo createNonAssociatedProjectSettings() { + Table emptySettings = ImmutableTable.of(); + Table emptyFileData = ImmutableTable.of(); + return new ProjectSettingsRepo(emptySettings, emptyFileData, null); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java new file mode 100644 index 000000000000..1e53de437bc4 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java @@ -0,0 +1,66 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.Table; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +import org.sonar.batch.protocol.input.FileData; + +import java.util.Date; +import java.util.Map; + +public class ProjectSettingsRepo { + private Table settingsByModule = null; + private Table fileDataByModuleAndPath = null; + private Date lastAnalysisDate; + + public ProjectSettingsRepo(Table settingsByModule, Table fileDataByModuleAndPath, + @Nullable Date lastAnalysisDate) { + super(); + this.settingsByModule = settingsByModule; + this.fileDataByModuleAndPath = fileDataByModuleAndPath; + this.lastAnalysisDate = lastAnalysisDate; + } + + public Map fileDataByPath(String moduleKey) { + return fileDataByModuleAndPath.row(moduleKey); + } + + public Table fileDataByModuleAndPath() { + return fileDataByModuleAndPath; + } + + public Map settings(String moduleKey) { + return settingsByModule.row(moduleKey); + } + + @CheckForNull + public FileData fileData(String projectKey, String path) { + return fileDataByModuleAndPath.get(projectKey, path); + } + + @CheckForNull + public Date lastAnalysisDate() { + return lastAnalysisDate; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java new file mode 100644 index 000000000000..1d41398bff4c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java @@ -0,0 +1,30 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 javax.annotation.Nullable; + +import org.sonar.batch.protocol.input.QProfile; + +import java.util.Collection; + +public interface QualityProfileLoader { + Collection load(@Nullable String projectKey, @Nullable String sonarProfile); +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java new file mode 100644 index 000000000000..6f40ef0c7a8b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java @@ -0,0 +1,48 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.protocol.input.QProfile; + +import java.util.Collection; + +import org.sonar.api.batch.AnalysisMode; +import org.sonar.batch.analysis.AnalysisProperties; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.batch.rule.ModuleQProfiles; +import org.picocontainer.injectors.ProviderAdapter; + +public class QualityProfileProvider extends ProviderAdapter { + private ModuleQProfiles profiles = null; + + public ModuleQProfiles provide(ProjectReactor projectReactor, QualityProfileLoader loader, AnalysisProperties props, AnalysisMode mode) { + if (this.profiles == null) { + String profile = null; + if (!mode.isIssues()) { + profile = props.property(ModuleQProfiles.SONAR_PROFILE_PROP); + } + Collection qps = loader.load(projectReactor.getRoot().getKeyWithBranch(), profile); + profiles = new ModuleQProfiles(qps); + } + + return profiles; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java b/sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java new file mode 100644 index 000000000000..f01cce9102b1 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java @@ -0,0 +1,74 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 javax.annotation.Nullable; + +import org.apache.commons.lang.mutable.MutableBoolean; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; +import org.sonar.batch.protocol.input.ProjectRepositories; + +public class SyncProjectRepositoriesFactory implements ProjectRepositoriesFactory { + private static final String LOG_MSG = "Load project repositories"; + private static final Logger LOG = Loggers.get(SyncProjectRepositoriesFactory.class); + private static final String NON_EXISTING = "non1-existing2-project3-key"; + + private final ProjectRepositoriesLoader loader; + private final String projectKey; + + private ProjectRepositories projectRepositories; + + public SyncProjectRepositoriesFactory(@Nullable String projectKey, ProjectRepositoriesLoader loader) { + this.projectKey = projectKey; + this.loader = loader; + } + + @Override + public ProjectRepositories create() { + if (projectRepositories == null) { + projectRepositories = newInstance(); + } + + return projectRepositories; + } + + public ProjectRepositories newInstance() { + if (projectRepositories == null) { + Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); + MutableBoolean fromCache = new MutableBoolean(); + projectRepositories = loader.load(getProjectKey(projectKey), null, fromCache); + profiler.stopInfo(fromCache.booleanValue()); + + if (projectRepositories.lastAnalysisDate() == null) { + LOG.warn("No analysis has been found on the server for this project. All issues will be marked as 'new'."); + } + } + return projectRepositories; + } + + private static String getProjectKey(@Nullable String projectKey) { + if (projectKey == null) { + return NON_EXISTING; + } + return projectKey; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java new file mode 100644 index 000000000000..bf2ff6d406aa --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java @@ -0,0 +1,28 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.batch.protocol.input.ActiveRule; + +import java.util.Collection; + +public interface ActiveRulesLoader { + Collection load(Collection qualityProfileKeys, String projectKey); +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java index 5bb0a4a86bd7..ee2529cadff7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java @@ -19,14 +19,17 @@ */ package org.sonar.batch.rule; +import org.sonar.api.batch.bootstrap.ProjectReactor; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.NewActiveRule; import org.sonar.api.rule.RuleKey; import org.sonar.batch.protocol.input.ActiveRule; -import org.sonar.batch.protocol.input.ProjectRepositories; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Map.Entry; /** @@ -37,16 +40,16 @@ public class ActiveRulesProvider extends ProviderAdapter { private ActiveRules singleton = null; - public ActiveRules provide(ProjectRepositories ref) { + public ActiveRules provide(ActiveRulesLoader ref, ModuleQProfiles qProfiles, ProjectReactor projectReactor) { if (singleton == null) { - singleton = load(ref); + singleton = load(ref, qProfiles, projectReactor); } return singleton; } - private static ActiveRules load(ProjectRepositories ref) { + private static ActiveRules load(ActiveRulesLoader loader, ModuleQProfiles qProfiles, ProjectReactor projectReactor) { ActiveRulesBuilder builder = new ActiveRulesBuilder(); - for (ActiveRule activeRule : ref.activeRules()) { + for (ActiveRule activeRule : loader.load(getKeys(qProfiles), projectReactor.getRoot().getKeyWithBranch())) { NewActiveRule newActiveRule = builder.create(RuleKey.of(activeRule.repositoryKey(), activeRule.ruleKey())); newActiveRule.setName(activeRule.name()); newActiveRule.setSeverity(activeRule.severity()); @@ -63,4 +66,14 @@ private static ActiveRules load(ProjectRepositories ref) { } return builder.build(); } + + private static Collection getKeys(ModuleQProfiles qProfiles) { + List keys = new ArrayList<>(qProfiles.findAll().size()); + + for (QProfile qp : qProfiles.findAll()) { + keys.add(qp.getKey()); + } + + return keys; + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java new file mode 100644 index 000000000000..05e588466488 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java @@ -0,0 +1,43 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.batch.repository.ProjectRepositoriesFactory; + +import org.sonar.batch.protocol.input.ActiveRule; + +import java.util.Collection; + +import org.sonar.batch.protocol.input.ProjectRepositories; + +public class DefaultActiveRulesLoader implements ActiveRulesLoader { + private final ProjectRepositoriesFactory projectRepositoriesFactory; + + public DefaultActiveRulesLoader(ProjectRepositoriesFactory projectRepositoriesFactory) { + this.projectRepositoriesFactory = projectRepositoriesFactory; + } + + @Override + public Collection load(Collection qualityProfileKeys, String projectKey) { + ProjectRepositories pr = projectRepositoriesFactory.create(); + return pr.activeRules(); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java index f7474cf181d0..3ac70d831507 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java @@ -21,7 +21,6 @@ import com.google.common.collect.ImmutableMap; import org.sonar.api.batch.BatchSide; -import org.sonar.batch.protocol.input.ProjectRepositories; import javax.annotation.CheckForNull; @@ -37,10 +36,10 @@ public class ModuleQProfiles { public static final String SONAR_PROFILE_PROP = "sonar.profile"; private final Map byLanguage; - public ModuleQProfiles(ProjectRepositories ref) { + public ModuleQProfiles(Collection profiles) { ImmutableMap.Builder builder = ImmutableMap.builder(); - for (org.sonar.batch.protocol.input.QProfile qProfile : ref.qProfiles()) { + for (org.sonar.batch.protocol.input.QProfile qProfile : profiles) { builder.put(qProfile.language(), new QProfile().setKey(qProfile.key()).setName(qProfile.name()).setLanguage(qProfile.language()).setRulesUpdatedAt(qProfile.rulesUpdatedAt())); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index 039da7d57e0e..89c5e8bcc171 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -148,7 +148,6 @@ private void addCoreComponents() { CoverageExclusions.class, // rules - ModuleQProfiles.class, new RulesProfileProvider(), QProfileSensor.class, CheckFactory.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java index f8b770f59dd3..00692d53cfae 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.scan; -import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.repository.ProjectSettingsRepo; +import org.sonar.batch.analysis.DefaultAnalysisMode; import com.google.common.collect.Lists; import java.util.List; @@ -30,20 +31,19 @@ import org.sonar.api.config.Settings; import org.sonar.api.utils.MessageException; import org.sonar.batch.bootstrap.GlobalSettings; -import org.sonar.batch.protocol.input.ProjectRepositories; /** * @since 2.12 */ public class ModuleSettings extends Settings { - private final ProjectRepositories projectReferentials; + private final ProjectSettingsRepo projectSettingsRepo; private DefaultAnalysisMode analysisMode; - public ModuleSettings(GlobalSettings batchSettings, ProjectDefinition moduleDefinition, ProjectRepositories projectReferentials, + public ModuleSettings(GlobalSettings batchSettings, ProjectDefinition moduleDefinition, ProjectSettingsRepo projectSettingsRepo, DefaultAnalysisMode analysisMode) { super(batchSettings.getDefinitions()); - this.projectReferentials = projectReferentials; + this.projectSettingsRepo = projectSettingsRepo; this.analysisMode = analysisMode; getEncryption().setPathToSecretKey(batchSettings.getString(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)); @@ -58,13 +58,13 @@ private ModuleSettings init(ProjectDefinition moduleDefinition, GlobalSettings b private void addProjectProperties(ProjectDefinition moduleDefinition, GlobalSettings batchSettings) { addProperties(batchSettings.getProperties()); - addProperties(projectReferentials.settings(moduleDefinition.getKeyWithBranch())); + addProperties(projectSettingsRepo.settings(moduleDefinition.getKeyWithBranch())); } private void addBuildProperties(ProjectDefinition project) { List orderedProjects = getTopDownParentProjects(project); for (ProjectDefinition p : orderedProjects) { - addProperties(p.getProperties()); + addProperties(p.properties()); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java index 8f42e7bc3667..6266bf44e6c6 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java @@ -55,7 +55,6 @@ public void tryLock() { if (lockFile == null) { failAlreadyInProgress(null); } - } catch (OverlappingFileLockException e) { failAlreadyInProgress(e); } catch (IOException e) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java index 96d7bad1c5c4..81b546093511 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.scan; -import org.apache.commons.lang.ArrayUtils; +import org.sonar.api.batch.AnalysisMode; +import org.apache.commons.lang.ArrayUtils; import org.sonar.batch.analysis.AnalysisProperties; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; @@ -108,12 +109,16 @@ public class ProjectReactorBuilder { */ private static final List NON_HERITED_PROPERTIES_FOR_CHILD = Lists.newArrayList(PROPERTY_PROJECT_BASEDIR, CoreProperties.WORKING_DIRECTORY, PROPERTY_MODULES, CoreProperties.PROJECT_DESCRIPTION_PROPERTY); + + private static final String NON_ASSOCIATED_PROJECT_KEY = "project"; - private AnalysisProperties taskProps; + private final AnalysisProperties taskProps; + private final AnalysisMode analysisMode; private File rootProjectWorkDir; - public ProjectReactorBuilder(AnalysisProperties props) { + public ProjectReactorBuilder(AnalysisProperties props, AnalysisMode analysisMode) { this.taskProps = props; + this.analysisMode = analysisMode; } public ProjectReactor execute() { @@ -160,8 +165,16 @@ private static void extractPropertiesByModule(Map> p extractPropertiesByModule(propertiesByModuleId, moduleId, currentModuleProperties); } } - + + private static void prepareNonAssociatedProject(Map props, AnalysisMode mode) { + if(mode.isIssues() && !props.containsKey(CoreProperties.PROJECT_KEY_PROPERTY)) { + props.put(CoreProperties.PROJECT_KEY_PROPERTY, NON_ASSOCIATED_PROJECT_KEY); + } + } + protected ProjectDefinition defineRootProject(Map rootProperties, @Nullable ProjectDefinition parent) { + prepareNonAssociatedProject(rootProperties, analysisMode); + if (rootProperties.containsKey(PROPERTY_MODULES)) { checkMandatoryProperties(rootProperties, MANDATORY_PROPERTIES_FOR_MULTIMODULE_PROJECT); } else { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java index b53e457ab7cd..a856b44b8947 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java @@ -28,7 +28,6 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.config.Settings; -import org.sonar.api.utils.SonarException; import org.sonar.core.component.ComponentKeys; /** @@ -58,7 +57,7 @@ public void validate(ProjectReactor reactor) { validateBranch(validationMessages, branch); if (!validationMessages.isEmpty()) { - throw new SonarException("Validation of project reactor failed:\n o " + Joiner.on("\n o ").join(validationMessages)); + throw new IllegalStateException("Validation of project reactor failed:\n o " + Joiner.on("\n o ").join(validationMessages)); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index b00888779cfe..d6f58f0852a7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -19,6 +19,16 @@ */ package org.sonar.batch.scan; +import org.sonar.batch.repository.DefaultProjectRepositoriesFactory; + +import org.sonar.batch.repository.QualityProfileProvider; +import org.sonar.batch.repository.DefaultQualityProfileLoader; +import org.sonar.batch.repository.QualityProfileLoader; +import org.sonar.batch.repository.ProjectSettingsLoader; +import org.sonar.batch.repository.DefaultProjectSettingsLoader; +import org.sonar.batch.repository.ProjectSettingsProvider; +import org.sonar.batch.rule.DefaultActiveRulesLoader; +import org.sonar.batch.rule.ActiveRulesLoader; import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.analysis.AnalysisWSLoaderProvider; import org.sonar.batch.analysis.AnalysisTempFolderProvider; @@ -69,7 +79,6 @@ import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.report.SourcePublisher; import org.sonar.batch.report.TestExecutionAndCoveragePublisher; -import org.sonar.batch.repository.ProjectRepositoriesProvider; import org.sonar.batch.repository.language.DefaultLanguagesRepository; import org.sonar.batch.rule.ActiveRulesProvider; import org.sonar.batch.scan.filesystem.InputPathCache; @@ -116,6 +125,7 @@ private void addBatchComponents() { props, DefaultAnalysisMode.class, ProjectReactorBuilder.class, + DefaultProjectRepositoriesFactory.class, new MutableProjectReactorProvider(), new ImmutableProjectReactorProvider(), ProjectBuildersExecutor.class, @@ -126,7 +136,6 @@ private void addBatchComponents() { DefaultProjectTree.class, ProjectExclusions.class, ProjectReactorValidator.class, - new ProjectRepositoriesProvider(), new AnalysisWSLoaderProvider(), CodeColorizers.class, MetricProvider.class, @@ -136,6 +145,7 @@ private void addBatchComponents() { Caches.class, BatchComponentCache.class, DefaultIssueCallback.class, + new ProjectSettingsProvider(), // temp new AnalysisTempFolderProvider(), @@ -146,6 +156,7 @@ private void addBatchComponents() { // rules new ActiveRulesProvider(), + new QualityProfileProvider(), // issues IssueUpdater.class, @@ -153,8 +164,8 @@ private void addBatchComponents() { IssueWorkflow.class, IssueCache.class, DefaultProjectIssues.class, - LocalIssueTracking.class, ServerIssueRepository.class, + LocalIssueTracking.class, // metrics DefaultMetricFinder.class, @@ -190,9 +201,16 @@ private void addBatchComponents() { ScanTaskObservers.class, UserRepositoryLoader.class); - addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class); addIfMissing(DefaultServerIssuesLoader.class, ServerIssuesLoader.class); addIfMissing(DefaultServerLineHashesLoader.class, ServerLineHashesLoader.class); + addIfMissing(DefaultActiveRulesLoader.class, ActiveRulesLoader.class); + addIfMissing(DefaultQualityProfileLoader.class, QualityProfileLoader.class); + addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class); + addIfMissing(DefaultProjectSettingsLoader.class, ProjectSettingsLoader.class); + } + + private boolean isProjectAssociated() { + return !getComponentByType(DefaultAnalysisMode.class).isNotAssociated(); } private void addBatchExtensions() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java index 7cd20223d20b..df9b51351004 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java @@ -19,8 +19,9 @@ */ package org.sonar.batch.scan; -import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.repository.ProjectSettingsRepo; +import org.sonar.batch.analysis.DefaultAnalysisMode; import com.google.common.collect.ImmutableMap; import java.util.Map; @@ -32,7 +33,6 @@ import org.sonar.api.utils.MessageException; import org.sonar.batch.bootstrap.DroppedPropertyChecker; import org.sonar.batch.bootstrap.GlobalSettings; -import org.sonar.batch.protocol.input.ProjectRepositories; public class ProjectSettings extends Settings { @@ -45,11 +45,11 @@ public class ProjectSettings extends Settings { ); private final GlobalSettings globalSettings; - private final ProjectRepositories projectRepositories; + private final ProjectSettingsRepo projectRepositories; private final DefaultAnalysisMode mode; public ProjectSettings(ProjectReactor reactor, GlobalSettings globalSettings, PropertyDefinitions propertyDefinitions, - ProjectRepositories projectRepositories, DefaultAnalysisMode mode) { + ProjectSettingsRepo projectRepositories, DefaultAnalysisMode mode) { super(propertyDefinitions); this.mode = mode; getEncryption().setPathToSecretKey(globalSettings.getString(CoreProperties.ENCRYPTION_SECRET_KEY_PATH)); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java index 36131c293316..51232ec5701f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java @@ -19,21 +19,22 @@ */ package org.sonar.batch.scan.filesystem; +import org.sonar.batch.repository.ProjectSettingsRepo; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; import org.sonar.batch.protocol.input.FileData; -import org.sonar.batch.protocol.input.ProjectRepositories; class StatusDetection { - private final ProjectRepositories projectReferentials; + private final ProjectSettingsRepo projectSettings; - StatusDetection(ProjectRepositories projectReferentials) { - this.projectReferentials = projectReferentials; + StatusDetection(ProjectSettingsRepo projectSettings) { + this.projectSettings = projectSettings; } InputFile.Status status(String projectKey, String relativePath, String hash) { - FileData fileDataPerPath = projectReferentials.fileData(projectKey, relativePath); + FileData fileDataPerPath = projectSettings.fileData(projectKey, relativePath); if (fileDataPerPath == null) { return InputFile.Status.ADDED; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java index 643f915cafca..f372b171df23 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java @@ -19,15 +19,16 @@ */ package org.sonar.batch.scan.filesystem; +import org.sonar.batch.repository.ProjectSettingsRepo; + import org.sonar.api.batch.BatchSide; -import org.sonar.batch.protocol.input.ProjectRepositories; @BatchSide public class StatusDetectionFactory { - private final ProjectRepositories projectReferentials; + private final ProjectSettingsRepo projectReferentials; - public StatusDetectionFactory(ProjectRepositories projectReferentials) { + public StatusDetectionFactory(ProjectSettingsRepo projectReferentials) { this.projectReferentials = projectReferentials; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java b/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java index 62144aa09d05..7d24836072dc 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java @@ -19,8 +19,11 @@ */ package org.sonar.batch.scm; +import org.sonar.batch.repository.ProjectSettingsRepo; + import java.util.LinkedList; import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; @@ -33,7 +36,6 @@ import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.protocol.input.FileData; -import org.sonar.batch.protocol.input.ProjectRepositories; import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.scan.filesystem.InputPathCache; @@ -44,16 +46,16 @@ public final class ScmSensor implements Sensor { private final ProjectDefinition projectDefinition; private final ScmConfiguration configuration; private final FileSystem fs; - private final ProjectRepositories projectReferentials; + private final ProjectSettingsRepo projectSettings; private final BatchComponentCache resourceCache; private final ReportPublisher publishReportJob; public ScmSensor(ProjectDefinition projectDefinition, ScmConfiguration configuration, - ProjectRepositories projectReferentials, FileSystem fs, InputPathCache inputPathCache, BatchComponentCache resourceCache, + ProjectSettingsRepo projectSettings, FileSystem fs, InputPathCache inputPathCache, BatchComponentCache resourceCache, ReportPublisher publishReportJob) { this.projectDefinition = projectDefinition; this.configuration = configuration; - this.projectReferentials = projectReferentials; + this.projectSettings = projectSettings; this.fs = fs; this.resourceCache = resourceCache; this.publishReportJob = publishReportJob; @@ -95,7 +97,7 @@ private List collectFilesToBlame() { if (configuration.forceReloadAll()) { addIfNotEmpty(filesToBlame, f); } else { - FileData fileData = projectReferentials.fileData(projectDefinition.getKeyWithBranch(), f.relativePath()); + FileData fileData = projectSettings.fileData(projectDefinition.getKeyWithBranch(), f.relativePath()); if (f.status() != Status.SAME || fileData == null || fileData.needBlame()) { addIfNotEmpty(filesToBlame, f); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java b/sonar-batch/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java index 838edd04d39e..4239ca1c024e 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java @@ -25,7 +25,6 @@ import org.sonar.home.cache.PersistentCacheLoader; -import org.junit.internal.runners.statements.ExpectException; import org.junit.rules.ExpectedException; import java.io.IOException; @@ -58,6 +57,7 @@ public class DefaultProjectCacheStatusTest { public void setUp() { cache = new PersistentCache(tmp.getRoot().toPath(), Long.MAX_VALUE, mock(Logger.class), null); client = mock(ServerClient.class); + when(client.getServerVersion()).thenReturn("5.2"); when(client.getURL()).thenReturn("localhost"); cacheStatus = new DefaultProjectCacheStatus(cache, client); } @@ -84,6 +84,17 @@ public void errorSave() throws IOException { cacheStatus.save(PROJ_KEY); } + @Test + public void useServerVersionAsKey() { + cacheStatus.save(PROJ_KEY); + assertThat(cacheStatus.getSyncStatus(PROJ_KEY)).isNotNull(); + assertThat(age(cacheStatus.getSyncStatus(PROJ_KEY))).isLessThan(2000); + + when(client.getServerVersion()).thenReturn("5.1"); + + assertThat(cacheStatus.getSyncStatus(PROJ_KEY)).isNull(); + } + @Test public void errorStatus() throws IOException { cache = mock(PersistentCache.class); diff --git a/sonar-batch/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java b/sonar-batch/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java new file mode 100644 index 000000000000..d7b3bbd6710a --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java @@ -0,0 +1,91 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 static org.mockito.Mockito.when; +import org.sonar.batch.protocol.input.ActiveRule; +import com.google.common.collect.ImmutableList; +import org.sonar.batch.protocol.input.QProfile; +import org.junit.Test; + +import java.util.Date; + +import static org.mockito.Mockito.mock; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import org.mockito.MockitoAnnotations; +import org.junit.Before; +import org.mockito.Mock; +import org.sonar.batch.rule.ActiveRulesLoader; +import org.sonar.batch.repository.QualityProfileLoader; + +public class NonAssociatedCacheSynchronizerTest { + private NonAssociatedCacheSynchronizer synchronizer; + + @Mock + private QualityProfileLoader qualityProfileLoader; + @Mock + private ActiveRulesLoader activeRulesLoader; + @Mock + private ProjectCacheStatus cacheStatus; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + QProfile pf = new QProfile("profile", "profile", "lang", new Date(1000)); + ActiveRule ar = mock(ActiveRule.class); + + when(qualityProfileLoader.load(null, null)).thenReturn(ImmutableList.of(pf)); + when(activeRulesLoader.load(ImmutableList.of("profile"), null)).thenReturn(ImmutableList.of(ar)); + + synchronizer = new NonAssociatedCacheSynchronizer(qualityProfileLoader, activeRulesLoader, cacheStatus); + } + + @Test + public void dont_sync_if_exists() { + when(cacheStatus.getSyncStatus(null)).thenReturn(new Date()); + synchronizer.execute(false); + verifyNoMoreInteractions(qualityProfileLoader, activeRulesLoader); + } + + @Test + public void always_sync_if_force() { + when(cacheStatus.getSyncStatus(null)).thenReturn(new Date()); + synchronizer.execute(true); + checkSync(); + } + + @Test + public void sync_if_doesnt_exist() { + synchronizer.execute(false); + checkSync(); + } + + private void checkSync() { + verify(cacheStatus).getSyncStatus(null); + verify(cacheStatus).save(null); + verify(qualityProfileLoader).load(null, null); + verify(activeRulesLoader).load(ImmutableList.of("profile"), null); + + verifyNoMoreInteractions(qualityProfileLoader, activeRulesLoader); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/cache/PersistentCacheProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/cache/PersistentCacheProviderTest.java index 9e4966a5687a..3f9c4af0e771 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/cache/PersistentCacheProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/cache/PersistentCacheProviderTest.java @@ -19,10 +19,12 @@ */ package org.sonar.batch.cache; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; import org.sonar.batch.bootstrap.GlobalProperties; import org.sonar.batch.cache.PersistentCacheProvider; -import java.nio.file.Paths; +import java.io.File; import java.util.Collections; import org.junit.Before; @@ -30,6 +32,9 @@ import org.junit.Test; public class PersistentCacheProviderTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + private PersistentCacheProvider provider = null; private GlobalProperties props = null; @@ -51,7 +56,8 @@ public void test_cache_dir() { @Test public void test_home() { - props.properties().put("sonar.userHome", "myhome"); - assertThat(provider.provide(props).getBaseDirectory()).isEqualTo(Paths.get("myhome/ws_cache")); + File f = temp.getRoot(); + props.properties().put("sonar.userHome", f.getAbsolutePath()); + assertThat(provider.provide(props).getBaseDirectory()).isEqualTo(f.toPath().resolve("ws_cache")); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java b/sonar-batch/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java index c36603145e3d..2621af334767 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java @@ -20,11 +20,27 @@ package org.sonar.batch.cache; import static org.mockito.Mockito.when; + +import org.sonar.batch.repository.ProjectRepositoriesFactory; + +import org.sonar.batch.repository.DefaultProjectRepositoriesFactory; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import org.sonar.batch.repository.ProjectSettingsRepo; +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import org.sonar.batch.protocol.input.ActiveRule; +import org.sonar.batch.protocol.input.QProfile; +import org.apache.commons.lang.mutable.MutableBoolean; +import org.sonar.batch.repository.DefaultProjectSettingsLoader; +import org.sonar.batch.rule.DefaultActiveRulesLoader; +import org.sonar.batch.repository.DefaultQualityProfileLoader; +import org.sonar.batch.repository.ProjectSettingsLoader; +import org.sonar.batch.rule.ActiveRulesLoader; +import org.sonar.batch.repository.QualityProfileLoader; import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.analysis.AnalysisProperties; import org.sonar.batch.protocol.input.ProjectRepositories; -import org.apache.commons.lang.mutable.MutableBoolean; -import org.sonar.batch.issue.tracking.DefaultServerLineHashesLoader; import org.sonar.batch.repository.DefaultServerIssuesLoader; import org.sonar.batch.repository.DefaultProjectRepositoriesLoader; import org.sonar.api.batch.bootstrap.ProjectReactor; @@ -36,6 +52,7 @@ import java.util.Date; import java.util.HashMap; +import static org.mockito.Matchers.eq; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Matchers.anyString; @@ -47,7 +64,6 @@ import org.mockito.MockitoAnnotations; import org.mockito.Mock; import org.sonar.api.batch.bootstrap.ProjectDefinition; -import org.sonar.batch.issue.tracking.ServerLineHashesLoader; import org.sonar.batch.repository.ProjectRepositoriesLoader; import org.sonar.batch.repository.ServerIssuesLoader; import org.sonar.batch.repository.user.UserRepositoryLoader; @@ -55,8 +71,10 @@ public class ProjectCacheSynchronizerTest { private static final String BATCH_PROJECT = "/batch/project?key=org.codehaus.sonar-plugins%3Asonar-scm-git-plugin&preview=true"; private static final String ISSUES = "/batch/issues?key=org.codehaus.sonar-plugins%3Asonar-scm-git-plugin"; - private static final String LINE_HASHES1 = "/api/sources/hash?key=org.codehaus.sonar-plugins%3Asonar-scm-git-plugin%3Asrc%2Ftest%2Fjava%2Forg%2Fsonar%2Fplugins%2Fscm%2Fgit%2FJGitBlameCommandTest.java"; - private static final String LINE_HASHES2 = "/api/sources/hash?key=org.codehaus.sonar-plugins%3Asonar-scm-git-plugin%3Asrc%2Fmain%2Fjava%2Forg%2Fsonar%2Fplugins%2Fscm%2Fgit%2FGitScmProvider.java"; + private static final String PROJECT_KEY = "org.codehaus.sonar-plugins:sonar-scm-git-plugin"; + + @Rule + public ExpectedException exception = ExpectedException.none(); @Mock private ProjectDefinition project; @@ -73,10 +91,10 @@ public class ProjectCacheSynchronizerTest { private ProjectRepositoriesLoader projectRepositoryLoader; private ServerIssuesLoader issuesLoader; - private ServerLineHashesLoader lineHashesLoader; private UserRepositoryLoader userRepositoryLoader; - - private ProjectCacheSynchronizer sync; + private QualityProfileLoader qualityProfileLoader; + private ActiveRulesLoader activeRulesLoader; + private ProjectSettingsLoader projectSettingsLoader; @Before public void setUp() throws IOException { @@ -84,62 +102,108 @@ public void setUp() throws IOException { String batchProject = getResourceAsString("batch_project.json"); ByteSource issues = getResourceAsByteSource("batch_issues.protobuf"); - String lineHashes2 = getResourceAsString("api_sources_hash_GitScmProvider.text"); - String lineHashes1 = getResourceAsString("api_sources_hash_JGitBlameCommand.text"); when(ws.loadString(BATCH_PROJECT)).thenReturn(new WSLoaderResult<>(batchProject, false)); when(ws.loadSource(ISSUES)).thenReturn(new WSLoaderResult<>(issues, false)); - when(ws.loadString(LINE_HASHES1)).thenReturn(new WSLoaderResult<>(lineHashes1, false)); - when(ws.loadString(LINE_HASHES2)).thenReturn(new WSLoaderResult<>(lineHashes2, false)); when(analysisMode.isIssues()).thenReturn(true); - when(project.getKeyWithBranch()).thenReturn("org.codehaus.sonar-plugins:sonar-scm-git-plugin"); - when(projectReactor.getRoot()).thenReturn(project); when(properties.properties()).thenReturn(new HashMap()); + } + + private ProjectCacheSynchronizer create(ProjectRepositories projectRepositories) { + if (projectRepositories == null) { + projectRepositoryLoader = new DefaultProjectRepositoriesLoader(ws, analysisMode); + } else { + projectRepositoryLoader = mock(ProjectRepositoriesLoader.class); + when(projectRepositoryLoader.load(anyString(), anyString(), any(MutableBoolean.class))).thenReturn(projectRepositories); + } + + ProjectReactor reactor = mock(ProjectReactor.class); + ProjectDefinition root = mock(ProjectDefinition.class); + when(root.getKeyWithBranch()).thenReturn(PROJECT_KEY); + when(reactor.getRoot()).thenReturn(root); + + ProjectRepositoriesFactory projectRepositoriesFactory = new DefaultProjectRepositoriesFactory(reactor, analysisMode, projectRepositoryLoader, properties); - projectRepositoryLoader = new DefaultProjectRepositoriesLoader(ws, analysisMode); issuesLoader = new DefaultServerIssuesLoader(ws); - lineHashesLoader = new DefaultServerLineHashesLoader(ws); userRepositoryLoader = new UserRepositoryLoader(ws); + qualityProfileLoader = new DefaultQualityProfileLoader(projectRepositoriesFactory); + activeRulesLoader = new DefaultActiveRulesLoader(projectRepositoriesFactory); + projectSettingsLoader = new DefaultProjectSettingsLoader(projectRepositoriesFactory); + + return new ProjectCacheSynchronizer(qualityProfileLoader, projectSettingsLoader, activeRulesLoader, issuesLoader, userRepositoryLoader, cacheStatus); + } + + private ProjectCacheSynchronizer createMockedLoaders(Date lastAnalysisDate) { + issuesLoader = mock(DefaultServerIssuesLoader.class); + userRepositoryLoader = mock(UserRepositoryLoader.class); + qualityProfileLoader = mock(DefaultQualityProfileLoader.class); + activeRulesLoader = mock(DefaultActiveRulesLoader.class); + projectSettingsLoader = mock(DefaultProjectSettingsLoader.class); + + QProfile pf = new QProfile("profile", "profile", "lang", new Date(1000)); + ActiveRule ar = mock(ActiveRule.class); + ProjectSettingsRepo repo = mock(ProjectSettingsRepo.class); - sync = new ProjectCacheSynchronizer(projectReactor, projectRepositoryLoader, properties, issuesLoader, lineHashesLoader, userRepositoryLoader, - cacheStatus); + when(qualityProfileLoader.load(PROJECT_KEY, null)).thenReturn(ImmutableList.of(pf)); + when(activeRulesLoader.load(ImmutableList.of("profile"), PROJECT_KEY)).thenReturn(ImmutableList.of(ar)); + when(repo.lastAnalysisDate()).thenReturn(lastAnalysisDate); + when(projectSettingsLoader.load(anyString(), any(MutableBoolean.class))).thenReturn(repo); + + return new ProjectCacheSynchronizer(qualityProfileLoader, projectSettingsLoader, activeRulesLoader, issuesLoader, userRepositoryLoader, cacheStatus); } @Test public void testSync() { - sync.load(false); + ProjectCacheSynchronizer sync = create(null); + + sync.load(PROJECT_KEY, false); verify(ws).loadString(BATCH_PROJECT); verify(ws).loadSource(ISSUES); - verify(ws).loadString(LINE_HASHES1); - verify(ws).loadString(LINE_HASHES2); verifyNoMoreInteractions(ws); verify(cacheStatus).save(anyString()); } + @Test + public void testLoadersUsage() { + ProjectCacheSynchronizer synchronizer = createMockedLoaders(new Date()); + synchronizer.load(PROJECT_KEY, false); + + verify(issuesLoader).load(eq(PROJECT_KEY), any(Function.class)); + verify(qualityProfileLoader).load(PROJECT_KEY, null); + verify(activeRulesLoader).load(ImmutableList.of("profile"), PROJECT_KEY); + verify(projectSettingsLoader).load(eq(PROJECT_KEY), any(MutableBoolean.class)); + + verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectSettingsLoader); + } + + @Test + public void testLoadersUsage_NoLastAnalysis() { + ProjectCacheSynchronizer synchronizer = createMockedLoaders(null); + synchronizer.load(PROJECT_KEY, false); + + verify(projectSettingsLoader).load(eq(PROJECT_KEY), any(MutableBoolean.class)); + + verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectSettingsLoader); + } + @Test public void testSyncNoLastAnalysis() { - projectRepositoryLoader = mock(DefaultProjectRepositoriesLoader.class); ProjectRepositories mockedProjectRepositories = mock(ProjectRepositories.class); when(mockedProjectRepositories.lastAnalysisDate()).thenReturn(null); - when(projectRepositoryLoader.load(any(ProjectDefinition.class), any(AnalysisProperties.class), any(MutableBoolean.class))).thenReturn(mockedProjectRepositories); - - sync = new ProjectCacheSynchronizer(projectReactor, projectRepositoryLoader, properties, issuesLoader, lineHashesLoader, userRepositoryLoader, - cacheStatus); - sync.load(true); - verify(cacheStatus).save("org.codehaus.sonar-plugins:sonar-scm-git-plugin"); + ProjectCacheSynchronizer sync = create(mockedProjectRepositories); + sync.load(PROJECT_KEY, true); + verify(cacheStatus).save(PROJECT_KEY); } @Test public void testDontSyncIfNotForce() { - when(cacheStatus.getSyncStatus("org.codehaus.sonar-plugins:sonar-scm-git-plugin")).thenReturn(new Date()); - - ProjectCacheSynchronizer sync = new ProjectCacheSynchronizer(projectReactor, projectRepositoryLoader, properties, issuesLoader, lineHashesLoader, userRepositoryLoader, - cacheStatus); - sync.load(false); + when(cacheStatus.getSyncStatus(PROJECT_KEY)).thenReturn(new Date()); + ProjectCacheSynchronizer sync = create(null); + sync.load(PROJECT_KEY, false); verifyNoMoreInteractions(ws); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java b/sonar-batch/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java index b6d132ae8b2f..15833a06ad53 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java @@ -19,16 +19,13 @@ */ package org.sonar.batch.cache; +import org.sonar.batch.protocol.input.ProjectRepositories; + import org.sonar.batch.bootstrap.GlobalProperties; import org.sonar.batch.bootstrap.ServerClient; import org.sonar.home.cache.PersistentCache; -import org.sonar.api.batch.bootstrap.ProjectReactor; -import org.sonar.batch.analysis.AnalysisProperties; import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import org.sonar.core.platform.ComponentContainer; @@ -47,24 +44,11 @@ private ComponentContainer createParentContainer() { return parent; } - public AnalysisProperties createProjectProperties() { - Map properties = new HashMap<>(); - properties.put("sonar.branch", "branch"); - properties.put("sonar.projectKey", "my:project"); - properties.put("sonar.projectName", "My project"); - properties.put("sonar.projectVersion", "1.0"); - properties.put("sonar.sources", "."); - properties.put("sonar.projectBaseDir", "."); - return new AnalysisProperties(properties); - } - @Test - public void testProjectKeyWithBranch() { - ProjectSyncContainer container = new ProjectSyncContainer(createParentContainer(), createProjectProperties(), true); + public void testProjectRepository() { + ProjectSyncContainer container = new ProjectSyncContainer(createParentContainer(), "my:project", true); container.doBeforeStart(); container.getPicoContainer().start(); - - ProjectReactor projectReactor = container.getComponentByType(ProjectReactor.class); - assertThat(projectReactor.getRoot().getKeyWithBranch()).isEqualTo("my:project:branch"); + container.getComponentByType(ProjectRepositories.class); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java index 1ba67f296f17..9a3c25f6a342 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java @@ -19,8 +19,8 @@ */ 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; @@ -32,6 +32,8 @@ 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; @@ -50,19 +52,19 @@ public void before() { @Test public void should_download_source_from_ws_if_preview_mode() { WSLoader wsLoader = mock(WSLoader.class); - when(wsLoader.loadString(anyString())).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true)); + 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"); + 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())).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true)); + when(server.loadString(anyString(), any(LoadStrategy.class))).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true)); ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server); @@ -70,13 +72,13 @@ public void should_download_source_with_space_from_ws_if_preview_mode() { 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"); + 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())).thenThrow(new HttpDownloader.HttpException(new URI(""), 500)); + when(server.loadString(anyString(), any(LoadStrategy.class))).thenThrow(new HttpDownloader.HttpException(new URI(""), 500)); ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server); diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java index b14d6ac3c88f..9f3e1c4f9c23 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java @@ -19,27 +19,26 @@ */ package org.sonar.batch.mediumtest; -import org.sonar.batch.analysis.AnalysisProperties; - +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.mutable.MutableBoolean; import javax.annotation.Nullable; import org.sonar.batch.cache.ProjectCacheStatus; -import org.sonar.api.batch.bootstrap.ProjectDefinition; 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 com.google.common.base.Function; -import com.google.common.io.Files; 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; @@ -75,12 +74,41 @@ 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, Files.createTempDir().getAbsolutePath()); + builder.bootstrapProperties.put(CoreProperties.WORKING_DIRECTORY, workingDir.toString()); + builder.bootstrapProperties.put(CoreProperties.GLOBAL_WORKING_DIRECTORY, globalWorkingDir.toString()); return builder; } @@ -93,12 +121,18 @@ public static class BatchMediumTesterBuilder { private final Map bootstrapProperties = new HashMap<>(); private final FakeRulesLoader rulesLoader = new FakeRulesLoader(); private final FakeProjectCacheStatus projectCacheStatus = new FakeProjectCacheStatus(); + 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; @@ -210,6 +244,11 @@ public void start() { public void stop() { batch.stop(); + try { + destroyWorkingDirs(); + } catch (IOException e) { + e.printStackTrace(); + } } public void syncProject(String projectKey) { @@ -217,21 +256,24 @@ public void syncProject(String projectKey) { } private BatchMediumTester(BatchMediumTesterBuilder builder) { - batch = Batch.builder() + Batch.Builder batchBuilder = Batch.builder() .setEnableLoggingConfiguration(true) .addComponents( new EnvironmentInformation("mediumTest", "1.0"), builder.pluginInstaller, builder.globalRefProvider, - builder.projectRefProvider, - builder.serverIssues, - builder.serverLineHashes, builder.rulesLoader, builder.projectCacheStatus, + builder.projectRefProvider, new DefaultDebtModel()) .setBootstrapProperties(builder.bootstrapProperties) - .setLogOutput(builder.logOutput) - .build(); + .setLogOutput(builder.logOutput); + + if (builder.associated) { + batchBuilder.addComponents( + builder.serverIssues); + } + batch = batchBuilder.build(); } public TaskBuilder newTask() { @@ -343,7 +385,7 @@ private static class FakeProjectRepositoriesLoader implements ProjectRepositorie private ProjectRepositories ref = new ProjectRepositories(); @Override - public ProjectRepositories load(ProjectDefinition projDefinition, AnalysisProperties taskProperties, @Nullable MutableBoolean fromCache) { + public ProjectRepositories load(String projectKey, @Nullable String sonarProfile, @Nullable MutableBoolean fromCache) { return ref; } @@ -385,7 +427,6 @@ public boolean load(String componentKey, Function consumer) { } return true; } - } private static class FakeProjectCacheStatus implements ProjectCacheStatus { diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java index 3c5d27499264..8216eea13234 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java @@ -19,22 +19,25 @@ */ package org.sonar.batch.mediumtest.cache; -import org.sonar.batch.protocol.input.FileData; - -import org.junit.Test; import com.google.common.collect.ImmutableMap; 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.batch.protocol.input.ActiveRule; +import org.sonar.batch.protocol.input.FileData; import org.sonar.xoo.XooPlugin; import org.sonar.xoo.rule.XooRulesDefinition; import java.util.Date; public class CacheSyncTest { + @Rule + public ExpectedException exception = ExpectedException.none(); - public BatchMediumTester tester; + private BatchMediumTester tester; @After public void stop() { @@ -47,23 +50,57 @@ public void stop() { @Test public void testSyncFirstTime() { FileData file1 = new FileData("hash", true); - String[] hashes = new String[] { - "line1", "line2" - }; tester = BatchMediumTester.builder() .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) .registerPlugin("xoo", new XooPlugin()) .addRules(new XooRulesDefinition()) + .addQProfile("lang", "name") .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo")) .setPreviousAnalysisDate(new Date()) .addFileData("test-project", "file1", file1) - .mockLineHashes("test-project:file1", hashes) .build(); tester.start(); tester.syncProject("test-project"); + } + + @Test + public void testNonAssociated() { + FileData file1 = new FileData("hash", true); + + tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .addQProfile("lang", "name") + .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo")) + .setPreviousAnalysisDate(new Date()) + .addFileData("test-project", "file1", file1) + .build(); + tester.start(); + tester.syncProject(null); + } + + @Test + public void testNoQProfile() { + FileData file1 = new FileData("hash", true); + + tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) + .registerPlugin("xoo", new XooPlugin()) + .addRules(new XooRulesDefinition()) + .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo")) + .setPreviousAnalysisDate(new Date()) + .addFileData("test-project", "file1", file1) + .build(); + + tester.start(); + + exception.expect(IllegalStateException.class); + exception.expectMessage("No quality"); + tester.syncProject("test-project"); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java new file mode 100644 index 000000000000..6b2ae4c342c8 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java @@ -0,0 +1,90 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.batch.protocol.input.ActiveRule; +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") + .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo")) + .activateRule(new ActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo")) + .activateRule(new ActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo")) + .activateRule(new ActiveRule("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-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java index 5e59d70beda8..3d2b9d18937b 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java @@ -23,10 +23,8 @@ import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.cache.WSLoader; -import org.sonar.batch.analysis.AnalysisProperties; import org.apache.commons.lang.mutable.MutableBoolean; import org.apache.commons.io.IOUtils; -import com.google.common.collect.Maps; import java.io.IOException; import java.util.Date; @@ -40,7 +38,6 @@ import org.sonar.api.utils.MessageException; import org.sonar.batch.protocol.input.ProjectRepositories; import org.sonar.batch.protocol.input.QProfile; -import org.sonar.batch.rule.ModuleQProfiles; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -56,7 +53,6 @@ public class DefaultProjectRepositoriesLoaderTest { private WSLoader wsLoader; private DefaultAnalysisMode analysisMode; private ProjectDefinition project; - private AnalysisProperties taskProperties; @Before public void prepare() { @@ -65,7 +61,6 @@ public void prepare() { loader = new DefaultProjectRepositoriesLoader(wsLoader, analysisMode); loader = spy(loader); when(wsLoader.loadString(anyString())).thenReturn(new WSLoaderResult<>("{}", true)); - taskProperties = new AnalysisProperties(Maps.newHashMap(), ""); } @Test @@ -73,11 +68,11 @@ public void passPreviewParameter() { addQualityProfile(); project = ProjectDefinition.create().setKey("foo"); when(analysisMode.isIssues()).thenReturn(false); - loader.load(project, taskProperties, null); + loader.load(project.getKeyWithBranch(), null, null); verify(wsLoader).loadString("/batch/project?key=foo&preview=false"); when(analysisMode.isIssues()).thenReturn(true); - loader.load(project, taskProperties, null); + loader.load(project.getKeyWithBranch(), null, null); verify(wsLoader).loadString("/batch/project?key=foo&preview=true"); } @@ -88,7 +83,7 @@ public void deserializeResponse() throws IOException { when(wsLoader.loadString(anyString())).thenReturn(new WSLoaderResult<>(response, true)); project = ProjectDefinition.create().setKey("foo"); MutableBoolean fromCache = new MutableBoolean(); - ProjectRepositories projectRepo = loader.load(project, taskProperties, fromCache); + ProjectRepositories projectRepo = loader.load(project.getKeyWithBranch(), null, fromCache); assertThat(fromCache.booleanValue()).isTrue(); assertThat(projectRepo.activeRules().size()).isEqualTo(221); @@ -100,7 +95,7 @@ public void deserializeResponse() throws IOException { public void passAndEncodeProjectKeyParameter() { addQualityProfile(); project = ProjectDefinition.create().setKey("foo bàr"); - loader.load(project, taskProperties, null); + loader.load(project.getKeyWithBranch(), null, null); verify(wsLoader).loadString("/batch/project?key=foo+b%C3%A0r&preview=false"); } @@ -108,8 +103,7 @@ public void passAndEncodeProjectKeyParameter() { public void passAndEncodeProfileParameter() { addQualityProfile(); project = ProjectDefinition.create().setKey("foo"); - taskProperties.properties().put(ModuleQProfiles.SONAR_PROFILE_PROP, "my-profile#2"); - loader.load(project, taskProperties, null); + loader.load(project.getKeyWithBranch(), "my-profile#2", null); verify(wsLoader).loadString("/batch/project?key=foo&profile=my-profile%232&preview=false"); } @@ -121,7 +115,7 @@ public void fail_with_message_exception_when_no_quality_profile() throws Excepti project = ProjectDefinition.create().setKey("foo"); when(wsLoader.loadString(anyString())).thenReturn(new WSLoaderResult<>(new ProjectRepositories().toJson(), true)); - loader.load(project, taskProperties, null); + loader.load(project.getKeyWithBranch(), null, null); } private void addQualityProfile() { diff --git a/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectSettingsLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectSettingsLoaderTest.java new file mode 100644 index 000000000000..9d0388fd6b6e --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectSettingsLoaderTest.java @@ -0,0 +1,77 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 org.sonar.batch.protocol.input.FileData; +import org.junit.Before; +import org.junit.Test; +import org.sonar.batch.protocol.input.ProjectRepositories; + +import java.util.Date; + +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 DefaultProjectSettingsLoaderTest { + private DefaultProjectSettingsLoader loader; + private DefaultProjectRepositoriesFactory factory; + private ProjectRepositories projectRepositories; + + private FileData f1; + private FileData f2; + + @Before + public void setUp() { + createProjectRepo(); + factory = mock(DefaultProjectRepositoriesFactory.class); + when(factory.create()).thenReturn(projectRepositories); + loader = new DefaultProjectSettingsLoader(factory); + } + + private void createProjectRepo() { + projectRepositories = new ProjectRepositories(); + projectRepositories.setLastAnalysisDate(new Date(1000)); + + f1 = new FileData("hash1", true); + f2 = new FileData("hash2", true); + projectRepositories.addFileData("module1", "file1", f1); + projectRepositories.addFileData("module1", "file2", f2); + + projectRepositories.addSettings("module1", ImmutableMap.of("key", "value")); + } + + @Test + public void test() { + ProjectSettingsRepo loaded = loader.load("project", null); + + assertThat(loaded.fileData("module1", "file1")).isEqualTo(f1); + assertThat(loaded.fileData("module1", "file2")).isEqualTo(f2); + assertThat(loaded.settings("module1")).isEqualTo(ImmutableMap.of("key", "value")); + assertThat(loaded.lastAnalysisDate()).isEqualTo(new Date(1000)); + + verify(factory).create(); + verifyNoMoreInteractions(factory); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java new file mode 100644 index 000000000000..06de84fc400e --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java @@ -0,0 +1,77 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.junit.Rule; +import org.junit.rules.ExpectedException; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.batch.protocol.input.ProjectRepositories; +import org.sonar.batch.protocol.input.QProfile; + +import java.util.Collection; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; +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 DefaultProjectRepositoriesFactory factory; + private ProjectRepositories projectRepositories; + + @Before + public void setUp() { + projectRepositories = new ProjectRepositories(); + projectRepositories.addQProfile(new QProfile("profile", "name", "lang", new Date())); + + factory = mock(DefaultProjectRepositoriesFactory.class); + when(factory.create()).thenReturn(projectRepositories); + qpLoader = new DefaultQualityProfileLoader(factory); + } + + @Test + public void test() { + Collection loaded = qpLoader.load("project", null); + + assertThat(loaded).hasSize(1); + assertThat(loaded.iterator().next().key()).isEqualTo("profile"); + verify(factory).create(); + verifyNoMoreInteractions(factory); + } + + @Test + public void testNoProfile() { + projectRepositories = new ProjectRepositories(); + when(factory.create()).thenReturn(projectRepositories); + + exception.expect(IllegalStateException.class); + exception.expectMessage("No quality profiles"); + + qpLoader.load("project", null); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java new file mode 100644 index 000000000000..357f0bcdcc51 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java @@ -0,0 +1,94 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.sonar.batch.protocol.input.QProfile; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +import static org.mockito.Mockito.verify; +import static org.assertj.core.api.Assertions.assertThat; +import org.sonar.batch.rule.ModuleQProfiles; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.junit.Test; +import org.sonar.batch.analysis.AnalysisProperties; +import org.mockito.MockitoAnnotations; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.mockito.Mock; +import org.junit.Before; + +public class QualityProfileProviderTest { + private QualityProfileProvider qualityProfileProvider; + + @Mock + private QualityProfileLoader loader; + @Mock + private ProjectReactor projectReactor; + @Mock + private AnalysisMode mode; + @Mock + private AnalysisProperties props; + + private Collection response; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + qualityProfileProvider = new QualityProfileProvider(); + + ProjectDefinition root = mock(ProjectDefinition.class); + when(root.getKeyWithBranch()).thenReturn("project"); + when(projectReactor.getRoot()).thenReturn(root); + + response = new ArrayList(1); + response.add(new QProfile("profile", "name", "lang", new Date())); + } + + @Test + public void testProvide() { + when(loader.load("project", null)).thenReturn(response); + ModuleQProfiles qps = qualityProfileProvider.provide(projectReactor, loader, props, mode); + assertResponse(qps); + + verify(loader).load("project", null); + } + + @Test + public void testProfileProp() { + when(loader.load("project", "custom")).thenReturn(response); + when(props.property(ModuleQProfiles.SONAR_PROFILE_PROP)).thenReturn("custom"); + + ModuleQProfiles qps = qualityProfileProvider.provide(projectReactor, loader, props, mode); + assertResponse(qps); + + verify(loader).load("project", "custom"); + } + + private void assertResponse(ModuleQProfiles qps) { + assertThat(qps.findAll()).hasSize(1); + assertThat(qps.findAll()).extracting("key").containsExactly("profile"); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java new file mode 100644 index 000000000000..7c3f2e118b1f --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java @@ -0,0 +1,70 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY 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.batch.repository.DefaultProjectRepositoriesFactory; + +import com.google.common.collect.ImmutableList; +import org.sonar.batch.protocol.input.ActiveRule; +import org.junit.Test; +import org.sonar.batch.protocol.input.ProjectRepositories; + +import java.util.Collection; + +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; +import org.junit.Before; + +public class DefaultActiveRulesLoaderTest { + private DefaultActiveRulesLoader loader; + private DefaultProjectRepositoriesFactory factory; + private ProjectRepositories projectRepositories; + + private ActiveRule response; + + @Before + public void setUp() { + response = mock(ActiveRule.class); + when(response.ruleKey()).thenReturn("rule"); + + projectRepositories = new ProjectRepositories(); + projectRepositories.addActiveRule(response); + + factory = mock(DefaultProjectRepositoriesFactory.class); + when(factory.create()).thenReturn(projectRepositories); + loader = new DefaultActiveRulesLoader(factory); + } + + @Test + public void test() { + Collection profiles = ImmutableList.of("profile1"); + Collection activeRules = loader.load(profiles, "project"); + + assertThat(activeRules).hasSize(1); + assertThat(activeRules.iterator().next().ruleKey()).isEqualTo("rule"); + + verify(factory).create(); + verifyNoMoreInteractions(factory); + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java index c8a5f5060180..b68eee07f32b 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java @@ -19,9 +19,11 @@ */ package org.sonar.batch.scan; -import org.sonar.batch.analysis.DefaultAnalysisMode; +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 org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -29,10 +31,13 @@ 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.protocol.input.ProjectRepositories; +import org.sonar.batch.protocol.input.FileData; +import org.sonar.batch.repository.ProjectSettingsRepo; import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -43,15 +48,23 @@ public class ModuleSettingsTest { @Rule public ExpectedException thrown = ExpectedException.none(); - ProjectRepositories projectRef; private DefaultAnalysisMode mode; @Before public void before() { - projectRef = new ProjectRepositories(); mode = mock(DefaultAnalysisMode.class); } + private ProjectSettingsRepo createSettings(String module, Map settingsMap) { + Table fileData = ImmutableTable.of(); + Table settings = HashBasedTable.create(); + + for (Map.Entry e : settingsMap.entrySet()) { + settings.put(module, e.getKey(), e.getValue()); + } + return new ProjectSettingsRepo(settings, fileData, null); + } + @Test public void testOrderedProjects() { ProjectDefinition grandParent = ProjectDefinition.create(); @@ -74,11 +87,12 @@ public void test_loading_of_module_settings() { "overridding", "batch", "on-batch", "true" )); - projectRef.addSettings("struts-core", ImmutableMap.of("on-module", "true", "overridding", "module")); + + ProjectSettingsRepo projSettingsRepo = createSettings("struts-core", ImmutableMap.of("on-module", "true", "overridding", "module")); ProjectDefinition module = ProjectDefinition.create().setKey("struts-core"); - ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projectRef, mode); + ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode); assertThat(moduleSettings.getString("overridding")).isEqualTo("module"); assertThat(moduleSettings.getString("on-batch")).isEqualTo("true"); @@ -93,11 +107,12 @@ public void should_not_fail_when_accessing_secured_properties() { when(batchSettings.getProperties()).thenReturn(ImmutableMap.of( "sonar.foo.secured", "bar" )); - projectRef.addSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2")); + + ProjectSettingsRepo projSettingsRepo = createSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2")); ProjectDefinition module = ProjectDefinition.create().setKey("struts-core"); - ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projectRef, mode); + ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode); assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); assertThat(moduleSettings.getString("sonar.foo.secured")).isEqualTo("bar"); @@ -110,13 +125,14 @@ public void should_fail_when_accessing_secured_properties_in_issues() { when(batchSettings.getProperties()).thenReturn(ImmutableMap.of( "sonar.foo.secured", "bar" )); - projectRef.addSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2")); + + ProjectSettingsRepo 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, projectRef, mode); + ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode); assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java index 5ba26864ec4b..75900b811ded 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java @@ -20,7 +20,8 @@ package org.sonar.batch.scan; import org.apache.commons.lang.StringUtils; - +import org.junit.Before; +import org.sonar.api.batch.AnalysisMode; import org.sonar.batch.analysis.AnalysisProperties; import com.google.common.collect.Maps; import org.junit.Rule; @@ -38,6 +39,9 @@ import java.util.Map; import java.util.Properties; +import static org.mockito.Mockito.when; + +import static org.mockito.Mockito.mock; import static org.assertj.core.api.Assertions.assertThat; public class ProjectReactorBuilderTest { @@ -45,6 +49,13 @@ public class ProjectReactorBuilderTest { @Rule public ExpectedException thrown = ExpectedException.none(); + private AnalysisMode mode; + + @Before + public void setUp() { + mode = mock(AnalysisMode.class); + } + @Test public void shouldDefineSimpleProject() { ProjectDefinition projectDefinition = loadProjectDefinition("simple-project"); @@ -78,12 +89,11 @@ public void fail_if_sources_not_set() { public void shouldNotFailIfBlankSourceDirectory() { loadProjectDefinition("simple-project-with-blank-source-dir"); } - + @Test public void modulesRepeatedIds() { thrown.expect(IllegalStateException.class); thrown.expectMessage("Two modules have the same id: module1"); - loadProjectDefinition("multi-module-repeated-id"); } @@ -101,8 +111,8 @@ public void shouldDefineMultiModuleProjectWithDefinitionsAllInRootProject() thro assertThat(rootProject.getTestDirs().contains("tests")).isFalse(); assertThat(rootProject.getBinaries().contains("target/classes")).isFalse(); // and module properties must have been cleaned - assertThat(rootProject.getProperties().getProperty("module1.sonar.projectKey")).isNull(); - assertThat(rootProject.getProperties().getProperty("module2.sonar.projectKey")).isNull(); + 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(TestUtils.getResource(this.getClass(), "multi-module-definitions-all-in-root")); @@ -125,8 +135,8 @@ public void shouldDefineMultiModuleProjectWithDefinitionsAllInRootProject() thro assertThat(module1.getTestDirs()).contains("tests"); assertThat(module1.getBinaries()).contains("target/classes"); // and module properties must have been cleaned - assertThat(module1.getProperties().getProperty("module1.sonar.projectKey")).isNull(); - assertThat(module1.getProperties().getProperty("module2.sonar.projectKey")).isNull(); + 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(TestUtils.getResource(this.getClass(), "multi-module-definitions-all-in-root/module1")); @@ -144,8 +154,8 @@ public void shouldDefineMultiModuleProjectWithDefinitionsAllInRootProject() thro assertThat(module2.getTestDirs()).contains("tests"); assertThat(module2.getBinaries()).contains("target/classes"); // and module properties must have been cleaned - assertThat(module2.getProperties().getProperty("module1.sonar.projectKey")).isNull(); - assertThat(module2.getProperties().getProperty("module2.sonar.projectKey")).isNull(); + 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(TestUtils.getResource(this.getClass(), "multi-module-definitions-all-in-root/module2")); @@ -160,8 +170,8 @@ public void shouldDefineMultiModuleProjectWithModuleKey() { // CHECK ROOT // module properties must have been cleaned - assertThat(rootProject.getProperties().getProperty("module1.sonar.moduleKey")).isNull(); - assertThat(rootProject.getProperties().getProperty("module2.sonar.moduleKey")).isNull(); + assertThat(rootProject.properties().get("module1.sonar.moduleKey")).isNull(); + assertThat(rootProject.properties().get("module2.sonar.moduleKey")).isNull(); // CHECK MODULES List modules = rootProject.getSubProjects(); @@ -301,8 +311,8 @@ public void shouldFailIfExplicitUnmatchingLibFolderOnModule() { public void multiModuleProperties() { ProjectDefinition projectDefinition = loadProjectDefinition("big-multi-module-definitions-all-in-root"); - assertThat(projectDefinition.getProperties().getProperty("module11.property")).isNull(); - assertThat(projectDefinition.getProperties().getProperty("sonar.profile")).isEqualTo("Foo"); + 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()) { @@ -312,12 +322,12 @@ public void multiModuleProperties() { module2 = prj; } } - assertThat(module1.getProperties().getProperty("module11.property")).isNull(); - assertThat(module1.getProperties().getProperty("property")).isNull(); - assertThat(module1.getProperties().getProperty("sonar.profile")).isEqualTo("Foo"); - assertThat(module2.getProperties().getProperty("module11.property")).isNull(); - assertThat(module2.getProperties().getProperty("property")).isNull(); - assertThat(module2.getProperties().getProperty("sonar.profile")).isEqualTo("Foo"); + 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; @@ -328,13 +338,13 @@ public void multiModuleProperties() { module12 = prj; } } - assertThat(module11.getProperties().getProperty("module1.module11.property")).isNull(); - assertThat(module11.getProperties().getProperty("module11.property")).isNull(); - assertThat(module11.getProperties().getProperty("property")).isEqualTo("My module11 property"); - assertThat(module11.getProperties().getProperty("sonar.profile")).isEqualTo("Foo"); - assertThat(module12.getProperties().getProperty("module11.property")).isNull(); - assertThat(module12.getProperties().getProperty("property")).isNull(); - assertThat(module12.getProperties().getProperty("sonar.profile")).isEqualTo("Foo"); + 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 @@ -344,7 +354,7 @@ public void shouldRemoveModulePropertiesFromTaskProperties() { AnalysisProperties taskProperties = new AnalysisProperties(props, null); assertThat(taskProperties.property("module1.module11.property")).isEqualTo("My module11 property"); - new ProjectReactorBuilder(taskProperties).execute(); + new ProjectReactorBuilder(taskProperties, mode).execute(); assertThat(taskProperties.property("module1.module11.property")).isNull(); } @@ -443,19 +453,27 @@ public void shouldMergeParentProperties() { @Test public void shouldInitRootWorkDir() { - ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(Maps.newHashMap(), null)); + ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(Maps.newHashMap(), null), mode); File baseDir = new File("target/tmp/baseDir"); File workDir = builder.initRootProjectWorkDir(baseDir, Maps.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 props = Maps.newHashMap(); props.put("sonar.working.directory", ".foo"); - ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null)); + ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode); File baseDir = new File("target/tmp/baseDir"); File workDir = builder.initRootProjectWorkDir(baseDir, props); @@ -467,7 +485,7 @@ public void shouldInitWorkDirWithCustomRelativeFolder() { public void shouldInitRootWorkDirWithCustomAbsoluteFolder() { Map props = Maps.newHashMap(); props.put("sonar.working.directory", new File("src").getAbsolutePath()); - ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null)); + ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode); File baseDir = new File("target/tmp/baseDir"); File workDir = builder.initRootProjectWorkDir(baseDir, props); @@ -477,16 +495,16 @@ public void shouldInitRootWorkDirWithCustomAbsoluteFolder() { @Test public void shouldFailIf2ModulesWithSameKey() { - Properties props = new Properties(); + Map props = new HashMap<>(); props.put("sonar.projectKey", "root"); ProjectDefinition root = ProjectDefinition.create().setProperties(props); - Properties props1 = new Properties(); + Map props1 = new HashMap<>(); props1.put("sonar.projectKey", "mod1"); root.addSubProject(ProjectDefinition.create().setProperties(props1)); // Check uniqueness of a new module: OK - Properties props2 = new Properties(); + Map props2 = new HashMap<>(); props2.put("sonar.projectKey", "mod2"); ProjectDefinition mod2 = ProjectDefinition.create().setProperties(props2); ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root); @@ -519,7 +537,7 @@ public void shouldSetModuleKeyIfNotPresent() { private ProjectDefinition loadProjectDefinition(String projectFolder) { Map props = loadProps(projectFolder); AnalysisProperties bootstrapProps = new AnalysisProperties(props, null); - ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps).execute(); + ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps,mode).execute(); return projectReactor.getRoot(); } @@ -594,8 +612,8 @@ public void doNotMixPropertiesWhenModuleKeyIsPrefixOfAnother() throws IOExceptio assertThat(rootProject.getTestDirs().contains("tests")).isFalse(); assertThat(rootProject.getBinaries().contains("target/classes")).isFalse(); // and module properties must have been cleaned - assertThat(rootProject.getProperties().getProperty("module1.sonar.projectKey")).isNull(); - assertThat(rootProject.getProperties().getProperty("module2.sonar.projectKey")).isNull(); + 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(TestUtils.getResource(this.getClass(), "multi-module-definitions-same-prefix")); @@ -618,8 +636,8 @@ public void doNotMixPropertiesWhenModuleKeyIsPrefixOfAnother() throws IOExceptio assertThat(module1.getTestDirs()).contains("tests"); assertThat(module1.getBinaries()).contains("target/classes"); // and module properties must have been cleaned - assertThat(module1.getProperties().getProperty("module1.sonar.projectKey")).isNull(); - assertThat(module1.getProperties().getProperty("module2.sonar.projectKey")).isNull(); + 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(TestUtils.getResource(this.getClass(), "multi-module-definitions-same-prefix/module1")); @@ -637,8 +655,8 @@ public void doNotMixPropertiesWhenModuleKeyIsPrefixOfAnother() throws IOExceptio assertThat(module1Feature.getTestDirs()).contains("tests"); assertThat(module1Feature.getBinaries()).contains("target/classes"); // and module properties must have been cleaned - assertThat(module1Feature.getProperties().getProperty("module1.sonar.projectKey")).isNull(); - assertThat(module1Feature.getProperties().getProperty("module2.sonar.projectKey")).isNull(); + 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(TestUtils.getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature")); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java index acc6fa82c34f..1cb70ff6dcb6 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java @@ -27,7 +27,6 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.config.Settings; -import org.sonar.api.utils.SonarException; public class ProjectReactorValidatorTest { @@ -90,7 +89,7 @@ public void not_fail_with_underscore_key() { public void fail_with_invalid_key() { ProjectReactor reactor = createProjectReactor("foo$bar"); - thrown.expect(SonarException.class); + thrown.expect(IllegalStateException.class); thrown.expectMessage("\"foo$bar\" is not a valid project or module key"); validator.validate(reactor); } @@ -99,7 +98,7 @@ public void fail_with_invalid_key() { public void fail_with_backslash_in_key() { ProjectReactor reactor = createProjectReactor("foo\\bar"); - thrown.expect(SonarException.class); + thrown.expect(IllegalStateException.class); thrown.expectMessage("\"foo\\bar\" is not a valid project or module key"); validator.validate(reactor); } @@ -117,7 +116,7 @@ public void not_fail_with_valid_branch() { @Test public void fail_with_invalid_branch() { ProjectReactor reactor = createProjectReactor("foo", "bran#ch"); - thrown.expect(SonarException.class); + thrown.expect(IllegalStateException.class); thrown.expectMessage("\"bran#ch\" is not a valid branch name"); validator.validate(reactor); } @@ -125,7 +124,7 @@ public void fail_with_invalid_branch() { @Test public void fail_with_colon_in_branch() { ProjectReactor reactor = createProjectReactor("foo", "bran:ch"); - thrown.expect(SonarException.class); + thrown.expect(IllegalStateException.class); thrown.expectMessage("\"bran:ch\" is not a valid branch name"); validator.validate(reactor); } @@ -134,7 +133,7 @@ public void fail_with_colon_in_branch() { public void fail_with_only_digits() { ProjectReactor reactor = createProjectReactor("12345"); - thrown.expect(SonarException.class); + thrown.expect(IllegalStateException.class); thrown.expectMessage("\"12345\" is not a valid project or module key"); validator.validate(reactor); } @@ -144,7 +143,7 @@ public void fail_with_deprecated_sonar_phase() { ProjectReactor reactor = createProjectReactor("foo"); settings.setProperty("sonar.phase", "phase"); - thrown.expect(SonarException.class); + thrown.expect(IllegalStateException.class); thrown.expectMessage("\"sonar.phase\" is deprecated"); validator.validate(reactor); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java index de99ed8e909c..412b563e2ac8 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java @@ -19,8 +19,13 @@ */ package org.sonar.batch.scan; -import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.protocol.input.FileData; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import com.google.common.collect.ImmutableTable; +import org.sonar.batch.repository.ProjectSettingsRepo; +import org.sonar.batch.analysis.DefaultAnalysisMode; import org.sonar.batch.bootstrap.GlobalMode; import com.google.common.collect.ImmutableMap; @@ -40,7 +45,6 @@ import org.sonar.batch.bootstrap.GlobalProperties; import org.sonar.batch.bootstrap.GlobalSettings; import org.sonar.batch.protocol.input.GlobalRepositories; -import org.sonar.batch.protocol.input.ProjectRepositories; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -52,16 +56,20 @@ public class ProjectSettingsTest { @Rule public LogTester logTester = new LogTester(); - ProjectRepositories projectRef; - ProjectDefinition project = ProjectDefinition.create().setKey("struts"); - GlobalSettings bootstrapProps; + private ProjectSettingsRepo projectRef; + private ProjectDefinition project; + private GlobalSettings bootstrapProps; + private Table emptyFileData; + private Table emptySettings; private GlobalMode globalMode; private DefaultAnalysisMode mode; @Before public void prepare() { - projectRef = new ProjectRepositories(); + 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.emptyMap()), new PropertyDefinitions(), new GlobalRepositories(), globalMode); @@ -71,6 +79,7 @@ public void prepare() { public void should_load_project_props() { project.setProperty("project.prop", "project"); + projectRef = new ProjectSettingsRepo(emptySettings, emptyFileData, null); ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); assertThat(batchSettings.getString("project.prop")).isEqualTo("project"); @@ -78,10 +87,12 @@ public void should_load_project_props() { @Test public void should_load_project_root_settings() { - projectRef.addSettings("struts", ImmutableMap.of("sonar.cpd.cross", "true", "sonar.java.coveragePlugin", "jacoco")); + Table settings = HashBasedTable.create(); + settings.put("struts", "sonar.cpd.cross", "true"); + settings.put("struts", "sonar.java.coveragePlugin", "jacoco"); + projectRef = new ProjectSettingsRepo(settings, emptyFileData, null); ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); - assertThat(batchSettings.getString("sonar.java.coveragePlugin")).isEqualTo("jacoco"); } @@ -89,7 +100,11 @@ public void should_load_project_root_settings() { public void should_load_project_root_settings_on_branch() { project.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "mybranch"); - projectRef.addSettings("struts:mybranch", ImmutableMap.of("sonar.cpd.cross", "true", "sonar.java.coveragePlugin", "jacoco")); + Table settings = HashBasedTable.create(); + settings.put("struts:mybranch", "sonar.cpd.cross", "true"); + settings.put("struts:mybranch", "sonar.java.coveragePlugin", "jacoco"); + + projectRef = new ProjectSettingsRepo(settings, emptyFileData, null); ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); @@ -98,8 +113,11 @@ public void should_load_project_root_settings_on_branch() { @Test public void should_not_fail_when_accessing_secured_properties() { - projectRef.addSettings("struts", ImmutableMap.of("sonar.foo.secured", "bar", "sonar.foo.license.secured", "bar2")); + Table settings = HashBasedTable.create(); + settings.put("struts", "sonar.foo.secured", "bar"); + settings.put("struts", "sonar.foo.license.secured", "bar2"); + projectRef = new ProjectSettingsRepo(settings, emptyFileData, null); ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); assertThat(batchSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); @@ -108,10 +126,13 @@ public void should_not_fail_when_accessing_secured_properties() { @Test public void should_fail_when_accessing_secured_properties_in_issues_mode() { - projectRef.addSettings("struts", ImmutableMap.of("sonar.foo.secured", "bar", "sonar.foo.license.secured", "bar2")); + Table 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 ProjectSettingsRepo(settings, emptyFileData, null); ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode); assertThat(batchSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2"); @@ -123,10 +144,13 @@ public void should_fail_when_accessing_secured_properties_in_issues_mode() { @Test public void should_log_a_warning_when_a_dropper_property_is_present() { - GlobalSettings settings = new GlobalSettings(new GlobalProperties(ImmutableMap.of("sonar.qualitygate", "somevalue")), new PropertyDefinitions(), new GlobalRepositories(), globalMode); + GlobalSettings settings = new GlobalSettings(new GlobalProperties(ImmutableMap.of("sonar.qualitygate", "somevalue")), new PropertyDefinitions(), new GlobalRepositories(), + globalMode); + projectRef = new ProjectSettingsRepo(emptySettings, emptyFileData, null); new ProjectSettings(new ProjectReactor(project), settings, new PropertyDefinitions(), projectRef, mode); assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Property 'sonar.qualitygate' is not supported any more. It will be ignored."); } + } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java index 53f5db286512..1e2f3ac36200 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java @@ -19,16 +19,16 @@ */ package org.sonar.batch.scan.filesystem; -import org.junit.Test; -import org.sonar.batch.protocol.input.ProjectRepositories; +import org.sonar.batch.repository.ProjectSettingsRepo; +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)); + StatusDetectionFactory factory = new StatusDetectionFactory(mock(ProjectSettingsRepo.class)); StatusDetection detection = factory.create(); assertThat(detection).isNotNull(); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java index c8db34b45428..7aa79e5554e7 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java @@ -19,23 +19,35 @@ */ package org.sonar.batch.scan.filesystem; +import com.google.common.collect.ImmutableTable; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; + +import org.sonar.batch.repository.ProjectSettingsRepo; import org.junit.Test; import org.sonar.api.batch.fs.InputFile; import org.sonar.batch.protocol.input.FileData; -import org.sonar.batch.protocol.input.ProjectRepositories; - import static org.assertj.core.api.Assertions.assertThat; public class StatusDetectionTest { @Test public void detect_status() { - ProjectRepositories ref = new ProjectRepositories(); - ref.addFileData("foo", "src/Foo.java", new FileData("ABCDE", true)); - ref.addFileData("foo", "src/Bar.java", new FileData("FGHIJ", true)); + Table t = ImmutableTable.of(); + ProjectSettingsRepo ref = new ProjectSettingsRepo(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 createTable() { + Table t = HashBasedTable.create(); + + t.put("foo", "src/Foo.java", new FileData("ABCDE", true)); + t.put("foo", "src/Bar.java", new FileData("FGHIJ", true)); + + return t; + } } diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a1/src/main/xoo/com/sonar/it/samples/modules/a1/HelloA1.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a1/src/main/xoo/com/sonar/it/samples/modules/a1/HelloA1.xoo new file mode 100644 index 000000000000..74d29a4fa08b --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a1/src/main/xoo/com/sonar/it/samples/modules/a1/HelloA1.xoo @@ -0,0 +1,16 @@ +package com.sonar.it.samples.modules.a1; + +public class HelloA1 { + private int i; + private HelloA1() { + + } + + public void hello() { + System.out.println("hello" + " xoo"); + } + + protected String getHello() { + return "hello"; + } +} \ No newline at end of file diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a2/src/main/xoo/com/sonar/it/samples/modules/a2/HelloA2.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a2/src/main/xoo/com/sonar/it/samples/modules/a2/HelloA2.xoo new file mode 100644 index 000000000000..42039538a92e --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a2/src/main/xoo/com/sonar/it/samples/modules/a2/HelloA2.xoo @@ -0,0 +1,12 @@ +package com.sonar.it.samples.modules.a2; + +public class HelloA2 { + private int i; + private HelloA2() { + + } + + public void hello() { + System.out.println("hello" + " xoo"); + } +} \ No newline at end of file diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b1/src/main/xoo/com/sonar/it/samples/modules/b1/HelloB1.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b1/src/main/xoo/com/sonar/it/samples/modules/b1/HelloB1.xoo new file mode 100644 index 000000000000..b83c3af128c9 --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b1/src/main/xoo/com/sonar/it/samples/modules/b1/HelloB1.xoo @@ -0,0 +1,12 @@ +package com.sonar.it.samples.modules.b1; + +public class HelloB1 { + private int i; + private HelloB1() { + + } + + public void hello() { + System.out.println("hello" + " world"); + } +} \ No newline at end of file diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b2/src/main/xoo/com/sonar/it/samples/modules/b2/HelloB2.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b2/src/main/xoo/com/sonar/it/samples/modules/b2/HelloB2.xoo new file mode 100644 index 000000000000..20b8bb3876ab --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b2/src/main/xoo/com/sonar/it/samples/modules/b2/HelloB2.xoo @@ -0,0 +1,12 @@ +package com.sonar.it.samples.modules.b2; + +public class HelloB2 { + private int i; + private HelloB2() { + + } + + public void hello() { + System.out.println("hello" + " world"); + } +} \ No newline at end of file diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/sonar-project.properties b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/sonar-project.properties new file mode 100644 index 000000000000..c2b00ede37cc --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/sonar-project.properties @@ -0,0 +1,31 @@ +# Root project information +#sonar.projectKey=com.sonarsource.it.samples:multi-modules-sample +sonar.projectName=Sonar :: Integration Tests :: Multi-modules Sample +sonar.projectVersion=1.0-SNAPSHOT + +sonar.language=xoo + +# Some properties that will be inherited by the modules +sonar.sources=src/main/xoo + +# List of the module identifiers +sonar.modules=module_a,module_b + +module_a.sonar.projectKey=module_a +module_a.sonar.projectName=Module A + +module_a.sonar.modules=module_a1,module_a2 + +module_a.module_a1.sonar.projectName=Sub-module A1 + +module_a.module_a2.sonar.projectName=Sub-module A2 + + +module_b.sonar.projectKey=module_b +module_b.sonar.projectName=Module B + +module_b.sonar.modules=module_b1,module_b2 + +module_b.module_b1.sonar.projectName=Sub-module B1 + +module_b.module_b2.sonar.projectName=Sub-module B2 diff --git a/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_GitScmProvider.text b/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_GitScmProvider.text deleted file mode 100644 index a9ad538e80a2..000000000000 --- a/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_GitScmProvider.text +++ /dev/null @@ -1,49 +0,0 @@ -523048e7f5ca9550505f2d8ea6d587e7 -50ff1975ec4309da19591231c6b5104b -eba1d423f8632818ce94c4eac1b90713 -6112a40c70ed55453a0753030d5564a4 -3389dae361af79b04c9c8e7057f60cc6 -eac5fc1130394e7268b1cfbc54cd7e4d -c0b153d8c08365f2de343e278d3b54c7 -eb4521cb5d193e1d37ecac25b0ffea43 -9210ed0dec59ed663c744d7fb68f0275 -3389dae361af79b04c9c8e7057f60cc6 -cd0fbdfa49d32525ecbdb8dab19dafe6 -ea12a10f5b7730daa639fe133867e088 -69739b9bc9312dfb1a6b8625a08c652a -ec21e054f7f5748d0161fe27cdad6462 -3389dae361af79b04c9c8e7057f60cc6 -951a83e8074813100da0cba92092b385 -c93caecd79a332773cfb06cd5d3b8895 -5832d52d5fcb22a3350f62c856993f0d -c4c9bdd47ee05028cb84873da0ebf2b5 -f89e422b117e518acef69df33f199d10 - -90aa2aae2384f6412c3b86d085d5ffa5 -647f262205ad09f32b0091df388992ed - -943d54ba3e8812437c4d26ef8aa263f8 - -340385b760d1441d3b74e5e39399cc0c - -a94613fd32125cd63160b0c1cf2bd078 - -3415664f5f4a608772e6a4c73a993804 -597b7f5598c56e77bd28b9ff15a30802 -cbb184dd8e05c9709e5dcaedaa0495cf - -2c953c12d2eb6ea958b7f3045ecf8e81 -864d4d5a0cd65f52d791700443cec75e -1b2437750694bba602fedc0a568c65de -cbb184dd8e05c9709e5dcaedaa0495cf - -2c953c12d2eb6ea958b7f3045ecf8e81 -d18a921e891f6f9af8564a882efea289 -8bc5ef2851a7dcf1cf096a68e2a47ba6 -cbb184dd8e05c9709e5dcaedaa0495cf - -2c953c12d2eb6ea958b7f3045ecf8e81 -9cf0f8aa69740d88788fb437589ed33f -0df2777822bbc7799716a10478ca58d4 -cbb184dd8e05c9709e5dcaedaa0495cf -cbb184dd8e05c9709e5dcaedaa0495cf diff --git a/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_JGitBlameCommand.text b/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_JGitBlameCommand.text deleted file mode 100644 index e21a25bd7b69..000000000000 --- a/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_JGitBlameCommand.text +++ /dev/null @@ -1,149 +0,0 @@ -523048e7f5ca9550505f2d8ea6d587e7 -50ff1975ec4309da19591231c6b5104b -eba1d423f8632818ce94c4eac1b90713 -6112a40c70ed55453a0753030d5564a4 -3389dae361af79b04c9c8e7057f60cc6 -eac5fc1130394e7268b1cfbc54cd7e4d -c0b153d8c08365f2de343e278d3b54c7 -eb4521cb5d193e1d37ecac25b0ffea43 -9210ed0dec59ed663c744d7fb68f0275 -3389dae361af79b04c9c8e7057f60cc6 -cd0fbdfa49d32525ecbdb8dab19dafe6 -ea12a10f5b7730daa639fe133867e088 -69739b9bc9312dfb1a6b8625a08c652a -ec21e054f7f5748d0161fe27cdad6462 -3389dae361af79b04c9c8e7057f60cc6 -951a83e8074813100da0cba92092b385 -c93caecd79a332773cfb06cd5d3b8895 -5832d52d5fcb22a3350f62c856993f0d -c4c9bdd47ee05028cb84873da0ebf2b5 -f89e422b117e518acef69df33f199d10 - -9e0ae10d6ada18721c856844d765b465 -ea3c894506f93b88c9fc6c9790da9008 -c5c303a0f47f5f15f22b6776fc1c8c93 -4f592acdcfc11c97e7f19231de9d69b0 -6aea6951956275cb62d01063a1e695fe -293f7a3f08e54359c17d5e984f721665 -18d24bd6a2c2c15d3914502e2776e372 -107e08f15be7e18888da7e69948ac3ba -90aa2aae2384f6412c3b86d085d5ffa5 -ef76944333105582ae8d3a51d29b3b8a -2a592c3d07126847ae4cbbed4a2b4d46 -6f382821d6f35beb6ae4080607046898 - -943d54ba3e8812437c4d26ef8aa263f8 -6fa05171389dfbeda44181c98e580d18 -391715e38dad3a13b75205d527e82c8a -bb3900a63a8cecc1e79592e054915c97 -7c8d40302b1200413bc859331a4f241d -0e2ad2ad1ad56b970e4348a3967d1e81 -049ace1ff1516be8a5fd7cddc0ab2f30 -d2c44db3922004ac2ae41fb402d005b1 -b0ba7766e9e1fddaf40e86b62a5c2900 - -62e220c0092e8ae61f5937f18e4b03bc - -a5eeb3bd06bd4499f8a9ad20ba426bbd - -2bb44a6b46970b3efd87cc8a68848fae - -5b9e24bad64529f3e35ba4f1aed892f2 -78f4e1ffbbf29629025b20f6b1be36f5 -cbb184dd8e05c9709e5dcaedaa0495cf - -2c953c12d2eb6ea958b7f3045ecf8e81 -0f253056876c021c4fd3f3e5ddc4d5b6 -520e98566046a173f9250aa3b7a40ec9 -c31394024dee65cba7e5c526c69af278 -80d5b17efc16ace990c07580fc3e85eb -d28bc6d296024d650b16efe1369128b0 -38ba857d93d3a54a6b7f1bfc1b8fb090 -d41f14cd3267e8d9c17b47ddcb71b0c1 -54f339c05c18199eca937a31fdc07857 -7efc34bf8e2ba01cec26c50875ac8acd -5a29aee8cfe3a5110dc892ab8adfc17f -d271e10accbea4cb6365b85150505b0d -cbb184dd8e05c9709e5dcaedaa0495cf -cbb184dd8e05c9709e5dcaedaa0495cf - -319f70c2d340b01002c3539d09dabbec -b225a5ae163bfc56684d172522993825 -791c95e71dd996b4d723f96df3f37ca4 -80d5b17efc16ace990c07580fc3e85eb -2a8af480cbdcc0ee9b44187a078c8fe7 -4340b548cda0dbc6043cbf4cf49d1b12 -2df758e8d85494e7ee23a02d4c3aa6a7 -e44feb14b61ab99767239eaded464459 -eb375774c265dedeefeb29283ceea9bc -c87c662bc284c5f9c01d3551957ee32e -cbb184dd8e05c9709e5dcaedaa0495cf -cbb184dd8e05c9709e5dcaedaa0495cf -cbb184dd8e05c9709e5dcaedaa0495cf - -1fcefdfae441ded6478da69428a30f12 -8fcd1ffa896d0e214539b1bfa179f3e8 -7a64b5f6e57cc9f13d5a1be99b9f7c23 -8884d72adb8d93474c6af620a7d6fdba -cbb184dd8e05c9709e5dcaedaa0495cf -7e53b1d2085d8c7ae88417eccc5a0893 -cbb184dd8e05c9709e5dcaedaa0495cf - -5f68d8059a28922be107658c3890c230 -6ded45bc62f1d535345e67001dac69d5 -34165242e2230ffca31d7942ec577e6b -02f5ec563dbaaef9ef402c6941a36c98 -784b65e21ca0539f3cefb1710ccc7768 -982e86cf85e0f121ba1a2f0ef462aa6b -cbb184dd8e05c9709e5dcaedaa0495cf -80d5b17efc16ace990c07580fc3e85eb -34c0d764eb79cc7c6dccb53e711fd4be -a5b2391dd7127292b7240c7c8c1ee92a -54d7949984c901073fffda9956190c12 -da38c234aa3fdca9ec1e0e2b991d3568 -6b2decb38be3882440910fd75ec508cd -ce51581950deb12616108c0e909f9c53 -cbb184dd8e05c9709e5dcaedaa0495cf -cbb184dd8e05c9709e5dcaedaa0495cf - -e8165f1ae4cf11035542d4b60ac7b14d -c8272ad357e7feaf2671a0612e52d3b5 -2c953c12d2eb6ea958b7f3045ecf8e81 -209935c0f7c635d91164fe2b14314a3d -448b1c0a64288dbeac516ba67c9f2574 -540c13e9e156b687226421b24f2df178 -cbb184dd8e05c9709e5dcaedaa0495cf -c0c97e22db5055def551d1cef15fb251 -cbb184dd8e05c9709e5dcaedaa0495cf - -8983f749749a87599ea48c8bc08cac86 -f298d78afda5708f64ded32af0e7f541 -248e216059cad9c12bc8ff8b3b6289e2 -80d5b17efc16ace990c07580fc3e85eb -318ddd60a422c3d0435acd5f7aa6923c -7c3bca9656325d15227ffac4ce5dfde9 -7d992ab2241888573c1c7bdfe5d33a35 -cd620097a91b074942d29535e822ebb0 -a50e423de97896afb97123424a960664 -35bb4ed816814a612f9605aec97c69fb -cbb184dd8e05c9709e5dcaedaa0495cf -280db1f26dcc577de8fc62d40627112b -e5b027822061ae041ced3d958b6f7f37 -11a0dde589dec02e8d95c4ea6d83780f -e5acab4c66ae60432ccc5a45d718d152 -3c4b4570f7e4c7037693b1aa1fd5a9ca -420d9af4c91e3248bb2a4e72a683a03a -d69bf14adaf9ea45b48c0fdfefa4f69d -505b97969baa28c3f607a38ee02f4f2d -cbb184dd8e05c9709e5dcaedaa0495cf -da94121a62e940229bd622927ad7702f -9d2a20a55b185c4a864efd3484a870fa -cbb184dd8e05c9709e5dcaedaa0495cf -daada2d57491a2d0b4d237f29fc039dc -53220ad5b69915dec696a01167d5237b -2fb05fbd558fb020eacca16faa325246 -cbb184dd8e05c9709e5dcaedaa0495cf -423c485e2882c1fd9a1b19983b812f50 -cbb184dd8e05c9709e5dcaedaa0495cf - -cbb184dd8e05c9709e5dcaedaa0495cf diff --git a/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/modules/module1/sources/Fake.java b/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/modules/module1/sources/Fake.java new file mode 100644 index 000000000000..e67004defc5c --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/modules/module1/sources/Fake.java @@ -0,0 +1 @@ +class Fake {} diff --git a/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/sonar-project.properties b/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/sonar-project.properties new file mode 100644 index 000000000000..c572ef1f9e50 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/sonar-project.properties @@ -0,0 +1,14 @@ +#sonar.projectKey=com.foo.project +sonar.projectName=Foo Project +sonar.projectVersion=1.0-SNAPSHOT +sonar.projectDescription=Description of Foo Project + +sonar.sources=sources +sonar.tests=tests +sonar.binaries=target/classes + +sonar.modules=module1 + +module1.sonar.projectBaseDir=modules/module1 +module1.sonar.projectKey=com.foo.project.module1 +module1.sonar.projectName=Foo Module 1 diff --git a/sonar-home/src/main/java/org/sonar/home/cache/FileCache.java b/sonar-home/src/main/java/org/sonar/home/cache/FileCache.java index 922a59e5e8d2..6d926a918e69 100644 --- a/sonar-home/src/main/java/org/sonar/home/cache/FileCache.java +++ b/sonar-home/src/main/java/org/sonar/home/cache/FileCache.java @@ -117,7 +117,7 @@ private File hashDir(String hash) { return new File(dir, hash); } - private void mkdirQuietly(File hashDir) { + private static void mkdirQuietly(File hashDir) { try { Files.createDirectories(hashDir.toPath()); } catch (IOException e) { diff --git a/sonar-home/src/main/java/org/sonar/home/cache/FileHashes.java b/sonar-home/src/main/java/org/sonar/home/cache/FileHashes.java index 549f8d10f639..e79a9b4fd747 100644 --- a/sonar-home/src/main/java/org/sonar/home/cache/FileHashes.java +++ b/sonar-home/src/main/java/org/sonar/home/cache/FileHashes.java @@ -56,7 +56,7 @@ public String of(InputStream input) { } } - private byte[] digest(InputStream input, MessageDigest digest) throws IOException { + private static byte[] digest(InputStream input, MessageDigest digest) throws IOException { final byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; int read = input.read(buffer, 0, STREAM_BUFFER_LENGTH); while (read > -1) { diff --git a/sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java b/sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java index c5c45090ed47..2b32c65b7f97 100644 --- a/sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java +++ b/sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java @@ -19,7 +19,9 @@ */ package org.sonar.home.cache; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; @@ -28,6 +30,7 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -77,7 +80,7 @@ public Path getBaseDirectory() { @CheckForNull public synchronized String getString(@Nonnull String obj, @Nullable final PersistentCacheLoader valueLoader) throws IOException { ValueLoaderDecoder decoder = valueLoader != null ? new ValueLoaderDecoder(valueLoader) : null; - + byte[] cached = get(obj, decoder); if (cached == null) { @@ -87,6 +90,20 @@ public synchronized String getString(@Nonnull String obj, @Nullable final Persis return new String(cached, ENCODING); } + @CheckForNull + public synchronized InputStream getStream(@Nonnull String obj) throws IOException { + String key = getKey(obj); + + try { + lock(); + Path path = getCacheCopy(key); + return new DeleteOnCloseInputStream(new FileInputStream(path.toFile()), path); + + } finally { + unlock(); + } + } + @CheckForNull public synchronized byte[] get(@Nonnull String obj, @Nullable PersistentCacheLoader valueLoader) throws IOException { String key = getKey(obj); @@ -116,6 +133,16 @@ public synchronized byte[] get(@Nonnull String obj, @Nullable PersistentCacheLoa return null; } + + public synchronized void put(@Nonnull String obj, @Nonnull InputStream stream) throws IOException { + String key = getKey(obj); + try { + lock(); + putCache(key, stream); + } finally { + unlock(); + } + } public synchronized void put(@Nonnull String obj, @Nonnull byte[] value) throws IOException { String key = getKey(obj); @@ -266,6 +293,11 @@ private void putCache(String key, byte[] value) throws IOException { Path cachePath = getCacheEntryPath(key); Files.write(cachePath, value, CREATE, WRITE, TRUNCATE_EXISTING); } + + private void putCache(String key, InputStream stream) throws IOException { + Path cachePath = getCacheEntryPath(key); + Files.copy(stream, cachePath, StandardCopyOption.REPLACE_EXISTING); + } private byte[] getCache(String key) throws IOException { Path cachePath = getCacheEntryPath(key); @@ -277,6 +309,39 @@ private byte[] getCache(String key) throws IOException { return Files.readAllBytes(cachePath); } + private Path getCacheCopy(String key) throws IOException { + Path cachePath = getCacheEntryPath(key); + + if (!validateCacheEntry(cachePath, this.defaultDurationToExpireMs)) { + return null; + } + + Path temp = Files.createTempFile("sonar_cache", null); + Files.copy(cachePath, temp, StandardCopyOption.REPLACE_EXISTING); + return temp; + } + + private static class DeleteOnCloseInputStream extends InputStream { + private final InputStream stream; + private final Path p; + + private DeleteOnCloseInputStream(InputStream stream, Path p) { + this.stream = stream; + this.p = p; + } + + @Override + public int read() throws IOException { + return stream.read(); + } + + @Override + public void close() throws IOException { + stream.close(); + Files.delete(p); + } + } + private boolean validateCacheEntry(Path cacheEntryPath, long durationToExpireMs) throws IOException { if (!Files.exists(cacheEntryPath)) { return false;