@Test
 public void testCustomQuorumFunctionIsPresent() throws Exception {
   Config config = new Config();
   QuorumConfig quorumConfig = new QuorumConfig();
   String quorumName = randomString();
   quorumConfig.setName(quorumName);
   quorumConfig.setEnabled(true);
   quorumConfig.setQuorumFunctionImplementation(
       new QuorumFunction() {
         @Override
         public boolean apply(Collection<Member> members) {
           return false;
         }
       });
   config.addQuorumConfig(quorumConfig);
   String mapName = randomMapName();
   MapConfig mapConfig = new MapConfig(mapName);
   mapConfig.setQuorumName(quorumName);
   config.addMapConfig(mapConfig);
   HazelcastInstance hazelcastInstance = createHazelcastInstance(config);
   IMap<Object, Object> map = hazelcastInstance.getMap(mapName);
   try {
     map.put("1", "1");
     fail();
   } catch (Exception e) {
   }
   Quorum quorum = hazelcastInstance.getQuorumService().getQuorum(quorumName);
   assertFalse(quorum.isPresent());
 }
  /**
   * 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());
  }
 // Verify that Duplicate Channel result in a failure
 @Test(expected = IOException.class)
 public void testDuplicateChannelFailure() throws IOException {
   quorum1 = new Quorum("q1test", hive, channel1);
   quorum1.addChannel(channel2);
   assertTrue(quorum1.getList().getNodesMap().containsValue(channel2));
   quorum1.addChannel(channel2);
   fail();
 }
 // Add Multiple Channels to a Quorum
 @Test
 public void testAddN() throws IOException {
   quorum1 = new Quorum("q1test", hive, channel1);
   quorum1.addChannel(channel2);
   assertTrue(quorum1.getList().getNodesMap().containsValue(channel2));
   quorum1.addChannel(channel3);
   assertTrue(quorum1.getList().getNodesMap().containsValue(channel3));
 }
 // Verify that the Quorum Creation behave appropriately
 @Test
 public void testQuorumCreation() throws IOException {
   quorum1 = new Quorum("q1test", hive, channel1);
   assertEquals(quorum1.Id, "q1test");
   assertTrue(quorum1.getList().getNodesMap().containsValue(channel1));
   assertTrue(hive.contains("q1test"));
   assertTrue(quorum1.isAlive());
 }
 // Fail adding a channel to Non-Existent Room
 @Test(expected = IOException.class)
 public void testSendMessageToNonExistentRoom() throws IOException {
   quorum1 = new Quorum("q1test", hive, channel1);
   quorum1.removeChannel(channel1);
   Utility.pause(500);
   quorum1.addChannel(channel2);
   fail();
 }
 // Verify that Duplicate Quourms added to the Hive Fails
 @Test
 public void testDuplicateQuorumFailure() {
   try {
     quorum1 = new Quorum("q1test", hive, channel1);
     quorumOne = new Quorum("q1test", hive, channel1);
     fail();
   } catch (IOException e) {
   }
   assertTrue(quorum1.getList().getNodesMap().containsValue(channel1));
   assertTrue(hive.contains("q1test"));
   assertTrue(hive.getQuorumsMap().containsValue(quorum1));
   assertFalse(hive.getQuorumsMap().containsValue(quorumOne));
   assertTrue(quorum1.isAlive());
 }
  /**
   * 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());
  }
  // Send message to the entire room
  @Test
  public void testSendMessageToQuorum() throws IOException {
    quorum1 = new Quorum("q1test", hive, channel1);
    quorum1.addChannel(channel2);
    assertTrue(quorum1.getList().getNodesMap().containsValue(channel2));
    quorum1.addChannel(channel3);
    assertTrue(quorum1.getList().getNodesMap().containsValue(channel3));

    channel1.getBuffer().clear();
    channel2.getBuffer().clear();
    channel3.getBuffer().clear();

    quorum1.updateBuffer("test-test-test");
    Utility.pause(500);

    String s1 = channel1.getBuffer().poll();
    assertEquals(s1, "Message (q1test): test-test-test");
    assertEquals(channel1.getBuffer().poll(), null);
    assertEquals(channel2.getBuffer().poll(), "Message (q1test): test-test-test");
    assertEquals(channel2.getBuffer().poll(), null);
    assertEquals(channel3.getBuffer().poll(), "Message (q1test): test-test-test");
    assertEquals(channel3.getBuffer().poll(), null);
  }
  // Remove all channels from Quorum and verify that the Quorum is removed
  @Test
  public void testRemoveAllChannels() throws IOException {
    quorum1 = new Quorum("q1test", hive, channel1);
    quorum1.addChannel(channel2);
    assertTrue(quorum1.getList().getNodesMap().containsValue(channel2));
    quorum1.addChannel(channel3);
    assertTrue(quorum1.getList().getNodesMap().containsValue(channel3));

    quorum1.removeChannel(channel1);
    quorum1.removeChannel(channel2);
    quorum1.removeChannel(channel3);
    Utility.pause(500);

    assertFalse(quorum1.getList().getNodesMap().containsValue(channel2));
    assertFalse(quorum1.getList().getNodesMap().containsValue(channel1));
    assertFalse(quorum1.getList().getNodesMap().containsValue(channel3));

    assertFalse(hive.contains("q1test"));
    assertFalse(hive.getQuorumsMap().containsValue(quorum1));
    assertFalse(quorum1.isAlive());
  }
  /**
   * 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 the voting protocol for a singleton quorum.
   *
   * <p>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);
  }