package pp.s1184725.boppi.test;

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

/**
 * Tests for simple (i.e. no functions involved) variable use.
 * 
 * @author Frank Wibbelink
 */
public class SimpleVariableTest {

	/**
	 * Correct variable use
	 */
	@Test
	public void correctVariableParsing() {
		BoppiTests.parseFile("programs/simpleVariable.boppi");
		assertThat(BoppiTests.log, empty());
	}

	/**
	 * Variable use with syntax errors
	 */
	@Test
	public void wrongVariableParsing() {
		BoppiTests.parseString("var");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.parseString("var bool 5");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.parseString("var 'c' varname");
		assertThat(BoppiTests.log, not(empty()));

		BoppiTests.parseString("var bool int; int := true");
		assertThat(BoppiTests.log, not(empty()));

		BoppiTests.parseString("var bool; true true;");
		assertThat(BoppiTests.log, hasSize(2));

		BoppiTests.parseString("var int a; var int b; a := 4 + b := 3;");
		assertThat(BoppiTests.log, hasSize(1));
	}

	/**
	 * Correct variable scoping
	 */
	@Test
	public void correctScopeParsing() {
		BoppiTests.parseFile("programs/simpleScope.boppi");
		assertThat(BoppiTests.log, empty());
	}

	/**
	 * Correct variable use
	 */
	@Test
	public void correctVariableChecking() {
		BoppiTests.checkFile("programs/simpleVariable.boppi");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("var const int x; x := 3; var x y; y := x;");
		assertThat(BoppiTests.log, is(empty()));

	}

	/**
	 * Variable use with type errors, undefined variables and reassigning constants
	 */
	@Test
	public void wrongVariableChecking() {
		BoppiTests.checkString("var bool name; name := 5");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("undefinedName");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("var undefinedType name; 1");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("var bool endsWithDeclaration;");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("var int x; 2+x;");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("var const int x; 2+x;");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("var const int x; x := 3; var int y; y := x; x := 2;");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("var bool var1; var var1 var2; var2 := 'c' ");
		assertThat(BoppiTests.log, hasSize(1));
	}

	/**
	 * Correct variable scoping
	 */
	@Test
	public void correctScopeChecking() {
		BoppiTests.checkFile("programs/simpleScope.boppi");
		assertThat(BoppiTests.log, is(empty()));
	}

	/**
	 * Variable redeclaring and out-of-scope errors
	 */
	@Test
	public void wrongScopeChecking() {
		BoppiTests.checkString("var bool var1; var bool var1; 1");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("var bool var1; var char var1; 1");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("{ var int var1; var1 := 4}; var int var2; var1");
		assertThat(BoppiTests.log, hasSize(1));
	}

	/**
	 * Correct variable use
	 */
	@Test
	public void correctVariableGeneration() {
		BoppiTests.compileAndRunString("print({var int x; x := 8; print(x)})");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("8", "8")));

		BoppiTests.compileAndRunString("var int x; var int y; x := 3*(y := 4); print(x,y)");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("12", "4")));

		BoppiTests.compileAndRunFile("programs/simpleVariable.boppi");
		assertThat(BoppiTests.log, is(empty()));
		
		BoppiTests.compileAndRunFile("programs/simpleScope.boppi");
		assertThat(BoppiTests.out, is(arrayContaining("9", "4", "true")));
		assertThat(BoppiTests.log, is(empty()));
	}

}
