Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Beta phase evaluation
Issue category | Task because not fixing bugs, only improving maintainability. |
---|---|
Issue priority | Critical because blocks #2429257: Bubble cache contexts, which is a critical performance issue. |
Unfrozen changes | Unfrozen because it only changes tests. |
Disruption | Zero disruption. |
Problem/Motivation
Drupal\system\Tests\Common\RenderTest
uses KernelTestBase
.
Since #2346937: Implement a Renderer service; reduces drupal_render / _theme service container calls, it can be converted to PHPUnit tests.
Proposed resolution
Convert to PHPUnit tests
Remaining tasks
TBD.
User interface changes
None.
API changes
None.
Comment | File | Size | Author |
---|---|---|---|
#37 | interdiff.txt | 660 bytes | dawehner |
#37 | 2378883-37.patch | 133.24 KB | dawehner |
#35 | interdiff.txt | 8.29 KB | dawehner |
#35 | 2378883-35.patch | 133.24 KB | dawehner |
#33 | 2378883-33.patch | 135.07 KB | Wim Leers |
Comments
Comment #1
Wim LeersNow unblocked :)
Comment #2
Wim LeersComment #3
dawehner@zealfire asked in IRC, so here is a first example of a conversion.
It converts the existing
RendererTest::testDrupalRenderSorting()
test into two test methods. (see, that is pretty much c&p)So c&p the existing test method, replace drupal_render() with $this->renderer->render() and hope it works :) Don't even try to convert
the ones which deals with cache atm., they neeed some work.
The other functions though seems to be less simple.
Comment #4
daffie CreditAttribution: daffie commentedFor the testbot.
Comment #5
tadityar CreditAttribution: tadityar commented@dawehner is this part of a META?
Comment #6
dawehnerNot that I know of.
Comment #7
dawehnerBut just to be clear, there is a lot to do, in order to be able to convert all those existing tests.
Comment #8
dawehnerAs its turns out access_callback is broken.
Here is some more conversion ... far from being finished. The case with #cache still needs to wait, obviously.
Comment #9
dawehnerAlright, its converted as much I think we can convert it for now.
Comment #10
Wim LeersYay! Once #2379741: Add Renderer::getCacheableRenderArray() to encapsulate which data is needed for caching a render array and have views use it is committed, this will be able to go further again.
dawehner++
Comment #11
dawehnerWe clearly have tests which are integration level tests, because they test both the theme manager and the renderer at the same time.
Comment #12
dawehnerRebase + Just continued with some more conversions.
@Wim Leers
Added some bugfixes to the renderer.
Comment #14
Wim LeersReviewed the code and reviewed the porting of only the
testDrupalRenderBasics()
test. Attached is a reroll that already fixes all nitpicks and slightly improves some comments, to better convey which tests cases form logical groups, hence making it more doable to compare the old test with the new test; otherwise it's hard to guarantee that I didn't overlook something.I've long considered adding this optimization also. +1 to this.
Perhaps update the preceding comment, from
toThese changes are wrong.
Run this little test script to convince yourself:
Wow. Never seen this before! Suddenly I understand why you like this issue so much :)
A bit of documentation would help though. What about:
Optionally, a closure can be provided that makes the necessary preparations to test the provided case. We bind the closure to $this, to make it run as if the mocks it creates were created right here.
Why the "link" part in this variable name? It makes sense in the next one, but not here.
The deleted code said
Url::fromUri('http:/drupal.org')
.Why this change?
"Preset" vs "Property"; let's standardize on "Property"
"AccessCallbackCallable" seems a bit much, why isn't just "AccessCallback" sufficient?
Clever!
Comment #15
dawehnerThank you for your quick review!
I am a bit confused. Let's take one example from the patch:
Let's assume we are in HEAD, and ['#acccess_callback'] is
\Drupal\Core\Entity\EntityAccess::access
. In that case the str_pos would find something like 20,which is NOT === FALSE, so we have to ask the controller resolver. Please enlighten me, but afaik, we want for all those callbacks to support
'Drupal\Core\DependencyInjection\ContainerInjectionInterface'
don't we?Well, its sad that you have to do that, but for me this seems to be the test way to make the test somehow good readable.
Well, I probably decided to go with consistency.
I went with that, because the test gets easier to read with the new version. For a unit test, the rendererTest should not care about how #type link is implemented internally.
sure.
Well, I wanted to be sure that both callable and foo::bar works.
Well, it is clever until your test code gets hard to understand.
Comment #16
dawehner.
Comment #18
dawehnerSome work in the meantime.
Comment #20
dawehnerDid some work here.
Note: I reverted most of the changes to Renderer itself.
Regarding the last remaining failure, I don't know how this could actually pass in HEAD. if you unset #cache #post_render_cache should not be executed, right?
Comment #22
dawehnerSometimes a return statement is important.
Comment #24
Wim LeersWow, great progress!
RE: that last failure in
RendererTest
:- $output = drupal_render_root($element);
$output = $this->renderer->render($element);
$output = $this->renderer->renderRoot($element);
:)
Now reviewing!
Comment #25
Wim LeersContinuing the reviewing of tests in my next comment. Wanted to post this because otherwise the interdiff will become unwieldy.
The included interdiff shows all changes, but the topical interdiffs show the steps I took to get there.
Code changes review
Could use an
@deprecated
.Fixed.
Ever since #2329101: CacheableInterface only has a getCacheKeys() method, no getCacheContexts(), leads to awkward implementations, this should actually be
['contexts' => [], 'tags' => []]
. But, what would be better, is if we'd instead check if either#cache['cid']
or#cache['keys']
is set. Because if either of those is set, then we *actually* want to do caching, hence that won't change in the future.(E.g. when max-age is added to cache metadata.)
Fixed.
This is only copied but it's actually wrong (it was right at one point but then wasn't updated when that #pre_render callback was removed).
Fixed.
Given #2364303: [meta] Formalize and define usage of #post_render_cache and the fact that we don't want break this API once it is introduced to minimize disruption… we might want to check with Fabianx which name makes most sense. I suspect just
generatePlaceholder()
will be better.(It's only a moving around of code, but technically this is also an API addition to this interface, as you will probably agree.)
Moved tests review
The following were not moved to the PHPUnit test:
testDrupalRenderThemePreprocessAttached()
: because it requires a module to be enabledThis only had code formatting nitpicks, comment nitpicks and missing
@covers
. Last but not least: it was very difficult to find your way through this long list of test cases, because they actually have logical/semantical grouping, but it's not clear. I added "Part X" comments in between to clarify the grouping.Fixed all that.
… the one other change to the basic tests that I made is the moving of these new (! — that's great1) test cases to the top of the test, where they have a more logical place.
Why? The first bunch of test cases specifically test without the theme system, and so does this.
These also cover
::doRender()
, same goes for every other@covers ::render
; adding@covers ::doRender
in all those places.Cleaned these up: their matching assert method now has docs, and sets
'#markup' => 'test'
, so that not each of these has to do that.This is a new test. Great.
It's not actually that odd. The first time, #printed = TRUE is set. Hence the second time, we don't get any output.
Updated this test accordingly.
In HEAD, the "children attached" test comes before this. So I moved it back up here. It wasn't in a better (more logical) place in this patch, so we might as well keep these things in sync.
In
::testRenderCache
&::testDrupalRenderChildrenAttached
, these can be replaced with calls to the::setupMemoryCache
helper.Fixed.
This is not specific to children or #attached, what this actually tests, is bubbling without
#pre_render
. Renamed as such:testRenderBubblingWithoutPreRender
.And renamed the existing
testRenderBubbling
totestRenderBubblingWithPrerender
.So actually, disregard the prior comment about the "children attached" test function; this *does* have a better place: together with the other bubbling tests.
This mock isn't necessary in this test (children attached still).
Removed.
Renamed this to
generatePostRenderCacheElement
and added docs.These are wrong. The intent here was to capture in the test assertions that nothing gets written to the cache. But it's still possible to write things to the null cache, you just can't get anything out of it.
Renamed it to
setUpUnusedCache
and changed the assertion to match the intent.s/WithCacheMiss/WithColdCache/
Changing this to use
::setUpUnusedCache
captures the same intent, but with simpler test code. It's more in line with PHPUnit tests.Comment #26
Wim LeersThe goal of this issue is to turn as much as possible of
RenderTest
into unit tests. Because unit tests allow us to truly test theRenderer
, in isolation.But since it is so very complex — it's one of the most complex pieces of code in Drupal core — I feel it makes more sense to spread the test cases across multiple files. One file with test cases that cover a certain aspect. That will make this much easier to review, and also much easier to maintain, and most importantly: much easier for other people to start to understand the renderer (and the involved complexities).
After all,
RendererTest
in its current incarnation is a whopping 1500 LoC.Renderer
itself is only ~600!Discussed with dawehner & Fabianx and they were generally +1, so doing that in the next reroll.
EDIT: to clarify: this is a pre-existing problem, not something introduced by this patch. But if we're doing this work to make the render system more maintainable, we might as well make the unit tests themselves maintainble too!
This is a special case of
#post_render_cache
, yet it is listed before all other#post_render_cache
tests. Does not make sense. Moved.Here and elsewhere, we don't want the space:
s/list (/list(/
. Fixed.This doesn't do anything. Removed it, and all its invocations.
Comment #27
Wim LeersThis doesn't change a single comment, or method name, or variable name. It only spreads
RendererTest
across the following files:RendererTestBase
(170 LoC)RendererBubblingTest
(200 LoC)RendererPostRenderCacheTest
(470 LoC)RendererRenderPlainTest
(150 LoC)RendererTest
(600 LoC), which now only contains the basics.This also lets us quite clearly see where the complexities lie (or perhaps where we are lacking test coverage).
Now going back to actually reviewing again.
Comment #28
dawehnerGreat split up!
Comment #29
Wim LeersRendererTest
review: finished, no more complaints. Next: the 3 remaining test files.Comment #30
Wim LeersDocs fixes, minor function name improvements and most of all: restoring of the original test logic/intent of
RenderTest::testDrupalRenderBubbling()
toRendererBubblingTest::testBubblingWithPrerender()
. Now with many more comments. Should be much more understandable than what's in HEAD.Comment #31
effulgentsia CreditAttribution: effulgentsia commentedRaising to critical and adding the blocker tag, based on #2429257-30: Bubble cache contexts.
Comment #32
Wim LeersRendererPostRenderCacheTest
review: finished, no more complaints. See below for an overview of changes.common_test_post_render_cache()
, we now havePostRenderCache::callback()
for that.common_test_post_render_cache_recursion()
, we now havePostRenderCacheRecursion::callback()
for that.RenderTest::testDrupalRenderRenderCachePlaceholder()
(from the kernel test) to\Drupal\Tests\Core\Render\RendererPostRenderCacheTest::testPlaceholder()
(unit test).RenderTest::testDrupalRenderChildElementRenderCachePlaceholder()
(from the kernel test) to\Drupal\Tests\Core\Render\RendererPostRenderCacheTest::testChildElementPlaceholder()
(unit test).common_test_post_render_cache_placeholder()
, we now havePostRenderCache::placeholder()
for that.core/modules/system/tests/modules/common_test/templates/common-test-render-element.html.twig
— that is now mocked behavior.i.e. moved the last two test cases from Kernel tests to Unit tests for which that conversion is possible.
Comment #33
Wim LeersRendererRecursionTest
review: finished, no more complaints.While cleaning up
RendererRenderPlainTest
, I noticed the name is not a perfect match.RendererRecursionTest
is better.Fixed docs.
Comment #34
Wim LeersWhile definitely not perfect unit test coverage, I feel like @dawehner's awesome work is in no way a regression, and only an improvement. The rerolls I've been posting since #25 are solely part of the review process:
RenderTest
had grown organically over the years and was extremely unwieldySince I feel the reroll work was a necessary part of the review process — or at least necessary to speed it up — and since I'm one of the very few people who actually know the
RenderTest
tests, I still feel comfortable RTBC'ing this patch.Comment #35
dawehnerDid a couple of more minor points, but I really think this does not block the RTBC status from it.
Let's clean up the tests a bit :) Both of them aren't true anylonger.
Comment #37
dawehnerThis was not intended.
Comment #38
Wim LeersAll of #35 is minor clean-up, no fundamental changes.
RTBC +1
Comment #39
Wim LeersAdded beta evaluation.
Comment #40
webchickHm. I'm not really clear on why this must be done in order to address the parent issue, it seems like more of a clean-up (the issue summary doesn't help). It's a good clean-up to make nevertheless, and it's not changing the public API, and makes things less fragile, so is fine to do during beta.
Committed and pushed to 8.0.x. Thanks!