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

CommentFileSizeAuthor
#64 2975081-62-8.6.x.patch22.89 KBamateescu
#62 interdiff-62.txt5.88 KBamateescu
#62 2975081-62.patch22.84 KBamateescu
#58 interdiff-58.txt7.31 KBamateescu
#58 2975081-58.patch22.41 KBamateescu
#53 interdiff-53.txt2.62 KBamateescu
#53 2975081-53.patch16.49 KBamateescu
#51 2975081-2-51.patch16.87 KBalexpott
#51 49-51-interdiff.txt1.89 KBalexpott
#49 interdiff-49.txt5.24 KBamateescu
#49 2975081-49.patch16.82 KBamateescu
#46 2975081-2-46.patch16.51 KBalexpott
#46 41-46-interdiff.txt6.13 KBalexpott
#41 2975081-2-40.patch10.37 KBalexpott
#41 39-40-interdiff.txt1.25 KBalexpott
#39 2975081-2-39.patch11.19 KBalexpott
#39 37-39-interdiff.txt2.84 KBalexpott
#37 2975081-2-37.patch12.1 KBalexpott
#37 34-37-interdiff.txt2.35 KBalexpott
#34 2975081-2-34.patch12.34 KBalexpott
#34 29-34-interdiff.txt5.83 KBalexpott
#29 2975081-2-28.patch8.38 KBalexpott
#28 2975081-2-28.patch11.48 KBalexpott
#28 25-28-interdiff.txt2.95 KBalexpott
#25 2975081-2-25.patch6.28 KBalexpott
#23 2975081-2-23.patch6.48 KBalexpott
#23 2975081-2-23.test-only.patch3.05 KBalexpott
#23 20-23-interdiff.txt3.11 KBalexpott
#20 2975081-2-20.patch6.22 KBalexpott
#20 2975081-2-20.test-only.patch2.79 KBalexpott
#20 19-20-interdiff.txt3.5 KBalexpott
#19 2975081-2-19.patch3.43 KBalexpott
#19 15-19-interdiff.txt648 bytesalexpott
#16 2975081-2-16.patch2.99 KBalexpott
#16 15-16-interdiff.txt1.91 KBalexpott
#15 2975081-2-15.patch3.32 KBalexpott
#15 14-15-interdiff.txt2.07 KBalexpott
#14 2975081-2-13.patch2.3 KBalexpott
#14 7-13-interdiff.txt1.43 KBalexpott
#7 2975081-6.patch2.62 KBalexpott
#7 4-6-interdiff.txt765 bytesalexpott
#4 2975081-4.patch1.88 KBalexpott
#4 2-4-interdiff.txt916 bytesalexpott
#3 2975081-8.5.3-2.patch1.92 KBalexpott
#2 2975081-2.patch2.03 KBalexpott

Comments

alexpott created an issue. See original summary.

alexpott’s picture

Status: Active » Needs review
StatusFileSize
new2.03 KB

Here's a fix.

alexpott’s picture

StatusFileSize
new1.92 KB

I've need an 8.5.3 version for a project so posting here for others.

alexpott’s picture

The last submitted patch, 2: 2975081-2.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 4: 2975081-4.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new765 bytes
new2.62 KB

Here'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.

dawehner’s picture

I'm wondering whether we could get \Drupal\Core\DrupalKernel::getModulesParameter to read the configuration instead. Would this solve the problem already?

alexpott’s picture

re #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.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

chr.fritsch’s picture

Status: Needs review » Reviewed & tested by the community

This 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().

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php
@@ -372,10 +372,25 @@ protected function runUpdates() {
+      foreach (array_keys($this->config('core.extension')->get('module')) as $module) {
+        if (!$module_handler->moduleExists($module)) {

could 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 calls

mtodor’s picture

I 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.

alexpott’s picture

StatusFileSize
new1.43 KB
new2.3 KB

From 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.

alexpott’s picture

StatusFileSize
new2.07 KB
new3.32 KB

Ah we need to do a bit more to update the kernel with the correct module list like installing or uninstalling modules does.

alexpott’s picture

StatusFileSize
new1.91 KB
new2.99 KB
alexpott’s picture

I've tested #16 on the update test we've been having trouble with (because it uninstalled and uninstall modules) and it works great.

Status: Needs review » Needs work

The last submitted patch, 16: 2975081-2-16.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new648 bytes
new3.43 KB

So #16 is a non starter. So going back to #15 and adding some docs.

alexpott’s picture

StatusFileSize
new3.5 KB
new2.79 KB
new6.22 KB

Here's some test coverage.

The last submitted patch, 20: 2975081-2-20.test-only.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 20: 2975081-2-20.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new3.11 KB
new3.05 KB
new6.48 KB

Fixing the test.

The last submitted patch, 23: 2975081-2-23.test-only.patch, failed testing. View results

alexpott’s picture

StatusFileSize
new6.28 KB

Here'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.

mtodor’s picture

Status: Needs review » Reviewed & tested by the community

I have checked patch from #25 with Thunder tests because we are using UpdatePathTestBase and it works correctly. With the patch, we don't have false fails anymore. I have also checked implementation and it looks good.

catch’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/field/tests/src/Functional/Update/FieldUpdateTest.php
@@ -192,6 +192,8 @@ public function testFieldUpdate8500() {
+    $this->deletedFieldsRepository = $this->container->get('entity_field.deleted_fields_repository');

Would it not be better to just use \Drupal::service() directly each time and not assign this to a property?

alexpott’s picture

StatusFileSize
new2.95 KB
new11.48 KB

Yeah 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.

alexpott’s picture

StatusFileSize
new8.38 KB

Whoops some extra got in there. The interdiff from #28 is correct.

Status: Needs review » Needs work

The last submitted patch, 29: 2975081-2-28.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
mtodor’s picture

Status: Needs review » Reviewed & tested by the community

I have checked the changes. It looks good to me.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Discussed 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

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new5.83 KB
new12.34 KB

Here'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

amateescu’s picture

Very 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?

alexpott’s picture

@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.

alexpott’s picture

StatusFileSize
new2.35 KB
new12.1 KB

@amateescu pointed out that now we are doing a resetAll() we can move things about an do less cache clearing. Which is nice.

alexpott’s picture

Crediting @amateescu for discussions leading to a better patch.
Crediting @catch and @larowlan for code review.

alexpott’s picture

StatusFileSize
new2.84 KB
new11.19 KB

@amateescu has pointed out that we should look for fails before checking config in order to fail fast.

The last submitted patch, 34: 2975081-2-34.patch, failed testing. View results

alexpott’s picture

StatusFileSize
new1.25 KB
new10.37 KB

Removing unnecessary change.

The last submitted patch, 37: 2975081-2-37.patch, failed testing. View results

The last submitted patch, 39: 2975081-2-39.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 41: 2975081-2-40.patch, failed testing. View results

alexpott’s picture

Nice 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\ResponsiveImageUpdateTest assume 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\UpdatePathTestBase that 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.

alexpott’s picture

Status: Needs work » Needs review
Issue tags: +Needs change record
StatusFileSize
new6.13 KB
new16.51 KB

Okay 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.

alexpott’s picture

There 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.

amateescu’s picture

Title: UpdatePathTestBase fails to pick up new modules » UpdatePathTestBase fails re-initialize the test site (rebuld kernel, clear caches) after running the database updates

I think we need a new title as well.

amateescu’s picture

Title: UpdatePathTestBase fails re-initialize the test site (rebuld kernel, clear caches) after running the database updates » UpdatePathTestBase fails to re-initialize the test site (rebuild container, clear caches) after running the database updates
StatusFileSize
new16.82 KB
new5.24 KB

Just some cosmetic fixes.

amateescu’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs change record

Wrote a CR for this: https://www.drupal.org/node/3032731, and I think the patch is ready to go.

alexpott’s picture

StatusFileSize
new1.89 KB
new16.87 KB

Here'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.

berdir’s picture

+++ b/core/modules/field/tests/src/Functional/Update/FieldUpdateTest.php
@@ -193,16 +185,19 @@ public function testFieldUpdate8500() {
 
+    // Refresh the service as running updates can rebuild the container.
+    $deleted_fields_repository = \Drupal::service('entity_field.deleted_fields_repository');

nitpick: from what I see, this isn't actually "refreshing" as the variable is set and used the first time here.

amateescu’s picture

StatusFileSize
new16.49 KB
new2.62 KB

Fixed #52 and reverted some test cleanups so the patch can pass on 8.6.x too.

amateescu’s picture

The last submitted patch, 7: 2975081-6.patch, failed testing. View results

amateescu’s picture

Issue tags: +Workflow Initiative, +blocker

This is currently a blocker for the Workflow Initiative, specifically for #2725523: Add a revision_parent field to revisionable entities.

catch’s picture

+++ b/core/modules/responsive_image/tests/src/Functional/Update/ResponsiveImageUpdateTest.php
@@ -65,6 +65,11 @@ public function testPostUpdateDependency() {
 
+    // This test incorrectly installs the responsive image module so we can't
+    // properly check entity definition updates. Skip failed update checking
+    // and rely on the assertions here.
+    $this->checkFailedUpdates = FALSE;
+

Does 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?

amateescu’s picture

StatusFileSize
new22.41 KB
new7.31 KB

We can fix the test here :)

alexpott’s picture

The 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.

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
  1. +++ b/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBase.php
    @@ -321,9 +321,38 @@ protected function runUpdates() {
    +        // Note that resetAll() does not reset the kernel module list so we
    +        // have to do that manually.
    +        $this->kernel->updateModules($module_handler_list, $module_handler_list);
    

    note to self: only the array keys of the first argument matter here, so it is fine to pass the same argument twice.

  2. +++ b/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBaseTest.php
    @@ -70,6 +73,42 @@ public function testDatabaseLoaded() {
    +    // Set a value in the cache to prove caches are cleared.
    

    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?

  3. +++ b/core/tests/Drupal/FunctionalTests/Update/UpdatePathTestBaseTest.php
    @@ -70,6 +73,42 @@ public function testDatabaseLoaded() {
    +    \Drupal::state()->set('update_test_schema_version', 8001);
    

    can you elaborate on why this is needed?

amateescu’s picture

Status: Needs review » Reviewed & tested by the community
StatusFileSize
new22.84 KB
new5.88 KB

Thanks @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.

larowlan’s picture

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

the interdiff at #62makes that clear, I wasn't familiar with that one - thanks.

amateescu’s picture

StatusFileSize
new22.89 KB

The 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 :)

amateescu’s picture

After 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 though update_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 :)

alexpott’s picture

Version: 8.7.x-dev » 8.6.x-dev

Last patch is 8.6.x and we are targeting that release so let's change the version.

larowlan’s picture

it's worth pointing out that it uses the update_test_schema_update_8003() function name even though update_test_schema_update_8002() doesn't exist (in 8.6.x)

Now we have two separate patches, would it prevent confusion if used 8003 in 8.7 and 8002 in 8.6?

alexpott’s picture

@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.

amateescu’s picture

Agreed with @alexpott, I think it's less confusing to have the same update function number in both 8.7.x and 8.6.x.

  • larowlan committed f4ef94a on 8.7.x
    Issue #2975081 by alexpott, amateescu, larowlan, mtodor, catch:...

  • larowlan committed 6cb23d6 on 8.6.x
    Issue #2975081 by alexpott, amateescu, larowlan, mtodor, catch:...
larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Committed f4ef94a and pushed to 8.7.x. Thanks!
c/p as 6cb23d6823 and pushed to 8.6.x

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.