public static <T> void waitForIt(final Supplier<T> valueSupplier,
                                     final T requiredValue,
                                     final Supplier<String> messageSupplier,
                                     final Duration timeout,
                                     final Duration pollFrequency,
                                     final Duration showProgressFrequency) {

        final Instant startTime = Instant.now();
        final Instant endTime = startTime.plus(timeout);
        Instant lastProgressUpdateTime = null;
        T currValue = null;
        boolean firstIteration = true;
        while (Instant.now().isBefore(endTime)) {
            currValue = valueSupplier.get();
            if (Objects.equals(currValue, requiredValue)) {
                LOGGER.debug("Waited {}", Duration.between(startTime, Instant.now()));
                return;
            } else {
                if (firstIteration) {
                    firstIteration = false;
                } else {
                    if (!showProgressFrequency.equals(Duration.ZERO)) {
                        final Duration timeSinceLastUpdate = Duration.between(
                                NullSafe.coalesce(lastProgressUpdateTime, startTime)
                                        .orElseThrow(), // startTime never null, so should not happen
                                Instant.now());

                        if (timeSinceLastUpdate.compareTo(showProgressFrequency) > 0) {
                            LOGGER.info("Still waiting for '{}' to be '{} ({})'. " +
                                        "Last value '{} ({})'. Waited {} so far.",
                                    messageSupplier.get(),
                                    requiredValue,
                                    NullSafe.getOrElse(
                                            requiredValue, Object::getClass, Class::getSimpleName, "null"),
                                    currValue,
                                    NullSafe.getOrElse(
                                            currValue, Object::getClass, Class::getSimpleName, "null"),
                                    Duration.between(startTime, Instant.now()));
                            lastProgressUpdateTime = Instant.now();
                        }
                    }
                }
                ThreadUtil.sleep(pollFrequency.toMillis());
            }
        }

        // Timed out so throw
        throw new RuntimeException(LogUtil.message("Timed out (timeout: {}) waiting for '{}' to be '{}'. " +
                                                   "Last value '{}'",
                timeout,
                messageSupplier.get(),
                requiredValue,
                currValue));
    }
Repeatedly call test with pollFrequency until it returns true, or timeout is reached.
If timeout is reached before test returns true a {@link RuntimeException} is thrown.

@param valueSupplier         Supplier of the value to test. This will be called repeatedly until
                             its return value match requiredValue, or timeout is reached.
@param requiredValue         The value that valueSupplier is required to ultimately return.
@param messageSupplier       Supplier of the name of the thing being waited for.
@param timeout               The timeout duration after which waitForIt will give up and throw
                             a {@link RuntimeException}.
@param pollFrequency         The time between calls to valueSupplier.
@param showProgressFrequency The time between progress update logged to info, set to zero to not show any.
@throws stroom.util.concurrent.UncheckedInterruptedException If sleep is interrupted
stroom-test-common/src/main/java/stroom/test/common/TestUtil.java