package pp.s1184725.boppi.test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import org.junit.Test;

/**
 * Tests for function use and simple function passing (i.e. no closures required).
 * 
 * @author Frank Wibbelink
 */
public class SimpleFunctionTest {

	/**
	 * Correct function declaration and calls
	 */
	@Test
	public void correctFunctionParsing() {
		BoppiTests.parseString("function int id(int a) a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function int id(int a) a; id(1)");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("var int a; a := 1; function int const() a; const()");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function char const(char a, char b) a; print(const('A', 'T'))");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}");
		assertThat(BoppiTests.log, is(empty()));
	}

	/**
	 * Function use with syntax errors
	 */
	@Test
	public void wrongFunctionParsing() {
		BoppiTests.parseString("function doNothing(int a, char b) a+b; print(add(4, 'T')+1)");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.parseString("function char add(int a, int b);");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.parseString("function int add(int a, int b) a+b; add(4,)");
		assertThat(BoppiTests.log, hasSize(1));
	}

	/**
	 * Correct function declaration and calls
	 */
	@Test
	public void correctFunctionChecking() {
		BoppiTests.checkString("function int id(int a) a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("function int id(int a) a; id(1); 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("var int a; function int const() a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("function char const(char a, char b) a; print(const('A', 'T'))");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}; 0");
		assertThat(BoppiTests.log, is(empty()));
	}

	/**
	 * Function use with type errors
	 */
	@Test
	public void wrongFunctionChecking() {
		BoppiTests.checkString("function int add(int a, char b) a+b; print(add(4, 'T')+1)");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("function char add(int a, int b) a+b; print(add(4, 5))");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("function int add(int a, int b) a+b; print(add(4, 'T')+1)");
		assertThat(BoppiTests.log, hasSize(1));
	}

	/**
	 * Correct evaluation of basic functions
	 */
	@Test
	public void correctSimpleFunctionGeneration() {
		BoppiTests.compileAndRunString("function int id(int a) a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.compileAndRunString("function int id(int a) a; id(1); 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.compileAndRunString("var int a; function int const(int b) a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.compileAndRunString("var int a; a := 1; function int const(int b) a; print(const(4))");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("1")));

		BoppiTests.compileAndRunString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("10")));

		BoppiTests.compileAndRunString("function char const(char a, char b) a; print(const('A', 'T'))");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("A")));
	}

	/**
	 * Correct evaluation of recursive functions, nested functions and function
	 * passing
	 */
	@Test
	public void correctComplexFunctionGeneration() {
		BoppiTests.compileAndRunString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}; 0");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.compileAndRunFile("recursiveFactorial.boppi", "5");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("120")));

		BoppiTests.compileAndRunFile("simpleFunctionPassing.boppi");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("40", "104", "1", "2", "8")));
	}

}
