Merge branch 'stable-3.5' into stable-3.6
* stable-3.5:
Make MailSenderIT.outgoingMailWithACommentsInUnchangedFile less fragile
Fix the email notification for comments unchanged files
Fix timeouts calculation in SshDaemon
Add IT test for email notifications with comments on unchanged files
Revert "ApprovalInference: Ignore approvals given on a non-current patchset"
ApprovalInference: Ignore approvals given on a non-current patchset
Update git submodules
Release-Notes: skip
Change-Id: Ie9c7278de7e282a59f9bf5c78f1ed31db6cfd3ff
diff --git a/java/com/google/gerrit/server/mail/send/CommentSender.java b/java/com/google/gerrit/server/mail/send/CommentSender.java
index ce5438b..0006fa0 100644
--- a/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -54,7 +54,9 @@
import java.util.Map;
import java.util.Optional;
import org.apache.james.mime4j.dom.field.FieldName;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
/** Send comments, after the author of them hit used Publish Comments in the UI. */
@@ -206,7 +208,8 @@
groups.add(currentGroup);
if (modifiedFiles != null && !modifiedFiles.isEmpty()) {
try {
- currentGroup.fileData = new PatchFile(repo, modifiedFiles, c.key.filename);
+ currentGroup.fileData =
+ loadPatchFile(repo, modifiedFiles, c.key.filename, patchSet.commitId());
} catch (IOException e) {
logger.atWarning().withCause(e).log(
"Cannot load %s from %s in %s",
@@ -227,6 +230,28 @@
return groups;
}
+ private PatchFile loadPatchFile(
+ Repository repo,
+ Map<String, FileDiffOutput> modifiedFiles,
+ String fileName,
+ ObjectId commitId)
+ throws IOException {
+ try {
+ return new PatchFile(repo, modifiedFiles, fileName);
+ } catch (MissingObjectException e) {
+ // check if the file has not been modified then is an unchanged file
+ if (!isModifiedFile(modifiedFiles, fileName)) {
+ return new PatchFile(repo, fileName, commitId);
+ }
+ throw e;
+ }
+ }
+
+ private boolean isModifiedFile(Map<String, FileDiffOutput> modifiedFiles, String fileName) {
+ return modifiedFiles.values().stream()
+ .anyMatch(f -> f.newPath().map(v -> v.equals(fileName)).orElse(false));
+ }
+
/** Get the set of accounts whose comments have been replied to in this email. */
private HashSet<Account.Id> getReplyAccounts() {
HashSet<Account.Id> replyAccounts = new HashSet<>();
diff --git a/java/com/google/gerrit/server/patch/PatchFile.java b/java/com/google/gerrit/server/patch/PatchFile.java
index 7a8180bd..6c7da25 100644
--- a/java/com/google/gerrit/server/patch/PatchFile.java
+++ b/java/com/google/gerrit/server/patch/PatchFile.java
@@ -114,6 +114,17 @@
}
}
+ public PatchFile(Repository repo, String fileName, ObjectId patchSetCommitId) throws IOException {
+ this.repo = repo;
+ this.diff = FileDiffOutput.empty(fileName, patchSetCommitId, patchSetCommitId);
+ try (ObjectReader reader = repo.newObjectReader();
+ RevWalk rw = new RevWalk(reader)) {
+ final RevCommit bCommit = rw.parseCommit(diff.newCommitId());
+ this.aTree = bCommit.getTree();
+ this.bTree = bCommit.getTree();
+ }
+ }
+
private String getOldName() {
String name = FilePathAdapter.getOldPath(diff.oldPath(), diff.changeType());
if (name != null) {
diff --git a/java/com/google/gerrit/sshd/SshDaemon.java b/java/com/google/gerrit/sshd/SshDaemon.java
index cc35a32..01b5ee8 100644
--- a/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/java/com/google/gerrit/sshd/SshDaemon.java
@@ -15,7 +15,6 @@
package com.google.gerrit.sshd;
import static com.google.gerrit.server.ssh.SshAddressesModule.IANA_SSH_PORT;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.apache.sshd.core.CoreModuleProperties.AUTH_TIMEOUT;
import static org.apache.sshd.core.CoreModuleProperties.IDLE_TIMEOUT;
@@ -184,23 +183,21 @@
AUTH_TIMEOUT.set(
this,
Duration.ofSeconds(
- MILLISECONDS.convert(
- ConfigUtil.getTimeUnit(cfg, "sshd", null, "loginGraceTime", 120, SECONDS),
- SECONDS)));
+ ConfigUtil.getTimeUnit(cfg, "sshd", null, "loginGraceTime", 120, SECONDS)));
long idleTimeoutSeconds = ConfigUtil.getTimeUnit(cfg, "sshd", null, "idleTimeout", 0, SECONDS);
- IDLE_TIMEOUT.set(this, Duration.ofSeconds(SECONDS.toMillis(idleTimeoutSeconds)));
- NIO2_READ_TIMEOUT.set(this, Duration.ofSeconds(SECONDS.toMillis(idleTimeoutSeconds)));
+ IDLE_TIMEOUT.set(this, Duration.ofSeconds(idleTimeoutSeconds));
+ NIO2_READ_TIMEOUT.set(this, Duration.ofSeconds(idleTimeoutSeconds));
long rekeyTimeLimit =
ConfigUtil.getTimeUnit(cfg, "sshd", null, "rekeyTimeLimit", 3600, SECONDS);
- REKEY_TIME_LIMIT.set(this, Duration.ofSeconds(SECONDS.toMillis(rekeyTimeLimit)));
+ REKEY_TIME_LIMIT.set(this, Duration.ofSeconds(rekeyTimeLimit));
REKEY_BYTES_LIMIT.set(
this, cfg.getLong("sshd", "rekeyBytesLimit", 1024 * 1024 * 1024 /* 1GB */));
long waitTimeoutSeconds = ConfigUtil.getTimeUnit(cfg, "sshd", null, "waitTimeout", 30, SECONDS);
- WAIT_FOR_SPACE_TIMEOUT.set(this, Duration.ofSeconds(SECONDS.toMillis(waitTimeoutSeconds)));
+ WAIT_FOR_SPACE_TIMEOUT.set(this, Duration.ofSeconds(waitTimeoutSeconds));
final int maxConnectionsPerUser = cfg.getInt("sshd", "maxConnectionsPerUser", 64);
if (0 < maxConnectionsPerUser) {
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java b/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
index cb1a679..97d3cd8 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
@@ -14,19 +14,27 @@
package com.google.gerrit.acceptance.server.mail;
+import static com.google.common.truth.Truth.assertThat;
+
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
+import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.client.Comment;
import com.google.gerrit.extensions.client.Side;
+import com.google.gerrit.extensions.common.ChangeInput;
+import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.mail.MailMessage;
import com.google.inject.Inject;
import java.time.Instant;
import java.util.HashMap;
+import java.util.List;
+import org.eclipse.jgit.lib.Constants;
import org.junit.Ignore;
@Ignore
@@ -69,6 +77,64 @@
return changeId;
}
+ String createChangeWithUnchangedFileReviewed(
+ TestAccount reviewer, String adminInlineComment, String userInlineComment) throws Exception {
+
+ // Create a change with a file and merge it
+ String fileContent = "this is line 1 \nthis is line 2 \nthis is line 3 \nthis is line 4";
+ PushOneCommit.Result firstChangeResult = createChange("First Change", FILE_NAME, fileContent);
+ firstChangeResult.assertOkStatus();
+ merge(firstChangeResult);
+
+ // Create an second empty change
+ String secondChangeId =
+ gApi.changes()
+ .create(new ChangeInput(project.get(), Constants.MASTER, "Second Change"))
+ .get()
+ .id;
+ RevisionApi secondChangeRevision = gApi.changes().id(secondChangeId).current();
+
+ // Add draft
+ DraftInput draftInputFromAdmin =
+ createInlineDraftInLine1(adminInlineComment, Side.REVISION, null);
+ secondChangeRevision.createDraft(draftInputFromAdmin);
+
+ // Review change
+ ReviewInput adminInput = new ReviewInput();
+ adminInput.drafts = ReviewInput.DraftHandling.PUBLISH_ALL_REVISIONS;
+ secondChangeRevision.review(adminInput);
+
+ requestScopeOperations.setApiUser(reviewer.id());
+
+ // Add draft
+ List<CommentInfo> comments = gApi.changes().id(secondChangeId).commentsRequest().getAsList();
+ assertThat(comments).hasSize(1);
+ CommentInfo adminCommentInfo = comments.get(0);
+ DraftInput draftInputFromUser =
+ createInlineDraftInLine1(userInlineComment, Side.REVISION, adminCommentInfo.id);
+ gApi.changes().id(secondChangeId).current().createDraft(draftInputFromUser);
+
+ // Review change
+ ReviewInput reviewerInput = new ReviewInput();
+ reviewerInput.drafts = ReviewInput.DraftHandling.PUBLISH_ALL_REVISIONS;
+ gApi.changes().id(secondChangeId).current().review(reviewerInput);
+
+ return secondChangeId;
+ }
+
+ private DraftInput createInlineDraftInLine1(String comment, Side side, String inReplyTo) {
+ DraftInput draftInput = new DraftInput();
+ draftInput.line = 1;
+ draftInput.message = comment;
+ draftInput.path = FILE_NAME;
+ draftInput.side = side;
+ draftInput.unresolved = true;
+ draftInput.patchSet = 1;
+ draftInput.inReplyTo = inReplyTo;
+
+ return draftInput;
+ }
+
protected static CommentInput newComment(String path, Side side, int line, String message) {
CommentInput c = new CommentInput();
c.path = path;
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
index 45a471b..9322502 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
@@ -16,10 +16,14 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.entities.EmailHeader;
import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.CommentInfo;
import java.net.URI;
+import java.util.List;
import java.util.Map;
import org.junit.Test;
@@ -63,9 +67,33 @@
assertThat(headerString(headers, "In-Reply-To")).isEqualTo(threadId);
}
+ @Test
+ public void outgoingMailWithACommentsInUnchangedFile() throws Exception {
+ String adminInlineComment = "comment from admin in Line 1";
+ String userInlineComment =
+ String.format("reply from user %s to comment made by admin in Line 1", user.fullName());
+ String changeId =
+ createChangeWithUnchangedFileReviewed(user, adminInlineComment, userInlineComment);
+ List<CommentInfo> comments = gApi.changes().id(changeId).commentsRequest().getAsList();
+ assertThat(comments).hasSize(2);
+ String lastCommentId = Iterables.getLast(comments).id;
+ ChangeInfo changeInfo = gApi.changes().id(changeId).get();
+ String expectedBodyAsString =
+ "PS1, Line 1: this is line 1 \n" + "> " + adminInlineComment + "\n" + userInlineComment;
+ assertThat(sender.getMessages()).hasSize(1);
+ String bodyAsString = sender.getMessages().iterator().next().body();
+ assertThat(bodyAsString).contains(expectedBodyAsString);
+ assertThat(bodyAsString).contains(getChangeUrl(changeInfo) + "/comment/" + lastCommentId);
+ assertThat(bodyAsString).contains("File gerrit-server/test.txt:\n");
+ }
+
private String headerString(Map<String, EmailHeader> headers, String name) {
EmailHeader header = headers.get(name);
assertThat(header).isInstanceOf(StringEmailHeader.class);
return ((StringEmailHeader) header).getString();
}
+
+ private String getChangeUrl(ChangeInfo changeInfo) {
+ return canonicalWebUrl.get() + "c/" + changeInfo.project + "/+/" + changeInfo._number;
+ }
}