/*
 * LpGroundDeciderTest.java
 * JUnit based test
 *
 * Copyright (C) 2006 - 2007 Martin Slota
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*
 * History:
 * v0.1 (2007-01-29): initial version
 * v0.2 (2007-01-29):
 * - 10 tests written and documented
 * v0.2.1 (2007-02-08):
 * - changed because LpParser changed (made reusable...)
 * v0.2.2 (2007-02-11):
 * - changed to reflect the new caching mechanism
 * v0.2.3 (2007-02-12):
 * - changed because of changes in LpParser
 * v1.0.0 (2007-05-05):
 * - promoted to version 1.0.0 :o)
 */

package lp.struct.util;

import java.util.ArrayList;
import junit.framework.*;
import java.util.List;
import lp.parse.LpParser;
import lp.struct.LpCompoundTerm;
import lp.struct.LpConstant;
import lp.struct.LpFunction;
import lp.struct.LpPredicate;
import lp.struct.LpStructureUnit;
import lp.struct.LpTerm;
import lp.struct.LpVariable;

/**
 * Contains tests of the {@link LpGroundDecider} class.
 * 
 * @author Martin Slota
 * @version 1.0.0
 * @see LpGroundDecider
 */
public class LpGroundDeciderTest extends TestCase {
	private final LpGroundDecider gd = new LpGroundDecider();
	
	/**
	 * A default test case constructor.
	 *
	 * @param testName the name of the test case
	 */
	public LpGroundDeciderTest(String testName) {
		super(testName);
	}

	/**
	 * Tests passing {@code null} to the 
	 * {@link LpGroundDecider#isGround(LpStructureUnit)} method.
	 */
	public void testIsGroundNull() {
		try {
			gd.isGround(null);
			fail("A NullPointerException expected!");
		} catch (NullPointerException e) {
			// behaves as expected
		}
	}
	
	/**
	 * Tests passing a {@link LpConstant} instance to the 
	 * {@link LpGroundDecider#isGround(LpStructureUnit)} method.
	 */
	public void testIsGroundConstant() {
		LpStructureUnit unit = LpConstant.getInstance("someConstant");
		doTest(true, unit);
	}
	
	/**
	 * Tests passing a {@link LpVariable} instance to the 
	 * {@link LpGroundDecider#isGround(LpStructureUnit)} method.
	 */
	public void testIsGroundVariable() {
		LpStructureUnit unit = LpVariable.getInstance("SomeVariable");
		doTest(false, unit);
	}
	
	/**
	 * Tests passing a {@link LpFunction} instance to the 
	 * {@link LpGroundDecider#isGround(LpStructureUnit)} method.
	 */
	public void testIsGroundFunction() {
		LpStructureUnit unit = LpFunction.getInstance("f", 10);
		doTest(true, unit);
	}
	
	/**
	 * Tests passing {@link LpCompoundTerm} instances to the 
	 * {@link LpGroundDecider#isGround(LpStructureUnit)} method.
	 */
	public void testIsGroundCompoundTerm() {
		LpFunction func = LpFunction.getInstance("g", 3);
		LpConstant constA = LpConstant.getInstance("a");
		LpConstant constB = LpConstant.getInstance("b");
		LpConstant constC = LpConstant.getInstance("c");
		LpVariable varX = LpVariable.getInstance("X");
		
		List<LpTerm> innerArgs = new ArrayList<LpTerm>(3);
		innerArgs.add(constC);
		innerArgs.add(constB);
		innerArgs.add(constA);
		
		List<LpTerm> args = new ArrayList<LpTerm>(3);
		args.add(constA);
		args.add(LpCompoundTerm.getInstance(func, innerArgs));
		args.add(constA);
		
		// unit is "f(a, f(c, b, a), a)"
		LpStructureUnit unit = LpCompoundTerm.getInstance(func, args);
		doTest(true, unit);
		
		
		innerArgs = new ArrayList<LpTerm>(3);
		innerArgs.add(constC);
		innerArgs.add(constB);
		innerArgs.add(varX);
		
		args = new ArrayList<LpTerm>(3);
		args.add(constA);
		args.add(LpCompoundTerm.getInstance(func, innerArgs));
		args.add(constA);
		
		// unit is "f(a, f(c, b, X), a)"
		unit = LpCompoundTerm.getInstance(func, args);
		doTest(false, unit);
	}
	
	/**
	 * Tests passing a {@link LpPredicate} instance to the 
	 * {@link LpGroundDecider#isGround(LpStructureUnit)} method.
	 */
	public void testIsGroundPredicate() {
		LpStructureUnit unit = LpPredicate.getInstance("p", 4);
		doTest(true, unit);
	}
	
	/**
	 * Tests passing {@link lp.struct.LpLiteral} instances to the 
	 * {@link LpGroundDecider#isGround(LpStructureUnit)} method.
	 *
	 * Uses {@link LpParser} to simplify the creation process of the complicated
	 * {@code LpLiteral} objects.
	 */
	public void testIsGroundLiteral() {
		String source = "p(X, a, b).\n" +
				"not q(a).\n" +
				"married(john, marry).\n" +
				"not son(john, X).";
		LpParser p = new LpParser();
		p.setInput(source);
		doTest(false, p.parseRule().getHead());
		doTest(true, p.parseRule().getHead());
		doTest(true, p.parseRule().getHead());
		doTest(false, p.parseRule().getHead());
	}
	
	/**
	 * Tests passing {@link lp.struct.LpRule} instances to the 
	 * {@link LpGroundDecider#isGround(LpStructureUnit)} method.
	 *
	 * Uses {@link LpParser} to simplify the creation process of the complicated
	 * {@code LpRule} objects.
	 */
	public void testIsGroundRule() {
		String source = "p(a, b) :- not q(c, f(a, X), d), r(a, b).\n" +
				"not q(a) :- p(a, f(g(a), c)).\n" +
				"married(john, marry).\n" +
				"u :- not u, son(john, X).";
		LpParser p = new LpParser();
		p.setInput(source);
		doTest(false, p.parseRule());
		doTest(true, p.parseRule());
		doTest(true, p.parseRule());
		doTest(false, p.parseRule());
	}
	
	/**
	 * Tests the {@link LpGroundDecider#isGround(LpStructureUnit)} when called 
	 * with {@code unit} as input.
	 *
	 * @param expected the expected boolean&#8212;{@code true} iff {@code unit}
	 * is ground
	 * @param unit the tested {@link LpStructureUnit}
	 */
	private void doTest(boolean expected, LpStructureUnit unit) {
		assertEquals("GroundDecider result not as expected!", 
				expected, gd.isGround(unit));
	}
}