Hiya,
Since upgrading to the latest version, when a test fails I get the following warning
include(PHP_Invoker.php): failed to open stream: No such file or directory
This seems to come from PHPUnit's GlobalState.php running class_exists on 'PHP_Invoker' which on my system obviously does not get picked up.
I'm using the composer.phar - I gather that PHPUnit is built into the .phar rather than using the system version. Does the composer.json need to include phpunit/PHP_Invoker package also?
Has anyone else seen these problems? Any help would be great
looks like it is a bug ( i got phpunit installed already and it is not happened, so i think need to pay attention on codeception-phpunit integration(
Hmm - did this area change recently? I'm sure it worked OK in the previous version?
lets wait main developer of the codeception)
yep, let me investigate it
i don't have PHPUnit globally installed and I don't see this issue.
Please provide a full stack trace.
Maybe you have PHPUnit installed globally or via Composer?
Hiya,
So I did some analysis of the stack which gave me a few more clues what's going on.
So I think the problem is how PHPUnit interacts with Yii (which probably isn't your problem - did I mention I was using Yii? :) ). So that stack below shows that the Yii autoload function is trying (and failing) to include the PHP_Invoker.php file. Not entirely sure if this is a problem with Yii or not.
The below shows there is a path, when using Yii and --xml, where PHP_Invoker is required else the run will fail. I did some experimenting with building my own codecept.phar - if I included php-invoker package in the composer.json and rebuilt the .phar, the error goes away, although it then just moves on to the next library which can't be found (I think it was DbUnit). Including that in compser.json also made it go away, although the next missing file (PHPUnit_Selenium) I couldn't get rid of.
Version Info taken from output:
[exec] Codeception PHP Testing Framework v1.5.7
[exec] Powered by PHPUnit 3.7.18 by Sebastian Bergmann.
Apologies, the stack-trace I've had to extract from a horrible Jenkins log file, this is the best I can do at the moment.
include(PHP_Invoker.php): failed to open stream: No such file or directory
/var/project/vendor/yiisoft/yii/framework/YiiBase.php(421)
phar:///var/project/tests/codecept.phar/vendor/phpunit/phpunit/PHPUnit/Util/GlobalState.php(409)
phar:///var/project/tests/codecept.phar/vendor/phpunit/phpunit/PHPUnit/Util/GlobalState.php(388)
phar:///var/project/tests/codecept.phar/vendor/phpunit/phpunit/PHPUnit/Util/Filter.php(76)
phar:///var/project/tests/codecept.phar/vendor/phpunit/phpunit/PHPUnit/Util/Log/JUnit.php(209)
phar:///var/project/tests/codecept.phar/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php(307)
phar:///var/project/tests/codecept.phar/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php(698)
phar:///var/project/tests/codecept.phar/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php(776)
phar:///var/lib/jenkins/jobs/Ethicall/workspace/tests/codecept.phar/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php(775)
phar:///var/project/tests/codecept.phar/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php(745)
phar:///var/project/tests/codecept.phar/src/Codeception/PHPUnit/Runner.php(107)
phar:///var/project/tests/codecept.phar/src/Codeception/SuiteManager.php(153)
phar:///var/project/tests/codecept.phar/src/Codeception/Codecept.php(110)
phar:///var/project/tests/codecept.phar/src/Codeception/Command/Run.php(70)
phar:///var/project/tests/codecept.phar/vendor/symfony/console/Symfony/Component/Console/Command/Command.php(240)
phar:///var/project/tests/codecept.phar/vendor/symfony/console/Symfony/Component/Console/Application.php(193)
etc....
So the stack appears to be because I'm requesting JUnit (xml) output (I'm running with flags --xml --steps). It also only happens in the code where a failure has occurred (PHPUnit/Util/Log/JUnit.php public function addFailure()).
So to save you some code reading...
TestResult.php(307)
307: $listener->$notifyMethod($test, $e, $time);
So presumably $listener is JUnit.php and $notifyMethod is addFailure
JUnit.php(209) => Calls static method
209: PHPUnit_Util_Filter::getFilteredStacktrace($e);
Filter.php(76) => Calls
75: if (!defined('PHPUNIT_TESTSUITE')) {
76: $blacklist = PHPUnit_Util_GlobalState::phpunitFiles();
77: } else {
78: $blacklist = array();
79: }
GlobalState.php(308)
383: public static function phpunitFiles()
384: {
385: if (self::$phpunitFiles === NULL) {
386: self::addDirectoryContainingClassToPHPUnitFilesList('File_Iterator');
387: self::addDirectoryContainingClassToPHPUnitFilesList('PHP_CodeCoverage');
388: self::addDirectoryContainingClassToPHPUnitFilesList('PHP_Invoker');
GlobalState.php(409)
408: if (!class_exists($className)) {
409: return;
410: }
YiiBase.php(421)
391 public static function autoload($className)
392 {
393 // use include so that the error PHP file may appear
394 if(isset(self::$classMap[$className]))
395 include(self::$classMap[$className]);
396 elseif(isset(self::$_coreClasses[$className]))
397 include(YII_PATH.self::$_coreClasses[$className]);
398 else
399 {
400 // include class file relying on include_path
401 if(strpos($className,'\\')===false) // class without namespace
402 {
403 if(self::$enableIncludePath===false)
404 {
405 foreach(self::$_includePaths as $path)
406 {
407 $classFile=$path.DIRECTORY_SEPARATOR.$className.'.php';
408 if(is_file($classFile))
409 {
410 include($classFile);
411 if(YII_DEBUG && basename(realpath($classFile))!==$className.'.php')
412 throw new CException(Yii::t('yii','Class name "{class}" does not match class file "{file}".', array(
413 '{class}'=>$className,
414 '{file}'=>$classFile,
415 )));
416 break;
417 }
418 }
419 }
420 else
421 include($className.'.php');
I think that even if you include Yii1 module as a last module in a modules list to make sure that yii register exception/error handlers at the end of the handlers stack, i think that need to investigate more with codeception internals. I was getting that problem when i did not installed all phpunit packages at first time, so at my point of view looks like codecepion-phpunit trying to load some components that needed by phpunit internals and not bundled with codeception.
Can u switch from Yii1 module to PhpBrowser and check if the problem is still exists?
Ok, the problem is clear now.
PHP_Invoker is not a part of PHPUnit package. Therefore it is not packaged with Codeception.
And can u post exactly which command u ran with --xml switch?
Ok, I have narrowed the problem. That's true the problem is inside Yii.
PHPUnit tries to locate it's files with this. PHP_Invoker is optional package in this case.
self::addDirectoryContainingClassToPHPUnitFilesList('PHP_Invoker');
Ok, PHPUnit didn't find this optional package and actually it doesn't care of it.
408: if (!class_exists($className)) {
409: return;
410: }
You see? It just skips invoker and continues to run.
But Yii says: "Hold on, man! You just asked for PHP_Invoker class. You can't continue. Let's try to autoload it!"
And you know what does Yii? It requires file from the global include_path (assuming file is installed via PEAR)
include($className.'.php');
Waaat? it doesn't even checks that file exists before including it.
So the problem is (pardon) shitty Yii autoloader.
Probably Yii has PHPUnit classes in it's autoload class_map and it thinks that it knows how to load them.
@Ragazzo is that clear now?
You are wrong.
So the problem is (pardon) shitty Yii autoloader.
Probably Yii has PHPUnit classes in it's autoload class_map and it thinks that it knows how to load them.
No. Problem is that phpunit trying to autoload packages and cant find them, then yii autoloader trying to get them and cant find them, thats why exception is thrown on yii autoloader. Problem is that this packages needed by phpunit.
So my proposal is to set YiiBase::$enableIncludePath to false.
That would fix the issue
Alternatively - install PHP_Invoker package via PEAR.
Still that doesn't fix that call to class_exists would crash your application.
fix with YiiBase::$enableIncludePath must be valid.
I can confirm that on my test system, setting YiiBase::$enableIncludePath = false; allows tests to fail without crashing in the manner above. Not sure if this is or should be the ultimate solution, but it's working for me (and I'm loving the new selenium2 screenshots working in 1.5.7!)
nice =)
anyway Yii just throws the notice even you didn't do anything inappropriate. I don't like this approach. They could just use @ in their autoloader.
I'm not sure I agree this issue is closed - basically everyone who uses the Yii1 module is going to need to do this? Are we just hoping they read this issue report? :)
Yep, you are right.
Probably we need to update documentation section for Yii1 module.
Or you have other ideas?
Hmm... for me I just created a helper module which I included with my WebGuy, and in the _initialize() it just ran the code above. However obviously that's not very nice. Can it go in the Yii1 module itself maybe?
Yes, agree need to force this behavior to the Yii1 module. can u make a PR with setting that property to false?
so, close this one?
Most helpful comment
I can confirm that on my test system, setting
YiiBase::$enableIncludePath = false;allows tests to fail without crashing in the manner above. Not sure if this is or should be the ultimate solution, but it's working for me (and I'm loving the new selenium2 screenshots working in 1.5.7!)