Skip to content

Commit

Permalink
Cleave of failing #1180 tests from passing #1173 tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Dec 31, 2023
1 parent ef9f0c9 commit 717cd6b
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 38 deletions.
10 changes: 8 additions & 2 deletions src/main/java/com/fasterxml/jackson/core/JsonLocation.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,16 @@ public Object getSourceRef() {
public int getLineNr() { return _lineNr; }

/**
* Access for getting column position of this location, if available.
* Access for getting column offset of this location, if available.
* Note that column position is typically not available for binary formats.
* Note: this returns an offset that is in units of input, so for {@code byte}-based
* input sources (like {@link java.io.InputStream}) this does not take into
* account multi-byte characters: one logical character can be 1, 2 or 3 bytes long.
* To calculate column position in characters either {@code char}-based input
* source (like {@link java.io.Reader}) needs to be used, or content needs to be
* explicitly decoded.
*
* @return Column position of the location (1-based), if available; {@code -1} if not.
* @return Column offset of the location (1-based), if available; {@code -1} if not.
*/
public int getColumnNr() { return _columnNr; }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.fasterxml.jackson.failing.read;
package com.fasterxml.jackson.core.read.loc;

import java.io.*;
import java.nio.charset.StandardCharsets;
Expand All @@ -21,8 +21,8 @@
import static org.junit.Assert.assertTrue;

/**
* Tests that the {@link JsonLocation} attached to a thrown {@link JsonProcessingException} due to invalid json points
* to the correct character.
* Tests that the {@link JsonLocation} attached to a thrown {@link StreamReadException}
* due to invalid JSON points to the correct character.
*/
public class LocationOfError1173Test
{
Expand Down Expand Up @@ -200,39 +200,6 @@ public JsonParser createParser(String input) throws Exception
1,
2
),
new InvalidJson(
"Incorrect case for false literal",
"{\"isThisValidJson\": FALSE}",
24,
24,
1,
25
),
new InvalidJson(
"Incorrect case for true literal",
"{\"shouldYouAvoidWritingJsonLikeThis\": TRUE}",
41,
41,
1,
42
),
new InvalidJson(
"Incorrect case for null literal",
"{\"licensePlate\": NULL}",
20,
20,
1,
21
),
new InvalidJson(
"Invalid JSON with raw unicode character",
// javac will parse the 3-byte unicode control sequence, it will be passed to the parser as a raw unicode character
a2q("{'validJson':'\u274c','right', 'here'}"),
26,
24,
1,
25
),
new InvalidJson(
"Error in middle of line for multiline input",
// missing comma delimiter between properties two and three
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package com.fasterxml.jackson.failing;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.async.ByteArrayFeeder;
import com.fasterxml.jackson.core.exc.StreamReadException;

import static com.fasterxml.jackson.core.BaseTest.a2q;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
* Tests that the {@link JsonLocation} attached to a thrown {@link StreamReadException}
* due to invalid JSON points to the correct character.
*/
public class LocationOfError1180Test
{
static final JsonFactory JSON_F = new JsonFactory();

/** Represents the different parser backends */
public enum ParserVariant
{
BYTE_ARRAY(
(String input) -> JSON_F.createParser(input.getBytes(StandardCharsets.UTF_8)),
true, // supports byte offsets in reported location
false, // supports character offsets in reported location
true // supports column numbers in reported location
),
CHAR_ARRAY(
(String input) -> JSON_F.createParser(input.toCharArray()),
false,
true,
true
),
ASYNC(
(String input) -> {
JsonParser parser = JSON_F.createNonBlockingByteArrayParser();
ByteArrayFeeder feeder = (ByteArrayFeeder) parser.getNonBlockingInputFeeder();
assertTrue(feeder.needMoreInput());

byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8);
feeder.feedInput(inputBytes, 0, inputBytes.length);
feeder.endOfInput();

return parser;
},
true,
false,
true
)
;

ParserVariant(
ParserGenerator parserGenerator,
boolean supportsByteOffset,
boolean supportsCharOffset,
boolean supportsColumnNr
)
{
_parserGenerator = parserGenerator;

this.supportsByteOffset = supportsByteOffset;
this.supportsCharOffset = supportsCharOffset;
this.supportsColumnNr = supportsColumnNr;
}

public JsonParser createParser(String input) throws Exception
{
return _parserGenerator.createParser(input);
}

private final ParserGenerator _parserGenerator;
public final boolean supportsByteOffset;
public final boolean supportsCharOffset;
public final boolean supportsColumnNr;
}

/** Collection of differing invalid JSON input cases to test */
private static final List<InvalidJson> INVALID_JSON_CASES = Arrays.asList(
new InvalidJson(
"Incorrect case for false literal",
"{\"isThisValidJson\": FALSE}",
24,
24,
1,
25
),
new InvalidJson(
"Incorrect case for true literal",
"{\"shouldYouAvoidWritingJsonLikeThis\": TRUE}",
41,
41,
1,
42
),
new InvalidJson(
"Incorrect case for null literal",
"{\"licensePlate\": NULL}",
20,
20,
1,
21
),
// NOTE: to be removed, eventually
new InvalidJson(
"Invalid JSON with raw unicode character",
// javac will parse the 3-byte unicode control sequence, it will be passed to the parser as a raw unicode character
a2q("{'validJson':'\u274c','right', 'here'}"),
26,
24,
1,
25
)
);

@ParameterizedTest
@MethodSource("_generateTestData")
public void testParserBackendWithInvalidJson(ParserVariant variant, InvalidJson invalidJson)
throws Exception
{
try (JsonParser parser = variant.createParser(invalidJson.input))
{
StreamReadException e = Assertions.assertThrows(
StreamReadException.class,
() -> {
// Blindly advance the parser through the end of input
while (parser.nextToken() != null) {}
}
);

JsonLocation location = e.getLocation();
assertEquals(invalidJson.lineNr, location.getLineNr());
final String msg = e.getOriginalMessage();

if (variant.supportsByteOffset)
{
assertEquals("Incorrect byte offset (for '"+msg+"')",
invalidJson.byteOffset, location.getByteOffset());
}
if (variant.supportsCharOffset)
{
assertEquals("Incorrect char offset (for '"+msg+"')",
invalidJson.charOffset, location.getCharOffset());
}
if (variant.supportsColumnNr)
{
assertEquals("Incorrect column (for '"+msg+"')",
invalidJson.columnNr, location.getColumnNr());
}
}
}

private static Stream<Arguments> _generateTestData()
{
return Arrays.stream(ParserVariant.values())
.flatMap(parserVariant -> INVALID_JSON_CASES.stream().map(
invalidJson -> Arguments.of(parserVariant, invalidJson)
));
}

@FunctionalInterface
public interface ParserGenerator
{
JsonParser createParser(String input) throws Exception;
}

static class InvalidJson
{
InvalidJson(String name, String input, int byteOffset, int charOffset,
int lineNr, int columnNr)
{
_name = name;

this.input = input;
this.byteOffset = byteOffset;
this.charOffset = charOffset;
this.lineNr = lineNr;
this.columnNr = columnNr;
}

@Override
public String toString()
{
return _name;
}

private final String _name;
public final String input;
public final int byteOffset;
public final int charOffset;
public final int lineNr;
public final int columnNr;
}
}

0 comments on commit 717cd6b

Please sign in to comment.