import java.io.*;

/**
 * Harness for running tests of the Cache class.  Creates instances of Cache and runs
 * a stream of operations specified by test files in the subdirectory "tests".  At the
 * end of each test, stats are checked to verify that the implementation is correct.  
 * Note that it is sometimes possible to generate correct stats with an incorrect implementation!
 *
 * @author Andrew Petersen
 * @version 1.0
 */
public class CacheTest {
	// Event counters
	protected static int evictions;			// The number of items evicted from the cache.
	protected static int misses;			// The number of loads and evicts that do not 
											// find their address in the cache.
	protected static int bad_vals;			// The number of loads that generate bad data.
	
	public static void main(String args[]) {
		// Test 0
		Cache c = new Cache(2, 2);
		runTest(c, "tests/2-2__2-5.test");
		if (evictions == 2 && misses == 5 && bad_vals == 0)
			System.out.println("2-2__2-5.test succeeded");
		else
			System.out.println("2-2__2-5.test failed with " + evictions + 
				" evictions, " + misses + " misses, and " + bad_vals + " bad values");
		
		// Test 1
		c = new Cache(16, 2);			
		runTest(c, "tests/16-2__8-8.test");
		if (evictions == 8 && misses == 8 && bad_vals == 0)
			System.out.println("16-2__8-8.test succeeded");
		else
			System.out.println("16-2__8-8.test failed with " + evictions + 
				" evictions, " + misses + " misses, and " + bad_vals + " bad values");
				
		// Test 2
		c = new Cache(4, 8);
		runTest(c, "tests/4-8__4-4.test");
		if (evictions == 4 && misses == 4 && bad_vals == 0)
			System.out.println("4-8__4-4.test succeeded");
		else
			System.out.println("4-8__4-4.test failed with " + evictions + 
				" evictions, " + misses + " misses, and " + bad_vals + " bad values");
	}	
		
	public static void runTest(Cache c, String inFile) {
		evictions = 0;
		misses = 0;
		bad_vals = 0;
		
		try {
			BufferedReader in = new BufferedReader(new FileReader(inFile));
			String line;
			while((line = in.readLine()) != null) {
				String [] op = line.split(",");
			
				// Don't you wish Java supported "switch" on strings?
				if (op[0].equals("s")) {
					if (c.Store(Integer.parseInt(op[1]), Integer.parseInt(op[2]))) 
						evictions++;	// An item had to be evicted to make room
				}
				else if (op[0].equals("l")) {
					try {
						int val = c.Load(Integer.parseInt(op[1]));
						if (val != Integer.parseInt(op[1]))
							bad_vals++;	// The load returned an incorrect value
					}
					catch (IllegalArgumentException iae) {
						misses++;		// The load didn't find its address in the cache
					}
				}
				else if (op[0].equals("e")) {
					try {
						c.Evict(Integer.parseInt(op[1]));
					}
					catch (IllegalArgumentException iae) {
						misses++;		// The evict didn't find its address in the cache
					}
					finally {
						evictions++;	// Item successfully evicted
					}
				}
				else {
					System.err.println("Test file included invalid operation on line:  " + line);
				}
			}
			
			in.close();
		}
		catch (FileNotFoundException fnf) {
			System.err.println(inFile + " was not found");
		}
		catch (IOException ie) {
			System.err.println("Input error while processing " + inFile);
		}
	}
}
