From 4093f6b63e322bd9250f27f885ecc2cf5161905b Mon Sep 17 00:00:00 2001
From: exyi <exyi@outlook.cz>
Date: Tue, 13 Oct 2020 16:35:18 +0000
Subject: [PATCH] add tests for exact match of grabbed sol/assignment

---
 .../tests/example_assignments/28-Z1-1.html    | 36 ++++++++++
 .../src/tests/example_assignments/31-3-3.html | 51 +++++++++++++++
 .../tests/example_assignments/32-Z1-1.html    | 28 ++++++++
 .../src/tests/example_solutions/28-Z1-1.html  | 65 +++++++++++++++++++
 .../src/tests/example_solutions/31-3-3.html   | 50 ++++++++++++++
 .../src/tests/example_solutions/32-Z1-1.html  | 27 ++++++++
 frontend/src/tests/grabber.test.ts            | 46 +++++++++++--
 7 files changed, 298 insertions(+), 5 deletions(-)
 create mode 100644 frontend/src/tests/example_assignments/28-Z1-1.html
 create mode 100644 frontend/src/tests/example_assignments/31-3-3.html
 create mode 100644 frontend/src/tests/example_assignments/32-Z1-1.html
 create mode 100644 frontend/src/tests/example_solutions/28-Z1-1.html
 create mode 100644 frontend/src/tests/example_solutions/31-3-3.html
 create mode 100644 frontend/src/tests/example_solutions/32-Z1-1.html

diff --git a/frontend/src/tests/example_assignments/28-Z1-1.html b/frontend/src/tests/example_assignments/28-Z1-1.html
new file mode 100644
index 0000000..084c34d
--- /dev/null
+++ b/frontend/src/tests/example_assignments/28-Z1-1.html
@@ -0,0 +1,36 @@
+<p>
+Kevin otevřel obálku (kterou našel ve schránce) a vyndal z&nbsp;ní všechny papíry. Jeden
+ho obzvláště zaujal (ten od KSP). Ihned si všiml, že (na začátku) obsahuje nějak
+mnoho závorek. Tak by ho zajímalo, jestli tam (autoři zadání) neudělali nějakou
+chybu. Pomůžete mu (s&nbsp;ověřením)?</p>
+<p>
+Pro danou posloupnost symbolů <tt>(</tt> a <tt>)</tt>, tedy otevíracích
+a zavíracích závorek, najděte od začátku co nejdelší úsek, který je platným
+uzávorkováním – tedy každá závorka má svoji do páru.</p>
+<p>
+<i>Tvar vstupu:</i>
+Na prvním řádku dostanete číslo&nbsp;<span class="math">N</span>. Na dalších <span class="math">N</span>&nbsp;řádcích budou jednotlivé
+testovací případy.</p>
+<p>
+<i>Tvar výstupu:</i>
+Pro každý testovací případ vypište na řádek délku nalezeného uzávorkování.</p>
+<p>
+Slibujeme, že <span class="math">N</span> bude nejvýše <span class="math">1 000</span>, a každý řádek bude mít nejvýše <span class="math">10<sup>5</sup></span>&nbsp;symbolů.</p>
+<div class="leftfloat" style="width: 48%;">
+<i>Ukázkový vstup:</i>
+<pre>3
+(()())())()
+((()())())()
+(()()))()()()()()
+</pre>
+</div>
+<div class="rightfloat" style="width: 48%;">
+<i>Ukázkový výstup:</i>
+<pre>8
+12
+6
+</pre>
+</div>
+<div class="clearfloat"></div>
+<p>
+Uzavírající závorka na deváté pozici nemá svůj protějšek. Druhý řádek je celý platné uzávorkování.</p>
diff --git a/frontend/src/tests/example_assignments/31-3-3.html b/frontend/src/tests/example_assignments/31-3-3.html
new file mode 100644
index 0000000..bad1e26
--- /dev/null
+++ b/frontend/src/tests/example_assignments/31-3-3.html
@@ -0,0 +1,51 @@
+<p>
+Na podlaze se nachází sesypaný hrách s&nbsp;popelem a&nbsp;mezi tím poskakuje několik
+holoubků a&nbsp;vrabčáků. Chtějí Popelce pomoct vysbírat všechen hrách. Pro každou
+kuličku hrachu chceme zjistit, zda ji nějaký ptáček dokáže sezobnout a&nbsp;přenést
+do ošatky. Holoubci i&nbsp;vrabčáci se však pohybují každý jinak. Vrabčáci poskakují
+rovně dopředu, dozadu, doleva i&nbsp;doprava, zato holoubci chodí šikmo do všech
+čtyř stran.</p>
+<p>
+Celou situaci si lze představit jako rozmístění figurek na šachovnici.
+Holoubci představují černé střelce, vrabčáci černé věže a&nbsp;kuličky hrachu pak
+bílé pěšáky. Pro každou bílou figurku chceme zjistit, zda ji dokážeme v&nbsp;jednom
+tahu nějakou černou figurkou vyhodit.</p>
+<p>
+<i>Formát vstupu:</i> Na prvním řádku vstupu se nachází dvě čísla <span class="math">B</span>&nbsp;a&nbsp;<span class="math">C</span>, a&nbsp;to
+počet kuliček hrachu (neboli bílých figurek) a&nbsp;počet ptáčků (neboli černých
+figurek). Na dalších <span class="math">B</span>&nbsp;řádcích se nachází souřadnice kuliček hrachu jako
+dvojice čísel oddělených mezerou, číslo řádku a&nbsp;číslo sloupce udávajících, kde
+se kulička nachází. Na dalších <span class="math">C</span>&nbsp;řádcích se pak nachází pozice ptáčků – každý
+takový řádek obsahuje znak <tt>H</tt> nebo <tt>V</tt> a&nbsp;dvojici čísel udávajících
+řádek a&nbsp;sloupec, kde ptáček stojí (opět vše oddělené mezerami). Znak&nbsp;<tt>H</tt>
+značí holoubka (pohybuje se jako střelec) a&nbsp;znak&nbsp;<tt>V</tt> vrabčáka (pohybuje se
+jako věž). Figurky na vstupu mohou být seřazené náhodně.</p>
+<p>
+<i>Formát výstupu:</i> Na <span class="math">B</span>&nbsp;řádků výstupu vypište (ve stejném pořadí, jako na
+vstupu) pro každou kuličku hrachu <tt>ANO</tt> nebo <tt>NE</tt> podle toho, jestli
+existuje nějaký holoubek nebo vrabčák, který může tuto kuličku hrachu jedním
+tahem sezobnout.</p>
+<div class="leftfloat" style="width: 48%;">
+<i>Ukázkový vstup:</i>
+<pre>4 3
+0 1
+0 2
+0 3
+2 2
+V 0 0
+H 1 3
+V 3 3
+</pre>
+</div>
+<div class="rightfloat" style="width: 48%;">
+<i>Ukázkový výstup:</i>
+<pre>ANO
+ANO
+NE
+ANO
+</pre>
+<img class="rightfloat image" src="http://localhost/h/ulohy/31/t3133.png" alt="Šachovnice s&nbsp;příkladem" title="Šachovnice s&nbsp;příkladem">
+</div>
+<div class="clearfloat"></div>
+<i>Poznámka:</i>
+BUG
diff --git a/frontend/src/tests/example_assignments/32-Z1-1.html b/frontend/src/tests/example_assignments/32-Z1-1.html
new file mode 100644
index 0000000..c2660be
--- /dev/null
+++ b/frontend/src/tests/example_assignments/32-Z1-1.html
@@ -0,0 +1,28 @@
+<p>
+Kevin se po prázdninách zase vrací do školy. Jako obvykle dostal seznam pomůcek,
+které si má obstarat. Malé sešity, velké sešity, pravítko, pastelky, barvy na
+výtvarku, učebnice na češtinu a dějepis a možná i novou aktovku. To vypadá na
+velký nákup!</p>
+<p>
+Kevin se tedy vydal do obchodu. Jak dává věci do košíku, píše si i seznam cen. V
+tom si ale uvědomí, že má jen <span class="math">P</span> korun. Kolik nejméně z <span class="math">N</span> věcí v košíku musí
+Kevin vyhodit, aby mu <span class="math">P</span> korun stačilo?</p>
+<p>
+<i>Tvar vstupu:</i> Na prvním řádku se nachází počet věcí v košíku <span class="math">N</span> a počet korun <span class="math">P</span>, které Kevin může
+utratit. Na druhém řádku je pak seznam cen věcí v košíku, tedy <span class="math">N</span> kladných čísel
+oddělených mezerami.</p>
+<p>
+<i>Tvar výstupu:</i> Vypište jedním číslem nejmenší počet věcí, které musí Kevin
+z&nbsp;košíku vyhodit.</p>
+<div class="leftfloat" style="width: 48%;">
+<i>Ukázkový vstup:</i>
+<pre>5 59
+8 20 30 1999 40
+</pre>
+</div>
+<div class="rightfloat" style="width: 48%;">
+<i>Ukázkový výstup:</i>
+<pre>2
+</pre>
+</div>
+<div class="clearfloat"></div>
diff --git a/frontend/src/tests/example_solutions/28-Z1-1.html b/frontend/src/tests/example_solutions/28-Z1-1.html
new file mode 100644
index 0000000..76bdd0b
--- /dev/null
+++ b/frontend/src/tests/example_solutions/28-Z1-1.html
@@ -0,0 +1,65 @@
+<p>
+Nejprve si vyřešme jednodušší úlohu: jak o&nbsp;nějaké posloupnosti závorek
+poznáme, jestli je (celá) správně uzávorkovaná? Určitě musí obsahovat
+stejný počet levých a&nbsp;pravých závorek – jinak těžko mohou tvořit páry.</p>
+<p>
+Ovšem to nestačí. Uvažte třeba posloupnost <tt>())(</tt>. Ta obsahuje dvě
+levé a&nbsp;dvě pravé závorky, ale korektním uzávorkováním určitě není.
+Na třetí pozici se pokoušíme ukončit závorku „dřív, než začala“.</p>
+<p>
+Jak takovou situaci poznáme? Podíváme-li se na první tři znaky naší
+posloupnosti, vidíme, že obsahují jednu levou závorku a&nbsp;dvě pravé.
+Obecně problém nastane, pokud existuje v&nbsp;posloupnosti nějaké místo
+takové, že v&nbsp;úseku od začátku po toto místo je více pravých závorek
+než levých. Pak alespoň jedna z&nbsp;těch pravých nemá před sebou žádnou
+levou, se kterou bychom ji mohli spárovat.</p>
+<p>
+Pokud posloupnost obsahuje stejně levých a&nbsp;pravých závorek a nenastane
+problém popsaný výše, pak už si snadno rozmyslíte, že je vždy jde
+správně spárovat, a&nbsp;tedy uzávorkování je korektní.</p>
+<div class="center"><img class="image" src="http://localhost/img/hippo_table_pilot.png" alt="Ilustrace: Hroší pilot" title="Ilustrace: Hroší pilot"></div>
+<p>
+Nyní k původní úloze. Představme si, že si chceme označit všechna
+místa v&nbsp;posloupnosti, kde může končit korektní uzávorkování (začínající
+na začátku). Třeba pro příklad ze zadání to budou následující místa
+(označena hvězdičkou): <tt>(()())*()*)()</tt>.</p>
+<p>
+Jak je najdeme? Budeme postupně procházet posloupnost od začátku a&nbsp;průběžně
+si počítat, kolik levých a&nbsp;pravých závorek už jsme viděli. Pokud narazíme
+na místo, kde jsme napočítali víc pravých než levých, můžeme rovnou skončit.
+Už víme, že takové uzávorkování korektní není, a&nbsp;přidáním libovolných dalších
+závorek na konec už nemůžeme napravit chybějící závorky nalevo od aktuální
+pozice.</p>
+<p>
+Pokud tato situace ještě nenastala a&nbsp;objevíme místo, před kterým je počet
+levých a&nbsp;pravých závorek stejný, označíme si jej. Podle toho, co jsme si
+řekli výše, uzávorkování končící na tomto místě je korektní.</p>
+<p>
+Ukažme si průběh algoritmu na předchozím příkladu:</p>
+<div class="center">
+<table class="centertable borderedtable">
+<tbody><tr><td></td><td> (</td><td> (</td><td>  ) </td><td> (</td><td> ) </td><td> ) </td><td>  (</td><td> ) </td><td> ) </td><td> (</td><td> )</td></tr>
+<tr><td>Pozice        </td><td> 1 </td><td> 2 </td><td>  3 </td><td> 4 </td><td> 5 </td><td> 6 </td><td>  7 </td><td> 8 </td><td> 9 </td><td> 10 </td><td> 11</td></tr>
+<tr><td>Počet levých  </td><td> 1 </td><td> 2 </td><td>  2 </td><td> 3 </td><td> 3 </td><td> 3 </td><td>  4 </td><td> 4 </td><td> 4 </td><td> - </td><td>  -</td></tr>
+<tr><td>Počet pravých </td><td> 0 </td><td> 0 </td><td>  1 </td><td> 1 </td><td> 2 </td><td> 3 </td><td>  3 </td><td> 4 </td><td> 5 </td><td> - </td><td>  -</td></tr>
+<tr><td>Rozdíl        </td><td> 1 </td><td> 2 </td><td>  1 </td><td> 2 </td><td> 1 </td><td> 0 </td><td>  1 </td><td> 0 </td><td> -1 </td><td> - </td><td> -</td></tr>
+<tr><td>Akce          </td><td>   </td><td>   </td><td>    </td><td>   </td><td>   </td><td> * </td><td>    </td><td> * </td><td> <tt>X</tt> </td><td>   </td><td></td></tr>  
+</tbody></table>
+</div>
+<p>
+Hvězdička znamená označení místa s&nbsp;korektním uzávorkováním
+a&nbsp;<tt>X</tt> konec prohledávání, když žádné další uzávorkování korektní být
+nemůže.</p>
+<p>
+Na konci jen vypíšeme nejpravější označené místo. Všimněme si, že
+si nemusíme ukládat všechna označená místa, stačí si vždy pamatovat
+poslední dosud nalezené. Další drobné zjednodušení je místo dvou
+počítadel použít jen jedno, uchovávající rozdíl počtu dosud načtených
+levých a&nbsp;pravých závorek (řádek „rozdíl“ v&nbsp;tabulce výše). Pak označujeme,
+když je toto počítadlo rovné nule, a&nbsp;končíme, pokud klesne pod nulu.</p>
+<p><a href="http://localhost/z/ulohy/28/28Z11-1.py">Program (Python 3) – počítání obou druhů závorek</a></p>
+<p><a href="http://localhost/z/ulohy/28/28Z11-2.py">Program (Python 3) – stačí nám počítat jen levé</a></p>
+<p class="author"><a href="http://localhost/kontakty/organizatori/">Filip Štědronský</a></p>
+<p>
+<i>Pokud máte pocit, že jste zde dříve viděli něco jiného, nemýlíte se. Věříme, že toto řešení pro vás bude stravitelnější.</i></p>
+<hr class="clearfloat">
diff --git a/frontend/src/tests/example_solutions/31-3-3.html b/frontend/src/tests/example_solutions/31-3-3.html
new file mode 100644
index 0000000..8ca6722
--- /dev/null
+++ b/frontend/src/tests/example_solutions/31-3-3.html
@@ -0,0 +1,50 @@
+<p>
+Pro každou z&nbsp;bílých figurek chceme zjistit, zda je ohrožována aspoň jednou
+černou. Můžeme zkusit vyzkoušet pro každou dvojici bílé a černé figurky, zda se
+ohrožují. Těchto dvojic je ovšem až kvadraticky mnoho a navíc se nám může stát, že
+i když by se dvojice ohrožovala, tak se mezi nimi nachází další figurka, která
+černé figurce z&nbsp;naší dvojice překáží ve výhledu a znemožňuje jí bílou figurku
+sebrat. Protože ověřit, zda dvojici nepřekáží ve výhledu další figurka, trvá
+lineárně dlouho, toto řešení je <span class="math">O(n<sup>3</sup>)</span>.</p>
+<p>
+Jelikož černé figurky jsou pouze věže a střelci, bílé figurky mohou být
+ohroženy pouze z&nbsp;dohromady osmi směrů, kterými se černé dokážou pohybovat.
+Řešení můžeme vylepšit tím, že budeme zjišťovat, zda je bílá figurka ohrožena,
+pro každý směr zvlášť. Například pokud chceme pro každou bílou figurku zjistit,
+zda je ohrožena zleva nebo zprava, stačí nám uvažovat pouze ty figurky, které
+leží na stejném řádku. Navíc každá figurka má na svém řádku nejvýše dva
+sousedy, jednoho zleva, druhého zprava. Stačí nám uvažovat jen tyto
+sousedy, protože budou všem ostatním figurkám na řádku překážet ve výhledu. Pro
+každou z&nbsp;lineárně mnoho bílých figurek tedy v&nbsp;lineárním čase najdeme ty
+figurky, které s&nbsp;nimi sdílí řádek (pro ostatní směry ty, které sdílí sloupec či
+diagonálu), v&nbsp;lineárním čase najdeme mezi nimi dva nejbližší sousedy bílé
+figurky a pro ty vyzkoušíme, zda figurku neohrožují, to jest zda se jedná o&nbsp;černé figurky a jestli jsou typu, který se umí v&nbsp;tomto směru pohybovat. Celkově je toto řešení
+<span class="math">O(n · (n + n))</span>, tedy <span class="math">O(n<sup>2</sup>)</span>.</p>
+<p>
+Abychom uměli rychle zjistit, které figurky leží na stejném řádku, můžeme si
+nejprve všechny figurky seřadit podle jejich souřadnice řádku. V&nbsp;seřazeném seznamu figurek
+leží figurky na stejném řádku vedle sebe. Pokud bychom navíc vzali
+všechny figurky na stejném řádku a seřadili je podle souřadnice sloupce a
+uložili do nějakého pole, budeme je mít uspořádané v&nbsp;tom pořadí, v&nbsp;jakém leží
+na řádku za sebou. Pro libovolnou figurku na řádku tedy umíme najít její
+sousedy prostě tak, že se podíváme v&nbsp;tomto uspořádaném poli na index o&nbsp;jedna
+nižší a o&nbsp;jedna vyšší.</p>
+<p>
+Tato dvě setřídění můžeme provést zároveň. Primárně figurky setřídíme podle
+souřadnice řádku, sekundárně pak podle souřadnice sloupce. Takové uspořádání,
+kde třídíme primárně podle jednoho kritéria a sekundárně podle jiného, se
+nazývá <i>lexikografické.</i> Nyní nám stačí projít setříděné pole a pro každou
+bílou figurku se podívat na její dva sousedy a rozhodnout, zda ji neohrožují.
+Také musíme dát pozor na to, že sousední figurky v&nbsp;setříděném poli figurek se
+nemusí nacházet na stejném řádku. Pokud je soused bíle figurky na jiném řádku,
+znamená to, že v&nbsp;daném směru se už na stejném řádku nenachází žádné figurky.
+Také si všimněme, že tímto uspořádáním najdeme všechny takové figurky, které
+bíle ohrožují ne jen z&nbsp;jednoho směru z&nbsp;osmi, ale ze dvou (zprava i zleva).</p>
+<p>
+Kompletní řešení pro každou ze čtyř „os“ (doleva-doprava, nahoru-dolů,
+diagonálně doprava nahoru, diagonálně doprava dolů) lexikograficky figurky
+uspořádá, uspořádané pole v&nbsp;lineárním čase projde a pro každou bílou figurku určí, zda
+je ohrožována. Toto řešení má tedy časovou složitost <span class="math">O(n  <span class="nomath">log</span> n)</span>.</p>
+<p><a href="http://localhost/h/ulohy/31/3133.c">Program (C)</a></p>
+<p class="author"><a href="http://localhost/kontakty/organizatori/">Kuba Pelc</a></p>
+<hr class="clearfloat">
diff --git a/frontend/src/tests/example_solutions/32-Z1-1.html b/frontend/src/tests/example_solutions/32-Z1-1.html
new file mode 100644
index 0000000..8807a0e
--- /dev/null
+++ b/frontend/src/tests/example_solutions/32-Z1-1.html
@@ -0,0 +1,27 @@
+<p>
+K&nbsp;tomu, abychom za věci utratili co možná nejméně, potřebujeme do košíku
+postupně vybírat věci od té nejlevnější po tu nejdražší.</p>
+<p>
+Pole cen tedy vzestupně setřídíme a budeme jej postupně procházet, přičemž
+si budeme pamatovat součet cen věcí, které při průchodu potkáme. Jakmile
+při procházení přesáhne tento součet koruny vyhrazené na nákup, tak podle
+pozice v&nbsp;poli víme, kolik věcí si můžeme koupit.</p>
+<p>
+Jelikož se však zadání ptá na počet věcí, které si koupit nemůžeme, tak jako
+řešení vypíšeme počet věcí od pozice, kde jsme skončili s&nbsp;procházením, do konce
+pole cen.</p>
+<p>
+Řešení je jistě správné, určitě se totiž nemůže stát, že bychom si mohli koupit
+víc věcí, než nám vypíše algoritmus. Vyhodili jsme z&nbsp;košíku ty nejdražší věci,
+které jsme mohli, takže nám jich určitě nestačilo vyhodit méně.</p>
+<p>
+Rychlost řešení ovlivní hlavně to, jak rychle dokážeme ceny setřídit. Existují
+různé třídící algoritmy a pokud použijete nějaký algoritmus zabudovaný přímo
+v&nbsp;programovacím jazyce, běží většinou v&nbsp;čase <span class="math">O(n  <span class="nomath">log</span> n)</span>. Pokud jste si
+implementovali vlastní jednoduché třídění, pravděpodobně bude mít časovou
+složitost <span class="math">O(n<sup>2</sup>)</span>. O&nbsp;třídících algoritmech si můžete přečíst
+v&nbsp;naší <a href="http://localhost/viz/kucharky/trideni">kuchařce o&nbsp;třídění</a>.</p>
+<p><a href="http://localhost/z/ulohy/32/32Z11.py">Program (Python 3)</a></p>
+<p><a href="http://localhost/z/ulohy/32/32Z11.cpp">Program (C++)</a></p>
+<p class="author"><a href="http://localhost/kontakty/organizatori/">Tom Sláma</a></p>
+<hr class="clearfloat">
diff --git a/frontend/src/tests/grabber.test.ts b/frontend/src/tests/grabber.test.ts
index 06b36e1..663b0c0 100644
--- a/frontend/src/tests/grabber.test.ts
+++ b/frontend/src/tests/grabber.test.ts
@@ -1,7 +1,8 @@
 import * as g from '../ksp-task-grabber'
-import { readFileSync } from 'fs';
+import { readFileSync, readdirSync } from 'fs';
 import { resolve } from 'path';
 import type { TaskDescriptor, TasksFile } from '../tasks';
+import { action_destroyer } from 'svelte/internal';
 
 const node_fetch: any = require('node-fetch')
 
@@ -41,9 +42,9 @@ describe('tasks assignment', () => {
     for (const t of tasks.tasks) {
         if (t.type != "open-data") continue;
 
-        test(`${t.id}`, async () => {
-            const assignment = await g.grabAssignment((t as any).taskReference)
-            expect((t as any).taskReference).toEqual(assignment.id)
+        test.concurrent(`${t.id}`, async () => {
+            const assignment = await g.grabAssignment(t.taskReference)
+            expect(t.taskReference).toEqual(assignment.id)
             expect(assignment.points).toBeGreaterThanOrEqual(1)
             expect(assignment.description.trim()).toBeTruthy()
             expect(assignment.name.trim()).toBeTruthy()
@@ -66,7 +67,7 @@ describe('tasks solutions', () => {
             continue
         }
 
-        test(`${t.id}`, async () => {
+        test.concurrent(`${t.id}`, async () => {
             const sol = await g.grabSolution(t.taskReference)
             expect(t.taskReference).toEqual(sol.id)
             expect(sol.description.trim()).toBeTruthy()
@@ -74,3 +75,38 @@ describe('tasks solutions', () => {
         })
     }
 })
+
+describe('task assignment (exact match)', () => {
+    const assignmentDir = resolve(__dirname, "example_assignments")
+    const assignmentFiles = readdirSync(assignmentDir)
+    for (const a of assignmentFiles) {
+        const [_, id] = /(.*?)\.html/.exec(a)!
+        test(id, async () => {
+            const assignment = await g.grabAssignment(id)
+            const expected = readFileSync(resolve(assignmentDir, a)).toString()
+            try {
+                expect(assignment.description.trim()).toEqual(expected.trim())
+            } catch(e) {
+                console.warn(assignment.description)
+                throw e
+            }
+        })
+    }
+})
+describe('task solution (exact match)', () => {
+    const assignmentDir = resolve(__dirname, "example_solutions")
+    const assignmentFiles = readdirSync(assignmentDir)
+    for (const a of assignmentFiles) {
+        const [_, id] = /(.*?)\.html/.exec(a)!
+        test(id, async () => {
+            const solution = await g.grabSolution(id)
+            const expected = readFileSync(resolve(assignmentDir, a)).toString()
+            try {
+                expect(solution.description.trim()).toEqual(expected.trim())
+            } catch(e) {
+                console.log(solution.description)
+                throw e
+            }
+        })
+    }
+})