/* * Copyright 2013 Cameron Beccario * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.nullschool.collect; import org.junit.Test; import java.util.*; import static net.nullschool.collect.IteratorTools.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import static net.nullschool.collect.CollectionTestingTools.*; /** * 2013-04-25<p/> * * @author Cameron Beccario */ public class IteratorToolsTest { @Test public void test_empty_map_iterator() { assertFalse(emptyMapIterator().hasNext()); try { emptyMapIterator().next(); fail(); } catch (NoSuchElementException ignored) {} try { emptyMapIterator().value(); fail(); } catch (IllegalStateException ignored) {} try { emptyMapIterator().entry(); fail(); } catch (IllegalStateException ignored) {} try { emptyMapIterator().remove(); fail(); } catch (IllegalStateException ignored) {} assertSame(emptyMapIterator(), emptyMapIterator()); } @Test public void test_newMapIterator_adapter() { // Create a mock map with two entries. Map map = mock(Map.class); Set entrySet = mock(Set.class); Iterator entrySetIter = mock(Iterator.class); Map.Entry entry1 = new AbstractMap.SimpleImmutableEntry<>("a", 1); Map.Entry entry2 = new AbstractMap.SimpleImmutableEntry<>("b", 2); when(map.isEmpty()).thenReturn(false); when(map.entrySet()).thenReturn(entrySet); when(entrySet.iterator()).thenReturn(entrySetIter); when(entrySetIter.hasNext()).thenReturn(true, true, false); when(entrySetIter.next()).thenReturn(entry1, entry2); // Now create the map iterator to test. MapIterator iter = newMapIterator((Map<?, ?>)map); // Before calling next, these methods should throw. try { iter.value(); fail(); } catch (IllegalStateException ignored) {} try { iter.entry(); fail(); } catch (IllegalStateException ignored) {} // Advance to the first entry. assertTrue(iter.hasNext()); assertEquals(entry1.getKey(), iter.next()); assertEquals(entry1.getValue(), iter.value()); assertSame(entry1, iter.entry()); // Remove the first entry. iter.remove(); try { iter.value(); fail(); } catch (IllegalStateException ignored) {} try { iter.entry(); fail(); } catch (IllegalStateException ignored) {} // Advance to the second entry. assertTrue(iter.hasNext()); assertEquals(entry2.getKey(), iter.next()); assertEquals(entry2.getValue(), iter.value()); assertSame(entry2, iter.entry()); // Remove the second entry. iter.remove(); try { iter.value(); fail(); } catch (IllegalStateException ignored) {} try { iter.entry(); fail(); } catch (IllegalStateException ignored) {} verify(entrySetIter, times(2)).hasNext(); verify(entrySetIter, times(2)).next(); verify(entrySetIter, times(2)).remove(); assertFalse(iter.hasNext()); } @Test public void test_newMapIterator_empty() { assertSame(emptyMapIterator(), newMapIterator(Collections.emptyMap())); IterableMap<?, ?> map = mock(AbstractIterableMap.class); when(map.isEmpty()).thenReturn(true); assertSame(emptyMapIterator(), newMapIterator(map)); } @Test public void test_newMapIterator_on_iterableMap() { IterableMap map = mock(IterableMap.class); MapIterator iter = mock(MapIterator.class); when(map.isEmpty()).thenReturn(false); when(map.iterator()).thenReturn(iter); assertSame(iter, newMapIterator((Map<?, ?>)map)); } @Test(expected = NullPointerException.class) public void test_newMapIterator_throws() { newMapIterator(null); } @Test @SuppressWarnings("unchecked") public void test_chainMapIterators() { // Create two mock iterators that both have two entries. MapIterator first = mock(MapIterator.class); MapIterator second = mock(MapIterator.class); Map.Entry entry1 = newEntry("a", 1); Map.Entry entry2 = newEntry("b", 2); Map.Entry entry3 = newEntry("c", 3); Map.Entry entry4 = newEntry("d", 4); when(first.hasNext()).thenReturn(true, true, false); when(first.next()).thenReturn(entry1.getKey(), entry2.getKey()); when(first.value()).thenReturn(entry1.getValue(), entry2.getValue()); when(first.entry()).thenReturn(entry1, entry2); when(second.hasNext()).thenReturn(true, true, false); when(second.next()).thenReturn(entry3.getKey(), entry4.getKey()).thenThrow(NoSuchElementException.class); when(second.value()).thenReturn(entry3.getValue(), entry4.getValue()); when(second.entry()).thenReturn(entry3, entry4); MapIterator iter = chainMapIterators(first, second); assertTrue(iter.hasNext()); assertEquals(entry1.getKey(), iter.next()); assertEquals(entry1.getValue(), iter.value()); assertSame(entry1, iter.entry()); assertTrue(iter.hasNext()); assertEquals(entry2.getKey(), iter.next()); assertEquals(entry2.getValue(), iter.value()); assertSame(entry2, iter.entry()); iter.remove(); assertTrue(iter.hasNext()); assertEquals(entry3.getKey(), iter.next()); assertEquals(entry3.getValue(), iter.value()); assertSame(entry3, iter.entry()); assertTrue(iter.hasNext()); assertEquals(entry4.getKey(), iter.next()); assertEquals(entry4.getValue(), iter.value()); assertSame(entry4, iter.entry()); assertFalse(iter.hasNext()); try { iter.next(); fail(); } catch (NoSuchElementException ignored) {} iter.remove(); verify(first, times(3)).hasNext(); verify(first, times(2)).next(); verify(first, times(2)).value(); verify(first, times(2)).entry(); verify(first, times(1)).remove(); verify(second, times(3)).hasNext(); verify(second, times(3)).next(); verify(second, times(2)).value(); verify(second, times(2)).entry(); verify(second, times(1)).remove(); } @Test @SuppressWarnings("unchecked") public void test_chainMapIterators_without_calling_hasNext() { // Create two mock iterators that both have one entry. MapIterator first = mock(MapIterator.class); MapIterator second = mock(MapIterator.class); Map.Entry entry1 = newEntry("a", 1); Map.Entry entry2 = newEntry("b", 2); when(first.next()).thenReturn(entry1.getKey()).thenThrow(NoSuchElementException.class); when(first.value()).thenReturn(entry1.getValue()); when(first.entry()).thenReturn(entry1); when(second.next()).thenReturn(entry2.getKey()).thenThrow(NoSuchElementException.class); when(second.value()).thenReturn(entry2.getValue()); when(second.entry()).thenReturn(entry2); MapIterator iter = chainMapIterators(first, second); assertEquals(entry1.getKey(), iter.next()); assertEquals(entry1.getValue(), iter.value()); assertSame(entry1, iter.entry()); // This call to next should swallow the first iterator's NoSuchElementException and move to the second. assertEquals(entry2.getKey(), iter.next()); assertEquals(entry2.getValue(), iter.value()); assertSame(entry2, iter.entry()); try { iter.next(); fail(); } catch (NoSuchElementException ignored) {} verify(first, times(0)).hasNext(); verify(first, times(2)).next(); verify(first, times(1)).value(); verify(first, times(1)).entry(); verify(second, times(0)).hasNext(); verify(second, times(2)).next(); verify(second, times(1)).value(); verify(second, times(1)).entry(); } @Test public void test_chainMapIterators_throws() { try { chainMapIterators(emptyMapIterator(), null); fail(); } catch (NullPointerException ignored) {} try { chainMapIterators(null, emptyMapIterator()); fail(); } catch (NullPointerException ignored) {} try { chainMapIterators(null, null); fail(); } catch (NullPointerException ignored) {} } @Test @SuppressWarnings("unchecked") public void test_chainMapIterators_short_circuits_empty_iterator() { MapIterator iter = mock(MapIterator.class); assertSame(iter, chainMapIterators(iter, emptyMapIterator())); assertSame(iter, chainMapIterators(emptyMapIterator(), iter)); } }