Add standalone test coverage for common/'s MiniJson
Requested after MCMapper-Backend's Phase 2: development follows TDD (test-first) from here on — this retrofits the piece already built (MiniJson, the hand-rolled JSON codec) before that request landed. No JUnit/Gradle involved, matching common/'s "must compile standalone under plain javac" constraint (see README.md) — a hand-rolled assertion runner in src/test/java, compiled and run via the new run-tests.sh. Covers write escaping, nested object/array encoding, parsing (including the exact columns-message shape DefaultBackendConnection actually builds), and a write-then-parse round-trip. Deliberately not wired into either leaf's build.gradle sourceSets (which only pull in src/main/java) — verified both forge-1_12_2 and forge-1_7_10 still compile with src/test/ present, confirming test code doesn't leak into the shipped mod jar.
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
package com.octoturge.mcmapper.common.json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Hand-rolled test runner (no JUnit) for {@link MiniJson}, following the same
|
||||
* "common/ must compile and run standalone under plain javac" constraint as the class under
|
||||
* test — see common/README.md for why, and common/run-tests.sh to actually run this.
|
||||
*/
|
||||
public class MiniJsonTest {
|
||||
private static int passed = 0;
|
||||
private static int failed = 0;
|
||||
|
||||
public static void main(String[] args) {
|
||||
test("writeObject encodes strings, numbers, booleans, and null", MiniJsonTest::writesScalars);
|
||||
test("writeObject escapes special characters in strings", MiniJsonTest::escapesStrings);
|
||||
test("writeObject encodes nested objects and arrays", MiniJsonTest::writesNestedStructures);
|
||||
test("parse reads a hello_ack-shaped object", MiniJsonTest::parsesHelloAck);
|
||||
test("parse reads nested arrays of objects (a columns message)", MiniJsonTest::parsesColumnsMessage);
|
||||
test("parse unescapes strings", MiniJsonTest::parseUnescapesStrings);
|
||||
test("parse reads numbers as Double, including negatives", MiniJsonTest::parsesNumbers);
|
||||
test("write-then-parse round-trips a columns-shaped message", MiniJsonTest::roundTripsColumnsMessage);
|
||||
|
||||
System.out.println();
|
||||
System.out.println(passed + " passed, " + failed + " failed");
|
||||
if (failed > 0) {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writesScalars() {
|
||||
Map<String, Object> obj = new LinkedHashMap<>();
|
||||
obj.put("type", "hello");
|
||||
obj.put("ok", true);
|
||||
obj.put("count", 3.0);
|
||||
obj.put("missing", null);
|
||||
assertEquals("{\"type\":\"hello\",\"ok\":true,\"count\":3.0,\"missing\":null}", MiniJson.writeObject(obj));
|
||||
}
|
||||
|
||||
private static void escapesStrings() {
|
||||
Map<String, Object> obj = new LinkedHashMap<>();
|
||||
obj.put("text", "line1\nline2\t\"quoted\"\\backslash");
|
||||
String json = MiniJson.writeObject(obj);
|
||||
assertEquals("{\"text\":\"line1\\nline2\\t\\\"quoted\\\"\\\\backslash\"}", json);
|
||||
}
|
||||
|
||||
private static void writesNestedStructures() {
|
||||
Map<String, Object> column = new LinkedHashMap<>();
|
||||
column.put("x", 1.0);
|
||||
column.put("z", 2.0);
|
||||
List<Object> columns = new ArrayList<>();
|
||||
columns.add(column);
|
||||
Map<String, Object> msg = new LinkedHashMap<>();
|
||||
msg.put("type", "columns");
|
||||
msg.put("columns", columns);
|
||||
assertEquals("{\"type\":\"columns\",\"columns\":[{\"x\":1.0,\"z\":2.0}]}", MiniJson.writeObject(msg));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void parsesHelloAck() {
|
||||
Object parsed = MiniJson.parse("{\"type\":\"hello_ack\",\"ok\":true,\"serverId\":\"abc-123\"}");
|
||||
Map<String, Object> obj = (Map<String, Object>) parsed;
|
||||
assertEquals("hello_ack", obj.get("type"));
|
||||
assertEquals(Boolean.TRUE, obj.get("ok"));
|
||||
assertEquals("abc-123", obj.get("serverId"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void parsesColumnsMessage() {
|
||||
String json = "{\"type\":\"columns\",\"dimension\":0,\"columns\":["
|
||||
+ "{\"x\":1,\"z\":2,\"height\":64,\"blockId\":2,\"blockMeta\":0},"
|
||||
+ "{\"x\":3,\"z\":4,\"height\":65,\"blockId\":3,\"blockMeta\":0}]}";
|
||||
Map<String, Object> obj = (Map<String, Object>) MiniJson.parse(json);
|
||||
List<Object> columns = (List<Object>) obj.get("columns");
|
||||
assertEquals(2, columns.size());
|
||||
Map<String, Object> first = (Map<String, Object>) columns.get(0);
|
||||
assertEquals(1.0, first.get("x"));
|
||||
assertEquals(64.0, first.get("height"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void parseUnescapesStrings() {
|
||||
Map<String, Object> obj = (Map<String, Object>) MiniJson.parse("{\"error\":\"bad \\\"token\\\"\\nvalue\"}");
|
||||
assertEquals("bad \"token\"\nvalue", obj.get("error"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void parsesNumbers() {
|
||||
Map<String, Object> obj = (Map<String, Object>) MiniJson.parse("{\"a\":-5,\"b\":3.5,\"c\":0}");
|
||||
assertEquals(-5.0, obj.get("a"));
|
||||
assertEquals(3.5, obj.get("b"));
|
||||
assertEquals(0.0, obj.get("c"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void roundTripsColumnsMessage() {
|
||||
// Mirrors exactly what DefaultBackendConnection.sendDeltas() builds — the real
|
||||
// regression risk isn't MiniJson in isolation, it's this shape drifting silently.
|
||||
Map<String, Object> col = new LinkedHashMap<>();
|
||||
col.put("x", 7.0);
|
||||
col.put("z", -3.0);
|
||||
col.put("height", 70.0);
|
||||
col.put("blockId", 2.0);
|
||||
col.put("blockMeta", 0.0);
|
||||
List<Object> columns = new ArrayList<>();
|
||||
columns.add(col);
|
||||
Map<String, Object> msg = new LinkedHashMap<>();
|
||||
msg.put("type", "columns");
|
||||
msg.put("dimension", 0.0);
|
||||
msg.put("columns", columns);
|
||||
|
||||
String json = MiniJson.writeObject(msg);
|
||||
Map<String, Object> reparsed = (Map<String, Object>) MiniJson.parse(json);
|
||||
assertEquals("columns", reparsed.get("type"));
|
||||
assertEquals(0.0, reparsed.get("dimension"));
|
||||
List<Object> reparsedColumns = (List<Object>) reparsed.get("columns");
|
||||
Map<String, Object> reparsedCol = (Map<String, Object>) reparsedColumns.get(0);
|
||||
assertEquals(7.0, reparsedCol.get("x"));
|
||||
assertEquals(-3.0, reparsedCol.get("z"));
|
||||
assertEquals(2.0, reparsedCol.get("blockId"));
|
||||
}
|
||||
|
||||
private static void test(String name, Runnable body) {
|
||||
try {
|
||||
body.run();
|
||||
passed++;
|
||||
System.out.println("PASS " + name);
|
||||
} catch (AssertionError e) {
|
||||
failed++;
|
||||
System.out.println("FAIL " + name + " — " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertEquals(Object expected, Object actual) {
|
||||
if (expected == null ? actual != null : !expected.equals(actual)) {
|
||||
throw new AssertionError("expected <" + expected + "> but got <" + actual + ">");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user