Problem/Motivation
#3119761: Replace multiple test methods in InfoParserUnitTest with 1 testInfoException and a dataprovider discovered in because that was converting all the test methods in \Drupal\Tests\Core\Extension\InfoParserUnitTest that thrown an exception into 1 method with a data provider.
\Drupal\Tests\Core\Extension\InfoParserUnitTest::testUnparsableCoreVersionRequirement() did not call the parser twice.
Therefore this call
try {
$supports_pre_core_version_requirement_version = static::isConstraintSatisfiedByPreviousVersion($parsed_info['core_version_requirement'], static::FIRST_CORE_VERSION_REQUIREMENT_SUPPORTED_VERSION);
}
catch (\UnexpectedValueException $e) {
throw new InfoParserException("The 'core_version_requirement' constraint ({$parsed_info['core_version_requirement']}) is not a valid value in $filename");
}
in \Drupal\Core\Extension\InfoParserDynamic::parse() did not act as expect on the 2nd call.
isConstraintSatisfiedByPreviousVersion() has static var that caches the results for any $constraint and $version combination.
It assumes false so it sets
$evaluated_constraints[$constraint][$version] = FALSE;
Before it evaluates the combination.
The first time the exception is thrown after this is set. The second time
if (!isset($evaluated_constraints[$constraint][$version])) {
is true because it was set to FALSE before the exception is thrown.
So the method returns false.
This is bad but it is not as bad as it could be further down in the function we have
$core_version_constraint = isset($parsed_info['core_version_requirement']) ? $parsed_info['core_version_requirement'] : $parsed_info['core'];
$parsed_info['core_incompatible'] = !Semver::satisfies(\Drupal::VERSION, $core_version_constraint);
If core version is invalid but we had the problem above this line still thrown exception because the original exception came from Semver::satisfies() evaluating the same value.
The only problem is that the reason we catch the error in the first place is to make the exception better my mentioning the filename of the info.yml file like all other parser errors, for example for a missing type key.
So the first time the parser is called with a invalid core_version_requirement value then get the exception we make with the filename and the 2nd time you get the exception straight from Semver::satisfies() without the filename
This make clear the importance of #3119761: Replace multiple test methods in InfoParserUnitTest with 1 testInfoException and a dataprovider so all our test methods in this class always call 2x and otherwise act the same.
Proposed resolution
In \Drupal\Core\Extension\InfoParserDynamic::isConstraintSatisfiedByPreviousVersion catch any exceptions and
unset($evaluated_constraints[$constraint][$version]);
Then rethrow the exception. That any exception regardless of type will not result in a cached false value.
Remaining tasks
Do it
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | interdiff-2-4.txt | 2.04 KB | tedbow |
| #4 | 3119822-4.patch | 3.82 KB | tedbow |
| #2 | 3119822-2.patch | 5.87 KB | tedbow |
| #2 | 3119822-test-only-FAIL.patch | 3.75 KB | tedbow |
Comments
Comment #2
tedbowHere is test only patch and test + fix patch.
We could fix this in #3119761: Replace multiple test methods in InfoParserUnitTest with 1 testInfoException and a dataprovider since the test method we are updating will be removed(but not the test case) but it seems better to keep that issue a test only issue.
Just to be clear of the importance #3119761. if that issue was done and we were then to add a test case like is covered in
testUnparsableCoreVersionRequirement()we would only have to add the yml and expected exception message to the dataProvider. The new test method in that issue would always call the parser 2x times for all the test cases so we couldn't add a new test case and forget to check the case where the parser is called 2x.We either forgot call the parser 2x in
testUnparsableCoreVersionRequirement()or we added that method before we figured we need to test that.It would be great to get this issue and then #3119761 in so can't find ourselves in this situation again.
Comment #4
tedbowTest failed as expected
Important thing to notice here is an exception is still thrown just not the expected type and the message is also different. This is why I didn't mark the issue major.
Here is patch without the Drupalci changes.
Comment #5
damienmckennaThis is likely what causes #3123537: /admin/reports/status should not WSOD if it finds an extension missing core_version_requirement in its info.yml file.. will test to see.
Comment #6
damienmckennaNope, the patch in #4 doesn't solve this new problem.
Comment #12
quietone commentedThe method being changed in the patch was removed in #3077703: Remove pre-8.7.7 core compatibility checks in extension parsing. And reading the commit I think the problem has been fixed. But the patch here has a test that is not in core. Comment in #2 refers to #3119761: Replace multiple test methods in InfoParserUnitTest with 1 testInfoException and a dataprovider which has additional tests, including the one here.
Therefore, closing as outdated. If this is incorrect reopen the issue, by setting the status to 'Active', and add a comment explaining what still needs to be done.
Thanks!