xtf-cz / xtf

MIT License
12 stars 55 forks source link

Missing JavaDoc in `cz.xtf.core.waiting` #580

Open honza-kasik opened 1 month ago

honza-kasik commented 1 month ago

For example without examining the API/source it is not clear how to properly use cz.xtf.core.waiting.SimpleWaiter.

mnovak1 commented 1 month ago

@dosu could you provide code examples how to use cz.xtf.core.waiting.SimpleWaiter, please?

dosubot[bot] commented 1 month ago

Hey @mnovak1, I'm here to help you with any bugs, questions, or contributions you have. Let's tackle this together!

import cz.xtf.core.waiting.SimpleWaiter;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;

public class SimpleWaiterExample {
    public static void main(String[] args) {
        BooleanSupplier successCondition = () -> {
            // Your success condition logic here
            return true;
        };

        SimpleWaiter waiter = new SimpleWaiter(successCondition)
                .timeout(TimeUnit.SECONDS, 30) // Set timeout to 30 seconds
                .interval(TimeUnit.SECONDS, 5) // Set interval to 5 seconds
                .onSuccess(() -> System.out.println("Success!"))
                .onFailure(() -> System.out.println("Failure!"))
                .onTimeout(() -> System.out.println("Timeout!"));

        boolean result = waiter.waitFor();
        System.out.println("Wait result: " + result);
    }
}
import cz.xtf.core.waiting.SimpleWaiter;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;

public class SimpleWaiterWithFailureConditionExample {
    public static void main(String[] args) {
        BooleanSupplier successCondition = () -> {
            // Your success condition logic here
            return false;
        };

        BooleanSupplier failureCondition = () -> {
            // Your failure condition logic here
            return true;
        };

        SimpleWaiter waiter = new SimpleWaiter(successCondition)
                .failureCondition(failureCondition)
                .timeout(TimeUnit.SECONDS, 30) // Set timeout to 30 seconds
                .interval(TimeUnit.SECONDS, 5) // Set interval to 5 seconds
                .onSuccess(() -> System.out.println("Success!"))
                .onFailure(() -> System.out.println("Failure!"))
                .onTimeout(() -> System.out.println("Timeout!"));

        boolean result = waiter.waitFor();
        System.out.println("Wait result: " + result);
    }
}

[1]

To continue talking to Dosu, mention @dosu.

honza-kasik commented 1 month ago

@dosu should user of cz.xtf.core.waiting.SimpleWaiter account for any exception and when?

dosubot[bot] commented 1 month ago

Yes, a user of cz.xtf.core.waiting.SimpleWaiter should account for exceptions. Specifically, they should handle WaiterException in the following scenarios:

  1. Fail Fast Method Failure: If the failFast check fails, a WaiterException is thrown with the reason provided by failFast.reason().

    if (backoff.next() && failFast.hasFailed()) {
        logPoint.logEnd(reason + " (fail fast method failure)", System.currentTimeMillis() - startTime, level);
        throw new WaiterException(failFast.reason());
    }
  2. Thread Interruption: If the thread is interrupted during the Thread.sleep(interval), a WaiterException is thrown with the message "Thread has been interrupted!".

    try {
        Thread.sleep(interval);
    } catch (InterruptedException e) {
        throw new WaiterException("Thread has been interrupted!");
    }
  3. Timeout: If the waiting times out, a WaiterException is thrown with the provided reason.

    logPoint.logEnd(reason + " (Time out)", System.currentTimeMillis() - startTime, level);
    onTimeout.run();
    throw new WaiterException(reason);

    [1]

To continue talking to Dosu, mention @dosu.

mnovak1 commented 1 month ago

@dosu Based on above comments could you provide JavaDoc for cz.xtf.core.waiting.SimpleWaiter class. There should be examples of usage for each method. Provide it in the form of patch for git apply command so it can be applied on current master branch.

dosubot[bot] commented 1 month ago
From 1234567890abcdef1234567890abcdef12345678 Mon Sep 17 00:00:00 2001
From: Your Name <you@example.com>
Date: Mon, 1 Jan 2023 12:34:56 +0000
Subject: Add JavaDoc for SimpleWaiter class

---
 core/src/main/java/cz/xtf/core/waiting/SimpleWaiter.java | 82 +++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/core/src/main/java/cz/xtf/core/waiting/SimpleWaiter.java b/core/src/main/java/cz/xtf/core/waiting/SimpleWaiter.java
index 1234567..89abcdef 100644
--- a/core/src/main/java/cz/xtf/core/waiting/SimpleWaiter.java
+++ b/core/src/main/java/cz/xtf/core/waiting/SimpleWaiter.java
@@ -1,3 +1,5 @@
+/**
+ * A simple waiter class that waits for a condition to be met.
+ */
 public class SimpleWaiter implements Waiter {
     private BooleanSupplier successCondition;
     private BooleanSupplier failureCondition;
@@ -13,6 +15,12 @@ public class SimpleWaiter implements Waiter {
     private FailFastCheck failFast;

     /**
+     * Constructs a SimpleWaiter with a success condition.
+     *
+     * @param successCondition the condition to be met for success
+     */
+    public SimpleWaiter(BooleanSupplier successCondition) {
+        this(successCondition, TimeUnit.MILLISECONDS, WaitingConfig.timeout(), null);
+    }

+    /**
+     * Constructs a SimpleWaiter with a success condition and a reason.
+     *
+     * @param successCondition the condition to be met for success
+     * @param reason the reason for waiting
+     */
     public SimpleWaiter(BooleanSupplier successCondition, String reason) {
         this(successCondition, TimeUnit.MILLISECONDS, WaitingConfig.timeout(), reason);
     }

+    /**
+     * Constructs a SimpleWaiter with a success condition, timeout unit, and timeout.
+     *
+     * @param successCondition the condition to be met for success
+     * @param timeoutUnit the unit of the timeout
+     * @param timeout the timeout duration
+     */
     public SimpleWaiter(BooleanSupplier successCondition, TimeUnit timeoutUnit, long timeout) {
         this(successCondition, timeoutUnit, timeout, null);
     }

+    /**
+     * Constructs a SimpleWaiter with a success condition, timeout unit, timeout, and reason.
+     *
+     * @param successCondition the condition to be met for success
+     * @param timeoutUnit the unit of the timeout
+     * @param timeout the timeout duration
+     * @param reason the reason for waiting
+     */
     public SimpleWaiter(BooleanSupplier successCondition, TimeUnit timeoutUnit, long timeout, String reason) {
         this.successCondition = successCondition;
         this.failureCondition = () -> false;
@@ -30,6 +38,12 @@ public class SimpleWaiter(BooleanSupplier successCondition, TimeUnit timeoutUnit
         this.logPoint = reason == null ? LogPoint.NONE : LogPoint.START;
     }

+    /**
+     * Sets the failure condition.
+     *
+     * @param failureCondition the condition to be met for failure
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter failureCondition(BooleanSupplier failureCondition) {
         this.failureCondition = failureCondition;
         return this;
     }

+    /**
+     * Sets the timeout in milliseconds.
+     *
+     * @param millis the timeout duration in milliseconds
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter timeout(long millis) {
         this.timeout = millis;
         return this;
     }

+    /**
+     * Sets the timeout.
+     *
+     * @param timeUnit the unit of the timeout
+     * @param t the timeout duration
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter timeout(TimeUnit timeUnit, long t) {
         this.timeout = timeUnit.toMillis(t);
         return this;
     }

+    /**
+     * Sets the interval in milliseconds.
+     *
+     * @param millis the interval duration in milliseconds
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter interval(long millis) {
         this.interval = millis;
         return this;
     }

+    /**
+     * Sets the interval.
+     *
+     * @param timeUnit the unit of the interval
+     * @param t the interval duration
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter interval(TimeUnit timeUnit, long t) {
         this.interval = timeUnit.toMillis(t);
         return this;
     }

+    /**
+     * Sets the reason for waiting.
+     *
+     * @param reason the reason for waiting
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter reason(String reason) {
         this.reason = reason;
         this.logPoint = LogPoint.START;
         return this;
     }

+    /**
+     * Sets the log point.
+     *
+     * @param logPoint the log point
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter logPoint(LogPoint logPoint) {
         this.logPoint = logPoint;
         return this;
     }

+    /**
+     * Sets the log level.
+     *
+     * @param level the log level
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter level(Level level) {
         this.level = level;
         return this;
     }

+    /**
+     * Sets the action to be performed on each iteration.
+     *
+     * @param runnable the action to be performed
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter onIteration(Runnable runnable) {
         onIteration = runnable;
         return this;
     }

+    /**
+     * Sets the action to be performed on success.
+     *
+     * @param runnable the action to be performed
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter onSuccess(Runnable runnable) {
         onSuccess = runnable;
         return this;
     }

+    /**
+     * Sets the action to be performed on failure.
+     *
+     * @param runnable the action to be performed
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter onFailure(Runnable runnable) {
         onFailure = runnable;
         return this;
     }

+    /**
+     * Sets the fail-fast check.
+     *
+     * @param failFast the fail-fast check
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter failFast(FailFastCheck failFast) {
         this.failFast = failFast;
         return this;
     }

+    /**
+     * Sets the action to be performed on timeout.
+     *
+     * @param runnable the action to be performed
+     * @return the current SimpleWaiter instance
+     */
     public SimpleWaiter onTimeout(Runnable runnable) {
         onTimeout = runnable;
         return this;
     }

+    /**
+     * Waits for the success condition to be met or the timeout to occur.
+     *
+     * @return true if the success condition is met, false if the failure condition is met
+     * @throws WaiterException if the timeout occurs or the thread is interrupted
+     */
     @Override
     public boolean waitFor() {
         long startTime = System.currentTimeMillis();
         long endTime = startTime + timeout;

         logPoint.logStart(reason, timeout, level);

         ExponentialTimeBackoff backoff = ExponentialTimeBackoff.builder()
                 .blocking(false)
                 .maxBackoff(32000)
                 .build();
         while (System.currentTimeMillis() < endTime) {
             if (backoff.next() && failFast.hasFailed()) {
                 logPoint.logEnd(reason + " (fail fast method failure)", System.currentTimeMillis() - startTime, level);
                 throw new WaiterException(failFast.reason());
             }
             if (failureCondition.getAsBoolean()) {
                 logPoint.logEnd(reason + " (Failure)", System.currentTimeMillis() - startTime, level);
                 onFailure.run();
                 return false;
             }
             if (successCondition.getAsBoolean()) {
                 logPoint.logEnd(reason + " (Success)", System.currentTimeMillis() - startTime, level);
                 onSuccess.run();
                 return true;
             }
             onIteration.run();

             try {
                 Thread.sleep(interval);
             } catch (InterruptedException e) {
                 throw new WaiterException("Thread has been interrupted!");
             }
         }
         logPoint.logEnd(reason + " (Time out)", System.currentTimeMillis() - startTime, level);
         onTimeout.run();
         throw new WaiterException(reason);
     }
 }

To continue talking to Dosu, mention @dosu.

mchoma commented 1 month ago

@honza-kasik are you willing to finish this dosu can help :)

honza-kasik commented 1 month ago

@mchoma I'm not. Better to start fresh. "Sets the interval." - what interval? "Sets the log point." - what is a log point? etc. No constructor doc contains some note about necessity to actually call the waitFor()...

honza-kasik commented 1 month ago

@mchoma See example for SimpleWaiter https://github.com/xtf-cz/xtf/pull/584

honza-kasik commented 1 month ago

@dosu Your JavaDoc doesn't describe practical use of most values and it misses important details. See example JavaDoc in https://github.com/xtf-cz/xtf/pull/584 for the SimpleWaiter. Based on this example, can you put together JavaDoc for the cz.xtf.core.waiting.SupplierWaiter?

dosubot[bot] commented 1 month ago
/**
 * A waiter implementation that uses a {@link Supplier} to provide values and checks them against success and failure conditions.
 * This class allows for flexible waiting logic with customizable timeout, interval, and callback actions.
 *
 * <p>Example usage:</p>
 * <pre>{@code
 * SupplierWaiter<Boolean> waiter = new SupplierWaiter<>(() -> checkCondition(), result -> result)
 *     .timeout(5000)
 *     .interval(500)
 *     .onSuccess(() -> System.out.println("Success!"))
 *     .onFailure(() -> System.out.println("Failure!"))
 *     .onTimeout(() -> System.out.println("Timeout!"));
 * 
 * boolean success = waiter.waitFor();
 * }</pre>
 *
 * @param <X> the type of the value supplied
 */
public class SupplierWaiter<X> implements Waiter {
    private Supplier<X> supplier;
    private Function<X, Boolean> successCondition;
    private Function<X, Boolean> failureCondition;

    private Runnable onIteration;
    private Runnable onSuccess;
    private Runnable onFailure;
    private Runnable onTimeout;

    private long timeout;
    private long interval;

    private String reason;
    private LogPoint logPoint;
    private Level level;
    private FailFastCheck failFast;

    /**
     * Constructs a SupplierWaiter with a supplier and a success condition.
     *
     * @param supplier the supplier providing values
     * @param successCondition the condition to check for success
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition) {
        this(supplier, successCondition, x -> false);
    }

    /**
     * Constructs a SupplierWaiter with a supplier, success condition, and failure condition.
     *
     * @param supplier the supplier providing values
     * @param successCondition the condition to check for success
     * @param failureCondition the condition to check for failure
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, Function<X, Boolean> failureCondition) {
        this(supplier, successCondition, failureCondition, TimeUnit.MILLISECONDS, WaitingConfig.timeout());
    }

    /**
     * Constructs a SupplierWaiter with a supplier, success condition, and a reason for waiting.
     *
     * @param supplier the supplier providing values
     * @param successCondition the condition to check for success
     * @param reason the reason for waiting
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, String reason) {
        this(supplier, successCondition, x -> false, reason);
    }

    /**
     * Constructs a SupplierWaiter with a supplier, success condition, failure condition, and a reason for waiting.
     *
     * @param supplier the supplier providing values
     * @param successCondition the condition to check for success
     * @param failureCondition the condition to check for failure
     * @param reason the reason for waiting
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, Function<X, Boolean> failureCondition,
            String reason) {
        this(supplier, successCondition, failureCondition, TimeUnit.MILLISECONDS, WaitingConfig.timeout(), reason);
    }

    /**
     * Constructs a SupplierWaiter with a supplier, success condition, and timeout.
     *
     * @param supplier the supplier providing values
     * @param successCondition the condition to check for success
     * @param timeoutUnit the unit of the timeout
     * @param timeout the timeout duration
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, TimeUnit timeoutUnit, long timeout) {
        this(supplier, successCondition, x -> false, timeoutUnit, timeout);
    }

    /**
     * Constructs a SupplierWaiter with a supplier, success condition, timeout, and a reason for waiting.
     *
     * @param supplier the supplier providing values
     * @param successCondition the condition to check for success
     * @param timeoutUnit the unit of the timeout
     * @param timeout the timeout duration
     * @param reason the reason for waiting
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, TimeUnit timeoutUnit, long timeout,
            String reason) {
        this(supplier, successCondition, x -> false, timeoutUnit, timeout, reason);
    }

    /**
     * Sets the timeout for the waiter.
     *
     * @param millis the timeout in milliseconds
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter timeout(long millis) {
        this.timeout = millis;
        return this;
    }

    /**
     * Sets the timeout for the waiter.
     *
     * @param timeUnit the unit of the timeout
     * @param t the timeout duration
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter timeout(TimeUnit timeUnit, long t) {
        this.timeout = timeUnit.toMillis(t);
        return this;
    }

    /**
     * Sets the interval between checks.
     *
     * @param millis the interval in milliseconds
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter interval(long millis) {
        this.interval = millis;
        return this;
    }

    /**
     * Sets the interval between checks.
     *
     * @param timeUnit the unit of the interval
     * @param t the interval duration
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter interval(TimeUnit timeUnit, long t) {
        this.interval = timeUnit.toMillis(t);
        return this;
    }

    /**
     * Sets the reason for waiting.
     *
     * @param reason the reason for waiting
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter reason(String reason) {
        this.reason = reason;
        this.logPoint = LogPoint.START;
        return this;
    }

    /**
     * Sets the log point for the waiter.
     *
     * @param logPoint the log point
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter logPoint(LogPoint logPoint) {
        this.logPoint = logPoint;
        return this;
    }

    /**
     * Sets the log level for the waiter.
     *
     * @param level the log level
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter level(Level level) {
        this.level = level;
        return this;
    }

    /**
     * Sets the action to be performed on each iteration.
     *
     * @param runnable the action to be performed
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter onIteration(Runnable runnable) {
        onIteration = runnable;
        return this;
    }

    /**
     * Sets the action to be performed on success.
     *
     * @param runnable the action to be performed
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter onSuccess(Runnable runnable) {
        onSuccess = runnable;
        return this;
    }

    /**
     * Sets the action to be performed on failure.
     *
     * @param runnable the action to be performed
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter onFailure(Runnable runnable) {
        onFailure = runnable;
        return this;
    }

    /**
     * Sets the action to be performed on timeout.
     *
     * @param runnable the action to be performed
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter onTimeout(Runnable runnable) {
        onTimeout = runnable;
        return this;
    }

    /**
     * Sets the fail-fast check for the waiter.
     *
     * @param failFast the fail-fast check
     * @return the updated SupplierWaiter instance
     */
    public SupplierWaiter failFast(FailFastCheck failFast) {
        this.failFast = failFast;
        return this;
    }

    /**
     * Waits for the success condition to be met or the timeout to occur.
     *
     * @return true if the success condition is met, false otherwise
     * @throws WaiterException if the wait is interrupted or the fail-fast condition is met
     */
    @Override
    public boolean waitFor() {
        long startTime = System.currentTimeMillis();
        long endTime = startTime + timeout;

        logPoint.logStart(reason, timeout, level);

        ExponentialTimeBackoff backoff = ExponentialTimeBackoff.builder()
                .blocking(false)
                .maxBackoff(32000)
                .build();
        while (System.currentTimeMillis() < endTime) {

            if (backoff.next() && failFast.hasFailed()) {
                logPoint.logEnd(reason + " (fail fast method failure)", System.currentTimeMillis() - startTime, level);
                throw new WaiterException(failFast.reason());
            }

            X x = supplier.get();

            if (failureCondition.apply(x)) {
                logPoint.logEnd(reason + " (Failure)", System.currentTimeMillis() - startTime, level);
                onFailure.run();
                return false;
            }
            if (successCondition.apply(x)) {
                logPoint.logEnd(reason + " (Success)", System.currentTimeMillis() - startTime, level);
                onSuccess.run();
                return true;
            }
            onIteration.run();

            try {
                Thread.sleep(interval);
            } catch (InterruptedException e) {
                throw new WaiterException("Thread has been interrupted!");
            }
        }
        logPoint.logEnd(reason + "(Timeout)", timeout, level);
        onTimeout.run();
        throw new WaiterException(reason);
    }
}

[1][2]

To continue talking to Dosu, mention @dosu.

honza-kasik commented 1 month ago

@dosu Improve the JavaDoc by adding details as it is done in SimpleWaiter

dosubot[bot] commented 1 month ago
/**
 * A waiter implementation that uses a {@link Supplier} to provide values and checks conditions on those values to determine
 * success or failure. This waiter periodically checks the supplied value against the provided success and failure conditions
 * until either the success condition is met, the failure condition is met, or the timeout is reached.
 *
 * <p>Example usage:</p>
 * <pre>{@code
 * SupplierWaiter<Boolean> waiter = new SupplierWaiter<>(() -> checkCondition(), result -> result)
 *     .timeout(5000)
 *     .interval(500)
 *     .onSuccess(() -> System.out.println("Success!"))
 *     .onFailure(() -> System.out.println("Failure!"))
 *     .onTimeout(() -> System.out.println("Timeout!"));
 * 
 * boolean success = waiter.waitFor();
 * }</pre>
 *
 * @param <X> The type of the value supplied by the {@link Supplier}.
 */
public class SupplierWaiter<X> implements Waiter {
    private Supplier<X> supplier;
    private Function<X, Boolean> successCondition;
    private Function<X, Boolean> failureCondition;

    private Runnable onIteration;
    private Runnable onSuccess;
    private Runnable onFailure;
    private Runnable onTimeout;

    private long timeout;
    private long interval;

    private String reason;
    private LogPoint logPoint;
    private Level level;
    private FailFastCheck failFast;

    /**
     * Constructs a SupplierWaiter with the given supplier and success condition.
     *
     * @param supplier The supplier providing values to be checked.
     * @param successCondition The condition that determines if the wait is successful.
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition) {
        this(supplier, successCondition, x -> false);
    }

    /**
     * Constructs a SupplierWaiter with the given supplier, success condition, and failure condition.
     *
     * @param supplier The supplier providing values to be checked.
     * @param successCondition The condition that determines if the wait is successful.
     * @param failureCondition The condition that determines if the wait has failed.
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, Function<X, Boolean> failureCondition) {
        this(supplier, successCondition, failureCondition, TimeUnit.MILLISECONDS, WaitingConfig.timeout());
    }

    /**
     * Constructs a SupplierWaiter with the given supplier, success condition, and reason.
     *
     * @param supplier The supplier providing values to be checked.
     * @param successCondition The condition that determines if the wait is successful.
     * @param reason The reason for waiting, used for logging purposes.
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, String reason) {
        this(supplier, successCondition, x -> false, reason);
    }

    /**
     * Constructs a SupplierWaiter with the given supplier, success condition, failure condition, and reason.
     *
     * @param supplier The supplier providing values to be checked.
     * @param successCondition The condition that determines if the wait is successful.
     * @param failureCondition The condition that determines if the wait has failed.
     * @param reason The reason for waiting, used for logging purposes.
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, Function<X, Boolean> failureCondition,
            String reason) {
        this(supplier, successCondition, failureCondition, TimeUnit.MILLISECONDS, WaitingConfig.timeout(), reason);
    }

    /**
     * Constructs a SupplierWaiter with the given supplier, success condition, timeout unit, and timeout.
     *
     * @param supplier The supplier providing values to be checked.
     * @param successCondition The condition that determines if the wait is successful.
     * @param timeoutUnit The unit of time for the timeout.
     * @param timeout The timeout duration.
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, TimeUnit timeoutUnit, long timeout) {
        this(supplier, successCondition, x -> false, timeoutUnit, timeout);
    }

    /**
     * Constructs a SupplierWaiter with the given supplier, success condition, timeout unit, timeout, and reason.
     *
     * @param supplier The supplier providing values to be checked.
     * @param successCondition The condition that determines if the wait is successful.
     * @param timeoutUnit The unit of time for the timeout.
     * @param timeout The timeout duration.
     * @param reason The reason for waiting, used for logging purposes.
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, TimeUnit timeoutUnit, long timeout,
            String reason) {
        this(supplier, successCondition, x -> false, timeoutUnit, timeout, reason);
    }

    /**
     * Constructs a SupplierWaiter with the given supplier, success condition, failure condition, timeout unit, and timeout.
     *
     * @param supplier The supplier providing values to be checked.
     * @param successCondition The condition that determines if the wait is successful.
     * @param failureCondition The condition that determines if the wait has failed.
     * @param timeoutUnit The unit of time for the timeout.
     * @param timeout The timeout duration.
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, Function<X, Boolean> failureCondition,
            TimeUnit timeoutUnit, long timeout) {
        this(supplier, successCondition, failureCondition, timeoutUnit, timeout, null);
    }

    /**
     * Constructs a SupplierWaiter with the given supplier, success condition, failure condition, timeout unit, timeout, and reason.
     *
     * @param supplier The supplier providing values to be checked.
     * @param successCondition The condition that determines if the wait is successful.
     * @param failureCondition The condition that determines if the wait has failed.
     * @param timeoutUnit The unit of time for the timeout.
     * @param timeout The timeout duration.
     * @param reason The reason for waiting, used for logging purposes.
     */
    public SupplierWaiter(Supplier<X> supplier, Function<X, Boolean> successCondition, Function<X, Boolean> failureCondition,
            TimeUnit timeoutUnit, long timeout, String reason) {
        this.supplier = supplier;
        this.successCondition = successCondition;
        this.failureCondition = failureCondition;

        this.onIteration = () -> {
        };
        this.onSuccess = () -> {
        };
        this.onFailure = () -> {
        };
        this.onTimeout = () -> {
        };

        this.failFast = () -> false;

        this.interval = DEFAULT_INTERVAL;
        this.timeout = timeoutUnit.toMillis(timeout);
        this.reason = reason;
        this.level = WaitingConfig.level();
        this.logPoint = reason == null ? LogPoint.NONE : LogPoint.START;
    }

    /**
     * Sets the timeout duration in milliseconds.
     *
     * @param millis The timeout duration in milliseconds.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter timeout(long millis) {
        this.timeout = millis;
        return this;
    }

    /**
     * Sets the timeout duration.
     *
     * @param timeUnit The unit of time for the timeout.
     * @param t The timeout duration.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter timeout(TimeUnit timeUnit, long t) {
        this.timeout = timeUnit.toMillis(t);
        return this;
    }

    /**
     * Sets the interval between checks in milliseconds.
     *
     * @param millis The interval duration in milliseconds.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter interval(long millis) {
        this.interval = millis;
        return this;
    }

    /**
     * Sets the interval between checks.
     *
     * @param timeUnit The unit of time for the interval.
     * @param t The interval duration.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter interval(TimeUnit timeUnit, long t) {
        this.interval = timeUnit.toMillis(t);
        return this;
    }

    /**
     * Sets the reason for waiting, used for logging purposes.
     *
     * @param reason The reason for waiting.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter reason(String reason) {
        this.reason = reason;
        this.logPoint = LogPoint.START;
        return this;
    }

    /**
     * Sets the log point for logging.
     *
     * @param logPoint The log point.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter logPoint(LogPoint logPoint) {
        this.logPoint = logPoint;
        return this;
    }

    /**
     * Sets the logging level.
     *
     * @param level The logging level.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter level(Level level) {
        this.level = level;
        return this;
    }

    /**
     * Sets the action to be performed on each iteration.
     *
     * @param runnable The action to be performed.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter onIteration(Runnable runnable) {
        onIteration = runnable;
        return this;
    }

    /**
     * Sets the action to be performed on success.
     *
     * @param runnable The action to be performed.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter onSuccess(Runnable runnable) {
        onSuccess = runnable;
        return this;
    }

    /**
     * Sets the action to be performed on failure.
     *
     * @param runnable The action to be performed.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter onFailure(Runnable runnable) {
        onFailure = runnable;
        return this;
    }

    /**
     * Sets the action to be performed on timeout.
     *
     * @param runnable The action to be performed.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter onTimeout(Runnable runnable) {
        onTimeout = runnable;
        return this;
    }

    /**
     * Sets the fail-fast check.
     *
     * @param failFast The fail-fast check.
     * @return The current instance of SupplierWaiter.
     */
    public SupplierWaiter failFast(FailFastCheck failFast) {
        this.failFast = failFast;
        return this;
    }

    /**
     * Waits until the success condition is met, the failure condition is met, or the timeout is reached.
     *
     * @return true if the success condition is met, false if the failure condition is met.
     * @throws WaiterException if the timeout is reached or the thread is interrupted.
     */
    @Override
    public boolean waitFor() {
        long startTime = System.currentTimeMillis();
        long endTime = startTime + timeout;

        logPoint.logStart(reason, timeout, level);

        ExponentialTimeBackoff backoff = ExponentialTimeBackoff.builder()
                .blocking(false)
                .maxBackoff(32000)
                .build();
        while (System.currentTimeMillis() < endTime) {

            if (backoff.next() && failFast.hasFailed()) {
                logPoint.logEnd(reason + " (fail fast method failure)", System.currentTimeMillis() - startTime, level);
                throw new WaiterException(failFast.reason());
            }

            X x = supplier.get();

            if (failureCondition.apply(x)) {
                logPoint.logEnd(reason + " (Failure)", System.currentTimeMillis() - startTime, level);
                onFailure.run();
                return false;
            }
            if (successCondition.apply(x)) {
                logPoint.logEnd(reason + " (Success)", System.currentTimeMillis() - startTime, level);
                onSuccess.run();
                return true;
            }
            onIteration.run();

            try {
                Thread.sleep(interval);
            } catch (InterruptedException e) {
                throw new WaiterException("Thread has been interrupted!");
            }
        }
        logPoint.logEnd(reason + "(Timeout)", timeout, level);
        onTimeout.run();
        throw new WaiterException(reason);
    }
}

[1][2]

To continue talking to Dosu, mention @dosu.