/*
 * Created on 04-Apr-2003
 */
package test.mockobjects.dynamic;

import com.mockobjects.constraint.*;
import com.mockobjects.constraint.IsEqual;
import com.mockobjects.dynamic.*;
import com.mockobjects.util.*;

import junit.framework.*;


public class MockTest extends TestCase {
	private static final String MOCK_NAME = "Test mock";
	final String METHOD_NOARG_NAME = "noArgMethodVoid";
	final String METHOD_NOARGANDRETURN_NAME = "noArgMethod";
	final String METHOD_NOARGANDRETURN_RESULT = "resultNoArgs";
	final String METHOD_ONEARG_NAME = "oneArgMethod";
	final String METHOD_ONEARG_RESULT = "result1Args";
	final String METHOD_TWOARG_NAME = "twoArgMethod";
	final String METHOD_TWOARG_RESULT = "resultTwoArgs";
	final Throwable METHOD_EXCEPTION = new DummyThrowable("Configured test throwable");
	final Object[] METHOD_NOARG_ARGS = new Object[0];
	final String[] METHOD_ONEARG_ARGS = new String[] { "oneP1" };
	final ConstraintMatcher METHOD_ONEARG_CONSTRAINTS = C.args(C.eq("oneP1"));
	final String[] METHOD_TWOARG_ARGS = new String[] { "twoP1", "twoP2" };
	final ConstraintMatcher METHOD_TWOARG_CONSTRAINTS = C.args(C.eq("twoP1"), C.eq("twoP2"));
	private DummyInterface proxy;
	private Mock mock;
	private MockCallFactory mockCallFactory = new MockCallFactory();
	private MockCallable mockCallMatch = new MockCallable();
	private MockCallable mockExpectedCall = new MockCallable();
	private MockCallable mockReturnStub = new MockCallable();
	private MockCallable mockThrowStub = new MockCallable();
	private MockCallable mockVoidStub = new MockCallable();
	private MockCallableAddable mockCallableAddable = new MockCallableAddable();

	public MockTest(String name) throws Exception {
		super(name);
	}

	public void setUp() {
		mock = new Mock(mockCallFactory, mockCallableAddable, DummyInterface.class, MOCK_NAME);

		try {
			proxy = (DummyInterface)mock.proxy();
		} catch (ClassCastException ex) {
			fail("proxy is not of expected interface type");
		}
	}

	public void testExpectManyAndVoid() throws Throwable {
		mockCallFactory.setExpectedCreateVoidStubCalls(1);
		mockCallFactory.setupCreateVoidStub(mockVoidStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS, mockVoidStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.addExpectedCreateCallExpectation(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);

		mock.expect(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}
	
	public void testExpectNoneAndReturn() throws Throwable {
		mockCallFactory.addExpectedCreateReturnStub(METHOD_NOARGANDRETURN_RESULT);
		mockCallFactory.setupCreateReturnStub(mockReturnStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_NOARGANDRETURN_NAME, C.NO_ARGS, mockReturnStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.addExpectedCreateCallExpectation(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);

		mock.expectAndReturn(METHOD_NOARGANDRETURN_NAME, METHOD_NOARGANDRETURN_RESULT);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}	
	
	public void testExpectNoneAndThrow() throws Throwable {
		mockCallFactory.addExpectedCreateThrowStub(METHOD_EXCEPTION);
		mockCallFactory.setupCreateThrowStub(mockThrowStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_NOARGANDRETURN_NAME, C.NO_ARGS, mockThrowStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.addExpectedCreateCallExpectation(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);

		mock.expectAndThrow(METHOD_NOARGANDRETURN_NAME, METHOD_EXCEPTION);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}	
	
	public void testExpectOneAndThrow() throws Throwable {
		mockCallFactory.addExpectedCreateThrowStub(METHOD_EXCEPTION);
		mockCallFactory.setupCreateThrowStub(mockThrowStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_ONEARG_NAME, METHOD_ONEARG_CONSTRAINTS, mockThrowStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.addExpectedCreateCallExpectation(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);

		mock.expectAndThrow(METHOD_ONEARG_NAME, METHOD_ONEARG_ARGS[0], METHOD_EXCEPTION);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}	
	
	public void testExpectNoneAndVoid() throws Throwable {
		mockCallFactory.setExpectedCreateVoidStubCalls(1);
		mockCallFactory.setupCreateVoidStub(mockVoidStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_NOARG_NAME, C.NO_ARGS, mockVoidStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.addExpectedCreateCallExpectation(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);
	
		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);
	
		mock.expect(METHOD_NOARG_NAME);
	
		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}
	
	public void testExpectOneAndVoid() throws Throwable {
		mockCallFactory.setExpectedCreateVoidStubCalls(1);
		mockCallFactory.setupCreateVoidStub(mockVoidStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_ONEARG_NAME, METHOD_ONEARG_CONSTRAINTS, mockVoidStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.addExpectedCreateCallExpectation(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);

		mock.expect(METHOD_ONEARG_NAME, METHOD_ONEARG_ARGS[0]);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}
	
	public void testExpectWithConstraint() throws Throwable {
		mockCallFactory.setupCreateVoidStub(mockVoidStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_ONEARG_NAME, METHOD_ONEARG_CONSTRAINTS, mockVoidStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);

		mock.expect(METHOD_ONEARG_NAME, new IsEqual(METHOD_ONEARG_ARGS[0]));

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}
	
	public void testExpectWithConstraintArray() throws Throwable {
		mockCallFactory.setupCreateVoidStub(mockVoidStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_ONEARG_NAME, METHOD_ONEARG_CONSTRAINTS, mockVoidStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);

		mock.expect(METHOD_ONEARG_NAME, new Constraint[] { new IsEqual(METHOD_ONEARG_ARGS[0])});

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}


	public void testExpectManyAndReturn() throws Throwable {
		mockCallFactory.addExpectedCreateReturnStub(METHOD_TWOARG_RESULT);
		mockCallFactory.setupCreateReturnStub(mockReturnStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS, mockReturnStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.addExpectedCreateCallExpectation(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);
		mockCallableAddable.setupCall(METHOD_TWOARG_RESULT);

		mock.expectAndReturn(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS, METHOD_TWOARG_RESULT);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}

	public void testExpectManyAndThrow() throws Throwable {
		mockCallFactory.addExpectedCreateThrowStub(METHOD_EXCEPTION);
		mockCallFactory.setupCreateThrowStub(mockThrowStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS, mockThrowStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.addExpectedCreateCallExpectation(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);
		mockCallableAddable.setupCall(METHOD_TWOARG_RESULT);

		mock.expectAndThrow(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS, METHOD_EXCEPTION);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}

	public void testExpectOneAndReturn() throws Throwable {
		mockCallFactory.addExpectedCreateReturnStub(METHOD_ONEARG_RESULT);
		mockCallFactory.setupCreateReturnStub(mockReturnStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_ONEARG_NAME, METHOD_ONEARG_CONSTRAINTS, mockReturnStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);
		mockCallFactory.addExpectedCreateCallExpectation(mockCallMatch);
		mockCallFactory.setupCreateCallExpectation(mockExpectedCall);

		mockCallableAddable.addExpectedAddExpect(mockExpectedCall);
		mockCallableAddable.setupCall(METHOD_ONEARG_RESULT);

		mock.expectAndReturn(METHOD_ONEARG_NAME, METHOD_ONEARG_ARGS[0], METHOD_ONEARG_RESULT);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}

	public void testMatchManyAndReturn() throws Throwable {
		mockCallFactory.addExpectedCreateReturnStub(METHOD_TWOARG_RESULT);
		mockCallFactory.setupCreateReturnStub(mockReturnStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS, mockReturnStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);

		mockCallableAddable.addExpectedAddMatch(mockCallMatch);
		mockCallableAddable.setupCall(METHOD_TWOARG_RESULT);

		mock.matchAndReturn(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS, METHOD_TWOARG_RESULT);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}

	public void testMatchNoneAndThrow() throws Throwable {
		mockCallFactory.addExpectedCreateThrowStub(METHOD_EXCEPTION);
		mockCallFactory.setupCreateThrowStub(mockThrowStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_NOARG_NAME, C.NO_ARGS, mockThrowStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);

		mockCallableAddable.addExpectedAddMatch(mockCallMatch);
		mockCallableAddable.setupCall(METHOD_NOARGANDRETURN_RESULT);

		mock.matchAndThrow(METHOD_NOARG_NAME, METHOD_EXCEPTION);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}
	
	public void testMatchOneAndThrow() throws Throwable {
		mockCallFactory.addExpectedCreateThrowStub(METHOD_EXCEPTION);
		mockCallFactory.setupCreateThrowStub(mockThrowStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_ONEARG_NAME, METHOD_ONEARG_CONSTRAINTS, mockThrowStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);

		mockCallableAddable.addExpectedAddMatch(mockCallMatch);
		mockCallableAddable.setupCall(METHOD_ONEARG_RESULT);

		mock.matchAndThrow(METHOD_ONEARG_NAME, METHOD_ONEARG_ARGS[0], METHOD_EXCEPTION);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}
	
	public void testMatchManyAndThrow() throws Throwable {
		mockCallFactory.addExpectedCreateThrowStub(METHOD_EXCEPTION);
		mockCallFactory.setupCreateThrowStub(mockThrowStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS, mockThrowStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);

		mockCallableAddable.addExpectedAddMatch(mockCallMatch);
		mockCallableAddable.setupCall(METHOD_TWOARG_RESULT);

		mock.matchAndThrow(METHOD_TWOARG_NAME, METHOD_TWOARG_CONSTRAINTS, METHOD_EXCEPTION);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}

	public void testMatchOneAndReturn() throws Throwable {
		mockCallFactory.addExpectedCreateReturnStub(METHOD_ONEARG_RESULT);
		mockCallFactory.setupCreateReturnStub(mockReturnStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_ONEARG_NAME, METHOD_ONEARG_CONSTRAINTS, mockReturnStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);

		mockCallableAddable.addExpectedAddMatch(mockCallMatch);
		mockCallableAddable.setupCall(METHOD_ONEARG_RESULT);

		mock.matchAndReturn(METHOD_ONEARG_NAME, METHOD_ONEARG_ARGS[0], METHOD_ONEARG_RESULT);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}
	
	public void testMatchNoneAndReturn() throws Throwable {
		mockCallFactory.addExpectedCreateReturnStub(METHOD_NOARGANDRETURN_RESULT);
		mockCallFactory.setupCreateReturnStub(mockReturnStub);
		mockCallFactory.addExpectedCreateCallSignature(METHOD_NOARG_NAME, C.NO_ARGS, mockReturnStub);
		mockCallFactory.setupCreateCallSignature(mockCallMatch);

		mockCallableAddable.addExpectedAddMatch(mockCallMatch);
		mockCallableAddable.setupCall(METHOD_ONEARG_RESULT);

		mock.matchAndReturn(METHOD_NOARG_NAME, METHOD_NOARGANDRETURN_RESULT);

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}

	public void testMockAnnotatesAssertionFailedErrors()
		throws Throwable {
		final String originalMessage = "original message";

		mockCallableAddable.setupCall(new AssertionFailedError(originalMessage));

		try {
			proxy.noArgMethodVoid();
		} catch (AssertionFailedError err) {
			AssertMo.assertIncludes("should contain original message", originalMessage, err.getMessage());
			AssertMo.assertIncludes("should contain mock name", MOCK_NAME, err.getMessage());
		}
	}

	public void testMockNameFromClass() throws Exception {
		assertEquals("mockString", Mock.mockNameFromClass(String.class));
	}

	public void testMockProxyReturnsConfiguredResult()
		throws Throwable {
		final String result = "configured result";

		mockCallableAddable.setupCall(result);

		assertSame("result is returned by mock", result, proxy.oneArgMethod(METHOD_TWOARG_ARGS[0]));
	}

	public void testMockProxySendsAllArgument() throws Throwable {
		mockCallableAddable.addExpectedCall(mock, METHOD_TWOARG_NAME, METHOD_TWOARG_ARGS);
		mockCallableAddable.setupCall("result ignored");

		proxy.twoArgMethod(METHOD_TWOARG_ARGS[0], METHOD_TWOARG_ARGS[1]);

		mockCallableAddable.verifyExpectations();
	}

	public void testMockProxySendsEmptyArrayWhenNoArguments()
		throws Exception {
		mockCallableAddable.addExpectedCall(mock, METHOD_NOARG_NAME, METHOD_NOARG_ARGS);
		mockCallableAddable.setupCall("result ignored");

		proxy.noArgMethodVoid();

		Verifier.verifyObject(this);
		mockCallableAddable.verifyExpectations();
	}

	public void testMockProxyThrowsConfiguredExceptions()
		throws Throwable {
		final Throwable throwable = new DummyThrowable();

		mockCallableAddable.setupCall(new ThrowStub(throwable));

		try {
			proxy.noArgMethodVoid();
		} catch (Throwable ex) {
			assertSame("exception is caught by mock", throwable, ex);
		}
	}

	public void testMockToStringContainsName() {
		AssertMo.assertIncludes("result of toString() should include name", MOCK_NAME, mock.toString());
	}

	public void testMockVerifies() throws Exception {
		mockCallableAddable.setExpectedVerifyCalls(1);

		mock.verify();

		mockCallableAddable.verifyExpectations();
	}
	
	public void testProxyEquality() throws Exception {
		assertTrue("Should handle proxy equality without checking expectations", proxy.equals(proxy));
	}
	
	public void testProxyInEquality() throws Exception {
		boolean IGNORED_RESULT = true;
		CallStub ret = new ReturnStub(new Boolean(IGNORED_RESULT));
		mockCallFactory.setupCreateReturnStub(ret);
		mockCallFactory.setupCreateCallSignature(new CallSignature("call",C.anyArgs(1),ret));
		mockCallableAddable.setupCall(new Boolean(false));
		
		mock.matchAndReturn("call", C.anyArgs(1), IGNORED_RESULT);
		assertFalse("Should handle proxy inequality by calling through", proxy.equals("not a proxy"));
		
		Verifier.verifyObject(this);	
	}
	
	public void testProxyToString() throws Exception {
			assertEquals("Should get a mock name without touching the underlying mock", MOCK_NAME, DynamicUtil.proxyToString(proxy));
			mock.verify();  // should not fail on a proxyToString call
		}
}
