Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heuristics to set memory limit for mutants running with PHPUnit #258

Merged
merged 4 commits into from Mar 28, 2018

Conversation

sanmai
Copy link
Member

@sanmai sanmai commented Mar 24, 2018

Since we know how much memory the initial test suite used, and only if we know, we can enforce a memory limit upon all mutation processes. Limit is set to be twice the known amount, because if we know that a normal test suite used X megabytes, if a mutant uses a lot more, this is a definite error.

Memory limit is introduced by altering a known temporary php.ini to include a directive to enable the limit as the very last line. We only apply a memory limit if there isn't one set.

This PR assumes no default memory limits.

This PR:

I rather hope this PR will be more satisfactory to @theofidry and others who didn't like an idea of a default memory limit. We are not forcing a default upon anyone here.

Copy link
Member

@maks-rafalko maks-rafalko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very nice to me.

if we know that a normal test suite used X megabytes, if a mutants uses a lot more, this is a definite error.

👍

But, should we show in the CLI reporter that the mutant is killed by exceeding the memory consumption?

I mean add a new letter to .: killed, M: escaped, S: uncovered, E: fatal error, T: timed out, for example, X: memory limit.


Am I right that this PR is a replacement for #253?

@@ -192,6 +194,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
return 1;
}

// We only apply a memory limit if there isn't one set
if ($adapter instanceof PhpUnitAdapter && ini_get('memory_limit') === '-1') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about other test frameworks, e.g. PhpSpec? shouldn't we care about it as well?

I'm sure if we even end up with PhpUnitAdapter for now, we should introduce an interface like MemoryUsageAware (or whatever you name it) and check it rather than hardcoding PHPUnit related stuff in the very base command

if ($adapter instanceof MemoryUsageAware && ini_get('memory_limit') === '-1') {
    $this->applyMemoryLimitFromTestFrameworkProcess();
}


class PhpUnitAdapter implement MemoryUsageAware...

Copy link
Member Author

@sanmai sanmai Mar 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MemoryUsageAware

@sanmai
Copy link
Member Author

sanmai commented Mar 24, 2018

If we could ignore other frameworks, then, yes, this is a bare-bones replacement for #253.

PhpSpec isn’t reporting memory usage like PHPUnit does (I checked), so if we need to support this in PhpSpec we have no choice but to go by #253.

@sanmai
Copy link
Member Author

sanmai commented Mar 24, 2018

I believe PHP interpreter will not indicate by an exit code if it died because of a language error or not. Therefore, we could only reason about exit statuses if we capture an actual error. Right now PHPUnit will not print any errors, but if it could be made to, it's a possibility.

@sanmai
Copy link
Member Author

sanmai commented Mar 24, 2018

I’m not sure if we can hack PhpSpec runner to output a number from memory_get_peak_usage(), and quick googling did not show anything.

Did I address all you concerns, @borNfreee?

@theofidry
Copy link
Member

👍 I must say I much prefer this one to the others.

I mean add a new letter to .: killed, M: escaped, S: uncovered, E: fatal error, T: timed out, for example, X: memory limit.

👍 👍

IIRC you get an error along the lines of Fatal error: Allowed memory size of or a special code for this kind of error so we could know if the failure was caused by a memory exhaustion that way.

I think we should also make it configurable like we do for the time out; This can wait though.

If we could ignore other frameworks

We could try to add a memory consumption on their end but I don't think it's a blocker for this PR.

Copy link
Member

@sidz sidz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this approach really working as you expect? It will set memory_limit only for the main phpunit process :)
But PHPUnit runs sub-processes for each test case. And this approach won't work for them at all.

As Marco said yesterday we need to do it in the same way like phpunit does. So I investigated a bit yesterday and here is how phpunit works:

https://github.com/sebastianbergmann/phpunit/blob/7.0.2/src/Runner/PhptTestCase.php#L125 - here it takes ini section from the xml and puts all ini's to the main $settings variable

then getCommand https://github.com/sebastianbergmann/phpunit/blob/7.0.2/src/Util/PHP/DefaultPhpProcess.php#L95 method will generate /usr/bin/php -d bla_bla=-1 file for each test case.

So this PR will tweak only the main phpunit process.

In this case we need to add ini section to the phpunit.xml file which Infection generates if we want to apply memory limit to each sub-process.

Also as we will replace our own XdebugHandler by https://github.com/composer/xdebug-handler please don't use any classes, constants from the Php namespace

@sanmai
Copy link
Member Author

sanmai commented Mar 25, 2018

@sidz, PHPUnit will pass trough all options we set in our custom php.ini even if we tell it to use process isolation:

bin/infection --test-framework-options="--process-isolation"

If someone decides to shoot oneself in the foot by setting own limits with <ini name="memory_limit" value="-1"/>, they gonna be hit by it. That's par for the course.

@sanmai
Copy link
Member Author

sanmai commented Mar 25, 2018

@sidz we use ConfigBuilder::ENV_TEMP_PHP_CONFIG_PATH in PhpExecutableFinder also, and I don't see any replacement to it in the composer/xdebug-handler. It doesn't even export a similar path it has. And PhpExecutableFinder needs to know which php.ini to use. I don't see how you gonna do this.

@sanmai sanmai force-pushed the memory-limit-auto branch 3 times, most recently from 5b95b33 to 81b86ea Compare March 25, 2018 02:46
@sidz
Copy link
Member

sidz commented Mar 25, 2018

@sanmai #215 (comment)

@sanmai
Copy link
Member Author

sanmai commented Mar 25, 2018

@sidz That's excellent! We would only have to change this line and this line to use php_ini_loaded_file() instead. It would be best if there will be an utility function. In any case this is trivial task and not a blocker to this PR.

@sidz
Copy link
Member

sidz commented Mar 25, 2018

@sanmai you can change this line 33ee1df#diff-a99db3f318d0547a301f216cb03b51a7R287 without any changes right now :) So please do it. thx

@sanmai
Copy link
Member Author

sanmai commented Mar 25, 2018

@sidz Done!

I'm happy to say that my E2E test for this issue is passing with php_ini_loaded_file(). This also means it is good to go in other places.

Anything else?

Copy link
Member

@maks-rafalko maks-rafalko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👏 🥇 🎉 Just added a minor comment

@sidz do you have any concerns yet?

@@ -0,0 +1,9 @@
# Title
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please either remove this file or add correct information

Copy link
Member

@sidz sidz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. What if I want or I need to set -1 for all sub-processes?

*
* @var int
*/
const DEFAULT_MEMORY_LIMIT_MULTIPLIER = 2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo all constants should be placed at the top of the class

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This isn't going to make the code more readable.
  2. PHP CS Fixer normally takes care of this if there's such policy, and it didn't.

When there's no policy, I side with Code Complete on minimizing live time for programming constructs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you find any place in the infection code where we did it in the same way - feel free to leave it as is. if no - please move it to the top of the class. thank you

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However you wish. Done.

* because if we know that a normal test suite used X megabytes,
* if a mutants uses a lot more, this is a definite error.
*/
$memoryLimit *= self::DEFAULT_MEMORY_LIMIT_MULTIPLIER;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please move this line under condition as we don't use it elsewhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@maks-rafalko
Copy link
Member

What if I want or I need to set -1 for all sub-processes?

what is the use case when you want PHPUnit to consume unlimited (and much more than initial test run consumed) memory?

@sidz
Copy link
Member

sidz commented Mar 27, 2018

can't give you any constructive example now but I have a stronger feeling that we're doing smth wrong.

@maks-rafalko
Copy link
Member

maks-rafalko commented Mar 27, 2018

TBH I don't get it. This PR limits PHPUnit processes only, with the twice bigger amount of memory than initial test suite consumes.

I'm just summing up it to myself as well to find any potential issues but nothing comes to my mind.

Anyway, we still have plenty of time before 0.9.0, so we can see how it's going

@sanmai sanmai force-pushed the memory-limit-auto branch 3 times, most recently from 0de80b3 to 5eb00d0 Compare March 28, 2018 01:30
Since we know how much memory the initial test suite used,
and only if we know, we can enforce a memory limit upon all
mutation processes. Limit is set to be twice the known amount,
because if we know that a normal test suite used X megabytes,
if a mutants uses a lot more, this is a definite error.

Memory limit is introduced by altering a known temporary php.ini
to include a directive to enable the limit as the very last line.
sanmai and others added 2 commits March 28, 2018 16:29
…oesn't need to know it use a concrete PhpUnitAdapter.
Test will fail with a timeout if the automatic memory limit is disabled.
@maks-rafalko
Copy link
Member

Thanks for all your hard work on this topic @sanmai, much appreciated

@maks-rafalko maks-rafalko merged commit b7eb404 into infection:master Mar 28, 2018
@sanmai sanmai deleted the memory-limit-auto branch March 28, 2018 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants