Warning
This page is located in archive.

Testování HW programů před odevzdáním

Ověření správnosti implementance domácích úkolů není nutné vyloženě realizovat odevzdáváním do BRUTE, tzv. zkoušet odevzdat. Mnohem výhodnější je prostě jen odevzdat správně fungující program, čímž lze ušetřit nejen čas, ale také případný maximální počet uploadů a vyhnout se tak dodatečné penalizaci. Pravděpodbně nejlepším způsobem je detailní inspekce a procházení kódů s tím, že je dobré vědět, co se přesně děje na každém řádku kódu a co se může stát v závislosti na konkrétním vstupu. Taková detailní inspekce však vyžaduje jistou dávku zkušenosti, proto jsme pro vás připravili tzv. trial/error možnosti jak ověřit, že program generuje požadovaný výstup.

Kromě testování správnosti generovaného výstupu je v případě použití dynamickké alokace v programu výhodné použít program valgrind pro odhalení nevhodného přístupu nákládání s pamětí viz. The Valgrind Quick Start Guide.

Porovnání s testovacími vstupy a výstupy

První způsobem jak otestovat funkčnost programu je využit přiložených vstupních a výstupních souborů. Například v případě HW 01 - Kreslení (ASCII art) vygenerujeme pro vstup data/pub01.in výstupní soubor my-pub01.out a porovnáme s přiloženým souborem

$ ./b3b36prg-hw01 < data/pub01.in > my-pub01.out
$ diff data/pub01.out my-pub01.out
1,7c1,7
<    X
<   X X
<  X   X
< XXXXXXX
< X     X
< X     X
< XXXXXXX
---
>    x
>   x x
>  x   x
> xxxxxxx
> x     x
> x     x
> xxxxxxx
kde na první pohled všimout, že jsme místo X použili x. Případně můžeme použít program vimdiff nebo jiný tzv. rozdílovač. Alternativně, zejména pak pro výstupy s bílými znaky může být výhodné zobrazit výstup ve hexadecimálním tvaru příkazem hexdump, např.
$ hexdump -C my-pub01.out
který můžeme též uložit do soubory a následně porovnat například již zmiňovaným vimdiff. To se může hodit pokud na první pohled nevidíme rozdíl jako například pokud nesprávně tiskneme tzv. bílé znaky, v tomto případě mezery.
$ diff data/pub01.out my-pub01.out
1,3c1,3
<    X
<   X X
<  X   X
---
>    X
>   X X
>  X   X

Převedením do hexavýpisu rozdíl spíše uvidíme

$ hexdump -C my-pub01.out >my-pub01.out.hex
$ hexdump -C data/pub01.out >pub01.out.hex
$diff pub01.out.hex my-pub01.out.hex
1,5c1,5
< 00000000  20 20 20 58 0a 20 20 58  20 58 0a 20 58 20 20 20  |   X.  X X. X   |
< 00000010  58 0a 58 58 58 58 58 58  58 0a 58 20 20 20 20 20  |X.XXXXXXX.X     |
< 00000020  58 0a 58 20 20 20 20 20  58 0a 58 58 58 58 58 58  |X.X     X.XXXXXX|
< *
< 00000030
---
> 00000000  20 20 20 58 20 20 20 0a  20 20 58 20 58 20 20 0a  |   X   .  X X  .|
> 00000010  20 58 20 20 20 58 20 0a  58 58 58 58 58 58 58 0a  | X   X .XXXXXXX.|
> 00000020  58 20 20 20 20 20 58 0a  58 20 20 20 20 20 58 0a  |X     X.X     X.|
> 00000030  58 58 58 58 58 58 58 0a                           |XXXXXXX.|
> 00000038

Z výpisu by již mělo být patrné, že tiskneme mezery až na plnou šiři domečku, což není očekávaný výstup.

Případně ještě lépe můžeme vidět při použití vimdiff nebo jiného rozdílovače.

$ vimdiff pub01.out.hex my-pub01.out.hex

Generování náhodného vstupu a referečního řešení

Další možností je využít přiloženého programu pro generování náhodných zadání (vstupních souborů), který zároveň funguje také jako refereční řešení. Například pro HW 02 - Prvočíselný rozklad se generátor/referenční řešení nachází v binárním souboru b3b36prg-hw02-genref, který je sestaven pro prostředí v počítačových učebnách, tj.

$ file b3b36prg-hw02-genref 
b3b36prg-hw02-genref: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, stripped

Program má minimalistické rozhraní a nápovědu lze vyvolat přepínačem -h, např. ./b3b36prg-hw02-genref -h

$ ./b3b36prg-hw02-genref -h
Generator and reference solution of the HW02
Version $Id: b3b36prg-hw02-genref.c 499 2019-02-17 21:40:16Z jf $
Usage:
./b3b36prg-hw02-genref [-generate] [-optional|-prg-optional] [-h]
Generator is enabled by -generate argument and produce a random instance of the HW02
Generator is also invoked if the program binary contains "gener" string, e.g., using symbolic link such as generate
Otherwise a reference solution expects the input and produces the output as it is required in the assigment of the 02
-h prints this help message

In a case of any troubles report them to the teachers together with the 'Version $Id: b3b36prg-hw02-genref.c 499 2019-02-17 21:40:16Z jf $'

Ve vychozím použití funguje jako refereční řešení očekávájící vstup na stdin a generuje výstup na stdout, např.

$ ./b3b36prg-hw02-genref <data/data/pub01-m.in >pub01-ref.out
$ diff data/pub01-m.out pub01-ref.out           
kdy je generovaný výstup shodný s přiloženým výstupním souborem data/pub01-m.out.

V případě uvedení argument -generate program vypíše na stdout náhodný vstup

$ ./b3b36prg-hw02-genref -generate > my01.in
Generate random instance for HW02
$ cat my01.in 
8804431
0
pro který lze následně vytvořit refereční výstup

$ ./b3b36prg-hw02-genref <my01.in >my01.out 
$ cat my01.out 
Prvociselny rozklad cisla 8804431 je:
347 x 25373
který lze použít pro ověření funkčnosti vlastní implementace způsobem uvedeným výše.

V případě domácího úkolu, který má povinnou a volitelnou část, lze generátor přeponout do režimu generování vstupu pro volitelné zadání přepínačem -optional. Například v případě HW 03 - Kreslení (ASCII art) se standardní zadání generuje

$ ./b3b36prg-hw03-genref -generate
Generate random instance for HW03
31 36
a vstup pro volitelné zadání
$ ./b3b36prg-hw03-genref -generate -optional
Generate random instance for HW03
33 33 3
V těchto případech je náhodně generovaný vstup vypsán na stdout a “Generate random instance for HW03” je vypsána na stderr.

Pamatujte, že generátor zpravidla vytváří validní vstupy, váš program však musí správně reagovat na špatné vstupy, které můžete například vytvořit editací generovaných vstupních souborů.

Skriptování

Náhodně generovanými vstupy sice nelze zaručit plné otestování aplikace, ale lze výrazně zvýšit odhalení chyby. Ručně generovat jednotky nebo desítky či stovky náhodných vstupů je zbytečně pracné a můžeme použít krátkého programu pro příkazový interpret. V archivu je tak vedle generátoru/referečního řešení přiložen skript generate_solution.sh, který např. vytvoří čtyři vstupní soubory a k tomu odpovídající referenční řešení:

$ cat 
#!/bin/sh
 
HW=02
PROGRAM=./b3b36prg-hw$HW-genref
 
mkdir -p files
for i in `seq 1 4`
do
   PROBLEM=files/test$i
   echo "Generate random input '$PROBLEM.in'"
   $PROGRAM -generate > $PROBLEM.in 2>/dev/null
   echo "Solve '$PROBLEM.in' and store the reference solution to '$PROBLEM.out'"
   $PROGRAM < $PROBLEM.in > $PROBLEM.out 2>$PROBLEM.err
done
kde pro definici čtyř vstupů je použit program seq, který na stdout vypíše posloupnost v rozsahu jeho argumentů.

Skriptu lze podobným způsobem využít pro dávkové otestování vlastního programu a porovnání s referenčním řešení, viz Skriptování v Bournově shellu

Testování implementace na dodaných nebo náhodně vytvořených vstupech nepředstavuje úplný a zaručený způsob ověření správnosti implementance. Je však způsobem relativně rychle a přímočaře objevit chyby způsobené přehlednutím nebo nepozorností. Pamatujete, že při řešení praktických úloh zpravidla nebudete mít k dispozici refereční řešení a je plně na programátorovi, že sám odhalí případné chyby. Proto berte generátory a refereční řešení jako doplněk, nikoliv jako garanci správnosti řešení.

Testování implementace v dynamicky linkované knihovně

Další z možností jak lze implementaci úkolu testovat je přímé použití implementovaných funkcí v nějaké programu. Sestavení takového testovacího programu s novou implementací vyžaduje zdrojové kódy nebo alespoň přeložené soubory (object file - .o). V případě, že chceme pouze změnit implementaci konkrétních funkci aniž bychom měnili rozhraní, můžeme využít mechanismu dynamicky linkovaných knihoven, kdy se konkrétní implementace linkuje k programu až při jeho spuštění. Binární testovací program tak zůstává identický a jediné co měníme je příslušná dynamická knihovna, která zachovává stejné rozhraní definované v příslušném hlavičkovém souboru.

V úloze HW 06 - Kruhová fronta v poli je cílem implementovat sadu funkcí pro práci s kruhovou frontou a to dle rozhraní předepsaného v queue.h, proto se přímo nabízí sestavit z implementace queue.c dynamickou knihovnu, což lze realizovat například volámím

clang -fPIC -shared queue.c -o libqueue.so
které je součástí přiloženého Makefile.

A pak již stačí spustit testovací program b3b36prg-hw06-test, který hledá dynamickou knihovnu v aktuálním pracovním adresáři, což lze například ověřit

$ ldd b3b36prg-hw06-test
b3b36prg-hw06-test:
	libqueue.so => ./libqueue.so (0x2c423000)
	libc.so.7 => /lib/libc.so.7 (0x2c624000)

Toho je docíleno linkováním

clang b3b36prg-hw06-test.c queue.c -o b3b36prg-hw06-test -L. -Wl,-rpath=. -lqueue
kde -lqueue specifikuje knihovnu a -L. cestu, kde se má knihovna hledat pro účely sestavení programy. Pro účely linkování dynamické knihovny při spuštění programu je nutné dále instruovat linker specifikací rpath, což je parameter linkeru, které se uvádějí za -Wl,.

Kromě rpath lze také explictině specifikovat dynamickou knihovnu prostřednictví proměnné prostředí LD_LIBRARY_PATH, kterou lze předefinovat aktuální systémové nastavení, jež lze přirozeně také upravit dle potřeb, více viz např. Shared libraries with GCC on Linux.

Po úspěšném sestavaní dynamické knihovny vlastní implementace lze spustit testovací program např.

$ ./b3b36prg-hw06-test 
Basic test ........... PASSED
Overflow test ........ PASSED
Multiple queues test . PASSED
Reallocation test .... PASSED
All tests passed

Nebo v případě dodatečného testování funkcionality volitelného zadání jako

$ ./b3b36prg-hw06-test -prg-optional
Basic test ........... PASSED
Overflow test ........ PASSED
Multiple queues test . PASSED
Reallocation test .... PASSED
Shrink test .......... PASSED
All tests passed

courses/b3b36prg/tutorials/testing.txt · Last modified: 2019/02/18 00:09 by faiglj