Write a Mini Test Runner

medium Testing and Clean Code

Problem

Build a tiny test framework. Each input line is a test: assertEquals A B, assertNotEquals A B, assertTrue X or assertFalse X (A, B and X are single words). For test number i (counting from 1) print PASS i if the check is true. If it is false, print FAIL i: followed by a message: expected A but was B (assertEquals), values are equal: A (assertNotEquals), expected true or expected false. At the end print Passed P of T. A failed test must not stop the other tests.

Constraints

  • 1 <= lines <= 10^4

Examples

Input

assertEquals 4 4
assertEquals 4 5

Output

PASS 1
FAIL 2: expected 4 but was 5
Passed 1 of 2

Solutions

Best approachTime O(n)Space O(n)

Test frameworks like JUnit work this way: an assertion throws on failure, and the runner catches the exception, records FAIL and continues. A passed test just returns.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Process lines.
 * Input   : Several lines.
 * Output  : Results and a summary.
 * Approach: Assertions that throw.
 * Time    : O(n)
 * Space   : O(n)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
        // Step 1: A failed check throws an AssertionFailed exception with a message. A passed check does nothing.
        static class AssertionFailed extends RuntimeException {
            AssertionFailed(String message) {
                super(message);
            }
        }

        // Step 2: The assertion methods. Each one compares and throws when the check fails.
        static void assertEquals(String expected, String actual) {
            if (!expected.equals(actual)) {
                throw new AssertionFailed("expected " + expected + " but was " + actual);
            }
        }

        static void assertNotEquals(String first, String second) {
            if (first.equals(second)) {
                throw new AssertionFailed("values are equal: " + first);
            }
        }

        static void assertTrue(String value) {
            if (!value.equals("true")) {
                throw new AssertionFailed("expected true");
            }
        }

        static void assertFalse(String value) {
            if (!value.equals("false")) {
                throw new AssertionFailed("expected false");
            }
        }

    // This method solves the task. It receives the input and returns the answer.
    static String[] solve(String[] lines) {
        String[] output = new String[lines.length + 1];
        int passed = 0;

        for (int i = 0; i < lines.length; i++) {
            String[] part = lines[i].split(" ");

            // Step 3: Run each test in a try block. An exception means FAIL, and we use its message.
            try {
                if (part[0].equals("assertEquals")) {
                    assertEquals(part[1], part[2]);
                } else if (part[0].equals("assertNotEquals")) {
                    assertNotEquals(part[1], part[2]);
                } else if (part[0].equals("assertTrue")) {
                    assertTrue(part[1]);
                } else {
                    assertFalse(part[1]);
                }
                output[i] = "PASS " + (i + 1);
                passed++;
            } catch (AssertionFailed failure) {
                output[i] = "FAIL " + (i + 1) + ": " + failure.getMessage();
            }
        }

        // Step 4: The summary line tells how many tests passed.
        output[lines.length] = "Passed " + passed + " of " + lines.length;
        return output;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        java.util.List<String> all = new java.util.ArrayList<>();
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine().trim();
            if (!line.isEmpty()) {
                all.add(line);
            }
        }
        String[] lines = all.toArray(new String[0]);
        System.out.println(String.join("\n", solve(lines)));
    }
}
Hints (1)
  1. What does the runner do when an assertion throws?