Problem/Motivation
tl;dr
Currently, if you run core's phpunit-based tests using phpunit, it will find and load traits for tests in contrib modules. If you do the same thing using TestDiscovery, it won't. That's because TestDiscovery currently only allows specific namespaces per module per test type, without allowing autoloading of the whole of the test namespace. So for instance if you have a Unit test and a Functional test and you define a trait for both of them to use, then TestDiscovery won't allow for autoloading that trait.
That's the problem we're trying to solve in this issue, in harmony with the principles in #123.
The solution is that we have TestDiscovery register the whole test namespace for autoloading, which creates a new problem: This allows autoloading of tests which don't fit into the categories core knows how to run.
My early solution to that problem was to throw an exception. @Torenware kept the exception, but added a category for 'Unclassified' that could be used for non-Core modules; the exceptions enforced the new set-up for Core, but not for Contrib. @alexpott added a test suite for 'unclassified', which puts some edges on where these unclassified tests should be in the file system; this approach treats Core and Contrib the same.
While factoring a complicated set of mocks for the file_example module in Examples, I've discovered that run-tests.sh cannot find a trait that is in the
Drupal\Tests when the trait is used in a PHPUnit test. The same tests run fine if run directly via ../vendor/bin/phpunit.
I've put together a demo that shows the problem. The test module lays out its files as so:
test_traits_in_tests/
├── src
│ └── NotTestTrait.php
├── test_traits_in_tests.info.yml
├── test_traits_in_tests.module
└── tests
└── src
├── TestTrait.php
└── Unit
├── AnotherTrivialTest.php
└── TrivialTest.php
TrivialTest uses TestTrait, and AnotherTrivialTest uses NotTestTrait. Except for their namespacing and use statements the two tests and the two traits are identical.
Running phpunit from the command line:
vagrant@drupalvm3:/var/www/drupal/core$ ../vendor/bin/phpunit ../modules/custom/test_traits_in_tests/ PHPUnit 4.8.11 by Sebastian Bergmann and contributors. .. Time: 23.53 seconds, Memory: 6.75Mb OK (2 tests, 2 assertions)
Using run-test.sh, however, TrivialTest crashes:
vagrant@drupalvm3:/var/www/drupal/core$ php scripts/run-tests.sh --url http://192.168.88.42/drupal/ --module test_traits_in_tests
Drupal test run
---------------
Tests to be run:
- Drupal\Tests\test_traits_in_tests\Unit\AnotherTrivialTest
- Drupal\Tests\test_traits_in_tests\Unit\TrivialTest
Test run started:
Saturday, October 31, 2015 - 22:04
Test summary
------------
Drupal\Tests\test_traits_in_tests\Unit\AnotherTrivialTest 1 passes
PHP Fatal error: Trait 'Drupal\Tests\test_traits_in_tests\TestTrait' not found in /var/www/drupal/modules/custom/test_traits_in_tests/tests/src/Unit/TrivialTest.php on line 22
PHP Stack trace:
PHP 1. {main}() /var/www/drupal/core/scripts/run-tests.sh:0
PHP 2. simpletest_script_run_one_test() /var/www/drupal/core/scripts/run-tests.sh:42
PHP 3. spl_autoload_call() /var/www/drupal/core/scripts/run-tests.sh:649
PHP 4. Symfony\Component\ClassLoader\ApcClassLoader->loadClass() /var/www/drupal/core/scripts/run-tests.sh:0
PHP 5. require() /var/www/drupal/vendor/symfony/class-loader/ApcClassLoader.php:114
Fatal error: Trait 'Drupal\Tests\test_traits_in_tests\TestTrait' not found in /var/www/drupal/modules/custom/test_traits_in_tests/tests/src/Unit/TrivialTest.php on line 22
Since factoring tests is a good thing, we ought to support this.
Proposed resolution
Currently our bootstrap.php for PHPunit autoloads from tests/src whereas simpltest's TestDiscovery autoloads from tests/src/FunctionalJavascript, tests/src/Functional, tests/src/Kernel, and tests/src/Unit. In order to support traits anywhere in tests/src we need to change TestDiscovery to work the same way as PHPunit's bootstrap.php. Once TestDiscovery can autoload from tests/src we have an additional issue - tests outside the 4 testsuites will be discovered. To handle this we introduce a new namespace called Traits.
We adopt a policy where test traits belong in tests/src/Traits, and this namespace is always autoloaded along with any of the test suite namespaces.
- PHPUnit's test runner, run-tests.sh and the Simpletest UI should all work in as similar a way as possible.
- It has to be possible to only run tests of a certain type. Lumping unit, functional and functional javascript tests together is a bad idea because they have different test infrastructure requirements.
- Users should be able to get all a module's tests to run with run-test.sh --module MODULE_NAME
- Running with run-test.sh --directory MODULE_DIRECTORY and ./vendor/bin/phpunit -c ./core/phpunit.xml.dist MODULE_DIRECTORY should result in running all the same PHPUnit based tests. (run-tests should also run the simpletest tests) These should also be the same tests when using the --module option.
- Running with ./vendor/bin/phpunit -c ./core/phpunit.xml.dist --filter MODULE_NAME should also run all the module's tests
See #198 for more discussion of the solution.
Remaining tasks
User interface changes
None.
API changes
Trait classes must be moved to the Traits namespace.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #11 | trait-discovery-issue-2605664-v2.patch | 2.53 KB | Torenware |
| #43 | trait-discovery-issue-2605664-v3.patch | 2.81 KB | Torenware |
| #44 | interdiff-2605664-1.txt | 2.59 KB | Torenware |
| #52 | 2605664_52.patch | 12.35 KB | mile23 |
| #52 | interdiff.txt | 10.18 KB | mile23 |
Comments
Comment #2
Torenware commentedThe demo code is enclosed in the patch. This will of course fail the testbot, since it is based off run-tests.sh. It works using phpunit directly.
Comment #3
Torenware commentedAfter a bit of searching, it turns out that the problem is in
\Drupal\simpletest\TestDiscovery::registerTestNamespaces():TestDiscovery::registerTestNamespaces() returns $this->testNameSpaces, and is doing two things. First, it's used to discover tests for the test runner to run. Second, it's adding module namespaces.
I'm not sure what the rationale is for restricting PHPUnit tests to the the Unit, Kernel and Functional subdirectories. But let's assume there's a good reason for this, and that tests in other directories should not be discovered. Still, it's reasonable that other assets get discovered in the tests/src directory for a module. So we should probably allow these classes to get loaded, as so:
Comment #4
Torenware commentedThis turns out to be an "autoloader hell" issue. PHPUnit's bootstrap.php file will find the needed files. But run-tests.sh does not have the needed configuraiton to call
class_existsonTrivialTest; the autoloader does a require on the class, discovers it cannot resolveTestTrait, and the script dies with a fatal error.I'm at a loss as to how you'd want to change run-tests.sh's autoloader and PSR-4 code so that the trait is discoverable. It can be found if we put it in the Tests\module\Unit or Tests\module\Kernel directory. The trait will also be found if the trait is no in the Drupal\Tests namespace. But otherwise, the damn thing will cause a fatal.
Comment #5
Torenware commentedAdding #2470661: Port "Flag a [entity type]" Rules action to 8.x as related.
This really does need to get looked at and fixed. Issues with running phpunit tests under run-tests.sh is a time sink for Contrib.
Comment #6
MixologicTheres a couple things here, as mentioned in another issue elsewhere: #2607866: [meta] DrupalCI/SimpletestUI/run-tests.sh/phpunit not in harmony .
We're not at all attached to run-tests.sh. But whatever it gets replaced with has to offer the same functionality, and phpunit does not currently do that.
Ideally we fix phpunit upstream, as we're not bound to run-tests.sh.
Comment #7
Torenware commented@Mixologic: that's an interesting thread. Clearly neither run-tests.sh and native phpunit can run everything. And it looks like Sebastian Bergman is a bit overwhelmed with his bug list; it might be a while until somebody can figure how to deal with seg fault issues.
We might, at least, be able to widen the set of directories for test discovery, and as a distinct issue, the list of directories the autoloader knows about. The latter needs to be a superset of the former.
My problem appears to occur because the set up of the autoloader for run-tests.sh is too narrow. Calling class_exists on a test class requires not only that run-tests.sh be able to resolve the test's class; it needs to be able resolve any of the classes that the test depends upon. Right now, the code in run-tests.sh and simpletest.module keeps discovery and autoloading very tightly linked. This is certainly a problem. I spent a long day playing with the autoloading code, and I appreciate how difficult it is to debug, and what kind of problems can come up if you change it. But I suspect there are enough people available that understand the autoloading process well enough that if code works running in an application, or in phpunit, it can be made to work in run-test.sh, if only be making the autoload set up a bit more like that of Core itself.
The argument on how best to handle discovery is "above my paygrade". But at least the tests that are discovered should run without any error that's specific to the test runner.
Comment #8
Torenware commentedI have a working fix.
The problem with the current code is that by attempting to prescribe "best practices" for setting up PHPUnit tests, Core over-restricts the directories available to the autoloader in the tests/src hierarchy. This patch removes this limitation. If it's a class or trait in test/src, it will be discovered. This does not affect tests that are currently discovered, and it will also discover tests in other directories (such as tests/src/Integration/, which is used by Rules module tests).
I'm not in favor of the "best practices" restriction -- I think that Contrib should be free to set up their tests as they see appropriate -- but it does make run-tests.sh run the same tests that ../vendor/bin/phpunit will run.
Comment #9
Torenware commentedThe test runner is misreporting this as a pass:
I may understand why this is; some of the autoloader related code in TestDiscovery.php may behave differently when run-tests.sh is called with "--class" than when called in other modes. Calling "--class 'Drupal\Tests\simpletest\Unit\TraitAccessTest'" gets a pass; calling "--module simpletest" shows the same test failing.
Comment #10
tim.plunkettSeems related to #2580293: Patch having test with "PHP Fatal error" is marked as PASSED
Comment #11
Torenware commentedOK, yet simpler explanation: bad patch that had an extra file.
Try again....
Comment #12
Torenware commented@tim.plunkett -- separate issue, since I'm getting the feeling that there are a lot of FAILs filtering through irrespective of this patch. Testing of --module simpletest even against my slightly stale copy of core gives 1 fail, and it passes. With my new patch, it turns out that there are a few more (why I'm not sure yet), and yet, PASS.
We'll see if we get another pass. But I'm starting to wonder what else our friend TestBot's been up to when we're not looking :-)
Comment #13
Torenware commentedThis is a real PASS this time.
One interesting thing about this patch: the test runner checks all the tests it can see. run-tests.sh before the patch couldn't see PHPUnit tests that were not in tests/src/Unit, tests/src/Kernel, and tests/src/Functional. With this patch, these tests will now be visible to run-tests.sh, and people who have tests that do not have the @group annotation will now bother run-test.sh. This won't affect the test bot, but people doing various things in contrib will see this. So this patch should come with a change record.
Rules in particular has a problem with this. I'm going put up a patch to handle that, but it's possible that other heavy users of phpunit (especially folks that have shifted development to github, as the Rules team has) will get surprised when tests that use run-tests.sh misbehave.
Comment #14
dawehnerThere is another bit up there:
which could be simplified as part of that. Do you think this is worth to change?
Comment #15
MixologicPrior to https://www.drupal.org/node/2232861, the code was exactly as you have it in this patch, so it might be worth investigating why the decision was made to split them up into "unit" and "functional" namespaces, and later add Kernel namespace (https://www.drupal.org/node/2304461).
Comment #17
dawehnerThis was added as part of #2232861-175: Create BrowserTestBase for web-testing on top of Mink, it seemed to be just about being explicit, which well, is a good reason, but should not block us here to fix the problem itself.
Comment #19
Mixologicbad patch in core caused race conditions. This might have been affected.
Comment #23
Torenware commentedRequeuing since this appears to be a bad robot problem (test never actually started).
Comment #26
mile23Patch still applies.
Halfway blocker for #2102651: Port file_example module to Drupal 8.
The last few CI runs have passed, so I'm setting RTBC and the testbot is welcome to shoot me down.
Comment #29
mile23Hid some files, re-running #11.
Comment #30
mile23Comment #31
mile23So as it turns out, I can't repro the error.
Here's a test-only patch derived from #11. Maybe the testbot has a different opinion.
Comment #33
mile23Uhm, of course I can't repro because I'm using a different test runner. :-)
I blame Thanksgiving tryptophan.
Setting back to RTBC.
Comment #36
mile23Hiding the test-only patch.
Comment #37
mile23See also: #2624926: Refactor run-tests.sh for Console component.
Comment #38
mile23Comment #39
Torenware commentedComment #40
Torenware commentedComment #41
alexpottThis is not a bug - it was a design de
No longer multiple namespaces.
This text could be written in a more helpful manner. Also this should be in the docblock for the trait not the @file documentation.
The string is not random.
"in its own namespace" does not make sense. It could already find traits if it was in the Drupal\Tests\simpletest\Unit namespace.
Comment #42
Torenware commentedAlex -- thanks for looking at this. I'll update the patch later today.
Comment #43
Torenware commentedComment #44
Torenware commentedComment #45
alexpottOne reason I've not committed this yet is that it removes the strictness of putting each test type in a defined folder. That said core's structure might not work for some modules.
Comment #46
mile23That part of
TestDiscoveryonly registers namespaces. It doesn't add to or limit discovery. We need the PSR-4 for discovering any valid *class* which might support the tests we eventually end up running.Basically the behavior you're talking about works by accident, while diminishing the ability to write tests.
We'd want to refactor
TestDiscovery::isUnitTest()such that it lets us examine classnames and approve them based on their namespace.Should that happen here or in a follow-up?
Also, I'm not really sure why we need to have this namespace limitation. It seems like the test runner could determine the type of test in a bunch of other ways that have more to do with functionality over naming convention. Using the naming convention we'd have false passes on patches whose test never gets run.
Comment #47
mile23Comment #48
Torenware commented@Mile23 -- the limitation of namespace is a side effect of the way the code is organized.
The code that @alexpotts points out is used to define where tests can be found. But it also, as a side effect, is also used to set up the class loader. So even if code is not a test (i.e., a trait, or a base class, or an interface), as the code was originally written, if tests can only be in \Drupal\Tests\module\{Kernel|Unit|Functional}, then no interface, base class, or trait not in one of those 3 directories will ever be found. This means you either need to put a shared class or trait into one of those 3 arbitrary directories in order to use it in your own module. It also means thats any utility class or trait used with a custom plugin type plugin type will not be in the places where implementers of that plugin will look. So the shared asset needs to be in some arbitrary place, or in a place that is frankly silly.
This already is having consequences. I've been following an issue with the Flag module where they're trying to implement a Rules plugin. That's easy enough. But they've been unable to get a PHPUnit test working. And short of doing cut-and-pasted with several of Rules base classes, they won't succeed, either, unless they decide to quit using run-tests.sh and go to some other non-Drupal-hosted test solution. I'd much rather factor the current Rules test code so it can be easily and reliably be shared. This is turning out to be neither intuitive or efficient with the test runner's current behavior.
I understand that logical and uniform deployment of tests is important. But I think that logical source hierarchies that are intuitive to developers, well factored code, and code sharing between modules is considerably more important.
It's possible to separate out the class loading from test discovery, but it requires touching a lot more scary code than the patch here does, since there are shared routines that cannot be shared if we make this change. But if doing that will get this into the tree, I'll do it. Because it very much needs to be done.
What I did -- simply relax the restriction -- is in my opinion a safer and more predictable solution.
If work needs to be done here, what does that work need to do in order to pass muster?
Comment #49
Torenware commentedYou would think. But if you'll notice, the same data structure is accessed in a couple other places in TestDiscovery. In effect, the code only allows you to see anything (again, base class, interface or trait) under \Drupal\Tests\module where a Unit, Kernel, or Functional test can reside. Because if it is not in those directories, the class loader will kill the test runner with a FATAL.
The underlying problem here is that TestDiscovery::registerTestNamespaces() is used for two distinct purposes. First, it is used to initialize the class loader (via simpletest_classloader_register() in simpletest.module), and it's used to come up with lists of tests in places like TestDiscovery::findAllClassFiles(), TestDiscovery::getTestClasses(), and simpletest_test_get_all() (called in run-tests.sh).
So this stuff calls each other fairly promiscuously. To get what Alex wants here, we want for the latter type of calls (simpletest_test_get_all(), TestDiscovery::findAllClassFiles()) to allow only the approved directories. To allow us to factor PHPUnit tests the way that SimpleTest already does, and for us to trust the test runner will behave something like PHPUnit's own test runner, we need simpletest_classloader_register() to set up the class loader to see more under \Drupal\Tests\module that it currently can see. Probably the whole hierarchy, which is what my patch does.
The underlying problem is we expect TestDiscovery::registerTestNamespaces() to do two slightly contradictory things. This is leading to subtle bugs in the test runner, as my experience with factoring the tests in the File example ended up showing us.
Comment #50
mile23So then at one extreme we want #2624926: Refactor run-tests.sh for Console component. :-)
At the other, we change those various methods in situ (without refactoring) to be more like
core/tests/bootstrap.php, which scans the file system for test classes independent of autoloading. Speaking of which: #2301873: Use ExtensionDiscovery to register test namespaces for running PHPUnit :-)Comment #51
mile23Working on the minor refactoring mentioned above.
Comment #52
mile23So here's the change.
The major changes are that I've introduced
isValidTestNamespace(), which necessitated changinggetExtensions()to store anExtensionDiscoveryobject as a class property so we don't re-scan all the time.I also separated concerns away from
registerTestNamespaces(), addinggetTestNamespaces()which only does the discovery and not the registration. I did this in hopes of writing a test, but we can't really test it just yet. See below. This change is a bit out of scope, so we can kick it out.After adding some error_log scaffolding I see this:
[23-Dec-2015 01:17:43 Europe/Berlin] filtered out: Drupal\Tests\simpletest\Traits\TestTraitWhich is exactly what we want, assuming all tests are where they should be.
I'd write tests, but that requires two other changes:
Comment #53
mile23Comment #55
mile23That failure is in a test of
getTestInfo(), and it looks like this when you run it the right way. :-)Drupal\Tests\simpletest\Functional\BrowserTestBaseTestwas formerly discovered and added to PSR-4 during a scan of unit tests, but its@groupofsimpletestwas retained.However, since the behavior of
getTestInfo()is to force all PHPUnit-based tests to the PHPUnit group, the addition of strictness toisUnitTest()means thatBrowserTestBaseTestis marked as PHPUnit. BrowserTestBaseTest ultimately extends from\PHPUnit_Framework_TestCase, so it is, but I'm pretty sure it's a special case that shouldn't count as a unit test.Additionally: There are no classes present in core which extend
BrowserTestBase(other thanBrowserTestBaseTest), so I'm wondering why it's there. I'm pretty sure it's part of the mink initiative, but the docblock is no help.So: Should namespaces like
Drupal\Tests\simpletest\Functional\be unit tests? If no, then we move this check fromisUnitTest()toisValidTestNamespace(). If yes, we change the test.Comment #56
mile23Bumping to 8.1.x, hopefully for cherry-picking to 8.0.x, because this is a bug.
Reverted the refactoring of
registerTestNamespaces()since it's really out of scope.Added a test for
isUnitTest().\Functional\tests are now a special case ofisValidTestNamespace(). Changes related to it should be easy to implement.TestInfoParsingTestpasses locally....AND
TraitAccessTestpasses. :-)Still seeing #2476139: Fix UI test fail in SimpleTestBrowserTest locally, and we don't have more tests because #2605654: Modify Drupal\Core\Extension\ExtensionDiscovery to allow for scanning multiple file systems, enabling vfsStream testing
Comment #57
mile23Comment #58
mile23Comment #60
alexpottShould be
$this->assertSame('stuff', $this->getStuff);Comment #61
alexpott@file is no longer required.
Comment #62
dawehnerThis would also need functional and FunctionalJavascript wouldn't it? Actually I think this sounds really similar to part of what
::getPhpunitTestSuite()is doing now.That documentation is bad ... this is not a unit test ... its rather a phpunit based test. I know the method name is wrong, but at least the documenation should make that clearer
Don't we need functional and functionalJavascript in there as well?
could we inject the drupal root as parameter?
let's get rid of that
Comment #63
mile23At the time I wrote that, there was *no* documentation mapping namespaces to test types. I'm not sure there is at this point, either.
Shows how old the patch is.
BTW, if you're in NOLA, find Torenware. :-)
Comment #64
mile23Re-roll of #56 for 8.2.x. Addressing changes when it's green. Or red. :-)
Comment #66
mile23Hello: #2721355: MissingDependentModuleUnitTest has the wrong namespace
That error leads to a new failure that was previously undiscovered. Therefore: TestDiscovery should burp when it finds a mismatch.
Comment #67
mile23This is mostly re-done work.
We add
TestDiscovery::getTestSuite($classname), which is similar to the existinggetPhpUnitTestSuite().getTestSuite()throwsTestInWrongNamespaceExceptionif the given classname does not fit into one of the established test suite categories such as PHPUnit-Kernel or Simpletest.I had to disable the devel module to test this, because it found
Drupal\Tests\devel_generate\DevelGenerateManagerTestand threw the exception. :-)Lots of testing in the patch. I found
TestDiscoveryTest::testGetPhpunitTestSuite()hidden onTestTestDiscovery, which is inTestInfoParsingTest.php...simpletest group tests pass locally. Let's see the testbot dash my hopes.
Comment #68
dawehnerThis is looking much better now!! Thanks a ton! Here is a couple of small feedback and nitpicks.
unrelated chage
I really like this change. Less code to maintain, which existed for no good reason
nitpick: unrelated changes
Nice variable name!
Can we suggest possible other places instead as part of the exception message?
Is it just me that caching this here is also a premature optimization and unneeded as part of this patch?
All that can happily live on the existing unit test, can't it?
Comment #69
mile23Thanks for the review.
Addressing most of the items in #68.
#68.5: Added a link to the PHPUnit test page. I couldn't find a better set of docs.
#68.6: The optimization was present in the old patch, but yah, no worries. Removed.
#68.7: You'd *think*, wouldn't you? :-) Well, the test method itself isn't on the test class, it's on the mock class so it *never runs.* Furthermore, that test class is named
TestInfoParsingTest, and we're testingTestDiscovery. I pondered foldingTestInfoParsingTest.phpintoTestDiscoveryTest.php, because that's where it really should be. ButTestDiscoveryTestneeds to be a kernel test, whereasTestInfoParsingTestis a unit test, so I decided to keep them separate. We really should rename it, but again: Out of scope unless someone tells me it isn't.Comment #70
mile23Oops forgot interdiff.
Comment #71
Torenware commentedSlight tweak to include Alex's #60.
Near done. Anybody wanna poke this with a fork?
Comment #73
Torenware commentedError looks like a transient CI error; logs do not indicate that any tests at all were executed. Trying again.
Comment #74
Torenware commentedComment #75
Torenware commentedComment #76
mile23Double your testing, double your fun.
Comment #79
Torenware commentedLocal testing suggests this is a new but related problem, with some of the new code in the patch. I get the following trace (not visible in the testbot console):
exception 'ReflectionException' with message 'Class Drupal\Tests\simpletest\TestTrait does not exist' in /var/www/drupalvm/drupal/core/modules/simpletest/src/TestDiscovery.php:329 Stack trace: #0 /var/www/drupalvm/drupal/core/modules/simpletest/src/TestDiscovery.php(329): ReflectionClass->__construct('Drupal\\Tests\\si...') #1 /var/www/drupalvm/drupal/core/modules/simpletest/src/TestDiscovery.php(178): Drupal\simpletest\TestDiscovery::getTestInfo('Drupal\\Tests\\si...', '') #2 /var/www/drupalvm/drupal/core/modules/simpletest/simpletest.module(511): Drupal\simpletest\TestDiscovery->getTestClasses('simpletest', Array) #3 /var/www/drupalvm/drupal/core/scripts/run-tests.sh(921): simpletest_test_get_all('simpletest', Array) #4 /var/www/drupalvm/drupal/core/scripts/run-tests.sh(105): simpletest_script_get_test_list()The original test in the issue (TraitAccessTest) runs fine, so the other changes in the discovery code are causing trouble. I'll track this down.
Comment #80
Torenware commentedOK, Stupid Developer Error (SDE). I added a spurious file to my patch, which screwed with test discovery.
Removed. Let's see if we're happier here.
Comment #82
Torenware commentedMinor tweak, per Alex' notes in #60.
Comment #83
Torenware commentedOne note on current behavior of the patch that we may want to check: discovery now throws an exception if a test is not in a known "suite". This does not affect core, but is going to affect a fair number of contrib modules. Here's one from devel_generate:
$ php scripts/run-tests.sh --list Available test groups & classes ------------------------------- exception 'Drupal\simpletest\Exception\TestInWrongNamespaceException' with message ' -- Test class Drupal\Tests\devel_generate\DevelGenerateManagerTest cannot be assigned to a test suite. See https://www.drupal.org/phpunit. -- ' in /var/www/drupalvm/drupal/core/modules/simpletest/src/TestDiscovery.php:466 Stack trace: #0 /var/www/drupalvm/drupal/core/modules/simpletest/src/TestDiscovery.php(357): Drupal\simpletest\TestDiscovery::getTestSuite('Drupal\\Tests\\de...') #1 /var/www/drupalvm/drupal/core/modules/simpletest/src/TestDiscovery.php(178): Drupal\simpletest\TestDiscovery::getTestInfo('Drupal\\Tests\\de...', '/**\n * @coversD...') #2 /var/www/drupalvm/drupal/core/modules/simpletest/simpletest.module(511): Drupal\simpletest\TestDiscovery->getTestClasses(NULL, Array) #3 /var/www/drupalvm/drupal/core/scripts/run-tests.sh(69): simpletest_test_get_all(NULL)We may want to allow for a "Unspecified" category as well, since throwing an exception when any non-compliant module is present probably is too unforgiving of behavior. Not everybody will adopt Core's standards for their test suites. The current code will cause trouble even if these modules are not installed.
Comment #84
Torenware commentedHere's a patch that makes test suites optional (i.e., no exception for Contrib in this case).
Comment #86
Torenware commentedI'm having difficulty getting a modified test to work here. Withdrawing the last patch.
Comment #87
Torenware commentedTests now pass again; the old provided data for "bad classes" no longer threw in the module case, so I'm no longer passing those cases.
Comment #89
Torenware commentedThe failed test here is TestDiscoverTest::testGetTestClassesWrongNamespace(). Underhood, the test checks if the path \Drupal\tests\Unfunctional\FunctionalExampleTest throws a TestInWrongNamespaceException. It fails because the patch allows an arbitrary test suite (in this case, "Unfunctional"). Which the patch probably *should* allow.
There are two possible ways to make this pass:
My inclination is to do the first of the two, since this would allow Alex to get consistency in Core w/o imposing the same solution on Contrib.
Opinions?
Comment #90
mile23The test runner needs a suite in order to run the test, regardless of core or contrib.
Contrib either has to impose the rules or we have to return a suite of something like 'unknown.'
So: If it's a core test we have to impose the rules. If it's not, then we don't.
The way to do that is add another static method called
fileIsACoreTest()or something similar. In this method, we have a hard-coded list of core modules to compare against. We make a magic regex to extract the module name from the file path.getTestInfo()will call this and add a member to$infonamed something like$info['test_is_core'] = TRUE. Then we pass this bool to theget*Suite()methods in order to break the rules selectively.That means we have to inject the file path into
getTestInfo(). Some callers ofgetTestInfo()don't care about this, such as the test form builder, so it's an optional argument.At least that's what I'd do. Unassign yourself if you'd rather I do it.
I mean, not everyone likes building a ship in a bottle. :-)
Comment #91
Torenware commentedI'm working right now on exactly those lines. There are a couple of technical problems, however.
First, most of the methods you need to touch in TestDiscovery are static methods, which is a PITA for both injection and for mocking. This includes TestDiscovery::getTestInfo().
Second, if you look carefully at TestDiscoveryTest:: testGetTestClassesWrongNamespace(), you see it does its test magic using a vfs: test file system. In general, such a file system will NEVER be under DRUPAL_ROOT.
I'm not certain if the methods in question really need to be static; if they aren't, I can inject services that will let me mock what I need. The static classes are used fairly locally, so it may be possible. But I'm still experimenting with this.
I'll grab the issue, but if you have any ideas here, by all means, give it a look.
Comment #92
Torenware commented@Mile23: solving this requires us to make TestDiscovery::getTestInfo() non-static. It's definitely doable, but it touches a number of files:
simpletest.module
TestDiscovery.php
SimpletestResultsForm.php
TestInfoParsingTest.php
run-tests.sh
I don't think that this is at all hard to do (hell, dependency injection is good exactly because it makes global objects, like this method, go away). The issue is whether we can get in a patch that touches these things.
I'm going to give it a try anyway, but I'd love your opinion on this one.
Comment #93
Torenware commentedI've gotten a working version of the mocks.
The code needs factoring and needs to get run through the style checkers, but I'm posting this so the testbot gets a taste of it. I've turned a couple of static methods in TestDiscovery into member methods, to make it easier to mock some of the behavior we need to test.
We can now distinguish between tests in core and tests not in core, which will allow the flexibility needed for contrib while allowing core to enforce standards for its tests.
Comment #95
Torenware commentedFixed a couple of typos that screwed up some other tests. I'm not sure this run will work either -- there appears to be problems accessing some files in lib/Drupal/Tests/ -- but at least the output will be cleaner.
LIkely problem: SimpleTestBrowserTest can't access BrowserTestBaseTest. The error looks like this when SimpleTest tries to run BrowserTestBaseTest:
Drupal\Tests\simpletest\Functional\BrowserTestBaseTest::testGoTo PHPUnit_Framework_Exception: sh: 1: : Permission denied
Other BrowserTestBaseTest.php 25 Drupal\Tests\simpletest\Functional\BrowserTestBaseTest->testGoTo()
The test runs successfully in phpunit, so it's another damn TestDiscovery issue, looks like.
Anyone have any ideas on this one?
Comment #96
Torenware commentedI'm gonna have to figure out why it ran successfully here, and not on my local install.
I'll clean up and factor the patch, in the hope that nothing crazy happens next time.
Comment #97
Torenware commentedThis should be reasonably formatted, and a bit factored as well. This is a good patch to review.
Comment #98
mile23Looks pretty good except for a few things.
The reason this is static is: http://cgit.drupalcode.org/drupal/tree/core/scripts/run-tests.sh#n1069
So either we leave it static, or we shave the yak in run-tests.sh. Let's leave it static for now.
Boo DRUPAL_ROOT. :-)
I'm going to assign this to myself and have a go.
Comment #99
mile23Oops I didn't make it this far down the patch. :-)
I'm pretty sure we don't want to rely on the service container in run-tests.sh. But we're already relying on simpletest being enabled, so we're also relying on the services that come with it, and that can enable it.
Comment #100
Torenware commentedGaaak, missed one. No, I need it non-static, since it's what allows us to test the core/non-core feature. So that bad boy's gonna go under the razor. The yak's going to need to be hairless.
As for DRUPAL_ROOT: by all means, s'il vous plaît.
Comment #101
Torenware commentedThe main issue here is testability. Static member functions won't mock AFAIK. So we need to get a test discovery object over to run-tests.sh somehow, and pulling in a service seems to be the simplest way to do it. In any case, you'll find that \Drupal::service() is already getting called in run-tests.sh, probably for similar reasons.
Comment #102
Torenware commentedOK, I already clipped the beast. So once you've removed the DRUPAL_ROOT reference, we're about there.
Another thing to look at, if you would. Look at the implementation of isCoreTest(). The technique I'm using here is pretty simple, and I'm not convinced it's adequate generally. The vfsStream case, for example, gets passed to us as a stream reference. This isn't a problem, since we can reasonably assume that the core/ directory is not being implemented on a test file system (and to test the patch, we can mock isCoreTest). We can also assume that, I think, that Drupal will be installed on a real file system.
Take a look at an issue I posted on Drupal Answers: http://drupal.stackexchange.com/questions/201102/detecting-if-a-file-pat... for more details.
Comment #103
mile23We move the methods back to static, and change their signature with an injected bool. This bool tells us whether to apply the core rules or not.
Please read the docblock changes and see if you can come up with better wording. :-)
I also tried making a
TestIdentifierobject, which is really the bestest solution, but also the trickiest to implement since it means untangling a whole bunch of dependencies and tests.Comment #104
Torenware commentedA reasonable alternative, good 'nuf.
Trailing space needs removing.
The @param and the actual variable should match. So either both need to be $results, or both need to be $test_results. Currently, the function is declared
Keep It Simple, Sahib. The current identifier array works well enough, me thinks. The Bestest is the Enemy of the Goodest.
Comment #105
mile23Well, TestIdentifier and implementation would be much simpler. It's just that it's already complex. :-)
Changes from #104 in this patch.
Comment #106
dawehnerThank you, this looks perfect for me!
Feel free to keep it: but in general this is totally out of scope of this issue
Comment #107
alexpottMissing documentation of
$is_core_test. However, why is all this $is_core_test/isCoreTest() stuff necessary to fix the issue outlined in the issue summary?Why is this necessary? Given this is not supported in HEAD introducing it here seems out of scope and deserves it's own issue.
Comment #108
mile23Because apparently we needed to allow for different test suite types for contrib.
Also apparently this comes as a surprise to maintainers. :-)
So remove the 'unclassified' thing, wait for someone to complain, add it back in a follow-up?
Comment #109
Torenware commented#83 summarizes the reason. The original issue was caused by how restricting the allowed test directories by setting up the class loader to only see tests in those directories. The problem with this was that the loader then could not see anything else under \Drupal\Tests\a_module\, including base classes or traits. Which preventing factoring tests, or sharing these kind of resources with other modules.
Rather than enforce the standard test directories via the class loader (which is what Core currently does), the patch enforces this by throwing exceptions when a module supplies a test in the "wrong" directory. This solution introduces a new problem: if a contrib module is non-compliant, it cannot be present (much less enabled) under the modules/ directory, or the test system will not work at all: running phpunit, run-tests.sh will fail before running any tests at all, and the admin/configuration/tests page white screens, all without any useful information about why this occurs.
$is_core_test and TestDiscovery::isCoreTest() are introduced so we know we can safely enforce the test directory standards or not, since we can for Core, and cannot for Contrib. It's a simple and unobtrusive way to allow Core to do what was originally intended with test directories, w/o breaking existing Drupal installs that use non-compliant modules. These currently include Rules and Devel, and probably quite a few more I've never installed. I did have those two modules installed, which alerted me to the issue.
I'll document the parameter, but I do think this is in scope, for the reasons listed here.
Comment #110
Torenware commentedBecause apparently we needed to allow for different test suite types for contrib.This. At the very least, we needed it because the lack of a test suite was the immediate cause for the spurious exceptions you get when a module like Devel is installed. Mile23 may also be aware of other places that require the test suite to be non-empty.
Comment #111
Torenware commentedAdded a bit of documentation for the 'Unclassified' PHPUnit test suite.
Comment #112
Torenware commentedComment #113
donquixote commentedI would suggest to do this first:
#2729711: Improve test coverage for ExtensionDiscovery
EDIT: Don't bother. As @Torenware points out in #116 below, this is not related at all.
Comment #114
donquixote commentedComment #115
Torenware commentedAdded the "Contributed project soft blocker" tag, since this patch would allow Rules to export helper classes and traits for Rules plugins, and is blocking our #2638290: Create stream_wrapper_example, which needs to factor some complex testing mocks.
Comment #116
Torenware commented@donquixote -- not sure if that's in scope here. For good or ill, the code in TestDiscovery does NOT share the regular core code for extension discovery. While I can't necessarily tell you the history or justification for that, the test system actually scans its own list of locations for extension discovery, some of which are not in the standard list of directories. For example, test modules that are list under MODULE_DIR/tests are found by TestDiscovery's mechanism, and not by the standard core set. So it's likely that any tests added to #2729711: Improve test coverage for ExtensionDiscovery would not tell us much about how the test system behaves.
Comment #117
alexpott@Torenware the whole unclassified thing still gives me pause.
This looks like a really good reason on first glance but digging deeper...
How is devel non-compliant? http://cgit.drupalcode.org/devel/tree/tests/src
I can see how rules is non-compliant... http://cgit.drupalcode.org/rules/tree/tests/src?h=8.x-3.x - but the thing is given that rules is not shipping with it's own phpunit.xml.dist I have no idea how these tests are run - even the travis.yml does not appear to have a way of running them. And here's the funny thing the Integration folder contains tests that extend Drupal\Tests\UnitTestCase so why are they making people jump through unnecessary hoops? Imo these tests should just be in an Integration folder under the Unit folder and then everything would just work.
Comment #118
donquixote commented@Torenware: You are right. I misread the issue title, or something like it.
Comment #119
Torenware commented@alexpott --
Several points here: first, is classification always meaningful or important here. Second, given that you get the suite from the directory the test is sitting in, what if you don't have such a directory (hint: I'm going to give you a current example where this does not hold). And third, what about types of tests that Core does not use, but might be useful for certain modules in Contrib.
So....
If we look at the current code in Core, we insist that any and all tests are in one of 4 subdirectories of test/src:
These are the ONLY directories you currently allow. Note that the directory test/src itself is NOT in that list (we'll get back to that in a minute).
As an unfortunate side effect of the current code in Core, you are also restricting any subclasses or traits a PHP unit test might have to these four directories. Whether or not it makes sense. Whether or not a subclass or trait might be shared with a test of a different type. Whether or not you might want to share a subclass or trait with another module.
This is simply stupid. Why break the test system in this manner?
So, rather than do something stupid that makes the test system harder to use, that creates mystifying issues for people who have been told that the test bot can be used for something other than Core, perhaps we ought to fix this. Sooner than later -- and note that this issue is now over 7 months old, and has a lot of time and effort invested in it. Hopefully to result into something other than a waste of the time expended.
Currently, if a module is non-compliant, it is invisible to the test system. Which brings us to:
The actual problem is with devel_generate, which has a single test, in tests/src:
$ tree devel_generate/tests devel_generate/tests ├── modules │ └── devel_generate_example │ ├── devel_generate_example.info.yml │ ├── devel_generate_example.module │ └── src │ └── Plugin │ └── DevelGenerate │ └── ExampleDevelGenerate.php └── src └── DevelGenerateManagerTest.phpDevelGenerateManagerTestwill NEVER BE RUN by your current code. It is simply invisible to the current, broken version of TestDiscovery. Try listing the classes to see this:Note that
DevelGenerateManagerTestappears nowhere in the list. You can try the same thing in the web UI, BTW. It does not appear. Does the test really pass? HTF would you or I know? Because the bug in the current implementation in Core is completely invisible. Using the testbot does not currently hit this test. You likely use devel; yet you are unaware of this. As is everybody else. Non-compliant modules simply have tests that are never run, and no one is the wiser of it.If you really want to fix this issue, you need to prevent these tests from being ignored by d.o.'s infrastructure, as they currently ARE being ignored.
Mile23's contribution to this patch is to make these currently invisible tests visible again, by throwing an exception when a test is not in one of the 4 canonical directories of Unit, Kernel, Functional, and FunctionalJavascript. This is reasonable for Core, since Core may reasonably enforce such a standard. I'm not sure why it's appropriate for Contrib in general, even if there are many modules that might standardize on this.
But the class loader does not know the difference between a module that is a part of Core, or a module that is part of contrib. Ignoring the tests, as your current code does now, is a serious problem. Throwing exceptions when a non-compliant is simply found in the file system, however, is worse, and sucks from DX perspective. So this patch also attempts to not be fascist about how contrib modules do their tests. We let contrib organize its tests as make sense for different modules. And, in addition, we let contrib factor tests in a reasonable way, and share this factoring with other modules.
The simplest way to do that is how we do it here: we assign a non-compliant test class an arbitrary "test suite". I'm not picky as to what that test suite is, or what it's called. But since
TestDiscovery::getPhpTestSuite()needs to exit non-empty if we are not to throw an exception, it is a simple and reasonable solution. I am currently uncertain if there is any other code in Core that actually cares what the suite name is in any case. I am open to other options, although it would be good if they are simple and readily discoverable to developers, and do not lock them into doing things that may not make sense for their use case.The current implementation is unacceptable. I present this one as superior to the current code. But as it goes in engineering, there are of course multiple ways of solving any given problem. I'm not picky. But this long after the release of D8, this is the kind of annoying issue that needs to get resolved, so people can spend their time more productively.
How would you have us fix this problem such that we can get it into the tree, and we can go about fixing the problems that the status-quo is creating in the interim?
Could you please enumerate some specific changes we can make so that we can complete this issue and move on?
Comment #120
Torenware commentedA quick comment on Rules, as well.
Right now, development for Rules is solely on Github. They run Travis when a PR is posted or updated; Klausi wrote the automation, and it works well.
The reason why I raise Rules as an example in this issue is that the module is used by a lot of other modules to write plug-ins. Some of these projects (Flag, for example) currently use the standard d.o. test bot. This means that:
Comment #121
mile23@torenware: Let's get the traits working here with the stricter rules that @alexpott seems to want, and then work on widening the rules for contrib in another issue.
Currently, those tests won't be discovered anyway, so we don't lose anything by not addressing it here.
So with that in mind, shall we just throw the exception for 'illegal' tests and not worry about whether a test is in core?
Comment #122
Torenware commented@Mile23 -- the code as currently done (or as of your patch of #103). solves the trait problem. If breaking contrib is not a problem for Drupal project maintainers, then I am solving a problem that does not need solving.
I am somewhat at a loss as to what to do from here. I've made multiple changes to this patch based upon various suggestions from the Powers That Be. Daniel has given a good deal of useful input, for example. But I don't want to presume that I understand what they want for this patch, or frankly, if they want it at all. My sense is that @alexpott considers current Core behavior correct and is not receptive to changes in behavior. If he's willing to give clearer guidance than "not like this", I'll change my mind on the issue. But I'm about done with this issue; not a productive use of my time.
If you like, revert the patch to #103. Since breaking contrib does not appear to be a consideration, it's largely a waste of time to continue working on that angle. Since #103 is broadly acceptable, let's go with that.
Comment #123
alexpott@torenware in my mind it is both rules and devel_generate that are breaking the core test contract - ie. the contract that says without knowing anything about the module you should be able to run a module's tests by running run-test.sh --module MODULE_NAME. That is what we should support and all modules working that way makes everyone's life simpler. This also means that Drupal.org will successfully run their tests for them.
As I've said I'm +1 on supporting traits in in tests/src but I'm -1 on supporting non-runnable tests in tests/src or having code that supports that.
devel_generate has a further issue in that sub modules should not currently have phpunit tests because the Drupal notion of being able to centrally run tests from /core is an anathema to PHPUnit and the way it discovers tests. In the PHPUnit world the devel_generate module should have a phpunit.xml.dist and it's own bootstrap.php that would allow it to discover classes any way it likes.
The rules example highlights an interesting issue related to devel_generate - it's tests are only discovered by its travis implementation because it does
/vendor/bin/phpunit -c ./core/phpunit.xml.dist ./module/rules. If it did/vendor/bin/phpunit -c ./core/phpunit.xml.dist --filter ruleseven though all the test classes contain the word rules it does not find the ones in /Integration because core's phpunit.xml.dist is not designed to find them but just providing a directory overrides the test discovery options we've put in place.In fact, I thought this issue introduced a new issue because if tests are run directly though phpunit then it needs to follow the same rules as core. However PHPunit’s bootstrap.php adds tests/src so all code in there is autoloadable. One massive problem we have is that supporting two test runners which use different discovery mechanisms is very very complex.
So what should we do?
I don't know how to reconcile the change we're making here with the current structure of phpunit.xml.dist. And having the different test suites available and enforced by both phpunit and run-tests.sh/simpletest is a good thing. And being able to only run the unit tests as opposed to the functional and functionalJavascript is also useful. I guess what we need to do is to work out a set of principles about how we want test discovery to work and code for that. These principles might just answer that if you want autoloaded code you have to put it in a valid test sub directory.
Here's what I think the principles should be:
run-test.sh --module MODULE_NAMErun-test.sh --directory MODULE_DIRECTORYand./vendor/bin/phpunit -c ./core/phpunit.xml.dist MODULE_DIRECTORYshould result in running all the same PHPUnit based tests. (run-tests should also run the simpletest tests) These should also be the same tests when using the --module option../vendor/bin/phpunit -c ./core/phpunit.xml.dist --filter MODULE_NAMEshould also run all the module's testsGiven these principles...
We can not control how PHPUnit works with --directory discovers files - it is going to find all the tests under tests/src so given how our test system works we need to make Simpletest able to autoload from tests/src and run its discovery from there. This leads us to the conclusion that all tests under tests/src should be PHPUnit runnable tests. In all the examples given in this issue they are.
In order to make the --filter argument work the same, the phpunit.xml.dist needs to scan from tests/src too. So we need to have a test suite for such tests. I guess an unclassified test suite will do. But I don’t think we should have a different rules for contrib and core - this tends to make life more difficult rather than simpler. So let’s let core’s modules also have unclassifiable tests too. This makes the patch simpler - we then have one simple rule - tests in tests/src will be discovered and run using PHPUnit. Also this makes unclassifiable tests easy to detect - you just need to do
./vendor/bin/phpunit -vvv -c ./core/phpunit.xml.dist --testsuite unclassified --tap- so if you this with the Rules project in ./modules this will run the tests in its Integration directory.The patch attached satisfies the above two paragraphs and meets the principles laid out too. To do this it makes all code in tests/src autoloadable, which solves the trait issue that led to this issue’s creation. All the rules module’s integration tests are now discoverable by both run-tests.sh and phpunit. It also makes devel_generate's test discoverable at least by run-tests.sh. Unfortunately it is still not discoverable by phpunit’s runner but that needs to be solved in a different fashion and by a different issue (which exists already). The patch also removes out-of-scope code cleanups made in the previous patches. The kernel test added by the previous patches is made a unit test because it can be - which resolves an @todo.
There is another opinion - having test code being autoloadable is a bug. The autoloader shouldn't be changed to include tests because doing so changes the system under test way more that a test should. I think we should consider moving to not having autoloadable tests in the future but that is a huge change which will need more planning - traits and base tests will need to move into a module’s src/Tests which is currently occupied by Simpletest's tests (web tests and its version of kernel tests).
Comment #124
mile23@alexpott thanks so much for this comment. You have to know it's music to my ears. :-)
I opened a meta about all these issues a while back (in a more complain-y voice): #2607866: [meta] DrupalCI/SimpletestUI/run-tests.sh/phpunit not in harmony
Could we move that larger conversation there, or some other planning issue please? I totally don't mind doing work towards any of it as long as there is some structure to the conversation.
Agreed with all the principles.
That's kind of the point of #2607866: [meta] DrupalCI/SimpletestUI/run-tests.sh/phpunit not in harmony ... They're already not reconciled before we fix this issue, and won't be reconciled afterwards, because this issue doesn't reconcile them. :-)
This is an interesting point, but it speaks to a hard-edged discipline that's completely missing from Drupal core. There are plenty of test quality topics we should cover before that, such as multiple @covers in unit tests and the like.
And of course moving traits and superclasses to src/Tests/ is the opposite of this issue. :-)
We could make a classmap for all files in test code directories rather than use PSR-4, but that seems ungainly.
Anyway. The patch.
Shouldn't core modules not ever have unclassifiable test types? We can add types in core, and in fact we have to. (Such as
BrowserTestBaseneeding extra infrastructure.)This leads us back to #2499239: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests but I can let it slide here.
Ultimately, in line with Principle #1, we're going to have to make a Drupal-flavored suite loader so no point in protesting in this issue.
Comment #125
alexpott@Mile23 thanks for the review.
Re the autoloader and testing...
This is not necessary - a test loader should just enumerate the test directories and load each test by itself. There's no need to autoload the test.
Re reconciliation...
After this issue lands
run-test.sh --directory MODULE_DIRECTORYand./vendor/bin/phpunit -c ./core/phpunit.xml.dist MODULE_DIRECTORYwill run the same set of tests. ATM for modules like Rules this is not the case. Both will autoload from tests/src - atm only PHPUnit does. That is some way towards reconciliation - which to me means running the same tests. I guess you are thinking about things like result formats - but that is not as important running the same tests. The only outlier is sub-modules which run-tests will pick up and PHPunit will not (by default --directory actually does) but that's #2499239: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests. And this is the same for core and contrib - it is just that no module in core has a sub module.Comment #126
dawehnerIn an world in which each module ships with its own
phpunit.xml.distfile, they could also ship with their own bootstrap.php file and in there make what they want, which might be for example providing the autoloading capabilities pointing to whatever files they need.Actually one doesn't need this indentation for namespaces, see https://3v4l.org/IQKDn
On top of that I'm wondering whether we could add some kind of documentation about good practises. Maybe #2499239: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests though is the better place for that.
Comment #127
alexpott@dawehner re the namespace thing... the second namespace in this instance is the global namespace so that's not possible. However, we only call
drupal_valid_test_uabecause we are going through a highly unlikely code path of discovering the site path. I've altered the unit test set up to create the dependencies necessary to not call the function. Doing this has the nice side effect of properly exposing the dependencies of test discovery in the test.Wrt to documentation I think this is the type of thing that belongs in the handbook - specifically https://www.drupal.org/node/2116043
Comment #128
alexpottWhere's the patch... here it is :)
Comment #129
dawehner+1 for an explicit setup!
Comment #130
neclimdulI assume everyone on the issue implicitly understands why we're adding a new test suite to phpunit and how that solves run-tests.sh's inability to load traits because of the discussions but reading the summary and skimming comments I can't figure it out. Could we get an updated issue summary?
Comment #131
alexpott@neclimdul the new test suite is not just about run-tests.sh inability to load traits. It is also about the difference between
./vendor/bin/phpunit -c ./core/phpunit.xml.dist --filter MODULE_NAMEand./vendor/bin/phpunit -c ./core/phpunit.xml.dist MODULE_DIRECTORY. This should result in the same tests running. Atm for rules this does not happen because of the testsuites defined in phpunit.xml.distAlso fair point about the IS - updated accordingly.
Comment #132
mile23Well, no not results formats, but that the discovery methods are different so therefore we will always be chasing this down.
For instance: #2301873: Use ExtensionDiscovery to register test namespaces for running PHPUnit
That's an issue attempting to make parity between how run-tests.sh and phpunit's bootstrap discover tests.
We should have the same discovery methods for both tools, but we don't. For instance your patch above looks an awful lot like this doesn't it? http://cgit.drupalcode.org/drupal/tree/core/tests/bootstrap.php#n70
RTBC++
Comment #133
alexpottBeing the title into line with the current patch.
Comment #134
alexpottUntil we either have no SImpletest or PHPUnit can run simpletest this will be the case. Of course we should have probably never allowed run-tests.sh and UI to run phpunit tests but we were too keen to add PHPUnit without affecting too much of how tests worked, users used them and the old DrupalQA worked. As we edge towards a Simpletest free future the test discovery stuff becomes moot. We just always use PHPUnit and ditch run-tests completely. But we will also need to solve #2499239: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests
Comment #135
donquixote commentedThe issue summary is still confusing for someone not already familiar with the issue.
If I understand correctly, there is a kind of tests that always lives in specific sub-namespaces (Unit, Kernel, Functional, FunctionalJavascript), but that may want to use traits or other stuff autoloaded from toplevel tests namespaces.
Discovery of such tests can happen in two ways: Via simpletest TestDiscovery, or via phpunit based on the phpunit.xml(.dist). And currently simpletest TestDiscovery only looks into the sub-namespaces, while phpunit looks into the toplevel tests namespaces.
Simpletest TestDiscovery only registers the sub-namespaces in the class loader, whereas phpunit registers the toplevel namespaces into the classloader (does it?). Also some part of the discovery depends on the class loader? Does it?
So there are two problems:
- In Simpletest, traits and possibly base or helper classes in toplevel tests namespaces cannot be autoloaded.
- Some tests are discovered by phpunit, but not found in simpletest.
Is this correct? If so, it would be great to update the issue summary accordingly. Start by briefly explaining the current situation, and why it is bad. Then what is the desirable situation. And then how we are planning to get there.
(I was going to ask this on IRC instead, but found none of the active participants online)
For the first problem, it seems that @Torenware's solution in #3 should be sufficient.
For the second problem, more is needed.
Right?
Comment #136
alexpott@Torenware thanks for working on this difficult issue for so long. I appreciate that you were frustrated by the lack of movement and the vagueness of my unease around the new 'unclassified' test type.
re #119
These statements are not alright. Many people have put many hours into getting PHPUnit into core and expanding it's capabilities with Kernel, Functional and FunctionalJavascript testing. They are not stupid, inferior, or out to implement a system that is unacceptable.
Your efforts to make our testing system more robust and predictable with this issue are valuable. In the future, however, please be more considerate of the work of others as you make your own contributions. It's my role as a framework manager to ask questions about the architecture. Thanks for sticking with this difficult conversation and hopefully the next issue you work on will go quicker.
Comment #137
alexpott@donquixote the solution in #3 opens up the additional problem - with that patch if you have the rule project and go to the SimpletestUI it breaks because it has tests without a type. This is because TestDiscovery entangles autoloading and test discovery. This is also outlined in the proposed resolution section...
I don't know how to make it clearer than that.
Comment #138
mile23Currently, if you run core's phpunit-based tests using
phpunit, it will find and load traits for modules. If you do the same thing usingTestDiscovery, it won't. That's becauseTestDiscoverycurrently only allows specific namespaces per module per test type, without allowing autoloading of the whole of the test namespace. So for instance if you have aUnittest and aFunctionaltest and you define a trait for both of them to use, thenTestDiscoverywon't allow for autoloading that trait.That's the problem we're trying to solve in this issue, in harmony with the principles in #123.
The solution is that we have
TestDiscoveryregister the whole test namespace for autoloading, which creates a new problem: This allows autoloading of tests which don't fit into the categories core knows how to run.An early solution to that problem was to throw an exception. @Torenware scrapped the exception and added a category for 'Unclassified.' @alexpott added a test suite for 'unclassified', which puts some edges on where these unclassified tests should be in the file system.
Updated issue summary with this comment. :-)
Comment #139
neclimdulLooking at rules and reading back through the discussion, I think this is the expected behavior given that its defining tests outside of the defined phpunit test groups. The fact that this might work in run-test seems like a bug in run-tests.sh not the other way.
Comment #140
mile23Looks like a good follow-up.
Comment #141
donquixote commentedAfter reading updated issue summaries, and a little chat with @alexpott on IRC, I still think this is a two-fold issue. As I currently see it, it might be better (or have been better) for understanding if these issues were treated separately.
The first problem is that ExtensionDiscovery does not distinguish between autoload namespaces and test namespaces.
This would be quite simple to fix (as was already proposed in #3):
Imo this could be a separate patch in a separate issue, with a dedicated issue summary and git commit message that everyone will understand.
It will not fix all the problems described in this issue, but it will also not introduce regressions.
With this proposed change, in simpletest, the toplevel tests namespaces would be used for autoloading, but tests would still only be discovered in the dedicated sub-namespaces. phpunit would not be affected.
As a side detail: The autoload classmap would still be based on the sub-namespaces, whereas the PSR-4 directories would be for the toplevel tests namespaces. This just means that classes outside the tests namespaces would not benefit from classmap for autoloading performance. So what.
------------------
The current patch in #128 also fixes this problem, but it does more than this:
I have the impression that the issue summary does still not properly distinguish these two problems. The issue summary text takes the coupling of tests namespaces and autoload namespaces for granted, and does not address it as a problem.
Splitting this issue into two issues would make things clearer, imo.
If we keep it in one issue, at least the issue summary should explain the two problems separately.
Maybe I can give it a shot myself, if someone can confirm (on IRC, if you want) that I correctly understand it. I just think I am the wrong person, since I have not actively participated much.
Comment #142
neclimdulNo, because if it is a bug, "unclassified" is syncing the bug to phpunit so the follow up would be rolling back this patch.
Comment #143
mile23@neclimdul: run-tests.sh can say
TestDiscovery::getPhpunitTestSuite($test)and not run the test based on the answer. It could already do this but doesn't.Edit: Oh wait, it does, indirectly through
simpletest_script_get_test_list(), as long as you specify--types.Comment #144
alexpottWe're in a world of pain if the following commands run a different sets of PHPUnit test for inexplicable reasons.
./vendor/bin/phpunit -c ./core/phpunit.xml.dist --filter MODULE_NAME./vendor/bin/phpunit -c ./core/phpunit.xml.dist MODULE_DIRECTORYrun-test.sh --directory MODULE_DIRECTORYrun-test.sh --module MODULE_NAMEComment #145
alexpottFor those that have not tested it (and I strongly you encourage to) just get the Rules project and run the commands in #144 with and without the patch.
I does not matter if a non PHPUnit test is in tests/src anywhere it'll not be detected as a PHPUnit test and nothing goes wrong. The problem is when PHPUnit tests are placed outside the currently expected tests/src sub-directories.
Comment #146
neclimdulP.S. I owe everyone an apology for the RTBC review. I totally wasn't following this close enough to catch the scope change from when I first read this.
As far as the commands, I did run exactly the listed commands. I saw exactly what I expected though.
I think its entirely a bug that run-test.sh would test
/modules/rules/tests/src/LolCoreIsSilly/mytest.php. The documentation is outdated and actually only lists theUnitnamespace but it very explicit that it must be in a specific namespace.Furthermore core's implementation and phpunit.xml.dist have a clearly defined sets of namespaces and "Integration" is not one of them. I don't think a bug in simpletest's runner implementation that contradicts documentation defines a new feature.
Comment #147
neclimdulerr... ok d.o...
Comment #148
Torenware commentedThis said, why could we not set the type/suite of the test via annotation, rather than use the directory structure? At a minimum, this would allow cases like devel_generate, where there is a single test in a top-level directory. This would also completely separate the test-discovery related issue of determining the suite from the class-loader related issue of what resources (not just tests, but traits, interfaces and base classes) can be exported by a module.
This issue has certainly had a fair bit of scope shift than its initial form; my concern originally was to make it possible to make test related resources like traits declared under tests/src discoverable by run-tests.sh, which currently has different behavior from running PHPUnit tests via bin/phpunit directly. The fix for this was, as folks have pointed, quite simple.
The problem is that it ran afoul of desired changes in how modules should be able to set up their tests. Currently, bin/phpunit is pretty lenient about this, and run-tests.sh is both more strict, and has weird behavior in certain corner cases. I'd argue some of these are bugs, and should be fixed. I suspect that @alexpott considers the bin/phpunit and phpunit.xml.dist behavior wrong; I actually don't know what he thinks about class and trait discovery.
If we want run-tests.sh to enforce the structure issue w/o hiding most of Drupal\Tests from the Composer-based loader, then some approach similar to @Mile23 is needed to selectively enforce which suites (defined now as blessed subdirectories now) are legal and which will not; illegal throws exceptions. It also "sees" tests that the current TestDiscover.php code just silently ignores. If we want to impose the same scheme on Contrib, @Miles23's patch in #103 does the job. If we don't, you need to distinguish between a Contrib module and a Core module, as do my post-#103 patches. Whether this is a good thing or not is in the eye of the beholder.
I have no problem with splitting this into two issues, although my sense is that what Alex wants here cannot be satisfied if loosened class loading also loosens test discovery. But that said: I do think loosening the restrictions on loading is very necessary, especially since it's the cause of a lot of wasted time by people who find their PHPUnit tests fail or fail to run on run-tests.sh, usually without any usable diagnostics as to why this happens.
Enforcing the directory and namespace structure that some of you desire is a different issue than making non-test classes, traits and interfaces
Comment #149
neclimdulThis would never work with phpunit's runner as far as I know which I think is a no-go. IMHO. I would be very sad to see my phpstorm phpunit tests go away anyways.
Comment #150
Torenware commented@alexpott in #136:
Please forgive my frustration. It's less the time that went into this issue, although there was quite a lot of that. It's that there is work on other modules -- in particular, any module that intends to implement Rules plugins -- that has sort of been put on hold until some of the complexity of testing those plugins can be hidden via good base classes, interfaces and traits.
As for your principles from #123 -- like @Mile23, +1, along with the patches you've posted in the last day or so, which do a lot to indicate what your priorities are, and therefore, what work needs doing and what work does not. This kind of guidance is "operational" -- it clarifies what specific work gets this issue closer to completion. Previously I've tried to figure this out, and I've guessed wrong. Clearer guidance prevents me, or other volunteers from putting in time that doesn't produce results, which I think we're in agreement is something the community wants to avoid.
As for
I do respect that, and I'm sorry if it's come across from me otherwise. In particular, I'm aware how much work has been done in the test system post-8.0-final. And that the code needs to work before it's fully documented.
But this, of course, has made the behavior of the test system a moving target, and during that time, a number of folks working on different contrib modules have needed to figure out the behavior of the system, and to understand which parts of the that behavior are intended and which were just temporary artifacts of getting the code done. I do appreciate the time and care that was put into the test system changes. But the changes have been very costly in terms of developer time. The original patch was a simple patch. But figuring out why run-tests.sh failed to run certain tests took me a couple of weeks IIRC; you can see what the problem looked like from a developer's perspective by looking at #2102651: Port file_example module to Drupal 8 between #20 and #67. I didn't start out working to change the test system. I just spent a few days figuring out how to replicate the test bot's behavior, and once I figured out how to replicate it in run-tests.sh, walking through the source debugger to figure out why it happened. Once I figured out that this was an interaction between TestDiscovery's test discovery vs. its class loader set up, coming up with a patch was not difficult. But figuring why that was the patch for a work-around certainly was not.
As I asked around about the problem, I discovered that I was not at all the only developer who was having weird looking problems with the test-bot; the issue with the Flag module attached to this issue shows what other developers experienced here. They did not have the time (or perhaps the pure contrariness) needed to figure out why they couldn't get their tests to work, and for a long while, they just gave up. This is a bad kind of problem in a project like this: the issue lacked transparency from a debugging perspective, it ate up developer time, and it prevented progress on a number of issues. It's an example of an 8.x time sink.
This kind of problem is particularly bad because it's not visible to the project as a whole, and to project leaders like yourself. After a while, it does give a guy the perhaps mistaken feeling that you need to yell to be heard. I'm sorry for yelling, but hope you will understand that it was a plaintive plea for help to get this issue resolved, or at least close to. I know it isn't fun to be yelled at, and I suspect you get a lot of that. So again, sorry.
And I thank you for hearing, and helping here, too.
Comment #151
Torenware commentedComment #152
neclimdulSo digging deeper I found some interesting things.
First, rules tests didn't run in simpletest with #128 and immediately blew up with standards violations (missing @group). Which made me realize they never where able to run under simpletest, phpunit or anything else. I'm sorry for my misleading, confusing and down right wrong comments. I clearly still didn't understand all the discussion again in this issue. gomen-nasai, mea culpa, I'm very sorry.
It seems if rules is testing these it must be using a custom phpunit.xml somewhere not in the repo which is totally ok though problematic for testbot and clearly confusing other users. Does anyone know where this namespace came from and how rules runs its tests?
Second,
/core/tests/bootstrap.phpwhere we setup phpunit's class loading(this is our design not phpunits), does exactly what donquixote suggested and registers the top level namespace into the autoloader. So aligning discovery seems as simple as broadening simpletest's autoloading, though the suggested change alone removes the core namespace registrations so it is only slightly more complicated. It would end up looking something like this.Additionally I attached a testlog roughly matching alex's commands in #144. As you can see the directory commands roughly align and the the module commands roughly align. I didn't investigate the differing failures in depth but they seem related to php7 incompatibilities not test discovery.
Comment #153
neclimdulThere seem to be some strong feelings about detecting tests outside of the previous test suites. While I personally disagree, I think we can all agree they are at least outside the scope of syncing the class loading. Maybe we can open a feature follow up to discuss improving the usability around placing phpunit tests outside the expected test suites?
Comment #154
neclimdulBest I can tell from a quick archaeological dig, prior to adding BrowserTestBase in beta 10 (april 2015), we didn't have separate test suites and all of the test directory was treated as unit tests. In this case Integration would have been treated as part of the unit test suite and "worked." So maybe rules just came up with its own thing really early on that doesn't fit into Drupal 8's current testing patterns and this part of the issue is just be a case of Rules falling behind and missing a changelog and misleading other developers.
Comment #155
donquixote commented#137, #148
I don't think so.
The snippets in #141 and in #3 only change the autoload namespaces, not the discovery namespaces.
I do not see any patch following this approach. Just the code snippets in the comments. But I think it should be easy to imagine.
Well this is currently so, but it doesn't have to be. The snippet in #141 / #3 would break this entanglement.
Comment #156
donquixote commentedMy concern with the current approach is that we are doing two things at the same time: A quite straighforward change that is almost a bug fix (making the namespaces autoloadable), and a possibly controversial behavior change.
The issue summary suggests that doing these two things at once is technically necessary. But I do not think this is correct.
Comment #157
Torenware commentedThe history here is that the Rules team (I'm not a chief here, just a random warrior) opted very early for a Github based development cycle. They do only PHPUnit tests, which they run automatically via Travis when a PR against fago's Github is created.
It's been a very productive work flow for them, and having worked a fair bit on projects like Examples that use the standard test bot, I appreciate how much more they get done per unit time. An embarrassingly large percentage of my time over the last year has been spent dealing with issues that are specific to the test bot. And I've become aware of a number of people who have a similar experience. Debugging these issues is very hard for most developers, and requires a pretty deep understanding of the bot's internals. Setting up a local install of the bot is not trivial for people who don't do system administration, and running xdebug against such an install is not for beginners. A lot of people just get frustrated. I at least figured out how my bug worked, which ended up putting me deep into the class loader code. Which you really don't want to debug if you can avoid it, since things go wrong pretty far from where you see an error.
So having decided not to use the standard Drupal work flow, they don't see much downside to continuing with what works for them. Testing is easy to set up, and debugging tests is not that hard. But it's been a problem for people doing Rules plug-in development but still in the standard work flow, since they are pretty much the only ones putting the Rules code base under test by the bot. This explains why problems like missing @group statements in tests creep in, even if we've tried to prevent this. I suspect it's also why some other projects like Commerce do the same, and I'd guess that developers in those ecosystems may well be having similar problems.
There's an argument as to how to deal with this diversion of work flow. I understand what the Core folk are trying to do, and why, but I understand the other side of it well too, and am not sure that the approach at hand is going to have the desired effect. As a developer caught in the middle, it's a problem. I think it would be better if the way of getting the Rules and Commerce teams "in line" did not involve breaking their respective test systems, which I suspect are well designed for what those groups want their test systems to do. But whether to do that or not is a good part of what we're discussing here.
Comment #158
Torenware commentedI don't speak for the Rules team -- I'm just a developer who's interested in what they're up to, and has contributed a number of patches -- but I'll try to give you their take on things as best I understand.
The Rules team wanted to get things working as soon as they could, largely so they knew what facilities they needed and wanted in Core. This was a good thing, especially since fago was able to get typed data into Core, and know that it was actually working. Like just about everybody, they underestimated how long the Drupal 8 cycle was going to take.
A lot of the changes in the test system that we're discussing here were deferred until after 8.0.0 went final and have led to some interesting conversations as to what can change/be broken between each of the new sub-releases. To test Rules, it takes a lot of mocks of various kinds (you want to learn Prophecy, work on Rules for a while). This needed to happen; they needed to test things that were proposed in Core, but had not yet been finished. These mocks and test classes have have been working well since well before 8.0.0 was released. IIRC, most of this stuff was in and was reliable a year ago, by DrupalCon Los Angeles. So reorganizing the test system, while certainly possible, was not a trivial amount of work. And since the test stuff was rolled in as several waves, keeping up with it would start eating up the time budget for the folks who are putting time into Rules. In particular, for klausi and for fago, who put as much time into it as they can personally afford to do.
These changes haven't really been treated as API changes per se, and for some of them, you would not have likely gotten the message things have changed via the change list. Mostly, the word's gotten out when things broke. This may not have been avoidable, but it has been source of friction without a doubt.
Comment #159
mile23@donquixote: So we don't tell the truth about registered namespaces as a return value from
registerTestNamespaces(). ThenfindAllClassFiles()does its thing to discover tests based on the namespaces we specified.We keep
testNamespacesaround so we don't have to re-do the scan, and as a side-effect we never re-register the actual namespaces with the autoloader because the test namespaces are cached in a property.Something like that?
That would solve the problem of traits in larger namespaces. It would however turn
TestDiscoveryinto even more of an oral history than an engineering practice. :-)To solve in this approach in an explicit manner, we'd want to keep the method for gathering the namespaces as it does now, for the purpose of discovery, without registering the namespaces.
The caller would be responsible for performing the registration, where we'd explicitly skip the various silo'd paths for modules and just register the top level. We'd provide a method for this on
TestDiscovery.This means we'd have to change
findAllClassFiles()to do this.That would solve the problem of finding traits in test namespaces without changing behavior, in a way that wouldn't make me cringe.
Comment #160
neclimdul@torenware No worries that actual clear it up. It looks like its not the beta change I found at fault. Rules uses drupal-ti and looking into drupal-ti's "phpunit-core" runner, it seems to have nothing to do with the way drupal core run's tests... It does exactly matches alex's command line though. https://github.com/LionsAd/drupal_ti/blob/master/runners/phpunit-core/sc...
I think its better we focus on wrapping up the autoloading fix and discuss the #2738041: Improve DX around phpunit tests outside of core's test suites. DX issue around tests outside of our test suites separately.
Comment #161
neclimdul@mile23 ::registerTestNamespaces() make no assertions about the returned namespaces connection to the registered autoloading namespaces. It doesn't even discuss autoloading in the documentation, its just an undocumented artifact. While it isn't how I would have written it, I don't see a reason to refactor it when the fix is simple.
If I where to refactor it, I'd converge the code in tests/bootstrap.php with what ever logic is here so it is literally running the same code instead of mirroring the same logic.
Comment #162
Torenware commented@Mile23:
Gee, you're talking about oral history as if it was a bad thing ;-)
Comment #163
alexpottHow is actually running tests provided by a module controversial. It way more controversial to not run them.
@neclimdul re the rules @group problem - yes one (just one) test is missing the @group and that's precisely because they can't test them using run-tests.sh so they've never noticed. Because DrupalCI is not running the tests. Which is the problem we're trying to solve.
But all that said, autoloading of anything in tests/src is actually wrong. Can these projects autoload their tests?
Nope, not a single one. Changing the autoloader of the system-under-test is architecturally wrong and something we should stop. So perhaps we should change tack and remove autoloading of anything in tests/src in both run-tests.sh and bootstrap.php and make discovery work through file listing like it does for PHPUnit.
Separately, test discovery needs to be transparent and not able to miss tests. All PHPUnit test under tests/src should be run by run-tests.sh/DrupalCI. Doing anything different is very confusing and not helpful to developers who have testing working locally but then don't on DrupalCI. The other way to go is to stop testing contrib with core's phpunit.xml.dist and encourage projects to ship with their own - then every one is free to come up with their own solutions. But I would argue that that approach is really unfriendly to contrib module developers.
Comment #164
alexpottOf course we can't change tack and stop autoloading tests at this point in the cycle because that would break a lot of tests. :(
So we're left between the proverbial rock and a hard place - we have to compromise. And I suggest that the compromise of #128 is the way to go, because, PHPUnit and Simpletest will then both autoload from the same point AND all the variants of the commands in #144 run the expected tests.
Comment #165
neclimdulAs don has pointed out repeatedly, none of this has anything to do with autoloading traits from a different namespace other then the fact that we where lazy and through the logic into one method.
Everyone of those projects has exactly one test suite. Additionally, none of them provide a framework for running tests for sub projects. IE, you don't just drop https://github.com/FriendsOfSymfony/FOSRestBundle into your symfony project and run symfony's test suite. You run FOSRestBundle's phpunit.xml.dist and that runs tests.
The problem I want to discuss in the other issues is we've built an opinionated framework for running tests for modules. We have (mostly) clearly laid out the rules for how that should be setup. At least from within phpunit.xml.dist. Another (opinionated) test runner is running tests for Drupal projects with a different set of opinions that are contradictory to this and its causing problems.
Do we cede our opinion and match that test runner?
Do we take a middle ground and gum up our test runner with some unmaintainable gibberish?
Do we try and find other ways of providing feedback to the user that we're not going to run their tests?
Do we investigate if we can handle something better in drupalci? like letting people provide a phpunit.xml?
Do we investigate if maybe drupal-ti wants to align its test runner to what core is doing? Do they even know their core runner is causing problems?
Comment #166
alexpottThere is no special runner for drupal-ti... they are just using phpunit, our phpunit.xml.dist and providing a directory path to the module. Nothing special is occurring. Providing a path completely overrides the testsuite stuff in our xml. This is how phpunit works and has nothing to do with drupal-ti.
Comment #167
alexpottAlso we're not ceding anything - we support three test runners in core - phpunit, run-tests.sh and Simpletest UI. This is about making them work (with any of their existing options) in as similar a way as possible.
@neclimdul you know better than to be offensive about efforts to improve Drupal. No one wants to gum up anything and maintaining the three runners is already hard.
Comment #168
donquixote commentedJust asking:
Could it be possible that someone (a contrib module) intentionally does not want their tests to be found by Drupal simpletest TestDiscovery, or by Drupal phpunit?
Such a module would put the tests that are meant to be found by Drupal into one of the dedicated sub-namespaces.
Other tests, which are not meant for Drupal, would go somewhere outside of these namespaces.
Such a module could already exist, right?
By changing the behavior of the test discovery, this hiding would no longer work.
Is this what we intend to do?
EDIT: I just notice that #152 is exactly what I've been proposing all along.
+1 :)
Comment #169
donquixote commentedI just notice that #152 is exactly what I've been proposing all along.
This patch eliminates all my concerns.
+1 :)
Comment #170
alexpottFirstly let's find a module that wants to do that - hopefully a well-used and important module to the Drupal ecosystem - because we certainly have a module that would like it's integration tests run by DrupalCI - that's Rules. Secondly, going back to principles... having tests that are hidden from test runners (that can be run by core's runners) sounds like... untested tests. Thirdly if you want to opt out of Drupal's test infra just don't put your tests in tests/src just stick them in tests/special_flower_test or whatever. You're going to have to write your own runner or use a different runner so using a completely different directory for that (way more special and rare use-case) is totally acceptable.
Comment #171
alexpottTo me #152 is the worst of all worlds - we continue to have more autoloadable in tests without being able to run Rules tests. So we encourage people to place stuff outside the currently supported directories without supporting tests there - unless you use phpunit and point it at the directory.
@neclimdul and @donquixote - why don't you want Rules integration tests to run? Why do you want developers to have to learn the arcane structure beneath src/tests? We've got evidence that developers are already making mistakes. Why do we want developers to have stuff that looks tested but is not on DrupalCI? Untested tests are worse than anything, because it is silent and you just might never know until stuff breaks on a real site.
Comment #172
donquixote commentedI just think that the two questions should be discussed separately, and then fixed with separate patches, in separate issues:
1. Which classes should be autoloadable?
2. Which classes should be discovered as tests?
So far I have not seen a convincing argument why this belongs together.
A number of comments imply a technical dependency between these two questions. But #152 shows there is none.
The current situation is like this:
In both phpunit and simpletest, tests are only discovered in the dedicated subdirectories for the test suites.
In phpunit, classes (and interfaces, and traits) in toplevel tests namespaces are autoloadable.
In simpletest, classes (and interfaces, and traits) in toplevel tests namespaces are NOT autoloadable. Only the classes (and interfaces, and traits) in the sub-namespaces are autoadable.
So the only difference between phpunit and simpletest, in this regard, is the autoloading.
There are clearly two steps (duh, this is 3 now. but 2 for D8):
Comment #173
alexpott@donquixote - I'm just proposing that "Which classes should be autoloadable?" the answer is none - but we can't do that cause BC. So this issue then should be about "Which classes should be discovered as tests?" because that's what is important. Hence the updates to the issue summary and title. Also #172 is ignoring the directory option in both run-tests and phpunit which currently work differently and shouldn't.
Comment #174
donquixote commentedBut the autoloading question is not over.
"None" is not an option, I agree.
But the current situation is also not an option, because it is different in phpunit and simpletest.
The only option left that does not break BC is to register the same autoload namespaces in simpletest that we already register for autoloading in phpunit. A patch as in #152 would fix this inconsistency, and level the playing field for a follow-up.
The other patches have the same effect on the autoload namespaces. But there this fix is packaged with something else, which makes it less transparent e.g. in the git history. And someone studying the commit will then find this issue, where a large portion of comments are talking about traits that fail to autoload.
The introduction of an "Unclassified" test suite deserves some more discussion, which better happens in a dedicated issue. E.g. what if we want to introduce other test suite subdirectories in the future, but these tests are already "Unclassified"?
Comment #175
alexpott@donquixote they just become whatever testsuite we've added. Exactly as would have already occurred when we added the FunctionalJavascript recently.
Comment #176
neclimdulre #166that is technically incorrect but only in a misunderstanding of terminology https://github.com/LionsAd/drupal_ti/tree/master/runners. Notice there is a phpunit and a phpunit-core. My assumption is that phpunit-core tries to mimic core's behavior but it is the root of this problem because it is not.
re #167I'm sorry for my phrasing. I do feel that the proposed solution is a significant maintenance pain and the introduction of a defacto API that could be a long term DX problem. That is why I'm pushing so hard to discuss that part of this issue further separately.
re #171I do want them to run. I don't know that running them in that namespace is correct though. I want them to engage people who care explicitly in fixing that problem in the best possible way though. I personally feel that changing our discovery is not the solution and exposing the fact that the tests are outside of the documented namespaces could be a much better option. We have opinions about where tests are located, we don't expect them to be in "src/tests" or "testing/src" but in "tests/src/(Unit|Kernel|Functional|FunctionalJavascript)" and those tests do not meet that criteria and I think that's valid.
My only problem with this issue has been that is was accidentally an under the radar discussion of changing test discovery listed as fixing autoloading traits in simpletest like phpunit. I would like to fix the problem at hand an open up the discussion about the DX issue with tests outside the defined namespace explicitly.
Comment #177
neclimdulSo after a discussion with Alex in IRC I'm just going to step aside on this. I do not think we should be running tests outside our defined namespaces and the directory versions of our commands are behaving as expected, bypassing any sort of automated test detection. But my complaint lodged I guess we're looking at #128.
Comment #178
jonathanshaw#128 RTBC as of #129 and #146, so resetting to RTBC.
Comment #179
mile23I think we should revisit this after we deal with #2499239: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests, which is narrower in scope, doesn't begin to add behavior, and might help us have more clearly-defined test suite loading behavior to modify here as needed.
Comment #182
jonathanshawComment #183
mark.creamer commented#TCDrupal 2016 Sprints
Comment #184
Torenware commented@Mile23 #179: if I grok you right, #2499239: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests will take over test discovery for phpunit tests. If so, then would the test discovery portion of this issue now be obsolete? Since #2499239's test suite classes are now committed?
I'm hoping we can separate the class loader from the test discovery mechanism, which would make this issue here much easier to resolve.
Comment #186
jonathanshaw#2499239: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests is fixed, so this is unblocked.
Comment #187
mile23I'd suggest that this issue could be 8.2.x, but we're in beta and it might be disruptive.
Yes, #2499239: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests takes over the test discovery part for discovery using the phpunit tool. It doesn't change the behavior of run-tests.sh, however.
Doing a bit of a reset here... This is the test we want to have pass. It currently passes when run from PHPUnit, but not from run-tests.sh.
Comment #188
mile23And now we make the test pass by adding a new rule:
If you want to use a trait across different test suites and have it run in run-tests.sh, put it in
$extensionname/tests/src/Traits/.Comment #190
klausiMaybe this has been answered already, but why do we have to make up yet another directory name? Can't we just do this for modules:
And remove all the extra folders for Unit, Kernel etc.?
If a test case is in the wrong folder then we can fail the test in the setUp() method of UnitTestCase for example, right?
Then people can put their traits wherever they want and we don't have to invent a trait location Drupalism.
Comment #191
mile23See: #2465029: Ensure that PHPUnit tests are in the correct namespace which makes a test listener to fail tests in the wrong place. We also have
TestSuiteBasewhich won't find the test if it's in the wrong directory for the suite.See #123 which says:
This is a good guiding principle though of course we're not strictly following it.
Agreed having a
*\Traits\namespace (as in #188) is not good, but it's an explicit way to deal with this issue: It's a way to not store test traits insrc/, while also following the principle of limiting classes which can be autoloaded.In an ideal world we'd change
TestDiscoveryto not class-load any test namespaces and use an approach similar toTestSuiteBase. In this scenario, we'd *only* set up autoloading for the traits directory.Comment #192
heddnAnother example where this is causing some issues: #2818891: Fatal error: plugin csv not found
Comment #193
Torenware commentedNow is a good time to ask if this issue has gone so far off the rails that I need to create a follow-up.
With the code in core as it currently stands, how is it possible to set up a module where test classes and traits can be shared?
I'm not sure what this issue is currently about, but it's not about that. Which was the original reason it was filed.
Can this be done now? If so, how?
Comment #194
klausiI don't follow either and I also don't understand why modifying the autoloader during testing is such a big problem. I disagree that test helpers such as traits and base classes should live with the production code. I rather have them in the explicit namespace/folder for tests.
Anyway, now that everybody is hopelessly confused I guess it makes sense to just close this issue and start again in a new one? Or shorten the issue summary so that it is understandable?
To get going again here we could also just do exactly whatever alexpott said and stop arguing. I don't care that much about the details of this, I just want to use test traits - so I promise to shut up now :)
Comment #195
klausi@Torenware: if you put your test base classes and traits in src/Test of your module as suggested by alexpott does that work for you?
Comment #196
heddnRe #195: #2818891-8: Fatal error: plugin csv not found and https://dispatcher.drupalci.org/job/default/236548/console. This uses 'Drupal\Tests\migrate_source_csv\CSVDataProvider' & 'tests/src/CSVDataProvider.php'.
Comment #197
klausi@heddn: did you try moving the trait to src/Tests/CSVDataProvider? Looks like you put it into the tests directory which is not what we are suggesting?
Comment #198
mile23So if you apply the patch in #11, you have a handy test of the current state of trait autoloading for tests. We assume that autoloading for contrib will work the same way as autoloading for a core module (in this case simpletest).
We can run this test in phpunit:
We can't run it in run-tests.sh:
Basically:
tests/src/.Unit/,Functional/, etc.Which is correct?
We have test cases and base classes that need to be autoloaded for the existing code base, within extensions in core. This works under both test runners because base classes inherit from the suite base classes (such as
BrowserTestBaseand so forth) so they are in the same PSR-4 space either way.Traits are supposed to work across these silo'd boundaries, allowing for DRY between different test types.
bootstrap.phppermissively allows this, whileTestDiscoverydoes not. We've been using traits in core for quite a while in order to allow for BC between new testing classes and old ones. This is possible because the traits are still silo'd next to their base classes, and the simpletest-based tests can then autoload them much like the base classes.So the work-around is that traits can exist inside one of these silos, such as
\Drupal\module\Functional\YourTraitand thus be autoloadable in either tool. AUnitsuite test could then use a trait from aFunctionalnamespace, though this does confuse the value of the namespace, and only works by accident. Future changes could have different test suite namespaces autoload in different ways such as only autoloading theFunctionalnamespace during functional tests.A true solution would be to determine the policy for autoloading such files and modify the tools as necessary to implement that policy.
So: What's the policy?
Enforcing the silos is probably a better policy, because it means that we're in more explicit control of what classes are autoloaded when.
This suggests that the best solution here is a special
Traitsnamespace, which is expected to be autoloaded for all test suites. It would be inmodule/tests/src/Traits, or\Drupal\module\Traits\YourTrait. This has the benefit of being explicit and the downside of being another DX hoop to jump through.How do we support this?
We make a change similar to that in #188. This explicitly adds the extensions'
Traitsnamespace to the autoloader.Then we make
bootstrap.phpperform autoloading similar to that inTestDiscovery, which adds the suite silo behavior, and also sets theTraitsnamespace to be autoloaded. That's the change in the patch presented here.Moved to 8.2.x so we can start using this sooner.
Comment #199
daffie commentedNitpick: "Functiona" should be "Functional".
Comment #200
yogeshmpawarupdated patch with changes from #199.
Comment #201
dawehner@Yogesh Pawar
Please always post an interdiff. Thank you!
Comment #202
yogeshmpawar@dawehner - Thank you so much for reminding me.
please check the interdiff.
Comment #203
mile23Thanks for catching my typo. :-)
Comment #204
klausiall unit tests should extend UnitTestCase.
I don't agree that this is the best solution (I would still just autoload everything in the tests directory, your explanations don't convince me). But if this is an acceptable compromise I would RTBC this for the sake of making progress.
Comment #205
dawehnerDo they? Sorry for my snarky comment, but at least for me this really doesn't matter. If you don't use anything from it, why should you use it.
I was also wondering the other day: Should we convert UnitTestCase into a single trait you could use?
Comment #206
klausiYes, they should extend UnitTestCase for consistency sake. The setUp() method of UnitTestCase covers some common pitfalls that might be hard to figure out otherwise once you expand your unit test.
Comment #207
mile23A unit test is supposed to be isolated. If you inherit the behavior of a
setUp()you don't need, then you break the isolation of that test.If we discover that we need the behavior of
setUp()later, then we can just switch base classes.Also,
UnitTestCasehas a problem, in that it always calls code in\DrupalandFileCacheFactory. This means that everyUnitTestCasetest that doesn't set up a@coversfor every test method now also inadvertently covers\DrupalandFileCacheFactory. This means that coverage metrics aren't accurate. #2626832: Figure out how to check for unintentionally covered codeIn this specific case, we don't need or care about
\DrupalorFileCacheFactory, and the state of those objects shouldn't have any bearing on the test. We only care about whether the trait is discovered. If the fact that we didn't clear the pseudoglobal\DrupalinsetUp()breaks our test later, then that's not an indication that this test should have usedUnitTestCase. It's an indication that subsequent code has broken our expectation that there's no dependency between this test's behavior and\Drupal. Then we'll learn something useful trying to fix that.As for the rest: In #198 I'm not trying to convince anyone that a
Traitsnamespace is the best solution. I'm showing how theTraitsnamespace is the best solution given what the maintainer said. If there's a better solution then yay. If there's more clarification from the maintainers about a better way, then yay. Do those instead.Comment #208
klausiThe problem is that the container is globally accessed in many places via \Drupal - if you don't clear the container in setUp() you might get unexpected test passes when your unit test grows bigger. Unfortunately at this point we need to accept that \Drupal is called in far too many places and we need to cope with that - by using UnitTestCase amongst other things. Otherwise you get into weird behavior with unit tests, and you only learn something after hours of debugging.
That's why using \PHPUnit_Framework_TestCase sets a bad example in Drupal, because Drupal is not as modern as we want it to be yet.
Anyway, I should probably open a dedicated issue for that. We are wrongly using \PHPUnit_Framework_TestCase in other places as well, so this should probably not hold up this issue.
RTBC otherwise!
Comment #209
klausiOpened #2824313: All unit tests should use UnitTestCase base class.
Comment #211
klausiLooks like a random test failure, queued all environments again.
Comment #212
xjmSo, 5 of 7 environments above show random test failures, but all of them can be accounted for with the known failures. Starting to think @klausi is cursed. ;)
Requeuing them again...
Comment #214
MixologicSorry, most of those rando failures were my fault. Deployed a new version of jenkins/ec2 plugin yesterday, which broke all the things.
Comment #215
mile23Updated IS. We still need a change record for this, because it changes the way the tools work.
Comment #216
mile23Comment #217
mile23Setting to NR because the testbot results are all green.
Comment #218
taran2lI agree with @klausi comment #204. Why not to autoload everything? What's the harm? I don't want to be forced into a certain test configuration, especially in contrib code.
Comment #219
taran2lAlternative patch (aka autoload everything)
Comment #220
mile23See summary in #191, which harkens back to #123.
Basically autoloading test classes is a bug in test-land, but we have to make a concession for test traits if we're going to allow them.
Comment #221
taran2l@alexpott patch in #123 allows for autoloading everything, but introduces new test suite called Unclassified (if test is not in one of the 4 test suites defined by core). I think it's much better approach.
And I do not understand why tests autoloading is a bug. Could you provide an example?
Comment #222
mile23Comment #223
taran2lOK, slightly modified patch from #200
Could we have it in core ASAP?
Comment #224
jonathanshawRTBC per #208
Comment #225
jonathanshawMy bad, we still need a change record.
Comment #226
taran2lI can try writing a change record for this issue.
Comment #227
taran2lAdded a change record https://www.drupal.org/node/2848889. Please review
Comment #228
jonathanshawCR looks good.
Comment #229
alexpottAs discussed many times I really don't like changing the autoloader for testing. Why? because autoloader bugs are hard and having different things autoloadable makes this harder. It is just not the same environment which breaks the idea that the test should not take place in a different environment. Another reservation is that utility of sharing traits between different test types is increased because we have both Simpletest and Functional tests - but in some future we don't. Also traits for programatically creating nodes etc really shouldn't be used in Functional and Simpletest tests because the whole not using the browser to do everything in these tests has caused lots of issues in the past. If the point of these tests is to test what a user does then we should only do things a user can do.
That said the current implementation of this issue is a good compromise between having autoloadable test code and consistency between the different methods of running tests and my reservations are all about the wider issues of how and what we test in Drupal. Therefore I think we should commit this and move on.
Committed and pushed 612665b to 8.4.x and 1abde91 to 8.3.x. Thanks!
Code style fixes.
Comment #232
drunken monkeyI think the change record needs to more clearly state that classes (or interfaces, or traits) directly within
tests/srcwon't be found anymore. Currently, it sounds (to me) like this is only relevant for traits and test classes.Comment #233
xjmComment #234
xjmTagging and temporarily reopening for #232. Please mark fixed again once the CR is updated. Thanks!
Comment #235
xjmComment #236
mile23Updated the change record: https://www.drupal.org/node/2848889
Also some documentation:
Comment #237
mile23