Commit d8ddd7da authored by Simon Barner's avatar Simon Barner
Browse files

Move semantic alignment test to org.fortiss.af3.testing

Issue-Ref: 3861
Issue-Url: https://af3-developer.fortiss.org/issues/3861

Signed-off-by: Simon Barner's avatarSimon Barner <barner@fortiss.org>
parent cf16e651
......@@ -20,6 +20,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import test.org.fortiss.af3.testing.semantic.alignment.c.CCodeEquivalenceTest;
import test.org.fortiss.af3.testing.semantic.alignment.java.JavaEquivalenceTest_SimpleTrafficLights;
//import test.org.fortiss.af3.testing.generator.GenerateStateAutomatonTest;
//import test.org.fortiss.af3.testing.generator.StateCoverageModelCheckingTest;
//import test.org.fortiss.af3.testing.generator.TransitionCoverageModelCheckingTest;
......@@ -37,7 +39,9 @@ import test.org.fortiss.af3.testing.simulator.TestCaseSimulatorTestWithAutomaton
//StateCoverageModelCheckingTest.class,
//TransitionCoverageModelCheckingTest.class,
//GenerateStateAutomatonTest.class,
TestCaseSimulatorTestWithAutomaton.class
TestCaseSimulatorTestWithAutomaton.class,
CCodeEquivalenceTest.class,
JavaEquivalenceTest_SimpleTrafficLights.class
})
// @CodeFormatterOn
public class AllTests {
......
/*-------------------------------------------------------------------------+
| Copyright 2011 fortiss GmbH |
| |
| 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 test.org.fortiss.af3.testing.semantic.alignment.c;
import static org.fortiss.af3.project.utils.ProjectUtils.loadProjectFromPlugin;
import static org.fortiss.af3.testing.method.random.util.RandomSpecificationModelElementFactory.createPortBinding;
import static org.fortiss.af3.testing.method.random.util.RandomSpecificationModelElementFactory.createRandomSpecificationPart;
import static org.fortiss.af3.testing.utils.TestingModelElementFactory.createTestSuiteSpecification;
import static org.fortiss.tooling.kernel.utils.KernelModelElementUtils.findContentElementByNameAndClass;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.conqat.lib.commons.collections.Pair;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.fortiss.af3.component.model.Component;
import org.fortiss.af3.component.model.InputPort;
import org.fortiss.af3.expression.model.terms.DoubleConst;
import org.fortiss.af3.project.model.FileProject;
import org.fortiss.af3.testing.Af3TestingActivator;
import org.fortiss.af3.testing.extension.data.TestSuiteGenerationException;
import org.fortiss.af3.testing.method.random.generator.RandomTestSuiteGenerator;
import org.fortiss.af3.testing.method.random.model.RandomSpecificationPart;
import org.fortiss.af3.testing.model.TestCase;
import org.fortiss.af3.testing.model.TestInput;
import org.fortiss.af3.testing.model.TestOutput;
import org.fortiss.af3.testing.model.TestStep;
import org.fortiss.af3.testing.model.TestSuite;
import org.fortiss.af3.testing.model.TestSuiteSpecification;
import org.fortiss.tooling.kernel.service.ICommandStackService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Tests for the C code equivalence.
*
* @author mou
*/
@RunWith(Parameterized.class)
public class CCodeEquivalenceTest {
/** The test-data area. */
private static final int TEST_CASES_NUM = 10;
/** The test-case to be used. */
private TestCase testCase;
/** The tested project. */
private FileProject project;
/** Constructor. */
public CCodeEquivalenceTest(Pair<FileProject, TestCase> pair) {
this.project = pair.getFirst();
this.testCase = pair.getSecond();
}
/**
* Test data.
*
* @throws InterruptedException
*/
@Parameters
public static Collection<Pair<FileProject, TestCase>[]> testDataCollection()
throws TestSuiteGenerationException, InterruptedException {
// @CodeFormatterOff
String[][] fileNamesAndComponents = {
new String[] {"platform:/plugin/org.fortiss.af3.testing/test-data/SimpleTrafficLightsExample.af3_23", "Controller"}
};
// @CodeFormatterOn
List<Pair<FileProject, TestCase>[]> testData =
new ArrayList<Pair<FileProject, TestCase>[]>();
// summarize all the FileProject TestCase pairs for all files and them components
for(String[] fileNameAndComponent : fileNamesAndComponents) {
// pack FileProjet TestCase pair in a one element array and then add it to the testData
// list
for(Pair<FileProject, TestCase> projectTestCasePair : collectTestCasesForProject(
fileNameAndComponent[0], fileNameAndComponent[1])) {
@SuppressWarnings("unchecked") Pair<FileProject, TestCase>[] projectTestCasePairArray =
new Pair[1];
projectTestCasePairArray[0] = projectTestCasePair;
testData.add(projectTestCasePairArray);
}
}
return testData;
}
/** */
static TestSuiteSpecification spec;
/** */
static RandomSpecificationPart rsp;
/** */
static TestSuite newSuite = null;
/**
* Generates the tests for a project.
*
* @param fileName
* @param componentName
* @return collection of project test case pair
* @throws TestSuiteGenerationException
* @throws InterruptedException
*/
private static Collection<Pair<FileProject, TestCase>>
collectTestCasesForProject(String fileName, String componentName)
throws TestSuiteGenerationException, InterruptedException {
FileProject project = loadProjectFromPlugin(Af3TestingActivator.PLUGIN_ID, fileName, true);
Component testedComponent =
findContentElementByNameAndClass(project, componentName, Component.class);
ICommandStackService.getInstance().runAsCommand(project, () -> {
spec = createTestSuiteSpecification();
testedComponent.getSpecifications().add(spec);
RandomSpecificationPart rsp = createRandomSpecificationPart();
spec.getSpecifications().add(rsp);
rsp.setNumberOfTestCases(TEST_CASES_NUM);
for(InputPort port : testedComponent.getInputPorts()) {
rsp.getPortBinding().add(createPortBinding(port));
}
RandomTestSuiteGenerator rtsg = new RandomTestSuiteGenerator();
try {
newSuite = rtsg.generate(spec, new NullProgressMonitor());
} catch(Exception e) {
e.printStackTrace();
}
});
List<Pair<FileProject, TestCase>> projectsAndTestCases =
new ArrayList<Pair<FileProject, TestCase>>();
for(TestCase tc : newSuite.getTestCases()) {
projectsAndTestCases.add(new Pair<FileProject, TestCase>(project, tc));
}
return projectsAndTestCases;
}
/** Simple test if code generation succeeded. */
@Test
public void codeGenerationTest() {
int caseNum = 0;
StringBuffer debugInfo = new StringBuffer();
// test the equivalence of expected value and simulated value of all TestSteps
for(int stepNum = 0; stepNum < testCase.getTestSteps().size(); stepNum++) {
TestStep step = testCase.getTestSteps().get(stepNum);
int inputPortPos = 0;
debugInfo.append("Step " + stepNum + " (");
// configure debug info StringBuffer with name and value of Inputports
for(TestInput ti : step.getInputs()) {
InputPort ip = testCase.getTestSuite().getInputPorts().get(inputPortPos);
debugInfo.append(ip.getName() + ":" + ti.getValue() + ", ");
inputPortPos++;
}
debugInfo.append(") -- ");
for(TestOutput to : step.getOutputs()) {
if(to.getExpectedValue() instanceof DoubleConst) {
// reduced equality assertion: expected and simulated are equal if first three
// characters are equal
String expected = to.getExpectedValue().toString().substring(0, 3);
String simulated = to.getSimulatedValue().toString().substring(0, 3);
if(!expected.equals(simulated)) {
assertEquals("Project: " + project.getName() +
" Output mismatch in test case " + caseNum + " (test step " +
stepNum + ") -- inputs are: " + debugInfo, expected, simulated);
}
} else {
if(!to.getExpectedValue().equals(to.getSimulatedValue())) {
// FIXME AssertionFailedError Bug #1164 {@link
// https://af3.fortiss.org/issues/1164}
assertEquals("Project: " + project.getName() +
" Output mismatch in test case " + caseNum + " (test step " +
stepNum + ") -- inputs are: " + debugInfo, to.getExpectedValue(),
to.getSimulatedValue());
}
}
}
}
caseNum++;
}
}
<!-- (c) 2017 fortiss GmbH -->
<body>
This package contains tests for assuring the alignment of the simulator and C code generator.
</body>
/*-------------------------------------------------------------------------+
| Copyright 2012 fortiss GmbH |
| |
| 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 test.org.fortiss.af3.testing.semantic.alignment.java;
import static java.io.File.separator;
import static org.conqat.lib.commons.filesystem.FileSystemUtils.deleteRecursively;
import static org.conqat.lib.commons.filesystem.FileSystemUtils.ensureDirectoryExists;
import static org.fortiss.af3.generator.common.utils.JavaLanguageModelElementFactory.SRC_GEN_SOURCE_PACKAGE;
import static org.fortiss.tooling.base.utils.SystemUtils.getTempDirPath;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import org.fortiss.af3.component.generator.java.ComponentArchitectureToJavaSourcePackageTransformation;
import org.fortiss.af3.component.generator.java.ComponentProgramToJavaSourcePackageTransformation;
import org.fortiss.af3.component.model.Component;
import org.fortiss.af3.component.model.ComponentArchitecture;
import org.fortiss.af3.expression.generator.java.DataDictionaryToJavaSourcePackageTransformation;
import org.fortiss.af3.expression.language.evaluation.NoVal;
import org.fortiss.af3.expression.model.DataDictionary;
import org.fortiss.af3.expression.utils.ExpressionModelElementFactory;
import org.fortiss.af3.generator.common.model.java.JavaSourcePackage;
import org.fortiss.af3.project.model.FileProject;
import org.fortiss.af3.project.model.typesystem.ITerm;
import org.fortiss.af3.project.utils.ProjectUtils;
import org.fortiss.af3.testing.Af3TestingActivator;
import org.fortiss.af3.testing.model.TestCase;
import org.fortiss.af3.testing.model.TestInput;
import org.fortiss.af3.testing.model.TestOutput;
import org.fortiss.af3.testing.model.TestStep;
import org.fortiss.af3.testing.model.TestSuite;
import org.fortiss.af3.testing.model.TestingModelFactory;
import org.fortiss.af3.testing.simulator.TestCaseSimulator;
import org.fortiss.af3.testing.simulator.TestCaseSimulatorException;
import org.fortiss.af3.testing.utils.TestingModelElementFactory;
import org.fortiss.tooling.kernel.extension.exception.TransformationFailedException;
import org.fortiss.tooling.kernel.model.IProjectRootElement;
import org.fortiss.tooling.kernel.utils.IdentifierUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Java code generator test. Checks using the Simple Traffic Lights model that the
* generated Java code behaves equivalently to the model simulator.
*
* @author trachtenherz
*/
public class JavaEquivalenceTest_SimpleTrafficLights {
/** Plug-in id */
public static final String PLUGIN_ID = Af3TestingActivator.PLUGIN_ID;
/** Project */
public FileProject project;
/** Component architecture of the model. */
ComponentArchitecture compArch;
/** Root component of the model. */
Component rootComp;
/** Data dictionary. */
DataDictionary dataDict;
/** Class representing the root component in the generated Java code */
protected Class<?> gen_rootComp;
/** Class representing the data dictionary in the generated Java code */
protected Class<?> gen_dataDict;
/** Method for clearing root component's input ports in the generated Java code */
Method genMethod_clear_inputs;
/** Method for clearing root component's output ports in the generated Java code */
Method genMethod_clear_outputs;
/** Method for initializing the root component in the generated Java code */
Method genMethod_init;
/** Method for performing a step of the root component in the generated Java code */
Method genMethod_perform_step;
/** Input port tlarchInButtonA in the generated Java code. */
Field genField_InButtonA;
/** Input port tlarchOutTrafficSignal in the generated Java code. */
Field genField_OutTrafficSignal;
/** Input port tlarchOutPedestrianSignal in the generated Java code. */
Field genField_OutPedestrianSignal;
/** Input port tlarchOutIndicatorSignalA in the generated Java code. */
Field genField_OutIndicatorSignalA;
/** Input port tlarchOutIndicatorSignalB in the generated Java code. */
Field genField_OutIndicatorSignalB;
/** Map of constant names to methods representing the constants in the generated Java code */
Map<String, Method> gen_dataDictMap = new HashMap<String, Method>();
/** Test data. */
enum InputSignal {
/** Test data value */
Present;
}
/** String value of the {@code Present} signal for the InButtonA input port. */
public static final String INPUT_SIGNAL_VALUE_STRING = "Present";
/** File representing the target forlder for Java code generation. */
File targetLocation;
/** Path to the root folder of the generated Java code. */
public File javaSourcePath;
/** Name of Java class representing the root component of the model. */
public String rootCompClassName;
/**
* Set up.
*
* @throws MalformedURLException
* if the temporary user directory for code generation cannot be found.
*/
@Before
public void setup() throws MalformedURLException {
setupProject();
setupJavaPaths();
generateJavaCode();
setupGeneratedJavaCode(javaSourcePath, rootCompClassName);
}
/** Loads the Traffic Lights model project for test. */
public void setupProject() {
project = ProjectUtils.loadProjectFromPlugin(Af3TestingActivator.PLUGIN_ID,
"/test-data/SimpleTrafficLightsExample.af3_23", true);
for(IProjectRootElement element : project.getRootElements()) {
if(element instanceof ComponentArchitecture) {
compArch = (ComponentArchitecture)element;
rootComp = compArch.getTopComponent();
} else if(element instanceof DataDictionary) {
dataDict = (DataDictionary)element;
}
}
}
/**
* Sets up the paths to generated Java code.
*
* @throws MalformedURLException
* if the temporary user directory for code generation cannot be found.
*
* @see #javaSourcePath
* @see #rootCompClassName
*/
protected void setupJavaPaths() throws MalformedURLException {
rootCompClassName = IdentifierUtils.getUniqueIdentifier(rootComp);
targetLocation = new File(getTempDirPath(false, getClass().getName()));
javaSourcePath = new File(targetLocation, SRC_GEN_SOURCE_PACKAGE);
}
/**
* Generates the Java code for the loaded model {@link #setupProject()} to a temporary folder.
*/
protected void generateJavaCode() {
try {
JavaSourcePackage javaSourcePackage =
(JavaSourcePackage)(new ComponentArchitectureToJavaSourcePackageTransformation())
.transform(compArch, null);
javaSourcePackage.setBaseLocation("");
if(targetLocation.exists()) {
deleteRecursively(targetLocation);
}
ensureDirectoryExists(targetLocation);
javaSourcePackage.writeToLocation(targetLocation.getAbsolutePath());
} catch(TransformationFailedException e) {
e.printStackTrace();
throw new AssertionError(e);
} catch(IOException e) {
e.printStackTrace();
throw new AssertionError(e);
}
}
/** Sets up the generated Java code. */
protected void setupGeneratedJavaCode(File sourcePath, String rootCompClassName) {
// Compile the generated Java code
String mainJavaFilePath = sourcePath + separator + "af3" + separator + "javagen" +
separator + "component" + separator + rootCompClassName + ".java";
String[] args = new String[] {"-sourcepath", sourcePath.toString(), mainJavaFilePath};
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, args);
ClassLoader classLoader = null;
try {
// Due to a bug in the URLClassPath (see
// https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/999dbd4192d0f819cb5224f26e9e7fa75ca6f289/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java#L471)
// generated classes are not found on Windows. Hence, we must "unify" the source path.
URL sourcePathURL = new URL(sourcePath.toURI().toString().replace("\\", "/"));
classLoader = URLClassLoader.newInstance(new URL[] {sourcePathURL});
// Load the classes for root component and data dictionary
gen_rootComp = Class.forName(
ComponentProgramToJavaSourcePackageTransformation.COMPONENT_JAVA_PACKAGE + "." +
rootCompClassName,
true, classLoader);
gen_dataDict = Class.forName(
DataDictionaryToJavaSourcePackageTransformation.DD_JAVA_PACKAGE + "." +
DataDictionaryToJavaSourcePackageTransformation.DD_JAVA_CLASS,
true, classLoader);
} catch(Exception e) {
e.printStackTrace();
throw new AssertionError(e);
}
// Store references to certain methods and fields in the generated Java code
readGenMethods();
readGenFields();
readGenDataDict();
}
/** Stored method references from the generated Java code required to perform the test. */
protected void readGenMethods() {
Method[] methods = gen_rootComp.getDeclaredMethods();
for(Method m : methods) {
final String methodName = m.getName();
if(methodName.contains("clear_inputs")) {
genMethod_clear_inputs = m;
} else if(methodName.contains("clear_outputs")) {
genMethod_clear_outputs = m;
} else if(methodName.contains("init")) {
genMethod_init = m;
} else if(methodName.contains("perform_step")) {
genMethod_perform_step = m;
} else {
// Nothing
}
}
}
/** Stored field references from the generated Java code required to perform the test. */
protected void readGenFields() {
Field[] fields = gen_rootComp.getDeclaredFields();
for(Field f : fields) {
final String fieldName = f.getName();
if(fieldName.contains("InButtonA")) {
genField_InButtonA = f;
} else if(fieldName.contains("OutTrafficSignal")) {
genField_OutTrafficSignal = f;
} else if(fieldName.contains("OutPedestrianSignal")) {
genField_OutPedestrianSignal = f;
} else if(fieldName.contains("OutIndicatorSignalA")) {
genField_OutIndicatorSignalA = f;
} else if(fieldName.contains("OutIndicatorSignalB")) {
genField_OutIndicatorSignalB = f;
} else {
// Nothing
}
}
}
/** Stores the data dictionary from the generated Java code. */
protected void readGenDataDict() {
Method[] methods = gen_dataDict.getDeclaredMethods();
for(Method m : methods) {
gen_dataDictMap.put(m.getName(), m);
}
}
/** Performs one step for the generated Java code. */
protected void genPerformStep()
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
genMethod_clear_outputs.invoke(null);
genMethod_perform_step.invoke(null);
genMethod_clear_inputs.invoke(null);
}
/**
* @param inputSignals
* Input values for the tlarchInButtonA input port of the Traffic Lights Example.
* {@link InputSignal#Present} correspond to {@code Present} message inputs while
* {@code null} values correspond to {@code NoVal} inputs.
* @param testSuite
* {@link TestSuite} to which the test case will be added. If {@code null} a new
* {@link TestSuite} instance is created.
* @return
* A {@link TestCase} with inputs corresponding to {@code inputSignals}.
*/
protected TestCase generateTestCase(InputSignal[] inputSignals, TestSuite testSuite) {
TestCase testCase = TestingModelFactory.eINSTANCE.createTestCase();
if(testSuite == null) {
testSuite = TestingModelElementFactory.createTestSuite("Java Generator Test Suite",
rootComp);
}
testSuite.getTestCases().add(testCase);