This is an automated email from the ASF dual-hosted git repository. afs pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/jena.git
commit 12c0dfb57da36a0fef8378d362d3876fcdb36372 Author: Andy Seaborne <[email protected]> AuthorDate: Fri May 22 08:32:25 2026 +0100 GH-3931: No surrogates - SPARQL --- .../sparql/expr/TestSPARQLKeywordFunctions.java | 25 ++- .../apache/jena/sparql/syntax/TestQueryParser.java | 32 ++-- .../java/org/apache/jena/atlas/lib/EscapeStr.java | 61 +++---- .../org/apache/jena/atlas/lib/TestEscapeStr.java | 175 ++++++++++++--------- 4 files changed, 160 insertions(+), 133 deletions(-) diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSPARQLKeywordFunctions.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSPARQLKeywordFunctions.java index 4502fe4639..529f392260 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSPARQLKeywordFunctions.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSPARQLKeywordFunctions.java @@ -24,7 +24,6 @@ package org.apache.jena.sparql.expr; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -199,17 +198,15 @@ public class TestSPARQLKeywordFunctions // As surrogate pair: 0xD83D 0xDC6A // Written here in forms which protect against binary file corruption. - //@Test public void substr_30() { test("substr('👪', 1)", "'👪'"); } + @Test public void substr_30() { test("substr('👪', 1)", "'👪'"); } + // Written using \-u escapes in SPARQL. Tests for supplemental codepoint handling. + @Test public void substr_31() { test("substr('\uD83D\uDC6A', 1)", "'\uD83D\uDC6A'"); } + @Test public void substr_32() { test("substr('\uD83D\uDC6A', 2)", kwEmptyString); } + @Test public void substr_33() { test("substr('ABC\uD83D\uDC6ADEF', 4, 1)", "'\uD83D\uDC6A'"); } + @Test public void substr_34() { test("substr('\uD83D\uDC6A!', -1, 3)", "'\uD83D\uDC6A'"); } + @Test public void substr_35() { test("substr('\uD83D\uDC6A!', -1, 4)", "'\uD83D\uDC6A!'"); } - // Written using \-u escapes in SPARQL. - @Test public void substr_30() { test("substr('\\uD83D\\uDC6A', 1)", "'\\uD83D\\uDC6A'"); } - // Same using Java string escapes. - @Test public void substr_30b() { test("substr('\uD83D\uDC6A', 1)", "'\uD83D\uDC6A'"); } - @Test public void substr_31() { test("substr('\\uD83D\\uDC6A', 2)", kwEmptyString); } - - @Test public void substr_32() { test("substr('ABC\\uD83D\\uDC6ADEF', 4, 1)", "'\\uD83D\\uDC6A'"); } - @Test public void substr_33() { test("substr('\\uD83D\\uDC6A!', -1, 3)", "'\\uD83D\\uDC6A'"); } - @Test public void substr_34() { test("substr('\\uD83D\\uDC6A!', -1, 4)", "'\\uD83D\\uDC6A!'"); } + // Now (1.2) numeric escape sequences do not allow surrogates. See TestQueryParser. // STRLEN @Test public void strlen_01() { test("strlen('abc')", "3"); } @@ -701,10 +698,6 @@ public class TestSPARQLKeywordFunctions private void testEvalException(String exprStr) { Expr expr = ExprUtils.parse(exprStr); - try { - NodeValue r = expr.eval(null, LibTestExpr.createTest()); - fail("No exception raised"); - } - catch (ExprEvalException ex) {} + assertThrows(ExprEvalException.class,()->expr.eval(null, LibTestExpr.createTest())); } } diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryParser.java b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryParser.java index 8d16dd1b03..dd40139037 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryParser.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryParser.java @@ -45,39 +45,51 @@ public class TestQueryParser { LogCtl.withLevel(loggerSPARQL, "fatal", action); } - // Single backslash so a Java string escape, raw surrogate in the string. + // These are U+0001F46A 👪 - FAMILY + // As surrogate pair: 0xD83D 0xDC6A + + @Test + public void syntax_unicode_raw_uri() { + testParse("SELECT * { <http://example/👪> ?p ?o}"); + } + + @Test + public void syntax_unicode_raw_string() { + testParse("SELECT * { ?s ?p '👪'}"); + } + + // Single backslash so a Java string escape, surrogate in the string. @Test public void syntax_unicode_raw_surrogate_uri() { - QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { <http://example/\uD800> ?p ?o}")); + QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { <http://example/\uD83D> ?p ?o}")); assertTrue(ex.getMessage().contains("surrogate")); } @Test public void syntax_unicode_raw_surrogate_string() { - QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { ?s ?p '\uD800' }")); + QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { ?s ?p '\uDC6A' }")); assertTrue(ex.getMessage().contains("surrogate")); } // Double backslash so the query string has an escape in it. @Test public void syntax_unicode_escaped_surrogate_uri() { - QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { <http://example/\\uD800> ?p ?o}")); + QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { <http://example/\\uD83D> ?p ?o}")); assertTrue(ex.getMessage().contains("surrogate")); } @Test public void syntax_unicode_escaped_surrogate_strings() { - QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { ?s ?p '\\uD800'}")); + QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { ?s ?p '\\uD83D'}")); assertTrue(ex.getMessage().contains("surrogate")); } @Test public void syntax_unicode_surrogate_pair_by_unicode_escape() { - // Allow - because Java strings may have surrogate pairs so we allow them in unicode escapes if paired. - testParse("SELECT * { ?s ?p '\\uD801\\uDC37'}"); - -// QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { ?s ?p '\\uD801\\uDC37'}")); -// assertTrue(ex.getMessage().contains("surrogate")); +// // Allow - because Java strings may have surrogate pairs so we allow them in unicode escapes if paired. +// testParse("SELECT * { ?s ?p '\\uD801\\uDC37'}"); + QueryParseException ex = assertThrows(QueryParseException.class, ()->testParse("SELECT * { ?s ?p '\\uD801\\uDC37'}")); + assertTrue(ex.getMessage().contains("Surrogate")); } private static void testParse(String string) { diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/EscapeStr.java b/jena-base/src/main/java/org/apache/jena/atlas/lib/EscapeStr.java index 5c2dde137d..61cf515af8 100644 --- a/jena-base/src/main/java/org/apache/jena/atlas/lib/EscapeStr.java +++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/EscapeStr.java @@ -21,6 +21,8 @@ package org.apache.jena.atlas.lib; +import static org.apache.jena.atlas.lib.Chars.isSurrogate; + import org.apache.jena.atlas.AtlasException; import org.apache.jena.atlas.io.AWriter; import org.apache.jena.atlas.io.OutputUtils; @@ -222,7 +224,7 @@ public class EscapeStr char ch = s.charAt(i); if ( ch != escape ) { - sb.append(ch); + insertCodepoint(sb, ch); continue; } @@ -244,7 +246,10 @@ public class EscapeStr if ( i+4 >= s.length() ) throw new AtlasException("\\u escape too short"); int x4 = Hex.hexStringToInt(s, i+1, 4); - sb.append((char)x4); + if ( isSurrogate(x4) ) + throw new AtlasException(String.format("Surrogate code point in \\u sequence. Value: 0x%04X", x4)); + // Do direct. + insertCodepoint(sb, (char)x4); // Jump 1 2 3 4 -- already skipped \ and u i = i+4; continue; @@ -253,19 +258,9 @@ public class EscapeStr if ( i+8 >= s.length() ) throw new AtlasException("\\U escape too short"); int ch8 = Hex.hexStringToInt(s, i+1, 8); - if ( Character.charCount(ch8) == 1 ) - sb.append((char)ch8); - else { - // See also TokenerText.insertCodepoint and TokenerText.readUnicodeEscape - // Convert to UTF-16. Note that the rest of any system this is used - // in must also respect codepoints and surrogate pairs. - if ( !Character.isDefined(ch8) && !Character.isSupplementaryCodePoint(ch8) ) - throw new AtlasException(String.format("Illegal codepoint: 0x%04X", ch8)); - if ( ch8 > Character.MAX_CODE_POINT ) - throw new AtlasException(String.format("Illegal code point in \\U sequence value: 0x%08X", ch8)); - char[] chars = Character.toChars(ch8); - sb.append(chars); - } + if ( isSurrogate(ch8) ) + throw new AtlasException(String.format("Surrogate code point in \\U sequence. Value: 0x%08X", ch8)); + insertCodepoint(sb, ch8); // Jump 1 2 3 4 5 6 7 8 -- already skipped \ and U i = i+8; continue; @@ -275,8 +270,8 @@ public class EscapeStr // If so, \X-anything else is legal as a literal "\" and "X" if ( pointCodeOnly ) { - sb.append('\\'); - sb.append(ch2); + insertCodepoint(sb, '\\'); + insertCodepoint(sb, ch2); continue; } @@ -294,7 +289,7 @@ public class EscapeStr } if ( actualCh != 0 ) { - sb.append(actualCh); + insertCodepoint(sb, actualCh); continue; } @@ -336,20 +331,26 @@ public class EscapeStr } if ( j == 0 ) throw new AtlasException("Empty \\u{} sequence"); + if ( isSurrogate(value) ) + throw new AtlasException(String.format("Surrogate code point in \\u{} sequence: 0x%08X", value)); + insertCodepoint(sb, value); + // Looking at the closing '}' + i = i+j; + return i; + } - int ch8 = value; - if ( Character.charCount(ch8) == 1 ) - sb.append((char)ch8); + private static void insertCodepoint(StringBuilder sb, int cp) { + if ( Character.charCount(cp) == 1 ) + sb.append((char)cp); else { - if ( !Character.isDefined(ch8) && !Character.isSupplementaryCodePoint(ch8) ) - throw new AtlasException(String.format("Illegal codepoint: 0x%04X", ch8)); - if ( ch8 > Character.MAX_CODE_POINT ) - throw new AtlasException(String.format("Illegal code point in \\u{..} sequence value: 0x%08X", ch8)); - char[] chars = Character.toChars(ch8); + if ( !Character.isDefined(cp) && !Character.isSupplementaryCodePoint(cp) ) + throw new AtlasException(String.format("Illegal codepoint: 0x%04X", cp)); + char[] chars = Character.toChars(cp); sb.append(chars); } - // Looking at the closing '}' - i = i+j; - return i; } -} \ No newline at end of file + + private static void insertCodepoint(StringBuilder sb, char cp) { + sb.append(cp); + } +} diff --git a/jena-base/src/test/java/org/apache/jena/atlas/lib/TestEscapeStr.java b/jena-base/src/test/java/org/apache/jena/atlas/lib/TestEscapeStr.java index bac02e4dd5..95c525f3c2 100644 --- a/jena-base/src/test/java/org/apache/jena/atlas/lib/TestEscapeStr.java +++ b/jena-base/src/test/java/org/apache/jena/atlas/lib/TestEscapeStr.java @@ -26,7 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.StringWriter; -import org.junit.jupiter.api.Test ; +import org.junit.jupiter.api.Test; import org.apache.jena.atlas.AtlasException; import org.apache.jena.atlas.io.AWriter; @@ -41,32 +41,32 @@ public class TestEscapeStr { private static char D_QUOTE = '"'; // General, for double quoted strings. - @Test public void escape_str_01() { test_esc("", "") ; } - @Test public void escape_str_02() { test_esc("A", "A") ; } - @Test public void escape_str_03() { test_esc("\n", "\\n") ; } - @Test public void escape_str_04() { test_esc("A\tB", "A\\tB") ; } - @Test public void escape_str_05() { test_esc("\"", "\\\"") ; } - @Test public void escape_str_06() { test_esc("'", "'") ; } + @Test public void escape_str_01() { test_esc("", ""); } + @Test public void escape_str_02() { test_esc("A", "A"); } + @Test public void escape_str_03() { test_esc("\n", "\\n"); } + @Test public void escape_str_04() { test_esc("A\tB", "A\\tB"); } + @Test public void escape_str_05() { test_esc("\"", "\\\""); } + @Test public void escape_str_06() { test_esc("'", "'"); } private static void test_esc(String input, String expected) { - String output = EscapeStr.stringEsc(input) ; + String output = EscapeStr.stringEsc(input); assertEquals(expected, output, ()->"Failed at escape"); String output2 = EscapeStr.unescapeStr(output); assertEquals(input, output2, ()->"Failed at unescape"); } // Single line - @Test public void escape_str_single_01() { test_esc1("a'x", S_QUOTE, "a\\'x") ; } - @Test public void escape_str_single_02() { test_esc1("a\"x", D_QUOTE, "a\\\"x") ; } + @Test public void escape_str_single_01() { test_esc1("a'x", S_QUOTE, "a\\'x"); } + @Test public void escape_str_single_02() { test_esc1("a\"x", D_QUOTE, "a\\\"x"); } - @Test public void escape_str_single_03() { test_esc1("a\"x", S_QUOTE, "a\"x") ; } - @Test public void escape_str_single_04() { test_esc1("a'x", D_QUOTE, "a'x") ; } + @Test public void escape_str_single_03() { test_esc1("a\"x", S_QUOTE, "a\"x"); } + @Test public void escape_str_single_04() { test_esc1("a'x", D_QUOTE, "a'x"); } - @Test public void escape_str_single_05() { test_esc1("a'", S_QUOTE, "a\\'") ; } - @Test public void escape_str_single_06() { test_esc1("a\"", D_QUOTE, "a\\\"") ; } + @Test public void escape_str_single_05() { test_esc1("a'", S_QUOTE, "a\\'"); } + @Test public void escape_str_single_06() { test_esc1("a\"", D_QUOTE, "a\\\""); } - @Test public void escape_str_single_07() { test_esc1("\"", S_QUOTE, "\"") ; } - @Test public void escape_str_single_08() { test_esc1("'", D_QUOTE, "'") ; } + @Test public void escape_str_single_07() { test_esc1("\"", S_QUOTE, "\""); } + @Test public void escape_str_single_08() { test_esc1("'", D_QUOTE, "'"); } private static void test_esc1(String input, char quoteChar, String expected) { StringWriter sw = new StringWriter(); @@ -79,43 +79,43 @@ public class TestEscapeStr { // Multiline quoting. // One character - @Test public void escape_str_multi_01() { test_esc3("a'x", S_QUOTE, "a'x") ; } - @Test public void escape_str_multi_02() { test_esc3("a\"x", D_QUOTE, "a\"x") ; } + @Test public void escape_str_multi_01() { test_esc3("a'x", S_QUOTE, "a'x"); } + @Test public void escape_str_multi_02() { test_esc3("a\"x", D_QUOTE, "a\"x"); } - @Test public void escape_str_multi_03() { test_esc3("'x", S_QUOTE, "'x") ; } - @Test public void escape_str_multi_04() { test_esc3("'x", D_QUOTE, "'x") ; } + @Test public void escape_str_multi_03() { test_esc3("'x", S_QUOTE, "'x"); } + @Test public void escape_str_multi_04() { test_esc3("'x", D_QUOTE, "'x"); } // Last character - @Test public void escape_str_multi_05() { test_esc3("a'", S_QUOTE, "a\\'") ; } - @Test public void escape_str_multi_06() { test_esc3("a'", D_QUOTE, "a'") ; } + @Test public void escape_str_multi_05() { test_esc3("a'", S_QUOTE, "a\\'"); } + @Test public void escape_str_multi_06() { test_esc3("a'", D_QUOTE, "a'"); } - @Test public void escape_str_multi_07() { test_esc3("a\"", S_QUOTE, "a\"") ; } - @Test public void escape_str_multi_08() { test_esc3("a\"", D_QUOTE, "a\\\"") ; } + @Test public void escape_str_multi_07() { test_esc3("a\"", S_QUOTE, "a\""); } + @Test public void escape_str_multi_08() { test_esc3("a\"", D_QUOTE, "a\\\""); } - @Test public void escape_str_multi_09() { test_esc3("'", S_QUOTE, "\\'") ; } - @Test public void escape_str_multi_10() { test_esc3("'", D_QUOTE, "'") ; } + @Test public void escape_str_multi_09() { test_esc3("'", S_QUOTE, "\\'"); } + @Test public void escape_str_multi_10() { test_esc3("'", D_QUOTE, "'"); } - @Test public void escape_str_multi_11() { test_esc3("\"", S_QUOTE, "\"") ; } - @Test public void escape_str_multi_12() { test_esc3("\"", D_QUOTE, "\\\"") ; } + @Test public void escape_str_multi_11() { test_esc3("\"", S_QUOTE, "\""); } + @Test public void escape_str_multi_12() { test_esc3("\"", D_QUOTE, "\\\""); } // 2 in a row - @Test public void escape_str_multi_2q_1() { test_esc3("a''z", S_QUOTE, "a''z") ; } - @Test public void escape_str_multi_2q_2() { test_esc3("a''z", D_QUOTE, "a''z") ; } - @Test public void escape_str_multi_2q_3() { test_esc3("a''", S_QUOTE, "a'\\'") ; } - @Test public void escape_str_multi_2q_4() { test_esc3("a''", D_QUOTE, "a''") ; } + @Test public void escape_str_multi_2q_1() { test_esc3("a''z", S_QUOTE, "a''z"); } + @Test public void escape_str_multi_2q_2() { test_esc3("a''z", D_QUOTE, "a''z"); } + @Test public void escape_str_multi_2q_3() { test_esc3("a''", S_QUOTE, "a'\\'"); } + @Test public void escape_str_multi_2q_4() { test_esc3("a''", D_QUOTE, "a''"); } // 3 in a row. - @Test public void escape_str_multi_3q_1() { test_esc3("a'''z", S_QUOTE, "a''\\'z") ; } - @Test public void escape_str_multi_3q_2() { test_esc3("a'''z", D_QUOTE, "a'''z") ; } - @Test public void escape_str_multi_3q_3() { test_esc3("a'''", S_QUOTE, "a''\\'") ; } - @Test public void escape_str_multi_3q_4() { test_esc3("a'''", D_QUOTE, "a'''") ; } + @Test public void escape_str_multi_3q_1() { test_esc3("a'''z", S_QUOTE, "a''\\'z"); } + @Test public void escape_str_multi_3q_2() { test_esc3("a'''z", D_QUOTE, "a'''z"); } + @Test public void escape_str_multi_3q_3() { test_esc3("a'''", S_QUOTE, "a''\\'"); } + @Test public void escape_str_multi_3q_4() { test_esc3("a'''", D_QUOTE, "a'''"); } // 4 in a row. - @Test public void escape_str_multi_4q_1() { test_esc3("a''''z", S_QUOTE, "a''\\''z") ; } - @Test public void escape_str_multi_4q_2() { test_esc3("a''''z", D_QUOTE, "a''''z") ; } + @Test public void escape_str_multi_4q_1() { test_esc3("a''''z", S_QUOTE, "a''\\''z"); } + @Test public void escape_str_multi_4q_2() { test_esc3("a''''z", D_QUOTE, "a''''z"); } - @Test public void escape_str_multi_4q_3() { test_esc3("a''''", S_QUOTE, "a''\\'\\'") ; } - @Test public void escape_str_multi_4q_4() { test_esc3("a''''", D_QUOTE, "a''''") ; } + @Test public void escape_str_multi_4q_3() { test_esc3("a''''", S_QUOTE, "a''\\'\\'"); } + @Test public void escape_str_multi_4q_4() { test_esc3("a''''", D_QUOTE, "a''''"); } // Unicode replacement char U+FFFD - write in Unicode escape form. @Test public void escape_str_repacementChar_1() { test_esc1("abc\uFFFDdef", S_QUOTE, "abc\\uFFFDdef"); } @@ -131,71 +131,92 @@ public class TestEscapeStr { } // Unescape - @Test public void unescape_str_10() { test_unesc("\\u0041", "A") ; } - @Test public void unescape_str_11() { test_unesc("\\U00000041", "A") ; } - @Test public void unescape_str_12() { test_unesc("12\\u004134", "12A34") ; } - @Test public void unescape_str_13() { test_unesc("12\\U0000004134", "12A34") ; } + @Test public void unescape_str_10() { test_unesc("\\u0041", "A"); } + @Test public void unescape_str_11() { test_unesc("\\U00000041", "A"); } + @Test public void unescape_str_12() { test_unesc("12\\u004134", "12A34"); } + @Test public void unescape_str_13() { test_unesc("12\\U0000004134", "12A34"); } private void test_unesc(String input, String expected) { - String output = EscapeStr.unescapeStr(input) ; + String output = EscapeStr.unescapeStr(input); assertEquals(expected, output); } - private void test_escape(String input, String expected) { + private void test_escape_string(String input, String expected) { String output = EscapeStr.stringEsc(input); assertEquals(expected, output); } - @Test public void unescape_unicode_1() { test_unesc_unicode("", "") ; } - @Test public void unescape_unicode_2() { test_unesc_unicode("abc\\u0020def", "abc def") ; } - @Test public void unescape_unicode_3() { test_unesc_unicode("\\u0020", " ") ; } - @Test public void unescape_unicode_4() { test_unesc_unicode("abc\\U00000020def", "abc def") ; } - @Test public void unescape_unicode_5() { test_unesc_unicode("\\U00000020", " ") ; } + + @Test public void unescape_unicode_1() { test_unesc_unicode("", ""); } + @Test public void unescape_unicode_2() { test_unesc_unicode("abc\\u0020def", "abc def"); } + @Test public void unescape_unicode_3() { test_unesc_unicode("\\u0020", " "); } + @Test public void unescape_unicode_4() { test_unesc_unicode("abc\\U00000020def", "abc def"); } + @Test public void unescape_unicode_5() { test_unesc_unicode("\\U00000020", " "); } // Leaves non-unicode untouched. - @Test public void unescape_unicode_10() { test_unesc_unicode("\\1\\2", "\\1\\2") ; } - @Test public void unescape_unicode_11() { test_unesc_unicode("\\n\\t", "\\n\\t") ; } - @Test public void unescape_unicode_12() { test_unesc_unicode("\\(\\)", "\\(\\)") ; } - @Test public void unescape_unicode_13() { test_unesc_unicode("\\\\", "\\\\") ; } + @Test public void unescape_unicode_10() { test_unesc_unicode("\\1\\2", "\\1\\2"); } + @Test public void unescape_unicode_11() { test_unesc_unicode("\\n\\t", "\\n\\t"); } + @Test public void unescape_unicode_12() { test_unesc_unicode("\\(\\)", "\\(\\)"); } + @Test public void unescape_unicode_13() { test_unesc_unicode("\\\\", "\\\\"); } // \-u{...} style Unicode escapes - @Test public void unescape_unicode_20() { test_unesc_unicode("\\u{41}", "A") ; } - @Test public void unescape_unicode_21() { test_unesc_unicode("\\u{000000}", "\u0000") ; } - @Test public void unescape_unicode_22() { test_unesc_unicode("\\u{1F0A1}", "🂡") ; } - @Test public void unescape_unicode_23() { test_unesc_unicode("\\u{01F0A1}", "🂡") ; } - @Test public void unescape_unicode_24() { test_unesc_unicode("\\u{10FFFF}", 0x10FFFF) ; } - - @Test public void unescape_unicode_30() { assertThrows(AtlasException.class, ()->test_unesc_unicode("\\u{}", "")) ; } - @Test public void unescape_unicode_31() { assertThrows(AtlasException.class, ()->test_unesc_unicode("\\u{123456789}", "")) ; } - @Test public void unescape_unicode_32() { assertThrows(AtlasException.class, ()->test_unesc_unicode("\\u{000000000}", "")) ; } + @Test public void unescape_unicode_20() { test_unesc_unicode("\\u{41}", "A"); } + @Test public void unescape_unicode_21() { test_unesc_unicode("\\u{000000}", "\u0000"); } + @Test public void unescape_unicode_22() { test_unesc_unicode("\\u{1F0A1}", "🂡"); } + @Test public void unescape_unicode_23() { test_unesc_unicode("\\u{01F0A1}", "🂡"); } + @Test public void unescape_unicode_24() { test_unesc_unicode("\\u{10FFFF}", 0x10FFFF); } + + @Test public void unescape_unicode_30() { test_unesc_unicode_bad("\\u{}"); } + @Test public void unescape_unicode_31() { test_unesc_unicode_bad("\\u{123456789}"); } + @Test public void unescape_unicode_32() { test_unesc_unicode_bad("\\u{000000000}"); } // If the limit is 6 - @Test public void unescape_unicode_33() { assertThrows(AtlasException.class, ()->test_unesc_unicode("\\u{1234567}", "")) ; } - @Test public void unescape_unicode_34() { assertThrows(AtlasException.class, ()->test_unesc_unicode("\\u{0000000}", "")) ; } + @Test public void unescape_unicode_33() { test_unesc_unicode_bad("\\u{1234567}"); } + @Test public void unescape_unicode_34() { test_unesc_unicode_bad("\\u{0000000}"); } + + // Surrogates via \-u, \-U and \-u{} + @Test public void unescape_unicode_40() { test_unesc_unicode_bad("\\uD83C\\uDCA1"); } + @Test public void unescape_unicode_41() { test_unesc_unicode_bad("\\U0000D83C\\U0000DCA1"); } + @Test public void unescape_unicode_42() { test_unesc_unicode_bad("\\u{00D83C}\\u{00DCA1}"); } + + // Not valid surrogate pairs. + @Test public void unescape_unicode_50() { test_unesc_unicode_bad("\\uD83Cxyz"); } + @Test public void unescape_unicode_51() { test_unesc_unicode_bad("\\U0000D83Cxyz"); } + @Test public void unescape_unicode_52() { test_unesc_unicode_bad("\\u{00D83C}xyz"); } + + @Test public void unescape_unicode_60() { test_unesc_unicode_bad("abc\\uDCA1xyz"); } + @Test public void unescape_unicode_61() { test_unesc_unicode_bad("abc\\U0000DCA1xyz"); } + @Test public void unescape_unicode_62() { test_unesc_unicode_bad("abc\\u{00DCA1}xyz"); } // Escaped surrogates, good and bad. // Use java character escapes to put the surrogates into the java string. // 🂡 is U+D83C U+DCA1 - @Test public void escape_unicode_50() { test_escape("\uD83C\uDCA1", "🂡"); } - @Test public void escape_unicode_51() { test_escape("abc\uD83C\uDCA1xyz", "abc🂡xyz"); } + @Test public void escape_unicode_70() { test_escape_string("\uD83C\uDCA1", "🂡"); } + @Test public void escape_unicode_71() { test_escape_string("abc\uD83C\uDCA1xyz", "abc🂡xyz"); } // low, then high -> illegal - @Test public void escape_unicode_55() { test_escape("\uDCA1\uD83C", "\\uFFFD\\uFFFD"); } - @Test public void escape_unicode_56() { test_escape("\uDCA1\uD83C@", "\\uFFFD\\uFFFD@"); } + @Test public void escape_unicode_75() { test_escape_string("\uDCA1\uD83C", "\\uFFFD\\uFFFD"); } + @Test public void escape_unicode_76() { test_escape_string("\uDCA1\uD83C@", "\\uFFFD\\uFFFD@"); } // Lone surrogate - @Test public void escape_unicode_60() { test_escape("\uD83C", "\\uFFFD"); } - @Test public void escape_unicode_61() { test_escape("abc\uD83Cxyz", "abc\\uFFFDxyz"); } - @Test public void escape_unicode_62() { test_escape("\uDCA1", "\\uFFFD"); } - @Test public void escape_unicode_63() { test_escape("abc\uDCA1xyz", "abc\\uFFFDxyz"); } + @Test public void escape_unicode_80() { test_escape_string("\uD83C", "\\uFFFD"); } + @Test public void escape_unicode_81() { test_escape_string("abc\uD83Cxyz", "abc\\uFFFDxyz"); } + @Test public void escape_unicode_82() { test_escape_string("\uDCA1", "\\uFFFD"); } + @Test public void escape_unicode_83() { test_escape_string("abc\uDCA1xyz", "abc\\uFFFDxyz"); } + // Illegal then legal + @Test public void escape_unicode_84() { test_escape_string("\uDCA1\uD83C\uDCA1", "\\uFFFD🂡"); } + // low, then high/low -> one illegal, encode legal pair. - @Test public void escape_unicode_59() { test_escape("\uDCA1\uD83C\uDCA1", "\\uFFFD🂡"); } private void test_unesc_unicode(String input, String expected) { - String output = EscapeStr.unescapeUnicode(input) ; + String output = EscapeStr.unescapeUnicode(input); assertEquals(expected, output); } private void test_unesc_unicode(String input, int expected) { - String output = EscapeStr.unescapeUnicode(input) ; + String output = EscapeStr.unescapeUnicode(input); int codepoint = output.codePointAt(0); assertEquals(expected, codepoint); } + + private void test_unesc_unicode_bad(String input) { + assertThrows(AtlasException.class, ()->EscapeStr.unescapeUnicode(input)); + } }
