Problem/Motivation
See #2336895: Allow entity type and field storage definition objects to be compared for definition equality. Switching out the storage class of a content entity should not result in a possible schema change, only if the storage actually returns a different schema.
This is a problem because it means that changing a storage handler that does not actually change anything about the schema (Not a very common thing, but it can happen, in my case, to provide a different implementation for the threaded-comments-list query) blocks update.php if you already have data. We have no other choice but to do that if there is an actual schema change, but we should try very hard to avoid that if we don't actually need any updates.
@catch considered this to be a critical problem in the parent issue in comment #6: #2336895-6: Allow entity type and field storage definition objects to be compared for definition equality.
Proposed resolution
Remove the check from SqlContentEntityStorageSchema::requiresEntityStorageSchemaChanges().
Remaining tasks
User interface changes
API changes
| Comment | File | Size | Author |
|---|---|---|---|
| #35 | entity-storage-class-switch-2419065-35.patch | 8.63 KB | jhedstrom |
| #35 | entity-storage-class-switch-2419065-35-TEST-ONLY.patch | 7.84 KB | jhedstrom |
Comments
Comment #1
jhedstromCan it really be this simple?
Comment #2
berdirYes, that is the relevant part of my patch from #2336895: Allow entity type and field storage definition objects to be compared for definition equality :)
What we need now is a unit test. Looks like this method has no unit test coverage yet.
Comment #3
jhedstromOh, I didn't realize the other issue had a patch for this one. I'll work on unit tests.
Comment #4
jhedstromThis adds unit tests for
SqlContentEntityStorageSchema::requiresEntityStorageSchemaChanges().Comment #5
jhedstromOops, test in #4 was a partial to my local branch. Here's the full test.
Comment #8
jhedstromPatch in #5 was expected to fail, back to needs review.
Comment #9
plachLooks good to me, thanks :)
@Berdir:
Want to RTBC this?
Comment #10
berdirSome feedback on the tests structure, what they are testing looks great to me as well.
No need for the constructor stuff on an Interface, just use getMock() ?
Multiple scenarios are usually either tested with data providers (would be very hard I guess here) or multiple methods. So this one would be come testRequiresEntityStorageSchemaChangesStorageClassChange() or so.
it is correct that any class works, but reads strange. There's a null implementation of the storage, you could use that, for example..
Comment #11
plach/me sucks at reviewing tests...
Comment #12
jhedstromRegarding data providers, I originally set out to use them, but since the internal logic of the method being called relies so heavily on making mocks for each iteration, the data provider method was going to be quite convoluted. Also, separate methods for each iteration of the method seem like overkill, but perhaps they'd be easier to read...
For now, here's an attempt at addressing #10 without splitting each variation into a separate method.
Comment #13
jhedstromOops, left over docblock comment.
Comment #14
berdirNitpick: Sorry, what I meant is just $this->getMock('\Drupal\Core\Entity\ContentEntityTypeInterface'); We don't need to get the mockbuilder here.
Did not review the rest yet.
Comment #17
jhedstromActually, I think I figured out a clean way to use data providers.
Comment #18
jhedstromThis switches to use a data provider, and to simply use
getMock()for interfaces (I updated an existing test to do this here as well).Comment #32
jhedstromRemoving 'needs tests' tag.
Comment #33
plachIsn't this the same of case 1?
It seems the data provider almost always provides values also for the last two arguments. Can we remove the default values and make sure we always the actual ones?
Comment #34
xjmPer @berdir:
Can we clarify that in the summary? Thanks!
Comment #35
jhedstromre: #33 you're right regarding those duplicated cases. I've updated the test to remove the duplicate, and also remove the default values as suggested.
Comment #37
berdirUpdated issue summary, better now?
The reason I didn't comment on the test yet is that I've had bad experiences with mock objects that are set up in a data provider, because they are all created before a test runs and then kinda-survive multiple test executions, but there is code that runs at the end of a test method on all mocks, for example to check that methods were called as often as expected.
The test seems to work, but I suspect it will no longer work as expected if you change those $this->any() to $this->once(), which would be more correct. The only alternative I can offer is to only set up some sort of instrumentalisation of the mock expections and only create the mocks in the test method. Or have multiple test methods with helper methods to pass the mocks into. But I'm think I'm OK with moving forward here if it works, but that is IMHO something that @alexpott/@catch should know about and make a final decision if they're OK with that.
Comment #38
jhedstromI spoke with Berdir on IRC regarding the
expects()and mocks from providers. I think in this particular case it should be okay as it is written, because it's either usingnever()or in the case ofonce()it's per-unique instance of an object, so shouldn't run into trouble with the way mocks and providers work. The test/provider immediately above this new one uses similar mocks.Comment #39
berdirReviewed this again. Agreed that this is probably OK like that (Although I'm not 100% sure the never() is working as expected, you call it on the clone and pass to multiple methods, but I'm not sure how that will actually be verified)
Anyway, +1 RTBC on the test coverage, actual change is what I wrote, although you came up with it separately I think. Leaving it to @plach to set RTBC.
Comment #40
plachIf @Berdir is ok with the current code I'll tentatively RTBC it.
For committers: please have a look to #37, just in case.
Comment #41
alexpottYep I'm not keen on setting up mocks in the provider either. But the mocks are not set on the test object so this should be okay. The test is, however, more memory hungry than it needs to be.
This issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 7f1b02e and pushed to 8.0.x. Thanks!