package test.uebung.uebung09.iterator;

import org.junit.Test;

import java.util.Iterator;
import java.util.NoSuchElementException;

import uebung09.iterator.*;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;

public class SkipNullIteratorTest {
    @Test
    public void testEmpty() {
        final Iterator<Object> it = new SkipNullIterator<>(emptyList().iterator());
        assertFalse(it.hasNext());
        assertThrows(NoSuchElementException.class, it::next);
    }

    @Test
    public void testNull() {
        final Iterator<Object> it = new SkipNullIterator<>(asList(null, null).iterator());
        assertFalse(it.hasNext());
        assertThrows(NoSuchElementException.class, it::next);
    }

    @Test
    public void testNonNull() {
        final Iterator<String> it = new SkipNullIterator<>(asList("foo").iterator());
        assertTrue(it.hasNext());
        assertEquals("foo", it.next());
        assertFalse(it.hasNext());
        assertThrows(NoSuchElementException.class, it::next);
    }

    @Test
    public void testMixed() {
        final Iterator<String> it = new SkipNullIterator<>(asList(null, "foo", null).iterator());
        assertTrue(it.hasNext());
        assertEquals("foo", it.next());
        assertFalse(it.hasNext());
        assertThrows(NoSuchElementException.class, it::next);
    }

    @Test
    public void testMixed2() {
        final Iterator<String> oriIt = asList("a", "b", null, "c").iterator();
        Iterator<String> it = new SkipNullIterator<>(oriIt);
        assertTrue(it.hasNext());
        assertEquals("a", it.next());
        assertTrue(it.hasNext());
        assertEquals("b", it.next());
        assertTrue(it.hasNext());
        assertEquals("c", it.next());
        assertFalse(it.hasNext());
        assertThrows(NoSuchElementException.class, it::next);
    }

    @Test
    public void testDontCallInCtor() {
        final Iterator<String> dontCallNext = new Iterator<>() {
            @Override
            public boolean hasNext() {
                throw new RuntimeException();
            }

            @Override
            public String next() {
                throw new RuntimeException();
            }
        };

        new SkipNullIterator<>(dontCallNext);
    }

    @Test
    public void testSkipNullIteratorInfinity() {
        final Iterator<String> oriIt = new Iterator<>() {
            @Override
            public boolean hasNext() {
                return true;
            }

            @Override
            public String next() {
                return "infinity";
            }
        };

        final Iterator<String> it = new SkipNullIterator<>(oriIt);
        for (int i = 0; i < 1000; i++) {
            assertTrue(it.hasNext());
            assertEquals("infinity", it.next());
        }
    }

    @Test
    public void testPathological() {
        final Iterator<String> oriIt = new Iterator<>() {
            private int ctr;

            @Override
            public boolean hasNext() {
                return true;
            }

            @Override
            public String next() {
                return ctr++ > 100000 ? "infinity" : null;
            }
        };

        final Iterator<String> it = new SkipNullIterator<>(oriIt);
        for (int i = 0; i < 1000; i++) {
            assertTrue(it.hasNext());
            assertEquals("infinity", it.next());
        }
    }
}