From 87bb21e6bb8510620ecea231229964a2163203b3 Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Fri, 21 Aug 2020 15:23:44 -0500 Subject: [PATCH] SONAR-13792 Embed sonar-scm-git --- build.gradle | 1 - sonar-application/bundled_plugins.gradle | 1 - .../scanner/scan/ProjectScanContainer.java | 3 + .../sonar/scm/git/ChangedLinesComputer.java | 116 +++ .../org/sonar/scm/git/GitIgnoreCommand.java | 52 ++ .../org/sonar/scm/git/GitScmProvider.java | 376 +++++++++ .../java/org/sonar/scm/git/GitScmSupport.java | 34 + .../org/sonar/scm/git/GitThreadFactory.java | 36 + .../scm/git/IncludedFilesRepository.java | 72 ++ .../org/sonar/scm/git/JGitBlameCommand.java | 129 +++ .../java/org/sonar/scm/git/JGitUtils.java | 44 + .../java/org/sonar/scm/git/package-info.java | 23 + .../mediumtest/fs/FileSystemMediumTest.java | 1 + .../scm/git/ChangedLinesComputerTest.java | 154 ++++ .../sonar/scm/git/GitIgnoreCommandTest.java | 140 +++ .../org/sonar/scm/git/GitScmProviderTest.java | 797 ++++++++++++++++++ .../org/sonar/scm/git/GitScmSupportTest.java | 33 + .../sonar/scm/git/GitThreadFactoryTest.java | 36 + .../sonar/scm/git/JGitBlameCommandTest.java | 343 ++++++++ .../test/java/org/sonar/scm/git/Utils.java | 67 ++ .../scm/git/test-repos/dummy-git-nested.zip | Bin 0 -> 60535 bytes .../test-repos/dummy-git-reference-clone.zip | Bin 0 -> 86575 bytes .../sonar/scm/git/test-repos/dummy-git.zip | Bin 0 -> 60003 bytes .../sonar/scm/git/test-repos/ignore-git.zip | Bin 0 -> 22192 bytes .../scm/git/test-repos/reference-git.zip | Bin 0 -> 61651 bytes .../sonar/scm/git/test-repos/shallow-git.zip | Bin 0 -> 7832 bytes 26 files changed, 2456 insertions(+), 2 deletions(-) create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scm/git/ChangedLinesComputer.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitIgnoreCommand.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmProvider.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmSupport.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitThreadFactory.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scm/git/IncludedFilesRepository.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scm/git/JGitBlameCommand.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scm/git/JGitUtils.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scm/git/package-info.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scm/git/ChangedLinesComputerTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitIgnoreCommandTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmProviderTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmSupportTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitThreadFactoryTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scm/git/JGitBlameCommandTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scm/git/Utils.java create mode 100644 sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/dummy-git-nested.zip create mode 100644 sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/dummy-git-reference-clone.zip create mode 100644 sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/dummy-git.zip create mode 100644 sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/ignore-git.zip create mode 100644 sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/reference-git.zip create mode 100644 sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/shallow-git.zip diff --git a/build.gradle b/build.gradle index d307531b1d5..9d5d86773f4 100644 --- a/build.gradle +++ b/build.gradle @@ -182,7 +182,6 @@ subprojects { dependency 'org.sonarsource.javascript:sonar-javascript-plugin:6.3.0.12464' // bundled_plugin:javascript:SonarJS dependency 'org.sonarsource.php:sonar-php-plugin:3.5.0.5655' // bundled_plugin:php:sonar-php dependency 'org.sonarsource.python:sonar-python-plugin:2.13.0.7236' // bundled_plugin:python:sonar-python - dependency 'org.sonarsource.scm.git:sonar-scm-git-plugin:1.12.0.2034' // bundled_plugin:scmgit:sonar-scm-git dependency 'org.sonarsource.scm.svn:sonar-scm-svn-plugin:1.10.0.1917' // bundled_plugin:scmsvn:sonar-scm-svn dependency 'org.sonarsource.slang:sonar-go-plugin:1.6.0.719' // bundled_plugin:go:slang-enterprise dependency 'org.sonarsource.slang:sonar-kotlin-plugin:1.5.0.315' // bundled_plugin:kotlin:slang-enterprise diff --git a/sonar-application/bundled_plugins.gradle b/sonar-application/bundled_plugins.gradle index 50f146c15e9..934e4c07e6d 100644 --- a/sonar-application/bundled_plugins.gradle +++ b/sonar-application/bundled_plugins.gradle @@ -12,7 +12,6 @@ dependencies { bundledPlugin 'org.sonarsource.slang:sonar-go-plugin@jar' bundledPlugin "org.sonarsource.slang:sonar-kotlin-plugin@jar" bundledPlugin "org.sonarsource.slang:sonar-ruby-plugin@jar" - bundledPlugin 'org.sonarsource.scm.git:sonar-scm-git-plugin@jar' bundledPlugin 'org.sonarsource.scm.svn:sonar-scm-svn-plugin@jar' bundledPlugin "org.sonarsource.slang:sonar-scala-plugin@jar" bundledPlugin 'org.sonarsource.xml:sonar-xml-plugin@jar' 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 7e14856626d..ce29d00a358 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 @@ -131,6 +131,7 @@ import org.sonar.scanner.sensor.ProjectSensorContext; import org.sonar.scanner.sensor.ProjectSensorExtensionDictionnary; import org.sonar.scanner.sensor.ProjectSensorOptimizer; import org.sonar.scanner.sensor.ProjectSensorsExecutor; +import org.sonar.scm.git.GitScmSupport; import static org.sonar.api.batch.InstantiationStrategy.PER_BATCH; import static org.sonar.core.extension.CoreExtensionsInstaller.noExtensionFilter; @@ -301,6 +302,8 @@ public class ProjectScanContainer extends ComponentContainer { AnalysisObservers.class); + add(GitScmSupport.getClasses()); + addIfMissing(DefaultProjectSettingsLoader.class, ProjectSettingsLoader.class); addIfMissing(DefaultRulesLoader.class, RulesLoader.class); addIfMissing(DefaultActiveRulesLoader.class, ActiveRulesLoader.class); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scm/git/ChangedLinesComputer.java b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/ChangedLinesComputer.java new file mode 100644 index 00000000000..1db66e65ba0 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/ChangedLinesComputer.java @@ -0,0 +1,116 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class ChangedLinesComputer { + private final Tracker tracker = new Tracker(); + + private final OutputStream receiver = new OutputStream() { + StringBuilder sb = new StringBuilder(); + + @Override + public void write(int b) { + sb.append((char) b); + if (b == '\n') { + tracker.parseLine(sb.toString()); + sb.setLength(0); + } + } + }; + + /** + * The OutputStream to pass to JGit's diff command. + */ + OutputStream receiver() { + return receiver; + } + + /** + * From a stream of unified diff lines emitted by Git for a single file, + * compute the line numbers that should be considered changed. + * Example input: + *
+   * diff --git a/lao.txt b/lao.txt
+   * index 635ef2c..7f050f2 100644
+   * --- a/lao.txt
+   * +++ b/lao.txt
+   * @@ -1,7 +1,6 @@
+   * -The Way that can be told of is not the eternal Way;
+   * -The name that can be named is not the eternal name.
+   *  The Nameless is the origin of Heaven and Earth;
+   * -The Named is the mother of all things.
+   * +The named is the mother of all things.
+   * +
+   *  Therefore let there always be non-being,
+   *    so we may see their subtlety,
+   *  And let there always be being,
+   * @@ -9,3 +8,6 @@ And let there always be being,
+   *  The two are the same,
+   *  But after they are produced,
+   *    they have different names.
+   * +They both may be called deep and profound.
+   * +Deeper and more profound,
+   * +The door of all subtleties!names.
+   * 
+ * See also: http://www.gnu.org/software/diffutils/manual/html_node/Example-Unified.html#Example-Unified + */ + Set changedLines() { + return tracker.changedLines(); + } + + private static class Tracker { + + private static final Pattern START_LINE_IN_TARGET = Pattern.compile(" \\+(\\d+)"); + + private final Set changedLines = new HashSet<>(); + + private boolean foundStart = false; + private int lineNumInTarget; + + private void parseLine(String line) { + if (line.startsWith("@@ ")) { + Matcher matcher = START_LINE_IN_TARGET.matcher(line); + if (!matcher.find()) { + throw new IllegalStateException("Invalid block header on line " + line); + } + foundStart = true; + lineNumInTarget = Integer.parseInt(matcher.group(1)); + } else if (foundStart) { + char firstChar = line.charAt(0); + if (firstChar == ' ') { + lineNumInTarget++; + } else if (firstChar == '+') { + changedLines.add(lineNumInTarget); + lineNumInTarget++; + } + } + } + + Set changedLines() { + return changedLines; + } + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitIgnoreCommand.java b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitIgnoreCommand.java new file mode 100644 index 00000000000..cfe31a1ed67 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitIgnoreCommand.java @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.IOException; +import java.nio.file.Path; +import org.sonar.api.batch.scm.IgnoreCommand; +import org.sonar.api.scanner.ScannerSide; + +import static java.util.Objects.requireNonNull; + +@ScannerSide +public class GitIgnoreCommand implements IgnoreCommand { + + private IncludedFilesRepository includedFilesRepository; + + @Override + public void init(Path baseDir) { + try { + this.includedFilesRepository = new IncludedFilesRepository(baseDir); + } catch (IOException e) { + throw new IllegalStateException("I/O error while indexing ignored files.", e); + } + } + + @Override + public boolean isIgnored(Path absolutePath) { + return !requireNonNull(includedFilesRepository, "Call init first").contains(absolutePath); + } + + @Override + public void clean() { + this.includedFilesRepository = null; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmProvider.java new file mode 100644 index 00000000000..19b81e1642a --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmProvider.java @@ -0,0 +1,376 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import javax.annotation.CheckForNull; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.diff.DiffAlgorithm; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.DiffFormatter; +import org.eclipse.jgit.diff.RawTextComparator; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryBuilder; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.revwalk.filter.RevFilter; +import org.eclipse.jgit.treewalk.AbstractTreeIterator; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.sonar.api.batch.scm.BlameCommand; +import org.sonar.api.batch.scm.ScmProvider; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +public class GitScmProvider extends ScmProvider { + + private static final Logger LOG = Loggers.get(GitScmProvider.class); + + private final JGitBlameCommand jgitBlameCommand; + private final AnalysisWarnings analysisWarnings; + private final GitIgnoreCommand gitIgnoreCommand; + private final System2 system2; + + public GitScmProvider(JGitBlameCommand jgitBlameCommand, AnalysisWarnings analysisWarnings, GitIgnoreCommand gitIgnoreCommand, System2 system2) { + this.jgitBlameCommand = jgitBlameCommand; + this.analysisWarnings = analysisWarnings; + this.gitIgnoreCommand = gitIgnoreCommand; + this.system2 = system2; + } + + @Override + public GitIgnoreCommand ignoreCommand() { + return gitIgnoreCommand; + } + + @Override + public String key() { + return "git"; + } + + @Override + public boolean supports(File baseDir) { + RepositoryBuilder builder = new RepositoryBuilder().findGitDir(baseDir); + return builder.getGitDir() != null; + } + + @Override + public BlameCommand blameCommand() { + return this.jgitBlameCommand; + } + + @CheckForNull + @Override + public Set branchChangedFiles(String targetBranchName, Path rootBaseDir) { + try (Repository repo = buildRepo(rootBaseDir)) { + Ref targetRef = resolveTargetRef(targetBranchName, repo); + if (targetRef == null) { + analysisWarnings.addUnique(String.format("Could not find ref '%s' in refs/heads, refs/remotes/upstream or refs/remotes/origin. " + + "You may see unexpected issues and changes. " + + "Please make sure to fetch this ref before pull request analysis.", targetBranchName)); + return null; + } + + if (isDiffAlgoInvalid(repo.getConfig())) { + LOG.warn("The diff algorithm configured in git is not supported. " + + "No information regarding changes in the branch will be collected, which can lead to unexpected results."); + return null; + } + + Optional mergeBaseCommit = findMergeBase(repo, targetRef); + if (!mergeBaseCommit.isPresent()) { + LOG.warn("No merge base found between HEAD and " + targetRef.getName()); + return null; + } + AbstractTreeIterator mergeBaseTree = prepareTreeParser(repo, mergeBaseCommit.get()); + + // we compare a commit with HEAD, so no point ignoring line endings (it will be whatever is committed) + try (Git git = newGit(repo)) { + List diffEntries = git.diff() + .setShowNameAndStatusOnly(true) + .setOldTree(mergeBaseTree) + .setNewTree(prepareNewTree(repo)) + .call(); + + return diffEntries.stream() + .filter(diffEntry -> diffEntry.getChangeType() == DiffEntry.ChangeType.ADD || diffEntry.getChangeType() == DiffEntry.ChangeType.MODIFY) + .map(diffEntry -> repo.getWorkTree().toPath().resolve(diffEntry.getNewPath())) + .collect(Collectors.toSet()); + } + } catch (IOException | GitAPIException e) { + LOG.warn(e.getMessage(), e); + } + return null; + } + + @CheckForNull + @Override + public Map> branchChangedLines(String targetBranchName, Path projectBaseDir, Set changedFiles) { + try (Repository repo = buildRepo(projectBaseDir)) { + Ref targetRef = resolveTargetRef(targetBranchName, repo); + if (targetRef == null) { + analysisWarnings.addUnique(String.format("Could not find ref '%s' in refs/heads, refs/remotes/upstream or refs/remotes/origin. " + + "You may see unexpected issues and changes. " + + "Please make sure to fetch this ref before pull request analysis.", targetBranchName)); + return null; + } + + if (isDiffAlgoInvalid(repo.getConfig())) { + // we already print a warning when branchChangedFiles is called + return null; + } + + // force ignore different line endings when comparing a commit with the workspace + repo.getConfig().setBoolean("core", null, "autocrlf", true); + + Optional mergeBaseCommit = findMergeBase(repo, targetRef); + if (!mergeBaseCommit.isPresent()) { + LOG.warn("No merge base found between HEAD and " + targetRef.getName()); + return null; + } + + Map> changedLines = new HashMap<>(); + Path repoRootDir = repo.getDirectory().toPath().getParent(); + + for (Path path : changedFiles) { + collectChangedLines(repo, mergeBaseCommit.get(), changedLines, repoRootDir, path); + } + return changedLines; + } catch (Exception e) { + LOG.warn("Failed to get changed lines from git", e); + } + return null; + } + + private void collectChangedLines(Repository repo, RevCommit mergeBaseCommit, Map> changedLines, Path repoRootDir, Path changedFile) { + ChangedLinesComputer computer = new ChangedLinesComputer(); + + try (DiffFormatter diffFmt = new DiffFormatter(new BufferedOutputStream(computer.receiver()))) { + // copied from DiffCommand so that we can use a custom DiffFormatter which ignores white spaces. + diffFmt.setRepository(repo); + diffFmt.setProgressMonitor(NullProgressMonitor.INSTANCE); + diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_ALL); + diffFmt.setPathFilter(PathFilter.create(toGitPath(repoRootDir.relativize(changedFile).toString()))); + + AbstractTreeIterator mergeBaseTree = prepareTreeParser(repo, mergeBaseCommit); + List diffEntries = diffFmt.scan(mergeBaseTree, new FileTreeIterator(repo)); + diffFmt.format(diffEntries); + diffFmt.flush(); + diffEntries.stream() + .filter(diffEntry -> diffEntry.getChangeType() == DiffEntry.ChangeType.ADD || diffEntry.getChangeType() == DiffEntry.ChangeType.MODIFY) + .findAny() + .ifPresent(diffEntry -> changedLines.put(changedFile, computer.changedLines())); + } catch (Exception e) { + LOG.warn("Failed to get changed lines from git for file " + changedFile, e); + } + } + + @Override + @CheckForNull + public Instant forkDate(String referenceBranchName, Path projectBaseDir) { + try (Repository repo = buildRepo(projectBaseDir)) { + Ref targetRef = resolveTargetRef(referenceBranchName, repo); + if (targetRef == null) { + LOG.warn("Branch '{}' not found in git", referenceBranchName); + return null; + } + + if (isDiffAlgoInvalid(repo.getConfig())) { + LOG.warn("The diff algorithm configured in git is not supported. " + + "No information regarding changes in the branch will be collected, which can lead to unexpected results."); + return null; + } + + Optional mergeBaseCommit = findMergeBase(repo, targetRef); + if (!mergeBaseCommit.isPresent()) { + LOG.warn("No fork point found between HEAD and " + targetRef.getName()); + return null; + } + + return Instant.ofEpochSecond(mergeBaseCommit.get().getCommitTime()); + } catch (Exception e) { + LOG.warn("Failed to find fork point with git", e); + } + + return null; + } + + private static String toGitPath(String path) { + return path.replaceAll(Pattern.quote(File.separator), "/"); + } + + @CheckForNull + private Ref resolveTargetRef(String targetBranchName, Repository repo) throws IOException { + String localRef = "refs/heads/" + targetBranchName; + String remoteRef = "refs/remotes/origin/" + targetBranchName; + String upstreamRef = "refs/remotes/upstream/" + targetBranchName; + + Ref targetRef; + // Because circle ci destroys the local reference to master, try to load remote ref first. + // https://discuss.circleci.com/t/git-checkout-of-a-branch-destroys-local-reference-to-master/23781 + if (runningOnCircleCI()) { + targetRef = getFirstExistingRef(repo, remoteRef, localRef, upstreamRef); + } else { + targetRef = getFirstExistingRef(repo, localRef, remoteRef, upstreamRef); + } + + if (targetRef == null) { + LOG.warn("Could not find ref: {} in refs/heads, refs/remotes/upstream or refs/remotes/origin", targetBranchName); + } + + return targetRef; + } + + @CheckForNull + private static Ref getFirstExistingRef(Repository repo, String... refs) throws IOException { + Ref targetRef = null; + for (String ref : refs) { + targetRef = repo.exactRef(ref); + if (targetRef != null) { + break; + } + } + return targetRef; + } + + private boolean runningOnCircleCI() { + return "true".equals(system2.envVariable("CIRCLECI")); + } + + @Override + public Path relativePathFromScmRoot(Path path) { + RepositoryBuilder builder = getVerifiedRepositoryBuilder(path); + return builder.getGitDir().toPath().getParent().relativize(path); + } + + @Override + @CheckForNull + public String revisionId(Path path) { + RepositoryBuilder builder = getVerifiedRepositoryBuilder(path); + try { + Ref head = getHead(builder.build()); + if (head == null || head.getObjectId() == null) { + // can happen on fresh, empty repos + return null; + } + return head.getObjectId().getName(); + } catch (IOException e) { + throw new IllegalStateException("I/O error while getting revision ID for path: " + path, e); + } + } + + private static boolean isDiffAlgoInvalid(Config cfg) { + try { + DiffAlgorithm.getAlgorithm(cfg.getEnum( + ConfigConstants.CONFIG_DIFF_SECTION, null, + ConfigConstants.CONFIG_KEY_ALGORITHM, + DiffAlgorithm.SupportedAlgorithm.HISTOGRAM)); + return false; + } catch (IllegalArgumentException e) { + return true; + } + } + + private static AbstractTreeIterator prepareNewTree(Repository repo) throws IOException { + CanonicalTreeParser treeParser = new CanonicalTreeParser(); + try (ObjectReader objectReader = repo.newObjectReader()) { + Ref head = getHead(repo); + if (head == null) { + throw new IOException("HEAD reference not found"); + } + treeParser.reset(objectReader, repo.parseCommit(head.getObjectId()).getTree()); + } + return treeParser; + } + + @CheckForNull + private static Ref getHead(Repository repo) throws IOException { + return repo.exactRef("HEAD"); + } + + private static Optional findMergeBase(Repository repo, Ref targetRef) throws IOException { + try (RevWalk walk = new RevWalk(repo)) { + Ref head = getHead(repo); + if (head == null) { + throw new IOException("HEAD reference not found"); + } + + walk.markStart(walk.parseCommit(targetRef.getObjectId())); + walk.markStart(walk.parseCommit(head.getObjectId())); + walk.setRevFilter(RevFilter.MERGE_BASE); + RevCommit next = walk.next(); + if (next == null) { + return Optional.empty(); + } + RevCommit base = walk.parseCommit(next); + walk.dispose(); + LOG.debug("Merge base sha1: {}", base.getName()); + return Optional.of(base); + } + } + + AbstractTreeIterator prepareTreeParser(Repository repo, RevCommit commit) throws IOException { + CanonicalTreeParser treeParser = new CanonicalTreeParser(); + try (ObjectReader objectReader = repo.newObjectReader()) { + treeParser.reset(objectReader, commit.getTree()); + } + return treeParser; + } + + Git newGit(Repository repo) { + return new Git(repo); + } + + Repository buildRepo(Path basedir) throws IOException { + return getVerifiedRepositoryBuilder(basedir).build(); + } + + static RepositoryBuilder getVerifiedRepositoryBuilder(Path basedir) { + RepositoryBuilder builder = new RepositoryBuilder() + .findGitDir(basedir.toFile()) + .setMustExist(true); + + if (builder.getGitDir() == null) { + throw MessageException.of("Not inside a Git work tree: " + basedir); + } + return builder; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmSupport.java b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmSupport.java new file mode 100644 index 00000000000..b5a845edc9a --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitScmSupport.java @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.util.Arrays; +import java.util.List; +import org.eclipse.jgit.util.FS; + +public final class GitScmSupport { + public static List> getClasses() { + FS.FileStoreAttributes.setBackground(true); + return Arrays.asList( + JGitBlameCommand.class, + GitScmProvider.class, + GitIgnoreCommand.class); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitThreadFactory.java b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitThreadFactory.java new file mode 100644 index 00000000000..cbca28cc628 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/GitThreadFactory.java @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory; +import java.util.concurrent.ForkJoinWorkerThread; + +public class GitThreadFactory implements ForkJoinWorkerThreadFactory { + private static final String NAME_PREFIX = "git-scm-"; + private int i = 0; + + @Override + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setName(NAME_PREFIX + i++); + return thread; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scm/git/IncludedFilesRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/IncludedFilesRepository.java new file mode 100644 index 00000000000..2d30df40513 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/IncludedFilesRepository.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.WorkingTreeIterator; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +public class IncludedFilesRepository { + + private static final Logger LOG = Loggers.get(IncludedFilesRepository.class); + private final Set includedFiles = new HashSet<>(); + + public IncludedFilesRepository(Path baseDir) throws IOException { + indexFiles(baseDir); + LOG.debug("{} non excluded files in this Git repository", includedFiles.size()); + } + + public boolean contains(Path absolutePath) { + return includedFiles.contains(absolutePath); + } + + private void indexFiles(Path baseDir) throws IOException { + try (Repository repo = JGitUtils.buildRepository(baseDir)) { + Path workTreeRoot = repo.getWorkTree().toPath(); + FileTreeIterator workingTreeIt = new FileTreeIterator(repo); + try (TreeWalk treeWalk = new TreeWalk(repo)) { + treeWalk.setRecursive(true); + if (!baseDir.equals(workTreeRoot)) { + Path relativeBaseDir = workTreeRoot.relativize(baseDir); + treeWalk.setFilter(PathFilterGroup.createFromStrings(relativeBaseDir.toString().replace('\\', '/'))); + } + treeWalk.addTree(workingTreeIt); + while (treeWalk.next()) { + + WorkingTreeIterator workingTreeIterator = treeWalk + .getTree(0, WorkingTreeIterator.class); + + if (!workingTreeIterator.isEntryIgnored()) { + includedFiles.add(workTreeRoot.resolve(treeWalk.getPathString())); + } + } + } + } + } + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scm/git/JGitBlameCommand.java b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/JGitBlameCommand.java new file mode 100644 index 00000000000..601a65413e5 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/JGitBlameCommand.java @@ -0,0 +1,129 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.File; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.blame.BlameResult; +import org.eclipse.jgit.diff.RawTextComparator; +import org.eclipse.jgit.lib.Repository; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.scm.BlameCommand; +import org.sonar.api.batch.scm.BlameLine; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +public class JGitBlameCommand extends BlameCommand { + + private static final Logger LOG = Loggers.get(JGitBlameCommand.class); + + private final PathResolver pathResolver; + private final AnalysisWarnings analysisWarnings; + + public JGitBlameCommand(PathResolver pathResolver, AnalysisWarnings analysisWarnings) { + this.pathResolver = pathResolver; + this.analysisWarnings = analysisWarnings; + } + + @Override + public void blame(BlameInput input, BlameOutput output) { + File basedir = input.fileSystem().baseDir(); + try (Repository repo = JGitUtils.buildRepository(basedir.toPath()); Git git = Git.wrap(repo)) { + File gitBaseDir = repo.getWorkTree(); + + if (cloneIsInvalid(gitBaseDir)) { + return; + } + + Stream stream = StreamSupport.stream(input.filesToBlame().spliterator(), true); + ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors(), new GitThreadFactory(), null, false); + forkJoinPool.submit(() -> stream.forEach(inputFile -> blame(output, git, gitBaseDir, inputFile))); + try { + forkJoinPool.shutdown(); + forkJoinPool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); + } catch (InterruptedException e) { + LOG.info("Git blame interrupted"); + } + } + } + + private boolean cloneIsInvalid(File gitBaseDir) { + if (Files.isRegularFile(gitBaseDir.toPath().resolve(".git/objects/info/alternates"))) { + LOG.info("This git repository references another local repository which is not well supported. SCM information might be missing for some files. " + + "You can avoid borrow objects from another local repository by not using --reference or --shared when cloning it."); + } + + if (Files.isRegularFile(gitBaseDir.toPath().resolve(".git/shallow"))) { + LOG.warn("Shallow clone detected, no blame information will be provided. " + + "You can convert to non-shallow with 'git fetch --unshallow'."); + analysisWarnings.addUnique("Shallow clone detected during the analysis. " + + "Some files will miss SCM information. This will affect features like auto-assignment of issues. " + + "Please configure your build to disable shallow clone."); + return true; + } + + return false; + } + + private void blame(BlameOutput output, Git git, File gitBaseDir, InputFile inputFile) { + String filename = pathResolver.relativePath(gitBaseDir, inputFile.file()); + LOG.debug("Blame file {}", filename); + BlameResult blameResult; + try { + blameResult = git.blame() + // Equivalent to -w command line option + .setTextComparator(RawTextComparator.WS_IGNORE_ALL) + .setFilePath(filename).call(); + } catch (Exception e) { + throw new IllegalStateException("Unable to blame file " + inputFile.relativePath(), e); + } + List lines = new ArrayList<>(); + if (blameResult == null) { + LOG.debug("Unable to blame file {}. It is probably a symlink.", inputFile.relativePath()); + return; + } + for (int i = 0; i < blameResult.getResultContents().size(); i++) { + if (blameResult.getSourceAuthor(i) == null || blameResult.getSourceCommit(i) == null) { + LOG.debug("Unable to blame file {}. No blame info at line {}. Is file committed? [Author: {} Source commit: {}]", inputFile.relativePath(), i + 1, + blameResult.getSourceAuthor(i), blameResult.getSourceCommit(i)); + return; + } + lines.add(new BlameLine() + .date(blameResult.getSourceCommitter(i).getWhen()) + .revision(blameResult.getSourceCommit(i).getName()) + .author(blameResult.getSourceAuthor(i).getEmailAddress())); + } + if (lines.size() == inputFile.lines() - 1) { + // SONARPLUGINS-3097 Git do not report blame on last empty line + lines.add(lines.get(lines.size() - 1)); + } + output.blameResult(inputFile, lines); + } + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scm/git/JGitUtils.java b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/JGitUtils.java new file mode 100644 index 00000000000..86d8792b53a --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/JGitUtils.java @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.IOException; +import java.nio.file.Path; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; + +public class JGitUtils { + + private JGitUtils() { + } + + public static Repository buildRepository(Path basedir) { + try { + Repository repo = GitScmProvider.getVerifiedRepositoryBuilder(basedir).build(); + try (ObjectReader objReader = repo.getObjectDatabase().newReader()) { + // SONARSCGIT-2 Force initialization of shallow commits to avoid later concurrent modification issue + objReader.getShallowCommits(); + return repo; + } + } catch (IOException e) { + throw new IllegalStateException("Unable to open Git repository", e); + } + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scm/git/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/package-info.java new file mode 100644 index 00000000000..aa91667ae34 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scm/git/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.scm.git; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java index 1f90a946b6c..712687ed105 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java @@ -751,6 +751,7 @@ public class FileSystemMediumTest { .newAnalysis(new File(projectDir, "sonar-project.properties")) .property("sonar.exclusions", "**/*.xoo.measures,**/*.xoo.scm") .property("sonar.test.exclusions", "**/*.xoo.measures,**/*.xoo.scm") + .property("sonar.scm.exclusions.disabled", "true") .execute(); assertThat(result.inputFiles()).hasSize(3); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/ChangedLinesComputerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/ChangedLinesComputerTest.java new file mode 100644 index 00000000000..141b011a79a --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/ChangedLinesComputerTest.java @@ -0,0 +1,154 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ChangedLinesComputerTest { + @Rule + public ExpectedException exception = ExpectedException.none(); + private final ChangedLinesComputer underTest = new ChangedLinesComputer(); + + @Test + public void do_not_count_deleted_line() throws IOException { + String example = "diff --git a/file-b1.xoo b/file-b1.xoo\n" + + "index 0000000..c2a9048\n" + + "--- a/foo\n" + + "+++ b/bar\n" + + "@@ -1 +0,0 @@\n" + + "-deleted line\n"; + + printDiff(example); + assertThat(underTest.changedLines()).isEmpty(); + } + + @Test + public void count_single_added_line() throws IOException { + String example = "diff --git a/file-b1.xoo b/file-b1.xoo\n" + + "index 0000000..c2a9048\n" + + "--- a/foo\n" + + "+++ b/bar\n" + + "@@ -0,0 +1 @@\n" + + "+added line\n"; + + printDiff(example); + assertThat(underTest.changedLines()).containsExactly(1); + } + + @Test + public void count_multiple_added_lines() throws IOException { + String example = "diff --git a/file-b1.xoo b/file-b1.xoo\n" + + "index 0000000..c2a9048\n" + + "--- a/foo\n" + + "+++ b/bar\n" + + "@@ -1 +1,3 @@\n" + + " unchanged line\n" + + "+added line 1\n" + + "+added line 2\n"; + + printDiff(example); + assertThat(underTest.changedLines()).containsExactly(2, 3); + } + + @Test + public void compute_from_multiple_hunks() throws IOException { + String example = "diff --git a/lao b/lao\n" + + "index 635ef2c..5af88a8 100644\n" + + "--- a/lao\n" + + "+++ b/lao\n" + + "@@ -1,7 +1,6 @@\n" + + "-The Way that can be told of is not the eternal Way;\n" + + "-The name that can be named is not the eternal name.\n" + + " The Nameless is the origin of Heaven and Earth;\n" + + "-The Named is the mother of all things.\n" + + "+The named is the mother of all things.\n" + + "+\n" + + " Therefore let there always be non-being,\n" + + " so we may see their subtlety,\n" + + " And let there always be being,\n" + + "@@ -9,3 +8,6 @@ And let there always be being,\n" + + " The two are the same,\n" + + " But after they are produced,\n" + + " they have different names.\n" + + "+They both may be called deep and profound.\n" + + "+Deeper and more profound,\n" + + "+The door of all subtleties!\n"; + printDiff(example); + assertThat(underTest.changedLines()).containsExactly(2, 3, 11, 12, 13); + } + + @Test + public void compute_from_multiple_hunks_with_extra_header_lines() throws IOException { + String example = "diff --git a/lao b/lao\n" + + "new file mode 100644\n" + + "whatever " + + "other " + + "surprise header lines git might throw at us...\n" + + "index 635ef2c..5af88a8 100644\n" + + "--- a/lao\n" + + "+++ b/lao\n" + + "@@ -1,7 +1,6 @@\n" + + "-The Way that can be told of is not the eternal Way;\n" + + "-The name that can be named is not the eternal name.\n" + + " The Nameless is the origin of Heaven and Earth;\n" + + "-The Named is the mother of all things.\n" + + "+The named is the mother of all things.\n" + + "+\n" + + " Therefore let there always be non-being,\n" + + " so we may see their subtlety,\n" + + " And let there always be being,\n" + + "@@ -9,3 +8,6 @@ And let there always be being,\n" + + " The two are the same,\n" + + " But after they are produced,\n" + + " they have different names.\n" + + "+They both may be called deep and profound.\n" + + "+Deeper and more profound,\n" + + "+The door of all subtleties!\n"; + printDiff(example); + assertThat(underTest.changedLines()).containsExactly(2, 3, 11, 12, 13); + } + + @Test + public void throw_exception_invalid_start_line_format() throws IOException { + String example = "diff --git a/file-b1.xoo b/file-b1.xoo\n" + + "index 0000000..c2a9048\n" + + "--- a/foo\n" + + "+++ b/bar\n" + + "@@ -1 +x1,3 @@\n" + + " unchanged line\n" + + "+added line 1\n" + + "+added line 2\n"; + + exception.expect(IllegalStateException.class); + printDiff(example); + } + + private void printDiff(String unifiedDiff) throws IOException { + try (OutputStreamWriter writer = new OutputStreamWriter(underTest.receiver())) { + writer.write(unifiedDiff); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitIgnoreCommandTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitIgnoreCommandTest.java new file mode 100644 index 00000000000..c4ea7ae0a5e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitIgnoreCommandTest.java @@ -0,0 +1,140 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import org.eclipse.jgit.api.Git; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.scm.git.Utils.javaUnzip; + +public class GitIgnoreCommandTest { + + @Rule + public LogTester logTester = new LogTester(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void ignored_files_should_match_files_ignored_by_git() throws IOException { + Path projectDir = temp.newFolder().toPath(); + javaUnzip("ignore-git.zip", projectDir.toFile()); + + Path baseDir = projectDir.resolve("ignore-git"); + GitIgnoreCommand underTest = new GitIgnoreCommand(); + underTest.init(baseDir); + + assertThat(underTest.isIgnored(baseDir.resolve(".gitignore"))).isFalse(); + assertThat(underTest.isIgnored(baseDir.resolve("pom.xml"))).isFalse(); + assertThat(underTest.isIgnored(baseDir.resolve("src/main/java/org/dummy/.gitignore"))).isFalse(); + assertThat(underTest.isIgnored(baseDir.resolve("src/main/java/org/dummy/AnotherDummy.java"))).isFalse(); + assertThat(underTest.isIgnored(baseDir.resolve("src/test/java/org/dummy/AnotherDummyTest.java"))).isFalse(); + + assertThat(underTest.isIgnored(baseDir.resolve("src/main/java/org/dummy/Dummy.java"))).isTrue(); + assertThat(underTest.isIgnored(baseDir.resolve("target"))).isTrue(); + } + + @Test + public void test_pattern_on_deep_repo() throws Exception { + Path projectDir = temp.newFolder().toPath(); + Git.init().setDirectory(projectDir.toFile()).call(); + + Files.write(projectDir.resolve(".gitignore"), Arrays.asList("**/*.java"), StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); + int child_folders_per_folder = 2; + int folder_depth = 10; + createDeepFolderStructure(projectDir, child_folders_per_folder, 0, folder_depth); + + logTester.setLevel(LoggerLevel.DEBUG); + + GitIgnoreCommand underTest = new GitIgnoreCommand(); + underTest.init(projectDir); + + assertThat(underTest + .isIgnored(projectDir.resolve("folder_0_0/folder_1_0/folder_2_0/folder_3_0/folder_4_0/folder_5_0/folder_6_0/folder_7_0/folder_8_0/folder_9_0/Foo.java"))) + .isTrue(); + assertThat(underTest + .isIgnored(projectDir.resolve("folder_0_0/folder_1_0/folder_2_0/folder_3_0/folder_4_0/folder_5_0/folder_6_0/folder_7_0/folder_8_0/folder_9_0/Foo.php"))) + .isFalse(); + + int expectedIncludedFiles = (int) Math.pow(child_folders_per_folder, folder_depth) + 1; // The .gitignore file is indexed + assertThat(logTester.logs(LoggerLevel.DEBUG)).contains(expectedIncludedFiles + " non excluded files in this Git repository"); + } + + @Test + public void dont_index_files_outside_basedir() throws Exception { + Path repoRoot = temp.newFolder().toPath(); + Git.init().setDirectory(repoRoot.toFile()).call(); + + Files.write(repoRoot.resolve(".gitignore"), Arrays.asList("**/*.java"), StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); + int child_folders_per_folder = 2; + int folder_depth = 10; + createDeepFolderStructure(repoRoot, child_folders_per_folder, 0, folder_depth); + + logTester.setLevel(LoggerLevel.DEBUG); + + GitIgnoreCommand underTest = new GitIgnoreCommand(); + // Define project baseDir as folder_0_0 so that folder_0_1 is excluded + Path projectBasedir = repoRoot.resolve("folder_0_0"); + underTest.init(projectBasedir); + + assertThat(underTest + .isIgnored(projectBasedir.resolve("folder_1_0/folder_2_0/folder_3_0/folder_4_0/folder_5_0/folder_6_0/folder_7_0/folder_8_0/folder_9_0/Foo.php"))) + .isFalse(); + assertThat(underTest + .isIgnored(repoRoot.resolve("folder_0_1/folder_1_0/folder_2_0/folder_3_0/folder_4_0/folder_5_0/folder_6_0/folder_7_0/folder_8_0/folder_9_0/Foo.php"))) + .isTrue(); + + int expectedIncludedFiles = (int) Math.pow(child_folders_per_folder, folder_depth - 1); + assertThat(logTester.logs(LoggerLevel.DEBUG)).contains(expectedIncludedFiles + " non excluded files in this Git repository"); + } + + private void createDeepFolderStructure(Path current, int childCount, int currentDepth, int maxDepth) throws IOException { + if (currentDepth >= maxDepth) { + Path javaFile = current.resolve("Foo.java"); + Path phpFile = current.resolve("Foo.php"); + if (!Files.exists(phpFile)) { + Files.createFile(phpFile); + } + if (!Files.exists(javaFile)) { + Files.createFile(javaFile); + } + return; + } + for (int j = 0; j < childCount; j++) { + Path newPath = current.resolve("folder_" + currentDepth + "_" + j); + if (!Files.exists(newPath)) { + Files.createDirectory(newPath); + } + createDeepFolderStructure(newPath, childCount, currentDepth + 1, maxDepth); + } + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmProviderTest.java new file mode 100644 index 00000000000..7f62b8912fa --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmProviderTest.java @@ -0,0 +1,797 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jgit.api.DiffCommand; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RefDatabase; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.treewalk.AbstractTreeIterator; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.System2; + +import static java.util.Collections.emptySet; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.data.MapEntry.entry; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.sonar.scm.git.Utils.javaUnzip; + +public class GitScmProviderTest { + + // Sample content for unified diffs + // http://www.gnu.org/software/diffutils/manual/html_node/Example-Unified.html#Example-Unified + private static final String CONTENT_LAO = "The Way that can be told of is not the eternal Way;\n" + + "The name that can be named is not the eternal name.\n" + + "The Nameless is the origin of Heaven and Earth;\n" + + "The Named is the mother of all things.\n" + + "Therefore let there always be non-being,\n" + + " so we may see their subtlety,\n" + + "And let there always be being,\n" + + " so we may see their outcome.\n" + + "The two are the same,\n" + + "But after they are produced,\n" + + " they have different names.\n"; + + private static final String CONTENT_TZU = "The Nameless is the origin of Heaven and Earth;\n" + + "The named is the mother of all things.\n" + + "\n" + + "Therefore let there always be non-being,\n" + + " so we may see their subtlety,\n" + + "And let there always be being,\n" + + " so we may see their outcome.\n" + + "The two are the same,\n" + + "But after they are produced,\n" + + " they have different names.\n" + + "They both may be called deep and profound.\n" + + "Deeper and more profound,\n" + + "The door of all subtleties!"; + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private GitIgnoreCommand gitIgnoreCommand = mock(GitIgnoreCommand.class); + private static final Random random = new Random(); + private static final System2 system2 = mock(System2.class); + + private Path worktree; + private Git git; + private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); + + @Before + public void before() throws IOException, GitAPIException { + worktree = temp.newFolder().toPath(); + Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile()); + repo.create(); + + git = new Git(repo); + + createAndCommitFile("file-in-first-commit.xoo"); + } + + @Test + public void sanityCheck() { + assertThat(newGitScmProvider().key()).isEqualTo("git"); + } + + @Test + public void returnImplem() { + JGitBlameCommand jblameCommand = new JGitBlameCommand(new PathResolver(), analysisWarnings); + GitScmProvider gitScmProvider = new GitScmProvider(jblameCommand, analysisWarnings, gitIgnoreCommand, system2); + + assertThat(gitScmProvider.blameCommand()).isEqualTo(jblameCommand); + } + + /** + * SONARSCGIT-47 + */ + @Test + public void branchChangedFiles_should_not_crash_if_branches_have_no_common_ancestors() throws GitAPIException, IOException { + String fileName = "file-in-first-commit.xoo"; + String renamedName = "file-renamed.xoo"; + git.checkout().setOrphan(true).setName("b1").call(); + + Path file = worktree.resolve(fileName); + Path renamed = file.resolveSibling(renamedName); + addLineToFile(fileName, 1); + + Files.move(file, renamed); + git.rm().addFilepattern(fileName).call(); + commit(renamedName); + + Set files = newScmProvider().branchChangedFiles("master", worktree); + + // no shared history, so no diff + assertThat(files).isNull(); + } + + @Test + public void testAutodetection() throws IOException { + File baseDirEmpty = temp.newFolder(); + assertThat(newGitScmProvider().supports(baseDirEmpty)).isFalse(); + + File projectDir = temp.newFolder(); + javaUnzip("dummy-git.zip", projectDir); + File baseDir = new File(projectDir, "dummy-git"); + assertThat(newScmProvider().supports(baseDir)).isTrue(); + } + + private static JGitBlameCommand mockCommand() { + return mock(JGitBlameCommand.class); + } + + @Test + public void branchChangedFiles_from_diverged() throws IOException, GitAPIException { + createAndCommitFile("file-m1.xoo"); + createAndCommitFile("file-m2.xoo"); + createAndCommitFile("file-m3.xoo"); + ObjectId forkPoint = git.getRepository().exactRef("HEAD").getObjectId(); + + appendToAndCommitFile("file-m3.xoo"); + createAndCommitFile("file-m4.xoo"); + + git.branchCreate().setName("b1").setStartPoint(forkPoint.getName()).call(); + git.checkout().setName("b1").call(); + createAndCommitFile("file-b1.xoo"); + appendToAndCommitFile("file-m1.xoo"); + deleteAndCommitFile("file-m2.xoo"); + + assertThat(newScmProvider().branchChangedFiles("master", worktree)) + .containsExactlyInAnyOrder( + worktree.resolve("file-b1.xoo"), + worktree.resolve("file-m1.xoo")); + } + + @Test + public void branchChangedFiles_should_not_fail_with_patience_diff_algo() throws IOException { + Path gitConfig = worktree.resolve(".git").resolve("config"); + Files.write(gitConfig, "[diff]\nalgorithm = patience\n".getBytes(StandardCharsets.UTF_8)); + Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile()); + git = new Git(repo); + + assertThat(newScmProvider().branchChangedFiles("master", worktree)).isNull(); + } + + @Test + public void branchChangedFiles_from_merged_and_diverged() throws IOException, GitAPIException { + createAndCommitFile("file-m1.xoo"); + createAndCommitFile("file-m2.xoo"); + createAndCommitFile("lao.txt", CONTENT_LAO); + ObjectId forkPoint = git.getRepository().exactRef("HEAD").getObjectId(); + + createAndCommitFile("file-m3.xoo"); + ObjectId mergePoint = git.getRepository().exactRef("HEAD").getObjectId(); + + appendToAndCommitFile("file-m3.xoo"); + createAndCommitFile("file-m4.xoo"); + + git.branchCreate().setName("b1").setStartPoint(forkPoint.getName()).call(); + git.checkout().setName("b1").call(); + createAndCommitFile("file-b1.xoo"); + appendToAndCommitFile("file-m1.xoo"); + deleteAndCommitFile("file-m2.xoo"); + + git.merge().include(mergePoint).call(); + createAndCommitFile("file-b2.xoo"); + + createAndCommitFile("file-m5.xoo"); + deleteAndCommitFile("file-m5.xoo"); + + Set changedFiles = newScmProvider().branchChangedFiles("master", worktree); + assertThat(changedFiles) + .containsExactlyInAnyOrder( + worktree.resolve("file-m1.xoo"), + worktree.resolve("file-b1.xoo"), + worktree.resolve("file-b2.xoo")); + + // use a subset of changed files for .branchChangedLines to verify only requested files are returned + assertThat(changedFiles.remove(worktree.resolve("file-b1.xoo"))).isTrue(); + + // generate common sample diff + createAndCommitFile("lao.txt", CONTENT_TZU); + changedFiles.add(worktree.resolve("lao.txt")); + + // a file that should not yield any results + changedFiles.add(worktree.resolve("nonexistent")); + + assertThat(newScmProvider().branchChangedLines("master", worktree, changedFiles)) + .containsOnly( + entry(worktree.resolve("lao.txt"), new HashSet<>(Arrays.asList(2, 3, 11, 12, 13))), + entry(worktree.resolve("file-m1.xoo"), new HashSet<>(Arrays.asList(4))), + entry(worktree.resolve("file-b2.xoo"), new HashSet<>(Arrays.asList(1, 2, 3)))); + + assertThat(newScmProvider().branchChangedLines("master", worktree, Collections.singleton(worktree.resolve("nonexistent")))) + .isEmpty(); + } + + @Test + public void forkDate_from_diverged() throws IOException, GitAPIException { + createAndCommitFile("file-m1.xoo", Instant.now().minus(8, ChronoUnit.DAYS)); + createAndCommitFile("file-m2.xoo", Instant.now().minus(7, ChronoUnit.DAYS)); + Instant expectedForkDate = Instant.now().minus(6, ChronoUnit.DAYS); + createAndCommitFile("file-m3.xoo", expectedForkDate); + ObjectId forkPoint = git.getRepository().exactRef("HEAD").getObjectId(); + + appendToAndCommitFile("file-m3.xoo"); + createAndCommitFile("file-m4.xoo"); + + git.branchCreate().setName("b1").setStartPoint(forkPoint.getName()).call(); + git.checkout().setName("b1").call(); + createAndCommitFile("file-b1.xoo"); + appendToAndCommitFile("file-m1.xoo"); + deleteAndCommitFile("file-m2.xoo"); + + assertThat(newScmProvider().forkDate("master", worktree)) + .isEqualTo(expectedForkDate.truncatedTo(ChronoUnit.SECONDS)); + } + + @Test + public void forkDate_should_not_fail_if_reference_is_the_same_branch() throws IOException, GitAPIException { + createAndCommitFile("file-m1.xoo", Instant.now().minus(8, ChronoUnit.DAYS)); + createAndCommitFile("file-m2.xoo", Instant.now().minus(7, ChronoUnit.DAYS)); + + ObjectId forkPoint = git.getRepository().exactRef("HEAD").getObjectId(); + git.branchCreate().setName("b1").setStartPoint(forkPoint.getName()).call(); + git.checkout().setName("b1").call(); + + Instant expectedForkDate = Instant.now().minus(6, ChronoUnit.DAYS); + createAndCommitFile("file-m3.xoo", expectedForkDate); + + assertThat(newScmProvider().forkDate("b1", worktree)) + .isEqualTo(expectedForkDate.truncatedTo(ChronoUnit.SECONDS)); + } + + @Test + public void forkDate_should_not_fail_with_patience_diff_algo() throws IOException { + Path gitConfig = worktree.resolve(".git").resolve("config"); + Files.write(gitConfig, "[diff]\nalgorithm = patience\n".getBytes(StandardCharsets.UTF_8)); + Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile()); + git = new Git(repo); + + assertThat(newScmProvider().forkDate("master", worktree)).isNull(); + } + + @Test + public void forkDate_should_not_fail_with_invalid_basedir() throws IOException { + assertThat(newScmProvider().forkDate("master", temp.newFolder().toPath())).isNull(); + } + + @Test + public void forkDate_should_not_fail_when_no_merge_base_is_found() throws IOException, GitAPIException { + createAndCommitFile("file-m1.xoo", Instant.now().minus(8, ChronoUnit.DAYS)); + + git.checkout().setOrphan(true).setName("b1").call(); + createAndCommitFile("file-b1.xoo"); + + assertThat(newScmProvider().forkDate("master", worktree)).isNull(); + } + + @Test + public void forkDate_without_target_branch() throws IOException, GitAPIException { + createAndCommitFile("file-m1.xoo", Instant.now().minus(8, ChronoUnit.DAYS)); + createAndCommitFile("file-m2.xoo", Instant.now().minus(7, ChronoUnit.DAYS)); + Instant expectedForkDate = Instant.now().minus(6, ChronoUnit.DAYS); + createAndCommitFile("file-m3.xoo", expectedForkDate); + ObjectId forkPoint = git.getRepository().exactRef("HEAD").getObjectId(); + + appendToAndCommitFile("file-m3.xoo"); + createAndCommitFile("file-m4.xoo"); + + git.branchCreate().setName("b1").setStartPoint(forkPoint.getName()).call(); + git.checkout().setName("b1").call(); + createAndCommitFile("file-b1.xoo"); + appendToAndCommitFile("file-m1.xoo"); + deleteAndCommitFile("file-m2.xoo"); + + assertThat(newScmProvider().forkDate("unknown", worktree)).isNull(); + } + + @Test + public void branchChangedLines_should_be_correct_when_change_is_not_committed() throws GitAPIException, IOException { + String fileName = "file-in-first-commit.xoo"; + git.branchCreate().setName("b1").call(); + git.checkout().setName("b1").call(); + + // this line is committed + addLineToFile(fileName, 3); + commit(fileName); + + // this line is not committed + addLineToFile(fileName, 1); + + Path filePath = worktree.resolve(fileName); + Map> changedLines = newScmProvider().branchChangedLines("master", worktree, Collections.singleton(filePath)); + + // both lines appear correctly + assertThat(changedLines).containsExactly(entry(filePath, new HashSet<>(Arrays.asList(1, 4)))); + } + + @Test + public void branchChangedLines_should_not_fail_if_there_is_no_merge_base() throws GitAPIException, IOException { + createAndCommitFile("file-m1.xoo"); + git.checkout().setOrphan(true).setName("b1").call(); + createAndCommitFile("file-b1.xoo"); + + Map> changedLines = newScmProvider().branchChangedLines("master", worktree, Collections.singleton(Paths.get(""))); + assertThat(changedLines).isNull(); + } + + @Test + public void branchChangedLines_returns_empty_set_for_files_with_lines_removed_only() throws GitAPIException, IOException { + String fileName = "file-in-first-commit.xoo"; + git.branchCreate().setName("b1").call(); + git.checkout().setName("b1").call(); + + removeLineInFile(fileName, 2); + commit(fileName); + + Path filePath = worktree.resolve(fileName); + Map> changedLines = newScmProvider().branchChangedLines("master", worktree, Collections.singleton(filePath)); + + // both lines appear correctly + assertThat(changedLines).containsExactly(entry(filePath, emptySet())); + } + + @Test + public void branchChangedLines_uses_relative_paths_from_project_root() throws GitAPIException, IOException { + String fileName = "project1/file-in-first-commit.xoo"; + createAndCommitFile(fileName); + + git.branchCreate().setName("b1").call(); + git.checkout().setName("b1").call(); + + // this line is committed + addLineToFile(fileName, 3); + commit(fileName); + + // this line is not committed + addLineToFile(fileName, 1); + + Path filePath = worktree.resolve(fileName); + Map> changedLines = newScmProvider().branchChangedLines("master", + worktree.resolve("project1"), Collections.singleton(filePath)); + + // both lines appear correctly + assertThat(changedLines).containsExactly(entry(filePath, new HashSet<>(Arrays.asList(1, 4)))); + } + + @Test + public void branchChangedFiles_when_git_work_tree_is_above_project_basedir() throws IOException, GitAPIException { + git.branchCreate().setName("b1").call(); + git.checkout().setName("b1").call(); + + Path projectDir = worktree.resolve("project"); + Files.createDirectory(projectDir); + createAndCommitFile("project/file-b1"); + assertThat(newScmProvider().branchChangedFiles("master", projectDir)) + .containsOnly(projectDir.resolve("file-b1")); + } + + @Test + public void branchChangedLines_should_not_fail_with_patience_diff_algo() throws IOException { + Path gitConfig = worktree.resolve(".git").resolve("config"); + Files.write(gitConfig, "[diff]\nalgorithm = patience\n".getBytes(StandardCharsets.UTF_8)); + Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile()); + git = new Git(repo); + + assertThat(newScmProvider().branchChangedLines("master", worktree, Collections.singleton(Paths.get("file")))).isNull(); + } + + /** + * Unfortunately it looks like JGit doesn't support this setting using .gitattributes. + */ + @Test + public void branchChangedLines_should_always_ignore_different_line_endings() throws IOException, GitAPIException { + Path filePath = worktree.resolve("file-m1.xoo"); + + createAndCommitFile("file-m1.xoo"); + ObjectId forkPoint = git.getRepository().exactRef("HEAD").getObjectId(); + + git.branchCreate().setName("b1").setStartPoint(forkPoint.getName()).call(); + git.checkout().setName("b1").call(); + + String newFileContent = new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8).replaceAll("\n", "\r\n"); + Files.write(filePath, newFileContent.getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING); + commit("file-m1.xoo"); + + assertThat(newScmProvider().branchChangedLines("master", worktree, Collections.singleton(filePath))) + .isEmpty(); + } + + @Test + public void branchChangedFiles_falls_back_to_origin_when_local_branch_does_not_exist() throws IOException, GitAPIException { + git.branchCreate().setName("b1").call(); + git.checkout().setName("b1").call(); + createAndCommitFile("file-b1"); + + Path worktree2 = temp.newFolder().toPath(); + Git.cloneRepository() + .setURI(worktree.toString()) + .setDirectory(worktree2.toFile()) + .call(); + + assertThat(newScmProvider().branchChangedFiles("master", worktree2)) + .containsOnly(worktree2.resolve("file-b1")); + verifyZeroInteractions(analysisWarnings); + } + + @Test + public void branchChangedFiles_use_remote_target_ref_when_running_on_circle_ci() throws IOException, GitAPIException { + when(system2.envVariable("CIRCLECI")).thenReturn("true"); + git.checkout().setName("b1").setCreateBranch(true).call(); + createAndCommitFile("file-b1"); + + Path worktree2 = temp.newFolder().toPath(); + Git local = Git.cloneRepository() + .setURI(worktree.toString()) + .setDirectory(worktree2.toFile()) + .call(); + + // Make local master match analyzed branch, so if local ref is used then change files will be empty + local.checkout().setCreateBranch(true).setName("master").setStartPoint("origin/b1").call(); + local.checkout().setName("b1").call(); + + assertThat(newScmProvider().branchChangedFiles("master", worktree2)) + .containsOnly(worktree2.resolve("file-b1")); + verifyZeroInteractions(analysisWarnings); + } + + @Test + public void branchChangedFiles_falls_back_to_local_ref_if_origin_branch_does_not_exist_when_running_on_circle_ci() throws IOException, GitAPIException { + when(system2.envVariable("CIRCLECI")).thenReturn("true"); + git.checkout().setName("b1").setCreateBranch(true).call(); + createAndCommitFile("file-b1"); + + Path worktree2 = temp.newFolder().toPath(); + Git local = Git.cloneRepository() + .setURI(worktree.toString()) + .setDirectory(worktree2.toFile()) + .call(); + + local.checkout().setName("local-only").setCreateBranch(true).setStartPoint("origin/master").call(); + local.checkout().setName("b1").call(); + + assertThat(newScmProvider().branchChangedFiles("local-only", worktree2)) + .containsOnly(worktree2.resolve("file-b1")); + verifyZeroInteractions(analysisWarnings); + } + + @Test + public void branchChangedFiles_falls_back_to_upstream_ref() throws IOException, GitAPIException { + git.branchCreate().setName("b1").call(); + git.checkout().setName("b1").call(); + createAndCommitFile("file-b1"); + + Path worktree2 = temp.newFolder().toPath(); + Git.cloneRepository() + .setURI(worktree.toString()) + .setRemote("upstream") + .setDirectory(worktree2.toFile()) + .call(); + + assertThat(newScmProvider().branchChangedFiles("master", worktree2)) + .containsOnly(worktree2.resolve("file-b1")); + verifyZeroInteractions(analysisWarnings); + + } + + @Test + public void branchChangedFiles_should_return_null_when_branch_nonexistent() { + assertThat(newScmProvider().branchChangedFiles("nonexistent", worktree)).isNull(); + } + + @Test + public void branchChangedFiles_should_throw_when_repo_nonexistent() throws IOException { + thrown.expect(MessageException.class); + thrown.expectMessage("Not inside a Git work tree: "); + newScmProvider().branchChangedFiles("master", temp.newFolder().toPath()); + } + + @Test + public void branchChangedFiles_should_throw_when_dir_nonexistent() { + thrown.expect(MessageException.class); + thrown.expectMessage("Not inside a Git work tree: "); + newScmProvider().branchChangedFiles("master", temp.getRoot().toPath().resolve("nonexistent")); + } + + @Test + public void branchChangedFiles_should_return_null_on_io_errors_of_repo_builder() { + GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) { + @Override + Repository buildRepo(Path basedir) throws IOException { + throw new IOException(); + } + }; + assertThat(provider.branchChangedFiles("branch", worktree)).isNull(); + verifyZeroInteractions(analysisWarnings); + } + + @Test + public void branchChangedFiles_should_return_null_if_repo_exactref_is_null() throws IOException { + Repository repository = mock(Repository.class); + RefDatabase refDatabase = mock(RefDatabase.class); + when(repository.getRefDatabase()).thenReturn(refDatabase); + when(refDatabase.findRef("branch")).thenReturn(null); + + GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) { + @Override + Repository buildRepo(Path basedir) { + return repository; + } + }; + assertThat(provider.branchChangedFiles("branch", worktree)).isNull(); + + String warning = "Could not find ref 'branch' in refs/heads, refs/remotes/upstream or refs/remotes/origin." + + " You may see unexpected issues and changes. Please make sure to fetch this ref before pull request analysis."; + verify(analysisWarnings).addUnique(warning); + } + + @Test + public void branchChangedFiles_should_return_null_on_errors() throws GitAPIException { + DiffCommand diffCommand = mock(DiffCommand.class); + when(diffCommand.setShowNameAndStatusOnly(anyBoolean())).thenReturn(diffCommand); + when(diffCommand.setOldTree(any())).thenReturn(diffCommand); + when(diffCommand.setNewTree(any())).thenReturn(diffCommand); + when(diffCommand.call()).thenThrow(mock(GitAPIException.class)); + + Git git = mock(Git.class); + when(git.diff()).thenReturn(diffCommand); + + GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) { + @Override + Git newGit(Repository repo) { + return git; + } + }; + assertThat(provider.branchChangedFiles("master", worktree)).isNull(); + verify(diffCommand).call(); + } + + @Test + public void branchChangedLines_returns_null_when_branch_doesnt_exist() { + assertThat(newScmProvider().branchChangedLines("nonexistent", worktree, emptySet())).isNull(); + } + + @Test + public void branchChangedLines_omits_files_with_git_api_errors() throws IOException, GitAPIException { + String f1 = "file-in-first-commit.xoo"; + String f2 = "file2-in-first-commit.xoo"; + + createAndCommitFile(f2); + + git.branchCreate().setName("b1").call(); + git.checkout().setName("b1").call(); + + // both files modified + addLineToFile(f1, 1); + addLineToFile(f2, 2); + + commit(f1); + commit(f2); + + AtomicInteger callCount = new AtomicInteger(0); + GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) { + @Override + AbstractTreeIterator prepareTreeParser(Repository repo, RevCommit commit) throws IOException { + if (callCount.getAndIncrement() == 1) { + throw new RuntimeException("error"); + } + return super.prepareTreeParser(repo, commit); + } + }; + Set changedFiles = new LinkedHashSet<>(); + changedFiles.add(worktree.resolve(f1)); + changedFiles.add(worktree.resolve(f2)); + + assertThat(provider.branchChangedLines("master", worktree, changedFiles)) + .isEqualTo(Collections.singletonMap(worktree.resolve(f1), Collections.singleton(1))); + } + + @Test + public void branchChangedLines_returns_null_on_io_errors_of_repo_builder() { + GitScmProvider provider = new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2) { + @Override + Repository buildRepo(Path basedir) throws IOException { + throw new IOException(); + } + }; + assertThat(provider.branchChangedLines("branch", worktree, emptySet())).isNull(); + } + + @Test + public void relativePathFromScmRoot_should_return_dot_project_root() { + assertThat(newGitScmProvider().relativePathFromScmRoot(worktree)).isEqualTo(Paths.get("")); + } + + private GitScmProvider newGitScmProvider() { + return new GitScmProvider(mock(JGitBlameCommand.class), analysisWarnings, gitIgnoreCommand, system2); + } + + @Test + public void relativePathFromScmRoot_should_return_filename_for_file_in_project_root() throws IOException { + Path filename = Paths.get("somefile.xoo"); + Path path = worktree.resolve(filename); + Files.createFile(path); + assertThat(newGitScmProvider().relativePathFromScmRoot(path)).isEqualTo(filename); + } + + @Test + public void relativePathFromScmRoot_should_return_relative_path_for_file_in_project_subdir() throws IOException { + Path relpath = Paths.get("sub/dir/to/somefile.xoo"); + Path path = worktree.resolve(relpath); + Files.createDirectories(path.getParent()); + Files.createFile(path); + assertThat(newGitScmProvider().relativePathFromScmRoot(path)).isEqualTo(relpath); + } + + @Test + public void revisionId_should_return_different_sha1_after_commit() throws IOException, GitAPIException { + Path projectDir = worktree.resolve("project"); + Files.createDirectory(projectDir); + + GitScmProvider provider = newGitScmProvider(); + + String sha1before = provider.revisionId(projectDir); + assertThat(sha1before).hasSize(40); + + createAndCommitFile("project/file1"); + String sha1after = provider.revisionId(projectDir); + assertThat(sha1after).hasSize(40); + + assertThat(sha1after).isNotEqualTo(sha1before); + assertThat(provider.revisionId(projectDir)).isEqualTo(sha1after); + } + + @Test + public void revisionId_should_return_null_in_empty_repo() throws IOException { + worktree = temp.newFolder().toPath(); + Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile()); + repo.create(); + + git = new Git(repo); + + Path projectDir = worktree.resolve("project"); + Files.createDirectory(projectDir); + + GitScmProvider provider = newGitScmProvider(); + + assertThat(provider.revisionId(projectDir)).isNull(); + } + + private String randomizedContent(String prefix, int numLines) { + StringBuilder sb = new StringBuilder(); + for (int line = 0; line < numLines; line++) { + sb.append(randomizedLine(prefix)); + sb.append("\n"); + } + return sb.toString(); + } + + private String randomizedLine(String prefix) { + StringBuilder sb = new StringBuilder(prefix); + for (int i = 0; i < 4; i++) { + sb.append(' '); + for (int j = 0; j < prefix.length(); j++) { + sb.append((char) ('a' + random.nextInt(26))); + } + } + return sb.toString(); + } + + private void createAndCommitFile(String relativePath) throws IOException, GitAPIException { + createAndCommitFile(relativePath, randomizedContent(relativePath, 3)); + } + + private void createAndCommitFile(String relativePath, Instant commitDate) throws IOException, GitAPIException { + createFile(relativePath, randomizedContent(relativePath, 3)); + commit(relativePath, commitDate); + } + + private void createAndCommitFile(String relativePath, String content) throws IOException, GitAPIException { + createFile(relativePath, content); + commit(relativePath); + } + + private void createFile(String relativePath, String content) throws IOException { + Path newFile = worktree.resolve(relativePath); + Files.createDirectories(newFile.getParent()); + Files.write(newFile, content.getBytes(), StandardOpenOption.CREATE); + } + + private void addLineToFile(String relativePath, int lineNumber) throws IOException { + Path filePath = worktree.resolve(relativePath); + List lines = Files.readAllLines(filePath); + lines.add(lineNumber - 1, randomizedLine(relativePath)); + Files.write(filePath, lines, StandardOpenOption.TRUNCATE_EXISTING); + } + + private void removeLineInFile(String relativePath, int lineNumber) throws IOException { + Path filePath = worktree.resolve(relativePath); + List lines = Files.readAllLines(filePath); + lines.remove(lineNumber - 1); + Files.write(filePath, lines, StandardOpenOption.TRUNCATE_EXISTING); + } + + private void appendToAndCommitFile(String relativePath) throws IOException, GitAPIException { + Files.write(worktree.resolve(relativePath), randomizedContent(relativePath, 1).getBytes(), StandardOpenOption.APPEND); + commit(relativePath); + } + + private void deleteAndCommitFile(String relativePath) throws GitAPIException { + git.rm().addFilepattern(relativePath).call(); + commit(relativePath); + } + + private void commit(String... relativePaths) throws GitAPIException { + for (String path : relativePaths) { + git.add().addFilepattern(path).call(); + } + String msg = String.join(",", relativePaths); + git.commit().setAuthor("joe", "joe@example.com").setMessage(msg).call(); + } + + private void commit(String relativePath, Instant date) throws GitAPIException { + PersonIdent person = new PersonIdent("joe", "joe@example.com", Date.from(date), TimeZone.getDefault()); + git.commit().setAuthor(person).setCommitter(person).setMessage(relativePath).call(); + } + + private GitScmProvider newScmProvider() { + return new GitScmProvider(mockCommand(), analysisWarnings, gitIgnoreCommand, system2); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmSupportTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmSupportTest.java new file mode 100644 index 00000000000..803b02088be --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitScmSupportTest.java @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class GitScmSupportTest { + + @Test + public void getClasses() { + assertThat(GitScmSupport.getClasses()).hasSize(3); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitThreadFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitThreadFactoryTest.java new file mode 100644 index 00000000000..39f46cdd2b8 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/GitThreadFactoryTest.java @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.util.concurrent.ForkJoinPool; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class GitThreadFactoryTest { + @Test + public void testName() { + GitThreadFactory factory = new GitThreadFactory(); + ForkJoinPool pool = new ForkJoinPool(); + assertThat(factory.newThread(pool).getName()).isEqualTo("git-scm-0"); + assertThat(factory.newThread(pool).getName()).isEqualTo("git-scm-1"); + + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/JGitBlameCommandTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/JGitBlameCommandTest.java new file mode 100644 index 00000000000..936ead4e69b --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/JGitBlameCommandTest.java @@ -0,0 +1,343 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.scm.BlameCommand.BlameInput; +import org.sonar.api.batch.scm.BlameCommand.BlameOutput; +import org.sonar.api.batch.scm.BlameLine; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.log.LogTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assume.assumeTrue; +import static org.mockito.Matchers.startsWith; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.sonar.scm.git.Utils.javaUnzip; + +public class JGitBlameCommandTest { + + private static final String DUMMY_JAVA = "src/main/java/org/dummy/Dummy.java"; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public LogTester logTester = new LogTester(); + + private final BlameInput input = mock(BlameInput.class); + + @Test + public void testBlame() throws IOException { + File projectDir = temp.newFolder(); + javaUnzip("dummy-git.zip", projectDir); + + JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); + + File baseDir = new File(projectDir, "dummy-git"); + DefaultFileSystem fs = new DefaultFileSystem(baseDir); + when(input.fileSystem()).thenReturn(fs); + DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA) + .setModuleBaseDir(baseDir.toPath()) + .build(); + fs.add(inputFile); + + BlameOutput blameResult = mock(BlameOutput.class); + when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile)); + jGitBlameCommand.blame(input, blameResult); + + Date revisionDate1 = DateUtils.parseDateTime("2012-07-17T16:12:48+0200"); + String revision1 = "6b3aab35a3ea32c1636fee56f996e677653c48ea"; + String author1 = "david@gageot.net"; + + // second commit, which has a commit date different than the author date + Date revisionDate2 = DateUtils.parseDateTime("2015-05-19T13:31:09+0200"); + String revision2 = "0d269c1acfb8e6d4d33f3c43041eb87e0df0f5e7"; + String author2 = "duarte.meneses@sonarsource.com"; + + List expectedBlame = new LinkedList<>(); + for (int i = 0; i < 25; i++) { + expectedBlame.add(new BlameLine().revision(revision1).date(revisionDate1).author(author1)); + } + for (int i = 0; i < 3; i++) { + expectedBlame.add(new BlameLine().revision(revision2).date(revisionDate2).author(author2)); + } + for (int i = 0; i < 1; i++) { + expectedBlame.add(new BlameLine().revision(revision1).date(revisionDate1).author(author1)); + } + + verify(blameResult).blameResult(inputFile, expectedBlame); + } + + @Test + public void properFailureIfNotAGitProject() throws IOException { + File projectDir = temp.newFolder(); + javaUnzip("dummy-git.zip", projectDir); + + JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); + + File baseDir = new File(projectDir, "dummy-git"); + + // Delete .git + FileUtils.forceDelete(new File(baseDir, ".git")); + + DefaultFileSystem fs = new DefaultFileSystem(baseDir); + when(input.fileSystem()).thenReturn(fs); + DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA).build(); + fs.add(inputFile); + + BlameOutput blameResult = mock(BlameOutput.class); + when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile)); + + thrown.expect(MessageException.class); + thrown.expectMessage("Not inside a Git work tree: "); + + jGitBlameCommand.blame(input, blameResult); + } + + @Test + public void testBlameOnNestedModule() throws IOException { + File projectDir = temp.newFolder(); + javaUnzip("dummy-git-nested.zip", projectDir); + + JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); + + File baseDir = new File(projectDir, "dummy-git-nested/dummy-project"); + DefaultFileSystem fs = new DefaultFileSystem(baseDir); + when(input.fileSystem()).thenReturn(fs); + DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA) + .setModuleBaseDir(baseDir.toPath()) + .build(); + fs.add(inputFile); + + BlameOutput blameResult = mock(BlameOutput.class); + when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile)); + jGitBlameCommand.blame(input, blameResult); + + Date revisionDate = DateUtils.parseDateTime("2012-07-17T16:12:48+0200"); + String revision = "6b3aab35a3ea32c1636fee56f996e677653c48ea"; + String author = "david@gageot.net"; + verify(blameResult).blameResult(inputFile, + Arrays.asList( + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author), + new BlameLine().revision(revision).date(revisionDate).author(author))); + } + + @Test + public void dontFailOnModifiedFile() throws IOException { + File projectDir = temp.newFolder(); + javaUnzip("dummy-git.zip", projectDir); + + JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); + + File baseDir = new File(projectDir, "dummy-git"); + DefaultFileSystem fs = new DefaultFileSystem(baseDir); + when(input.fileSystem()).thenReturn(fs); + String relativePath = DUMMY_JAVA; + DefaultInputFile inputFile = new TestInputFileBuilder("foo", relativePath).build(); + fs.add(inputFile); + + // Emulate a modification + Files.write(baseDir.toPath().resolve(relativePath), "modification and \n some new line".getBytes()); + + BlameOutput blameResult = mock(BlameOutput.class); + + when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile)); + jGitBlameCommand.blame(input, blameResult); + } + + @Test + public void dontFailOnNewFile() throws IOException { + File projectDir = temp.newFolder(); + javaUnzip("dummy-git.zip", projectDir); + + JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); + + File baseDir = new File(projectDir, "dummy-git"); + DefaultFileSystem fs = new DefaultFileSystem(baseDir); + when(input.fileSystem()).thenReturn(fs); + String relativePath = DUMMY_JAVA; + String relativePath2 = "src/main/java/org/dummy/Dummy2.java"; + DefaultInputFile inputFile = new TestInputFileBuilder("foo", relativePath).build(); + fs.add(inputFile); + DefaultInputFile inputFile2 = new TestInputFileBuilder("foo", relativePath2).build(); + fs.add(inputFile2); + + // Emulate a new file + FileUtils.copyFile(new File(baseDir, relativePath), new File(baseDir, relativePath2)); + + BlameOutput blameResult = mock(BlameOutput.class); + + when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile, inputFile2)); + jGitBlameCommand.blame(input, blameResult); + } + + @Test + public void dontFailOnSymlink() throws IOException { + assumeTrue(!System2.INSTANCE.isOsWindows()); + File projectDir = temp.newFolder(); + javaUnzip("dummy-git.zip", projectDir); + + JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); + + File baseDir = new File(projectDir, "dummy-git"); + DefaultFileSystem fs = new DefaultFileSystem(baseDir); + when(input.fileSystem()).thenReturn(fs); + String relativePath = DUMMY_JAVA; + String relativePath2 = "src/main/java/org/dummy/Dummy2.java"; + DefaultInputFile inputFile = new TestInputFileBuilder("foo", relativePath) + .setModuleBaseDir(baseDir.toPath()) + .build(); + fs.add(inputFile); + DefaultInputFile inputFile2 = new TestInputFileBuilder("foo", relativePath2) + .setModuleBaseDir(baseDir.toPath()) + .build(); + fs.add(inputFile2); + + // Create symlink + Files.createSymbolicLink(inputFile2.file().toPath(), inputFile.file().toPath()); + + BlameOutput blameResult = mock(BlameOutput.class); + + when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile, inputFile2)); + jGitBlameCommand.blame(input, blameResult); + } + + @Test + public void return_early_when_shallow_clone_detected() throws IOException { + File projectDir = temp.newFolder(); + javaUnzip("shallow-git.zip", projectDir); + + File baseDir = new File(projectDir, "shallow-git"); + + DefaultFileSystem fs = new DefaultFileSystem(baseDir); + when(input.fileSystem()).thenReturn(fs); + + DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA).build(); + when(input.filesToBlame()).thenReturn(Collections.singleton(inputFile)); + + // register warning with default wrapper + AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); + JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(new PathResolver(), analysisWarnings); + BlameOutput output = mock(BlameOutput.class); + jGitBlameCommand.blame(input, output); + + assertThat(logTester.logs()).first() + .matches(s -> s.contains("Shallow clone detected, no blame information will be provided.")); + verifyZeroInteractions(output); + + verify(analysisWarnings).addUnique(startsWith("Shallow clone detected")); + } + + @Test + public void return_early_when_clone_with_reference_detected() throws IOException { + File projectDir = temp.newFolder(); + javaUnzip("dummy-git-reference-clone.zip", projectDir); + + Path baseDir = projectDir.toPath().resolve("dummy-git2"); + + DefaultFileSystem fs = new DefaultFileSystem(baseDir); + when(input.fileSystem()).thenReturn(fs); + + DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA).setModuleBaseDir(baseDir).build(); + when(input.filesToBlame()).thenReturn(Collections.singleton(inputFile)); + + // register warning + AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); + JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(new PathResolver(), analysisWarnings); + TestBlameOutput output = new TestBlameOutput(); + jGitBlameCommand.blame(input, output); + + assertThat(logTester.logs()).first() + .matches(s -> s.contains("This git repository references another local repository which is not well supported")); + + // contains commits referenced from the old clone and commits in the new clone + assertThat(output.blame.keySet()).contains(inputFile); + assertThat(output.blame.get(inputFile).stream().map(BlameLine::revision)) + .containsOnly("6b3aab35a3ea32c1636fee56f996e677653c48ea", "843c7c30d7ebd9a479e8f1daead91036c75cbc4e", "0d269c1acfb8e6d4d33f3c43041eb87e0df0f5e7"); + verifyZeroInteractions(analysisWarnings); + } + + private JGitBlameCommand newJGitBlameCommand() { + return new JGitBlameCommand(new PathResolver(), mock(AnalysisWarnings.class)); + } + + private static class TestBlameOutput implements BlameOutput { + private Map> blame = new LinkedHashMap<>(); + + @Override public void blameResult(InputFile inputFile, List list) { + blame.put(inputFile, list); + } + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scm/git/Utils.java b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/Utils.java new file mode 100644 index 00000000000..cdee9fde16f --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scm/git/Utils.java @@ -0,0 +1,67 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scm.git; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.apache.commons.io.FileUtils; + +import static java.lang.String.format; + +public class Utils { + public static void javaUnzip(String zipFileName, File toDir) throws IOException { + try { + File testRepos = new File(Utils.class.getResource("test-repos").toURI()); + File zipFile = new File(testRepos, zipFileName); + javaUnzip(zipFile, toDir); + } catch (URISyntaxException e) { + throw new IOException(e); + } + } + + private static void javaUnzip(File zip, File toDir) { + try { + try (ZipFile zipFile = new ZipFile(zip)) { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + File to = new File(toDir, entry.getName()); + if (entry.isDirectory()) { + FileUtils.forceMkdir(to); + } else { + File parent = to.getParentFile(); + if (parent != null) { + FileUtils.forceMkdir(parent); + } + + Files.copy(zipFile.getInputStream(entry), to.toPath()); + } + } + } + } catch (Exception e) { + throw new IllegalStateException(format("Fail to unzip %s to %s", zip, toDir), e); + } + } +} diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/dummy-git-nested.zip b/sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/dummy-git-nested.zip new file mode 100644 index 0000000000000000000000000000000000000000..b0ee03b6b16645def6cf37ff804c120868ccef72 GIT binary patch literal 60535 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 z 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 literal 0 HcmV?d00001 diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/dummy-git-reference-clone.zip b/sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/dummy-git-reference-clone.zip new file mode 100644 index 0000000000000000000000000000000000000000..080812c60d092a56e449442d604663c081e6e074 GIT binary patch literal 86575 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%0At}xA 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 JI{>27{U2*_i$(wd literal 0 HcmV?d00001 diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/dummy-git.zip b/sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/dummy-git.zip new file mode 100644 index 0000000000000000000000000000000000000000..f8900d5be39f645ea4577b6f967aa6a8a6afdd1c GIT binary patch literal 60003 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

EmT;or{{Xv`9!oFE>2tZe(6XP&D?K^>=3o& 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 literal 0 HcmV?d00001 diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/shallow-git.zip b/sonar-scanner-engine/src/test/resources/org/sonar/scm/git/test-repos/shallow-git.zip new file mode 100644 index 0000000000000000000000000000000000000000..353633c1d121184dafee32e3c1e45b7ef97e6349 GIT binary patch literal 7832 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 ZC7z@d8KnufdFU?10w@R5?Vcg@?Z0s8v|IoH literal 0 HcmV?d00001 -- 2.39.5