|
| 1 | +import java.util.*; |
| 2 | + |
| 3 | +/** |
| 4 | + * Test suite for concoredocker.literalEval() recursive descent parser. |
| 5 | + * Covers: dicts, lists, tuples, numbers, strings, booleans, None, |
| 6 | + * nested structures, escape sequences, scientific notation, |
| 7 | + * toPythonLiteral serialization, and fractional simtime. |
| 8 | + */ |
| 9 | +public class TestLiteralEval { |
| 10 | + static int passed = 0; |
| 11 | + static int failed = 0; |
| 12 | + |
| 13 | + public static void main(String[] args) { |
| 14 | + testEmptyDict(); |
| 15 | + testSimpleDict(); |
| 16 | + testDictWithIntValues(); |
| 17 | + testEmptyList(); |
| 18 | + testSimpleList(); |
| 19 | + testListOfDoubles(); |
| 20 | + testNestedDictWithList(); |
| 21 | + testNestedListsDeep(); |
| 22 | + testBooleansAndNone(); |
| 23 | + testStringsWithCommas(); |
| 24 | + testStringsWithColons(); |
| 25 | + testStringEscapeSequences(); |
| 26 | + testScientificNotation(); |
| 27 | + testNegativeNumbers(); |
| 28 | + testTuple(); |
| 29 | + testTrailingComma(); |
| 30 | + testToPythonLiteralBooleans(); |
| 31 | + testToPythonLiteralNone(); |
| 32 | + testToPythonLiteralString(); |
| 33 | + testFractionalSimtime(); |
| 34 | + |
| 35 | + System.out.println("\n=== Results: " + passed + " passed, " + failed + " failed out of " + (passed + failed) + " tests ==="); |
| 36 | + if (failed > 0) { |
| 37 | + System.exit(1); |
| 38 | + } |
| 39 | + } |
| 40 | + |
| 41 | + static void check(String testName, Object expected, Object actual) { |
| 42 | + if (Objects.equals(expected, actual)) { |
| 43 | + System.out.println("PASS: " + testName); |
| 44 | + passed++; |
| 45 | + } else { |
| 46 | + System.out.println("FAIL: " + testName + " | expected: " + expected + " | actual: " + actual); |
| 47 | + failed++; |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + static void testEmptyDict() { |
| 52 | + Object result = concoredocker.literalEval("{}"); |
| 53 | + check("empty dict", new HashMap<>(), result); |
| 54 | + } |
| 55 | + |
| 56 | + static void testSimpleDict() { |
| 57 | + @SuppressWarnings("unchecked") |
| 58 | + Map<String, Object> result = (Map<String, Object>) concoredocker.literalEval("{'PYM': 1}"); |
| 59 | + check("simple dict key", true, result.containsKey("PYM")); |
| 60 | + check("simple dict value", 1, result.get("PYM")); |
| 61 | + } |
| 62 | + |
| 63 | + static void testDictWithIntValues() { |
| 64 | + @SuppressWarnings("unchecked") |
| 65 | + Map<String, Object> result = (Map<String, Object>) concoredocker.literalEval("{'a': 10, 'b': 20}"); |
| 66 | + check("dict int value a", 10, result.get("a")); |
| 67 | + check("dict int value b", 20, result.get("b")); |
| 68 | + } |
| 69 | + |
| 70 | + static void testEmptyList() { |
| 71 | + Object result = concoredocker.literalEval("[]"); |
| 72 | + check("empty list", new ArrayList<>(), result); |
| 73 | + } |
| 74 | + |
| 75 | + static void testSimpleList() { |
| 76 | + @SuppressWarnings("unchecked") |
| 77 | + List<Object> result = (List<Object>) concoredocker.literalEval("[1, 2, 3]"); |
| 78 | + check("simple list size", 3, result.size()); |
| 79 | + check("simple list[0]", 1, result.get(0)); |
| 80 | + check("simple list[2]", 3, result.get(2)); |
| 81 | + } |
| 82 | + |
| 83 | + static void testListOfDoubles() { |
| 84 | + @SuppressWarnings("unchecked") |
| 85 | + List<Object> result = (List<Object>) concoredocker.literalEval("[0.0, 1.5, 2.7]"); |
| 86 | + check("list doubles[0]", 0.0, result.get(0)); |
| 87 | + check("list doubles[1]", 1.5, result.get(1)); |
| 88 | + } |
| 89 | + |
| 90 | + static void testNestedDictWithList() { |
| 91 | + @SuppressWarnings("unchecked") |
| 92 | + Map<String, Object> result = (Map<String, Object>) concoredocker.literalEval("{'key': [1, 2, 3]}"); |
| 93 | + check("nested dict has key", true, result.containsKey("key")); |
| 94 | + @SuppressWarnings("unchecked") |
| 95 | + List<Object> inner = (List<Object>) result.get("key"); |
| 96 | + check("nested list size", 3, inner.size()); |
| 97 | + check("nested list[0]", 1, inner.get(0)); |
| 98 | + } |
| 99 | + |
| 100 | + static void testNestedListsDeep() { |
| 101 | + @SuppressWarnings("unchecked") |
| 102 | + List<Object> result = (List<Object>) concoredocker.literalEval("[[1, 2], [3, 4]]"); |
| 103 | + check("nested lists size", 2, result.size()); |
| 104 | + @SuppressWarnings("unchecked") |
| 105 | + List<Object> inner = (List<Object>) result.get(0); |
| 106 | + check("inner list[0]", 1, inner.get(0)); |
| 107 | + check("inner list[1]", 2, inner.get(1)); |
| 108 | + } |
| 109 | + |
| 110 | + static void testBooleansAndNone() { |
| 111 | + @SuppressWarnings("unchecked") |
| 112 | + List<Object> result = (List<Object>) concoredocker.literalEval("[True, False, None]"); |
| 113 | + check("boolean True", Boolean.TRUE, result.get(0)); |
| 114 | + check("boolean False", Boolean.FALSE, result.get(1)); |
| 115 | + check("None", null, result.get(2)); |
| 116 | + } |
| 117 | + |
| 118 | + static void testStringsWithCommas() { |
| 119 | + @SuppressWarnings("unchecked") |
| 120 | + Map<String, Object> result = (Map<String, Object>) concoredocker.literalEval("{'key': 'hello, world'}"); |
| 121 | + check("string with comma", "hello, world", result.get("key")); |
| 122 | + } |
| 123 | + |
| 124 | + static void testStringsWithColons() { |
| 125 | + @SuppressWarnings("unchecked") |
| 126 | + Map<String, Object> result = (Map<String, Object>) concoredocker.literalEval("{'url': 'http://example.com'}"); |
| 127 | + check("string with colon", "http://example.com", result.get("url")); |
| 128 | + } |
| 129 | + |
| 130 | + static void testStringEscapeSequences() { |
| 131 | + Object result = concoredocker.literalEval("'hello\\nworld'"); |
| 132 | + check("escaped newline", "hello\nworld", result); |
| 133 | + } |
| 134 | + |
| 135 | + static void testScientificNotation() { |
| 136 | + Object result = concoredocker.literalEval("1.5e3"); |
| 137 | + check("scientific notation", 1500.0, result); |
| 138 | + } |
| 139 | + |
| 140 | + static void testNegativeNumbers() { |
| 141 | + @SuppressWarnings("unchecked") |
| 142 | + List<Object> result = (List<Object>) concoredocker.literalEval("[-1, -2.5, 3]"); |
| 143 | + check("negative int", -1, result.get(0)); |
| 144 | + check("negative double", -2.5, result.get(1)); |
| 145 | + check("positive int", 3, result.get(2)); |
| 146 | + } |
| 147 | + |
| 148 | + static void testTuple() { |
| 149 | + @SuppressWarnings("unchecked") |
| 150 | + List<Object> result = (List<Object>) concoredocker.literalEval("(1, 2, 3)"); |
| 151 | + check("tuple size", 3, result.size()); |
| 152 | + check("tuple[0]", 1, result.get(0)); |
| 153 | + } |
| 154 | + |
| 155 | + static void testTrailingComma() { |
| 156 | + @SuppressWarnings("unchecked") |
| 157 | + List<Object> result = (List<Object>) concoredocker.literalEval("[1, 2, 3,]"); |
| 158 | + check("trailing comma size", 3, result.size()); |
| 159 | + } |
| 160 | + |
| 161 | + // --- Serialization tests (toPythonLiteral via write format) --- |
| 162 | + |
| 163 | + static void testToPythonLiteralBooleans() { |
| 164 | + // Test that booleans serialize to Python format (True/False, not true/false) |
| 165 | + @SuppressWarnings("unchecked") |
| 166 | + List<Object> input = (List<Object>) concoredocker.literalEval("[True, False]"); |
| 167 | + // Re-parse and check the values are correct Java booleans |
| 168 | + check("parsed True is Boolean.TRUE", Boolean.TRUE, input.get(0)); |
| 169 | + check("parsed False is Boolean.FALSE", Boolean.FALSE, input.get(1)); |
| 170 | + } |
| 171 | + |
| 172 | + static void testToPythonLiteralNone() { |
| 173 | + @SuppressWarnings("unchecked") |
| 174 | + List<Object> input = (List<Object>) concoredocker.literalEval("[None, 1]"); |
| 175 | + check("parsed None is null", null, input.get(0)); |
| 176 | + check("parsed 1 is Integer 1", 1, input.get(1)); |
| 177 | + } |
| 178 | + |
| 179 | + static void testToPythonLiteralString() { |
| 180 | + Object result = concoredocker.literalEval("'hello'"); |
| 181 | + check("parsed string", "hello", result); |
| 182 | + } |
| 183 | + |
| 184 | + static void testFractionalSimtime() { |
| 185 | + // Simtime values like [0.5, 1.0, 2.0] should preserve fractional part |
| 186 | + @SuppressWarnings("unchecked") |
| 187 | + List<Object> result = (List<Object>) concoredocker.literalEval("[0.5, 1.0, 2.0]"); |
| 188 | + check("fractional simtime[0]", 0.5, result.get(0)); |
| 189 | + check("fractional simtime[1]", 1.0, result.get(1)); |
| 190 | + check("fractional simtime[2]", 2.0, result.get(2)); |
| 191 | + } |
| 192 | +} |
0 commit comments