Každý program obsahuje jeden chybný řádek. Každý program jde zkrátit o jeden řádek. Z toho plyne, že každý program jde zkrátit na jeden řádek, který je chybný.
Jak jste si už jistě všimli, i v relativně jednoduchých programech lze udělat
nemalé množství chyb. Naštěstí testovat programy, které pouze berou vstup a
vrací výstup, není až tak složité. Stačí napsat generátor vstupů, for
cyklus a pár podmínek, kterými budeme vyhodnocovat výstup, a máme základní
testování…
Tak jednoduché to bohužel není – ale nápad je to dobrý. Abychom vás podpořili v psaní si vlastních testů, ať už ručně psaných, nebo generovaných, dáváme vám k dispozici dva python skripty, které vám to mohou usnadnit.
Soubor process_handler.py
obsahuje interní logiku, která za vás ošetřuje
některé okrajové případy. Ze třídy ProcessHandler
je pro vás důležitá pouze
statická metoda run
.
Váš vlastní kód doporučujeme psát do separátního souboru, například do
test_your_c_program.py
. V něm se můžete podívat na ukázku, jak metodu run
volat
a jak lze případně psát assert
(pravidla kontrolující určité podmínky).
Jakmile si oba soubory stáhnete z odkazu, který je níže, klidně si rovnou
zkuste spustit ukázku z terminálu pomocí python3 test_your_c_program.py
. V ukázce
testujeme chování programu base64
. Pokud jste na Windows, klidně si zaměňte
volání za jiný program dle svého výběru.
A jak to tedy využít? Doporučujeme si úkoly testovat po každém z následujících kroků:
-
Zkopírovat si do testovátka všechny testy uvedené v zadání.
-
Napsat si alespoň pár vlastních testů, a to na validní vstupy, okrajové případy a případně i nevalidní vstupy.
-
Napsat si generátor vstupů a zkusit, zda váš Céčkový program na některém z nich nepadá. V tuto chvíli stačí, že nepadá, výstup moc kontrolovat nemusíte.
-
Pokud všechny předchozí části prošly, doporučujeme přejít na Aisu. Tam zkoušejte pouštět program a vzorovou implementaci na stejných vstupech, a tyto výstupy porovnávat.
-
Profit?
A pak samozřejmě znovu spustit testy vždy, když uděláte nějakou (větší) úpravu svého Céčkového kódu.
Mějte na paměti, že sdílené fakultní stroje používá hodně lidí, a buďte k nim
ohleduplní. Je to nejen slušné chování, ale taky se tím vyhnete například
dočasné blokaci účtu. Pro více informací se podívejte na příslušnou stránku na
fakultním webu. Příkaz
nice
už za vás řeší ProcessHandler
.
Jak použít testovátko
První krok je si testovátko stáhnout.
- process_handler.py
-
Interní kód testovátka
- test_your_c_program.py
-
Ukázka, jak testovátko volat.
Je psané tak, aby stačilo interagovat se statickou metodou ProcessHandler.run
.
Ta má jeden povinný a tři volitelné parametry a vrací tříprvkový Tuple
obsahující:
-
návratový kód programu,
-
string
sstdout
, -
string
prostderr
.
def run(command: List[str],
input_str: Optional[str] = None,
print_output: bool = True,
timeout: int = 5 # seconds
) -> Tuple[int, str, str]:
Prvním (a jediným povinným) parametrem je command
, který očekává seznam
řetězců. První prvek je adresa souboru, který chceme spustit. Případné další
prvky seznamu jsou jednotlivé argumenty.
Pojďme si to ukázat rovnou na příkladu. Následujícím způsobem můžete programu
předat argumenty. První příkaz fungovat bude, druhý však ne. Spouštíme totiž
program přímo, bez pomoci shellu (např. bash
). Tedy nějaká funkčnost, na kterou
jste zvyklí, není k dispozici.
from process_handler import ProcessHandler
ProcessHandler.run(["ls", ".", "-a", "-l", "--human-readable", "--width=50"]) # this works
ProcessHandler.run(["ls", "~", "-a", "-l", "--human-readable", "--width=50"]) # this doesn't, because we don't have ~ outside of shell
Je možné spouštět i příkazy skrz shell, to ale nedoporučujeme. Nad příkazy spuštěnými přímo má testovátko větší kontrolu.
from process_handler import ProcessHandler
ProcessHandler.run(['/bin/sh', '-c', 'echo Lorem Ipsum'], print_output=True, timeout=0.5)
Zbývá už tedy jen zmínit, jak lze předávat vstup a jak pracovat s výstupem.
from typing import List
from process_handler import ProcessHandler
command_to_execute: List[str] = ["base64"]
input_str: str = f"Lorem ipsum from stdin"
return_code, outs, errs = ProcessHandler.run(command_to_execute, input_str=input_str, print_output=True)
assert return_code == 0
assert "TG9yZW0gaXBzdW0gZnJvbSBzdGRpbg==" in outs # beware different newlines based on system
assert len(errs) == 0
Jak testovat svůj vlastní kód, a nejen již existující programy jako ls
nebo
base64
? Úplně stejně, stačí jen změnit cestu k souboru, který se má spustit.
Pokud kompilujete ručně pomocí gcc
a nezvolíte vlastní jméno pro výstupní
soubor, tak se automaticky pojmenuje a.out
. Pokud kompilujete skrz CMake,
který dodáváme ke cvičením, nejspíše bude výstupní program v adresáři
cmake-build-debug
a bude se jmenovat dle aktuálního úkolu, např. calc
. Pokud
jste na Windows, tak to nejspíše bude něco jako calc.exe
. Věříme, že to už
zvládnete dohledat.
from process_handler import ProcessHandler
ProcessHandler.run(['./a.out'])
ProcessHandler.run(['./cmake-build-debug/calc'])
ProcessHandler.run(['./cmake-build-debug/calc.exe'])
Zbývá poslední otázka – jak si generovat testy. To necháme na vás. Věříme, že si s Pythonem poradíte.
Na závěr připomeneme, že testovátko lze použít několika různými způsoby. Doporučujeme si vyzkoušet všechny:
-
Testovat vůči ručně psaným testům.
-
Testovat pomocí generovaných testů (fuzzing) a to jak na správnost, tak na stabilitu programu.
-
Testovat vůči vzorové implementaci
Tak vzhůru do toho, půl testů je hotovo!