Write a Mini Test Runner
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
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.
/*
* 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)
- What does the runner do when an assertion throws?