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

Comments

tedbow created an issue. See original summary.

tedbow’s picture

StatusFileSize
new4.96 KB

Here are extra test cases

tedbow’s picture

Status: Active » Needs review
tedbow’s picture

StatusFileSize
new10.86 KB

Ok here are even more test cases for support of

  1. 1 or more space or none before version numbers and after operators
  2. <> in addition to !=
  3. ==, = or no operator for equals.
tedbow’s picture

StatusFileSize
new12.74 KB
  1. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -25,69 +26,139 @@ public function testIsCompatible(Constraint $version_info, $current_version, $re
    +    foreach (['', '=', '=='] as $equal_operator) {
    +      // Stable version.
    +      $stable = ["{$equal_operator}8.x-1.0", '8.x'];
    +      $tests["$equal_operator(=8.x-1.0)-1.0"] = [$stable, '1.0', TRUE];
    +      $tests["$equal_operator(=8.x-1.0)-1.1"] = [$stable, '1.1', FALSE];
    +      $tests["$equal_operator(=8.x-1.0)-0.9"] = [$stable, '0.9', FALSE];
    +    }
    

    I meant to remove this section because of the loops that are end of this method that swap out the equals operator and other strings.

  2. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -25,69 +26,139 @@ public function testIsCompatible(Constraint $version_info, $current_version, $re
    +    foreach ($original_tests as $key => $original_test) {
    +      if (strpos($original_test[0][0], ' ') !== FALSE) {
    +        $tests["$key-no-space"] = $original_test;
    +        $tests["$key-no-space"][0][0] = str_replace(' ', '', $original_test[0][0]);
    +
    +        $tests["$key-extra-space"] = $original_test;
    +        $tests["$key-extra-space"][0][0] = str_replace(' ', '     ', $original_test[0][0]);
    +      }
    +    }
    

    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.

Status: Needs review » Needs work

The last submitted patch, 5: 3066448-5.patch, failed testing. View results

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new10.86 KB
new11.07 KB
  1. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -27,67 +27,126 @@ public function providerIsCompatible() {
    +          $tests["($constraints)-1.1"] = [$less, '1.1', FALSE];
    +          $tests["($constraints)-1.1"] = [$less, '1.0', TRUE];
    +          $tests["($constraints)-1.0"] = [new Constraint("<{$space}8.x-1.0", '8.x'), '1.1', FALSE];
    

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

  2. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -97,7 +156,7 @@ public function providerIsCompatible() {
    -    $constraint = new Constraint('<8.x-4.x,>8.x-1.x', '8.x');
    +    $constraint = new Constraint('<{$space}8.x-4.x,$space>{$space}8.x-1.x', '8.x');
    

    Didn't mean to change this. Fixed

tedbow’s picture

StatusFileSize
new2.71 KB
new10.88 KB
  1. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -27,67 +27,89 @@ public function providerIsCompatible() {
    +          $constraints = "{$equal_operator}{$space}8.x-2.x,{$space}>= 2.4-alpha2";
    ...
    +          $constraints = "{$equal_operator}{$space}8.x-2.0,$space>= 2.4-alpha2";
    ...
    +          $constraints = ">{$space}8.x-4.x,{$space}< 8.x-1.x";
    

    Missed space characters that can be replaced by $space

  2. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -27,67 +27,89 @@ public function providerIsCompatible() {
    +          $tests += $this->createTestsForVersions($constraints, ['4.0', '3.9', '2.1', '1,9'], FALSE);
    ...
    +          $tests += $this->createTestsForVersions($constraints, ['4.0', '3.9', '2.1', '1,9'], FALSE, '7.x');
    

    '1,9' should be '1.9'

tedbow’s picture

StatusFileSize
new9.04 KB
new4.37 KB
  1. So all of these changes are a lot but I do think it is worth it to add much more test coverage.

    But 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 of providerIsCompatible()(renamed to original_providerIsCompatible() which testTempToProveAllOldCasesCovered() will use.

tedbow’s picture

StatusFileSize
new8.73 KB

Also to help reviewing here is a no whitespace patch

Also sorry for using the wrong issue numbers in my patches above 😱

tedbow’s picture

tedbow’s picture

Assigned: tedbow » Unassigned
wim leers’s picture

StatusFileSize
new8.96 KB
new8.99 KB
  1. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -91,6 +91,100 @@ public function providerIsCompatible() {
    +        foreach (['', ' '] as $space) {
    

    Missed opportunity for naming this $spaciness 😂

  2. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -91,6 +91,100 @@ public function providerIsCompatible() {
    +          $constraints = "{$equal_operator}{$space}8.x-1.0";
    

    Nit: s/constraints/constraint/. Fixed.

  3. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -91,6 +91,100 @@ public function providerIsCompatible() {
    +          $tests += $this->createTestsForVersions($constraints, ['4.0', '1.9'], FALSE);
    

    This is also the case in HEAD, but … why does 1.9 not meet the <8.x-4.x,>8.x-1.x constraint?

  4. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -91,6 +91,100 @@ public function providerIsCompatible() {
    +          // Test greater than  or equals and equals exact version.
    

    Übernit: double space. Fixed.

  5. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -91,6 +91,100 @@ public function providerIsCompatible() {
    +          // Test a nonsensical greater than and less than - no compatible versions.
    

    Übernit: 80 cols. Fixed.

  6. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -91,6 +91,100 @@ public function providerIsCompatible() {
    +          // Test greater than and less than with an incorrect core compatbility.
    

    Übernit: typo. Fixed.

  7. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -91,6 +91,100 @@ public function providerIsCompatible() {
    +          $tests += $this->createTestsForVersions($constraints, ['4.0', '3.9', '2.1', '1.9'], FALSE, '7.x');
    

    We're testing for 8.x by default. This is the only place where we deviate from that: we test 7.x here. Shouldn't we also test 9.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?

  8. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -104,4 +198,42 @@ public function testToArray() {
    +  public function testTempToProveAllOldCasesCovered() {
    

    Hah, clever!

wim leers’s picture

For clarity: only points 3 and 7 still need to be addressed.

tedbow’s picture

StatusFileSize
new2.11 KB
new9.54 KB

@Wim Leers thanks for the review!

  1. re #13.1 we can only hope that a core committer uses their power to demand $spaciness 😜
  2. Re #13.3

    This is also the case in HEAD, but … why does 1.9 not meet the <8.x-4.x,>8.x-1.x constraint?

    1.9 would satisfy <8.x-4.x,>=8.x-1.x

    But >8.x-1.x doesn't include any 1.x versions

    +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -91,6 +91,102 @@ public function providerIsCompatible() {
    +          // Test greater than.
    +          $constraint = ">{$space}8.x-1.x";
    +          $tests += $this->createTestsForVersions($constraint, ['2.0'], TRUE);
    +          $tests += $this->createTestsForVersions($constraint, ['1.1', '0.9'], FALSE);
    

    This test above confirms this

  3. re #13.7 Good catch!
    I move this below
    +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -91,6 +91,102 @@ public function providerIsCompatible() {
    +          // Test greater than and less than.
    +          $constraint = "<{$space}8.x-4.x,{$space}>{$space}8.x-1.x";
    +          $tests += $this->createTestsForVersions($constraint, ['4.0', '1.9'], FALSE);
    +          $tests += $this->createTestsForVersions($constraint, ['3.9', '2.1'], TRUE);
    

    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.x which 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.

  4. I had removed 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 to
    the new providerIsCompatible() to certain we still cover all old cases. We can remove it once it gets closer to RTBC.
wim leers’s picture

  1. Ahhhh! Thanks, makes sense :)
  2. Thanks for addressing that, much better now. Though it makes me wonder whether we should also be testing a constraint containing 9.x as the core version in the constraint and specify 9.x as core compatibility, to test a succeeding constraint for the next major Drupal version?
  3. +1 to removing it in the next patch iteration. If it weren't for my question in the previous point, I would have RTBC'd and removed it myself here :)

Once point 3 is addressed, I think this is ready.

tedbow’s picture

StatusFileSize
new10.71 KB
new11.38 KB
new17.23 KB
new10.73 KB
  1. re #16.3 good point! yes we should be testing 9.x. But also this got me thinking, would we write the same test coverage if we were writing this in the 9.x cycle? Having "8.x" everywhere would look weird. The fact is \Drupal\Component\Version\Constraint is 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 loop
    // Test that core compatibility works for different core versions.
    foreach (['8.x', '9.x', '10.x'] as $core_compatibility) {
    

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

  2. I am uploading to version of the patch and interdiff.
    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
wim leers’s picture

Status: Needs review » Reviewed & tested by the community

This should cover us for a while 😜

😂

I think this is ready now.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 17: 3066448-17.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new800 bytes
new10.73 KB

Retesting and setting back to RTBC. #19 was on fail because of Drupal\FunctionalJavascriptTests\Ajax\DialogTest with

Element ... is not clickable at point (646, 331). Other element would receive the click:

Which is random drupalci test failure

Oh wait there is an extra space nit too. New patch

xjm’s picture

Status: Needs review » Reviewed & tested by the community

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

xjm’s picture

Status: Reviewed & tested by the community » Needs work

Excellent expansion of coverage. Just some small stuff:

  1. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -27,67 +27,102 @@ public function providerIsCompatible() {
    +    foreach (['', '=', '=='] as $equal_operator) {
    +      foreach (['!=', '<>'] as $not_equals_operator) {
    

    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_operator but $not_equalS_operator with an "S". Let's standardize on one or the other to avoid bugs where we might accidentally test NULL because 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.)

  2. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -27,67 +27,102 @@ public function providerIsCompatible() {
    +        foreach (['', ' '] as $space) {
    

    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?
     

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

  4. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -27,67 +27,102 @@ public function providerIsCompatible() {
    +          $tests += $this->createTestsForVersions($constraint, ['4.0', '3.9', '2.1', '1.9'], FALSE, '7.x');
    +          $tests += $this->createTestsForVersions($constraint, ['4.0', '3.9', '2.1', '1.9'], FALSE, '9.x');
    

    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?

  5. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -104,4 +139,28 @@ public function testToArray() {
    +   *   The constraint string to test.
    ...
    +   *   The versions.
    ...
    +   *   The core compatibility.
    

    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, ..."

  6. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -104,4 +139,28 @@ public function testToArray() {
    +   * @param array $versions
    ...
    +   * @return array
    

    We should almost always use a more specific data type than array. I think $versions is a string[] and the return value looks to be best described as array[].

  7. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -104,4 +139,28 @@ public function testToArray() {
    +      $tests["$core_compatibility-($constraint_string)-$version"] = [$constraint, $version, $expected_result];
    

    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?

wim leers’s picture

#22

  1. Might I suggest spaciness? 😜
  2. Perhaps we just hardcode a single space and then do a str_replace() to replace it with zero or two spaces?
  1. AFAIK all data providers do @return array.
wim leers’s picture

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new11.43 KB
new11.71 KB

re #22
@xjm thanks for the review!

  1. How is empty-string an equality operator? 🤔
    if no operator then equals is assumed so for every equals testing both operators and no operators. Add comment

    changed to $not_equal_operator

  2. using $whitespace
  3. Agree this affects the readability. We could go back to the method in #4 where all the test cases are made and then we loop through and change the constraint strings. But this str_replace calls in where we don't have to have them. Also it require changing \Drupal\Tests\Component\Version\ConstraintTest::testIsCompatible() to receive strings instead of instance of \Drupal\Component\Version\Constraint

    I personally like the way it is instead because there is less logic and no change to \Drupal\Tests\Component\Version\ConstraintTest::testIsCompatible()

  4. I put this outside of the $core_compatibility loop 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.

  5. I change the descriptions to say where they will ultimately be used.
  6. fixed
  7. Changed to '::" delimiter

re <a href="#comment-13179942">#22</a>.3 I think if we are going back to using <code> str_replace()

we should use for all operators like in #4 and before

tedbow’s picture

StatusFileSize
new1.42 KB
new11.81 KB

more fixes for #22.5 after chatting with @xjm again

xjm’s picture

Didn't complete a full review yet, just noticed this bit of spinach:

+++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
@@ -27,67 +27,111 @@ public function providerIsCompatible() {
+        //version strings and between operators and version strings.

Coding standards error: missing space between // and comment text.

wim leers’s picture

Spinach?! :D

tedbow’s picture

StatusFileSize
new904 bytes
new11.82 KB

fixed #27

wim leers’s picture

Status: Needs review » Reviewed & tested by the community
Mixologic’s picture

Status: Reviewed & tested by the community » Needs work

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

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new12.48 KB
new3.48 KB
new8.28 KB

re #31
@Mixologic thanks for the review!

  1. re #25.3 : Im -1 on introducing loops and logic, and additional function in the data provider, as not only does it impact readability,

    Yeah was just pointing out we could go this way if the readability was big concern but I would rather not too

  2. the whitespace loop is really only proving that \s* in the regex doesnt someday lose its *

    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.

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

    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

    return [
          [new Constraint('8.x-2.0', '8.x'), '2.0', TRUE],
          [new Constraint('2.0', '8.x'), '2.0', TRUE],
          [new Constraint('9.x-2.0', '9.x'), '2.0', TRUE],
          [new Constraint('9.x-2.0', '8.x'), '2.0', FALSE],
        ];
    

    The test coverage also proves it can be provided but it is not necessary.

    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.

    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.

  4. As per a suggestion from @xjm I added comments with an example before each setting of $constraint to 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

    Dependencies can also include version restrictions, for example webform:webform (>=8.x-5.x).

    Our Drupal 7 docs are better but I won't expect someone new to Drupal as of 8 to actually look at these.

Mixologic’s picture

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.

Ah, this makes a lot more sense framed that way. I concur 100%.

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.

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.

wim leers’s picture

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.

This is not accurate, although I totally understand why you arrived at this conclusion :)

+++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
@@ -27,67 +27,116 @@ public function providerIsCompatible() {
+            $tests += $this->createTestsForVersions($constraint, ['4.0', '3.9', '2.1', '1.9'], FALSE, $core_compatibility);
...
+            $tests += $this->createTestsForVersions($constraint, ['2.4-beta3'], FALSE, $core_compatibility);
...
+            $tests += $this->createTestsForVersions($constraint, [$core_compatibility], TRUE, $core_compatibility);

@@ -104,4 +153,33 @@ public function testToArray() {
+      $tests["$core_compatibility::($constraint_string)::$version"] = [$constraint, $version, $expected_result];

The combination of

  • returning an array with named keys that depends on the actual constraint string
  • using the + operator

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

Mixologic’s picture

Thanks Wim. I failed to notice that we're generating the cases first, then running them. My mistake.

tedbow’s picture

StatusFileSize
new10.62 KB
new12.25 KB

re #33 @Mixologic thanks for pushing to have the test more readable and maintainable!

  1. 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,

    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.

  2. for example. (i.e when we add ^/~ operators, where would those go? )

    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\Semver so we wouldn't actually need to add this logic to \Drupal\Component\Version\Constraint so won't have to worry about this test class becoming more complicated.

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

    Ok this patch gets rid of the $whitespace loop 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 $whitespace in 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.

  4. 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,

    Hope the comments I added in #32

    +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -47,84 +48,88 @@ public function providerIsCompatible() {
    -            // RC version.
    +            // RC version. For example "=8.x-1.1-rc11".
                 $constraint = "{$equal_operator}{$whitespace}$core_compatibility-1.1-rc11";
    

    Make this easier to read because you have version of the constraint in the comment without the variables.

Status: Needs review » Needs work

The last submitted patch, 36: 3066448-36.patch, failed testing. View results

tedbow’s picture

Status: Needs work » Needs review

pretty sure #37 is unrelated(random?) test failure

tedbow’s picture

  1. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -27,67 +27,129 @@ public function providerIsCompatible() {
    +          // Test 2 equals with 1 that matching and with nonsensical missing a
    +          // dash. For example "2.x,2.4-beta3".
    +          $constraint = "{$equal_operator} {$core_compatibility}2.x, {$equal_operator} 2.4-beta3";
    +          $tests += $this->createTestsForVersions($constraint, ['2.4-beta3'], FALSE, $core_compatibility);
    

    This comment is wrong updating to match the test case.

  2. +++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
    @@ -27,67 +27,129 @@ public function providerIsCompatible() {
    +          $constraint = "{$equal_operator} {$core_compatibility}2";
    +          $tests += $this->createTestsForVersions($constraint, [$core_compatibility], TRUE, $core_compatibility);
    

    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.

tedbow’s picture

wim leers’s picture

+++ b/core/tests/Drupal/Tests/Component/Version/ConstraintTest.php
@@ -27,67 +27,125 @@ public function providerIsCompatible() {
+          // Stable version. For example "=8.x-1.0".
+          $constraint = "{$equal_operator} $core_compatibility-1.0";

Ü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 $constraint variables' expressions easier to read. I don't think this is worth holding the patch up for though.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community
  • #36.2:
    for example. (i.e when we add ^/~ operators, where would those go? )

    Are we planning on adding those? I would hope we would just wait until #3005229:

    Per @Mixologic's comment at #3069795-14: [meta] Improve dependency management for extensions.4, that's indeed what we are going to do.

  • #39.2: nice find, and +1 to fixing that in another issue.

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.

Mixologic’s picture

Yep. Im 100% +1 on this. Adds valuable test coverage for functionality that we don't want to accidentally impact.

larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Committed f9ca4c5 and pushed to 8.8.x. Thanks!

  • larowlan committed f9ca4c5 on 8.8.x
    Issue #3066448 by tedbow, Wim Leers, Mixologic, xjm: Harden test...
tedbow’s picture

🎉 @larowlan thanks for committing and @Wim Leers @xjm and @Mixologic thanks for all the help!

Status: Fixed » Closed (fixed)

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