/** * 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 java.io.PrintStream; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import abs.frontend.ast.ClassDecl; import abs.frontend.ast.Decl; import abs.frontend.ast.InterfaceDecl; import abs.frontend.ast.MethodSig; import abs.frontend.ast.Model; import abs.frontend.ast.ModuleDecl; import abs.frontend.ast.ParamDecl; import abs.frontend.ast.ParametricDataTypeUse; import abs.frontend.typechecker.Type; /** * * @author pwong * @deprecated use {@link ASTBasedABSTestRunnerGenerator} */ @Deprecated public class StringBasedABSTestRunnerGenerator extends AbstractABSTestRunnerGenerator { public StringBasedABSTestRunnerGenerator(Model model) { super(model); } private Map<InterfaceDecl, Set<ClassDecl>> tests = new HashMap<InterfaceDecl, Set<ClassDecl>>(); @Override public void generateTestRunner(PrintStream stream) { TestRunnerScriptBuilder imports = generateImports(); TestRunnerScriptBuilder main = generateMainBlock(imports); stream.println("module "+RUNNER_MAIN+";"); stream.println(imports.toString()); stream.print("{"); stream.print(TestRunnerScriptBuilder.NEWLINE); stream.print(TestRunnerScriptBuilder.INDENT); stream.print(main); stream.print(TestRunnerScriptBuilder.NEWLINE); stream.print("}"); stream.print(TestRunnerScriptBuilder.NEWLINE); } /** * For each test interface and classes, append the corresponding * import declaration to a {@link StringBuilder} and return that * string builder * * @return a reference to that string builder */ private TestRunnerScriptBuilder generateImports() { TestRunnerScriptBuilder builder = new TestRunnerScriptBuilder(); for (InterfaceDecl key : tests.keySet()) { builder = generateImport(builder, key); for (ClassDecl clazz : tests.get(key)) { builder = generateImport(builder, clazz); } } return builder; } /** * Append a line of the form "import n from m;" to builder where n is the * name of the specified decl and m is the name of the {@link ModuleDecl} of * that decl. * * @param builder * @param decl * @return a reference to {@code builder} */ private TestRunnerScriptBuilder generateImport(TestRunnerScriptBuilder builder, Decl decl) { return generateImport(builder, decl.getName(), decl.getModuleDecl().getName()); } private TestRunnerScriptBuilder generateImport(TestRunnerScriptBuilder builder, String name, String module) { if (module.equals(absStdLib)) { return builder; } return builder.append("import ").append(name).append(" from ").append(module).append(";").newLine(); } private TestRunnerScriptBuilder generateImports(TestRunnerScriptBuilder builder, Set<Type> types) { for (Type type : types) { generateImport(builder, type.getSimpleName(), type.getModuleName()); } return builder; } private TestRunnerScriptBuilder generateMainBlock(TestRunnerScriptBuilder imports) { TestRunnerScriptBuilder builder = new TestRunnerScriptBuilder(); builder.increaseIndent(); Set<Type> paramNames = new HashSet<Type>(); builder.append("Set<Fut<Unit>> ").append(futs).append(" = EmptySet;").newLine(); builder.append("Fut<Unit> ").append(fut).append(";").newLine(); for (InterfaceDecl key : tests.keySet()) { builder.append("//Test cases for ").append(key.getType().getQualifiedName()).newLine(); for (ClassDecl clazz : tests.get(key)) { builder.append("//Test cases for implementation ").append(clazz.getName()).newLine(); paramNames.addAll(generateTestClassImpl(key, clazz, builder)); } } generateWaitSync(builder); generateImports(imports, paramNames); return builder; } private Set<Type> generateTestClassImpl(InterfaceDecl inf, ClassDecl clazz, TestRunnerScriptBuilder main) { Set<Type> paramNames = new HashSet<Type>(); Type dataType = generateDataPoints(inf, clazz, paramNames, main); String namePrefix = clazz.getName(); int instance = 0; for (MethodSig method : getTestMethods(inf)) { boolean needdata = method.getParamList().iterator().hasNext(); if (needdata) { 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); main.append("while (hasNext(").append(dataPointSet).append(")) {").newLine().increaseIndent(); // begin while main.append("Pair<Set<").append(dataType).append(">,").append(dataType) .append("> nt = next(").append(dataPointSet).append(");").newLine(); main.append(dataType).append(" ").append(dataValue).append(" = snd(nt);").newLine(); main.append(dataPointSet).append(" = fst(nt);").newLine(); } /* * Add those methods that are not ignored */ if (! isIgnored(clazz,method)) { main.append("//Test cases for method ").append(method.getName()).newLine(); String objectRef = uncap(namePrefix) + instance; main = newCog(main, inf, clazz, objectRef); generateAsyncTestCall(main, objectRef, method); } if (needdata) { main.decreaseIndent().append("}").newLine(); // end while } instance++; } return paramNames; } /** * Generates data points for test class {@code clazz} * * @param inf * @param clazz * @param paramNames * @param main * @return The data type the set return type is parametric on, null if * {@code inf} does not define a data point method or {@code clazz} * does not implement such method. */ private Type generateDataPoints(InterfaceDecl inf, ClassDecl clazz, Set<Type> paramNames, TestRunnerScriptBuilder main) { MethodSig dataPoint = findDataPoints(inf); if (dataPoint == null) { return null; } /* * It must be a Set of data */ Type data = ((ParametricDataTypeUse) dataPoint.getReturnType()).getParams().iterator().next().getType(); /* * make sure the return type can be resolved TODO this needs to be * resolved recursively */ paramNames.add(data); /* * create an object in the same cog as main for retrieving the data * points */ String objName = uncap(clazz.getName()) + "dataPoint"; String dataSet = dataPointSetName(clazz); newObj(main, inf, clazz, objName, false); main.append(dataPoint.getReturnType()).append(" ").append(dataSet).append(" = ").append(objName).append(".") .append(dataPoint.getName()).append("();").newLine(); return data; } private TestRunnerScriptBuilder generateWaitSync(TestRunnerScriptBuilder builder) { return builder .append("//waits for methods return...").newLine() .append("while (hasNext(").append(futs).append(")) {").newLine().increaseIndent() // begin while .append("Pair<Set<Fut<Unit>>,Fut<Unit>> nt = next(").append(futs).append(");").newLine() .append(fut).append(" = snd(nt);").newLine() .append(futs).append(" = fst(nt);").newLine() .append(fut).append(".get;").newLine() .decreaseIndent().append("}").newLine(); // end while } /** * Write the line of the form {@code objectRef!method(d);}, where d is * {@link #dataValue} to {@code builder}, if {@code method} takes an * argument, otherwise it write the line of the form * {@code objectRef!method();} to {@code builder}. * * @param main * @param objectRef * @param method */ private void generateAsyncTestCall(TestRunnerScriptBuilder main, String objectRef, MethodSig method) { main.append(fut).append(" = ").append(objectRef).append("!").append(method.getName()); Iterator<ParamDecl> paramIts = method.getParamList().iterator(); if (paramIts.hasNext()) { main.append("(").append(dataValue).append(");").newLine(); // add // parameter // values } else { main.append("();").newLine(); // no parameter } main.append(futs).append("= Insert(").append(fut).append(",").append(futs).append(");").newLine(); } /** * Write the line of the form {@code inf name = new cog clazz();} to * {@code builder} * * @param main * @param inf * @param clazz * @param name * @return a reference to builder. */ private TestRunnerScriptBuilder newCog(TestRunnerScriptBuilder main, InterfaceDecl inf, ClassDecl clazz, String name) { return newObj(main, inf, clazz, name, true); } /** * If {@code cog} is true, this method writes the line of the form * {@code inf name = new cog clazz();} to {@code builder}, otherwise it * writes the line of the form {@code inf name = new clazz();} to * {@code builder}. * * @param main * @param inf * @param clazz * @param name * @param cog * @return a reference to builder. */ private TestRunnerScriptBuilder newObj(TestRunnerScriptBuilder main, InterfaceDecl inf, ClassDecl clazz, String name, boolean cog) { return main.append(inf.getName()).append(" ").append(name).append((cog) ? " = new cog " : " = new ") .append(clazz.getName()).append("();").newLine(); } }