[serde-gson,serde-hjson,serde-jackson] Unify JSON reader tests

This commit is contained in:
2025-08-03 21:19:27 +02:00
parent 7fec263af0
commit a666ac6c33
4 changed files with 224 additions and 135 deletions

View File

@@ -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<Double> 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() {

View File

@@ -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());
}
}
}

View File

@@ -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;

View File

@@ -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) {