From e19f3f56fe2d8bc2101086b166ec4de92a5c3c8f Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Tue, 19 Sep 2017 17:25:57 +0200 Subject: SONAR-9837 Detect files changed in the current branch with SCM --- .../api/batch/fs/internal/DefaultInputFile.java | 4 - .../org/sonar/api/batch/scm/ScmBranchProvider.java | 47 ++++++++ .../sonar/scanner/scan/ModuleScanContainer.java | 1 - .../sonar/scanner/scan/ProjectScanContainer.java | 4 + .../scanner/scan/filesystem/MetadataGenerator.java | 6 +- .../scanner/scan/filesystem/StatusDetection.java | 14 ++- .../scan/filesystem/StatusDetectionFactory.java | 7 +- .../org/sonar/scanner/scm/ScmChangedFiles.java | 43 +++++++ .../sonar/scanner/scm/ScmChangedFilesProvider.java | 73 ++++++++++++ .../org/sonar/scanner/scm/ScmConfiguration.java | 9 +- .../scan/filesystem/MetadataGeneratorTest.java | 5 +- .../filesystem/StatusDetectionFactoryTest.java | 3 +- .../scan/filesystem/StatusDetectionTest.java | 40 ++++++- .../scanner/scm/ScmChangedFilesProviderTest.java | 125 +++++++++++++++++++++ .../org/sonar/scanner/scm/ScmChangedFilesTest.java | 54 +++++++++ 15 files changed, 408 insertions(+), 27 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmBranchProvider.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFiles.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesTest.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java index 917440ecb77..6def80e8ea6 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java @@ -137,10 +137,6 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile return indexedFile.getProjectRelativePath(); } - /** - * @deprecated since 6.6 - */ - @Deprecated @Override public String absolutePath() { return indexedFile.absolutePath(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmBranchProvider.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmBranchProvider.java new file mode 100644 index 00000000000..5984182c010 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmBranchProvider.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.api.batch.scm; + +import java.nio.file.Path; +import java.util.Collection; +import javax.annotation.Nullable; +import org.sonar.api.ExtensionPoint; +import org.sonar.api.batch.InstantiationStrategy; +import org.sonar.api.batch.ScannerSide; + +/** + * A {@link ScmProvider} with the capability of finding out which files were changed in a branch. + * @since 6.6 + */ +@ScannerSide +@InstantiationStrategy(InstantiationStrategy.PER_BATCH) +@ExtensionPoint +public abstract class ScmBranchProvider extends ScmProvider { + + /** + * Return absolute path of files changed in the current branch, compared to the provided target branch. + * @return null if SCM provider was not able to compute the list of files. + */ + @Nullable + public Collection branchChangedFiles(String targetBranchName, Path rootBaseDir) { + return null; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java index b9c7d779257..14e403f2a01 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java @@ -119,7 +119,6 @@ public class ModuleScanContainer extends ComponentContainer { ExclusionFilters.class, new MetadataGeneratorProvider(), FileMetadata.class, - StatusDetectionFactory.class, LanguageDetection.class, FileIndexer.class, InputFileBuilder.class, 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 7deb3166880..fc34c441582 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 @@ -93,9 +93,11 @@ import org.sonar.scanner.scan.branch.BranchType; import org.sonar.scanner.scan.branch.ProjectBranchesProvider; import org.sonar.scanner.scan.filesystem.BatchIdGenerator; import org.sonar.scanner.scan.filesystem.InputComponentStoreProvider; +import org.sonar.scanner.scan.filesystem.StatusDetectionFactory; import org.sonar.scanner.scan.measure.DefaultMetricFinder; import org.sonar.scanner.scan.measure.DeprecatedMetricFinder; import org.sonar.scanner.scan.measure.MeasureCache; +import org.sonar.scanner.scm.ScmChangedFilesProvider; import org.sonar.scanner.storage.Storages; public class ProjectScanContainer extends ComponentContainer { @@ -157,6 +159,8 @@ public class ProjectScanContainer extends ComponentContainer { new InputModuleHierarchyProvider(), DefaultComponentTree.class, BatchIdGenerator.class, + new ScmChangedFilesProvider(), + StatusDetectionFactory.class, // rules new ActiveRulesProvider(), diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/MetadataGenerator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/MetadataGenerator.java index f0bfe220d08..3ad3c9960ad 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/MetadataGenerator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/MetadataGenerator.java @@ -62,15 +62,15 @@ class MetadataGenerator { if (charsetDetector.run()) { charset = charsetDetector.charset(); } else { - LOG.debug("Failed to detect a valid charset for file '{}'. Using default charset.", inputFile.relativePath()); + LOG.debug("Failed to detect a valid charset for file '{}'. Using default charset.", inputFile); charset = defaultEncoding; } InputStream is = charsetDetector.inputStream(); inputFile.setCharset(charset); Metadata metadata = fileMetadata.readMetadata(is, charset, inputFile.absolutePath(), exclusionsScanner.createCharHandlerFor(inputFile.key())); inputFile.setMetadata(metadata); - inputFile.setStatus(statusDetection.status(inputModule.definition().getKeyWithBranch(), inputFile.relativePath(), metadata.hash())); - LOG.debug("'{}' generated metadata {} with charset '{}'", inputFile.relativePath(), inputFile.type() == Type.TEST ? "as test " : "", charset); + inputFile.setStatus(statusDetection.status(inputModule.definition().getKeyWithBranch(), inputFile, metadata.hash())); + LOG.debug("'{}' generated metadata {} with charset '{}'", inputFile, inputFile.type() == Type.TEST ? "as test " : "", charset); } catch (Exception e) { throw new IllegalStateException(e); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java index 79f3576e67b..b91a8165734 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java @@ -20,23 +20,26 @@ package org.sonar.scanner.scan.filesystem; import javax.annotation.concurrent.Immutable; - import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.scanner.repository.FileData; import org.sonar.scanner.repository.ProjectRepositories; +import org.sonar.scanner.scm.ScmChangedFiles; @Immutable class StatusDetection { private final ProjectRepositories projectRepositories; + private final ScmChangedFiles scmChangedFiles; - StatusDetection(ProjectRepositories projectSettings) { + StatusDetection(ProjectRepositories projectSettings, ScmChangedFiles scmChangedFiles) { this.projectRepositories = projectSettings; + this.scmChangedFiles = scmChangedFiles; } - InputFile.Status status(String projectKeyWithBranch, String relativePath, String hash) { - FileData fileDataPerPath = projectRepositories.fileData(projectKeyWithBranch, relativePath); + InputFile.Status status(String projectKeyWithBranch, DefaultInputFile inputFile, String hash) { + FileData fileDataPerPath = projectRepositories.fileData(projectKeyWithBranch, inputFile.relativePath()); if (fileDataPerPath == null) { return InputFile.Status.ADDED; } @@ -47,6 +50,9 @@ class StatusDetection { if (StringUtils.isEmpty(previousHash)) { return InputFile.Status.ADDED; } + if (!scmChangedFiles.confirmChanged(inputFile.path())) { + return InputFile.Status.SAME; + } return InputFile.Status.CHANGED; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetectionFactory.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetectionFactory.java index e4eabe778d4..b1cdf614e6b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetectionFactory.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetectionFactory.java @@ -21,17 +21,20 @@ package org.sonar.scanner.scan.filesystem; import org.sonar.api.batch.ScannerSide; import org.sonar.scanner.repository.ProjectRepositories; +import org.sonar.scanner.scm.ScmChangedFiles; @ScannerSide public class StatusDetectionFactory { private final ProjectRepositories projectReferentials; + private final ScmChangedFiles scmChangedFiles; - public StatusDetectionFactory(ProjectRepositories projectReferentials) { + public StatusDetectionFactory(ProjectRepositories projectReferentials, ScmChangedFiles scmChangedFiles) { this.projectReferentials = projectReferentials; + this.scmChangedFiles = scmChangedFiles; } StatusDetection create() { - return new StatusDetection(projectReferentials); + return new StatusDetection(projectReferentials, scmChangedFiles); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFiles.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFiles.java new file mode 100644 index 00000000000..d4dcdd9ac00 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFiles.java @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.scm; + +import java.nio.file.Path; +import java.util.Collection; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +public class ScmChangedFiles { + @Nullable + private final Collection fileCollection; + + public ScmChangedFiles(@Nullable Collection changedFiles) { + this.fileCollection = changedFiles; + } + + public boolean verifyChanged(Path file) { + return fileCollection == null || fileCollection.contains(file); + } + + Collection get() { + return fileCollection; + } +} 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 new file mode 100644 index 00000000000..60b87a1fc0a --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.scm; + +import java.nio.file.Path; +import java.util.Collection; +import javax.annotation.CheckForNull; +import org.picocontainer.annotations.Nullable; +import org.picocontainer.injectors.ProviderAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.batch.scm.ScmBranchProvider; +import org.sonar.api.batch.scm.ScmProvider; +import org.sonar.scanner.scan.branch.BranchConfiguration; + +public class ScmChangedFilesProvider extends ProviderAdapter { + private static final Logger LOG = LoggerFactory.getLogger(ScmChangedFilesProvider.class); + + private ScmChangedFiles scmBranchChangedFiles; + + /* + * ScmConfiguration is not available in issues mode + */ + public ScmChangedFiles provide(@Nullable ScmConfiguration scmConfiguration, BranchConfiguration branchConfiguration, InputModuleHierarchy inputModuleHierarchy) { + if (scmBranchChangedFiles == null) { + if (scmConfiguration == null) { + scmBranchChangedFiles = new ScmChangedFiles(null); + } else { + Path rootBaseDir = inputModuleHierarchy.root().getBaseDir(); + Collection changedFiles = loadChangedFilesIfNeeded(scmConfiguration, branchConfiguration, rootBaseDir); + scmBranchChangedFiles = new ScmChangedFiles(changedFiles); + } + } + return scmBranchChangedFiles; + } + + @CheckForNull + private static Collection loadChangedFilesIfNeeded(ScmConfiguration scmConfiguration, BranchConfiguration branchConfiguration, Path rootBaseDir) { + if (branchConfiguration.isShortLivingBranch()) { + ScmProvider scmProvider = scmConfiguration.provider(); + if (scmProvider != null && (scmProvider instanceof ScmBranchProvider)) { + ScmBranchProvider scmBranchProvider = (ScmBranchProvider) scmProvider; + Collection changedFiles = scmBranchProvider.branchChangedFiles(branchConfiguration.branchTarget(), rootBaseDir); + if (changedFiles != null) { + LOG.debug("SCM reported {} files changed in the branch", changedFiles.size()); + return changedFiles; + } + } + + LOG.debug("SCM information about changed files in the branch is not available"); + } + return null; + } + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java index ef380a0c74e..f34abc0eba6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java @@ -19,9 +19,10 @@ */ package org.sonar.scanner.scm; -import com.google.common.base.Joiner; import java.util.LinkedHashMap; import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.CheckForNull; import org.apache.commons.lang.StringUtils; import org.picocontainer.Startable; import org.sonar.api.CoreProperties; @@ -51,7 +52,7 @@ import org.sonar.api.utils.log.Loggers; }) @InstantiationStrategy(InstantiationStrategy.PER_BATCH) @ScannerSide -public final class ScmConfiguration implements Startable { +public class ScmConfiguration implements Startable { private static final Logger LOG = Loggers.get(ScmConfiguration.class); public static final String FORCE_RELOAD_KEY = "sonar.scm.forceReloadAll"; @@ -103,7 +104,8 @@ public final class ScmConfiguration implements Startable { if (providerPerKey.containsKey(forcedProviderKey)) { this.provider = providerPerKey.get(forcedProviderKey); } else { - String supportedProviders = providerPerKey.isEmpty() ? "No SCM provider installed" : ("Supported SCM providers are " + Joiner.on(",").join(providerPerKey.keySet())); + String supportedProviders = providerPerKey.isEmpty() ? "No SCM provider installed" + : ("Supported SCM providers are " + providerPerKey.keySet().stream().collect(Collectors.joining(","))); throw new IllegalArgumentException("SCM provider was set to \"" + forcedProviderKey + "\" but no SCM provider found for this key. " + supportedProviders); } } @@ -132,6 +134,7 @@ public final class ScmConfiguration implements Startable { } } + @CheckForNull public ScmProvider provider() { return provider; } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MetadataGeneratorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MetadataGeneratorTest.java index 27fa5362f54..2dd1961963c 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MetadataGeneratorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MetadataGeneratorTest.java @@ -140,11 +140,10 @@ public class MetadataGeneratorTest { FileUtils.write(srcFile.toFile(), "single line"); // status - when(statusDetection.status("foo", "src/main/java/foo/Bar.java", "6c1d64c0b3555892fe7273e954f6fb5a")) + DefaultInputFile inputFile = createInputFileWithMetadata(baseDir, "src/main/java/foo/Bar.java"); + when(statusDetection.status("foo", inputFile, "6c1d64c0b3555892fe7273e954f6fb5a")) .thenReturn(InputFile.Status.ADDED); - InputFile inputFile = createInputFileWithMetadata(baseDir, "src/main/java/foo/Bar.java"); - assertThat(inputFile.type()).isEqualTo(InputFile.Type.MAIN); assertThat(inputFile.file()).isEqualTo(srcFile.toFile()); assertThat(inputFile.absolutePath()).isEqualTo(PathUtils.sanitize(srcFile.toAbsolutePath().toString())); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/StatusDetectionFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/StatusDetectionFactoryTest.java index 160580763f0..259b5e5a653 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/StatusDetectionFactoryTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/StatusDetectionFactoryTest.java @@ -21,6 +21,7 @@ package org.sonar.scanner.scan.filesystem; import org.junit.Test; import org.sonar.scanner.repository.ProjectRepositories; +import org.sonar.scanner.scm.ScmChangedFiles; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -28,7 +29,7 @@ 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(ProjectRepositories.class), mock(ScmChangedFiles.class)); StatusDetection detection = factory.create(); assertThat(detection).isNotNull(); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/StatusDetectionTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/StatusDetectionTest.java index a295fdc08be..bcf70eb65d7 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/StatusDetectionTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/StatusDetectionTest.java @@ -22,23 +22,47 @@ package org.sonar.scanner.scan.filesystem; import com.google.common.collect.HashBasedTable; import com.google.common.collect.ImmutableTable; import com.google.common.collect.Table; +import java.nio.file.Paths; +import java.util.Collections; import org.junit.Test; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.scanner.repository.FileData; import org.sonar.scanner.repository.ProjectRepositories; +import org.sonar.scanner.scm.ScmChangedFiles; import static org.assertj.core.api.Assertions.assertThat; public class StatusDetectionTest { @Test public void detect_status() { - Table t = ImmutableTable.of(); - ProjectRepositories ref = new ProjectRepositories(t, createTable(), null); - StatusDetection statusDetection = new StatusDetection(ref); + ProjectRepositories ref = new ProjectRepositories(ImmutableTable.of(), createTable(), null); + ScmChangedFiles changedFiles = new ScmChangedFiles(null); + StatusDetection statusDetection = new StatusDetection(ref, changedFiles); - 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); + assertThat(statusDetection.status("foo", createFile("src/Foo.java"), "ABCDE")).isEqualTo(InputFile.Status.SAME); + assertThat(statusDetection.status("foo", createFile("src/Foo.java"), "XXXXX")).isEqualTo(InputFile.Status.CHANGED); + assertThat(statusDetection.status("foo", createFile("src/Other.java"), "QWERT")).isEqualTo(InputFile.Status.ADDED); + } + + @Test + public void detect_status_branches_exclude() { + ProjectRepositories ref = new ProjectRepositories(ImmutableTable.of(), createTable(), null); + ScmChangedFiles changedFiles = new ScmChangedFiles(Collections.emptyList()); + StatusDetection statusDetection = new StatusDetection(ref, changedFiles); + + // normally changed + assertThat(statusDetection.status("foo", createFile("src/Foo.java"), "XXXXX")).isEqualTo(InputFile.Status.SAME); + } + + @Test + public void detect_status_branches_confirm() { + ProjectRepositories ref = new ProjectRepositories(ImmutableTable.of(), createTable(), null); + ScmChangedFiles changedFiles = new ScmChangedFiles(Collections.singletonList(Paths.get("module", "src", "Foo.java"))); + StatusDetection statusDetection = new StatusDetection(ref, changedFiles); + + assertThat(statusDetection.status("foo", createFile("src/Foo.java"), "XXXXX")).isEqualTo(InputFile.Status.CHANGED); } private static Table createTable() { @@ -49,4 +73,8 @@ public class StatusDetectionTest { return t; } + + private static DefaultInputFile createFile(String relativePath) { + return new TestInputFileBuilder("module", relativePath).build(); + } } 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 new file mode 100644 index 00000000000..6777aaa7333 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java @@ -0,0 +1,125 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.scm; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.batch.scm.ScmBranchProvider; +import org.sonar.api.batch.scm.ScmProvider; +import org.sonar.scanner.scan.branch.BranchConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class ScmChangedFilesProviderTest { + @Mock + private ScmConfiguration scmConfiguration; + @Mock + private BranchConfiguration branchConfiguration; + @Mock + private InputModuleHierarchy inputModuleHierarchy; + @Mock + private ScmBranchProvider scmProvider; + + private Path rootBaseDir = Paths.get("root"); + private ScmChangedFilesProvider provider; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + DefaultInputModule root = mock(DefaultInputModule.class); + when(root.getBaseDir()).thenReturn(rootBaseDir); + when(inputModuleHierarchy.root()).thenReturn(root); + provider = new ScmChangedFilesProvider(); + } + + @Test + public void testNoScmProvider() { + when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); + + assertThat(scmChangedFiles.get()).isNull(); + verify(scmConfiguration).provider(); + } + + @Test + public void testProviderDoesntSupport() { + when(branchConfiguration.branchTarget()).thenReturn("target"); + when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(scmConfiguration.provider()).thenReturn(scmProvider); + when(scmProvider.branchChangedFiles("target", rootBaseDir)).thenReturn(null); + ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); + + assertThat(scmChangedFiles.get()).isNull(); + verify(scmProvider).branchChangedFiles("target", rootBaseDir); + } + + @Test + public void testNoOpInNonShortLivedBranch() { + when(branchConfiguration.isShortLivingBranch()).thenReturn(false); + ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); + + assertThat(scmChangedFiles.get()).isNull(); + verifyZeroInteractions(scmConfiguration); + } + + @Test + public void testLegacyScmProvider() { + ScmProvider legacy = mock(ScmProvider.class); + when(scmConfiguration.provider()).thenReturn(legacy); + when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + + ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); + + assertThat(scmChangedFiles.get()).isNull(); + verify(scmConfiguration).provider(); + verifyZeroInteractions(legacy); + } + + @Test + public void testReturnChangedFiles() { + when(branchConfiguration.branchTarget()).thenReturn("target"); + when(branchConfiguration.isShortLivingBranch()).thenReturn(true); + when(scmConfiguration.provider()).thenReturn(scmProvider); + when(scmProvider.branchChangedFiles("target", rootBaseDir)).thenReturn(Collections.singletonList(Paths.get("changedFile"))); + ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); + + assertThat(scmChangedFiles.get()).containsOnly(Paths.get("changedFile")); + verify(scmProvider).branchChangedFiles("target", rootBaseDir); + } + + @Test + public void testCacheObject() { + provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); + provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy); + verify(branchConfiguration).isShortLivingBranch(); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesTest.java new file mode 100644 index 00000000000..ec1ce40a091 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesTest.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.scm; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.Collections; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ScmChangedFilesTest { + private ScmChangedFiles scmChangedFiles; + + @Test + public void testGetter() { + Collection files = Collections.singletonList(Paths.get("files")); + scmChangedFiles = new ScmChangedFiles(files); + assertThat(scmChangedFiles.get()).containsOnly(Paths.get("files")); + } + + @Test + public void testNullable() { + scmChangedFiles = new ScmChangedFiles(null); + assertThat(scmChangedFiles.get()).isNull(); + assertThat(scmChangedFiles.verifyChanged(Paths.get("files2"))).isTrue(); + } + + @Test + public void testConfirm() { + Collection files = Collections.singletonList(Paths.get("files")); + scmChangedFiles = new ScmChangedFiles(files); + assertThat(scmChangedFiles.verifyChanged(Paths.get("files"))).isTrue(); + assertThat(scmChangedFiles.verifyChanged(Paths.get("files2"))).isFalse(); + } +} -- cgit v1.2.3