/******************************************************************************
* Copyright (c) 2009 - 2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*****************************************************************************/
/**
*
*/
package test;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.memsat.Miniatur;
import com.ibm.wala.memsat.Options;
import com.ibm.wala.memsat.Results;
import com.ibm.wala.memsat.concurrent.Execution;
import com.ibm.wala.memsat.concurrent.Justification;
import com.ibm.wala.memsat.concurrent.MemoryModel;
import com.ibm.wala.memsat.translation.concurrent.ConcurrentTranslation;
import com.ibm.wala.memsat.util.Strings;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.graph.Graph;
import kodkod.ast.Expression;
import kodkod.ast.Relation;
import kodkod.engine.Solution;
import kodkod.engine.Solution.Outcome;
import kodkod.engine.satlab.SATFactory;
import kodkod.engine.ucore.RCEStrategy;
import kodkod.instance.Bounds;
import kodkod.instance.TupleSet;
/**
* Common harness for testing concurrent code.
* @author etorlak
*/
public abstract class ConcurrentTests {
private final void addDisplayStrings(Execution exec, String suffix, Map<String,String> display) {
final Class<? extends Execution> c = exec.getClass();
for(Method m : c.getMethods()) {
if (m.getParameterTypes().length==0) {
if (Expression.class.isAssignableFrom(m.getReturnType())) {
try {
m.setAccessible(true);
final Expression expr = (Expression)m.invoke(exec, new Object[0]);
if (!(expr instanceof Relation))
display.put(expr.toString(), m.getName()+suffix);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
private final Map<String,String> printProblem(ConcurrentTranslation transl) {
System.out.println();
final Justification just = transl.context();
final Map<String, String> display = new LinkedHashMap<String, String>();
addDisplayStrings(just.execution(), "", display);
for(int i = 0, max = just.speculations().size(); i < max; i++) {
addDisplayStrings(just.speculations().get(i), String.valueOf(i), display);
}
System.out.println(Strings.prettyPrint(transl.formula(),2,200,display));
System.out.println("BOUNDS: ");
final Bounds bounds = transl.bounds();
for(Relation r : bounds.relations()) {
final TupleSet lower = bounds.lowerBound(r);
final TupleSet upper = bounds.upperBound(r);
if (lower.equals(upper))
System.out.println(r + " = " + lower);
else
System.out.println(r + " : [" + lower + ", "+ upper +"]");
}
return display;
}
protected final void test(Miniatur miniatur, File srcPath, Graph<MethodReference> methods, boolean sat) {
test(miniatur, Collections.singletonList(srcPath), methods, sat);
}
/**
* Checks that the result produced by applying the given miniatur instance to the given
* methods is satisfiable or unsatisfiable, depending on the value of the specified flag.
*/
protected final void test(Miniatur miniatur, List<File> srcPath, Graph<MethodReference> methods, boolean sat) {
try {
final Results<ConcurrentTranslation> results = miniatur.analyze(methods, srcPath);
final Solution solution = results.solution();
final Map<String,String> display = printProblem(results.translation());
final long core;
if (solution.instance()==null) {
System.out.println("Outcome: "+solution.outcome());
System.out.println(solution.stats());
final long corestart = System.currentTimeMillis();
solution.proof().minimize(new RCEStrategy(solution.proof().log()));
core = System.currentTimeMillis() - corestart;
System.out.println("CORE: ");
System.out.println(Strings.prettyPrint(solution.proof().highLevelCore().values(), 2, 200, display));
} else {
core = 0;
System.out.println("Outcome: "+solution.outcome());
System.out.println(solution.stats());
for(Relation r : solution.instance().relations()) {
System.out.println(r + " = " + solution.instance().tuples(r));
}
System.out.println("\n****************");
System.out.println(solution.instance().universe());
System.out.println(results);
}
// System.out.println(results);
final String expected = sat ? "y" : "n";
final String found = solution.instance()!=null ? "y":"n";
final long time = results.analysisTime() + results.translationTime() + solution.stats().translationTime() + solution.stats().solvingTime() + core;
System.out.println("\nOUTPUT\t" + expected + "\t" + found + "\t" + time + "\t" + solution.stats().variables() + "\t" + solution.stats().clauses());
if (sat) {
assert solution.outcome().equals(Outcome.SATISFIABLE) ||
solution.outcome().equals(Outcome.TRIVIALLY_SATISFIABLE);
} else {
assert solution.outcome().equals(Outcome.UNSATISFIABLE) ||
solution.outcome().equals(Outcome.TRIVIALLY_UNSATISFIABLE);
}
} catch (CancelException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns a miniatur analysis engine generated by calling
* {@linkplain #miniatur(int, Set) miniatur(maxSpeculations, Collections.EMPTY_SET)}.
* @return a miniatur analysis engine generated by calling
* {@linkplain #miniatur(int, Set) miniatur(maxSpeculations, Collections.EMPTY_SET)}.
*/
@SuppressWarnings("unchecked")
protected final Miniatur miniatur(int maxSpeculations) {
return miniatur(maxSpeculations, Collections.EMPTY_SET);
}
/**
* Returns a miniatur analysis engine that uses the memory model generated by calling
* {@linkplain #memoryModel(int, Set) memoryModel(maxSpeculations, special)} and that treats user assertions as assumes.
* @return a miniatur analysis engine that uses the memory model generated by calling
* {@linkplain #memoryModel(int, Set) memoryModel(maxSpeculations, special)} and that treats user assertions as assumes.
*/
protected Miniatur miniatur(int maxSpeculations, Set<MethodReference> special) {
return new Miniatur(getOptions(maxSpeculations, special));
}
protected Options getOptions(int maxSpeculations, Set<MethodReference> special) {
final Options opts = new Options();
opts.setMemoryModel(memoryModel(maxSpeculations, special));
opts.setAssertsAreAssumptions(true);
opts.kodkodOptions().setBitwidth(3);
opts.kodkodOptions().setLogTranslation(1);
opts.kodkodOptions().setSolver(SATFactory.MiniSatProver);
// opts.kodkodOptions().setCoreGranularity(3);
return opts;
}
/**
* Returns a memory model instance that allows at most the given number of speculations.
* This parameter may be ignored if the memory model permits no speculations.
* @return a memory model instance that allows at most the given number of speculations.
*/
protected abstract MemoryModel memoryModel(int maxSpeculations, Set<MethodReference> special);
}