From 1e34aaf536cdb1f92f49205a0f9df55f38d97a3d Mon Sep 17 00:00:00 2001 From: Matthew Hague <matthew.hague@rhul.ac.uk> Date: Wed, 9 Dec 2020 19:32:46 +0000 Subject: [PATCH] Add call and callStatic that check null return You can define if a null return value is considered an error. Useful if you need to use the returned object later on in the test. --- .../uk/ac/rhul/cs/javatester/CodeTester.java | 129 +++++++++++++++--- .../uk/ac/rhul/cs/javatester/UnitTests.java | 30 ++++ .../cs/javatester/solution/NullPtrMethod.java | 4 + 3 files changed, 147 insertions(+), 16 deletions(-) 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 d6968a9..2ecad55 100644 --- a/src/main/java/uk/ac/rhul/cs/javatester/CodeTester.java +++ b/src/main/java/uk/ac/rhul/cs/javatester/CodeTester.java @@ -302,7 +302,6 @@ public class CodeTester { return array; } - /** * Convenience for constructMsg with default messages */ @@ -325,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 * @@ -332,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()) @@ -355,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; @@ -373,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) { @@ -394,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); } /** @@ -404,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()) @@ -433,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; @@ -443,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) { @@ -472,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 */ @@ -483,7 +581,6 @@ public class CodeTester { return objectNames.get(System.identityHashCode(o)); } - /** * Expand a string with log and arguments. * 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 34e0db3..a05098f 100644 --- a/src/test/java/uk/ac/rhul/cs/javatester/UnitTests.java +++ b/src/test/java/uk/ac/rhul/cs/javatester/UnitTests.java @@ -315,4 +315,34 @@ public class UnitTests { }; 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; + } + }; + assertFalse(tester.runTests()); + } } 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 1788f9f..6ecdff3 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 @@ -16,4 +16,8 @@ public class NullPtrMethod { String n = null; n.length(); } + + public static String getNull() { + return null; + } } -- GitLab