import edu.neu.ccs.*;
import edu.neu.ccs.gui.*;
import edu.neu.ccs.codec.*;
import edu.neu.ccs.console.*;
import edu.neu.ccs.filter.*;
import edu.neu.ccs.jpf.*;
import edu.neu.ccs.parser.*;
import edu.neu.ccs.pedagogy.*;
import edu.neu.ccs.quick.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import java.util.*;
import java.math.*;
import java.beans.*;
import java.lang.reflect.*;
import java.net.URL;
import java.util.regex.*;

/**
 * @author Viera K. Proulx
 *
 */
public class SimpleTestHarness implements ConsoleAware{
	
	String testResults = "Test results: \n";
	String fullTestResults = "Full test results: \n";
	int total = 0;
	int failed = 0;
	
	// constructor with the name of the test suite
	public SimpleTestHarness(String testsuite){
		this.testResults = testsuite + " test results: \n";
		this.fullTestResults = testsuite + "Full test results: \n";
	}
	
	// default constructor without the name of the testsuite
	public SimpleTestHarness(){
		this("");
	}
	
	//--------- TESTS --------------------------------------------------
	
	// test that only reports success or failure
	void test(String testname, boolean result){
		if (!result){
			this.failed = this.failed + 1;
			this.testResults = this.testResults + 
						  	  testname + ": failed \n";
		}
		this.total = this.total + 1;
	}
	
	// test that compares two objects using Java (or overridden) equals
	void test(String testname, Object expected, Object actual){
		if (expected.equals(actual))		
			this.reportSuccess(testname, expected, actual);
		else
			this.reportFailed(testname, expected, actual);			
	}

	// test that compares two objects using same method
	void test(String testname, ISame expected, ISame actual){
		
		if (((ISame)expected).same(actual))
			
			this.reportSuccess(testname, expected, actual);
		else
			this.reportFailed(testname, expected, actual);			
	}

	// test that compares two objects using same method
	void test(String testname, IRange expected, IRange actual){
		
		if (sameIt(expected, actual))
			
			this.reportSuccess(testname, expected, actual);
		else
			this.reportFailed(testname, expected, actual);			
	}

	
	// compare two structures for iterator equality
	public boolean sameIt(IRange it1, IRange it2){
		if (it1.hasMore() && it2.hasMore())
			return ((ISame)it1.current()).same(it2.current())
			    && this.sameIt(it1.next(), it2.next());
		else
			return (!it1.hasMore()) && (!it2.hasMore());
	}
	
	// test that compares two int-s using == operator
	void test(String testname, int expected, int actual){
		if (expected == actual)		
			this.reportSuccess(testname, "" + expected, "" + actual);
		else
			this.reportFailed(testname, "" + expected, "" + actual);			
	}

	// test that compares two short-s using == operator
	void test(String testname, short expected, short actual){		
		if (expected == actual)			
			this.reportSuccess(testname, "" + expected, "" + actual);
		else
			this.reportFailed(testname, "" + expected, "" + actual);			
	}


	// test that compares two integers using == operator
	void test(String testname, long expected, long actual){		
		if (expected == actual)
			this.reportSuccess(testname, "" + expected, "" + actual);
		else
			this.reportFailed(testname, "" + expected, "" + actual);			
	}


	// test that compares two boolean-s using == operator
	void test(String testname, boolean expected, boolean actual){		
		if (expected == actual)			
			this.reportSuccess(testname, "" + expected, "" + actual);
		else
			this.reportFailed(testname, "" + expected, "" + actual);			
	}


	// test that compares two char-s using == operator
	void test(String testname, char expected, char actual){		
		if (expected == actual)			
			this.reportSuccess(testname, "" + expected, "" + actual);
		else
			this.reportFailed(testname, "" + expected, "" + actual);			
	}

	// test that compares two float-s using == operator
	void test(String testname, float expected, float actual, float epsilon){	
		if (Math.abs(expected - actual) < epsilon)			
			this.reportSuccess(testname, "" + expected, "" + actual);
		else
			this.reportFailed(testname, "" + expected, "" + actual);			
	}

	// test that compares two double-s using == operator
	void test(String testname, double expected, double actual, double epsilon){
		if (Math.abs(expected - actual) < epsilon)		
			this.reportSuccess(testname, "" + expected, "" + actual);
		else
			this.reportFailed(testname, "" + expected, "" + actual);			
	}

	//	--------- TEST REPORTS ------------------------------------------------
	
	// record a failed test
	void reportFailed(String testname, 
			Object expected, 
			Object actual){
		// update the count of the failed tests
		this.failed = this.failed + 1;
		
		// add test report to the failed tests report
		this.testResults = this.testResults + "\n" +
		   testname + ": failed \n" +
		   "expected: " + expected + "\n" +
		   "actual:   " + actual + "\n";
		
		// add test report to the full test report
		this.fullTestResults = this.fullTestResults + "\n" +
		   testname + ": failed \n" +
		   "expected: " + expected + "\n" +
		   "actual:   " + actual + "\n";
		
		// update the count of all tests
		this.total = this.total + 1;
	}
	
	// record a successful test
	void reportSuccess(String testname, 
			Object expected, 
			Object actual){
		
		// add test report to the full test report
		this.fullTestResults = this.fullTestResults + "\n" +
		   testname + ": success \n" +
		   "expected: " + expected + "\n" +
		   "actual:   " + actual + "\n";
		
		// update the count of all tests
		this.total = this.total + 1;
	}
	
	// report on the number and nature of failed tests
	void testReport(){
		if (this.failed == 1){
			console.out.println(this.testResults + 
					            this.failed + " test failed.\n");
		}
		else if (this.failed > 1){
			console.out.println(this.testResults + 
		            this.failed + " tests failed.\n");
		}
		else
			console.out.println(this.testResults + 
					           "All tests passed.\n");
	}
	
	// produce test names and values compared for all tests
	void fullTestReport(){
		if (this.failed == 1){
			console.out.println("\n 1 test failed.\n");
		}
		else if (this.failed > 1){
			console.out.println("\n " + this.failed + " tests failed.\n");
		}
		else
			console.out.println("All tests passed.\n");
		console.out.println(this.fullTestResults);
	}
	
}
