Follow-up to #2257769: Adding an Entity Reference field in the Field UI throws a PHP notice; fails to add field
abstract protected function FieldDefinitionTestBase::getNamespacePath() was introduced in #2257769: Adding an Entity Reference field in the Field UI throws a PHP notice; fails to add field
Implementations of this method are supposed to return paths like "core/modules/path/src" (with PSR-4) or "core/modules/path/lib/Drupal/path" (PSR-0, soon deprecated and then unsupported).
The result will then be used to determine the module name, and it is used as the path to look for plugins.
The problem this solves is that phpunit does not know any drupal_get_path().
Problem: "core/modules/path/src" does not reliably tell us the module name. For most core modules this works, but it is pure coincidence.
A module is not required to be in a folder of the same name.
Solution:
The signature of the method needs to change, or new methods need to be added.
On the other hand, we don't really need the namespace path, this can be determined from the module directory.
If we are not sure about PSR-0 vs PSR-4, we simply register both. And once PSR-0 gets deprecated, we remove it and only leave the PSR-4 path.
Options:
protected function getModuleInfoFilePath() {
return 'core/modules/path/path.info.yml';
}
Pro: Only one method, simple return type, gives all we need.
Con: Needs preg_match() or similar to analyse the result.
protected function getModuleAndPath() {
return array('path', 'core/modules/path');
}
Pro: Only one method, gives all we need.
Con: Arbitrary magic numeric array indices.
protected function getModuleName() {
return 'path';
}
protected function getModuleDirectory() {
return 'core/modules/path';
}
Pro: Very explicit.
Con: Two methods = verbose, cluttered, more work to implement
Pollutes the already crowded namespace for method names in a test class, especially because these two method names sound rather generic.
protected function getNamespace() {
return 'Drupal\\path';
}
protected function getNamespacePath() {
return 'core/modules/path/src';
}
Pro: Very explicit. Immediately gives us the directory to look for plugins.
Con: Module name needs to be extracted with preg_match() or similar.
I personally prefer the one-method solutions, and would suggest to add a default implementation that extracts module name and path from the test class, using reflection.
| Comment | File | Size | Author |
|---|---|---|---|
| #14 | interdiff-13-14.txt | 1.33 KB | xjm |
| #14 | 2273311-14.patch | 2.83 KB | xjm |
| #13 | D8-2273311-13-FieldDefinitionTestBase.patch | 2.77 KB | donquixote |
Comments
Comment #1
donquixote commentedHere is the implementation from #2247991-34: [May 27] Move all module code from …/lib/Drupal/… to …/src/… for PSR-4, where the method returns
DRUPAL_ROOT . '/core/modules/path/path.info.yml'.Not saying this is the best one, but we have to start somewhere.
Two patches: One with only the method, another with a default implementation.
Comment #2
donquixote commentedComment #3
donquixote commentedComment #4
donquixote commentedNow with getModuleAndPath() instead of getModuleInfoFilePath().
Has a lot of @todo, that needs feedback.
Comment #5
xjmComment #6
xjmIs this true? I thought for D8 we deliberately changed it to be one module, one directory specifically because of things like this. That's why views and views_ui are both top-level modules in HEAD, for example.
Comment #7
donquixote commentedTalking with xjm on irc, this is a critical bug because it blocks #2247991: [May 27] Move all module code from …/lib/Drupal/… to …/src/… for PSR-4.
We discussed this on irc, result:
#1532250: Only one module per directory does not explicitly mention what the directory name should be.
Moving views to _views and running drush pm-info views gives "_views", which shows that a policy of dirname = module name, if exists, is not enforced.
Either way we should not rely on it, and I think return array($module_name, $module_dir) is a solid solution and preferable to what we have now.
Imo we should commit one of the patches in #4, preferably the shorter one, and then re-discuss this when we have more use cases (more field tests).
I'm going to post an intended-to-fail test to show why this is a bug, some time later.
Comment #8
sunA few clarifications:
basename($info_filename, '.info.yml')is the internal name of a module/extension.\Drupal\$name).basename('Drupal\mymodule') == 'mymodule')Comment #9
xjmThanks @donquixote. We can see that this is a hard blocker for #2247991: [May 27] Move all module code from …/lib/Drupal/… to …/src/… for PSR-4 in comments 10-13 of that issue.
To clarify the bug a little, the question is, do we support:
myproject/modules/subproject/myproject_subproject.info.ymlOr do we require:
myproject/modules/myproject_subproject/myproject_subproject.info.ymlIn #1299424: Allow one module per directory and move system tests to core/modules/system we added the requirement that there only be one module per directory (see the change record), but there is nothing enforcing that the directory have the same name as the module. For example, @donquixote confirmed that you can move the Views module to
core/modules/_views/and still enable it successfully.This is a specific problem for PSR-4 because with PSR-0 we had the information in the directory path, e.g there would have been:
myproject/modules/subproject/lib/Drupal/myproject_subproject/Classes/HereWith PSR-4 we no longer have that.
Either we need to separately enforce that the directory name is the same as the module name, which would require a separate policy discussion, or we treat this as a bug, and include an automated test to prove that enabling a module in a directory with a different name than its name works (which @donquixote will explore today).
Comment #10
xjmBased on #8, it sounds like the answer is, yes, we explicitly support the directory name differing from the module name:
myproject/modules/subproject/myproject_subproject.info.ymlAnd therefore should go ahead and fix it here.
Comment #11
sunSorry, but this issue is about a test base class. We're not going to add tests for tests.
Let's also make sure that each of these child issues relates to the parent PSR-4 issue.
Comment #12
xjm@ sun is correct; I apparently was confusing this with a different part of the patch from #2247991: [May 27] Move all module code from …/lib/Drupal/… to …/src/… for PSR-4 that was changing some API. Adding a test to ensure a module can be enabled in a different directory name would be worthwhile but is out of scope here if this bug only exists in test code.
Comment #13
donquixote commentedYo.
So let's focus on the simple version from #4, which I think is the best candidate to be approved quickly.
Unfortunately, it seems that I messed up the patch upload in #4, and so the relevant patch is not even online. Duh!
So I'm doing it again, and hiding the others.
Comment #14
xjmThanks @donquixote. Agreed that we should fix it in the simplest way possible to just unblock the other issue. E.g.:
This is a bit of a silly contorted abstraction and opaque, especially since PathFieldDefinitionTest is the only implementation of this base class, but it's pre-existing in HEAD so I agree we should just fix the PSR-4-blocking bug here.
I rerolled to tweak the docs a little to our normal standards, but the patch looks fine to me. Let's get it in. (RTBCing since I just tweaked docs.)
Comment #15
alexpottCommitted 7ae6f6b and pushed to 8.x. Thanks!
FieldDefinitionTestBase is a mess but that is not the fault of PSR-4
Comment #17
tstoecklerA little bit unhappy that we went with a single method that returns two completely different things instead of two separate methods, especially because I had discussed this with @donquixote in IRC, but at the end of the day this is a freaking test base class so whatever...
Comment #18
xjm@tstoeckler, apologies, I didn't see any indication of an IRC conversation. =/ Yeah, it's far from perfect, but then that entire entire part of the test and base class is kind of goofy and ugly (not to mention there's abstract methods on the TEST base class that get implemented.... once). We just don't need to block a beta blocker on cleaning up the test. Maybe file a followup? We can definitely improve the test so long as it doesn't regress the PSR-4 support.
Comment #19
donquixote commented@xjm (#18):
I did not really study the issue that introduced this, but I am assuming that the idea was we would get more tests of this kind in the future.
So let's actually observe this and rethink the API if we have more use cases?
@tstoeckler (#17):
Sorry, I did not intend to go over you. You did in fact suggest two separate methods, but in the end I had the impression you would be happy with either solution, as long as it is documented.
I personally was against this kind of array return value at first, for the reason mentioned in the issue summary. But then changed my mind.
Having both in the same method makes it clear that you want the module name and the path for the *same module*, instead of just any module name and then any module path. If you return array('system', 'core/modules/views'), you really feel you are doing it wrong.
Also if you automate this with a default implementation, as in the bigger patch in #4, you generally compute both the name and the path at the same time, so you would then have to artificially split this up to two methods, or even compute it twice.
The same where this stuff is used, you want to have both in the same place.
One alternative I could imagine is to remove FieldDefinitionTestBase::setUp() and the abstract methods, and instead introduce a FieldDefinitionTestBase::createFieldDefinition($module_name, $module_dir, $plugin_id);
So a test that inherits from that would have
It won't be as easy anymore to automate this with a default implementation. But maybe we don't care.
Comment #20
tstoecklerRight, my previous post sounded like @donquixote went over my head or something. That was not really the case. Sorry for that. It was more a miscommunication. I would have supported returning and array of $namespace => $directory, as that is a (somewhat) meaningful data structure on its own.
But again, let's move on. There are bigger fry to fish... :-)
And thanks for fixing this!!!
Comment #21
donquixote commentedThe reason I did not do this is that array($namespace => $directory) suggests that you could have more than one such a pair. E.g.
array('path' => 'core/modules/path', 'system' => 'core/modules/system'). Whereas witharray($namespace, $path)it is clear you can only have one module with its path.(I think noone here is trying to bikeshed this atm, I simply want to explain this for others looking at this issue in the future.)