1 // © 2022 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 4 package com.ibm.icu.dev.test.message2; 5 6 import org.junit.Test; 7 import org.junit.runner.RunWith; 8 import org.junit.runners.JUnit4; 9 10 import com.ibm.icu.dev.test.TestFmwk; 11 12 /** 13 * These tests come from the test suite created for the JavaScript implementation of MessageFormat v2. 14 * 15 * <p>Original JSON file 16 * <a href="https://github.com/messageformat/messageformat/blob/master/packages/mf2-messageformat/src/__fixtures/test-messages.json">here</a>.</p> 17 */ 18 @RunWith(JUnit4.class) 19 @SuppressWarnings("javadoc") 20 public class FromJsonTest extends TestFmwk { 21 22 static final TestCase[] TEST_CASES = { 23 new TestCase.Builder() 24 .pattern("{hello}") 25 .expected("hello") 26 .build(), 27 new TestCase.Builder() 28 .pattern("{hello {(world)}}") 29 .expected("hello world") 30 .build(), 31 new TestCase.Builder() 32 .pattern("{hello {()}}") 33 .expected("hello ") 34 .build(), 35 new TestCase.Builder() 36 .pattern("{hello {$place}}") 37 .arguments(Args.of("place", "world")) 38 .expected("hello world") 39 .build(), 40 new TestCase.Builder() 41 .pattern("{hello {$place}}") 42 .expected("hello {$place}") 43 // errorsJs: ["missing-var"] 44 .build(), 45 new TestCase.Builder() 46 .pattern("{{$one} and {$two}}") 47 .arguments(Args.of("one", 1.3, "two", 4.2)) 48 .expected("1.3 and 4.2") 49 .build(), 50 new TestCase.Builder() 51 .pattern("{{$one} et {$two}}") 52 .locale("fr") 53 .arguments(Args.of("one", 1.3, "two", 4.2)) 54 .expected("1,3 et 4,2") 55 .build(), 56 new TestCase.Builder() 57 .pattern("{hello {(4.2) :number}}") 58 .expected("hello 4.2") 59 .build(), 60 new TestCase.Builder() // not in the original JSON 61 .locale("ar-EG") 62 .pattern("{hello {(4.2) :number}}") 63 .expected("hello \u0664\u066B\u0662") 64 .build(), 65 new TestCase.Builder() 66 .pattern("{hello {(foo) :number}}") 67 .expected("hello NaN") 68 .build(), 69 new TestCase.Builder() 70 .pattern("{hello {:number}}") 71 .expected("hello NaN") 72 // This is different from JS, should be an error. 73 .errors("ICU4J: exception.") 74 .build(), 75 new TestCase.Builder() 76 .pattern("{hello {(4.2) :number minimumFractionDigits=2}}") 77 .expected("hello 4.20") 78 .build(), 79 new TestCase.Builder() 80 .pattern("{hello {(4.2) :number minimumFractionDigits=(2)}}") 81 .expected("hello 4.20") 82 .build(), 83 new TestCase.Builder() 84 .pattern("{hello {(4.2) :number minimumFractionDigits=$foo}}") 85 .arguments(Args.of("foo", 2f)) 86 .expected("hello 4.20") 87 .build(), 88 new TestCase.Builder() 89 .pattern("{hello {(4.2) :number minimumFractionDigits=$foo}}") 90 .arguments(Args.of("foo", "2")) 91 .expected("hello 4.20") 92 // errorsJs: ["invalid-type"] 93 .build(), 94 new TestCase.Builder() 95 .pattern("let $foo = {(bar)} {bar {$foo}}") 96 .expected("bar bar") 97 .build(), 98 new TestCase.Builder() 99 .pattern("let $foo = {(bar)} {bar {$foo}}") 100 .arguments(Args.of("foo", "foo")) 101 // expectedJs: "bar foo" 102 // It is undefined if we allow arguments to override local variables, or it is an error. 103 // And undefined who wins if that happens, the local variable of the argument. 104 .expected("bar bar") 105 .build(), 106 new TestCase.Builder() 107 .pattern("let $foo = {$bar} {bar {$foo}}") 108 .arguments(Args.of("bar", "foo")) 109 .expected("bar foo") 110 .build(), 111 new TestCase.Builder() 112 .pattern("let $foo = {$bar :number} {bar {$foo}}") 113 .arguments(Args.of("bar", 4.2)) 114 .expected("bar 4.2") 115 .build(), 116 new TestCase.Builder() 117 .pattern("let $foo = {$bar :number minimumFractionDigits=2} {bar {$foo}}") 118 .arguments(Args.of("bar", 4.2)) 119 .expected("bar 4.20") 120 .build(), 121 new TestCase.Builder().ignore("Maybe") // Because minimumFractionDigits=foo 122 .pattern("let $foo = {$bar :number minimumFractionDigits=foo} {bar {$foo}}") 123 .arguments(Args.of("bar", 4.2)) 124 .expected("bar 4.2") 125 .errors("invalid-type") 126 .build(), 127 new TestCase.Builder().ignore("Maybe. Function specific behavior.") 128 .pattern("let $foo = {$bar :number} {bar {$foo}}") 129 .arguments(Args.of("bar", "foo")) 130 .expected("bar NaN") 131 .build(), 132 new TestCase.Builder() 133 .pattern("let $foo = {$bar} let $bar = {$baz} {bar {$foo}}") 134 .arguments(Args.of("baz", "foo")) 135 // expectedJs: "bar foo" 136 // It is currently undefined if a local variable (like $foo) 137 // can reference a local variable that was not yet defined (like $bar). 138 // That is called hoisting and it is valid in JavaScript or Python. 139 // Not allowing that would prevent circular references. 140 // https://github.com/unicode-org/message-format-wg/issues/292 141 .expected("bar {$bar}") 142 .build(), 143 new TestCase.Builder() 144 .patternJs("match {$foo} when (1) {one} when * {other}") 145 .pattern("match {$foo :select} when (1) {one} when * {other}") 146 .arguments(Args.of("foo", "1")) 147 .expected("one") 148 .build(), 149 new TestCase.Builder() 150 .pattern("match {$foo :plural} when 1 {one} when * {other}") 151 .arguments(Args.of("foo", "1")) // Should this be error? Plural on string? 152 // expectedJs: "one" 153 .expected("other") 154 .build(), 155 new TestCase.Builder() 156 .pattern("match {$foo :select} when (1) {one} when * {other}") 157 .arguments(Args.of("foo", "1")) 158 .expected("one") 159 .build(), 160 new TestCase.Builder() 161 .patternJs("match {$foo} when 1 {one} when * {other}") 162 .pattern("match {$foo :plural} when 1 {one} when * {other}") 163 .arguments(Args.of("foo", 1)) 164 .expected("one") 165 .build(), 166 new TestCase.Builder() 167 .pattern("match {$foo :plural} when 1 {one} when * {other}") 168 .arguments(Args.of("foo", 1)) 169 .expected("one") 170 .build(), 171 new TestCase.Builder().ignore("not possible to put a null in a map") 172 .pattern("match {$foo} when 1 {one} when * {other}") 173 .arguments(Args.of("foo", null)) 174 .expected("other") 175 .build(), 176 new TestCase.Builder() 177 .patternJs("match {$foo} when 1 {one} when * {other}") 178 .pattern("match {$foo :plural} when 1 {one} when * {other}") 179 .expected("other") 180 .errors("missing-var") 181 .build(), 182 new TestCase.Builder() 183 .patternJs("match {$foo} when one {one} when * {other}") 184 .pattern("match {$foo :plural} when one {one} when * {other}") 185 .arguments(Args.of("foo", 1)) 186 .expected("one") 187 .build(), 188 new TestCase.Builder() 189 .patternJs("match {$foo} when 1 {=1} when one {one} when * {other}") 190 .pattern("match {$foo :plural} when 1 {=1} when one {one} when * {other}") 191 .arguments(Args.of("foo", 1)) 192 .expected("=1") 193 .build(), 194 new TestCase.Builder() 195 .patternJs("match {$foo} when one {one} when 1 {=1} when * {other}") 196 .pattern("match {$foo :plural} when one {one} when 1 {=1} when * {other}") 197 .arguments(Args.of("foo", 1)) 198 .expected("one") 199 .build(), 200 new TestCase.Builder() 201 .patternJs("match {$foo} {$bar} when one one {one one} when one * {one other} when * * {other}") 202 .pattern("match {$foo :plural} {$bar :plural} when one one {one one} when one * {one other} when * * {other}") 203 .arguments(Args.of("foo", 1, "bar", 1)) 204 .expected("one one") 205 .build(), 206 new TestCase.Builder() 207 .patternJs("match {$foo} {$bar} when one one {one one} when one * {one other} when * * {other}") 208 .pattern("match {$foo :plural} {$bar :plural} when one one {one one} when one * {one other} when * * {other}") 209 .arguments(Args.of("foo", 1, "bar", 2)) 210 .expected("one other") 211 .build(), 212 new TestCase.Builder() 213 .patternJs("match {$foo} {$bar} when one one {one one} when one * {one other} when * * {other}") 214 .pattern("match {$foo :plural} {$bar :plural} when one one {one one} when one * {one other} when * * {other}") 215 .arguments(Args.of("foo", 2, "bar", 2)) 216 .expected("other") 217 .build(), 218 new TestCase.Builder() 219 .patternJs("let $foo = {$bar} match {$foo} when one {one} when * {other}") 220 .pattern("let $foo = {$bar} match {$foo :plural} when one {one} when * {other}") 221 .arguments(Args.of("bar", 1)) 222 .expected("one") 223 .build(), 224 new TestCase.Builder() 225 .patternJs("let $foo = {$bar} match {$foo} when one {one} when * {other}") 226 .pattern("let $foo = {$bar} match {$foo :plural} when one {one} when * {other}") 227 .arguments(Args.of("bar", 2)) 228 .expected("other") 229 .build(), 230 new TestCase.Builder() 231 .patternJs("let $bar = {$none} match {$foo} when one {one} when * {{$bar}}") 232 .pattern("let $bar = {$none} match {$foo :plural} when one {one} when * {{$bar}}") 233 .arguments(Args.of("foo", 1)) 234 .expected("one") 235 .build(), 236 new TestCase.Builder() 237 .patternJs("let $bar = {$none} match {$foo} when one {one} when * {{$bar}}") 238 .pattern("let $bar = {$none :plural} match {$foo} when one {one} when * {{$bar}}") 239 .arguments(Args.of("foo", 2)) 240 .expected("{$bar}") 241 .errors("missing-var") 242 .build(), 243 new TestCase.Builder() 244 .pattern("let bar = {(foo)} {{$bar}}") 245 .expected("{$bar}") 246 .errors("missing-char", "missing-var") 247 .build(), 248 new TestCase.Builder() 249 .pattern("let $bar {(foo)} {{$bar}}") 250 .expected("foo") 251 .errors("missing-char") 252 .build(), 253 new TestCase.Builder() 254 .pattern("let $bar = (foo) {{$bar}}") 255 .expected("{$bar}") 256 .errors("missing-char", "junk-element") 257 .build(), 258 new TestCase.Builder().ignore("no markup support") 259 .pattern("{{+tag}}") 260 .expected("{+tag}") 261 .build(), 262 new TestCase.Builder().ignore("no markup support") 263 .pattern("{{+tag}content}") 264 .expected("{+tag}content") 265 .build(), 266 new TestCase.Builder().ignore("no markup support") 267 .pattern("{{+tag}content{-tag}}") 268 .expected("{+tag}content{-tag}") 269 .build(), 270 new TestCase.Builder().ignore("no markup support") 271 .pattern("{{-tag}content}") 272 .expected("{-tag}content") 273 .build(), 274 new TestCase.Builder().ignore("no markup support") 275 .pattern("{{+tag foo=bar}}") 276 .expected("{+tag foo=bar}") 277 .build(), 278 new TestCase.Builder().ignore("no markup support") 279 .pattern("{{+tag foo=(foo) bar=$bar}}") 280 .arguments(Args.of("bar", "b a r")) 281 .expected("{+tag foo=foo bar=(b a r)}") 282 .build(), 283 new TestCase.Builder() 284 .pattern("{bad {(foo) +markup}}") 285 .expected("bad {+markup}") 286 .errors("extra-content") 287 .build(), 288 new TestCase.Builder() 289 .pattern("{{-tag foo=bar}}") 290 .expected("{-tag}") 291 .errors("extra-content") 292 .build(), 293 new TestCase.Builder() 294 .pattern("no braces") 295 .expected("{no braces}") 296 .errors("parse-error", "junk-element") 297 .build(), 298 new TestCase.Builder() 299 .pattern("no braces {$foo}") 300 .arguments(Args.of("foo", 2)) 301 .expected("{no braces {$foo}}") 302 .errors("parse-error", "junk-element") 303 .build(), 304 new TestCase.Builder().ignore("infinite loop!") 305 .pattern("{missing end brace") 306 .expected("missing end brace") 307 .errors("missing-char") 308 .build(), 309 new TestCase.Builder() 310 .pattern("{missing end {$brace") 311 .expected("missing end {$brace}") 312 .errors("missing-char", "missing-char", "missing-var") 313 .build(), 314 new TestCase.Builder() 315 .pattern("{extra} content") 316 .expected("extra") 317 .errors("extra-content") 318 .build(), 319 new TestCase.Builder() 320 .pattern("{empty { }}") 321 .expected("empty ") 322 // errorsJs: ["parse-error", "junk-element"] 323 .build(), 324 new TestCase.Builder() 325 .pattern("{bad {:}}") 326 .expected("bad {:}") 327 .errors("empty-token", "missing-func") 328 .build(), 329 new TestCase.Builder() 330 .pattern("{bad {placeholder}}") 331 .expected("bad {placeholder}") 332 .errors("parse-error", "extra-content", "junk-element") 333 .build(), 334 new TestCase.Builder() 335 .pattern("{no-equal {(42) :number minimumFractionDigits 2}}") 336 .expected( "no-equal 42.00") 337 .errors("missing-char") 338 .build(), 339 new TestCase.Builder() 340 .pattern("{bad {:placeholder option=}}") 341 .expected("bad {:placeholder}") 342 .errors("empty-token", "missing-func") 343 .build(), 344 new TestCase.Builder() 345 .pattern("{bad {:placeholder option value}}") 346 .expected("bad {:placeholder}") 347 .errors("missing-char", "missing-func") 348 .build(), 349 new TestCase.Builder() 350 .pattern("{bad {:placeholder option}}") 351 .expected("bad {:placeholder}") 352 .errors("missing-char", "empty-token", "missing-func") 353 .build(), 354 new TestCase.Builder() 355 .pattern("{bad {$placeholder option}}") 356 .expected("bad {$placeholder}") 357 .errors("extra-content", "extra-content", "missing-var") 358 .build(), 359 new TestCase.Builder() 360 .pattern("{no {$placeholder end}") 361 .expected("no {$placeholder}") 362 .errors("extra-content", "missing-var") 363 .build(), 364 new TestCase.Builder() 365 .pattern("match {} when * {foo}") 366 .expected("foo") 367 .errors("parse-error", "bad-selector", "junk-element") 368 .build(), 369 new TestCase.Builder() 370 .pattern("match {+foo} when * {foo}") 371 .expected("foo") 372 .errors("bad-selector") 373 .build(), 374 new TestCase.Builder() 375 .pattern("match {(foo)} when*{foo}") 376 .expected("foo") 377 .errors("missing-char") 378 .build(), 379 new TestCase.Builder() 380 .pattern("match when * {foo}") 381 .expected("foo") 382 .errors("empty-token") 383 .build(), 384 new TestCase.Builder() 385 .pattern("match {(x)} when * foo") 386 .expected("") 387 .errors("key-mismatch", "missing-char") 388 .build(), 389 new TestCase.Builder() 390 .pattern("match {(x)} when * {foo} extra") 391 .expected("foo") 392 .errors("extra-content") 393 .build(), 394 new TestCase.Builder() 395 .pattern("match (x) when * {foo}") 396 .expected("") 397 .errors("empty-token", "extra-content") 398 .build(), 399 new TestCase.Builder() 400 .pattern("match {$foo} when * * {foo}") 401 .expected("foo") 402 .errors("key-mismatch", "missing-var") 403 .build(), 404 new TestCase.Builder() 405 .pattern("match {$foo} {$bar} when * {foo}") 406 .expected("foo") 407 .errors("key-mismatch", "missing-var", "missing-var") 408 .build() 409 }; 410 411 @Test test()412 public void test() { 413 int ignoreCount = 0; 414 for (TestCase testCase : TEST_CASES) { 415 if (testCase.ignore) 416 ignoreCount++; 417 TestUtils.runTestCase(testCase); 418 } 419 System.out.printf("Executed %d test cases out of %d, skipped %d%n", 420 TEST_CASES.length - ignoreCount, TEST_CASES.length, ignoreCount); 421 } 422 } 423