AsyncHexEscapeNonAscii1787Test.java
package tools.jackson.core.unittest.json.async;
import java.io.ByteArrayOutputStream;
import org.junit.jupiter.api.Test;
import tools.jackson.core.exc.StreamReadException;
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.fail;
/**
* Async variant of [jackson-core#1787] tests: non-ASCII chars must not be
* accepted as hex digits of a {@code \}{@code u} escape, and should be
* reported as decoded chars, not as UTF-8 lead bytes.
*/
class AsyncHexEscapeNonAscii1787Test extends AsyncTestBase
{
private final JsonFactory F = newStreamFactory();
private final static int[] READ_SIZES = new int[] { 1, 2, 3, 5, 99, 1000 };
@Test
void nonAsciiHexDigitsRejected() throws Exception
{
_testBroken("\\u\u2042\u2042\u2042\u2042", "'\u2042' (code 8258 / 0x2042)");
_testBroken("\\u\u0141BCD", "'\u0141' (code 321 / 0x141)");
_testBroken("\\u00\u01300", "'\u0130' (code 304 / 0x130)");
_testBroken("\\u\uD83D\uDE00", "'\uD83D\uDE00' (code 128512 / 0x1f600)");
}
// Invalid or truncated UTF-8 in hex escape: lead byte reported as-is
@Test
void invalidUtf8HexDigitsRejected() throws Exception
{
_testBrokenBytes(new int[] { 0x80 }, "\"]", "(CTRL-CHAR, code 128)");
_testBrokenBytes(new int[] { 0xFF }, "\"]", "'\u00FF' (code 255)");
_testBrokenBytes(new int[] { 0xC3, 'A' }, "\"]", "'\u00C3' (code 195)");
_testBrokenBytes(new int[] { 0xE2, 0x81, 'B' }, "\"]", "'\u00E2' (code 226)");
_testBrokenBytes(new int[] { 0xC0, 0x80 }, "\"]", "'\u00C0' (code 192)");
_testBrokenBytes(new int[] { 0xF5, 0x80, 0x80, 0x80 }, "\"]", "'\u00F5' (code 245)");
_testBrokenBytes(new int[] { 0xE0, 0x80, 0x80 }, "\"]", "'\u00E0' (code 224)");
_testBrokenBytes(new int[] { 0xED, 0xA0, 0x80 }, "\"]", "'\u00ED' (code 237)");
// truncated at end-of-input
_testBrokenBytes(new int[] { 0xE2, 0x81 }, "", "'\u00E2' (code 226)");
}
private void _testBrokenBytes(int[] badBytes, String suffix, String expCharDesc) throws Exception
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bytes.write(_jsonDoc("[\"\\u"));
for (int b : badBytes) {
bytes.write(b);
}
bytes.write(_jsonDoc(suffix));
final byte[] data = bytes.toByteArray();
for (int readSize : READ_SIZES) {
for (boolean byteBuffer : new boolean[] { false, true }) {
try (AsyncReaderWrapper p = byteBuffer ? asyncForByteBuffer(F, readSize, data, 0)
: asyncForBytes(F, readSize, data, 0)) {
while (p.nextToken() != null) {
p.currentText();
}
fail("Should not pass for readSize " + readSize);
} catch (StreamReadException e) {
verifyException(e, "Unexpected character (" + expCharDesc
+ "): expected a hex-digit for character escape sequence");
}
}
}
}
private void _testBroken(String escape, String expCharDesc) throws Exception
{
for (int readSize : READ_SIZES) {
for (boolean byteBuffer : new boolean[] { false, true }) {
for (String doc : new String[] { "[\"" + escape + "\"]", "{\"" + escape + "\":1}" }) {
byte[] data = _jsonDoc(doc);
try (AsyncReaderWrapper p = byteBuffer ? asyncForByteBuffer(F, readSize, data, 0)
: asyncForBytes(F, readSize, data, 0)) {
while (p.nextToken() != null) {
p.currentText();
}
fail("Should not pass for readSize " + readSize + ", doc: " + doc);
} catch (StreamReadException e) {
verifyException(e, "expected a hex-digit for character escape sequence");
// best-effort decoding needs whole char buffered
if (readSize >= 99) {
verifyException(e, "Unexpected character (" + expCharDesc + ")");
}
}
}
}
}
}
}