ソースを参照

SONAR-6817 Issues mode should support analysis of projects not associated

tags/5.2-RC1
Duarte Meneses 8年前
コミット
4be016e61f
77個のファイルの変更2076行の追加598行の削除
  1. 5
    0
      it/it-projects/shared/xoo-sample-non-associated/sonar-project.properties
  2. 16
    0
      it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo
  3. 11
    0
      it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo.measures
  4. 33
    10
      it/it-tests/src/test/java/batch/IssuesModeTest.java
  5. 4
    0
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectRepositories.java
  6. 14
    0
      sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java
  7. 12
    4
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
  8. 21
    1
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
  9. 1
    5
      sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
  10. 11
    5
      sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java
  11. 92
    0
      sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java
  12. 2
    2
      sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java
  13. 5
    3
      sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java
  14. 52
    95
      sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java
  15. 38
    24
      sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java
  16. 15
    6
      sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java
  17. 3
    2
      sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java
  18. 8
    3
      sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java
  19. 37
    9
      sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesFactory.java
  20. 4
    7
      sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java
  21. 56
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java
  22. 50
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java
  23. 28
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java
  24. 41
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java
  25. 1
    3
      sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java
  26. 28
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java
  27. 64
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java
  28. 66
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java
  29. 30
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java
  30. 48
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java
  31. 74
    0
      sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java
  32. 28
    0
      sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java
  33. 18
    5
      sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java
  34. 43
    0
      sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java
  35. 2
    3
      sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java
  36. 0
    1
      sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
  37. 7
    7
      sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java
  38. 0
    1
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java
  39. 17
    4
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java
  40. 1
    2
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java
  41. 22
    4
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
  42. 4
    4
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java
  43. 6
    5
      sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java
  44. 4
    3
      sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java
  45. 7
    5
      sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java
  46. 12
    1
      sonar-batch/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java
  47. 91
    0
      sonar-batch/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java
  48. 9
    3
      sonar-batch/src/test/java/org/sonar/batch/cache/PersistentCacheProviderTest.java
  49. 97
    33
      sonar-batch/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java
  50. 5
    21
      sonar-batch/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java
  51. 8
    6
      sonar-batch/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java
  52. 54
    13
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java
  53. 45
    8
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java
  54. 90
    0
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java
  55. 6
    12
      sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java
  56. 77
    0
      sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectSettingsLoaderTest.java
  57. 77
    0
      sonar-batch/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java
  58. 94
    0
      sonar-batch/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java
  59. 70
    0
      sonar-batch/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java
  60. 26
    10
      sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java
  61. 58
    40
      sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java
  62. 6
    7
      sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java
  63. 36
    12
      sonar-batch/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java
  64. 3
    3
      sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java
  65. 17
    5
      sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java
  66. 16
    0
      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
  67. 12
    0
      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
  68. 12
    0
      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
  69. 12
    0
      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
  70. 31
    0
      sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/sonar-project.properties
  71. 0
    49
      sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_GitScmProvider.text
  72. 0
    149
      sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_JGitBlameCommand.text
  73. 1
    0
      sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/modules/module1/sources/Fake.java
  74. 14
    0
      sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/sonar-project.properties
  75. 1
    1
      sonar-home/src/main/java/org/sonar/home/cache/FileCache.java
  76. 1
    1
      sonar-home/src/main/java/org/sonar/home/cache/FileHashes.java
  77. 66
    1
      sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java

+ 5
- 0
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

+ 16
- 0
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();
}
}
}

+ 11
- 0
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

+ 33
- 10
it/it-tests/src/test/java/batch/IssuesModeTest.java ファイルの表示

@@ -69,28 +69,45 @@ public class IssuesModeTest {
}

@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 class IssuesModeTest {
boolean expectedError = false;
for (Future<BuildResult> 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 @@ public class IssuesModeTest {
return runner;
}

private void setDefaultQualityProfile(String languageKey, String profileName) {
orchestrator.getServer().adminWsClient().post("api/qualityprofiles/set_default",
"language", languageKey,
"profileName", profileName);
}

}

+ 4
- 0
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectRepositories.java ファイルの表示

@@ -45,6 +45,10 @@ public class ProjectRepositories {
public Map<String, String> settings(String moduleKey) {
return settingsByModule.containsKey(moduleKey) ? settingsByModule.get(moduleKey) : Collections.<String, String>emptyMap();
}
public Map<String, Map<String, String>> settings() {
return settingsByModule;
}

public ProjectRepositories addSettings(String moduleKey, Map<String, String> settings) {
Map<String, String> existingSettings = settingsByModule.get(moduleKey);

+ 14
- 0
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 class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
return mediumTestMode;
}

public boolean isNotAssociated() {
return notAssociated;
}

private void init(Map<String, String> globalProps, Map<String, String> analysisProps) {
// make sure analysis is consistent with global properties
boolean globalPreview = isIssues(globalProps);
@@ -64,6 +69,7 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
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 class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
if (mediumTestMode) {
LOG.info("Medium test mode");
}
if (notAssociated) {
LOG.info("Project is not associated with the server");
}
}

private static String getPropertyWithFallback(Map<String, String> props1, Map<String, String> props2, String key) {
@@ -93,4 +102,9 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
return CoreProperties.ANALYSIS_MODE_ISSUES.equals(mode);
}

private static boolean rootProjectKeyMissing(Map<String, String> props) {
// ProjectReactorBuilder depends on this class, so it will only create this property later
return !props.containsKey(CoreProperties.PROJECT_KEY_PROPERTY);
}

}

+ 12
- 4
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java ファイルの表示

@@ -123,14 +123,22 @@ public class GlobalContainer extends ComponentContainer {
public void executeAnalysis(Map<String, String> 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<String, String> 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) {

+ 21
- 1
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.JsonElement;
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 @@ import org.sonar.core.util.DefaultHttpDownloader;
*/
@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 class ServerClient {
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);

+ 1
- 5
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 @@ public final class Batch {
*/
public Batch syncProject(String projectKey) {
checkStarted();
Map<String, String> props = ImmutableMap.of(CoreProperties.PROJECT_KEY_PROPERTY, projectKey);
bootstrapContainer.syncProject(props, true);
bootstrapContainer.syncProject(projectKey, true);
return this;
}


+ 11
- 5
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 class DefaultProjectCacheStatus implements ProjectCacheStatus {
}

@Override
public void save(String projectKey) {
public void save(@Nullable String projectKey) {
Date now = new Date();

try {
@@ -55,7 +57,7 @@ public class DefaultProjectCacheStatus implements ProjectCacheStatus {
}

@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 class DefaultProjectCacheStatus implements ProjectCacheStatus {
}

@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 class DefaultProjectCacheStatus implements ProjectCacheStatus {
}
}

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();
}
}
}

+ 92
- 0
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<String> getKeys(Collection<QProfile> qProfiles) {
List<String> 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<QProfile> qProfiles = qualityProfileLoader.load(null, null);
profiler.stopInfo();

profiler.startInfo("Load default active rules");
activeRulesLoader.load(getKeys(qProfiles), null);
profiler.stopInfo();
}
}

+ 2
- 2
sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java ファイルの表示

@@ -49,14 +49,14 @@ public class PersistentCacheProvider extends ProviderAdapter {
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");

+ 5
- 3
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);
}

+ 52
- 95
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<QProfile> qProfiles = qualityProfileLoader.load(projectKey, null);
profiler.stopInfo();

Collection<String> 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 @@ public class ProjectCacheSynchronizer {
userRepository.load(login, null);
}
profiler.stopInfo("Load user information");

loadLineHashes(projectRepo.fileDataByModuleAndPath(), profiler);
}

private void loadLineHashes(Map<String, Map<String, FileData>> fileDataByModuleAndPath, Profiler profiler) {
ExecutorService executor = Executors.newFixedThreadPool(NUM_THREAD);
int numFiles = 0;

for (Map<String, FileData> fileDataByPath : fileDataByModuleAndPath.values()) {
numFiles += fileDataByPath.size();
}
profiler.startInfo("Load line file hashes (" + numFiles + " files)");

for (Entry<String, Map<String, FileData>> e1 : fileDataByModuleAndPath.entrySet()) {
String moduleKey = e1.getKey();

for (Entry<String, FileData> 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<String> getKeys(Collection<QProfile> qProfiles) {
List<String> 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<Void> {
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<ServerIssue, Void> {

+ 38
- 24
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<String, String> 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);
}

}

+ 15
- 6
sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java ファイルの表示

@@ -51,13 +51,13 @@ public class WSLoader {
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 class WSLoader {

@Nonnull
public WSLoaderResult<ByteSource> loadSource(String id) {
WSLoaderResult<byte[]> byteResult = load(id);
WSLoaderResult<byte[]> byteResult = load(id, defautLoadStrategy);
return new WSLoaderResult<ByteSource>(ByteSource.wrap(byteResult.get()), byteResult.isFromCache());
}

@Nonnull
public WSLoaderResult<String> loadString(String id) {
WSLoaderResult<byte[]> byteResult = load(id);
return loadString(id, defautLoadStrategy);
}
@Nonnull
public WSLoaderResult<String> loadString(String id, WSLoader.LoadStrategy strategy) {
WSLoaderResult<byte[]> byteResult = load(id, strategy);
return new WSLoaderResult<String>(new String(byteResult.get(), StandardCharsets.UTF_8), byteResult.isFromCache());
}

@Nonnull
public WSLoaderResult<byte[]> load(String id) {
switch (loadStrategy) {
return load(id, defautLoadStrategy);
}
@Nonnull
public WSLoaderResult<byte[]> load(String id, WSLoader.LoadStrategy strategy) {
switch (strategy) {
case CACHE_FIRST:
return loadFromCacheFirst(id, true);
case CACHE_ONLY:
@@ -91,7 +100,7 @@ public class WSLoader {
}

public LoadStrategy getStrategy() {
return this.loadStrategy;
return this.defautLoadStrategy;
}

private void switchToOffline() {

+ 3
- 2
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 @@ public class DefaultServerLineHashesLoader implements ServerLineHashesLoader {
Profiler profiler = Profiler.createIfDebug(Loggers.get(getClass()))
.addContext("file", fileKey)
.startDebug("Load line hashes");
WSLoaderResult<String> result = wsLoader.loadString("/api/sources/hash?key=" + BatchUtils.encodeForUrl(fileKey));
WSLoaderResult<String> result = wsLoader.loadString("/api/sources/hash?key=" + BatchUtils.encodeForUrl(fileKey), LoadStrategy.CACHE_FIRST);
try {
if (fromCache != null) {
fromCache.setValue(result.isFromCache());

+ 8
- 3
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.api.utils.KeyValueFormat;
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 class LocalIssueTracking {
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() {

sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesProvider.java → 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 class ProjectRepositoriesProvider extends ProviderAdapter {
}
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;
}
}

+ 4
- 7
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java ファイルの表示

@@ -23,12 +23,10 @@ import org.sonar.batch.cache.WSLoaderResult;

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 class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad
}

@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();


+ 56
- 0
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 <T, U, V> Table<T, U, V> toTable(Map<T, Map<U, V>> map) {
Table<T, U, V> table = HashBasedTable.create();

for (Map.Entry<T, Map<U, V>> e1 : map.entrySet()) {
for (Map.Entry<U, V> e2 : e1.getValue().entrySet()) {
table.put(e1.getKey(), e2.getKey(), e2.getValue());
}
}

return table;
}
}

+ 50
- 0
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<QProfile> load(@Nullable String projectKey, @Nullable String sonarProfile) {
ProjectRepositories pr = projectRepositoriesFactory.create();
validate(pr.qProfiles());
return pr.qProfiles();
}

private static void validate(Collection<QProfile> 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.");
}
}

}

+ 28
- 0
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();

}

+ 41
- 0
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;
}
}

+ 1
- 3
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);

}

+ 28
- 0
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);
}

+ 64
- 0
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<String, String, String> emptySettings = ImmutableTable.of();
Table<String, String, FileData> emptyFileData = ImmutableTable.of();
return new ProjectSettingsRepo(emptySettings, emptyFileData, null);
}
}

+ 66
- 0
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<String, String, String> settingsByModule = null;
private Table<String, String, FileData> fileDataByModuleAndPath = null;
private Date lastAnalysisDate;

public ProjectSettingsRepo(Table<String, String, String> settingsByModule, Table<String, String, FileData> fileDataByModuleAndPath,
@Nullable Date lastAnalysisDate) {
super();
this.settingsByModule = settingsByModule;
this.fileDataByModuleAndPath = fileDataByModuleAndPath;
this.lastAnalysisDate = lastAnalysisDate;
}

public Map<String, FileData> fileDataByPath(String moduleKey) {
return fileDataByModuleAndPath.row(moduleKey);
}

public Table<String, String, FileData> fileDataByModuleAndPath() {
return fileDataByModuleAndPath;
}

public Map<String, String> 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;
}
}

+ 30
- 0
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<QProfile> load(@Nullable String projectKey, @Nullable String sonarProfile);
}

+ 48
- 0
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<QProfile> qps = loader.load(projectReactor.getRoot().getKeyWithBranch(), profile);
profiles = new ModuleQProfiles(qps);
}

return profiles;
}

}

+ 74
- 0
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;
}
}

+ 28
- 0
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<ActiveRule> load(Collection<String> qualityProfileKeys, String projectKey);
}

+ 18
- 5
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 @@ public class ActiveRulesProvider extends ProviderAdapter {
}
return builder.build();
}

private static Collection<String> getKeys(ModuleQProfiles qProfiles) {
List<String> keys = new ArrayList<>(qProfiles.findAll().size());

for (QProfile qp : qProfiles.findAll()) {
keys.add(qp.getKey());
}

return keys;
}
}

+ 43
- 0
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<ActiveRule> load(Collection<String> qualityProfileKeys, String projectKey) {
ProjectRepositories pr = projectRepositoriesFactory.create();
return pr.activeRules();
}

}

+ 2
- 3
sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java ファイルの表示

@@ -21,7 +21,6 @@ package org.sonar.batch.rule;

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<String, QProfile> byLanguage;

public ModuleQProfiles(ProjectRepositories ref) {
public ModuleQProfiles(Collection<org.sonar.batch.protocol.input.QProfile> profiles) {
ImmutableMap.Builder<String, QProfile> 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()));
}

+ 0
- 1
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java ファイルの表示

@@ -148,7 +148,6 @@ public class ModuleScanContainer extends ComponentContainer {
CoverageExclusions.class,

// rules
ModuleQProfiles.class,
new RulesProfileProvider(),
QProfileSensor.class,
CheckFactory.class,

+ 7
- 7
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.batch.bootstrap.ProjectDefinition;
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 @@ public class ModuleSettings extends Settings {

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<ProjectDefinition> orderedProjects = getTopDownParentProjects(project);
for (ProjectDefinition p : orderedProjects) {
addProperties(p.getProperties());
addProperties(p.properties());
}
}


+ 0
- 1
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java ファイルの表示

@@ -55,7 +55,6 @@ public class ProjectLock implements Startable {
if (lockFile == null) {
failAlreadyInProgress(null);
}
} catch (OverlappingFileLockException e) {
failAlreadyInProgress(e);
} catch (IOException e) {

+ 17
- 4
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<String> 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 @@ public class ProjectReactorBuilder {
extractPropertiesByModule(propertiesByModuleId, moduleId, currentModuleProperties);
}
}

private static void prepareNonAssociatedProject(Map<String, String> 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<String, String> rootProperties, @Nullable ProjectDefinition parent) {
prepareNonAssociatedProject(rootProperties, analysisMode);
if (rootProperties.containsKey(PROPERTY_MODULES)) {
checkMandatoryProperties(rootProperties, MANDATORY_PROPERTIES_FOR_MULTIMODULE_PROJECT);
} else {

+ 1
- 2
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java ファイルの表示

@@ -28,7 +28,6 @@ import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.config.Settings;
import org.sonar.api.utils.SonarException;
import org.sonar.core.component.ComponentKeys;

/**
@@ -58,7 +57,7 @@ public class ProjectReactorValidator {
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));
}
}


+ 22
- 4
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.MetadataPublisher;
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 @@ public class ProjectScanContainer extends ComponentContainer {
props,
DefaultAnalysisMode.class,
ProjectReactorBuilder.class,
DefaultProjectRepositoriesFactory.class,
new MutableProjectReactorProvider(),
new ImmutableProjectReactorProvider(),
ProjectBuildersExecutor.class,
@@ -126,7 +136,6 @@ public class ProjectScanContainer extends ComponentContainer {
DefaultProjectTree.class,
ProjectExclusions.class,
ProjectReactorValidator.class,
new ProjectRepositoriesProvider(),
new AnalysisWSLoaderProvider(),
CodeColorizers.class,
MetricProvider.class,
@@ -136,6 +145,7 @@ public class ProjectScanContainer extends ComponentContainer {
Caches.class,
BatchComponentCache.class,
DefaultIssueCallback.class,
new ProjectSettingsProvider(),

// temp
new AnalysisTempFolderProvider(),
@@ -146,6 +156,7 @@ public class ProjectScanContainer extends ComponentContainer {

// rules
new ActiveRulesProvider(),
new QualityProfileProvider(),

// issues
IssueUpdater.class,
@@ -153,8 +164,8 @@ public class ProjectScanContainer extends ComponentContainer {
IssueWorkflow.class,
IssueCache.class,
DefaultProjectIssues.class,
LocalIssueTracking.class,
ServerIssueRepository.class,
LocalIssueTracking.class,

// metrics
DefaultMetricFinder.class,
@@ -190,9 +201,16 @@ public class ProjectScanContainer extends ComponentContainer {
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() {

+ 4
- 4
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.config.Settings;
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));

+ 6
- 5
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;
}

+ 4
- 3
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;
}


+ 7
- 5
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.SensorContext;
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 @@ public final class ScmSensor implements Sensor {
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);
}

+ 12
- 1
sonar-batch/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java ファイルの表示

@@ -25,7 +25,6 @@ import static org.assertj.core.api.Assertions.assertThat;

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 class DefaultProjectCacheStatusTest {
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);

+ 91
- 0
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);
}
}

+ 9
- 3
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 static org.assertj.core.api.Assertions.assertThat;
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 class PersistentCacheProviderTest {

@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"));
}
}

+ 97
- 33
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.nio.charset.StandardCharsets;
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.junit.Before;
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 @@ import org.sonar.batch.repository.user.UserRepositoryLoader;
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 class ProjectCacheSynchronizerTest {

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<String, String>());
}

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);
}

+ 5
- 21
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 @@ public class ProjectSyncContainerTest {
return parent;
}

public AnalysisProperties createProjectProperties() {
Map<String, String> 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);
}
}

+ 8
- 6
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 org.sonar.api.utils.HttpDownloader;
import java.net.URI;
import java.net.URISyntaxException;

import static org.mockito.Matchers.any;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
@@ -50,19 +52,19 @@ public class DefaultServerLineHashesLoaderTest {
@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 class DefaultServerLineHashesLoaderTest {
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);


+ 54
- 13
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 class BatchMediumTester {
private final Map<String, String> 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 class BatchMediumTester {

public void stop() {
batch.stop();
try {
destroyWorkingDirs();
} catch (IOException e) {
e.printStackTrace();
}
}

public void syncProject(String projectKey) {
@@ -217,21 +256,24 @@ public class BatchMediumTester {
}

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 @@ public class BatchMediumTester {
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 class BatchMediumTester {
}
return true;
}

}

private static class FakeProjectCacheStatus implements ProjectCacheStatus {

+ 45
- 8
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 class CacheSyncTest {
@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");
}

}

+ 90
- 0
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();

}
}

+ 6
- 12
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java ファイルの表示

@@ -23,10 +23,8 @@ import org.sonar.batch.cache.WSLoaderResult;

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.batch.bootstrap.ProjectDefinition;
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 class DefaultProjectRepositoriesLoaderTest {
loader = new DefaultProjectRepositoriesLoader(wsLoader, analysisMode);
loader = spy(loader);
when(wsLoader.loadString(anyString())).thenReturn(new WSLoaderResult<>("{}", true));
taskProperties = new AnalysisProperties(Maps.<String, String>newHashMap(), "");
}

@Test
@@ -73,11 +68,11 @@ public class DefaultProjectRepositoriesLoaderTest {
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 class DefaultProjectRepositoriesLoaderTest {
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 class DefaultProjectRepositoriesLoaderTest {
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 class DefaultProjectRepositoriesLoaderTest {
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 class DefaultProjectRepositoriesLoaderTest {
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() {

+ 77
- 0
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);
}
}

+ 77
- 0
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<QProfile> 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);
}
}

+ 94
- 0
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<QProfile> 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<QProfile>(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");
}
}

+ 70
- 0
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<String> profiles = ImmutableList.of("profile1");
Collection<ActiveRule> activeRules = loader.load(profiles, "project");

assertThat(activeRules).hasSize(1);
assertThat(activeRules.iterator().next().ruleKey()).isEqualTo("rule");

verify(factory).create();
verifyNoMoreInteractions(factory);
}

}

+ 26
- 10
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.junit.rules.ExpectedException;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.utils.MessageException;
import org.sonar.batch.analysis.DefaultAnalysisMode;
import org.sonar.batch.bootstrap.GlobalSettings;
import org.sonar.batch.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<String, String> settingsMap) {
Table<String, String, FileData> fileData = ImmutableTable.of();
Table<String, String, String> settings = HashBasedTable.create();

for (Map.Entry<String, String> e : settingsMap.entrySet()) {
settings.put(module, e.getKey(), e.getValue());
}
return new ProjectSettingsRepo(settings, fileData, null);
}

@Test
public void testOrderedProjects() {
ProjectDefinition grandParent = ProjectDefinition.create();
@@ -74,11 +87,12 @@ public class ModuleSettingsTest {
"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 class ModuleSettingsTest {
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 class ModuleSettingsTest {
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");


+ 58
- 40
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.List;
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 class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {

// 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<ProjectDefinition> modules = rootProject.getSubProjects();
@@ -301,8 +311,8 @@ public class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {

@Test
public void shouldInitRootWorkDir() {
ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(Maps.<String, String>newHashMap(), null));
ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(Maps.<String, String>newHashMap(), null), mode);
File baseDir = new File("target/tmp/baseDir");

File workDir = builder.initRootProjectWorkDir(baseDir, Maps.<String, String>newHashMap());

assertThat(workDir).isEqualTo(new File(baseDir, ".sonar"));
}
@Test
public void nonAssociatedMode() {
when(mode.isIssues()).thenReturn(true);
ProjectDefinition project = loadProjectDefinition("multi-module-with-basedir-not-associated");
assertThat(project.getKey()).isEqualTo("project");
}

@Test
public void shouldInitWorkDirWithCustomRelativeFolder() {
Map<String, String> props = Maps.<String, String>newHashMap();
props.put("sonar.working.directory", ".foo");
ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null));
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 class ProjectReactorBuilderTest {
public void shouldInitRootWorkDirWithCustomAbsoluteFolder() {
Map<String, String> props = Maps.<String, String>newHashMap();
props.put("sonar.working.directory", new File("src").getAbsolutePath());
ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null));
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 class ProjectReactorBuilderTest {

@Test
public void shouldFailIf2ModulesWithSameKey() {
Properties props = new Properties();
Map<String, String> props = new HashMap<>();
props.put("sonar.projectKey", "root");
ProjectDefinition root = ProjectDefinition.create().setProperties(props);

Properties props1 = new Properties();
Map<String, String> 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<String, String> props2 = new HashMap<>();
props2.put("sonar.projectKey", "mod2");
ProjectDefinition mod2 = ProjectDefinition.create().setProperties(props2);
ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);
@@ -519,7 +537,7 @@ public class ProjectReactorBuilderTest {
private ProjectDefinition loadProjectDefinition(String projectFolder) {
Map<String, String> 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 class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {
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 class ProjectReactorBuilderTest {
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"));

+ 6
- 7
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java ファイルの表示

@@ -27,7 +27,6 @@ import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.config.Settings;
import org.sonar.api.utils.SonarException;

public class ProjectReactorValidatorTest {

@@ -90,7 +89,7 @@ public class ProjectReactorValidatorTest {
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 class ProjectReactorValidatorTest {
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 class ProjectReactorValidatorTest {
@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 class ProjectReactorValidatorTest {
@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 class ProjectReactorValidatorTest {
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 class ProjectReactorValidatorTest {
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);
}

+ 36
- 12
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.api.utils.log.LoggerLevel;
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<String, String, FileData> emptyFileData;
private Table<String, String, String> 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.<String, String>emptyMap()), new PropertyDefinitions(), new GlobalRepositories(), globalMode);
@@ -71,6 +79,7 @@ public class ProjectSettingsTest {
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 class ProjectSettingsTest {

@Test
public void should_load_project_root_settings() {
projectRef.addSettings("struts", ImmutableMap.of("sonar.cpd.cross", "true", "sonar.java.coveragePlugin", "jacoco"));
Table<String, String, String> 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 class ProjectSettingsTest {
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<String, String, String> 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 class ProjectSettingsTest {

@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<String, String, String> 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 class ProjectSettingsTest {

@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<String, String, String> settings = HashBasedTable.create();
settings.put("struts", "sonar.foo.secured", "bar");
settings.put("struts", "sonar.foo.license.secured", "bar2");

when(mode.isIssues()).thenReturn(true);

projectRef = new 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 class ProjectSettingsTest {

@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.");

}

}

+ 3
- 3
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();
}

+ 17
- 5
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<String, String, String> 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<String, String, FileData> createTable() {
Table<String, String, FileData> 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;
}
}

+ 16
- 0
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";
}
}

+ 12
- 0
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");
}
}

+ 12
- 0
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");
}
}

+ 12
- 0
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");
}
}

+ 31
- 0
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

+ 0
- 49
sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_GitScmProvider.text ファイルの表示

@@ -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

+ 0
- 149
sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_JGitBlameCommand.text ファイルの表示

@@ -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

+ 1
- 0
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 {}

+ 14
- 0
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

+ 1
- 1
sonar-home/src/main/java/org/sonar/home/cache/FileCache.java ファイルの表示

@@ -117,7 +117,7 @@ public class FileCache {
return new File(dir, hash);
}

private void mkdirQuietly(File hashDir) {
private static void mkdirQuietly(File hashDir) {
try {
Files.createDirectories(hashDir.toPath());
} catch (IOException e) {

+ 1
- 1
sonar-home/src/main/java/org/sonar/home/cache/FileHashes.java ファイルの表示

@@ -56,7 +56,7 @@ public class FileHashes {
}
}

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) {

+ 66
- 1
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.charset.StandardCharsets;
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 class PersistentCache {
@CheckForNull
public synchronized String getString(@Nonnull String obj, @Nullable final PersistentCacheLoader<String> valueLoader) throws IOException {
ValueLoaderDecoder decoder = valueLoader != null ? new ValueLoaderDecoder(valueLoader) : null;
byte[] cached = get(obj, decoder);

if (cached == null) {
@@ -87,6 +90,20 @@ public class PersistentCache {
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<byte[]> valueLoader) throws IOException {
String key = getKey(obj);
@@ -116,6 +133,16 @@ public class PersistentCache {

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 @@ public class PersistentCache {
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 @@ public class PersistentCache {
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;

読み込み中…
キャンセル
保存