diff options
author | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2018-02-01 15:30:48 +0100 |
---|---|---|
committer | Teryk Bellahsene <teryk@users.noreply.github.com> | 2018-03-13 14:05:36 +0100 |
commit | 751e4000e40a4af66b80767d632b1bef64dc5647 (patch) | |
tree | 0bf9735c29f6bae50ec10c11d8fbf7e50e909722 /sonar-scanner-engine | |
parent | 23e4b51cde3ed0dc76ca1d73aa6cc31715dd25d4 (diff) | |
download | sonarqube-751e4000e40a4af66b80767d632b1bef64dc5647.tar.gz sonarqube-751e4000e40a4af66b80767d632b1bef64dc5647.zip |
MMF-1134 Make pull request a 1st class citizen
SONAR-10366 Add pull request object to api/project_branches/list and api/ce/activity
SONAR-10365 Analyze pull requests as 1st class citizen
SONAR-10366 SONAR-10367 Create WS api/projet_pull_requests/list and delete
SONAR-10383 Add Pull Request information when listing CE tasks
SONAR-10365 Add key type in PROJECT_BRANCHES (#3063)
SONAR-10371 Add pullRequest parameter in the Web API (#3076)
* ComponentFinder searches by branch or pull request
* Add pullRequest parameter to WS api/issues/* WS
* Add pullRequest parameter to api/settings/* WS
* Add pullRequest parameter to api/badges/* WS
* Add pullRequest parameter to api/components/* WS
* Add pullRequest parameter to api/sources/* WS
SONAR-10368 Copy issue states from pull request after it's merged
SONAR-10373 Send notifications for events on issues of a pull request
SONAR-10365 Add pull_request_binary column in project_branches (#3073)
SONAR-10433 Store pull request in projects table
SONAR-10371 Add pullRequest field in the Web API
SONAR-10365 Analyze pull requests as 1st class citizen
BRANCH-45 Expose issue resolution for PR decoration
BRANCH-49 Basic support of pull request analysis on pull request branch
BRANCH-47 Fail when user tries to analyze a pull request and the plugin is not available
SONAR-10366 update pull request decorated links to project and issues
SONAR-10365 Use pull request id as key instead of branch name
SONAR-10454 Update embedded Git 1.4 and SVN 1.7
SONAR-10365 rename sonar.pullrequest.id to sonar.pullrequest.key
SONAR-10383 api/navigation/component returns the pull request key
Diffstat (limited to 'sonar-scanner-engine')
34 files changed, 619 insertions, 98 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java index 5d47999757e..b143361ec41 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java @@ -83,8 +83,8 @@ public class CpdExecutor { } public void execute() { - if (branchConfiguration.isShortLivingBranch()) { - LOG.info("Skipping CPD calculation for short living branch"); + if (branchConfiguration.isShortOrPullRequest()) { + LOG.info("Skipping CPD calculation for short living branch and pull request"); return; } execute(TIMEOUT); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java index c1edd6f92f7..c36b8a196e6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java @@ -152,7 +152,8 @@ public class ComponentsPublisher implements ReportPublisherStep { } private boolean shouldSkipComponent(DefaultInputComponent component, Collection<InputComponent> children) { - if (component instanceof InputModule && children.isEmpty() && branchConfiguration.isShortLivingBranch()) { + if (component instanceof InputModule && children.isEmpty() + && (branchConfiguration.isShortOrPullRequest())) { // no children on a module in short branch analysis -> skip it (except root) return !moduleHierarchy.isRoot((InputModule) component); } else if (component instanceof InputDir && children.isEmpty()) { @@ -165,7 +166,7 @@ public class ComponentsPublisher implements ReportPublisherStep { } else if (component instanceof DefaultInputFile) { // skip files not marked for publishing DefaultInputFile inputFile = (DefaultInputFile) component; - return !inputFile.isPublished() || (branchConfiguration.isShortLivingBranch() && inputFile.status() == Status.SAME); + return !inputFile.isPublished() || (branchConfiguration.isShortOrPullRequest() && inputFile.status() == Status.SAME); } return false; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java index 202d8edae8f..a17c691f8bb 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java @@ -91,30 +91,13 @@ public class MetadataPublisher implements ReportPublisherStep { settings.get(ORGANIZATION).ifPresent(builder::setOrganizationKey); if (branchConfiguration.branchName() != null) { - builder.setBranchName(branchConfiguration.branchName()); - builder.setBranchType(toProtobufBranchType(branchConfiguration.branchType())); - String branchTarget = branchConfiguration.branchTarget(); - if (branchTarget != null) { - builder.setMergeBranchName(branchTarget); - } + addBranchInformation(builder); } + Optional.ofNullable(rootProject.getBranch()).ifPresent(builder::setDeprecatedBranch); if (scmConfiguration != null) { - ScmProvider scmProvider = scmConfiguration.provider(); - if (scmProvider != null) { - Path projectBasedir = moduleHierarchy.root().getBaseDir(); - try { - builder.setRelativePathFromScmRoot(toSonarQubePath(scmProvider.relativePathFromScmRoot(projectBasedir))); - } catch (UnsupportedOperationException e) { - LOG.debug(e.getMessage()); - } - try { - builder.setScmRevisionId(scmProvider.revisionId(projectBasedir)); - } catch (UnsupportedOperationException e) { - LOG.debug(e.getMessage()); - } - } + addScmInformation(builder); } for (QProfile qp : qProfiles.findAll()) { @@ -132,7 +115,40 @@ public class MetadataPublisher implements ReportPublisherStep { writer.writeMetadata(builder.build()); } + private void addScmInformation(ScannerReport.Metadata.Builder builder) { + ScmProvider scmProvider = scmConfiguration.provider(); + if (scmProvider != null) { + Path projectBasedir = moduleHierarchy.root().getBaseDir(); + try { + builder.setRelativePathFromScmRoot(toSonarQubePath(scmProvider.relativePathFromScmRoot(projectBasedir))); + } catch (UnsupportedOperationException e) { + LOG.debug(e.getMessage()); + } + try { + builder.setScmRevisionId(scmProvider.revisionId(projectBasedir)); + } catch (UnsupportedOperationException e) { + LOG.debug(e.getMessage()); + } + } + } + + private void addBranchInformation(ScannerReport.Metadata.Builder builder) { + builder.setBranchName(branchConfiguration.branchName()); + BranchType branchType = toProtobufBranchType(branchConfiguration.branchType()); + builder.setBranchType(branchType); + String branchTarget = branchConfiguration.branchTarget(); + if (branchTarget != null) { + builder.setMergeBranchName(branchTarget); + } + if (branchType == BranchType.PULL_REQUEST) { + builder.setPullRequestKey(branchConfiguration.pullRequestKey()); + } + } + private static BranchType toProtobufBranchType(org.sonar.scanner.scan.branch.BranchType branchType) { + if (branchType == org.sonar.scanner.scan.branch.BranchType.PULL_REQUEST) { + return BranchType.PULL_REQUEST; + } if (branchType == org.sonar.scanner.scan.branch.BranchType.LONG) { return BranchType.LONG; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java index 58066ab0351..97b01015e2c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java @@ -57,6 +57,7 @@ import org.sonarqube.ws.client.WsResponse; import static org.sonar.core.config.ScannerProperties.BRANCH_NAME; import static org.sonar.core.config.ScannerProperties.ORGANIZATION; import static org.sonar.core.util.FileUtils.deleteQuietly; +import static org.sonar.scanner.scan.branch.BranchType.PULL_REQUEST; @ScannerSide public class ReportPublisher implements Startable { @@ -180,8 +181,12 @@ public class ReportPublisher implements Startable { String branchName = branchConfiguration.branchName(); if (branchName != null) { - post.setParam(CHARACTERISTIC, "branch=" + branchName); - post.setParam(CHARACTERISTIC, "branchType=" + branchConfiguration.branchType().name()); + if (branchConfiguration.branchType() != PULL_REQUEST) { + post.setParam(CHARACTERISTIC, "branch=" + branchName); + post.setParam(CHARACTERISTIC, "branchType=" + branchConfiguration.branchType().name()); + } else { + post.setParam(CHARACTERISTIC, "pullRequest=" + branchConfiguration.pullRequestKey()); + } } WsResponse response; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java index f63b163ae37..45b9e7f139e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java @@ -55,7 +55,7 @@ public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { @Override public void publish(ScannerReportWriter writer) { - if (branchConfiguration.isShortLivingBranch()) { + if (branchConfiguration.isShortOrPullRequest()) { return; } final ScannerReport.Test.Builder testBuilder = ScannerReport.Test.newBuilder(); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java index 15a5b42076c..94233256871 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java @@ -23,17 +23,26 @@ import com.google.common.base.Joiner; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; import javax.annotation.Nullable; -import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.utils.MessageException; import org.sonar.core.component.ComponentKeys; -import org.sonar.core.config.ScannerProperties; import org.sonar.scanner.bootstrap.GlobalConfiguration; import org.sonar.scanner.scan.branch.BranchParamsValidator; +import static java.lang.String.format; +import static java.util.Objects.nonNull; +import static org.apache.commons.lang.StringUtils.isNotEmpty; +import static org.sonar.core.config.ScannerProperties.BRANCHES_DOC_LINK; +import static org.sonar.core.config.ScannerProperties.BRANCH_NAME; +import static org.sonar.core.config.ScannerProperties.BRANCH_TARGET; +import static org.sonar.core.config.ScannerProperties.PULL_REQUEST_BASE; +import static org.sonar.core.config.ScannerProperties.PULL_REQUEST_BRANCH; +import static org.sonar.core.config.ScannerProperties.PULL_REQUEST_KEY; + /** * This class aims at validating project reactor * @since 3.6 @@ -69,11 +78,11 @@ public class ProjectReactorValidator { String deprecatedBranchName = reactor.getRoot().getBranch(); - if (branchParamsValidator != null) { - // branch plugin is present + if (isBranchFeatureAvailable()) { branchParamsValidator.validate(validationMessages, deprecatedBranchName); } else { validateBranchParamsWhenPluginAbsent(validationMessages); + validatePullRequestParamsWhenPluginAbsent(validationMessages); } validateBranch(validationMessages, deprecatedBranchName); @@ -84,38 +93,49 @@ public class ProjectReactorValidator { } private void validateBranchParamsWhenPluginAbsent(List<String> validationMessages) { - for (String param : Arrays.asList(ScannerProperties.BRANCH_NAME, ScannerProperties.BRANCH_TARGET)) { - if (StringUtils.isNotEmpty(settings.get(param).orElse(null))) { - validationMessages.add(String.format("To use the property \"%s\", the branch plugin is required but not installed. " - + "See the documentation of branch support: %s.", param, ScannerProperties.BRANCHES_DOC_LINK)); + for (String param : Arrays.asList(BRANCH_NAME, BRANCH_TARGET)) { + if (isNotEmpty(settings.get(param).orElse(null))) { + validationMessages.add(format("To use the property \"%s\", the branch plugin is required but not installed. " + + "See the documentation of branch support: %s.", param, BRANCHES_DOC_LINK)); } } } + private void validatePullRequestParamsWhenPluginAbsent(List<String> validationMessages) { + Stream.of(PULL_REQUEST_KEY, PULL_REQUEST_BRANCH, PULL_REQUEST_BASE) + .filter(param -> nonNull(settings.get(param).orElse(null))) + .forEach(param -> validationMessages.add(format("To use the property \"%s\", the branch plugin is required but not installed. " + + "See the documentation of branch support: %s.", param, BRANCHES_DOC_LINK))); + } + private static void validateModuleIssuesMode(ProjectDefinition moduleDef, List<String> validationMessages) { if (!ComponentKeys.isValidModuleKeyIssuesMode(moduleDef.getKey())) { - validationMessages.add(String.format("\"%s\" is not a valid project or module key. " + validationMessages.add(format("\"%s\" is not a valid project or module key. " + "Allowed characters in issues mode are alphanumeric, '-', '_', '.', '/' and ':', with at least one non-digit.", moduleDef.getKey())); } } private static void validateModule(ProjectDefinition moduleDef, List<String> validationMessages) { if (!ComponentKeys.isValidModuleKey(moduleDef.getKey())) { - validationMessages.add(String.format("\"%s\" is not a valid project or module key. " + validationMessages.add(format("\"%s\" is not a valid project or module key. " + "Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", moduleDef.getKey())); } String originalVersion = moduleDef.getOriginalVersion(); if (originalVersion != null && originalVersion.length() > 100) { - validationMessages.add(String.format("\"%s\" is not a valid version name for module \"%s\". " + + validationMessages.add(format("\"%s\" is not a valid version name for module \"%s\". " + "The maximum length for version numbers is 100 characters.", originalVersion, moduleDef.getKey())); } } private static void validateBranch(List<String> validationMessages, @Nullable String branch) { - if (StringUtils.isNotEmpty(branch) && !ComponentKeys.isValidBranch(branch)) { - validationMessages.add(String.format("\"%s\" is not a valid branch name. " + if (isNotEmpty(branch) && !ComponentKeys.isValidBranch(branch)) { + validationMessages.add(format("\"%s\" is not a valid branch name. " + "Allowed characters are alphanumeric, '-', '_', '.' and '/'.", branch)); } } + private boolean isBranchFeatureAvailable() { + return branchParamsValidator != null; + } + } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index b57cc8f10b2..3dd06bbab78 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -20,6 +20,7 @@ package org.sonar.scanner.scan; import com.google.common.annotations.VisibleForTesting; +import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.InstantiationStrategy; @@ -91,6 +92,7 @@ import org.sonar.scanner.scan.branch.BranchConfiguration; import org.sonar.scanner.scan.branch.BranchConfigurationProvider; import org.sonar.scanner.scan.branch.BranchType; import org.sonar.scanner.scan.branch.ProjectBranchesProvider; +import org.sonar.scanner.scan.branch.ProjectPullRequestsProvider; import org.sonar.scanner.scan.filesystem.BatchIdGenerator; import org.sonar.scanner.scan.filesystem.InputComponentStoreProvider; import org.sonar.scanner.scan.filesystem.StatusDetection; @@ -146,6 +148,7 @@ public class ProjectScanContainer extends ComponentContainer { new RulesProvider(), new BranchConfigurationProvider(), new ProjectBranchesProvider(), + new ProjectPullRequestsProvider(), DefaultAnalysisMode.class, new ProjectRepositoriesProvider(), @@ -254,7 +257,13 @@ public class ProjectScanContainer extends ComponentContainer { String branchName = props.property(ScannerProperties.BRANCH_NAME); if (branchName != null) { BranchConfiguration branchConfig = getComponentByType(BranchConfiguration.class); - LOG.info("Branch name: {}, type: {}", branchName, toDisplayName(branchConfig.branchType())); + LOG.info("Branch name: {}, type: {}", branchName, branchTypeToDisplayName(branchConfig.branchType())); + } + + String pullRequestBranch = props.property(ScannerProperties.PULL_REQUEST_BRANCH); + if (pullRequestBranch != null) { + String pullRequestBase = props.property(ScannerProperties.PULL_REQUEST_BASE); + LOG.info("Pull request into {}: {}", pullRequestBaseToDisplayName(pullRequestBase), pullRequestBranch); } LOG.debug("Start recursive analysis of project modules"); @@ -265,7 +274,11 @@ public class ProjectScanContainer extends ComponentContainer { } } - private static String toDisplayName(BranchType branchType) { + private static String pullRequestBaseToDisplayName(@Nullable String pullRequestBase) { + return pullRequestBase != null ? pullRequestBase : "default branch"; + } + + private static String branchTypeToDisplayName(BranchType branchType) { switch (branchType) { case LONG: return "long living"; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfiguration.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfiguration.java index 4ddcff98abd..695c3c6d62d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfiguration.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfiguration.java @@ -36,8 +36,8 @@ public interface BranchConfiguration { */ BranchType branchType(); - default boolean isShortLivingBranch() { - return branchType() == BranchType.SHORT; + default boolean isShortOrPullRequest() { + return branchType() == BranchType.PULL_REQUEST || branchType() == BranchType.SHORT; } /** @@ -54,7 +54,19 @@ public interface BranchConfiguration { /** * The name of the base branch to determine project repository and changed files. + * + * Note: this is important for the scanner during the analysis of long living branches. + * For short living branches, branchBase is always the same as branchTarget. + * For long living branches, branchBase is the target in case of first analysis, + * otherwise it's the branch itself. */ @CheckForNull String branchBase(); + + /** + * The key of the pull request. + * + * @throws IllegalStateException if this branch configuration is not a pull request. + */ + String pullRequestKey(); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationLoader.java index 24b57650ee9..5b845195d05 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationLoader.java @@ -27,5 +27,5 @@ import org.sonar.api.batch.ScannerSide; @ScannerSide @InstantiationStrategy(InstantiationStrategy.PER_BATCH) public interface BranchConfigurationLoader { - BranchConfiguration load(Map<String, String> localSettings, Supplier<Map<String, String>> remoteSettingsSupplier, ProjectBranches branches); + BranchConfiguration load(Map<String, String> localSettings, Supplier<Map<String, String>> remoteSettingsSupplier, ProjectBranches branches, ProjectPullRequests pullRequests); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationProvider.java index 08caee68ca2..276ba411c28 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationProvider.java @@ -39,14 +39,14 @@ public class BranchConfigurationProvider extends ProviderAdapter { private BranchConfiguration branchConfiguration = null; public BranchConfiguration provide(@Nullable BranchConfigurationLoader loader, GlobalConfiguration globalConfiguration, ProjectKey projectKey, - SettingsLoader settingsLoader, ProjectBranches branches) { + SettingsLoader settingsLoader, ProjectBranches branches, ProjectPullRequests pullRequests) { if (branchConfiguration == null) { if (loader == null) { branchConfiguration = new DefaultBranchConfiguration(); } else { Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); Supplier<Map<String, String>> settingsSupplier = createSettingsSupplier(globalConfiguration, projectKey, settingsLoader); - branchConfiguration = loader.load(globalConfiguration.getProperties(), settingsSupplier, branches); + branchConfiguration = loader.load(globalConfiguration.getProperties(), settingsSupplier, branches, pullRequests); profiler.stopInfo(); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchType.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchType.java index b90f633ded0..e8143ce4480 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchType.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchType.java @@ -20,5 +20,5 @@ package org.sonar.scanner.scan.branch; public enum BranchType { - SHORT, LONG + SHORT, LONG, PULL_REQUEST } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/DefaultBranchConfiguration.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/DefaultBranchConfiguration.java index 045c3e3ccd4..2e048e6c689 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/DefaultBranchConfiguration.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/DefaultBranchConfiguration.java @@ -46,4 +46,9 @@ public class DefaultBranchConfiguration implements BranchConfiguration { public String branchBase() { return null; } + + @Override + public String pullRequestKey() { + throw new IllegalStateException("Only a branch of type PULL_REQUEST can have a pull request id."); + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java index 2a59148dbd0..043360fb64f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java @@ -26,8 +26,6 @@ import org.sonar.api.batch.bootstrap.ProjectKey; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; -import org.sonar.core.config.ScannerProperties; -import org.sonar.scanner.bootstrap.GlobalConfiguration; public class ProjectBranchesProvider extends ProviderAdapter { @@ -36,16 +34,19 @@ public class ProjectBranchesProvider extends ProviderAdapter { private ProjectBranches branches = null; - public ProjectBranches provide(@Nullable ProjectBranchesLoader loader, ProjectKey projectKey, GlobalConfiguration settings) { - if (branches == null) { - if (loader == null || !settings.get(ScannerProperties.BRANCH_NAME).isPresent()) { - branches = new ProjectBranches(Collections.emptyList()); - } else { - Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); - branches = loader.load(projectKey.get()); - profiler.stopInfo(); - } + public ProjectBranches provide(@Nullable ProjectBranchesLoader loader, ProjectKey projectKey) { + if (branches != null) { + return branches; } + + if (loader == null) { + branches = new ProjectBranches(Collections.emptyList()); + return branches; + } + + Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); + branches = loader.load(projectKey.get()); + profiler.stopInfo(); return branches; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequests.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequests.java new file mode 100644 index 00000000000..a9f4c2c3d07 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequests.java @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan.branch; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + +/** + * Container class for information about the pull requests of a project. + */ +@Immutable +public class ProjectPullRequests { + + private final Map<String, PullRequestInfo> pullRequestsById; + + public ProjectPullRequests(List<PullRequestInfo> pullRequestsById) { + this.pullRequestsById = pullRequestsById.stream().collect(Collectors.toMap(PullRequestInfo::getBranch, Function.identity())); + } + + @CheckForNull + public PullRequestInfo get(String branch) { + return pullRequestsById.get(branch); + } + + public boolean isEmpty() { + return pullRequestsById.isEmpty(); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsLoader.java new file mode 100644 index 00000000000..525afd56f38 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsLoader.java @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan.branch; + +import org.sonar.api.batch.InstantiationStrategy; +import org.sonar.api.batch.ScannerSide; + +@ScannerSide +@InstantiationStrategy(InstantiationStrategy.PER_BATCH) +public interface ProjectPullRequestsLoader { + + /** + * Load the pull requests of a project. + */ + ProjectPullRequests load(String projectKey); + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProvider.java new file mode 100644 index 00000000000..15023c8c2fc --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProvider.java @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan.branch; + +import java.util.Collections; +import org.picocontainer.injectors.ProviderAdapter; +import org.sonar.api.batch.bootstrap.ProjectKey; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; + +public class ProjectPullRequestsProvider extends ProviderAdapter { + + private static final Logger LOG = Loggers.get(ProjectPullRequestsProvider.class); + private static final String LOG_MSG = "Load project pull requests"; + + private ProjectPullRequests pullRequests = null; + + public ProjectPullRequests provide(@org.picocontainer.annotations.Nullable ProjectPullRequestsLoader loader, ProjectKey projectKey) { + if (pullRequests != null) { + return pullRequests; + } + + if (loader == null) { + pullRequests = new ProjectPullRequests(Collections.emptyList()); + return pullRequests; + } + + Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); + pullRequests = loader.load(projectKey.get()); + profiler.stopInfo(); + return pullRequests; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/PullRequestInfo.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/PullRequestInfo.java new file mode 100644 index 00000000000..6ccc3656fb5 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/PullRequestInfo.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan.branch; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Container class for information about a pull request. + */ +@Immutable +public class PullRequestInfo { + private final String id; + private final String branch; + private final String base; + + public PullRequestInfo(String id, String branch, @Nullable String base) { + this.id = id; + this.branch = branch; + this.base = base; + } + + public String getId() { + return id; + } + + public String getBranch() { + return branch; + } + + @CheckForNull + public String getBase() { + return base; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java index 3ea00232847..e6d96e51733 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java @@ -80,7 +80,8 @@ public class InputComponentStore { return inputFileCache.values().stream() .map(f -> (DefaultInputFile) f) .filter(DefaultInputFile::isPublished) - .filter(f -> (!branchConfiguration.isShortLivingBranch()) || f.status() != Status.SAME)::iterator; + .filter(f -> !branchConfiguration.isShortOrPullRequest() || f.status() != Status.SAME) + ::iterator; } public Iterable<InputFile> allFiles() { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java index 38e2416bfad..a04b4f5021a 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java @@ -62,7 +62,7 @@ public class ScmChangedFilesProvider extends ProviderAdapter { @CheckForNull private static Collection<Path> loadChangedFilesIfNeeded(ScmConfiguration scmConfiguration, BranchConfiguration branchConfiguration, Path rootBaseDir) { - if (branchConfiguration.isShortLivingBranch() && branchConfiguration.branchTarget() != null) { + if (branchConfiguration.isShortOrPullRequest() && branchConfiguration.branchTarget() != null) { ScmProvider scmProvider = scmConfiguration.provider(); if (scmProvider != null) { Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java index cbdafc68bcd..bb587269fd9 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java @@ -103,7 +103,7 @@ public final class ScmPublisher { } if (configuration.forceReloadAll() || f.status() != Status.SAME) { addIfNotEmpty(filesToBlame, f); - } else if (!branchConfiguration.isShortLivingBranch()) { + } else if (!branchConfiguration.isShortOrPullRequest()) { // File status is SAME so that mean fileData exists FileData fileData = projectRepositories.fileData(inputModule.definition().getKeyWithBranch(), inputFile.getModuleRelativePath()); if (StringUtils.isEmpty(fileData.revision())) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java index 6947f918d60..6972e241500 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java @@ -148,7 +148,7 @@ public class DefaultSensorContext implements SensorContext { @Override public NewCoverage newCoverage() { - if (branchConfiguration.isShortLivingBranch()) { + if (branchConfiguration.isShortOrPullRequest()) { return NO_OP_NEW_COVERAGE; } return new DefaultCoverage(sensorStorage); @@ -156,7 +156,7 @@ public class DefaultSensorContext implements SensorContext { @Override public NewCpdTokens newCpdTokens() { - if (analysisMode.isIssues() || branchConfiguration.isShortLivingBranch()) { + if (analysisMode.isIssues() || branchConfiguration.isShortOrPullRequest()) { return NO_OP_NEW_CPD_TOKENS; } return new DefaultCpdTokens(config, sensorStorage); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java index 43d9198a06c..6b5a7137d94 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java @@ -355,7 +355,7 @@ public class DefaultSensorStorage implements SensorStorage { } private boolean shouldSkipStorage(DefaultInputFile defaultInputFile) { - return branchConfiguration.isShortLivingBranch() && defaultInputFile.status() == InputFile.Status.SAME; + return branchConfiguration.isShortOrPullRequest() && defaultInputFile.status() == InputFile.Status.SAME; } /** diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java index 8605ab5418d..f685e4f22cf 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java @@ -100,7 +100,18 @@ public class CpdExecutorTest { @Test public void skipIfShortBranch() { - when(branchConfig.isShortLivingBranch()).thenReturn(true); + when(branchConfig.isShortOrPullRequest()).thenReturn(true); + index = mock(SonarCpdBlockIndex.class); + executor = new CpdExecutor(settings, index, publisher, componentStore, branchConfig); + + executor.execute(); + + verifyZeroInteractions(index); + } + + @Test + public void skip_if_pull_request() { + when(branchConfig.isShortOrPullRequest()).thenReturn(true); index = mock(SonarCpdBlockIndex.class); executor = new CpdExecutor(settings, index, publisher, componentStore, branchConfig); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java index 71d86e98de6..eb28ba1a3d0 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java @@ -72,6 +72,7 @@ import org.sonar.scanner.scan.branch.BranchConfiguration; import org.sonar.scanner.scan.branch.BranchConfigurationLoader; import org.sonar.scanner.scan.branch.BranchType; import org.sonar.scanner.scan.branch.ProjectBranches; +import org.sonar.scanner.scan.branch.ProjectPullRequests; import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; import org.sonarqube.ws.Rules.ListResponse.Rule; @@ -420,6 +421,11 @@ public class ScannerMediumTester extends ExternalResource { public String branchBase() { return branchBase; } + + @Override + public String pullRequestKey() { + throw new UnsupportedOperationException(); + } } public ScannerMediumTester setBranchType(BranchType branchType) { @@ -439,7 +445,7 @@ public class ScannerMediumTester extends ExternalResource { private class FakeBranchConfigurationLoader implements BranchConfigurationLoader { @Override - public BranchConfiguration load(Map<String, String> localSettings, Supplier<Map<String, String>> settingsSupplier, ProjectBranches branches) { + public BranchConfiguration load(Map<String, String> localSettings, Supplier<Map<String, String>> settingsSupplier, ProjectBranches branches, ProjectPullRequests pullRequests) { return branchConfiguration; } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java index eda36629c96..1e84cff4434 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java @@ -310,7 +310,77 @@ public class ComponentsPublisherTest { @Test public void skip_unchanged_components_in_short_branches() throws IOException { - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); + ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); + when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); + + Path moduleBaseDir = temp.newFolder().toPath(); + ProjectDefinition rootDef = ProjectDefinition.create() + .setKey("foo") + .setProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0") + .setName("Root project") + .setDescription("Root description") + .setBaseDir(moduleBaseDir.toFile()) + .setWorkDir(temp.newFolder()); + DefaultInputModule root = new DefaultInputModule(rootDef, 1); + + moduleHierarchy = mock(InputModuleHierarchy.class); + when(moduleHierarchy.root()).thenReturn(root); + when(moduleHierarchy.children(root)).thenReturn(Collections.emptyList()); + + // dir with changed files + DefaultInputDir dir = new DefaultInputDir("module1", "src", 2) + .setModuleBaseDir(moduleBaseDir); + tree.index(dir, root); + + // dir without changed files or issues + DefaultInputDir dir2 = new DefaultInputDir("module1", "src2", 3) + .setModuleBaseDir(moduleBaseDir); + tree.index(dir2, root); + + // dir without changed files but has issues + DefaultInputDir dir3 = new DefaultInputDir("module1", "src3", 4) + .setModuleBaseDir(moduleBaseDir); + tree.index(dir3, root); + writeIssue(4); + + DefaultInputFile file = new TestInputFileBuilder("module1", "src/Foo.java", 5) + .setLines(2) + .setPublish(true) + .setStatus(InputFile.Status.ADDED) + .build(); + tree.index(file, dir); + + DefaultInputFile file2 = new TestInputFileBuilder("module1", "src2/Foo2.java", 6) + .setPublish(true) + .setStatus(InputFile.Status.SAME) + .setLines(2) + .build(); + tree.index(file2, dir2); + + DefaultInputFile file3 = new TestInputFileBuilder("module1", "src3/Foo3.java", 7) + .setPublish(true) + .setStatus(InputFile.Status.SAME) + .setLines(2) + .build(); + tree.index(file3, dir3); + + ComponentsPublisher publisher = new ComponentsPublisher(moduleHierarchy, tree, branchConfiguration); + publisher.publish(writer); + + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 1)).isTrue(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 2)).isTrue(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 4)).isTrue(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 5)).isTrue(); + + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 3)).isFalse(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 6)).isFalse(); + assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 7)).isFalse(); + } + + @Test + public void skip_unchanged_components_in_pull_requests() throws IOException { + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java index 128561c45bf..b25f773f536 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java @@ -59,6 +59,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.sonar.scanner.scan.branch.BranchType.PULL_REQUEST; import static org.sonar.scanner.scan.branch.BranchType.SHORT; public class ReportPublisherTest { @@ -267,4 +268,42 @@ public class ReportPublisherTest { .containsExactlyInAnyOrder("branch=" + branchName, "branchType=" + SHORT.name()); } + @Test + public void send_pull_request_characteristic() throws Exception { + ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), + new ReportPublisherStep[0], branchConfiguration); + + String orgName = "MyOrg"; + settings.setProperty(ScannerProperties.ORGANIZATION, orgName); + + String branchName = "feature"; + String pullRequestId = "pr-123"; + when(branchConfiguration.branchName()).thenReturn(branchName); + when(branchConfiguration.branchType()).thenReturn(PULL_REQUEST); + when(branchConfiguration.pullRequestKey()).thenReturn(pullRequestId); + + WsResponse response = mock(WsResponse.class); + + PipedOutputStream out = new PipedOutputStream(); + PipedInputStream in = new PipedInputStream(out); + Ce.SubmitResponse.newBuilder().build().writeTo(out); + out.close(); + + when(response.failIfNotSuccessful()).thenReturn(response); + when(response.contentStream()).thenReturn(in); + + when(wsClient.call(any(WsRequest.class))).thenReturn(response); + underTest.upload(temp.newFile()); + + ArgumentCaptor<WsRequest> capture = ArgumentCaptor.forClass(WsRequest.class); + verify(wsClient).call(capture.capture()); + + WsRequest wsRequest = capture.getValue(); + assertThat(wsRequest.getParameters().getKeys()).hasSize(3); + assertThat(wsRequest.getParameters().getValues("organization")).containsExactly(orgName); + assertThat(wsRequest.getParameters().getValues("projectKey")).containsExactly("struts"); + assertThat(wsRequest.getParameters().getValues("characteristic")) + .containsExactlyInAnyOrder("pullRequest=" + pullRequestId); + } + } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisherTest.java index 7b35a9af541..a1175ee2332 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisherTest.java @@ -40,7 +40,21 @@ public class TestExecutionAndCoveragePublisherTest { @Test public void do_nothing_for_short_living_branches() throws IOException { BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); + InputComponentStore componentStore = mock(InputComponentStore.class); + TestExecutionAndCoveragePublisher publisher = new TestExecutionAndCoveragePublisher(componentStore, null, branchConfiguration); + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + + publisher.publish(writer); + + verifyZeroInteractions(componentStore); + } + + @Test + public void do_nothing_for_pull_requests() throws IOException { + BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); InputComponentStore componentStore = mock(InputComponentStore.class); TestExecutionAndCoveragePublisher publisher = new TestExecutionAndCoveragePublisher(componentStore, null, branchConfiguration); File outputDir = temp.newFolder(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java index e71a71f161b..0c0799c9ab3 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java @@ -191,6 +191,45 @@ public class ProjectReactorValidatorTest { } @Test + public void fail_when_pull_request_id_specified_but_branch_plugin_not_present() { + ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo"); + ProjectReactor reactor = new ProjectReactor(def); + + when(settings.get(eq(ScannerProperties.PULL_REQUEST_KEY))).thenReturn(Optional.of("#1984")); + + thrown.expect(MessageException.class); + thrown.expectMessage("the branch plugin is required but not installed"); + + validator.validate(reactor); + } + + @Test + public void fail_when_pull_request_branch_is_specified_but_branch_plugin_not_present() { + ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo"); + ProjectReactor reactor = new ProjectReactor(def); + + when(settings.get(eq(ScannerProperties.PULL_REQUEST_BRANCH))).thenReturn(Optional.of("feature1")); + + thrown.expect(MessageException.class); + thrown.expectMessage("the branch plugin is required but not installed"); + + validator.validate(reactor); + } + + @Test + public void fail_when_pull_request_base_specified_but_branch_plugin_not_present() { + ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo"); + ProjectReactor reactor = new ProjectReactor(def); + + when(settings.get(eq(ScannerProperties.PULL_REQUEST_BASE))).thenReturn(Optional.of("feature1")); + + thrown.expect(MessageException.class); + thrown.expectMessage("the branch plugin is required but not installed"); + + validator.validate(reactor); + } + + @Test public void not_fail_with_valid_version() { validator.validate(createProjectReactor("foo", def -> def.setVersion("1.0"))); validator.validate(createProjectReactor("foo", def -> def.setVersion("2017-10-16"))); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/BranchConfigurationProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/BranchConfigurationProviderTest.java index 067114dd43b..a8fbc75cc8b 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/BranchConfigurationProviderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/BranchConfigurationProviderTest.java @@ -41,6 +41,7 @@ public class BranchConfigurationProviderTest { private BranchConfigurationLoader loader; private BranchConfiguration config; private ProjectBranches branches; + private ProjectPullRequests pullRequests; private ProjectKey projectKey; private Map<String, String> globalPropertiesMap; private Map<String, String> remoteProjectSettings; @@ -52,6 +53,7 @@ public class BranchConfigurationProviderTest { loader = mock(BranchConfigurationLoader.class); config = mock(BranchConfiguration.class); branches = mock(ProjectBranches.class); + pullRequests = mock(ProjectPullRequests.class); settingsLoader = mock(SettingsLoader.class); projectKey = mock(ProjectKey.class); globalPropertiesMap = new HashMap<>(); @@ -61,22 +63,24 @@ public class BranchConfigurationProviderTest { @Test public void should_cache_config() { - BranchConfiguration configuration = provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches); - assertThat(provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches)).isSameAs(configuration); + BranchConfiguration configuration = provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches, pullRequests); + assertThat(provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches, pullRequests)).isSameAs(configuration); } @Test public void should_use_loader() { - when(loader.load(eq(globalPropertiesMap), any(Supplier.class), eq(branches))).thenReturn(config); - BranchConfiguration branchConfig = provider.provide(loader, globalConfiguration, projectKey, settingsLoader, branches); + when(loader.load(eq(globalPropertiesMap), any(Supplier.class), eq(branches), eq(pullRequests))).thenReturn(config); - assertThat(branchConfig).isSameAs(config); + BranchConfiguration result = provider.provide(loader, globalConfiguration, projectKey, settingsLoader, branches, pullRequests); + + assertThat(result).isSameAs(config); } @Test public void should_return_default_if_no_loader() { - BranchConfiguration configuration = provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches); - assertThat(configuration.branchTarget()).isNull(); - assertThat(configuration.branchType()).isEqualTo(BranchType.LONG); + BranchConfiguration result = provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches, pullRequests); + + assertThat(result.branchTarget()).isNull(); + assertThat(result.branchType()).isEqualTo(BranchType.LONG); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectBranchesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectBranchesProviderTest.java index 6b75d766e3e..fd22c4d96e8 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectBranchesProviderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectBranchesProviderTest.java @@ -19,11 +19,8 @@ */ package org.sonar.scanner.scan.branch; -import java.util.Optional; import org.junit.Before; import org.junit.Test; -import org.sonar.api.config.Configuration; -import org.sonar.scanner.bootstrap.GlobalConfiguration; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; @@ -34,33 +31,30 @@ public class ProjectBranchesProviderTest { private ProjectBranchesProvider provider = new ProjectBranchesProvider(); private ProjectBranchesLoader mockLoader; private ProjectBranches mockBranches; - private GlobalConfiguration mockSettings; @Before public void setUp() { mockLoader = mock(ProjectBranchesLoader.class); mockBranches = mock(ProjectBranches.class); - mockSettings = mock(GlobalConfiguration.class); } @Test public void should_cache_branches() { - ProjectBranches branches = provider.provide(null, () -> "project", mockSettings); - assertThat(provider.provide(null, () -> "project", mockSettings)).isSameAs(branches); + ProjectBranches branches = provider.provide(null, () -> "project"); + assertThat(provider.provide(null, () -> "project")).isSameAs(branches); } @Test public void should_use_loader() { when(mockLoader.load("key")).thenReturn(mockBranches); - when(mockSettings.get(anyString())).thenReturn(Optional.of("somebranch")); - ProjectBranches branches = provider.provide(mockLoader, () -> "key", mockSettings); + ProjectBranches branches = provider.provide(mockLoader, () -> "key"); assertThat(branches).isSameAs(mockBranches); } @Test public void should_return_default_if_no_loader() { - ProjectBranches branches = provider.provide(null, () -> "project", mockSettings); + ProjectBranches branches = provider.provide(null, () -> "project"); assertThat(branches.isEmpty()).isTrue(); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProviderTest.java new file mode 100644 index 00000000000..b3c1ab87fd7 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProviderTest.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan.branch; + +import org.junit.Before; +import org.junit.Test; + +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ProjectPullRequestsProviderTest { + private ProjectPullRequestsProvider provider = new ProjectPullRequestsProvider(); + private ProjectPullRequestsLoader mockLoader; + private ProjectPullRequests pullRequests; + + @Before + public void setUp() { + mockLoader = mock(ProjectPullRequestsLoader.class); + pullRequests = new ProjectPullRequests(emptyList()); + } + + @Test + public void cache_pull_requests() { + ProjectPullRequests pullRequests = provider.provide(null, () -> "project"); + + assertThat(provider.provide(null, () -> "project")).isSameAs(pullRequests); + } + + @Test + public void should_use_loader() { + when(mockLoader.load("key")).thenReturn(pullRequests); + + ProjectPullRequests result = provider.provide(mockLoader, () -> "key"); + + assertThat(result).isSameAs(pullRequests); + } + + @Test + public void should_return_default_if_no_loader() { + ProjectPullRequests result = provider.provide(null, () -> "project"); + + assertThat(result.isEmpty()).isTrue(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java index 83db811b6b9..bc212aa9489 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java @@ -66,7 +66,7 @@ public class ScmChangedFilesProviderTest { @Test public void testNoScmProvider() { - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); when(branchConfiguration.branchTarget()).thenReturn("target"); ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); @@ -78,7 +78,7 @@ public class ScmChangedFilesProviderTest { @Test public void testFailIfRelativePath() { when(branchConfiguration.branchTarget()).thenReturn("target"); - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); when(scmConfiguration.provider()).thenReturn(scmProvider); when(scmProvider.branchChangedFiles("target", rootBaseDir)).thenReturn(Collections.singleton(Paths.get("changedFile"))); @@ -90,7 +90,7 @@ public class ScmChangedFilesProviderTest { @Test public void testProviderDoesntSupport() { when(branchConfiguration.branchTarget()).thenReturn("target"); - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); when(scmConfiguration.provider()).thenReturn(scmProvider); when(scmProvider.branchChangedFiles("target", rootBaseDir)).thenReturn(null); ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); @@ -101,7 +101,7 @@ public class ScmChangedFilesProviderTest { @Test public void testNoOpInNonShortLivedBranch() { - when(branchConfiguration.isShortLivingBranch()).thenReturn(false); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(false); ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); assertThat(scmChangedFiles.get()).isNull(); @@ -118,7 +118,7 @@ public class ScmChangedFilesProviderTest { }; when(scmConfiguration.provider()).thenReturn(legacy); - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); when(branchConfiguration.branchTarget()).thenReturn("target"); ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); @@ -130,7 +130,7 @@ public class ScmChangedFilesProviderTest { @Test public void testReturnChangedFiles() { when(branchConfiguration.branchTarget()).thenReturn("target"); - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); when(scmConfiguration.provider()).thenReturn(scmProvider); when(scmProvider.branchChangedFiles("target", rootBaseDir)).thenReturn(Collections.singleton(Paths.get("changedFile").toAbsolutePath())); ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); @@ -143,7 +143,7 @@ public class ScmChangedFilesProviderTest { public void testCacheObject() { provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); - verify(branchConfiguration).isShortLivingBranch(); + verify(branchConfiguration).isShortOrPullRequest(); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java index d09769ee3a8..68771fe5059 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java @@ -91,7 +91,14 @@ public class DefaultSensorContextTest { @Test public void shouldSkipDupsAndCoverageOnShortBranches() { - when(branchConfig.isShortLivingBranch()).thenReturn(true); + when(branchConfig.isShortOrPullRequest()).thenReturn(true); + assertThat(adaptor.newCpdTokens()).isEqualTo(DefaultSensorContext.NO_OP_NEW_CPD_TOKENS); + assertThat(adaptor.newCoverage()).isEqualTo(DefaultSensorContext.NO_OP_NEW_COVERAGE); + } + + @Test + public void shouldSkipDupsAndCoverageOnPullRequests() { + when(branchConfig.isShortOrPullRequest()).thenReturn(true); assertThat(adaptor.newCpdTokens()).isEqualTo(DefaultSensorContext.NO_OP_NEW_CPD_TOKENS); assertThat(adaptor.newCoverage()).isEqualTo(DefaultSensorContext.NO_OP_NEW_COVERAGE); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java index 52da53edbf5..843e4730b45 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java @@ -127,7 +127,7 @@ public class DefaultSensorStorageTest { @Test public void should_skip_issue_on_short_branch_when_file_status_is_SAME() { InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").setStatus(InputFile.Status.SAME).build(); - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); DefaultIssue issue = new DefaultIssue().at(new DefaultIssueLocation().on(file)); underTest.store(issue); @@ -151,7 +151,7 @@ public class DefaultSensorStorageTest { DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php") .setContents("// comment") .setStatus(InputFile.Status.SAME).build(); - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); DefaultHighlighting highlighting = new DefaultHighlighting(underTest).onFile(file).highlight(0, 1, TypeOfText.KEYWORD); underTest.store(highlighting); @@ -178,7 +178,20 @@ public class DefaultSensorStorageTest { @Test public void should_skip_file_measure_on_short_branch_when_file_status_is_SAME() { InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").setStatus(InputFile.Status.SAME).build(); - when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); + + underTest.store(new DefaultMeasure() + .on(file) + .forMetric(CoreMetrics.NCLOC) + .withValue(10)); + + verifyZeroInteractions(measureCache); + } + + @Test + public void should_skip_file_measure_on_pull_request_when_file_status_is_SAME() { + InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").setStatus(InputFile.Status.SAME).build(); + when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); underTest.store(new DefaultMeasure() .on(file) |