/*
* Copyright 2014 WANdisco
*
* WANdisco licenses this file to you 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 c5db.replication;
import c5db.RpcMatchers;
import c5db.interfaces.replication.QuorumConfiguration;
import c5db.interfaces.replication.ReplicatorInstanceEvent;
import c5db.replication.generated.LogEntry;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import java.util.List;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
/**
* Assorted matchers used for replicator tests.
*/
class ReplicationMatchers {
static Matcher<ReplicatorInstanceEvent> leaderElectedEvent(Matcher<Long> leaderMatcher,
Matcher<Long> termMatcher) {
return new TypeSafeMatcher<ReplicatorInstanceEvent>() {
@Override
protected boolean matchesSafely(ReplicatorInstanceEvent item) {
return item.eventType == ReplicatorInstanceEvent.EventType.LEADER_ELECTED
&& leaderMatcher.matches(item.newLeader)
&& termMatcher.matches(item.leaderElectedTerm);
}
@Override
public void describeTo(Description description) {
description.appendText("a ReplicatorInstanceEvent indicating a leader was elected")
.appendText(" with id ").appendDescriptionOf(leaderMatcher)
.appendText(" with term ").appendDescriptionOf(termMatcher);
}
};
}
static Matcher<ReplicatorInstanceEvent> aQuorumChangeCommittedEvent(QuorumConfiguration configuration,
Matcher<Long> fromMatcher) {
return new TypeSafeMatcher<ReplicatorInstanceEvent>() {
@Override
protected boolean matchesSafely(ReplicatorInstanceEvent item) {
return item.eventType == ReplicatorInstanceEvent.EventType.QUORUM_CONFIGURATION_COMMITTED
&& fromMatcher.matches(item.instance.getId())
&& item.configuration.equals(configuration);
}
@Override
public void describeTo(Description description) {
description.appendText("a ReplicatorInstanceEvent indicating quorum configuration ").appendValue(configuration)
.appendText(" was committed from replicator with ID ").appendDescriptionOf(fromMatcher);
}
};
}
static Matcher<ReplicatorInstanceEvent> aReplicatorEvent(ReplicatorInstanceEvent.EventType type) {
return new TypeSafeMatcher<ReplicatorInstanceEvent>() {
@Override
protected boolean matchesSafely(ReplicatorInstanceEvent item) {
return item.eventType == type;
}
@Override
public void describeTo(Description description) {
description.appendText("a ReplicatorInstanceEvent of type ").appendValue(type);
}
};
}
static Matcher<InRamTest.PeerController> theLeader() {
return new TypeSafeMatcher<InRamTest.PeerController>() {
@Override
protected boolean matchesSafely(InRamTest.PeerController peer) {
return peer.isCurrentLeader();
}
@Override
public void describeTo(Description description) {
description.appendText("The peer who is the leader of the current term");
}
};
}
static Matcher<InRamTest.PeerController> wonAnElectionWithTerm(Matcher<Long> termMatcher) {
return new TypeSafeMatcher<InRamTest.PeerController>() {
@Override
protected boolean matchesSafely(InRamTest.PeerController peer) {
return peer.hasWonAnElection(termMatcher);
}
@Override
public void describeTo(Description description) {
description.appendText("a peer who won an election with term ").appendDescriptionOf(termMatcher);
}
};
}
static Matcher<InRamTest.PeerController> hasCommittedEntriesUpTo(long index) {
return new TypeSafeMatcher<InRamTest.PeerController>() {
@Override
protected boolean matchesSafely(InRamTest.PeerController peer) {
return peer.hasCommittedEntriesUpTo(index);
}
@Override
public void describeTo(Description description) {
description.appendText("Peer has committed log entries up to index" + index);
}
};
}
static Matcher<InRamTest.PeerController> willCommitEntriesUpTo(long index) {
return new TypeSafeMatcher<InRamTest.PeerController>() {
Throwable matchException;
@Override
protected boolean matchesSafely(InRamTest.PeerController peer) {
try {
peer.waitForCommit(index);
assert peer.log.getLastIndex() >= index;
} catch (Exception e) {
matchException = e;
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("Peer will commit log entries up to index " + index);
}
@Override
public void describeMismatchSafely(InRamTest.PeerController peer, Description description) {
if (matchException != null) {
description.appendValue(matchException.toString());
}
}
};
}
static Matcher<InRamTest.PeerController> willCommitConfiguration(QuorumConfiguration configuration) {
return new TypeSafeMatcher<InRamTest.PeerController>() {
Throwable matchException;
@Override
protected boolean matchesSafely(InRamTest.PeerController peer) {
try {
peer.waitForQuorumCommit(configuration);
} catch (Exception e) {
matchException = e;
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("Peer will commit the quorum configuration ").appendValue(configuration);
}
@Override
public void describeMismatchSafely(InRamTest.PeerController peer, Description description) {
if (matchException != null) {
description.appendValue(matchException.toString());
}
}
};
}
static Matcher<InRamTest.PeerController> willRespondToAnAppendRequest(long minimumTerm) {
return new TypeSafeMatcher<InRamTest.PeerController>() {
Throwable matchException;
@Override
protected boolean matchesSafely(InRamTest.PeerController peer) {
try {
peer.waitForAppendReply(greaterThanOrEqualTo(minimumTerm));
} catch (Exception e) {
matchException = e;
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("Peer will respond to an AppendEntries request");
}
@Override
public void describeMismatchSafely(InRamTest.PeerController peer, Description description) {
if (matchException != null) {
description.appendValue(matchException.toString());
}
}
};
}
static Matcher<InRamTest.PeerController> willSend(RpcMatchers.RequestMatcher requestMatcher) {
return new TypeSafeMatcher<InRamTest.PeerController>() {
Throwable matchException;
@Override
protected boolean matchesSafely(InRamTest.PeerController peer) {
try {
peer.waitForRequest(requestMatcher);
} catch (Exception e) {
matchException = e;
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("Peer will send an AppendEntries request ").appendDescriptionOf(requestMatcher);
}
@Override
public void describeMismatchSafely(InRamTest.PeerController peer, Description description) {
if (matchException != null) {
description.appendValue(matchException.toString());
}
}
};
}
static Matcher<List<LogEntry>> aListOfEntriesWithConsecutiveSeqNums(long start, long end) {
return new TypeSafeMatcher<List<LogEntry>>() {
@Override
protected boolean matchesSafely(List<LogEntry> entries) {
if (entries.size() != (end - start)) {
return false;
}
long expectedIndex = start;
for (LogEntry entry : entries) {
if (entry.getIndex() != expectedIndex) {
return false;
}
expectedIndex++;
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("a list of LogEntry with consecutive indexes from ")
.appendValue(start).appendText(" inclusive to ").appendValue(end).appendText(" exclusive");
}
};
}
}