<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[54493] trunk: Editor: Fix performance regression in WP_Theme_JSON_Resolver.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/54493">54493</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/54493","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>hellofromTonya</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2022-10-11 17:15:11 +0000 (Tue, 11 Oct 2022)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Editor: Fix performance regression in WP_Theme_JSON_Resolver.

A significant performance regression was added late in WP 6.1 beta cycle when some of the existing caching for `theme.json` processing was removed. The rationale for removing the caching was this code was now used before all the blocks are registered (aka get template data, etc.) and resulted in stale cache that created issues (see [https://github.com/WordPress/gutenberg/issues/44434 Gutenberg Issue 44434] and [https://github.com/WordPress/gutenberg/issues/44619 Gutenberg Issue 44619]). The changes were limited to only reads from the file system. However, it introduced a big impact in performance.

This commit adds caching and checks for blocks with different origins. How? It add caching for the calculated data for core, theme, and user based on the blocks that are registered. If the blocks haven't changed since the last time they were calculated for the origin, the cached data is returned. Otherwise, the data is recalculated and cached.

Essentially, this brings back the previous cache, but refreshing it when the blocks change.

It partially adds unit tests for these changes. Additional tests will be added.

References:
* [https://github.com/WordPress/gutenberg/issues/44772 Performance regression in WP 6.1 for theme.json processing]

Follow-up to <a href="https://core.trac.wordpress.org/changeset/54251">[54251]</a>, <a href="https://core.trac.wordpress.org/changeset/54399">[54399]</a>.

Props aristath, oandregal, bernhard-reiter, spacedmonkey, hellofromTonya.
See <a href="https://core.trac.wordpress.org/ticket/56467">#56467</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesclasswpthemejsonresolverphp">trunk/src/wp-includes/class-wp-theme-json-resolver.php</a></li>
<li><a href="#trunktestsphpunitteststhemewpThemeJsonResolverphp">trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesclasswpthemejsonresolverphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-theme-json-resolver.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-theme-json-resolver.php    2022-10-11 17:03:08 UTC (rev 54492)
+++ trunk/src/wp-includes/class-wp-theme-json-resolver.php      2022-10-11 17:15:11 UTC (rev 54493)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -21,6 +21,19 @@
</span><span class="cx" style="display: block; padding: 0 10px"> class WP_Theme_JSON_Resolver {
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Container for keep track of registered blocks.
+        *
+        * @since 6.1.0
+        * @var array
+        */
+       protected static $blocks_cache = array(
+               'core'   => array(),
+               'blocks' => array(),
+               'theme'  => array(),
+               'user'   => array(),
+       );
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Container for data coming from core.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -29,6 +42,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">        protected static $core = null;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Container for data coming from the blocks.
+        *
+        * @since 6.1.0
+        * @var WP_Theme_JSON
+        */
+       protected static $blocks = null;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Container for data coming from the theme.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -145,6 +166,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return WP_Theme_JSON Entity that holds core data.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public static function get_core_data() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( null !== static::$core && static::has_same_registered_blocks( 'core' ) ) {
+                       return static::$core;
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $config = static::read_json_file( __DIR__ . '/theme.json' );
</span><span class="cx" style="display: block; padding: 0 10px">                $config = static::translate( $config );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -163,6 +188,37 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Checks whether the registered blocks were already processed for this origin.
+        *
+        * @since 6.1.0
+        *
+        * @param string $origin Data source for which to cache the blocks.
+        *                       Valid values are 'core', 'blocks', 'theme', and 'user'.
+        * @return bool True on success, false otherwise.
+        */
+       protected static function has_same_registered_blocks( $origin ) {
+               // Bail out if the origin is invalid.
+               if ( ! isset( static::$blocks_cache[ $origin ] ) ) {
+                       return false;
+               }
+
+               $registry = WP_Block_Type_Registry::get_instance();
+               $blocks   = $registry->get_all_registered();
+
+               // Is there metadata for all currently registered blocks?
+               $block_diff = array_diff_key( $blocks, static::$blocks_cache[ $origin ] );
+               if ( empty( $block_diff ) ) {
+                       return true;
+               }
+
+               foreach ( $blocks as $block_name => $block_type ) {
+                       static::$blocks_cache[ $origin ][ $block_name ] = true;
+               }
+
+               return false;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Returns the theme's data.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * Data from theme.json will be backfilled from existing
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -189,19 +245,21 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $options = wp_parse_args( $options, array( 'with_supports' => true ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json' ) );
-               $theme_json_data = static::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( null === static::$theme || ! static::has_same_registered_blocks( 'theme' ) ) {
+                       $theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json' ) );
+                       $theme_json_data = static::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /**
-                * Filters the data provided by the theme for global styles and settings.
-                *
-                * @since 6.1.0
-                *
-                * @param WP_Theme_JSON_Data Class to access and update the underlying data.
-                */
-               $theme_json      = apply_filters( 'theme_json_theme', new WP_Theme_JSON_Data( $theme_json_data, 'theme' ) );
-               $theme_json_data = $theme_json->get_data();
-               static::$theme   = new WP_Theme_JSON( $theme_json_data );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 /**
+                        * Filters the data provided by the theme for global styles and settings.
+                        *
+                        * @since 6.1.0
+                        *
+                        * @param WP_Theme_JSON_Data Class to access and update the underlying data.
+                        */
+                       $theme_json      = apply_filters( 'theme_json_theme', new WP_Theme_JSON_Data( $theme_json_data, 'theme' ) );
+                       $theme_json_data = $theme_json->get_data();
+                       static::$theme   = new WP_Theme_JSON( $theme_json_data );
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( wp_get_theme()->parent() ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        // Get parent theme.json.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -258,7 +316,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">                $with_theme_supports = new WP_Theme_JSON( $theme_support_data );
</span><span class="cx" style="display: block; padding: 0 10px">                $with_theme_supports->merge( static::$theme );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">                 return $with_theme_supports;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -272,7 +329,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public static function get_block_data() {
</span><span class="cx" style="display: block; padding: 0 10px">                $registry = WP_Block_Type_Registry::get_instance();
</span><span class="cx" style="display: block; padding: 0 10px">                $blocks   = $registry->get_all_registered();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $config   = array( 'version' => 2 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               if ( null !== static::$blocks && static::has_same_registered_blocks( 'blocks' ) ) {
+                       return static::$blocks;
+               }
+
+               $config = array( 'version' => 2 );
</ins><span class="cx" style="display: block; padding: 0 10px">                 foreach ( $blocks as $block_name => $block_type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( isset( $block_type->supports['__experimentalStyle'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $config['styles']['blocks'][ $block_name ] = static::remove_json_comments( $block_type->supports['__experimentalStyle'] );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -298,7 +360,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $theme_json = apply_filters( 'theme_json_blocks', new WP_Theme_JSON_Data( $config, 'blocks' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                $config     = $theme_json->get_data();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return new WP_Theme_JSON( $config, 'blocks' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         static::$blocks = new WP_Theme_JSON( $config, 'blocks' );
+               return static::$blocks;
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -407,6 +470,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return WP_Theme_JSON Entity that holds styles for user data.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public static function get_user_data() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( null !== static::$user && static::has_same_registered_blocks( 'user' ) ) {
+                       return static::$user;
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $config   = array();
</span><span class="cx" style="display: block; padding: 0 10px">                $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme() );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -562,9 +629,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.8.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.9.0 Added the `$user`, `$user_custom_post_type_id`,
</span><span class="cx" style="display: block; padding: 0 10px">         *              and `$i18n_schema` variables to reset.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 6.1.0 Added the `$blocks` and `$blocks_cache` variables
+        *              to reset.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public static function clean_cached_data() {
</span><span class="cx" style="display: block; padding: 0 10px">                static::$core                     = null;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                static::$blocks                   = null;
+               static::$blocks_cache             = array(
+                       'core'   => array(),
+                       'blocks' => array(),
+                       'theme'  => array(),
+                       'user'   => array(),
+               );
</ins><span class="cx" style="display: block; padding: 0 10px">                 static::$theme                    = null;
</span><span class="cx" style="display: block; padding: 0 10px">                static::$user                     = null;
</span><span class="cx" style="display: block; padding: 0 10px">                static::$user_custom_post_type_id = null;
</span></span></pre></div>
<a id="trunktestsphpunitteststhemewpThemeJsonResolverphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php   2022-10-11 17:03:08 UTC (rev 54492)
+++ trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php     2022-10-11 17:15:11 UTC (rev 54493)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -33,6 +33,52 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        private $queries = array();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * WP_Theme_JSON_Resolver::$blocks_cache property.
+        *
+        * @var ReflectionProperty
+        */
+       private static $property_blocks_cache;
+
+       /**
+        * Original value of the WP_Theme_JSON_Resolver::$blocks_cache property.
+        *
+        * @var array
+        */
+       private static $property_blocks_cache_orig_value;
+
+       /**
+        * WP_Theme_JSON_Resolver::$core property.
+        *
+        * @var ReflectionProperty
+        */
+       private static $property_core;
+
+       /**
+        * Original value of the WP_Theme_JSON_Resolver::$core property.
+        *
+        * @var WP_Theme_JSON
+        */
+       private static $property_core_orig_value;
+
+       public static function set_up_before_class() {
+               parent::set_up_before_class();
+
+               static::$property_blocks_cache = new ReflectionProperty( WP_Theme_JSON_Resolver::class, 'blocks_cache' );
+               static::$property_blocks_cache->setAccessible( true );
+               static::$property_blocks_cache_orig_value = static::$property_blocks_cache->getValue();
+
+               static::$property_core = new ReflectionProperty( WP_Theme_JSON_Resolver::class, 'core' );
+               static::$property_core->setAccessible( true );
+               static::$property_core_orig_value = static::$property_core->getValue();
+       }
+
+       public static function tear_down_after_class() {
+               static::$property_blocks_cache->setValue( WP_Theme_JSON_Resolver::class, static::$property_blocks_cache_orig_value );
+               static::$property_core->setValue( WP_Theme_JSON_Resolver::class, static::$property_core_orig_value );
+               parent::tear_down_after_class();
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public function set_up() {
</span><span class="cx" style="display: block; padding: 0 10px">                parent::set_up();
</span><span class="cx" style="display: block; padding: 0 10px">                $this->theme_root = realpath( DIR_TESTDATA . '/themedir1' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -55,6 +101,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $GLOBALS['wp_theme_directories'] = $this->orig_theme_dir;
</span><span class="cx" style="display: block; padding: 0 10px">                wp_clean_themes_cache();
</span><span class="cx" style="display: block; padding: 0 10px">                unset( $GLOBALS['wp_themes'] );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               // Reset data between tests.
+               WP_Theme_JSON_Resolver::clean_cached_data();
</ins><span class="cx" style="display: block; padding: 0 10px">                 parent::tear_down();
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -190,7 +239,199 @@
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        private function get_registered_block_names( $hard_reset = false ) {
+               static $expected_block_names;
+
+               if ( ! $hard_reset && ! empty( $expected_block_names ) ) {
+                       return $expected_block_names;
+               }
+
+               $expected_block_names = array();
+               $resolver             = WP_Block_Type_Registry::get_instance();
+               $blocks               = $resolver->get_all_registered();
+               foreach ( array_keys( $blocks ) as $block_name ) {
+                       $expected_block_names[ $block_name ] = true;
+               }
+
+               return $expected_block_names;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Tests when WP_Theme_JSON_Resolver::$blocks_cache is empty or does not match
+        * the all registered blocks.
+        *
+        * Though this is a non-public method, it is vital to other functionality.
+        * Therefore, tests are provided to validate it functions as expected.
+        *
+        * @dataProvider data_has_same_registered_blocks_when_all_blocks_not_cached
+        * @ticket 56467
+        *
+        * @param string $origin The origin to test.
+        */
+       public function test_has_same_registered_blocks_when_all_blocks_not_cached( $origin, array $cache = array() ) {
+               $has_same_registered_blocks = new ReflectionMethod( WP_Theme_JSON_Resolver::class, 'has_same_registered_blocks' );
+               $has_same_registered_blocks->setAccessible( true );
+               $expected_cache = $this->get_registered_block_names();
+
+               // Set up the blocks cache for the origin.
+               $blocks_cache            = static::$property_blocks_cache->getValue();
+               $blocks_cache[ $origin ] = $cache;
+               static::$property_blocks_cache->setValue( null, $blocks_cache );
+
+               $this->assertFalse( $has_same_registered_blocks->invoke( null, $origin ), 'WP_Theme_JSON_Resolver::has_same_registered_blocks() should return false when same blocks are not cached' );
+               $blocks_cache = static::$property_blocks_cache->getValue();
+               $this->assertSameSets( $expected_cache, $blocks_cache[ $origin ], 'WP_Theme_JSON_Resolver::$blocks_cache should contain all expected block names for the given origin' );
+       }
+
+       /**
+        * Data provider.
+        *
+        * @return array
+        */
+       public function data_has_same_registered_blocks_when_all_blocks_not_cached() {
+               return array(
+                       'origin: core; cache: empty'       => array(
+                               'origin' => 'core',
+                       ),
+                       'origin: blocks; cache: empty'     => array(
+                               'origin' => 'blocks',
+                       ),
+                       'origin: theme; cache: empty'      => array(
+                               'origin' => 'theme',
+                       ),
+                       'origin: user; cache: empty'       => array(
+                               'origin' => 'user',
+                       ),
+                       'origin: core; cache: not empty'   => array(
+                               'origin' => 'core',
+                               'cache'  => array(
+                                       'core/block' => true,
+                               ),
+                       ),
+                       'origin: blocks; cache: not empty' => array(
+                               'origin' => 'blocks',
+                               'cache'  => array(
+                                       'core/block'    => true,
+                                       'core/comments' => true,
+                               ),
+                       ),
+                       'origin: theme; cache: not empty'  => array(
+                               'origin' => 'theme',
+                               'cache'  => array(
+                                       'core/cover' => true,
+                               ),
+                       ),
+                       'origin: user; cache: not empty'   => array(
+                               'origin' => 'user',
+                               'cache'  => array(
+                                       'core/gallery' => true,
+                               ),
+                       ),
+               );
+       }
+
+       /**
+        * Tests when WP_Theme_JSON_Resolver::$blocks_cache is empty or does not match
+        * the all registered blocks.
+        *
+        * Though this is a non-public method, it is vital to other functionality.
+        * Therefore, tests are provided to validate it functions as expected.
+        *
+        * @dataProvider data_has_same_registered_blocks_when_all_blocks_are_cached
+        * @ticket 56467
+        *
+        * @param string $origin The origin to test.
+        */
+       public function test_has_same_registered_blocks_when_all_blocks_are_cached( $origin ) {
+               $has_same_registered_blocks = new ReflectionMethod( WP_Theme_JSON_Resolver::class, 'has_same_registered_blocks' );
+               $has_same_registered_blocks->setAccessible( true );
+               $expected_cache = $this->get_registered_block_names();
+
+               // Set up the cache with all registered blocks.
+               $blocks_cache            = static::$property_blocks_cache->getValue();
+               $blocks_cache[ $origin ] = $this->get_registered_block_names();
+               static::$property_blocks_cache->setValue( null, $blocks_cache );
+
+               $this->assertTrue( $has_same_registered_blocks->invoke( null, $origin ), 'WP_Theme_JSON_Resolver::has_same_registered_blocks() should return true when using the cache' );
+               $this->assertSameSets( $expected_cache, $blocks_cache[ $origin ], 'WP_Theme_JSON_Resolver::$blocks_cache should contain all expected block names for the given origin' );
+       }
+
+       /**
+        * Data provider.
+        *
+        * @return array
+        */
+       public function data_has_same_registered_blocks_when_all_blocks_are_cached() {
+               return array(
+                       'core'   => array( 'core' ),
+                       'blocks' => array( 'blocks' ),
+                       'theme'  => array( 'theme' ),
+                       'user'   => array( 'user' ),
+               );
+       }
+
+       /**
+        * @dataProvider data_get_core_data
+        * @covers WP_Theme_JSON_Resolver::get_core_data
+        * @ticket 56467
+        */
+       public function test_get_core_data( $should_fire_filter, $core_is_cached, $blocks_are_cached ) {
+               WP_Theme_JSON_Resolver::clean_cached_data();
+
+               // If should cache core, then fire the method to cache it before running the tests.
+               if ( $core_is_cached ) {
+                       WP_Theme_JSON_Resolver::get_core_data();
+               }
+
+               // If should cache registered blocks, then set them up before running the tests.
+               if ( $blocks_are_cached ) {
+                       $blocks_cache         = static::$property_blocks_cache->getValue();
+                       $blocks_cache['core'] = $this->get_registered_block_names();
+                       static::$property_blocks_cache->setValue( null, $blocks_cache );
+               }
+
+               $expected_filter_count = did_filter( 'theme_json_default' );
+               $actual                = WP_Theme_JSON_Resolver::get_core_data();
+               if ( $should_fire_filter ) {
+                       $expected_filter_count++;
+               }
+
+               $this->assertSame( $expected_filter_count, did_filter( 'theme_json_default' ), 'The filter "theme_json_default" should fire the given number of times' );
+               $this->assertInstanceOf( WP_Theme_JSON::class, $actual, 'WP_Theme_JSON_Resolver::get_core_data() should return instance of WP_Theme_JSON' );
+               $this->assertSame( static::$property_core->getValue(), $actual, 'WP_Theme_JSON_Resolver::$core property should be the same object as returned from WP_Theme_JSON_Resolver::get_core_data()' );
+       }
+
+       /**
+        * Data provider.
+        *
+        * @return array
+        */
+       public function data_get_core_data() {
+               return array(
+                       'When both caches are empty'     => array(
+                               'should_fire_filter' => true,
+                               'core_is_cached'     => false,
+                               'blocks_are_cached'  => false,
+                       ),
+                       'When the blocks_cache is not empty and matches' => array(
+                               'should_fire_filter' => true,
+                               'core_is_cached'     => false,
+                               'blocks_are_cached'  => true,
+                       ),
+                       'When blocks_cache is empty but core cache is not' => array(
+                               'should_fire_filter' => true,
+                               'core_is_cached'     => true,
+                               'blocks_are_cached'  => false,
+                       ),
+                       'When both caches are not empty' => array(
+                               'should_fire_filter' => true,
+                               'core_is_cached'     => true,
+                               'blocks_are_cached'  => false,
+                       ),
+               );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @ticket 52991
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_switching_themes_recalculates_data() {
</span></span></pre>
</div>
</div>

</body>
</html>