/*
 * $Id$
 *
 * SARL is an general-purpose agent programming language.
 * More details on http://www.sarl.io
 *
 * Copyright (C) 2014-2026 SARL.io, the original authors and main authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.sarl.lang.tests.general.parsing.aop;

import static io.sarl.tests.api.tools.TestAssertions.assertNullOrEmpty;
import static io.sarl.tests.api.tools.TestAssertions.assertParameterNames;
import static io.sarl.tests.api.tools.TestAssertions.assertParameterTypes;
import static io.sarl.tests.api.tools.TestAssertions.assertTypeReferenceIdentifier;
import static io.sarl.tests.api.tools.TestAssertions.assertTypeReferenceIdentifiers;
import static io.sarl.tests.api.tools.TestAssertions.assertXExpression;
import static io.sarl.tests.api.tools.TestEObjects.file;
import static io.sarl.tests.api.tools.TestUtils.multilineString;
import static io.sarl.tests.api.tools.TestValidator.validate;
import static org.eclipse.xtend.core.validation.IssueCodes.FIELD_NOT_INITIALIZED;
import static org.eclipse.xtend.core.validation.IssueCodes.INVALID_MEMBER_NAME;
import static org.eclipse.xtend.core.validation.IssueCodes.INVALID_MODIFIER;
import static org.eclipse.xtend.core.validation.IssueCodes.MISSING_ABSTRACT;
import static org.eclipse.xtend.core.validation.IssueCodes.MISSING_OVERRIDE;
import static org.eclipse.xtend.core.validation.IssueCodes.OBSOLETE_OVERRIDE;
import static org.eclipse.xtext.xbase.validation.IssueCodes.CLASS_EXPECTED;
import static org.eclipse.xtext.xbase.validation.IssueCodes.CYCLIC_INHERITANCE;
import static org.eclipse.xtext.xbase.validation.IssueCodes.DUPLICATE_FIELD;
import static org.eclipse.xtext.xbase.validation.IssueCodes.DUPLICATE_METHOD;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.google.common.base.Strings;
import org.eclipse.xtext.common.types.JvmTypeConstraint;
import org.eclipse.xtext.common.types.JvmTypeParameter;
import org.eclipse.xtext.common.types.JvmVisibility;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.diagnostics.Diagnostic;
import org.eclipse.xtext.xbase.XNumberLiteral;
import org.eclipse.xtext.xbase.XStringLiteral;
import org.eclipse.xtext.xbase.XbasePackage;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import io.sarl.lang.sarl.SarlAction;
import io.sarl.lang.sarl.SarlAgent;
import io.sarl.lang.sarl.SarlBehaviorUnit;
import io.sarl.lang.sarl.SarlCapacity;
import io.sarl.lang.sarl.SarlCapacityUses;
import io.sarl.lang.sarl.SarlEvent;
import io.sarl.lang.sarl.SarlField;
import io.sarl.lang.sarl.SarlPackage;
import io.sarl.lang.sarl.SarlScript;
import io.sarl.lang.tests.api.AbstractSarlTest;
import io.sarl.lang.validation.IssueCodes;

/**
 * @author $Author: srodriguez$
 * @author $Author: sgalland$
 * @version $Name$ $Revision$ $Date$
 * @mavengroupid $GroupId$
 * @mavenartifactid $ArtifactId$
 */
@SuppressWarnings("all")
@DisplayName("Syntax: agent")
@Tag("core")
public class AgentParsingTest {

	@Nested
	public class TopElementTest extends AbstractSarlTest {

		@Test
		@Tag("sarlParsing")
		public void declaration() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"agent A1 {}",
					"agent A2 {}"
					));
			assertEquals(2, mas.getXtendTypes().size());
			//
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent1.getName());
			assertNull(agent1.getExtends());
			assertEquals(0, agent1.getMembers().size());
			//
			SarlAgent agent2 = (SarlAgent) mas.getXtendTypes().get(1);
			assertEquals("A2", agent2.getName());
			assertNull(agent2.getExtends());
			assertEquals(0, agent2.getMembers().size());
		}

		@Test
		@Tag("sarlValidation")
		public void invalidExtend() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"capacity C1 {",
					"}",
					"agent A1 extends C1 {",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					CLASS_EXPECTED,
					"Invalid supertype. Expecting a class");
		}

		@Test
		@Tag("sarlValidation")
		public void recursiveAgentExtension_0() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 extends A1 {",
					"}"
					));

			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					CYCLIC_INHERITANCE,
					"The inheritance hierarchy of A1 contains cycles");
		}

		@Test
		@Tag("sarlValidation")
		public void recursiveAgentExtension_1() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 extends A2 {",
					"}",
					"agent A2 extends A1 {",
					"}"
					));

			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					IssueCodes.INVALID_EXTENDED_TYPE,
					"Supertype must be of type 'io.sarl.lang.core.Agent'");
		}

		@Test
		@Tag("sarlValidation")
		public void recursiveAgentExtension_2() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 extends A3 {",
					"}",
					"agent A2 extends A1 {",
					"}",
					"agent A3 extends A2 {",
					"}"
					));

			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					IssueCodes.INVALID_EXTENDED_TYPE,
					"Supertype must be of type 'io.sarl.lang.core.Agent'");
		}

		@Test
		@Tag("sarlParsing")
		public void sequenceAgentDefinition_valid00() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 extends A2 {",
					"}",
					"agent A2 extends A3 {",
					"}",
					"agent A3 {",
					"}"
					));
			assertEquals(3, mas.getXtendTypes().size());
			//
			assertTrue(Strings.isNullOrEmpty(mas.getPackage()));
			//
			SarlAgent agent3 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent3.getName());
			assertTypeReferenceIdentifier(agent3.getExtends(), "A2");
			assertTrue(agent3.getMembers().isEmpty());
			//
			SarlAgent agent2 = (SarlAgent) mas.getXtendTypes().get(1);
			assertEquals("A2", agent2.getName());
			assertTypeReferenceIdentifier(agent2.getExtends(), "A3");
			assertTrue(agent2.getMembers().isEmpty());
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(2);
			assertEquals("A3", agent1.getName());
			assertNull(agent1.getExtends());
			assertTrue(agent1.getMembers().isEmpty());
		}

		@Test
		@Tag("sarlParsing")
		public void sequenceAgentDefinition_valid01() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"agent A3 {",
					"}",
					"agent A2 extends A3 {",
					"}",
					"agent A1 extends A2 {",
					"}"
					));
			assertEquals(3, mas.getXtendTypes().size());
			//
			assertTrue(Strings.isNullOrEmpty(mas.getPackage()));
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A3", agent1.getName());
			assertNull(agent1.getExtends());
			assertTrue(agent1.getMembers().isEmpty());
			//
			SarlAgent agent2 = (SarlAgent) mas.getXtendTypes().get(1);
			assertEquals("A2", agent2.getName());
			assertTypeReferenceIdentifier(agent2.getExtends(), "A3");
			assertTrue(agent2.getMembers().isEmpty());
			//
			SarlAgent agent3 = (SarlAgent) mas.getXtendTypes().get(2);
			assertEquals("A1", agent3.getName());
			assertTypeReferenceIdentifier(agent3.getExtends(), "A2");
			assertTrue(agent3.getMembers().isEmpty());
		}

		@Test
		@Tag("sarlParsing")
		public void agentmodifier_public() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public agent A1 {}"
					));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent1.getName());
			assertNull(agent1.getExtends());
			assertEquals(JvmVisibility.PUBLIC, agent1.getVisibility());
			assertEquals(0, agent1.getMembers().size());
			assertFalse(agent1.isAbstract());
			assertFalse(agent1.isAnonymous());
			assertFalse(agent1.isFinal());
			assertFalse(agent1.isLocal());
			assertFalse(agent1.isStatic());
			assertFalse(agent1.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void agentmodifier_none() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"agent A1 {}"
					));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent1.getName());
			assertNull(agent1.getExtends());
			assertEquals(JvmVisibility.PUBLIC, agent1.getVisibility());
			assertEquals(0, agent1.getMembers().size());
			assertFalse(agent1.isAbstract());
			assertFalse(agent1.isAnonymous());
			assertFalse(agent1.isFinal());
			assertFalse(agent1.isLocal());
			assertFalse(agent1.isStatic());
			assertFalse(agent1.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_private() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"private agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER,
					"Illegal modifier for the definition of A1; only public, package, abstract and final are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_protected() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"protected agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER,
					"Illegal modifier for the definition of A1; only public, package, abstract and final are permitted");
		}

		@Test
		@Tag("sarlParsing")
		public void agentmodifier_package() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"package agent A1 {}"
					));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent1.getName());
			assertNull(agent1.getExtends());
			assertEquals(JvmVisibility.DEFAULT, agent1.getVisibility());
			assertEquals(0, agent1.getMembers().size());
			assertFalse(agent1.isAbstract());
			assertFalse(agent1.isAnonymous());
			assertFalse(agent1.isFinal());
			assertFalse(agent1.isLocal());
			assertFalse(agent1.isStatic());
			assertFalse(agent1.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void agentmodifier_abstract() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"abstract agent A1 {}"
					));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent1.getName());
			assertNull(agent1.getExtends());
			assertEquals(JvmVisibility.PUBLIC, agent1.getVisibility());
			assertEquals(0, agent1.getMembers().size());
			assertTrue(agent1.isAbstract());
			assertFalse(agent1.isAnonymous());
			assertFalse(agent1.isFinal());
			assertFalse(agent1.isLocal());
			assertFalse(agent1.isStatic());
			assertFalse(agent1.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_static() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"static agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER,
					"Illegal modifier for the definition of A1; only public, package, abstract and final are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_dispatch() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"dispatch agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER);
		}

		@Test
		@Tag("sarlParsing")
		public void agentmodifier_final() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"final agent A1 {}"
					));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent1.getName());
			assertNull(agent1.getExtends());
			assertEquals(JvmVisibility.PUBLIC, agent1.getVisibility());
			assertEquals(0, agent1.getMembers().size());
			assertFalse(agent1.isAbstract());
			assertFalse(agent1.isAnonymous());
			assertTrue(agent1.isFinal());
			assertFalse(agent1.isLocal());
			assertFalse(agent1.isStatic());
			assertFalse(agent1.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_strictfp() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"strictfp agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER);
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_native() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"native agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER,
					"Illegal modifier for the definition of A1; only public, package, abstract and final are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_volatile() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"volatile agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER,
					"Illegal modifier for the definition of A1; only public, package, abstract and final are permitted");
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_synchronized() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"synchronized agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER);
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_transient() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"transient agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER);
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_abstract_final() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"abstract final agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER,
					"The definition of A1 can either be abstract or final, not both");
		}

		@Test
		@Tag("sarlValidation")
		public void modifier_abstract_action_0() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"agent A1 {",
					"	def name",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAction(),
					MISSING_ABSTRACT);
		}

		@Test
		@Tag("sarlValidation")
		public void modifier_abstract_action_1() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"agent A1 {",
					"	abstract def name",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAction(),
					MISSING_ABSTRACT);
		}

		@Test
		@Tag("sarlParsing")
		public void modifier_abstract_action_2() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"abstract agent A1 {",
					"	abstract def name",
					"}"));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent1.getName());
			assertNull(agent1.getExtends());
			assertTrue(agent1.isAbstract());
			assertFalse(agent1.isAnonymous());
			assertFalse(agent1.isFinal());
			assertFalse(agent1.isLocal());
			assertFalse(agent1.isStatic());
			assertFalse(agent1.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlParsing")
		public void modifier_abstract_action_3() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"abstract agent A1 {",
					"	def name",
					"}"));
			validate(getValidationHelper(), getInjector(), mas).assertWarning(
					SarlPackage.eINSTANCE.getSarlAction(),
					MISSING_ABSTRACT);
			//
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertEquals("io.sarl.lang.tests.test", mas.getPackage());
			//
			SarlAgent agent1 = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent1.getName());
			assertNull(agent1.getExtends());
			assertTrue(agent1.isAbstract());
			assertFalse(agent1.isAnonymous());
			assertFalse(agent1.isFinal());
			assertFalse(agent1.isLocal());
			assertFalse(agent1.isStatic());
			assertFalse(agent1.isStrictFloatingPoint());
		}

		@Test
		@Tag("sarlValidation")
		public void agentmodifier_public_package() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"package io.sarl.lang.tests.test",
					"public package agent A1 {}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlAgent(),
					INVALID_MODIFIER,
					"The definition of A1 can only set one of public, package, abstract or final");
		}

	}

	@Nested
	public class BehaviorUnitTest extends AbstractSarlTest {

		@Test
		public void itInsideEventHandlerGuards_explicitIt() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"event E {",
					"  var field : int",
					"}",
					"agent A1 {",
					"  on E [ it.field == 4 ] {",
					"  }",
					"}"
					));
		}

		@Test
		public void itInsideEventHandlerGuards_implicitIt() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"event E {",
					"  var field : int",
					"}",
					"agent A1 {",
					"  on E [ field == 4 ] {",
					"  }",
					"}"
					));
		}

		@Test
		public void synchronizedGuard() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"event E {}",
					"agent A1 {",
					"  var myfield = new Object",
					"  on E [ synchronized(this.myfield) { this.myfield.hashCode != 0 } ] {",
					"  }",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertNoErrors();
		}

		@Test
		public void declarationWithoutGuard() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"event E {}",
					"agent A1 {",
					"on E {}",
					"}"
					));
			assertEquals(2, mas.getXtendTypes().size());
			//
			assertTrue(Strings.isNullOrEmpty(mas.getPackage()));
			//
			SarlEvent event = (SarlEvent) mas.getXtendTypes().get(0);
			assertEquals("E", event.getName());
			assertNull(event.getExtends());
			assertTrue(event.getMembers().isEmpty());
			//
			SarlAgent agent = (SarlAgent) mas.getXtendTypes().get(1);
			assertEquals("A1", agent.getName());
			assertNull(agent.getExtends());
			assertEquals(1, agent.getMembers().size());
			//
			SarlBehaviorUnit eventHandler = (SarlBehaviorUnit) agent.getMembers().get(0);
			assertTypeReferenceIdentifier(eventHandler.getName(), "E");
			assertNull(eventHandler.getGuard());
		}

		@Test
		public void declarationWithGuard() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"event E {}",
					"agent A1 {",
					"on E [ occurrence.source !== null] {}",
					"}"
					));
			assertEquals(2, mas.getXtendTypes().size());
			//
			assertTrue(Strings.isNullOrEmpty(mas.getPackage()));
			//
			SarlEvent event = (SarlEvent) mas.getXtendTypes().get(0);
			assertEquals("E", event.getName());
			assertNull(event.getExtends());
			assertTrue(event.getMembers().isEmpty());
			//
			SarlAgent agent = (SarlAgent) mas.getXtendTypes().get(1);
			assertEquals("A1", agent.getName());
			assertNull(agent.getExtends());
			assertEquals(1, agent.getMembers().size());
			//
			SarlBehaviorUnit eventHandler = (SarlBehaviorUnit) agent.getMembers().get(0);
			assertTypeReferenceIdentifier(eventHandler.getName(), "E");
			assertNotNull(eventHandler.getGuard());
		}

		@Test
		public void missedEventDeclaration() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 {",
					"on E  {}",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					TypesPackage.eINSTANCE.getJvmParameterizedTypeReference(),
					Diagnostic.LINKING_DIAGNOSTIC,
					"E cannot be resolved to a type");
		}

		@Test
		public void invalidBehaviorUnit_EventType() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 {",
					"on String {}",
					"}"
					));

			validate(getValidationHelper(), getInjector(), mas).assertError(
					TypesPackage.eINSTANCE.getJvmParameterizedTypeReference(),
					org.eclipse.xtext.xbase.validation.IssueCodes.TYPE_BOUNDS_MISMATCH,
					"Invalid type: 'java.lang.String'. Only events can be used after the keyword 'on'");
		}

		@Test
		public void invalidBehaviorUnit_GuardType() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"event E1",
					"agent A1 {",
					"on E1 [ \"hello\" ] {}",
					"}"
					));

			validate(getValidationHelper(), getInjector(), mas).assertError(
					XbasePackage.eINSTANCE.getXExpression(),
					org.eclipse.xtext.xbase.validation.IssueCodes.INCOMPATIBLE_TYPES,
					"Type mismatch: cannot convert from String to boolean");
		}

		@Test
		public void trueGuardBehaviorUnit() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"event E1",
					"agent A1 {",
					"on E1 [ true ] {}",
					"}"
					));

			validate(getValidationHelper(), getInjector(), mas).assertWarning(
					XbasePackage.eINSTANCE.getXBooleanLiteral(),
					IssueCodes.DISCOURAGED_BOOLEAN_EXPRESSION,
					"Discouraged boolean value. The guard is always true");
		}

		@Test
		public void falseGuardBehaviorUnit() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"event E1",
					"agent A1 {",
					"on E1 [ false ] {}",
					"}"
					));

			validate(getValidationHelper(), getInjector(), mas).assertWarning(
					SarlPackage.eINSTANCE.getSarlBehaviorUnit(),
					IssueCodes.UNREACHABLE_BEHAVIOR_UNIT,
					"Dead code. The guard is always false");
		}

		@Test
		public void invalidBehaviorUnit_SideEffect0() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"event E1",
					"agent A1 {",
					"var t = 5",
					"on E1 [t += 5] {",
					"}",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					XbasePackage.eINSTANCE.getXBinaryOperation(),
					org.eclipse.xtext.xbase.validation.IssueCodes.INCOMPATIBLE_TYPES,
					"cannot convert from int to boolean");
		}

		@Test
		public void invalidBehaviorUnit_SideEffect1() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"event E1",
					"agent A1 {",
					"var t = 5",
					"on E1 [(t += 5) > 0] {",
					"}",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					XbasePackage.eINSTANCE.getXBinaryOperation(),
					org.eclipse.xtext.xbase.validation.IssueCodes.INVALID_INNER_EXPRESSION,
					"Expression with side effect is not allowed in guards");
		}

		@Test
		public void castGuard_invalid() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"import java.util.List",
					"event E1 { var parameters : List<Object> }",
					"agent A1 {",
					"   var t = 5",
					"	on E1 [ ((t += 5) > 0) as boolean ] {",
					"	}",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					XbasePackage.eINSTANCE.getXCastedExpression(),
					org.eclipse.xtext.xbase.validation.IssueCodes.INVALID_INNER_EXPRESSION,
					"Expression with side effect is not allowed in guards");
		}

		@Test
		public void validBehaviorUnit_noSideEffect() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"import java.util.List",
					"event E1 { var parameters : List<Object> }",
					"agent A1 {",
					"	on E1 [ occurrence.parameters.empty ] {",
					"	}",
					"}"
					));

			validate(getValidationHelper(), getInjector(), mas).assertNoIssues();
		}

	}

	@Nested
	public class FieldTest extends AbstractSarlTest {

		@Test
		public void variableDefinition() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"agent A1 {",
					"var name : String = \"Hello\"",
					"var number : Integer",
					"}"
					));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertTrue(Strings.isNullOrEmpty(mas.getPackage()));
			//
			SarlAgent agent = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent.getName());
			assertNull(agent.getExtends());
			assertEquals(2, agent.getMembers().size());
			//
			SarlField attr1 = (SarlField) agent.getMembers().get(0);
			assertEquals("name", attr1.getName());
			assertTypeReferenceIdentifier(attr1.getType(), "java.lang.String");
			assertTrue(attr1.getInitialValue() instanceof XStringLiteral);
			assertEquals("Hello", ((XStringLiteral) attr1.getInitialValue()).getValue());
			assertFalse(attr1.isFinal());
			//
			SarlField attr2 = (SarlField) agent.getMembers().get(1);
			assertEquals("number", attr2.getName());
			assertTypeReferenceIdentifier(attr2.getType(), "java.lang.Integer");
			assertNull(attr2.getInitialValue());
			assertFalse(attr2.isFinal());
		}

		@Test
		public void valueDefinition() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"agent A1 {",
					"val name : String = \"Hello\"",
					"var number : Integer",
					"}"
					));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertTrue(Strings.isNullOrEmpty(mas.getPackage()));
			//
			SarlAgent agent = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent.getName());
			assertNull(agent.getExtends());
			assertEquals(2, agent.getMembers().size());
			//
			SarlField attr1 = (SarlField) agent.getMembers().get(0);
			assertEquals("name", attr1.getName());
			assertTypeReferenceIdentifier(attr1.getType(), "java.lang.String");
			assertXExpression(attr1.getInitialValue(), XStringLiteral.class, "Hello");
			assertTrue(attr1.isFinal());
			//
			SarlField attr2 = (SarlField) agent.getMembers().get(1);
			assertEquals("number", attr2.getName());
			assertTypeReferenceIdentifier(attr2.getType(), "java.lang.Integer");
			assertNull(attr2.getInitialValue());
			assertFalse(attr2.isFinal());
		}

		@Test
		public void multipleVariableDefinition() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 {",
					"var myfield : int",
					"var myfield1 : String",
					"var myfield : double",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlField(),
					DUPLICATE_FIELD,
					"Duplicate field myfield");
		}

		@Test
		public void multipleValueDefinition() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 {",
					"val myfield : int = 4",
					"val myfield1 : String = \"\"",
					"val myfield : double = 5",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlField(),
					DUPLICATE_FIELD,
					"Duplicate field myfield");
		}

		@Test
		public void invalidAttributeName_0() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 {",
					"var myfield1 = 4.5",
					"var $FORMAL_PARAMETER_DEFAULT_VALUE_MYFIELD = \"String\"",
					"var myfield2 = true",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlField(),
					org.eclipse.xtext.xbase.validation.IssueCodes.VARIABLE_NAME_DISALLOWED,
					"Invalid name '$FORMAL_PARAMETER_DEFAULT_VALUE_MYFIELD'");
		}

		@Test
		public void invalidAttributeName_1() throws Exception {
			SarlScript mas = file(getParseHelper(), multilineString(
					"agent A1 {",
					"val myfield1 = 4.5",
					"val $FORMAL_PARAMETER_DEFAULT_VALUE_MYFIELD = \"String\"",
					"val myfield2 = true",
					"}"
					));
			validate(getValidationHelper(), getInjector(), mas).assertError(
					SarlPackage.eINSTANCE.getSarlField(),
					org.eclipse.xtext.xbase.validation.IssueCodes.VARIABLE_NAME_DISALLOWED,
					"Invalid name '$FORMAL_PARAMETER_DEFAULT_VALUE_MYFIELD'");
		}

		@Test
		public void variableUse() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"agent A1 {",
					"var name : String = \"Hello\"",
					"def myfct() { println(name) }",
					"}"
					));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertTrue(Strings.isNullOrEmpty(mas.getPackage()));
			//
			SarlAgent agent = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent.getName());
			assertNull(agent.getExtends());
			assertEquals(2, agent.getMembers().size());
			//
			SarlField attr1 = (SarlField) agent.getMembers().get(0);
			assertEquals("name", attr1.getName());
			assertTypeReferenceIdentifier(attr1.getType(), "java.lang.String");
			assertTrue(attr1.getInitialValue() instanceof XStringLiteral);
			assertEquals("Hello", ((XStringLiteral) attr1.getInitialValue()).getValue());
			assertFalse(attr1.isFinal());
			//
			SarlAction act = (SarlAction) agent.getMembers().get(1);
			assertEquals("myfct", act.getName());
		}

		@Test
		public void valueUse() throws Exception {
			SarlScript mas = file(getParseHelper(), getValidationHelper(), multilineString(
					"agent A1 {",
					"val name : String = \"Hello\"",
					"def myfct() { println(name) }",
					"}"
					));
			assertEquals(1, mas.getXtendTypes().size());
			//
			assertTrue(Strings.isNullOrEmpty(mas.getPackage()));
			//
			SarlAgent agent = (SarlAgent) mas.getXtendTypes().get(0);
			assertEquals("A1", agent.getName());
