From 8b28c3843f20641e2eca54198e208745d5baaffa Mon Sep 17 00:00:00 2001 From: Mihai Nita Date: Wed, 13 Nov 2024 10:38:00 -0800 Subject: [PATCH] ICU-22927 Duplicate (back) the MF2 test data between icu4c and icu4j The C++ and Java implementations are done by two different people different companies. With different time constraints and availability. The spec is still not final (although it is close), and the implementations are still quite a bit behind. Sharing these test files slows down development, by forcing any C++ and Java changes to happen in the same time. There are other components that share test files that are not shared yet, even if they are more stable. So I don't know why we would force this on MF2 only. This is temporary, and the data files will be de-duplicated again at a later time, when the two implementations are more stable. That de-duplication will include other shared files, and in a slightly different structure (we have a doc and a discution on this topic). --- .../ibm/icu/dev/test/message2/TestUtils.java | 17 +- .../com/ibm/icu/dev/test/message2/README.txt | 43 ++ .../message2/alias-selector-annotations.json | 18 + .../test/message2/duplicate-declarations.json | 43 ++ .../dev/test/message2/icu-parser-tests.json | 57 ++ .../dev/test/message2/icu-test-functions.json | 221 ++++++ .../message2/icu-test-previous-release.json | 139 ++++ .../dev/test/message2/icu-test-selectors.json | 372 ++++++++++ .../invalid-number-literals-diagnostics.json | 25 + .../dev/test/message2/invalid-options.json | 47 ++ .../com/ibm/icu/dev/test/message2/markup.json | 15 + .../dev/test/message2/matches-whitespace.json | 34 + .../test/message2/more-data-model-errors.json | 177 +++++ .../icu/dev/test/message2/more-functions.json | 117 +++ .../icu/dev/test/message2/normalization.json | 67 ++ .../dev/test/message2/resolution-errors.json | 14 + .../icu/dev/test/message2/runtime-errors.json | 27 + .../test/message2/spec/data-model-errors.json | 185 +++++ .../test/message2/spec/functions/date.json | 46 ++ .../message2/spec/functions/datetime.json | 68 ++ .../test/message2/spec/functions/integer.json | 32 + .../test/message2/spec/functions/number.json | 407 ++++++++++ .../test/message2/spec/functions/string.json | 51 ++ .../test/message2/spec/functions/time.json | 43 ++ .../dev/test/message2/spec/syntax-errors.json | 180 +++++ .../icu/dev/test/message2/spec/syntax.json | 699 ++++++++++++++++++ .../syntax-errors-diagnostics-multiline.json | 50 ++ .../message2/syntax-errors-diagnostics.json | 347 +++++++++ .../message2/syntax-errors-end-of-input.json | 29 + .../test/message2/syntax-errors-reserved.json | 22 + .../test/message2/tricky-declarations.json | 19 + .../message2/unsupported-expressions.json | 77 ++ .../test/message2/unsupported-statements.json | 27 + .../icu/dev/test/message2/valid-tests.json | 156 ++++ 34 files changed, 3855 insertions(+), 16 deletions(-) create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/README.txt create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/alias-selector-annotations.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/duplicate-declarations.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-parser-tests.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-functions.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-previous-release.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-selectors.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/invalid-number-literals-diagnostics.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/invalid-options.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/markup.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/matches-whitespace.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/more-data-model-errors.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/more-functions.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/normalization.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/resolution-errors.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/runtime-errors.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/data-model-errors.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/date.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/datetime.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/integer.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/number.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/string.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/time.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/syntax-errors.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/syntax.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-diagnostics-multiline.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-diagnostics.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-end-of-input.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-reserved.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/tricky-declarations.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/unsupported-expressions.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/unsupported-statements.json create mode 100644 icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/valid-tests.json diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/TestUtils.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/TestUtils.java index 7bfcf65dc56d..71b9708dd7b7 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/TestUtils.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/TestUtils.java @@ -200,20 +200,5 @@ private static Path getTestFile(Class cls, String fileName) throws URISyntaxE URI getPath = cls.getClassLoader().getResource(packageName).toURI(); Path filePath = Paths.get(getPath); Path json = Paths.get(fileName); - // First, check the top level of the source directory, - // in case we're in a source tarball - Path icuTestdataInSourceDir = filePath.resolve("../../../../../../../../../../../testdata/message2/").normalize(); - Path icuTestdataDir = icuTestdataInSourceDir; - if (!Files.isDirectory(icuTestdataInSourceDir)) { - // If that doesn't exist, check one directory higher, in case we're - // in a checked-out repo - Path icuTestdataInRepo = Paths.get("../").resolve(icuTestdataInSourceDir).normalize(); - if (!Files.isDirectory(icuTestdataInRepo)) { - throw new java.io.FileNotFoundException("Test data directory does not exist: tried " - + icuTestdataInSourceDir + " and " - + icuTestdataInRepo); - } - icuTestdataDir = icuTestdataInSourceDir; - } - return icuTestdataDir.resolve(json); + return filePath.resolve(json); }} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/README.txt b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/README.txt new file mode 100644 index 000000000000..b803ebc0425e --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/README.txt @@ -0,0 +1,43 @@ +© 2024 and later: Unicode, Inc. and others. +License & terms of use: http://www.unicode.org/copyright.html + +The format of the JSON files in this directory and subdirectories +follow the test schema defined in the Conformance repository: + +https://github.com/unicode-org/conformance/blob/main/schema/message_fmt2/testgen_schema.json + +(as of https://github.com/unicode-org/conformance/pull/255 or later). + +# JSON notes + +In the "params" field, a date parameter can be expressed as: +{ "date": n } +where n is a number representing a Unix timestamp. + +In the "params" field, a decimal string parameter can be expressed as: +{ "decimal": s } +where s is a string. + +Optional fields, "ignoreJava" and "ignoreCpp" can be used +for tests currently expected to fail in the respective language. +The field may have any value; if it's +present, the test is ignored. (The value can be a comment explaining +why it's expected to fail.) + +Tests in the `spec/` subdirectory are taken from https://github.com/unicode-org/message-format-wg/blob/main/test . +If the contents change upstream, then the corresponding tests in CLDR +need to be updated (also see https://unicode-org.atlassian.net/browse/ICU-22812 ). + +## ICU4J only + +The `cleanSrc` fields is used to represent normalized input (ICU4C has its +own function for normalizing input). + +## ICU4C only + +Additional "char" and "line" fields may be present with integer values, +used for tests expected to trigger a syntax error. +If present, "char" reflects the expected character offset and "line" +reflects the expected line number in the parse error. +The files with "diagnostics" in the name have these fields filled in. + diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/alias-selector-annotations.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/alias-selector-annotations.json new file mode 100644 index 000000000000..c064d40d4c82 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/alias-selector-annotations.json @@ -0,0 +1,18 @@ +{ + "scenario": "Selector annotations", + "description": "Tests for indirectly annotated selectors", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": ".local $one = {|The one| :string}\n .match {$one}\n 1 {{Value is one}}\n * {{Value is not one}}", + "exp": "Value is not one" + }, + { + "src": ".local $one = {|The one| :string}\n .local $two = {$one}\n .match {$two}\n 1 {{Value is one}}\n * {{Value is not one}}", + "exp": "Value is not one" + } + ] +} + diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/duplicate-declarations.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/duplicate-declarations.json new file mode 100644 index 000000000000..cd3acc1576d3 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/duplicate-declarations.json @@ -0,0 +1,43 @@ +{ + "scenario": "Duplicate declaration errors", + "description": "Tests that should trigger a duplicate declaration error", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + "tests": [ + { + "src": ".local $foo = {$foo} .local $foo = {42} {{bar {$foo}}}", + "params": [{ "name": "foo", "value": "foo" }], + "exp": "bar 42" + }, + { + "src": ".local $foo = {42} .local $foo = {42} {{bar {$foo}}}", + "params": [{ "name": "foo", "value": "foo" }], + "exp": "bar 42" + }, + { + "src": ".local $foo = {:unknown} .local $foo = {42} {{bar {$foo}}}", + "params": [{ "name": "foo", "value": "foo" }], + "exp": "bar 42" + }, + { + "src": ".local $x = {42} .local $y = {$x} .local $x = {13} {{{$x} {$y}}}", + "exp": "13 42" + }, + { + "src": ".local $foo = {$foo} {{bar {$foo}}}", + "params": [{ "name": "foo", "value": "foo" }], + "exp": "bar foo" + }, + { + "src": ".local $foo = {$bar} .local $bar = {$baz} {{bar {$foo}}}", + "params": [{ "name": "baz", "value": "foo" }], + "exp": "bar {$bar}" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-parser-tests.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-parser-tests.json new file mode 100644 index 000000000000..0eb54b266514 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-parser-tests.json @@ -0,0 +1,57 @@ +{ + "scenario": "Valid tests", + "description": "Additional valid tests", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { "src": "" }, + { "src": "Hello" }, + { "src": "Hello world!" }, + { "src": "Hello \t \n \r \\{ world!" }, + { "src": "Hello world {:datetime}" }, + { "src": "Hello world {foo}" }, + { "src": "Hello {0} world" }, + { "src": "Hello {123} world" }, + { "src": "Hello {-123} world" }, + { "src": "Hello {3.1416} world" }, + { "src": "Hello {-3.1416} world" }, + { "src": "Hello {123E+2} world" }, + { "src": "Hello {123E-2} world" }, + { "src": "Hello {123.456E+2} world" }, + { "src": "Hello {123.456E-2} world" }, + { "src": "Hello {-123.456E+2} world" }, + { "src": "Hello {-123.456E-2} world" }, + { "src": "Hello {-123E+2} world" }, + { "src": "Hello {-123E-2} world" }, + { "src": "Hello world {$exp}" }, + { "src": "Hello world {$exp :datetime}" }, + { "src": "Hello world {|2024-02-27| :datetime}" }, + { "src": "Hello world {$exp :datetime style=long} and more" }, + { "src": "Hello world {$exp :function number=1234} and more" }, + { "src": "Hello world {$exp :function unquoted=left } and more" }, + { "src": "Hello world {$exp :function quoted=|Something| } and more" }, + { "src": "Hello world {$exp :function quoted=|Something with spaces| } and more" }, + { "src": "Hello world {$exp :function quoted=|Something with \\| spaces and \\| escapes| } and more" }, + { "src": "Hello world {$exp :function number=1234 unquoted=left quoted=|Something|}" }, + { "src": "Hello world {$exp :function number=1234 unquoted=left quoted=|Something longer|}" }, + { "src": "Hello world {$exp :function number=1234 unquoted=left quoted=|Something \\| longer|}" }, + { "src": "Hello world {$exp}" }, + { "src": "Hello world {$exp @attr}" }, + { "src": "Hello world {$exp @valid @attr=a @attrb=123 @atrn=|foo bar|}" }, + { "src": "Hello world {$exp :date @valid @attr=aaaa @attrb=123 @atrn=|foo bar|}" }, + { "src": "Hello world {$exp :date year=numeric month=long day=numeric int=12 @valid @attr=a @attrb=123 @atrn=|foo bar|}" }, + { "src": "{{.starting with dot is OK here}}" }, + { "src": "{{Some string pattern \\}, with {$foo} and {$exp :date style=long}!}}" }, + { "src": ".input {$pi :number} {{}}" }, + { "src": ".input {$exp :date} {{}}" }, + { "src": ".local $foo = {$exp} {{}}" }, + { "src": ".local $foo = {$exp :date} {{}}" }, + { "src": ".local $foo = {$exp :date year=numeric month=long day=numeric} {{}}" }, + { "src": ".local $bar = {$foo :date month=medium} {{}}" }, + { "src": ".input {$a :date} .local $exp = {$a :date style=full} {{Your card expires on {$exp}!}}" }, + { "src": ".input {$a :date} .local $b = {$a :date year=numeric month=long day=numeric} .local $c = {$b :date month=medium} {{}}" }, + { "src": ".input {$x :number} {{_}}" }, + { "src": ".local $foo = {|1|} {{_}}" } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-functions.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-functions.json new file mode 100644 index 000000000000..a97446addf0e --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-functions.json @@ -0,0 +1,221 @@ +{ + "scenario": "Function tests", + "description": "Tests for ICU-specific formatting behavior.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": "Expires on {$exp}", + "exp": "Expires on 8/3/24, 9:43 PM", + "comment": "Modified from ICU4J copy to add params (likewise with the other date/time tests); 1722746637000 is 2024-08-03 21:43:57 PDT", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime}", + "exp": "Expires on 8/3/24, 9:43 PM", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime icu:skeleton=yMMMMdjmsSSEE}", + "exp": "Expires on Sat, August 3, 2024 at 9:43:57.00 PM", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }], + "ignoreCpp": "ICU-22754 Skeleton option not implemented yet" + }, + { + "src": "Expires on {$exp :datetime dateStyle=full}", + "exp": "Expires on Saturday, August 3, 2024", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime dateStyle=long}", + "exp": "Expires on August 3, 2024", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime dateStyle=medium}", + "exp": "Expires on Aug 3, 2024", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime timeStyle=long}", + "exp": "Expires on 9:43:57 PM PDT", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime timeStyle=medium}", + "exp": "Expires on 9:43:57 PM", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime timeStyle=short}", + "exp": "Expires on 9:43 PM", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime dateStyle=full timeStyle=medium}", + "exp": "Expires on Saturday, August 3, 2024 at 9:43:57 PM", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime year=numeric month=long}", + "exp": "Expires on August 2024", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {$exp :datetime year=numeric month=medium day=numeric weekday=long hour=numeric minute=numeric}", + "exp": "Expires on 3 Saturday 2024, 9:43 PM", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "comment": "Make sure we ignore date / time fields if needed", + "src": "Expires on {$exp :date year=numeric month=medium day=numeric weekday=long hour=numeric minute=numeric}", + "exp": "Expires on 3 Saturday 2024", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }], + "ignoreCpp": "ICU-22754 ICU4C doesn't accept field options for `:date` or `:time` -- see spec" + }, + { + "comment": "Make sure we ignore date / time fields if needed", + "src": "Expires at {$exp :time year=numeric month=medium day=numeric weekday=long hour=numeric minute=numeric}", + "exp": "Expires at 9:43 PM", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }], + "ignoreCpp": "ICU-22754 ICU4C doesn't accept field options for `:date` or `:time` -- see spec" + }, + { + "comment": "Make sure we ignore date / time fields if needed", + "src": "Expires at {$exp :time style=long dateStyle=full timeStyle=medium}", + "exp": "Expires at 9:43:57 PM PDT", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "comment": "Make sure we ignore date / time fields if needed", + "src": "Expires on {$exp :date style=long dateStyle=full timeStyle=medium}", + "exp": "Expires on August 3, 2024", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Expires on {|2025-02-27| :datetime dateStyle=full}", + "exp": "Expires on Thursday, February 27, 2025" + }, + { + "src": "Expires at {|2024-07-02T19:23:45| :datetime timeStyle=full}", + "exp": "Expires at 7:23:45 PM Pacific Daylight Time" + }, + { + "src": "Expires at {|2024-07-02T19:23:45.123| :datetime timeStyle=full}", + "exp": "Expires at 7:23:45 PM Pacific Daylight Time" + }, + { + "src": "Expires on {|2025-02-27T19:23:45| :datetime dateStyle=full}", + "exp": "Expires on Thursday, February 27, 2025" + }, + { + "src": "Expires at {|2024-07-02T19:23:45Z| :datetime timeStyle=long}", + "exp": "Expires at 7:23:45 PM GMT", + "ignoreCpp": "ICU-22754 Time zones not working yet (bug)" + }, + { + "src": "Expires at {|2024-07-02T19:23:45+03:30| :datetime timeStyle=full}", + "exp": "Expires at 7:23:45 PM GMT+03:30", + "ignoreCpp": "ICU-22754 Time zones not working yet (bug)" + }, + { + "comment": "Horibly long, but I really wanted to test multiple declarations with overrides, and you can't join strings in JSON", + "src": [ + ".input {$exp :datetime timeStyle=short}\n", + ".input {$user :string}\n", + ".local $longExp = {$exp :datetime dateStyle=long}\n", + ".local $zooExp = {$exp :datetime dateStyle=short timeStyle=$tsOver}\n", + "{{Hello John, you want '{$exp}', '{$longExp}', or '{$zooExp}' or even '{$exp :datetime dateStyle=full}'?}}" + ], + "exp": "Hello John, you want '9:43 PM', 'August 3, 2024 at 9:43 PM', or '8/3/24, 9:43:57 PM Pacific Daylight Time' or even 'Saturday, August 3, 2024 at 9:43 PM'?", + "params": [{"name": "exp", "value": { "date": 1722746637000 }}, + {"name": "user", "value": "John"}, + {"name": "tsOver", "value": "full" }], + "ignoreCpp": "ICU-22754 ICU4C doesn't implement this kind of function composition yet. See https://github.com/unicode-org/message-format-wg/issues/515" + }, + { + "src": [ + ".input {$exp :datetime year=numeric month=numeric day=|2-digit|}\n", + ".local $longExp = {$exp :datetime month=long weekday=long}\n", + "{{Expires on '{$exp}' ('{$longExp}').}}" + ], + "exp": "Expires on '8/03/2024' ('Saturday, August 03, 2024').", + "params": [{ "name": "exp", "value": { "date": 1722746637000 } }] + }, + { + "src": "Format {$val} number", + "params": [{ "name": "val", "value": 31 }], + "exp": "Format 31 number" + }, + { + "src": "Format {123456789.9876} number", + "locale": "en-IN", + "exp": "Format 123456789.9876 number", + "comment": "Number literals are not formatted as numbers by default" + }, + { + "src": "Format {|3.1416|} number", + "locale": "ar-AR-u-nu-latn", + "exp": "Format 3.1416 number" + }, + { + "src": "Format {|3.1416|} number", + "locale": "ar-AR-u-nu-arab", + "exp": "Format 3.1416 number", + "comment": "Number literals are not formatted as numbers by default" + }, + { + "src": "Format {3.1415926 :number}", + "exp": "Format 3.141593" + }, + { + "src": "Format {3.1415926 :number maximumFractionDigits=4}", + "exp": "Format 3.1416" + }, + { + "src": "Format {3 :number minimumFractionDigits=2}", + "exp": "Format 3.00" + }, + { + "src": "Format {3.2 :number minimumFractionDigits=2}", + "exp": "Format 3.20" + }, + { + "src": "Format {123456789.97531 :number maximumSignificantDigits=4}", + "exp": "Format 123,500,000" + }, + { + "src": "Format {3.1415926 :number}", + "exp": "Format 3.141593" + }, + { + "src": "Numbering system {123456 :number numberingSystem=deva}", + "exp": "Numbering system १२३,४५६" + }, + { + "src": "Percent {0.1416 :number style=percent}", + "exp": "Percent 14.16%" + }, + { + "src": "Scientific {123456789.97531 :number notation=scientific}", + "exp": "Scientific 1.234568E8" + }, + { + "src": "Engineering {123456789.97531 :number notation=engineering}", + "exp": "Engineering 123.45679E6" + }, + { + "src": "Compact {123456789.97531 :number notation=compact}", + "exp": "Compact 123M" + }, + { + "src": "Compact {123456789.97531 :number notation=compact compactDisplay=long}", + "exp": "Compact 123 million" + }, + { + "src": "Compact {123456789.97531 :number notation=compact compactDisplay=short}", + "exp": "Compact 123M" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-previous-release.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-previous-release.json new file mode 100644 index 000000000000..0a1e27dff6e2 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-previous-release.json @@ -0,0 +1,139 @@ +{ + "scenario": "Tests from original ICU4J release", + "description": "Tests taken from the September 2022 MF2 ICU4J release", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": "hello {|4.2| :number}", + "exp": "hello 4.2" + }, + { + "locale": "ar-EG", + "src": "hello {|4.2| :number}", + "exp": "hello \u0664\u066B\u0662" + }, + { + "comment": "This is not an error! foo is not used before the local declaration, so the local declaration of $foo shadows the input variable.", + "src": ".local $foo = {bar} {{bar {$foo}}}", + "exp": "bar bar", + "params": [{ "name": "foo", "value": "foo" }] + }, + { + "src": ".local $foo = {$bar :number} {{bar {$foo}}}", + "params": [{ "name": "bar", "value": 4.2 }], + "exp": "bar 4.2" + }, + { + "src": ".local $bar = {$baz} .local $foo = {$bar} {{bar {$foo}}}", + "params": [{ "name": "baz", "value": "foo" }], + "exp": "bar foo" + }, + { + "src": ".match {$foo :number} 1 {{one}} * {{other}}", + "params": [{ "name": "foo", "value": "1" }], + "exp": "one", + "ignoreJava": "See ICU-22809" + }, + { + "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "params": [{ "name": "foo", "value": "1" }], + "exp": "one" + }, + { + "src": ".match {$foo :number} 1 {{one}} * {{other}}", + "params": [{ "name": "foo", "value": 1 }], + "exp": "one" + }, + { + "ignoreJava": "Can't pass null in a map", + "ignoreCpp": "Same as Java", + "src": ".match {$foo} 1 {{one}} * {{other}}", + "params": [{ "name": "foo", "value": null }], + "exp": "other" + }, + { + "src": ".match {$foo :number} 1 {{one}} * {{other}}", + "exp": "other", + "expErrors": [{ "type": "unresolved-variable" }] + }, + { + "src": ".local $foo = {$bar} .match {$foo :number} one {{one}} * {{other}}", + "params": [{ "name": "bar", "value": 1 }], + "exp": "one" + }, + { + "src": ".local $foo = {$bar} .match {$foo :number} one {{one}} * {{other}}", + "params": [{ "name": "bar", "value": 2 }], + "exp": "other" + }, + { + "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", + "params": [{ "name": "foo", "value": 1 }, {"name": "none", "value": "" }], + "exp": "one" + }, + { + "src": ".local $bar = {$none :number} .match {$foo :string} one {{one}} * {{{$bar}}}", + "params": [{ "name": "foo", "value": 2 }], + "exp": "{$none}", + "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "{{#tag}}", + "exp": "#tag" + }, + { + "src": "{#tag}content{/tag}", + "exp": "content" + }, + { + "src": "{#tag foo=foo bar=$bar}", + "params": [{ "name": "bar", "value": "b a r" }], + "exp": "" + }, + { + "src": "bad {#markup/} test", + "exp": "bad test" + }, + { + "src": "no braces {$foo}", + "params": [{ "name": "foo", "value": 2 }], + "exp": "no braces 2" + }, + { + "src": "empty { }", + "exp": "empty ", + "expErrors": [{ "type": "syntax-error" }], + "ignoreCpp": "Fallback is unclear. See https://github.com/unicode-org/message-format-wg/issues/703" + }, + { + "src": "bad {:}", + "exp": "bad {:}", + "expErrors": [{ "type": "syntax-error" }, { "type": "unknown-function" }] + }, + { + "src": "{bad {$placeholder option}}", + "exp": "bad {$placeholder}", + "expErrors": [{ "type": "syntax-error"}, { "type": "unresolved-variable" }], + "ignoreCpp": "Fallback is unclear. See https://github.com/unicode-org/message-format-wg/issues/703" + }, + { + "src": ".match {|foo| :string} *{{foo}}", + "exp": "foo" + }, + { + "src": ".match {$foo :string} * * {{foo}}", + "exp": "foo", + "expErrors": [{ "type": "variant-key-mismatch" }, { "type": "unresolved-variable" }], + "ignoreCpp": "Fallback is unclear. See https://github.com/unicode-org/message-format-wg/issues/735" + }, + { + "src": ".match {$foo :string} {$bar :string} * {{foo}}", + "exp": "foo", + "expErrors": [{ "type": "variant-key-mismatch" }, { "type": "unresolved-variable" }], + "ignoreCpp": "Fallback is unclear. See https://github.com/unicode-org/message-format-wg/issues/735" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-selectors.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-selectors.json new file mode 100644 index 000000000000..102bdfd88f50 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/icu-test-selectors.json @@ -0,0 +1,372 @@ +{ + "scenario": "Match tests", + "description": "Tests for various match constructs", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "comment": "Testing simple plural", + "src": [ + ".match {$count :number}\n", + "one {{{$count} file}}\n", + " * {{{$count} files}}" + ], + "params": [{"name": "count", "value": 0}], + "exp": "0 files" + }, + { + "comment": "Testing simple plural", + "src": [ + ".match {$count :number}\n", + "one {{{$count} file}}\n", + " * {{{$count} files}}" + ], + "params": [{"name": "count", "value": 1}], + "exp": "1 file" + }, + { + "comment": "Testing simple plural", + "src": [ + ".match {$count :number}\n", + "one {{{$count} file}}\n", + " * {{{$count} files}}" + ], + "params": [{"name": "count", "value": 3}], + "exp": "3 files" + }, + { + "comment": "Testing simple plural", + "locale": "fr", + "src": [ + ".match {$count :number}\n", + "one {{{$count} file}}\n", + " * {{{$count} files}}" + ], + "params": [{"name": "count", "value": 0}], + "exp": "0 file" + }, + { + "comment": "Testing simple plural", + "locale": "fr", + "src": [ + ".match {$count :number}\n", + "one {{{$count} file}}\n", + " * {{{$count} files}}" + ], + "params": [{"name": "count", "value": 1}], + "exp": "1 file" + }, + { + "comment": "Testing simple plural", + "locale": "fr", + "src": [ + ".match {$count :number}\n", + "one {{{$count} file}}\n", + " * {{{$count} files}}" + ], + "params": [{"name": "count", "value": 3}], + "exp": "3 files" + }, + { + "comment": "Testing simple plural, but swap variant order", + "src": [ + ".match {$count :number}\n", + " * {{You deleted {$count} files}}\n", + "one {{You deleted {$count} file}}" + ], + "params": [{"name": "count", "value": 1}], + "exp": "You deleted 1 file" + }, + { + "comment": "Testing simple plural, but swap variant order", + "src": [ + ".match {$count :number}\n", + " * {{You deleted {$count} files}}\n", + "one {{You deleted {$count} file}}" + ], + "params": [{"name": "count", "value": 3}], + "exp": "You deleted 3 files" + }, + { + "comment": "Ordinal, with mixed order and exact matches", + "src": [ + ".match {$place :number select=ordinal}\n", + "* {{You finished in the {$place}th place}}\n", + "two {{You finished in the {$place}nd place}}\n", + "one {{You finished in the {$place}st place}}\n", + "1 {{You got the gold medal}}\n", + "2 {{You got the silver medal}}\n", + "3 {{You got the bronze medal}}\n", + "few {{You finished in the {$place}rd place}}" + ], + "params": [{"name": "place", "value": 1}], + "exp": "You got the gold medal" + }, + { + "comment": "Ordinal, with mixed order and exact matches", + "src": [ + ".match {$place :number select=ordinal}\n", + "* {{You finished in the {$place}th place}}\n", + "two {{You finished in the {$place}nd place}}\n", + "one {{You finished in the {$place}st place}}\n", + "1 {{You got the gold medal}}\n", + "2 {{You got the silver medal}}\n", + "3 {{You got the bronze medal}}\n", + "few {{You finished in the {$place}rd place}}" + ], + "params": [{"name": "place", "value": 2}], + "exp": "You got the silver medal" + }, + { + "comment": "Ordinal, with mixed order and exact matches", + "src": [ + ".match {$place :number select=ordinal}\n", + "* {{You finished in the {$place}th place}}\n", + "two {{You finished in the {$place}nd place}}\n", + "one {{You finished in the {$place}st place}}\n", + "1 {{You got the gold medal}}\n", + "2 {{You got the silver medal}}\n", + "3 {{You got the bronze medal}}\n", + "few {{You finished in the {$place}rd place}}" + ], + "params": [{"name": "place", "value": 3}], + "exp": "You got the bronze medal" + }, + { + "comment": "Ordinal, with mixed order and exact matches", + "src": [ + ".match {$place :number select=ordinal}\n", + "* {{You finished in the {$place}th place}}\n", + "two {{You finished in the {$place}nd place}}\n", + "one {{You finished in the {$place}st place}}\n", + "1 {{You got the gold medal}}\n", + "2 {{You got the silver medal}}\n", + "3 {{You got the bronze medal}}\n", + "few {{You finished in the {$place}rd place}}" + ], + "params": [{"name": "place", "value": 7}], + "exp": "You finished in the 7th place" + }, + { + "comment": "Ordinal, with mixed order and exact matches", + "src": [ + ".match {$place :number select=ordinal}\n", + "* {{You finished in the {$place}th place}}\n", + "two {{You finished in the {$place}nd place}}\n", + "one {{You finished in the {$place}st place}}\n", + "1 {{You got the gold medal}}\n", + "2 {{You got the silver medal}}\n", + "3 {{You got the bronze medal}}\n", + "few {{You finished in the {$place}rd place}}" + ], + "params": [{"name": "place", "value": 21}], + "exp": "You finished in the 21st place" + }, + { + "comment": "Ordinal, with mixed order and exact matches", + "src": [ + ".match {$place :number select=ordinal}\n", + "* {{You finished in the {$place}th place}}\n", + "two {{You finished in the {$place}nd place}}\n", + "one {{You finished in the {$place}st place}}\n", + "1 {{You got the gold medal}}\n", + "2 {{You got the silver medal}}\n", + "3 {{You got the bronze medal}}\n", + "few {{You finished in the {$place}rd place}}" + ], + "params": [{"name": "place", "value": 22}], + "exp": "You finished in the 22nd place" + }, + { + "comment": "Ordinal, with mixed order and exact matches", + "src": [ + ".match {$place :number select=ordinal}\n", + "* {{You finished in the {$place}th place}}\n", + "two {{You finished in the {$place}nd place}}\n", + "one {{You finished in the {$place}st place}}\n", + "1 {{You got the gold medal}}\n", + "2 {{You got the silver medal}}\n", + "3 {{You got the bronze medal}}\n", + "few {{You finished in the {$place}rd place}}" + ], + "params": [{"name": "place", "value": 23}], + "exp": "You finished in the 23rd place" + }, + { + "comment": "Ordinal, with mixed order and exact matches", + "src": [ + ".match {$place :number select=ordinal}\n", + "* {{You finished in the {$place}th place}}\n", + "two {{You finished in the {$place}nd place}}\n", + "one {{You finished in the {$place}st place}}\n", + "1 {{You got the gold medal}}\n", + "2 {{You got the silver medal}}\n", + "3 {{You got the bronze medal}}\n", + "few {{You finished in the {$place}rd place}}" + ], + "params": [{"name": "place", "value": 28}], + "exp": "You finished in the 28th place" + }, + { + "comment": "Plural combinations, mixed order", + "src": [ + ".match {$fileCount :number} {$folderCount :number}\n", + " * * {{You found {$fileCount} files in {$folderCount} folders}}\n", + " one one {{You found {$fileCount} file in {$folderCount} folder}}\n", + " one * {{You found {$fileCount} file in {$folderCount} folders}}\n", + " * one {{You found {$fileCount} files in {$folderCount} folder}}" + ], + "params": [{"name": "fileCount", "value": 1}, + {"name": "folderCount", "value": 1}], + "exp": "You found 1 file in 1 folder" + }, + { + "comment": "Plural combinations, mixed order", + "src": [ + ".match {$fileCount :number} {$folderCount :number}\n", + " * * {{You found {$fileCount} files in {$folderCount} folders}}\n", + " one one {{You found {$fileCount} file in {$folderCount} folder}}\n", + " one * {{You found {$fileCount} file in {$folderCount} folders}}\n", + " * one {{You found {$fileCount} files in {$folderCount} folder}}" + ], + "params": [{"name": "fileCount", "value": 1}, + {"name": "folderCount", "value": 5}], + "exp": "You found 1 file in 5 folders" + }, + { + "comment": "Plural combinations, mixed order", + "src": [ + ".match {$fileCount :number} {$folderCount :number}\n", + " * * {{You found {$fileCount} files in {$folderCount} folders}}\n", + " one one {{You found {$fileCount} file in {$folderCount} folder}}\n", + " one * {{You found {$fileCount} file in {$folderCount} folders}}\n", + " * one {{You found {$fileCount} files in {$folderCount} folder}}" + ], + "params": [{"name": "fileCount", "value": 7}, + {"name": "folderCount", "value": 1}], + "exp": "You found 7 files in 1 folder" + }, + { + "comment": "Plural combinations, mixed order", + "src": [ + ".match {$fileCount :number} {$folderCount :number}\n", + " * * {{You found {$fileCount} files in {$folderCount} folders}}\n", + " one one {{You found {$fileCount} file in {$folderCount} folder}}\n", + " one * {{You found {$fileCount} file in {$folderCount} folders}}\n", + " * one {{You found {$fileCount} files in {$folderCount} folder}}" + ], + "params": [{"name": "fileCount", "value": 7}, + {"name": "folderCount", "value": 3}], + "exp": "You found 7 files in 3 folders" + }, + { + "comment": "Test that the selection honors the formatting option (`1.00 dollars`)", + "src": [ + ".local $c = {$price :number minimumFractionDigits=$minF}\n", + ".match {$c}\n", + " one {{{$c} dollar}}\n", + " * {{{$c} dollars}}" + ], + "params": [{ "name": "price", "value": 1 }, + { "name": "minF", "value": 0 }], + "exp": "1 dollar" + }, + { + "comment": "Test that the selection honors the formatting option (`1.00 dollars`)", + "src": [ + ".local $c = {$price :number minimumFractionDigits=$minF}\n", + ".match {$c}\n", + " one {{{$c} dollar}}\n", + " * {{{$c} dollars}}" + ], + "params": [{ "name": "price", "value": 1}, + { "name": "minF", "value": 2 }], + "exp": "1.00 dollars" + }, + { + "comment": "Test that the selection honors the formatting option (`1.00 dollars`)", + "src": [ + ".local $c = {$price :number maximumFractionDigits=$maxF}\n", + ".match {$c}\n", + " one {{{$c} dollar}}\n", + " * {{{$c} dollars}}" + ], + "params": [{ "name": "price", "value": 1.25}, + { "name": "maxF", "value": 0 }], + "exp": "1 dollar" + }, + { + "comment": "Test that the selection honors the formatting option (`1.00 dollars`)", + "src": [ + ".local $c = {$price :number maximumFractionDigits=$maxF}\n", + ".match {$c}\n", + " one {{{$c} dollar}}\n", + " * {{{$c} dollars}}" + ], + "params": [{ "name": "price", "value": 1.25}, + { "name": "maxF", "value": 2 }], + "exp": "1.25 dollars" + }, + { + "comment": "Test that the selection honors the `:integer` over options", + "src": [ + ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", + ".match {$c}\n", + " one {{{$c} dollar}}\n", + " * {{{$c} dollars}}" + ], + "params": [{ "name": "price", "value": 1}, + { "name": "maxF", "value": 0 }], + "exp": "1 dollar" + }, + { + "comment": "Test that the selection honors the `:integer` over options", + "src": [ + ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", + ".match {$c}\n", + " one {{{$c} dollar}}\n", + " * {{{$c} dollars}}" + ], + "params": [{ "name": "price", "value": 1}, + { "name": "maxF", "value": 2 }], + "exp": "1 dollar" + }, + { + "comment": "Test that the selection honors the `:integer` over options", + "src": [ + ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", + ".match {$c}\n", + " one {{{$c} dollar}}\n", + " * {{{$c} dollars}}" + ], + "params": [{ "name": "price", "value": 1.25}, + { "name": "maxF", "value": 0 }], + "exp": "1 dollar" + }, + { + "comment": "Test that the selection honors the `:integer` over options", + "src": [ + ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", + ".match {$c}\n", + " one {{{$c} dollar}}\n", + " * {{{$c} dollars}}" + ], + "params": [{ "name": "price", "value": 1.25 }, + { "name": "maxF", "value": 2 }], + "exp": "1 dollar" + }, + { + "comment": "Test that the selection honors the `:integer` over options", + "src": [ + ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", + ".match {$c}\n", + " one {{{$c} dollar}}\n", + " * {{{$c} dollars}}" + ], + "params": [{ "name": "price", "value": 4.12345 }, + { "name": "maxF", "value": 4 }], + "exp": "4 dollars" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/invalid-number-literals-diagnostics.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/invalid-number-literals-diagnostics.json new file mode 100644 index 000000000000..d35c16b23386 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/invalid-number-literals-diagnostics.json @@ -0,0 +1,25 @@ +{ + "scenario": "Number literal syntax errors", + "description": "Syntax errors with number literals; for ICU4C, the character offset in the parse error is checked", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "syntax-error" + } + ] + }, + "tests": [ + { "src": "{00}", "char": 2}, + { "src": "{042}", "char": 2}, + { "src": "{1.}", "char": 3}, + { "src": "{1e}", "char": 3}, + { "src": "{1E}", "char": 3}, + { "src": "{1.e}", "char": 3}, + { "src": "{1.2e}", "char": 5}, + { "src": "{1.e3}", "char": 3}, + { "src": "{1e+}", "char": 4}, + { "src": "{1e-}", "char": 4}, + { "src": "{1.0e2.0}", "char": 6} + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/invalid-options.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/invalid-options.json new file mode 100644 index 000000000000..698583cd5578 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/invalid-options.json @@ -0,0 +1,47 @@ +{ + "scenario": "Bad options for built-in functions", + "description": "Tests for the bad-option error; only run in ICU4C for now", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "bad-option" + } + ] + }, + "tests": [ + { "comment": "Neither impl validates options right now; see https://github.com/unicode-org/message-format-wg/issues/738", + "src": ".local $foo = {1 :number minimumIntegerDigits=-1} {{bar {$foo}}}", + "ignoreCpp": "ICU4C doesn't validate options", + "ignoreJava": "ICU4J doesn't validate options" + }, + { "src": ".local $foo = {1 :number minimumIntegerDigits=foo} {{bar {$foo}}}", + "ignoreCpp": "ICU4C doesn't validate options", + "ignoreJava": "ICU4J doesn't validate options" + }, + { "src": ".local $foo = {1 :number minimumFractionDigits=foo} {{bar {$foo}}}", + "ignoreCpp": "ICU4C doesn't validate options", + "ignoreJava": "ICU4J doesn't validate options" + }, + { "src": ".local $foo = {1 :number maximumFractionDigits=foo} {{bar {$foo}}}", + "ignoreCpp": "ICU4C doesn't validate options", + "ignoreJava": "ICU4J doesn't validate options" + }, + { "src": ".local $foo = {1 :number minimumSignificantDigits=foo} {{bar {$foo}}}", + "ignoreCpp": "ICU4C doesn't validate options", + "ignoreJava": "ICU4J doesn't validate options" + }, + { "src": ".local $foo = {1 :number maximumSignificantDigits=foo} {{bar {$foo}}}", + "ignoreCpp": "ICU4C doesn't validate options", + "ignoreJava": "ICU4J doesn't validate options" + }, + { "src": ".local $foo = {1 :integer minimumIntegerDigits=foo} {{bar {$foo}}}", + "ignoreCpp": "ICU4C doesn't validate options", + "ignoreJava": "ICU4J doesn't validate options" + }, + { "src": ".local $foo = {1 :integer maximumSignificantDigits=foo} {{bar {$foo}}}", + "ignoreCpp": "ICU4C doesn't validate options", + "ignoreJava": "ICU4J doesn't validate options" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/markup.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/markup.json new file mode 100644 index 000000000000..29b4408e2c4a --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/markup.json @@ -0,0 +1,15 @@ +{ + "scenario": "Markup", + "description": "Tests for valid markup strings", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { "src": "{#tag/}", "exp": "" }, + { "src": "{/tag}", "exp": "" }, + { "src": "{#tag}content{/tag}", "exp": "content" }, + { "src": "{#tag foo=|foo| bar=$bar}", + "params": [{ "name": "bar", "value": "b a r" }], + "exp": "" } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/matches-whitespace.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/matches-whitespace.json new file mode 100644 index 000000000000..a0af4c4d143e --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/matches-whitespace.json @@ -0,0 +1,34 @@ +{ + "scenario": "Matches with whitespace", + "description": "Tests for valid match constructs with whitespace in various places", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { "src": ".match {one :string} {bar :string} one * {{one}} * * {{other}}", + "exp": "one" }, + { "src": ".match {foo :string} {bar :string}one * {{one}} * * {{other}}", + "exp": "other" + }, + { "src": ".match {foo :string}{bar :string} one * {{one}} * * {{other}}", + "exp": "other" + }, + { "src": ".match {one :string}{bar :string}one * {{one}} * * {{other}}", + "exp": "one" + }, + { "src": ".match{foo :string} {bar :string} one * {{one}} * * {{other}}", + "exp": "other" + }, + { "src": ".match {foo :string} {bar :string} one * {{one}}* * {{other}}", + "exp": "other" }, + { "src": ".match {foo :string} {bar :string}one * {{one}}* * {{other}}", + "exp": "other" + }, + { "src": ".match {foo :string} {bar :string} one *{{one}} * * {{foo}}", + "exp": "foo" + }, + { "src": ".match {foo :string} {bar :string} one * {{one}} * * {{foo}}", + "exp": "foo" } + ] +} + diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/more-data-model-errors.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/more-data-model-errors.json new file mode 100644 index 000000000000..32744083af1c --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/more-data-model-errors.json @@ -0,0 +1,177 @@ +{ + "scenario": "Additional data model errors", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": ".match {$foo :number} {$bar :number} one{{one}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + + { + "src": ".match {$foo :number} {$bar :number} one {{one}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + + { + "src": ".match {$foo :number} {$bar :number} one {{one}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + + { + "src": ".match {$foo :number} * * {{foo}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + + { + "src": ".match {$one :number}\n 1 2 {{Too many}}\n * {{Otherwise}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + + { + "src": ".match {$one :number} {$two :number}\n 1 2 {{Two keys}}\n * {{Missing a key}}\n * * {{Otherwise}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + + { + "src": ".match {$foo :x} {$bar :x} * {{foo}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + + { + "src": ".match {$one :number}\n 1 {{Value is one}}\n 2 {{Value is two}}", + "expErrors": [ + { + "type": "missing-fallback-variant" + } + ] + }, + + { + "src": ".match {$one :number} {$two :number}\n 1 * {{First is one}}\n * 1 {{Second is one}}", + "expErrors": [ + { + "type": "missing-fallback-variant" + } + ] + }, + + { + "src": ".match {$one}\n 1 {{Value is one}}\n * {{Value is not one}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + + { + "src": ".local $one = {|The one|}\n .match {$one}\n 1 {{Value is one}}\n * {{Value is not one}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + { + "src": ".input {$foo} .match {$foo} one {{one}} * {{other}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + + { + "src": ".local $foo = {$bar} .match {$foo} one {{one}} * {{other}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + + { + "src": ".local $x = {|1|} .input {$x :number} {{{$x}}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + + { + "src": ".input {$x :number} .input {$x :string} {{{$x}}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + + { + "src": "{:foo a=1 b=2 a=1}", + "expErrors": [ + { + "type": "duplicate-option-name" + } + ] + }, + + { + "src": "{:foo a=1 a=1}", + "expErrors": [ + { + "type": "duplicate-option-name" + } + ] + }, + + { + "src": "{:foo a=1 a=2}", + "expErrors": [ + { + "type": "duplicate-option-name" + } + ] + }, + + { + "src": "{|x| :foo a=1 a=2}", + "expErrors": [ + { + "type": "duplicate-option-name" + } + ] + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/more-functions.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/more-functions.json new file mode 100644 index 000000000000..b34803635ce9 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/more-functions.json @@ -0,0 +1,117 @@ +{ + "scenario": "Function tests 2", + "description": "More tests for ICU-specific formatting behavior.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": "Format {123456789.9876 :number} number", + "locale": "en-IN", + "exp": "Format 12,34,56,789.9876 number", + "comment": "From ICU4J tests, with explicit formatter added" + }, + { + "src": "Format {|3.1416| :number} number", + "locale": "ar-AR-u-nu-latn", + "exp": "Format 3.1416 number", + "comment": "From ICU4J tests, with explicit formatter added" + }, + { + "src": "Format {|3.1416| :number} number", + "locale": "ar-AR-u-nu-arab", + "exp": "Format ٣٫١٤١٦ number", + "comment": "From ICU4J tests, with explicit formatter added" + }, + { + "src": [".local $dateStr = {$date :datetime}\n", + "{{Testing date formatting: {$dateStr :datetime}.}}"], + "exp": "Testing date formatting: 23.11.2022, 19:42.", + "locale": "ro", + "params": [{ "name": "date", "value": { "date": 1669261357000 }}] + }, + { + "src": "Testing date formatting: {$date :date style=long}.", + "exp": "Testing date formatting: November 23, 2022.", + "params": [{ "name": "date", "value": { "date": 1669261357000 } }] + }, + { + "src": "Testing date formatting: {$date :date style=medium}.", + "exp": "Testing date formatting: Nov 23, 2022.", + "params": [{ "name": "date", "value": { "date": 1669261357000 } }] + }, + { + "src": "Testing date formatting: {$date :date style=short}.", + "exp": "Testing date formatting: 11/23/22.", + "params": [{ "name": "date", "value": { "date": 1669261357000 } }] + }, + { + "src": "Testing date formatting: {$date :time style=long}.", + "exp": "Testing date formatting: 7:42:37\u202FPM PST.", + "params": [{ "name": "date", "value": { "date": 1669261357000 } }] + }, + { + "src": "Testing date formatting: {$date :time style=medium}.", + "exp": "Testing date formatting: 7:42:37\u202FPM.", + "params": [{ "name": "date", "value": { "date": 1669261357000 } }] + }, + { + "src": "Testing date formatting: {$date :time style=short}.", + "exp": "Testing date formatting: 7:42\u202FPM.", + "params": [{ "name": "date", "value": { "date": 1669261357000 } }] + }, + { + "src": [".local $num = {|42| :number}\n", + "{{Testing date formatting: {$num :datetime}}}"], + "exp": "Testing date formatting: {|42|}", + "expErrors": [{"type": "bad-operand"}] + }, + { + "src": "From literal: {|123456789,531| :number}!", + "exp": "From literal: {|123456789,531|}!", + "locale": "en-IN", + "params": [{ "name": "val", "value": 1234567890.97531 }], + "expErrors": [{"type": "bad-operand"}], + "comment": "Should fail because number literals are not treated as localized numbers", + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "From literal: {|123456789.531| :number}!", + "exp": "From literal: \u1041\u1042\u1043,\u1044\u1045\u1046,\u1047\u1048\u1049.\u1045\u1043\u1041!", + "locale": "my", + "params": [{ "name": "val", "value": 1234567890.97531 }] + }, + { + "src": "Default double: {$val}!", + "exp": "Default double: 1,23,45,67,890.97531!", + "locale": "en-IN", + "params": [{ "name": "val", "value": 1234567890.97531 }], + "comment": "The next few tests check that numeric variables are formatted without specifying :number" + }, + { + "src": "Default double: {$val}!", + "exp": "Default double: 1.234.567.890,97531!", + "locale": "ro", + "params": [{ "name": "val", "value": 1234567890.97531 }] + }, + { + "src": "Default float: {$val}!", + "exp": "Default float: 3,141593!", + "locale": "ro", + "params": [{"name": "val", "value": 3.1415926535}] + }, + { + "src": "Default int64: {$val}!", + "exp": "Default int64: 1.234.567.890.123.456.789!", + "locale": "ro", + "params": [{ "name": "val", "value": {"decimal": "1234567890123456789"} }], + "comment": "Rounded due to JSON not supporting full 64-bit ints" + }, + { + "src": "Default number: {$val}!", + "exp": "Default number: 1.234.567.890.123.456.789,987654!", + "locale": "ro", + "params": [{ "name": "val", "value": {"decimal": "1234567890123456789.987654321"} }] + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/normalization.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/normalization.json new file mode 100644 index 000000000000..283e3dac8a82 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/normalization.json @@ -0,0 +1,67 @@ +{ + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", + "scenario": "Syntax", + "description": "Test cases that do not depend on any registry definitions.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "description": "NFC: text is not normalized", + "src": "\u1E0A\u0323", + "exp": "\u1E0A\u0323" + }, + { + "description": "NFC: variables are compared to each other as-if normalized; decl is non-normalized, use is", + "src": ".local $\u0044\u0323\u0307 = {foo} {{{$\u1E0c\u0307}}}", + "exp": "foo" + }, + { + "description": "NFC: variables are compared to each other as-if normalized; decl is normalized, use isn't", + "src": ".local $\u1E0c\u0307 = {foo} {{{$\u0044\u0323\u0307}}}", + "exp": "foo" + }, + { + "description": "NFC: variables are compared to each other as-if normalized; decl is normalized, use isn't", + "src": ".input {$\u1E0c\u0307} {{{$\u0044\u0323\u0307}}}", + "params": [{"name": "\u1E0c\u0307", "value": "foo"}], + "exp": "foo" + }, + { + "description": "NFC: variables are compared to each other as-if normalized; decl is non-normalized, use is", + "src": ".input {$\u0044\u0323\u0307} {{{$\u1E0c\u0307}}}", + "params": [{"name": "\u0044\u0323\u0307", "value": "foo"}], + "exp": "foo" + }, + { + "description": "NFC: variables are compared to each other as-if normalized; decl is non-normalized, use is; reordering", + "src": ".local $\u0044\u0307\u0323 = {foo} {{{$\u1E0c\u0307}}}", + "exp": "foo" + }, + { + "description": "NFC: variables are compared to each other as-if normalized; decl is non-normalized, use is; special case mapping", + "src": ".local $\u0041\u030A\u0301 = {foo} {{{$\u01FA}}}", + "exp": "foo" + }, + { + "description": "NFC: keys are normalized", + "src": ".local $x = {\u1E0C\u0307 :string} .match {$x} \u1E0A\u0323 {{Right}} * {{Wrong}}", + "exp": "Right" + }, + { + "description": "NFC: keys are normalized (unquoted)", + "src": ".local $x = {\u1E0A\u0323 :string} .match {$x} \u1E0A\u0323 {{Not normalized}} \u1E0C\u0307 {{Normalized}} * {{Wrong}}", + "expErrors": [{"type": "duplicate-variant"}] + }, + { + "description": "NFC: keys are normalized (quoted)", + "src": ".local $x = {\u1E0A\u0323 :string} .match {$x} |\u1E0A\u0323| {{Not normalized}} |\u1E0C\u0307| {{Normalized}} * {{Wrong}}", + "expErrors": [{"type": "duplicate-variant"}] + }, + { + "description": "NFC: keys are normalized (mixed)", + "src": ".local $x = {\u1E0A\u0323 :string} .match {$x} \u1E0A\u0323 {{Not normalized}} |\u1E0C\u0307| {{Normalized}} * {{Wrong}}", + "expErrors": [{"type": "duplicate-variant"}] + } +] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/resolution-errors.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/resolution-errors.json new file mode 100644 index 000000000000..8f1ac2fb9364 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/resolution-errors.json @@ -0,0 +1,14 @@ +{ + "scenario": "Resolution errors", + "description": "Tests for unknown variables and functions", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { "src": "{$oops}", "exp": "{$oops}", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "ICU4J doesn't signal unresolved variable errors?"}, + { "src": ".input {$x :number} {{{$x}}}", "exp": "{$x}", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"}, + { "src": ".local $foo = {$bar} .match {$foo :number} one {{one}} * {{other}}", "exp": "other", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"}, + { "src": ".local $bar = {$none :number} .match {$foo :string} one {{one}} * {{{$bar}}}", "exp": "{$none}", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"}, + { "src": "The value is {horse :func}.", "exp": "The value is {|horse|}.", "expErrors": [{ "type": "unknown-function" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"} + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/runtime-errors.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/runtime-errors.json new file mode 100644 index 000000000000..b1bb0cd491a0 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/runtime-errors.json @@ -0,0 +1,27 @@ +{ + "scenario": "Runtime errors", + "description": "Tests for bad-selector and bad-operand errors", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": ".match {|horse| :date}\n 1 {{The value is one.}}\n * {{Formatter used as selector.}}", + "exp": "Formatter used as selector.", + "expErrors": [{"type": "bad-selector"}], + "ignoreJava": "ICU4J doesn't signal runtime errors?" + }, + { + "src": ".match {|horse| :number}\n 1 {{The value is one.}}\n * {{horse is not a number.}}", + "exp": "horse is not a number.", + "expErrors": [{"type": "bad-selector"}], + "ignoreJava": "ICU4J doesn't signal runtime errors?" + }, + { + "src": ".local $sel = {|horse| :number}\n .match {$sel}\n 1 {{The value is one.}}\n * {{horse is not a number.}}", + "exp": "horse is not a number.", + "expErrors": [{"type": "bad-selector"}], + "ignoreJava": "ICU4J doesn't signal runtime errors?" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/data-model-errors.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/data-model-errors.json new file mode 100644 index 000000000000..86a674c43961 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/data-model-errors.json @@ -0,0 +1,185 @@ +{ + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", + "scenario": "Data model errors", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": ".match {$foo :x} * * {{foo}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + { + "src": ".match {$foo :x} {$bar :x} * {{foo}}", + "expErrors": [ + { + "type": "variant-key-mismatch" + } + ] + }, + { + "src": ".match {:foo} 1 {{_}}", + "expErrors": [ + { + "type": "missing-fallback-variant" + } + ] + }, + { + "src": ".match {:foo} other {{_}}", + "expErrors": [ + { + "type": "missing-fallback-variant" + } + ] + }, + { + "src": ".match {:foo} {:bar} * 1 {{_}} 1 * {{_}}", + "expErrors": [ + { + "type": "missing-fallback-variant" + } + ] + }, + { + "src": ".match {$foo} one {{one}} * {{other}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + { + "src": ".input {$foo} .match {$foo} one {{one}} * {{other}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + { + "src": ".local $foo = {$bar} .match {$foo} one {{one}} * {{other}}", + "expErrors": [ + { + "type": "missing-selector-annotation" + } + ] + }, + { + "src": ".input {$foo} .input {$foo} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".input {$foo} .local $foo = {42} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {42} .input {$foo} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {:unknown} .local $foo = {42} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {$bar} .local $bar = {42} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {$foo} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {$bar} .local $bar = {$baz} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {$bar :func} .local $bar = {$baz} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {42 :func opt=$foo} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": ".local $foo = {42 :func opt=$bar} .local $bar = {42} {{_}}", + "expErrors": [ + { + "type": "duplicate-declaration" + } + ] + }, + { + "src": "bad {:placeholder option=x option=x}", + "expErrors": [ + { + "type": "duplicate-option-name" + } + ] + }, + { + "src": "bad {:placeholder ns:option=x ns:option=y}", + "expErrors": [ + { + "type": "duplicate-option-name" + } + ] + }, + { + "src": ".match {$var :string} * {{The first default}} * {{The second default}}", + "expErrors": [ + { + "type": "duplicate-variant" + } + ] + }, + { + "src": ".match {$x :string} {$y :string} * foo {{The first foo variant}} bar * {{The bar variant}} * |foo| {{The second foo variant}} * * {{The default variant}}", + "expErrors": [ + { + "type": "duplicate-variant" + } + ] + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/date.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/date.json new file mode 100644 index 000000000000..dd14e6785fb6 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/date.json @@ -0,0 +1,46 @@ +{ + "scenario": "Date function", + "description": "The built-in formatter for dates.", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [] + }, + "tests": [ + { + "src": "{:date}", + "exp": "{:date}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "{horse :date}", + "exp": "{|horse|}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "{|2006-01-02| :date}" + }, + { + "src": "{|2006-01-02T15:04:06| :date}" + }, + { + "src": "{|2006-01-02| :date style=long}" + }, + { + "src": ".local $d = {|2006-01-02| :date style=long} {{{$d :date}}}" + }, + { + "src": ".local $t = {|2006-01-02T15:04:06| :time} {{{$t :date}}}", + "ignoreJava": "ICU4J doesn't support this kind of composition" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/datetime.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/datetime.json new file mode 100644 index 000000000000..bdfea3096cda --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/datetime.json @@ -0,0 +1,68 @@ +{ + "scenario": "Datetime function", + "description": "The built-in formatter for datetimes.", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [] + }, + "tests": [ + { + "src": "{:datetime}", + "exp": "{:datetime}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "{$x :datetime}", + "exp": "{$x}", + "params": [ + { + "name": "x", + "value": true + } + ], + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "{horse :datetime}", + "exp": "{|horse|}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "{|2006-01-02T15:04:06| :datetime}" + }, + { + "src": "{|2006-01-02T15:04:06| :datetime year=numeric month=|2-digit|}" + }, + { + "src": "{|2006-01-02T15:04:06| :datetime dateStyle=long}" + }, + { + "src": "{|2006-01-02T15:04:06| :datetime timeStyle=medium}" + }, + { + "src": "{$dt :datetime}", + "params": [ + { + "type": "datetime", + "name": "dt", + "value": "2006-01-02T15:04:06" + } + ] + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/integer.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/integer.json new file mode 100644 index 000000000000..c8e75077a221 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/integer.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", + "scenario": "Integer function", + "description": "The built-in formatter for integers.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": "hello {4.2 :integer}", + "exp": "hello 4" + }, + { + "src": "hello {-4.20 :integer}", + "exp": "hello -4" + }, + { + "src": "hello {0.42e+1 :integer}", + "exp": "hello 4" + }, + { + "src": ".match {$foo :integer} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1.2 + } + ], + "exp": "one" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/number.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/number.json new file mode 100644 index 000000000000..1b81c705622b --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/number.json @@ -0,0 +1,407 @@ +{ + "scenario": "Number function", + "description": "The built-in formatter for numbers.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": "hello {4.2 :number}", + "exp": "hello 4.2" + }, + { + "src": "hello {-4.20 :number}", + "exp": "hello -4.2" + }, + { + "src": "hello {0.42e+1 :number}", + "exp": "hello 4.2" + }, + { + "src": "hello {foo :number}", + "exp": "hello {|foo|}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "invalid number literal {|.1| :number}", + "exp": "invalid number literal {|.1|}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "invalid number literal {|1.| :number}", + "exp": "invalid number literal {|1.|}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "invalid number literal {|01| :number}", + "exp": "invalid number literal {|01|}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "invalid number literal {|+1| :number}", + "exp": "invalid number literal {|+1|}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "invalid number literal {|0x1| :number}", + "exp": "invalid number literal {|0x1|}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "hello {:number}", + "exp": "hello {:number}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "hello {4.2 :number minimumFractionDigits=2}", + "exp": "hello 4.20" + }, + { + "src": "hello {|4.2| :number minimumFractionDigits=|2|}", + "exp": "hello 4.20" + }, + { + "src": "hello {4.2 :number minimumFractionDigits=$foo}", + "params": [ + { + "name": "foo", + "value": 2 + } + ], + "exp": "hello 4.20" + }, + { + "src": "hello {|4.2| :number minimumFractionDigits=$foo}", + "params": [ + { + "name": "foo", + "value": "2" + } + ], + "exp": "hello 4.20" + }, + { + "src": ".local $foo = {$bar :number} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": 4.2 + } + ], + "exp": "bar 4.2" + }, + { + "src": ".local $foo = {$bar :number minimumFractionDigits=2} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": 4.2 + } + ], + "exp": "bar 4.20" + }, + { + "src": ".local $foo = {$bar :number minimumFractionDigits=foo} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": 4.2 + } + ], + "exp": "bar {$bar}", + "expErrors": [ + { + "type": "bad-option" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": ".local $foo = {$bar :number} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": "foo" + } + ], + "exp": "bar {$bar}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": ".input {$foo :number} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": 4.2 + } + ], + "exp": "bar 4.2" + }, + { + "src": ".input {$foo :number minimumFractionDigits=2} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": 4.2 + } + ], + "exp": "bar 4.20" + }, + { + "src": ".input {$foo :number minimumFractionDigits=foo} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": 4.2 + } + ], + "exp": "bar {$foo}", + "expErrors": [ + { + "type": "bad-option" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": ".input {$foo :number} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": "foo" + } + ], + "exp": "bar {$foo}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": ".match {$foo :number} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".match {$foo :number} 1 {{=1}} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "=1" + }, + { + "src": ".match {$foo :number} one {{one}} 1 {{=1}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "=1" + }, + { + "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + }, + { + "name": "bar", + "value": 1 + } + ], + "exp": "one one" + }, + { + "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + }, + { + "name": "bar", + "value": 2 + } + ], + "exp": "one other" + }, + { + "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", + "params": [ + { + "name": "foo", + "value": 2 + }, + { + "name": "bar", + "value": 2 + } + ], + "exp": "other" + }, + { + "src": ".input {$foo :number} .match {$foo} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".local $foo = {$bar :number} .match {$foo} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".input {$foo :number} .local $bar = {$foo} .match {$bar} one {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".input {$bar :number} .match {$bar} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 2 + } + ], + "exp": "other" + }, + { + "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", + "params": [ + { + "name": "bar", + "value": 2 + } + ], + "exp": "other" + }, + { + "src": ".input {$none} .match {$foo :number} one {{one}} * {{{$none}}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one" + }, + { + "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", + "params": [ + { + "name": "foo", + "value": 2 + } + ], + "exp": "{$none}", + "expErrors": [ + { + "type": "unresolved-variable" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "{42 :number @foo @bar=13}", + "exp": "42", + "expParts": [ + { + "type": "number", + "source": "|42|", + "parts": [ + { + "type": "integer", + "value": "42" + } + ] + } + ] + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/string.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/string.json new file mode 100644 index 000000000000..5858079b99a6 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/string.json @@ -0,0 +1,51 @@ +{ + "scenario": "String function", + "description": "The built-in formatter for strings.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "src": ".match {$foo :string} |1| {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": "1" + } + ], + "exp": "one" + }, + { + "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": 1 + } + ], + "exp": "one", + "ignoreJava": ":string doesn't stringify numbers?" + }, + { + "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "params": [ + { + "name": "foo", + "value": null + } + ], + "exp": "other", + "ignoreCpp": "Can't handle null value for input variable" + }, + { + "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "exp": "other", + "expErrors": [ + { + "type": "unresolved-variable" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/time.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/time.json new file mode 100644 index 000000000000..845934a5e16a --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/time.json @@ -0,0 +1,43 @@ +{ + "scenario": "Time function", + "description": "The built-in formatter for times.", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [] + }, + "tests": [ + { + "src": "{:time}", + "exp": "{:time}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "{horse :time}", + "exp": "{|horse|}", + "expErrors": [ + { + "type": "bad-operand" + } + ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "src": "{|2006-01-02T15:04:06| :time}" + }, + { + "src": "{|2006-01-02T15:04:06| :time style=medium}" + }, + { + "src": ".local $t = {|2006-01-02T15:04:06| :time style=medium} {{{$t :time}}}" + }, + { + "src": ".local $d = {|2006-01-02T15:04:06| :date} {{{$d :time}}}", + "ignoreJava": "ICU4J doesn't support this kind of composition" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/syntax-errors.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/syntax-errors.json new file mode 100644 index 000000000000..34d9aa484572 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/syntax-errors.json @@ -0,0 +1,180 @@ +{ + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", + "scenario": "Syntax errors", + "description": "Strings that produce syntax errors when parsed.", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "syntax-error" + } + ] + }, + "tests": [ + { + "src": "." + }, + { + "src": "{" + }, + { + "src": "}" + }, + { + "src": "{}" + }, + { + "src": "{{" + }, + { + "src": "{{}" + }, + { + "src": "{{}}}" + }, + { + "src": "{|foo| #markup}" + }, + { + "src": "{{missing end brace}" + }, + { + "src": "{{missing end braces" + }, + { + "src": "{{missing end {$braces" + }, + { + "src": "{{extra}} content" + }, + { + "src": "empty { } placeholder" + }, + { + "src": "missing space {42:func}" + }, + { + "src": "missing space {|foo|:func}" + }, + { + "src": "missing space {|foo|@bar}" + }, + { + "src": "missing space {:func@bar}" + }, + { + "src": "missing space {:func @bar@baz}" + }, + { + "src": "missing space {:func @bar=42@baz}" + }, + { + "src": "missing space {+reserved@bar}" + }, + { + "src": "missing space {&private@bar}" + }, + { + "src": "bad {:} placeholder" + }, + { + "src": "bad {\\u0000placeholder}" + }, + { + "src": "no-equal {|42| :number minimumFractionDigits 2}" + }, + { + "src": "bad {:placeholder option=}" + }, + { + "src": "bad {:placeholder option value}" + }, + { + "src": "bad {:placeholder option:value}" + }, + { + "src": "bad {:placeholder option}" + }, + { + "src": "bad {:placeholder:}" + }, + { + "src": "bad {::placeholder}" + }, + { + "src": "bad {:placeholder::foo}" + }, + { + "src": "bad {:placeholder option:=x}" + }, + { + "src": "bad {:placeholder :option=x}" + }, + { + "src": "bad {:placeholder option::x=y}" + }, + { + "src": "bad {$placeholder option}" + }, + { + "src": "bad {:placeholder @attribute=}" + }, + { + "src": "bad {:placeholder @attribute=@foo}" + }, + { + "src": "{ @misplaced = attribute }" + }, + { + "src": "no {placeholder end" + }, + { + "src": "no {$placeholder end" + }, + { + "src": "no {:placeholder end" + }, + { + "src": "no {|placeholder| end" + }, + { + "src": "no {|literal} end" + }, + { + "src": "no {|literal or placeholder end" + }, + { + "src": ".local bar = {|foo|} {{_}}" + }, + { + "src": ".local #bar = {|foo|} {{_}}" + }, + { + "src": ".local $bar {|foo|} {{_}}" + }, + { + "src": ".local $bar = |foo| {{_}}" + }, + { + "src": ".match {#foo} * {{foo}}" + }, + { + "src": ".match {} * {{foo}}" + }, + { + "src": ".match {|foo| :x} {|bar| :x} ** {{foo}}" + }, + { + "src": ".match * {{foo}}" + }, + { + "src": ".match {|x| :x} * foo" + }, + { + "src": ".match {|x| :x} * {{foo}} extra" + }, + { + "src": ".match |x| * {{foo}}" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/syntax.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/syntax.json new file mode 100644 index 000000000000..558fc64062b4 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/syntax.json @@ -0,0 +1,699 @@ +{ + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", + "scenario": "Syntax", + "description": "Test cases that do not depend on any registry definitions.", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { + "description": "message -> simple-message -> ", + "src": "", + "exp": "" + }, + { + "description": "message -> simple-message -> simple-start pattern -> simple-start-char", + "src": "a", + "exp": "a" + }, + { + "description": "message -> simple-message -> simple-start pattern -> simple-start-char pattern -> ...", + "src": "hello", + "exp": "hello" + }, + { + "description": "message -> simple-message -> simple-start pattern -> escaped-char", + "src": "\\\\", + "exp": "\\" + }, + { + "description": "message -> simple-message -> simple-start pattern -> simple-start-char pattern -> ... -> simple-start-char *text-char placeholder", + "src": "hello {world}", + "exp": "hello world" + }, + { + "description": "message -> simple-message -> simple-start pattern -> simple-start-char pattern -> ... -> simple-start-char *text-char placeholder", + "src": "hello {|world|}", + "exp": "hello world" + }, + { + "description": "message -> simple-message -> s simple-start pattern -> s simple-start-char pattern -> ...", + "src": "\n hello\t", + "exp": "\n hello\t" + }, + { + "src": "hello {$place}", + "params": [ + { + "name": "place", + "value": "world" + } + ], + "exp": "hello world" + }, + { + "src": "hello {$place-.}", + "params": [ + { + "name": "place-.", + "value": "world" + } + ], + "exp": "hello world" + }, + { + "src": "hello {$place}", + "expErrors": [ + { + "type": "unresolved-variable" + } + ], + "exp": "hello {$place}", + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> expression -> literal-expression -> \"{\" literal \"}\"", + "src": "{a}", + "exp": "a" + }, + { + "description": "... -> literal-expression -> \"{\" literal s annotation \"}\" -> \"{\" literal s function \"}\" -> \"{\" literal s \":\" identifier \"}\" -> \"{\" literal s \":\" name \"}\"", + "src": "{a :f}", + "exp": "{|a|}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... -> \"{\" literal s \":\" namespace \":\" name \"}\"", + "src": "{a :u:f}", + "exp": "{|a|}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> expression -> variable-expression -> \"{\" variable \"}\"", + "src": "{$x}", + "exp": "{$x}", + "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... -> variable-expression -> \"{\" variable s annotation \"}\" -> \"{\" variable s function \"}\" -> \"{\" variable s \":\" identifier \"}\" -> \"{\" variable s \":\" name \"}\"", + "src": "{$x :f}", + "exp": "{$x}", + "expErrors": [{ "type": "unresolved-variable" }, { "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... -> \"{\" variable s \":\" namespace \":\" name \"}\"", + "src": "{$x :u:f}", + "exp": "{$x}", + "expErrors": [{ "type": "unresolved-variable" }, { "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... -> annotation-expression -> function -> \"{\" \":\" namespace \":\" name \"}\"", + "src": "{:u:f}", + "exp": "{:u:f}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... -> annotation-expression -> function -> \"{\" \":\" name \"}\"", + "src": "{:f}", + "exp": "{:f}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "message -> complex-message -> complex-body -> quoted-pattern -> \"{{\" pattern \"}}\" -> \"{{\"\"}}\"", + "src": "{{}}", + "exp": "" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" \"#\" identifier \"}\"", + "src": "{#tag}", + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "tag" + } + ] + }, + { + "description": "message -> complex-message -> *(declaration [s]) complex-body -> declaration complex-body -> input-declaration complex-body -> input variable-expression complex-body", + "src": ".input{$x}{{}}", + "exp": "" + }, + { + "description": "message -> complex-message -> s *(declaration [s]) complex-body s -> s declaration complex-body s -> s input-declaration complex-body s -> s input variable-expression complex-body s", + "src": "\t.input{$x}{{}}\n", + "exp": "" + }, + { + "description": "message -> complex-message -> *(declaration [s]) complex-body -> declaration declaration complex-body -> input-declaration input-declaration complex-body -> input variable-expression input variable-expression complex-body", + "src": ".input{$x}.input{$y}{{}}", + "exp": "" + }, + { + "description": "message -> complex-message -> *(declaration [s]) complex-body -> declaration s declaration complex-body -> input-declaration s input-declaration complex-body -> input variable-expression s input variable-expression complex-body", + "src": ".input{$x} .input{$y}{{}}", + "exp": "" + }, + { + "description": "message -> complex-message -> s *(declaration [s]) complex-body s -> s complex-body s", + "src": " {{}} ", + "exp": "" + }, + { + "description": "message -> complex-message -> *(declaration [s]) complex-body -> declaration declaration complex-body -> local-declaration input-declaration complex-body -> local s variable [s] \"=\" [s] expression input variable-expression complex-body", + "src": ".local $x ={a}.input{$y}{{}}", + "exp": "" + }, + { + "description": "message -> complex-message -> complex-body -> matcher -> match-statement variant -> match selector key quoted-pattern -> \".match\" expression literal quoted-pattern", + "src": ".match{a :f}a{{}}*{{}}", + "exp": "", + "expErrors": [ { "type": "unknown-function" } ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... input-declaration -> input s variable-expression ...", + "src": ".input {$x}{{}}", + "exp": "" + }, + { + "description": "... local-declaration -> local s variable s \"=\" expression ...", + "src": ".local $x ={a}{{}}", + "exp": "" + }, + { + "description": "... local-declaration -> local s variable \"=\" s expression ...", + "src": ".local $x= {a}{{}}", + "exp": "" + }, + { + "description": "... local-declaration -> local s variable s \"=\" expression ...", + "src": ".local $x = {a}{{}}", + "exp": "" + }, + { + "description": "... matcher -> match-statement [s] variant -> match 1*([s] selector) variant -> match selector selector variant -> match selector selector variant key s key quoted-pattern", + "src": ".match{a :f}{b :f}a b{{}}* *{{}}", + "exp": "", + "expErrors": [ { "type": "unknown-function" } ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... matcher -> match-statement [s] variant -> match 1*([s] selector) variant -> match selector variant variant ...", + "src": ".match{a :f}a{{}}b{{}}*{{}}", + "exp": "", + "expErrors": [ { "type": "unknown-function" } ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... variant -> key s quoted-pattern -> ...", + "src": ".match{a :f}a {{}}*{{}}", + "exp": "", + "expErrors": [ { "type": "unknown-function" } ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... variant -> key s key s quoted-pattern -> ...", + "src": ".match{a :f}{b :f}a b {{}}* *{{}}", + "exp": "", + "expErrors": [ { "type": "unknown-function" } ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... key -> \"*\" ...", + "src": ".match{a :f}*{{}}", + "exp": "", + "expErrors": [ { "type": "unknown-function" } ], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "simple-message -> simple-start pattern -> placeholder -> expression -> literal-expression -> \"{\" s literal \"}\"", + "src": "{ a}", + "exp": "a" + }, + { + "description": "... literal-expression -> \"{\" literal s attribute \"}\" -> \"{\" literal s \"@\" identifier \"}\"", + "src": "{a @c}", + "exp": "a" + }, + { + "description": "... -> literal-expression -> \"{\" literal s \"}\"", + "src": "{a }", + "exp": "a" + }, + { + "description": "simple-message -> simple-start pattern -> placeholder -> expression -> variable-expression -> \"{\" s variable \"}\"", + "src": "{ $x}", + "exp": "{$x}", + "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... variable-expression -> \"{\" variable s attribute \"}\" -> \"{\" variable s \"@\" identifier \"}\"", + "src": "{$x @c}", + "exp": "{$x}", + "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... -> variable-expression -> \"{\" variable s \"}\"", + "src": "{$x }", + "exp": "{$x}", + "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "simple-message -> simple-start pattern -> placeholder -> expression -> annotation-expression -> \"{\" s annotation \"}\"", + "src": "{ :f}", + "exp": "{:f}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... annotation-expression -> \"{\" annotation s attribute \"}\" -> \"{\" annotation s \"@\" identifier \"}\"", + "src": "{:f @c}", + "exp": "{:f}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... -> annotation-expression -> \"{\" annotation s \"}\"", + "src": "{:f }", + "exp": "{:f}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" s \"#\" identifier \"}\"", + "src": "{ #a}", + "exp": "" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" \"#\" identifier option \"}\" -> \"{\" \"#\" identifier identifier \"=\" literal \"}\"", + "src": "{#tag foo=bar}", + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "tag", + "options": { + "foo": "bar" + } + } + ] + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" \"#\" identifier attribute \"}\" -> \"{\" \"#\" identifier identifier \"=\" literal \"}\"", + "src": "{#a @c}", + "exp": "" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" \"#\" identifier s \"}\" -> \"{\" \"#\" identifier identifier \"=\" literal \"}\"", + "src": "{#a }", + "exp": "" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" \"#\" identifier \"/\" \"}\" -> \"{\" \"#\" identifier identifier \"=\" literal \"}\"", + "src": "{#a/}", + "exp": "" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" \"/\" identifier \"}\"", + "src": "{/a}", + "exp": "" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" s \"/\" identifier \"}\"", + "src": "{ /a}", + "exp": "" + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" \"/\" identifier option \"}\"", + "src": "{/tag foo=bar}", + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "close", + "name": "tag", + "options": { + "foo": "bar" + } + } + ] + }, + { + "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" \"/\" identifier s \"}\"", + "src": "{/a }", + "exp": "" + }, + { + "description": "... annotation-expression -> function -> \":\" identifier option", + "src": "{:f k=v}", + "exp": "{:f}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... option -> identifier s \"=\" literal", + "src": "{:f k =v}", + "exp": "{:f}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... option -> identifier \"=\" s literal", + "src": "{:f k= v}", + "exp": "{:f}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... option -> identifier s \"=\" s literal", + "src": "{:f k = v}", + "exp": "{:f}", + "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + }, + { + "description": "... attribute -> \"@\" identifier \"=\" literal ...", + "src": "{a @c=d}", + "exp": "a" + }, + { + "description": "... attribute -> \"@\" identifier s \"=\" literal ...", + "src": "{a @c =d}", + "exp": "a" + }, + { + "description": "... attribute -> \"@\" identifier \"=\" s literal ...", + "src": "{a @c= d}", + "exp": "a" + }, + { + "description": "... attribute -> \"@\" identifier s \"=\" s literal ...", + "src": "{a @c = d}", + "exp": "a" + }, + { + "description": "... attribute -> \"@\" identifier s \"=\" s variable ...", + "src": "{42 @foo=$bar}", + "exp": "42", + "expParts": [ + { + "type": "string", + "source": "|42|", + "value": "42" + } + ] + }, + { + "description": "... literal -> quoted-literal -> \"|\" \"|\" ...", + "src": "{||}", + "exp": "" + }, + { + "description": "... quoted-literal -> \"|\" quoted-char \"|\"", + "src": "{|a|}", + "exp": "a" + }, + { + "description": "... quoted-literal -> \"|\" escaped-char \"|\"", + "src": "{|\\\\|}", + "exp": "\\" + }, + { + "description": "... quoted-literal -> \"|\" quoted-char escaped-char \"|\"", + "src": "{|a\\\\|}", + "exp": "a\\" + }, + { + "description": "... unquoted-literal -> number-literal -> %x30", + "src": "{0}", + "exp": "0" + }, + { + "description": "... unquoted-literal -> number-literal -> \"-\" %x30", + "src": "{-0}", + "exp": "-0" + }, + { + "description": "... unquoted-literal -> number-literal -> (%x31-39 *DIGIT) -> %x31", + "src": "{1}", + "exp": "1" + }, + { + "description": "... unquoted-literal -> number-literal -> (%x31-39 *DIGIT) -> %x31 DIGIT -> 11", + "src": "{11}", + "exp": "11" + }, + { + "description": "... unquoted-literal -> number-literal -> %x30 \".\" 1*DIGIT -> 0 \".\" 1", + "src": "{0.1}", + "exp": "0.1" + }, + { + "description": "... unquoted-literal -> number-literal -> %x30 \".\" 1*DIGIT -> %x30 \".\" DIGIT DIGIT -> 0 \".\" 1 2", + "src": "{0.12}", + "exp": "0.12" + }, + { + "description": "... unquoted-literal -> number-literal -> %x30 %i\"e\" 1*DIGIT -> %x30 \"e\" DIGIT", + "src": "{0e1}", + "exp": "0e1" + }, + { + "description": "... unquoted-literal -> number-literal -> %x30 %i\"e\" 1*DIGIT -> %x30 \"E\" DIGIT", + "src": "{0E1}", + "exp": "0E1" + }, + { + "description": "... unquoted-literal -> number-literal -> %x30 %i\"e\" \"-\" 1*DIGIT ...", + "src": "{0E-1}", + "exp": "0E-1" + }, + { + "description": "... unquoted-literal -> number-literal -> %x30 %i\"e\" \"+\" 1*DIGIT ...", + "src": "{0E-1}", + "exp": "0E-1" + }, + { + "src": "hello { world\t\n}", + "exp": "hello world" + }, + { + "src": "hello {\u3000world\r}", + "exp": "hello world" + }, + { + "src": "{$one} and {$two}", + "params": [ + { + "name": "one", + "value": 1.3 + }, + { + "name": "two", + "value": 4.2 + } + ], + "exp": "1.3 and 4.2" + }, + { + "src": "{$one} et {$two}", + "locale": "fr", + "params": [ + { + "name": "one", + "value": 1.3 + }, + { + "name": "two", + "value": 4.2 + } + ], + "exp": "1,3 et 4,2" + }, + { + "src": ".local $foo = {bar} {{bar {$foo}}}", + "exp": "bar bar" + }, + { + "src": ".local $foo = {|bar|} {{bar {$foo}}}", + "exp": "bar bar" + }, + { + "src": ".local $foo = {|bar|} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": "foo" + } + ], + "exp": "bar bar" + }, + { + "src": ".local $foo = {$bar} {{bar {$foo}}}", + "params": [ + { + "name": "bar", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".local $foo = {$baz} .local $bar = {$foo} {{bar {$bar}}}", + "params": [ + { + "name": "baz", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".input {$foo} {{bar {$foo}}}", + "params": [ + { + "name": "foo", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".input {$foo} .local $bar = {$foo} {{bar {$bar}}}", + "params": [ + { + "name": "foo", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".local $foo = {$baz} .local $bar = {$foo} {{bar {$bar}}}", + "params": [ + { + "name": "baz", + "value": "foo" + } + ], + "exp": "bar foo" + }, + { + "src": ".local $x = {42} .local $y = {$x} {{{$x} {$y}}}", + "exp": "42 42" + }, + { + "src": "{#tag}content", + "exp": "content", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "tag" + }, + { + "type": "literal", + "value": "content" + } + ] + }, + { + "src": "{#ns:tag}content{/ns:tag}", + "exp": "content", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "ns:tag" + }, + { + "type": "literal", + "value": "content" + }, + { + "type": "markup", + "kind": "close", + "name": "ns:tag" + } + ] + }, + { + "src": "{/tag}content", + "exp": "content", + "expParts": [ + { + "type": "markup", + "kind": "close", + "name": "tag" + }, + { + "type": "literal", + "value": "content" + } + ] + }, + { + "src": "{#tag foo=bar/}", + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "standalone", + "name": "tag", + "options": { + "foo": "bar" + } + } + ] + }, + { + "src": "{#tag a:foo=|foo| b:bar=$bar}", + "params": [ + { + "name": "bar", + "value": "b a r" + } + ], + "exp": "", + "expParts": [ + { + "type": "markup", + "kind": "open", + "name": "tag", + "options": { + "a:foo": "foo", + "b:bar": "b a r" + } + } + ] + }, + { + "src": "{42 @foo @bar=13}", + "exp": "42", + "expParts": [ + { + "type": "string", + "source": "|42|", + "value": "42" + } + ] + }, + { + "src": "{{trailing whitespace}} \n", + "exp": "trailing whitespace" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-diagnostics-multiline.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-diagnostics-multiline.json new file mode 100644 index 000000000000..facfadebab47 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-diagnostics-multiline.json @@ -0,0 +1,50 @@ +{ + "scenario": "Syntax errors with character and line offsets", + "description": "Syntax errors; for ICU4C, the character and line offsets in the parse error are checked", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "syntax-error" + } + ] + }, + "tests": [ + { + "src": "{{hello wo\nrld", + "char": 3, + "line": 1, + "comment": "Check that the missing closing '}' is reported on the correct line" + }, + { + "src": "{{hello wo\nr\nl\ndddd", + "char": 4, + "line": 3, + "comment": "Check that the missing closing '}' is reported on the correct line" + }, + { + "src": "{{hello wo\nr\nl\n", + "char": 0, + "line": 3, + "comment": "Offset for end-of-input should be 0 here because the line begins after the '\n', but there is no character after the '\n'" + }, + { + "src": "hello {|wo\nrld", + "char": 3, + "line": 1, + "comment": "Quoted literals may include newlines; check that the missing closing '|' is reported on the correct line" + }, + { + "src": "hello {|wo\nr\nl\ndddd", + "char": 4, + "line": 3, + "comment": "Quoted literals may include newlines; check that the missing closing '|' is reported on the correct line" + }, + { + "src": "hello {|wo\nr\nl\n", + "char": 0, + "line": 3, + "comment": "Offset for end-of-input should be 0 here because the line begins after the '\n', but there is no character after the '\n'" + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-diagnostics.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-diagnostics.json new file mode 100644 index 000000000000..79a29a027f9b --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-diagnostics.json @@ -0,0 +1,347 @@ +{ + "scenario": "Syntax errors with character offsets", + "description": "Syntax errors; for ICU4C, the character offset in the parse error is checked", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "syntax-error" + } + ] + }, + "tests": [ + { "src": "}{|xyz|", "char": 0 }, + { "src": "}", "char": 0 }, + { + "src": "{{{%\\y{}}", + "char": 5, + "comment": "Backslash followed by non-backslash followed by a '{' -- this should be an error immediately after the first backslash" + }, + { + "src": "{%abc|\\z}}", + "char": 7, + "comment": "Reserved chars followed by a '|' that doesn't begin a valid literal -- this should be an error at the first invalid char in the literal" + }, + { + "src": "{%\\y{p}}", + "char": 3, + "comment": "Same pattern, but with a valid reserved-char following the erroneous reserved-escape -- the offset should be the same as with the previous one" + }, + { + "src": "{{{%ab|\\z|cd}}", + "char": 8, + "comment": "Erroneous literal inside a reserved string -- the error should be at the first erroneous literal char" + }, + { + "src": "hello {|4.2| %num\\ber}}", + "char": 18, + "comment": "Single backslash not allowed" + }, + { + "src": "hello {|4.2| %num{be\\|r}}", + "char": 17, + "comment": "Unescaped '{' not allowed" + }, + { + "src": "hello {|4.2| %num}be\\|r}}", + "char": 23, + "comment": "Unescaped '}' -- will be interpreted as the end of the reserved string, and the error will be reported at the index of the next '}'" }, + { + "src": "hello {|4.2| %num\\{be|r}}", + "char": 25, + "comment": "Unescaped '|' -- will be interpreted as the beginning of a literal. Error at end of input" + }, + { + "src": ".match{|y|}|y|{{{|||}}}", + "char": 19, + "comment": "No spaces are required here. The error should be in the pattern, not before" + }, + { + "src": ".match {|y|}|foo|bar {{{a}}}", + "char": 17, + "comment": "Missing spaces between keys" + }, + { + "src": ".match {|y|} |quux| |foo|bar {{{a}}}", + "char": 25, + "comment": "Missing spaces between keys" + }, + { + "src": ".match {|y|} |quux| |foo||bar| {{{a}}}", + "char": 26, + "comment": "Missing spaces between keys" + }, + { + "src": ".match {|y|} |\\q| * %{! {z}", + "char": 16, + "comment": "Error parsing the first key -- the error should be there, not in the also-erroneous third key" + }, + { + "src": ".match {|y|} * %{! {z} |\\q|", + "char": 16, + "comment": "Error parsing the second key -- the error should be there, not in the also-erroneous third key" + }, + { + "src": ".match {|y|} * |\\q| {\\z}", + "char": 18, + "comment": "Error parsing the last key -- the error should be there, not in the erroneous pattern" + }, + { + "src": ".match {|y|} {\\|} {@} * * * {{a}}", + "char": 14, + "comment": "Non-expression as scrutinee in pattern -- error should be at the first non-expression, not the later non-expression" + }, + { + "src": ".match {|y|} $foo * {{a}} when * :bar {{b}}", + "char": 14, + "comment": "Non-key in variant -- error should be there, not in the next erroneous variant" + }, + { + "src": "{{ foo {|bar|} \\q baz ", + "char": 16, + "comment": "Error should be within the first erroneous `text` or expression" + }, + { + "src": "{{{: }}}", + "char": 4, + "comment": "':' has to be followed by a function name -- the error should be at the first whitespace character" + }, + { + "src": ".local $x = }|foo|}", + "char": 12, + "comment": "Expression not starting with a '{'" + }, + { + "src": ".local $bar {|foo|} {{$bar}}", + "char": 12, + "comment": "Missing '=' in `.local` declaration" + }, + { + "src": ".local bar = {|foo|} {{$bar}}", + "char": 7, + "comment": "LHS of declaration doesn't start with a '$'" + }, + { + "src": ".local $bar = |foo| {{$bar}}", + "char": 14, + "comment": "`.local` RHS isn't an expression" + }, + { + "src": "{{extra}}content", + "char": 9, + "comment": "Trailing characters that are not whitespace" + }, + { + "src": ".match {|x|} * {{foo}}extra", + "char": 28, + "comment": "Trailing characters that are not whitespace" + }, + { + "src": "empty { }", + "char": 8, + "comment": "Empty expression" + }, + { + "src": ".match {} * {{foo}}", + "char": 8, + "comment": "Empty expression" + }, + { + "src": "bad {:}", + "char": 6, + "comment": "':' not preceding a function name" + }, + { + "src": "{{no-equal {|42| :number m }}}", + "char": 27, + "comment": "Missing '=' after option name" + }, + { + "src": "{{no-equal {|42| :number minimumFractionDigits 2}}}", + "char": 47, + "comment": "Missing '=' after option name" + }, + { + "src": "bad {:placeholder option value}", + "char": 25, + "comment": "Missing '=' after option name" + }, + { + "src": "hello {|4.2| :number min=2=3}", + "char": 26, + "comment": "Extra '=' after option name" + }, + { + "src": "hello {|4.2| :number min=2max=3}", + "char": 26, + "comment": "Missing space between options" + }, + { + "src": "hello {|4.2| :number min=|a|max=3}", + "char": 28, + "comment": "Missing whitespace between valid options" + }, + { + "src": "hello {|4.2| :number min=|\\a|}", + "char": 27, + "comment": "Ill-formed RHS of option -- the error should be within the RHS, not after parsing options" + }, + { + "src": "no-equal {|42| :number {}", + "char": 25, + "comment": "Junk after annotation" + }, + { + "src": "bad {:placeholder option=}", + "char": 25, + "comment": "Missing RHS of option" + }, + { + "src": "bad {:placeholder option}", + "char": 24, + "comment": "Missing RHS of option" + }, + { + "src": "bad {$placeholder option}", + "char": 18, + "comment": "Annotation is not a function or reserved text" + }, + { + "src": "no {$placeholder end", + "char": 17, + "comment": "Annotation is not a function or reserved text" + }, + { + "src": ".match * {{foo}}", + "char": 8, + "comment": "Missing expression in selectors" + }, + { + "src": ".match |x| * {{foo}}", + "char": 7, + "comment": "Non-expression in selectors" + }, + { + "src": ".match {|x|} * foo", + "char": 19, + "comment": "Missing RHS in variant" + }, + { + "src": "{$:abc}", + "char": 2, + "comment": "Variable names can't start with a : or -" + }, + { + "src": "{$-abc}", + "char": 2, + "comment": "Variable names can't start with a : or -" + }, + { + "src": "{$bar+foo}", + "char": 5, + "comment": "Missing space before annotation. Note that {{$bar:foo}} and {{$bar-foo}} are valid, because variable names can contain a ':' or a '-'" + }, + { + "src": "{|3.14|:foo}", + "char": 7, + "comment": "Missing space before annotation." + }, + { + "src": "{|3.14|-foo}", + "char": 7, + "comment": "Missing space before annotation." + }, + { + "src": "{|3.14|+foo}", + "char": 7, + "comment": "Missing space before annotation." + }, + { + "src": ".local $foo = {$bar} .match {$foo} :one {one} * {other}", + "char": 36, + "comment": "Unquoted literals can't begin with a ':'" + }, + { + "src": ".local $foo = {$bar :fun option=:a} {{bar {$foo}}}", + "char": 32, + "comment": "Unquoted literals can't begin with a ':'" + }, + { "src": "{|foo| {#markup}}", "char": 7, "comment": "Markup in wrong place" }, + { "src": "{|foo| #markup}", "char": 7, "comment": "Markup in wrong place" }, + { "src": "{|foo| {#markup/}}", "char": 7, "comment": "Markup in wrong place" }, + { "src": "{|foo| {/markup}}", "char": 7, "comment": "Markup in wrong place" }, + { "src": ".input $x = {|1|} {{{$x}}}", "char": 7, "comment": ".input with non-variable-expression" }, + { "src": ".input $x = {:number} {{{$x}}}", "char": 7, "comment": ".input with non-variable-expression" }, + { "src": ".input {|1| :number} {{{$x}}}", "char": 7, "comment": ".input with non-variable-expression" }, + { "src": ".input {:number} {{{$x}}}", "char": 7, "comment": ".input with non-variable-expression" }, + { "src": ".input {|1|} {{{$x}}}", "char": 7, "comment": ".input with non-variable-expression" }, + { "src": ".", "char": 1}, + { "src": "{", "char": 1}, + { "src": "}", "char": 0}, + { "src": "{}", "char": 1}, + { "src": "{{", "char": 2}, + { "src": "{{}", "char": 3}, + { "src": "{{}}}", "char": 4}, + { "src": "{|foo| #markup}", "char": 7}, + { "src": "{{missing end brace}", "char": 20}, + { "src": "{{missing end braces", "char": 20}, + { "src": "{{missing end {$braces", "char": 22}, + { "src": "{{extra}} content", "char": 10}, + { "src": "empty { } placeholder", "char": 8}, + { "src": "missing space {42:func}", "char": 17}, + { "src": "missing space {|foo|:func}", "char": 20}, + { "src": "missing space {|foo|@bar}", "char": 20}, + { "src": "missing space {:func@bar}", "char": 20}, + { "src": "{:func @bar@baz}", "char": 11}, + { "src": "{:func @bar=42@baz}", "char": 14}, + { "src": "{+reserved@bar}", "char": 10}, + { "src": "{&private@bar}", "char": 9}, + { "src": "bad {:} placeholder", "char": 6}, + { "src": "bad {\\u0000placeholder}", "char": 5}, + { "src": "no-equal {|42| :number minimumFractionDigits 2}", "char": 45}, + { "src": "bad {:placeholder option=}", "char": 25}, + { "src": "bad {:placeholder option value}", "char": 25}, + { "src": "bad {:placeholder option:value}", "char": 30}, + { "src": "bad {:placeholder option}", "char": 24}, + { "src": "bad {:placeholder:}", "char": 18}, + { "src": "bad {::placeholder}", "char": 6}, + { "src": "bad {:placeholder::foo}", "char": 18}, + { "src": "bad {:placeholder option:=x}", "char": 25}, + { "src": "bad {:placeholder :option=x}", "char": 18}, + { "src": "bad {:placeholder option::x=y}", "char": 25}, + { "src": "bad {$placeholder option}", "char": 18}, + { "src": "bad {:placeholder @attribute=}", "char": 29}, + { "src": "bad {:placeholder @attribute=@foo}", "char": 29}, + { "src": "no {placeholder end", "char": 16}, + { "src": "no {$placeholder end", "char": 17}, + { "src": "no {:placeholder end", "char": 20}, + { "src": "no {|placeholder| end", "char": 18}, + { "src": "no {|literal} end", "char": 17}, + { "src": "no {|literal or placeholder end", "char": 31}, + { "src": ".local bar = {|foo|} {{_}}", "char": 7}, + { "src": ".local #bar = {|foo|} {{_}}", "char": 7}, + { "src": ".local $bar {|foo|} {{_}}", "char": 12}, + { "src": ".local $bar = |foo| {{_}}", "char": 14}, + { "src": ".match {#foo} * {{foo}}", "char": 8}, + { "src": ".match {} * {{foo}}", "char": 8}, + { "src": ".match {|foo| :x} {|bar| :x} ** {{foo}}", "char": 30}, + { "src": ".match * {{foo}}", "char": 7}, + { "src": ".match {|x| :x} * foo", "char": 21}, + { "src": ".match {|x| :x} * {{foo}} extra", "char": 31}, + { "src": ".match |x| * {{foo}}", "char": 7}, + { "src": ".match {|foo| :string} o:ne {{one}} * {{other}}", "char": 24, "comment" : "tests for ':' in unquoted literals (not allowed)" }, + { "src": ".match {|foo| :string} one: {{one}} * {{other}}", "char": 26, "comment" : "tests for ':' in unquoted literals (not allowed)" }, + { "src": ".local $foo = {|42| :number option=a:b} {{bar {$foo}}}", "char": 36, "comment" : "tests for ':' in unquoted literals (not allowed)" }, + { "src": ".local $foo = {|42| :number option=a:b:c} {{bar {$foo}}}", "char": 36, "comment" : "tests for ':' in unquoted literals (not allowed)" }, + { "src": "{$bar:foo}", "char": 5, "comment" : "tests for ':' in unquoted literals (not allowed)"}, + { + "src": ".match {1} {{_}}", + "char": 12, + "comment": "Disambiguating a wrong .match from an unsupported statement" + }, + { + "src": "{{{/p o4.􍅲 = 1}}}", + "char": 9 + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-end-of-input.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-end-of-input.json new file mode 100644 index 000000000000..d97dcb24ac82 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-end-of-input.json @@ -0,0 +1,29 @@ +{ + "scenario": "Syntax errors for unexpected end-of-input", + "description": "Syntax errors; for ICU4C, the character offset is expected to be the last character", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "syntax-error" + } + ] + }, + "tests": [ + { "src": ".local ", "char": 9 }, + { "src": ".lo", "char": 3 }, + { "src": ".local $foo", "char": 11 }, + { "src": ".local $foo = ", "char": 17 }, + { "src": "{:fszzz", "char": 7 }, + { "src": "{:fszzz ", "char": 10 }, + { "src": ".match {$foo} |xyz", "char": 19 }, + { "src": "{:f aaa", "char": 7 }, + { "src": "{{missing end brace", "char": 19 }, + { "src": "{{missing end brace}", "char": 20 }, + { "src": "{{missing end {$brace", "char": 21 }, + { "src": "{{missing end {$brace}", "char": 22 }, + { "src": "{{missing end {$brace}}", "char": 23 }, + { "src": "{{%xyz", "char": 6 } + ] +} + diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-reserved.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-reserved.json new file mode 100644 index 000000000000..e82421485d23 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/syntax-errors-reserved.json @@ -0,0 +1,22 @@ +{ + "scenario": "Reserved and private annotations", + "description": "Tests for old unsupported expression (reserved/private) syntax -- these are now syntax errors", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "syntax-error" + } + ] + }, + "tests": [ + { "src" : "\\\\{ ? 󗟋 𫓜|@} |}" }, + { "src" : "\\\\{ ? 󗟋 𫓜|@} | \\} @𠟅򑚎𥪙𽧫=|| @򒘒򳷦㥞򉊷=$򸚶񽱆񅗽񤕞 @𰺱:񎫛񢶛򶈎񄮒}" }, + { "src" : "{ $iFN ^ @USh =$u @l}" }, + { "src" : ".local $dS ={ $p4 ^ |.| \\\\ @g:FV = $kd}{{@}}" }, + { "src" : ".D. \\\\ ||{1}{{}} " }, + { "src" : ".cIT ||{|@| % \\} } { *󔜫񥘃󸇀 }{{}}" }, + { "src" : ".򱋿󠆿 . |@| {$󛒁񓝖 & \\{ . @𯼼򋼡= $򵀀񓞈}{{\\\\}}" } + ] +} + diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/tricky-declarations.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/tricky-declarations.json new file mode 100644 index 000000000000..3fded666e633 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/tricky-declarations.json @@ -0,0 +1,19 @@ +{ + "scenario": "Declarations tests", + "description": "Tests for interesting combinations of .local and .input", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { "src": ".input {$var :number minimumFractionDigits=$var2} .input {$var2 :number minimumFractionDigits=5} {{{$var} {$var2}}}", + "exp": "1.000 3.00000", + "params": [{ "name": "var", "value": 1}, {"name": "var2", "value": 3 }], + "expErrors": [{ "type": "duplicate-declaration" }] + }, + { "src": ".local $var = {$var2} .local $var2 = {1} {{{$var} {$var2}}}", + "exp": "5 1", + "params": [{ "name": "var2", "value": 5 }], + "expErrors": [{ "type": "duplicate-declaration" }] + } + ] +} diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/unsupported-expressions.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/unsupported-expressions.json new file mode 100644 index 000000000000..7e9a64943bd1 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/unsupported-expressions.json @@ -0,0 +1,77 @@ +{ + "scenario": "Reserved and private annotations", + "description": "Tests for unsupported expressions (reserved/private) (now syntax errors)", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "syntax-error" + } + ] + }, + "tests": [ + { "src": "hello {|4.2| %number}" }, + { "src": "hello {|4.2| %n|um|ber}" }, + { "src": "{+42}" }, + { "src": "hello {|4.2| &num|be|r}" }, + { "src": "hello {|4.2| ^num|be|r}" }, + { "src": "hello {|4.2| +num|be|r}" }, + { "src": "hello {|4.2| ?num|be||r|s}" }, + { "src": "hello {|foo| !number}" }, + { "src": "hello {|foo| *number}" }, + { "src": "hello {?number}" }, + { "src": "{xyzz }" }, + { "src": "hello {$foo ~xyzz }" }, + { "src": "hello {$x xyzz }" }, + { "src": "{ !xyzz }" }, + { "src": "{~xyzz }" }, + { "src": "{ num x \\\\ abcde |aaa||3.14||42| r }" }, + { "src": "hello {$foo >num x \\\\ abcde |aaa||3.14| |42| r }" }, + { "src" : ".input{ $n ~ }{{{$n}}}" }, + { "src": "foo {+reserved}"}, + { "src": "foo {&private}" }, + { "src": "foo {?reserved @a @b=$c}" }, + { "src": "{!.}" }, + { "src": "{! .}" }, + { "src": "{%}" }, + { "src": "{*}" }, + { "src": "{+}" }, + { "src": "{<}" }, + { "src": "{>}" }, + { "src": "{?}" }, + { "src": "{~}" }, + { "src": "{^.}" }, + { "src": "{^ .}" }, + { "src": "{&}" }, + { "src": "{!.\\{}" }, + { "src": "{!. \\{}" }, + { "src": "{!|a|}" }, + { "src": "{^}" }, + { "src": "{!}" }, + { "src": ".match {$foo !select} |1| {{one}} * {{other}}" }, + { "src": ".match {$foo ^select} |1| {{one}} * {{other}}" }, + { "src": ".match {|horse| ^private}\n 1 {{The value is one.}} * {{The value is not one.}}" }, + { "src": ".match {$foo ^select} |1| {{one}} * {{other}}" } + ] +} + diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/unsupported-statements.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/unsupported-statements.json new file mode 100644 index 000000000000..46cf75ac1e99 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/unsupported-statements.json @@ -0,0 +1,27 @@ +{ + "scenario": "Reserved statements", + "description": "Tests for unsupported statements (now syntax errors)", + "defaultTestProperties": { + "locale": "en-US", + "expErrors": [ + { + "type": "syntax-error" + } + ] + }, + "tests": [ + { "src" : ".i {1} {{}}" }, + { "src" : ".l $y = {|bar|} {{}}" }, + { "src" : ".l $x.y = {|bar|} {{}}" }, + { "src": ".matc {-1} {{hello}}" }, + { "src": ".m {-1} {{hello}}" }, + { "src": ".n{a}{{}}" }, + { "src": ".foo {42} {{bar}}" }, + { "src": ".foo{42}{{bar}}" }, + { "src": ".foo |}lit{| {42}{{bar}}" }, + { "src": ".n .{a}{{}}" }, + { "src": ".n. {a}{{}}" }, + { "src": ".n.{a}{b}{{}}" } + ] +} + diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/valid-tests.json b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/valid-tests.json new file mode 100644 index 000000000000..4b1ce8d67fb6 --- /dev/null +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/valid-tests.json @@ -0,0 +1,156 @@ +{ + "scenario": "Valid tests", + "description": "Additional valid tests", + "defaultTestProperties": { + "locale": "en-US" + }, + "tests": [ + { "src": "hello {|4.2| :number minimumFractionDigits=2}", "exp": "hello 4.20"}, + { "src": "hello {|4.2| :number minimumFractionDigits = 2}", "exp": "hello 4.20" }, + { "src": "hello {|4.2| :number minimumFractionDigits= 2}", "exp": "hello 4.20" }, + { "src": "hello {|4.2| :number minimumFractionDigits =2}", "exp": "hello 4.20"}, + { "src": "hello {|4.2| :number minimumFractionDigits=2 }", "exp": "hello 4.20"}, + { "src": "hello {|4.2| :number minimumFractionDigits=2 bar=3}", "exp": "hello 4.20"}, + { "src": "hello {|4.2| :number minimumFractionDigits=2 bar=3 }", "exp": "hello 4.20"}, + { "src": "hello {|4.2| :number minimumFractionDigits=|2|}", "exp": "hello 4.20"}, + { "src": "content -tag", "exp": "content -tag"}, + { "src": "", "exp": ""}, + { "src": "{|hel\\\\lo|}", "exp": "hel\\lo"}, + { "src": "{|hel\\|lo|}", "exp": "hel|lo"}, + { "src": "{|hel\\|\\\\lo|}", "exp": "hel|\\lo"}, + { "src": "hel\\{lo", "exp": "hel{lo"}, + { "src": "hel\\}lo", "exp": "hel}lo"}, + { "src": "hel\\\\lo", "exp": "hel\\lo"}, + { "src": "hel\\{\\\\lo", "exp": "hel{\\lo"}, + { "src": "hel\\{\\}lo", "exp": "hel{}lo"}, + { "src": "hello {|wo\nrld|}", "exp": "hello wo\nrld"}, + { "src": "hello wo\nrld", "exp": "hello wo\nrld"}, + { "src": "{#tag/} content", "exp": " content"}, + { "src": "{#tag} content", "exp": " content"}, + { "src": "{#tag/} {|content|}", "exp": " content"}, + { "src": "{#tag} {|content|}", "exp": " content"}, + { "src": "{|content|} {#tag/}", "exp": "content "}, + { "src": "{|content|} {#tag}", "exp": "content "}, + { "src": "{/tag} {|content|}", "exp": " content"}, + { "src": "{|content|} {/tag}", "exp": "content "}, + { "src": "{#tag} {|content|} {/tag}", "exp": " content "}, + { "src": "{/tag} {|content|} {#tag}", "exp": " content "}, + { "src": "{#tag/} {|content|} {#tag}", "exp": " content "}, + { "src": "{#tag/} {|content|} {/tag}", "exp": " content "}, + { "src": "{#tag foo=bar/} {|content|}", "exp": " content"}, + { "src": "{#tag foo=bar} {|content|}", "exp": " content"}, + { "src": "{/tag foo=bar} {|content|}", "exp": " content"}, + { "src": "{#tag foo=bar} {|content|} {/tag foo=bar}", "exp": " content "}, + { "src": "{/tag foo=bar} {|content|} {#tag foo=bar}", "exp": " content "}, + { "src": "{#tag foo=bar /} {|content|} {#tag foo=bar}", "exp": " content "}, + { "src": "{#tag foo=bar/} {|content|} {/tag foo=bar}", "exp": " content "}, + { "src": "The value is {horse @horse}.", "exp": "The value is horse."}, + { "src": "hello {|4.2| @number}", "exp": "hello 4.2"}, + { "src": "The value is {horse @horse=cool}.", "exp": "The value is horse."}, + { "src": "hello {|4.2| @number=5}", "exp": "hello 4.2"}, + { "src": "{-1}", "exp": "-1"}, + { "src": "{0}", "exp": "0"}, + { "src": "{0.0123}", "exp": "0.0123"}, + { "src": "{1.234e5}", "exp": "1.234e5", + "ignoreJava": "See ICU-22810"}, + { "src": "{1.234E5}", "exp": "1.234E5", + "ignoreJava": "See ICU-22810"}, + { "src": "{1.234E+5}", "exp": "1.234E+5", + "ignoreJava": "See ICU-22810"}, + { "src": "{1.234e-5}", "exp": "1.234e-5", + "ignoreJava": "See ICU-22810"}, + { "src": "{42e5}", "exp": "42e5", + "ignoreJava": "See ICU-22810"}, + { "src": "{42e0}", "exp": "42e0", + "ignoreJava": "See ICU-22810"}, + { "src": "{42e000}", "exp": "42e000", + "ignoreJava": "See ICU-22810"}, + { "src": "{42e369}", "exp": "42e369", + "ignoreJava": "See ICU-22810"}, + { "src": "hello {|3| :number }", "exp": "hello 3" }, + { "src": "{:foo}", "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{:foo }", "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{:foo }", "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{:foo k=v}", "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{:foo k=v }", "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{:foo k1=v1 k2=v2}", "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{:foo k1=v1 k2=v2 }", "expErrors": [{ "type": "unknown-function" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{|3.14| }", "exp": "3.14" }, + { "src": "{|3.14| }", "exp": "3.14" }, + { "src": "{|3.14| :number}", "exp": "3.14" }, + { "src": "{|3.14| :number }", "exp": "3.14" }, + { "src": "{$bar }", "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{$bar }", "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{$bar :foo}", "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{$bar :foo }", "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": "{$bar-foo}", "expErrors": [{ "type": "unresolved-variable" }], + "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" }, + { "src": ".local $foo = {|hello|} .local $foo = {$foo} {{{$foo}}}", + "expErrors": [{ "type": "duplicate-declaration" }] }, + { "src": "good {placeholder}", "exp": "good placeholder" }, + { + "src": "a\\\\qbc", + "exp": "a\\qbc", + "comment": "pattern -> escaped-char -> backslash backslash" + }, + { + "comment": "message -> simple-message -> simple-start pattern -> escaped-char", + "src": "\\\\", + "exp": "\\" + }, + { + "comment": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" s \"#\" identifier \"}\"", + "src": "{ #a}", + "exp": "" + }, + { + "comment": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" s \"/\" identifier \"}\"", + "src": "{ /a}", + "exp": "" + }, + { + "comment": "message -> complex-message -> *(declaration [s]) complex-body -> declaration complex-body -> input-declaration complex-body -> input variable-expression complex-body", + "src": ".input{$x}{{}}", + "exp": "" + }, + { + "comment": "Test that escaped characters are re-escaped properly when serializing literals", + "src": "{|\\}|}", + "exp": "}" + }, + { + "comment": "Test that escaped characters are re-escaped properly when serializing patterns", + "src": "\\|", + "exp": "|" + }, + { + "comment": "Test that parser and serializer treat optionally-escaped characters consistently", + "src" : "{{{1}|}}", + "exp": "1|" + }, + { + "comment": "Trailing whitespace after match is valid", + "src": ".match {1 :string} * {{}} ", + "exp": "" + }, + { + "src" : "𴢸", + "exp" : "𴢸" + }, + { + "src" : "{{􍅲}}", + "exp" : "􍅲" + } + ] +}