Co-authored-by: lukasz-jarocki-sonarsource <lukasz.jarocki@sonarsource.com> Co-authored-by: leo-geoffroy-sonarsource <leo.geoffroy@sonarsource.com> Co-authored-by: benjamin-campomenosi-sonarsource <benjamin.campomenosi@sonarsource.com> Co-authored-by: Duarte Meneses <duarte.meneses@sonarsource.com>tags/10.0.0.68432
@@ -352,6 +352,7 @@ subprojects { | |||
dependency 'org.postgresql:postgresql:42.5.1' | |||
dependency 'org.reflections:reflections:0.10.2' | |||
dependency 'org.simpleframework:simple:5.1.6' | |||
dependency 'org.sonarsource.git.blame:git-files-blame:1.0.0.157' | |||
dependency 'org.sonarsource.orchestrator:sonar-orchestrator:3.40.0.183' | |||
dependency 'org.sonarsource.update-center:sonar-update-center-common:1.29.0.1000' | |||
dependency("org.springframework:spring-context:${springVersion}") { |
@@ -34,6 +34,7 @@ dependencies { | |||
api 'org.slf4j:log4j-over-slf4j' | |||
api 'org.slf4j:slf4j-api' | |||
api 'org.sonarsource.api.plugin:sonar-plugin-api' | |||
api 'org.sonarsource.git.blame:git-files-blame' | |||
api 'org.sonarsource.update-center:sonar-update-center-common' | |||
api 'org.springframework:spring-context' | |||
@@ -60,7 +61,7 @@ dependencies { | |||
} | |||
license { | |||
excludes(["**/Fake.java", "**/Fake.groovy", "org/sonar/scanner/cpd/ManyStatements.java"]) | |||
excludes(["**/Fake.java", "**/Fake.groovy", "org/sonar/scanner/cpd/ManyStatements.java", "org/sonar/scm/git/expected-blame/**/*"]) | |||
} | |||
artifactoryPublish.skip = false |
@@ -22,14 +22,19 @@ package org.sonar.scm.git; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.file.Files; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.Executors; | |||
import java.util.concurrent.TimeUnit; | |||
import org.eclipse.jgit.api.Git; | |||
import org.eclipse.jgit.api.errors.GitAPIException; | |||
import org.eclipse.jgit.diff.RawTextComparator; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.ObjectId; | |||
import org.eclipse.jgit.lib.Repository; | |||
@@ -44,8 +49,13 @@ import org.sonar.api.scan.filesystem.PathResolver; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.api.utils.log.Profiler; | |||
import org.sonar.scm.git.blame.BlameResult; | |||
import org.sonar.scm.git.blame.RepositoryBlameCommand; | |||
import org.sonar.scm.git.strategy.BlameStrategy; | |||
import org.sonar.scm.git.strategy.DefaultBlameStrategy.BlameAlgorithmEnum; | |||
import static java.util.Optional.ofNullable; | |||
import static org.sonar.scm.git.strategy.DefaultBlameStrategy.BlameAlgorithmEnum.GIT_FILES_BLAME; | |||
public class CompositeBlameCommand extends BlameCommand { | |||
private static final Logger LOG = Loggers.get(CompositeBlameCommand.class); | |||
@@ -53,12 +63,16 @@ public class CompositeBlameCommand extends BlameCommand { | |||
private final AnalysisWarnings analysisWarnings; | |||
private final PathResolver pathResolver; | |||
private final JGitBlameCommand jgitCmd; | |||
private final GitBlameCommand nativeCmd; | |||
private final NativeGitBlameCommand nativeCmd; | |||
private boolean nativeGitEnabled = false; | |||
public CompositeBlameCommand(AnalysisWarnings analysisWarnings, PathResolver pathResolver, JGitBlameCommand jgitCmd, GitBlameCommand nativeCmd) { | |||
private final BlameStrategy blameStrategy; | |||
public CompositeBlameCommand(AnalysisWarnings analysisWarnings, PathResolver pathResolver, JGitBlameCommand jgitCmd, | |||
NativeGitBlameCommand nativeCmd, BlameStrategy blameStrategy) { | |||
this.analysisWarnings = analysisWarnings; | |||
this.pathResolver = pathResolver; | |||
this.blameStrategy = blameStrategy; | |||
this.jgitCmd = jgitCmd; | |||
this.nativeCmd = nativeCmd; | |||
} | |||
@@ -66,23 +80,48 @@ public class CompositeBlameCommand extends BlameCommand { | |||
@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)) { | |||
try (Repository repo = JGitUtils.buildRepository(basedir.toPath())) { | |||
File gitBaseDir = repo.getWorkTree(); | |||
if (cloneIsInvalid(gitBaseDir)) { | |||
return; | |||
} | |||
Profiler profiler = Profiler.create(LOG); | |||
profiler.startDebug("Collecting committed files"); | |||
Set<String> committedFiles = collectAllCommittedFiles(repo); | |||
Map<String, InputFile> inputFileByGitRelativePath = getCommittedFilesToBlame(repo, gitBaseDir, input); | |||
profiler.stopDebug(); | |||
BlameAlgorithmEnum blameAlgorithmEnum = this.blameStrategy.getBlameAlgorithm(Runtime.getRuntime().availableProcessors(), inputFileByGitRelativePath.size()); | |||
LOG.debug("Using {} strategy to blame files", blameAlgorithmEnum); | |||
if (blameAlgorithmEnum == GIT_FILES_BLAME) { | |||
blameWithFilesGitCommand(output, repo, inputFileByGitRelativePath); | |||
} else { | |||
blameWithNativeGitCommand(output, repo, inputFileByGitRelativePath, gitBaseDir); | |||
} | |||
} | |||
} | |||
private Map<String, InputFile> getCommittedFilesToBlame(Repository repo, File gitBaseDir, BlameInput input) { | |||
Set<String> committedFiles = collectAllCommittedFiles(repo); | |||
Map<String, InputFile> inputFileByGitRelativePath = new HashMap<>(); | |||
for (InputFile inputFile : input.filesToBlame()) { | |||
String relative = pathResolver.relativePath(gitBaseDir, inputFile.file()); | |||
if (relative == null || !committedFiles.contains(relative)) { | |||
continue; | |||
} | |||
inputFileByGitRelativePath.put(relative, inputFile); | |||
} | |||
return inputFileByGitRelativePath; | |||
} | |||
private void blameWithNativeGitCommand(BlameOutput output, Repository repo, Map<String, InputFile> inputFileByGitRelativePath, File gitBaseDir) { | |||
try (Git git = Git.wrap(repo)) { | |||
nativeGitEnabled = nativeCmd.checkIfEnabled(); | |||
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new GitThreadFactory()); | |||
for (InputFile inputFile : input.filesToBlame()) { | |||
String filename = pathResolver.relativePath(gitBaseDir, inputFile.file()); | |||
if (filename == null || !committedFiles.contains(filename)) { | |||
continue; | |||
} | |||
for (Map.Entry<String, InputFile> e : inputFileByGitRelativePath.entrySet()) { | |||
InputFile inputFile = e.getValue(); | |||
String filename = e.getKey(); | |||
// exceptions thrown by the blame method will be ignored | |||
executorService.submit(() -> blame(output, git, gitBaseDir, inputFile, filename)); | |||
} | |||
@@ -151,6 +190,29 @@ public class CompositeBlameCommand extends BlameCommand { | |||
} | |||
} | |||
private static void blameWithFilesGitCommand(BlameOutput output, Repository repo, Map<String, InputFile> inputFileByGitRelativePath) { | |||
RepositoryBlameCommand blameCommand = new RepositoryBlameCommand(repo) | |||
.setTextComparator(RawTextComparator.WS_IGNORE_ALL) | |||
.setMultithreading(true) | |||
.setFilePaths(inputFileByGitRelativePath.keySet()); | |||
try { | |||
BlameResult blameResult = blameCommand.call(); | |||
for (Map.Entry<String, InputFile> e : inputFileByGitRelativePath.entrySet()) { | |||
BlameResult.FileBlame fileBlameResult = blameResult.getFileBlameByPath().get(e.getKey()); | |||
if (fileBlameResult == null) { | |||
LOG.debug("Unable to blame file {}.", e.getValue().filename()); | |||
continue; | |||
} | |||
saveBlameInformationForFileInTheOutput(fileBlameResult, e.getValue(), output); | |||
} | |||
} catch (GitAPIException e) { | |||
LOG.warn("There was an issue when interacting with git repository: " + e.getMessage(), e); | |||
} | |||
} | |||
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. " | |||
@@ -166,4 +228,27 @@ public class CompositeBlameCommand extends BlameCommand { | |||
return false; | |||
} | |||
private static void saveBlameInformationForFileInTheOutput(BlameResult.FileBlame fileBlame, InputFile file, BlameOutput output) { | |||
List<BlameLine> linesList = new ArrayList<>(); | |||
for (int i = 0; i < fileBlame.lines(); i++) { | |||
if (fileBlame.getAuthorEmails()[i] == null || fileBlame.getCommitHashes()[i] == null || fileBlame.getCommitDates()[i] == null) { | |||
LOG.debug("Unable to blame file {}. No blame info at line {}. Is file committed? [Author: {} Source commit: {}]", file.filename()); | |||
linesList.clear(); | |||
break; | |||
} | |||
linesList.add(new BlameLine() | |||
.date(fileBlame.getCommitDates()[i]) | |||
.revision(fileBlame.getCommitHashes()[i]) | |||
.author(fileBlame.getAuthorEmails()[i])); | |||
} | |||
if (!linesList.isEmpty()) { | |||
if (linesList.size() == file.lines() - 1) { | |||
// SONARPLUGINS-3097 Git does not report blame on last empty line | |||
linesList.add(linesList.get(linesList.size() - 1)); | |||
} | |||
output.blameResult(file, linesList); | |||
} | |||
} | |||
} |
@@ -22,6 +22,7 @@ package org.sonar.scm.git; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import org.eclipse.jgit.util.FS; | |||
import org.sonar.scm.git.strategy.DefaultBlameStrategy; | |||
public final class GitScmSupport { | |||
private GitScmSupport() { | |||
@@ -33,8 +34,9 @@ public final class GitScmSupport { | |||
return Arrays.asList( | |||
JGitBlameCommand.class, | |||
CompositeBlameCommand.class, | |||
NativeGitBlameCommand.class, | |||
DefaultBlameStrategy.class, | |||
ProcessWrapperFactory.class, | |||
GitBlameCommand.class, | |||
GitScmProvider.class, | |||
GitIgnoreCommand.class); | |||
} |
@@ -39,13 +39,13 @@ import org.springframework.beans.factory.annotation.Autowired; | |||
import static java.util.Collections.emptyList; | |||
import static org.sonar.api.utils.Preconditions.checkState; | |||
public class GitBlameCommand { | |||
public class NativeGitBlameCommand { | |||
protected static final String BLAME_COMMAND = "blame"; | |||
protected static final String GIT_DIR_FLAG = "--git-dir"; | |||
protected static final String GIT_DIR_ARGUMENT = "%s/.git"; | |||
protected static final String GIT_DIR_FORCE_FLAG = "-C"; | |||
private static final Logger LOG = Loggers.get(GitBlameCommand.class); | |||
private static final Logger LOG = Loggers.get(NativeGitBlameCommand.class); | |||
private static final Pattern EMAIL_PATTERN = Pattern.compile("<(.*?)>"); | |||
private static final String COMMITTER_TIME = "committer-time "; | |||
private static final String AUTHOR_MAIL = "author-mail "; | |||
@@ -64,12 +64,12 @@ public class GitBlameCommand { | |||
private String gitCommand; | |||
@Autowired | |||
public GitBlameCommand(System2 system, ProcessWrapperFactory processWrapperFactory) { | |||
public NativeGitBlameCommand(System2 system, ProcessWrapperFactory processWrapperFactory) { | |||
this.system = system; | |||
this.processWrapperFactory = processWrapperFactory; | |||
} | |||
GitBlameCommand(String gitCommand, System2 system, ProcessWrapperFactory processWrapperFactory) { | |||
NativeGitBlameCommand(String gitCommand, System2 system, ProcessWrapperFactory processWrapperFactory) { | |||
this.gitCommand = gitCommand; | |||
this.system = system; | |||
this.processWrapperFactory = processWrapperFactory; |
@@ -0,0 +1,25 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 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.strategy; | |||
public interface BlameStrategy { | |||
DefaultBlameStrategy.BlameAlgorithmEnum getBlameAlgorithm(int availableProcessors, int numberOfFiles); | |||
} |
@@ -0,0 +1,80 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 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.strategy; | |||
import org.sonar.api.config.Configuration; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import static org.sonar.scm.git.strategy.DefaultBlameStrategy.BlameAlgorithmEnum.*; | |||
/** | |||
* The blame strategy defines when the files-blame algorithm is used over jgit or native git algorithm. | |||
* It has been found that the JGit/Git native algorithm performs better in certain circumstances, such as: | |||
* - When we have many cores available for multi-threading | |||
* - The number of files to be blame by the algorithm | |||
* This two metrics are correlated: | |||
* - The more threads available, the more it is favorable to use the JGit/Git native algorithm | |||
* - The more files available, the more it is favorable to use the git-files-blame algorithm | |||
*/ | |||
public class DefaultBlameStrategy implements BlameStrategy { | |||
private static final Logger LOG = Loggers.get(DefaultBlameStrategy.class); | |||
private static final int FILES_GIT_BLAME_TRIGGER = 10; | |||
public static final String PROP_SONAR_SCM_USE_BLAME_ALGORITHM = "sonar.scm.use.blame.algorithm"; | |||
private final Configuration configuration; | |||
public DefaultBlameStrategy(Configuration configuration) { | |||
this.configuration = configuration; | |||
} | |||
@Override | |||
public BlameAlgorithmEnum getBlameAlgorithm(int availableProcessors, int numberOfFiles) { | |||
BlameAlgorithmEnum forcedStrategy = configuration.get(PROP_SONAR_SCM_USE_BLAME_ALGORITHM) | |||
.map(BlameAlgorithmEnum::valueOf) | |||
.orElse(null); | |||
if (forcedStrategy != null) { | |||
return forcedStrategy; | |||
} | |||
if (availableProcessors == 0) { | |||
LOG.warn("Available processors are 0. Falling back to native git blame"); | |||
return GIT_NATIVE_BLAME; | |||
} | |||
if (numberOfFiles / availableProcessors > FILES_GIT_BLAME_TRIGGER) { | |||
return GIT_FILES_BLAME; | |||
} | |||
return GIT_NATIVE_BLAME; | |||
} | |||
public enum BlameAlgorithmEnum { | |||
/** | |||
* Strategy using native git for the blame, or JGit on single file as a fallback | |||
*/ | |||
GIT_NATIVE_BLAME, | |||
/** | |||
* Strategy using git-files-blame algorithm | |||
*/ | |||
GIT_FILES_BLAME; | |||
} | |||
} |
@@ -0,0 +1,184 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 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 com.tngtech.java.junit.dataprovider.DataProvider; | |||
import com.tngtech.java.junit.dataprovider.DataProviderRunner; | |||
import com.tngtech.java.junit.dataprovider.UseDataProvider; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.file.Files; | |||
import java.nio.file.LinkOption; | |||
import java.nio.file.Path; | |||
import java.time.OffsetDateTime; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.Stream; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.junit.runner.RunWith; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultFileSystem; | |||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | |||
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.System2; | |||
import org.sonar.scm.git.strategy.DefaultBlameStrategy.BlameAlgorithmEnum; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
@RunWith(DataProviderRunner.class) | |||
public class CompositeBlameCommandIT { | |||
private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); | |||
private final BlameCommand.BlameInput input = mock(BlameCommand.BlameInput.class); | |||
private final JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(); | |||
private final ProcessWrapperFactory processWrapperFactory = new ProcessWrapperFactory(); | |||
private final NativeGitBlameCommand nativeGitBlameCommand = new NativeGitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
@UseDataProvider("namesOfTheTestRepositoriesWithBlameAlgorithm") | |||
public void testThatBlameAlgorithmOutputsTheSameDataAsGitNativeBlame(String folder, BlameAlgorithmEnum blameAlgorithm) throws Exception { | |||
CompositeBlameCommand underTest = new CompositeBlameCommand(analysisWarnings, new PathResolver(), jGitBlameCommand, nativeGitBlameCommand, (p, f) -> blameAlgorithm); | |||
TestBlameOutput output = new TestBlameOutput(); | |||
File gitFolder = unzipGitRepository(folder); | |||
setUpBlameInputWithFile(gitFolder.toPath()); | |||
underTest.blame(input, output); | |||
assertBlameMatchesExpectedBlame(output.blame, gitFolder); | |||
} | |||
@DataProvider | |||
public static Object[][] namesOfTheTestRepositoriesWithBlameAlgorithm() { | |||
List<String> testCases = List.of("one-file-one-commit", | |||
"one-file-two-commits", | |||
"two-files-one-commit", | |||
"merge-commits", | |||
"5lines-5commits", | |||
"5files-5commits", | |||
"two-files-moved-around-with-conflicts", | |||
"one-file-renamed-many-times", | |||
"one-file-many-merges-and-renames", | |||
"two-merge-commits", | |||
"dummy-git", | |||
"dummy-git-few-comitters" | |||
); | |||
List<BlameAlgorithmEnum> blameStrategies = Arrays.stream(BlameAlgorithmEnum.values()).collect(Collectors.toList()); | |||
return testCases.stream() | |||
.flatMap(t -> blameStrategies.stream().map(b -> new Object[]{t, b})) | |||
.toArray(Object[][]::new); | |||
} | |||
private void assertBlameMatchesExpectedBlame(Map<InputFile, List<BlameLine>> blame, File gitFolder) throws Exception { | |||
Map<Path, List<BlameLine>> expectedBlame = readExpectedBlame(gitFolder.getName()); | |||
assertThat(blame.entrySet()) | |||
.as("Blamed files: " + blame.keySet() + ". Expected blamed files " + expectedBlame.keySet()) | |||
.hasSize(expectedBlame.size()); | |||
for (Map.Entry<InputFile, List<BlameLine>> actualBlame : blame.entrySet()) { | |||
Path relativeFilePath = gitFolder.toPath().relativize(actualBlame.getKey().path()); | |||
assertThat(actualBlame.getValue()) | |||
.as("A difference is found in file " + relativeFilePath) | |||
.isEqualTo(expectedBlame.get(relativeFilePath)); | |||
} | |||
} | |||
// --- helper methods --- // | |||
private Map<Path, List<BlameLine>> readExpectedBlame(String expectedBlameFolder) throws Exception { | |||
Path expectedBlameFiles = new File(Utils.class.getResource("expected-blame/" + expectedBlameFolder).toURI()).toPath(); | |||
Map<Path, List<BlameLine>> expectedBlame = new HashMap<>(); | |||
List<Path> filesInExpectedBlameFolder = Files.walk(expectedBlameFiles).filter(Files::isRegularFile).collect(Collectors.toList()); | |||
for (Path expectedFileBlamePath : filesInExpectedBlameFolder) { | |||
List<BlameLine> blameLines = new ArrayList<>(); | |||
List<String> expectedBlameStrings = Files.readAllLines(expectedFileBlamePath); | |||
for (String line : expectedBlameStrings) { | |||
String revision = line.substring(0, 40); | |||
int beginningEmail = line.indexOf("<") + 1, endEmail = line.indexOf(">"); | |||
String email = line.substring(beginningEmail, endEmail); | |||
int beginningDate = line.indexOf("2", endEmail), dateLength = 25; | |||
String sDate = line.substring(beginningDate, beginningDate + dateLength); | |||
Date parsedDate = new Date(OffsetDateTime.parse(sDate).toInstant().toEpochMilli()); | |||
BlameLine blameLine = new BlameLine() | |||
.revision(revision) | |||
.author(email) | |||
.date(parsedDate); | |||
blameLines.add(blameLine); | |||
} | |||
expectedBlame.put(expectedBlameFiles.relativize(expectedFileBlamePath), blameLines); | |||
} | |||
return expectedBlame; | |||
} | |||
private File unzipGitRepository(String repositoryName) throws IOException { | |||
File gitFolderForEachTest = temp.newFolder().toPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toFile(); | |||
Utils.javaUnzip(repositoryName + ".zip", gitFolderForEachTest); | |||
return gitFolderForEachTest.toPath().resolve(repositoryName).toFile(); | |||
} | |||
private static class TestBlameOutput implements BlameCommand.BlameOutput { | |||
private final Map<InputFile, List<BlameLine>> blame = new ConcurrentHashMap<>(); | |||
@Override | |||
public void blameResult(InputFile inputFile, List<BlameLine> list) { | |||
blame.put(inputFile, list); | |||
} | |||
} | |||
private void setUpBlameInputWithFile(Path baseDir) throws IOException { | |||
DefaultFileSystem fs = new DefaultFileSystem(baseDir); | |||
when(input.fileSystem()).thenReturn(fs); | |||
try (Stream<Path> stream = Files.walk(baseDir)) { | |||
List<InputFile> inputFiles = stream.filter(Files::isRegularFile) | |||
.map(f -> new TestInputFileBuilder("foo", baseDir.toFile(), f.toFile()).build()) | |||
.filter(f -> !f.toString().startsWith(".git") && !f.toString().endsWith(".class")) | |||
.collect(Collectors.toList()); | |||
when(input.filesToBlame()).thenReturn(inputFiles); | |||
} | |||
} | |||
} |
@@ -19,6 +19,9 @@ | |||
*/ | |||
package org.sonar.scm.git; | |||
import com.tngtech.java.junit.dataprovider.DataProvider; | |||
import com.tngtech.java.junit.dataprovider.DataProviderRunner; | |||
import com.tngtech.java.junit.dataprovider.UseDataProvider; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.file.Files; | |||
@@ -30,10 +33,14 @@ import java.util.Date; | |||
import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.IntStream; | |||
import org.apache.commons.io.FileUtils; | |||
import org.assertj.core.api.Condition; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.junit.runner.RunWith; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultFileSystem; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
@@ -46,26 +53,33 @@ 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 org.sonar.api.utils.log.LoggerLevel; | |||
import org.sonar.scm.git.strategy.BlameStrategy; | |||
import org.sonar.scm.git.strategy.DefaultBlameStrategy.BlameAlgorithmEnum; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | |||
import static org.junit.Assume.assumeTrue; | |||
import static org.mockito.ArgumentMatchers.any; | |||
import static org.mockito.ArgumentMatchers.intThat; | |||
import static org.mockito.ArgumentMatchers.startsWith; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoInteractions; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.scm.git.Utils.javaUnzip; | |||
import static org.sonar.scm.git.strategy.DefaultBlameStrategy.BlameAlgorithmEnum.GIT_FILES_BLAME; | |||
import static org.sonar.scm.git.strategy.DefaultBlameStrategy.BlameAlgorithmEnum.GIT_NATIVE_BLAME; | |||
@RunWith(DataProviderRunner.class) | |||
public class CompositeBlameCommandTest { | |||
private static final String DUMMY_JAVA = "src/main/java/org/dummy/Dummy.java"; | |||
private final PathResolver pathResolver = new PathResolver(); | |||
private final ProcessWrapperFactory processWrapperFactory = new ProcessWrapperFactory(); | |||
private final JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(); | |||
private final GitBlameCommand gitBlameCommand = new GitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
private final NativeGitBlameCommand nativeGitBlameCommand = new NativeGitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); | |||
private final CompositeBlameCommand blameCommand = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, gitBlameCommand); | |||
private final PathResolver pathResolver = new PathResolver(); | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@@ -74,10 +88,15 @@ public class CompositeBlameCommandTest { | |||
public LogTester logTester = new LogTester(); | |||
private final BlameCommand.BlameInput input = mock(BlameCommand.BlameInput.class); | |||
@DataProvider | |||
public static List<Object> blameAlgorithms() { | |||
return Arrays.stream(BlameAlgorithmEnum.values()).collect(Collectors.toList()); | |||
} | |||
@Test | |||
public void use_jgit_if_native_git_disabled() throws IOException { | |||
GitBlameCommand gitCmd = new GitBlameCommand("invalidcommandnotfound", System2.INSTANCE, processWrapperFactory); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, gitCmd); | |||
NativeGitBlameCommand gitCmd = new NativeGitBlameCommand("invalidcommandnotfound", System2.INSTANCE, processWrapperFactory); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, gitCmd, (p, f) -> GIT_NATIVE_BLAME); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
@@ -85,14 +104,33 @@ public class CompositeBlameCommandTest { | |||
setUpBlameInputWithFile(baseDir.toPath()); | |||
TestBlameOutput output = new TestBlameOutput(); | |||
blameCmd.blame(input, output); | |||
assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Using GIT_NATIVE_BLAME strategy to blame files"); | |||
assertThat(output.blame).hasSize(1); | |||
assertThat(output.blame.get(input.filesToBlame().iterator().next())).hasSize(29); | |||
} | |||
@Test | |||
public void blame_shouldCallStrategyWithCorrectSpecifications() throws IOException { | |||
BlameStrategy strategyMock = mock(BlameStrategy.class); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, nativeGitBlameCommand, strategyMock); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
File baseDir = new File(projectDir, "dummy-git"); | |||
setUpBlameInputWithFile(baseDir.toPath()); | |||
TestBlameOutput output = new TestBlameOutput(); | |||
blameCmd.blame(input, output); | |||
verify(strategyMock).getBlameAlgorithm(intThat((i) -> i > 0), intThat(i -> i == 1)); | |||
} | |||
@Test | |||
public void fallback_to_jgit_if_native_git_fails() throws Exception { | |||
GitBlameCommand gitCmd = mock(GitBlameCommand.class); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, gitCmd); | |||
NativeGitBlameCommand gitCmd = mock(NativeGitBlameCommand.class); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, gitCmd, (p, f) -> GIT_NATIVE_BLAME); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
@@ -102,6 +140,8 @@ public class CompositeBlameCommandTest { | |||
setUpBlameInputWithFile(baseDir.toPath()); | |||
TestBlameOutput output = new TestBlameOutput(); | |||
blameCmd.blame(input, output); | |||
assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Using GIT_NATIVE_BLAME strategy to blame files"); | |||
assertThat(output.blame).hasSize(1); | |||
assertThat(output.blame.get(input.filesToBlame().iterator().next())).hasSize(29); | |||
@@ -111,12 +151,15 @@ public class CompositeBlameCommandTest { | |||
} | |||
@Test | |||
public void skip_files_not_committed() throws Exception { | |||
@UseDataProvider("blameAlgorithms") | |||
public void skip_files_not_committed(BlameAlgorithmEnum strategy) throws Exception { | |||
// skip if git not installed | |||
assumeTrue(gitBlameCommand.checkIfEnabled()); | |||
if (strategy == GIT_NATIVE_BLAME) { | |||
assumeTrue(nativeGitBlameCommand.checkIfEnabled()); | |||
} | |||
JGitBlameCommand jgit = mock(JGitBlameCommand.class); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jgit, gitBlameCommand); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jgit, nativeGitBlameCommand, (p, f) -> strategy); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
@@ -126,18 +169,16 @@ public class CompositeBlameCommandTest { | |||
blameCmd.blame(input, output); | |||
assertThat(output.blame).hasSize(1); | |||
assertThat(output.blame.get(input.filesToBlame().iterator().next())).hasSize(29); | |||
// never had to fall back to jgit | |||
verifyNoInteractions(jgit); | |||
} | |||
@Test | |||
public void skip_files_when_head_commit_is_missing() throws IOException { | |||
@UseDataProvider("blameAlgorithms") | |||
public void skip_files_when_head_commit_is_missing(BlameAlgorithmEnum strategy) throws IOException { | |||
// skip if git not installed | |||
assumeTrue(gitBlameCommand.checkIfEnabled()); | |||
assumeTrue(nativeGitBlameCommand.checkIfEnabled()); | |||
JGitBlameCommand jgit = mock(JGitBlameCommand.class); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jgit, gitBlameCommand); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jgit, nativeGitBlameCommand, (p, f) -> strategy); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("no-head-git.zip", projectDir); | |||
@@ -149,31 +190,15 @@ public class CompositeBlameCommandTest { | |||
assertThat(output.blame).isEmpty(); | |||
verifyNoInteractions(jgit); | |||
assertThat(logTester.logs()) | |||
assertThat(logTester.logs(LoggerLevel.WARN)) | |||
.contains("Could not find HEAD commit"); | |||
} | |||
@Test | |||
public void use_native_git_by_default() throws IOException { | |||
// skip test if git is not installed | |||
assumeTrue(gitBlameCommand.checkIfEnabled()); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
File baseDir = new File(projectDir, "dummy-git"); | |||
@UseDataProvider("blameAlgorithms") | |||
public void return_early_when_shallow_clone_detected(BlameAlgorithmEnum strategy) throws IOException { | |||
CompositeBlameCommand blameCommand = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, nativeGitBlameCommand, (p, f) -> strategy); | |||
JGitBlameCommand jgit = mock(JGitBlameCommand.class); | |||
BlameCommand blameCmd = new CompositeBlameCommand(analysisWarnings, pathResolver, jgit, gitBlameCommand); | |||
setUpBlameInputWithFile(baseDir.toPath()); | |||
TestBlameOutput output = new TestBlameOutput(); | |||
blameCmd.blame(input, output); | |||
assertThat(output.blame).hasSize(1); | |||
assertThat(output.blame.get(input.filesToBlame().iterator().next())).hasSize(29); | |||
verifyNoInteractions(jgit); | |||
} | |||
@Test | |||
public void return_early_when_shallow_clone_detected() throws IOException { | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("shallow-git.zip", projectDir); | |||
@@ -189,6 +214,8 @@ public class CompositeBlameCommandTest { | |||
@Test | |||
public void fail_if_not_git_project() throws IOException { | |||
CompositeBlameCommand blameCommand = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, nativeGitBlameCommand, (p, f) -> GIT_FILES_BLAME); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
@@ -207,7 +234,10 @@ public class CompositeBlameCommandTest { | |||
} | |||
@Test | |||
public void dont_fail_with_symlink() throws IOException { | |||
@UseDataProvider("blameAlgorithms") | |||
public void dont_fail_with_symlink(BlameAlgorithmEnum strategy) throws IOException { | |||
CompositeBlameCommand blameCommand = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, nativeGitBlameCommand, (p, f) -> strategy); | |||
assumeTrue(!System2.INSTANCE.isOsWindows()); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
@@ -229,10 +259,13 @@ public class CompositeBlameCommandTest { | |||
when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile, inputFile2)); | |||
TestBlameOutput output = new TestBlameOutput(); | |||
blameCommand.blame(input, output); | |||
assertThat(output.blame).isNotEmpty(); | |||
} | |||
@Test | |||
public void return_early_when_clone_with_reference_detected() throws IOException { | |||
CompositeBlameCommand blameCommand = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, nativeGitBlameCommand, (p, f) -> GIT_FILES_BLAME); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git-reference-clone.zip", projectDir); | |||
@@ -248,8 +281,9 @@ public class CompositeBlameCommandTest { | |||
TestBlameOutput output = new TestBlameOutput(); | |||
blameCommand.blame(input, output); | |||
assertThat(logTester.logs()).first() | |||
.matches(s -> s.contains("This git repository references another local repository which is not well supported")); | |||
assertThat(logTester.logs()) | |||
.haveAtLeastOne(new Condition<>(s-> s.startsWith("This git repository references another local repository which is not well supported"), | |||
"log for reference detected")); | |||
// contains commits referenced from the old clone and commits in the new clone | |||
assertThat(output.blame).containsKey(inputFile); | |||
@@ -259,7 +293,9 @@ public class CompositeBlameCommandTest { | |||
} | |||
@Test | |||
public void blame_on_nested_module() throws IOException { | |||
@UseDataProvider("blameAlgorithms") | |||
public void blame_on_nested_module(BlameAlgorithmEnum strategy) throws IOException { | |||
CompositeBlameCommand blameCommand = new CompositeBlameCommand(analysisWarnings, pathResolver, jGitBlameCommand, nativeGitBlameCommand, (p, f) -> strategy); | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git-nested.zip", projectDir); | |||
File baseDir = new File(projectDir, "dummy-git-nested/dummy-project"); | |||
@@ -271,40 +307,16 @@ public class CompositeBlameCommandTest { | |||
fs.add(inputFile); | |||
BlameCommand.BlameOutput blameResult = mock(BlameCommand.BlameOutput.class); | |||
when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile)); | |||
when(input.filesToBlame()).thenReturn(List.of(inputFile)); | |||
blameCommand.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))); | |||
IntStream.range(0, 26) | |||
.mapToObj(i -> new BlameLine().revision(revision).date(revisionDate).author(author)) | |||
.collect(Collectors.toList())); | |||
} | |||
private BlameCommand.BlameInput setUpBlameInputWithFile(Path baseDir) { | |||
@@ -317,7 +329,7 @@ public class CompositeBlameCommandTest { | |||
} | |||
private File createNewTempFolder() throws IOException { | |||
// This is needed for Windows, otherwise the created File point to invalid (shortened by Windows) temp folder path | |||
// This is needed for Windows, otherwise the created File points to invalid (shortened by Windows) temp folder path | |||
return temp.newFolder().toPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toFile(); | |||
} | |||
@@ -54,6 +54,7 @@ import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.config.Configuration; | |||
import org.sonar.api.notifications.AnalysisWarnings; | |||
import org.sonar.api.scan.filesystem.PathResolver; | |||
import org.sonar.api.utils.MessageException; | |||
@@ -61,6 +62,7 @@ import org.sonar.api.utils.System2; | |||
import org.sonar.api.utils.log.LogAndArguments; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.core.documentation.DocumentationLinkGenerator; | |||
import org.sonar.scm.git.strategy.DefaultBlameStrategy; | |||
import static java.lang.String.format; | |||
import static java.util.Collections.emptySet; | |||
@@ -145,8 +147,8 @@ public class GitScmProviderTest { | |||
@Test | |||
public void returnImplem() { | |||
JGitBlameCommand jblameCommand = new JGitBlameCommand(); | |||
GitBlameCommand nativeBlameCommand = new GitBlameCommand(System2.INSTANCE, new ProcessWrapperFactory()); | |||
CompositeBlameCommand compositeBlameCommand = new CompositeBlameCommand(analysisWarnings, new PathResolver(), jblameCommand, nativeBlameCommand); | |||
NativeGitBlameCommand nativeBlameCommand = new NativeGitBlameCommand(System2.INSTANCE, new ProcessWrapperFactory()); | |||
CompositeBlameCommand compositeBlameCommand = new CompositeBlameCommand(analysisWarnings, new PathResolver(), jblameCommand, nativeBlameCommand, new DefaultBlameStrategy(mock(Configuration.class))); | |||
GitScmProvider gitScmProvider = new GitScmProvider(compositeBlameCommand, analysisWarnings, gitIgnoreCommand, system2, documentationLinkGenerator); | |||
assertThat(gitScmProvider.blameCommand()).isEqualTo(compositeBlameCommand); |
@@ -40,5 +40,29 @@ public class GitUtils { | |||
Files.write(newFile, content.getBytes(), StandardOpenOption.CREATE); | |||
} | |||
public static void createFile(Path worktree, String relativePath, String... lines) throws IOException { | |||
Path newFile = worktree.resolve(relativePath); | |||
Files.createDirectories(newFile.getParent()); | |||
String content = String.join(System.lineSeparator(), lines) + System.lineSeparator(); | |||
Files.write(newFile, content.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); | |||
} | |||
public static void deleteFile(Path worktree, String relativePath) throws IOException { | |||
Path fileToDelete = worktree.resolve(relativePath); | |||
Files.delete(fileToDelete); | |||
} | |||
public static void copyFile(Path worktree, String origin, String dest) throws IOException { | |||
Path originPath = worktree.resolve(origin); | |||
Path destPath = worktree.resolve(dest); | |||
Files.copy(originPath, destPath); | |||
} | |||
public static void moveFile(Path worktree, String origin, String dest) throws IOException { | |||
Path originPath = worktree.resolve(origin); | |||
Path destPath = worktree.resolve(dest); | |||
Files.move(originPath, destPath); | |||
} | |||
} |
@@ -30,6 +30,7 @@ import java.util.List; | |||
import org.apache.commons.io.FileUtils; | |||
import org.eclipse.jgit.api.Git; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
@@ -53,15 +54,20 @@ public class JGitBlameCommandTest { | |||
public LogTester logTester = new LogTester(); | |||
private final JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(); | |||
private Path baseDir; | |||
@Test | |||
public void blame_returns_all_lines() throws IOException { | |||
@Before | |||
public void before() throws IOException { | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
baseDir = projectDir.toPath().resolve("dummy-git"); | |||
} | |||
@Test | |||
public void blame_returns_all_lines() { | |||
File baseDir = new File(projectDir, "dummy-git"); | |||
try (Git git = loadRepository(baseDir.toPath())) { | |||
try (Git git = loadRepository(baseDir)) { | |||
List<BlameLine> blameLines = jGitBlameCommand.blame(git, DUMMY_JAVA); | |||
Date revisionDate1 = DateUtils.parseDateTime("2012-07-17T16:12:48+0200"); | |||
@@ -90,10 +96,6 @@ public class JGitBlameCommandTest { | |||
@Test | |||
public void modified_file_returns_no_blame() throws IOException { | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
Path baseDir = projectDir.toPath().resolve("dummy-git"); | |||
// Emulate a modification | |||
Files.write(baseDir.resolve(DUMMY_JAVA), "modification and \n some new line".getBytes()); | |||
@@ -105,16 +107,12 @@ public class JGitBlameCommandTest { | |||
@Test | |||
public void new_file_returns_no_blame() throws IOException { | |||
File projectDir = createNewTempFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
File baseDir = new File(projectDir, "dummy-git"); | |||
String relativePath2 = "src/main/java/org/dummy/Dummy2.java"; | |||
// Emulate a new file | |||
FileUtils.copyFile(new File(baseDir, DUMMY_JAVA), new File(baseDir, relativePath2)); | |||
FileUtils.copyFile(new File(baseDir.toFile(), DUMMY_JAVA), new File(baseDir.toFile(), relativePath2)); | |||
try (Git git = loadRepository(baseDir.toPath())) { | |||
try (Git git = loadRepository(baseDir)) { | |||
assertThat(jGitBlameCommand.blame(git, DUMMY_JAVA)).hasSize(29); | |||
assertThat(jGitBlameCommand.blame(git, relativePath2)).isEmpty(); | |||
} | |||
@@ -123,10 +121,6 @@ public class JGitBlameCommandTest { | |||
@Test | |||
public void symlink_doesnt_fail() throws IOException { | |||
assumeTrue(!System2.INSTANCE.isOsWindows()); | |||
File projectDir = temp.newFolder(); | |||
javaUnzip("dummy-git.zip", projectDir); | |||
Path baseDir = projectDir.toPath().resolve("dummy-git"); | |||
String relativePath2 = "src/main/java/org/dummy/Dummy2.java"; | |||
// Create symlink |
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.scm.git; | |||
import com.tngtech.java.junit.dataprovider.DataProviderRunner; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.file.Files; | |||
@@ -35,6 +36,7 @@ import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.junit.runner.RunWith; | |||
import org.sonar.api.batch.scm.BlameLine; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonar.api.utils.System2; | |||
@@ -51,15 +53,16 @@ import static org.mockito.ArgumentMatchers.isNull; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.scm.git.GitBlameCommand.BLAME_COMMAND; | |||
import static org.sonar.scm.git.GitBlameCommand.GIT_DIR_ARGUMENT; | |||
import static org.sonar.scm.git.GitBlameCommand.GIT_DIR_FLAG; | |||
import static org.sonar.scm.git.GitBlameCommand.GIT_DIR_FORCE_FLAG; | |||
import static org.sonar.scm.git.NativeGitBlameCommand.BLAME_COMMAND; | |||
import static org.sonar.scm.git.NativeGitBlameCommand.GIT_DIR_ARGUMENT; | |||
import static org.sonar.scm.git.NativeGitBlameCommand.GIT_DIR_FLAG; | |||
import static org.sonar.scm.git.NativeGitBlameCommand.GIT_DIR_FORCE_FLAG; | |||
import static org.sonar.scm.git.GitUtils.createFile; | |||
import static org.sonar.scm.git.GitUtils.createRepository; | |||
import static org.sonar.scm.git.Utils.javaUnzip; | |||
public class GitBlameCommandTest { | |||
@RunWith(DataProviderRunner.class) | |||
public class NativeGitBlameCommandTest { | |||
private static final String DUMMY_JAVA = "src/main/java/org/dummy/Dummy.java"; | |||
@Rule | |||
@@ -67,7 +70,7 @@ public class GitBlameCommandTest { | |||
@Rule | |||
public LogTester logTester = new LogTester(); | |||
private final ProcessWrapperFactory processWrapperFactory = new ProcessWrapperFactory(); | |||
private final GitBlameCommand blameCommand = new GitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
private final NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
@Before | |||
public void skipTestsIfNoGitFound() { | |||
@@ -161,7 +164,7 @@ public class GitBlameCommandTest { | |||
anyString(), anyString(), anyString(), anyString(), anyString(), anyString())) | |||
.then(invocation -> mockProcess); | |||
GitBlameCommand blameCommand = new GitBlameCommand(gitCommand, System2.INSTANCE, mockFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(gitCommand, System2.INSTANCE, mockFactory); | |||
blameCommand.blame(baseDir.toPath(), DUMMY_JAVA); | |||
verify(mockFactory).create(any(), any(), eq(gitCommand), | |||
@@ -204,27 +207,27 @@ public class GitBlameCommandTest { | |||
@Test | |||
public void git_should_be_detected() { | |||
GitBlameCommand blameCommand = new GitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
assertThat(blameCommand.checkIfEnabled()).isTrue(); | |||
} | |||
@Test | |||
public void git_should_not_be_detected() { | |||
GitBlameCommand blameCommand = new GitBlameCommand("randomcmdthatwillneverbefound", System2.INSTANCE, processWrapperFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand("randomcmdthatwillneverbefound", System2.INSTANCE, processWrapperFactory); | |||
assertThat(blameCommand.checkIfEnabled()).isFalse(); | |||
} | |||
@Test | |||
public void git_should_not_be_enabled_if_version_command_is_not_found() { | |||
ProcessWrapperFactory mockedCmd = mockGitVersionCommand("error: unknown option `version'"); | |||
GitBlameCommand blameCommand = new GitBlameCommand(System2.INSTANCE, mockedCmd); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(System2.INSTANCE, mockedCmd); | |||
assertThat(blameCommand.checkIfEnabled()).isFalse(); | |||
} | |||
@Test | |||
public void git_should_not_be_enabled_if_version_command_does_not_return_string_output() { | |||
ProcessWrapperFactory mockedCmd = mockGitVersionCommand(null); | |||
GitBlameCommand blameCommand = new GitBlameCommand(System2.INSTANCE, mockedCmd); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(System2.INSTANCE, mockedCmd); | |||
assertThat(blameCommand.checkIfEnabled()).isFalse(); | |||
} | |||
@@ -244,7 +247,7 @@ public class GitBlameCommandTest { | |||
return mock(ProcessWrapper.class); | |||
}); | |||
GitBlameCommand blameCommand = new GitBlameCommand(System2.INSTANCE, mockedCmd); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(System2.INSTANCE, mockedCmd); | |||
assertThat(blameCommand.checkIfEnabled()).isTrue(); | |||
}); | |||
} | |||
@@ -252,14 +255,14 @@ public class GitBlameCommandTest { | |||
@Test | |||
public void git_should_not_be_enabled_if_version_is_less_than_required_minimum() { | |||
ProcessWrapperFactory mockFactory = mockGitVersionCommand("git version 1.9.0"); | |||
GitBlameCommand blameCommand = new GitBlameCommand(System2.INSTANCE, mockFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(System2.INSTANCE, mockFactory); | |||
assertThat(blameCommand.checkIfEnabled()).isFalse(); | |||
} | |||
@Test | |||
public void throw_exception_if_command_fails() throws Exception { | |||
Path baseDir = temp.newFolder().toPath(); | |||
GitBlameCommand blameCommand = new GitBlameCommand("randomcmdthatwillneverbefound", System2.INSTANCE, processWrapperFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand("randomcmdthatwillneverbefound", System2.INSTANCE, processWrapperFactory); | |||
assertThatThrownBy(() -> blameCommand.blame(baseDir, "file")).isInstanceOf(IOException.class); | |||
} | |||
@@ -271,7 +274,7 @@ public class GitBlameCommandTest { | |||
createFile(filePath, "line", baseDir); | |||
commitWithNoEmail(git, filePath); | |||
GitBlameCommand blameCommand = new GitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
assertThat(blameCommand.checkIfEnabled()).isTrue(); | |||
List<BlameLine> blame = blameCommand.blame(baseDir, filePath); | |||
assertThat(blame).hasSize(1); | |||
@@ -289,7 +292,7 @@ public class GitBlameCommandTest { | |||
createFile(filePath, "line", baseDir); | |||
commit(git, filePath, "my DOT name AT server DOT com"); | |||
GitBlameCommand blameCommand = new GitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
assertThat(blameCommand.checkIfEnabled()).isTrue(); | |||
List<BlameLine> blame = blameCommand.blame(baseDir, filePath); | |||
assertThat(blame).hasSize(1); | |||
@@ -304,7 +307,7 @@ public class GitBlameCommandTest { | |||
createFile(filePath, "line", baseDir); | |||
commitWithNoEmail(git, filePath); | |||
GitBlameCommand blameCommand = new GitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(System2.INSTANCE, processWrapperFactory); | |||
assertThat(blameCommand.checkIfEnabled()).isTrue(); | |||
List<BlameLine> blame = blameCommand.blame(baseDir, filePath); | |||
assertThat(blame).hasSize(1); | |||
@@ -329,7 +332,7 @@ public class GitBlameCommandTest { | |||
return mockProcess; | |||
}); | |||
GitBlameCommand blameCommand = new GitBlameCommand(system2, mockFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(system2, mockFactory); | |||
assertThat(blameCommand.checkIfEnabled()).isTrue(); | |||
assertThat(logTester.logs()).contains("Found git.exe at C:\\mockGit.exe"); | |||
} | |||
@@ -343,7 +346,7 @@ public class GitBlameCommandTest { | |||
ProcessWrapperFactory mockFactory = mock(ProcessWrapperFactory.class); | |||
mockGitWhereOnWindows(mockFactory); | |||
GitBlameCommand blameCommand = new GitBlameCommand(system2, mockFactory); | |||
NativeGitBlameCommand blameCommand = new NativeGitBlameCommand(system2, mockFactory); | |||
assertThat(blameCommand.checkIfEnabled()).isFalse(); | |||
} | |||
@@ -0,0 +1,82 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 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.strategy; | |||
import java.util.Optional; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.api.config.Configuration; | |||
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.assertj.core.api.Assertions.assertThatThrownBy; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.scm.git.strategy.DefaultBlameStrategy.BlameAlgorithmEnum.GIT_FILES_BLAME; | |||
import static org.sonar.scm.git.strategy.DefaultBlameStrategy.BlameAlgorithmEnum.GIT_NATIVE_BLAME; | |||
public class DefaultBlameStrategyTest { | |||
private final Configuration configuration = mock(Configuration.class); | |||
private final BlameStrategy underTest = new DefaultBlameStrategy(configuration); | |||
@Rule | |||
public LogTester logTester = new LogTester(); | |||
@Test | |||
public void useRepositoryBlame_whenFileBlamePropsEnabled_shouldDisableRepoBlame() { | |||
when(configuration.get(DefaultBlameStrategy.PROP_SONAR_SCM_USE_BLAME_ALGORITHM)).thenReturn(Optional.of(GIT_FILES_BLAME.name())); | |||
assertThat(underTest.getBlameAlgorithm(1, 1)).isEqualTo(GIT_FILES_BLAME); | |||
} | |||
@Test | |||
public void useRepositoryBlame_whenFileBlamePropsDisableOrUnspecified_shouldEnableRepoBlame() { | |||
when(configuration.get(DefaultBlameStrategy.PROP_SONAR_SCM_USE_BLAME_ALGORITHM)).thenReturn(Optional.of(GIT_NATIVE_BLAME.name())); | |||
assertThat(underTest.getBlameAlgorithm(1, 10000)).isEqualTo(GIT_NATIVE_BLAME); | |||
} | |||
@Test | |||
public void useRepositoryBlame_whenFileBlamePropsInvalid_shouldThrowException() { | |||
when(configuration.get(DefaultBlameStrategy.PROP_SONAR_SCM_USE_BLAME_ALGORITHM)).thenReturn(Optional.of("unknown")); | |||
assertThatThrownBy(() -> underTest.getBlameAlgorithm(1, 1)).isInstanceOf(IllegalArgumentException.class); | |||
} | |||
@Test | |||
public void useRepositoryBlame_whenProcessorsCountAndFileSizeSpecified_shouldEnableRepoBlame() { | |||
when(configuration.getBoolean(DefaultBlameStrategy.PROP_SONAR_SCM_USE_BLAME_ALGORITHM)).thenReturn(Optional.empty()); | |||
assertThat(underTest.getBlameAlgorithm(1, 10000)).isEqualTo(GIT_FILES_BLAME); | |||
assertThat(underTest.getBlameAlgorithm(8, 10)).isEqualTo(GIT_NATIVE_BLAME); | |||
assertThat(underTest.getBlameAlgorithm(1, 10)).isEqualTo(GIT_NATIVE_BLAME); | |||
assertThat(underTest.getBlameAlgorithm(1, 11)).isEqualTo(GIT_FILES_BLAME); | |||
assertThat(underTest.getBlameAlgorithm(0, 10)).isEqualTo(GIT_NATIVE_BLAME); | |||
assertThat(logTester.logs(LoggerLevel.WARN)).contains("Available processors are 0. Falling back to native git blame"); | |||
} | |||
} |
@@ -0,0 +1 @@ | |||
37463a67cb907766a8fb2178fd4fc9a1b6f4f14f (<lukasz.jarocki@sonarsource.com> 2023-02-24T09:51:22+01:00 1) fifth-file - changed |
@@ -0,0 +1 @@ | |||
5c2578e72d07e95781682a3ce1be61a954e39df8 (<lukasz.jarocki@sonarsource.com> 2023-02-24T09:49:41+01:00 1) first-file |
@@ -0,0 +1 @@ | |||
a7015bf40760ca91ae7d73d9102958fc870b30b9 (<lukasz.jarocki@sonarsource.com> 2023-02-24T09:51:08+01:00 1) fourth-file - changed |
@@ -0,0 +1 @@ | |||
8bda33b1296875441f08c157140eb8504e13ebb2 (<lukasz.jarocki@sonarsource.com> 2023-02-24T09:49:59+01:00 1) second-file - changed |
@@ -0,0 +1 @@ | |||
442ca0c2c76fe6eee3246cae40fe36c077f4d4a0 (<lukasz.jarocki@sonarsource.com> 2023-02-24T09:50:13+01:00 1) third-file - changed |
@@ -0,0 +1,5 @@ | |||
d0da62499e8476f7d7599b37d2f2201770cf6790 (<lukasz.jarocki@sonarsource.com> 2023-03-01T10:31:46+01:00 1) first line | |||
bed9b1f0ae87ecda5ef1c805e303e2295676317b (<lukasz.jarocki@sonarsource.com> 2023-03-01T10:32:16+01:00 2) second line | |||
939282feaabde0d049e739fd3e30937836c6fc13 (<lukasz.jarocki@sonarsource.com> 2023-03-01T10:32:33+01:00 3) third line | |||
f4cfbd0b9fe74daea9dd588ec41e8da71505e959 (<lukasz.jarocki@sonarsource.com> 2023-03-01T10:32:46+01:00 4) fourth line | |||
5fa3e610adad109fdc89cdfa68592ddd7a0ba3c6 (<lukasz.jarocki@sonarsource.com> 2023-03-01T10:33:03+01:00 5) fifth line |
@@ -0,0 +1,6 @@ | |||
This folder contains expected blame data for corresponding repositories (stored in test-repos folder). The data in expected-blame folder is | |||
produced by blaming each file with | |||
git blame {file_name_from_the_repository} --date=iso-strict --show-email -l --root > expected-blame/repository-name/{file_name_from_the_repository} | |||
The expected blame data is then used in the corresponding unit and integration tests to assert correctness of the new git blame algorithm | |||
implemented in the scanner engine. |
@@ -0,0 +1,52 @@ | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 1) <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 2) <modelVersion>4.0.0</modelVersion> | |||
783905e3d8b3e6f89a63c573d5fcf60af7845ebe (<david@gageot.net> 2012-07-20T18:52:36+02:00 3) <groupId>dummy-git</groupId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 4) <artifactId>dummy</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 5) <version>1.0</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 6) <packaging>jar</packaging> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 7) <name>Dummy</name> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 8) | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 9) <properties> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 10) <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 11) </properties> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 12) | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 13) <build> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 14) <pluginManagement> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 15) <plugins> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 16) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 17) <artifactId>maven-clean-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 18) <version>2.4.1</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 19) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 20) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 21) <artifactId>maven-compiler-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 22) <version>2.3.2</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 23) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 24) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 25) <artifactId>maven-resources-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 26) <version>2.5</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 27) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 28) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 29) <artifactId>maven-source-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 30) <version>2.1.2</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 31) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 32) </plugins> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 33) </pluginManagement> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 34) <plugins> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 35) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 36) <artifactId>maven-compiler-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 37) <configuration> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 38) <source>1.5</source> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 39) <target>1.5</target> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 40) </configuration> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 41) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 42) </plugins> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 43) </build> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 44) | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 45) <dependencies> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 46) <dependency> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 47) <groupId>junit</groupId> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 48) <artifactId>junit</artifactId> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 49) <version>4.10</version> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 50) </dependency> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 51) </dependencies> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 52) </project> |
@@ -0,0 +1,29 @@ | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 1) /* | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 2) * Sonar, open source software quality management tool. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 3) * Copyright (C) 2008-2012 SonarSource | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 4) * mailto:contact AT sonarsource DOT com | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 5) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 6) * Sonar is free software; you can redistribute it and/or | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 7) * modify it under the terms of the GNU Lesser General Public | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 8) * License as published by the Free Software Foundation; either | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 9) * version 3 of the License, or (at your option) any later version. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 10) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 11) * Sonar is distributed in the hope that it will be useful, | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 12) * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 13) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 14) * Lesser General Public License for more details. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 15) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 16) * You should have received a copy of the GNU Lesser General Public | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 17) * License along with Sonar; if not, write to the Free Software | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 18) * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 19) */ | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 20) package org.dummy; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 21) | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 22) public class AnotherDummy { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 23) public String say(boolean hello) { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 24) if (hello) { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 25) return "Hello"; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 26) } | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 27) return new String("GoodBye"); | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 28) } | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 29) } |
@@ -0,0 +1,29 @@ | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 1) /* | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 2) * Sonar, open source software quality management tool. | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 3) * Copyright (C) 2008-2012 SonarSource | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 4) * mailto:contact AT sonarsource DOT com | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 5) * | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 6) * Sonar is free software; you can redistribute it and/or | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 7) * modify it under the terms of the GNU Lesser General Public | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 8) * License as published by the Free Software Foundation; either | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 9) * version 3 of the License, or (at your option) any later version. | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 10) * | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 11) * Sonar is distributed in the hope that it will be useful, | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 12) * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 13) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 14) * Lesser General Public License for more details. | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 15) * | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 16) * You should have received a copy of the GNU Lesser General Public | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 17) * License along with Sonar; if not, write to the Free Software | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 18) * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 19) */ | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 20) package org.dummy; | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 21) | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 22) public class Dummy { | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 23) public String sayHello() { | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 24) return "Hello"; | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 25) } | |||
7609f824d5ff7018bebf107cdbe4edcc901b574f (<duarte.meneses@sonarsource.com> 2022-10-11T14:14:26+02:00 26) public String sayBye() { | |||
7609f824d5ff7018bebf107cdbe4edcc901b574f (<duarte.meneses@sonarsource.com> 2022-10-11T14:14:26+02:00 27) return "Bye"; | |||
7609f824d5ff7018bebf107cdbe4edcc901b574f (<duarte.meneses@sonarsource.com> 2022-10-11T14:14:26+02:00 28) } | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 29) } |
@@ -0,0 +1,31 @@ | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 1) /* | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 2) * Sonar, open source software quality management tool. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 3) * Copyright (C) 2008-2012 SonarSource | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 4) * mailto:contact AT sonarsource DOT com | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 5) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 6) * Sonar is free software; you can redistribute it and/or | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 7) * modify it under the terms of the GNU Lesser General Public | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 8) * License as published by the Free Software Foundation; either | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 9) * version 3 of the License, or (at your option) any later version. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 10) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 11) * Sonar is distributed in the hope that it will be useful, | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 12) * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 13) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 14) * Lesser General Public License for more details. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 15) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 16) * You should have received a copy of the GNU Lesser General Public | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 17) * License along with Sonar; if not, write to the Free Software | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 18) * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 19) */ | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 20) package org.dummy; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 21) | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 22) import org.junit.Test; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 23) | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 24) import static org.junit.Assert.assertEquals; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 25) | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 26) public class AnotherDummyTest { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 27) @Test | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 28) public void should_say_hello() { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 29) assertEquals("Hello", new AnotherDummy().say(true)); | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 30) } | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 31) } |
@@ -0,0 +1,52 @@ | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 1) <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 2) <modelVersion>4.0.0</modelVersion> | |||
783905e3d8b3e6f89a63c573d5fcf60af7845ebe (<david@gageot.net> 2012-07-20T18:52:36+02:00 3) <groupId>dummy-git</groupId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 4) <artifactId>dummy</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 5) <version>1.0</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 6) <packaging>jar</packaging> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 7) <name>Dummy</name> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 8) | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 9) <properties> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 10) <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 11) </properties> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 12) | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 13) <build> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 14) <pluginManagement> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 15) <plugins> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 16) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 17) <artifactId>maven-clean-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 18) <version>2.4.1</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 19) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 20) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 21) <artifactId>maven-compiler-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 22) <version>2.3.2</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 23) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 24) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 25) <artifactId>maven-resources-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 26) <version>2.5</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 27) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 28) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 29) <artifactId>maven-source-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 30) <version>2.1.2</version> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 31) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 32) </plugins> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 33) </pluginManagement> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 34) <plugins> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 35) <plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 36) <artifactId>maven-compiler-plugin</artifactId> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 37) <configuration> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 38) <source>1.5</source> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 39) <target>1.5</target> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 40) </configuration> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 41) </plugin> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 42) </plugins> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 43) </build> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 44) | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 45) <dependencies> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 46) <dependency> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 47) <groupId>junit</groupId> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 48) <artifactId>junit</artifactId> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 49) <version>4.10</version> | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 50) </dependency> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 51) </dependencies> | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 52) </project> |
@@ -0,0 +1,29 @@ | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 1) /* | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 2) * Sonar, open source software quality management tool. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 3) * Copyright (C) 2008-2012 SonarSource | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 4) * mailto:contact AT sonarsource DOT com | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 5) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 6) * Sonar is free software; you can redistribute it and/or | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 7) * modify it under the terms of the GNU Lesser General Public | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 8) * License as published by the Free Software Foundation; either | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 9) * version 3 of the License, or (at your option) any later version. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 10) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 11) * Sonar is distributed in the hope that it will be useful, | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 12) * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 13) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 14) * Lesser General Public License for more details. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 15) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 16) * You should have received a copy of the GNU Lesser General Public | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 17) * License along with Sonar; if not, write to the Free Software | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 18) * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 19) */ | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 20) package org.dummy; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 21) | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 22) public class AnotherDummy { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 23) public String say(boolean hello) { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 24) if (hello) { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 25) return "Hello"; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 26) } | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 27) return new String("GoodBye"); | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 28) } | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 29) } |
@@ -0,0 +1,29 @@ | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 1) /* | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 2) * Sonar, open source software quality management tool. | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 3) * Copyright (C) 2008-2012 SonarSource | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 4) * mailto:contact AT sonarsource DOT com | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 5) * | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 6) * Sonar is free software; you can redistribute it and/or | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 7) * modify it under the terms of the GNU Lesser General Public | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 8) * License as published by the Free Software Foundation; either | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 9) * version 3 of the License, or (at your option) any later version. | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 10) * | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 11) * Sonar is distributed in the hope that it will be useful, | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 12) * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 13) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 14) * Lesser General Public License for more details. | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 15) * | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 16) * You should have received a copy of the GNU Lesser General Public | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 17) * License along with Sonar; if not, write to the Free Software | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 18) * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 19) */ | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 20) package org.dummy; | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 21) | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 22) public class Dummy { | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 23) public String sayHello() { | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 24) return "Hello"; | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 25) } | |||
0d269c1acfb8e6d4d33f3c43041eb87e0df0f5e7 (<duarte.meneses@sonarsource.com> 2015-05-19T13:31:09+02:00 26) public String sayBye() { | |||
0d269c1acfb8e6d4d33f3c43041eb87e0df0f5e7 (<duarte.meneses@sonarsource.com> 2015-05-19T13:31:09+02:00 27) return "Bye"; | |||
0d269c1acfb8e6d4d33f3c43041eb87e0df0f5e7 (<duarte.meneses@sonarsource.com> 2015-05-19T13:31:09+02:00 28) } | |||
6b3aab35a3ea32c1636fee56f996e677653c48ea (<david@gageot.net> 2012-07-17T16:12:48+02:00 29) } |
@@ -0,0 +1,31 @@ | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 1) /* | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 2) * Sonar, open source software quality management tool. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 3) * Copyright (C) 2008-2012 SonarSource | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 4) * mailto:contact AT sonarsource DOT com | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 5) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 6) * Sonar is free software; you can redistribute it and/or | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 7) * modify it under the terms of the GNU Lesser General Public | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 8) * License as published by the Free Software Foundation; either | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 9) * version 3 of the License, or (at your option) any later version. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 10) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 11) * Sonar is distributed in the hope that it will be useful, | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 12) * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 13) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 14) * Lesser General Public License for more details. | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 15) * | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 16) * You should have received a copy of the GNU Lesser General Public | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 17) * License along with Sonar; if not, write to the Free Software | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 18) * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 19) */ | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 20) package org.dummy; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 21) | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 22) import org.junit.Test; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 23) | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 24) import static org.junit.Assert.assertEquals; | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 25) | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 26) public class AnotherDummyTest { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 27) @Test | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 28) public void should_say_hello() { | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 29) assertEquals("Hello", new AnotherDummy().say(true)); | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 30) } | |||
ffb55a516440d069145dd9199e4ac67976b2338b (<david@gageot.net> 2012-07-20T15:26:23+02:00 31) } |
@@ -0,0 +1,3 @@ | |||
2b6ccf6d99dea2840613563aa54fdc5c8efc05cb (<lukasz.jarocki@sonarsource.com> 2023-03-01T14:43:49+01:00 1) first commit | |||
5a729e03b90e11d3d4f582a63cd2df93a7db41e0 (<lukasz.jarocki@sonarsource.com> 2023-03-01T14:44:47+01:00 2) second commit on master | |||
e62f234121479b8aa3bf60481324784c6ed6ba40 (<lukasz.jarocki@sonarsource.com> 2023-03-01T14:45:59+01:00 3) commit on branch |
@@ -0,0 +1,10 @@ | |||
691242bf1b8319a7398be257c206203f7d6b8743 file1.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:28:35+01:00 1) 111 - changed | |||
cb5f118c653af2e6d4d30f798714f45f5338c11c file.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:29:03+01:00 2) 222 - changed | |||
84dd29d9b45487fecae9d952f1e71c5dea4cd356 file.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:26:31+01:00 3) 333 | |||
ea53e21a12061a733434454cf6cb8c6c29094fd0 file1.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:33:06+01:00 4) 444 - changed in master | |||
84dd29d9b45487fecae9d952f1e71c5dea4cd356 file.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:26:31+01:00 5) 555 | |||
223e2f4e80d72199dae13b94690099dccc20eeea file1.js (<lukasz.jarocki@sonarsource.com> 2023-03-01T10:52:44+01:00 6) 666 - changed on branch1 | |||
84dd29d9b45487fecae9d952f1e71c5dea4cd356 file.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:26:31+01:00 7) 777 | |||
84dd29d9b45487fecae9d952f1e71c5dea4cd356 file.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:26:31+01:00 8) 888 | |||
84dd29d9b45487fecae9d952f1e71c5dea4cd356 file.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:26:31+01:00 9) 999 | |||
c700a4b511aeecb225b4bd86dbe964f29730cffd file.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:27:59+01:00 10) 100 - changed |
@@ -0,0 +1 @@ | |||
23628a3b878ae8dd5e51042c7a39057a3e4ce45c (<lukasz.jarocki@sonarsource.com> 2023-02-23T14:29:45+01:00 1) hello world |
@@ -0,0 +1,5 @@ | |||
13bc62d2a546d41696e25e26ae44b3671f5ddf89 c.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:16:08+01:00 1) initial-change | |||
13bc62d2a546d41696e25e26ae44b3671f5ddf89 c.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:16:08+01:00 2) second-change | |||
13bc62d2a546d41696e25e26ae44b3671f5ddf89 c.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:16:08+01:00 3) third-change | |||
3561639d487376f1d8c0297838fd2a94c5563d61 d.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:16:39+01:00 4) fourth-change | |||
5123767d66854a431e9f6a05a88cf7db6e73aabc e.js (<lukasz.jarocki@sonarsource.com> 2023-02-24T10:17:15+01:00 5) fifth-change |
@@ -0,0 +1,2 @@ | |||
51c8ead53b248e93e66e5038c826524c0730f271 (<lukasz.jarocki@sonarsource.com> 2023-02-23T14:39:44+01:00 1) first line first commit | |||
b497efbe6a7ff547dee5dcc368b8920dba0fd908 (<lukasz.jarocki@sonarsource.com> 2023-02-23T14:40:18+01:00 2) second line second commit |
@@ -0,0 +1 @@ | |||
5ee03833d69a38d440a8685eb33dd96b0bea24e6 first.js (<lukasz.jarocki@sonarsource.com> 2023-03-02T11:47:42+01:00 1) commit on master, file: first.js |
@@ -0,0 +1 @@ | |||
dab8b2a7c229b1111fbfe7c7dda7f9913ebb71f9 (<lukasz.jarocki@sonarsource.com> 2023-03-02T11:46:47+01:00 1) second commit, file: second.js |
@@ -0,0 +1 @@ | |||
a66e868e71a5e871ca9d9d74035adc0a743ac537 (<lukasz.jarocki@sonarsource.com> 2023-02-23T16:45:23+01:00 1) first file |
@@ -0,0 +1 @@ | |||
a66e868e71a5e871ca9d9d74035adc0a743ac537 (<lukasz.jarocki@sonarsource.com> 2023-02-23T16:45:23+01:00 1) second file |
@@ -0,0 +1,4 @@ | |||
48559ab7c378fb7181dc515967bd0dcfc71a8a57 (<lukasz.jarocki@sonarsource.com> 2023-03-01T14:54:30+01:00 1) first commit | |||
b5767c89ce517009ede7a6c7e69f46f4d2270fb5 (<lukasz.jarocki@sonarsource.com> 2023-03-01T14:56:53+01:00 2) fifth commit on master | |||
69cf0175d31c5a2c1517ac2d3515864324c436e9 (<lukasz.jarocki@sonarsource.com> 2023-03-01T14:55:02+01:00 3) second commit on branch | |||
c65549591670241841ae5882453ea05e51e3df20 (<lukasz.jarocki@sonarsource.com> 2023-03-01T14:56:33+01:00 4) fourth commit on branch |