Homework 1

This commit is contained in:
Carlos Galindo 2020-01-15 22:18:07 +01:00
parent 49bca7f856
commit 12f678a924
Signed by: kauron
GPG key ID: 83E68706DEE119A3
43 changed files with 3703 additions and 0 deletions

View file

@ -0,0 +1,223 @@
package cd.test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import cd.Config;
import cd.Main;
import cd.backend.codegen.AssemblyFailedException;
import cd.frontend.parser.ParseFailure;
import cd.ir.Ast.ClassDecl;
import cd.util.FileUtil;
import cd.util.debug.AstDump;
abstract public class AbstractTestAgainstFrozenReference {
public static final String SEMANTIC_OK = "OK";
public static final String PARSE_FAILURE = "ParseFailure";
public File file, sfile, binfile, infile;
public File parserreffile, semanticreffile, execreffile, cfgreffile, optreffile;
public File errfile;
public Main main;
public static int counter = 0;
@Test(timeout=10000)
public void test() throws Throwable {
System.err.println("[" + counter++ + " = " + file + "]");
try {
// Delete intermediate files from previous runs:
if (sfile.exists())
sfile.delete();
if (binfile.exists())
binfile.delete();
runReference();
List<ClassDecl> astRoots = testParser();
if (astRoots != null) {
{
testCodeGenerator(astRoots);
}
}
} catch (org.junit.ComparisonFailure cf) {
throw cf;
} catch (Throwable e) {
PrintStream err = new PrintStream(errfile);
err.println("Debug information for file: " + this.file);
err.println(this.main.debug.toString());
err.println("Test failed because an exception was thrown:");
err.println(" " + e.getLocalizedMessage());
err.println("Stack trace:");
e.printStackTrace(err);
System.err.println(FileUtil.read(errfile));
throw e;
}
// if we get here, then the test passed, so delete the errfile:
// (which has been accumulating debug output etc)
if (errfile.exists())
errfile.delete();
}
private void runReference() throws IOException, InterruptedException {
String slash = File.separator;
String colon = File.pathSeparator;
String javaExe = System.getProperty("java.home") + slash + "bin" + slash + Config.JAVA_EXE;
ProcessBuilder pb = new ProcessBuilder(
javaExe, "-Dcd.meta_hidden.Version=" + referenceVersion(),
"-cp", "lib/frozenReferenceObf.jar" + colon + " lib/junit-4.12.jar" + colon + "lib/antlr-4.4-complete.jar",
"cd.FrozenReferenceMain", file.getAbsolutePath());
Process proc = pb.start();
proc.waitFor();
try (InputStream err = proc.getErrorStream()) {
if (err.available() > 0) {
byte b[] = new byte[err.available()];
err.read(b, 0, b.length);
System.err.println(new String(b));
}
}
}
private static String referenceVersion() {
{
return "CD_HW_CODEGEN_SIMPLE_SOL";
}
}
/** Run the parser and compare the output against the reference results */
private List<ClassDecl> testParser() throws Exception {
String parserRef = findParserRef();
List<ClassDecl> astRoots = null;
String parserOut;
try {
astRoots = main.parse(new FileReader(this.file));
parserOut = AstDump.toString(astRoots);
} catch (ParseFailure pf) {
{
// For this assignment, we expect all tests to parse!
throw pf;
}
}
{
assertEquals("parser", parserRef, parserOut);
}
return astRoots;
}
private String findParserRef() throws IOException {
// Check for a .ref file
if (parserreffile.exists() && parserreffile.lastModified() >= file.lastModified()) {
return FileUtil.read(parserreffile);
}
throw new RuntimeException("ERROR: could not find parser .ref");
}
/**
* Run the code generator, assemble the resulting .s file, and (if the output
* is well-defined) compare against the expected output.
*/
private void testCodeGenerator(List<ClassDecl> astRoots)
throws IOException {
// Determine the input and expected output.
String inFile = (infile.exists() ? FileUtil.read(infile) : "");
String execRef = findExecRef();
// Run the code generator:
try (FileWriter fw = new FileWriter(this.sfile)) {
main.generateCode(astRoots, fw);
}
// At this point, we have generated a .s file and we have to compile
// it to a binary file. We need to call out to GCC or something
// to do this.
String asmOutput = FileUtil.runCommand(
Config.ASM_DIR,
Config.ASM,
new String[] { binfile.getAbsolutePath(),
sfile.getAbsolutePath() }, null, false);
// To check if gcc succeeded, check if the binary file exists.
// We could use the return code instead, but this seems more
// portable to other compilers / make systems.
if (!binfile.exists())
throw new AssemblyFailedException(asmOutput);
// Execute the binary file, providing input if relevant, and
// capturing the output. Check the error code so see if the
// code signaled dynamic errors.
String execOut = FileUtil.runCommand(new File("."),
new String[] { binfile.getAbsolutePath() }, new String[] {},
inFile, true);
// Compute the output to what we expected to see.
if (!execRef.equals(execOut))
assertEqualOutput("exec", execRef, execOut);
}
private String findExecRef() throws IOException {
// Check for a .ref file
if (execreffile.exists() && execreffile.lastModified() > file.lastModified()) {
return FileUtil.read(execreffile);
}
throw new RuntimeException("ERROR: could not find execution .ref");
}
private void assertEquals(String phase, String exp, String act_) {
String act = act_.replace("\r\n", "\n"); // for windows machines
if (!exp.equals(act)) {
warnAboutDiff(phase, exp, act);
}
}
/**
* Compare the output of two executions
*/
private void assertEqualOutput(String phase, String exp, String act_) {
String act = act_.replace("\r\n", "\n"); // for windows machines
if (!exp.equals(act)) {
warnAboutDiff(phase, exp, act);
}
}
private void warnAboutDiff(String phase, String exp, String act) {
try {
try (PrintStream err = new PrintStream(errfile)) {
err.println("Debug information for file: " + this.file);
err.println(this.main.debug.toString());
err.println(String.format(
"Phase %s failed because we expected to see:", phase));
err.println(exp);
err.println("But we actually saw:");
err.println(act);
err.println("The difference is:");
err.println(Diff.computeDiff(exp, act));
}
} catch (FileNotFoundException exc) {
System.err.println("Unable to write debug output to " + errfile
+ ":");
exc.printStackTrace();
}
Assert.assertEquals(
String.format("Phase %s for %s failed!", phase,
file.getPath()), exp, act);
}
}

87
test/cd/test/Diff.java Normal file
View file

@ -0,0 +1,87 @@
package cd.test;
// NOTE: This code was adapted from the code found at
// http://www.cs.princeton.edu/introcs/96optimization/Diff.java.html
/*************************************************************************
* Compilation: javac Diff
* Execution: java Diff filename1 filename2
* Dependencies: In.java
*
* Reads in two files and compute their diff.
* A bare bones version.
*
* % java Diff input1.txt input2.txt
*
*
* Limitations
* -----------
* - Could hash the lines to avoid potentially more expensive
* string comparisons.
*
*************************************************************************/
public class Diff {
public static String computeDiff(String in0, String in1) {
StringBuffer out = new StringBuffer();
// break into lines of each file
String[] x = in0.split("\\n");
String[] y = in1.split("\\n");
// number of lines of each file
int M = x.length;
int N = y.length;
// opt[i][j] = length of LCS of x[i..M] and y[j..N]
int[][] opt = new int[M+1][N+1];
// compute length of LCS and all subproblems via dynamic programming
for (int i = M-1; i >= 0; i--) {
for (int j = N-1; j >= 0; j--) {
if (x[i].equals(y[j]))
opt[i][j] = opt[i+1][j+1] + 1;
else
opt[i][j] = Math.max(opt[i+1][j], opt[i][j+1]);
}
}
// recover LCS itself and print out non-matching lines to standard output
boolean lineNumber = false;
int i = 0, j = 0;
while(i < M && j < N) {
if (x[i].equals(y[j])) {
i++;
j++;
lineNumber = false;
}
else {
if (!lineNumber) {
out.append(String.format(
"At line %3d / %3d:\n", i+1, j+1));
lineNumber = true;
}
assert lineNumber;
if (opt[i+1][j] >= opt[i][j+1])
out.append("< " + x[i++] + "\n");
else
out.append("> " + y[j++] + "\n");
}
}
// dump out one remainder of one string if the other is exhausted
if (!lineNumber) {
out.append(String.format("Line %3d / %3d:\n", i+1, j+1));
}
while(i < M || j < N) {
if (i == M)
out.append("> " + y[j++] + "\n");
else if (j == N)
out.append("< " + x[i++] + "\n");
}
return out.toString();
}
}

View file

@ -0,0 +1,74 @@
package cd.test;
import java.io.File;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import cd.Config;
import cd.Main;
import cd.util.FileUtil;
@RunWith(Parameterized.class)
public class TestSamplePrograms extends AbstractTestAgainstFrozenReference {
/**
* If you want to run the test on just one file, then initialize this
* variable like:
* {@code justFile = new File("javali_tests/HW2/Inheritance.javali")}.
*/
// public static final File justFile = new File("javali_tests/HW1/HelloWorld.javali");
public static final File justFile = null;
/**
* Directory in which to search for test files. If null, then the default is
* the current directory (to include all files). To run only tests in a
* particular directory, use sth. like:
* {@code testDir = new File("javali_tests/HW2/")}.
*/
// public static final File testDir = new File("javali_tests/HW1");
public static final File testDir = new File("javali_tests");
@Parameters(name="{index}:{0}")
public static Collection<Object[]> testFiles() {
List<Object[]> result = new ArrayList<Object[]>();
if (justFile != null)
result.add(new Object[] { justFile });
else if (testDir != null) {
for (File file : FileUtil.findJavaliFiles(testDir)) {
result.add(new Object[] { file });
}
} else {
for (File file : FileUtil.findJavaliFiles(new File("."))) {
result.add(new Object[] { file });
}
}
return result;
}
/**
* @param file
* The javali file to test.
*/
public TestSamplePrograms(File file) {
this.file = file;
this.sfile = new File(file.getPath() + Config.ASMEXT);
this.binfile = new File(file.getPath() + Config.BINARYEXT);
this.infile = new File(file.getPath() + ".in");
this.parserreffile = new File(file.getPath() + ".parser.ref");
this.semanticreffile = new File(file.getPath() + ".semantic.ref");
this.execreffile = new File(file.getPath() + ".exec.ref");
this.cfgreffile = new File(file.getPath() + ".cfg.dot.ref");
this.optreffile = new File(file.getPath() + ".opt.ref");
this.errfile = new File(String.format("%s.err", file.getPath()));
this.main = new Main();
this.main.debug = new StringWriter();
}
}