From a666ac6c33ceb42bd5744c9d393ebba97406f13d Mon Sep 17 00:00:00 2001 From: Siphalor Date: Sun, 3 Aug 2025 21:19:27 +0200 Subject: [PATCH] [serde-gson,serde-hjson,serde-jackson] Unify JSON reader tests --- .../testutils/serde/json/JsonReaderTest.java | 132 ++++++++++++ .../tweed5/data/hjson/HjsonReaderTest.java | 188 +++++------------- .../tweed5/data/jackson/JacksonReader.java | 20 ++ .../data/jackson/JacksonReaderTest.java | 19 ++ 4 files changed, 224 insertions(+), 135 deletions(-) diff --git a/test-utils/serde-json/src/main/java/de/siphalor/tweed5/testutils/serde/json/JsonReaderTest.java b/test-utils/serde-json/src/main/java/de/siphalor/tweed5/testutils/serde/json/JsonReaderTest.java index 5a152f0..634e56e 100644 --- a/test-utils/serde-json/src/main/java/de/siphalor/tweed5/testutils/serde/json/JsonReaderTest.java +++ b/test-utils/serde-json/src/main/java/de/siphalor/tweed5/testutils/serde/json/JsonReaderTest.java @@ -1,14 +1,146 @@ package de.siphalor.tweed5.testutils.serde.json; import de.siphalor.tweed5.dataapi.api.TweedDataReader; +import de.siphalor.tweed5.dataapi.api.TweedDataToken; import lombok.SneakyThrows; +import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; public interface JsonReaderTest { + default Offset getDoublePrecision() { + return Offset.offset(0.000000001D); + } + TweedDataReader createJsonReader(String text); + @ParameterizedTest + @CsvSource({ + "127,127", + "-128,-128", + }) + @SneakyThrows + default void jsonByte(String input, byte expected) { + try (var reader = createJsonReader(input)) { + var token = reader.readToken(); + assertThat(token.canReadAsByte()).as("%s should be a valid byte", input).isTrue(); + assertThat(token.readAsByte()).isEqualTo(expected); + } + } + + @ParameterizedTest + @CsvSource({ + "1.23e2,123", + "120e-1,12", + }) + @SneakyThrows + default void jsonByteFloaty(String input, byte expected) { + try (var reader = createJsonReader(input)) { + var token = reader.readToken(); + assertThat(token.canReadAsByte()).as("%s should be a valid byte", input).isTrue(); + assertThat(token.readAsByte()).isEqualTo(expected); + } + } + + @ParameterizedTest + @CsvSource({ + "128", + "1.23", + "-129", + "1.23e3", + "123E-1", + }) + @SneakyThrows + default void jsonByteIllegal(String input) { + try (var reader = createJsonReader(input)) { + var token = reader.readToken(); + assertThat(token.canReadAsByte()).as("%s should not be a valid byte", input).isFalse(); + } + } + + @ParameterizedTest + @CsvSource(value = { + "123,123", + "-132893892,-132893892", + }) + @SneakyThrows + default void jsonInt(String input, int expected) { + try (var reader = createJsonReader(input)) { + var token = reader.readToken(); + assertThat(token.canReadAsInt()).as("%s should be a valid int", input).isTrue(); + assertThat(token.readAsInt()).isEqualTo(expected); + } + } + + @ParameterizedTest + @CsvSource(value = { + "123e4,1230000", + "9.87e3,9870", + "45670E-1,4567", + "-123.56E+5,-12356000", + }) + @SneakyThrows + default void jsonIntFloaty(String input, int expected) { + try (var reader = createJsonReader(input)) { + var token = reader.readToken(); + assertThat(token.canReadAsInt()).as("%s should be a valid int", input).isTrue(); + assertThat(token.readAsInt()).isEqualTo(expected); + } + } + + @ParameterizedTest + @CsvSource({ + "123,123", + "12.34,12.34", + "123456789.123456789,123456789.123456789", + "1234.057e8,123405700000", + "987.654E-5,0.00987654", + }) + @SneakyThrows + default void jsonDouble(String input, double expected) { + try (var reader = createJsonReader(input)) { + var token = reader.readToken(); + assertThat(token.canReadAsDouble()).as("%s should be a valid double", input).isTrue(); + assertThat(token.readAsDouble()).isEqualTo(expected, getDoublePrecision()); + } + } + + @ParameterizedTest + @CsvSource({ + "123,123", + "98765,98765" + }) + @SneakyThrows + default void jsonIntAsDouble(String input, double expected) { + try (var reader = createJsonReader(input)) { + var token = reader.readToken(); + assertThat(token.canReadAsDouble()).as("%s should be a valid double", input).isTrue(); + assertThat(token.readAsDouble()).isEqualTo(expected, getDoublePrecision()); + } + } + + @ParameterizedTest + @ValueSource(strings = { + "[]", + "[\n\n]", + "[ ]", + "[\n\t\t]", + }) + @SneakyThrows + default void jsonEmptyArray(String input) { + try (var reader = createJsonReader(input)) { + TweedDataToken token; + token = reader.readToken(); + assertThat(token.isListStart()).isTrue(); + token = reader.readToken(); + assertThat(token.isListEnd()).isTrue(); + } + } + @Test @SneakyThrows default void complexJsonReadTest() { diff --git a/tweed5-serde-hjson/src/test/java/de/siphalor/tweed5/data/hjson/HjsonReaderTest.java b/tweed5-serde-hjson/src/test/java/de/siphalor/tweed5/data/hjson/HjsonReaderTest.java index 3f1b18a..1f34d7d 100644 --- a/tweed5-serde-hjson/src/test/java/de/siphalor/tweed5/data/hjson/HjsonReaderTest.java +++ b/tweed5-serde-hjson/src/test/java/de/siphalor/tweed5/data/hjson/HjsonReaderTest.java @@ -1,11 +1,10 @@ package de.siphalor.tweed5.data.hjson; -import de.siphalor.tweed5.dataapi.api.TweedDataReadException; import de.siphalor.tweed5.dataapi.api.TweedDataReader; import de.siphalor.tweed5.dataapi.api.TweedDataToken; import de.siphalor.tweed5.testutils.serde.json.JsonReaderTest; +import lombok.SneakyThrows; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; import java.io.StringReader; @@ -13,116 +12,54 @@ import java.io.StringReader; import static org.junit.jupiter.api.Assertions.*; class HjsonReaderTest implements JsonReaderTest { - private static final double DOUBLE_PRECISION = 0.000000001D; - @Override public TweedDataReader createJsonReader(String text) { return new HjsonReader(new HjsonLexer(new StringReader(text))); } - @ParameterizedTest - @CsvSource({ - "127,127", - "-128,-128", - "1.23e2,123", - "1230E-1,123", - }) - void testByte(String input, byte expected) { - HjsonReader hjsonReader = setupReaderWithLexer(input); - TweedDataToken token = assertDoesNotThrow(hjsonReader::readToken); - assertEquals(expected, assertDoesNotThrow(token::readAsByte)); - assertTrue(token.canReadAsByte()); - } - - @ParameterizedTest - @ValueSource(strings = { - "128", - "1.23", - "-129", - "1.23e3", - "123E-1", - }) - void testByteIllegal(String input) { - HjsonReader hjsonReader = setupReaderWithLexer(input); - TweedDataToken token = assertDoesNotThrow(hjsonReader::readToken); - assertThrows(TweedDataReadException.class, token::readAsByte); - assertFalse(token.canReadAsByte()); - } - - @ParameterizedTest - @CsvSource(value = { - "123,123", - "123e4,1230000", - "9.87e3,9870", - "45670E-1,4567", - "-123.56E+5,-12356000", - }) - void testInteger(String input, int expected) { - HjsonReader hjsonReader = setupReaderWithLexer(input); - TweedDataToken token = assertDoesNotThrow(hjsonReader::readToken); - assertEquals(expected, assertDoesNotThrow(token::readAsInt)); - assertTrue(token.canReadAsInt()); - } - - @ParameterizedTest - @CsvSource( - ignoreLeadingAndTrailingWhitespace = false, - value = { - "123,123", - "12.34,12.34", - "123456789.123456789,123456789.123456789", - "1234.057e8,123405700000", - "987.654E-5,0.00987654", - } - ) - void testDouble(String input, double expected) { - HjsonReader hjsonReader = setupReaderWithLexer(input); - TweedDataToken token = assertDoesNotThrow(hjsonReader::readToken); - assertEquals(expected, assertDoesNotThrow(token::readAsDouble), DOUBLE_PRECISION); - assertTrue(token.canReadAsDouble()); - } - @ParameterizedTest @ValueSource(strings = { "{test:abc\ncdef:123\na:true}", "\n{test: abc \ncdef:123,a\n:\ntrue\n}", "// \n{\n\ttest:abc\ncdef:123e0,a: true ,}", }) + @SneakyThrows void testObject(String input) { - HjsonReader hjsonReader = setupReaderWithLexer(input); - TweedDataToken token; - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isMapStart()); + try (var reader = createJsonReader(input)) { + TweedDataToken token; + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isMapStart()); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isMapEntryKey()); - assertTrue(token.canReadAsString()); - assertEquals("test", assertDoesNotThrow(token::readAsString)); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isMapEntryValue()); - assertTrue(token.canReadAsString()); - assertEquals("abc", assertDoesNotThrow(token::readAsString)); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isMapEntryKey()); + assertTrue(token.canReadAsString()); + assertEquals("test", assertDoesNotThrow(token::readAsString)); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isMapEntryValue()); + assertTrue(token.canReadAsString()); + assertEquals("abc", assertDoesNotThrow(token::readAsString)); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isMapEntryKey()); - assertTrue(token.canReadAsString()); - assertEquals("cdef", assertDoesNotThrow(token::readAsString)); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isMapEntryValue()); - assertTrue(token.canReadAsInt()); - assertEquals(123, assertDoesNotThrow(token::readAsInt)); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isMapEntryKey()); + assertTrue(token.canReadAsString()); + assertEquals("cdef", assertDoesNotThrow(token::readAsString)); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isMapEntryValue()); + assertTrue(token.canReadAsInt()); + assertEquals(123, assertDoesNotThrow(token::readAsInt)); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isMapEntryKey()); - assertTrue(token.canReadAsString()); - assertEquals("a", assertDoesNotThrow(token::readAsString)); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isMapEntryValue()); - assertTrue(token.canReadAsBoolean()); - assertEquals(true, assertDoesNotThrow(token::readAsBoolean)); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isMapEntryKey()); + assertTrue(token.canReadAsString()); + assertEquals("a", assertDoesNotThrow(token::readAsString)); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isMapEntryValue()); + assertTrue(token.canReadAsBoolean()); + assertEquals(true, assertDoesNotThrow(token::readAsBoolean)); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isMapEnd()); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isMapEnd()); + } } @ParameterizedTest @@ -132,49 +69,30 @@ class HjsonReaderTest implements JsonReaderTest { "[\n12\n\t\t34\n\t56\n]", "[\n12,34\n\t56\n]", }) + @SneakyThrows void testArray(String input) { - HjsonReader hjsonReader = setupReaderWithLexer(input); - TweedDataToken token; - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isListStart()); + try (var reader = createJsonReader(input)) { + TweedDataToken token; + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isListStart()); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isListValue()); - assertTrue(token.canReadAsInt()); - assertEquals(12, assertDoesNotThrow(token::readAsInt)); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isListValue()); + assertTrue(token.canReadAsInt()); + assertEquals(12, assertDoesNotThrow(token::readAsInt)); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isListValue()); - assertTrue(token.canReadAsInt()); - assertEquals(34, assertDoesNotThrow(token::readAsInt)); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isListValue()); + assertTrue(token.canReadAsInt()); + assertEquals(34, assertDoesNotThrow(token::readAsInt)); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isListValue()); - assertTrue(token.canReadAsInt()); - assertEquals(56, assertDoesNotThrow(token::readAsInt)); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isListValue()); + assertTrue(token.canReadAsInt()); + assertEquals(56, assertDoesNotThrow(token::readAsInt)); - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isListEnd()); - } - - @ParameterizedTest - @ValueSource(strings = { - "[]", - "[\n\n]", - "[ ]", - "[\n\t\t]", - }) - void testEmptyArray(String input) { - HjsonReader hjsonReader = setupReaderWithLexer(input); - TweedDataToken token; - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isListStart()); - - token = assertDoesNotThrow(hjsonReader::readToken); - assertTrue(token.isListEnd()); - } - - private HjsonReader setupReaderWithLexer(String input) { - return new HjsonReader(new HjsonLexer(new StringReader(input))); + token = assertDoesNotThrow(reader::readToken); + assertTrue(token.isListEnd()); + } } } diff --git a/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonReader.java b/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonReader.java index 8a69438..5dd0756 100644 --- a/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonReader.java +++ b/tweed5-serde-jackson/src/main/java/de/siphalor/tweed5/data/jackson/JacksonReader.java @@ -127,6 +127,26 @@ public class JacksonReader implements TweedDataReader { public long readAsLong() { return longValue; } + + @Override + public boolean canReadAsFloat() { + return true; + } + + @Override + public float readAsFloat() { + return (float) longValue; + } + + @Override + public boolean canReadAsDouble() { + return true; + } + + @Override + public double readAsDouble() { + return (double) longValue; + } }); afterValueRead(); return token; diff --git a/tweed5-serde-jackson/src/test/java/de/siphalor/tweed5/data/jackson/JacksonReaderTest.java b/tweed5-serde-jackson/src/test/java/de/siphalor/tweed5/data/jackson/JacksonReaderTest.java index 0e96c99..6b05296 100644 --- a/tweed5-serde-jackson/src/test/java/de/siphalor/tweed5/data/jackson/JacksonReaderTest.java +++ b/tweed5-serde-jackson/src/test/java/de/siphalor/tweed5/data/jackson/JacksonReaderTest.java @@ -5,11 +5,30 @@ import com.fasterxml.jackson.core.StreamReadFeature; import de.siphalor.tweed5.dataapi.api.TweedDataReader; import de.siphalor.tweed5.testutils.serde.json.JsonReaderTest; import lombok.SneakyThrows; +import org.junit.jupiter.api.Disabled; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; class JacksonReaderTest implements JsonReaderTest { + @Disabled("Jackson does not support integer values with floating point syntax elements") + @Override + public void jsonByteFloaty(String input, byte expected) { + JsonReaderTest.super.jsonByteFloaty(input, expected); + } + + @Disabled("Jackson does not support integer values with floating point syntax elements") + @Override + public void jsonIntFloaty(String input, int expected) { + JsonReaderTest.super.jsonIntFloaty(input, expected); + } + + @Disabled("Jackson's double precision is abysmal in comparison with everyone else's even with fast double parsing turned off") + @Override + public void jsonDouble(String input, double expected) { + JsonReaderTest.super.jsonDouble(input, expected); + } + @SneakyThrows @Override public TweedDataReader createJsonReader(String text) {