From b4fbf045cad2369c6e4e7b5304a836008976cb6d Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Mon, 26 Aug 2024 19:58:06 +0300 Subject: [PATCH] Add tests for pattern selection (#863) * Add tests for pattern selection * Add missing errors * Apply suggestions from code review Co-authored-by: Addison Phillips --------- Co-authored-by: Addison Phillips --- test/README.md | 33 +++++- test/tests/functions/number.json | 167 ------------------------------ test/tests/pattern-selection.json | 150 +++++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 171 deletions(-) create mode 100644 test/tests/pattern-selection.json diff --git a/test/README.md b/test/README.md index 95a8ef7f0..6bf731bae 100644 --- a/test/README.md +++ b/test/README.md @@ -65,23 +65,34 @@ The function `:test:function` requires a [Number Operand](/spec/registry.md#numb #### Options -The only _option_ `:test:function` recognizes is `decimalPlaces`, -a _digit size option_ for which only `0` and `1` are valid values. +The following _options_ are available on `:test:function`: +- `decimalPlaces`, a _digit size option_ for which only `0` and `1` are valid values. + - `0` + - `1` +- `fails` + - `never` (default) + - `select` + - `format` + - `always` All other _options_ and their values are ignored. #### Behavior When resolving a `:test:function` expression, -its `Input` and `DecimalPlaces` values are determined as follows: +its `Input`, `DecimalPlaces`, `FailsFormat`, and `FailsSelect` values are determined as follows: 1. Let `DecimalPlaces` be 0. +1. Let `FailsFormat` be `false`. +1. Let `FailsSelect` be `false`. 1. Let `arg` be the resolved value of the _expression_ _operand_. 1. If `arg` is the resolved value of an _expression_ with a `:test:function`, `:test:select`, or `:test:format` _annotation_ for which resolution has succeeded, then 1. Let `Input` be the `Input` value of `arg`. 1. Set `DecimalPlaces` to be `DecimalPlaces` value of `arg`. + 1. Set `FailsFormat` to be `FailsFormat` value of `arg`. + 1. Set `FailsSelect` to be `FailsSelect` value of `arg`. 1. Else if `arg` is a numerical value or a string matching the `number-literal` production, then 1. Let `Input` be the numerical value of `arg`. @@ -96,12 +107,24 @@ its `Input` and `DecimalPlaces` values are determined as follows: 1. Else if its value is not an unresolved value set by _option resolution_, 1. Emit "bad-option" _Resolution Error_. 1. Use a _fallback value_ as the resolved value of the _expression_. +1. If the `fails` _option_ is set, then + 1. If its value resolves to the string `'always'`, then + 1. Set `FailsFormat` to be `true`. + 1. Set `FailsSelect` to be `true`. + 1. Else if its value resolves to the string `'format'`, then + 1. Set `FailsFormat` to be `true`. + 1. Else if its value resolves to the string `'select'`, then + 1. Set `FailsSelect` to be `true`. + 1. Else if its value does not resolve to the string `'never'`, then + 1. Emit "bad-option" _Resolution Error_. When `:test:function` is used as a _selector_, the behaviour of calling it as the `rv` value of MatchSelectorKeys(`rv`, `keys`) (see [Resolve Preferences](/spec/formatting.md#resolve-preferences) for more information) -depends on its `Input` and `DecimalPlaces` values. +depends on its `Input`, `DecimalPlaces` and `FailsSelect` values. +- If `FailsSelect` is `true`, + calling the method will fail and not return any value. - If the `Input` is 1 and `DecimalPlaces` is 1, the method will return some slice of the list « `'1.0'`, `'1'` », depending on whether those values are included in `keys`. @@ -128,6 +151,8 @@ If the formatting target is a sequence of parts, each of the above parts will be emitted separately rather than being concatenated into a single string. +If `FailsFormat` is `true`, +attempting to format the _placeholder_ to any formatting target will fail. ### `:test:select` diff --git a/test/tests/functions/number.json b/test/tests/functions/number.json index f59e77343..2b00d83e4 100644 --- a/test/tests/functions/number.json +++ b/test/tests/functions/number.json @@ -209,173 +209,6 @@ } ] }, - { - "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" - } - ] - }, { "src": "{42 :number @foo @bar=13}", "exp": "42", diff --git a/test/tests/pattern-selection.json b/test/tests/pattern-selection.json new file mode 100644 index 000000000..314d2da78 --- /dev/null +++ b/test/tests/pattern-selection.json @@ -0,0 +1,150 @@ +{ + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", + "scenario": "Pattern selection", + "description": "Tests for pattern selection", + "defaultTestProperties": { + "locale": "und" + }, + "tests": [ + { + "src": ".match {1 :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "1" + }, + { + "src": ".match {0 :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "other" + }, + { + "src": ".match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1" + }, + { + "src": ".match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 2 }], + "exp": "other" + }, + { + "src": ".input {$x} .match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1" + }, + { + "src": ".input {$x} .match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 2 }], + "exp": "other" + }, + { + "src": ".input {$x :test:select} .match {$x} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1" + }, + { + "src": ".input {$x :test:select} .match {$x} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 2 }], + "exp": "other" + }, + { + "src": ".input {$x :test:select} .local $y = {$x} .match {$y} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1" + }, + { + "src": ".input {$x :test:select} .local $y = {$x} .match {$y} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 2 }], + "exp": "other" + }, + { + "src": ".match {1 :test:select decimalPlaces=1} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "1.0" + }, + { + "src": ".match {1 :test:select decimalPlaces=1} 1 {{1}} 1.0 {{1.0}} * {{other}}", + "exp": "1.0" + }, + { + "src": ".match {1 :test:select decimalPlaces=9} 1.0 {{1.0}} 1 {{1}} * {{bad-option-value}}", + "exp": "bad-option-value", + "expErrors": [{ "type": "bad-option" }, { "type": "bad-selector" }] + }, + { + "src": ".input {$x :test:select} .match {$x :test:select decimalPlaces=1} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1.0" + }, + { + "src": ".input {$x :test:select decimalPlaces=1} .match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1.0" + }, + { + "src": ".input {$x :test:select} .local $y = {$x :test:select decimalPlaces=1} .match {$y} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1.0" + }, + { + "src": ".input {$x :test:select decimalPlaces=1} .local $y = {$x :test:select} .match {$y} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1.0" + }, + { + "src": ".input {$x :test:select decimalPlaces=9} .match {$x :test:select decimalPlaces=1} 1.0 {{1.0}} 1 {{1}} * {{bad-option-value}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "bad-option-value", + "expErrors": [ + { "type": "bad-option" }, + { "type": "bad-operand" }, + { "type": "bad-selector" } + ] + }, + { + "src": ".match {1 :test:select fails=select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "other", + "expErrors": [{ "type": "bad-selector" }] + }, + { + "src": ".match {1 :test:select fails=format} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "1" + }, + { + "src": ".match {1 :test:format} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "other", + "expErrors": [{ "type": "bad-selector" }] + }, + { + "src": ".match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "other", + "expErrors": [ + { "type": "unresolved-variable" }, + { "type": "bad-operand" }, + { "type": "bad-selector" } + ] + }, + { + "src": ".match {1 :test:select} {1 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "1,1" + }, + { + "src": ".match {1 :test:select} {0 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "1,*" + }, + { + "src": ".match {0 :test:select} {1 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "*,1" + }, + { + "src": ".match {0 :test:select} {0 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "*,*" + }, + { + "src": ".match {1 :test:select fails=select} {1 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "*,1", + "expErrors": [{ "type": "bad-selector" }] + }, + { + "src": ".match {1 :test:select} {1 :test:format} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "1,*", + "expErrors": [{ "type": "bad-selector" }] + } + ] +}