package com.nurkiewicz.lazyseq;
import org.mockito.Mock;
import org.testng.annotations.Test;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
import java.util.function.Supplier;
import static com.nurkiewicz.lazyseq.LazySeq.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import static org.mockito.Mockito.*;
/**
* @author Tomasz Nurkiewicz
* @since 5/12/13, 9:30 AM
*/
public class LazySeqIteratorTest extends AbstractBaseTestCase {
@Mock
private Supplier<LazySeq<Integer>> supplierMock;
@Mock
private Consumer<Character> consumerMock;
@Test
public void shouldReturnEmptyIteratorForEmptySeq() throws Exception {
final Iterator<Object> iterator = empty().iterator();
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldAllowCallingHasNextContinuouslyOnEmptyIterator() throws Exception {
//given
final Iterator<Object> iterator = empty().iterator();
//when
iterator.hasNext();
//then
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldThrowWhenTryingToAdvanceIteratorOfEmptySeq() throws Exception {
//given
final Iterator<Object> iterator = empty().iterator();
try {
//when
iterator.next();
failBecauseExceptionWasNotThrown(NoSuchElementException.class);
} catch (NoSuchElementException e) {
//then
}
}
@Test
public void shouldReturnNonEmptyIteratorForSingleElementSeq() throws Exception {
//given
final Iterator<String> iterator = of("a").iterator();
assertThat(iterator.hasNext()).isTrue();
}
@Test
public void shouldAllowAdvancingIteratorForSingleElementSeq() throws Exception {
//given
final Iterator<String> iterator = of("a").iterator();
//when
final String next = iterator.next();
//then
assertThat(next).isEqualTo("a");
}
@Test
public void shouldAllowAdvancingIteratorForSingleElementSeqWhenHasNextWasCalledFirst() throws Exception {
//given
final Iterator<String> iterator = of("a").iterator();
iterator.hasNext();
//when
final String next = iterator.next();
//then
assertThat(next).isEqualTo("a");
}
@Test
public void shouldNotHaveNextAfterAdvancingButSingleElement() throws Exception {
//given
final Iterator<String> iterator = of("a").iterator();
iterator.next();
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldThrowWhenTryingToAdvanceIteratorAfterConsumingSingleElement() throws Exception {
//given
final Iterator<String> iterator = of("a").iterator();
iterator.next();
try {
//when
iterator.next();
failBecauseExceptionWasNotThrown(NoSuchElementException.class);
} catch (NoSuchElementException e) {
//then
}
}
@Test
public void shouldReturnNonEmptyIteratorForFixedSeq() throws Exception {
//given
final Iterator<String> iterator = of("a", "b", "c").iterator();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo("a");
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo("b");
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo("c");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldAllowTraversingWithoutCallingHasNext() throws Exception {
//given
final Iterator<String> iterator = of("a", "b", "c").iterator();
assertThat(iterator.next()).isEqualTo("a");
assertThat(iterator.next()).isEqualTo("b");
assertThat(iterator.next()).isEqualTo("c");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldAllowCallingHasNextMultipleTimes() throws Exception {
//given
final Iterator<String> iterator = of("a", "b", "c").iterator();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo("a");
assertThat(iterator.next()).isEqualTo("b");
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo("c");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldAllowTraversingLazyButFiniteSeq() throws Exception {
//given
final Iterator<Integer> iterator = cons(3,
() -> cons(-2,
() -> cons(8,
() -> cons(5,
() -> cons(-4,
() -> cons(11,
() -> cons(2,
() -> of(1)))))))).iterator();
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(3);
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(-2);
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(8);
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(5);
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(-4);
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(11);
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(2);
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(1);
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldAllowTraversingLazyButFiniteSeqWithoutCallingHasNext() throws Exception {
//given
final Iterator<Integer> iterator = cons(3,
() -> cons(-2,
() -> cons(8,
() -> cons(5,
() -> cons(-4,
() -> cons(11,
() -> cons(2,
() -> of(1)))))))).iterator();
assertThat(iterator.next()).isEqualTo(3);
assertThat(iterator.next()).isEqualTo(-2);
assertThat(iterator.next()).isEqualTo(8);
assertThat(iterator.next()).isEqualTo(5);
assertThat(iterator.next()).isEqualTo(-4);
assertThat(iterator.next()).isEqualTo(11);
assertThat(iterator.next()).isEqualTo(2);
assertThat(iterator.next()).isEqualTo(1);
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldNotEvaluateTailWhenCreatingIterator() throws Exception {
//given
final LazySeq<Integer> lazy = cons(1, supplierMock);
//when
lazy.iterator();
//then
verifyZeroInteractions(supplierMock);
}
@Test
public void shouldEvaluateTailOnlyOnceWhenAdvancingITerator() throws Exception {
//given
final LazySeq<Integer> lazy = cons(1, supplierMock);
//when
lazy.iterator().next();
//then
verify(supplierMock).get();
}
@Test
public void shouldCreateIteratorForInfiniteSeq() throws Exception {
//given
final LazySeq<Integer> naturals = numbers(1);
//when
final Iterator<Integer> iterator = naturals.iterator();
//then
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(1);
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(2);
assertThat(iterator.hasNext()).isTrue();
assertThat(iterator.next()).isEqualTo(3);
assertThat(iterator.next()).isEqualTo(4);
assertThat(iterator.next()).isEqualTo(5);
}
@Test
public void shouldCallConsumerForEachElementOfIteratorForFinitSeq() throws Exception {
//given
final Iterator<Character> iterator = of('a', 'b', 'c').iterator();
//when
iterator.forEachRemaining(consumerMock);
//then
verify(consumerMock).accept('a');
verify(consumerMock).accept('b');
verify(consumerMock).accept('c');
verifyNoMoreInteractions(consumerMock);
}
@Test
public void shouldAllowJava5ForEachIteration() throws Exception {
//given
final LazySeq<Character> fixed = of('a', 'b', 'c');
//when
for (char c : fixed) {
consumerMock.accept(c);
}
//then
verify(consumerMock).accept('a');
verify(consumerMock).accept('b');
verify(consumerMock).accept('c');
}
}