Problem/Motivation

This is a child issue of #2949363: Impossible to make trigger_error in some files without test fails

In that issue we discovered that we can't perform test discovery on files which @trigger_error() for deprecation without causing false-fail test runs.

These include abstract classes, such as base classes, traits, and interfaces.

The problem: TestDiscovery uses reflection, which loads the file, which triggers the deprecation error whether the class is used or not in any test.

In #2296635: Evaluate performance impact of limiting test discovery to *Test.php filename suffix we considered excluding all files from scan except for *test.php files. This presents BC problems because it could lead to false positives for the unaware.

Proposed resolution

Modify TestDiscovery to exclude reflection on files which end in TestBase.php, Trait.php and Iterface.php.

This mitigates the problem of triggering E_USER_DEPRECATED in the various abstract test types, while also allowing greater BC than simply ignoring all files that don't end in *Test.php.

Map:

  • #8: version with exclude TestBase.php, Trait.php and Interface.php
  • #14: version with exclude TestBase.php and Trait.php

Remaining tasks

User interface changes

API changes

Data model changes

CommentFileSizeAuthor
#18 2973127-18.patch3.47 KBAnonymous (not verified)
#16 2973127-8.patch3.41 KBAnonymous (not verified)
#14 interdiff-8-14.txt2.58 KBAnonymous (not verified)
#14 2973127-14.patch3.16 KBAnonymous (not verified)
#8 interdiff-6-8.txt1.48 KBAnonymous (not verified)
#8 2973127-8.patch3.41 KBAnonymous (not verified)
#6 interdiff-4-6.txt3.8 KBAnonymous (not verified)
#6 2973127-6.patch3.66 KBAnonymous (not verified)
#4 interdiff.txt971 bytesmile23
#4 2973127_4.patch2.53 KBmile23
#3 2973127_3.patch2.54 KBmile23

Comments

Mile23 created an issue. See original summary.

mile23’s picture

$ ./vendor/bin/phpunit -c core --filter ExpectDeprecationTest
PHPUnit 6.5.8 by Sebastian Bergmann and contributors.

Testing 
..                                                                  2 / 2 (100%)

Time: 13.02 seconds, Memory: 586.50MB

OK (2 tests, 3 assertions)

Remaining deprecation notices (50)

  1x: The Drupal\Tests\hal\Functional\EntityResource\File\FileUploadHalJsonTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use Drupal\Tests\file\Functional\Hal\FileUploadHalJsonTestBase. See https://www.drupal.org/node/2971931.
    1x in FunctionalTestSuite::suite from Drupal\Tests\TestSuites

[ ..repeat x49.. ]

This is because the test suite classes call PHPUnit\Framework\TestSuite::addTestFile() at some point, which performs reflection. We can't really change that.

This means we have to change TestDiscovery::scanDirectory() to exclude *TestBase.php, since the test suite classes use scanDirectory().

mile23’s picture

Status: Active » Needs review
StatusFileSize
new2.54 KB

Modified scanDirectory() and added a test.

mile23’s picture

StatusFileSize
new2.53 KB
new971 bytes
+++ b/core/modules/simpletest/src/TestDiscovery.php
@@ -288,7 +288,12 @@ public static function scanDirectory($namespace_prefix, $path) {
+        $file_name = $current->getFilename();
+        return !(strtolower(substr($file_name, -12)) == 'testbase.php' || strtolower(substr($file_name, -9)) == 'trait.php');

Boo. Perform strtolower() once.

Anonymous’s picture

Why we need strolower at all?) We expect to here some naming patterns, let's compare them more strictly. This will also help prevent accidental coincidences like TestBasE.php = Bas (bioaktive substanz) + E (vitamin) 🙃

Maybe also include interface.php? Something like:

Edit:

// We don't want to discover abstract TestBase classes, traits, interface. They
// can be deprecated and will call @trigger_error() during discovery.
return
  $current->isFile() &&
  ($file_name = $current->getFilename()) &&
  substr($file_name, -4) === '.php' &&
  substr($file_name, -12) !== 'TestBase.php' &&
  substr($file_name, -9) !== 'Trait.php' &&
  substr($file_name, -13) !== 'Interface.php';
Anonymous’s picture

StatusFileSize
new3.66 KB
new3.8 KB

Status: Needs review » Needs work

The last submitted patch, 6: 2973127-6.patch, failed testing. View results

Anonymous’s picture

Status: Needs work » Needs review
StatusFileSize
new3.41 KB
new1.48 KB

Okay, let's remove extra KernelExampleTraIT check, but save 'KernelExampleTest3' to ensure, that we check correct keys.

mile23’s picture

I limited to *TestBase.php and *Trait.php because that's what @alexpott had mentioned in #28 and #29 in #2949363: Impossible to make trigger_error in some files without test fails

I'm not even sure we have any test interfaces to exclude... Does contrib?

alexpott’s picture

I think we can now skip this issue a move on doing #2296635: Evaluate performance impact of limiting test discovery to *Test.php filename suffix in a planned fashion since the bug of triggering deprecation messages when scanning all the tests is now fixed.

mile23’s picture

#2949363: Impossible to make trigger_error in some files without test fails only fixes it for run-tests.sh. This bug still exists for the test suite classes. See #2.

Anonymous’s picture

  • Interfaces in tests folder is really rare (7 in core, 20 in contrib)
  • Interfaces with trigger_error even more rare (4 in core, 3 in contrib)
  • Interfaces with trigger_error in tests folder - not found.

So this check is based only on the fact that technically the syntax allows you to do this case. Although we have not real cases :) I agree to remove, for the sake of performance.


interfaces in tests folder
#Core
core/tests/Drupal/TestSite/TestSetupInterface.php
core/tests/Drupal/Tests/Component/Plugin/Fixtures/vegetable/VegetableInterface.php
core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/FruitInterface.php
core/tests/Drupal/Tests/Core/Routing/TestRouterInterface.php
core/tests/Drupal/Tests/Core/Field/TestBaseFieldDefinitionInterface.php
core/modules/migrate/tests/src/Kernel/MigrateDumpAlterInterface.php
core/modules/config/tests/config_test/src/ConfigTestInterface.php

#Contrib
/blazy/tests/modules/blazy_test/src/BlazyFormatterTestInterface.php
/blazy/tests/modules/blazy_test/src/BlazySkinTestInterface.php
/blazy/tests/modules/blazy_test/src/Form/BlazyAdminTestInterface.php
/composer/vendor/composer/composer/tests/Composer/Test/Autoload/Fixtures/classmap/SomeInterface.php
/composer/vendor/composer/composer/tests/Composer/Test/Plugin/Mock/CapablePluginInterface.php
/conditional_fields/tests/src/FunctionalJavascript/TestCases/ConditionalFieldCheckedUncheckedInterface.php
/conditional_fields/tests/src/FunctionalJavascript/TestCases/ConditionalFieldFilledEmptyInterface.php
/conditional_fields/tests/src/FunctionalJavascript/TestCases/ConditionalFieldValueInterface.php
/courier/tests/modules/courier_test_message/src/Entity/TestMessageInterface.php
/ctools/tests/modules/ctools_wizard_test/src/ExampleConfigEntityInterface.php
/entity/tests/modules/entity_module_bundle_plugin_test/src/Plugin/BundlePluginTest/BundlePluginTestInterface.php
/graphql/tests/modules/graphql_plugin_test/src/GarageInterface.php
/heisencache/src/Heisencache/tests/MockEventSubscriberInterface.php
/mollom/tests/modules/mollom_test/src/Entity/PostInterface.php
/openapi/tests/modules/openapi_test/src/Entity/OpenApiTestEntityInterface.php
/openapi/tests/modules/openapi_test/src/Entity/OpenApiTestEntityTypeInterface.php
/plugin/tests/modules/plugin_test_helper/src/Plugin/PluginTestHelper/MockPluginInterface.php
/search_api/tests/src/Unit/TestComplexDataInterface.php
/search_api/tests/src/Unit/TestNodeInterface.php
/search_api/tests/src/Unit/TestUserInterface.php

Interfaces with trigger:

#Core
core/lib/Drupal/Core/Routing/RouteFilterInterface.php
core/modules/migrate_drupal/src/Plugin/MigrateCckFieldPluginManagerInterface.php
core/modules/migrate_drupal/src/Plugin/MigrateCckFieldInterface.php
core/lib/Drupal/Core/Routing/EnhancerInterface.php

#Contrib
entity/src/Entity/RevisionableEntityBundleInterface.php
composer/vendor/composer/composer/src/Composer/Package/LinkConstraint/LinkConstraintInterface.php
commerce/src/BundlePluginInterface.php
alexpott’s picture

I think it is fine to exclude interfaces now - eventually once we fully implement #2296635: Evaluate performance impact of limiting test discovery to *Test.php filename suffix we'll not need this but this is a good halfway step.

Anonymous’s picture

StatusFileSize
new3.16 KB
new2.58 KB

#13: Done.

alexpott’s picture

/me should have been clearer... by

exclude

I meant that excluding interfaces as done in #8 seems fine. Sorry.

Anonymous’s picture

Title: Exclude *TestBase.php and *Trait.php files from test discovery reflection » Exclude *TestBase.php, *Trait.php and *Interface.php files from test discovery reflection
Issue summary: View changes
StatusFileSize
new3.41 KB

Hah, np) Reupload #8 + updated the scope in Title/IS.

Status: Needs review » Needs work

The last submitted patch, 16: 2973127-8.patch, failed testing. View results

Anonymous’s picture

Status: Needs work » Needs review
StatusFileSize
new3.47 KB

Opps, reroll.

mile23’s picture

Issue summary: View changes

Added a CR: https://www.drupal.org/node/2974044

Updated IS for clarity.

I can't RTBC or I would. :-)

Anonymous’s picture

Issue summary: View changes

RTBC++

borisson_’s picture

Status: Needs review » Reviewed & tested by the community

RTBC based on #19 and #20 and the fact that there is sufficient test-coverage.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 18: 2973127-18.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Reviewed & tested by the community

Using run-tests.sh --list command to test this will a few popular contrib modules lying around. Good news is that core is unaffected. Interestingly the following differences are found:

diff old.txt new.txt
914d913
<  - Drupal\ds\Tests\FastTestBase
1335d1333
<  - Drupal\Tests\field_example\Functional\FieldExampleBrowserTestBase
3331d3328
<  - Drupal\Tests\rules\Unit\Integration\Event\EventTestBase
/**
 * Base test for Display Suite.
 *
 * @group ds
 */
abstract class FastTestBase extends WebTestBase {
/**
 * Class FieldExampleBrowserTestBase.
 *
 * @group field_example
 * @group examples
 */
abstract class FieldExampleBrowserTestBase extends ExamplesBrowserTestBase {
/**
 * Base class for testing Rules Event definitions.
 *
 * @group RulesEvent
 */
abstract class EventTestBase extends RulesEntityIntegrationTestBase {

So this looks great. These are abstract classes that shouldn't be listed and as a result of this change won't be. Nice one.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 18: 2973127-18.patch, failed testing. View results

mile23’s picture

Status: Needs work » Reviewed & tested by the community

@mixologic put out the fire, so we can go back to RTBC.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 18: 2973127-18.patch, failed testing. View results

Anonymous’s picture

Status: Needs work » Reviewed & tested by the community
alexpott’s picture

Category: Bug report » Task
Status: Reviewed & tested by the community » Fixed

Committed 8f92d9a and pushed to 8.6.x. Thanks!

Only committed to 8.6.x because we shouldn't add new deprecations to 8.5.x so this is really more of a task. Also I'm not sure how long this code will actually hang about about if we do #2296635: Evaluate performance impact of limiting test discovery to *Test.php filename suffix during 8.x - I think we could deprecate support for non *Test.php classes in 8.6.x and remove support in 8.7.x - so this code might only last to 8.7.x - still it is better to have it than not.

  • alexpott committed 8f92d9a on 8.6.x
    Issue #2973127 by vaplas, Mile23, alexpott: Exclude *TestBase.php, *...
mile23’s picture

Published CR.

Status: Fixed » Closed (fixed)

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