Page MenuHomePhabricator

"Language::__destruct() was not expected to be called." failure due to garbage collector brings PHPUnit down
Closed, ResolvedPublic


I've experienced this failure locally. Steps to reproduce:

  • Add a PHPUnit extensions which triggers the garbage collector (gc_collect_cycles()) after each test
  • Run php tests/phpunit/phpunit.php tests/phpunit/languages/LanguageConverterTest.php

Very soon PHPUnit will print:

Language::__destruct() was not expected to be called.

and the test suite will be killed without any additional information.

I'm checking whether this should be reported upstream.

Upstream issue:

Event Timeline

We've had issues with this before; see T231656: MediaWikiTestCaseTrait::creatNoOpMock should permit calling destructors. The issue is on line 40, where the test language instance is mocked to never be called, except for the methods factory, getNsText and ucfirst. Because Language defines a __destruct magic method, the PHP engine will call this unconditionally, but the mock does not permit that.

Depending on how you look at it, PHPUnit should ignore __destruct when told to never allow method calls because the PHP engine will call this method unexpectedly, but it's actually doing what it should.

For a workaround, LanguageConverterTest should use createNoOpMock instead of manually doing this.

Fixing this in our tests should be trivial, yes. However, the real issue is not the test failure, but the fact that the suite dies without telling where exactly it failed. Sometimes it doesn't even print the "Language::__destruct() was not expected to be called." message.

The real fix would be making PHPUnit not exit if this bug happens.

Filed upstream. The minimum test case shows that this is not MW-specific. Perhaps even not PHPUnit-related, but a GC bug.

Seems like it's not going to be fixed upstream, so pushing a fix now.

Change 681703 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/core@master] tests: ensure __destruct is never doubled with anythingBut

Another thing worth pointing out: when this error happens for the "real" suite (i.e. not my minimal test case above), the error message (Language::__destruct() was not expected to be called.) is not guaranteed to be printed. I suppose something uses output buffering and then discards the error message. I say "not guaranteed" because it all depends on when exactly the garbage collector kicks in (i.e. during a test, after a test, before a test etc.).

I'm not entirely sure about what exactly is using output buffering, but I suspect it's our fault, see phpunit.php. This might be swallowing the warning, if the buffer is discarded without flushing. Which would add another good reason for us to pursue T90875.

Change 681703 merged by jenkins-bot:

[mediawiki/core@master] tests: ensure __destruct is never doubled with anythingBut

Daimona claimed this task.

For the record, noting here that the upstream issue was resolved by making PHPUnit refuse to mock __destruct, which I think is a good idea and fixes our issue.