/**
 * 
 */
package cs211.examples.test;

import static org.junit.Assert.*;

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

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import cs211.examples.ArrayList;
import cs211.examples.List;

/**
 * @author C. Andrews
 *
 */
public class ArrayListTest {
List<Integer> _list;
	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		_list = new ArrayList<Integer>();
		
		for (int i = 0; i < 25; i++){
			_list.add(i);
		}
	}



	/**
	 * Test method for {@link cs211.examples.ArrayList#add(java.lang.Object)}.
	 */
	@Test
	public void testAddE() {
		assertEquals(25, _list.size());
		_list.add(25);
		assertEquals(26, _list.size());
		assertEquals(25, _list.get(25).intValue());
	}

	/**
	 * Test method for {@link cs211.examples.ArrayList#add(java.lang.Object)}.
	 */
	@Test
	public void testAddExpand() {
		_list = new ArrayList<Integer>(25);
		for (int i = 0; i < 100; i++){
			_list.add(i);
		}
		assertEquals(100, _list.size());
		
		for (int i = 0; i < 100; i++){
			assertEquals(i, _list.get(i).intValue());
		}
		
	}
	/**
	 * Test method for {@link cs211.examples.ArrayList#add(int, java.lang.Object)}.
	 */
	@Test
	public void testAddIntE() {
		fail("Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link cs211.examples.ArrayList#clear()}.
	 */
	@Test
	public void testClear() {
		fail("Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link cs211.examples.ArrayList#contains(java.lang.Object)}.
	 */
	@Test
	public void testContains() {
		fail("Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link cs211.examples.ArrayList#get(int)}.
	 */
	@Test
	public void testGet() {
		assertEquals(10, _list.get(10).intValue());
		
		assertEquals(0, _list.get(0).intValue());
		
		assertEquals(24, _list.get(24).intValue());
		assertEquals(_list.size() -1, _list.get(_list.size() - 1).intValue());
	}

	/**
	 * Test method for {@link cs211.examples.ArrayList#get(int)}.
	 */
	@Test (expected=IndexOutOfBoundsException.class)
	public void testGetBad1() {
	_list.get(-1);
	fail("Did not throw IndexOutOfBoundException on left bound");

	}
	
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#get(int)}.
	 */
	@Test (expected=IndexOutOfBoundsException.class)
	public void testGetBad2() {
		_list.get(25);
	fail("Did not throw IndexOutOfBoundException on right bound");
	}
	
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#indexOf(java.lang.Object)}.
	 */
	@Test
	public void testIndexOf() {
		for (int i = 0; i < 25; i++){
			assertEquals(i, _list.indexOf(i));
		}
	}

	/**
	 * Test method for {@link cs211.examples.ArrayList#indexOf(java.lang.Object)}.
	 * Tests for duplicate handling.
	 */
	@Test
	public void testIndexOf2() {
		_list.add(5);
		assertEquals(5, _list.indexOf(5));
		
		_list.add(0, 5);
		assertEquals(0, _list.indexOf(5));
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#indexOf(java.lang.Object)}.
	 * Tests for missing element.
	 */
	@Test
	public void testIndexOf3() {
		assertEquals(-1, _list.indexOf(42));
	}
	
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#remove(int)}.
	 */
	@Test
	public void testRemove() {
		fail("Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link cs211.examples.ArrayList#set(int, java.lang.Object)}.
	 */
	@Test
	public void testSet() {
		fail("Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link cs211.examples.ArrayList#size()}.
	 */
	@Test
	public void testSize() {
		_list = new ArrayList<Integer>();
		assertEquals(0,_list.size());
		_list.add(5);
		_list.add(9);
		assertEquals(2,_list.size());
		_list.remove(0);
		assertEquals(1,_list.size());
		_list.remove(0);
		assertEquals(0,_list.size());
	}

	
	/************************ Iterator ******************************/
	/**
	 * Test method for {@link cs211.examples.ArrayList#iterator()}.
	 * Tests normal behavior of {@code next} and {@code hasNext}.
	 */
	@Test
	public void testIterator() {
		Iterator<Integer> iter = _list.iterator();
		int expected = 0;
		assertTrue(iter.hasNext());
		while (iter.hasNext()){
			assertEquals(expected, iter.next().intValue());
			expected++;
		}
		assertEquals(25, expected);
		assertFalse(iter.hasNext());
		
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#iterator()}.
	 * Tests for exception when {@code next} is called with no next element.
	 */
	@Test(expected=NoSuchElementException.class)
	public void testIteratorError() {
		Iterator<Integer> iter = _list.iterator();
		int expected = 0;
		while (iter.hasNext()){
			assertEquals(expected, iter.next().intValue());
			expected++;
		}
		iter.next();
		fail("Extra call to next() should have caused exception");
		
	}
	
	
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#iterator()}.
	 * Tests normal behavior of {@code remove}.
	 */
	@Test
	public void testIteratorRemove() {
		Iterator<Integer> iter = _list.iterator();
		
		assertEquals(25, _list.size());
		assertEquals(0, iter.next().intValue());
		assertEquals(1, iter.next().intValue());
		assertEquals(2, iter.next().intValue());
		
		iter.remove();
		
		assertEquals(24, _list.size());
		
		assertEquals(1, _list.get(1).intValue());
		assertEquals(3, _list.get(2).intValue());
		
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#iterator()}.
	 * Tests behavior of {@code remove} if there has been no call to {@code next}.
	 */
	@Test(expected=IllegalStateException.class)
	public void testIteratorRemoveError1() {
		Iterator<Integer> iter = _list.iterator();
		
		iter.remove();
		fail("Iterator did not throw exception when remove was called before next.");
		
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#iterator()}.
	 * Tests behavior of {@code remove} if it is called twice without another call to {@code next}.
	 */
	@Test(expected=IllegalStateException.class)
	public void testIteratorRemoveError2() {
		Iterator<Integer> iter = _list.iterator();
		iter.next();
		iter.next();
		iter.remove();
		iter.remove();
		fail("Iterator did not throw exception when remove was called twice.");
		
	}
	
	/************************ ListIterator ******************************/

	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests normal behavior of {@code next}, {@code hasNext} and {@code nextIndex}.
	 */
	@Test
	public void testListIteratorNext() {
		ListIterator<Integer> iter = _list.listIterator();
		int expected = 0;
		assertTrue(iter.hasNext());
	
		while (iter.hasNext()){
			assertEquals(expected, iter.nextIndex());
			assertEquals(expected, iter.next().intValue());
			expected++;
		}
		assertEquals(expected, iter.nextIndex());
		assertEquals(25, expected);
		assertFalse(iter.hasNext());
	}

	
	
	
	
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests for exception when {@code next} is called with no next element.
	 */
	@Test(expected=NoSuchElementException.class)
	public void testListIteratorNextError() {
		ListIterator<Integer> iter = _list.listIterator();
		int expected = 0;
		while (iter.hasNext()){
			assertEquals(expected, iter.next().intValue());
			expected++;
		}
		iter.next();
		fail("Extra call to next() should have caused exception");
		
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests normal behavior of {@code previous}, {@code hasPrevious} and {@code previousIndex}.
	 */
	@Test
	public void testListIteratorPrevious() {
		ListIterator<Integer> iter = _list.listIterator();
		int expected = 0;
		assertFalse(iter.hasPrevious());
		assertEquals(-1, iter.previousIndex());
		while (iter.hasNext()){
			assertEquals(expected, iter.next().intValue());
			expected++;
		}
		assertEquals(25, expected);
		assertFalse(iter.hasNext());
		
		assertTrue(iter.hasPrevious());
		expected--;
		while (iter.hasPrevious()){
			assertEquals(expected, iter.previousIndex());
			assertEquals(expected, iter.previous().intValue());
			expected--;
		}
		assertEquals(-1, expected);
		assertFalse(iter.hasPrevious());
		
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests for exception when {@code previous} is called with no previous element.
	 */
	@Test(expected=NoSuchElementException.class)
	public void testListIteratorPreviousError() {
		ListIterator<Integer> iter = _list.listIterator();
		iter.previous();
		fail("Call to previous() should have caused exception");
	}
	
	
	
	
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests normal behavior of {@code add}.
	 */
	@Test
	public void testListIteratorAdd() {
		ListIterator<Integer> iter = _list.listIterator();
		// add with nothing before it
		iter.add(22);
		assertEquals(26, _list.size());
		assertEquals(22, _list.get(0).intValue());
		assertEquals(0, _list.get(1).intValue());
		
		assertEquals(0, iter.next().intValue());
		
		// look at the effect of adding on previous and the two index functions
		assertEquals(1, iter.previousIndex());
		assertEquals(2, iter.nextIndex());
		iter.add(33);
		assertEquals(2, iter.previousIndex());
		assertEquals(3, iter.nextIndex());
		
		assertEquals(33, iter.previous().intValue());
		assertEquals(27, _list.size());
	}
	
	
	
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests normal behavior of {@code remove} after a call to {@code next}.
	 */
	@Test
	public void testListIteratorRemove1() {
		ListIterator<Integer> iter = _list.listIterator();
		
		assertEquals(25, _list.size());
		assertEquals(0, iter.next().intValue());
		assertEquals(1, iter.next().intValue());
		assertEquals(2, iter.next().intValue());
		
		iter.remove();
		
		assertEquals(24, _list.size());
		
		assertEquals(1, _list.get(1).intValue());
		assertEquals(3, _list.get(2).intValue());
		
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests normal behavior of {@code remove} after a call to {@code previous}.
	 */
	@Test
	public void testListIteratorRemove2() {
		ListIterator<Integer> iter = _list.listIterator();
		
		assertEquals(25, _list.size());
		assertEquals(0, iter.next().intValue());
		assertEquals(1, iter.next().intValue());
		assertEquals(1, iter.previous().intValue());
		
		iter.remove();
		
		assertEquals(24, _list.size());
		
		assertEquals(0, _list.get(0).intValue());
		assertEquals(2, _list.get(1).intValue());
		
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests normal behavior of {@code remove} between calls to {@code previous} and {@code next}.
	 */
	@Test
	public void testListIteratorRemove3() {
		ListIterator<Integer> iter = _list.listIterator();
		
		assertEquals(25, _list.size());
		assertEquals(0, iter.next().intValue());
		assertEquals(1, iter.next().intValue());
		assertEquals(1, iter.previous().intValue());
		
		iter.remove();
		
		assertEquals(2, iter.next().intValue());
		iter.remove();
		
		assertEquals(23, _list.size());
		
		assertEquals(0, _list.get(0).intValue());
		assertEquals(3, _list.get(1).intValue());
		
	}
	
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests behavior of {@code remove} if there has been no call to {@code next}.
	 */
	@Test(expected=IllegalStateException.class)
	public void testListIteratorRemoveError1() {
		ListIterator<Integer> iter = _list.listIterator();
		
		iter.remove();
		fail("Iterator did not throw exception when remove was called before next.");
		
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests behavior of {@code remove} if it is called twice without another call to {@code next}.
	 */
	@Test(expected=IllegalStateException.class)
	public void testListIteratorRemoveError2() {
		ListIterator<Integer> iter = _list.listIterator();
		iter.next();
		iter.next();
		iter.remove();
		iter.remove();
		fail("Iterator did not throw exception when remove was called twice.");
		
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests normal behavior of {@code set}.
	 */
	@Test
	public void testListIteratorSet() {
		ListIterator<Integer> iter = _list.listIterator();
		iter.next();	
		iter.set(500);
		
		assertEquals(500, _list.get(0).intValue());
		iter.next();
		iter.next();
		iter.set(600);
		assertEquals(600, _list.get(2).intValue());
		
		iter.previous();
		assertEquals(1, iter.previous().intValue());
		iter.set(700);
		assertEquals(700, _list.get(1).intValue());
	}
	
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests behavior of {@code set} when neither {@code next} or {@code previous} have been called.
	 */
	@Test(expected=IllegalStateException.class)
	public void testListIteratorSetError1() {
		ListIterator<Integer> iter = _list.listIterator();
		
		iter.set(42);
		fail("Set can not be called without calling next or previous first");
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests behavior of {@code set} if {@code add} has been called since last call to {@code next} or {@code previous}.
	 */
	@Test(expected=IllegalStateException.class)
	public void testListIteratorSetError2() {
		ListIterator<Integer> iter = _list.listIterator();
		iter.next();
		iter.add(66);
		iter.set(42);
		fail("Set can not be called after add");
	}
	
	/**
	 * Test method for {@link cs211.examples.ArrayList#listIterator()}.
	 * Tests behavior of {@code set} if {@code remove} has been called since last call to {@code next} or {@code previous}.
	 */
	@Test(expected=IllegalStateException.class)
	public void testListIteratorSetError3() {
		ListIterator<Integer> iter = _list.listIterator();
		iter.next();
		iter.remove();
		iter.set(42);
		fail("Set can not be called after remove");
	}

	
}