Compare commits

..

10 commits

Author SHA1 Message Date
58796c8f73
Add Reader.flatMap() (#228)
* Add link to online Javadoc.

* Add Reader.flatMap
2021-08-20 07:37:28 +01:00
5b09c1b525
Add Reader.map and Reader.andThen methods (#227) 2021-08-19 13:46:22 +01:00
dependabot[bot]
33869a7c26
Bump actions/setup-java from 2.1.0 to 2.2.0 (#220)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Paul Campbell <pcampbell@kemitix.net>
2021-08-19 13:41:56 +01:00
b23337f25c
Remove Deprecated methods (#226)
* Result.andThen: remove deprecated method

Replace uses with `map`.

* Result.swap: remove deprecated method

* Remove unused imports

* ResultTest: add tests for ResultVoid.andThen()
2021-08-19 13:03:30 +01:00
892e3ec9f5
Result.match should return Result/ResultVoid (#225)
* ResultTest: group match tests and add missing errorVoid test

* Result{Void}.match() returns Result/ResultVoid
2021-08-19 11:47:02 +01:00
d1f4c61f00
Result.onSuccess should return Result/ResultVoid (#224)
* Version set to 3.2.0

* Result{Void}.onSuccess() now returns a Result<T>/ResultVoid
2021-08-19 11:35:05 +01:00
dependabot[bot]
03b288da4c
Bump pitest-maven from 1.6.8 to 1.6.9 (#221)
Bumps [pitest-maven](https://github.com/hcoles/pitest) from 1.6.8 to 1.6.9.
- [Release notes](https://github.com/hcoles/pitest/releases)
- [Commits](https://github.com/hcoles/pitest/compare/pitest-parent-1.6.8...1.6.9)

---
updated-dependencies:
- dependency-name: org.pitest:pitest-maven
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-19 11:12:17 +01:00
dependabot[bot]
7890359f21
Bump spotbugs-annotations from 4.3.0 to 4.4.0 (#222)
Bumps [spotbugs-annotations](https://github.com/spotbugs/spotbugs) from 4.3.0 to 4.4.0.
- [Release notes](https://github.com/spotbugs/spotbugs/releases)
- [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spotbugs/spotbugs/compare/4.3.0...4.4.0)

---
updated-dependencies:
- dependency-name: com.github.spotbugs:spotbugs-annotations
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Paul Campbell <pcampbell@kemitix.net>
2021-08-19 10:51:45 +01:00
913df97a1d
EitherTest: add missing test display name values (#223) 2021-08-19 10:24:10 +01:00
786569a995
Implement a Reader monad (#214)
* Add Reader monad

* WrapperTest: clean up

* Reader: add API status annotations

* Version set to 3.1.0

* pom: remove duplicate dependency
2021-08-18 17:00:51 +01:00
16 changed files with 405 additions and 275 deletions

View file

@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: setup-jdk-${{ matrix.java }}
uses: actions/setup-java@v2.1.0
uses: actions/setup-java@v2.2.0
with:
distribution: 'adopt'
java-version: ${{ matrix.java }}

View file

@ -12,7 +12,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up JDK
uses: actions/setup-java@v2.1.0
uses: actions/setup-java@v2.2.0
with:
distribution: 'adopt'
java-version: 11

View file

@ -14,6 +14,8 @@ https://oss.sonatype.org/content/repositories/releases/net/kemitix/mon/)
https://img.shields.io/maven-central/v/net.kemitix/mon.svg?style=for-the-badge)](
https://search.maven.org/artifact/net.kemitix/mon)
The documentation below is being slowly migrated to [Javadoc](https://kemitix.github.io/mon/).
- [Maven Usage](#Maven-Usage)
- [Wrapper](#Wrapper) - light-weight type-alias-like
- [TypeAlias](#TypeAlias) - type-alias-like monadic wrapper

View file

@ -11,7 +11,7 @@
</parent>
<artifactId>mon</artifactId>
<version>3.0.0</version>
<version>3.3.0</version>
<name>Mon</name>
<description>Wrapper, TypeAlias, Maybe, Result, Tree, Lazy, Either and Combinators for Java.
@ -41,9 +41,9 @@
<kemitix-maven-tiles.version>3.0.1</kemitix-maven-tiles.version>
<digraph-dependency.basePackage>net.kemitix.mon</digraph-dependency.basePackage>
<kemitix-checkstyle.version>5.5.0</kemitix-checkstyle.version>
<pitest-maven-plugin.version>1.6.8</pitest-maven-plugin.version>
<pitest-maven-plugin.version>1.6.9</pitest-maven-plugin.version>
<pitest-junit5-plugin.version>0.14</pitest-junit5-plugin.version>
<spotbugs.version>4.3.0</spotbugs.version>
<spotbugs.version>4.4.0</spotbugs.version>
<apiguardian-api.version>1.1.2</apiguardian-api.version>
</properties>
@ -60,7 +60,6 @@
<artifactId>apiguardian-api</artifactId>
<version>${apiguardian-api.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>

View file

@ -0,0 +1,64 @@
package net.kemitix.mon.reader;
import org.apiguardian.api.API;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Returns a program ready to run upon the supply of a suitable environment.
*
* @param <E> The type of the environment required by the program.
* @param <R> The type of the result returned by the program.
*/
@API(status = API.Status.EXPERIMENTAL, since = "3.0.1")
@FunctionalInterface
public interface Reader<E, R> {
/**
* Executes the program.
*
* @param env the required environment
* @return the result of the program
*/
R run(E env);
/**
* Applies the function provided to the reader when it is run.
*
* @param f the function, which takes an {@link E} as its only parameter
* @param <V> the type of the functions output
* @return a new Reader to provide the result of the supplied function
*/
@API(status = API.Status.EXPERIMENTAL)
default <V> Reader<E, V> map(Function<R, V> f) {
return e -> f.apply(run(e));
}
/**
* Applies the function provided to the reader when it is run.
*
* @param f the function, which takes an {@link E} and the previously
* generated value as its two parameters
* @param <V> the type of the functions output
* @return a new Reader to provided the result of the supplied function
*/
@API(status = API.Status.EXPERIMENTAL)
default <V> Reader<E, V> flatMap(BiFunction<E, R, V> f) {
return env -> f.apply(env, run(env));
}
/**
* Applies the function provided to the reader when run, and executes that
* resulting reader.
*
* @param f the function which takes the previously generated value and
* produces another Reader
* @param <V> the type of the value the new Reader returns
* @return a new Reader
*/
@API(status = API.Status.EXPERIMENTAL)
default <V> Reader<E, V> flatMap(Function<R, Reader<E, V>> f) {
return env -> f.apply(run(env)).run(env);
}
}

View file

@ -0,0 +1 @@
package net.kemitix.mon.reader;

View file

@ -24,7 +24,6 @@ package net.kemitix.mon.result;
import lombok.RequiredArgsConstructor;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
@ -67,8 +66,9 @@ class Err<T> implements Result<T> {
}
@Override
public void match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) {
public Result<T> match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) {
onError.accept(error);
return this;
}
@Override
@ -101,8 +101,8 @@ class Err<T> implements Result<T> {
}
@Override
public void onSuccess(final Consumer<T> successConsumer) {
// do nothing
public Result<T> onSuccess(final Consumer<T> successConsumer) {
return this;
}
@Override
@ -122,11 +122,6 @@ class Err<T> implements Result<T> {
return this;
}
@Override
public <R> Result<R> andThen(final Function<T, Callable<R>> f) {
return (Result<R>) this;
}
@Override
public Result<T> thenWith(final Function<T, WithResultContinuation<T>> f) {
return this;

View file

@ -26,11 +26,12 @@ public class ErrVoid implements ResultVoid {
}
@Override
public void match(
public ResultVoid match(
final Runnable onSuccess,
final Consumer<Throwable> onError
) {
onError.accept(error);
return this;
}
@Override
@ -39,8 +40,8 @@ public class ErrVoid implements ResultVoid {
}
@Override
public void onSuccess(final Runnable runnable) {
// do nothing
public ResultVoid onSuccess(final Runnable runnable) {
return this;
}
@Override

View file

@ -446,23 +446,6 @@ public interface Result<T> extends BaseResult, ThrowableFunctor<T, ThrowableFunc
return maybeResult.flatMap(f);
}
/**
* Swaps the inner {@code Result} of a {@link Maybe}, so that a {@code Result} contains a {@code Maybe}.
*
* @param maybeResult the Maybe the contains a Result
* @param <T> the type of the value that may be in the Result
* @return a Result containing a Maybe, the value in the Maybe was the value in a successful Result within the
* original Maybe. If the original Maybe is Nothing, the Result will contain Nothing. If the original Result was an
* error, then the Result will also be an error.
* @deprecated
*/
@API(status = DEPRECATED)
@Deprecated
static <T> Result<Maybe<T>> swap(final Maybe<Result<T>> maybeResult) {
return maybeResult.orElseGet(() -> Result.ok(null))
.flatMap(value -> Result.ok(Maybe.maybe(value)));
}
/**
* Creates a {@link Maybe} from the {@code Result}.
*
@ -657,9 +640,10 @@ public interface Result<T> extends BaseResult, ThrowableFunctor<T, ThrowableFunc
*
* @param onSuccess the Consumer to pass the value of a successful Result to
* @param onError the Consumer to pass the error from an error Result to
* @return the original Result
*/
@API(status = STABLE)
void match(Consumer<T> onSuccess, Consumer<Throwable> onError);
Result<T> match(Consumer<T> onSuccess, Consumer<Throwable> onError);
/**
* Provide the value within the Result, if it is a success, to the Consumer, and returns this Result.
@ -707,9 +691,10 @@ public interface Result<T> extends BaseResult, ThrowableFunctor<T, ThrowableFunc
* success value. When this is an error, then nothing happens.</p>
*
* @param successConsumer the consumer to handle the success
* @return the original Result
*/
@API(status = STABLE)
void onSuccess(Consumer<T> successConsumer);
Result<T> onSuccess(Consumer<T> successConsumer);
/**
* A handler for error state, when the error matches the errorClass.
@ -738,29 +723,6 @@ public interface Result<T> extends BaseResult, ThrowableFunctor<T, ThrowableFunc
Consumer<E> consumer
);
/**
* Maps a Success Result to another Result using a Callable that is able to throw a checked exception.
*
* <p>Combination of {@link #flatMap(Function)} and {@link #of(Callable)}.</p>
*
* <pre><code>
* Integer doSomething() {...}
* String doSomethingElse(final Integer value) {...}
* Result&lt;String&gt; r = Result.of(() -&gt; doSomething())
* .andThen(value -&gt; () -&gt; doSomethingElse(value));
* </code></pre>
*
* <p>When the Result is an Err, then the original error is carried over and the Callable is never called.</p>
*
* @param f the function to map the Success value into the Callable
* @param <R> the type of the final Result
* @return a new Result
* @deprecated Use {@link #map(ThrowableFunction)}
*/
@API(status = DEPRECATED)
@Deprecated
<R> Result<R> andThen(Function<T, Callable<R>> f);
/**
* Perform the continuation with the value within the success {@code Result}
* and return itself.

View file

@ -27,9 +27,10 @@ public interface ResultVoid extends BaseResult {
*
* @param onSuccess the Consumer to pass the value of a successful Result to
* @param onError the Consumer to pass the error from an error Result to
* @return the original ResultVoid
*/
@API(status = STABLE)
void match(Runnable onSuccess, Consumer<Throwable> onError);
ResultVoid match(Runnable onSuccess, Consumer<Throwable> onError);
/**
* Attempts to restore an error {@code ResultVoid} to a success.
@ -66,9 +67,10 @@ public interface ResultVoid extends BaseResult {
* success value. When this is an error, then nothing happens.</p>
*
* @param runnable the call if the Result is a success
* @return the original ResultVoid
*/
@API(status = STABLE)
void onSuccess(Runnable runnable);
ResultVoid onSuccess(Runnable runnable);
/**
* A handler for error state, when the error matches the errorClass.

View file

@ -24,7 +24,6 @@ package net.kemitix.mon.result;
import lombok.RequiredArgsConstructor;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
@ -62,8 +61,9 @@ class Success<T> implements Result<T> {
}
@Override
public void match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) {
public Result<T> match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) {
onSuccess.accept(value);
return this;
}
@Override
@ -93,8 +93,9 @@ class Success<T> implements Result<T> {
}
@Override
public void onSuccess(final Consumer<T> successConsumer) {
public Result<T> onSuccess(final Consumer<T> successConsumer) {
successConsumer.accept(value);
return this;
}
@Override
@ -110,11 +111,6 @@ class Success<T> implements Result<T> {
return this;
}
@Override
public <R> Result<R> andThen(final Function<T, Callable<R>> f) {
return result(f.apply(value));
}
@Override
public Result<T> thenWith(final Function<T, WithResultContinuation<T>> f) {
return f.apply(value).call(this);

View file

@ -37,8 +37,9 @@ public class SuccessVoid implements ResultVoid {
}
@Override
public void match(final Runnable onSuccess, final Consumer<Throwable> onError) {
public ResultVoid match(final Runnable onSuccess, final Consumer<Throwable> onError) {
onSuccess.run();
return this;
}
@Override
@ -47,8 +48,9 @@ public class SuccessVoid implements ResultVoid {
}
@Override
public void onSuccess(final Runnable runnable) {
public ResultVoid onSuccess(final Runnable runnable) {
runnable.run();
return this;
}
@Override

View file

@ -209,7 +209,7 @@ class EitherTest implements WithAssertions {
}
@Nested @DisplayName("getLeft") public class GetLeft {
@Test
@DisplayName("")
@DisplayName("when is a Left then get the value")
public void whenLeft_thenGetValue() {
//given
Either<String, Integer> either = Either.left("value");
@ -220,7 +220,7 @@ class EitherTest implements WithAssertions {
}
@Test
@DisplayName("")
@DisplayName("when is a Right then is empty")
public void whenRight_thenGetEmpty() {
//given
Either<Integer, String> either = Either.right("value");
@ -232,7 +232,7 @@ class EitherTest implements WithAssertions {
}
@Nested @DisplayName("getRight") public class GetRight {
@Test
@DisplayName("")
@DisplayName("when is a Left then is empty")
public void whenLeft_thenGetEmpty() {
//given
Either<String, Integer> either = Either.left("value");
@ -243,7 +243,7 @@ class EitherTest implements WithAssertions {
}
@Test
@DisplayName("")
@DisplayName("when is a Right then get the value")
public void whenRight_thenGetValue() {
//given
Either<Integer, String> either = Either.right("value");

View file

@ -0,0 +1,110 @@
package net.kemitix.mon;
import net.kemitix.mon.reader.Reader;
import org.assertj.core.api.WithAssertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
public class ReaderTest
implements WithAssertions {
@Test
@DisplayName("read a value and return it")
void readAndReturn() {
//given
Integer value = 123;
Environment env = new Environment() {
@Override
public Integer intValue() {
return value;
}
};
Reader<Environment, Integer> program = (Environment e) -> {
return e.intValue();
};
//when
Integer result = program.run(env);
//then
assertThat(result).isEqualTo(value);
}
@Test
@DisplayName("map")
void map() {
//given
AtomicInteger value = new AtomicInteger(123);
Environment env = new Environment() {
@Override
public Integer intValue() {
return value.incrementAndGet();
}
};
Reader<Environment, String> program =
((Reader<Environment, Integer>) e -> e.intValue())
.map(i -> i * 2)
.map(i -> Integer.toString(i));
//when
String result = program.run(env);
//then
assertThat(result).isEqualTo("248");// (123 + 1) * 2
}
@Test
@DisplayName("andThen")
void andThen() {
//given
AtomicInteger value = new AtomicInteger(123);
Environment env = new Environment() {
@Override
public Integer intValue() {
return value.incrementAndGet();
}
};
Reader<Environment, String> program =
((Reader<Environment, Integer>) e -> e.intValue())
.flatMap((e, i) -> e.intValue() + (i * 2))
.flatMap((e, i) -> String.format("%.2f",
i.floatValue() / e.intValue()));
//when
String result = program.run(env);
//then
assertThat(result).isEqualTo("2.96");// ((123 * 2) + (123 + 1)) / (123 + 2)
}
@Test
@DisplayName("flatMap")
void flatMap() {
//given
AtomicInteger value = new AtomicInteger(123);
Environment env = new Environment() {
@Override
public Integer intValue() {
return value.incrementAndGet();
}
};
Function<Integer, Reader<Environment, Integer>> addNextValue =
integer -> (Reader<Environment, Integer>) e -> e.intValue() + integer;
Function<Integer, Reader<Environment, Float>> divideByNextValue =
integer -> (Reader<Environment, Float>) e -> integer.floatValue() / e.intValue();
Reader<Environment, String> program =
((Reader<Environment, Integer>) e -> e.intValue())
.flatMap(addNextValue)
.flatMap(divideByNextValue)
.map(f -> String.format("%.3f", f));
//when
String result = program.run(env);
//then
assertThat(result).isEqualTo("1.976");// (123 + 124) / 125
}
private interface Environment {
Integer intValue();
}
}

View file

@ -106,17 +106,6 @@ class ResultTest implements WithAssertions {
assertThat(result.isError()).isFalse();
}
@Test
void whenOkayVoid_match_isNull() {
//when
var result = Result.ok();
//then
result.match(
() -> assertThat(true).isTrue(),
error -> fail("not an error")
);
}
@Test
void whenOk_isOkay() {
//when
@ -133,17 +122,6 @@ class ResultTest implements WithAssertions {
assertThat(result.isError()).isFalse();
}
@Test
void whenOkay_matchSuccess() {
//given
final Result<String> result = Result.ok("good");
//then
result.match(
success -> assertThat(success).isEqualTo("good"),
error -> fail("not an error")
);
}
@Test
void whenError_isError() {
//when
@ -160,17 +138,6 @@ class ResultTest implements WithAssertions {
assertThat(result.isError()).isTrue();
}
@Test
void whenError_matchError() {
//given
final Result<Integer> result = anError(new Exception("bad"));
//then
result.match(
success -> fail("not a success"),
error -> assertThat(error.getMessage()).isEqualTo("bad")
);
}
@Test
void okay_toString() {
//given
@ -245,6 +212,95 @@ class ResultTest implements WithAssertions {
.flatMap(s -> Result.of(() -> {throw e;}));
}
@Nested
@DisplayName("match")
class MatchTests {
@Test
void whenOkay_matchSuccess() {
//given
Result<String> ok = Result.ok("good");
//then
ok.match(
success -> assertThat(success).isEqualTo("good"),
error -> fail("not an error")
);
}
@Test
void whenError_matchError() {
//given
Result<Integer> error = anError(new Exception("bad"));
//then
error.match(
success -> fail("not a success"),
e -> assertThat(e.getMessage()).isEqualTo("bad")
);
}
@Test
void whenOkayVoid_matchOkay() {
//when
var ok = Result.ok();
//then
ok.match(
() -> assertThat(true).isTrue(),
error -> fail("not an error")
);
}
@Test
void whenErrorVoid_matchError() {
//when
var error = Result.error(new RuntimeException());
//then
error.match(
() -> fail("not a success"),
e -> assertThat(true).isTrue()
);
}
@Test
void whenOkay_match_returnsSelf() {
//given
final Result<String> ok = Result.ok("good");
//when
Result<String> result = ok.match(s -> {}, e -> {});
//then
assertThat(result).isSameAs(ok);
}
@Test
void whenError_match_returnsSelf() {
//given
final Result<Integer> error = anError(new Exception("bad"));
//then
Result<Integer> result = error.match(s -> {}, e -> {});
//then
assertThat(result).isSameAs(error);
}
@Test
void whenOkayVoid_match_returnsSelf() {
//given
ResultVoid ok = Result.ok();
//when
ResultVoid result = ok.match(() -> {}, e -> {});
//then
assertThat(result).isSameAs(ok);
}
@Test
void whenErrorVoid_match_returnsSelf() {
//given
ResultVoid error = Result.error(new RuntimeException());
//when
ResultVoid result = error.match(() -> {}, e -> {});
//then
assertThat(result).isSameAs(error);
}
}
@Nested
@DisplayName("flatMap")
class FlatMapTests {
@ -527,50 +583,6 @@ class ResultTest implements WithAssertions {
}
}
@Nested
@DisplayName("invert")
class InvertTests {
@Test
void justOkay_whenInvert_thenOkayJust() {
//given
final Maybe<Result<Integer>> justSuccess = Maybe.just(Result.ok(1));
//when
final Result<Maybe<Integer>> result = Result.swap(justSuccess);
//then
result.match(
success -> assertThat(success.toOptional()).contains(1),
error -> fail("Not an error")
);
}
@Test
void JustError_whenInvert_isError() {
//given
final RuntimeException exception = new RuntimeException();
final Maybe<Result<Integer>> justError = Maybe.just(anError(exception));
//when
final Result<Maybe<Integer>> result = Result.swap(justError);
//then
result.match(
success -> fail("Not a success"),
error -> assertThat(error).isSameAs(exception)
);
}
@Test
void nothing_whenInvert_thenOkayNothing() {
//given
final Maybe<Result<Integer>> nothing = Maybe.nothing();
//when
final Result<Maybe<Integer>> result = Result.swap(nothing);
//then
result.match(
success -> assertThat(success.toOptional()).isEmpty(),
error -> fail("Not an error")
);
}
}
@Nested
@DisplayName("use cases")
class UseCaseTests {
@ -832,6 +844,52 @@ class ResultTest implements WithAssertions {
//then
assertThat(capture).hasValue(1);
}
@Test
void error_whenOnSuccess_returnsSelf() {
//given
final Result<Integer> error = anError(new RuntimeException());
//when
final Result<Integer> result = error.onSuccess(x -> {});
//then
assertThat(result).isSameAs(error);
}
@Test
void success_whenOnSuccess_returnsSelf() {
//given
final Result<Integer> ok = Result.ok(1);
//when
final Result<Integer> result = ok.onSuccess(x -> {});
//then
assertThat(result).isSameAs(ok);
}
@Test void errorVoid_whenOnSuccess_returnsSelf() {
//given
final ResultVoid error = Result.error(new RuntimeException());
//when
final ResultVoid result = error.onSuccess(() -> {});
//then
assertThat(result).isSameAs(error);
}
@Test void successVoid_whenOnSuccess_returnsSelf() {
//given
final ResultVoid ok = Result.ok();
//when
final ResultVoid result = ok.onSuccess(() -> {});
//then
assertThat(result).isSameAs(ok);
}
}
@Nested
@ -957,122 +1015,6 @@ class ResultTest implements WithAssertions {
}
@Nested
@DisplayName("andThen")
class AndThenTests {
@Test
void okay_whenAndThen_whereSuccess_isUpdatedSuccess() {
//given
final Result<Integer> ok = Result.ok(1);
//when
final Result<String> result = ok.andThen(v -> () -> "success");
//then
result.match(
v -> assertThat(v).isEqualTo("success"),
e -> fail("not an error"));
}
@Test
void okay_whenAndThen_whereError_isError() {
//given
final Result<Integer> ok = Result.ok(1);
final RuntimeException exception = new RuntimeException();
//when
final Result<Object> result = ok.andThen(v -> () -> {
throw exception;
});
//then
result.match(
x -> fail("not a success"),
e -> assertThat(e).isSameAs(exception));
}
@Test
void error_whereAndThen_whereSuccess_isError() {
//given
final RuntimeException exception = new RuntimeException();
final Result<Integer> error = anError(exception);
//when
final Result<Object> result = error.andThen(v -> () -> "success");
//then
result.match(
x -> fail("not a success"),
e -> assertThat(e).isSameAs(exception));
}
@Test
void error_whenAndThen_whereError_isOriginalError() {
//given
final RuntimeException exception1 = new RuntimeException();
final Result<Integer> error = anError(exception1);
//when
final Result<Object> result = error.andThen(v -> () -> {
throw new RuntimeException();
});
//then
result.match(
x -> fail("not a success"),
e -> assertThat(e).isSameAs(exception1));
}
@Test
void okayVoid_whenAndThen_whereSuccess_isUpdatedSuccess() {
//given
final ResultVoid ok = Result.ok();
//when
final ResultVoid result = ok.andThen(() -> {
// do nothing
});
//then
assertThat(result.isOkay()).isTrue();
}
@Test
void okayVoid_whenAndThen_whereError_isError() {
//given
final ResultVoid ok = Result.ok();
final RuntimeException exception = new RuntimeException();
//when
final ResultVoid result = ok.andThen(() -> {
throw exception;
});
//then
result.match(
() -> fail("not a success"),
e -> assertThat(e).isSameAs(exception));
}
@Test
void errorVoid_whereAndThen_whereSuccess_isError() {
//given
final RuntimeException exception = new RuntimeException();
final ResultVoid error = Result.error(exception);
//when
final ResultVoid result = error.andThen(() -> {
// do nothing
});
//then
result.match(
() -> fail("not a success"),
e -> assertThat(e).isSameAs(exception));
}
@Test
void errorVoid_whenAndThen_whereError_isOriginalError() {
//given
final RuntimeException exception1 = new RuntimeException();
final ResultVoid error = Result.error(exception1);
//when
final ResultVoid result = error.andThen(() -> {
throw new RuntimeException();
});
//then
result.match(
() -> fail("not a success"),
e -> assertThat(e).isSameAs(exception1));
}
}
@Nested
@DisplayName("thenWith")
class ThenWithTests {
@ -1668,6 +1610,64 @@ class ResultTest implements WithAssertions {
}
@Nested
@DisplayName("andThen")
class AndThenTests {
@Test
void successVoid_andThen_returnSelf() {
//given
ResultVoid ok = Result.ok();
//when
ResultVoid result = ok.andThen(() -> {});
//then
assertThat(result).isSameAs(ok);
}
@Test
void successVoid_andThen_isCalled() {
//given
ResultVoid ok = Result.ok();
AtomicBoolean called = new AtomicBoolean(false);
//when
ok.andThen(() -> called.set(true));
//then
assertThat(called).isTrue();
}
@Test
void successVoid_andThen_exception_errorVoid() {
//given
ResultVoid ok = Result.ok();
//when
ResultVoid result = ok.andThen(() -> {throw new RuntimeException();});
//then
assertThat(result.isError()).isTrue();
}
@Test
void errorVoid_andThen_returnsSelf() {
//given
ResultVoid error = Result.error(new RuntimeException());
//when
ResultVoid result = error.andThen(() -> {});
//then
assertThat(result).isSameAs(error);
}
@Test
void errorVoid_andThen_notCalled() {
//given
ResultVoid error = Result.error(new RuntimeException());
final AtomicBoolean called = new AtomicBoolean(false);
//when
error.andThen(() -> called.set(true));
//then
assertThat(called).isFalse();
}
}
/**
* These include snippets from the Javadocs and are meant to prove that the examples are valid.
*/
@ -2293,7 +2293,7 @@ class ResultTest implements WithAssertions {
Result<Double> businessOperation(final String fileName1, final String fileName2) {
return readIntFromFile(fileName1)
.andThen(intFromFile1 -> () -> adjustValue(intFromFile1))
.map(this::adjustValue)
.flatMap(adjustedIntFromFile1 -> readIntFromFile(fileName2)
.flatMap(intFromFile2 -> adjustedIntFromFile1
.flatMap(aif1 -> calculateAverage(aif1, intFromFile2))));

View file

@ -1,10 +1,7 @@
package net.kemitix.mon;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -18,8 +15,6 @@ class WrapperTest {
interface WrappedIterableString extends Wrapper<Iterable<String>> { }
interface WrappedListInteger extends Wrapper<List<Integer>> {}
interface AWrapper extends Wrapper<String> {}
@Test
@ -65,6 +60,7 @@ class WrapperTest {
final WrappedString wrappedString = () -> "1";
final WrappedInteger wrappedInteger = () -> 1;
//then
//noinspection AssertBetweenInconvertibleTypes
assertThat(wrappedString).isNotEqualTo(wrappedInteger);
}