diff -u b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php --- b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php +++ b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php @@ -213,147 +213,281 @@ */ public function providerTestPostProcessExtension() { $data = []; - $data['manual_base_hook'] = [ - 'defined_functions' => ['cat', 'mouse', 'cheese'], + + // This is test data for unit testing + // \Drupal\Core\Theme\Registry::postProcessExtension(), not what happens + // before it. Therefore, for all test data: + // - Explicitly defined hooks also come with explicitly defined preprocess + // functions, because those are collected in + // \Drupal\Core\Theme\Registry::processExtension(). + // - Explicitly defined hooks that set a 'base hook' also have + // 'incomplete preprocess functions' set to TRUE, since that is done in + // \Drupal\Core\Theme\Registry::processExtension(). + + // Test the discovery of suggestions via the presence of preprocess + // functions that follow the "__" naming pattern. + $data['base_hook_with_autodiscovered_suggestions'] = [ + 'defined_functions' => [ + 'test_preprocess_test_hook__suggestion', + 'test_preprocess_test_hook__suggestion__another', + ], 'hooks' => [ 'test_hook' => [ - 'preprocess functions' => ['cat', 'mouse'], + 'preprocess functions' => ['explicit_preprocess_test_hook'], ], - 'test_hook_manual' => [ - 'preprocess functions' => ['cheese'], + ], + 'expected' => [ + 'test_hook' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + ], + ], + 'test_hook__suggestion' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + 'test_preprocess_test_hook__suggestion', + ], 'base hook' => 'test_hook', - 'incomplete preprocess functions' => TRUE, ], - 'test_hook_manual__foo' => [ - 'preprocess functions' => ['foo'], - 'base hook' => 'test_hook_manual', - 'incomplete preprocess functions' => TRUE, + 'test_hook__suggestion__another' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + 'test_preprocess_test_hook__suggestion', + 'test_preprocess_test_hook__suggestion__another', + ], + 'base hook' => 'test_hook', + ], + ], + ]; + + // Test that suggestions following the "__" naming pattern can also be + // explicitly defined in hook_theme(), such as 'field__node__title' defined + // in node_theme(). + $data['base_hook_with_explicit_suggestions'] = [ + 'defined_functions' => [ + ], + 'hooks' => [ + 'test_hook' => [ + 'preprocess functions' => ['explicit_preprocess_test_hook'], ], - 'test_hook_manual__foo__bar' => [ - 'preprocess functions' => ['bar'], - 'base hook' => 'test_hook_manual', + 'test_hook__suggestion__another' => [ + 'base hook' => 'test_hook', + 'preprocess functions' => ['explicit_preprocess_test_hook__suggestion__another'], 'incomplete preprocess functions' => TRUE, ], ], 'expected' => [ 'test_hook' => [ - 'preprocess functions' => ['cat', 'mouse'], + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + ], ], - 'test_hook_manual' => [ - 'preprocess functions' => ['cat', 'mouse', 'cheese'], + 'test_hook__suggestion__another' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + 'explicit_preprocess_test_hook__suggestion__another', + ], 'base hook' => 'test_hook', ], - 'test_hook_manual__foo' => [ - 'preprocess functions' => ['cat', 'mouse', 'cheese', 'foo'], - 'base hook' => 'test_hook_manual', + ], + ]; + + // Same as above, but also test that a preprocess function for an + // intermediary suggestion level gets discovered. + $data['base_hook_with_explicit_suggestions_and_intermediary_preprocess_function'] = [ + 'defined_functions' => [ + 'test_preprocess_test_hook__suggestion' + ], + 'hooks' => [ + 'test_hook' => [ + 'preprocess functions' => ['explicit_preprocess_test_hook'], ], - 'test_hook_manual__foo__bar' => [ - 'preprocess functions' => ['cat', 'mouse', 'cheese', 'foo', 'bar'], - 'base hook' => 'test_hook_manual', + 'test_hook__suggestion__another' => [ + 'base hook' => 'test_hook', + 'preprocess functions' => ['explicit_preprocess_test_hook__suggestion__another'], + 'incomplete preprocess functions' => TRUE, + ], + ], + 'expected' => [ + 'test_hook' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + ], + ], + 'test_hook__suggestion' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + 'test_preprocess_test_hook__suggestion', + ], + 'base hook' => 'test_hook', + ], + 'test_hook__suggestion__another' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + 'test_preprocess_test_hook__suggestion', + 'explicit_preprocess_test_hook__suggestion__another', + ], + 'base hook' => 'test_hook', ], ], ]; - $data['missing_base_hook'] = [ - 'defined_functions' => ['cat', 'mouse', 'cheese'], + + // Test that hooks not following the "__" naming pattern can explicitly + // specify a base hook, such as is done in + // \Drupal\Core\Layout\LayoutPluginManager::getThemeImplementations(). + $data['child_hook_without_magic_naming'] = [ + 'defined_functions' => [ + ], 'hooks' => [ 'test_hook' => [ - 'preprocess functions' => ['cat', 'mouse'], + 'preprocess functions' => ['explicit_preprocess_test_hook'], ], - 'test_hook_manual' => [ - 'preprocess functions' => ['cheese'], - 'base hook' => 'missing_hook', + 'child_hook' => [ + 'base hook' => 'test_hook', + 'preprocess functions' => ['explicit_preprocess_child_hook'], 'incomplete preprocess functions' => TRUE, ], ], 'expected' => [ 'test_hook' => [ - 'preprocess functions' => ['cat', 'mouse'], + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + ], ], - 'test_hook_manual' => [ - 'preprocess functions' => ['cheese'], - 'base hook' => 'missing_hook', + 'child_hook' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + 'explicit_preprocess_child_hook', + ], + 'base hook' => 'test_hook', ], ], ]; - $data['manual_suggestion'] = [ + + // Same as above, but also test that such child hooks can also be extended + // with magically named suggestions. + /* + $data['child_hook_with_suggestions'] = [ 'defined_functions' => [ - 'cat', - 'mouse', - 'cheese', - 'bread', - 'test_preprocess_test_hook__suggestion', - 'test_preprocess_test_hook__suggestion__another', - 'and_now_for_something_completely_different', + 'test_preprocess_child_hook__suggestion', + 'test_preprocess_child_hook__suggestion__another', ], 'hooks' => [ - 'unrelated_hook' => [ - 'preprocess functions' => ['and_now_for_something_completely_different'], - ], 'test_hook' => [ - 'preprocess functions' => ['cat', 'mouse'], - 'template' => 'test-hook', + 'preprocess functions' => ['explicit_preprocess_test_hook'], ], - 'test_hook__suggestion' => [ - 'preprocess functions' => ['bread'], + 'child_hook' => [ + 'base hook' => 'test_hook', + 'preprocess functions' => ['explicit_preprocess_child_hook'], 'incomplete preprocess functions' => TRUE, ], - 'test_hook__suggestion__another' => [ - 'preprocess functions' => ['cheese'], - 'incomplete preprocess functions' => TRUE, + ], + 'expected' => [ + 'test_hook' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + ], + ], + 'child_hook' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + 'explicit_preprocess_child_hook', + ], + 'base hook' => 'test_hook', + ], + 'child_hook__suggestion' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + 'explicit_preprocess_child_hook', + 'test_preprocess_child_hook__suggestion', + ], + 'base hook' => 'test_hook', ], - 'test_hook__suggestion__with_base_hook' => [ - 'base hook' => 'unrelated_hook', - 'preprocess functions' => ['something_new'], + 'child_hook__suggestion__another' => [ + 'preprocess functions' => [ + 'explicit_preprocess_test_hook', + 'explicit_preprocess_child_hook', + 'test_preprocess_child_hook__suggestion', + 'test_preprocess_child_hook__suggestion__another', + ], + 'base hook' => 'test_hook', + ], + ], + ]; + */ + + // Test that a suggestion following the "__" naming pattern can specify a + // different base hook than what is implied by that pattern. Ensure that + // preprocess functions from both the naming pattern and from 'base hook' + // are collected. + $data['suggestion_with_alternate_base_hook'] = [ + 'defined_functions' => [ + 'test_preprocess_test_hook__suggestion' + ], + 'hooks' => [ + 'test_hook' => [ + 'preprocess functions' => ['explicit_preprocess_test_hook'], + ], + 'alternate_base_hook' => [ + 'preprocess functions' => ['explicit_preprocess_alternate_base_hook'], + ], + 'test_hook__suggestion__another' => [ + 'base hook' => 'alternate_base_hook', + 'preprocess functions' => ['explicit_preprocess_test_hook__suggestion__another'], 'incomplete preprocess functions' => TRUE, ], ], 'expected' => [ - 'unrelated_hook' => [ - 'preprocess functions' => ['and_now_for_something_completely_different'], - ], 'test_hook' => [ 'preprocess functions' => [ - 'cat', - 'mouse', + 'explicit_preprocess_test_hook', + ], + ], + 'alternate_base_hook' => [ + 'preprocess functions' => [ + 'explicit_preprocess_alternate_base_hook', ], - 'template' => 'test-hook', ], 'test_hook__suggestion' => [ 'preprocess functions' => [ - 'cat', - 'mouse', - 'bread', + 'explicit_preprocess_test_hook', 'test_preprocess_test_hook__suggestion', ], 'base hook' => 'test_hook', - 'template' => 'test-hook', ], 'test_hook__suggestion__another' => [ 'preprocess functions' => [ - 'cat', - 'mouse', - 'bread', + 'explicit_preprocess_alternate_base_hook', + 'explicit_preprocess_test_hook', 'test_preprocess_test_hook__suggestion', - 'cheese', - 'test_preprocess_test_hook__suggestion__another', + 'explicit_preprocess_test_hook__suggestion__another', ], + 'base hook' => 'alternate_base_hook', + ], + ], + ]; + + // Test when a base hook is missing. + $data['missing_base_hook'] = [ + 'defined_functions' => [ + ], + 'hooks' => [ + 'child_hook' => [ 'base hook' => 'test_hook', - 'template' => 'test-hook', + 'preprocess functions' => ['explicit_preprocess_child_hook'], + 'incomplete preprocess functions' => TRUE, ], - 'test_hook__suggestion__with_base_hook' => [ + ], + 'expected' => [ + 'child_hook' => [ 'preprocess functions' => [ - 'and_now_for_something_completely_different', - 'cat', - 'mouse', - 'bread', - 'test_preprocess_test_hook__suggestion', - 'something_new', + 'explicit_preprocess_child_hook', ], - 'base hook' => 'unrelated_hook', - 'template' => 'test-hook', + 'base hook' => 'test_hook', ], ], ]; + return $data; }