/** * CS 231 — Lab 1 * ArrayListTester * * Compile: * javac ArrayList.java ArrayListTester.java * * Run: * java ArrayListTester * * This tester compares the expected behavior of your ArrayList with * the output generated by your implementation. */ public class ArrayListTester { private static int testsRun; private static int testsPassed; public static void main(String[] args) { System.out.println("========================================"); System.out.println("ArrayList Tester"); System.out.println("========================================"); System.out.println(); System.out.println("For each test, compare the expected output"); System.out.println("with the output generated by your ArrayList."); System.out.println(); printTask1And2Note(); if (!testTask3()) { stopAfterTask(3); return; } if (!testTask4()) { stopAfterTask(4); return; } if (!testTask5()) { stopAfterTask(5); return; } if (!testTask6()) { stopAfterTask(6); return; } printSuccess(); } // ------------------------------------------------------------ // TASKS 1 AND 2 // ------------------------------------------------------------ private static void printTask1And2Note() { System.out.println("----------------------------------------"); System.out.println("TASK 1 — Create Instance Variables and Constant"); System.out.println("----------------------------------------"); System.out.println("Ask the instructor or a TA to review your code"); System.out.println("for Tasks 1 and 2 before moving on."); System.out.println(); System.out.println("----------------------------------------"); System.out.println("TASK 2 — Constructors and Size"); System.out.println("----------------------------------------"); System.out.println("The automated behavioral tests begin with Task 3."); System.out.println(); } // ------------------------------------------------------------ // TASK 3 — Add Elements and Grow the Array // ------------------------------------------------------------ private static boolean testTask3() { startTask("TASK 3 — Add Elements and Grow the Array"); testsRun++; System.out.println("TEST: Add several elements"); System.out.println(); try { ArrayList list = new ArrayList<>(); System.out.println("Creating: new ArrayList()"); System.out.println("Starting List: " + view(list)); System.out.println(); System.out.println("Calling:"); System.out.println(" add(1)"); System.out.println(" add(2)"); System.out.println(" add(3)"); list.add(1); list.add(2); list.add(3); String generated = view(list); System.out.println(); System.out.println("Expected Output: [1, 2, 3]"); System.out.println("Generated Output: " + generated); System.out.println("Expected Size: 3"); System.out.println("Generated Size: " + list.size()); reportResult( "[1, 2, 3]".equals(generated) && list.size() == 3); } catch (Throwable problem) { reportProblem(problem); } System.out.println(); testsRun++; System.out.println("TEST: Grow the underlying array"); System.out.println(); try { ArrayList list = new ArrayList<>(3); System.out.println("Creating: new ArrayList(3)"); System.out.println("Initial Capacity: 3"); System.out.println("Starting List: " + view(list)); System.out.println(); System.out.println("Calling:"); System.out.println(" add(1)"); System.out.println(" add(2)"); System.out.println(" add(3)"); list.add(1); list.add(2); list.add(3); System.out.println(); System.out.println("Current List: " + view(list)); System.out.println("Current Size: " + list.size()); System.out.println(); System.out.println("The underlying array is now full."); System.out.println(); System.out.println("Calling:"); System.out.println(" add(4)"); System.out.println(); System.out.println("The fourth element requires the underlying"); System.out.println("array to grow beyond its original capacity."); list.add(4); String generated = view(list); System.out.println(); System.out.println("Expected Output: [1, 2, 3, 4]"); System.out.println("Generated Output: " + generated); System.out.println("Expected Size: 4"); System.out.println("Generated Size: " + list.size()); reportResult( "[1, 2, 3, 4]".equals(generated) && list.size() == 4); } catch (Throwable problem) { reportProblem(problem); } System.out.println(); runSimpleTest("Default constructor works with add()", () -> { ArrayList list = new ArrayList<>(); list.add(10); checkEquals(1, list.size()); checkEquals(10, list.get(0)); }); runSimpleTest("Growing preserves existing elements", () -> { ArrayList list = new ArrayList<>(1); list.add("A"); list.add("B"); list.add("C"); list.add("D"); checkEquals("A", list.get(0)); checkEquals("B", list.get(1)); checkEquals("C", list.get(2)); checkEquals("D", list.get(3)); }); return finishTask(); } // ------------------------------------------------------------ // TASK 4 — Get and Set Elements // ------------------------------------------------------------ private static boolean testTask4() { startTask("TASK 4 — Get and Set Elements"); runValueTest( "get(1)", "[10, 20, 30]", "20", () -> { ArrayList list = new ArrayList<>(); list.add(10); list.add(20); list.add(30); return new ValueResult(view(list), String.valueOf(list.get(1))); } ); runListTest( "set(1, 99)", "[10, 20, 30]", "[10, 99, 30]", () -> { ArrayList list = new ArrayList<>(); list.add(10); list.add(20); list.add(30); String starting = view(list); int beforeSize = list.size(); list.set(1, 99); checkEquals(beforeSize, list.size()); return new ListResult( starting, view(list), beforeSize, list.size()); } ); runSimpleTest("set() changes only the requested position", () -> { ArrayList list = new ArrayList<>(); list.add(10); list.add(20); list.add(30); list.set(0, 88); checkEquals(88, list.get(0)); checkEquals(20, list.get(1)); checkEquals(30, list.get(2)); }); return finishTask(); } // ------------------------------------------------------------ // TASK 5 — Remove an Element // ------------------------------------------------------------ private static boolean testTask5() { startTask("TASK 5 — Remove an Element"); runRemoveTest( "remove(1) — remove from the middle", "[A, B, C, D]", "B", "[A, C, D]", () -> { ArrayList list = makeStringList("A", "B", "C", "D"); String starting = view(list); int beforeSize = list.size(); String removed = list.remove(1); return new RemoveResult( starting, removed, view(list), beforeSize - 1, list.size()); } ); runRemoveTest( "remove(0) — remove the first element", "[A, B, C, D]", "A", "[B, C, D]", () -> { ArrayList list = makeStringList("A", "B", "C", "D"); String starting = view(list); int beforeSize = list.size(); String removed = list.remove(0); return new RemoveResult( starting, removed, view(list), beforeSize - 1, list.size()); } ); runRemoveTest( "remove(3) — remove the last element", "[A, B, C, D]", "D", "[A, B, C]", () -> { ArrayList list = makeStringList("A", "B", "C", "D"); String starting = view(list); int beforeSize = list.size(); String removed = list.remove(3); return new RemoveResult( starting, removed, view(list), beforeSize - 1, list.size()); } ); runListTest( "Add after removing an element", "[10, 20, 30]", "[10, 30, 40]", () -> { ArrayList list = new ArrayList<>(3); list.add(10); list.add(20); list.add(30); String starting = view(list); list.remove(1); list.add(40); return new ListResult( starting, view(list), 3, list.size()); } ); return finishTask(); } // ------------------------------------------------------------ // TASK 6 — Create a String Representation // ------------------------------------------------------------ private static boolean testTask6() { startTask("TASK 6 — Create a String Representation"); runToStringTest("Empty list", "[]", () -> { ArrayList list = new ArrayList<>(); return list.toString(); }); runToStringTest("One element", "[4]", () -> { ArrayList list = new ArrayList<>(); list.add(4); return list.toString(); }); runToStringTest("Several elements", "[4, 7, 2]", () -> { ArrayList list = new ArrayList<>(); list.add(4); list.add(7); list.add(2); return list.toString(); }); runToStringTest("Unused capacity is not displayed", "[A, B]", () -> { ArrayList list = new ArrayList<>(10); list.add("A"); list.add("B"); return list.toString(); }); runToStringTest("Correct after remove()", "[A, C, D]", () -> { ArrayList list = makeStringList("A", "B", "C", "D"); list.remove(1); return list.toString(); }); return finishTask(); } // ------------------------------------------------------------ // VISUAL TEST SUPPORT // ------------------------------------------------------------ private static void runListTest( String name, String expectedStarting, String expectedOutput, ListOperation operation) { testsRun++; System.out.println("TEST: " + name); try { ListResult result = operation.run(); System.out.println("Starting List: " + result.starting); System.out.println("Expected Output: " + expectedOutput); System.out.println("Generated Output: " + result.generated); System.out.println("Expected Size: " + result.expectedSize); System.out.println("Generated Size: " + result.generatedSize); boolean passed = expectedStarting.equals(result.starting) && expectedOutput.equals(result.generated) && result.expectedSize == result.generatedSize; reportResult(passed); } catch (Throwable problem) { reportProblem(problem); } System.out.println(); } private static void runValueTest( String name, String expectedStarting, String expectedOutput, ValueOperation operation) { testsRun++; System.out.println("TEST: " + name); try { ValueResult result = operation.run(); System.out.println("Starting List: " + result.starting); System.out.println("Expected Output: " + expectedOutput); System.out.println("Generated Output: " + result.generated); boolean passed = expectedStarting.equals(result.starting) && expectedOutput.equals(result.generated); reportResult(passed); } catch (Throwable problem) { reportProblem(problem); } System.out.println(); } private static void runRemoveTest( String name, String expectedStarting, String expectedRemoved, String expectedOutput, RemoveOperation operation) { testsRun++; System.out.println("TEST: " + name); try { RemoveResult result = operation.run(); System.out.println("Starting List: " + result.starting); System.out.println("Expected Removed: " + expectedRemoved); System.out.println("Generated Removed: " + result.removed); System.out.println("Expected Output: " + expectedOutput); System.out.println("Generated Output: " + result.generated); System.out.println("Expected Size: " + result.expectedSize); System.out.println("Generated Size: " + result.generatedSize); boolean passed = expectedStarting.equals(result.starting) && expectedRemoved.equals(String.valueOf(result.removed)) && expectedOutput.equals(result.generated) && result.expectedSize == result.generatedSize; reportResult(passed); } catch (Throwable problem) { reportProblem(problem); } System.out.println(); } private static void runToStringTest( String name, String expectedOutput, StringOperation operation) { testsRun++; System.out.println("TEST: " + name); try { String generated = operation.run(); System.out.println("Expected Output: " + expectedOutput); System.out.println("Generated Output: " + generated); reportResult(expectedOutput.equals(generated)); } catch (Throwable problem) { reportProblem(problem); } System.out.println(); } private static void runSimpleTest(String name, Test test) { testsRun++; System.out.println("TEST: " + name); try { test.run(); testsPassed++; System.out.println("PASS"); } catch (TestFailure failure) { System.out.println("FAIL"); System.out.println("Reason: " + failure.getMessage()); } catch (Throwable problem) { reportProblem(problem); } System.out.println(); } private static void reportResult(boolean passed) { if (passed) { testsPassed++; System.out.println("PASS"); } else { System.out.println("FAIL"); } } private static void reportProblem(Throwable problem) { System.out.println("FAIL"); System.out.println("Generated Output: code caused " + friendlyProblemName(problem) + formatMessage(problem.getMessage())); } /* * Builds a display without using the student's toString(). * This lets Tasks 3-5 be displayed before Task 6 is complete. */ private static String view(ArrayList list) { String result = "["; for (int i = 0; i < list.size(); i++) { result += list.get(i); if (i < list.size() - 1) { result += ", "; } } result += "]"; return result; } // ------------------------------------------------------------ // GENERAL SUPPORT // ------------------------------------------------------------ private static void checkEquals(Object expected, Object actual) { boolean equal = (expected == null) ? actual == null : expected.equals(actual); if (!equal) { throw new TestFailure( "expected=" + display(expected) + ", generated=" + display(actual)); } } private static ArrayList makeStringList(String... values) { ArrayList list = new ArrayList<>(); for (String value : values) { list.add(value); } return list; } private static void startTask(String title) { testsRun = 0; testsPassed = 0; System.out.println("----------------------------------------"); System.out.println(title); System.out.println("----------------------------------------"); System.out.println(); } private static boolean finishTask() { boolean passed = testsRun > 0 && testsPassed == testsRun; System.out.println("Task result: " + testsPassed + "/" + testsRun + " tests passed"); System.out.println( passed ? "TASK PASS" : "TASK NOT YET COMPLETE"); System.out.println(); return passed; } private static void stopAfterTask(int taskNumber) { System.out.println("========================================"); System.out.println("STOP HERE FOR NOW"); System.out.println("========================================"); System.out.println("Task " + taskNumber + " is not yet complete."); System.out.println("Compare Expected Output with Generated Output."); System.out.println("Use the difference to decide what to fix."); System.out.println("Then compile and run the tester again."); System.out.println("========================================"); } private static void printSuccess() { System.out.println("========================================"); System.out.println("FINAL RESULTS"); System.out.println("========================================"); System.out.println("Tasks 1-2: review with instructor or TA"); System.out.println("Task 3: PASS"); System.out.println("Task 4: PASS"); System.out.println("Task 5: PASS"); System.out.println("Task 6: PASS"); System.out.println(); System.out.println("ALL AUTOMATED TESTS PASSED"); System.out.println("Your ArrayList is ready for the lab checkpoint."); System.out.println("========================================"); } private static String display(Object value) { if (value == null) { return "null"; } if (value instanceof String) { return "\"" + value + "\""; } return value.toString(); } private static String friendlyProblemName(Throwable problem) { if (problem instanceof NullPointerException) { return "a NullPointerException"; } if (problem instanceof ArrayIndexOutOfBoundsException) { return "an ArrayIndexOutOfBoundsException"; } if (problem instanceof IndexOutOfBoundsException) { return "an IndexOutOfBoundsException"; } if (problem instanceof StackOverflowError) { return "a StackOverflowError"; } return "a " + problem.getClass().getSimpleName(); } private static String formatMessage(String message) { if (message == null || message.length() == 0) { return "."; } return ": " + message; } // ------------------------------------------------------------ // SMALL RESULT TYPES USED BY THE TESTER // ------------------------------------------------------------ private static class ListResult { String starting; String generated; int expectedSize; int generatedSize; ListResult( String starting, String generated, int expectedSize, int generatedSize) { this.starting = starting; this.generated = generated; this.expectedSize = expectedSize; this.generatedSize = generatedSize; } } private static class ValueResult { String starting; String generated; ValueResult(String starting, String generated) { this.starting = starting; this.generated = generated; } } private static class RemoveResult { String starting; Object removed; String generated; int expectedSize; int generatedSize; RemoveResult( String starting, Object removed, String generated, int expectedSize, int generatedSize) { this.starting = starting; this.removed = removed; this.generated = generated; this.expectedSize = expectedSize; this.generatedSize = generatedSize; } } @FunctionalInterface private interface Test { void run(); } @FunctionalInterface private interface ListOperation { ListResult run(); } @FunctionalInterface private interface ValueOperation { ValueResult run(); } @FunctionalInterface private interface RemoveOperation { RemoveResult run(); } @FunctionalInterface private interface StringOperation { String run(); } private static class TestFailure extends RuntimeException { TestFailure(String message) { super(message); } } }