/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Jun 2, 2010
*/
package com.bigdata.quorum;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import junit.framework.AssertionFailedError;
import com.bigdata.quorum.MockQuorumFixture.MockQuorumMember;
/**
* Test the quorum semantics for a singleton quorum. This test suite allows us
* to verify that each quorum state change is translated into the appropriate
* methods against the public API of the quorum client or quorum member.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id: TestSingletonQuorumSemantics.java 2984 2010-06-06 22:10:32Z
* thompsonbry $
*/
public class TestSingletonQuorumSemantics extends AbstractQuorumTestCase {
/**
*
*/
public TestSingletonQuorumSemantics() {
}
/**
* @param name
*/
public TestSingletonQuorumSemantics(String name) {
super(name);
}
@Override
protected void setUp() throws Exception {
k = 1;
super.setUp();
}
/**
* Unit test for quorum member add/remove.
*
* @throws InterruptedException
*/
public void test_memberAddRemove() throws InterruptedException {
final Quorum<?, ?> quorum = quorums[0];
final QuorumMember<?> client = clients[0];
final QuorumActor<?,?> actor = actors[0];
final UUID serviceId = client.getServiceId();
// client is not a member.
assertFalse(client.isMember());
assertEquals(new UUID[] {}, quorum.getMembers());
// instruct actor to add client as a member.
actor.memberAdd();
fixture.awaitDeque();
// client is a member.
assertTrue(client.isMember());
assertEquals(new UUID[] {serviceId}, quorum.getMembers());
// instruct actor to remove client as a member.
actor.memberRemove();
fixture.awaitDeque();
// client is not a member.
assertFalse(client.isMember());
assertEquals(new UUID[] {}, quorum.getMembers());
}
/**
* Unit test for quorum member add followed by the termination of the quorum
* client. This checks for proper termination of the client, including the
* clear down of the quorum's internal state.
*
* @throws InterruptedException
*/
public void test_memberAdd_terminateClient() throws InterruptedException {
final Quorum<?, ?> quorum = quorums[0];
final QuorumMember<?> client = clients[0];
final QuorumActor<?,?> actor = actors[0];
final UUID serviceId = client.getServiceId();
// client is not a member.
assertFalse(client.isMember());
assertEquals(new UUID[] {}, quorum.getMembers());
// instruct actor to add client as a member.
actor.memberAdd();
fixture.awaitDeque();
// client is a member.
assertTrue(client.isMember());
assertEquals(new UUID[] {serviceId}, quorum.getMembers());
/*
* Verify termination of the quorum for that client.
*/
assertEquals(client, quorum.getClient());
quorum.terminate();
try {
quorum.getClient();
} catch (IllegalStateException ex) {
log.info("Ignoring expected exception: " + ex);
}
// State was cleared.
assertEquals(Quorum.NO_QUORUM, quorum.token());
assertEquals(Quorum.NO_QUORUM, quorum.lastValidToken());
assertEquals(new UUID[] {}, quorum.getMembers());
assertEquals(new UUID[] {}, quorum.getJoined());
assertEquals(new UUID[] {}, quorum.getPipeline());
assertEquals(Collections.emptyMap(), quorum.getVotes());
try {
// Note: Quorum reference was cleared. Client throws exception.
assertFalse(client.isMember());
} catch (IllegalStateException ex) {
log.info("Ignoring expected exception: " + ex);
}
// Double-termination is safe.
quorum.terminate();
}
/**
* Unit test for write pipeline add/remove.
* @throws InterruptedException
*/
public void test_pipelineAddRemove() throws InterruptedException {
final Quorum<?, ?> quorum = quorums[0];
final MockQuorumMember<?> client = clients[0];
final QuorumActor<?,?> actor = actors[0];
final UUID serviceId = client.getServiceId();
assertFalse(client.isMember());
assertNull(client.downStreamId);
assertFalse(client.isPipelineMember());
assertEquals(new UUID[]{},quorum.getPipeline());
actor.memberAdd();
fixture.awaitDeque();
/*
* add to the pipeline. since this is a singleton quorum, the downstream
* service will remain null.
*/
assertNull(client.downStreamId);
actor.pipelineAdd();
fixture.awaitDeque();
assertNull(client.downStreamId);
assertTrue(client.isPipelineMember());
assertEquals(new UUID[]{serviceId},quorum.getPipeline());
/*
* remove from the pipeline. since this is a singleton quorum, the
* downstream service will remain null.
*/
assertNull(client.downStreamId);
actor.pipelineRemove();
fixture.awaitDeque();
assertNull(client.downStreamId);
assertFalse(client.isPipelineMember());
assertEquals(new UUID[]{},quorum.getPipeline());
actor.memberRemove();
fixture.awaitDeque();
assertFalse(client.isMember());
}
/**
* Runs a specific unit test a bunch of times looking for instances where
* the test does not run within the target time period.
*
* @see #test_voting()
*/
static public void main(final String[] args) throws Exception {
// count anything as a "hang" which takes longer than this to run.
final long hangTime = 1000;
// #of times to run the test.
final int limit = 100;
// run the test.
int nhang = 0;
int nerr = 0;
int nrun = 0;
for (; nrun < limit; nrun++) {
final TestSingletonQuorumSemantics t = new TestSingletonQuorumSemantics(
Integer.toString(nrun));
final long begin = System.currentTimeMillis();
t.setUp();
try {
t.test_voting();
} catch (AssertionFailedError e) {
log.error(e,e);
nerr++;
} finally {
t.tearDown();
}
final long elapsed = System.currentTimeMillis() - begin;
if (elapsed > hangTime) {
nhang++;
// System.err.print('.');
// if (nhang % 50 == 0)
// System.err.println("");
}
System.err.println("hang=" + (elapsed > hangTime ? "Y" : "N")
+ ", nhang=" + nhang + ", nerr=" + nerr + ", nrun=" + nrun
+ ", elapsed=" + elapsed + "\n");
System.err.flush();
}
// System.err.println("\nnhang=" + nhang + ", nerr=" + nerr + ", nrun="
// + nrun);
if (nerr > 0)
fail("There were " + nerr + " failures.");
}
/**
* Unit test for the voting protocol for a singleton quorum.
*
* FIXME For some reason this unit test occasionally takes much longer to
* run than would otherwise be expected (up to a few seconds versus a small
* fraction of a second). You can see this in the timestamps of the logger.
* <p>
* The test terminates when the fixture tears down the AbstractQuorum's
* internal watcher action service, which has a hung action. If the
* WatcherActionService is also single-threaded, then this could clearly
* lead to a deadlock since there would be no thread available to handle new
* events.
* <p>
* It is awaiting the quorumBreak condition in
* AbstractQuorumActor.clearToken(). This issue may be that we have two
* distinct signals for quorumBreak versus quorumMeet which need to be
* combined and then the various methods modified to also test the condition
* variable. [I've made that change.]
* <p>
* It seems likely that either a concurrent watcherActionService -or- a
* finite timeout would get the unit tests to pass. However, only the former
* would work around a deadlock due to a stuck Condition.
* <p>
* Look again at what Condition is getting stuck and at the stress test in
* {@link #main(String[])} for causes. [nhang=17, nerr=0, nrun=1000]. There
* are several different causes, each of which clearly reflects a different
* ordering of the events.
* <p>
* It maybe that we see the problem with a singleton quorum because there
* are no other sources of events to kick the quorum into motion again once
* it fails to join under the initial impetus.
* <p>
* The problem appears to stem from withdrawing the cast vote before the
* quorum meets.
*/
public void test_voting() throws InterruptedException,
AsynchronousQuorumCloseException, TimeoutException {
/*
* When true, this test also exercises the awaitQuorum() and
* awaitBreak() methods that accept a timeout, but only for the case in
* which the condition should be true on entry. There is another unit
* test in this class that verifies that the TimeoutException is
* correctly thrown if the condition does not become true within the
* timeout.
*/
final boolean awaitMeetsAndBreaks = true;
final Quorum<?, ?> quorum = quorums[0];
final MockQuorumMember<?> client = clients[0];
final QuorumActor<?,?> actor = actors[0];
final UUID serviceId = client.getServiceId();
final long lastCommitTime1 = 0L;
final long lastCommitTime2 = 2L;
// Verify that no consensus has been achieved yet.
assertEquals(-1L, clients[0].lastConsensusValue);
// add as member service.
actor.memberAdd();
fixture.awaitDeque();
assertTrue(clients[0].isMember());
// join the pipeline.
actor.pipelineAdd();
fixture.awaitDeque();
// Verify that timestamps must be non-negative.
try {
actor.castVote(-1L);
fail("Expected " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
// Should not be any votes.
assertEquals(0,quorum.getVotes().size());
// Cast a vote.
actor.castVote(lastCommitTime1);
fixture.awaitDeque();
// Should be just one vote.
assertEquals(1,quorum.getVotes().size());
// Verify the consensus was updated
assertEquals(lastCommitTime1, client.lastConsensusValue);
if (awaitMeetsAndBreaks)
assertEquals(Quorum.NO_QUORUM + 1, quorum.awaitQuorum(100,
TimeUnit.MILLISECONDS));
if(awaitMeetsAndBreaks) {
actor.withdrawVote();
quorum.awaitBreak();
/*
* FIXME I have modified castVote() to automatically add the service
* to the pipeline. Otherwise we must do this explicitly after a
* quorum break. Maybe the better approach to take is to have each
* service self-report its lastCommitTime and have Quorum#start(M)
* automatically strive towards a service join while
* Quorum#terminate() causes leaves and then halts processing for
* the service. Alternatively, each client could set its target
* state in MEMBER, PIPELINE, JOIN. That would allow clients to
* manage resynchronization, which they need to do.
*/
// actor.pipelineAdd();
}
// Cast another vote.
actor.castVote(lastCommitTime2);
fixture.awaitDeque();
// Should be just one vote since a service can only vote for one
// lastCommitTime at a time.
if (quorum.getVotes().size() != 1) {
assertEquals(quorum.getVotes().toString(), 1, quorum.getVotes()
.size());
}
// Verify the consensus was updated again.
assertEquals(lastCommitTime2, client.lastConsensusValue);
if (awaitMeetsAndBreaks)
assertEquals(Quorum.NO_QUORUM + 2, quorum.awaitQuorum(100,
TimeUnit.MILLISECONDS));
// Remove as a member.
actor.memberRemove();
fixture.awaitDeque();
assertFalse(clients[0].isMember());
// The service vote was also removed.
assertEquals(0,quorum.getVotes().size());
if (awaitMeetsAndBreaks)
quorum.awaitBreak(100, TimeUnit.MILLISECONDS);
}
/**
* Unit test for the protocol up to a service join, which triggers a leader
* election. Since the singleton quorum has only one member our client will
* be elected the leader.
*
* @throws InterruptedException
*/
public void test_serviceJoin() throws InterruptedException {
final AbstractQuorum<?, ?> quorum = quorums[0];
final MockQuorumMember<?> client = clients[0];
final QuorumActor<?,?> actor = actors[0];
final UUID serviceId = client.getServiceId();
final long lastCommitTime = 0L;
final long lastCommitTime2 = 2L;
// declare the service as a quorum member.
actor.memberAdd();
fixture.awaitDeque();
assertTrue(client.isMember());
assertEquals(new UUID[]{serviceId},quorum.getMembers());
// add to the pipeline.
actor.pipelineAdd();
fixture.awaitDeque();
assertTrue(client.isPipelineMember());
assertEquals(new UUID[]{serviceId},quorum.getPipeline());
// cast a vote for a lastCommitTime.
actor.castVote(lastCommitTime);
fixture.awaitDeque();
assertEquals(1,quorum.getVotes().size());
assertEquals(new UUID[] { serviceId }, quorum.getVotes().get(
lastCommitTime));
// verify the consensus was updated.
assertEquals(lastCommitTime, client.lastConsensusValue);
// wait for quorum meet.
final long token1 = quorum.awaitQuorum();
// verify service was joined.
assertTrue(client.isJoinedMember(quorum.token()));
assertEquals(new UUID[] { serviceId }, quorum.getJoined());
// validate the token was assigned.
fixture.awaitDeque();
assertEquals(Quorum.NO_QUORUM + 1, quorum.lastValidToken());
assertEquals(Quorum.NO_QUORUM + 1, quorum.token());
assertTrue(quorum.isQuorumMet());
/*
* Do service leave, quorum should break.
*/
actor.serviceLeave();
fixture.awaitDeque();
quorum.awaitBreak();
// vote was withdrawn.
assertEquals(0,quorum.getVotes().size());
assertEquals(null,quorum.getVotes().get(lastCommitTime));
// verify the consensus was updated.
assertEquals(-1L, client.lastConsensusValue);
assertFalse(quorum.isQuorumMet());
assertEquals(Quorum.NO_QUORUM, quorum.token());
assertEquals(token1, quorum.lastValidToken());
assertFalse(client.isJoinedMember(quorum.token()));
assertEquals(new UUID[]{},quorum.getJoined());
assertFalse(client.isPipelineMember());
assertEquals(new UUID[]{},quorum.getPipeline());
/*
* Cast another vote, the quorum should meet again.
*/
actor.pipelineAdd();
fixture.awaitDeque();
actor.castVote(lastCommitTime2);
fixture.awaitDeque();
assertEquals(1,quorum.getVotes().size());
assertEquals(null,quorum.getVotes().get(lastCommitTime));
assertEquals(new UUID[] { serviceId }, quorum.getVotes().get(
lastCommitTime2));
// verify the consensus was updated.
assertEquals(lastCommitTime2, client.lastConsensusValue);
// await meet.
final long token2 = quorum.awaitQuorum();
// verify the joined services.
assertEquals(new UUID[] { serviceId }, quorum.getJoined());
// validate the token was assigned by the leader.
assertTrue(quorum.isQuorumMet());
assertEquals(token1 + 1, token2);
assertEquals(token1 + 1, quorum.lastValidToken());
assertTrue(client.isJoinedMember(token2));
assertTrue(client.isLeader(token2));
assertFalse(client.isFollower(token2));
/*
* Do service leave, quorum should break again.
*/
actor.serviceLeave();
fixture.awaitDeque();
quorum.awaitBreak();
// vote was withdrawn.
assertEquals(0,quorum.getVotes().size());
assertEquals(null,quorum.getVotes().get(lastCommitTime));
assertEquals(null,quorum.getVotes().get(lastCommitTime2));
// verify the consensus was updated.
assertEquals(-1L, client.lastConsensusValue);
assertFalse(quorum.isQuorumMet());
assertEquals(Quorum.NO_QUORUM, quorum.token());
assertEquals(token2, quorum.lastValidToken());
assertFalse(client.isJoinedMember(quorum.token()));
assertEquals(new UUID[]{},quorum.getJoined());
assertFalse(client.isPipelineMember());
assertEquals(new UUID[]{},quorum.getPipeline());
}
/**
* Unit test verifying that we clear down the quorum's reflection of the
* distributed quorum state where we first have a quorum meet and then
* terminate the quorum client.
*
* @throws InterruptedException
*/
public void test_serviceJoin_terminateClient() throws InterruptedException {
final AbstractQuorum<?, ?> quorum = quorums[0];
final MockQuorumMember<?> client = clients[0];
final QuorumActor<?,?> actor = actors[0];
final UUID serviceId = client.getServiceId();
final long lastCommitTime = 0L;
final long lastCommitTime2 = 2L;
// declare the service as a quorum member.
actor.memberAdd();
fixture.awaitDeque();
assertTrue(client.isMember());
assertEquals(new UUID[]{serviceId},quorum.getMembers());
// add to the pipeline.
actor.pipelineAdd();
fixture.awaitDeque();
assertTrue(client.isPipelineMember());
assertEquals(new UUID[]{serviceId},quorum.getPipeline());
// cast a vote for a lastCommitTime.
actor.castVote(lastCommitTime);
fixture.awaitDeque();
assertEquals(1,quorum.getVotes().size());
assertEquals(new UUID[] { serviceId }, quorum.getVotes().get(
lastCommitTime));
// verify the consensus was updated.
assertEquals(lastCommitTime, client.lastConsensusValue);
// wait for quorum meet.
final long token1 = quorum.awaitQuorum();
// verify service was joined.
assertTrue(client.isJoinedMember(quorum.token()));
assertEquals(new UUID[] { serviceId }, quorum.getJoined());
// validate the token was assigned.
fixture.awaitDeque();
assertEquals(Quorum.NO_QUORUM + 1, quorum.lastValidToken());
assertEquals(Quorum.NO_QUORUM + 1, quorum.token());
assertTrue(quorum.isQuorumMet());
/*
* Terminate the quorum. The state should be cleared down.
*/
// Verify termination of the quorum for that client.
assertEquals(client, quorum.getClient());
// terminate the quorum.
quorum.terminate();
try {
quorum.getClient();
} catch (IllegalStateException ex) {
log.info("Ignoring expected exception: " + ex);
}
// State was cleared.
assertEquals(Quorum.NO_QUORUM, quorum.token());
assertEquals(Quorum.NO_QUORUM, quorum.lastValidToken());
assertEquals(new UUID[] {}, quorum.getMembers());
assertEquals(new UUID[] {}, quorum.getJoined());
assertEquals(new UUID[] {}, quorum.getPipeline());
assertEquals(Collections.emptyMap(), quorum.getVotes());
try {
// Note: Quorum reference was cleared. Client throws exception.
assertFalse(client.isMember());
} catch (IllegalStateException ex) {
log.info("Ignoring expected exception: " + ex);
}
// re-terminate() is safe.
quorum.terminate();
}
/**
* Unit test of timeout in {@link Quorum#awaitQuorum(long, TimeUnit)}. and
* {@link Quorum#awaitBreak(long, TimeUnit)}.
*
* @throws AsynchronousQuorumCloseException
* @throws InterruptedException
*/
public void test_awaitQuorum() throws AsynchronousQuorumCloseException, InterruptedException {
final AbstractQuorum<?, ?> quorum = quorums[0];
final MockQuorumMember<?> client = clients[0];
final QuorumActor<?,?> actor = actors[0];
final UUID serviceId = client.getServiceId();
final long lastCommitTime = 0L;
final long lastCommitTime2 = 2L;
// declare the service as a quorum member.
actor.memberAdd();
fixture.awaitDeque();
assertTrue(client.isMember());
assertEquals(new UUID[]{serviceId},quorum.getMembers());
// add to the pipeline.
actor.pipelineAdd();
fixture.awaitDeque();
assertTrue(client.isPipelineMember());
assertEquals(new UUID[]{serviceId},quorum.getPipeline());
final long timeout = 1500;// ms
final long slop = 100;// margin of error.
{
/*
* Verify that a we timeout when awaiting a quorum meet that does
* not occur.
*/
final AtomicLong didTimeout = new AtomicLong(-1L);
final Thread t = new Thread() {
public void run() {
final long begin = System.currentTimeMillis();
try {
// wait for a quorum (but will not meet).
log.info("Waiting for quorum meet.");
quorum.awaitQuorum(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
// This is what we are looking for.
final long elapsed = System.currentTimeMillis() - begin;
didTimeout.set(elapsed);
if (log.isInfoEnabled())
log.info("Timeout after " + elapsed + "ms");
} catch (Exception e) {
log.error(e, e);
}
}
};
t.run();
Thread.sleep(timeout + 250/* ms */);
t.interrupt();
final long elapsed = didTimeout.get();
assertTrue("did not timeout", elapsed != -1);
assertTrue("Timeout occurred too soon: elapsed=" + elapsed
+ ",timeout=" + timeout, elapsed >= timeout);
assertTrue("Timeout took too long: elapsed=" + elapsed
+ ",timeout=" + timeout, elapsed < (timeout + slop));
}
// cast a vote for a lastCommitTime.
actor.castVote(lastCommitTime);
fixture.awaitDeque();
assertEquals(1,quorum.getVotes().size());
assertEquals(new UUID[] { serviceId }, quorum.getVotes().get(
lastCommitTime));
// verify the consensus was updated.
assertEquals(lastCommitTime, client.lastConsensusValue);
// wait for quorum meet.
final long token1 = quorum.awaitQuorum();
// verify service was joined.
assertTrue(client.isJoinedMember(quorum.token()));
assertEquals(new UUID[] { serviceId }, quorum.getJoined());
// validate the token was assigned.
fixture.awaitDeque();
assertEquals(Quorum.NO_QUORUM + 1, quorum.lastValidToken());
assertEquals(Quorum.NO_QUORUM + 1, quorum.token());
assertTrue(quorum.isQuorumMet());
{
/*
* Verify that we timeout when awaiting a quorum break that does not
* occur.
*/
final AtomicLong didTimeout = new AtomicLong(-1L);
final Thread t = new Thread() {
public void run() {
final long begin = System.currentTimeMillis();
try {
// wait for a quorum break (but will not break).
log.info("Waiting for quorum break.");
quorum.awaitBreak(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
// This is what we are looking for.
final long elapsed = System.currentTimeMillis() - begin;
didTimeout.set(elapsed);
if (log.isInfoEnabled())
log.error("Timeout after " + elapsed + "ms");
} catch (Exception e) {
log.error(e, e);
}
}
};
t.run();
Thread.sleep(timeout + 250/* ms */);
t.interrupt();
final long elapsed = didTimeout.get();
assertTrue("did not timeout", elapsed != -1);
assertTrue("Timeout occurred too soon: elapsed=" + elapsed
+ ",timeout=" + timeout, elapsed >= timeout);
assertTrue("Timeout took too long: elapsed=" + elapsed
+ ",timeout=" + timeout, elapsed < (timeout + slop));
}
try {
// Verify awaitBreak() does not return normally.
quorum.awaitBreak(1, TimeUnit.MILLISECONDS);
fail("Not expecting quorum break");
} catch (TimeoutException e) {
if (log.isInfoEnabled())
log.info("Ignoring expected excption: " + e);
}
/*
* Do service leave, quorum should break.
*/
actor.serviceLeave();
fixture.awaitDeque();
quorum.awaitBreak();
try {
// Verify awaitBreak() returns normally.
quorum.awaitBreak(1, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
fail("Not expecting " + e, e);
}
}
/**
* Check all valid quorum transitions with:
* currentToken,
* nextToken,
* isJoined state
* haReady token.
*
* Compare tokens to check Quorum state.
* Compare tokens with haReady and isJoined to check service state.
*
* Possible outcomes are:
* quorum didBreak
* quorum didMeet
* service didLeave
* service didJoin
*
* Clearly didBreak/didMeet and didLeave/didJoin are exclusive (or should be),
* but what about didMeet and didLeave or didBreak and didJoin?
*
* TODO This test only verifies the expected presence or absence of an
* assertion error thrown by the {@link QuorumTokenTransitions} class.
* It should do actual post-condition checks on the public fields of
* that class.
*/
public void testQuorumTransitions() {
// combinations are possible current/new/haReady - each with a true/false isJoined
final long tokens[][] = new long[][] {
// current, new, haReady
new long[] {-1, -1, -1}, // wasn't met, not met, wasn't joined
new long[] {-1, 0, -1}, // met
new long[] {0, 0, -1}, // remains met
new long[] {0, -1, -1}, // break
new long[] {-1, -1, 0}, // notmet, wasJoined
new long[] {-1, 0, 0}, // met, wasJoined
new long[] {0, 0, 0}, // remains met, wasJoined
new long[] {0, -1, 0}, // break, wasJoined
// Are these scenarios plausible only as a result of locking problem
// or possibly Zookeeper bounce?
// new long[] {0, 2, -1}, // token bumped more than one
// new long[] {0, 2, 0}, // token bumped more than one
// new long[] {0, 2, 1}, // token bumped more than one
// new long[] {0, 2, 2}, // token bumped more than one
};
for (int i = 0; i < tokens.length; i++) {
final long[] tst = tokens[i];
// token combinations with isJoined == true
final QuorumTokenTransitions qtjoined = new QuorumTokenTransitions(tst[0]/*current token*/, tst[1]/*new token*/, true/*isJoined*/, tst[2]/*haReady*/);
// token combinations with isJoined == false
final QuorumTokenTransitions qtnotjoined = new QuorumTokenTransitions(tst[0]/*current token*/, tst[1]/*new token*/, false/*isJoined*/, tst[2]/*haReady*/);
}
// test invalid scenarios to confirm AssertionErrors are thrown
try {
new QuorumTokenTransitions(1/*current token*/, 2/*new token*/, true/*isJoined*/, 1/*haReady*/);
fail("Expected assertion error, cannot progress quorum token without break");
} catch (AssertionError ae) {
// expected;
}
try {
new QuorumTokenTransitions(2/*current token*/, 1/*new token*/, true/*isJoined*/, 1/*haReady*/);
fail("Expected assertion error, new valid < current valid");
} catch (AssertionError ae) {
// expected;
}
try {
new QuorumTokenTransitions(1/*current token*/, 1/*new token*/, true/*isJoined*/, 2/*haReady*/);
fail("Expected assertion error, haReady > newToken");
} catch (AssertionError ae) {
// expected;
}
try {
new QuorumTokenTransitions(1/*current token*/, 2/*new token*/, true/*isJoined*/, 2/*haReady*/);
fail("Expected assertion error, haReady > currentToken");
} catch (AssertionError ae) {
// expected;
}
}
}