Motivation
This is a followup to #1271060: Allow tests to declare 'requirements', skip tests where requirements are not met. There is still a need for tests to be able to define requirements. However, as discussed on irc, it is still necessary to show failed tests (and allow them to run and show a failed status).
Currently, TestDiscovery::getTestClasses()
checks the annotations of all test classes and rejects tests which have @requires module
or @dependencies
for modules which do not exist on the filesystem.
This behavior has tests in the existing codebase, so it is supported and should be maintained.
This behavior should not be the responsibility of TestDiscovery
. It should be the behavior of the test class itself, which can mark the test as skipped. This way, the user can better understand why a test did not run.
This behavior is being moved to the test classes for PHPUnit-based tests in #2728579: Explicitly skip @requires module in PHPUnit Kernel and Browser tests
We should move the behavior for Simpletest-based tests to \Drupal\simpletest\TestBase
in this issue.
TestBase already has a checkRequirements() method. This allows test classes to do whatever they want WRT requirements. However, this does not allow for annotations.
Proposed Resolution
Create a patch that does the following on a given test class:
- Check the annotated requirements
- If there are any missing requirements, list them and mark the test as skipped. Otherwise, continue on and run the tests.
Annotation requirements checks should follow the same API as TestBase::checkRequirements()
, returning an array with messages about missing requirements.
Also, this check can not occur in TestBase::checkRequirements()
because subclasses can override and might not call the parent method.
So a new method should be created which can determine the requirements based on annotation. In order to avoid adding features to Simpletest (which is essentially in maintenance-only mode), this method will be final
and private
, so that it's the only code which defines how Simpletest interacts with annotations, and is not an API addition. See PHPUnit initiative: #2807237: PHPUnit initiative
API Additions
None.
Remaining Tasks
Comment | File | Size | Author |
---|---|---|---|
#85 | 1273478-nr-bot.txt | 132 bytes | needs-review-queue-bot |
#1 | 1273478_1.patch | 3.41 KB | BTMash |
#7 | 1273478_7.patch | 3.27 KB | BTMash |
#13 | 1273478_13.patch | 4.77 KB | BTMash |
#16 | 1273478_16.patch | 6.89 KB | BTMash |
Comments
Comment #1
BTMash CreditAttribution: BTMash commentedAttaching patch.
Comment #2
BTMash CreditAttribution: BTMash commentedChanging to feature request as well. Not sure why the test is not running.
Comment #3
Alan Evans CreditAttribution: Alan Evans commentedHi there ... Thanks for the patch, I rather like this feature. Here's what I've tested so far:
On the whole, it's looking good, but I'm not hugely keen on this line:
As what it's checking ultimately is just the same as:
Why not just go with the shorter version? (It won't emit notices)
Also this line:
Is actually covered by the default in DrupalTestCase::insertAssert (default is 'Unknown', likewise default line number is 0, so we shouldn't need to set either of these - I'd recommend removing both the function and line number here. I've just tried doing that, and the result is the same, except that the assert shows the default function name of "Unknown" instead of "n/a")
Comment #4
matason CreditAttribution: matason commentedHey @btmash, just reviewed your patch, my only suggestion would be simplify the return value of checkRequirements - perhaps have it return an array only, an empty array by default indicating no problems and alternatively an array of errors if any requirements are not met. It'll make your logic on line 47 of the patch simpler and reduce a possible element of confusion. Perhaps needs a little more on the usage in method comment block too.
Comment #5
matason CreditAttribution: matason commentedAh, missed your post @Alan Evans, setting to needs work.
Comment #6
Alan Evans CreditAttribution: Alan Evans commentedYep, I'd tend to agree - making that return value always an array would be a good way to simplify things
Comment #7
BTMash CreditAttribution: BTMash commentedThanks for the thorough review :) I've made the changes put forward by Alan and I've made the return value be an array as recommended by matason.
Comment #8
Alan Evans CreditAttribution: Alan Evans commentedThanks for the update! Checks done:
Was going to do a full test run with and without checkRequirements, but I realise this is going to take hours on my laptop, so I think we'll just trust the bot.
One slight annoyance is that the batch test run reports fails 0, passes 0, exceptions 0 when failing on requirements, but the end test result report is correct. I don't think that's anything to do with your patch though - the other DrupalTestCase::insertAssert in prior code would do exactly the same thing (the one that checks for completion of individual tests). It looks to me like that's just how insertAssert is implemented that means that the batch runner doesn't know about the fail until the end.
Going to mark this RTBC for now, can always go back if there are objections.
Comment #9
sunSorry, I need 1) a better issue summary here, and 2) a better explanation of what is being changed and why (which circles back into 1)). Right now, I can't make any sense of this patch.
Comment #10
BTMash CreditAttribution: BTMash commented@sun, let me know if this works as an issue summary.
With the introduction of #1182290: Add boilerplate upgrade path tests (the test requires the zlib extension), tests which may have requirements outside of module dependencies are being introduced. @matason introduced #1271060: Allow tests to declare 'requirements', skip tests where requirements are not met which would mean that tests could have requirements. However, the issue was closed since @webchick, @davereid, and a few others felt that tests with requirements should not get skipped from being displayed to the user and it would be better to fail instead (the user has the option of not enabling a test to run).
The aim of this issue is to focus on allowing tests to have requirements and allow them to fail if someone tries to run them. This has been separated from the prior issue after discussion with @webchick. What the patch currently does:
checkRequirements()
. By default, this returns an empty array (an empty array implies no errors). For any other test classes that implement the function, they can return an array of errors for any unmet requirements.setUp()
),checkRequirements
is called to see if the class has any unmet requirements. If the array is *not* empty, the list of unmet requirement errors are added to the test summary and the tests are not run. Otherwise, it will run the tests and output any success/errors from the tests as necessary.Comment #10.0
BTMash CreditAttribution: BTMash commentedUpdated code for check requirements.
Comment #10.1
BTMash CreditAttribution: BTMash commentedAdd an issue summary.
Comment #11
BTMash CreditAttribution: BTMash commentedUpdated first post in issue summary format.
Comment #12
sunThanks for the improved summary.
Looks like this patch
1) needs more in-depth reviews from simpletest/testing-framework maintainers but also OO-masters, and
2) could use an example implementation (I'm quite sure we already have tests in core that contain an early-return based on an environment condition)
Comment #12.0
sunWrong post.
Comment #13
BTMash CreditAttribution: BTMash commentedI am attaching a patch that tackles (2) since I am no OO-master :) The ImageToolkitGdTestCase requires GD to be enabled. Right now all the tests skip (yet show a passing grade). The requirements would now make the test fail and skip all the tests within the test case.
Comment #14
Dave ReidYes, the test should fail if GD is not installed rather than skip especially since core only ships with gd support.
Comment #15
BTMash CreditAttribution: BTMash commentedOk, great! As per sun's requests, are there any thoughts for the checkRequirements() implementation?
Comment #16
BTMash CreditAttribution: BTMash commentedI'm updating this patch to now have 2 examples.
1) The image GD testing requirement.
2) The D7->D8 upgrade tests zlib requirement. In this case, we already have an early fail occurring to stop the tests from running. However, with the missing checkRequirements(), we currently have a variable being tracked through the testing process (and thus run the parent class setup/teardowns since the upgrade test class implements its own setup/teardown). This greatly simplifies the code for that portion.
Comment #17
Lars Toomre CreditAttribution: Lars Toomre commentedI like this patch and thinks it makes great sense. However, I am neither a simpletest/testing-framework maintainer nor a OO-master.
The only issue I noticed was an incorrect indenting of the word Array below @return line.
While awaiting review from others, it make sense to identify the other cases in existing tests where this new function could be used. I would not code them up yet in the patch, but the information will be helpful for when the final patch is rolled and hopefully applied.
Comment #18
BTMash CreditAttribution: BTMash commentedOk, I've updated the patch with the minor change to the comment. And agreed - no more examples until someone can say if the code is any good (I wanted to show one example where this clearly makes sense).
Comment #18.0
BTMash CreditAttribution: BTMash commentedNew patch with updated examples.
Comment #19
xjmLooks like the summary's been added; untagging.
Comment #20
Alan Evans CreditAttribution: Alan Evans commentedI've taken another look over the latest patch and here are the results:
Remaining TODO:
Comment #21
Alan Evans CreditAttribution: Alan Evans commentedTurns out the patching issue was a spelling correction in a comment in upstream which happened to coincide with one of the context lines of the patch.
Here's a rerolled version of the patch. The only changes are:
Comment #22
BTMash CreditAttribution: BTMash commentedI just took a look through the patch; the protected change sounds good to me. I like the idea of using extension_loaded (did not know about it; makes more sense to me) instead of checking for a particular function. So +1 (^_~) from me.
Comment #23
BTMash CreditAttribution: BTMash commentedI'm unassigning this from myself so someone else knows to step in to do some sort of review or whatever else is necessary.
Comment #24
xjmThanks for working on this patch. This will need to be rerolled on account of #22336: Move all core Drupal files under a /core folder to improve usability and upgrades. Also, here are a few minor points about the patch's code style.
The docblock summaries should use a third-person present indicative verb (in this case, "Checks" instead of "Check.) We're currently cleaning this up as a part of #1310084: [meta] API documentation cleanup sprint, so we should make sure new dockblocks agree with the standard as well.
Also, there should be two spaces of indentation after @return, like so:
Tagging as novice for the task of rerolling the Drupal 8.x patch and making the code style corrections above.
If you need help rerolling this patch, you can come to core office hours or ask in #drupal-gitsupport on IRC.
Comment #25
kathyh CreditAttribution: kathyh commentedApplied d8 /core patch change. In some of the files, D8 path updates in the code affected patch apply. Modified patch to include those references to /core/includes/update.inc' and /core/update.php'
Added updates per #24
Comment #26
xjmPatch looks good to me. Do we also need to add a test to simpletest's self-tests that tests whether missing dependencies properly cause failed tests? :)
Comment #27
BTMash CreditAttribution: BTMash commentedHere is an updated patch with a test.
Comment #28
BTMash CreditAttribution: BTMash commentedGet rid of whitespace.
Comment #29
BTMash CreditAttribution: BTMash commentedCleaned up the test a little.
Comment #30
xjmThanks @BTMash. Can you also upload the test separately to expose its failure? (If you put the test-only patch first and the combined patch second in the same comment, testbot won't set it to NW for the intended failure.)
Comment #31
xjmRight, so this is new functionality and therefore exposing test failure with a separate patch doesn't really make sense. Sorry. :)
Comment #32
BTMash CreditAttribution: BTMash commentedOn recommendation from @xjm, changing the category to task.
Comment #33
boombatower CreditAttribution: boombatower commentedSeems like a solid plan. We had a brief conversation in IRC and all seems well.
Comment #34
boombatower CreditAttribution: boombatower commentedComment #35
catch#29: 1273478_29.patch queued for re-testing.
Comment #37
BTMash CreditAttribution: BTMash commented#29: 1273478_29.patch queued for re-testing.
Comment #39
xjmI'm betting it needs to be rerolled for #1212992: Prevent tests from deleting main installation's tables when parent::setUp() is not called.
Comment #40
BTMash CreditAttribution: BTMash commentedI've rerolled the patch. Hopefully, it works out :)
Comment #41
chx CreditAttribution: chx commentedPlease change $access to $missing_requirements and the $error accordingly to $missing_requirement and then feel free to RTBC, it's just a variable rename, I am fine (and so was Jimmy above) with the patch.
Comment #42
BTMash CreditAttribution: BTMash commentedI've made the necessary changes. Hopefully ready for review. Adding interdiff as well.
Comment #43
xjmThanks; this variable naming makes the code clearer and more self-documenting.
Comment #44
catchCommitted/pushed to 8x, thanks!
Will need a re-roll for 7.x (but might want to wait for table deletion issue first to save double re-rolling)_.
Comment #45
xjmHere's a D7 backport. Not a clean reroll so NR. (It does at least have the same number of fingers and toes.)
Comment #46
catchLooks good to me.
Comment #47
sunHm. I really wonder why we didn't make checkRequirements() a static method.
Right now it's not, which means you need to instantiate the test first, in order to figure out whether it can be run in the first place. Whereas we can discover tests and get their info without instantiating. That slightly contradicts the meaning of "requirements."
I'd actually prefer to bump this back to D8 and make that method static. Potentially also consider to auto-invoke it directly from __construct() and throw an exception, so code that doesn't check the requirements correctly blows up.
Comment #48
BTMash CreditAttribution: BTMash commentedI'm ok with this going either way though I'll need to get a better understanding of the static method calls and if we can override them on a per-class basis.
Comment #49
sunLooked at this again, and it seems like the intention is to make tests with unmet requirements explicitly fail.
This still means that such test classes are (successfully) instantiated and attempted to run. Whereas I'm pretty sure that we're able to check and return requirements either even before instantiating, or right within the class instantiation (i.e., __construct()). Architecturally, that would make more sense to me.
Aside from that, I don't really get why we're re-inventing Exceptions with a custom array of error strings, which are manually inserted as errors into the test log afterwards?
However, I don't want to hold off progress for Testing system improvements... the only reason for raising the concerns here and not in a new issue is that we're about to backport this API addition to D7, and so, while we can possibly adjust D8 afterwards, that's not going to be possible for D7 anymore.
---
To merely outline the direction I mean, see attached patch. It's just a start to show the approach using exceptions, and doesn't involve __construct(); it's not a final patch in any way. Left indentation and whatnot alone, so it's easier to see the functional changes.
Comment #51
BTMash CreditAttribution: BTMash commentedHmm, I like the idea of throwing exceptions instead of doing the thing I did. I'll try out what can be done during the instantiation and if it will behave nicely but if not, then atleast work with what you have in your approach.
Comment #52
tim.plunkettRecategorizing.
Comment #53
sunThe current implementation throws a test failure when requirements are not met, but that is bogus and also insufficient. A test that lacks requirements neither means success nor a failure. #843114: DatabaseConnection::__construct() and DatabaseConnection_mysql::__construct() leaks $this (Too many connections) requires a way to skip a test when not running on MySQL.
Took me some time to find the docs for the corresponding functionality in PHPUnit:
http://www.phpunit.de/manual/current/en/incomplete-and-skipped-tests.html
So scratch the exceptions approach I mentioned earlier; what we need is:
Table 9.2. API for Skipping Tests
void markTestSkipped()
void markTestSkipped(string $message)
$message
as an explanatory message.Comment #54
tim.plunkettComment #54.0
tim.plunkettupdating summary.
Comment #55
sunMinimally revising this issue to account for the latest and greatest efforts.
One of my topmost/primary concerns is that the current test runner code simply skips classes with unmet dependencies during the test discovery already, so you do not even see that additional tests may exist but might have been skipped; i.e., they're not even listed as available tests - the test runner simply presumes they wouldn't exist at all.
My plan is to introduce full parity for Simpletest tests with PHPUnit tests in D8. That not only means that you'll be able to specify
"@requires PHP 5.5"
or"@requires extension curl"
, it also means that Drupal module dependencies will be handled in the same way; i.e.,"@requires module libraries"
.If an environment requirement is not met when a test is attempted to be run, the test will be marked as "skipped".
Next to custom Drupal extension type requirements, the goal is to fully adopt/resemble PHPUnit's
@requires
annotation for skipping tests.All of that happens at test execution/runtime, so it doesn't affect PIFR - aside from new assertion result categories. I briefly talked to @jthorson about necessary changes on that front already.
In concrete terms, this means that the list of assertion categories in
TestBase::$results
will be extended by "skipped", "incomplete", and "risky" — fully adopting all of PHPUnit's possible assertion result categories.The meta data will be delivered by
TestDiscovery::getTestInfo()
, by courtesy of #697760: Replace getInfo() in tests with native phpDoc + annotations (following PHPUnit)We may also consider to add explicit corresponding methods to
TestBase
(cf. #53), but that may also happen later and is not a priority for now.Postponing on the parent issue.
Comment #56
moshe weitzman CreditAttribution: moshe weitzman commentedComment #57
tim.plunkettComment #58
mgiffordJust unassigning issues that haven't been developed for a bit in the D8 queue.
Comment #66
Mile23Related: #2728579: Explicitly skip @requires module in PHPUnit Kernel and Browser tests which checks for @require module on phpunit-based tests.
Comment #67
Mile23Comment #68
Mile23Comment #69
Mile23Rescoping a bit because the IS was way behind the times. Moving to 8.4.x because, even though it's a testing improvement, it's disruptive.
This patch does not mark tests as skipped, because Simpletest doesn't know what 'skipped' means (yet). Scope for adding a skipped status to Simpletest can be figured out here. Should it be in the patch? Should it be another issue that blocks this one?
This patch is a WIP. Please comment. :-)
Comment #70
Mile23Comment #73
Mile23Duplicate: #2294379: Tests with dependencies do not show up if the dependencies are present
Comment #74
Mile23#2905007: Allow run-tests.sh to report skipped/risky/incomplete PHPUnit-based tests adds some infrastructure to run-tests.sh to support skipped tests. That will make it easier to support them here eventually.
Comment #85
needs-review-queue-bot CreditAttribution: needs-review-queue-bot as a volunteer commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #87
mondrakeWith PHPUnit 10 getting extremely strict about the use of annotations to only those they support, this is pretty much outdated now.