Problem/Motivation
If you have a hook_update_N() function that installs a module and changes entity definitions (for example using hook_entity_base_field_info()) then any UpdatePathTestBase tests will fail because the entity definition update check in \Drupal\FunctionalTests\Update\UpdatePathTestBase::runUpdates() will detect changes. This is because the test runner's module handler service has not been updated with the new modules. Calling \Drupal\Core\Test\FunctionalTestSetupTrait::resetAll() or \Drupal\Core\Test\FunctionalTestSetupTrait::rebuildContainer() is not enough to update the module list.
Proposed resolution
Add new modules to the test runner's module handler and then reset everything so the test running environment is up-to-date with any changes.
Remaining tasks
User interface changes
None
API changes
None
Data model changes
None
| Comment | File | Size | Author |
|---|---|---|---|
| #64 | 2975081-62-8.6.x.patch | 22.89 KB | amateescu |
| #62 | interdiff-62.txt | 5.88 KB | amateescu |
| #62 | 2975081-62.patch | 22.84 KB | amateescu |
| #58 | interdiff-58.txt | 7.31 KB | amateescu |
| #58 | 2975081-58.patch | 22.41 KB | amateescu |
Comments
Comment #2
alexpottHere's a fix.
Comment #3
alexpottI've need an 8.5.3 version for a project so posting here for others.
Comment #4
alexpottRealised I wiped out some of #2973299: Add the label of the entity type to the error message when there are outstanding entity changes in UpdatePathTestBase so fixing that.
Comment #7
alexpottHere's a fix for that test fail. I'm not a fan of \Drupal\Tests\field\Functional\Update\FieldUpdateTest::setUp()'s sticking stuff from the container on test class in set up and then running updates. That's very fragile.
Comment #8
dawehnerI'm wondering whether we could get
\Drupal\Core\DrupalKernel::getModulesParameterto read the configuration instead. Would this solve the problem already?Comment #9
alexpottre #8 I don't think so because that'll still be out-of-sync because this is about modules being installed during updates and the test running environment being out of date. One interesting thing is that rebuilding the container using \Drupal\Core\Test\FunctionalTestSetupTrait::rebuildContainer() doesn't work either.
Comment #11
chr.fritschThis looks good to me and it fixes a problem we have with an update_hook in Thunder where we install the scheduler_content_moderation_integration module. That module adds new fields to node by hook_entity_base_field_info().
Comment #12
larowlancould we do
array_diff(array_keys($this->config('core.extension')->get('module')), array_keys($module_handler->getModuleList()))instead here to fetch the new modules in one pass? We'd still have to call ->addModule for each, but it would save a lot of moduleExists callsComment #13
mtodor commentedI have a similar problem, but in my case, it's when a module is uninstalled. I guess that proposal from #12 by @larowlan would cover that case too.
Comment #14
alexpottFrom another bug in Thunder it looks like we have to care about uninstalls too.
I'm not sure we need to call addModule() because we do a full cache flush and a container rebuild - let's see.
Comment #15
alexpottAh we need to do a bit more to update the kernel with the correct module list like installing or uninstalling modules does.
Comment #16
alexpottComment #17
alexpottI've tested #16 on the update test we've been having trouble with (because it uninstalled and uninstall modules) and it works great.
Comment #19
alexpottSo #16 is a non starter. So going back to #15 and adding some docs.
Comment #20
alexpottHere's some test coverage.
Comment #23
alexpottFixing the test.
Comment #25
alexpottHere's a tweaked patch so that we can apply this to both 8.6.x and 8.7x at the same time. Moved around the test so that it doesn't depend on 8.7.x only stuff.
Comment #26
mtodor commentedI have checked patch from #25 with Thunder tests because we are using
UpdatePathTestBaseand it works correctly. With the patch, we don't have false fails anymore. I have also checked implementation and it looks good.Comment #27
catchWould it not be better to just use \Drupal::service() directly each time and not assign this to a property?
Comment #28
alexpottYeah properties with services in them in tests is asking for trouble. I've only converted the problmatic one here and I'm still using $this->container->get() to not make the test to weird.
Comment #29
alexpottWhoops some extra got in there. The interdiff from #28 is correct.
Comment #31
alexpottComment #32
mtodor commentedI have checked the changes. It looks good to me.
Comment #33
alexpottDiscussed a bit with @amateescu and we concluded that we don't need to be clever - because there are many reasons caches need to be cleared - for example adding a field - see https://www.drupal.org/project/drupal/issues/2725523#comment-12968324
Comment #34
alexpottHere's the a fix the flushes the caches in the running environment - and now nicely we can lose an odd work around in \Drupal\Tests\block\Functional\Update\BlockConditionMissingSchemaUpdateTest
Now we have a warm cache update test this is even more necessary. But also it points to the fact we're not doing update testing properly - see #3031379: Add a new test type to do real update testing
Comment #35
amateescu commentedVery nice, this fixes the test failure from #2725523-43: Add a revision_parent field to revisionable entities :)
Does this new approach mean that we can revert the changes to
FieldUpdateTest?Comment #36
alexpott@amateescu nope - that test is using a very dangerous pattern of sticking services on tests as protected properties. The pattern feels really wrong and extremely fragile.
Comment #37
alexpott@amateescu pointed out that now we are doing a resetAll() we can move things about an do less cache clearing. Which is nice.
Comment #38
alexpottCrediting @amateescu for discussions leading to a better patch.
Crediting @catch and @larowlan for code review.
Comment #39
alexpott@amateescu has pointed out that we should look for fails before checking config in order to fail fast.
Comment #41
alexpottRemoving unnecessary change.
Comment #45
alexpottNice so the fails in #41 are occurring because the tests currently rely on using stale cache. With a clean cache they fail because their assumptions are wrong. For example
Drupal\Tests\responsive_image\Functional\Update\ResponsiveImageUpdateTestassume it can install module without config by hacking core.extension. It forgets to install the responsive_image_style entity type so when UpdatePathTestBase clears the cache and does the entity update check it falls over. It think what\Drupal\Tests\responsive_image\Functional\Update\ResponsiveImageUpdateTest::setUp()is doing is wrong because it's not in anyway tests a real update situation. However it's not possible to balance the old behaviour with doing it right so we need a different solution.I wonder if we can add a new version of
\Drupal\FunctionalTests\Update\UpdatePathTestBasethat clears caches, rebuilds the container and properly updates the kernel and then "deprecate"\Drupal\FunctionalTests\Update\UpdatePathTestBase. The reason "deprecate" is in quotes is because we can't deprecate as per usual because all update tests are legacy tests by definition. I think we need to trigger a warning that a developer can disable with a flag if they use the old class.Comment #46
alexpottOkay it turns outs we can do
$this->checkFailedUpdates = FALSE;- I'm not that concerned about adding it to one test because it can be documented and we have plenty of other update tests where this update runs without issue.The other fails are due to service properties that get out of date.
@amateescu pointed out that we can are going to need a good CR for this issue in case someone has an update path test that fails due to this change. The good news is that there is a quick fix - do
$this->checkFailedUpdates = FALSE;before calling ::runUpdates() - and the better news is that if it does fail it is because there are some assumptions that the test is making that are not correct.Comment #47
alexpottThere are not so many update path test base tests in contrib http://grep.xnddx.ru/search?text=UpdatePathTestBase - and even less those that use the BrowserTestBase version so I really do not think this change is likely to be very disruptive.
Comment #48
amateescu commentedI think we need a new title as well.
Comment #49
amateescu commentedJust some cosmetic fixes.
Comment #50
amateescu commentedWrote a CR for this: https://www.drupal.org/node/3032731, and I think the patch is ready to go.
Comment #51
alexpottHere's a patch that works for 8.6.x and 8.7.x - not that we should commit this there but some projects might want to be early adopters.
Comment #52
berdirnitpick: from what I see, this isn't actually "refreshing" as the variable is set and used the first time here.
Comment #53
amateescu commentedFixed #52 and reverted some test cleanups so the patch can pass on 8.6.x too.
Comment #54
amateescu commentedAlso updated the CR to mention a change required for test-only configurable fields after #2976035: Entity type CRUD operations must use the last installed entity type and field storage definitions , as we're doing in core in #3031086: Configurable fields that are created only for update path tests should be defined with custom_storage = TRUE.
Comment #56
amateescu commentedThis is currently a blocker for the Workflow Initiative, specifically for #2725523: Add a revision_parent field to revisionable entities.
Comment #57
catchDoes this mean the test is deliberately incorrectly installing the module, or is it pointing to a bug in the test? If the latter, is it not possible to fix the test here?
Comment #58
amateescu commentedWe can fix the test here :)
Comment #59
alexpottThe other fix would be to remove the test - it is from 2016 as part of #2657110: ResponsiveImageFormatter should define dependencies to responsive image style entity and so not part of any supported version of Drupal 8.
Comment #60
alexpottBut I guess that would require a decision in #2942096: [policy, no patch] Remove old update hooks prior to each major version (Drupal 10 and later)
Comment #61
larowlannote to self: only the array keys of the first argument matter here, so it is fine to pass the same argument twice.
because we're calling ::updateModules on the container, should we test that the 'container.namespaces' container parameter before and after to ensure we have test coverage for that too?
can you elaborate on why this is needed?
Comment #62
amateescu commentedThanks @larowlan for the review!
Re #61:
2. Good point, added assertions for 'container.namespaces' as well.
3. That's how the update path test base test works, it reads a schema version from state to figure out which test update function to run :) But while looking into it, I found that the new code was piggybacking on the existing
update_test_schema_update_8001()update function, and it would be cleaner to write a separate one for this test.Comment #63
larowlanthe interdiff at #62makes that clear, I wasn't familiar with that one - thanks.
Comment #64
amateescu commentedThe patch from #62 applies cleanly to 8.6.x with a 3way merge, but here's a separate patch if it's easier to commit like this :)
Comment #65
amateescu commentedAfter a quick discussion with @alexpott on the 8.6.x patch, it's worth pointing out that it uses the
update_test_schema_update_8003()function name even thoughupdate_test_schema_update_8002()doesn't exist (in 8.6.x), but that doesn't matter because which update function runs is controlled by the test via state :)Comment #66
alexpottLast patch is 8.6.x and we are targeting that release so let's change the version.
Comment #67
larowlanNow we have two separate patches, would it prevent confusion if used 8003 in 8.7 and 8002 in 8.6?
Comment #68
alexpott@larowlan I don't think it matters because tests that use these update hooks use state to manipulate what fires so /shrug. And it feels less confusing to use the same update numbers across the branches.
That said this was why I used the _8001 update before - I wanted a patch to apply to 8.6.x and 8.7.x and have the same test.
Comment #69
amateescu commentedAgreed with @alexpott, I think it's less confusing to have the same update function number in both 8.7.x and 8.6.x.
Comment #72
larowlanCommitted f4ef94a and pushed to 8.7.x. Thanks!
c/p as 6cb23d6823 and pushed to 8.6.x