Skip to content

Commit

Permalink
Merge pull request #440 from fo-code/delta-api-extension
Browse files Browse the repository at this point in the history
Extended delta API
  • Loading branch information
uhafner authored Mar 15, 2022
2 parents ecf8024 + 864bf4b commit 2d12149
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 70 deletions.
2 changes: 1 addition & 1 deletion plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<font-awesome-api.version>6.0.0-1</font-awesome-api.version>
<plugin-util-api.version>2.14.0</plugin-util-api.version>
<data-tables-api.version>1.11.4-3</data-tables-api.version>
<forensics-api-plugin.version>1.9.0</forensics-api-plugin.version>
<forensics-api-plugin.version>1.12.0</forensics-api-plugin.version>
<bootstrap5-api.version>5.1.3-6</bootstrap5-api.version>
<streamex.version>0.8.1</streamex.version>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import java.util.Optional;

import edu.hm.hafner.util.FilteredLog;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import org.jenkinsci.plugins.gitclient.GitClient;
import hudson.model.Run;

import io.jenkins.plugins.forensics.delta.DeltaCalculator;
import io.jenkins.plugins.forensics.delta.model.Delta;
import io.jenkins.plugins.forensics.git.reference.GitCommitsRecord;
import io.jenkins.plugins.forensics.git.util.RemoteResultWrapper;

/**
Expand All @@ -22,6 +23,7 @@
public class GitDeltaCalculator extends DeltaCalculator {

static final String DELTA_ERROR = "Computing delta information failed with an exception:";
static final String EMPTY_COMMIT_ERROR = "Calculating the Git code delta is not possible due to an unknown commit ID";

private static final long serialVersionUID = -7303579046266608368L;

Expand All @@ -39,25 +41,30 @@ public GitDeltaCalculator(final GitClient git) {
}

@Override
public Optional<Delta> calculateDelta(@NonNull final String currentCommit, @NonNull final String referenceCommit,
@NonNull final FilteredLog log) {
try {
log.logInfo(
"Invoking Git delta calculator for determining the made changes between the commits with the IDs %s and %s",
currentCommit, referenceCommit);

public Optional<Delta> calculateDelta(final Run<?, ?> build, final Run<?, ?> referenceBuild,
final String scmKeyFilter, final FilteredLog log) {
Optional<GitCommitsRecord> buildCommits = GitCommitsRecord.findRecordForScm(build, scmKeyFilter);
Optional<GitCommitsRecord> referenceCommits = GitCommitsRecord.findRecordForScm(referenceBuild, scmKeyFilter);
if (buildCommits.isPresent() && referenceCommits.isPresent()) {
String currentCommit = buildCommits.get().getLatestCommit();
String referenceCommit = referenceCommits.get().getLatestCommit();
if (!currentCommit.isEmpty() && !referenceCommit.isEmpty()) {
RemoteResultWrapper<Delta> wrapped = git.withRepository(
new DeltaRepositoryCallback(currentCommit, referenceCommit));
wrapped.getInfoMessages().forEach(log::logInfo);

return Optional.of(wrapped.getResult());
log.logInfo(
"Invoking Git delta calculator for determining the made changes between the commits with the IDs %s and %s",
currentCommit, referenceCommit);
try {
RemoteResultWrapper<Delta> wrapped = git.withRepository(
new DeltaRepositoryCallback(currentCommit, referenceCommit));
wrapped.getInfoMessages().forEach(log::logInfo);
return Optional.of(wrapped.getResult());
}
catch (IOException | InterruptedException exception) {
log.logException(exception, DELTA_ERROR);
return Optional.empty();
}
}
}
catch (IOException | InterruptedException exception) {
log.logException(exception, DELTA_ERROR);
}

log.logError(EMPTY_COMMIT_ERROR);
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.jenkins.plugins.forensics.git.delta;

import java.io.IOException;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
Expand All @@ -10,15 +11,21 @@
import edu.hm.hafner.util.FilteredLog;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import hudson.model.FreeStyleProject;
import hudson.model.Run;
import hudson.plugins.git.GitSCM;

import io.jenkins.plugins.forensics.delta.model.Change;
import io.jenkins.plugins.forensics.delta.model.ChangeEditType;
import io.jenkins.plugins.forensics.delta.model.Delta;
import io.jenkins.plugins.forensics.delta.model.FileChanges;
import io.jenkins.plugins.forensics.delta.model.FileEditType;
import io.jenkins.plugins.forensics.git.delta.model.GitDelta;
import io.jenkins.plugins.forensics.git.reference.GitReferenceRecorder;
import io.jenkins.plugins.forensics.git.util.GitITest;

import static io.jenkins.plugins.forensics.assertions.Assertions.*;
import static org.mockito.Mockito.*;

/**
* Integration test for the class {@link GitDeltaCalculator}.
Expand All @@ -27,6 +34,8 @@
*/
public class GitDeltaCalculatorITest extends GitITest {

private static final String EMPTY_SCM_KEY = "";

/**
* The delta result should be empty if there are invalid commits.
*/
Expand All @@ -35,9 +44,7 @@ public void shouldCreateEmptyDeltaIfCommitsAreInvalid() {
GitDeltaCalculator deltaCalculator = createDeltaCalculator();

FilteredLog log = createLog();
Optional<Delta> delta = deltaCalculator.calculateDelta("", "", log);

assertThat(delta).isEmpty();
assertThat(deltaCalculator.calculateDelta(mock(Run.class), mock(Run.class), EMPTY_SCM_KEY, log)).isEmpty();
}

/**
Expand All @@ -46,18 +53,22 @@ public void shouldCreateEmptyDeltaIfCommitsAreInvalid() {
@Test
@SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "The cast is confirmed via an assertion")
public void shouldCreateDiffFile() {
FreeStyleProject job = createJobWithReferenceRecorder();

GitDeltaCalculator deltaCalculator = createDeltaCalculator();
FilteredLog log = createLog();

final String referenceCommit = getHead();
final String fileName = "newFile";
final String content = "content";
Run<?, ?> referenceBuild = buildSuccessfully(job);
String referenceCommit = getHead();
String fileName = "newFile";
String content = "content";
writeFile(fileName, content);
addFile(fileName);
commit("test");
final String currentCommit = getHead();
Run<?, ?> build = buildSuccessfully(job);
String currentCommit = getHead();

Optional<Delta> result = deltaCalculator.calculateDelta(currentCommit, referenceCommit, log);
Optional<Delta> result = deltaCalculator.calculateDelta(build, referenceBuild, EMPTY_SCM_KEY, log);
assertThat(result).isNotEmpty();

Delta delta = result.get();
Expand All @@ -80,19 +91,20 @@ public void shouldCreateDiffFile() {
*/
@Test
public void shouldDetermineAddedFile() {
FreeStyleProject job = createJobWithReferenceRecorder();
GitDeltaCalculator deltaCalculator = createDeltaCalculator();
FilteredLog log = createLog();

final String referenceCommit = getHead();
Run<?, ?> referenceBuild = buildSuccessfully(job);

final String newFileName = "newFile";
final String content = "added";
String newFileName = "newFile";
String content = "added";
writeFile(newFileName, content);
addFile(newFileName);
commit("test");
final String currentCommit = getHead();
Run<?, ?> build = buildSuccessfully(job);

Optional<Delta> result = deltaCalculator.calculateDelta(currentCommit, referenceCommit, log);
Optional<Delta> result = deltaCalculator.calculateDelta(build, referenceBuild, EMPTY_SCM_KEY, log);
assertThat(result).isNotEmpty();

Delta delta = result.get();
Expand All @@ -107,16 +119,17 @@ public void shouldDetermineAddedFile() {
*/
@Test
public void shouldDetermineModifiedFile() {
FreeStyleProject job = createJobWithReferenceRecorder();
GitDeltaCalculator deltaCalculator = createDeltaCalculator();
FilteredLog log = createLog();

final String content = "modified";
String content = "modified";
commitFile("test");
final String referenceCommit = getHead();
Run<?, ?> referenceBuild = buildSuccessfully(job);
commitFile(content);
final String currentCommit = getHead();
Run<?, ?> build = buildSuccessfully(job);

Optional<Delta> result = deltaCalculator.calculateDelta(currentCommit, referenceCommit, log);
Optional<Delta> result = deltaCalculator.calculateDelta(build, referenceBuild, EMPTY_SCM_KEY, log);
assertThat(result).isNotEmpty();

Delta delta = result.get();
Expand All @@ -131,17 +144,18 @@ public void shouldDetermineModifiedFile() {
*/
@Test
public void shouldDetermineDeletedFile() {
FreeStyleProject job = createJobWithReferenceRecorder();
GitDeltaCalculator deltaCalculator = createDeltaCalculator();
FilteredLog log = createLog();

final String content = "content";
String content = "content";
commitFile(content);
final String referenceCommit = getHead();
Run<?, ?> referenceBuild = buildSuccessfully(job);
git("rm", GitITest.INITIAL_FILE);
commit("test");
final String currentCommit = getHead();
Run<?, ?> build = buildSuccessfully(job);

Optional<Delta> result = deltaCalculator.calculateDelta(currentCommit, referenceCommit, log);
Optional<Delta> result = deltaCalculator.calculateDelta(build, referenceBuild, EMPTY_SCM_KEY, log);
assertThat(result).isNotEmpty();

Delta delta = result.get();
Expand All @@ -156,17 +170,18 @@ public void shouldDetermineDeletedFile() {
*/
@Test
public void shouldDetermineAddedLines() {
FreeStyleProject job = createJobWithReferenceRecorder();
GitDeltaCalculator deltaCalculator = createDeltaCalculator();
FilteredLog log = createLog();

final String content = "Test\nTest\n";
final String insertedContent = "Test\nInsert1\nInsert2\nTest\n";
String content = "Test\nTest\n";
String insertedContent = "Test\nInsert1\nInsert2\nTest\n";
commitFile(content);
final String referenceCommit = getHead();
Run<?, ?> referenceBuild = buildSuccessfully(job);
commitFile(insertedContent);
final String currentCommit = getHead();
Run<?, ?> build = buildSuccessfully(job);

Optional<Delta> result = deltaCalculator.calculateDelta(currentCommit, referenceCommit, log);
Optional<Delta> result = deltaCalculator.calculateDelta(build, referenceBuild, EMPTY_SCM_KEY, log);
assertThat(result).isNotEmpty();

Delta delta = result.get();
Expand All @@ -184,17 +199,18 @@ public void shouldDetermineAddedLines() {
*/
@Test
public void shouldDetermineModifiedLines() {
FreeStyleProject job = createJobWithReferenceRecorder();
GitDeltaCalculator deltaCalculator = createDeltaCalculator();
FilteredLog log = createLog();

final String content = "Test\nTest\nTest\nTest";
final String modified = "Test\nModified\nModified2\nTest";
String content = "Test\nTest\nTest\nTest";
String modified = "Test\nModified\nModified2\nTest";
commitFile(content);
final String referenceCommit = getHead();
Run<?, ?> referenceBuild = buildSuccessfully(job);
commitFile(modified);
final String currentCommit = getHead();
Run<?, ?> build = buildSuccessfully(job);

Optional<Delta> result = deltaCalculator.calculateDelta(currentCommit, referenceCommit, log);
Optional<Delta> result = deltaCalculator.calculateDelta(build, referenceBuild, EMPTY_SCM_KEY, log);
assertThat(result).isNotEmpty();

Delta delta = result.get();
Expand All @@ -212,17 +228,18 @@ public void shouldDetermineModifiedLines() {
*/
@Test
public void shouldDetermineDeletedLines() {
FreeStyleProject job = createJobWithReferenceRecorder();
GitDeltaCalculator deltaCalculator = createDeltaCalculator();
FilteredLog log = createLog();

final String content = "Test\nTest3\nTest";
final String modified = "Test\nTest";
String content = "Test\nTest3\nTest";
String modified = "Test\nTest";
commitFile(content);
final String referenceCommit = getHead();
Run<?, ?> referenceBuild = buildSuccessfully(job);
commitFile(modified);
final String currentCommit = getHead();
Run<?, ?> build = buildSuccessfully(job);

Optional<Delta> result = deltaCalculator.calculateDelta(currentCommit, referenceCommit, log);
Optional<Delta> result = deltaCalculator.calculateDelta(build, referenceBuild, EMPTY_SCM_KEY, log);
assertThat(result).isNotEmpty();

Delta delta = result.get();
Expand All @@ -240,17 +257,18 @@ public void shouldDetermineDeletedLines() {
*/
@Test
public void shouldDetermineAllChangeTypesTogether() {
FreeStyleProject job = createJobWithReferenceRecorder();
GitDeltaCalculator deltaCalculator = createDeltaCalculator();
FilteredLog log = createLog();

final String content = "Test1\nTest2\nTest3\nTest4";
final String newContent = "Modified\nTest2\nInserted\nTest3";
String content = "Test1\nTest2\nTest3\nTest4";
String newContent = "Modified\nTest2\nInserted\nTest3";
commitFile(content);
final String referenceCommit = getHead();
Run<?, ?> referenceBuild = buildSuccessfully(job);
commitFile(newContent);
final String currentCommit = getHead();
Run<?, ?> build = buildSuccessfully(job);

Optional<Delta> result = deltaCalculator.calculateDelta(currentCommit, referenceCommit, log);
Optional<Delta> result = deltaCalculator.calculateDelta(build, referenceBuild, EMPTY_SCM_KEY, log);
assertThat(result).isNotEmpty();

Delta delta = result.get();
Expand Down Expand Up @@ -344,4 +362,21 @@ private FilteredLog createLog() {
private GitDeltaCalculator createDeltaCalculator() {
return new GitDeltaCalculator(createGitClient());
}

/**
* Creates a {@link FreeStyleProject} which contains a {@link GitReferenceRecorder} within the publishers list.
*
* @return the created project
*/
private FreeStyleProject createJobWithReferenceRecorder() {
try {
FreeStyleProject job = createFreeStyleProject();
job.setScm(new GitSCM(getRepositoryRoot()));
job.getPublishersList().add(new GitReferenceRecorder());
return job;
}
catch (IOException exception) {
throw new AssertionError(exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.jenkins.plugins.forensics.git.delta;

import java.io.IOException;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
Expand All @@ -9,9 +8,11 @@
import edu.hm.hafner.util.FilteredLog;

import org.jenkinsci.plugins.gitclient.GitClient;
import hudson.model.Run;

import io.jenkins.plugins.forensics.delta.model.Delta;

import static io.jenkins.plugins.forensics.git.delta.GitDeltaCalculator.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

Expand All @@ -22,21 +23,17 @@
*/
class GitDeltaCalculatorTest {

private static final String EMPTY_SCM_KEY = "";

@Test
void shouldAbortIfWithRepositoryThrowsException() throws IOException, InterruptedException {
GitClient gitClient = createGitClientWithException(new IOException());
void shouldAbortIfCommitsAreEmpty() {
GitClient gitClient = mock(GitClient.class);
GitDeltaCalculator deltaCalculator = new GitDeltaCalculator(gitClient);
FilteredLog log = new FilteredLog(StringUtils.EMPTY);

Optional<Delta> result = deltaCalculator.calculateDelta("x", "x", log);
Optional<Delta> result = deltaCalculator.calculateDelta(mock(Run.class), mock(Run.class), EMPTY_SCM_KEY, log);

assertThat(result).isEmpty();
assertThat(log.getErrorMessages()).contains(GitDeltaCalculator.DELTA_ERROR);
}

private GitClient createGitClientWithException(final Exception exception) throws InterruptedException, IOException {
GitClient gitClient = mock(GitClient.class);
when(gitClient.withRepository(any())).thenThrow(exception);
return gitClient;
assertThat(log.getErrorMessages()).contains(EMPTY_COMMIT_ERROR);
}
}
Loading

0 comments on commit 2d12149

Please sign in to comment.