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
Conversation
823f12d
to
c45182e
Compare
There was a problem hiding this 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?
src/Command/InfectionCommand.php
Outdated
@@ -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') { |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ MemoryUsageAware
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. |
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? |
👍 I must say I much prefer this one to the others.
👍 👍 IIRC you get an error along the lines of I think we should also make it configurable like we do for the time out; This can wait though.
We could try to add a memory consumption on their end but I don't think it's a blocker for this PR. |
There was a problem hiding this 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
@sidz, PHPUnit will pass trough all options we set in our custom php.ini even if we tell it to use process isolation:
If someone decides to shoot oneself in the foot by setting own limits with |
@sidz we use |
5b95b33
to
81b86ea
Compare
fcdcc60
to
117af15
Compare
@sanmai you can change this line 33ee1df#diff-a99db3f318d0547a301f216cb03b51a7R287 without any changes right now :) So please do it. thx |
@sidz Done! I'm happy to say that my E2E test for this issue is passing with Anything else? |
84d04d7
to
5d9bd3f
Compare
There was a problem hiding this 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 |
There was a problem hiding this comment.
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
There was a problem hiding this 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?
src/Command/InfectionCommand.php
Outdated
* | ||
* @var int | ||
*/ | ||
const DEFAULT_MEMORY_LIMIT_MULTIPLIER = 2; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- This isn't going to make the code more readable.
- 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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However you wish. Done.
src/Command/InfectionCommand.php
Outdated
* 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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
what is the use case when you want PHPUnit to consume unlimited (and much more than initial test run consumed) memory? |
can't give you any constructive example now but I have a stronger feeling that we're doing smth wrong. |
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 |
0de80b3
to
5eb00d0
Compare
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.
…oesn't need to know it use a concrete PhpUnitAdapter.
Test will fail with a timeout if the automatic memory limit is disabled.
5eb00d0
to
4bf257c
Compare
Thanks for all your hard work on this topic @sanmai, much appreciated |
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.