Skip to content

Commit

Permalink
More flexible way to enable logging (#255)
Browse files Browse the repository at this point in the history
* add logging methods to ConditionFactory and Awaitility, add tests

* fix test with sout ByteArrayOutputStream buffer
  • Loading branch information
a-simeshin committed Mar 8, 2024
1 parent b0d02e1 commit 3ece50a
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 0 deletions.
35 changes: 35 additions & 0 deletions awaitility/src/main/java/org/awaitility/Awaitility.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

Expand Down Expand Up @@ -266,6 +267,7 @@ public static void pollThread(final Function<Runnable, Thread> threadSupplier) {
* <li>Catch all uncaught exceptions - true</li>
* <li>Do not ignore caught exceptions</li>
* <li>Don't handle condition evaluation results</li>
* <li>Don't log anything</li>
* <li>No fail fast condition</li>
* </ul>
*/
Expand Down Expand Up @@ -474,6 +476,39 @@ public static void setDefaultConditionEvaluationListener(ConditionEvaluationList
Awaitility.defaultConditionEvaluationListener = defaultConditionEvaluationListener;
}

/**
* Sets the default logging condition evaluation listener that all await statements will use. Method could override
* result of usage {@link #setDefaultConditionEvaluationListener(ConditionEvaluationListener)}.
*
* @param loggingListener logging condition evaluation each time evaluation of a condition occurs.
* @see #setDefaultConditionEvaluationListener(ConditionEvaluationListener)
*/
public static void setLoggingListener(ConditionEvaluationListener loggingListener) {
Awaitility.defaultConditionEvaluationListener = loggingListener;
}

/**
* Sets the default logging condition evaluation listener that all await statements will use. Method could override
* result of usage {@link #setDefaultConditionEvaluationListener(ConditionEvaluationListener)} with custom consumer.
*
* @param logPrinter consumer for condition logging
* @see #setDefaultConditionEvaluationListener(ConditionEvaluationListener)
*/
public static void setLogging(Consumer<String> logPrinter) {
setLoggingListener(new ConditionEvaluationLogger(logPrinter));
}

/**
* Sets the default logging condition evaluation listener that all await statements will use. Method could override
* result of usage {@link #setDefaultConditionEvaluationListener(ConditionEvaluationListener)} with logging to
* System.out.
*
* @see #setDefaultConditionEvaluationListener(ConditionEvaluationListener)
*/
public static void setDefaultLogging() {
setLoggingListener(new ConditionEvaluationLogger(System.out::println));
}

/**
* If the supplied Callable <i>ever</i> returns false, it indicates our condition will <i>never</i> be true, and if so fail the system immediately.
* Throws a {@link TerminalFailureException} if fail fast condition evaluates to <code>true</code>. If you want to specify a more descriptive error message
Expand Down
21 changes: 21 additions & 0 deletions awaitility/src/main/java/org/awaitility/core/ConditionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

Expand Down Expand Up @@ -134,6 +135,26 @@ public ConditionFactory conditionEvaluationListener(ConditionEvaluationListener
exceptionsIgnorer, conditionEvaluationListener, executorLifecycle, failFastCondition);
}

/**
* Logging condition evaluation results each time evaluation of a condition occurs to System.out.
*
* @return the condition factory
*/
public ConditionFactory logging() {
return new ConditionFactory(alias, timeoutConstraint, pollInterval, pollDelay, catchUncaughtExceptions,
exceptionsIgnorer, new ConditionEvaluationLogger(), executorLifecycle, failFastCondition);
}

/**
* Logging condition evaluation results each time evaluation of a condition occurs to chosen consumer.
*
* @return the condition factory
*/
public ConditionFactory logging(Consumer<String> logPrinter) {
return new ConditionFactory(alias, timeoutConstraint, pollInterval, pollDelay, catchUncaughtExceptions,
exceptionsIgnorer, new ConditionEvaluationLogger(logPrinter), executorLifecycle, failFastCondition);
}

/**
* Await at most <code>timeout</code> before throwing a timeout exception.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@

package org.awaitility.core;

import org.awaitility.Awaitility;
import org.junit.After;
import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import static org.awaitility.Awaitility.await;
import static org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS;
Expand All @@ -28,6 +34,140 @@

public class ConditionEvaluationLoggerTest {

private final PrintStream standardOut = System.out;

@After
@SuppressWarnings("unused")
public void resetDefaultSystemOut() {
System.setOut(standardOut);
Awaitility.reset();
}

/**
* Test should check that await.logging(Consumer) properly logging data to Consumer
*/
@Test(timeout = 2000)
public void it_is_possible_to_use_logging_with_syntactic_sugar_via_condition_evaluation_logger_by_consumer() {
CopyOnWriteArrayList<String> logs = new CopyOnWriteArrayList<>();

await().with()
.logging(logs::add)
.pollInterval(ONE_HUNDRED_MILLISECONDS)
.until(logs::size, is(4));

assertThat(
logs,
everyItem(
anyOf(
equalTo("Starting evaluation"),
containsString("expected <4> but was"),
containsString("reached its end value of <4>")
)
)
);
}

/**
* Test should check that Awaitility.setLoggingListener(new ConditionEvaluationLogger(Consumer)) properly logging data to Consumer
*/
@Test(timeout = 2000)
public void it_is_possible_to_use_logging_with_Awaitlity_static_settings_via_condition_evaluation_logger_by_consumer() {
CopyOnWriteArrayList<String> logs = new CopyOnWriteArrayList<>();
Awaitility.setLoggingListener(new ConditionEvaluationLogger(logs::add));

await().with()
.pollInterval(ONE_HUNDRED_MILLISECONDS)
.until(logs::size, is(4));

assertThat(
logs,
everyItem(
anyOf(
equalTo("Starting evaluation"),
containsString("expected <4> but was"),
containsString("reached its end value of <4>")
)
)
);
}

/**
* Test should check that Awaitility.setLogging(new ConditionEvaluationLogger(Consumer)) properly logging data to Consumer
*/
@Test(timeout = 2000)
public void it_is_possible_to_use_logging_with_Awaitlity_static_settings_via_consumer() {
CopyOnWriteArrayList<String> logs = new CopyOnWriteArrayList<>();
Awaitility.setLogging(logs::add);

await().with()
.pollInterval(ONE_HUNDRED_MILLISECONDS)
.until(logs::size, is(4));

assertThat(
logs,
everyItem(
anyOf(
equalTo("Starting evaluation"),
containsString("expected <4> but was"),
containsString("reached its end value of <4>")
)
)
);
}

/**
* Test should check that await.logging() properly logging data to System.out
*/
@Test(timeout = 2000)
public void it_is_possible_to_use_sout_logging_with_syntactic_sugar_via_condition_evaluation_logger() {
final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStreamCaptor));
final AtomicInteger stub = new AtomicInteger(0);

await().with()
.logging()
.pollInterval(ONE_HUNDRED_MILLISECONDS)
.until(stub::getAndIncrement, is(4));

assertThat(
Arrays.asList(outputStreamCaptor.toString().replaceAll("\r", "").split("\n")),
everyItem(
anyOf(
equalTo("Starting evaluation"),
containsString("expected <4> but was"),
containsString("reached its end value of <4>")
)
)
);
}

/**
* Test should check that Awaitility.setDefaultLogging() properly logging data to System.out
*/
@Test(timeout = 2000)
public void it_is_possible_to_use_sout_logging_with_Awaitility_static_settings_via_condition_evaluation_logger() {
final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStreamCaptor));
final AtomicInteger stub = new AtomicInteger(0);

Awaitility.setDefaultLogging();

await().with()
.pollInterval(ONE_HUNDRED_MILLISECONDS)
.until(stub::getAndIncrement, is(4));

assertThat(
Arrays.asList(outputStreamCaptor.toString().replaceAll("\r", "").split("\n")),
everyItem(
anyOf(
equalTo("Starting evaluation"),
containsString("expected <4> but was"),
containsString("reached its end value of <4>")
)
)
);
}

@Test(timeout = 2000)
public void it_is_possible_to_override_the_way_condition_evaluation_logger_logs_the_results_when_using_ctor_to_create_the_condition_evaluation_logger() {
CopyOnWriteArrayList<String> logs = new CopyOnWriteArrayList<>();
Expand Down

0 comments on commit 3ece50a

Please sign in to comment.