Paralelní testy
- vrana
- Člen | 131
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.
- vrana
- Člen | 131
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?
- Milo
- Nette Core | 1283
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
- vrana
- Člen | 131
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
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
@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
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
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 | 8227
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 | 8227
Milo napsal(a):
V těch
5 failures
jeImage.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?
- Milo
- Nette Core | 1283
David Grudl napsal(a):
Milo napsal(a):
V těch
5 failures
jeImage.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
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
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.
- vrana
- Člen | 131
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 | 8227
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()
amkdir()
, 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
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()
amkdir()
, 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?
- Acci
- Člen | 83
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.