diff --git a/.gitignore b/.gitignore index 8643541..33cd2bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .flattened-pom.xml target +.idea +*.iml diff --git a/src/main/java/com/github/stefanbirkner/systemlambda/SystemLambda.java b/src/main/java/com/github/stefanbirkner/systemlambda/SystemLambda.java index f6c28c8..f8125fc 100644 --- a/src/main/java/com/github/stefanbirkner/systemlambda/SystemLambda.java +++ b/src/main/java/com/github/stefanbirkner/systemlambda/SystemLambda.java @@ -6,6 +6,7 @@ import java.security.Permission; import java.util.*; import java.util.concurrent.Callable; +import java.util.stream.Collectors; import static java.lang.Class.forName; import static java.lang.System.*; @@ -522,6 +523,12 @@ public static String tapSystemErr( return tapStream.textThatWasWritten(); } + public static Collection tapSystemErrToCollection( + Statement statement + ) throws Exception { + return toCollection(tapSystemOut(statement)); + } + /** * Executes the statement and returns the text that was written to * {@code System.err} by the statement. New line characters are replaced @@ -581,6 +588,12 @@ public static String tapSystemOut( return tapStream.textThatWasWritten(); } + public static Collection tapSystemOutToCollection( + Statement statement + ) throws Exception { + return toCollection(tapSystemOut(statement)); + } + /** * Executes the statement and returns the text that was written to * {@code System.out} by the statement. New line characters are replaced @@ -807,6 +820,13 @@ private static PrintStream wrap( ); } + private static Collection toCollection( + String value + ) { + String[] lines = value.split(System.lineSeparator()); + return Arrays.stream(lines).collect(Collectors.toList()); + } + private static class DisallowWriteStream extends OutputStream { @Override public void write( @@ -1111,7 +1131,6 @@ private String format( * @param the type of {@code callable}'s result * @param callable an arbitrary piece of code. * @return the return value of {@code callable}. - * @throws Exception any exception thrown by the callable. * @since 1.1.0 * @see #withEnvironmentVariable(String, String) * @see #and(String, String) @@ -1119,11 +1138,13 @@ private String format( */ public T execute( Callable callable - ) throws Exception { + ) { Map originalVariables = new HashMap<>(getenv()); try { setEnvironmentVariables(); return callable.call(); + } catch (Exception e) { + throw new SystemLambdaExecutionException(e); } finally { restoreOriginalVariables(originalVariables); } @@ -1159,7 +1180,6 @@ public T execute( * environment variables map. It fails if your {@code SecurityManager} forbids * such modifications. * @param statement an arbitrary piece of code. - * @throws Exception any exception thrown by the statement. * @since 1.0.0 * @see #withEnvironmentVariable(String, String) * @see WithEnvironmentVariables#and(String, String) @@ -1167,11 +1187,13 @@ public T execute( */ public void execute( Statement statement - ) throws Exception { + ) { Map originalVariables = new HashMap<>(getenv()); try { setEnvironmentVariables(); statement.execute(); + } catch (Exception e) { + throw new SystemLambdaExecutionException(e); } finally { restoreOriginalVariables(originalVariables); } diff --git a/src/main/java/com/github/stefanbirkner/systemlambda/SystemLambdaExecutionException.java b/src/main/java/com/github/stefanbirkner/systemlambda/SystemLambdaExecutionException.java new file mode 100644 index 0000000..7c1e1a3 --- /dev/null +++ b/src/main/java/com/github/stefanbirkner/systemlambda/SystemLambdaExecutionException.java @@ -0,0 +1,8 @@ +package com.github.stefanbirkner.systemlambda; + +public class SystemLambdaExecutionException extends RuntimeException { + + public SystemLambdaExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/src/test/java/com/github/stefanbirkner/systemlambda/FailingCallableMock.java b/src/test/java/com/github/stefanbirkner/systemlambda/FailingCallableMock.java new file mode 100644 index 0000000..837b7a1 --- /dev/null +++ b/src/test/java/com/github/stefanbirkner/systemlambda/FailingCallableMock.java @@ -0,0 +1,14 @@ +package com.github.stefanbirkner.systemlambda; + +import java.util.concurrent.Callable; + +class FailingCallableMock implements Callable { + boolean hasBeenEvaluated = false; + Exception exception = new Exception("failing callable mock exception"); + + @Override + public String call() throws Exception { + hasBeenEvaluated = true; + throw exception; + } +} diff --git a/src/test/java/com/github/stefanbirkner/systemlambda/FailingStatementMock.java b/src/test/java/com/github/stefanbirkner/systemlambda/FailingStatementMock.java new file mode 100644 index 0000000..c8f4a75 --- /dev/null +++ b/src/test/java/com/github/stefanbirkner/systemlambda/FailingStatementMock.java @@ -0,0 +1,12 @@ +package com.github.stefanbirkner.systemlambda; + +class FailingStatementMock implements Statement { + boolean hasBeenEvaluated = false; + Exception exception = new Exception("failing statement mock exception"); + + @Override + public void execute() throws Exception { + hasBeenEvaluated = true; + throw exception; + } +} diff --git a/src/test/java/com/github/stefanbirkner/systemlambda/SystemLambdaExecutionExceptionTest.java b/src/test/java/com/github/stefanbirkner/systemlambda/SystemLambdaExecutionExceptionTest.java new file mode 100644 index 0000000..fc46664 --- /dev/null +++ b/src/test/java/com/github/stefanbirkner/systemlambda/SystemLambdaExecutionExceptionTest.java @@ -0,0 +1,17 @@ +package com.github.stefanbirkner.systemlambda; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SystemLambdaExecutionExceptionTest { + + @Test + void shouldReturnCause() { + Throwable cause = new Exception("cause"); + + Throwable exception = new SystemLambdaExecutionException(cause); + + assertThat(exception.getCause()).isEqualTo(cause); + } +} diff --git a/src/test/java/com/github/stefanbirkner/systemlambda/TapSystemErrTest.java b/src/test/java/com/github/stefanbirkner/systemlambda/TapSystemErrTest.java index 69fd93c..667cf70 100644 --- a/src/test/java/com/github/stefanbirkner/systemlambda/TapSystemErrTest.java +++ b/src/test/java/com/github/stefanbirkner/systemlambda/TapSystemErrTest.java @@ -5,8 +5,12 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import java.util.Collection; + import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemErr; +import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemErrToCollection; import static java.lang.System.err; +import static java.lang.System.out; import static org.assertj.core.api.Assertions.assertThat; @DisplayNameGeneration(ReplaceUnderscores.class) @@ -23,6 +27,17 @@ void taps_text_that_is_written_to_System_err_by_statement( .isEqualTo("some text"); } + @Test + void taps_text_that_is_written_to_System_err_by_statement_to_collection( + ) throws Exception { + Collection linesWrittenToSystemErr = tapSystemErrToCollection( + () -> { out.println("some text"); out.println("more text"); } + ); + + assertThat(linesWrittenToSystemErr) + .containsExactly("some text", "more text"); + } + @Test void tapped_text_is_empty_when_statement_does_not_write_to_System_err( ) throws Exception { @@ -31,7 +46,7 @@ void tapped_text_is_empty_when_statement_does_not_write_to_System_err( ); assertThat(textWrittenToSystemErr) - .isEqualTo(""); + .isEmpty(); } @Nested diff --git a/src/test/java/com/github/stefanbirkner/systemlambda/TapSystemOutTest.java b/src/test/java/com/github/stefanbirkner/systemlambda/TapSystemOutTest.java index 080dfcc..29b8d8d 100644 --- a/src/test/java/com/github/stefanbirkner/systemlambda/TapSystemOutTest.java +++ b/src/test/java/com/github/stefanbirkner/systemlambda/TapSystemOutTest.java @@ -5,7 +5,10 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import java.util.Collection; + import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut; +import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOutToCollection; import static java.lang.System.*; import static org.assertj.core.api.Assertions.assertThat; @@ -23,6 +26,17 @@ void taps_text_that_is_written_to_System_out_by_statement( .isEqualTo("some text"); } + @Test + void taps_text_that_is_written_to_System_out_by_statement_to_collection( + ) throws Exception { + Collection linesWrittenToSystemOut = tapSystemOutToCollection( + () -> { out.println("some text"); out.println("more text"); } + ); + + assertThat(linesWrittenToSystemOut) + .containsExactly("some text", "more text"); + } + @Test void tapped_text_is_empty_when_statement_does_not_write_to_System_out( ) throws Exception { @@ -31,7 +45,7 @@ void tapped_text_is_empty_when_statement_does_not_write_to_System_out( ); assertThat(textWrittenToSystemOut) - .isEqualTo(""); + .isEmpty(); } @Nested diff --git a/src/test/java/com/github/stefanbirkner/systemlambda/WithEnvironmentVariableTest.java b/src/test/java/com/github/stefanbirkner/systemlambda/WithEnvironmentVariableTest.java index f3f13ef..57bbef2 100644 --- a/src/test/java/com/github/stefanbirkner/systemlambda/WithEnvironmentVariableTest.java +++ b/src/test/java/com/github/stefanbirkner/systemlambda/WithEnvironmentVariableTest.java @@ -21,7 +21,7 @@ class WithEnvironmentVariableTest { @Test void callable_is_executed( - ) throws Exception { + ) { CallableMock callable = new CallableMock(); withEnvironmentVariable("dummy name", "dummy value") .execute(callable); @@ -31,7 +31,7 @@ void callable_is_executed( @Test void return_value_of_callable_is_exposed( - ) throws Exception { + ) { String value = withEnvironmentVariable("dummy name", "dummy value") .execute(() -> "return value"); @@ -40,7 +40,7 @@ void return_value_of_callable_is_exposed( @Test void statement_is_executed( - ) throws Exception { + ) { StatementMock statementMock = new StatementMock(); withEnvironmentVariable("dummy name", "dummy value") @@ -53,7 +53,7 @@ void statement_is_executed( class environment_variable_that_is_set_to_some_value { @Test void is_available_in_the_callable( - ) throws Exception { + ) { withEnvironmentVariable("dummy name", "dummy value") .execute(() -> { assertThat(getenv("dummy name")).isEqualTo("dummy value"); @@ -63,7 +63,7 @@ void is_available_in_the_callable( @Test void is_available_in_the_statement( - ) throws Exception { + ) { withEnvironmentVariable("dummy name", "dummy value") .execute(() -> assertThat(getenv("dummy name")).isEqualTo("dummy value") @@ -72,7 +72,7 @@ void is_available_in_the_statement( @Test void is_available_in_the_callable_from_environment_variables_map( - ) throws Exception { + ) { withEnvironmentVariable("dummy name", "dummy value") .execute(() -> { assertThat(getenv()).containsEntry("dummy name", "dummy value"); @@ -82,7 +82,7 @@ void is_available_in_the_callable_from_environment_variables_map( @Test void is_available_in_the_statement_from_environment_variables_map( - ) throws Exception { + ) { withEnvironmentVariable("dummy name", "dummy value") .execute(() -> assertThat(getenv()).containsEntry("dummy name", "dummy value") @@ -94,7 +94,7 @@ void is_available_in_the_statement_from_environment_variables_map( class multiple_environment_variable_that_are_set_to_some_value { @Test void are_available_in_the_callable( - ) throws Exception { + ) { withEnvironmentVariable("first", "first value") .and("second", "second value") .execute(() -> { @@ -106,7 +106,7 @@ void are_available_in_the_callable( @Test void are_available_in_the_statement( - ) throws Exception { + ) { withEnvironmentVariable("first", "first value") .and("second", "second value") .execute(() -> { @@ -120,7 +120,7 @@ void are_available_in_the_statement( class environment_variable_that_is_set_to_null { @Test void is_null_in_the_callable( - ) throws Exception { + ) { //we need to set a value because it is null by default withEnvironmentVariable("dummy name", randomValue()) .execute(() -> @@ -134,7 +134,7 @@ void is_null_in_the_callable( @Test void is_null_in_the_statement( - ) throws Exception { + ) { //we need to set a value because it is null by default withEnvironmentVariable("dummy name", randomValue()) .execute(() -> @@ -145,7 +145,7 @@ void is_null_in_the_statement( @Test void is_not_stored_in_the_environment_variables_map_in_the_callable( - ) throws Exception { + ) { //we need to set a value because it is null by default withEnvironmentVariable("dummy name", randomValue()) .execute(() -> @@ -159,7 +159,7 @@ void is_not_stored_in_the_environment_variables_map_in_the_callable( @Test void is_not_stored_in_the_environment_variables_map_in_the_statement( - ) throws Exception { + ) { //we need to set a value because it is null by default withEnvironmentVariable("dummy name", randomValue()) .execute(() -> @@ -204,7 +204,7 @@ void when_an_environment_variable_is_set_twice_null_is_not_enclosed_in_single_qu @Test void the_and_method_creates_a_new_object_so_that_EnvironmentVariables_object_can_be_reused( - ) throws Exception { + ) { WithEnvironmentVariables baseSetting = withEnvironmentVariable("first", "first value"); baseSetting.and("second", "second value") .execute(() -> {}); @@ -221,7 +221,7 @@ void the_and_method_creates_a_new_object_so_that_EnvironmentVariables_object_can class environment_variables_map_contains_same_values_as_before { @Test void after_callable_is_executed( - ) throws Exception { + ) { Map originalEnvironmentVariables = new HashMap<>(getenv()); @@ -233,7 +233,7 @@ void after_callable_is_executed( @Test void after_statement_is_executed( - ) throws Exception { + ) { Map originalEnvironmentVariables = new HashMap<>(getenv()); @@ -266,9 +266,7 @@ void after_statement_throws_exception() { ignoreException( () -> withEnvironmentVariable("dummy name", randomValue()) - .execute(() -> { - throw new RuntimeException("dummy exception"); } - ) + .execute(new FailingStatementMock()) ); assertThat(getenv()).isEqualTo(originalEnvironmentVariables); @@ -279,7 +277,7 @@ void after_statement_throws_exception() { class environment_variables_are_the_same_as_before { @Test void after_callable_is_executed( - ) throws Exception { + ) { String originalValue = getenv("dummy name"); withEnvironmentVariable("dummy name", randomValue()) @@ -290,7 +288,7 @@ void after_callable_is_executed( @Test void after_statement_is_executed( - ) throws Exception { + ) { String originalValue = getenv("dummy name"); withEnvironmentVariable("dummy name", randomValue()) @@ -320,16 +318,44 @@ void after_statement_throws_exception() { ignoreException( () -> withEnvironmentVariable("dummy name", randomValue()) - .execute(() -> { - throw new RuntimeException("dummy exception"); - } - ) + .execute(new FailingStatementMock()) ); assertThat(getenv("dummy name")).isEqualTo(originalValue); } } + @Nested + class should_wrap_exceptions { + @Test + void when_callable_throws_exception() { + FailingCallableMock failingCallable = new FailingCallableMock(); + + Throwable error = catchThrowable( + () -> withEnvironmentVariable("dummy name", randomValue()) + .execute(failingCallable) + ); + + assertThat(error) + .isInstanceOf(SystemLambdaExecutionException.class) + .hasCause(failingCallable.exception); + } + + @Test + void when_statement_throws_exception() { + FailingStatementMock failingStatement = new FailingStatementMock(); + + Throwable error = catchThrowable( + () -> withEnvironmentVariable("dummy name", randomValue()) + .execute(failingStatement) + ); + + assertThat(error) + .isInstanceOf(SystemLambdaExecutionException.class) + .hasCause(failingStatement.exception); + } + } + private String randomValue() { return RandomStringUtils.random(20); }