AsyncStringObjectTest.java
package tools.jackson.core.unittest.json.async;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import org.junit.jupiter.api.Test;
import tools.jackson.core.JsonToken;
import tools.jackson.core.json.JsonFactory;
import tools.jackson.core.unittest.async.AsyncTestBase;
import tools.jackson.core.unittest.testutil.AsyncReaderWrapper;
import static org.junit.jupiter.api.Assertions.*;
class AsyncStringObjectTest extends AsyncTestBase
{
private final static String STR0_9 = "0123456789";
private final static String ASCII_SHORT_NAME = "a"+STR0_9+"z";
private final static String UNICODE_SHORT_NAME = "Unicode"+UNICODE_3BYTES+"RlzOk";
private final static String UNICODE_LONG_NAME = String.format(
"Unicode-"+UNICODE_3BYTES+"-%s-%s-%s-"+UNICODE_2BYTES+"-%s-%s-%s-"+UNICODE_3BYTES+"-%s-%s-%s",
STR0_9, STR0_9, STR0_9, STR0_9, STR0_9, STR0_9, STR0_9, STR0_9, STR0_9);
private final JsonFactory JSON_F = new JsonFactory();
@Test
void basicFieldsNames() throws IOException
{
final String json = a2q(String.format("{'%s':'%s','%s':'%s','%s':'%s'}",
UNICODE_SHORT_NAME, UNICODE_LONG_NAME,
UNICODE_LONG_NAME, UNICODE_SHORT_NAME,
ASCII_SHORT_NAME, ASCII_SHORT_NAME));
final JsonFactory f = JSON_F;
byte[] data = _jsonDoc(json);
_testBasicFieldsNames(f, data, 0, 100);
_testBasicFieldsNames(f, data, 0, 3);
_testBasicFieldsNames(f, data, 0, 1);
_testBasicFieldsNames(f, data, 1, 100);
_testBasicFieldsNames(f, data, 1, 3);
_testBasicFieldsNames(f, data, 1, 1);
}
private void _testBasicFieldsNames(JsonFactory f,
byte[] data, int offset, int readSize) throws IOException
{
_testBasicFieldsNames2(f, data, offset, readSize, true);
_testBasicFieldsNames2(f, data, offset, readSize, false);
}
private void _testBasicFieldsNames2(JsonFactory f,
byte[] data, int offset, int readSize, boolean verifyContents) throws IOException
{
try (AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset)) {
// start with "no token"
assertNull(r.currentToken());
assertToken(JsonToken.START_OBJECT, r.nextToken());
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
if (verifyContents) {
assertEquals(UNICODE_SHORT_NAME, r.currentName());
assertEquals(UNICODE_SHORT_NAME, r.currentText());
}
assertToken(JsonToken.VALUE_STRING, r.nextToken());
// also, should always be accessible this way:
if (verifyContents) {
assertTrue(r.parser().hasStringCharacters());
assertEquals(UNICODE_LONG_NAME, r.currentText());
}
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
if (verifyContents) {
assertEquals(UNICODE_LONG_NAME, r.currentName());
assertEquals(UNICODE_LONG_NAME, r.currentText());
}
assertToken(JsonToken.VALUE_STRING, r.nextToken());
if (verifyContents) {
assertEquals(UNICODE_SHORT_NAME, r.currentText());
}
// and ASCII entry
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
if (verifyContents) {
assertEquals(ASCII_SHORT_NAME, r.currentName());
assertEquals(ASCII_SHORT_NAME, r.currentText());
}
assertToken(JsonToken.VALUE_STRING, r.nextToken());
if (verifyContents) {
assertEquals(ASCII_SHORT_NAME, r.currentText());
}
assertToken(JsonToken.END_OBJECT, r.nextToken());
assertNull(r.nextToken());
}
// Second round, try with alternate read method
if (verifyContents) {
try (AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset)) {
assertToken(JsonToken.START_OBJECT, r.nextToken());
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
assertEquals(UNICODE_SHORT_NAME, r.currentTextViaWriter());
assertToken(JsonToken.VALUE_STRING, r.nextToken());
assertEquals(UNICODE_LONG_NAME, r.currentTextViaWriter());
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
assertEquals(UNICODE_LONG_NAME, r.currentTextViaWriter());
assertToken(JsonToken.VALUE_STRING, r.nextToken());
assertEquals(UNICODE_SHORT_NAME, r.currentTextViaWriter());
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
assertEquals(ASCII_SHORT_NAME, r.currentTextViaWriter());
assertToken(JsonToken.VALUE_STRING, r.nextToken());
assertEquals(ASCII_SHORT_NAME, r.currentTextViaWriter());
assertToken(JsonToken.END_OBJECT, r.nextToken());
}
}
}
// [core#1288]: readString(Writer) should not be supported for non-blocking parsers
@Test
void readStringNotSupported() throws IOException
{
final String json = a2q("{'name':'value'}");
byte[] data = _jsonDoc(json);
_testReadStringNotSupported(data, 0, 100);
_testReadStringNotSupported(data, 0, 3);
_testReadStringNotSupported(data, 0, 1);
}
private void _testReadStringNotSupported(byte[] data, int offset, int readSize)
throws IOException
{
try (AsyncReaderWrapper r = asyncForBytes(JSON_F, readSize, data, offset)) {
assertToken(JsonToken.START_OBJECT, r.nextToken());
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
assertEquals("name", r.currentName());
assertToken(JsonToken.VALUE_STRING, r.nextToken());
assertEquals("value", r.currentText());
// readString(Writer) should throw UnsupportedOperationException
Writer writer = new StringWriter();
UnsupportedOperationException ex = assertThrows(
UnsupportedOperationException.class,
() -> r.parser().readString(writer),
"Should throw UnsupportedOperationException for readString() on non-blocking parser"
);
assertTrue(ex.getMessage().contains("not supported for non-blocking"),
"Exception message should mention non-blocking parsers");
assertTrue(ex.getMessage().contains("readString"),
"Exception message should mention readString method");
assertToken(JsonToken.END_OBJECT, r.nextToken());
}
}
@Test
void readStringNotSupportedForVariousTokens() throws IOException
{
// Test with different token types to ensure consistent behavior
final String json = a2q("{'prop':'text','num':42,'flag':true,'nul':null}");
byte[] data = _jsonDoc(json);
try (AsyncReaderWrapper r = asyncForBytes(JSON_F, 100, data, 0)) {
assertToken(JsonToken.START_OBJECT, r.nextToken());
// Property name
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
Writer writer = new StringWriter();
assertThrows(UnsupportedOperationException.class,
() -> r.parser().readString(writer));
// String value
assertToken(JsonToken.VALUE_STRING, r.nextToken());
assertThrows(UnsupportedOperationException.class,
() -> r.parser().readString(writer));
// Number
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken());
assertThrows(UnsupportedOperationException.class,
() -> r.parser().readString(writer));
// Boolean
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
assertToken(JsonToken.VALUE_TRUE, r.nextToken());
assertThrows(UnsupportedOperationException.class,
() -> r.parser().readString(writer));
// Null
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
assertToken(JsonToken.VALUE_NULL, r.nextToken());
assertThrows(UnsupportedOperationException.class,
() -> r.parser().readString(writer));
assertToken(JsonToken.END_OBJECT, r.nextToken());
}
}
@Test
void readStringNotSupportedWithLongString() throws IOException
{
// Test with a longer string to ensure it's not a length-related issue
final String longValue = "x".repeat(1000);
final String json = a2q("{'data':'" + longValue + "'}");
byte[] data = _jsonDoc(json);
try (AsyncReaderWrapper r = asyncForBytes(JSON_F, 100, data, 0)) {
assertToken(JsonToken.START_OBJECT, r.nextToken());
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
assertToken(JsonToken.VALUE_STRING, r.nextToken());
// Verify we can still read via getString()
assertEquals(longValue, r.currentText());
// But readString(Writer) should throw
Writer writer = new StringWriter();
UnsupportedOperationException ex = assertThrows(
UnsupportedOperationException.class,
() -> r.parser().readString(writer)
);
assertTrue(ex.getMessage().contains("not supported"));
assertToken(JsonToken.END_OBJECT, r.nextToken());
}
}
}