Problem/Motivation

Simpletest.module assumes that a simpletest style test is a failure from a fatal error unless otherwise told it is passing: http://cgit.drupalcode.org/drupal/tree/core/modules/simpletest/src/TestB...

When phpunit fatal errors, it creates a 0 byte xml file in /sites/default/files/simpletest/phpunit-xx.xml. When it comes time to parse that output (http://cgit.drupalcode.org/drupal/tree/core/modules/simpletest/simpletes...), In line 673 it checks and if the file is 0- bytes, it simply bails out.

This causes the test to get "lost" with no record of that test either passing or failling. The legacy testbots look for "Fatal Error" in the output of the run and if it finds it, sets the whole test to a "Failing" state, but does not contain any indication of which particular test failed: (e.g.: https://www.drupal.org/node/2337191#comment-10205585)

The drupalci testbots will also have to implement this functionality to avoid 'passing' a failing test along, but it would be ideal to eventually have this handled properly in the simpletest module.

What I propose is that we create a default junit xml formatted 'failure' file in simpletest_phpunit_run_command().(http://cgit.drupalcode.org/drupal/tree/core/modules/simpletest/simpletes...) that would then get overwritten on a successful execution of that particular test run, that way it mimics the behavior of the simpletest style tests, and gives us the ability to know which specific unit test aborted on a Fatal Error.

Proposed resolution

Check for the return code on the executed phpunit file. If its not 0, an error occured and we can provide a special "xml" with that failure included.

Remaining tasks

User interface changes

API changes

Data model changes

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug/Task/Feature because ...
Issue priority Critical because broken tests aren't seen.
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Mixologic created an issue. See original summary.

Mixologic’s picture

dawehner’s picture

How feasible would it be to run the phpunit tests via the phpunit executable at that point? I guess jenkins would already have support for all that.

dawehner’s picture

Tried to start with that, but the problem is that PHPUnit opens up that file and overwrites it in http://cgit.drupalcode.org/drupal/tree/core/vendor/phpunit/phpunit/src/U...

Mixologic’s picture

Okay, re #3 The phpunit executable needs to run in isolation mode, but the issue there is also, we do not have any sort of handling for phpunit's own executable when it encounters a fatal error. Putting this into simpletest allows us to have a wrapper around phpunit that can handle those anomolies.

I figured out a good place to put this - after the parsing of the output takes place.

Attached is a first stab at this. Im not too savvy with how to write a test that proves that a failing phpunit test breaks, so hoping somebody else can help with that.

With manual testing I've show this to work.

Mixologic’s picture

Some additional info on the patch. Yes, '.' does seem to be the return value when there is an error and phpunit does not return.

Mixologic’s picture

Status: Active » Needs review
dawehner’s picture

+++ b/core/modules/simpletest/simpletest.module
@@ -178,8 +178,25 @@ function simpletest_run_tests($test_list) {
+  if ($ret == ".") {

MHH doesn,'t "." mean something broke on the first test, but there might be more. Can't you look at the return value 0, 1, etc?

mradcliffe’s picture

The exec() command in simpletest_phpunit_run_command() needs to have the the optional 3rd argument which has the execution status. Then that var can be checked for 1/0 as @dawehner suggests in #8.

I am not sure what the behavior of return_var is on non-unix systems.

Mixologic’s picture

Fixed to incorporate #8/#9

Also attached is a patch which shows how some deliberately broken tests would actually get caught as broken.

Im going to hope that '0' is success on non-unix.

chx’s picture

Title: PHPUnit tests should assume failure until success is returned » Fatal PHPUnit tests are not always reported as failure
Category: Feature request » Bug report
Priority: Normal » Critical
Status: Needs review » Reviewed & tested by the community
FileSize
1.58 KB
607 bytes

Please do not credit me, this is a trivial change. Also, since this blocks DrupalCI, it blocks Drupal 8 so it's a critical.

webchick’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

Can we add test coverage for this? This seems like the very definition of things we do not want to ever break again.

The last submitted patch, 10: 2560643-phpunit-fatal-error-handling-breaking-10.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review

I doubt we can/should test this at this level ... ideally for example we would use phpunit directly in which case the test failure would need to be caught on the level of DrupalCI, as this would execute that script.
Its hard to test the testing system, this is for sure.

mradcliffe’s picture

I am not sure if simpletest_phpunit_run_command is testable. Maybe passing in garbage as $phpunit_file, and hope the command crashes, and assert that ret is non-zero?

dawehner’s picture

Well to be clear, there might be a chance to test it now, but on the longrun we really should move away from having a simpletest(phpunit()) wrapping construct,
as it makes things problematic. Once we have that not any longer, the test we will introduce here, won't work any longer.

Mixologic’s picture

Uploading the "this is what the tests look like without this fix, but with the same breakage" - its the best I can think of for a test of this. What we should see is invisible fails, it might even come back greeen. but the log will show all the problems.

Mixologic’s picture

Alrighty then. #17 came back green, despite function calls to conjunction_junction_undefined_function(), a recursion, and a deliberate segfault (see the console log here: https://dispatcher.drupalci.org/job/default/13343/consoleFull)

So hopefully that demonstrates the kinds of broken that the patch is supposed to fix (as seen in #10) and also demonstrates that the patch wont break anything else (as seen in #10 and #11.

Status: Needs review » Needs work

The last submitted patch, 19: 2560643_19.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
FileSize
3.94 KB

Oh bother, a missing @group.

Edit: preg_match('/(.(?!b))*/', str_repeat("a", 10000)); is from http://ilia.ws/archives/5_Top_10_ways_to_crash_PHP.html -- it causes a segfault immediately. If a standalone PCRE library is chosen AND the --disable-stack-for-recursion option is given at compilation THEN this will not segfault. However, this is extremely unpopular (and no distributions ship with it) as it slows down PCRE for the sake of what is simply known as degenerate regexps. You can test by running pcretest -C | grep 'Match recursion' it will say (if you have a standalone PCRE install, that is) Match recursion uses stack.

Edit2: this is better than using bananas() to cause a fatal error because shutdown functions still might run after a fatal error. Nothing runs after the process itself is aborted this way. We can't abort reliably normally as exit/die will also run destructors and shutdown functions. Alternatively we could use $pid = getmypid(); exec("kill $pid"); exec("TSKILL $pid"); to get the OS to terminate the process immediately. Hopefully PHPunit won't add signal handlers... (it doesn't have one now).

The last submitted patch, 10: 2560643-phpunit-fatal-error-handling-breaking-10.patch, failed testing.

The last submitted patch, 19: 2560643_19.patch, failed testing.

dawehner’s picture

  1. +++ b/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php
    @@ -0,0 +1,15 @@
    +
    +class SimpletestPhpunitRunCommandTestWillDie extends UnitTestCase {
    ...
    +      preg_match('/(.(?!b))*/', str_repeat("a", 10000));
    

    Can we have a quick one line comment what we do here?

  2. +++ b/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php
    @@ -0,0 +1,15 @@
    +  function testWillDie() {
    

    public ...

  3. +++ b/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php
    @@ -0,0 +1,38 @@
    +    include_once __DIR__ .'/../../fixtures/simpletest_phpunit_run_command_test.php';
    +    $app_root = __DIR__ . '/../../../../../..';
    

    This one loads code so we need additional annotations, see IRC

chx’s picture

Issue tags: -Needs tests
FileSize
4.36 KB
1.6 KB
dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Thank you. Otherwise we would have had another major, at least. You really don't want to break a workflow committers use.

Ran the phpunit tests manually:

phpunit testsuite unit
............................................................. 8540 / 8569 ( 99%)
.............................

Time: 42.1 seconds, Memory: 275.25Mb

OK (8569 tests, 17001 assertions)
  1. +++ b/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php
    @@ -4,10 +4,18 @@
    + *
    + * To avoid accidentally running, it is not in a normal PSR-4 directory, the
    + * file name does not adhere to PSR-4 and an environment variable also needs to
    + * be set for the crash to happen.
    + */
    

    Perfect, thank you!

  2. +++ b/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php
    @@ -11,7 +11,12 @@
    + * @runTestsInSeparateProcesses
    + * @preserveGlobalState disabled
    

    This is needed in order to avoid #2565971: failures running phpunit from command line.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 26: 2560643_26.patch, failed testing.

The last submitted patch, 26: 2560643_26.patch, failed testing.

chx’s picture

Unfollowing the issue: I had a green patch then came phpunit annotations and I can't reproduce the fail locally from running the test alone with phpunit, running the simpletest group with phpunit or running the test with simpletest. It always passes. I can't and won't fight phpunit, I let others do that. I was against shoveling in PHPunit without documentation, education and proper integration and now this is going to block the RC There was no comparison between modern testing framework done, we just added PHPunit just because. Well done.

Edit: I reported two years ago that the integration is broken #2096615: Testbot does not return PHPunit fails properly. PHPunit is still unmaintained and the integration never got fixed.

larowlan’s picture

With Drupal CI we can run these outside simpletest right?

dawehner’s picture

Past is past and time is limited.

I asked for this additional annotations because otherwise state will leak into the tests running. Test failures like #2565971: failures running phpunit from command line. are hard to debug and should be prevented
in beforehand. We have more criticals to solve, no reason to freak out :( It burns everyone out, who is involved in the process!

Edit: I reported two years ago that the integration is broken #2096615: Testbot does not return PHPunit fails properly. PHPunit is still unmaintained and the integration never got fixed.

You might have the right idea, get rid of the integration and rely on running phpunit directly. Its IMHO the only thing we can and should do!
#2566767: Don't allow running phpunit-based tests via the UI is the first issue of that kind. @Mixologic how difficult is it to run the phpunit tests separate?

I tried to use the drupalci_testrunner, but it turns out, I can't install it at the moment.
Feel free to commit #21 + the comments and maybe create a follow up?

larowlan’s picture

This is the fail (getting it locally)

There was 1 error:

1) Drupal\Tests\simpletest\Unit\SimpletestPhpunitRunCommandTest::testSimpletestPhpUnitRunCommand
PHPUnit_Framework_Exception: Segmentation fault

Code I used to run it locally

./core/vendor/bin/phpunit -c ./core/ ./core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php
larowlan’s picture

The issue is in \PHPUnit_Util_PHP::processChildResult

larowlan’s picture

Status: Needs work » Needs review
FileSize
733 bytes
4.43 KB

Moar isolation

This passes locally

dawehner’s picture

Let's hope it passes. This would be nice!

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

larowlan++

dawehner’s picture

Issue summary: View changes

Worked on the issue summary / beta eval, just to be sure.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Well since I run all the phpunit tests before I commit most patches we don't actually have any broken tests but it is certainly critical that our test infrastructure does not pick up all test fails. Nice that this comes with a test. However the test does not work for me or @dawehner since the changes in #35. In fact it works as expected without those changes. I don't get why those change matter one jot - this is a totally separate invocation of phpunit so it is already isolated BUT it should not matter if it is. My recommendation here is to remove the tests and add a major followup to add them in when we can get them to work consistently for everyone.

dawehner’s picture

dawehner’s picture

Status: Needs work » Needs review
FileSize
2.86 KB
1.58 KB

There we go, removed the tests

webchick’s picture

Status: Needs review » Reviewed & tested by the community

Since this is the exact same patch that already passed above, moving to RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed c27852c and pushed to 8.0.x. Thanks!

  • alexpott committed c27852c on 8.0.x
    Issue #2560643 by chx, Mixologic, dawehner, larowlan: Fatal PHPUnit...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.