package com.googlecode.totallylazy;
import com.googlecode.totallylazy.functions.Callables;
import com.googlecode.totallylazy.functions.Function1;
import com.googlecode.totallylazy.matchers.NumberMatcher;
import org.junit.Test;
import java.util.NoSuchElementException;
import static com.googlecode.totallylazy.Either.applicate;
import static com.googlecode.totallylazy.Left.left;
import static com.googlecode.totallylazy.predicates.Predicates.isLeft;
import static com.googlecode.totallylazy.predicates.Predicates.isRight;
import static com.googlecode.totallylazy.Right.right;
import static com.googlecode.totallylazy.Sequences.sequence;
import static com.googlecode.totallylazy.matchers.IterableMatcher.hasExactly;
import static com.googlecode.totallylazy.numbers.Numbers.DIVIDE_BY_ZERO;
import static com.googlecode.totallylazy.numbers.Numbers.add;
import static com.googlecode.totallylazy.numbers.Numbers.divide;
import static com.googlecode.totallylazy.numbers.Numbers.numbers;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
public class EitherTest {
@Test
public void isIterableSoCanFlatMap() throws Exception {
assertThat(sequence(Either.<String, Number>right(3), Either.<String, Number>left("error")).
flatMap(Either.identity(String.class, Number.class)), is(numbers(3)));
}
@Test
public void eitherToStringDoesNotThrowNullPointer() throws Exception {
assertThat(left(null).toString(), is("left(null)"));
assertThat(right(null).toString(), is("right(null)"));
}
@Test
public void eitherEqualityDoesNotThrowNullPointer() throws Exception {
assertThat(left(null).equals(left(null)), is(true));
assertThat(left(null).equals(left(1)), is(false));
assertThat(right(null).equals(right(null)), is(true));
assertThat(right(null).equals(right(2)), is(false));
}
@Test
public void eitherHashCodeDoesNotThrowNullPointer() throws Exception {
assertThat(left(null).hashCode(), is(19));
assertThat(right(null).hashCode(), is(31));
}
@Test
public void supportsApplicativeUsage() throws Exception {
assertThat(Either.<String, Number>left("error").applicate(Either.<String, Function1<Number, Number>>right(add(3))), is(Either.<String, Number>left("error")));
assertThat(Either.<String, Number>right(2).applicate(Either.<String, Function1<Number, Number>>left("error")), is(Either.<String, Number>left("error")));
assertThat(Either.<String, Number>right(3).applicate(Either.<String, Function1<Number, Number>>right(add(3))), is(Either.<String, Number>right(6)));
assertThat(applicate(Either.<String, Function1<Number, Number>>right(add(3)), Either.<String, Number>left("error")), is(Either.<String, Number>left("error")));
assertThat(applicate(Either.<String, Function1<Number, Number>>left("error"), Either.<String, Number>right(2)), is(Either.<String, Number>left("error")));
assertThat(applicate(Either.<String, Function1<Number, Number>>right(add(3)), Either.<String, Number>right(3)), is(Either.<String, Number>right(6)));
}
@Test
public void canBeUsedInFilterAndMap() throws Exception {
final Sequence<Either<String, Integer>> eithers = sequence(Left.<String, Integer>left("error"), Right.<String, Integer>right(3));
assertThat(eithers.filter(isLeft()).map(Callables.left(String.class)), hasExactly("error"));
assertThat(eithers.filter(isRight()).map(Callables.right(Integer.class)), hasExactly(3));
}
@Test
public void supportsMap() throws Exception {
assertThat(Either.right(3).map(add(2)), is(Either.right((Number) 5)));
assertThat(left((Number) 3).map(add(2), null), NumberMatcher.is(5));
assertThat(right((Number) 3).map(null, add(2)), NumberMatcher.is(5));
}
@Test
public void supportsMapLeft() throws Exception {
assertThat(Either.<Number, Number>right(3).mapLeft(add(2)), is(Either.<Number, Number>right(3)));
assertThat(Either.<Number, Number>left(3).mapLeft(add(2)), is(Either.<Number, Number>left(5)));
}
@Test
public void supportsFlatMap() throws Exception {
assertThat(Either.<Exception, Number>right(4).flatMap(divide(2).orException()), is(Either.<Exception, Number>right(2)));
assertThat(Either.<Exception, Number>right(4).flatMap(divide(0).orException()), is(Either.<Exception, Number>left(DIVIDE_BY_ZERO)));
}
@Test
public void supportsFlatten() throws Exception {
Right<Exception, Either<Exception, Number>> error = right(Either.<Exception, Number>left(DIVIDE_BY_ZERO));
assertThat(Either.flatten(error), is(Either.<Exception, Number>left(DIVIDE_BY_ZERO)));
Right<Exception, Either<Exception, Number>> correct = right(Either.<Exception, Number>right(1));
assertThat(Either.flatten(correct), is(Either.<Exception, Number>right(1)));
}
@Test
public void supportsFold() throws Exception {
assertThat(left((Number)3).fold(2, add(), null), NumberMatcher.is(5));
assertThat(right((Number)3).fold(2, null, add()), NumberMatcher.is(5));
}
@Test
public void supportsCreatingRights() throws Exception {
Either<Exception, Integer> either = right(3);
assertThat(either.isRight(), is(true));
assertThat(either.isLeft(), is(false));
assertThat(either.right(), is(3));
}
@Test
public void supportsCreatingLefts() throws Exception {
final NumberFormatException exception = new NumberFormatException("waah");
Either<NumberFormatException, Integer> either = left(exception);
assertThat(either.isRight(), is(false));
assertThat(either.isLeft(), is(true));
assertThat(either.left(), is(exception));
}
@Test
public void canMapToLeft() throws Exception {
assertThat(Option.<String>some("Foo").map(Either.functions.<String, String>asLeft()).get(), is(Either.<String, String>left("Foo")));
}
@Test
public void canMapToRight() throws Exception {
assertThat(Option.<String>some("Bar").map(Either.functions.<String, String>asRight()).get(), is(Either.<String, String>right("Bar")));
}
@Test(expected = NoSuchElementException.class)
public void doesNotSupportGettingLeftFromARight() throws Exception {
Either<Exception, Integer> either = right(3);
either.left();
}
@Test(expected = NoSuchElementException.class)
public void doesNotSupportGettingRightFromALeft() throws Exception {
Either<RuntimeException, Integer> either = left(new RuntimeException());
either.right();
}
}