package eu.dnetlib.contract.cp.eval;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.HashMap;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;

import eu.dnetlib.contract.conv.DummyObectToStringConverter;
import eu.dnetlib.contract.cp.EntryCheckPoint;
import eu.dnetlib.contract.cp.comp.ComparatorManager;
import eu.dnetlib.contract.cp.comp.IComparatorModule;
import eu.dnetlib.contract.event.EntryContractEvent;
import eu.dnetlib.contract.node.EvaluationResult;
import eu.dnetlib.contract.node.EvaluationResult.Status;


/**
 * Entry CheckPoint evaluation tests.
 * @author mhorst
 *
 */
public class EntryCheckPointEvaluationTest {

	EntryCheckPoint checkPoint = null;
	JoinPoint joinPoint = null;
	
	private Mockery context;
	
//	target method props
	String className = "java.lang.String";
	String methodName = "compareTo";
	String stringValue = "stringValue";
	String anotherString = "anotherStringValue";
	
	@Before
	public void init() {
		context = new Mockery();
		joinPoint = context.mock(JoinPoint.class);
		checkPoint = getValidCheckPoint();
		ComparatorManager comparatorManager = new ComparatorManager();
		comparatorManager.setModulesRegistry(new HashMap<Class<? extends Object>, IComparatorModule<? extends Object>>());
		checkPoint.setComparatorManager(comparatorManager);
		checkPoint.setConverter(new DummyObectToStringConverter());
	}
	
	@Test
	public void testEvaluateWithNullJoinPoint() throws Exception {
		joinPoint = null;
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FATAL,result.getStatus());
	}
	
	@Test
	public void testEvaluateInvalidJoinPointInvaldTargetObject() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);

		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		
//		setting invalid target object
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new Integer(0)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidJoinPointInvaldMethodName() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
//		setting invalid method name
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue("invalidMethodName"));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidJoinPointNullArgs() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting null arguments
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {null}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidJoinPointInvalidArgs() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		
//		setting invalid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {"someInvalidArgument"}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointBadClassName() throws Exception {
//		setting invalid className
		checkPoint.setClassName("invalidClassName");
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointBadMethodName() throws Exception {
//		setting invalid className
		checkPoint.setMethodName("invalidMethodName");
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointNullAttributes() throws Exception {
//		setting invalid className
		checkPoint.setArgs(null);
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointEmptyAttributes() throws Exception {
//		setting invalid className
		checkPoint.setArgs(new Object[0]);
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointBadAttributes() throws Exception {
//		setting invalid className
		checkPoint.setArgs(new Object[] {"bad attribute"});
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateValid() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.OK,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	EntryCheckPoint getValidCheckPoint() {
		EntryCheckPoint checkPoint = new EntryCheckPoint();
		checkPoint.setClassName(className);
		checkPoint.setMethodName(methodName);
		checkPoint.setArgs(new Object[] {anotherString});
		checkPoint.getArgsStrictChecking()[0] = true;
		return checkPoint;
	}
}
