Audamir

časť 24

Chyby týždňa: päť vecí, ktoré sa nedali zopakovať na požiadanie

Test, ktorý padal raz z desiatich behov, dve kontroly migrácie závislé od dátumu a šťastia a kocka, cez ktorú presvitali steny. Čo sa zmeralo, ako sa to opravilo a ktorých päť pravidiel z toho plynie.

· 11 min čítania

Najhoršia chyba v testoch nie je tá, ktorá padá vždy. Tú sa dá opraviť za pol hodiny. Najhoršia je tá, ktorá padne raz z desiatich behov, raz po zmene dátumu a raz len na stroji, ktorý je práve zaťažený. Týždeň od 23. do 27. októbra 2026 priniesol päť takých prípadov: tri kontroly, ktoré závisia od náhody alebo od hodín, jedna zaseknutá slučka a jedna vizuálna chyba, ktorú oko po prvej oprave už nevidelo, hoci tam stále bola.

Tento článok nadväzuje na článok o testovaní, kde sa hovorilo o tom, čo testy strážia. Tu je opačná otázka: čo robia, keď sa na ne nedá spoľahnúť. Všetky čísla sú z commitov a z dokumentácie hry (zdroje sú na konci). Nič z toho sa pri písaní článku nespúšťalo znova; výnimkou sú snímky kocky v oddiele 4, ktoré sú zo starých commitov hry a ukazujú len to, čo sa na nich dá vidieť.

Prehľad prípadov

#Čo zlyhaloČo sa menilo medzi behmiOpravaDôkaz po oprave
1test sledovanej hrynáhodná soľ hodov kociekpevná soľ v stránke testu40 behov, 0 pádov
2kontrola migrácie dennej obrazovkydátum, semená, soľ hodovpripnuté hodiny, počítadlo namiesto náhody5 behov bajt po bajte rovnakých
3kontrola migrácie pravidielto isté a navyše pat v hreodpoveď na pat, stráž proti zaseknutiu8 behov bajt po bajte rovnakých
4kocka Avatara[1]uhol pohľaduplatne za stenami, potom ich zväčšenie0 presvitaní zo 640 póz
5hrany kocky z viacerých materiálovmateriály stienjedna farba hrany pre celú kockupozretie v dvoch prehliadačoch

1. Test, ktorý hral inú hru pri každom behu

Pri práci na krokoch M4 (aplikácia číta tabuľku režimov, pozri článok o realmoch) sa ukázal test režimu Sledovať hru, v ktorom počítač hrá sám proti sebe. Test čakal najviac 600 virtuálnych sekúnd na koniec hry. Raz z desiatich behov sa hra nedohrala včas.

Prvé podozrenie bolo zaťaženie stroja, lebo v tom čase bežali aj iné ťažké nástroje. Meranie ho vyvrátilo: 40 behov po jednom, bez zaťaženia, 4 padli (DESIGN §54.3). Zaťaženie len urobilo behov viac, takže chyba bola vidieť častejšie.

Skutočná príčina bola v samotnej hre. Sledovaná hra nemá hráča, ktorý by si „zafixoval“ hody kociek, preto Flow losuje náhodnú soľ hodov. Mapa je daná semenom, ale hra na nej trvala podľa soli 13 až 29 kôl, čo je od 138 do viac ako 600 virtuálnych sekúnd (medián 343). Test s limitom 600 sekúnd teda niekedy nestihol skončiť hru, ktorá je úplne regulárna.

Oprava je jednoduchá: stránka testu pre tento režim dostala pevnú soľ (Watch.makeFlow zabalené do beforeApp). Soľ MODES-B dá hru na 13 kôl a 288 sekúnd. Po oprave 40 behov po jednom, 0 pádov, a pôvodný scenár (súbor so šiestimi ďalšími testami rozhrania vedľa calibrate --verify) trikrát, tiež bez pádu.

2. Kontrola migrácie, ktorá čítala hodiny

Migračné kontroly v tejto hre fungujú tak, že starý kód (z git archive starého commitu) odohrá skutočnú históriu, nový kód sa spustí nad jeho dátami a porovná sa výsledok. Prečo je to dôležité, je v článku o dennej výzve. Kontrola tools/daily-hub-migration.js občas padla s hláškou, že záznam dňa ukazuje dnešnú výzvu ako prehratú, hoci sa čakala výhra.

Medzi behmi sa menili dve veci:

  • Náhoda. Každá hra losovala semeno (Kniežatstvá[2], voľná hra) aj soľ hodov.
  • Dátum. Denná výzva je každý deň iná mapa. AI raz vyhrala dnešnú, raz prehrala, a kontrola žiadala výhru.

Oprava (26. 10. 2026): hodiny oboch procesov sú pripnuté na 2026-10-24 (pevný posun, takže čas stále plynie), semená a soľ starej aplikácie pochádzajú z počítadla a záznam dňa sa porovnáva s tým, čo zapísal starý kód: výhra, keď starý profil má ten deň medzi vyhratými, inak prehra. Test navyše overuje, že hodiny sú naozaj pripnuté. Päť behov celého nástroja dáva rovnaký výstup bajt po bajte, aj keď sa systémový dátum posunie o +4, +30 a −10 dní.

Pripnutie hodín odkrylo druhú chybu

Po pripnutí hodín kontrola žiaľ padala pri každom behu: správa commitu uvádza dvojicu čísel 108/110 proti 65/108 (bez bližšieho vysvetlenia, čo presne počítajú). Vysvetlenie je v inom týždennom príbehu, v skóre podľa dĺžky hry. Hra vyhratá na body novým kódom sa počíta novým vzorcom a činiteľom dĺžky daného dňa (1,13 až 1,89 podľa nameraných dní), takže jej záznam už nemá rovnaké čísla ako ten, čo vytvoril starý kód. Kontrola pritom žiadala rovnosť. Pred pripnutím hodín to „prešlo alebo neprešlo“ podľa dátumu behu, a preto si to nik nevšimol.

Oprava to rieši: najnovší záznam dennej výzvy sa môže líšiť v súčte, ale jeho základ musí byť rovný bodom nastavenia toho dňa (Score.compute z Settings.daily(day)); ostatné záznamy sa porovnávajú ako predtým.

3. Kontrola migrácie, ktorá čakala na odpoveď, ktorú nikto nedal

Rovnakú kontrolu mala aj migrácia pravidiel (tools/rules-migration.js), a navyše ju trápilo niečo horšie. Test (tests/tools.test.js) sa občas skončil hláškou null !== 0. V Node to znamená, že podproces nedoběhol a bol zabitý signálom: nástroj narazil na svoj limit 120 sekúnd.

Podľa poznámok z vývoja boli príčiny aspoň tri a nie všetky isté:

  1. Zaseknutie po pate. Hra starého kódu, ktorá skončí patom (Kniežatstvá v 14. kole alebo denná výzva), sa opýta „Ukončiť na body?“ a drží hru, kým niekto neodpovie. Pomocná funkcia playOut odpoveď nemala a ani stráž proti zaseknutiu, takže AI čakala 20 000 krát po 20 sekundách. Na čistej vývojovej verzii hry sa nástroj zasekol v 3 zo 6 behov.
  2. Limit pamäte. Pod ulimit -v 24000000 trval nástroj podľa jednej relácie asi 480 sekúnd namiesto asi 11 sekúnd bez limitu. To nie je pravidlo (iné behy s limitom prešli), ale pri hľadaní príčiny treba na to myslieť.
  3. Zaťaženie stroja. Keď iné relácie púšťali celé sady testov, dva behy za sebou padli po 120 sekundách aj na čistej vývojovej verzii hry. Na voľnom stroji trvá nástroj asi 12 až 14 sekúnd.

Oprava (26. 10. 2026) použila pomocníkov z predchádzajúcej opravy: pripnuté hodiny, semená z počítadla, odpoveď „Ukončiť na body“ na otázku o pate a hra, ktorá sa zasekne, je okamžite chyba, nie 20 000 čakaní. Test dáva nástroju 280 sekúnd (na pokojnom stroji trvá 14). Osem behov je bajt po bajte rovnakých (päť obyčajných a tri s posunutým dátumom). Cestu odpovede overilo desať sád semien: dve z nich došli k patu (Kniežatstvá v 11. kole a denná hra v 68. kole) a kontrola skončila správou „starý a nový kód sa zhodujú“.

4. Kocka, cez ktorú vidno

Posledné dva prípady nie sú o testoch, ale ukazujú, kde testy nepomôžu. Hrdina hry je kocka z CSS 3D (transform-style: preserve-3d, šesť stien .face, bez canvasu; popis je v článku o skrinkách a v docs/HERO-DICE.md). Steny majú zaoblené rohy (18 %) a za nimi nebolo nič. Keď sa kocka otočila rohom k divákovi, cez rohy a hrany presvitali bodky vzdialenej steny. Upozornila na to moja poznámka pri hraní (26. 10. 2026).

Kocka hrdinu v tej istej póze pred a po oprave: vľavo verzia 0.50.5, kde tmavá bodka zadnej steny presvitá pod stredom spodného rohu; vpravo verzia 0.50.8, kde je hrana vyplnená platňami a bodka zmizla
Vľavo verzia 0.50.5 z 26. 10. 2026, vpravo verzia 0.50.8 z 26. 10. 2026: kocka hrdinu v hube, rovnaká póza a rovnaký výrez. Zdroj: kód hry v oboch verziách (commity), snímané 8. 11. 2026; nejde o fotografiu z 26. 10.

Prvá oprava (verzia 0.50.6). Za každú stenu sa dala štvorcová platňa vo farbe hrany steny. Pozrelo sa v prehliadači na domovskej obrazovke, v hre aj v karte Kocka, v pózach s rohom k divákovi aj po ťahaní. Vyzeralo to dobre.

Druhá oprava (verzia 0.50.7). Podľa môjho hlásenia presvitanie pretrvávalo, najviac pri svetlých bodkách kamennej a železnej kocky. Príčina: platňa mala inset: 0, ktoré sa zastaví na vnútri okraja, takže bola o 2 px (kocka hrdinu) alebo 4 px (kocka v karte) menšia než stena a lúč pri rohu prešiel vedľa nej. Tentoraz sa nepozeralo, ale meralo sa. Na jednu kocku v každej bunke sa nastavila náhodná póza, rozsvietili sa len bodky najviac odvrátenej steny a každý rozsvietený pixel bol únik. Merané vo Firefoxe v 80 pózach pre veľkosti 64, 30 a 90 px, pre kocku v karte aj kocku hrdinu:

Verzia kockyPóz s únikom
bez platní16 z 80
prvé platne (0.50.6)11 z 80
platne ako celé steny (0.50.7)0 zo 640

V Chromiu žiadny únik v 60 pózach. Prvá oprava teda odstránila len asi tretinu úniku (z 16 na 11 póz) a z pohľadu na pár póz sa to nedalo poznať.

Tretia úprava (verzia 0.50.8). Tam, kde sa stretli dve steny z rôznych materiálov, mali okraj aj platňa farbu vlastného materiálu, takže rohy železnej kocky s hnedými a striebornými stenami vyzerali ako záplaty. Kocka teraz nesie svet a stupeň svojej hrany (data-world, data-rim) a všetky okraje a platne majú farbu hrany najnižšieho stupňa z jej stien. Tabuľka hrán v style.css sa testom drží rovnaká ako --face-edge v pravidlách stupňov. Pozreté vo Firefoxe (tmavá aj svetlá téma, osem kociek troch svetov vo štyroch pózach) a v Chromiu; meranie pevnosti ostalo rovnaké, lebo sa zmenili len farby.

Test, ktorý ostal pádavý, a prečo

Nie všetko sa opravilo. Pri kroku M4 sa celá sada (node --test --test-concurrency=3, 2 508 testov, 7 minút) skončila s jednou chybou: tests/rollwin.test.js (test „a move to make“). Padá približne raz z tridsiatich behov aj na čistej vývojovej verzii hry: 1 z 30 na čistom pracovnom strome verzie 0.50.19 a 1 z 12 na strome s rozpracovanou zmenou. Test spustí Kniežatstvá na náhodnej mape a pýta sa AI na prvý ťah miesta človeka. To je rovnaký vzorec ako v prípade 1: náhodná mapa v teste. Dokumentácia zapisuje, že sa test nezmenil. Oprava teda čaká.

Z tej istej sady pochádza ešte jedna zaujímavosť. Pri behu, počas ktorého v paneli prehliadača hrali hry, padli aj dva testy tests/autopilot-ui.test.js, ktoré čakajú na AI v reálnom čase. Samostatne prešli (3 z 3). Test závislý od reálneho času je test závislý od zaťaženia, aj keď to tak nevyzerá.

A jeden ďalší príbeh z toho istého kroku: stránka testu beží v kontexte vm, kde majú objektové literály iný prototyp Object než v samotnom module. Kontrola „obyčajných dát“ v správcovi realmov preto pri prvom spustení odmietla všetky tri manifesty so správou „value is not a plain object“. V prehliadači by sa to nestalo. Kontrola teraz akceptuje koreňový prototyp ľubovoľného realmu a stále odmieta triedy, Object.create(x), napodobeniny a getter, ktorý vyhodí chybu. Test sa tu správal inak než skutočné prostredie, a to je chyba iného druhu, o ktorej treba vedieť.

Čo si z toho odnášam

  • Test hrá jednu pevnú hru. Ak hra pozná semeno aj soľ hodov, test ich nastaví obe. Prípad Sledovať hru: 4 pády zo 40, pevná soľ, 0 pádov zo 40. Neopravený prípad rollwin je zatiaľ presne opačný príklad.
  • Kontrola nečíta hodiny a nehádže kockou. Pripnutý čas a počítadlo namiesto náhody (prípady 2 a 3). Pripnutie má vedľajší účinok: kontroly, ktoré „prešli podľa dátumu“, začnú padať vždy, a odhalia chyby, ktoré boli dovtedy skryté (108/110 proti 65/108 v správe commitu). Dobrá správa je, že chyba je teraz zopakovateľná.
  • Každé čakanie má strop a vie povedať, prečo sa zastavilo. 20 000 čakaní po 20 sekundách nie je čakanie, ale zaseknutie. Stráž proti zaseknutiu a odpoveď na otázku o pate zmenili „raz za čas visí“ na „skončí s chybou hneď“.
  • Vizuálna chyba sa meria, nie ohliada. Prvá oprava kocky prešla pohľadom v troch obrazovkách a odstránila tretinu úniku. Až meranie rozsvieteného pixela v 80 pózach ukázalo číslo, ktoré sa dá porovnať: 16, 11, 0.
  • Nezhoda medzi prostrediami je chyba testu, nie hry. vm kontext a prehliadač majú iné prototypy; to, že kód v prehliadači funguje, nestačí, kým test bežia inde.

Zdroje a poznámky

  • Test sledovanej hry: commit z 27. 10. 2026 a docs/DESIGN.md §54.3 (40 behov, 13 až 29 kôl, medián 343 s).
  • Kontrola dennej obrazovky: dva commity a zlúčenie z 26. 10. 2026, docs/DESIGN.md §48.9. Činiteľ dĺžky (1,13 až 1,89) je z §51; čísla 108/110 a 65/108 sú zo správy druhého commitu.
  • Kontrola pravidiel: commit a zlúčenie z 26. 10. 2026; čas 14 s a limit 120 s sú zo správy commitu. Počty behov, časy a zaseknuté procesy (oddiel 3) sú z poznámok vývoja, nie z nezávislého merania.
  • Kocka: verzie 0.50.6 až 0.50.8 z 26. 10. 2026; docs/HERO-DICE.md, kap. 11 (geometria kocky). Merania 16/80, 11/80 a 0/640 vo Firefoxe (tools/firefox-check.js) a 0/60 v Chromiu sú zo správy commitu verzie 0.50.7.
  • **Pádavý rollwin a vm:** docs/DESIGN.md §54.5 a §54.2 (27. 10. 2026).
  • Čo sa nezopakovalo: build:check, tools/smoke.js a i18n:scan, lebo Chromium sa na vývojovom stroji z terminála nespustí (snap-confine; docs/DESIGN.md §50.10 a §54.5).

Poznámky

  1. Do verzie 0.80.1 z 1. novembra 2026 sa Avatar volal Hero Dice (pozri Z Hero Dice na Avatar (pripravovaný článok č. 39)). ↩︎
  2. Do verzie 0.65.6 z 31. októbra 2026 sa volal Klasik. ↩︎