diff --git a/src/main/java/uk/ac/rhul/cs/javatester/BaseTester.java b/src/main/java/uk/ac/rhul/cs/javatester/BaseTester.java index 9d66a81d4dc302c4190f865548d92933518493c2..7a2ec7021cf3a3008d4a6aecf16dc8f54f67b64e 100644 --- a/src/main/java/uk/ac/rhul/cs/javatester/BaseTester.java +++ b/src/main/java/uk/ac/rhul/cs/javatester/BaseTester.java @@ -7,9 +7,7 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.net.MalformedURLException; import java.net.URL; -import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; @@ -28,6 +26,7 @@ public abstract class BaseTester { * For custom error messages for tests failing by exception */ public static class FailedTestException extends Exception { + private static final long serialVersionUID = -7087131188058589135L; public FailedTestException(String msg) { super(msg); } } @@ -259,7 +258,7 @@ public abstract class BaseTester { */ protected boolean outputResult(List<String> failedMessages, SubmissionWrapper wrapper) { - return outputResult(failedMessages, wrapper, false); + return outputResult(failedMessages, wrapper, false, null); } /** @@ -272,17 +271,23 @@ public abstract class BaseTester { * scriptedInteraction containing at least one error message * @param wrapper the submission wrapper used * @param outputCommandLine include the command line in the output + * @param description a description of the test to show before + * failing interaction (or null if non to show) * @return true if failedMessage is null (i.e. test passed) */ protected boolean outputResult(List<String> failedMessages, SubmissionWrapper wrapper, - boolean outputCommandLine) { + boolean outputCommandLine, + String description) { if (failedMessages != null) { out.printCodeLine("Test failed!\n"); + + if (description != null) + out.printCodeLine(description); + out.printCodeLine("Failing interaction:"); if (outputCommandLine) { - String[] args = wrapper.getCommandLineArgs(); out.printCodeLine("$ " + wrapper.getRunCommand()); } diff --git a/src/main/java/uk/ac/rhul/cs/javatester/CodeTester.java b/src/main/java/uk/ac/rhul/cs/javatester/CodeTester.java index c38190ef83fc083bf6e0bb00e0cf887e0856ea4f..2ecad55e88f32e9f12988b038bc749452595e2ca 100644 --- a/src/main/java/uk/ac/rhul/cs/javatester/CodeTester.java +++ b/src/main/java/uk/ac/rhul/cs/javatester/CodeTester.java @@ -5,6 +5,7 @@ import java.io.File; import java.lang.ClassLoader; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -98,6 +99,7 @@ public class CodeTester { */ public static class StudentCodeException extends BaseTester.FailedTestException { + private static final long serialVersionUID = -4498256171743133274L; public StudentCodeException(String msg) { super(msg); } } @@ -253,9 +255,11 @@ public class CodeTester { String className, Object... params) throws BaseTester.FailedTestException { - Class<?>[] parameterTypes - = Stream.of(params).map(p -> p.getClass()) - .toArray(Class<?>[]::new); + + Class<?>[] parameterTypes = Stream.of(params).map( + p -> p != null ? p.getClass() : NullType.class + ).toArray(Class<?>[]::new); + try { Class<?> klass = loadClass(className); Constructor<?> constructor @@ -279,6 +283,25 @@ public class CodeTester { throw new BaseTester.FailedTestException(expandMsg(msg)); } + /** + * Attempts to construct an array and log it + * + * @param klass the type of the array (can be e.g. int.class for + * primitives) + * @param values the values to fill array with + * @return the array + */ + public Object constructArray(Class<?> klass, Object[] values) { + Object array = Array.newInstance(klass, values.length); + for (int i = 0; i < values.length; i++) { + Array.set(array, i, values[i]); + } + + recordConstructArray(array, values); + + return array; + } + /** * Convenience for constructMsg with default messages */ @@ -301,6 +324,23 @@ public class CodeTester { return constructMsg(msg, invokeMsg, className, params); } + /** + * Convenience for callMsgAcceptNull with acceptNull set to true + */ + public Object callMsg(String msg, + String invocationMsg, + Object o, + String methodName, + Object... params) throws + BaseTester.FailedTestException { + return callMsgAcceptNull(msg, + invocationMsg, + true, + o, + methodName, + params); + } + /** * Attempts to call an object method * @@ -308,20 +348,24 @@ public class CodeTester { * msg and invocationMsg can contain LOG_EXPAND which will be replaced by * getIndentedLinesString in the exception. * + * Can specify whether to cause a failure if the method returns null + * * @param msg the failure message * @param invocationMsg failure message prefix in case method call * throws exception (student code fails) + * @param acceptNull whether the result of the call can be null * @param o the object to call the method on * @param String name method name * @param parameterTypes the arguments (cannot be null else type * can't be inferred, with throw NPE) * @return the result object */ - public Object callMsg(String msg, - String invocationMsg, - Object o, - String methodName, - Object... params) throws + public Object callMsgAcceptNull(String msg, + String invocationMsg, + boolean acceptNull, + Object o, + String methodName, + Object... params) throws BaseTester.FailedTestException { Class<?>[] parameterTypes = Stream.of(params).map(p -> p.getClass()) @@ -331,6 +375,13 @@ public class CodeTester { Object r = invokeMethod(invocationMsg, m, o, params); + if (r == null && !acceptNull) { + throw new BaseTester.FailedTestException( + expandMsg(invocationMsg) + "\n\n" + + "The method call returned null but a non-null value was expected." + ); + } + recordCall(r, o, m, params); return r; @@ -349,13 +400,27 @@ public class CodeTester { throw new BaseTester.FailedTestException(expandMsg(msg)); } + /** * Convenience for callMsg with default messages. + * + * Will allow method to return null. */ public Object call(Object o, String methodName, Object... params) throws BaseTester.FailedTestException { + return callAcceptNull(true, o, methodName, params); + } + + /** + * Convenience for callMsgAcceptNull with default messages. + */ + public Object callAcceptNull(boolean acceptNull, + Object o, + String methodName, + Object... params) throws + BaseTester.FailedTestException { String base; if (getLines().size() > 0) { @@ -370,7 +435,30 @@ public class CodeTester { String msg = base + "could not be made."; String invokeMsg = base + "went wrong."; - return callMsg(msg, invokeMsg, o, methodName, params); + return callMsgAcceptNull(msg, + invokeMsg, + acceptNull, + o, + methodName, + params); + } + + + /** + * Convenience for callStaticMsgAcceptNull with acceptNull as true + */ + public Object callStaticMsg(String msg, + String invocationMsg, + Class<?> klass, + String methodName, + Object... params) throws + BaseTester.FailedTestException { + return callStaticMsgAcceptNull(msg, + invocationMsg, + true, + klass, + methodName, + params); } /** @@ -380,19 +468,23 @@ public class CodeTester { * msg and invocationMsg can contain LOG_EXPAND which will be replaced by * getIndentedLinesString in the exception. * + * Can specify whether a null return value is an error. + * * @param msg the failure message * @param invocationMsg failure message prefix in case method call * throws exception (student code fails) + * @param acceptNull whether null is an ok return value * @param klass the class having the method * @param String name method name * @param parameterTypes the arguments * @return the result object */ - public Object callStaticMsg(String msg, - String invocationMsg, - Class<?> klass, - String methodName, - Object... params) throws + public Object callStaticMsgAcceptNull(String msg, + String invocationMsg, + boolean acceptNull, + Class<?> klass, + String methodName, + Object... params) throws BaseTester.FailedTestException { Class<?>[] parameterTypes = Stream.of(params).map(p -> p.getClass()) @@ -409,6 +501,13 @@ public class CodeTester { Object r = invokeMethod(invocationMsg, m, null, params); + if (r == null && !acceptNull) { + throw new BaseTester.FailedTestException( + expandMsg(invocationMsg) + "\n\n" + + "The method call returned null but a non-null value was expected." + ); + } + recordStaticCall(r, klass, m, params); return r; @@ -419,21 +518,40 @@ public class CodeTester { Utils.makeThrowableMsg(e.getCause()) ); } + catch (BaseTester.FailedTestException e) { + throw e; + } catch (Throwable e) { /* fall through */ + System.out.println(e); } // failures fall through here throw new BaseTester.FailedTestException(expandMsg(msg)); } + /** - * Convenience for callMsg with default messages. + * Convenience for callMsgAcceptNull with acceptNull as true */ public Object callStatic(Class<?> klass, String methodName, Object... params) throws BaseTester.FailedTestException { + return callStaticAcceptNull(true, + klass, + methodName, + params); + } + + /** + * Convenience for callStaticMsgAcceptNull with default messages. + */ + public Object callStaticAcceptNull(boolean acceptNull, + Class<?> klass, + String methodName, + Object... params) throws + BaseTester.FailedTestException { String base; if (getLines().size() > 0) { @@ -448,10 +566,14 @@ public class CodeTester { String msg = base + "could not be made."; String invokeMsg = base + "went wrong."; - return callStaticMsg(msg, invokeMsg, klass, methodName, params); + return callStaticMsgAcceptNull(msg, + invokeMsg, + acceptNull, + klass, + methodName, + params); } - /** * If a name is recorded for the object, return it, else null */ @@ -459,7 +581,6 @@ public class CodeTester { return objectNames.get(System.identityHashCode(o)); } - /** * Expand a string with log and arguments. * @@ -615,7 +736,10 @@ public class CodeTester { * dependencies.. */ private boolean isAssignable(Class<?> k1, Class<?> k2) { - return boxClass(k1).isAssignableFrom(boxClass(k2)); + if (k2 == NullType.class) + return true; + else + return boxClass(k1).isAssignableFrom(boxClass(k2)); } /** @@ -654,6 +778,28 @@ public class CodeTester { setLastCall("new " + className + "(" + paramsString + ")"); } + /** + * Record construction of an array with given values + * + * The values will be converted to (internal) variable names if they + * are object already constructed by this object. Else we rely on + * the toString method returning something useful. + */ + private void recordConstructArray(Object array, Object[] values) { + String name = getNewVariableName(array); + String className = getClassString(array.getClass()); + String valuesString = getParamsString(values); + + if (getIsRecording()) { + lines.add(className + " " + name + + " = new " + className + + " { " + valuesString + " }"); + } + + setLastCall("new " + className + " { " + valuesString + " }"); + } + + /** * Record a call method object with given params. */ @@ -747,6 +893,8 @@ public class CodeTester { if (name == null) { if (p instanceof String) name = "\"" + p + "\""; + else if (p instanceof Character) + name = "'" + p + "'"; else name = p.toString(); } @@ -786,7 +934,14 @@ public class CodeTester { if (Character.isUpperCase(c)) sb.append(Character.toLowerCase(c)); } - return sb.toString(); + + String base = sb.toString(); + + // primitive arrays will be empty this way + if (base.isEmpty()) + return Character.toString(className.charAt(0)); + else + return base; } /** @@ -941,4 +1096,9 @@ public class CodeTester { else return logIdealizer.apply(output); } + + /** + * A class to represent a null argument which can be any object type + */ + private class NullType { } } diff --git a/src/main/java/uk/ac/rhul/cs/javatester/Config.java b/src/main/java/uk/ac/rhul/cs/javatester/Config.java index 32ef90d101ae935f7787dbef7aa4fe08e78de782..47c0cd8ba404c6922504969fbc70c24d5826f604 100644 --- a/src/main/java/uk/ac/rhul/cs/javatester/Config.java +++ b/src/main/java/uk/ac/rhul/cs/javatester/Config.java @@ -4,10 +4,8 @@ package uk.ac.rhul.cs.javatester; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; /** * Class for handling command line parameters. diff --git a/src/main/java/uk/ac/rhul/cs/javatester/SubmissionWrapper.java b/src/main/java/uk/ac/rhul/cs/javatester/SubmissionWrapper.java index a1609af2d01bdcd5f7812cf20df49a6792b99321..05d791c31b72b0a48574bc0a7135fe76a82d3027 100644 --- a/src/main/java/uk/ac/rhul/cs/javatester/SubmissionWrapper.java +++ b/src/main/java/uk/ac/rhul/cs/javatester/SubmissionWrapper.java @@ -174,6 +174,8 @@ public class SubmissionWrapper implements AutoCloseable { * Close the streams and kill the submission */ public void close() { + waitFor(); + flushOutputStreams(); try { @@ -496,6 +498,30 @@ public class SubmissionWrapper implements AutoCloseable { .toArray(String[]::new); } + /** + * Construct a default interaction with default fail response for an output + */ + public static String[] expectedOutput(String output) { + return new String[] { + "> " + output, + "Expected the output '" + output + "'." + }; + } + + /** + * Construct a default interaction with fail response for an output + */ + public static String[] expectedOutput(String output, String failMsg) { + return new String[] { "> " + output, failMsg }; + } + + /** + * Construct an interaction for an input + */ + public static String[] input(String input) { + return new String[] { "< " + input }; + } + private void recordInteractionOutputLine(String line) { interactionBuffer.append("> " + line + "\n"); outputLines.add(line); @@ -527,6 +553,9 @@ public class SubmissionWrapper implements AutoCloseable { try { process.waitFor(TIMEOUT, TimeUnit.SECONDS); } catch (InterruptedException e) { + // no nothing + } finally { + // make sure it's really shutdown process.destroyForcibly(); } } diff --git a/src/main/java/uk/ac/rhul/cs/javatester/TesterOutput.java b/src/main/java/uk/ac/rhul/cs/javatester/TesterOutput.java index f524ffb5f9e67d7fa6b00b8ae2afed67ce692c2d..48d799a83b28a15803513680b39630e23b22b5bd 100644 --- a/src/main/java/uk/ac/rhul/cs/javatester/TesterOutput.java +++ b/src/main/java/uk/ac/rhul/cs/javatester/TesterOutput.java @@ -43,11 +43,13 @@ public class TesterOutput { if (!isHTML) { // will work for \r\n and \n - indented = CODE_INDENT; + indented = ""; if (code == null) { - indented += code; - } else { - indented += code.replaceAll("\n", "\n" + CODE_INDENT); + indented = CODE_INDENT + code; + } else if (code.length() > 0) { + indented + = CODE_INDENT + + code.replaceAll("\n(?!($|\n))", "\n" + CODE_INDENT); } } diff --git a/src/test/java/uk/ac/rhul/cs/javatester/UnitTests.java b/src/test/java/uk/ac/rhul/cs/javatester/UnitTests.java index 84034287f2f51c42d16898ed60a5dac5cb08ed8e..a05098f0e4cb1f1033411a899e2d58443331114d 100644 --- a/src/test/java/uk/ac/rhul/cs/javatester/UnitTests.java +++ b/src/test/java/uk/ac/rhul/cs/javatester/UnitTests.java @@ -14,11 +14,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; public class UnitTests { - private static final String THING_RE = ".*XYZ\\s*"; private static final String OUT_THING_RE = "> .*XYZ\\s*"; private static final String THING_FAIL_MSG = "Expected a thing ending in XYX"; @@ -37,14 +35,13 @@ public class UnitTests { private static final String NULL_PTR_CLASS = "uk.ac.rhul.cs.javatester.solution.NullPtrMethod"; - // TODO: import some of the non-fail tests from Assignment3Tester - @Rule public Timeout globalTimeout = Timeout.seconds(10); @Test public void testMainClassFail() throws IOException { BaseTester base = new BaseTester("Not a class") { + @SuppressWarnings("unused") public void testMainClass() throws BaseTester.FailedTestException { CodeTester ct = new CodeTester(); @@ -151,6 +148,7 @@ public class UnitTests { @Test public void testLoadClass() { BaseTester tester = new BaseTester(STORAGE) { + @SuppressWarnings("unused") public boolean testLoadClass() throws BaseTester.FailedTestException { CodeTester ct = new CodeTester(); @@ -164,6 +162,7 @@ public class UnitTests { @Test public void testLoadClassFail() { BaseTester tester = new BaseTester(STORAGE) { + @SuppressWarnings("unused") public boolean testLoadClass() throws BaseTester.FailedTestException { CodeTester ct = new CodeTester(); @@ -177,6 +176,7 @@ public class UnitTests { @Test public void testConstruct() { BaseTester tester = new BaseTester(STORAGE) { + @SuppressWarnings("unused") public boolean testConstruct() throws BaseTester.FailedTestException { CodeTester ct = new CodeTester(); @@ -190,6 +190,7 @@ public class UnitTests { @Test public void testGetConstructFail() { BaseTester tester = new BaseTester(STORAGE) { + @SuppressWarnings("unused") public boolean testGetConstruct() throws BaseTester.FailedTestException { CodeTester ct = new CodeTester(); @@ -203,6 +204,7 @@ public class UnitTests { @Test public void testCallMethod() { BaseTester tester = new BaseTester(STORAGE) { + @SuppressWarnings("unused") public boolean testCallMethod() throws BaseTester.FailedTestException { CodeTester ct = new CodeTester(); @@ -217,6 +219,7 @@ public class UnitTests { @Test public void testCallMethodFail() { BaseTester tester = new BaseTester(STORAGE) { + @SuppressWarnings("unused") public boolean testCallMethod() throws BaseTester.FailedTestException { CodeTester ct = new CodeTester(); @@ -231,6 +234,7 @@ public class UnitTests { @Test public void testOptInOutHasNot() { BaseTester tester = new BaseTester(SIMPLE_IN_OUT) { + @SuppressWarnings("unused") public boolean testOptInOut() throws BaseTester.FailedTestException, IOException { return scriptedInteraction( @@ -250,6 +254,7 @@ public class UnitTests { @Test public void testOptInOutHasSo() { BaseTester tester = new BaseTester(LONGER_IN_OUT) { + @SuppressWarnings("unused") public boolean testOptInOut() throws BaseTester.FailedTestException, IOException { return scriptedInteraction( @@ -269,6 +274,7 @@ public class UnitTests { @Test public void testPrivate() { BaseTester tester = new BaseTester(PRIVATE_CLASS) { + @SuppressWarnings("unused") public boolean testAccess() throws FailedTestException { CodeTester ct = new CodeTester(); Object priv = ct.construct(PRIVATE_CLASS); @@ -286,6 +292,7 @@ public class UnitTests { @Test public void testNullPtrInCall() { BaseTester tester = new BaseTester(NULL_PTR_CLASS) { + @SuppressWarnings("unused") public boolean testNullPtr() throws FailedTestException { CodeTester ct = new CodeTester(); Object np = ct.construct(NULL_PTR_CLASS); @@ -299,9 +306,40 @@ public class UnitTests { @Test public void testNullPtrInConstructor() { BaseTester tester = new BaseTester(NULL_PTR_CLASS) { + @SuppressWarnings("unused") public boolean testNullPtr() throws FailedTestException { CodeTester ct = new CodeTester(); - Object np = ct.construct(NULL_PTR_CLASS, "x"); + ct.construct(NULL_PTR_CLASS, "x"); + return true; + } + }; + assertFalse(tester.runTests()); + } + + @Test + public void testCallMethodNoNull() { + BaseTester tester = new BaseTester(NULL_PTR_CLASS) { + @SuppressWarnings("unused") + public boolean testCallMethod() + throws BaseTester.FailedTestException { + CodeTester ct = new CodeTester(); + Object o = ct.construct(NULL_PTR_CLASS); + ct.callAcceptNull(false, o, "getNull"); + return true; + } + }; + assertFalse(tester.runTests()); + } + + @Test + public void testCallMethodStaticNoNull() { + BaseTester tester = new BaseTester(NULL_PTR_CLASS) { + @SuppressWarnings("unused") + public boolean testCallMethod() + throws BaseTester.FailedTestException { + CodeTester ct = new CodeTester(); + Class<?> klass = ct.loadClass(NULL_PTR_CLASS); + ct.callStaticAcceptNull(false, klass, "getNull"); return true; } }; diff --git a/src/test/java/uk/ac/rhul/cs/javatester/solution/Assignment.java b/src/test/java/uk/ac/rhul/cs/javatester/solution/Assignment.java index 75f42a4b53931f033313ffbd11403af2e4876962..33fc12b25cdf6f6fa13428a7eabec3d514cede22 100644 --- a/src/test/java/uk/ac/rhul/cs/javatester/solution/Assignment.java +++ b/src/test/java/uk/ac/rhul/cs/javatester/solution/Assignment.java @@ -2,11 +2,6 @@ package uk.ac.rhul.cs.javatester.solution; import java.util.Scanner; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; public class Assignment { public static void main(String[] args) { @@ -14,9 +9,10 @@ public class Assignment { doThing("This is a question"); } + @SuppressWarnings("resource") private static void doThing(String question) { System.out.println(question + " XYZ"); Scanner scanner = new Scanner(System.in); - String input = scanner.nextLine().trim(); + scanner.nextLine().trim(); } } diff --git a/src/test/java/uk/ac/rhul/cs/javatester/solution/LongerInOut.java b/src/test/java/uk/ac/rhul/cs/javatester/solution/LongerInOut.java index 85004724f400905ae9898c9a685b60da080228df..b86447920d0ab110fd1b2f1424ab61dc78188dba 100644 --- a/src/test/java/uk/ac/rhul/cs/javatester/solution/LongerInOut.java +++ b/src/test/java/uk/ac/rhul/cs/javatester/solution/LongerInOut.java @@ -4,6 +4,7 @@ package uk.ac.rhul.cs.javatester.solution; import java.util.Scanner; public class LongerInOut { + @SuppressWarnings("resource") public static void main(String[] args) { Scanner scanner = new Scanner(System.in); diff --git a/src/test/java/uk/ac/rhul/cs/javatester/solution/NullPtrMethod.java b/src/test/java/uk/ac/rhul/cs/javatester/solution/NullPtrMethod.java index dc641a3775f7178d8fa5adec29d7e759e3d90dc9..6ecdff3de984e15de4eebf97420c273d8565d8ec 100644 --- a/src/test/java/uk/ac/rhul/cs/javatester/solution/NullPtrMethod.java +++ b/src/test/java/uk/ac/rhul/cs/javatester/solution/NullPtrMethod.java @@ -5,13 +5,19 @@ public class NullPtrMethod { public NullPtrMethod() { } + @SuppressWarnings("null") public NullPtrMethod(String x) { String n = null; n.length(); } + @SuppressWarnings("null") public void nullPtr() { String n = null; n.length(); } + + public static String getNull() { + return null; + } } diff --git a/src/test/java/uk/ac/rhul/cs/javatester/solution/PrivateClass.java b/src/test/java/uk/ac/rhul/cs/javatester/solution/PrivateClass.java index fdc5b39eb68ba0c2e95c960ea36cbd4d731bf87a..d74147777b2f6437110ad0bcb8b90ef5e42582b9 100644 --- a/src/test/java/uk/ac/rhul/cs/javatester/solution/PrivateClass.java +++ b/src/test/java/uk/ac/rhul/cs/javatester/solution/PrivateClass.java @@ -8,8 +8,10 @@ package uk.ac.rhul.cs.javatester.solution; class PrivateClass { PrivateClass() { } + @SuppressWarnings("unused") private PrivateClass(String isPrivate) { } + @SuppressWarnings("unused") private void privateMethod() { } protected void protectedMethod() { } void packageMethod() { } diff --git a/src/test/java/uk/ac/rhul/cs/javatester/solution/SimpleInOut.java b/src/test/java/uk/ac/rhul/cs/javatester/solution/SimpleInOut.java index fa25ea4a84ba689d4125277af615a8e7665d5d68..63935ee65677b8a556656e900e4d60e7cec75474 100644 --- a/src/test/java/uk/ac/rhul/cs/javatester/solution/SimpleInOut.java +++ b/src/test/java/uk/ac/rhul/cs/javatester/solution/SimpleInOut.java @@ -4,6 +4,7 @@ package uk.ac.rhul.cs.javatester.solution; import java.util.Scanner; public class SimpleInOut { + @SuppressWarnings("resource") public static void main(String[] args) { Scanner scanner = new Scanner(System.in); diff --git a/src/test/java/uk/ac/rhul/cs/javatester/solution/Storage.java b/src/test/java/uk/ac/rhul/cs/javatester/solution/Storage.java index dd1359d5c388bd7ef580b91c14e5c99cf2c646a6..a278813d0c80c6e0008e37c0f7348a62be92d654 100644 --- a/src/test/java/uk/ac/rhul/cs/javatester/solution/Storage.java +++ b/src/test/java/uk/ac/rhul/cs/javatester/solution/Storage.java @@ -1,10 +1,6 @@ package uk.ac.rhul.cs.javatester.solution; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; - public class Storage { String value;