V této kapitole se chci zabývat testováním aplikační části diplomové
práce, tedy databáze benchmarků, řádkového analyzátoru a pomocných programů.

{\odstavec
Testování je důležitou součástí každého softwarového projektu. Softwarové systémy
jsou obyčejně natolik komplikované, že není možné předpokládat, že funguje
naprosto bezchybně ihned po napsání. Proto po vytvoření aplikace, ale obyčejně i 
v jejím průběhu, dochází k testování aplikace. V případě, že vývojový cyklus je
složitější (program je časem rozšiřován, upravován apod.), testovací procedury
se opakují. 

Z hlediska znalosti rozdělujeme testování na dvě skupiny:
}

\begin{itemize}

\item
Black box testing - tester nebere v potaz nebo dokonce nezná vnitřní fungování
testovaného systému. Tomuto druhu testování se také říká testování zaměřené na vstup-výstup
(input-output driven) nebo zaměřené na data (data-driven).

\item
White box testing - tester testuje systém se zřetelem k vnitřnímu fungování systému. Testovací
data jsou volena s ohledem ke znalosti logické struktury programu.
        
\end{itemize}

Z hlediska rozsahu rozeznáváme čtyři druhy testů:

\begin{itemize}

\item
Jednotkové testy (unit tests) testují funkčnost samostatné jednotky. V objektovém
programování se často jedná o jednu třídu.

\item
Integrační testování - testuje se skupina jednotek spojená ve větší logický celek.

\item
Systémové testování - testuje se celý systém, zda vyhovuje specifikovaným požadavkům. 
Systémové testování by mělo probíhat stylem black box testingu.

\item
Akceptační testy - testování celého systému zákazníkem, zda systém vyhovuje
specifikovaným požadavkům. Akceptační testování patří rovněž do kategorie black box
testingu.
        
\end{itemize}

Zaměřím se na systémové testování a budu předpokládat, že akceptační testy proběhnou
podle stejných scénářů. Na závěr se dotknu jednotkového white box testingu programů
v jazyce Python a ukážu praktické nasazení na analyzátoru benchmarkových souborů.

\section{Systémové a akceptační testy}

\subsection{Skupiny scénářů}

Systémové a akceptační scénáře jsem rozdělil do několika skupin:

\begin{itemize}

\item
Testování instalace a inicializace databáze - budeme předpokládat existenci prázdné databáze,
která má obsahovat tabulky benchmarků. Nainstalujeme aplikaci, provedeme vytvoření tabulek
a naplnění základními daty.

\item
Testování řádkového analyzátoru - otestujeme, zda je řádkový analyzátor schopen analyzovat
soubory všech podporovaných formátů a zda jsou údaje ve výpisu v pořádku.

\item
Testování plnění databáze skriptem - otestujeme, zda je skript schopen dávkově naplnit data
všech skriptů z daného adresáře do databáze.

\item
Testování webového rozhraní - otestujeme, zda webové rozhraní funguje podle specifikace ve
čtyřech skupinách.

\begin{itemize}

\item Prohlížení databáze

\item Editace databáze

\item Správa uživatelských účtů

\item Přístupová práva
        
\end{itemize}

\item
Testování přístupu - otestujeme, zda výhradně uživatelé s potřebnými právy mohou provádět zadané
akce.
        
\end{itemize}


\subsection{Testování instalace a inicializace}

\subsubsection{Instalace aplikace}

\begin{description}

\item[Předpoklady:]
Aplikace není nainstalována

\item[Akce:]
Nainstalujeme aplikaci podle instalační příručky

\item[Úspěch:]
Aplikaci se povedlo nainstalovat

\item[Neúspěch:]
Aplikaci se nepovedlo nainstalovat
        
\end{description}

\subsubsection{Vygenerování konfiguračních souborů}

\begin{description}

\item[Předpoklady:]
Žádné

\item[Akce:]
Vyplníme metakonfigurační soubor podle instalační příručky a konfigurace hostitelského systému,
vygenerujeme konfigurační soubory pro PHP a Python, zkopírujeme je do příslušných adresářů

\item[Úspěch:]
Soubory se povedlo vygenerovat a zkopírovat

\item[Neúspěch:]
Soubory se nepovedlo vygenerovat nebo zkopírovat
        
\end{description}

\subsubsection{Vytvoření tabulek}

\begin{description}

\item[Předpoklady:]
Aplikace je správně nainstalována a zkonfigurována, hostitelská databáze neobsahuje 
žádnou z tabulek databáze benchmarků

\item[Akce:]
Vytvoříme tabulky podle instalační příručky

\item[Úspěch:]
Tabulky se vytvořily podle zadaného souboru

\item[Neúspěch:]
Tabulky se nevytvořily podle zadaného souboru

\end{description}

\subsubsection{Vložení základních dat}
        
\begin{description}

\item[Předpoklady:]
Aplikace je správně nainstalována a zkonfigurována, tabulky jsou vytvořeny a jsou prázdné.

\item[Akce:]
Naplníme databázi základními daty podle instalační příručky

\item[Úspěch:]
Tabulky obsahují očekávaná data

\item[Neúspěch:]
Tabulky neobsahují očekávaná data

\end{description}


\netisknout{
Opakovaně jsem na svém stroji i na stroji vlsi.felk.cvut.cz zkoušel znovu nainstalovat
aplikaci a inicializovat tabulky a základní data. Instalace i inicializace proběhly úspěšně.
}


\subsection{Testování řádkového analyzátoru}

\subsubsection{Testování správné analýzy všech formátů}
        
\begin{description}

\item[Předpoklady:]
Aplikace je řádně nainstalována

\item[Akce:]
Spustíme řádkový analyzátor, jeho parametry jsou soubory všech podporovaných formátů

\item[Úspěch:]
Řádkový analyzátor správně zanalyzuje všechny soubory

\item[Neúspěch:]
Výpočet skončí běhovou chybou nebo jsou vypsány nesprávné výsledky

\end{description}

\subsubsection{Testování explicitního zadání formátu}
        
\begin{description}

\item[Předpoklady:]
Aplikace je řádně nainstalována

\item[Akce:]
Spustíme řádkový analyzátor, jeho parametry jsou jméno formátu a soubor přejmenovaný tak, aby
z přípony nebylo možné formát určit.

\item[Úspěch:]
Řádkový analyzátor správně zanalyzuje zadaný soubor

\item[Neúspěch:]
Výpočet skončí běhovou chybou nebo jsou vypsány nesprávné výsledky

\end{description}

\netisknout{
K dispozici jsem měl spoustu vzorků podporovaných formátů benchmarků. Postupoval jsem tak,
že jsem vybral reprezentativní, dostatečně rozsáhlé vzorky a zkoumal, zda parser projde
soubory bez běhové chyby a zda jsou výsledky v pořádku (porovnáním s ruční analýzou benchmarku
a případnými informacemi o počtech atributů v komentářové řádce).
}


\subsection{Testování plnění databáze dávkovým skriptem}
\subsubsection{Testování plnění známou sadou}
        
\begin{description}

\item[Předpoklady:]
Aplikace je řádně nainstalována

\item[Akce:]
Spustíme skript pro dávkové plnění, parametrem bude adresář obsahující známou
sadou benchmarků (např. LGSynth91).

\item[Úspěch:]
Všechny benchmarky sady jsou správně naplněny do databáze.

\item[Neúspěch:]
Benchmarky nejsou správně naplněny do databáze.

\end{description}
\netisknout{
Plnění databáze skriptem plně vyzkoušelo schopnosti a nedokonalosti parseru. Měl jsem k dispozici
různé sady benchmarků a na všechny sady jsem rekurzivně aplikoval plnící skript. Zjistil jsem,
že některé benchmarky mají nekorektní syntaxi oproti specifikaci nebo že jsem neuvažoval s možností
použití některých znaků jako identifikátorů. Po úpravách parseru jsem všechna data naplnil úspěšně.
}


\subsection{Testování funkcí webového rozhraní - prohlížení dat}

\subsubsection{Vypsání seznamu benchmarkových sad a jejich vlastností}
        
\begin{description}

\item[Předpoklady:]
Aplikace je řádně nainstalována a nakonfigurována, uživatel má dostatečná
přístupová práva.

\item[Akce:]
Vypíšeme si seznam benchmarkových sad, vypíšeme si obsah sady, vyhledáme
instance benchmarků sady.

\item[Úspěch:]
Vypíší se správná data

\item[Neúspěch:]
Dojde k běhové chybě nebo jsou vypsána nesprávná data.

\end{description}

\subsubsection{Vypsání seznamu formátů a jejich vlastností}
        
\begin{description}

\item[Předpoklady:]
Aplikace je řádně nainstalována a nakonfigurována, uživatel má dostatečná
přístupová práva.

\item[Akce:]
Vypíšeme si seznam formátů, vypíšeme si informace o formátu

\item[Úspěch:]
Vypíší se správná data

\item[Neúspěch:]
Dojde k běhové chybě nebo jsou vypsána nesprávná data.

\end{description}

\subsubsection{Vyhledání instancí benchmarků podle zadaných kritérií, zobrazení dat instance}
        
\begin{description}

\item[Předpoklady:]
Aplikace je řádně nainstalována a nakonfigurována, uživatel má dostatečná
přístupová práva.

\item[Akce:]
Spustíme vyhledávání, zobrazíme data některé z vyhledaných instancí

\item[Úspěch:]
Vyhledají se správné instance, vypíší se správná data

\item[Neúspěch:]
Dojde k běhové chybě nebo jsou vyhledány nesprávné instance nebo vypsána nesprávná data.

\end{description}

\subsubsection{Stažení jednotlivého souboru}
        
\begin{description}

\item[Předpoklady:]
Provedli jsme vyhledávání, jehož výsledkem je benchmark s nám známým obsahem.

\item[Akce:]
Zadáme stažení souboru.

\item[Úspěch:]
Stáhne se soubor se správným obsahem.

\item[Neúspěch:]
Dojde k běhové chybě nebo se stáhne nesprávný soubor.

\end{description}

\subsubsection{Agregované stažení vyhledaných benchmarků}
        
\begin{description}

\item[Předpoklady:]
Provedli jsme vyhledávání, jehož výsledkem je neprázdná množina vyhledaných benchmarků.

\item[Akce:]
Zadáme agregované stažení souborů.

\item[Úspěch:]
Stáhne se ZIP archiv obsahující vyhledané benchmarky

\item[Neúspěch:]
Dojde k běhové chybě, archiv je poškozený, neobsahuje odpovídající soubory.

\end{description}

\



\subsection{Testování funkcí webového rozhraní - vkládání a změna dat}

\subsubsection{Vložení instance benchmarku se zadáním hodnot atributů}
        
\begin{description}

\item[Předpoklady:]
Máme nainstalovanou a nakonfigurovanou databázi benchmarků obsahující minimálně jeden formát a
jednu benchmarkovou sadu, uživatel smí editovat databázi benchmarků

\item[Akce:]
Vložíme novou instanci benchmarků a hodnoty atributů zadáme ručně.

\item[Úspěch:]
Vloží se nová instance s vloženými hodnotami.

\item[Neúspěch:]
Instance se nevloží nebo vložené hodnoty neodpovídají vstupu.

\end{description}

\subsubsection{Vložení instance benchmarku s volbou automatického vyplnění hodnot atributů}
        
\begin{description}

\item[Předpoklady:]
Máme nainstalovanou databázi a nakonfigurovanou benchmarků obsahující minimálně jeden formát a
jednu benchmarkovou sadu, uživatel smí editovat databázi benchmarků

\item[Akce:]
Vložíme novou instanci benchmarků, zadáme benchmarkový soubor a zaškrtneme automatické
vyplnění atributů.

\item[Úspěch:]
Vloží se nová instance se správně vyplněnými hodnotami hodnotami.

\item[Neúspěch:]
Instance se nevloží nebo vložené hodnoty neodpovídají obsahu souboru a zadaným údajům.

\end{description}

\subsubsection{Editace instance}
        
\begin{description}

\item[Předpoklady:]
Máme nainstalovanou databázi a nakonfigurovanou benchmarků obsahující minimálně jednu
vloženou instanci benchmarku.

\item[Akce:]
Zeditujeme hodnoty instance.    

\item[Úspěch:]
Hodnoty se změní podle specifikace.

\item[Neúspěch:]
Hodnoty se nezmění správným způsobem.

\item[Poznámka:]
Podobný test provedeme i se sadou benchmarků a formátem.

\end{description}

\subsection{Testování webového rozhraní - správa uživatelských účtů}

\subsubsection{Vypsání uživatelů}
        
\begin{description}

\item[Předpoklady:]
Máme nainstalovanou a nakonfigurovanou databázi benchmarků a více než jednoho uživatele,
přihlášený uživatel smí pracovat s uživatelskými účty.

\item[Akce:]
Zobrazíme si výpis uživatelů.

\item[Úspěch:]
Zobrazí se správný výpis uživatelů, je možné editovat nebo mazat cizí účty, nikoli vlastní.

\item[Neúspěch:]
Výpis se nezobrazí korektně nebo není možné editovat cizí účet nebo je možné editovat vlastní
účet.

\end{description}

\subsubsection{Přidání uživatele}
        
\begin{description}

\item[Předpoklady:]
Máme nainstalovanou a nakonfigurovanou databázi benchmarků, přihlášený uživatel smí editovat
uživatelské účty.

\item[Akce:]
Přidáme nový uživatelský účet

\item[Úspěch:]
Uživatelský účet je správně vytvořen, lze se přihlásit s daným heslem a přístupová práva jsou
správně nastavena.

\item[Neúspěch:]
Účet se nevytvoří, nelze se přihlásit nebo přístupová práva nejsou v pořádku.

\end{description}

\subsubsection{Smazání uživatelského účtu}
        
\begin{description}

\item[Předpoklady:]
Máme nainstalovanou a nakonfigurovanou databázi benchmarků a minimálně jednoho dalšího uživatele,
přihlášený uživatel smí editovat uživatelské účty.

\item[Akce:]
Smažeme uživatelský účet.

\item[Úspěch:]
Uživatel zmizí z databáze, nemůže se přihlásit a již přihlášený uživatel nemůže dále pracovat
s vyššími právy, než má nepřihlášený uživatel.

\item[Neúspěch:]
Uživatelský účet nadále existuje, uživatel může dále pracovat s vyššími právy.

\item[Poznámka:]
Podobně postupujeme u testu editace uživatelského účtu.

\end{description}

\subsection{Testování webového rozhraní - přístupová práva}

\subsubsection{Testování volného přístupu při povolení volného přístupu}
        
\begin{description}

\item[Předpoklady:]
Máme nainstalovanou a nakonfigurovanou databázi benchmarků. Je povoleno nepřihlášeným
uživatelům prohlížet databázi.

\item[Akce:]
Vstoupíme do webového rozhraní jako nepřihlášený uživatel

\item[Úspěch:]
Lze prohlížet databázi benchmarků    

\item[Neúspěch:]
Nelze prohlížet databázi benchmarků

\item[Poznámka:]
Provedeme opačný test - nepřihlášený uživatel nesmí mít přístup, pokud je volný přístup
zakázán.

\end{description}

\subsubsection{Testování změny vlastního hesla}
        
\begin{description}

\item[Předpoklady:]
Uživatel je přihlášen a smí měnit vlastní heslo.    

\item[Akce:]
Změníme vlastní heslo uživatele

\item[Úspěch:]
Heslo se změnilo

\item[Neúspěch:]
Heslo se nezměnilo

\item[Poznámka:]
Provedeme opačný test - uživatel se zákazem změny hesla nesmí mít možnost měnit heslo

\end{description}

\subsubsection{Testování přístupových práv pro prohlížení}
        
\begin{description}

\item[Předpoklady:]
Uživatel je přihlášen a volný přístup do databáze je zakázán.

\item[Akce:]
Uživatel zkusí základní akce - například si zobrazí seznam sad.

\item[Úspěch:]
Prohlížení je možné

\item[Neúspěch:]
Prohlížení není možné

\end{description}

\subsubsection{Testování změny uživatelských účtů bez příslušného oprávnění}
        
\begin{description}

\item[Předpoklady:]
Uživatel je přihlášen a nesmí editovat uživatelské účty.

\item[Akce:]
Pokusíme se vypsat uživatelské účty.

\item[Úspěch:]
Výpis není k dispozici.

\item[Neúspěch:]
Výpis je k dispozici

\item[Poznámka:]
Provedeme tento test i pro nepřihlášeného uživatele při volně dostupném
prohlížení databáze. Oba testy provedeme analogicky i pro akci, která edituje databázi
benchmarků - například editaci sady benchmarků.

\end{description}


\section{Jednotkové testování}

Jednotkové (modulární) testování je testování jednotlivých funkcí, metod, v objektovém
programování nejčastěji tříd použitých v programu. Účelem je otestování, zda chování testované 
jednotky odpovídá zadané specifikaci.


\subsection{Jednotkové testování v jazyce Python}

V jazyce Python existuje několik frameworků používaných pro jednotkové testování. Mezi ně patří:

\begin{itemize}

\item
Modul \textit{unittest}, dříve zvaný PyUnit, je založen na principech frameworku JUnit používaného pro
jazyk Java.

\item
Modul \textit{testoob}, který rozšiřuje možnosti modulu \textit{unittest} o podporu barevného výstupu,
výběr testů pomocí regulárních výrazů, výstup do XML a HTML.

\item
Modul doctest, založený na příkladech použití uložených v dokumentačních řetězcích funkcí, metod,
tříd a modulů.
        
\end{itemize}


\subsubsection{Testování s pomocí modulu doctest}

Modul doctest umožňuje vkládat testovací příklady do dokumentačních řetězců. Tyto
příklady jsou v lidsky čitelné formě a simulují práci v interaktivním interpreteru Pythonu.
Následující dokumentační řetězec dokumentuje správnou funkci standardních funkcí min() a max():

\begin{verbatim}
"""Example of correct output of function min():
>>>min(3, 8, 1, 5)
1

Example of correct output of function max():
>>>max(3, 8, 1, 5)
8
"""
\end{verbatim}

K provedení testu ovšem musíme zajistit import modulu doctest a spustit v něm definovanou
funkci testmod(). To zajistíme například přidáním následujícího kódu na konec testovaného
modulu:

\begin{verbatim}
import doctest
doctest.testmod()
\end{verbatim}

Pak už stačí jenom na příkazové řádce zadat příkaz (za předpokladu, že je testovaný modul
v souboru mymodule.py:

\begin{verbatim}
python mymodule.py
\end{verbatim}

\subsection{Příklad nasazení v řádkovém analyzátoru benchmarků}

Příkladem nasazení jednotkového testování v řádkovém analyzátorů
je kontrola správnosti detekce typu hradla v PLA tabulce formátu BLIF. Třída
BenchmarkFileParser\_BLIF definuje metodu getGateType(), která na základě
seznamu symbolů korespondujících s PLA tabulkou detekuje typ hradla. Tato
detekce je poměrně složitá a náchylná k chybám. Vhodnou aplikací testování
pomocí modulu doctest() je možno otestovat správnou funkčnost této detekční metody.
Nejprve se vytvoří testovací kód v dokumentačním řetězci metody:

\begin{verbatim}
>>> parser = BenchmarkFileParser_BLIF()
>>> parser.getGateType([[(0, '1',), (0, '1',),],]) == 
... BenchmarkFileParser_BLIF.GT_BUF
True
>>> parser.getGateType([[(0, '1',), (0, '0',),],]) == 
... BenchmarkFileParser_BLIF.GT_NOT
True
>>> parser.getGateType([
... [(0, '00',), (0, '0',),],
... [(0, '01',), (0, '0',),],
... [(0, '10',), (0, '0',),],
... [(0, '11',), (0, '1',),],
... ]) == BenchmarkFileParser_BLIF.GT_AND
True
>>> parser.getGateType([
... [(0, '00',), (0, '0',),],
... [(0, '01',), (0, '1',),],
... [(0, '10',), (0, '1',),],
... [(0, '11',), (0, '1',),],
... ]) == BenchmarkFileParser_BLIF.GT_OR
True
>>> parser.getGateType([
... [(0, '00',), (0, '1',),],
... [(0, '01',), (0, '0',),],
... [(0, '10',), (0, '0',),],
... [(0, '11',), (0, '0',),],
... ]) == BenchmarkFileParser_BLIF.GT_NOR
True
>>> parser.getGateType([
... [(0, '00',), (0, '1',),],
... [(0, '01',), (0, '1',),],
... [(0, '10',), (0, '1',),],
... [(0, '11',), (0, '0',),],
... ]) == BenchmarkFileParser_BLIF.GT_NAND
True
\end{verbatim}

{\odstavec
Tento kód napřed vytvoří instanci třídy BenchmarkFileParser\_BLIF(), na níž opakovaným
voláním testujeme správnost výstupů funkce pro určité PLA tabulky.

Spuštění testů zajistí následující kód na konci modulu:
}

\begin{verbatim}
def _test():
    # DOC {{{
    """Runs doctest on the module.
    """
    # }}}

    # CODE {{{
    import doctest
    doctest.testmod()
    # }}}


# when the script is the main script, run the test {{{
if __name__ == "__main__":
    _test()
# }}}
\end{verbatim}


Při úspěšném složení testu nevypíše příkaz \textit{./python BenchmarkFileParser\_BLIF.py} nic, pokud
však modifikujeme např. detekční kód tak, že vynecháme detekci hradla NAND a metoda tedy vrací
kód pro neznámý typ hradla, vypíše se následující chybová zpráva:


\begin{verbatim}
**********************************************************************
File "BenchmarkFileParser_BLIF.py", line 232, 
in __main__.BenchmarkFileParser_BLIF.getGateType
Failed example:
    parser.getGateType([
    [(0, '00',), (0, '1',),],
    [(0, '01',), (0, '1',),],
    [(0, '10',), (0, '1',),],
    [(0, '11',), (0, '0',),],
    ]) == BenchmarkFileParser_BLIF.GT_NAND
Expected:
    True
Got:
    False
**********************************************************************
1 items had failures:
   1 of   7 in __main__.BenchmarkFileParser_BLIF.getGateType
***Test Failed*** 1 failures.
\end{verbatim}

\section{Kontrola korektnosti kódu}

{\odstavec

Korektnost nebo nekorektnost napsaného kódu se nejlépe prokáže při běhu aplikace. Ale jelikož
není obvykle možné zkoumat korektnost funkce aplikace za všech možných podmínek, je vhodné
zkontrolovat korektnost kódu před spuštěním. Programátor se může dopustit syntaktických chyb,
chyb sémantiky a logických chyb. Syntaktické a sémantické chyby jsou obyčejně odhaleny kompilátorem
nebo vstupem modulu do interpreteru v případě interpretovaných jazyků. Logické chyby se odhalují
hůře, ale v jednotlivých programovacích jazycích existují konstrukce, které lze s největší
pravděpodobností považovat za chybné - ať už jde o překlep nebo chybu návrhu. Ke kontrole kódu
v Pythonu jsem využil program \textit{Pylint}, který je jakousi obdobou programu \textit{lint}
známého z unixového prostředí, kde se dlouhá léta používal pro kontrolu korektnosti zdrojových
kódů v jazyce C.

}

\section{Závěrečné poznámky}

Funkčnost aplikace jsem testoval podle vytvořených scénářů, objevené problémy jsem opravil.
Testoval jsem programy i z hlediska rychlosti a shledal jsem tuto rychlost dostatečnou a
odpovídající. V případě webového rozhraní databáze šlo zejména o (z hlediska uživatele)
v podstatě okamžitou odezvu, řádkový analyzátor zpracovává soubory úměrně jejich složitosti.

% vim:enc=iso8859-2:fenc=iso8859-2:syntax=tex:
