JGitBlameCommand 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)) { File gitBaseDir = repo.getWorkTree(); if (cloneIsInvalid(gitBaseDir)) { return; } Stream<InputFile> stream = StreamSupport.stream(input.filesToBlame().spliterator(), true); ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors(), new GitThreadFactory(), null, false); forkJoinPool.submit(() -> stream.forEach(inputFile -> blame(output, git, gitBaseDir, inputFile))); try { forkJoinPool.shutdown(); forkJoinPool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); } catch (InterruptedException e) { LOG.info("Git blame interrupted"); } } } JGitBlameCommand(PathResolver pathResolver, AnalysisWarnings analysisWarnings); @Override void blame(BlameInput input, BlameOutput output);  }
@Test public void testBlame() throws IOException { File projectDir = temp.newFolder(); javaUnzip(new File("test-repos/dummy-git.zip"), projectDir); JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); File baseDir = new File(projectDir, "dummy-git"); DefaultFileSystem fs = new DefaultFileSystem(baseDir); when(input.fileSystem()).thenReturn(fs); DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA) .setModuleBaseDir(baseDir.toPath()) .build(); fs.add(inputFile); BlameOutput blameResult = mock(BlameOutput.class); when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile)); jGitBlameCommand.blame(input, blameResult); Date revisionDate1 = DateUtils.parseDateTime("2012-07-17T16:12:48+0200"); String revision1 = "6b3aab35a3ea32c1636fee56f996e677653c48ea"; String author1 = "david@gageot.net"; Date revisionDate2 = DateUtils.parseDateTime("2015-05-19T13:31:09+0200"); String revision2 = "0d269c1acfb8e6d4d33f3c43041eb87e0df0f5e7"; String author2 = "duarte.meneses@sonarsource.com"; List<BlameLine> expectedBlame = new LinkedList<>(); for (int i = 0; i < 25; i++) { expectedBlame.add(new BlameLine().revision(revision1).date(revisionDate1).author(author1)); } for (int i = 0; i < 3; i++) { expectedBlame.add(new BlameLine().revision(revision2).date(revisionDate2).author(author2)); } for (int i = 0; i < 1; i++) { expectedBlame.add(new BlameLine().revision(revision1).date(revisionDate1).author(author1)); } verify(blameResult).blameResult(inputFile, expectedBlame); }
@Test public void properFailureIfNotAGitProject() throws IOException { File projectDir = temp.newFolder(); javaUnzip(new File("test-repos/dummy-git.zip"), projectDir); JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); File baseDir = new File(projectDir, "dummy-git"); FileUtils.forceDelete(new File(baseDir, ".git")); DefaultFileSystem fs = new DefaultFileSystem(baseDir); when(input.fileSystem()).thenReturn(fs); DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA).build(); fs.add(inputFile); BlameOutput blameResult = mock(BlameOutput.class); when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile)); thrown.expect(MessageException.class); thrown.expectMessage("Not inside a Git work tree: "); jGitBlameCommand.blame(input, blameResult); }
@Test public void testBlameOnNestedModule() throws IOException { File projectDir = temp.newFolder(); javaUnzip(new File("test-repos/dummy-git-nested.zip"), projectDir); JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); File baseDir = new File(projectDir, "dummy-git-nested/dummy-project"); DefaultFileSystem fs = new DefaultFileSystem(baseDir); when(input.fileSystem()).thenReturn(fs); DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA) .setModuleBaseDir(baseDir.toPath()) .build(); fs.add(inputFile); BlameOutput blameResult = mock(BlameOutput.class); when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile)); jGitBlameCommand.blame(input, blameResult); Date revisionDate = DateUtils.parseDateTime("2012-07-17T16:12:48+0200"); String revision = "6b3aab35a3ea32c1636fee56f996e677653c48ea"; String author = "david@gageot.net"; verify(blameResult).blameResult(inputFile, Arrays.asList( new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author), new BlameLine().revision(revision).date(revisionDate).author(author))); }
@Test public void dontFailOnModifiedFile() throws IOException { File projectDir = temp.newFolder(); javaUnzip(new File("test-repos/dummy-git.zip"), projectDir); JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); File baseDir = new File(projectDir, "dummy-git"); DefaultFileSystem fs = new DefaultFileSystem(baseDir); when(input.fileSystem()).thenReturn(fs); String relativePath = DUMMY_JAVA; DefaultInputFile inputFile = new TestInputFileBuilder("foo", relativePath).build(); fs.add(inputFile); Files.write(baseDir.toPath().resolve(relativePath), "modification and \n some new line".getBytes()); BlameOutput blameResult = mock(BlameOutput.class); when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile)); jGitBlameCommand.blame(input, blameResult); }
@Test public void dontFailOnNewFile() throws IOException { File projectDir = temp.newFolder(); javaUnzip(new File("test-repos/dummy-git.zip"), projectDir); JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); File baseDir = new File(projectDir, "dummy-git"); DefaultFileSystem fs = new DefaultFileSystem(baseDir); when(input.fileSystem()).thenReturn(fs); String relativePath = DUMMY_JAVA; String relativePath2 = "src/main/java/org/dummy/Dummy2.java"; DefaultInputFile inputFile = new TestInputFileBuilder("foo", relativePath).build(); fs.add(inputFile); DefaultInputFile inputFile2 = new TestInputFileBuilder("foo", relativePath2).build(); fs.add(inputFile2); FileUtils.copyFile(new File(baseDir, relativePath), new File(baseDir, relativePath2)); BlameOutput blameResult = mock(BlameOutput.class); when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile, inputFile2)); jGitBlameCommand.blame(input, blameResult); }
@Test public void dontFailOnSymlink() throws IOException { assumeTrue(!System2.INSTANCE.isOsWindows()); File projectDir = temp.newFolder(); javaUnzip(new File("test-repos/dummy-git.zip"), projectDir); JGitBlameCommand jGitBlameCommand = newJGitBlameCommand(); File baseDir = new File(projectDir, "dummy-git"); DefaultFileSystem fs = new DefaultFileSystem(baseDir); when(input.fileSystem()).thenReturn(fs); String relativePath = DUMMY_JAVA; String relativePath2 = "src/main/java/org/dummy/Dummy2.java"; DefaultInputFile inputFile = new TestInputFileBuilder("foo", relativePath) .setModuleBaseDir(baseDir.toPath()) .build(); fs.add(inputFile); DefaultInputFile inputFile2 = new TestInputFileBuilder("foo", relativePath2) .setModuleBaseDir(baseDir.toPath()) .build(); fs.add(inputFile2); Files.createSymbolicLink(inputFile2.file().toPath(), inputFile.file().toPath()); BlameOutput blameResult = mock(BlameOutput.class); when(input.filesToBlame()).thenReturn(Arrays.asList(inputFile, inputFile2)); jGitBlameCommand.blame(input, blameResult); }
@Test public void return_early_when_shallow_clone_detected() throws IOException { File projectDir = temp.newFolder(); javaUnzip(new File("test-repos/shallow-git.zip"), projectDir); File baseDir = new File(projectDir, "shallow-git"); DefaultFileSystem fs = new DefaultFileSystem(baseDir); when(input.fileSystem()).thenReturn(fs); DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA).build(); when(input.filesToBlame()).thenReturn(Collections.singleton(inputFile)); AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(new PathResolver(), analysisWarnings); BlameOutput output = mock(BlameOutput.class); jGitBlameCommand.blame(input, output); assertThat(logTester.logs()).first() .matches(s -> s.contains("Shallow clone detected, no blame information will be provided.")); verifyZeroInteractions(output); verify(analysisWarnings).addUnique(startsWith("Shallow clone detected")); }
@Test public void return_early_when_clone_with_reference_detected() throws IOException { File projectDir = temp.newFolder(); javaUnzip(new File("test-repos/dummy-git-reference-clone.zip"), projectDir); Path baseDir = projectDir.toPath().resolve("dummy-git2"); DefaultFileSystem fs = new DefaultFileSystem(baseDir); when(input.fileSystem()).thenReturn(fs); DefaultInputFile inputFile = new TestInputFileBuilder("foo", DUMMY_JAVA).setModuleBaseDir(baseDir).build(); when(input.filesToBlame()).thenReturn(Collections.singleton(inputFile)); AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); JGitBlameCommand jGitBlameCommand = new JGitBlameCommand(new PathResolver(), analysisWarnings); TestBlameOutput output = new TestBlameOutput(); jGitBlameCommand.blame(input, output); assertThat(logTester.logs()).first() .matches(s -> s.contains("This git repository references another local repository which is not well supported")); assertThat(output.blame.keySet()).contains(inputFile); assertThat(output.blame.get(inputFile).stream().map(BlameLine::revision)) .containsOnly("6b3aab35a3ea32c1636fee56f996e677653c48ea", "843c7c30d7ebd9a479e8f1daead91036c75cbc4e", "0d269c1acfb8e6d4d33f3c43041eb87e0df0f5e7"); verifyZeroInteractions(analysisWarnings); }