package org.overture.vdm2jml.tests;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.definitions.SFunctionDefinition;
import org.overture.ast.definitions.SOperationDefinition;
import org.overture.ast.lex.Dialect;
import org.overture.ast.modules.AModuleModules;
import org.overture.ast.util.ClonableString;
import org.overture.codegen.ir.IRSettings;
import org.overture.codegen.ir.PIR;
import org.overture.codegen.ir.declarations.ADefaultClassDeclIR;
import org.overture.codegen.ir.declarations.AMethodDeclIR;
import org.overture.codegen.utils.GeneralCodeGenUtils;
import org.overture.codegen.utils.GeneratedData;
import org.overture.codegen.utils.GeneratedModule;
import org.overture.codegen.vdm2java.JavaFormat;
import org.overture.codegen.vdm2java.JavaSettings;
import org.overture.codegen.vdm2jml.JmlGenerator;
import org.overture.config.Release;
import org.overture.config.Settings;
import org.overture.typechecker.util.TypeCheckerUtil;
import org.overture.typechecker.util.TypeCheckerUtil.TypeCheckResult;
abstract public class AnnotationTestsBase
{
private static final String MODULE_STATE_NAME = "St";
private static final String MODULE_NAME = "M";
public static final String TEST_RESOURCES_ROOT = "src" + File.separatorChar
+ "test" + File.separatorChar + "resources" + File.separatorChar;
public static final String TEST_RES_STATIC_ANALYSIS_ROOT = TEST_RESOURCES_ROOT
+ "static_analysis" + File.separatorChar;
public static final String SPEC_PUBLIC_ANNOTATION = "/*@ spec_public @*/";
public static final String PURE_ANNOTATION = "/*@ pure @*/";
public static final String HELPER_ANNOTATION = "/*@ helper @*/";
private static final boolean VERBOSE = false;
// The IR class that the input module generates to
protected static ADefaultClassDeclIR genModule;
// The IR class that is used to represent the type of the module state
protected static ADefaultClassDeclIR genStateType;
private static Logger log = Logger.getLogger(AnnotationTestsBase.class.getName());
@BeforeClass
public static void prepareVdmTypeChecker()
{
Settings.dialect = Dialect.VDM_SL;
Settings.release = Release.VDM_10;
}
public static void init(String fileName) throws AnalysisException
{
List<ADefaultClassDeclIR> classes = getClasses(fileName);
for (ADefaultClassDeclIR clazz : classes)
{
if (clazz.getName().equals(MODULE_NAME))
{
genModule = clazz;
} else if (clazz.getName().equals(MODULE_STATE_NAME))
{
genStateType = clazz;
}
}
}
protected static void validGeneratedModule()
{
Assert.assertTrue("No module was generated", genModule != null);
Assert.assertEquals("Expected generated module to be in different package", JmlGenerator.DEFAULT_JAVA_ROOT_PACKAGE, genModule.getPackage());
}
public static void validateGenModuleAndStateType()
{
validGeneratedModule();
Assert.assertTrue("State type was not generated", genStateType != null);
String stateClassPackage = JmlGenerator.DEFAULT_JAVA_ROOT_PACKAGE + "."
+ genModule.getName() + JavaFormat.TYPE_DECL_PACKAGE_SUFFIX;
Assert.assertEquals("Generated state type is located in a wrong package", stateClassPackage, genStateType.getPackage());
}
public static void initJmlGen(JmlGenerator jmlGen)
{
IRSettings irSettings = jmlGen.getIrSettings();
irSettings.setCharSeqAsString(true);
irSettings.setGeneratePreConds(true);
irSettings.setGeneratePreCondChecks(false);
irSettings.setGeneratePostConds(true);
irSettings.setGeneratePostCondChecks(false);
JavaSettings javaSettings = jmlGen.getJavaSettings();
javaSettings.setDisableCloning(false);
javaSettings.setJavaRootPackage(null); // Default package
javaSettings.setGenRecsAsInnerClasses(false);
}
public static List<ADefaultClassDeclIR> getClasses(GeneratedData data)
{
List<ADefaultClassDeclIR> classes = new LinkedList<ADefaultClassDeclIR>();
for (GeneratedModule node : data.getClasses())
{
if (VERBOSE)
{
log.info(node.getContent());
log.info("*******************");
}
if (node.getIrNode() instanceof ADefaultClassDeclIR)
{
classes.add((ADefaultClassDeclIR) node.getIrNode());
}
}
return classes;
}
public static List<AMethodDeclIR> getGenFunctions(
List<AMethodDeclIR> methods)
{
List<AMethodDeclIR> genFuncs = new LinkedList<AMethodDeclIR>();
for (AMethodDeclIR m : methods)
{
if (m.getSourceNode() != null
&& m.getSourceNode().getVdmNode() instanceof SFunctionDefinition)
{
genFuncs.add(m);
}
}
return genFuncs;
}
public static AMethodDeclIR getMethod(List<AMethodDeclIR> methods,
String name)
{
for (AMethodDeclIR m : methods)
{
if (m.getName().equals(name))
{
return m;
}
}
return null;
}
public static List<AMethodDeclIR> getGenMethods(List<AMethodDeclIR> methods)
{
List<AMethodDeclIR> genOps = new LinkedList<AMethodDeclIR>();
for (AMethodDeclIR m : methods)
{
if (m.getSourceNode() != null
&& m.getSourceNode().getVdmNode() instanceof SOperationDefinition)
{
genOps.add(m);
}
}
return genOps;
}
public static List<ADefaultClassDeclIR> getClasses(String fileName)
throws AnalysisException
{
List<File> files = new LinkedList<File>();
files.add(new File(TEST_RES_STATIC_ANALYSIS_ROOT + fileName));
TypeCheckResult<List<AModuleModules>> tcResult = TypeCheckerUtil.typeCheckSl(files);
if (GeneralCodeGenUtils.hasErrors(tcResult))
{
Assert.fail("Could not parse/type check VDM model:\n"
+ GeneralCodeGenUtils.errorStr(tcResult));
}
JmlGenerator jmlGen = new JmlGenerator();
initJmlGen(jmlGen);
GeneratedData data = jmlGen.generateJml(tcResult.result);
return getClasses(data);
}
public static String getLastAnnotation(PIR node)
{
if (node.getMetaData() != null)
{
return getAnnotation(node, node.getMetaData().size() - 1);
} else
{
return null;
}
}
public static String getAnnotation(PIR node, int idx)
{
List<? extends ClonableString> metaData = node.getMetaData();
if (metaData != null && idx >= 0 && idx < metaData.size())
{
return metaData.get(idx).value;
}
return null;
}
public void assertFuncIsPureOnly(String funcName)
{
AMethodDeclIR preCondFunc = getMethod(genModule.getMethods(), funcName);
Assert.assertTrue("Expected only a @pure annotaton for the pre condition function", preCondFunc.getMetaData().size() == 1);
Assert.assertEquals("Expected pre condition function to be pure", PURE_ANNOTATION, getLastAnnotation(preCondFunc));
}
public static void assertHelper(PIR node, String msg)
{
for (ClonableString m : node.getMetaData())
{
if (m.value.equals(HELPER_ANNOTATION))
{
return;
}
}
Assert.assertTrue(msg, false);
}
public static void assertPure(List<AMethodDeclIR> methods)
{
Assert.assertTrue("Expected functions to be defined", methods != null
&& !methods.isEmpty());
for (AMethodDeclIR func : methods)
{
if (!func.getIsConstructor())
{
assertPureMethod(func);
}
}
}
public static void assertPureMethod(AMethodDeclIR method)
{
String failureMsg = "Expected method " + method.getName()
+ " to be pure";
List<? extends ClonableString> metaData = method.getMetaData();
Assert.assertTrue(failureMsg, metaData != null && !metaData.isEmpty());
for (ClonableString m : method.getMetaData())
{
if (m.value.equals(PURE_ANNOTATION))
{
return;
}
}
Assert.assertTrue(failureMsg, false);
}
public static void assertNotPureMethod(AMethodDeclIR method)
{
String failureMsg = "Expected method " + method.getName()
+ " not to be pure";
for (ClonableString m : method.getMetaData())
{
if (m.value.equals(PURE_ANNOTATION))
{
Assert.fail(failureMsg);
}
}
}
public static void assertRecMethodsPurity(List<AMethodDeclIR> stateMethods)
{
for (AMethodDeclIR m : stateMethods)
{
if (m.getName().equals("hashCode") || m.getName().equals("equals")
|| m.getName().equals("toString")
|| m.getName().equals("copy")
|| m.getName().startsWith("get_")
|| m.getName().equals("valid"))
{
assertPureMethod(m);
} else
{
assertNotPureMethod(m);
}
}
}
}