Problem/Motivation
@alexpott:
By default a machine name can only contain lowercase. 'replace_pattern' => '[^a-z0-9_]+', from \Drupal\Core\Render\Element\MachineName::processMachineName(). I think the random machine name generated should conform to our default regex.
Current code:
# Drupal\Tests\UnitTestCase
public function randomMachineName($length = 8) {
return $this->getRandomGenerator()->name($length, TRUE);
}
# Drupal\Component\Utility\Random
/**
* Generates a random string containing letters and numbers.
*
* The string will always start with a letter. The letters may be upper or
* lower case ...
*/
public function name($length = 8, $unique = FALSE) {
...
}
Why upper and lower mix can be a problem:
1. It can be the cause of random fails in tests that use randomMachineName() like value for UI field. Because the value will be automatically converted to lowercase, which does not mean its unique.
Example (psedo code):
createFieldById('Value'); #Ok, 'Value' -> 'value'.
createFieldById('VALUE'); #Error, 'VALUE' -> 'value', but the field with id "value" already exists
2. It also causes inconvenience when assert expected and actual id:
$expected = randomMachineName();
$field = createFieldById($expected);
$actual = $field->id();
Or when creating other instances, example:
# Drupal\Component\Utility::getId()
$id = str_replace([' ', '_', '[', ']'], ['-', '-', '-', ''], mb_strtolower($id));3. It also causes conflicts with other places because only lowercase for id is a common practice.
Proposed resolution
Add new Random::machineName() function which is used by RandomGeneratorTrait::randomMachineName() and UnitTestCase::randomMachineName()
This new function generates a random machine name containing only lower case letters and numbers. A RuntimeException is thrown when a unique machine name can't be generated within 100 tries.
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|
Issue fork drupal-2972573
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #1
Anonymous (not verified) commentedvaplas created an issue. See original summary.
Comment #2
Anonymous (not verified) commentedComment #3
jonathanshawThis is not a duplicate of #2556711: Tired of Unicode::strtolower($this->randomMachineName()). This is a broader solution that would make #2556711: Tired of Unicode::strtolower($this->randomMachineName()) unnecessary and address problems potentially still present in that approach.
This came up again in #3174874: MediaTypeCreationTrait creates media type with invalid machine-readable name where @lendude said:
Comment #4
lendudeSo I would think, something like this.
If this is ok then we can remove all the wrappers around the current calls to randomMachineName()
Comment #6
jonathanshawThere is a dirtier but DRYer approach possible - we could get machine names and other names to share their lower case uniqueness:
Comment #7
lendudeThis should fix the failing tests.
Not sure about #6, yeah we could reuse some code but I like that this is a little more explicit. ¯\_(ツ)_/¯
Comment #9
anmolgoyal74 commentedUpdated Schematest and SqlContentEntityStorageSchemaTest to use
machinename()instead ofname()Comment #10
lendude@anmolgoyal74 uhhh? Why? How is that in scope?
Comment #12
krzysztof domańskiThe machine name is not universal. Random text containing underscore (e.g.
cy7q0twj5qpl98_c) is not valid for menu."The machine-readable name must contain only lowercase letters, numbers, and hyphens."
Comment #13
krzysztof domańskiCan we add a pattern of allowed characters as a parameter of the
randomMachineNamemethod?...or something like
$validatorfrom thestring($length, $unique, $validator)method.Comment #14
jonathanshawI suggest we should not do this.
The randomMachineName() method is basically a helper to simplify using randomName(). It seems to me that it should be really simple.
The idea of a pattern of allowed characters makes sense, but let's make that a separate issue for adding that option to randomName().
This also gives me an idea for an alternative way to DRY randomMachineName: we could add an optional $lower_case parameter to it, and make randomMachineName a wrapper around randomName..
Comment #15
krzysztof domański1. Let's check where machine name cannot contain underscore.
2. See #12. Can we set any machine names instead of random where underscore is not allowed?
Comment #16
krzysztof domański1. #7 is a good approach. Setting any machine names (like #15.2) will fix the tests.
2. Changing the behavior of
randomMachineNamerequires change record.Comment #17
jonathanshaw#15.1 is awesome! It looks like you've identified 28 random fails (each a critical issue). There are a significant concern for core maintainers. But this seems to be a seperate issue to this one - why should we do this together in this issue?
Comment #18
jonathanshawSorry, I'm wrong. #15 has nothing to do with random failures. It puts a character into random::name that is not normally there.
Comment #19
krzysztof domańskiWe commonly use the
randomMachineNamemethod because there is no other that generates a random string containing only letters and numbers. Unfortunately, it is also used in situations for which it was not intended, e.g. user roles, block or menu ids where underscore is not allowed.Comment #20
jonathanshawHow about this approach:
Utility/Random
RandomGeneratorTrait
Comment #21
krzysztof domańskiI added a separate issue #3176270: Add to Drupal\Component\Utility\Random::string an additional parameter of possible characters to select from. Then we will change the
randomMachineName()easier. Any other method can be added in a similar way, e.g.randomDomId().Comment #22
krzysztof domańskiDrupal\Tests\RandomGeneratorTrait::randomMachineName() now generates random strings of lowercase letters, numbers and underscore
Comment #23
jonathanshawUsing #3176270: Add to Drupal\Component\Utility\Random::string an additional parameter of possible characters to select from in randomMachineName is not simple. #7 and Random::Name have a narrower criteria for the first character in the string than for the rest of the string.
Comment #25
larowlanComment #26
krzysztof domańskiI agree that #7 is simpler. The disadvantage is that it requires a new method
machineName. An additional parameter of possible characters is more universal.Comment #27
lendudeLet's go for the simple approach first. Let's remove the underscore so it should be compatible with all machine names in use.
Comment #28
krzysztof domańskiIn#27 we duplicate many methods (
name -> machineName,testRandomMachineNameException -> testRandomNameException,testRandomNamesUniqueness -> testRandomMachineNamesUniqueness,testRandomNameNonUnique -> testRandomMachineNameNonUnique).With #3176270: Add to Drupal\Component\Utility\Random::string an additional parameter of possible characters to select from it is much simpler.
Comment #29
krzysztof domańskiEasier to understand.
Comment #31
jonathanshaw#28 is true, but doesn't need to be an argument for postponing this.
The test fails are because the first character needs different handling, as mentioned #23.
Comment #32
lendudeSo, did I get it green this time?
Comment #34
jonathanshawNit:(optional)
This is a mistake. $unique is supposed to guarantee the uniqueness of the returned string. But we should store the generated string even if this call does not require uniqueness, so that other calls to the method can avoid enforce uniqueness if they want. However, the other methods in this class have the same issue, so it makes sense to do it this way here for consistency.
There's a reasonable chance of this test passing even without enforcing uniqueness as there are 36 possible characters. I wonder if it should be $i <= 25.
Comment #39
pooja saraah commentedThanks for your suggestions @jonathanshaw
Addressed comment #34 point 1,3
Attached patch against Drupal 10.1.x
Attached reroll patch
Comment #40
jonathanshawComment #41
catchThis ought to be able to have scalar type hints and a return type hint now.
Also needs a @throws for the exception.
I think this could use assertArrayNotHasKey()
Comment #42
anchal_gupta commentedI uploaded the patch and addressed #41 both the point. Please review it
Comment #43
jonathanshawThank for the patch @anil_gupta!
You missed this review point from #41.
Exception thrown when either $counteror are not generate a unique random machine name.Let's improve the grammar/spelling:
Thrown if a unique machine name cannot be generated within the allowed number of random attempts.
Please set this issue to Need Review when uploading a patch in order to run the tests.
Comment #44
ravi.shankar commentedAddressed comment #43, please review.
Comment #46
acbramley commentedI think this may be causing a random failure #3354138: [random test failure] MenuUiTest::testMenuAdministration can generate "escaped locator"
Comment #47
acbramley commentedCreated an MR, starting from #39 since #42 and #44 didn't actually address the feedback (and 44 changed code out of scope of this issue)
Hiding all old patches.
Comment #49
mondrakeRelated, #3353658: [PHPUnit 10] Provide a static alternative to randomMachineName() and implement in data providers.
Comment #50
smustgrave commentedChanges look good but could the issue summary be updated with the solution.
Example if this is just replacing $this->getRandomGenerator()->name($length
Comment #51
acbramley commentedIS updated
Comment #52
smustgrave commentedThanks that clears things up!
Comment #54
longwaveNeeds rebase/merge following #3353658: [PHPUnit 10] Provide a static alternative to randomMachineName() and implement in data providers
Also added a couple of nitpicks to the test.
Comment #57
rpayanmAdded the @longwave's suggestions and moved the changes to the branch 11.x
Please review.
Comment #58
smustgrave commentedReviewing 4193
Reroll seems good and points made on the tests have been addressed.
Comment #59
longwaveI don't think the reroll is correct, it is undoing some of the recent work in #3358416: Use RandomGeneratorTrait in UnitTestCase and remove direct implementations of randomMachineName and getRandomGenerator and #3353658: [PHPUnit 10] Provide a static alternative to randomMachineName() and implement in data providers
Comment #60
rpayanmSorry for messing it up, trying to fix it.
Comment #61
rpayanmPlease review.
Comment #62
smustgrave commentedLeft a comment on the MR.
Thanks!
Comment #63
rpayanm@smustgrave sorry, I don't see the comment, can you link it in a comment, please.
Comment #64
wim leersI ran into this very problem too over at #3361534-57: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate.
In fixing it, I noticed that a bunch of places do
Shouldn't this issue remove all those
strtolower()(andmb_strtolower()) occurrences? 🤔Because this issue only touches
Random::machineName(), but\Drupal\Tests\RandomGeneratorTrait::randomMachineName()calls it:Comment #65
wim leersThis now blocks #3361534 since per @longwave in #3361534-58: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate, I don't think I can land that issue without this being fixed first.
The current implementation is on its own responsible for a huge number of invalid config entities, but for now we're all blissfully unaware 😅
Comment #66
wim leersComment #67
borisson_This last commit introduced new test failures
Comment #68
borisson_Discussed with @longwave that the
(new Random())here that is wrapped in an anonymous instance is the problem which lead to the the test fail in the last patch. Reverted that change, back to rtbc.Comment #69
longwaveAdded typehint and default value to the new property.
Somewhere the code that actually uses this new method has been lost; we need to update \Drupal\TestTools\Random to use this new method instead of
->name()?Comment #70
borisson_Back to needs review.
Comment #71
smustgrave commentedRemarking. #69 appeared to be addressed in https://git.drupalcode.org/project/drupal/-/merge_requests/4193/diffs?co...
Previous reviews still apply.
Comment #74
longwave@Wim Leers makes a good point in #64 that we should no longer need to wrap random machine names in strtolower(), let's open a followup to clean that up.
Committed 9a1ce8c and pushed to 11.x. Thanks!
Comment #75
longwaveOpened #3376281: Random machine names no longer need to be wrapped in strtolower()
Comment #76
lauriiiLooks like this caused a new random fail: #3376563: Random test fail in Drupal\Tests\Component\Utility\RandomTest::testRandomMachineNamesUniqueness. 😅