%% @doc Helper module to implement waiting conditions. %% %% It periodically queries and validates values from a callback %% until the condition is met or a timeout passes. %% %% Common method when testing for eventual consistency. -module(wait_helper). -export([wait_until/2, wait_until/3]). -export_type([name/0, sleep_time/0, callback/1, validator/1, opts/1, return/1]). -type name() :: atom(). %% Condition name, defaults to `timeout'. -type wait_error(Expected) :: {name(), Expected, history(), undefined | term()}. %% An error type that can be returned or thrown when the condition eventually fails. %% %% The last element of the tuple is the return value of the `on_error' callback given to `t:opts/1', %% if given, otherwise `undefined'. -type return(Expected) :: {ok, Expected} | {error, wait_error(Expected)} | no_return(). -type sleep_time() :: non_neg_integer(). %% How long to wait between queries for new results. -type history() :: [term()]. %% History of results. %% %% Note that this history will be simplified, that is, if elements repeat consecutively, %% they will be agregated to the history only once together with a count. -type callback(Expected) :: fun(() -> Expected | term()). %% A function that returns a value to verify. %% %% By default, the return value will be checked for equality against the expected value, %% but a custom `t:validator/1' function can be specified. %% %% The return value of this callback is what will be returned by the whole `wait_until' operation. -type validator(Expected) :: fun((Expected | term()) -> boolean()). %% Validation to do against the callbacks return value. %% %% This is useful when an intermediate value of the callback is desired in the return value, %% but the condition to be checked requires further computation that is not needed outside %% of the scope of the condition. %% %% For example: %% ``` %% Validator = fun(ReturnValue) -> contains(ReturnValue, ...) end, %% {ok, Result} = mongoose_helper:wait_until( %% fun() -> do_get_value(...) end, %% expected, %% #{validator => Validator}), %% ''' %% where `Result' is the return value of the original callback, %% after we know it is accepted by the validator -type opts(Expected) :: #{ validator => validator(Expected), expected_value => Expected, time_left => timeout(), sleep_time => sleep_time(), on_error => fun(() -> term()), no_throw => boolean(), name => name() }. %% From mongoose_helper %% @see wait_until/3 %% @doc Waits for `Fun' to return `ExpectedValue'. -spec wait_until(callback(Expected), Expected) -> return(Expected). wait_until(Fun, ExpectedValue) -> wait_until(Fun, ExpectedValue, #{}). %% @doc Waits for `Fun' to return a value `validator' accepts. %% %%