package com.dhemery.expressing; import com.dhemery.core.Action; import com.dhemery.core.Condition; import com.dhemery.core.Feature; import com.dhemery.core.Sampler; import com.dhemery.polling.PollTimeoutException; import com.dhemery.polling.Ticker; import com.dhemery.polling.TickingPoller; import org.hamcrest.Matcher; import static com.dhemery.core.FeatureSampler.sampled; import static com.dhemery.core.SamplerCondition.sampleOf; import static com.dhemery.expressing.QuietlyTrue.isQuietlyTrue; /** * Composable methods to * evaluate boolean expressions, * make assertions, * wait for conditions, * and take action when preconditions are met. */ public abstract class Expressive extends ImmediateExpressions { public abstract Ticker createDefaultTicker(); /** * Prepare the condition for polling. * This implementation simply returns the condition unenhanced. * Subclasses may wish to override this method to enhance the condition before polling. * A typical use is to wrap the condition in a {@link PublishingCondition}. */ public Condition prepareToPoll(Condition condition) { return condition; } /** * Assert that the condition is satisfied during a poll. * <p>Example:</p> * <pre> * {@code * * Ticker withinTenSeconds = ...; * Condition launchIsInProgress = ...; * ... * assertThat(withinTenSeconds, launchIsInProgress); * } */ public void assertThat(Ticker ticker, Condition condition) { poll(ticker, condition); } /** * Assert that a polled sample of the variable satisfies the criteria. * <p>Example:</p> * <pre> * {@code * * Sampler<Integer> threadCount = ...; * ... * assertThat(threadCount, eventually(), is(9)); * } */ public <V> void assertThat(Sampler<V> variable, Ticker ticker, Matcher<? super V> criteria) { assertThat(ticker, sampleOf(variable, criteria)); } /** * Assert that a polled sample of the variable is {@code true}. * <p>Example:</p> * <pre> * {@code * * Sampler<Boolean> theresAFlyInMySoup = ...; * ... * assertThat(eventually(), theresAFlyInMySoup); * } */ public void assertThat(Ticker ticker, Sampler<Boolean> variable) { assertThat(variable, ticker, isQuietlyTrue()); } /** * Assert that a polled sample of the feature satisfies the criteria. * <p>Example:</p> * <pre> * {@code * * Page searchResultsPage = ...; * Feature<Page,Boolean> resultCount() { ... } * Ticker withinTenSeconds = ...; * ... * assertThat(searchResultsPage, resultCount(), withinTenSeconds, is(greaterThan(9))); * } */ public <S,V> void assertThat(S subject, Feature<? super S, V> feature, Ticker ticker, Matcher<? super V> criteria) { assertThat(sampled(subject, feature), ticker, criteria); } /** * Assert that a polled sample of the feature is {@code true}. * <p>Example:</p> * <pre> * {@code * * Page settingsPage = ...; * Feature<Page,Boolean> displayed() { ... } * ... * assertThat(settingsPage, eventually(), is(displayed())); * } */ public <S> void assertThat(S subject, Ticker ticker, Feature<? super S, Boolean> feature) { assertThat(subject, feature, ticker, isQuietlyTrue()); } /** * Return a new default ticker obtained from this {@code Expressive}'s helper. * This method is named to read nicely in expressions. * <p>Example:</p> * <pre> * {@code * * View settingsView = ...; * Feature<View,Boolean> visible() { ... } * assertThat(settingsView, eventually(), is(visible())); * } * </pre> */ public Ticker eventually() { return createDefaultTicker(); } /** * Report whether the condition is satisfied during a poll. */ public boolean the(Condition condition, Ticker ticker) { try { poll(ticker, condition); return true; } catch (PollTimeoutException ignored) { return false; } } /** * Report whether a polled sample of the variable satisfies the criteria. */ public <V> boolean the(Sampler<V> variable, Ticker ticker, Matcher<? super V> criteria) { return the(sampleOf(variable, criteria), ticker); } /** * Report whether a polled sample of the variable is {@code true}. */ public boolean the(Sampler<Boolean> variable, Ticker ticker) { return the(variable, ticker, isQuietlyTrue()); } /** * Report whether a polled sample of the feature satisfies the criteria. */ public <S,V> boolean the(S subject, Feature<? super S, V> feature, Ticker ticker, Matcher<? super V> criteria) { return the(sampled(subject, feature), ticker, criteria); } /** * Report whether a polled sample of the feature is {@code true}. */ public <S> boolean the(S subject, Ticker ticker, Feature<? super S, Boolean> feature) { return the(subject, feature, ticker, isQuietlyTrue()); } /** * Wait until the polled condition is satisfied. * Uses a default ticker. */ public void waitUntil(Condition condition) { waitUntil(eventually(), condition); } /** * Wait until the polled condition is satisfied. */ public void waitUntil(Ticker ticker, Condition condition) { poll(ticker, condition); } /** * Wait until a polled sample of the variable satisfies the criteria. * Uses a default ticker. */ public <V> void waitUntil(Sampler<V> variable, Matcher<? super V> criteria) { waitUntil(variable, eventually(), criteria); } /** * Wait until a polled sample of the variable is {@code true}. * Uses a default ticker. */ public void waitUntil(Sampler<Boolean> variable) { waitUntil(variable, eventually(), isQuietlyTrue()); } /** * Wait until a polled sample of the variable satisfies the criteria. */ public <V> void waitUntil(Sampler<V> variable, Ticker ticker, Matcher<? super V> criteria) { waitUntil(ticker, sampleOf(variable, criteria)); } /** * Wait until a polled sample of the variable is [@code true). */ public void waitUntil(Sampler<Boolean> variable, Ticker ticker) { waitUntil(variable, ticker, isQuietlyTrue()); } /** * Wait until a polled sample of the feature satisfies the criteria. * Uses a default ticker. */ public <S,V> void waitUntil(S subject, Feature<? super S, V> feature, Matcher<? super V> criteria) { waitUntil(subject, feature, eventually(), criteria); } /** * Wait until a polled sample of the feature is {@code true}. * Uses a default ticker. */ public <S> void waitUntil(S subject, Feature<? super S, Boolean> feature) { waitUntil(subject, feature, eventually(), isQuietlyTrue()); } /** * Wait until a polled sample of the feature satisfies the criteria. */ public <S,V> void waitUntil(S subject, Feature<? super S, V> feature, Ticker ticker, Matcher<? super V> criteria) { waitUntil(sampled(subject, feature), ticker, criteria); } /** * Wait until a polled sample of the feature is {@code true}. */ public <S> void waitUntil(S subject, Ticker ticker, Feature<? super S, Boolean> feature) { waitUntil(subject, feature, ticker, isQuietlyTrue()); } /** * Return the subject when a polled sample of the feature satisfies the criteria. * Uses a default ticker. */ public <S,V> S when(S subject, Feature<? super S, V> feature, Matcher<? super V> criteria) { return when(subject, feature, eventually(), criteria); } /** * Return the subject when a polled sample of the feature is {@code true}. * Uses a default ticker. */ public <S> S when(S subject, Feature<? super S, Boolean> feature) { return when(subject, feature, eventually(), isQuietlyTrue()); } /** * Return the subject when a polled sample of the feature satisfies the criteria. */ public <S,V> S when(S subject, Feature<? super S, V> feature, Ticker ticker, Matcher<? super V> criteria) { waitUntil(subject, feature, ticker, criteria); return subject; } /** * Return the subject when a polled sample of the feature is {@code true}. */ public <S> S when(S subject, Ticker ticker, Feature<? super S, Boolean> feature) { return when(subject, feature, ticker, isQuietlyTrue()); } /** * Act on the subject when a polled sample of the feature satisfies the criteria. * Uses a default ticker. */ public <S,V> void when(S subject, Feature<? super S, V> feature, Matcher<? super V> criteria, Action<? super S> action) { when(subject, feature, eventually(), criteria, action); } /** * Act on the subject when a polled sample of the feature is {@code true}. * Uses a default ticker. */ public <S> void when(S subject, Feature<? super S, Boolean> feature, Action<? super S> action) { when(subject, feature, isQuietlyTrue(), action); } /** * Act on the subject when a polled sample of the feature satisfies the criteria. */ public <S,V> void when(S subject, Feature<? super S, V> feature, Ticker ticker, Matcher<? super V> criteria, Action<? super S> action) { waitUntil(subject, feature, ticker, criteria); action.actOn(subject); } /** * Act on the subject when a polled sample of the feature is {@code true}. */ public <S> void when(S subject, Ticker ticker, Feature<? super S, Boolean> feature, Action<? super S> action) { when(subject, feature, ticker, isQuietlyTrue(), action); } private void poll(Ticker ticker, Condition condition) { new TickingPoller(ticker).poll(prepareToPoll(condition)); } }