Paralelní testy­­­­­­­­­­­­­­­­­­­­­­

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
vrana
Člen | 131
+
0
-

Do testů jsem přidělal podporu pro jejich paralelní spouštění. Funguje to na Windows a mělo by to fungovat i jinde. Zkrátilo to čas spuštění všech testů ze dvou minut na deset sekund. Je to v branchi parallel-tests.

Některé testy ještě padají, protože nejspíš nepočítají s tím, že spolu s nimi poběží ještě něco dalšího. Napočítal jsem jich asi třináct. Pokud by se je někomu chtělo projít a opravit, tak by to bylo fajn.

hrach
Člen | 1834
+
0
-

Predpokladan ze bude padat nette/database… zadne vhodne reseni me nenapada

vrana
Člen | 131
+
0
-

Databáze by měla jít snadno vyřešit pomocí transakcí. Trochu jsem to vylepšil a už neprošlo jen 7 testů.

Je možné, že se při tom podaří odhalit i nějaké chyby ve frameworku. Nepochopil jsem třeba, jak by mělo fungovat tohle – vždyť pokud ten adresář někdo vytvoří mezi is_dir() a mkdir(), tak to hodí warning – to asi není žádoucí, ne?

Mimochodem jak je na tom s podporou paralelního spouštění PHPUnit, na který chtěl někdo všechny testy předělat?

norbe
Backer | 405
+
0
-

Dle tohohle článku by to mělo jít od verze 4…

Nox
Člen | 378
+
0
-
  1. Pokud vím, tak Nette nemá PHPUnit testy ale vlastní, takže to není relevantní
  2. Když Jakub píše, že to jede, k čemu je příspěvek, že to nemá jet?
Milo
Nette Core | 1283
+
0
-

Tak jsem si to prubnul na Linuxu (Debian stable). Zatím nebudu posílat pully, jen sem napíšu postřehy.

  • V dodaném php.ini je v podstatě potřeba vypnout všechny extension, neboť jsou zakompilovány do php binárky. Jinak vyskočí spousta warningů.
  • PHP Notice: Undefined offset: 1 in /var/www/dev/nette/tests/Test/TestCase.php on line 188 při spuštění NEparalelně protože $this->output je prázdný. Dočasně jsem si fixnul jako if ($this->phpType === 'CGI' && strlen($this->output)), ani nevím jestli správně.
  • proc_close() vrací vždy –1, protože proc_get_status() v metodě TestCase::isReady() návratový kód jednou přečte a pak už je vždy minus jedna. Rychlým fixem se chyba napraví, ale zase chybují všechny testy, když je spustím sekvenčně.
private $retCode;

public function isReady() {
	$this->output .= stream_get_contents($this->stdout);
	$status = proc_get_status($this->proc);
	$this->retCode = $status['exitcode'];
	return !$status['running'];
}

Pak už se testy paralelně pěkně rozběhnou.

Test 'php-cgi' '/var/www/dev/nette/tests/Nette/Latte/macros.general.html.phpt' mi stále vrací –1, nevím proč. Tak jsem si Exception nahradil za TestCaseException aby testy běžely dál.

Výsledek je FAILURES! (475 tests, 26 failures, 26 skipped). Spousta jich je typu „Unable to write to directory“

Když je spustím sekvenčně:
FAILURES! (475 tests, 13 failures, 26 skipped)

Až se k tomu zase dostanu, zkusím ty testy víc odladit.

-> Nette\Templating\Helpers::date()
   file: /var/www/dev/nette/tests/Nette/Templating/Helpers.date().phpt


-> Nette\Diagnostics\Debugger exception in production mode.
   file: /var/www/dev/nette/tests/Nette/Diagnostics/Debugger.exception.production.phpt
   Expected HTTP code 500 is not same as actual code 200

-> Nette\Diagnostics\Debugger E_ERROR in production mode.
   file: /var/www/dev/nette/tests/Nette/Diagnostics/Debugger.E_ERROR.production.phpt
   Expected HTTP code 500 is not same as actual code 200

-> Nette\Diagnostics\Debugger eval error in HTML.
   file: /var/www/dev/nette/tests/Nette/Diagnostics/Debugger.error-in-eval.phpt
   Expected HTTP code 500 is not same as actual code 200

-> Nette\Diagnostics\Debugger exception in HTML.
   file: /var/www/dev/nette/tests/Nette/Diagnostics/Debugger.exception.html.phpt
   Fatal error

-> Nette\Diagnostics\Debugger notices and warnings with $strictMode in HTML.
   file: /var/www/dev/nette/tests/Nette/Diagnostics/Debugger.strict.html.phpt
   Fatal error

-> Nette\Latte\Macros\UIMacros and renderSnippets.
   file: /var/www/dev/nette/tests/Nette/Latte/UIMacros.renderSnippets.phpt
   Fatal error

-> Nette\Caching\Storages\MemcachedStorage tags dependency test.
   file: /var/www/dev/nette/tests/Nette/Caching/Memcached.tags.phpt
   Fatal error

-> Nette\Caching\Storages\MemcachedStorage files dependency test.
   file: /var/www/dev/nette/tests/Nette/Caching/Memcached.files.phpt
   Fatal error

-> Nette\Caching\Storages\MemcachedStorage expiration test.
   file: /var/www/dev/nette/tests/Nette/Caching/Memcached.expiration.phpt
   Fatal error

-> Nette\Caching\Storages\MemcachedStorage sliding expiration test.
   file: /var/www/dev/nette/tests/Nette/Caching/Memcached.sliding.phpt
   Fatal error

-> Nette\Caching\Storages\MemcachedStorage priority test.
   file: /var/www/dev/nette/tests/Nette/Caching/Memcached.priority.phpt
   Fatal error

-> Nette\Image flip.
   file: /var/www/dev/nette/tests/Nette/common/Image.flip.phpt
Milo
Nette Core | 1283
+
0
-

Ještě jsem chtěl dodat, že paralelně to běží fakt svižně :)

vrana
Člen | 131
+
0
-

norbe napsal(a):

Dle tohohle článku by to mělo jít od verze 4…

Je tam odkaz na neexistující branch a na GitHubu jsem našel akorát parallel-test-execution-with-make, což mi přijde trochu nešťastné. Článek je navíc z roku 2007, to se s ním od té doby opravdu nepohlo?

Editoval vrana (16. 2. 2012 17:17)

vrana
Člen | 131
+
0
-

Milo napsal(a):

Díky za vyzkoušení!

  • if ($this->phpType === 'CGI' && strlen($this->output)) by jen zamaskovalo skutečnou chybu – $this->output by nemělo být prázdné ani při neparalelním spuštění. Opravil jsem to.
  • proc_close() se na Linuxu chová fakt hloupě – opravil jsem to.

Je možné, že ti 13 testů padá i normálně a nesouvisí to se způsobem jejich spouštění. Zkus to prosím na masteru.

hason
Člen | 23
+
0
-

@vrana PHPUnit by měl umět paralelní testy nejdříve v roce 2013 https://github.com/…nium/pull/64#… Zařadí se PHPUnit po bok Doctrine2, phpMyAdmin a Zend View? ;)

Milo
Nette Core | 1283
+
0
-

Teď už testy spustím bez úprav. Úspěšnost testů v master větvi mám FAILURES! (475 tests, 5 failures, 26 skipped) (do masteru jsem poslal opravu na dva testy). To je naprosto shodné s výsledkem testů ve větvi parallel-tests pokud je spustím sekvenčně. Když spustím paralelně dva joby, je to stále stejné, ale od třech jobů výš začínají testy selhávat.

-> Nette\Caching\Storages\MemcachedStorage files dependency test.
   file: /var/www/dev/nette/tests/Nette/Caching/Memcached.files.phpt

Failed asserting that TRUE is FALSE on line 65

-> Nette\Caching\Storages\FileStorage items dependency test.
   file: /var/www/dev/nette/tests/Nette/Caching/FileStorage.items.phpt

Failed asserting that TRUE is FALSE on line 52

-> Nette\Caching\Storages\FileStorage sliding expiration test.
   file: /var/www/dev/nette/tests/Nette/Caching/FileStorage.sliding.phpt

Failed asserting that TRUE is FALSE on line 45

-> Nette\Latte\Engine: iCal template
   file: /var/www/dev/nette/tests/Nette/Latte/macros.ical.phpt

Fatal error: Uncaught exception 'Nette\InvalidStateException' with message 'Unable to write to directory '/var/www/dev/nette/tests/Nette/../tmp/cache'. Make this directory writable.' in /var/www/dev/nette/Nette/Config/Extensions/NetteExtension.php:392
Stack trace:
#0 /var/www/dev/nette/Nette/Config/Extensions/NetteExtension.php(357): Nette\Config\Extensions\NetteExtension->checkTempDir('/var/www/dev/ne...')
#1 /var/www/dev/nette/Nette/Config/Compiler.php(166): Nette\Config\Extensions\NetteExtension->afterCompile(Object(Nette\Utils\PhpGenerator\ClassType))
#2 /var/www/dev/nette/Nette/Config/Compiler.php(100): Nette\Config\Compiler->generateCode('SystemContainer', 'Nette\DI\Contai...')
#3 /var/www/dev/nette/Nette/Config/Configurator.php(238): Nette\Config\Compiler->compile(Array, 'SystemContainer', 'Nette\DI\Contai...')
#4 /var/www/dev/nette/Nette/Config/Configurator.php(185): Nette\Config\Configurator->buildContainer(NULL)
#5 /var/www/dev/nette/tests/Nette/Latte/macros.ical.phpt(23): Nette\Config\Configurator->createContai in /var/www/dev/nette/Nette/Config/Extensions/NetteExtension.php on line 392

A při 50 jobech FAILURES! (475 tests, 28 failures, 26 skipped). Skipped testy jsou většinout databázové, nemám MySQL.

Milo
Nette Core | 1283
+
0
-

V těch 5 failures je Image.flip.phpt ale to bude jen verzí GD. A pak dva:

-> Nette\Diagnostics\Debugger exception in HTML.
   file: /var/www/dev/nette/tests/Nette/Diagnostics/Debugger.exception.html.phpt

Fatal error: Exception thrown without a stack frame in Unknown on line 0


-> Nette\Diagnostics\Debugger notices and warnings with $strictMode in HTML.
   file: /var/www/dev/nette/tests/Nette/Diagnostics/Debugger.strict.html.phpt

Fatal error: Exception thrown without a stack frame in Unknown on line 0

Nicméně, pokud je spustím přes CLI:
php Nette/Diagnostics/Debugger.exception.html.phpt proběhnou v pořádku. Ale když přes CGI:
php-cgi Nette/Diagnostics/Debugger.exception.html.phpt tak spadnou.

Došel jsem až na Debugger:428

self::$blueScreen->render($exception);
if (self::$bar) {
	self::$bar->render();
}

Když zakomentuji jedno z těch render, je jedno jaké, tak test proběhne sice s chybou, ale až do konce. Chyba vzniká v Nette\Diagnostics\templates\bluesceen.phtml:483, funkce get_defined_constants(). Když ji dám volat bez TRUE, test se dokončí. Už jsem se s tím setkal a jestli si dobře pamatuji, může za to Suhoshin.

David Grudl
Nette Core | 8138
+
0
-

Tohle už před dvěma lety naprogramoval Jakub Kulhan, ani přesně nevím, proč se to nepoužilo, každopádně tahle implementace se mi líbí víc.

Jen mám dotaz, proč se spouští 40 vláken, když běžně jsou v počítačích 2–4 jádra?

David Grudl
Nette Core | 8138
+
0
-

Milo napsal(a):

V těch 5 failures je Image.flip.phpt ale to bude jen verzí GD.

Pošleš mi soubor, který ti to generuje?

jestli si dobře pamatuji, může za to Suhoshin.

Lze nějak Suhoshin detekovat, že by se v jeho případě konstanty nevypisovaly?

mkoubik
Člen | 728
+
0
-

Smysl má IMHO i víc vláken, než je jader procesoru. Zatímco jedno vlákno čeká na I/O (u testů dost častá věc), další může počítat.

Milo
Nette Core | 1283
+
0
-

David Grudl napsal(a):

Milo napsal(a):

V těch 5 failures je Image.flip.phpt ale to bude jen verzí GD.

Pošleš mi soubor, který ti to generuje?

Tady ke stažení. Verze GD knihovny je 2.0.

jestli si dobře pamatuji, může za to Suhoshin.

Lze nějak Suhoshin detekovat, že by se v jeho případě konstanty nevypisovaly?

Detekovat se dá jako běžný modul PHP. Ale teď už si nejsem jistý, jestli je to jím. Zkoušel jsem dnes navýšení některých suhosin hodnot v php.ini, chyby z logu zmizely, ale Exception bez trace logu vyskakuje stále. Zkusím to ještě víc debugnout.

Milo
Nette Core | 1283
+
0
-

Tak je to suhosinem. Myslel jsem, že je natvrdo zakompilovaný, ale PHP je v Debianu kompilováno s --with-config-file-scan-dir=/etc/php5/$SAPI/conf.d takže ikdyž použiji -c php.ini ostatní ini z /etc/php5/cgi/conf.d se také načtou, tím pádem i suhosin modul. Když se modul suhosin deaktivuje, vyjímka přestane vyskakovat a test projde.

Bude to nějaký suhosin BUG za podmínek které neznám, protože v Nette aplikacích laďenku běžně používám i s get_defined_constants(TRUE). Google prozradí, že s tím má víc lidí problém, i tady na fóru.

extension_loaded('suhosin')
vrana
Člen | 131
+
0
-

David Grudl napsal(a):

Tohle už před dvěma lety naprogramoval Jakub Kulhan, ani přesně nevím, proč se to nepoužilo, každopádně tahle implementace se mi líbí víc.

Nevíš, kde to skončilo? Dá se to ještě někde najít?

Jen mám dotaz, proč se spouští 40 vláken, když běžně jsou v počítačích 2–4 jádra?

Default je 20. Šteloval jsem to tak dlouho, dokud čas nebyl nejkratší :-). Vysvětlení je:

mkoubik napsal(a):

Zatímco jedno vlákno čeká na I/O (u testů dost častá věc), další může počítat.

David Grudl
Nette Core | 8138
+
0
-
vrana
Člen | 131
+
0
-

David Grudl napsal(a):

Dal jsem ti to sem https://github.com/…llel-run-juk

Dík, to moje se mi taky líbí víc :-). Jen by mě zajímalo, jestli Jakubovi tehdy fungovalo stream_select(), protože to se mi zaboha nepodařilo rozchodit. Výsledkem je, že se vždycky zbytečně čeká na dokončení prvního testu ve frontě. Proto jsem v mém řešení použil usleep() a stream_set_blocking(). Zkoušel jsem to na tomhle:

<?php
$tests = array();
for ($i=0; $i < 10; $i++) {
	$tests[] = 'php sleep.php ' . rand(1, 3);
}
?>
David Grudl
Nette Core | 8138
+
0
-

vrana napsal(a):

Je možné, že se při tom podaří odhalit i nějaké chyby ve frameworku. Nepochopil jsem třeba, jak by mělo fungovat tohle – vždyť pokud ten adresář někdo vytvoří mezi is_dir() a mkdir(), tak to hodí warning – to asi není žádoucí, ne?

Přesně tak, fixnul jsem to.

Každopádně by asi každé vlákno mělo mít vlastní temp dir, kvůli tomu, že je na začátku promazán.

duke
Člen | 650
+
0
-

David Grudl napsal(a):

vrana napsal(a):

Je možné, že se při tom podaří odhalit i nějaké chyby ve frameworku. Nepochopil jsem třeba, jak by mělo fungovat tohle – vždyť pokud ten adresář někdo vytvoří mezi is_dir() a mkdir(), tak to hodí warning – to asi není žádoucí, ne?

Přesně tak, fixnul jsem to.

A co když adresář někdo naopak mezi !is_dir() a fopen() smaže (v důsledku čehož nedojde k mkdir() a fopen() selže)? Nebo k tomu nemůže z nějakého důvodu dojít?

Milo
Nette Core | 1283
+
0
-

vrana napsal(a):
Jen by mě zajímalo, jestli Jakubovi tehdy fungovalo stream_select(), protože to se mi zaboha nepodařilo rozchodit.

Na Linuxu to běhá spolehlivě, ale na Windows se mi to nepodařilo rozběhat nikdy. Pouze s TCP/UDP streamy.

David Grudl
Nette Core | 8138
+
0
-

Paralelní zpracování by se hodilo asi i na PHP lint.

Acci
Člen | 83
+
0
-

David Grudl napsal(a):

Paralelní zpracování by se hodilo asi i na PHP lint.

Vytvořil jsem PHP Parallel Lint, který je inspirován právě paralelním spouštěním testů v Nette. Tak pokud se ti bude líbit, můžeš ho použít.

David Grudl
Nette Core | 8138
+
0
-

Super, díky!