Problem/Motivation
Drupal core benefits from proper deprecation testing use @trigger_error(...
- see https://www.drupal.org/core/deprecation.
Contributed modules also want to leverage the same system for testing to prove they are up-to-date with the latest API changes and to handle deprecating their own code. However this is tricky because a contributed module typically has to support the current patch branch, the future development branch and the old patch branch which is still security supported. As of 25th Sept 2018 that's 8.5.x, 8.6.x and 8.7.x. If core adds a new deprecation in 8.6.x and new code to support that deprecation (eg. #2996789: Deprecate Drupal\field\Tests\EntityReference\EntityReferenceTestTrait) then it is not possible to have passing tests on all three branches. In 2996789 the contrib maintainers argue for the deprecation to be removed from 8.6.x but this only pushes the problem down the line and they'll still have fails in 8.7.x.
Possible solutions
- Only add deprecation messages to core at a later date. I.e introduce the new code path in 8.6.x and only commit the deprecation
@trigger_error()
in 8.8.x. That way when 8.8.x starts to trigger the deprecation the 8.7.x (patch) and 8.6.x (security) branches have the code and everyone can easily move. @alexpott thinks this is a terrible solution because it means new usages of deprecated code will be added to core and we will forget to add deprecations. - We could do solution 1 and mitigate @alexpott's concerns by opening a future deprecation branch (eg. 8.8.x) to commit deprecation messages to but that will increase complexity for everyone working on core especially core maintainer. And @alexpott still thinks this is not a good solution
- Contrib needs a way to say ignore deprecations when testing against these branches. So for example a contrib module could disable deprecation testing against the patch and security branch (now 8.5.x and 8.6.x) but enable it against the future dev branch (8.7.x). That way the contrib module can prove they work against the currently support Drupal branches but also be aware of future work coming up. Additionally it would be great to be test any matrix the contrib author would like. I.e. to have testing with and without deprecations against the future dev branch.
Proposed resolution
Remaining tasks
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#11 | 3002148_11.patch | 3 KB | Mile23 |
Comments
Comment #2
alexpottThis is probably not the correct queue for this issue. Will ping @Mixologic and @Mile23
Comment #3
Mile23Adding in at least some of this flexibility is what DrupalCI issue #2951375: Allow for build targets is about.
Comment #4
Wim Leers#3001958: 4 test fails due to using deprecated code on 8.6 and 8.7 since #2996789: temporarily fork the test trait is the contrib project issue that triggered this. It worked around the problem by duplicating/forking core code into the contrib module so that it could pass tests on 8.5, 8.6 and 8.7 with deprecation testing turned on.
Comment #5
BerdirWhat if we treat deprecation messages similar to coding style violations?
We report them separately, additionally, specifically *changes* to the branch test on patches. So you can see it, but deprecations would not fail your test.
Comment #6
Berdir> could pass tests on 8.5, 8.6 and 8.7 with deprecation testing turned on.
Because there should not be a requirement to do this. Deprecations are by design introduced in arbitrary minor versions and you can only use the new stuff if you no longer support the old ones.
Even more so now with the new security policy of supporting two minors.
Comment #7
Mile23Ideally we'd have a way to report deprecations without failing, but we don't.
I still think we should have more flexibility in drupalci, but some conversations on slack are bringing me around to the idea that contrib shouldn't concern itself with deprecations other than when they show up in the lowest supported core version. Theoretically, contrib should never have to change based on deprecations until preparing for D9.
Also, if we had proper semver you could just make a new branch, target it to core 8.5.x (instead of 8.x), release it as jsonapi 8.x-8.5.0, and be done. #1612910: [policy, no patch] Switch to Semantic Versioning for Drupal contrib extensions (modules, themes, etc)
Comment #8
Berdir> Ideally we'd have a way to report deprecations without failing, but we don't.
I know we don't, I'm just saying that it might be easier to build a system like the one we have for coding style violations instead of a complex matrix based test system, not least because people like to just test everything with everything then.
> Also, if we had proper semver you could just make a new branch, target it to core 8.5.x (instead of 8.x), release it as jsonapi 8.x-8.5.0, and be done.
It's trivial to release jsonapi-8.x-1.whatever and combine that with a dependency on 8.5.x. Works with both composer dependency as well the version checks of Drupal core. The only thing you can't do at the moment is require e.g. 8.5.3. And 8.x-1.2 is already exposed to composer as 1.2.0
> are bringing me around to the idea that contrib shouldn't concern itself with deprecations other than when they show up in the lowest supported core version
Yep. Which is interesting too, in a way. All our deprecation messages contain the version. What if we'd somehow add an environment variable that can be set somehow that would filter out deprecation messages of later versions. Could even be based on the existing version information that we have of a project. So if module X doesn't declare an explicit version dependency on core, we'd not report any deprecation messages, and if it says "drupal:system (>=8.5)", like pathauto for example does, we could say that we can report deprecation messages except those that contain 8.6.x or higher.
Comment #9
Mile23This is an assumption you might want to check. :-)
We could theoretically do a @deprecated diff between releases, store deprecation error messages in a lookup table, and use that database to filter for targeted core based on info file. Even that analysis would be broken because not all error messages are static, and the mapping between @deprecation and @trigger_error() is not 1:1 even when they're both present.
Or, we could add a way to surface deprecation messages without failing tests by adding something like
run-tests: suppress-deprecations: report-only
to drupalci.yml. This puts E_USER_DEPRECATION in the realm of coding standards, in terms of allowing maintainers to have different policies.Seems like the latter is more doable.
It also helps us figure out a way to say that a contrib module is 'D9-ready', because d.o could run all the tests to a spec and then add a banner to projects that pass tests and don't report deprecations.
Comment #10
Wim LeersThat means contrib modules will not be ready when D9 ships, which is the opposite of the intent here.
+1
Oohhh!
Oohhh!
Yes! 🎉
Comment #11
Mile23This is a modification to
DeprecationListenerTrait
. It looks for an environmental variable calledDRUPAL_PHPUNIT_DEPRECATIONS_ARE_WARNINGS
and turns deprecations into warnings in the test results.Warnings can be configured to fail a test run, but they normally don't.
Looking for some nice easy deprecations to log, I commented out the guardrails around @groups legacy within
DeprecationListenerTrait::deprecationEndTest()
. Then I ran PHPUnit like this:DRUPAL_PHPUNIT_DEPRECATIONS_ARE_WARNINGS=true ./vendor/bin/phpunit -c core/ --testsuite unit --log-junit results.xml --group legacy
We see warnings like this when we run the test at the command line:
These would only show up in a DrupalCI run if you looked at the test artifacts, which isn't visible enough.
Luckily, in PHPUnit 6+, JUnit reports have a warning tag. (I'm pretty sure PHPUnit 5 or less doesn't.)
Then we can go over to the d.o integration and figure out a way to show warnings in additions to fails. We could have it look for the keyword 'E_USER_DEPRECATION', or define a deprecation warning class and filter by warning type.
And we'd also force
DRUPAL_PHPUNIT_DEPRECATIONS_ARE_WARNINGS=true
for all contrib tests, and make sure it's not set for core.Note also that this patch will fail
Drupal\Tests\datetime\Unit\Plugin\migrate\field\d6\DateFieldTest::testUnknownDateType
because that method is annotated@runInSeparateProcess
. Apparently somewhere between our process-isolated stuff and symfony's process-isolated stuff, we don't take that into account.Comment #13
alexpottNote that Symfony's deprecation handler has this kind of built in. There's a weak mode. We could try adding a new flag to run-tests.sh for this or enhancing --suppress-deprecations. I think one issue is then it only counts deprecations instead of doing full reports but maybe this hints that we should attempt an upstream PR to have an extended weak mode that does a complete report but does not fail the test.
Comment #14
Wim Leers#13++
Comment #15
Wim LeersRelated: https://github.com/symfony/symfony/issues/27936 + https://github.com/symfony/recipes/pull/442 + https://github.com/symfony/symfony/issues/28048 (on which @alexpott was pinged, cool!).
Comment #16
jibranIf we allow drupalci to move the file created at
DRUPAL_EXPECTED_DEPRECATIONS_SERIALIZE
path to build artifacts then we can add a coding violation kind of warning to the build result. This will allow us to enable theSYMFONY_DEPRECATIONS_HELPER
by default for all projects.Comment #17
Mile23What a silly thing to say. :-) Tests don't fail, PHPUnit doesn't say, 'fail!', but it exits with 1.
Restating the use cases:
1) You can run core tests and you want tests to fail due to deprecations. That's the problem we've already solved for.
2) You can run contrib tests where you normally don't want to know about deprecations. We've already solved for not knowing. :-)
3) You can run contrib tests where you always find out about deprecations. Alter your branch's drupalci.yml to tell you. See https://cgit.drupalcode.org/examples/tree/drupalci.yml (Examples is a special case where we want to exemplify code, so it should always be up-to-date. Other contrib projects will have different BC needs.)
4) You want to run contrib tests where you know about deprecations but don't fail your branch tests or issues. We don't have this in a structured way, but you can make an issue that patches drupalci.yml and then run it from time to time and find out what you're missing.
So for case #4 we want to surface the information, much like we do CS and test fail info. You can do one click and find the results on a page like this: https://www.drupal.org/pift-ci-job/1082800
That page shows fails, and it also says "12 coding standards messages", although the link there is a little non-functional. Ideally, we'd have a similar notice: "35345 deprecation messages...." and you could click it and see some document about it.
In order to have a DrupalCI document about it, we have to make a log file artifact, or deduce one based on other log files. This is where I was headed in #11.
Currently, symfony/phpunit-bridge only gives us the ability to fail tests based on deprecations, and not the ability to log deprecations, whether or not they fail the tests. None of the issues in #15 look like they want to give us that.
#16 is on the right track, though there are problems: Mainly that we want
SYMFONY_DEPRECATIONS_SERIALIZE
instead, and the fact thatSYMFONY_DEPRECATIONS_SERIALIZE
only catches process-isolated tests:That's why I added to our test listener in #11, so we'd catch all tests, including plain-vanilla unit tests.
So if we have an upstream issue where we maybe add some kind of result to the
Result
object in a listener, which only optionally fails the test, and which adds that to the JUnit that's eventually exported, then we'll have something that meets our needs.Otherwise we need #11 plus more better ideas. :-)
Comment #18
MegaChriz CreditAttribution: MegaChriz as a volunteer commented@Mile23
Is 4) about deprecations in vendor libraries? Because when I run a kernel test locally on the command line, I see for example the following:
Looks to me like it should somehow already be possible to grep these deprecation messages. And then upstream changes to Symfony/PHPUnit won't be absolutely necessary. I have little to no knowledge about DrupalCI, so maybe what I'm saying here is really hard to do.
Comment #19
jibranI wrote a blog post about it https://www.previousnext.com.au/node/660.
Comment #20
alexpott@jibran hmmm but the problem is that you're telling contrib authors to get themselves into the state that they can't support the current supported branch ala jsonapi. We're not ready for this yet :(
At the very least the blog post needs to be updated with the nuance from this issue.
Comment #21
jibranRE #20: DrupalCI always runs contrib tests against the latest core branch. As a contrib module maintainer if I have to make a compatibility change for core minor version then I create a new release and add that to releases notes after the stable release of core minor version e.g. https://www.drupal.org/project/dynamic_entity_reference/releases/8.x-2.0.... I never have to create a new release for the core patch release at least till now but yes I don't know how would I name the new release if I ever have to do that but then again that's contrib semvar issue.
My point is contrib module current version should always support the core current supported branch(8.6.x) and tag a new release, if need be, as soon as the core release the new version till then keep all the latest core branch(8.7.x) changes in dev release. At least that's what I'm doing for the modules I maintain in contrib.
Comment #22
Mile23Why do you have to?
There's always a BC layer, and if there isn't then that's a bug in core.
So you could, say, make an issue patch that turns on deprecation fails and run that patch against the previous core minor. Now you know your technical debt for your current release, so you can work on fixing that in your current dev.
That's good because current core dev's deprecations are a moving target, while the previous minor is not.
Comment #23
Mixologicalso re: #21 DrupalCI supports testing against any core branch thats open, as well as against the latest stable release. It does not always run against the latest core branch.
Comment #24
catchComment #25
Gábor HojtsyComment #27
andrewbelcher CreditAttribution: andrewbelcher at FreelyGive commentedAnother slight curve-ball the throw into the mix here is upstream deprecations when using a composer based workflow (such as
drupal-composer/drupal-project
which I understand is the recommendation). As well as affecting any project level tests, this seems to be the 'go to' method for running crontrib tests outside of drupal.org's CI (e.g. github or gitlab), where you create a drupal project and include the module, then runs it's tests.With that workflow, the dependencies of Drupal are included via
drupal/core
'scomposer.json
. This means that you don't run against the exact dependencies in core'scomposer.lock
and as a result can hit deprecations in it's dependencies.The example I've just hit is that
symfony/psr-http-message-bridge
yesterday released1.2.0
which matches core's dependency of^1.0
and so is installed, but deprecatesSymfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory
which is still used in core.Comment #28
Gábor Hojtsy@andrewbelcher yeah core has a list of deprecations it ignores for those cases.
Comment #29
andrewbelcher CreditAttribution: andrewbelcher at FreelyGive commented@Gábor Hojtsy is that something committed in core, or part of drupal.org's test infra? It would be quite handy to have some visibility of that.
Comment #30
Gábor Hojtsy@andrewbelcher See https://api.drupal.org/api/drupal/core%21tests%21Drupal%21Tests%21Listen...
Comment #31
Gábor HojtsyThe key here is multiple branches, as deprecation testing by itself is already supported.
Comment #32
Gábor HojtsyReparenting.
Comment #37
xjmPosted #3225792: Allow core deprecation testing to distinguish between major versions which is not quite the same, but related.