@Test
  public void locatorWaitsForLocatorWaitTimeUntilAllLocatorsContacted()
      throws InterruptedException, TimeoutException, ExecutionException {

    final Supplier<ExecutorService> executorServiceSupplier =
        () -> LoggingExecutors.newCachedThreadPool("membership", false);

    int[] locatorPorts = AvailablePortHelper.getRandomAvailableTCPPorts(2);

    int locatorWaitTime = (int) Duration.ofMinutes(5).getSeconds();
    final MembershipConfig config =
        createMembershipConfig(true, locatorWaitTime, locatorPorts[0], locatorPorts[1]);

    /*
     * Start a locator trying to contact the locator that hasn't started it's port
     *
     * Set its locator-wait-time so it'll not become a coordinator right away, allowing time for the
     * other member to start and become a coordinator.
     */
    CompletableFuture<Membership<MemberIdentifier>> createMembership0 =
        launchLocator(executorServiceSupplier, locatorPorts[0], config);

    // minimum duration a locator waits to become the coordinator, regardless of locatorWaitTime
    final Duration minimumJoinWaitTime = Duration
        // amount of sleep time per retry in GMSJoinLeave.join()
        .ofMillis(JOIN_RETRY_SLEEP + FIND_LOCATOR_RETRY_SLEEP)
        // expected number of retries in GMSJoinLeave.join()
        .multipliedBy(getMinimumRetriesBeforeBecomingCoordinator(locatorPorts.length));

    /*
     * By sleeping for 2x the minimumJoinWaitTime, we are trying to make sure we sleep for
     * longer than the minimum but shorter than the locatorWaitTime so we can detect whether the
     * lateJoiningMembership is waiting for the full locatorWaitTime and not just the minimum
     * wait time.
     */
    Thread.sleep(2 * minimumJoinWaitTime.toMillis());

    assertThat(createMembership0.getNow(null)).isNull();

    /*
     * Now start the other locator, after waiting longer than the minimum wait time for
     * connecting to a locator but shorter than the locator-wait-time.
     */
    CompletableFuture<Membership<MemberIdentifier>> createMembership1 =
        launchLocator(executorServiceSupplier, locatorPorts[1], config);


    // Make sure the members are created in less than the locator-wait-time
    Membership<MemberIdentifier> membership0 = createMembership0.get(2, TimeUnit.MINUTES);
    Membership<MemberIdentifier> membership1 = createMembership1.get(2, TimeUnit.MINUTES);

    // Make sure the members see each other in the view
    await().untilAsserted(() -> assertThat(membership0.getView().getMembers()).hasSize(2));
    await().untilAsserted(() -> assertThat(membership1.getView().getMembers()).hasSize(2));

    stop(membership0, membership1);
  }
Tests of using the membership APIs to make multiple Membership systems that communicate
with each other and form a group
geode-membership/src/integrationTest/java/org/apache/geode/distributed/internal/membership/gms/MembershipIntegrationTest.java