/**
* Copyright (c) 2009-2011, The HATS Consortium. All rights reserved.
* This file is licensed under the terms of the Modified BSD License.
*/
package abs.backend.tests;
import static abs.backend.tests.AbsASTBuilderUtil.getExpStmt;
import static abs.backend.tests.AbsASTBuilderUtil.getFnApp;
import static abs.backend.tests.AbsASTBuilderUtil.getFutUnitType;
import static abs.backend.tests.AbsASTBuilderUtil.getType;
import static abs.backend.tests.AbsASTBuilderUtil.getVAssign;
import static abs.backend.tests.AbsASTBuilderUtil.getVarDecl;
import static abs.backend.tests.AbsASTBuilderUtil.newObj;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;
import abs.backend.prettyprint.DefaultABSFormatter;
import abs.frontend.ast.Access;
import abs.frontend.ast.AsyncCall;
import abs.frontend.ast.Block;
import abs.frontend.ast.ClassDecl;
import abs.frontend.ast.DataConstructorExp;
import abs.frontend.ast.DataTypeUse;
import abs.frontend.ast.FromImport;
import abs.frontend.ast.GetExp;
import abs.frontend.ast.Import;
import abs.frontend.ast.InterfaceDecl;
import abs.frontend.ast.List;
import abs.frontend.ast.MainBlock;
import abs.frontend.ast.MethodSig;
import abs.frontend.ast.Model;
import abs.frontend.ast.ModuleDecl;
import abs.frontend.ast.Name;
import abs.frontend.ast.NamedImport;
import abs.frontend.ast.ParametricDataTypeUse;
import abs.frontend.ast.PureExp;
import abs.frontend.ast.StarImport;
import abs.frontend.ast.SyncCall;
import abs.frontend.ast.TypeUse;
import abs.frontend.ast.VarDeclStmt;
import abs.frontend.ast.VarUse;
import abs.frontend.ast.WhileStmt;
import abs.frontend.tests.ABSFormatter;
/**
* The ABSUnit test runner generator
*
* @author pwong
*
*/
public class ASTBasedABSTestRunnerGenerator extends AbstractABSTestRunnerGenerator {
public ASTBasedABSTestRunnerGenerator(Model model) {
super(model);
}
@Override
public void generateTestRunner(PrintStream stream) {
ModuleDecl module = new ModuleDecl();
module.setName(RUNNER_MAIN);
module.setImportList(generateImportsAST());
module.setBlock(generateMainBlockAST(module.getImportList()));
PrintWriter writer = new PrintWriter(stream, true);
ABSFormatter formatter = new DefaultABSFormatter(writer);
module.doPrettyPrint(writer, formatter);
}
private void getImportsFrom(Set<String> mn, Set<String> qn, ModuleDecl m) {
mn.add(m.getName());
for (Import i : m.getImportList()) {
if (i instanceof NamedImport) {
for (Name n : ((NamedImport) i).getNameList()) {
qn.add(n.getName());
}
} else if (i instanceof FromImport) {
mn.add(((FromImport) i).getModuleName());
} else if (i instanceof StarImport) {
mn.add(((StarImport) i).getModuleName());
}
}
}
private List<Import> generateImportsAST() {
List<Import> imports = new List<Import>();
Set<String> mn = new HashSet<String>();
Set<String> qn = new HashSet<String>();
for (InterfaceDecl key : tests.keySet()) {
getImportsFrom(mn, qn, key.getModuleDecl());
for (ClassDecl clazz : tests.get(key)) {
getImportsFrom(mn, qn, clazz.getModuleDecl());
}
}
for (String m : mn) {
imports.add(new StarImport(m));
}
if (!qn.isEmpty()) {
List<Name> names = new List<Name>();
for (String q : qn) {
names.add(new Name(q));
}
imports.add(new NamedImport(names));
}
return imports;
}
private MainBlock generateMainBlockAST(List<Import> list) {
final MainBlock block = new MainBlock();
DataConstructorExp empty = new DataConstructorExp("EmptySet",new List<PureExp>());
VarDeclStmt futsStatement = getVarDecl(futs, getType("Set", getFutUnitType()), empty);
block.addStmtNoTransform(futsStatement);
VarDeclStmt futStatement = getVarDecl(fut, getFutUnitType(), null);
block.addStmtNoTransform(futStatement);
Set<TypeUse> use = new HashSet<TypeUse>();
for (InterfaceDecl key : tests.keySet()) {
for (ClassDecl clazz : tests.get(key)) {
use.addAll(generateTestClassImplAST(key, clazz, block));
}
}
block.addStmtNoTransform(generateWaitSyncAST());
return block;
}
private WhileStmt generateWaitSyncAST() {
WhileStmt ws = new WhileStmt();
ws.setCondition(getFnApp("hasNext",new VarUse(futs)));
Block body = new Block();
DataTypeUse u = getType("Pair", getType("Set",
getType("Fut", getType("Unit"))),getType("Fut", getType("Unit")));
body.addStmtNoTransform(getVarDecl("nt", u, getFnApp("next",new VarUse(futs))));
body.addStmtNoTransform(getVAssign(fut, getFnApp("snd",new VarUse("nt"))));
body.addStmtNoTransform(getVAssign(futs, getFnApp("fst",new VarUse("nt"))));
body.addStmtNoTransform(getExpStmt(new GetExp(new VarUse("fut"))));
// Attach body at the end, since JastAdd will avoid touching ASTs without parents.
ws.setBody(body);
return ws;
}
private Set<TypeUse> generateTestClassImplAST(
InterfaceDecl inf, ClassDecl clazz, MainBlock block) {
Set<TypeUse> accesses = new HashSet<TypeUse>();
TypeUse dataType = generateDataPointsAST(inf, clazz, accesses, block);
String namePrefix = clazz.getName();
int instance = 0;
for (MethodSig method : getTestMethods(inf)) {
Block thisBlock = block;
WhileStmt ws = null;
if (method.getNumParam() > 0) {
if (dataType == null) {
throw new IllegalStateException("Test method requires arguments but test class defines no data point");
}
/*
* a while loop over all data points
*/
String dataPointSet = dataPointSetName(clazz);
ws = new WhileStmt();
ws.setCondition(getFnApp("hasNext",new VarUse(dataPointSet)));
Block body = new Block();
thisBlock = body;
DataTypeUse u = getType("Pair", getType("Set", (TypeUse)dataType.copy()), (TypeUse)dataType.copy());
thisBlock.addStmtNoTransform(getVarDecl("nt", u, getFnApp("next",new VarUse(dataPointSet))));
thisBlock.addStmtNoTransform(getVarDecl(dataValue, (TypeUse)dataType.copy(), getFnApp("snd",new VarUse("nt"))));
thisBlock.addStmtNoTransform(getVAssign(dataPointSet, getFnApp("fst",new VarUse("nt"))));
ws.setBody(body);
}
/*
* Add those methods that are not ignored
*/
if (! isIgnored(clazz,method)) {
String objectRef = uncap(namePrefix) + instance;
thisBlock.addStmtNoTransform(newObj(inf, clazz, objectRef, false));
generateAsyncTestCallAST(thisBlock, objectRef, method);
}
if (ws != null) {
block.addStmtNoTransform(ws);
}
instance++;
}
return accesses;
}
private TypeUse generateDataPointsAST(InterfaceDecl key, ClassDecl clazz,
Set<TypeUse> use, MainBlock block) {
MethodSig dataPoint = findDataPoints(key);
if (dataPoint == null) {
return null;
}
Access rt = dataPoint.getReturnType();
if (!(rt instanceof ParametricDataTypeUse)) {
return null;
}
ParametricDataTypeUse prt = (ParametricDataTypeUse) rt;
if (! prt.getName().equals("Set")) {
return null;
}
//Set has only one type parameter
TypeUse u = (TypeUse) prt.getParam(0).copy();
use.add(u);
String objName = uncap(clazz.getName()) + "dataPoint";
String dataSet = dataPointSetName(clazz);
block.addStmtNoTransform(newObj(key, clazz, objName, true));
block.addStmtNoTransform(getVarDecl(dataSet, prt.copy(),
new SyncCall(new VarUse(objName), dataPoint.getName(), new List<PureExp>())));
return u;
}
private void generateAsyncTestCallAST(Block block, String objectRef, MethodSig method) {
List<PureExp> args = new List<PureExp>();
if (method.getNumParam() > 0) {
args.add(new VarUse(dataValue));
}
block.addStmtNoTransform(getVAssign(fut, new AsyncCall(new VarUse(objectRef), method.getName(), args)));
block.addStmtNoTransform(getVAssign(futs, getFnApp("Insert", new VarUse(fut), new VarUse(futs))));
}
}