Skip to content

Commit 04d5b02

Browse files
librazclaude
andcommitted
test(oracle): Relabel mislabelled M365 target as win-2019-ja_JP and add dedicated ARRAYTOTEXT/FILTERXML suites
The host previously committed under win-365-ja_JP turned out to run ProPlus2019Retail (perpetual Office 2019), not Microsoft 365, so modern function goldens were missing entries like XLOOKUP/ARRAYTOTEXT/LET as #NAME?. Move those goldens to a new win-2019-ja_JP target, reserve the win-365-ja_JP slot as wanted for a real M365 install, and split the external_stubs smoke probes for ARRAYTOTEXT and FILTERXML into dedicated parity suites covering escape rules, separators, XPath dialect, namespaces, and CDATA. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d58ea5c commit 04d5b02

98 files changed

Lines changed: 2088 additions & 17 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

tests/divergence.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,13 +1507,13 @@ entries:
15071507
last_verified_excel_version: "16.108.1"
15081508
mode: skip-oracle
15091509
- id: external_stub_arraytotext
1510-
reason: "ARRAYTOTEXT impl exists but full Excel-format parity (escape rules, locale separators) is unverified; closure deferred to a dedicated suite."
1510+
reason: "Single-formula smoke probe inside the external_stubs cluster. Dedicated parity coverage now lives in tests/oracle/cases/arraytotext.yaml (escape rules, separators, array shapes, format-arg coercion); this smoke entry stays skip-oracle so the external_stubs cluster remains aligned across the array probes that Formulon still reduces eagerly."
15111511
prefer: mac-excel-365
15121512
first_noted: 2026-04-28
15131513
last_verified_excel_version: "16.108.1"
15141514
mode: skip-oracle
15151515
- id: external_stub_filterxml
1516-
reason: "FILTERXML uses pugixml's XPath 1.0; Excel's XPath dialect has minor quirks that may diverge. Closure deferred to a dedicated XPath suite."
1516+
reason: "Single-formula smoke probe inside the external_stubs cluster. Dedicated XPath dialect coverage now lives in tests/oracle/cases/filterxml.yaml (single/multi-node, attribute axis, namespaces, CDATA, entity decoding, XPath 1.0 vs 2.0 boundary); this smoke entry stays skip-oracle to keep external_stubs uniform with the other deferred-to-dedicated-suite cases."
15171517
prefer: mac-excel-365
15181518
first_noted: 2026-04-28
15191519
last_verified_excel_version: "16.108.1"
Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
suite: arraytotext
2+
description: >
3+
ARRAYTOTEXT(array, [format]) serialises an array (or scalar) to text in
4+
one of two modes:
5+
6+
* format=0 (concise, default) — element-by-element rendering separated
7+
by a comma+space inter-column delimiter and a semicolon+space
8+
inter-row delimiter (en_US/ja-JP). Text passes through unquoted;
9+
numbers/booleans/blanks render the same as VALUETOTEXT concise.
10+
* format=1 (strict) — wraps the whole result in `{...}` braces. Text
11+
elements are wrapped in double-quotes with embedded `"` doubled to
12+
`""`. Numbers render bare. The element separators match the array
13+
literal grammar: `,` inter-column and `;` inter-row.
14+
15+
Errors propagate from any element (Excel returns the first error
16+
encountered as the function's result, NOT a per-cell error spill).
17+
18+
Probes are organised by axis:
19+
20+
* Scalar passthrough — pins ARRAYTOTEXT == VALUETOTEXT for non-array
21+
arguments. These should hold today; Formulon's ARRAYTOTEXT_ is a
22+
thin alias for VALUETOTEXT_ on scalars.
23+
* Strict-mode quoting — embedded quotes, separators, whitespace,
24+
Unicode. Sensitive to per-host Excel build quirks.
25+
* Array shape — 1xN row, Nx1 column, MxN block, sequences from
26+
SEQUENCE / SORT. Until Formulon's spill pipeline materialises these
27+
as multi-cell arrays the eager dispatcher reduces the input to its
28+
first element, so these cases will diverge until that work lands;
29+
capturing Excel's actual output is the whole point of the suite.
30+
* Format-arg coercion — bool/text format args, out-of-range values
31+
that should surface #VALUE!.
32+
* Error propagation — both arguments.
33+
34+
Locale note: ja-JP Excel keeps `,` and `;` as the inter-column /
35+
inter-row separators in strict mode (the array-literal grammar is
36+
invariant). Concise mode also uses `,` and `;`; this differs from
37+
decimal-comma locales (de-DE, fr-FR) where `\\` and `;` show up. Those
38+
variants belong to the win-365-de_DE / -fr_FR targets.
39+
tolerance: { abs: 0.0, rel: 0.0 }
40+
locale: ja-JP
41+
cases:
42+
# --------------------------------------------------------------------
43+
# Scalar passthrough — concise mode (format=0, default)
44+
# --------------------------------------------------------------------
45+
- id: arraytotext_scalar_number_default
46+
description: "Concise default: integer passes through bare"
47+
formula: "=ARRAYTOTEXT(123)"
48+
49+
- id: arraytotext_scalar_negative_number_default
50+
description: "Concise default: negative number keeps sign"
51+
formula: "=ARRAYTOTEXT(-7.25)"
52+
53+
- id: arraytotext_scalar_zero_default
54+
description: "Concise default: zero renders as 0"
55+
formula: "=ARRAYTOTEXT(0)"
56+
57+
- id: arraytotext_scalar_decimal_default
58+
description: "Concise default: fractional number, no quoting"
59+
formula: "=ARRAYTOTEXT(1.5)"
60+
61+
- id: arraytotext_scalar_small_magnitude_default
62+
description: "Concise default: very small number — VALUETOTEXT threshold"
63+
formula: "=ARRAYTOTEXT(0.000001)"
64+
65+
- id: arraytotext_scalar_large_magnitude_default
66+
description: "Concise default: very large number, expect scientific notation handoff"
67+
formula: "=ARRAYTOTEXT(1.23456789E20)"
68+
69+
- id: arraytotext_scalar_text_default
70+
description: "Concise default: text passes through unquoted"
71+
formula: "=ARRAYTOTEXT(\"hello\")"
72+
73+
- id: arraytotext_scalar_empty_string_default
74+
description: "Concise default: empty string renders as empty"
75+
formula: "=ARRAYTOTEXT(\"\")"
76+
77+
- id: arraytotext_scalar_bool_true_default
78+
description: "Concise default: TRUE renders uppercase"
79+
formula: "=ARRAYTOTEXT(TRUE)"
80+
81+
- id: arraytotext_scalar_bool_false_default
82+
description: "Concise default: FALSE renders uppercase"
83+
formula: "=ARRAYTOTEXT(FALSE)"
84+
85+
# --------------------------------------------------------------------
86+
# Scalar passthrough — strict mode (format=1)
87+
# --------------------------------------------------------------------
88+
- id: arraytotext_scalar_text_strict
89+
description: "Strict: scalar text wraps in double-quotes"
90+
formula: "=ARRAYTOTEXT(\"hello\",1)"
91+
92+
- id: arraytotext_scalar_empty_string_strict
93+
description: "Strict: empty string is quoted as \"\""
94+
formula: "=ARRAYTOTEXT(\"\",1)"
95+
96+
- id: arraytotext_scalar_number_strict
97+
description: "Strict: scalar number stays bare (no braces, no quotes)"
98+
formula: "=ARRAYTOTEXT(42,1)"
99+
100+
- id: arraytotext_scalar_bool_strict
101+
description: "Strict: scalar TRUE stays uppercase, no quotes"
102+
formula: "=ARRAYTOTEXT(TRUE,1)"
103+
104+
- id: arraytotext_scalar_text_embedded_quote_strict
105+
description: "Strict: embedded \" inside text is doubled to \"\""
106+
formula: "=ARRAYTOTEXT(\"a\"\"b\",1)"
107+
108+
- id: arraytotext_scalar_text_embedded_comma_strict
109+
description: "Strict: embedded comma keeps its place inside the quoted token"
110+
formula: "=ARRAYTOTEXT(\"a,b\",1)"
111+
112+
- id: arraytotext_scalar_text_embedded_semicolon_strict
113+
description: "Strict: embedded semicolon stays inside the quoted token"
114+
formula: "=ARRAYTOTEXT(\"a;b\",1)"
115+
116+
- id: arraytotext_scalar_text_with_braces_strict
117+
description: "Strict: text containing `{` and `}` is not escaped further"
118+
formula: "=ARRAYTOTEXT(\"{a}\",1)"
119+
120+
- id: arraytotext_scalar_text_with_newline_strict
121+
description: "Strict: embedded LF — kept verbatim in quoted token"
122+
formula: "=ARRAYTOTEXT(\"a\" & CHAR(10) & \"b\",1)"
123+
124+
- id: arraytotext_scalar_text_with_tab_strict
125+
description: "Strict: embedded tab — kept verbatim"
126+
formula: "=ARRAYTOTEXT(\"a\" & CHAR(9) & \"b\",1)"
127+
128+
- id: arraytotext_scalar_text_unicode_jp_strict
129+
description: "Strict: Japanese text inside quoted token"
130+
formula: "=ARRAYTOTEXT(\"日本語\",1)"
131+
132+
- id: arraytotext_scalar_text_with_backslash_strict
133+
description: "Strict: backslash is not an escape char in Excel — passes through"
134+
formula: "=ARRAYTOTEXT(\"a\\b\",1)"
135+
136+
# --------------------------------------------------------------------
137+
# 1xN row arrays — concise vs strict
138+
# --------------------------------------------------------------------
139+
- id: arraytotext_row_numbers_default
140+
description: "Row of numbers, concise: `a, b, c`"
141+
formula: "=ARRAYTOTEXT({1,2,3})"
142+
143+
- id: arraytotext_row_numbers_strict
144+
description: "Row of numbers, strict: `{a,b,c}`"
145+
formula: "=ARRAYTOTEXT({1,2,3},1)"
146+
147+
- id: arraytotext_row_text_default
148+
description: "Row of text, concise: unquoted, comma-separated"
149+
formula: "=ARRAYTOTEXT({\"a\",\"b\",\"c\"})"
150+
151+
- id: arraytotext_row_text_strict
152+
description: "Row of text, strict: each element quoted"
153+
formula: "=ARRAYTOTEXT({\"a\",\"b\",\"c\"},1)"
154+
155+
- id: arraytotext_row_mixed_default
156+
description: "Row mixing number, text, bool, concise mode"
157+
formula: "=ARRAYTOTEXT({1,\"two\",TRUE})"
158+
159+
- id: arraytotext_row_mixed_strict
160+
description: "Row mixing number, text, bool, strict mode"
161+
formula: "=ARRAYTOTEXT({1,\"two\",TRUE},1)"
162+
163+
- id: arraytotext_row_with_embedded_quote_strict
164+
description: "Row element containing `\"` — strict mode escapes to `\"\"`"
165+
formula: "=ARRAYTOTEXT({\"a\"\"b\",\"c\"},1)"
166+
167+
- id: arraytotext_row_with_embedded_separators_strict
168+
description: "Row elements containing `,` and `;` — strict keeps them inside quotes"
169+
formula: "=ARRAYTOTEXT({\"a,b\",\"c;d\"},1)"
170+
171+
- id: arraytotext_singleton_row_strict
172+
description: "1x1 row {\"only\"} — strict still emits `{\"only\"}` braces"
173+
formula: "=ARRAYTOTEXT({\"only\"},1)"
174+
175+
- id: arraytotext_singleton_row_default
176+
description: "1x1 row in concise mode — passthrough as scalar text"
177+
formula: "=ARRAYTOTEXT({\"only\"})"
178+
179+
# --------------------------------------------------------------------
180+
# Nx1 column arrays
181+
# --------------------------------------------------------------------
182+
- id: arraytotext_column_numbers_default
183+
description: "Column of numbers, concise: semicolon+space row separator"
184+
formula: "=ARRAYTOTEXT({1;2;3})"
185+
186+
- id: arraytotext_column_numbers_strict
187+
description: "Column of numbers, strict: `{a;b;c}`"
188+
formula: "=ARRAYTOTEXT({1;2;3},1)"
189+
190+
- id: arraytotext_column_text_default
191+
description: "Column of text, concise"
192+
formula: "=ARRAYTOTEXT({\"x\";\"y\";\"z\"})"
193+
194+
- id: arraytotext_column_text_strict
195+
description: "Column of text, strict"
196+
formula: "=ARRAYTOTEXT({\"x\";\"y\";\"z\"},1)"
197+
198+
# --------------------------------------------------------------------
199+
# MxN block arrays
200+
# --------------------------------------------------------------------
201+
- id: arraytotext_2x2_numbers_default
202+
description: "2x2 numbers, concise"
203+
formula: "=ARRAYTOTEXT({1,2;3,4})"
204+
205+
- id: arraytotext_2x2_numbers_strict
206+
description: "2x2 numbers, strict: `{a,b;c,d}`"
207+
formula: "=ARRAYTOTEXT({1,2;3,4},1)"
208+
209+
- id: arraytotext_2x3_mixed_default
210+
description: "2x3 mix of text and numbers, concise"
211+
formula: "=ARRAYTOTEXT({\"a\",1,TRUE;\"b\",2,FALSE})"
212+
213+
- id: arraytotext_2x3_mixed_strict
214+
description: "2x3 mix, strict — quoting / brace order"
215+
formula: "=ARRAYTOTEXT({\"a\",1,TRUE;\"b\",2,FALSE},1)"
216+
217+
# --------------------------------------------------------------------
218+
# Dynamic-array producers — SEQUENCE / TRANSPOSE / ROW
219+
# --------------------------------------------------------------------
220+
- id: arraytotext_sequence_row_default
221+
description: "SEQUENCE(1,5) → 1x5 row, concise"
222+
formula: "=ARRAYTOTEXT(SEQUENCE(1,5))"
223+
224+
- id: arraytotext_sequence_row_strict
225+
description: "SEQUENCE(1,5) → 1x5 row, strict"
226+
formula: "=ARRAYTOTEXT(SEQUENCE(1,5),1)"
227+
228+
- id: arraytotext_sequence_column_default
229+
description: "SEQUENCE(5) → 5x1 column, concise"
230+
formula: "=ARRAYTOTEXT(SEQUENCE(5))"
231+
232+
- id: arraytotext_sequence_column_strict
233+
description: "SEQUENCE(5) → 5x1 column, strict"
234+
formula: "=ARRAYTOTEXT(SEQUENCE(5),1)"
235+
236+
- id: arraytotext_sequence_2d_default
237+
description: "SEQUENCE(2,3) → 2x3, concise"
238+
formula: "=ARRAYTOTEXT(SEQUENCE(2,3))"
239+
240+
- id: arraytotext_sequence_2d_strict
241+
description: "SEQUENCE(2,3) → 2x3, strict"
242+
formula: "=ARRAYTOTEXT(SEQUENCE(2,3),1)"
243+
244+
- id: arraytotext_transposed_row_strict
245+
description: "TRANSPOSE({1;2;3}) — 1x3 row from a column source"
246+
formula: "=ARRAYTOTEXT(TRANSPOSE({1;2;3}),1)"
247+
248+
# --------------------------------------------------------------------
249+
# Blank handling — empty cells inside an array reference
250+
# --------------------------------------------------------------------
251+
- id: arraytotext_range_with_blank_default
252+
description: "Range with a blank cell — blank renders as empty token in concise"
253+
formula: "=ARRAYTOTEXT(A1:A3)"
254+
setup:
255+
A1: "alpha"
256+
A2: null # blank
257+
A3: "gamma"
258+
259+
- id: arraytotext_range_with_blank_strict
260+
description: "Range with a blank cell — blank renders as \"\" in strict"
261+
formula: "=ARRAYTOTEXT(A1:A3,1)"
262+
setup:
263+
A1: "alpha"
264+
A2: null # blank
265+
A3: "gamma"
266+
267+
- id: arraytotext_range_all_blanks_default
268+
description: "All-blank range — concise mode separators only"
269+
formula: "=ARRAYTOTEXT(A1:A2)"
270+
setup:
271+
A1: null
272+
A2: null
273+
274+
- id: arraytotext_range_all_blanks_strict
275+
description: "All-blank range — strict mode emits `{,}` style"
276+
formula: "=ARRAYTOTEXT(A1:A2,1)"
277+
setup:
278+
A1: null
279+
A2: null
280+
281+
- id: arraytotext_range_mixed_kinds_strict
282+
description: "Range mixing text/number/bool/blank — strict mode formatting"
283+
formula: "=ARRAYTOTEXT(A1:A4,1)"
284+
setup:
285+
A1: "name"
286+
A2: 42
287+
A3: true
288+
A4: null
289+
290+
# --------------------------------------------------------------------
291+
# Format-arg coercion
292+
# --------------------------------------------------------------------
293+
- id: arraytotext_format_true_strict_alias
294+
description: "format=TRUE → strict mode (TRUE coerces to 1)"
295+
formula: "=ARRAYTOTEXT(\"hi\",TRUE)"
296+
297+
- id: arraytotext_format_false_concise_alias
298+
description: "format=FALSE → concise mode (FALSE coerces to 0)"
299+
formula: "=ARRAYTOTEXT(\"hi\",FALSE)"
300+
301+
- id: arraytotext_format_text_zero
302+
description: "format=\"0\" — text-numeric coercion to concise"
303+
formula: "=ARRAYTOTEXT(\"hi\",\"0\")"
304+
305+
- id: arraytotext_format_text_one
306+
description: "format=\"1\" — text-numeric coercion to strict"
307+
formula: "=ARRAYTOTEXT(\"hi\",\"1\")"
308+
309+
- id: arraytotext_format_out_of_range_two
310+
description: "format=2 — out of {0,1} → #VALUE!"
311+
formula: "=ARRAYTOTEXT(\"hi\",2)"
312+
313+
- id: arraytotext_format_negative
314+
description: "format=-1 — out of range → #VALUE!"
315+
formula: "=ARRAYTOTEXT(\"hi\",-1)"
316+
317+
- id: arraytotext_format_text_nonnumeric
318+
description: "format=\"abc\" — non-numeric text → #VALUE!"
319+
formula: "=ARRAYTOTEXT(\"hi\",\"abc\")"
320+
321+
# --------------------------------------------------------------------
322+
# Error propagation
323+
# --------------------------------------------------------------------
324+
- id: arraytotext_error_in_value_arg
325+
description: "First arg #DIV/0! — propagates the error"
326+
formula: "=ARRAYTOTEXT(1/0)"
327+
328+
- id: arraytotext_error_in_format_arg
329+
description: "Format arg #DIV/0! — propagates the error"
330+
formula: "=ARRAYTOTEXT(\"hi\",1/0)"
331+
332+
- id: arraytotext_error_in_array_default
333+
description: "Array containing an error — concise mode: first error returned"
334+
formula: "=ARRAYTOTEXT({1,NA(),3})"
335+
336+
- id: arraytotext_error_in_array_strict
337+
description: "Array containing an error — strict mode: same error propagation"
338+
formula: "=ARRAYTOTEXT({1,NA(),3},1)"
339+
340+
- id: arraytotext_only_errors_default
341+
description: "Array of all errors — first error returned"
342+
formula: "=ARRAYTOTEXT({NA(),#DIV/0!})"
343+
344+
- id: arraytotext_arity_zero
345+
description: "Zero args — #VALUE! (Excel signature requires array)"
346+
formula: "=ARRAYTOTEXT()"
347+
348+
- id: arraytotext_arity_three
349+
description: "Three args — #VALUE! (Excel signature is at most 2)"
350+
formula: "=ARRAYTOTEXT(\"a\",0,0)"

0 commit comments

Comments
 (0)