Merge branch 'stable-3.5' into stable-3.6
* stable-3.5:
Add missing binding of ProjectDeletedListener
Fix the Docker-based setup for the HA test environment
Verify high-availability formatting using GJF 1.7
Change-Id: Id5248ae07bd66a11fc3d17f42868c2f5397a205b
diff --git a/BUILD b/BUILD
index 2ee0faa..7f32bc1 100644
--- a/BUILD
+++ b/BUILD
@@ -47,5 +47,7 @@
":high-availability__plugin",
"@global-refdb//jar",
"@wiremock//jar",
+ "@jgroups//jar",
+ "@commons-net//jar"
],
)
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index da477e6..eaa632e 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -15,6 +15,6 @@
maven_jar(
name = "global-refdb",
- artifact = "com.gerritforge:global-refdb:3.5.4",
- sha1 = "6f96965d4cedd8b01b1fd9047d8c443c752bd675",
+ artifact = "com.gerritforge:global-refdb:3.6.3.1",
+ sha1 = "0f5229856d6a17e9c2382c8a404f43851f1f0287",
)
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/AccountReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/AccountReindexRunnable.java
index 70ef751..4a3f473 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/AccountReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/AccountReindexRunnable.java
@@ -54,7 +54,7 @@
protected Optional<Timestamp> indexIfNeeded(AccountState as, Timestamp sinceTs) {
try {
Account a = as.account();
- Timestamp accountTs = a.registeredOn();
+ Timestamp accountTs = Timestamp.from(a.registeredOn());
if (accountTs.after(sinceTs)) {
log.atInfo().log("Index %s/%s/%s/%s", a.id(), a.fullName(), a.preferredEmail(), accountTs);
accountIdx.index(a.id(), Operation.INDEX, Optional.empty());
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnable.java
index 2de2122..aa44770 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnable.java
@@ -101,7 +101,7 @@
@Override
protected Optional<Timestamp> indexIfNeeded(Change c, Timestamp sinceTs) {
try {
- Timestamp changeTs = c.getLastUpdatedOn();
+ Timestamp changeTs = Timestamp.from(c.getLastUpdatedOn());
if (changeTs.after(sinceTs)) {
log.atInfo().log(
"Index %s/%s/%s was updated after %s", c.getProject(), c.getId(), changeTs, sinceTs);
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java
index 4ded55e..05efe07 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java
@@ -30,6 +30,7 @@
import com.google.inject.Inject;
import java.io.IOException;
import java.sql.Timestamp;
+import java.time.Instant;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
@@ -73,7 +74,7 @@
Optional<InternalGroup> internalGroup = groups.getGroup(g.getUUID());
if (internalGroup.isPresent()) {
InternalGroup group = internalGroup.get();
- Timestamp groupCreationTs = group.getCreatedOn();
+ Timestamp groupCreationTs = Timestamp.from(group.getCreatedOn());
Repository allUsersRepo = repoManager.openRepository(allUsers);
@@ -81,23 +82,24 @@
groups.getSubgroupsAudit(allUsersRepo, g.getUUID());
Stream<Timestamp> groupIdAudAddedTs =
subGroupMembersAud.stream()
- .map(AccountGroupByIdAudit::addedOn)
+ .map(accountGroupByIdAudit -> Timestamp.from(accountGroupByIdAudit.addedOn()))
.filter(Objects::nonNull);
Stream<Timestamp> groupIdAudRemovedTs =
subGroupMembersAud.stream()
.map(AccountGroupByIdAudit::removedOn)
- .filter(Optional<Timestamp>::isPresent)
- .map(Optional<Timestamp>::get);
-
+ .filter(Optional<Instant>::isPresent)
+ .map(inst -> Timestamp.from(inst.get()));
List<AccountGroupMemberAudit> groupMembersAud =
groups.getMembersAudit(allUsersRepo, g.getUUID());
Stream<Timestamp> groupMemberAudAddedTs =
- groupMembersAud.stream().map(AccountGroupMemberAudit::addedOn).filter(Objects::nonNull);
+ groupMembersAud.stream()
+ .map(accountGroupByIdAudit -> Timestamp.from(accountGroupByIdAudit.addedOn()))
+ .filter(Objects::nonNull);
Stream<Timestamp> groupMemberAudRemovedTs =
groupMembersAud.stream()
.map(AccountGroupMemberAudit::removedOn)
- .filter(Optional<Timestamp>::isPresent)
- .map(Optional<Timestamp>::get);
+ .filter(Optional<Instant>::isPresent)
+ .map(inst -> Timestamp.from(inst.get()));
Optional<Timestamp> groupLastTs =
Streams.concat(
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
index a6539e0..d05c45d 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
@@ -32,6 +32,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
@@ -168,7 +169,8 @@
IndexName.CHANGE,
!changeNotes.isPresent()
? LocalDateTime.now()
- : changeNotes.get().getChange().getLastUpdatedOn().toLocalDateTime());
+ : Timestamp.from(changeNotes.get().getChange().getLastUpdatedOn())
+ .toLocalDateTime());
} catch (Exception e) {
log.atWarning().withCause(e).log("Unable to update the latest TS for change %d", id);
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ReindexRunnable.java
index 3cb0ed0..92ec127 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ReindexRunnable.java
@@ -53,18 +53,20 @@
int count = 0;
int errors = 0;
Stopwatch stopwatch = Stopwatch.createStarted();
+ Timestamp maxFetchedItemTs = Timestamp.valueOf(newLastIndexTs.toLocalDateTime());
for (T c : fetchItems()) {
try {
Optional<Timestamp> itemTs = indexIfNeeded(c, newLastIndexTs);
if (itemTs.isPresent()) {
count++;
- newLastIndexTs = maxTimestamp(newLastIndexTs, itemTs.get());
+ maxFetchedItemTs = maxTimestamp(maxFetchedItemTs, itemTs.get());
}
} catch (Exception e) {
log.atSevere().withCause(e).log("Unable to reindex %s %s", itemNameString, c);
errors++;
}
}
+ newLastIndexTs = maxTimestamp(newLastIndexTs, maxFetchedItemTs);
long elapsedNanos = stopwatch.stop().elapsed(TimeUnit.NANOSECONDS);
if (count > 0) {
log.atInfo().log(
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImpl.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImpl.java
index a8ba722..252b7e8 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImpl.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImpl.java
@@ -188,7 +188,7 @@
private long getTsFromChangeAndDraftComments(ChangeNotes notes) {
Change change = notes.getChange();
- Timestamp changeTs = change.getLastUpdatedOn();
+ Timestamp changeTs = Timestamp.from(change.getLastUpdatedOn());
for (HumanComment comment : commentsUtil.draftByChange(changeNotes.get())) {
Timestamp commentTs = comment.writtenOn;
changeTs = commentTs.after(changeTs) ? commentTs : changeTs;
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
index ce47390..0e3fdc0 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
@@ -16,6 +16,7 @@
import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.ericsson.gerrit.plugins.highavailability.peers.PeerInfo;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.events.LifecycleListener;
@@ -157,6 +158,16 @@
}
}
+ @VisibleForTesting
+ void setChannel(JChannel channel) {
+ this.channel = channel;
+ }
+
+ @VisibleForTesting
+ void setPeerInfo(Optional<PeerInfo> peerInfo) {
+ this.peerInfo = peerInfo;
+ }
+
@Override
public Set<PeerInfo> get() {
return peerInfo.isPresent() ? ImmutableSet.of(peerInfo.get()) : ImmutableSet.of();
@@ -178,4 +189,14 @@
peerInfo = Optional.empty();
peerAddress = null;
}
+
+ @VisibleForTesting
+ Address getPeerAddress() {
+ return peerAddress;
+ }
+
+ @VisibleForTesting
+ void setPeerAddress(Address peerAddress) {
+ this.peerAddress = peerAddress;
+ }
}
diff --git a/src/test/docker/gerrit/Dockerfile b/src/test/docker/gerrit/Dockerfile
index 4e64994..bbf9802 100644
--- a/src/test/docker/gerrit/Dockerfile
+++ b/src/test/docker/gerrit/Dockerfile
@@ -1,6 +1,6 @@
-FROM gerritcodereview/gerrit:3.5.0-rc2
+FROM gerritcodereview/gerrit:3.6.4
-ENV GERRIT_BRANCH=stable-3.5
+ENV GERRIT_BRANCH=stable-3.6
ENV GERRIT_CI_URL=https://archive-ci.gerritforge.com/job
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnableTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnableTest.java
index 65001de..e338dcb 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnableTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/ChangeReindexRunnableTest.java
@@ -59,8 +59,10 @@
@Mock private AllUsersName allUsers;
@Mock private ChangeNotes.Factory changeNotesFactory;
@Mock private Repository repo;
- @Mock private ChangeNotesResult changeNotesRes;
- @Mock private ChangeNotes changeNotes;
+ @Mock private ChangeNotesResult changeNotesResFirst;
+ @Mock private ChangeNotesResult changeNotesResSecond;
+ @Mock private ChangeNotes changeNotesFirst;
+ @Mock private ChangeNotes changeNotesSecond;
private ChangeReindexRunnable changeReindexRunnable;
@@ -75,7 +77,7 @@
public void changeIsIndexedWhenItIsCreatedAfterLastChangeReindex() throws Exception {
Timestamp currentTime = Timestamp.valueOf(LocalDateTime.now());
Timestamp afterCurrentTime = new Timestamp(currentTime.getTime() + 1000L);
- Change change = newChange(afterCurrentTime);
+ Change change = newChange(123, afterCurrentTime);
Optional<Timestamp> changeLastTs = changeReindexRunnable.indexIfNeeded(change, currentTime);
assertThat(changeLastTs.isPresent()).isTrue();
@@ -88,16 +90,17 @@
LocalDateTime currentTime = LocalDateTime.now(ZoneOffset.UTC);
Timestamp afterCurrentTime =
new Timestamp(currentTime.toEpochSecond(ZoneOffset.UTC) * 1000 + 1000L);
- Change change = newChange(afterCurrentTime);
+ Change change = newChange(123, afterCurrentTime);
when(indexTs.getUpdateTs(AbstractIndexRestApiServlet.IndexName.CHANGE))
.thenReturn(Optional.of(currentTime));
when(projectCache.all()).thenReturn(ImmutableSortedSet.of(change.getProject()));
when(repoManager.openRepository(change.getProject())).thenReturn(repo);
- when(changeNotesFactory.scan(repo, change.getProject())).thenReturn(Stream.of(changeNotesRes));
- lenient().when(changeNotesRes.error()).thenReturn(Optional.empty());
- when(changeNotesRes.notes()).thenReturn(changeNotes);
- when(changeNotes.getChange()).thenReturn(change);
+ when(changeNotesFactory.scan(repo, change.getProject()))
+ .thenReturn(Stream.of(changeNotesResFirst));
+ lenient().when(changeNotesResFirst.error()).thenReturn(Optional.empty());
+ when(changeNotesResFirst.notes()).thenReturn(changeNotesFirst);
+ when(changeNotesFirst.getChange()).thenReturn(change);
changeReindexRunnable.run();
@@ -109,7 +112,7 @@
LocalDateTime currentTime = LocalDateTime.now(ZoneOffset.UTC);
Timestamp afterCurrentTime =
new Timestamp(currentTime.toEpochSecond(ZoneOffset.UTC) * 1000 + 1000L);
- Change change = newChange(afterCurrentTime);
+ Change change = newChange(123, afterCurrentTime);
when(indexTs.getUpdateTs(AbstractIndexRestApiServlet.IndexName.CHANGE))
.thenReturn(Optional.of(currentTime));
@@ -119,10 +122,10 @@
lenient().when(invalidChangeRes.notes()).thenThrow(IllegalStateException.class);
when(invalidChangeRes.error()).thenReturn(Optional.of(new IllegalStateException()));
when(changeNotesFactory.scan(repo, change.getProject()))
- .thenReturn(Stream.of(invalidChangeRes, changeNotesRes));
- when(changeNotesRes.error()).thenReturn(Optional.empty());
- when(changeNotesRes.notes()).thenReturn(changeNotes);
- when(changeNotes.getChange()).thenReturn(change);
+ .thenReturn(Stream.of(invalidChangeRes, changeNotesResFirst));
+ when(changeNotesResFirst.error()).thenReturn(Optional.empty());
+ when(changeNotesResFirst.notes()).thenReturn(changeNotesFirst);
+ when(changeNotesFirst.getChange()).thenReturn(change);
changeReindexRunnable.run();
@@ -133,7 +136,7 @@
public void groupIsNotIndexedWhenItIsCreatedBeforeLastGroupReindex() throws Exception {
Timestamp currentTime = Timestamp.valueOf(LocalDateTime.now());
Timestamp beforeCurrentTime = new Timestamp(currentTime.getTime() - 1000L);
- Change change = newChange(beforeCurrentTime);
+ Change change = newChange(123, beforeCurrentTime);
Optional<Timestamp> changeLastTs = changeReindexRunnable.indexIfNeeded(change, currentTime);
assertThat(changeLastTs.isPresent()).isFalse();
@@ -141,16 +144,46 @@
.index(changeProjectIndexKey(change), Operation.INDEX, Optional.empty());
}
+ @Test
+ public void twoChangesAreIndexedDuringTheRunWhenItIsCreatedAfterLastChangeReindex()
+ throws Exception {
+ LocalDateTime currentTime = LocalDateTime.now(ZoneOffset.UTC);
+ Timestamp afterCurrentTime =
+ new Timestamp(currentTime.toEpochSecond(ZoneOffset.UTC) * 1000 + 1000L);
+ Timestamp secondAfterCurrentTime =
+ new Timestamp(currentTime.toEpochSecond(ZoneOffset.UTC) * 1000 + 2000L);
+ Change firstChange = newChange(123, secondAfterCurrentTime);
+ Change secondChange = newChange(456, afterCurrentTime);
+
+ when(indexTs.getUpdateTs(AbstractIndexRestApiServlet.IndexName.CHANGE))
+ .thenReturn(Optional.of(currentTime));
+ when(projectCache.all()).thenReturn(ImmutableSortedSet.of(firstChange.getProject()));
+ when(repoManager.openRepository(firstChange.getProject())).thenReturn(repo);
+ when(changeNotesFactory.scan(repo, firstChange.getProject()))
+ .thenReturn(Stream.of(changeNotesResFirst, changeNotesResSecond));
+ lenient().when(changeNotesResFirst.error()).thenReturn(Optional.empty());
+ lenient().when(changeNotesResSecond.error()).thenReturn(Optional.empty());
+ when(changeNotesResFirst.notes()).thenReturn(changeNotesFirst);
+ when(changeNotesResSecond.notes()).thenReturn(changeNotesSecond);
+ when(changeNotesFirst.getChange()).thenReturn(firstChange);
+ when(changeNotesSecond.getChange()).thenReturn(secondChange);
+
+ changeReindexRunnable.run();
+
+ verify(indexer).index(changeProjectIndexKey(firstChange), Operation.INDEX, Optional.empty());
+ verify(indexer).index(changeProjectIndexKey(secondChange), Operation.INDEX, Optional.empty());
+ }
+
private String changeProjectIndexKey(Change change) {
return change.getProject() + "~" + change.getChangeId();
}
- private Change newChange(Timestamp changeTs) {
+ private Change newChange(int id, Timestamp changeTs) {
return new Change(
Change.key("changekey"),
- Change.id(123),
+ Change.id(id),
Account.id(1000000),
BranchNameKey.create("projectname", "main"),
- changeTs);
+ changeTs.toInstant());
}
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnableTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnableTest.java
index afd6df4..0513d42 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnableTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnableTest.java
@@ -110,7 +110,7 @@
Collections.singletonList(
AccountGroupMemberAudit.builder()
.addedBy(Account.Id.tryParse("1").get())
- .addedOn(afterCurrentTime)
+ .addedOn(afterCurrentTime.toInstant())
.memberId(Account.Id.tryParse("1").get())
.groupId(AccountGroup.Id.parse("1"))
.build()));
@@ -130,10 +130,10 @@
AccountGroupMemberAudit accountGroupMemberAudit =
AccountGroupMemberAudit.builder()
.addedBy(Account.Id.tryParse("1").get())
- .addedOn(beforeCurrentTime)
+ .addedOn(beforeCurrentTime.toInstant())
.memberId(Account.Id.tryParse("1").get())
.groupId(AccountGroup.Id.parse("2"))
- .removed(Account.Id.tryParse("2").get(), afterCurrentTime)
+ .removed(Account.Id.tryParse("2").get(), afterCurrentTime.toInstant())
.build();
when(groups.getGroup(uuid)).thenReturn(getInternalGroup(currentTime));
when(groups.getMembersAudit(any(), any()))
@@ -158,7 +158,7 @@
.groupId(AccountGroup.Id.parse("1"))
.includeUuid(UUID.parse("123"))
.addedBy(Account.Id.tryParse("1").get())
- .addedOn(afterCurrentTime)
+ .addedOn(afterCurrentTime.toInstant())
.build()));
Optional<Timestamp> groupLastTs =
groupReindexRunnable.indexIfNeeded(groupReference, currentTime);
@@ -178,8 +178,8 @@
.groupId(AccountGroup.Id.parse("1"))
.includeUuid(UUID.parse("123"))
.addedBy(Account.Id.tryParse("1").get())
- .addedOn(beforeCurrentTime)
- .removed(Account.Id.tryParse("2").get(), afterCurrentTime)
+ .addedOn(beforeCurrentTime.toInstant())
+ .removed(Account.Id.tryParse("2").get(), afterCurrentTime.toInstant())
.build();
when(groups.getGroup(uuid)).thenReturn(getInternalGroup(beforeCurrentTime));
@@ -201,7 +201,7 @@
.setOwnerGroupUUID(uuid)
.setVisibleToAll(true)
.setGroupUUID(UUID.parse("12"))
- .setCreatedOn(timestamp)
+ .setCreatedOn(timestamp.toInstant())
.setMembers(ImmutableSet.<Id>builder().build())
.setSubgroups(ImmutableSet.<UUID>builder().build())
.build());
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java
index 7c1e6df..ba0c8c5 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedIndexChangeHandlerTest.java
@@ -30,8 +30,8 @@
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.util.OneOffRequestContext;
-import com.google.gerrit.server.util.time.TimeUtil;
import java.io.IOException;
+import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import org.junit.Before;
@@ -67,7 +67,7 @@
@Before
public void setUp() throws Exception {
id = Change.id(TEST_CHANGE_NUMBER);
- Change change = new Change(null, id, null, null, TimeUtil.nowTs());
+ Change change = new Change(null, id, null, null, Instant.now());
when(changeNotes.getChange()).thenReturn(change);
when(configMock.index()).thenReturn(indexMock);
when(changeCheckerFactoryMock.create(any())).thenReturn(changeCheckerAbsentMock);
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImplTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImplTest.java
new file mode 100644
index 0000000..38f71e5
--- /dev/null
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/ChangeCheckerImplTest.java
@@ -0,0 +1,84 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.ericsson.gerrit.plugins.highavailability.index;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.ChangeFinder;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.util.OneOffRequestContext;
+import java.io.IOException;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ChangeCheckerImplTest {
+
+ @Mock private GitRepositoryManager gitRepoMgr;
+ @Mock private CommentsUtil commentsUtil;
+ @Mock private ChangeFinder changeFinder;
+ @Mock private OneOffRequestContext oneOffReqCtx;
+ @Mock private ChangeNotes testChangeNotes;
+ @Mock private Change testChange;
+
+ private final Instant testLastUpdatedOn = Instant.now();
+ private final String changeId = "1";
+ Optional<IndexEvent> event = Optional.empty();
+ private Optional<Long> computedChangeTs = Optional.empty();
+ private ChangeCheckerImpl changeChecker;
+
+ @Before
+ public void setUp() {
+ changeChecker =
+ new ChangeCheckerImpl(gitRepoMgr, commentsUtil, changeFinder, oneOffReqCtx, changeId);
+ }
+
+ @Test
+ public void testGetChangeNotes() {
+ when(changeFinder.findOne(changeId)).thenReturn(Optional.of(testChangeNotes));
+ assertThat(changeChecker.getChangeNotes()).isEqualTo(Optional.of(testChangeNotes));
+ }
+
+ @Test
+ public void testGetComputedChangeTs() {
+ long testTime = Timestamp.from(testLastUpdatedOn).getTime();
+ computedChangeTs = Optional.of(testTime / 1000);
+ when(changeChecker.getChangeNotes()).thenReturn(Optional.of(testChangeNotes));
+ when(testChangeNotes.getChange()).thenReturn(testChange);
+ when(testChange.getLastUpdatedOn()).thenReturn(testLastUpdatedOn);
+ assertThat(changeChecker.getComputedChangeTs()).isEqualTo(computedChangeTs);
+ }
+
+ @Test
+ public void testNewIndexEventWhenChangeTimeStampIsEmpty() throws IOException {
+ assertThat(changeChecker.newIndexEvent().isPresent()).isFalse();
+ }
+
+ @Test
+ public void testIsChangeUpToDateWhenComputedChangeTsIsNotPresent() throws IOException {
+ assertThat(changeChecker.isChangeUpToDate(event)).isFalse();
+ }
+}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
index 4793678..7ed5f55 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
@@ -22,7 +22,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import com.ericsson.gerrit.plugins.highavailability.Configuration;
@@ -78,6 +78,7 @@
private static final String OTHER_UUID = "4";
private static final Integer INDEX_WAIT_TIMEOUT_MS = 5;
private static final int MAX_TEST_PARALLELISM = 4;
+ private static final String EXECUTOR_THREAD_NAME = "EXECUTOR_THREAD";
private IndexEventHandler indexEventHandler;
@Mock private Forwarder forwarder;
@@ -93,6 +94,7 @@
@Mock private RequestContext mockCtx;
@Mock private Configuration configuration;
private IndexEventLocks idLocks;
+ private Thread executorThread;
private CurrentRequestContext currCtx =
new CurrentRequestContext(null, null, null) {
@@ -157,6 +159,7 @@
@Test
public void shouldIndexInRemoteOnChangeIndexedEvent() throws Exception {
indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
+ executorThread.join();
verify(forwarder).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
}
@@ -259,7 +262,7 @@
setUpIndexEventHandler(currCtx, locks);
indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
-
+ executorThread.join();
verify(locks, times(2)).withLock(any(), any(), any());
verify(forwarder, times(1)).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
}
@@ -278,9 +281,8 @@
when(httpCfg.maxTries()).thenReturn(10);
when(cfg.http()).thenReturn(httpCfg);
setUpIndexEventHandler(currCtx, locks, cfg);
-
indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
-
+ executorThread.join();
verify(locks, times(11)).withLock(any(), any(), any());
verify(forwarder, never()).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
}
@@ -301,7 +303,7 @@
setUpIndexEventHandler(currCtx, locks, cfg);
indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
-
+ executorThread.join();
verify(locks, times(1)).withLock(any(), any(), any());
verify(forwarder, never()).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
}
@@ -321,8 +323,9 @@
setUpIndexEventHandler(currCtx, locks);
indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
+ executorThread.join();
indexEventHandler.onAccountIndexed(accountId.get());
-
+ executorThread.join();
verify(forwarder, never()).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
verify(forwarder).indexAccount(eq(ACCOUNT_ID), any());
}
@@ -335,29 +338,32 @@
Configuration.Index cfgIndex = mock(Configuration.Index.class);
when(cfgMock.index()).thenReturn(cfgIndex);
when(cfgIndex.synchronizeForced()).thenReturn(true);
-
setUpIndexEventHandler(new CurrentRequestContext(threadLocalCtxMock, cfgMock, oneOffCtxMock));
+
indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
+ executorThread.join();
verify(forwarder).indexChange(eq(PROJECT_NAME), eq(CHANGE_ID), any());
}
@Test
public void shouldIndexInRemoteOnAccountIndexedEvent() throws Exception {
indexEventHandler.onAccountIndexed(accountId.get());
+ executorThread.join();
verify(forwarder).indexAccount(eq(ACCOUNT_ID), any());
}
@Test
public void shouldDeleteFromIndexInRemoteOnChangeDeletedEvent() throws Exception {
indexEventHandler.onChangeDeleted(changeId.get());
+ executorThread.join();
verify(forwarder).deleteChangeFromIndex(eq(CHANGE_ID), any());
- verifyZeroInteractions(
- changeCheckerMock); // Deleted changes should not be checked against NoteDb
+ verifyNoInteractions(changeCheckerMock); // Deleted changes should not be checked against NoteDb
}
@Test
public void shouldIndexInRemoteOnGroupIndexedEvent() throws Exception {
indexEventHandler.onGroupIndexed(accountGroupUUID.get());
+ executorThread.join();
verify(forwarder).indexGroup(eq(UUID), any());
}
@@ -367,7 +373,7 @@
indexEventHandler.onChangeIndexed(PROJECT_NAME, changeId.get());
indexEventHandler.onChangeDeleted(changeId.get());
Context.unsetForwardedEvent();
- verifyZeroInteractions(forwarder);
+ verifyNoInteractions(forwarder);
}
@Test
@@ -376,7 +382,7 @@
indexEventHandler.onAccountIndexed(accountId.get());
indexEventHandler.onAccountIndexed(accountId.get());
Context.unsetForwardedEvent();
- verifyZeroInteractions(forwarder);
+ verifyNoInteractions(forwarder);
}
@Test
@@ -385,7 +391,7 @@
indexEventHandler.onGroupIndexed(accountGroupUUID.get());
indexEventHandler.onGroupIndexed(accountGroupUUID.get());
Context.unsetForwardedEvent();
- verifyZeroInteractions(forwarder);
+ verifyNoInteractions(forwarder);
}
@Test
@@ -686,7 +692,6 @@
}
private class CurrentThreadScheduledExecutorService implements ScheduledExecutorService {
-
@Override
public void shutdown() {}
@@ -752,7 +757,9 @@
@Override
public void execute(Runnable command) {
- command.run();
+ executorThread = new Thread(command);
+ executorThread.setName(EXECUTOR_THREAD_NAME);
+ executorThread.start();
}
@Override
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/InetAddressFinderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/InetAddressFinderTest.java
index bfcffbb..c375345 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/InetAddressFinderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/InetAddressFinderTest.java
@@ -16,10 +16,20 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.google.common.collect.ImmutableList;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,11 +42,68 @@
@Mock(answer = RETURNS_DEEP_STUBS)
private Configuration configuration;
+ @Mock private NetworkInterface mockInterface;
+
private InetAddressFinder finder;
+ private List<NetworkInterface> testNetworkInterfaces;
@Before
public void setUp() {
finder = new InetAddressFinder(configuration);
+ testNetworkInterfaces = new ArrayList<>();
+ }
+
+ @Test
+ public void testNoSuitableInterfaceWhenFindFirstAppropriateAddress() throws SocketException {
+ when(mockInterface.isLoopback()).thenReturn(true);
+ when(configuration.jgroups().skipInterface()).thenReturn(ImmutableList.of("mockInterface1"));
+ testNetworkInterfaces.add(mockInterface);
+ assertThat(finder.findFirstAppropriateAddress(testNetworkInterfaces).isPresent()).isFalse();
+ }
+
+ @Test
+ public void testOptionalEmptyIsReturnedWhenFindFirstAppropriateAddress() throws SocketException {
+ setUpCustomMockInterfaceMocks();
+ when(configuration.jgroups().skipInterface()).thenReturn(ImmutableList.of());
+ testNetworkInterfaces.add(mockInterface);
+ Enumeration mockInetAddresses = mock(Enumeration.class);
+
+ when(mockInterface.getInetAddresses()).thenReturn(mockInetAddresses);
+ assertThat(finder.findFirstAppropriateAddress(testNetworkInterfaces))
+ .isEqualTo(Optional.empty());
+ }
+
+ @Test
+ public void testInet6AddressIsReturnedWhenFindFirstAppropriateAddress() throws SocketException {
+ setUpCustomMockInterfaceMocks();
+ when(configuration.jgroups().skipInterface()).thenReturn(ImmutableList.of());
+ testNetworkInterfaces.add(mockInterface);
+ Inet6Address mockInet6Address = mock(Inet6Address.class);
+ List<Inet6Address> mocklist = new ArrayList<>();
+ mocklist.add(mockInet6Address);
+ Enumeration mockInetAddresses = Collections.enumeration(mocklist);
+
+ when(mockInterface.getInetAddresses()).thenReturn(mockInetAddresses);
+ assertThat(finder.findFirstAppropriateAddress(testNetworkInterfaces))
+ .isEqualTo(Optional.of(mockInet6Address));
+ }
+
+ @Test
+ public void testInet4AddressIsReturnedWhenFindFirstAppropriateAddress() throws SocketException {
+ setUpCustomMockInterfaceMocks();
+ when(configuration.jgroups().skipInterface()).thenReturn(ImmutableList.of());
+ System.setProperty("java.net.preferIPv4Stack", "true");
+ testNetworkInterfaces.add(mockInterface);
+ Inet4Address mockInet4Address = mock(Inet4Address.class);
+ List<Inet4Address> mocklist = new ArrayList<>();
+ mocklist.add(mockInet4Address);
+
+ Enumeration mockInetAddresses = Collections.enumeration(mocklist);
+ when(mockInterface.getInetAddresses()).thenReturn(mockInetAddresses);
+
+ finder = new InetAddressFinder(configuration);
+ assertThat(finder.findFirstAppropriateAddress(testNetworkInterfaces))
+ .isEqualTo(Optional.of(mockInet4Address));
}
@Test
@@ -64,4 +131,10 @@
assertThat(finder.shouldSkip("foo1")).isTrue();
assertThat(finder.shouldSkip("bar")).isFalse();
}
+
+ private void setUpCustomMockInterfaceMocks() throws SocketException {
+ when(mockInterface.isLoopback()).thenReturn(false);
+ when(mockInterface.isUp()).thenReturn(true);
+ when(mockInterface.supportsMulticast()).thenReturn(true);
+ }
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java
new file mode 100644
index 0000000..1110fc3
--- /dev/null
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java
@@ -0,0 +1,158 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.ericsson.gerrit.plugins.highavailability.peers.jgroups;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.when;
+
+import com.ericsson.gerrit.plugins.highavailability.Configuration;
+import com.ericsson.gerrit.plugins.highavailability.peers.PeerInfo;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.jgroups.Address;
+import org.jgroups.JChannel;
+import org.jgroups.Message;
+import org.jgroups.View;
+import org.jgroups.stack.IpAddress;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Test.None;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class JGroupsPeerInfoProviderTest {
+
+ @Mock(answer = RETURNS_DEEP_STUBS)
+ private Configuration pluginConfigurationMock;
+
+ private InetAddressFinder finder;
+ private JGroupsPeerInfoProvider jGroupsPeerInfoProvider;
+ private Optional<PeerInfo> peerInfo;
+ @Mock private JChannel channel;
+ @Mock private MyUrlProvider myUrlProviderTest;
+ @Mock private Message message;
+ @Mock private Address peerAddress;
+ @Mock private View view;
+ @Mock private List<Address> members;
+
+ @Before
+ public void setUp() throws Exception {
+ finder = new InetAddressFinder(pluginConfigurationMock);
+ jGroupsPeerInfoProvider =
+ new JGroupsPeerInfoProvider(pluginConfigurationMock, finder, myUrlProviderTest);
+ peerInfo = Optional.of(new PeerInfo("test message"));
+ channel.setName("testChannel");
+ }
+
+ @Test
+ public void testRecieveWhenPeerAddressIsNull() {
+ when(message.getSrc()).thenReturn(peerAddress);
+ when(message.getObject()).thenReturn("test message");
+
+ jGroupsPeerInfoProvider.receive(message);
+
+ assertThat(jGroupsPeerInfoProvider.getPeerAddress()).isEqualTo(peerAddress);
+ Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+ for (PeerInfo testPeerInfo : testPeerInfoSet) {
+ assertThat(testPeerInfo.getDirectUrl()).contains("test message");
+ }
+ assertThat(testPeerInfoSet.size()).isEqualTo(1);
+ }
+
+ @Test
+ public void testReceiveWhenPeerAddressIsNotNull() throws Exception {
+ jGroupsPeerInfoProvider.setPeerAddress(new IpAddress("checkAddress.com"));
+
+ jGroupsPeerInfoProvider.receive(message);
+
+ Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+ assertThat(testPeerInfoSet.isEmpty()).isTrue();
+ assertThat(testPeerInfoSet.size()).isEqualTo(0);
+ }
+
+ @Test(expected = None.class)
+ public void testViewAcceptedWithNoExceptionThrown() throws Exception {
+ when(view.getMembers()).thenReturn(members);
+ when(view.size()).thenReturn(3);
+ when(members.size()).thenReturn(3);
+ jGroupsPeerInfoProvider.setChannel(channel);
+ jGroupsPeerInfoProvider.viewAccepted(view);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testViewAcceptedWithExceptionThrown() throws Exception {
+ when(view.getMembers()).thenReturn(members);
+ when(view.size()).thenReturn(2);
+ when(members.size()).thenReturn(2);
+ jGroupsPeerInfoProvider.viewAccepted(view);
+ }
+
+ @Test
+ public void testViewAcceptedWhenPeerAddressIsNotNullAndIsNotMemberOfView() {
+ when(view.getMembers()).thenReturn(members);
+ when(view.size()).thenReturn(2);
+ when(members.size()).thenReturn(2);
+ when(members.contains(peerAddress)).thenReturn(false);
+ jGroupsPeerInfoProvider.setPeerAddress(peerAddress);
+ jGroupsPeerInfoProvider.setPeerInfo(peerInfo);
+ jGroupsPeerInfoProvider.setChannel(channel);
+ jGroupsPeerInfoProvider.viewAccepted(view);
+
+ assertThat(jGroupsPeerInfoProvider.getPeerAddress()).isEqualTo(null);
+ Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+ assertThat(testPeerInfoSet.isEmpty()).isTrue();
+ assertThat(testPeerInfoSet.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testConnect() throws NoSuchFieldException, IllegalAccessException {
+ jGroupsPeerInfoProvider.connect();
+ Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+ assertThat(testPeerInfoSet.isEmpty()).isTrue();
+ assertThat(testPeerInfoSet.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetWhenPeerInfoIsOptionalEmpty() {
+ Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+ assertThat(testPeerInfoSet.isEmpty()).isTrue();
+ assertThat(testPeerInfoSet.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetWhenPeerInfoIsPresent() {
+ jGroupsPeerInfoProvider.setPeerInfo(peerInfo);
+ Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+ for (PeerInfo testPeerInfo : testPeerInfoSet) {
+ assertThat(testPeerInfo.getDirectUrl()).contains("test message");
+ }
+ assertThat(testPeerInfoSet.size()).isEqualTo(1);
+ }
+
+ @Test
+ public void testStop() throws Exception {
+ jGroupsPeerInfoProvider.setPeerAddress(peerAddress);
+ jGroupsPeerInfoProvider.setPeerInfo(peerInfo);
+ jGroupsPeerInfoProvider.stop();
+ assertThat(jGroupsPeerInfoProvider.getPeerAddress()).isEqualTo(null);
+ Set<PeerInfo> testPeerInfoSet = jGroupsPeerInfoProvider.get();
+ assertThat(testPeerInfoSet.isEmpty()).isTrue();
+ assertThat(testPeerInfoSet.size()).isEqualTo(0);
+ }
+}
diff --git a/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d
--- /dev/null
+++ b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline