Problem/Motivation
#3004459: [PP-1] Fold in dependency parsing wisdom from project_composer and #2641658: Module version dependency in .info.yml is ineffective for patch releases both have proposed changes to \Drupal\Component\Version\Constraint. Only one of these issue will probably be needed because they affect the same logic and fix the problem documented in #2641658
They both also exposed currently functionality of \Drupal\Component\Version\Constraint that is not being covered in tests. Since determining module and core dependency is a extremely critical task we should be very careful about breaking the current functionality.
If we commit either one of these issues without pre-existing comprehensive testing for dependency constraints it is not unlikely they will break current functionality edge cases that sites are depending on.
Proposed resolution
Update \Drupal\Tests\Component\Version\ConstraintTest and possibly \Drupal\Tests\system\Functional\Module\VersionTest with more test cases to cover the currently functionality for dependency logic.
Remaining tasks
Write the tests, review
User interface changes
None
API changes
None
Data model changes
None
Release notes snippet
Note needed
| Comment | File | Size | Author |
|---|---|---|---|
| #39 | 3066448-39.patch | 12 KB | tedbow |
| #39 | interdiff-36-39.txt | 1.53 KB | tedbow |
| #36 | 3066448-36.patch | 12.25 KB | tedbow |
| #36 | interdiff-32-36-no-whitespace.txt | 10.62 KB | tedbow |
| #32 | interdiff-29-32.txt | 8.28 KB | tedbow |
Comments
Comment #2
tedbowHere are extra test cases
Comment #3
tedbowComment #4
tedbowOk here are even more test cases for support of
Comment #5
tedbowI meant to remove this section because of the loops that are end of this method that swap out the equals operator and other strings.
I talked with @wim leers about this issue and he pointed out that these loops put logic in the dataprovider which make the test more complicated and harder to read.
The next patch will remove this will keeping the added test cases. Instead of looping over the existing test cases it surround the test cases in 3 foreach loops, one for each operator or space that has possible variations.
Comment #7
tedbowThe last 2 cases here have the wrong key. So missed a test case.
Doing the string concatenation for each test case makes this more likely to have copy/paste errors.
This next patch has all of this is single method so it only happens once.
Didn't mean to change this. Fixed
Comment #8
tedbowMissed space characters that can be replaced by $space
'1,9' should be '1.9'
Comment #9
tedbowBut of course it makes it hard to review because how can we be certain that all the original test cases are still covered?
So I am to add temporary method
testTempToProveAllOldCasesCovered()which will do what it's name says. So I am adding back the original version ofproviderIsCompatible()(renamed tooriginal_providerIsCompatible()whichtestTempToProveAllOldCasesCovered()will use.Comment #10
tedbowAlso to help reviewing here is a no whitespace patch
Also sorry for using the wrong issue numbers in my patches above 😱
Comment #11
tedbowRelating the 2 issues mention in the issue summary
Comment #12
tedbowComment #13
wim leersMissed opportunity for naming this
$spaciness😂Nit: s/constraints/constraint/. Fixed.
This is also the case in HEAD, but … why does
1.9not meet the<8.x-4.x,>8.x-1.xconstraint?Übernit: double space. Fixed.
Übernit: 80 cols. Fixed.
Übernit: typo. Fixed.
We're testing for
8.xby default. This is the only place where we deviate from that: we test7.xhere. Shouldn't we also test9.x?More importantly: what is the value of this test if it's already destined to not find any matching versions due to the unsatisfiable constraint that it uses from the preceding test case?
Hah, clever!
Comment #14
wim leersFor clarity: only points 3 and 7 still need to be addressed.
Comment #15
tedbow@Wim Leers thanks for the review!
$spaciness😜1.9 would satisfy
<8.x-4.x,>=8.x-1.xBut
>8.x-1.xdoesn't include any 1.x versionsThis test above confirms this
I move this below
To use the constraint we know passes for some cases.
I also added a test for 9.x
In addition added a test for
<4.x,>1.xwhich doesn't specify a core version in the constraint to show that 7.x, 8.x, and 9.x will all have the same behavior when the core version is not specified.testTempToProveAllOldCasesCovered()locally but I see @Wim Leers left it in his patch and I guess it makes sense to leave it in as long there are changes happening tothe new
providerIsCompatible()to certain we still cover all old cases. We can remove it once it gets closer to RTBC.Comment #16
wim leers9.xas the core version in the constraint and specify9.xas core compatibility, to test a succeeding constraint for the next major Drupal version?Once point 3 is addressed, I think this is ready.
Comment #17
tedbow\Drupal\Component\Version\Constraintis major version agnostic. '8.x' is not the code anywhere except comments. This class and test should not have to be changed in 9.x. Also we really don't want any "8.x" logic to ever unintentionally creep into that class. So I added another outside loopThis should cover us for a while 😜
I put the test for cover incompatible core_compatibility outside of this loop so we are still covering that.
1 with
testTempToProveAllOldCasesCovered()and drupalci.yml changes still in to prove these latest changes still cover all old cases.2 without these changes if @Wim Leers or some one else decides to RTBC
Comment #18
wim leers😂
I think this is ready now.
Comment #20
tedbowRetesting and setting back to RTBC. #19 was on fail because of Drupal\FunctionalJavascriptTests\Ajax\DialogTest with
Which is random drupalci test failure
Oh wait there is an extra space nit too. New patch
Comment #21
xjmI confirmed the monthly-or-so failures of the above test in my inbox-of-CI-sadness, e.g.: https://www.drupal.org/pift-ci-job/1319858, https://www.drupal.org/pift-ci-job/1302886
I don't think fixing whitespace needs peer signoff so setting back to RTBC. :) (I haven't reviewed the whole patch myself.)
Comment #22
xjmExcellent expansion of coverage. Just some small stuff:
How is empty-string an equality operator? 🤔
I think we could add an inline comment here just saying that both versions of both operators are supported. Which I never knew! (Does the API documentation for info files document this support?)
Also, nitpick: we have
$equal_operatorbut$not_equalS_operatorwith an "S". Let's standardize on one or the other to avoid bugs where we might accidentally testNULLbecause of a variable name spelling error.Both variations of the nominal phrase seem common. I think we should lose the "s" since that will most closely match https://www.php.net/manual/en/language.operators.comparison.php and "not equals" is not grammatical in a sentence. (t's more like sounding out the operator one character at a time.)
Nitpick: I think it'd be better to call this variable
$spacer,$spacing,$whitespace, or something along those lines. When I was first reviewing a word diff, I asked myself "Why are they making a variable for a string literal?".Relatedly: what if it's multiple spaces? Do we support that?
I (now) get why we're looping over variables for the spacer and the comparison operators, but it does reduce the readability of the provider a lot. Not sure what to do about that.
Why 7.x and 9.x but not 8.x or 10.x? Any reason not to just put this inside the next loop with the branch foreach?
These docs are all a bit vague and I think it's valuable to make them clear since this method also documents the whole test, in a sense. Let's add a few more words, even if it's just, "..., for example, ..."
We should almost always use a more specific data type than
array. I think$versionsis astring[]and the return value looks to be best described asarray[].The hyphen is also a possible value of a branch name. Let's use a different delimiter in the array key, or a nested array or something? Otherwise it's theoretically possible to have key collisions and overwrite one test with another?
Comment #23
wim leers#22
spaciness? 😜str_replace()to replace it with zero or two spaces?@return array.Comment #24
wim leersThis is effectively a follow-up for #2677532: Move drupal_check_incompatibility() functionality to a new Dependency class and Version component.
We should consider updating the change record for that issue: https://www.drupal.org/node/2756875
Comment #25
tedbowre #22
@xjm thanks for the review!
$whitespace\Drupal\Tests\Component\Version\ConstraintTest::testIsCompatible()to receive strings instead of instance of\Drupal\Component\Version\ConstraintI personally like the way it is instead because there is less logic and no change to
\Drupal\Tests\Component\Version\ConstraintTest::testIsCompatible()$core_compatibilityloop because we have core compatibility in+++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
@@ -27,67 +27,102 @@ public function providerIsCompatible() {
+ // Test greater than and less than with an incorrect core
+ // compatibility.
+ $constraint = "<{$space}8.x-4.x,{$space}>{$space}8.x-1.x";
...
+ foreach (['8.x', '9.x', '10.x'] as $core_compatibility) {
that shouldn't be replaced with the $core_compatibility.
But now I changed this to put it inside the loop and just use different constraint string when
$core_compatibility === '8.x'This covers more branches.
we should use for all operators like in #4 and before
Comment #26
tedbowmore fixes for #22.5 after chatting with @xjm again
Comment #27
xjmDidn't complete a full review yet, just noticed this bit of spinach:
Coding standards error: missing space between
//and comment text.Comment #28
wim leersSpinach?! :D
Comment #29
tedbowfixed #27
Comment #30
wim leersComment #31
Mixologicre #25.3 : Im -1 on introducing loops and logic, and additional function in the data provider, as not only does it impact readability, it also makes it more difficult to maintain when we add new functionality to the Constraint class, like we want to do over in #3004459: [PP-1] Fold in dependency parsing wisdom from project_composer.
I think a couple of the loops are somewhat unnecessary: the whitespace loop is really only proving that
\s*in the regex doesnt someday lose its*, and the core compatibility portion of the Constraint class exists only so that we can strip it out, as its not part of the version comparision logic at all.Please see my comment here https://www.drupal.org/project/drupal/issues/2677532#comment-12802004
So, we're kind of adding coverage for something (core_compatibility) that I don't believe we really want to see in the future. If our goal is to have modules that work with both 8 and 9, then we definitely never want to see '9.x-x.y' in the contrib ecosystem.
As an aside, there are no contrib modules on drupal.org that use the <> pattern, or use more than two constraints, but I do think we ought to add those test cases because there might be some usage somewhere, and we wouldn't want to break that for somebody.
Comment #32
tedbowre #31
@Mixologic thanks for the review!
Yeah was just pointing out we could go this way if the readability was big concern but I would rather not too
I guess I would say we want a test where the underlying implementation can change completely(to not use regex functions at all say) and if the test still pass we can be confident we aren't breaking BC or any current sites.
The reason I added the whitespace variation was because I noticed the current test just had 1 constraint string where we were using a space after the operator this wasn't commented on. So we weren't explicitly testing this. If someone had updated that test line for some other reason they could have easily removed the space and it could have gone unnoticed. Poof we have lost test coverage for any space at all after the operator.
We already currently don't have test coverage for a space after the comma.
I will upload patch that shows how changing that 1 line would allow the test to pass without support a space after the operator by also removing it from the regex. It will also remove the `\s*` completely for after the comma and show we currently have no test coverage for this.
Even though we just strip it also effectively makes sure there is not a different major version
As in this case where only the last test case would fail
The test coverage also proves it can be provided but it is not necessary.
But this how the class works now. There is no exclusion for `9.x` and there is not special logic for `8.x`. If we want this class to exclude 9.x always we should add that class not exclude it from test coverage. adding the test coverage to the class I think just makes it more clear that we have make changes here to exclude 9.x.
$constraintto readability. I hope this lets us leave the loops in for the edge cases even though they make actual string less readable.We can know what is used in contrib modules for the version constraint but we have no visibility into what people are using in their custom modules. This plus that fact that our Drupal 8 documentation for .info.yml files gives virtually no direction on what can be used for dependency constraints makes important that our test coverage be as robust as possible. This is all it says
Our Drupal 7 docs are better but I won't expect someone new to Drupal as of 8 to actually look at these.
Comment #33
MixologicAh, this makes a lot more sense framed that way. I concur 100%.
I had forgotten that when this became a component that the core_compatibility string was passed into the class, as I've been bouncing back and forth from the d7 to d8 versions in looking at project_composer and this issue.
Ultimately those suggestions were a result of me searching for ways to get rid of the loops, and trying to justify not covering those cases in order to do so.
Primarily because I cant easily look at the test and figure out, without unrolling the loops in my head, what it is that we're testing, which makes me think that its going to become challenging to introduce additional test cases and patterns that we want to add support for.
It's not easy for me to understand why we have the equality operators in one loop, the inequality operators in another loop, and the greater than/less than/gte/lte operators embedded, for example. (i.e when we add ^/~ operators, where would those go? )
And since many of the test cases don't use those operators at all, we end up, in some cases re-running the same test 36 times, but only having 6 variants actually exercised.
I guess Im mostly just advocating that we go back to adding the additional coverage via the former constraint pattern, and add more scenarios to this test without adding a lot of logic to our tests - as that ultimately becomes untested code.
Comment #34
wim leersThis is not accurate, although I totally understand why you arrived at this conclusion :)
The combination of
+operatormeans that if a particular constraint string doesn't use any of, or only a subset of the loop-based permutations, then the same test case is just generated multiple times, which is harmless.
Comment #35
MixologicThanks Wim. I failed to notice that we're generating the cases first, then running them. My mistake.
Comment #36
tedbowre #33 @Mixologic thanks for pushing to have the test more readable and maintainable!
From my point of view these having the loops nested gives us more test coverage. for instance we have cases for both not equal operators against each equal operator against each core version. If there was some combo that any change made in the future would break this test would break.
Are we planning on adding those? I would hope we would just wait until #3005229: Provide optional support for using composer.json for dependency metadata to add those operators and at point we would simply be relying on
\Composer\Semver\Semverso we wouldn't actually need to add this logic to\Drupal\Component\Version\Constraintso won't have to worry about this test class becoming more complicated.Ok this patch gets rid of the
$whitespaceloop and adds 3 cases to the top of the method that test the possible combinations of whitespace that we could have. I think this makes the other constraints for readable because we had$whitespacein every constraint string, usually multiple times.I would hope we could leave the other loops because of reasons in state in 1) in this comment.
Hope the comments I added in #32
Make this easier to read because you have version of the constraint in the comment without the variables.
Comment #38
tedbowpretty sure #37 is unrelated(random?) test failure
Comment #39
tedbowThis comment is wrong updating to match the test case.
This seems like it should actually not pass.
I think I got this case from
\Drupal\Tests\system\Functional\Module\VersionTest::testModuleVersions()which actually uses "=8.x2.x"But it seems like the current case should actually be incompatible.
I created #3071150: Dependency version constraints evaluate true even the constraint strings aren't valid to fix this. If we think we should not fix because it would break sites then we could keep the test case.
Or we could it add them in that issue.
Comment #40
tedbowComment #41
wim leersÜbernit: it's a little bit odd to have the example not contain a space (
=8.x-1.0) but the actual constraint then does have one (= 8.x-1.0). I think we should keep the space because it makes the$constraintvariables' expressions easier to read. I don't think this is worth holding the patch up for though.Comment #42
wim leersPer @Mixologic's comment at #3069795-14: [meta] Improve dependency management for extensions.4, that's indeed what we are going to do.
Just like @tedbow, I also agree with @Mixologic's request for more readable tests and not testing things unnecessarily.
I answered a small portion of that in #34 (which @Mixologic confirmed in #35), @tedbow addressed the majority in #36. Ted removed one loop (#36.3), justified the others (#36.1) and explained why we won't be needing more loops that would be awkward to fit in (which @Mixologic most likely agrees with per his comment at #3069795-14: [meta] Improve dependency management for extensions.4). Combined, I think this addresses @Mixologic's concerns adequately.
Therefore, re-RTBC'ing.
Comment #43
MixologicYep. Im 100% +1 on this. Adds valuable test coverage for functionality that we don't want to accidentally impact.
Comment #44
larowlanCommitted f9ca4c5 and pushed to 8.8.x. Thanks!
Comment #46
tedbow🎉 @larowlan thanks for committing and @Wim Leers @xjm and @Mixologic thanks for all the help!