diff options
19 files changed, 337 insertions, 214 deletions
diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitBlameCommand.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitBlameCommand.java index 6cd5145457f..0e7c05f6b05 100644 --- a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitBlameCommand.java +++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitBlameCommand.java @@ -21,8 +21,6 @@ package org.sonar.plugins.scm.git; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.BatchComponent; -import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.scm.BlameCommand; @@ -35,8 +33,7 @@ import org.sonar.api.utils.command.StringStreamConsumer; import java.io.File; import java.util.List; -@InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public class GitBlameCommand implements BlameCommand, BatchComponent { +public class GitBlameCommand extends BlameCommand { private static final Logger LOG = LoggerFactory.getLogger(GitBlameCommand.class); private final CommandExecutor commandExecutor; @@ -50,9 +47,10 @@ public class GitBlameCommand implements BlameCommand, BatchComponent { } @Override - public void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result) { + public void blame(BlameInput input, BlameOutput output) { + FileSystem fs = input.fileSystem(); LOG.debug("Working directory: " + fs.baseDir().getAbsolutePath()); - for (InputFile inputFile : files) { + for (InputFile inputFile : input.filesToBlame()) { String filename = inputFile.relativePath(); Command cl = createCommandLine(fs.baseDir(), filename); GitBlameConsumer consumer = new GitBlameConsumer(filename); @@ -67,7 +65,7 @@ public class GitBlameCommand implements BlameCommand, BatchComponent { // SONARPLUGINS-3097 Git do not report blame on last empty line lines.add(lines.get(lines.size() - 1)); } - result.add(inputFile, lines); + output.blameResult(inputFile, lines); } } diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitBlameConsumer.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitBlameConsumer.java index 350d3dd956f..c8e4eec4f50 100644 --- a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitBlameConsumer.java +++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/GitBlameConsumer.java @@ -35,7 +35,6 @@ public class GitBlameConsumer implements StreamConsumer { private static final String GIT_COMMITTER_PREFIX = "committer"; private static final String GIT_COMMITTER_TIME = GIT_COMMITTER_PREFIX + "-time "; private static final String GIT_AUTHOR_EMAIL = "author-mail "; - private static final String GIT_COMMITTER_EMAIL = GIT_COMMITTER_PREFIX + "-mail "; private static final String OPENING_EMAIL_FIELD = "<"; private static final String CLOSING_EMAIL_FIELD = ">"; @@ -55,7 +54,6 @@ public class GitBlameConsumer implements StreamConsumer { private String revision = null; private String author = null; - private String committer = null; private Date time = null; private final String filename; @@ -93,11 +91,6 @@ public class GitBlameConsumer implements StreamConsumer { return true; } - if (line.startsWith(GIT_COMMITTER_EMAIL)) { - committer = extractEmail(line); - return true; - } - if (line.startsWith(GIT_COMMITTER_TIME)) { String timeStr = line.substring(GIT_COMMITTER_TIME.length()); time = new Date(Long.parseLong(timeStr) * 1000L); @@ -118,7 +111,7 @@ public class GitBlameConsumer implements StreamConsumer { } private void consumeContentLine() { - BlameLine blameLine = new BlameLine(time, revision, author, committer); + BlameLine blameLine = new BlameLine().date(time).revision(revision).author(author); getLines().add(blameLine); // keep commitinfo for this sha-1 @@ -142,7 +135,6 @@ public class GitBlameConsumer implements StreamConsumer { if (oldLine != null) { // restore the commit info author = oldLine.author(); - committer = oldLine.committer(); time = oldLine.date(); } diff --git a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/JGitBlameCommand.java b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/JGitBlameCommand.java index cfc044ad467..9f69f924fdb 100644 --- a/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/JGitBlameCommand.java +++ b/plugins/sonar-git-plugin/src/main/java/org/sonar/plugins/scm/git/JGitBlameCommand.java @@ -26,9 +26,6 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.BatchComponent; -import org.sonar.api.batch.InstantiationStrategy; -import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.scm.BlameCommand; import org.sonar.api.batch.scm.BlameLine; @@ -39,8 +36,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -@InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public class JGitBlameCommand implements BlameCommand, BatchComponent { +public class JGitBlameCommand extends BlameCommand { private static final Logger LOG = LoggerFactory.getLogger(JGitBlameCommand.class); @@ -51,9 +47,9 @@ public class JGitBlameCommand implements BlameCommand, BatchComponent { } @Override - public void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result) { + public void blame(BlameInput input, BlameOutput output) { Git git = null; - File basedir = fs.baseDir(); + File basedir = input.fileSystem().baseDir(); try { Repository repo = new RepositoryBuilder() .findGitDir(basedir) @@ -61,8 +57,8 @@ public class JGitBlameCommand implements BlameCommand, BatchComponent { .build(); git = Git.wrap(repo); File gitBaseDir = repo.getWorkTree(); - for (InputFile inputFile : files) { - blame(result, git, gitBaseDir, inputFile); + for (InputFile inputFile : input.filesToBlame()) { + blame(output, git, gitBaseDir, inputFile); } } catch (IOException e) { throw new IllegalStateException("Unable to open Git repository", e); @@ -75,7 +71,7 @@ public class JGitBlameCommand implements BlameCommand, BatchComponent { } } - private void blame(BlameResult result, Git git, File gitBaseDir, InputFile inputFile) throws GitAPIException { + private void blame(BlameOutput output, Git git, File gitBaseDir, InputFile inputFile) throws GitAPIException { String filename = pathResolver.relativePath(gitBaseDir, inputFile.file()); org.eclipse.jgit.blame.BlameResult blameResult = git.blame() // Equivalent to -w command line option @@ -83,22 +79,19 @@ public class JGitBlameCommand implements BlameCommand, BatchComponent { .setFilePath(filename).call(); List<BlameLine> lines = new ArrayList<BlameLine>(); for (int i = 0; i < blameResult.getResultContents().size(); i++) { - if (blameResult.getSourceAuthor(i) == null || blameResult.getSourceCommit(i) == null || blameResult.getSourceCommitter(i) == null) { + if (blameResult.getSourceAuthor(i) == null || blameResult.getSourceCommit(i) == null) { LOG.info("Author: " + blameResult.getSourceAuthor(i)); - LOG.info("Committer: " + blameResult.getSourceCommitter(i)); LOG.info("Source commit: " + blameResult.getSourceCommit(i)); throw new IllegalStateException("Unable to blame file " + inputFile.relativePath() + ". No blame info at line " + (i + 1) + ". Is file commited?"); } - lines.add(new org.sonar.api.batch.scm.BlameLine(blameResult.getSourceAuthor(i).getWhen(), - blameResult.getSourceCommit(i).getName(), - blameResult.getSourceAuthor(i).getEmailAddress(), - blameResult.getSourceCommitter(i).getEmailAddress())); + lines.add(new org.sonar.api.batch.scm.BlameLine().date(blameResult.getSourceAuthor(i).getWhen()).revision(blameResult.getSourceCommit(i).getName()) + .author(blameResult.getSourceAuthor(i).getEmailAddress())); } if (lines.size() == inputFile.lines() - 1) { // SONARPLUGINS-3097 Git do not report blame on last empty line lines.add(lines.get(lines.size() - 1)); } - result.add(inputFile, lines); + output.blameResult(inputFile, lines); } } diff --git a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitBlameCommandTest.java b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitBlameCommandTest.java index 574f68dc4da..f5fff20d9da 100644 --- a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitBlameCommandTest.java +++ b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/GitBlameCommandTest.java @@ -30,7 +30,8 @@ import org.mockito.stubbing.Answer; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.scm.BlameCommand.BlameResult; +import org.sonar.api.batch.scm.BlameCommand.BlameInput; +import org.sonar.api.batch.scm.BlameCommand.BlameOutput; import org.sonar.api.batch.scm.BlameLine; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.command.Command; @@ -57,12 +58,15 @@ public class GitBlameCommandTest { private DefaultFileSystem fs; private File baseDir; + private BlameInput input; @Before public void prepare() throws IOException { baseDir = temp.newFolder(); fs = new DefaultFileSystem(); fs.setBaseDir(baseDir); + input = mock(BlameInput.class); + when(input.fileSystem()).thenReturn(fs); } @Test @@ -72,7 +76,7 @@ public class GitBlameCommandTest { DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()); fs.add(inputFile); - BlameResult result = mock(BlameResult.class); + BlameOutput result = mock(BlameOutput.class); CommandExecutor commandExecutor = mock(CommandExecutor.class); when(commandExecutor.execute(any(Command.class), any(StreamConsumer.class), any(StreamConsumer.class), anyLong())).thenAnswer(new Answer<Integer>() { @@ -98,11 +102,13 @@ public class GitBlameCommandTest { return 0; } }); - - new GitBlameCommand(commandExecutor).blame(fs, Arrays.<InputFile>asList(inputFile), result); - verify(result).add(inputFile, - Arrays.asList(new BlameLine(DateUtils.parseDateTime("2011-08-05T10:49:31+0200"), "2c68c473da7fc293e12ca50f19380c5118be7ead", "simon.brandhof@gmail.com"), - new BlameLine(DateUtils.parseDateTime("2011-08-05T10:49:31+0200"), "2c68c473da7fc293e12ca50f19380c5118be7ead", "simon.brandhof@gmail.com"))); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + new GitBlameCommand(commandExecutor).blame(input, result); + verify(result).blameResult( + inputFile, + Arrays.asList( + new BlameLine().date(DateUtils.parseDateTime("2011-08-05T10:49:31+0200")).revision("2c68c473da7fc293e12ca50f19380c5118be7ead").author("simon.brandhof@gmail.com"), + new BlameLine().date(DateUtils.parseDateTime("2011-08-05T10:49:31+0200")).revision("2c68c473da7fc293e12ca50f19380c5118be7ead").author("simon.brandhof@gmail.com"))); } @Test @@ -112,7 +118,7 @@ public class GitBlameCommandTest { DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()); fs.add(inputFile); - BlameResult result = mock(BlameResult.class); + BlameOutput result = mock(BlameOutput.class); CommandExecutor commandExecutor = mock(CommandExecutor.class); when(commandExecutor.execute(any(Command.class), any(StreamConsumer.class), any(StreamConsumer.class), anyLong())).thenAnswer(new Answer<Integer>() { @@ -141,7 +147,8 @@ public class GitBlameCommandTest { thrown.expect(IllegalStateException.class); thrown.expectMessage("Unable to blame file src/foo.xoo. No blame info at line 1. Is file commited?"); - new GitBlameCommand(commandExecutor).blame(fs, Arrays.<InputFile>asList(inputFile), result); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + new GitBlameCommand(commandExecutor).blame(input, result); } @Test @@ -151,7 +158,7 @@ public class GitBlameCommandTest { DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()); fs.add(inputFile); - BlameResult result = mock(BlameResult.class); + BlameOutput result = mock(BlameOutput.class); CommandExecutor commandExecutor = mock(CommandExecutor.class); when(commandExecutor.execute(any(Command.class), any(StreamConsumer.class), any(StreamConsumer.class), anyLong())).thenAnswer(new Answer<Integer>() { @@ -167,7 +174,8 @@ public class GitBlameCommandTest { thrown.expect(IllegalStateException.class); thrown.expectMessage("The git blame command [git blame --porcelain src/foo.xoo -w] failed: My error"); - new GitBlameCommand(commandExecutor).blame(fs, Arrays.<InputFile>asList(inputFile), result); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + new GitBlameCommand(commandExecutor).blame(input, result); } } diff --git a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/JGitBlameCommandTest.java b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/JGitBlameCommandTest.java index 20b96962f83..7f74bea84a9 100644 --- a/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/JGitBlameCommandTest.java +++ b/plugins/sonar-git-plugin/src/test/java/org/sonar/plugins/scm/git/JGitBlameCommandTest.java @@ -22,6 +22,7 @@ package org.sonar.plugins.scm.git; import com.google.common.io.Closeables; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -29,7 +30,8 @@ import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.scm.BlameCommand.BlameResult; +import org.sonar.api.batch.scm.BlameCommand.BlameInput; +import org.sonar.api.batch.scm.BlameCommand.BlameOutput; import org.sonar.api.batch.scm.BlameLine; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.DateUtils; @@ -46,6 +48,7 @@ import java.util.zip.ZipFile; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class JGitBlameCommandTest { @@ -57,6 +60,16 @@ public class JGitBlameCommandTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); + private DefaultFileSystem fs; + private BlameInput input; + + @Before + public void prepare() throws IOException { + fs = new DefaultFileSystem(); + input = mock(BlameInput.class); + when(input.fileSystem()).thenReturn(fs); + } + @Test public void testBlame() throws IOException { File projectDir = temp.newFolder(); @@ -64,47 +77,47 @@ public class JGitBlameCommandTest { JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(new PathResolver()); - DefaultFileSystem fs = new DefaultFileSystem(); File baseDir = new File(projectDir, "dummy-git"); fs.setBaseDir(baseDir); DefaultInputFile inputFile = new DefaultInputFile("foo", DUMMY_JAVA) .setFile(new File(baseDir, DUMMY_JAVA)); fs.add(inputFile); - BlameResult blameResult = mock(BlameResult.class); - jGitBlameCommand.blame(fs, Arrays.<InputFile>asList(inputFile), blameResult); + BlameOutput blameResult = mock(BlameOutput.class); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + jGitBlameCommand.blame(input, blameResult); Date revisionDate = DateUtils.parseDateTime("2012-07-17T16:12:48+0200"); String revision = "6b3aab35a3ea32c1636fee56f996e677653c48ea"; String author = "david@gageot.net"; - verify(blameResult).add(inputFile, + verify(blameResult).blameResult(inputFile, Arrays.asList( - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, 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), + new BlameLine().revision(revision).date(revisionDate).author(author))); } @Test @@ -114,47 +127,47 @@ public class JGitBlameCommandTest { JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(new PathResolver()); - DefaultFileSystem fs = new DefaultFileSystem(); File baseDir = new File(projectDir, "dummy-git-nested/dummy-project"); fs.setBaseDir(baseDir); DefaultInputFile inputFile = new DefaultInputFile("foo", DUMMY_JAVA) .setFile(new File(baseDir, DUMMY_JAVA)); fs.add(inputFile); - BlameResult blameResult = mock(BlameResult.class); - jGitBlameCommand.blame(fs, Arrays.<InputFile>asList(inputFile), blameResult); + BlameOutput blameResult = mock(BlameOutput.class); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + jGitBlameCommand.blame(input, blameResult); Date revisionDate = DateUtils.parseDateTime("2012-07-17T16:12:48+0200"); String revision = "6b3aab35a3ea32c1636fee56f996e677653c48ea"; String author = "david@gageot.net"; - verify(blameResult).add(inputFile, + verify(blameResult).blameResult(inputFile, Arrays.asList( - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, author), - new BlameLine(revisionDate, revision, 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), + new BlameLine().revision(revision).date(revisionDate).author(author))); } @Test @@ -164,7 +177,6 @@ public class JGitBlameCommandTest { JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(new PathResolver()); - DefaultFileSystem fs = new DefaultFileSystem(); File baseDir = new File(projectDir, "dummy-git"); fs.setBaseDir(baseDir); String relativePath = DUMMY_JAVA; @@ -175,11 +187,12 @@ public class JGitBlameCommandTest { // Emulate a modification FileUtils.write(new File(baseDir, relativePath), "modification and \n some new line", true); - BlameResult blameResult = mock(BlameResult.class); + BlameOutput blameResult = mock(BlameOutput.class); thrown.expect(IllegalStateException.class); thrown.expectMessage("Unable to blame file " + relativePath + ". No blame info at line 27. Is file commited?"); - jGitBlameCommand.blame(fs, Arrays.<InputFile>asList(inputFile), blameResult); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + jGitBlameCommand.blame(input, blameResult); } @Test @@ -189,7 +202,6 @@ public class JGitBlameCommandTest { JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(new PathResolver()); - DefaultFileSystem fs = new DefaultFileSystem(); File baseDir = new File(projectDir, "dummy-git"); fs.setBaseDir(baseDir); String relativePath = DUMMY_JAVA; @@ -204,11 +216,12 @@ public class JGitBlameCommandTest { // Emulate a new file FileUtils.copyFile(new File(baseDir, relativePath), new File(baseDir, relativePath2)); - BlameResult blameResult = mock(BlameResult.class); + BlameOutput blameResult = mock(BlameOutput.class); thrown.expect(IllegalStateException.class); thrown.expectMessage("Unable to blame file " + relativePath2 + ". No blame info at line 1. Is file commited?"); - jGitBlameCommand.blame(fs, Arrays.<InputFile>asList(inputFile, inputFile2), blameResult); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile, inputFile2)); + jGitBlameCommand.blame(input, blameResult); } private static void javaUnzip(File zip, File toDir) { diff --git a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameCommand.java b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameCommand.java index 48b65ffee6d..27647c8cad2 100644 --- a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameCommand.java +++ b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameCommand.java @@ -22,8 +22,6 @@ package org.sonar.plugins.scm.svn; import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.BatchComponent; -import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.scm.BlameCommand; @@ -44,8 +42,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -@InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public class SvnBlameCommand implements BlameCommand, BatchComponent { +public class SvnBlameCommand extends BlameCommand { private static final Logger LOG = LoggerFactory.getLogger(SvnBlameCommand.class); private final CommandExecutor commandExecutor; @@ -61,12 +58,13 @@ public class SvnBlameCommand implements BlameCommand, BatchComponent { } @Override - public void blame(final FileSystem fs, Iterable<InputFile> files, final BlameResult result) { + public void blame(final BlameInput input, final BlameOutput output) { + FileSystem fs = input.fileSystem(); LOG.debug("Working directory: " + fs.baseDir().getAbsolutePath()); ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1); List<Future<Void>> tasks = new ArrayList<Future<Void>>(); - for (InputFile inputFile : files) { - tasks.add(submitTask(fs, result, executorService, inputFile)); + for (InputFile inputFile : input.filesToBlame()) { + tasks.add(submitTask(fs, output, executorService, inputFile)); } for (Future<Void> task : tasks) { @@ -81,7 +79,7 @@ public class SvnBlameCommand implements BlameCommand, BatchComponent { } } - private Future<Void> submitTask(final FileSystem fs, final BlameResult result, ExecutorService executorService, final InputFile inputFile) { + private Future<Void> submitTask(final FileSystem fs, final BlameOutput result, ExecutorService executorService, final InputFile inputFile) { return executorService.submit(new Callable<Void>() { @Override public Void call() { @@ -91,7 +89,7 @@ public class SvnBlameCommand implements BlameCommand, BatchComponent { }); } - private void blame(final FileSystem fs, final InputFile inputFile, final BlameResult result) { + private void blame(final FileSystem fs, final InputFile inputFile, final BlameOutput output) { String filename = inputFile.relativePath(); Command cl = createCommandLine(fs.baseDir(), filename); SvnBlameConsumer consumer = new SvnBlameConsumer(filename); @@ -111,7 +109,7 @@ public class SvnBlameCommand implements BlameCommand, BatchComponent { // SONARPLUGINS-3097 SVN do not report blame on last empty line lines.add(lines.get(lines.size() - 1)); } - result.add(inputFile, lines); + output.blameResult(inputFile, lines); } private int execute(Command cl, StreamConsumer consumer, StreamConsumer stderr) { diff --git a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameConsumer.java b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameConsumer.java index dcf67a50572..8224fab347d 100644 --- a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameConsumer.java +++ b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameConsumer.java @@ -43,6 +43,8 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.scm.BlameLine; import org.sonar.api.utils.command.StreamConsumer; +import javax.annotation.CheckForNull; + import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -101,13 +103,14 @@ public class SvnBlameConsumer implements StreamConsumer { String date = matcher.group(1); String time = matcher.group(2); Date dateTime = parseDateTime(date + " " + time); - lines.add(new BlameLine(dateTime, revision, author)); + lines.add(new BlameLine().revision(revision).author(author).date(dateTime)); lineNumber = 0; revision = null; author = null; } } + @CheckForNull protected Date parseDateTime(String dateTimeStr) { try { return dateFormat.parse(dateTimeStr); diff --git a/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnBlameCommandTest.java b/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnBlameCommandTest.java index f7bb1fc66dc..b85b71d2fdb 100644 --- a/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnBlameCommandTest.java +++ b/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnBlameCommandTest.java @@ -31,7 +31,8 @@ import org.mockito.stubbing.Answer; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.scm.BlameCommand.BlameResult; +import org.sonar.api.batch.scm.BlameCommand.BlameInput; +import org.sonar.api.batch.scm.BlameCommand.BlameOutput; import org.sonar.api.batch.scm.BlameLine; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.Settings; @@ -48,7 +49,9 @@ import java.util.List; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class SvnBlameCommandTest { @@ -60,12 +63,15 @@ public class SvnBlameCommandTest { private DefaultFileSystem fs; private File baseDir; + private BlameInput input; @Before public void prepare() throws IOException { baseDir = temp.newFolder(); fs = new DefaultFileSystem(); fs.setBaseDir(baseDir); + input = mock(BlameInput.class); + when(input.fileSystem()).thenReturn(fs); } @Test @@ -75,7 +81,7 @@ public class SvnBlameCommandTest { DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()); fs.add(inputFile); - BlameResult result = mock(BlameResult.class); + BlameOutput result = mock(BlameOutput.class); CommandExecutor commandExecutor = mock(CommandExecutor.class); when(commandExecutor.execute(any(Command.class), any(StreamConsumer.class), any(StreamConsumer.class), anyLong())).thenAnswer(new Answer<Integer>() { @@ -91,12 +97,14 @@ public class SvnBlameCommandTest { } }); - new SvnBlameCommand(commandExecutor, mock(SvnConfiguration.class)).blame(fs, Arrays.<InputFile>asList(inputFile), result); - verify(result).add(inputFile, + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + + new SvnBlameCommand(commandExecutor, mock(SvnConfiguration.class)).blame(input, result); + verify(result).blameResult(inputFile, Arrays.asList( - new BlameLine(DateUtils.parseDateTime("2009-04-18T10:29:59+0000"), "9491", "simon.brandhof"), - new BlameLine(DateUtils.parseDateTime("2009-04-18T10:29:59+0000"), "9491", "simon.brandhof"), - new BlameLine(DateUtils.parseDateTime("2009-08-31T22:32:17+0000"), "10558", "david"))); + new BlameLine().date(DateUtils.parseDateTime("2009-04-18T10:29:59+0000")).revision("9491").author("simon.brandhof"), + new BlameLine().date(DateUtils.parseDateTime("2009-04-18T10:29:59+0000")).revision("9491").author("simon.brandhof"), + new BlameLine().date(DateUtils.parseDateTime("2009-08-31T22:32:17+0000")).revision("10558").author("david"))); } @Test @@ -106,7 +114,7 @@ public class SvnBlameCommandTest { DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()); fs.add(inputFile); - BlameResult result = mock(BlameResult.class); + BlameOutput result = mock(BlameOutput.class); CommandExecutor commandExecutor = mock(CommandExecutor.class); when(commandExecutor.execute(any(Command.class), any(StreamConsumer.class), any(StreamConsumer.class), anyLong())).thenAnswer(new Answer<Integer>() { @@ -122,12 +130,14 @@ public class SvnBlameCommandTest { } }); - new SvnBlameCommand(commandExecutor, mock(SvnConfiguration.class)).blame(fs, Arrays.<InputFile>asList(inputFile), result); - verify(result).add(inputFile, + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + + new SvnBlameCommand(commandExecutor, mock(SvnConfiguration.class)).blame(input, result); + verify(result).blameResult(inputFile, Arrays.asList( - new BlameLine(DateUtils.parseDateTime("2009-04-18T10:29:59+0000"), "9491", "simon.brandhof"), - new BlameLine(DateUtils.parseDateTime("2009-04-01T10:29:59+0000"), "1", null), - new BlameLine(DateUtils.parseDateTime("2009-08-31T22:32:17+0000"), "10558", "david"))); + new BlameLine().date(DateUtils.parseDateTime("2009-04-18T10:29:59+0000")).revision("9491").author("simon.brandhof"), + new BlameLine().date(DateUtils.parseDateTime("2009-04-01T10:29:59+0000")).revision("1"), + new BlameLine().date(DateUtils.parseDateTime("2009-08-31T22:32:17+0000")).revision("10558").author("david"))); } @Test @@ -137,7 +147,7 @@ public class SvnBlameCommandTest { DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()); fs.add(inputFile); - BlameResult result = mock(BlameResult.class); + BlameOutput result = mock(BlameOutput.class); CommandExecutor commandExecutor = mock(CommandExecutor.class); when(commandExecutor.execute(any(Command.class), any(StreamConsumer.class), any(StreamConsumer.class), anyLong())).thenAnswer(new Answer<Integer>() { @@ -156,7 +166,8 @@ public class SvnBlameCommandTest { thrown.expect(IllegalStateException.class); thrown.expectMessage("Unable to blame file src/foo.xoo. No blame info at line 2. Is file commited?"); - new SvnBlameCommand(commandExecutor, mock(SvnConfiguration.class)).blame(fs, Arrays.<InputFile>asList(inputFile), result); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + new SvnBlameCommand(commandExecutor, mock(SvnConfiguration.class)).blame(input, result); } @Test @@ -166,7 +177,7 @@ public class SvnBlameCommandTest { DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()); fs.add(inputFile); - BlameResult result = mock(BlameResult.class); + BlameOutput result = mock(BlameOutput.class); CommandExecutor commandExecutor = mock(CommandExecutor.class); when(commandExecutor.execute(any(Command.class), any(StreamConsumer.class), any(StreamConsumer.class), anyLong())).thenAnswer(new Answer<Integer>() { @@ -182,7 +193,8 @@ public class SvnBlameCommandTest { thrown.expect(IllegalStateException.class); thrown.expectMessage("The svn blame command [svn blame --xml src/foo.xoo --non-interactive] failed: My error"); - new SvnBlameCommand(commandExecutor, mock(SvnConfiguration.class)).blame(fs, Arrays.<InputFile>asList(inputFile), result); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + new SvnBlameCommand(commandExecutor, mock(SvnConfiguration.class)).blame(input, result); } @Test diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/scm/XooBlameCommand.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/scm/XooBlameCommand.java index 0534d92d8cb..b78deb2812c 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/scm/XooBlameCommand.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/scm/XooBlameCommand.java @@ -23,9 +23,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; -import org.sonar.api.BatchComponent; -import org.sonar.api.batch.InstantiationStrategy; -import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.scm.BlameCommand; import org.sonar.api.batch.scm.BlameLine; @@ -34,27 +31,25 @@ import org.sonar.api.utils.DateUtils; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Date; import java.util.List; -@InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public class XooBlameCommand implements BlameCommand, BatchComponent { +public class XooBlameCommand extends BlameCommand { private static final String SCM_EXTENSION = ".scm"; @Override - public void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result) { - for (InputFile inputFile : files) { + public void blame(BlameInput input, BlameOutput result) { + for (InputFile inputFile : input.filesToBlame()) { processFile(inputFile, result); } } @VisibleForTesting - protected void processFile(InputFile inputFile, BlameResult result) { + protected void processFile(InputFile inputFile, BlameOutput result) { File ioFile = inputFile.file(); File scmDataFile = new java.io.File(ioFile.getParentFile(), ioFile.getName() + SCM_EXTENSION); if (!scmDataFile.exists()) { - throw new IllegalStateException("Missing file " + scmDataFile); + return; } try { @@ -71,13 +66,16 @@ public class XooBlameCommand implements BlameCommand, BatchComponent { } String revision = StringUtils.trimToNull(fields[0]); String author = StringUtils.trimToNull(fields[1]); + BlameLine blameLine = new BlameLine().revision(revision).author(author); String dateStr = StringUtils.trimToNull(fields[2]); // Will throw an exception, when date is not in format "yyyy-MM-dd" - Date date = dateStr != null ? DateUtils.parseDate(dateStr) : null; - blame.add(new BlameLine(date, revision, author)); + if (dateStr != null) { + blameLine.date(DateUtils.parseDate(dateStr)); + } + blame.add(blameLine); } } - result.add(inputFile, blame); + result.blameResult(inputFile, blame); } catch (IOException e) { throw new IllegalStateException(e); } diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/scm/XooBlameCommandTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/scm/XooBlameCommandTest.java index bba495dd400..793752bcf0c 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/scm/XooBlameCommandTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/scm/XooBlameCommandTest.java @@ -28,7 +28,8 @@ import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.scm.BlameCommand.BlameResult; +import org.sonar.api.batch.scm.BlameCommand.BlameInput; +import org.sonar.api.batch.scm.BlameCommand.BlameOutput; import org.sonar.api.batch.scm.BlameLine; import org.sonar.api.utils.DateUtils; import org.sonar.xoo.Xoo; @@ -39,6 +40,7 @@ import java.util.Arrays; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class XooBlameCommandTest { @@ -50,11 +52,14 @@ public class XooBlameCommandTest { private DefaultFileSystem fs; private File baseDir; + private BlameInput input; @Before public void prepare() throws IOException { baseDir = temp.newFolder(); fs = new DefaultFileSystem(); + input = mock(BlameInput.class); + when(input.fileSystem()).thenReturn(fs); } @Test @@ -66,10 +71,11 @@ public class XooBlameCommandTest { DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()).setLanguage(Xoo.KEY); fs.add(inputFile); - BlameResult result = mock(BlameResult.class); - new XooBlameCommand().blame(fs, Arrays.<InputFile>asList(inputFile), result); - verify(result).add(inputFile, Arrays.asList(new BlameLine(DateUtils.parseDate("2014-12-12"), "123", "julien"), - new BlameLine(DateUtils.parseDate("2014-12-24"), "234", "julien"))); + BlameOutput result = mock(BlameOutput.class); + when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile)); + new XooBlameCommand().blame(input, result); + verify(result).blameResult(inputFile, Arrays.asList( + new BlameLine().revision("123").author("julien").date(DateUtils.parseDate("2014-12-12")), + new BlameLine().revision("234").author("julien").date(DateUtils.parseDate("2014-12-24")))); } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scm/DefaultBlameInput.java b/sonar-batch/src/main/java/org/sonar/batch/scm/DefaultBlameInput.java new file mode 100644 index 00000000000..f115bf89880 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scm/DefaultBlameInput.java @@ -0,0 +1,46 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.scm; + +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.scm.BlameCommand.BlameInput; + +class DefaultBlameInput implements BlameInput { + + private FileSystem fs; + private Iterable<InputFile> filesToBlame; + + DefaultBlameInput(FileSystem fs, Iterable<InputFile> filesToBlame) { + this.fs = fs; + this.filesToBlame = filesToBlame; + } + + @Override + public FileSystem fileSystem() { + return fs; + } + + @Override + public Iterable<InputFile> filesToBlame() { + return filesToBlame; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scm/DefaultBlameResult.java b/sonar-batch/src/main/java/org/sonar/batch/scm/DefaultBlameOutput.java index f62e3d00683..de873b135db 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scm/DefaultBlameResult.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scm/DefaultBlameOutput.java @@ -21,7 +21,7 @@ package org.sonar.batch.scm; import com.google.common.base.Preconditions; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.scm.BlameCommand.BlameResult; +import org.sonar.api.batch.scm.BlameCommand.BlameOutput; import org.sonar.api.batch.scm.BlameLine; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.measures.CoreMetrics; @@ -33,24 +33,29 @@ import javax.annotation.Nullable; import java.text.Normalizer; import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.regex.Pattern; -class DefaultBlameResult implements BlameResult { +class DefaultBlameOutput implements BlameOutput { private static final Pattern NON_ASCII_CHARS = Pattern.compile("[^\\x00-\\x7F]"); private static final Pattern ACCENT_CODES = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); private final SensorContext context; + private final Set<InputFile> allFilesToBlame = new HashSet<InputFile>(); - DefaultBlameResult(SensorContext context) { + DefaultBlameOutput(SensorContext context, List<InputFile> filesToBlame) { this.context = context; + this.allFilesToBlame.addAll(filesToBlame); } @Override - public void add(InputFile file, List<BlameLine> lines) { + public void blameResult(InputFile file, List<BlameLine> lines) { Preconditions.checkNotNull(file); Preconditions.checkNotNull(lines); + Preconditions.checkArgument(allFilesToBlame.contains(file), "It was not expected to blame file " + file.relativePath()); Preconditions.checkArgument(lines.size() == file.lines(), "Expected one blame result per line but provider returned " + lines.size() + " blame lines while file " + file.relativePath() + " has " + file.lines() + " lines"); @@ -67,6 +72,7 @@ class DefaultBlameResult implements BlameResult { lineNumber++; } ScmSensor.saveMeasures(context, file, authors.buildData(), dates.buildData(), revisions.buildData()); + allFilesToBlame.remove(file); } private String normalizeString(@Nullable String inputString) { @@ -90,4 +96,8 @@ class DefaultBlameResult implements BlameResult { private static PropertiesBuilder<Integer, String> propertiesBuilder(Metric metric) { return new PropertiesBuilder<Integer, String>(metric); } + + public Set<InputFile> remainingFiles() { + return allFilesToBlame; + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java b/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java index e3de5aa41d1..4480938a7a2 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java @@ -84,7 +84,11 @@ public final class ScmSensor implements Sensor { if (!filesToBlame.isEmpty()) { LOG.info("SCM provider for this project is: " + configuration.provider().key()); TimeProfiler profiler = new TimeProfiler().start("Retrieve SCM blame information"); - configuration.provider().blameCommand().blame(fs, filesToBlame, new DefaultBlameResult(context)); + DefaultBlameOutput output = new DefaultBlameOutput(context, filesToBlame); + configuration.provider().blameCommand().blame(new DefaultBlameInput(fs, filesToBlame), output); + if (!output.remainingFiles().isEmpty()) { + throw new IllegalStateException("Some files were not blamed"); + } profiler.stop(); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java index 22a3592cd7d..1c3b4209e51 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/scm/ScmMediumTest.java @@ -23,7 +23,9 @@ import com.google.common.collect.ImmutableMap; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; @@ -42,6 +44,9 @@ public class ScmMediumTest { @org.junit.Rule public TemporaryFolder temp = new TemporaryFolder(); + @Rule + public ExpectedException thrown = ExpectedException.none(); + public BatchMediumTester tester = BatchMediumTester.builder() .registerPlugin("xoo", new XooPlugin()) .addDefaultQProfile("xoo", "Sonar Way") @@ -90,6 +95,31 @@ public class ScmMediumTest { } @Test + public void failIfMissingFile() throws IOException { + + File baseDir = prepareProject(); + File xooFile = new File(baseDir, "src/sample2.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent\n3\n4\n5"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Some files were not blamed"); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.scm.provider", "xoo") + .build()) + .start(); + + } + + @Test public void configureUsingScmURL() throws IOException { File baseDir = prepareProject(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scm/DefaultBlameResultTest.java b/sonar-batch/src/test/java/org/sonar/batch/scm/DefaultBlameOutputTest.java index 90d7dc7e1f6..069e3d5f679 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scm/DefaultBlameResultTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scm/DefaultBlameOutputTest.java @@ -28,7 +28,7 @@ import org.sonar.api.batch.scm.BlameLine; import java.util.Arrays; -public class DefaultBlameResultTest { +public class DefaultBlameOutputTest { @Rule public ExpectedException thrown = ExpectedException.none(); @@ -40,7 +40,18 @@ public class DefaultBlameResultTest { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Expected one blame result per line but provider returned 1 blame lines while file src/main/java/Foo.java has 10 lines"); - new DefaultBlameResult(null).add(file, Arrays.asList(new BlameLine(null, "1", "guy"))); + new DefaultBlameOutput(null, Arrays.asList(file)).blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy"))); + } + + @Test + public void shouldFailIfNotExpectedFile() { + InputFile file = new DefaultInputFile("foo", "src/main/java/Foo.java").setLines(1); + + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("It was not expected to blame file src/main/java/Foo.java"); + + new DefaultBlameOutput(null, Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/main/java/Foo2.java"))) + .blameResult(file, Arrays.asList(new BlameLine().revision("1").author("guy"))); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/BlameCommand.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/BlameCommand.java index e8c74a03e36..b775385eabb 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/BlameCommand.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/BlameCommand.java @@ -19,33 +19,55 @@ */ package org.sonar.api.batch.scm; +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import java.util.List; /** + * This class should be implemented by SCM providers. * @since 5.0 */ -public interface BlameCommand { +@InstantiationStrategy(InstantiationStrategy.PER_BATCH) +public abstract class BlameCommand implements BatchComponent { /** - * Compute blame of the provided files. Computation can be done in parallel. + * Compute blame of the provided files. + * Computation can be done in parallel if this is more efficient. * If there is an error that prevent to blame a file then an exception should be raised. If * one file is new or contains local modifications then an exception should be raised. */ - void blame(FileSystem fs, Iterable<InputFile> files, BlameResult result); + public abstract void blame(BlameInput input, BlameOutput output); /** * Callback for the provider to report results of blame per file. */ - public static interface BlameResult { + public static interface BlameInput { + + /** + * Filesystem of the current (sub )project. + */ + FileSystem fileSystem(); + + /** + * List of files that should be blamed. + */ + Iterable<InputFile> filesToBlame(); + + } + + /** + * Callback for the provider to report results of blame per file. + */ + public static interface BlameOutput { /** * Add result of the blame command for a single file. Number of lines should - * be consistent with {@link InputFile#lines()}. + * be consistent with {@link InputFile#lines()}. This method is thread safe. */ - void add(InputFile file, List<BlameLine> lines); + void blameResult(InputFile file, List<BlameLine> lines); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/BlameLine.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/BlameLine.java index 7772d706ec2..9aed38d9f32 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/BlameLine.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/BlameLine.java @@ -37,37 +37,14 @@ public class BlameLine { private Date date; private String revision; private String author; - private String committer; - /** - * @param date of the commit - * @param revision of the commit - * @param author will also be used as committer identification - */ - public BlameLine(@Nullable Date date, String revision, @Nullable String author) { - this(date, revision, author, author); + public String revision() { + return revision; } - /** - * - * @param date of the commit - * @param revision of the commit - * @param author the person who wrote the line - * @param committer the person who committed the change - */ - public BlameLine(@Nullable Date date, String revision, @Nullable String author, @Nullable String committer) { - if (date != null) { - this.date = new Date(date.getTime()); - } else { - this.date = null; - } + public BlameLine revision(String revision) { this.revision = revision; - this.author = author; - this.committer = committer; - } - - public String revision() { - return revision; + return this; } @CheckForNull @@ -75,9 +52,9 @@ public class BlameLine { return author; } - @CheckForNull - public String committer() { - return committer; + public BlameLine author(@Nullable String author) { + this.author = author; + return this; } /** @@ -85,10 +62,12 @@ public class BlameLine { */ @CheckForNull public Date date() { - if (date != null) { - return (Date) date.clone(); - } - return null; + return date; + } + + public BlameLine date(@Nullable Date date) { + this.date = date; + return this; } // For testing purpose @@ -109,7 +88,6 @@ public class BlameLine { .append(date, rhs.date) .append(revision, rhs.revision) .append(author, rhs.author) - .append(committer, rhs.committer) .isEquals(); } @@ -119,7 +97,6 @@ public class BlameLine { append(date) .append(revision) .append(author) - .append(committer) .toHashCode(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java index a32bec98538..c3f86878293 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java @@ -20,18 +20,21 @@ package org.sonar.api.batch.scm; import org.sonar.api.BatchExtension; +import org.sonar.api.CoreProperties; import org.sonar.api.batch.InstantiationStrategy; import java.io.File; /** + * See {@link CoreProperties#LINKS_SOURCES_DEV} to get old Maven URL format. * @since 5.0 */ @InstantiationStrategy(InstantiationStrategy.PER_BATCH) public abstract class ScmProvider implements BatchExtension { /** - * Unique identifier of the provider. Can be used in SCM URL to define the provider to use. + * Unique identifier of the provider. Can be passed to {@link CoreProperties#SCM_PROVIDER_KEY} + * Can be used in SCM URL to define the provider to use. */ public abstract String key(); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/scm/BlameLineTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/scm/BlameLineTest.java index 1ff1eee16b2..4a5fb76eb24 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/scm/BlameLineTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/scm/BlameLineTest.java @@ -30,14 +30,13 @@ public class BlameLineTest { @Test public void testBlameLine() { Date date = new Date(); - BlameLine line1 = new BlameLine(date, "1", "foo"); - BlameLine line1b = new BlameLine(date, "1", "foo"); - BlameLine line2 = new BlameLine(null, "2", "foo2"); + BlameLine line1 = new BlameLine().date(date).revision("1").author("foo"); + BlameLine line1b = new BlameLine().date(date).revision("1").author("foo"); + BlameLine line2 = new BlameLine().date(null).revision("2").author("foo2"); assertThat(line1.author()).isEqualTo("foo"); assertThat(line1.date()).isEqualTo(date); assertThat(line1.revision()).isEqualTo("1"); - assertThat(line1.committer()).isEqualTo("foo"); assertThat(line1).isEqualTo(line1); assertThat(line1).isNotEqualTo(null); @@ -46,7 +45,7 @@ public class BlameLineTest { assertThat(line1).isNotEqualTo(line2); assertThat(line1).isNotEqualTo("foo"); - assertThat(line1.toString()).contains("revision=1,author=foo,committer=foo"); + assertThat(line1.toString()).contains("revision=1,author=foo"); } } |