Ask your Symfony questions! Pay money and get answers fast! (more info)

Symfony 1.4, Doctrine, Wamp - How to speed up PHPUnit? Symfony

Hi,

Running Wampserver 2.0 on local machine, Symfony 1.4, Doctrine and PHPUnit 3.5.5 / sfPHPUnit2Plugin.

Very simple test is taking 1 minute to run on my machine. A slightly longer test, 33 minutes (this runs in 4 seconds on a colleague's machine).

Happy to pay $40 to the person who can point out what's wrong and why it's taking so long.

If you need my phpinfo / ini files etc, no problem, just let me know what you need.

Many Thanks in advance.

Answers (5)

2010-12-07

Jimish Gamit answers:

Well, PHPUnit CodeCoverage is pretty nice and exports some nice HTML files to take a look into. However, a project most often contains more than just PHP files and for a symfony project this could be the phpunit.xml.dist. It removes all folders from your coverage report, that most likely are not your stuff. This one is based on the shipped file of sfPHPUnit2Plugin. The relevant part is the <filter> section.

<phpunit
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="true">

<filter>
<blacklist>
<directory>cache</directory>
<directory>data</directory>
<directory>log</directory>
<directory>lib/vendor</directory>
<directory>plugins</directory>
<directory>web</directory>
</blacklist>
</filter>

<testsuites>
<testsuite name="Unit Tests">
<directory>test/phpunit/unit/</directory>
</testsuite>
<testsuite name="Functional Tests">
<directory>test/phpunit/functional/</directory>
</testsuite>
</testsuites>
</phpunit>


<strong>Source:</strong>[[LINK href="http://toni.uebernickel.info/development/speed-up-phpunit-codecoverage-in-symfony-projects/"]]http://toni.uebernickel.info/development/speed-up-phpunit-codecoverage-in-symfony-projects/[[/LINK]]


darren13 comments:

Hi Jimish,

Thanks for reply.

I've got plugins/sfPHPUnit2Plugin/data/phpunit.xml.dist.tpl

<phpunit
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="true">

<testsuites>
<testsuite name="Unit Tests">
<directory>test/phpunit/unit/</directory>
</testsuite>
<testsuite name="Functional Tests">
<directory>test/phpunit/functional/</directory>
</testsuite>
</testsuites>
</phpunit>


but no phpunit.xml.dist anywhere in project.

I inserted the blacklist code you gave to this file, but still tests taking long time.

Maybe there is a problem with sfPHPUnit2Plugin?

Can I copy/rename the above file to the 'correct' directory if this is not the right one?

Thanks,
Darren.

2010-12-07

Wojciech Sznapka answers:

Try to load model and fixtures to sqlite memory. In bootstrap file you can do (remember to define ROOT_DIR and TEST_DIR vars):

Doctrine::createTablesFromModels(sprintf("%s/lib/model", $ROOT_DIR));
Doctrine::loadData(sprintf("%s/fixtures", $TEST_DIR));

and put this to the databases.yml:

test:
doctrine:
class: sfDoctrineDatabase
param:
dsn: 'sqlite::memory:'

It should be way faster.

2010-12-07

Christian Schaefer answers:

Hi Darren,

you told me that on linux your tests run just nicely and the performance drop only occurs on windows. If that is still the case you need to show us one of your tests to get an idea of what you're trying to do.

Other than that the above mentioned things apply.

Cheers
/Christian


darren13 comments:

Hi Christian,

I'm not sure actually if it's a Linux box that it's running fine on - I'll ask my colleague, who's on the other side of the world and not available atm.

But on our Dev web server, this test below takes 14 minutes. On my Windows/Wamp setup, 1 minute. On my colleague's machine, 4 seconds for a much longer test.

(this test comes back OK when it eventually comes back)

<?php

require_once dirname(__FILE__) . '/../../bootstrap/functional.php';

class LoginTest extends sfPHPUnitBaseFunctionalTestCase {

protected function getApplication() {
return 'backend';
}

public function testLoginSimple() {
$browser = $this->getBrowser();

// DR Rewriting to test Backend Homepage Login.
$browser->info('1 = The Client Homepage')->
info(' 1.1 - Get index')->
get('/')->
with('request')->begin()->
end()->

//###############################################
// DR Test Login Fails with Invalid Login

with('form')->begin()->
info(' 1.2 Submit Incorrect Login')->
click('Login', array(
'signin' => array(
'username' => 'norbert123456789',
'password' => 'merrigoround')))->
end()->

with('response')->begin()->
info(' 1.3 Check login failed with incorrect details')->
checkElement('ul.error_list:contains("The username and/or password is invalid")', true)->
end()->

//###############################################
// DR Test Login Fails with No Login Details, but a valid Password
with('form')->begin()->
info(' 1.4 Submit No Login, correct Password')->
click('Login', array(
'signin' => array(
'username' => '',
'password' => '******')))->
end()->

with('response')->begin()->
info(' 1.5 Check login failed with no user, correct password')->
checkElement('body:contains("Username")', true)->
checkElement('body:contains("Password")', true)->
end()->

//###############################################
// DR Test Login Fails with No Login or Password Details

with('form')->begin()->
//checkElement('body:contains("Login")', true)->
info(' 1.6 Submit No Login or Password')->
click('Login', array(
'signin' => array(
'username' => '',
'password' => '')))->
end()->

with('response')->begin()->
info(' 1.7 Check login failed with no user or pass')->
checkElement('body:contains("Username")', true)->
checkElement('body:contains("Password")', true)->
end()->

//#####################################################
//DR Check Login fields are present

get('/')->
with('response')->begin()->
info(' 1.8 Check Login fields are present')->
checkElement('body:contains("Username")', true)->
checkElement('body:contains("Dashboard")', false)->
checkElement('input #signin_username', true)->
checkElement('input #signin_password', true)->
end()->

//#####################################################
// DR Test Login Works with Valid Login

get('/')->

with('form')->begin()->
info(' 1.9 Submit Correct Login')->
click('Login', array(
'signin' => array(
'username' => '********',
'password' => '********')))->
end()->

// Form should have no errors with correct login details:
with('form')->begin()->
hasErrors(false)->
end()->

//Go to
get('/')->
//DR Check Homepage Title and Nav Links is present
with('response')->begin()->
info(' 2.0 Check Dashboard fields are present')->
checkElement('body:contains("Homepage")', true)->
checkElement('#accordion ul:contains("Dashboard")', true)->
checkElement('#accordion ul:contains("View Candidates")', true)->
checkElement('#accordion ul:contains("View Clients")', true)->
checkElement('#accordion ul:contains("Contacts")', true)->
checkElement('#accordion ul:contains("Manage Jobs")', true)->
checkElement('#accordion ul:contains("Migration")', true)->
checkElement('#accordion ul:contains("Marketing")', true)->
checkElement('#accordion ul:contains("Web Tools")', true)->
checkElement('#accordion ul:contains("Check your mail")', true)->
checkElement('#accordion ul:contains("Settings")', true)->
checkElement('#accordion ul:contains("Logout")', true)->
end()->

//DR check Logout works
get('/user/logout')->
with('response')->begin()->
info(' 2.1 Logout ')->
end()->

get('/')->
with('response')->begin()->
info(' 2.3 Check homepage is back to Login screen')->
checkElement('body:contains("Username")', true)->
checkElement('body:contains("Password")', true)->
end()


;
}

}


Christian Schaefer comments:

ah ok so you are wrapping a lime functional test inside a phpunit test. thats fine.
but it also explains why this takes longer than a few seconds. functional tests will always take longer than unit tests as they test from the outside and all the framework needs to boot up.

the test itself looks good enough for me but it implies the question: do you test against a live database or a test one?

2010-12-07

Nate Flink answers:

This sounds like a problem with the environment. Here are some things I would look at. Have you checked the basic stuff like php.ini for memory limit. Depending on your set up you might need to check the php cli ini file.

Can you isolate where the choke point is occurring? Do you have logs to look at this, maybe comparing time stamps?

It sort of sounds like maybe the query is taking a long time. Are the databases on both machines the same?

Are the PEAR implementations on both machines the same version?

Are the environment PATH variables set up correctly on the windows machine?




2010-12-16

Florian Klein answers:

To check where your script is taking too much time, you can use Xdebug and KCacheGrind.

Just configure xdebug to enable the profiler option.
It will save in a file the call chains and others infos like time spent on each method.

This file will be readable by KCacheGrind.

Here is a sample config for xdebug.ini ( on my distrib: /etc/php5/conf.d/xdebug.ini )


xdebug.remote_enable=1
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
; profiler settings
xdebug.profiler_append=1
;xdebug.profiler_append=0
xdebug.profiler_enable=1
xdebug.profiler_enable_trigger=1
xdebug.profiler_output_name = cachegrind.out.%s
xdebug.profiler_output_dir=/xdebug/



Wou then just have to (eventually) reboot your apache (if any), and reload your test suite.

You will see appear a file in /xdebug/cachegrind.out.*, just open it with [[LINK href="http://kcachegrind.sourceforge.net/html/Home.html"]]KCacheGrind[[/LINK]] or [[LINK href="http://sourceforge.net/projects/wincachegrind"]]winCacheGrind[[/LINK]] to see a graphical representation of your memory footprint, call graph or callee graph.