-
Couldn't load subscription status.
- Fork 4
Fix/infinite loop on restub invoke #30
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
Fix/infinite loop on restub invoke #30
Conversation
| if ( isset( $value ) ) { | ||
| return self::filter_return_for( $value ); | ||
| } | ||
| return \Patchwork\relay(); |
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 am not entirely sure why we need to relay here.
I did not intend to change this part (this is not directly part of the fix) but without this, the call count of a stubbed method gets increased. For example:
1) ConsecutiveExecutionTest::test__undefined_function___can_be_restubbed
Failed asserting that 2 matches expected 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.
I'm having trouble finding the docs for this version of Patchwork to remember what this did exactly, but I assume the reason we relay here is so that a global function could be spied upon without needing to also mock it. Something like:
function getName() {
return 'human';
}
//...
$spy = \Spies\get_spy_for( 'getName' );
$name = getName();
$this->assertSame( 'human', $name );
$this->assertTrue( $spy->was_called() );If this is removed, won't that prevent the original global function from being called?
Although, I see that you have a test test__global_function__can_be_restubbed which covers this case, and it passes. How does that work?
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.
Ok, I followed the code and it looks like when GlobalSpies::handle_call_for() is called, it eventually calls GlobalSpies::call_original_global_function() thanks to Spy->get_return_for(), which uses \Patchwork\restore() to call the function before recreating the mock with GlobalSpies::replace_global_function().
That explains why this works. Maybe that wasn't always the case which would explain the relay? Or maybe there's another edge case we're not considering here. π€·
c01ea2d to
cff9765
Compare
cff9765 to
6a8bc03
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.
Looks fine to me! Thank you so much for adding tests.
| if ( isset( $value ) ) { | ||
| return self::filter_return_for( $value ); | ||
| } | ||
| return \Patchwork\relay(); |
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 having trouble finding the docs for this version of Patchwork to remember what this did exactly, but I assume the reason we relay here is so that a global function could be spied upon without needing to also mock it. Something like:
function getName() {
return 'human';
}
//...
$spy = \Spies\get_spy_for( 'getName' );
$name = getName();
$this->assertSame( 'human', $name );
$this->assertTrue( $spy->was_called() );If this is removed, won't that prevent the original global function from being called?
Although, I see that you have a test test__global_function__can_be_restubbed which covers this case, and it passes. How does that work?
| if ( isset( $value ) ) { | ||
| return self::filter_return_for( $value ); | ||
| } | ||
| return \Patchwork\relay(); |
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.
Ok, I followed the code and it looks like when GlobalSpies::handle_call_for() is called, it eventually calls GlobalSpies::call_original_global_function() thanks to Spy->get_return_for(), which uses \Patchwork\restore() to call the function before recreating the mock with GlobalSpies::replace_global_function().
That explains why this works. Maybe that wasn't always the case which would explain the relay? Or maybe there's another edge case we're not considering here. π€·
Fixes #31
(I know this repo has not been developed in long time but your feedback is appreciated. π )
The first commit adds tests for #28, to make sure there is no regression.
The second and third commit respectively tests and fixes the problem described in #31.
Without the change in the third commit, the tests would get stuck in the infinite loop or cause
Allowed Memory Exhaustederror.Testing instructions
Please run tests with a memory limit (i.e.
vendor/bin/phpunit -d memory_limit=24M) so that tests are not stuck in a loop for a long time.Apply this patch (to remove @runTestsInSeparateProcesses and add finish_spying)
master, comment outself::$global_functions = [];insrc/Spies/GlobalSpies.php(one liner change from Forget the name of the global functions on restoreΒ #28) run the tests; some tests fail, so there could be come problems withfinish_spying(indicates the problem that PR tries to fix)master, revert the one liner change and run tests again: tests get memory limit error, so the infinite loop is possibly introduced (indicates the problem this PR is trying to fix)