Co to znamená
Případ v test/cases/ může být neschopný běžet tam, kde se ocitne: žádné jq, žádné zola, žádné node_modules, žádný prohlížeč, žádný sestavený spustitelný soubor. Několik desítek jich to říká. Do této změny to říkaly jediným způsobem, jemuž runner rozuměl — vypsaly řádek o přeskočení a skončily 0 — a exit 0 je stav, pro který test/run.sh zapisuje ok.
Report, který runner předává majordomus evidence record, tak nesl totéž slovo pro dvě různé události: případ, který provedl každou aserci, již měl provést, a případ, který neprovedl žádnou. Recorder je poctivý k tomu, co dostane, a oba označí commitem, digestem a časem. Tvrzení pojmenované případem, který odmítl běžet, se pak četlo jako podpořené na základě běhu, který nic neměřil, a jediný povrch, který mohl říct opak, byl log, který po zeleném výsledku nikdo nečte.
Přeskočení má teď od začátku do konce vlastní slovo: případ volá skip, což skončí s kódem 4 a zanechá značku, kterou runner pojmenoval, runner vypíše skip a zapíše SKIP a ledger zaznamená skip — výsledek, který nedokazuje. Stále to není selhání: návratový stav běhu se nemění a běh, v němž všechny případy odmítly běžet, je běh, který proběhl, a ne „nenalezeny žádné případy“. A selhání není přeskočení: případ, který skončí kódem 4, aniž by zavolal skip, je neúspěšný případ.
Princip
test/lib.sh deklaruje stav a pomocníka:
MJ_SKIP_STATUS=4
skip() {
printf ' skip: %s\n' "$*"
if [ -n "${MJ_SKIP_MARK:-}" ]; then printf '%s\n' "$*" > "$MJ_SKIP_MARK"; fi
exit "$MJ_SKIP_STATUS"
}
test/run.sh zná totéž číslo. run_case propaguje vlastní stav případu místo sbalení každé nenuly na 1 a pak ho mapuje: 0 je úspěch, 4 je přeskočení, je-li přítomna značka, vše ostatní je selhání. Vlastní stavy runneru — 2 pro fixture, který nešlo připravit, 3 pro spuštění limitu — vrací sám runner a nikdy se nečtou z případu, takže případ, který skončí kódem 3 z vlastní vůle, je neúspěšný případ, a ne hlášený timeout.
Samotný stav není deklarace, protože případ běží pod set -e a končí stavem toho příkazu, který selhal. jq -e selže se 4, když nevytvořil žádný výsledek — což dělá na prázdném výstupu příkazu, který se rozbil — a případy v test/cases/ tvrdí holými řádky jq -e všude. Runner, který by četl 4 jako přeskočení, by zaznamenal každé z těchto selhání jako případ, který odmítl běžet, a udržel běh zelený: vada, kterou toto tvrzení uzavírá, přicházející z druhé strany. Proto run_case jmenuje soubor vedle fixture případu v MJ_SKIP_MARK, skip jej zapíše a 4 je přeskočení, jen když soubor existuje.
verdict získává jednu větev. Řádek součtu získává třetí číslo a pole výsledku v reportu získává SKIP vedle ok, FAIL a TIMEOUT. Paralelní fáze vykresluje své verdikty ze souborů, které zapsali její workery, toutéž funkcí, takže obě fáze píší stejné slovo — runner, který sériově říkal pravdu a paralelně ok, by byl horší než ten, který ji neříkal nikdy, protože CI spouští sadu paralelně. Souhrn úlohy CI (scripts/ci/summary) čte tentýž report a SKIP počítá jako přeskočené; dříve počítal každý řádek, který nebyl ok, jako selhání, čímž by se to slovo obrátilo v opačnou lež.
V modelu v Rustu se nic nezměnilo. Outcome::Skip už tam bylo, Outcome::parse už čtlo skip a skipped a Outcome::proves() pro něj už bylo nepravdivé. Chybělo, aby runner mohl to slovo kdy zapsat: model měl slovník, kterým producent nemohl mluvit.
Slovo pomáhá jen tam, kde ho případ říká, takže starý tvar je odmítnut v samotné sadě. Dva případy si ho po konverzi ponechaly — jeden jím nedotčený, jeden sloučený z jiné větve později — a každý by dál zapisoval ok pro běh, který nic netvrdil, zatímco všechny ostatní části tohoto tvrzení byly zelené. Případ 413 proto čte každý soubor v test/cases/ a odmítne ten, který říká exit 0 kdekoli jinde než ve svém posledním příkazu: případ odchází se stavem 0 jen tím, že dojde ke svému konci. Průchod přeskakuje řádky komentářů a tělo každého heredoc, kde žijí stuby a fixtury, které končí 0 záměrně, a heredoc, jehož konec nevidí, hlásí místo tichého čtení dál. Čte doslovný stav: holé exit se nikde neodmítá, protože programy awk psané inline v případech říkají exit také a řádkové skenování oba jazyky nerozliší. Běží před vlastními předpoklady případu, protože nepotřebuje nic než awk, a je dokázán jako kontrola jako první: oba staré tvary zasazené do vlastního adresáře jsou odmítnuty a stub v heredoc, here-string, výraz sed, komentář a závěrečné exit 0 projdou.
Čítač napájí také dvě odmítnutí „nic neběželo“ v runneru. bash test/run.sh 09_site_mobile_first na stroji bez zola vybere případ, který existuje, a odmítne ho; dříve to bylo 0 passed, 0 failed a exit 2, „no case matches“, což posílá čtenáře hledat soubor, který tam je.
Jak to vidět
bash test/run.sh 413_a_skipped_case_is_not_a_proof
MJ_TEST_REPORT=suite.tsv bash test/run.sh 09_site_mobile_first && cat suite.tsv
majordomus evidence claim a-skipped-case-is-not-a-proof
test/cases/413_a_skipped_case_is_not_a_proof.sh pouští skutečný runner nad harnessem tří vlastních případů — jeden projde, jeden odmítne, jeden selže — a tvrdí tři slova v obou fázích i to, že souhrn jobu CI nad tím reportem počítá jedno selhání a jedno přeskočení. Přidá čtvrtý, který selže přes jq -e se stavem 4 a nikdy nevolá skip, nejdřív ověří, že skutečně skončí 4, a tvrdí, že obě fáze pro něj zapíší FAIL a běh zčervenají, zatímco deklarované přeskočení vedle něj zůstane SKIP. Pak zaznamená SKIP pro zaručené tvrzení ve fixture repozitáři a tvrdí, že tvrzení není prokázáno a zůstává nálezem, a zaznamená totožný report s ok v tom jediném poli a tvrdí, že je prokázáno: rozdíl mezi nimi je to slovo, což dělá z prvního tvrzení o slovu, ne o fixture. Před tím vším prohledá test/cases/ na případ, který ještě před koncem odchází s exit 0, poté, co na podstrčených souborech ukáže, že skenování odmítne oba staré tvary a propustí to, co jim jen vypadá podobně.
Co nepokrývá
Neudělá z procházejícího případu případ, který byl procvičen. Případ, který doběhne do konce, aniž by cokoli tvrdil — protože jeho aserce jsou ve větvi, kterou nic nevzalo, nebo protože je tak napsán — stále končí kódem 0 a stále se zaznamenává jako důkaz. Tohle uzavírá deklarované přeskočení, což je půlka, kterou stroj vidí; druhá půlka je recenze, kterou drží pravidlo project.no-claim-without-test.
Nerozděluje případ. Několik případů přeskakuje jednu sekci — vlastní sady crate tam, kde není cargo, round trip tam, kde není zip — řeknou to a pokračují v ověřování zbytku; runner má jedno slovo na případ a takový případ je ok pro sekce, které proběhly. Sken je nechává na pokoji, protože neskončí předčasně: co přeskakují, je zapsáno vedle tvrzení, která dělají, a přesunout přeskočenou polovinu do vlastního případu je způsob, jak by získala vlastní slovo.
Neauditují se důvody. Případ, který odmítne běžet, protože chybí jq, a případ, který odmítne, protože nemohl nastartovat prohlížeč, jsou tu jedno slovo, a zda měl být skip v daném prostředí selháním, je úsudek, který činí případ sám — několik případů už selhává místo skipu, když je nastaveno CI, a to zůstává jejich věcí.
Crate nedosahuje. Výstup cargo test se parsuje na testovací binárku, kde je ignorovaný test neviditelný uvnitř binárky, která hlásí ok; povrchem, o němž toto tvrzení je, je TSV sady.
Proč existuje
Tento repozitář už na přesně toto selhání narazil, z opačného konce: případ, jehož node_modules chyběly, proměnil červenou v zelenou, a vzpomínka na to je jeden řádek dlouhá — přeskočený případ hlásí ok. Fabric důkazů cenu zvyšuje. Dříve přeskočení zaznamenané jako ok stálo čtenáře jeden zavádějící řádek v logu; nyní se zapisuje do sledované hlavní knihy s commitem a digestem vedle, publikuje se a kontrola jej čte zpět jako důkaz záruky. Podsystém, jehož celým argumentem jsou důkazy nad tvrzeními, nemůže dovolit svému producentovi říkat ok pro „nedíval jsem se“.