Follow-up to #697760: Replace getInfo() in tests with native phpDoc + annotations (following PHPUnit) + #2293825: Various test classes do not have a "Test" suffix

Objective

  1. ~10% of all class files that are evaluated by the test discovery are not tests (with just core).
  2. Every class is introspected + loaded persistently into memory, which increases memory/resource consumption.
  3. Limiting test discovery to *Test.php filenames would avoid needless introspection/reflection of ~130+ classes.

Tasks

  1. Change the file filter in TestDiscovery.
  2. Profile/benchmark the performance difference.
  3. Discuss, based on hard numbers.

Notes

  1. PHPUnit only discovers *Test.php files by default, so as to ignore test base classes + any other possibly existing fixtures.
  2. Following PHPUnit's (industry standard) lead would definitely improve consistency across testing frameworks. → Learn once, stay familiar.
  3. Drupal implements test suites which can load different test types under the phpunit tool. These suites inherit their file scanning abilities from TestDiscovery::scanDirectory(). So any improvement to scanDirectory() will be an improvement for our implementation of PHPUnit as well.

Counter-Arguments / Concerns

  1. The filter is applied during discovery already. A wrongly named test file is not executed and does not throw an error, because it is not found in the first place.

    This could result in "false-positive" test results, because a newly added test was not actually executed.

    Counter-Counter-Arguments:

    1. *Test.php filename suffixes are industry standard. The class is a test, so you call it a *Test. PHP developers are used to this notion already. Even Drupal core follows suit since D8 (and similarly in D7 already, just with a *TestCase suffix).
    2. Tests are exclusively authored by PHP developers. Catching mistakes like a malformed test class file/name is the job of peer-reviews.
    3. A git repository pre-commit or push hook would be able to catch such mistakes and prevent them from entering the mainline. That is a much more appropriate location and point in time for performing sanity checks.
CommentFileSizeAuthor
#61 2296635-61.patch2.73 KBHardik_Patel_12
#58 interdiff-51-58.txt1.76 KBjungle
#58 2296635-58.patch4.35 KBjungle
#57 interdiff_51-55.txt2.01 KBNeslee Canil Pinto
#55 2296635-55.patch4.39 KBNeslee Canil Pinto
#51 interdiff-2296635.txt4.22 KBdawehner
#51 2296635-51.patch4.41 KBdawehner
#49 2296635-49.patch899 bytesdawehner
#47 2296635-47.patch846 bytesdawehner
#36 interdiff-33-36.txt1.46 KBAnonymous (not verified)
#36 2296635-36.patch4.49 KBAnonymous (not verified)
#33 interdiff-28-33.txt676 bytesKingdutch
#33 2296635-33.patch5.92 KBKingdutch
#11 2296635-11.patch1.32 KBdawehner
#13 interdiff.txt0 bytesdawehner
#13 2296635-13.patch1.69 KBdawehner
#21 2296635_21.patch3.84 KBMile23
#22 2296635_22-test-only.patch1.46 KBAnonymous (not verified)
#22 unit-not-works-2296635_21-test-only.patch1.58 KBAnonymous (not verified)
#22 2296635_22.patch5.28 KBAnonymous (not verified)
#24 2296635-24-test-only.patch1.45 KBAnonymous (not verified)
#24 2296635-24.patch5.21 KBAnonymous (not verified)
#24 interdiff-22-24.txt2.39 KBAnonymous (not verified)
#28 2296635-28.patch6.08 KBKingdutch
#28 interdiff-24-28.txt1.43 KBKingdutch
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sun’s picture

Issue summary: View changes
chx’s picture

Framing this as performance is just the kind of insidiousness I expect. It's not about performance. It's about piss poor DX. It's not like the parent found 20 tests scattered among all of core that wouldn't be picked up. My suggestion is: namespace + annotation. Learn plugins once, reuse for tests.

Performance is a noble goal except when you are so fast you don't find the very thing you want to run.

sun’s picture

It's not about performance.

This issue is about test discovery performance. Let's refrain from making arbitrary claims without data points. I did phrase the issue summary accordingly already.

chx’s picture

Title: Evaluate performance impact of limiting test discovery to *Test.php filename suffix (following PHPUnit) » Remove the *Test.php suffix requirement
Issue summary: View changes
Status: Postponed » Active
Issue tags: +DX
sun’s picture

Title: Remove the *Test.php suffix requirement » Evaluate performance impact of limiting test discovery to *Test.php filename suffix (following PHPUnit)
Status: Active » Postponed
Issue tags: -DX

Sorry, but that's not the goal nor the intention of this issue. (the suggested change is the opposite of this issue)

FWIW, this issue is the result of excessive performance profiling on which I worked almost exclusively over the past weeks, which gave sufficient hints that skipping the needless processing of (hundreds of) non-test classes most likely makes a measurable and visible difference.

However, I did not benchmark/profile the situation of skipping non-test class files yet, so there are no concrete data points to argue for or against yet. Not planning to do that before the parent issue has landed, so still postponed.

tim.plunkett’s picture

Status: Postponed » Active
chx’s picture

Issue summary: View changes

Restoring the IS since my proposal is in a different issue already.

Mile23’s picture

Status: Active » Closed (won't fix)

This seems kind of settled...

Mile23’s picture

Status: Closed (won't fix) » Active

Hmm. Found a todo about this.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

dawehner’s picture

Status: Active » Needs review
Issue tags: +DX (Developer Experience)
FileSize
1.32 KB

The developer experience problem can be solved using the following patch:
It checks for every executed test, whether the filename ends with Test.php, otherwise throws an error.

Status: Needs review » Needs work

The last submitted patch, 11: 2296635-11.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
0 bytes
1.69 KB

One way of fixing is to use case insensitive regex checking.

Status: Needs review » Needs work

The last submitted patch, 13: 2296635-13.patch, failed testing.

Mile23’s picture

Version: 8.1.x-dev » 8.2.x-dev

The @todo is in the docblock for Drupal\simpletest\TestDiscovery::scanDirectory().

The patch in #13 deals with PHPUnit tests only and is probably out of scope here. Tho make another issue because it's a good idea. That said, PHPUnit tests now use scanDirectory() to find test suites.

Mile23’s picture

Title: Evaluate performance impact of limiting test discovery to *Test.php filename suffix (following PHPUnit) » Evaluate performance impact of limiting test discovery to *Test.php filename suffix
dawehner’s picture

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Mile23’s picture

Version: 8.5.x-dev » 8.6.x-dev
Issue summary: View changes
Status: Needs work » Needs review
Related issues: +#2949363: Impossible to make trigger_error in some files without test fails
FileSize
3.84 KB

#2949363: Impossible to make trigger_error in some files without test fails adds an interesting wrinkle because our test discovery system ends up loading discovered files. If a test trait has a @trigger_error(), it will trigger during test file scans, rather than test run time.

If TestDiscovery did not allow files to end with anything other than *Test.php, we wouldn't load trait files for introspection, and they could be fully deprecated. The down-side is that test files with non-standard names would be excluded, so we might get a false positive based on an ill-named test. This would not be caught by our coding standards test listener because the test file would not be discovered.

Here's a patch which does the filtering on *Test.php.

Anonymous’s picture

Let's close #2949363: Impossible to make trigger_error in some files without test fails like duplicate. Please add credit to @timmillwood and @Lendude who helped in solving the problem.

This is blocker for #2946419: Entity: Convert system functional tests to phpunit and other Simpletest ->PHPUnit issues. So change status to Major. Perhaps it is also Bug report, not Task.

Based on @Lendude's post, lets add special test for this changes. I tried made Unit tests, but it is not works, so BTB test.


Nit question:
+++ b/core/modules/simpletest/src/TestDiscovery.php
@@ -288,7 +285,10 @@ public static function scanDirectory($namespace_prefix, $path) {
-      return $current->isFile() && $current->getExtension() === 'php';
+      if ($current->isFile() && $current->getExtension() === 'php') {
+        return strpos($current->getFilename(), 'Test.php') !== FALSE;
+      }
+      return FALSE;

What about

- return $current->isFile() && $current->getExtension() === 'php';
+ return $current->isFile() && substr($current->getFilename(), -8) === 'Test.php';

Interdiff with #21 is test-only.patch.

The last submitted patch, 22: 2296635_22-test-only.patch, failed testing. View results

Anonymous’s picture

The last submitted patch, 24: 2296635-24-test-only.patch, failed testing. View results

longwave’s picture

I think this code in getTestClasses() can be simplified slightly now, as every class name in this loop should end in Test already?

      catch (MissingGroupException $e) {
        // If the class name ends in Test and is not a migrate table dump.
        if (preg_match('/Test$/', $classname) && strpos($classname, 'migrate_drupal\Tests\Table') === FALSE) {
          throw $e;
        }
        // If the class is @group annotation just skip it. Most likely it is an
        // abstract class, trait or test fixture.
        continue;
      }
dawehner’s picture

+++ b/core/modules/simpletest/src/TestDiscovery.php
@@ -288,7 +285,7 @@ public static function scanDirectory($namespace_prefix, $path) {
       }
-      return $current->isFile() && $current->getExtension() === 'php';
+      return $current->isFile() && substr($current->getFilename(), -8) === 'Test.php';
     });

We could document a line: Ensure that files end up with Test.php to replicate the phpunit built in behaviour.

Kingdutch’s picture

FileSize
1.43 KB
6.08 KB

I've simplified the catch statement of getTestClasses() as requested by #26. I've adjusted the documentation to indicate the new behaviour. Group annotations, traits or test fixtures shouldn't make it to this piece of code anymore (as they'll be filtered out by the Test.php requirement). I didn't dare remove the if statement because I wasn't certain what classes would be generated for drupal_migrate (e.g. a google search helped me find a sandbox that contained a "ContentFieldTest.php" file containing the schema for a content_field_test table).

I've also added the suggestion by dawehner from #27 with regards to the comment verbatim.

Anonymous’s picture

Looks nice! RTBC +1

dawehner’s picture

+++ b/core/modules/simpletest/src/TestDiscovery.php
@@ -177,12 +177,10 @@ public function getTestClasses($extension = NULL, array $types = []) {
-        // If the class is @group annotation just skip it. Most likely it is an
-        // abstract class, trait or test fixture.
         continue;

Isn't this still helpful information?

Kingdutch’s picture

Re #30

At the time, I reasoned that these classes would no longer make it to this part of the codebase due to the new filename restrictions.

I'm not sure that at this time I can come to the same conclusion so maybe that was too much removal indeed :/

dawehner’s picture

@Kingdutch
Let's add it back, as it makes for example the patch easier to review. Otherwise this looks rocksolid.

Kingdutch’s picture

FileSize
5.92 KB
676 bytes

Added back the text

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Thank you @Kingdutch

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/simpletest/tests/src/Traits/TestTraitDeprecationTrait.php
@@ -0,0 +1,13 @@
+trait TestTraitDeprecationTrait {

I don't really get why this is here. If it does need to be here the comments on it need to explain it. Given that the whole point of this change is to limit to *Test.php I'm not sure why we need this. Ah I see I guess it's TestDiscoveryTest - I'm not sure once we limit to 'Test.php' this is really necessary. And if it is then the trait needs an @see to the test and the test needs an @see to this thing. Personally I'd remove both.

Anonymous’s picture

Status: Needs work » Needs review
FileSize
4.49 KB
1.46 KB

The only reason is the desire to prevent regression in the future. But maybe it's too zealous insurance. After #2946419: Entity: Convert system functional tests to phpunit we will have a real deprecated trait. And we already have 6 tests that use getTestClasses() method.

So, let's remove both and boldly step forward :)

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

The only reason is the desire to prevent regression in the future. But maybe it's too zealous insurance.

Yeah, if needed you can always add the trigger_error call later.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review

I read this line in the issue summary

The filter is applied during discovery already. A wrongly named test file is not executed and does not throw an error, because it is not found in the first place.

But I couldn't find any evidence that this is true - maybe I missed something. If we're not already limiting to *Test.php then I think we need to deprecate support for tests that do not end in *Test.php first and then we can remove this in Drupal 9.

alexpott’s picture

Yeah we support other endings :(

Drupal test run
---------------

Tests to be run:
  - \Drupal\Tests\simpletest\Unit\TestDiscoveryTests

Test run started:
  Monday, May 7, 2018 - 12:40

Test summary
------------

Drupal\Tests\simpletest\Unit\TestDiscoveryTests               26 passes

Test run duration: 1 sec
./vendor/bin/phpunit -c ./core core/modules/simpletest/tests/src/Unit/TestDiscoveryTests.php
PHPUnit 6.5.8 by Sebastian Bergmann and contributors.

Testing Drupal\Tests\simpletest\Unit\TestDiscoveryTests
..........................                                        26 / 26 (100%)

Time: 510 ms, Memory: 8.00MB

Also the argument about what PHPUnit supports is not really correct - it'll run any file you tell it to ^^

dawehner’s picture

Also the argument about what PHPUnit supports is not really correct - it'll run any file you tell it to ^^

Does it run this also when we just execute the testsuite?

alexpott’s picture

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

Testing
..........................                                        26 / 26 (100%)

Time: 14.79 seconds, Memory: 792.00MB

OK (26 tests, 29 assertions)

Remaining deprecation notices (1)

  1x: Drupal\taxonomy\Tests\TaxonomyTestTrait is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\taxonomy\Functional\TaxonomyTestTrait
    1x in KernelTestSuite::suite from Drupal\Tests\TestSuites

Yep.

Mile23’s picture

OK, then we still have the problem of reflection against traits that trigger deprecation errors during discovery: #2949363-23: Impossible to make trigger_error in some files without test fails

alexpott’s picture

Let's do this in steps. I think given that tests are meant to be run we could make an argument to deprecate this in 8.6.x and remove support in 8.7.x. This would need release manager support. But at least that would give people time to fix any tests.

The problem is if we remove support we have no way to inform people because we're just no longer scanning the files.

Anonymous’s picture

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

alexpott’s picture

This is still a very good idea! Because we do this we caused https://www.drupal.org/project/drupal/issues/3026414#comment-12940455

dawehner’s picture

Just seeing how much would fail if we throw a deprecation.

Status: Needs review » Needs work

The last submitted patch, 47: 2296635-47.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

dawehner’s picture

Status: Needs work » Needs review
FileSize
899 bytes

This time it actually checks whether a valid Test has an invalid path.

Status: Needs review » Needs work

The last submitted patch, 49: 2296635-49.patch, failed testing. View results

dawehner’s picture

Status: Needs work » Needs review
FileSize
4.41 KB
4.22 KB

This should rename the tests which are wrongly named in Drupal core.

Mile23’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record
+++ b/core/modules/simpletest/src/TestDiscovery.php
@@ -175,6 +175,11 @@ public function getTestClasses($extension = NULL, array $types = []) {
+          @trigger_error($pathname, E_USER_DEPRECATED);
+          @trigger_error('Naming a test without a Test.php suffix is deprecated in Drupal 8.7 and will be removed before Drupal 9.0.x. Use a Test.php suffix instead.', E_USER_DEPRECATED);

"...will not be allowed in Drupal 9.0.0."

Also add $pathname to the deprecation message so there's only one @trigger_error().

This is the closest change record I could find but it doesn't say to use Test.php suffix for your file: https://www.drupal.org/node/1543796

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Neslee Canil Pinto’s picture

Status: Needs work » Needs review
FileSize
4.39 KB
jungle’s picture

@Neslee Canil Pinto, thanks for working on this! Would be great to attach an interdiff, see https://www.drupal.org/documentation/git/interdiff

Neslee Canil Pinto’s picture

FileSize
2.01 KB
jungle’s picture

#55 did unexpected change. And did not address #52

+++ b/core/modules/node/tests/src/FunctionalJavascript/SettingSummariesContentTypeTest.php
@@ -9,7 +9,7 @@
-class TestSettingSummariesContentTypeTest extends WebDriverTestBase {
+class SettingSummariesContentTypeTest extends WebDriverTestBase {

But #55 spotted the wrong renaming from my understanding.

Orginally, it's TestSettingSummariesContentType.php. In #51 it's renamed to TestSettingSummariesContentTypeTest.php, (Test append, but The leading Test still there). #55 renamed it to SettingSummariesContentTypeTest.php by removing the leading Test.

Instead of working upon #55, this patch started from #51. Made two changes.

  1. Addressed #52
  2. Rename the file TestSettingSummariesContentType.php to SettingSummariesContentTypeTest.php and renamed the class as well.
jungle’s picture

Issue tags: -Needs change record

A simple Change Record added https://www.drupal.org/node/3123888

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Hardik_Patel_12’s picture

jungle’s picture

Version: 9.1.x-dev » 8.9.x-dev
+++ b/core/modules/simpletest/src/TestDiscovery.php
@@ -96,6 +96,10 @@ public function getTestClasses($extension = NULL, array $types = []) {
+        if (substr($pathname, -strlen('Test.php')) !== 'Test.php') {
+          @trigger_error("$pathname, naming a test without a Test.php suffix is deprecated in Drupal 8.7 and will not be allowed in Drupal 9.0.0. Use a Test.php suffix instead.", E_USER_DEPRECATED);

I do not think the re-roll in #61 is unnecessary, changing back the target branch to 8.9.x

jungle’s picture

Component: simpletest.module » phpunit
Status: Needs review » Needs work

The target component should be PHPUnit, I guess. And #58 only deprecated/@trigger_error for simpletest tests, not all types of test files covered.

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.