Created
September 18, 2016 20:40
-
-
Save jkeesh/bc057feaa7af5b36048720f261ef653c to your computer and use it in GitHub Desktop.
Snackpack Main Scanner grader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.*; | |
import java.lang.*; | |
import java.util.*; | |
import org.json.*; | |
public class Autograder | |
{ | |
/** | |
* Necessary private fields for the Autograder class. | |
*/ | |
private Interceptor interceptor; | |
private HashMap<String, ArrayList<String>> outputs; | |
private PrintStream origOut; | |
/** | |
* Autograders contain the main logic for autograding and are used by | |
* Grader.java | |
*/ | |
public Autograder() | |
{ | |
tests = new ArrayList<TestCase>(); | |
outputs = new HashMap<String, ArrayList<String>>(); | |
origOut = System.out; | |
interceptor = new Interceptor(origOut, this); | |
System.setOut(interceptor);// just add the interceptor | |
} | |
/** | |
* This adds a TestCase. Instead of doing the comparisons itself, it takes | |
* a boolean which is the result of some custom test. Also allows for any | |
* custom message. | |
* @param <T> This allows for any type of output | |
* @param testName Name of the test being run | |
* @param status Boolean result of anything | |
* @param studentOutput Student output that was tested | |
* @param solutionOutput Solution output that was tested | |
* @param message Message to show | |
*/ | |
public <T> void addTest(String testName, boolean status, | |
T studentOutput, T solutionOutput, String message) | |
{ | |
tests.add(new TestCase(testName, status, message, studentOutput, solutionOutput)); | |
} | |
/** | |
* This will do a standard comparison between studentOutput and | |
* solutionOutput. And depending on the result will create a TestCase with | |
* the result and either the messagePass or messageFail | |
* @param <T> This allows for any type of output | |
* @param testName Name of the test being run | |
* @param studentOutput Student output to test | |
* @param solutionOutput Solution output to test | |
* @param messagePass Message to show if assertEqual passes | |
* @param messageFail Message to show if assertEqual fails | |
*/ | |
public <T> void assertEqual(String testName, T studentOutput, | |
T solutionOutput, String messagePass, String messageFail) | |
{ | |
String studentOutputString; | |
String solutionOutputString; | |
if (studentOutput == null) | |
{ | |
studentOutputString = ""; | |
} else | |
{ | |
studentOutputString = studentOutput.toString(); | |
} | |
if (solutionOutput == null) | |
{ | |
solutionOutputString = ""; | |
} else | |
{ | |
solutionOutputString = solutionOutput.toString(); | |
} | |
boolean success = solutionOutput.equals(studentOutput); | |
String message = success ? messagePass : messageFail; | |
addTest(testName, success, studentOutputString, solutionOutputString, | |
message); | |
} | |
/** | |
* Allows you to get the original output stream. | |
* @return System.out | |
*/ | |
public PrintStream getOriginal() | |
{ | |
return origOut; | |
} | |
/** | |
* Adds an output from the Interceptor to outputs | |
* @param className Name of the class the output came from | |
* @param s Output string | |
*/ | |
public void addOutput(String className, String s) | |
{ | |
ArrayList<String> arr = outputs.get(className); | |
if(arr == null) | |
{ | |
arr = new ArrayList<String>(); | |
} | |
arr.add(s); | |
outputs.put(className, arr); | |
} | |
/** | |
* Get a string of all the outputs for a given class name. | |
* @param className Name of the class to retriev output from. | |
* @return Returns output for the given className as a concated string | |
*/ | |
public String getOutput(String className) | |
{ | |
ArrayList<String> output = outputs.get(className); | |
if (output == null) | |
{ | |
return "You forgot to print something."; | |
} | |
String outputString = ""; | |
for (String s : output) | |
{ | |
outputString += s; | |
} | |
return outputString; | |
} | |
/** | |
* Get an ArrayList of all outputs for a given class name. | |
* @param className Name of the class to retriev output from. | |
* @return Returns output for the given className as an ArrayList. | |
*/ | |
public ArrayList<String> getOutputArrayList(String className) | |
{ | |
ArrayList<String> output = outputs.get(className); | |
return output; | |
} | |
/** | |
* This resets the current outputs. This should be used after | |
* create a new test so that tests don't also test previous | |
* outputs. | |
*/ | |
public void clearOutput() | |
{ | |
outputs = new HashMap<String, ArrayList<String>>(); | |
} | |
/** | |
* Similar to the method above, however, instead of clearing out all output | |
* it will only clear output for a given classname. However, it only does | |
* this if output exists for the class. Otherwise, it does nothing. | |
* @param className Class to clear output for | |
*/ | |
public void clearOutput(String className) | |
{ | |
ArrayList<String> output = outputs.get(className); | |
if (output != null) | |
{ | |
ArrayList<String> arr = new ArrayList<String>(); | |
outputs.put(className, arr); | |
} | |
} | |
/** | |
* Used for printing the output of all test results. | |
* @return string of JSON-like results | |
*/ | |
public String toString() { | |
JSONObject jsonResults = new JSONObject(); | |
JSONArray jsonTests = new JSONArray(); | |
jsonResults.put("tests", jsonTests); | |
for (int i = 0; i < tests.size(); i++) { | |
TestCase test = tests.get(i); | |
JSONObject jsonTest = new JSONObject(); | |
jsonTest.put("success", test.success); | |
jsonTest.put("test", test.test); | |
jsonTest.put("message", test.message); | |
jsonTest.put("studentOutput", test.studentOutput); | |
jsonTest.put("solutionOutput", test.solutionOutput); | |
jsonTests.put(jsonTest); | |
} | |
return "__unittests__" + jsonResults.toString(); | |
} | |
/** | |
* Contains all created test cases. | |
*/ | |
private ArrayList<TestCase> tests; | |
/** | |
* Contains the results a of single test case. | |
*/ | |
class TestCase | |
{ | |
public TestCase(String test, boolean success, String message, | |
Object studentOutput, Object solutionOutput) | |
{ | |
this.success = success; | |
this.message = message; | |
this.studentOutput = studentOutput; | |
this.solutionOutput = solutionOutput; | |
this.test = test; | |
} | |
private String message; | |
private boolean success; | |
private Object studentOutput; | |
private Object solutionOutput; | |
private String test; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.*; | |
public class Grader | |
{ | |
public static void runInputTest(Autograder grader, String[] inputs, String message) | |
{ | |
// Create a student program | |
String[] input = {}; | |
Scanner.setInputs(inputs); | |
SnackPack.main(input); | |
// Create a solution program | |
String[] input2 = {}; | |
Scanner.setInputs(inputs); | |
SnackPackSolution.main(input); | |
// Get the string output from the student and reference solution | |
String studentOutput = grader.getOutput("SnackPack"); | |
String solutionOutput = grader.getOutput("SnackPackSolution"); | |
// Add a test to the grader | |
grader.assertEqual(message, studentOutput, solutionOutput, "Great!", "Not quite."); | |
grader.clearOutput(); | |
} | |
public static void main(String [] args) | |
{ | |
Autograder grader = new Autograder(); | |
runInputTest(grader, new String[]{"0","0","0","0","0"}, "Input: 0,0,0,0,0"); | |
runInputTest(grader, new String[]{"0","1","0","0","0"}, "Input: 0,1,0,0,0"); | |
runInputTest(grader, new String[]{"3","2","1","1","1"}, "Input: 3,2,1,1,1"); | |
runInputTest(grader, new String[]{"0","1","4","8","9"}, "Input: 0,1,4,8,9"); | |
runInputTest(grader, new String[]{"0","1","1","2","1"}, "Input: 0,1,1,2,1"); | |
System.out.println(grader); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.*; | |
import java.lang.*; | |
/** | |
* This class intercepts output destined for System.out so it can be tracked. | |
*/ | |
public class Interceptor extends PrintStream | |
{ | |
private Autograder prog; | |
private static final int CLASS_STACK_POS = 3; | |
/** | |
* Setups up an interceptor for System.out | |
* @param out The output stream being used (should be System.out) | |
* @param prog The autograder that is using this Incerceptor | |
*/ | |
public Interceptor(OutputStream out, Autograder prog) | |
{ | |
super(out, true); | |
this.prog = prog; | |
} | |
/** | |
* We need to prevent double printing so see if the stack trace already | |
* has a call to addOutput. | |
* @param arr | |
* @return | |
*/ | |
private boolean stackTraceAddOutputAppearsTwice(StackTraceElement[] arr) | |
{ | |
int count = 0; | |
for(int i = 0; i < arr.length; i++){ | |
if(arr[i].getMethodName().equals("addOutput")){ | |
count++; | |
} | |
} | |
return count >= 2; | |
} | |
/** | |
* This handles the general logic for printing to System.out. Based on the | |
* `newLine` boolean, we decide whether we print or add a line break. After, | |
* we determine which class it came from, based on the stacktrace. If the | |
* class is `ConsoleProgram`, we look one level up on the stack trace. | |
* @param output The output to print to System.out and store | |
* @param newLine Boolean for whether this is using print or println | |
*/ | |
private void addOutput(String output, boolean newLine) | |
{ | |
if (newLine) | |
{ | |
super.println(output); | |
output += "\n"; | |
} else { | |
super.print(output); | |
} | |
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); | |
String className = stackTraceElements[CLASS_STACK_POS].getClassName(); | |
int stackPos = CLASS_STACK_POS; | |
while (true) | |
{ | |
if(stackPos >= stackTraceElements.length -1){ | |
break; | |
} | |
boolean passThrough = false; | |
// Pass through PrintStream | |
if(className.indexOf("PrintStream") != -1){ | |
stackPos++; | |
passThrough = true; | |
} | |
// Pass through Interceptor | |
if(className.indexOf("Interceptor") != -1){ | |
stackPos++; | |
passThrough = true; | |
} | |
// Pass through Scanner | |
if(className.indexOf("Scanner") != -1){ | |
stackPos++; | |
passThrough = true; | |
} | |
if(!passThrough){ | |
break; | |
} | |
className = stackTraceElements[stackPos].getClassName(); | |
} | |
// Don't double print. | |
if(stackTraceAddOutputAppearsTwice(stackTraceElements)){ | |
return; | |
} | |
this.prog.addOutput(className, output); | |
} | |
/** | |
* This overrides System.out.print. It prints out to System.out like normal. | |
* Afterwards, it stores the output in the Autograder along with the class | |
* name that is trying to print based on the stack trace (up one for | |
* ConsoleProgram). | |
* @param s String to be printed | |
*/ | |
@Override | |
public void print(String s) | |
{ | |
this.addOutput(s, false); | |
} | |
/** | |
* This overrides System.out.print. It prints out to System.out like normal. | |
* Afterwards, it stores the output in the Autograder along with the class | |
* name that is trying to print based on the stack trace (up one for | |
* ConsoleProgram). | |
* @param x Boolean to be printed | |
*/ | |
@Override | |
public void print(boolean x) | |
{ | |
String output = Boolean.toString(x); | |
this.addOutput(output, false); | |
} | |
/** | |
* This overrides System.out.print. It prints out to System.out like normal. | |
* Afterwards, it stores the output in the Autograder along with the class | |
* name that is trying to print based on the stack trace (up one for | |
* ConsoleProgram). | |
* @param x Character to be printed | |
*/ | |
@Override | |
public void print(char x) | |
{ | |
String output = Character.toString(x); | |
this.addOutput(output, false); | |
} | |
/** | |
* This overrides System.out.print. It prints out to System.out like normal. | |
* Afterwards, it stores the output in the Autograder along with the class | |
* name that is trying to print based on the stack trace (up one for | |
* ConsoleProgram). | |
* @param x Character array to be printed | |
*/ | |
@Override | |
public void print(char[] x) | |
{ | |
String output = new String(x); | |
this.addOutput(output, false); | |
} | |
/** | |
* This overrides System.out.print. It prints out to System.out like normal. | |
* Afterwards, it stores the output in the Autograder along with the class | |
* name that is trying to print based on the stack trace (up one for | |
* ConsoleProgram). | |
* @param x Integer to be printed | |
*/ | |
@Override | |
public void print(int x) | |
{ | |
String output = Integer.toString(x); | |
this.addOutput(output, false); | |
} | |
/** | |
* This overrides System.out.print. It prints out to System.out like normal. | |
* Afterwards, it stores the output in the Autograder along with the class | |
* name that is trying to print based on the stack trace (up one for | |
* ConsoleProgram). | |
* @param x Long to be printed | |
*/ | |
@Override | |
public void print(long x) | |
{ | |
String output = Long.toString(x); | |
this.addOutput(output, false); | |
} | |
/** | |
* This overrides System.out.print. It prints out to System.out like normal. | |
* Afterwards, it stores the output in the Autograder along with the class | |
* name that is trying to print based on the stack trace (up one for | |
* ConsoleProgram). | |
* @param x Float to be printed | |
*/ | |
@Override | |
public void print(float x) | |
{ | |
String output = Float.toString(x); | |
this.addOutput(output, false); | |
} | |
/** | |
* This overrides System.out.print. It prints out to System.out like normal. | |
* Afterwards, it stores the output in the Autograder along with the class | |
* name that is trying to print based on the stack trace (up one for | |
* ConsoleProgram). | |
* @param x Double to be printed | |
*/ | |
@Override | |
public void print(double x) | |
{ | |
String output = Double.toString(x); | |
this.addOutput(output, false); | |
} | |
/** | |
* This overrides System.out.print. It prints out to System.out like normal. | |
* Afterwards, it stores the output in the Autograder along with the class | |
* name that is trying to print based on the stack trace (up one for | |
* ConsoleProgram). | |
* @param o Object to be printed | |
*/ | |
@Override | |
public void print(Object o) | |
{ | |
String output = o.toString(); | |
this.addOutput(output, false); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
* @param s String to be printed | |
*/ | |
@Override | |
public void println(String s) | |
{ | |
this.addOutput(s, true); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
*/ | |
@Override | |
public void println() | |
{ | |
this.addOutput("", true); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
* @param x Boolean to be printed | |
*/ | |
@Override | |
public void println(boolean x) | |
{ | |
String output = Boolean.toString(x); | |
this.addOutput(output, true); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
* @param x Character to be printed | |
*/ | |
@Override | |
public void println(char x) | |
{ | |
String output = Character.toString(x); | |
this.addOutput(output, true); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
* @param x Character array to be printed | |
*/ | |
@Override | |
public void println(char[] x) | |
{ | |
String output = new String(x); | |
this.addOutput(output, true); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
* @param x Integer to be printed | |
*/ | |
@Override | |
public void println(int x) | |
{ | |
String output = Integer.toString(x); | |
this.addOutput(output, true); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
* @param x Long to be printed | |
*/ | |
@Override | |
public void println(long x) | |
{ | |
String output = Long.toString(x); | |
this.addOutput(output, true); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
* @param x Float to be printed | |
*/ | |
@Override | |
public void println(float x) | |
{ | |
String output = Float.toString(x); | |
this.addOutput(output, true); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
* @param x Double to be printed | |
*/ | |
@Override | |
public void println(double x) | |
{ | |
String output = Double.toString(x); | |
this.addOutput(output, true); | |
} | |
/** | |
* This overrides System.out.println. It prints out to System.out like | |
* normal. Afterwards, it stores the output in the Autograder along with | |
* the class name that is trying to print based on the stack trace (up one | |
* for ConsoleProgram). | |
* @param o Object to be printed | |
*/ | |
@Override | |
public void println(Object o) | |
{ | |
String output = o.toString(); | |
this.addOutput(output, true); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.*; | |
public class Scanner | |
{ | |
private static int curIndex; | |
private static String[] curInputs = {}; | |
public static void setInputs(String[] inputs) | |
{ | |
curInputs = inputs; | |
curIndex = 0; | |
} | |
public Scanner(InputStream source) | |
{ | |
curIndex = 0; | |
} | |
public int nextInt() | |
{ | |
String stringVal = curInputs[curIndex]; | |
curIndex++; | |
System.out.println(stringVal); | |
return Integer.parseInt(stringVal); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment