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" : "􍅲" + } + ] +}