From 756cc86011d0144d3a6af379d1e822dfe8ab8a5f Mon Sep 17 00:00:00 2001 From: hhimanshu <6589036+hhimanshu@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:24:39 +1200 Subject: [PATCH] fixtures(google): SPARKLINE conformance rows (#766) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 115 rows exported from the Google Sheets conformance fixtures, evaluated against live Sheets across eight probe batches. Every batch after the first was triggered by a specific finding that could not be settled by argument. 114 of them go to google.tsv. The 115th, `=MIN(SPARKLINE({1,2,3}),{})` → `#REF!`, is a known engine divergence and goes to bugs.tsv, this repo's home for GS-captured rows the engine does not yet match: MIN has no empty-array rule at all, so it answers 0 — for `=MIN({})` too, with no sparkline involved. `=MAX({})` is `#REF!`, so MAX's counterpart row passes. The gap predates sparklines and is unrelated to them; fixing it from here would move MIN for inputs that have nothing to do with this work. The row is recorded verbatim, so it flips to passing the moment MIN is fixed and can then move to google.tsv. The second batch supplied the control the first was missing: `=SPARKLINE({1,2,3})=SPARKLINE({1,2,3})` → TRUE looked like evidence that a sparkline's value identity is its parsed spec, but `=SPARKLINE({1,2,3})=SPARKLINE({9,9,9})` → TRUE disproves it — every sparkline compares equal to every other. COUNTUNIQUE (2 for different, 1 for identical) is what actually requires the spec to be retained. Two row descriptions in that batch ("identity: different data is not equal", "...different charttype is not equal") were written before the run and read backwards against their own recorded TRUE; the recorded values, not the descriptions, are the ground truth. The third and fourth batches settled the currency/conversion split — DOLLAR and FIXED reject a sparkline while the whole TO_* family answers "" — plus the single-argument aggregates and the blanket numeric rejection (ROUND, ABS, INT, unary minus and percent). The fifth and sixth batches probe a sparkline arriving through a RANGE, from a cell holding `=SPARKLINE(...)` (`Data!K1`, with 5 in `Data!K2`) — how a real workbook delivers one. `=PRODUCT(Data!K1:K1)` is 0, so an aggregate answers the same whichever way the sparkline arrived; the two-cell rows cannot settle that alone, since 1 × 5 = 5 either way. `=TYPE(Data!K1)` is 128: reading a cell is not a coercion point, despite the empty display. The seventh batch closes the aggregate family: every aggregate answers 0 when a sparkline is the only thing in scope — MAX, MIN, MAXA and MINA included — with AVERAGE's #DIV/0! the single exception. MINA matching MAXA was a coin-flip until probed: it had no implementation arm at all. The eighth batch backs the one direct form that was still being inferred from its range form (`=MIN(SPARKLINE({1,2,3}))` → 0) and finds a boundary: an explicitly empty array argument outranks the sparkline skip (`=MAX(SPARKLINE({1,2,3}),{})` → #REF!) while a text-only array does not (`=MAX(SPARKLINE({1,2,3}),{"a"})` → 0). Coverage: each charttype, an omitted charttype, colour options, ymin/ymax, text inside the data, all-negative data, a genuine blank cell inside a real source range, `bar` given three values, an unknown option key, an unknown charttype value, a malformed options shape, the value-model probes, identity and ordering against sparklines and scalars, the EQ/NE/GTE alias functions, the three coercion seams, the TO_* family, presence (COUNTA, ISBLANK), COUNTUNIQUE, and the aggregates direct, through a range, and against empty and text-only arrays. 69 of google.tsv's 114 SPARKLINE rows are enforced by the conformance runner. Of the other 45, 29 record an empty expected value (a rendered chart has no text projection) and 16 read authored input cells the standalone runner has no workbook to supply — it skips both, and now says so per file (core#767). crates/core/tests/sparkline.rs asserts those cases directly, the range rows against a seeded resolver. One row is deliberately named as a parser artefact: `=SPARKLINE({1,,3})` → `#ERROR!` is Sheets rejecting the array-literal syntax before SPARKLINE runs, not a blank-cell rule. The `Data!H`/`Data!I` rows are the trustworthy blank-cell probe and its all-present control. Refs #766 --- .../tests/fixtures/google_sheets/bugs.tsv | 1 + .../tests/fixtures/google_sheets/google.tsv | 114 ++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/crates/core/tests/fixtures/google_sheets/bugs.tsv b/crates/core/tests/fixtures/google_sheets/bugs.tsv index 59adc1ee5..679296431 100644 --- a/crates/core/tests/fixtures/google_sheets/bugs.tsv +++ b/crates/core/tests/fixtures/google_sheets/bugs.tsv @@ -2236,3 +2236,4 @@ SHEET inside IF =IF(SHEET()=1,"first sheet","other") other nested string STEYX with floating-point coordinates =STEYX({1.1,2.2,3.3,4.4},{0.9,1.8,2.7,3.6}) #NUM! compatibility error SHEETS with no argument returns count of sheets =SHEETS() 19 official number GS and Excel both have SHEETS function =SHEETS() 19 compatibility number +MIN with a sparkline and an empty array =MIN(SPARKLINE({1,2,3}),{}) #REF! edge error diff --git a/crates/core/tests/fixtures/google_sheets/google.tsv b/crates/core/tests/fixtures/google_sheets/google.tsv index c06d3a69e..c1bcd2a5a 100644 --- a/crates/core/tests/fixtures/google_sheets/google.tsv +++ b/crates/core/tests/fixtures/google_sheets/google.tsv @@ -13,3 +13,117 @@ too many arguments returns error =ARRAYFORMULA({1,2},{3,4}) #N/A error error nested ARRAYFORMULA SUM in IF =IF(ARRAYFORMULA(SUM({1,2,3}))=6,"correct","wrong") correct nested string nested ARRAYFORMULA with UPPER =ARRAYFORMULA(UPPER({"hello","world"})) HELLO nested string GS-only — Excel uses Ctrl+Shift+Enter array formulas or dynamic arrays natively =ARRAYFORMULA({1,2,3}*{4,5,6}) 4 compatibility number +SPARKLINE default line chart from an array =SPARKLINE({1,2,3}) official string +SPARKLINE TYPE of the result =TYPE(SPARKLINE({1,2,3})) 128 edge number +SPARKLINE ISERROR of a valid result =ISERROR(SPARKLINE({1,2,3})) FALSE edge boolean +SPARKLINE ISNA of a valid result =ISNA(SPARKLINE({1,2,3})) FALSE edge boolean +SPARKLINE arithmetic coercion =SPARKLINE({1,2,3})+1 #VALUE! edge error +SPARKLINE text length coercion =LEN(SPARKLINE({1,2,3})) 0 edge number +SPARKLINE concatenation coercion ="x"&SPARKLINE({1,2,3}) #VALUE! edge error +SPARKLINE wrapped in N =N(SPARKLINE({1,2,3})) 0 edge number +SPARKLINE wrapped in TO_TEXT =TO_TEXT(SPARKLINE({1,2,3})) edge string +SPARKLINE compared for equality with itself =SPARKLINE({1,2,3})=SPARKLINE({1,2,3}) TRUE edge boolean +SPARKLINE explicit column charttype =SPARKLINE({1,2,3},{"charttype","column"}) official string +SPARKLINE explicit bar charttype with two values =SPARKLINE({1,2},{"charttype","bar"}) official string +SPARKLINE explicit winloss charttype =SPARKLINE({1,-1,1},{"charttype","winloss"}) official string +SPARKLINE line charttype with color option =SPARKLINE({1,2,3},{"charttype","line";"color","red"}) official string +SPARKLINE with ymin and ymax =SPARKLINE({1,2,3},{"ymin",0;"ymax",10}) official string +SPARKLINE with unknown option key =SPARKLINE({1,2,3},{"bogus","x"}) edge string +SPARKLINE with unknown charttype value =SPARKLINE({1,2,3},{"charttype","bogus"}) #VALUE! edge error +SPARKLINE bar charttype with three values =SPARKLINE({1,2,3},{"charttype","bar"}) edge string +SPARKLINE with an odd-shaped options array =SPARKLINE({1,2,3},{"charttype"}) #REF! edge error +SPARKLINE with a scalar instead of a range =SPARKLINE(5) #N/A edge error +SPARKLINE with an empty array =SPARKLINE({}) #REF! edge error +SPARKLINE with text inside the data =SPARKLINE({1,"a",3}) edge string +SPARKLINE with an invalid array literal (Sheets rejects the {1,,3} syntax itself) =SPARKLINE({1,,3}) #ERROR! edge error +SPARKLINE with all-negative data =SPARKLINE({-1,-2,-3}) edge string +SPARKLINE with a single value =SPARKLINE({5}) #N/A edge error +SPARKLINE with no arguments =SPARKLINE() #N/A edge error +SPARKLINE over a real range containing a genuine blank cell =SPARKLINE(Data!H1:H3) edge string +SPARKLINE over a real range with all values present =SPARKLINE(Data!I1:I3) edge string +identity: unknown option key vs no options =SPARKLINE({1,2,3},{"bogus","x"})=SPARKLINE({1,2,3}) TRUE edge boolean +identity: redundant recognised option vs no options =SPARKLINE({1,2,3},{"charttype","line"})=SPARKLINE({1,2,3}) TRUE edge boolean +identity: two different unknown option values =SPARKLINE({1,2,3},{"bogus","x"})=SPARKLINE({1,2,3},{"bogus","y"}) TRUE edge boolean +identity: different data is not equal =SPARKLINE({1,2,3})=SPARKLINE({9,9,9}) TRUE edge boolean +identity: different charttype is not equal =SPARKLINE({1,2,3},{"charttype","column"})=SPARKLINE({1,2,3},{"charttype","line"}) TRUE edge boolean +comparison alias EQ on identical sparklines =EQ(SPARKLINE({1,2,3}),SPARKLINE({1,2,3})) TRUE edge boolean +comparison alias NE on identical sparklines =NE(SPARKLINE({1,2,3}),SPARKLINE({1,2,3})) FALSE edge boolean +comparison operator <> on identical sparklines =SPARKLINE({1,2,3})<>SPARKLINE({1,2,3}) FALSE edge boolean +ordering: sparkline greater-than a number =SPARKLINE({1,2,3})>1 TRUE edge boolean +ordering: sparkline greater-than text =SPARKLINE({1,2,3})>"zzzz" TRUE edge boolean +ordering: sparkline greater-than boolean =SPARKLINE({1,2,3})>TRUE TRUE edge boolean +ordering: sparkline greater-than another sparkline =SPARKLINE({1,2,3})>SPARKLINE({9,9,9}) FALSE edge boolean +ordering: sparkline less-than another sparkline =SPARKLINE({1,2,3})= itself =SPARKLINE({1,2,3})>=SPARKLINE({1,2,3}) TRUE edge boolean +ordering: alias GTE on two sparklines =GTE(SPARKLINE({1,2,3}),SPARKLINE({9,9,9})) TRUE edge boolean +identity: sparkline compared to empty text =SPARKLINE({1,2,3})="" FALSE edge boolean +text coercion via LEFT =LEFT(SPARKLINE({1,2,3}),1) edge string +text coercion via EXACT against empty text =EXACT(SPARKLINE({1,2,3}),"") TRUE edge boolean +text coercion via TEXT =TEXT(SPARKLINE({1,2,3}),"0") edge string +text coercion via CONCATENATE =CONCATENATE(SPARKLINE({1,2,3}),"x") x edge string +text coercion via TEXTJOIN =TEXTJOIN(",",TRUE,SPARKLINE({1,2,3})) edge string +boolean coercion via IF condition =IF(SPARKLINE({1,2,3}),1,2) 2 edge number +COUNTA of a sparkline =COUNTA(SPARKLINE({1,2,3})) 1 edge number +ISBLANK of a sparkline =ISBLANK(SPARKLINE({1,2,3})) FALSE edge boolean +COUNTUNIQUE of two different sparklines =COUNTUNIQUE(SPARKLINE({1,2,3}),SPARKLINE({9,9,9})) 2 edge number +COUNTUNIQUE of two identical sparklines =COUNTUNIQUE(SPARKLINE({1,2,3}),SPARKLINE({1,2,3})) 1 edge number +MAX with a sparkline argument =MAX(SPARKLINE({1,2,3}),1) 1 edge number +SUM with a sparkline argument =SUM(SPARKLINE({1,2,3}),1) 1 edge number +number-reading text function DOLLAR =DOLLAR(SPARKLINE({1,2,3})) #VALUE! edge error +number-reading text function FIXED =FIXED(SPARKLINE({1,2,3})) #VALUE! edge error +number-reading conversion TO_PERCENT =TO_PERCENT(SPARKLINE({1,2,3})) edge string +text function TRIM =TRIM(SPARKLINE({1,2,3})) edge string +text function UPPER =UPPER(SPARKLINE({1,2,3})) edge string +conversion VALUE =VALUE(SPARKLINE({1,2,3})) 0 edge number +PRODUCT with a direct sparkline argument =PRODUCT(SPARKLINE({1,2,3}),3) 3 edge number +COUNT with a sparkline argument =COUNT(SPARKLINE({1,2,3}),1) 1 edge number +option key given as a boolean =SPARKLINE({1,2,3},{TRUE,"x"}) edge string +option key given as a number =SPARKLINE({1,2,3},{0,"x"}) edge string +option key with different casing =SPARKLINE({1,2,3},{"CHARTTYPE","column"}) edge string +charttype value with different casing =SPARKLINE({1,2,3},{"charttype","COLUMN"}) edge string +two-dimensional data argument =SPARKLINE({1,2;3,4}) edge string +single-row two-column data =SPARKLINE({1,2}) edge string +conversion TO_DOLLARS =TO_DOLLARS(SPARKLINE({1,2,3})) edge string +conversion TO_PURE_NUMBER =TO_PURE_NUMBER(SPARKLINE({1,2,3})) edge string +conversion TO_DATE =TO_DATE(SPARKLINE({1,2,3})) edge string +PRODUCT with a sparkline as the only argument =PRODUCT(SPARKLINE({1,2,3})) 0 edge number +SUM with a sparkline as the only argument =SUM(SPARKLINE({1,2,3})) 0 edge number +MAX with a sparkline as the only argument =MAX(SPARKLINE({1,2,3})) 0 edge number +COUNT with a sparkline as the only argument =COUNT(SPARKLINE({1,2,3})) 0 edge number +AVERAGE with a sparkline as the only argument =AVERAGE(SPARKLINE({1,2,3})) #DIV/0! edge error +numeric function ROUND =ROUND(SPARKLINE({1,2,3})) #VALUE! edge error +numeric function ABS =ABS(SPARKLINE({1,2,3})) #VALUE! edge error +numeric function INT =INT(SPARKLINE({1,2,3})) #VALUE! edge error +unary minus =-SPARKLINE({1,2,3}) #VALUE! edge error +unary percent =SPARKLINE({1,2,3})% #VALUE! edge error +SUM over a range holding a sparkline cell =SUM(Data!K1:K2) 5 edge number +PRODUCT over a range holding a sparkline cell =PRODUCT(Data!K1:K2) 5 edge number +MAX over a range holding a sparkline cell =MAX(Data!K1:K2) 5 edge number +COUNT over a range holding a sparkline cell =COUNT(Data!K1:K2) 1 edge number +COUNTA over a range holding a sparkline cell =COUNTA(Data!K1:K2) 2 edge number +SUM over a range holding only a sparkline cell =SUM(Data!K1:K1) 0 edge number +a sparkline cell referenced directly =Data!K1 edge string +TYPE of a referenced sparkline cell =TYPE(Data!K1) 128 edge number +CELL type of a sparkline =CELL("type",SPARKLINE({1,2,3})) #N/A edge error +MEDIAN with a sparkline argument =MEDIAN(SPARKLINE({1,2,3}),1) 1 edge number +STDEV with a sparkline argument =STDEV(SPARKLINE({1,2,3}),1,2) 0.7071067811865476 edge number +SUMSQ with a sparkline as the only argument =SUMSQ(SPARKLINE({1,2,3})) 0 edge number +MAXA with a sparkline argument =MAXA(SPARKLINE({1,2,3})) 0 edge number +AVERAGEA with a sparkline argument =AVERAGEA(SPARKLINE({1,2,3})) #DIV/0! edge error +SUMPRODUCT with a sparkline argument =SUMPRODUCT(SPARKLINE({1,2,3})) 0 edge number +binary subtraction =SPARKLINE({1,2,3})-1 #VALUE! edge error +binary multiplication =SPARKLINE({1,2,3})*2 #VALUE! edge error +binary division =SPARKLINE({1,2,3})/2 #VALUE! edge error +PRODUCT over a range holding only a sparkline cell =PRODUCT(Data!K1:K1) 0 edge number +MIN over a range holding a sparkline cell =MIN(Data!K1:K2) 5 edge number +MAXA over a range holding only a sparkline cell =MAXA(Data!K1:K1) 0 edge number +MAX over a range holding only a sparkline cell =MAX(Data!K1:K1) 0 edge number +MIN over a range holding only a sparkline cell =MIN(Data!K1:K1) 0 edge number +MINA with a sparkline argument =MINA(SPARKLINE({1,2,3})) 0 edge number +MINA over a range holding only a sparkline cell =MINA(Data!K1:K1) 0 edge number +MAXA over a range holding a sparkline and a number =MAXA(Data!K1:K2) 5 edge number +AVERAGE over a range holding only a sparkline cell =AVERAGE(Data!K1:K1) #DIV/0! edge error +COUNTUNIQUE over a range holding a sparkline =COUNTUNIQUE(Data!K1:K2) 2 edge number +MIN with a sparkline as the only argument =MIN(SPARKLINE({1,2,3})) 0 edge number +MAX with a sparkline and an empty array =MAX(SPARKLINE({1,2,3}),{}) #REF! edge error +MAX with a sparkline and a text-only array =MAX(SPARKLINE({1,2,3}),{"a"}) 0 edge number