STŘEDNÍ ŠKOLA PRŮMYSLOVÁ, TECHNICKÁ A AUTOMOBILNÍ JIHLAVA MATURITNÍ PRÁCE JIHLAVA 2016 MICHAL TICHÝ
STŘEDNÍ ŠKOLA PRŮMYSLOVÁ, TECHNICKÁ A AUTOMOBILNÍ JIHLAVA Automatická kontrola Java, C - aplikací Maturitní práce Jihlava 2015 Michal Tichý Vedoucí maturitní práce: Ing. Karel Johanovský
Obsah Zadání dlouhodobé maturitní práce... 1 Poděkování... 1 Prohlášení... 1 Představení aplikace agrader... 1 Výhody oproti konkurenčním řešením... 1 Systémové požadavky... 1 Zvolené technologie... 2 C#... 2 Windows Forms... 2 Výkon aplikace... 3 Princip fungování aplikace agrader... 4 Testovací protokol... 4 Provádění a vyhodnocování testů... 5 implementace některých částí aplikace... 6 Vytvoření WinFormu s testovým úkonem... 6 Vytvoření testovacího protokolu a spouštění testů... 8 Uživatelský manuál... 12 Návod pro vytvoření testovacího protokolu... 12 Návod pro spuštění testů... 13 UI... 14 jednotlivých akcí, ze kterých může být složen testovací protokol... 16 Vstupy... 16 Výstupy... 19 Nastavení... 22 Akce... 23 Závěr... 25 Seznam použitých pramenů... 26
Zadání dlouhodobé maturitní práce Žák zpracuje ve zvoleném programovacím jazyce aplikaci umožňující automatizovanou kontrolu jednoduchých C a JAVA aplikací. Aplikace bude sloužit pro kontrolu naprogramovaných prací studentů převážně 2. a 3. ročníku oboru IT. Aplikace bude obsahovat přehledné GUI, ve kterém si nastavím sérii testů, kterými musí studentské práce projít, a následně tyto testy spustím. GUI by mělo přehledně vypsat, které testy prošly a které selhaly. Aplikace by měla rovněž obsahovat jednoduchou kontrolu opsaných prací. Součástí práce bude kompletní programátorská dokumentace aplikace a uživatelský manuál.
Poděkování Děkuji za vedení DMP a cenné rady panu učiteli Ing. Karlu Johanovskému. Prohlášení Maturitní práci jsem vypracoval samostatně s použitím uvedené literatury a na základě konzultací s vedoucím maturitní práce. Datum: Podpis:
Představení aplikace agrader Aplikace agrader byla vyvinuta v rámci dlouhodobé maturitní práce na Střední škole průmyslové, technické a automobilní Jihlava. Tato aplikace slouží k automatickému opravování zdrojových kódu napsaných v jazyce C nebo Java. Aplikace umožňuje: tvorbu zadání (testovacího protokolu) v přehledném GUI export/import testovacích protokolů paralelní testování více zdrojových kódů zobrazovat výsledky testů v tabulce a grafu export výsledků Výhody oproti konkurenčním řešením Zdarma OpenSource (BSD simplified licence) Není třeba psát žádný kód pro tvorbu testovacího protokolu GUI je kompletně v českém jazyce nebo angličtině (závisí na aktuálním CultureInfo) Testy probíhají paralelně Změření náročnosti jednotlivých testovaných kódů Systémové požadavky Podporované operační systémy: Windows Vista SP2 (x86 a x64) Windows 7 SP1 (x86 a x64) Windows Server 2008 R2 SP1 (x64) Požadavky na software: Microsoft.NET Framework 4.5 Požadavky na hardware: Procesor s frekvencí 1 GHz nebo vyšší 512 MB paměti RAM 1
Zvolené technologie C# Vysokoúrovňový objektově orientovaný jazyk vyvinutý firmou Microsoft. Tento jazyk lze použít například ke tvorbě webových aplikací (ASP.NET), formulářových aplikací (WinForms), konzolových aplikací nebo aplikací pro mobilní zařízení. Tento jazyk jsem si vybral, protože s ním mám nejvíce zkušeností a díky vysoké abstrakci umožňuje rychlou tvorbu. Bohužel nevýhodou zvoleného jazyka je závislost na.net frameworku (kvůli umožnění běhu na co největším počtu PC byla zvolena starší verze 4.5) a tudíž do vydání použitelné verze.net Core i nemožnost jednoduše spustit na jiném OS než MS Windows. Windows Forms Grafická knihovna pro tvorbu okenních aplikací pro MS Windows. Uživatelské rozhraní napsané ve WinForms využívá postupy event-driven development. To znamená, že veškeré metody spouštěné z UI jsou vyvolány spuštěním události. 2
Výkon aplikace Díky využití pokročilých technik pro paralelní zpracování testů se podařilo dosáhnout velmi rychlého provedení testů. Všechny měření byly provedeny na stroji s následujícími parametry: Procesor i5-4200h 2.8GHz RAM: 8GB DISK: SSD Od stisknutí tlačítka po skutečné zahájení testu (spuštění kompilace) uplyne cca 22ms, pokud se testuje jen jeden zdrojový kód. Při testování více zdrojových kódů se každý další test spustí jen za 3ms. Test 120 zdrojových kódů proběhne za 2.9 sekundy a je během něj vytvořeno 11 vláken. Pro měření výkonu byl použitý zdrojový kód, který spočítal a vypsal obsah a obvod čtverce z uživatelem zadaných dat. Jako vstupní data byla použita vygenerovaná desetinná čísla. 3
Princip fungování aplikace agrader Aplikace se skládá ze dvou částí. Tvorby testovacího protokolu (popisu toho, co bude testovanému programu předáno jako vstupy, jaké výstupy budou očekávány a také různé akce a nastavení samotného testu). Kompilace a spuštění zdrojových kódů a vyhodnocení výstupů testu. Testovací protokol Testovací protokol se skládá z kombinace akcí: Vstupy o Textový soubor o Číslo o Náhodné číslo o Text Výstupy o Číslo o Text o Číslo založené na vygenerovaném (náhodném) číslu o Číslo splňující podmínky o Počet čísel splňujících podmínky Akce o Opakování poslední akce o Načtení čísel z textového souboru o Porovnání souborů o Spuštění externí aplikace Nastavení o Maximální odchylka o Maximální doba pro běh testu (timeout) o Zakázané příkazy o Vyžadované příkazy o Spouštěcí argumenty Poskládáním těchto akcí uživatel vytvoří testovací protokol. 4
Provádění a vyhodnocování testů Testy probíhají ve čtyřech krocích: 1. Vytvoření Testovacího protokolu 2. Vytvoření samostatného tasku pro každý zdrojový kód 3. Spuštění testů 4. Zobrazování výsledků již proběhlých testů Výsledky každého testu jsou zobrazeny ihned po jeho dokončení. Průběh samotného testu 5
implementace některých částí aplikace Vytvoření WinFormu s testovým úkonem Při detekování změny vybrané položky v comboboxu, který obsahuje seznam úkonů, se spustí metoda, která se stará o vytvoření zvoleného WinFormu. Z comboboxu je získán index aktuálně zvolené hodnoty, který je následně převeden na záznam z Enumu. Díky tomuto přístupu vznikne problém, že je třeba ze stringového názvu formu vytvořit datový typ, který dědí z WinForm. Tuto nevýhodu vyvažuje to, že není nutné, aby existoval switch, který obsahuje seznam všech úkonů (díky čemuž není nutné přidávat záznam na několika místech při přidání nového úkonu). public static void Show(SideForms formname) { Close(); _sideform = Activator.CreateInstance(Type.GetType("aGrader.IOForms." + formname)); UpdatePosition(); _sideform.show(); } Možné úskalí tohoto přístupu je, že při změně namespace, ve kterém jsou uloženy WinFormy, je nutné aktualizovat i namespace na tomto místě (toto naštěstí lze eliminovat refaktorováním přes vyspělé nástroje, které projdou a změní i stringové hodnoty). VÝSTUP: číslo splňující podmínky a VÝSTUP: počet čísel splňujících podmínky V těchto dvou úkonech může uživatel zkontrolovat, jestli výstup/y splňují podmínky. Uživatel může tvořit i složité podmínky typu: (X%7=0 OR X%9) AND X%2=0. Vyhodnocování takto složitých booleanovský výrazů by bylo příliš složité (a zbytečné) naprogramovat, a proto se pro tento úkon používá knihovna Flee. 6
VÝSTUP: počet čísel splňujících podmínky V tomto úkonu se zjišťuje, kolik z posledních čísel splňuje zadané podmínky. K zjištění maximálního počtu možných čísel je nutné vyfiltrovat jen testové úkony, které jsou číselným vstupem/výstupem. Je nutné také zahrnout vstupy/výstupy, které jsou vytvořené pomocí úkonu AKCE: opakuj poslední. Také je důležité ověřit, že zadaný počet čísel je možný i po odebrání některého z úkonů anebo změny jejich pořadí. public bool IsRequestedCountOfNumbersValid() { var formid = InputsOutputs.GetIdOfForm(this); var countofprecedingnumbres = radioinputs.checked? InputsOutputs.GetInputsList(FormType.Number, formid).count() : InputsOutputs.GetOutputsList(FormType.Number, formid).count(); var numberofvalidnumbersaddedbyrepeaters = GetCountOfValidRepetitions(); if (numcountofnumbers.value <= countofprecedingnumbres + numberofvalidnumbersaddedbyrepeaters) { numcountofnumbers.backcolor = DefaultBackColor; return true; } numcountofnumbers.backcolor = Color.Red; MessageBox.Show(Resources.OutputCountOfNumbersMatchingConditionsRequestedNumberOfNumbersIsNotPossible); return false; } private int GetCountOfValidRepetitions() { var validrepeaters = InputsOutputs.GetList(typeof(ActionRepeatLast), InputsOutputs.GetIdOfForm(this)).Where( t => t.getrepeatedform().gettype().tostring().startswith(radioinputs.checked? "agrader.ioforms.input" : "agrader.ioforms.output") && t.getrepeatedform().gettype().tostring().contains("number")); } int i = 0; foreach (dynamic validrepeater in validrepeaters) { i += validrepeater.repetitions; } return i; 7
Vytvoření testovacího protokolu a spouštění testů public static async void TestAllSourceCodes() { _protocol = new TestProtocol(); } var testtasks= SourceCodes.GetSourceCodeFiles().Select(CreateTestTask).ToList(); testtasks.foreach(t=>t.start()); while (testtasks.count>0) { Task<TestResult> firstfinishedtask = await Task.WhenAny(testTasks); testtasks.remove(firstfinishedtask); TestResult result = await firstfinishedtask; OnResultReady(result); } private static Task<TestResult> CreateTestTask(SourceCode sourcecode) { if (sourcecode is SourceCodeC) return new Task<TestResult>(new TestC(sourceCode, _protocol).runtest); if (sourcecode is SourceCodeJava) return new Task<TestResult>(new TestJava(sourceCode, _protocol).runtest); } MessageBox.Show(Resources.TestManager_UnsuportedTypeOfSourceCode); throw new ApplicationException($"Unsupoorted type of SourceCode! {sourcecode.gettype()}"); Testovací protokol je vytvořen zpracováním testovacích akcí. Pro každý testovaný zdrojový kód se vytvoří samostatný task, toto umožňuje spouštět testy paralelně. Pro každý test se nevytváří samostatné vlákno, ale je vytvořeno několik vláken pro všechny testy a ty jsou opakovaně používány. Počet vláken si určuje systém podle dostupných prostředků a mohou se měnit za běhu. 8
Spouštění zdrojových kódů Aplikace agrader provádí kompilaci a spouštění zdrojových kódů přes externí nástroje (kompilátory, VM,...). Způsob, kterým se vytvoří proces, který zkompiluje a spustí zdrojový kód, záleží na programovacím jazyce zdrojového kódu. C Například pro práci se zdrojovými soubory v jazyce C se používá pro kompilaci i spouštění kompilátor TCC. Pokud kompilátor není nalezen, je uživatel dotázán, zda si přeje kompilátor stáhnout z internetu. Kompilace a spuštění zdrojového kódu se provádí spuštěním TCC s následujícím nastavením: var app = new Process { StartInfo = { FileName = pathtotcc, UseShellExecute = false, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, Arguments = $"-run \"{code.path}\"" } }; if (Protocol?.StartupArguments!= null) app.startinfo.arguments +=$" {Protocol.StartupArguments}"; 9
Java Kompilace a spouštění zdrojových kódů v jazyce Java je náročnější, protože nelze zkompilovat a spustit zdrojový kód bez uložení zkompilovaného souboru na disk. Vzhledem k tomu je nutné dočasně uložit zkompilovaný kód. Umístění složky JDK, která obsahuje kompilační nástroj, se zjišťuje kontrolou následujících umístění: 1. Systémová proměnná JAVA_HOME 2. Klíč v registrech SOFTWARE\JavaSoft\Java Development Kit 3. Uživatelem zvolená cesta Ve chvíli, kdy je JDK nalezeno, už se znovu nehledá při spuštění dalších testů. 10
Kompilace zdrojového souboru 1. Získání cesty k souboru javac.exe (java kompilátor) 2. Vytvoření dočasné složky 3. Sestaveni spouštěcích argumentů a. Cílová složka b. sourcepath c. Umístění zdrojového souboru 4. Spuštění kompilace private Process CreateCompilatonProcess() { var pathjavac = $@"{GetPathToJava()}\bin\javac.exe"; CreateDestinationFolder(); if (!File.Exists(pathJavac)) { MessageBox.Show(text: String.Format(Resources.Test_CompilatorNotFound, pathjavac)); ExceptionsLog.LogException($"Javac not found! {pathjavac}"); } var app = new Process { StartInfo = { FileName = pathjavac, UseShellExecute = false, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, Arguments = BuilCompilationdArguments() } }; return app; } private string BuilCompilationdArguments() { var arguments = new StringBuilder(); arguments.append($"-d \"{_destinationfolder}\" "); arguments.append($"-sourcepath \"{Path.GetDirectoryName(SourceCode.Path)}\" "); arguments.append($"\"{sourcecode.path}\""); if (((SourceCodeJava) SourceCode).Dependencies == null) return arguments.tostring(); } foreach (var dependency in ((SourceCodeJava) SourceCode).Dependencies) arguments.append($" \"{dependency}\""); return arguments.tostring(); 11
Uživatelský manuál Návod pro vytvoření testovacího protokolu Nejdříve se musíme přepnout na kartu Protokol. (DOLE). Poté už můžeme začít skládat protokol z jednotlivých akcí. ( jednotlivých akcí, ze kterých může být složen testovací protokol) Přidání akce: VÝBĚR AKCE 1. Vybereme akci 2. Zadáme potřebné informace 3. Uložíme akci Úprava akce 1. Zvolíme akci, kterou chceme upravit ze seznamu akcí 2. Upravíme akci 3. Uložíme akci Po dokončení tvorby testovacího protokolu můžeme: Spustit samotné testy (Návod pro spuštění testů) Exportovat testovací protokol SEZNAM AKCÍ 12
Návod pro spuštění testů Na kartě průzkumník pomocí tlačítka Procházet (DOLE) vybereme typ zdrojových kódů a jejich umístění. Všechny nalezené zdrojové kódy se zobrazí v seznamu v dolní části okna. (DOLE) Po zvolení zdrojového kódu ze seznamu se v levé části UI otevře náhled zdrojového kódu (DOLE) (tlačítkem otevřít soubor (DOLE) lze otevřít zdrojový kód v externím editoru). Pokud proběhly testy, v levé spodní části UI se zobrazí vstupy a výstupy. (DOLE) Samotné testy zahájíme kliknutím na tlačítko Spustit test. (DOLE) 13
UI OBR 1 1. Spuštění testů 2. Zobrazení průběhu testování 3. Otevření aktuálně vybraného zdrojového kódu v externím editoru 4. Pole pro zobrazení zdrojového kódu/logu posledního testu 5. Zobrazení vstupů a výstupů testu/zobrazení výsledku testů pro jednotlivé zdrojové kódy 6. Přepínač mezi výběrem zdrojových kódů a tvorbou testovacího protokolu 7. Aktualizování zdrojových souborů 8. Zvolení složky se zdrojovými soubory 9. Seznam zdrojových souborů OBR 2 1. Výběr akce 2. Změna pozice vybrané akce 3. Seznam akcí 4. Import/Export testovacího protokolu 14
OBR 3 1. Uloží log testu 2. Zobrazí graf z výsledků testů OBR 4 1. Graf 2. Zvolení zobrazených hodnot 3. Zvolení způsobu řazení 4. Uložení grafu 15
jednotlivých akcí, ze kterých může být složen testovací protokol Vstupy VSTUP: textový soubor Řádky souboru budou použity jako vstupní data pro testovaný program (každý řádek bude odeslán samostatně). V případě vyobrazeném na ukázkovém obrázku to budou: řádky ze souboru C:\TEST.txt VSTUP: číslo Zadané číslo bude použito jako vstup pro testovaný program. Povolený rozsah je -2 000 000 000 až 2 000 000 000. V případě vyobrazeném na ukázkovém obrázku to bude číslo 0,958. 16
VSTUP: náhodné číslo Vygeneruje náhodné číslo v zadaném rozsahu, které bude použito jako vstup pro testovaný program. CheckBox jen celá čísla určuje, jestli vygenerované číslo bude desetinné. Povolený rozsah je -2 000 000 000 až 2 000 000 000. Pokud se generuje desetinné číslo, je generováno se třemi desetinnými místy. V případě vyobrazeném na ukázkovém obrázku to bude: náhodné desetinné číslo v rozsahu 0 až 100. VSTUP: text Zadaný text bude použitý jako vstup pro testovaný program. V případě vyobrazeném na ukázkovém obrázku to bude text ABC. 17
VSTUP: číslo založené na vygenerovaném číslu Od testovaného programu bude očekáváno na výstupu číslo, jehož hodnota bude závislá na vstupních náhodných vygenerovaných číslech. Jednotlivá vygenerovaná čísla jsou označena zkratkou ve tvaru X + ID. V případě vyobrazeném na ukázkovém obrázku to bude: součet druhého vygenerovaného čísla (desetinné číslo v rozsahu 0 až 1) a čtyřnásobku prvního vygenerovaného čísla (celé číslo v rozsahu 39 až 100). 18
Výstupy VÝSTUP: číslo splňující podmínky Od testovaného programu bude očekáváno na výstupu číslo, které bude splňovat zadané podmínky. Nastavená odchylka pro porovnávání čísel není brána v potaz. V případě vyobrazeném na ukázkovém obrázku to bude: číslo beze zbytku dělitelné 5 a zároveň číslo dělitelné beze zbytku 2 nebo 3. Podmínky uvedené v příkladu by se daly zapsat i jako jedna podmínka a to následovně: X%5=0 AND (X%2=0 OR X%3=0) 19
VÝSTUP: počet čísel splňujících podmínky Od testovaného programu bude očekáváno na výstupu číslo, které bude reprezentovat počet čísel, která splnila podmínky. V případě vyobrazeném na ukázkovém obrázku to bude: počet čísel z posledních dvou výstupů testovaného programu, které jsou liché (X%2=1). VÝSTUP: číslo Od testovaného programu bude očekáváno na výstupu zadané číslo. V případě vyobrazeném na ukázkovém obrázku to bude číslo 5. 20
VÝSTUP: text Od testovaného programu bude očekáván na výstupu zadaný text. V případě vyobrazeném na ukázkovém obrázku to bude ABC. 21
Nastavení NASTAVENÍ: odchylka Všechny číselné výstupy budou uznány, pokud se od očekávaných výstupů neliší o více než nastavenou odchylku (standartně nastavena na 0.001). V případě vyobrazeném na ukázkovém obrázku bude odchylka nastavena na 0.1. NASTAVENÍ: zakázaný příkaz Zdrojový kód nebude uznán jako platný, pokud obsahuje zadaný text. V případě vyobrazeném na ukázkovém obrázku nesmí obsahovat příkaz while. NASTAVENÍ: vyžadovaný příkaz Zdrojový kód nebude uznán jako platný, pokud neobsahuje zadaný text. Vyžadovaný text nebude uznán, pokud je obsažen jen v komentáři. V případě vyobrazeném na ukázkovém obrázku musí obsahovat příkaz while. 22
Akce AKCE: spouštěcí parametry Zdrojový kód bude spuštěn se zadanými spouštěcími parametry. V případě vyobrazeném na ukázkovém obrázku bude programu předáno číslo 125 jako parametr. AKCE: opakování poslední akce Poslední akce se X krát zopakuje. AKCE: opakování poslední akce nelze použít jako první akci. Některé akce nelze opakovat (např. nastavení odchylky). V případě vyobrazeném na ukázkovém obrázku se 10x zopakuje akce VSTUP: náhodné číslo od 0 do 100. AKCE: načti výstupy ze souboru Výstupy budou načteny ze souboru. Soubor musí mít shodný název jako zdrojový kód s příponou txt a musí být umístěn ve stejné složce. 23
AKCE: porovnej soubory Porovná soubor vygenerovaný testovaným programem a kontrolní soubor. Soubor musí mít shodný název jako zdrojový kód s příponou txt a musí být umístěn ve stejné složce. Je možné soubory porovnávat po řádcích nebo jen jejich HASH. Porovnávat jen HASH je výhodnější u velmi velkých souborů. V případě vyobrazeném na ukázkovém obrázku bude porovnán vygenerovaný soubor se souborem TEST.txt a porovnávání proběhne po jednotlivých řádcích. AKCE: spustit externí aplikaci Spustí externí aplikaci se zadanými parametry. Parametry mohou obsahovat zástupný text, který bude nahrazen daty. Po proběhnutí testu spustí aplikaci ext.exe a jako spouštěcí parametry jí předá jméno testovaného kódu a počet špatných výstupů. 24
Závěr Aplikace agrader úspěšně splnila všechna kritéria zadání dlouhodobé maturitní práce. Dle mého názoru má aplikace potenciál na praktické nasazení do procesu kontroly a vyhodnocení prací žáků. Trochu čísel na závěr Vývoj projektu trval zhruba 14 měsíců. Aplikace je tvořena 31 tisíci řádky kódu. 25
Seznam použitých pramenů StackOverflow [online]. Stack Exchange Inc [cit. 2016-03-26]. Dostupné z: http://stackoverflow.com/ MSDN [online]. Microsoft [cit. 2016-03-26]. Dostupné z: https://msdn.microsoft.com/enus/library/kx37x362.aspx 26