package de.fosd.typechef.jcpp;
import de.fosd.typechef.LexerToken;
import de.fosd.typechef.VALexer;
import de.fosd.typechef.conditional.Conditional;
import de.fosd.typechef.featureexpr.FeatureExpr;
import de.fosd.typechef.featureexpr.FeatureExprFactory;
import de.fosd.typechef.lexer.Feature;
import de.fosd.typechef.lexer.FeatureExprLib;
import de.fosd.typechef.lexer.LexerException;
import de.fosd.typechef.lexer.LexerFrontend;
import org.junit.Assert;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class AbstractCheckTests {
public AbstractCheckTests() {
super();
}
/**
* parses a file and checks the result against the results specified in the
* filename.check file
*
* @param filename
* @throws LexerException
* @throws IOException
*/
protected void testFile(String filename) throws LexerException, IOException {
testFile(filename, false);
}
protected void testFile(String filename, boolean debug) throws LexerException, IOException {
testFile(filename, debug, false);
}
protected void testFile(String filename, boolean debug, boolean ignoreWarning)
throws LexerException, IOException {
String folder = "tc_data/";
InputStream inputStream = getClass().getResourceAsStream(
"/" + folder + filename);
URL inputURI = getClass().getResource(
"/" + folder + filename);
if (inputStream == null) {
throw new FileNotFoundException("Input file not found: " + filename);
}
Conditional<LexerFrontend.LexerResult> output = null;
String error = null;
LexerException ex = null;
try {
//getResource() returns an URL containing escapes. toURI().getPath() is needed to unescape them.
//Otherwise one gets a path where, e.g., spaces are represented by %20!
output = lex(new VALexer.StreamSource(inputStream, inputURI.getFile()),
debug, getClass().getResource("/" + folder).toURI().getPath(), ignoreWarning);
} catch (LexerException e) {
ex = e;
error = "ERROR: " + e.toString();
Assert.fail(error);
} catch (URISyntaxException use) {
//The URI was generated by Java libraries, so it should always be valid!
System.err.println("Supposedly impossible exception!!");
use.printStackTrace();
}
if (!check(filename, folder, output))
if (ex != null)
throw ex;
}
protected String preprocessCodeFragment(String code) throws LexerException,
IOException {
return tokenStreamToString(lex(new VALexer.TextSource(code), false, null, false));
}
private boolean check(String filename, String folder,
Conditional<LexerFrontend.LexerResult> lexerResult)
throws FileNotFoundException, IOException {
boolean containsErrorCheck = false;
InputStream inputStream = getClass().getResourceAsStream(
"/" + folder + filename + ".check");
Assert.assertNotNull("cannot load file /" + folder + filename + ".check", inputStream);
BufferedReader checkFile = new BufferedReader(new InputStreamReader(
inputStream));
String line;
String cleanedOutput = tokenStreamToString(lexerResult).replace("definedEx(",
"defined(");
FeatureExpr errorCondition = LexerFrontend.getErrorCondition(lexerResult);
while ((line = checkFile.readLine()) != null) {
//expecting NOT to find a token
if (line.startsWith("!")) {
String substring = line.substring(2);
if (cleanedOutput.contains(substring)) {
System.err.println(cleanedOutput);
Assert.fail(substring
+ " found but not expected in output\n"
+ cleanedOutput);
}
}
//expecting to find a token a specific number of times
if (line.startsWith("+")) {
int expected = Integer.parseInt(line.substring(1, 2));
int found = 0;
String substring = line.substring(3);
String content = cleanedOutput;
int idx = content.indexOf(substring);
while (idx >= 0) {
found++;
content = content.substring(idx + substring.length());
idx = content.indexOf(substring);
}
if (expected != found) {
failOutput(cleanedOutput);
Assert.fail(substring + " found " + found
+ " times, but expected " + expected + " times\n"
+ content);
}
}
//expect to find a token an arbitrary number of times
if (line.startsWith("*")) {
String substring = line.substring(2);
int idx = cleanedOutput.indexOf(substring);
if (idx < 0) {
failOutput(cleanedOutput);
Assert.fail(substring + " not found but expected\n"
+ cleanedOutput);
}
}
//expect to find a token with a specific presence condition
if (line.startsWith("T")) {
// checks presence condition for token
// Syntax: T <tokenText> with <presenceCondition>
String expectedName = line.substring(2);
String expectedFeature = expectedName.substring(expectedName
.indexOf(" with ") + 6);
expectedName = expectedName.substring(0, expectedName
.indexOf(" with "));
FeatureExpr expectedExpr = FeatureExprLib.featureExprParser().parse(expectedFeature);
boolean foundToken = false;
for (LexerToken t : LexerFrontend.conditionalResultToList(lexerResult, FeatureExprFactory.True())) {
if (t.getText().equals(expectedName)) {
foundToken = true;
// expect equivalent presence conditions
Assert.assertTrue("found token " + expectedName
+ " with " + t.getFeature()
+ " instead of expected " + expectedExpr,
t.getFeature().equivalentTo(expectedExpr));
}
}
Assert.assertTrue("token " + expectedName + " not found.",
foundToken);
}
//expects a lexer error (always or under a specific condition)
if (line.startsWith("error")) {
//error <- fails under all conditions
//error CONFIG_FOO | CONFIG_BAR <- fails under specific conditions
String condition = line.substring(5).trim();
if (condition.length() == 0)
condition = "1";
FeatureExpr expectedErrorCondition = FeatureExprLib.featureExprParser().parse(condition);
containsErrorCheck = true;
Assert.assertTrue(
"Expected error IF " + expectedErrorCondition + ", but preprocessing succeeded unless " + errorCondition,
errorCondition.equivalentTo(expectedErrorCondition));
}
if (line.trim().equals("print")) {
System.out.println(cleanedOutput);
}
if (line.trim().equals("macrooutput")) {
// pp.debugPreprocessorDone();
}
}
return containsErrorCheck;
}
private void failOutput(String output) {
System.err.println(output);
// if (pp != null)
// pp.debugPreprocessorDone();
}
private Conditional<LexerFrontend.LexerResult> lex(VALexer.LexerInput source, boolean debug, final String folder, final boolean ignoreWarnings)
throws LexerException, IOException {
return new LexerFrontend().run(new LexerFrontend.DefaultLexerOptions(source, debug, null) {
@Override
public boolean isReturnLanguageTokensOnly() {
return false;
}
@Override
public List<String> getIncludePaths() {
return Collections.singletonList(folder);
}
@Override
public boolean isHandleWarningsAsErrors() {
return !ignoreWarnings;
}
@Override
public Set<Feature> getFeatures() {
Set<Feature> features = new HashSet<>();
features.add(Feature.DIGRAPHS);
features.add(Feature.TRIGRAPHS);
features.add(Feature.LINEMARKERS);
features.add(Feature.GNUCEXTENSIONS);
return features;
}
}, true);
// // XXX Why here? And isn't the whole thing duplicated from elsewhere?
//
//
// pp = new Preprocessor(new MacroFilter().setPrefixFilter("CONFIG_"), null);
// pp.addFeature(Feature.DIGRAPHS);
// pp.addFeature(Feature.TRIGRAPHS);
// pp.addFeature(Feature.LINEMARKERS);
// pp.addFeature(Feature.GNUCEXTENSIONS);
// for (Warning w : Warning.allWarnings())
// pp.addWarning(w);
// pp.setListener(new PreprocessorListener(pp) {
// @Override
// public void handleWarning(String source, int line, int column,
// String msg) throws LexerException {
// super.handleWarning(source, line, column, msg);
// if (!ignoreWarnings)
// throw new LexerException(msg + " " + source + ":" + line + ":"
// + column);
// }
// });
// pp.addMacro("__JCPP__", FeatureExprLib.True());
//
// // include path
// if (folder != null)
// pp.addSystemIncludePath(folder);
//
// pp.addInput(source);
//
// List<LexerToken> output = new ArrayList<LexerToken>();
// for (; ; ) {
// LexerToken tok = pp.getNextToken();
// if (tok == null)
// break;
// if (tok.isEOF())
// break;
//
// output.add(tok);
// if (debug)
// System.out.print(tok.getText());
// }
// return output;
}
private String tokenStreamToString(Conditional<LexerFrontend.LexerResult> lexerResult) {
List<LexerToken> tokenstream = LexerFrontend.conditionalResultToList(lexerResult, FeatureExprFactory.True());
StringWriter strWriter = new StringWriter();
PrintWriter writer = new PrintWriter(strWriter);
if (tokenstream != null)
for (LexerToken t : tokenstream)
t.lazyPrint(writer);
return strWriter.getBuffer().toString();
}
}