@@ -56,19 +56,38 @@ import (
5656// of this set on purpose, and saying so here is the point - a set that
5757// quietly skipped it would read as covering everything.
5858//
59- // macOS, measured on the owner's Mac Mini on 2026-08-17 rather than assumed.
60- // All eleven screens differ there, and the shape of the difference is what
61- // decided the design: a few thousand pixels spread over the whole window, never
62- // more than 2 of 255 in any channel. Same glyphs, same layout, same colours -
63- // the last bit of edge blending rounds the other way.
59+ // arm64 draws the same screen a shade differently, and the tolerance below is
60+ // for that. Measured twice rather than assumed, on two different systems:
61+ // the owner's Mac Mini on 2026-08-17, and Linux in a container on 2026-08-23.
62+ // Both round the same way - a few thousand pixels spread over the whole window,
63+ // never more than 3 of 255 in any channel. Same glyphs, same layout, same
64+ // colours - the last bit of edge blending rounds the other way.
65+ //
66+ // It is the ARCHITECTURE, not the system, and that took a second measurement to
67+ // see. The first one only had a Mac, so the difference was written down as a
68+ // macOS thing and the tolerance was keyed on the operating system. Then Linux
69+ // on arm64 came back with the same rounding, and Linux on amd64 - the same
70+ // binary, the same references, the same container, the same moment - came back
71+ // identical to the byte. Two arm64 platforms round, two amd64 platforms do not.
72+ // Keying this on darwin was reading one measurement as if it were the rule.
73+ //
74+ // The strongest part of that measurement is not the pixels. The widget tree,
75+ // compared separately below, matched to the byte on all twenty five screens -
76+ // and it carries sizes, positions, colours and every string on the screen. So
77+ // the layout is identical and only the rasterising differs, which is exactly
78+ // the case this file calls "tree the same and pixels different".
6479//
6580// So the tolerance is on how FAR a channel moved, not on how many pixels moved,
66- // and it applies only where that rounding was measured. The two worlds are
67- // eight times apart: narrowing one field by a single pixel - the smallest real
68- // change this window can make - moves a channel by 16.
81+ // and it applies only where that rounding was measured.
82+ //
83+ // What is still NOT proven, and both matter:
6984//
70- // What is still NOT proven: whether 2 belongs to macOS or to that one machine
71- // and that one version of it. One Mac, measured once.
85+ // - darwin/amd64. There is no Intel Mac to measure and it is not a supported
86+ // target - release.yml says so - so this stops giving it a tolerance rather
87+ // than carrying an untested claim about it.
88+ // - WHY arm64 rounds the other way. Go allows FMA contraction on arm64 and
89+ // not on amd64, which would do it, but that is a hypothesis nobody here
90+ // measured. Named so the next person knows it is a lead, not a finding.
7291//
7392// To regenerate after a deliberate change:
7493//
@@ -89,21 +108,79 @@ const (
89108// tools/probes/guirender pins the same string for its -compare mode.
90109const pinnedOutputDirectory = "/tfg/out"
91110
92- // macOSBlendTolerance is how far one channel may move on macOS before this
93- // stops calling it rounding. Measured at 2 on every screen, so this is double
94- // that - and still four times below the 16 that the smallest real change makes .
95- const macOSBlendTolerance = 4
111+ // measuredArmBlend is the largest channel distance arm64 was actually seen to
112+ // move: 2 of 255 on twenty four screens and 3 on preset-refused, measured
113+ // 2026-08-23 across all twenty five, and 2 on the Mac on 2026-08-17 .
114+ const measuredArmBlend = 3
96115
97- // pixelTolerance is deliberately different per system rather than uniform.
98- // Windows and Linux were measured at exactly zero, and taking a tolerance there
99- // would give away accuracy this guard actually has.
116+ // smallestRealChange is how far a channel moves when this window makes the
117+ // smallest change it CAN make - one field narrowed by one pixel. Measured
118+ // 2026-08-23 by doing exactly that, parts.NumericWidth from 140 to 139, and
119+ // reading what this guard reported: twenty four of the twenty five screens
120+ // moved, by at least 16, typically 16, up to 203.
121+ //
122+ // It is written down so the tolerance can be held against a measurement rather
123+ // than against a feeling, which is what the test below does.
124+ const smallestRealChange = 16
125+
126+ // armBlendTolerance is how far one channel may move on arm64 before this stops
127+ // calling it rounding. Double the measured 3, so a machine that rounds slightly
128+ // harder than the two measured ones does not go red - and still comfortably
129+ // under the 16 that the smallest real change makes.
130+ //
131+ // Written as its own number rather than as measuredArmBlend * 2, and that is
132+ // not a style choice. Derived from the measurement, it can never fall below it,
133+ // so the test below would be holding it against a bound arithmetic already
134+ // guarantees - a check that reads like one and cannot fail. It is a decision
135+ // sitting between two measurements, so it is spelled as one.
136+ const armBlendTolerance = 6
137+
138+ // pixelTolerance is deliberately different per architecture rather than
139+ // uniform. amd64 was measured at exactly zero on Windows and on Linux, and
140+ // taking a tolerance there would give away accuracy this guard actually has.
100141func pixelTolerance () int {
101- if runtime .GOOS == "darwin " {
102- return macOSBlendTolerance
142+ if runtime .GOARCH == "arm64 " {
143+ return armBlendTolerance
103144 }
104145 return 0
105146}
106147
148+ // What this defends. A tolerance is the one thing in this file that can make it
149+ // pass for the wrong reason, and it can do that quietly - a number nudged up
150+ // far enough stops the pictures from ever disagreeing again, and a green run
151+ // looks the same either way.
152+ //
153+ // So the number is held against the two measurements that bracket it: the
154+ // rounding it has to absorb, and the smallest real change it must never
155+ // absorb. Both are recorded above with the date they were taken.
156+ //
157+ // This asks about the constants rather than about a rendering on purpose,
158+ // because it has to mean something on the machine it runs on. The arm64 branch
159+ // is dead code on amd64, so a test that only rendered would be green here no
160+ // matter what that branch said - which is the shape of guard this project has
161+ // been bitten by more than once.
162+ func TestTheBlendToleranceStaysBetweenTheRoundingAndTheSmallestRealChange (t * testing.T ) {
163+ if armBlendTolerance < measuredArmBlend {
164+ t .Errorf ("the arm64 tolerance is %d, below the %d that was actually measured.\n " +
165+ "Reason: a tolerance under the rounding it exists for makes arm64 red for edge blending.\n " +
166+ "What to do: either raise it back above the measurement or measure again and move both numbers together." ,
167+ armBlendTolerance , measuredArmBlend )
168+ }
169+ if limit := smallestRealChange / 2 ; armBlendTolerance > limit {
170+ t .Errorf ("the arm64 tolerance is %d, more than half of the %d that the smallest real change makes.\n " +
171+ "Reason: at that size it starts being able to hide a change somebody meant to see, which is the one thing this guard exists to catch.\n " +
172+ "Allowed: at most %d.\n " +
173+ "What to do: if a machine really rounds that hard, measure it and say so - do not widen this to make a run go green." ,
174+ armBlendTolerance , smallestRealChange , limit )
175+ }
176+ if runtime .GOARCH != "arm64" && pixelTolerance () != 0 {
177+ t .Errorf ("this is %s and the tolerance is %d rather than 0.\n " +
178+ "Reason: amd64 was measured at exactly zero on Windows and on Linux, so any tolerance here throws away accuracy this guard has.\n " +
179+ "What to do: keep the tolerance on the architecture that was measured to need it." ,
180+ runtime .GOARCH , pixelTolerance ())
181+ }
182+ }
183+
107184// scene is one screen in one state, ready to be photographed.
108185type scene struct {
109186 tab fyne.CanvasObject
@@ -627,7 +704,7 @@ func compareAgainstReference(t *testing.T, name, reference string, got image.Ima
627704 // starts climbing, the tolerance is covering something it was not
628705 // measured to cover, and nobody would see that from a green run.
629706 t .Logf ("%d pixels differ by at most %d of 255, which is inside the %d allowed on %s - treated as edge blending" ,
630- differing , worst , pixelTolerance (), runtime .GOOS )
707+ differing , worst , pixelTolerance (), runtime .GOARCH )
631708 return
632709 }
633710 dir := saveEvidence (t , name , wantPix , gotPix )
0 commit comments