<!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>[58076] trunk: Build/Test Tools: Overhaul performance tests to improve stability and cover more scenarios.</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/58076">58076</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/58076","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>swissspidy</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2024-05-02 13:57:49 +0000 (Thu, 02 May 2024)</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'>Build/Test Tools: Overhaul performance tests to improve stability and cover more scenarios.
Simplifies the tests setup by leveraging a test matrix, improving maintenance and making it much easier to test more scenarios. With this change, tests are now also run with an external object cache (Memcached). Additional information such as memory usage and the number of database queries is now collected as well.
Improves test setup and cleanup by disabling external HTTP requests and cron for the tests, as well as deleting expired transients and flushing the cache in-between. This should aid the test stability.
When testing the previous commit / target branch, this now leverages the already built artifact from the build process workflow. Raw test results are now also uploaded as artifacts to aid debugging.
Props swissspidy, adamsilverstein, joemcgill, mukesh27, desrosj, youknowriad, flixos90.
Fixes <a href="https://core.trac.wordpress.org/ticket/59900">#59900</a></pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkgithubworkflowsperformanceyml">trunk/.github/workflows/performance.yml</a></li>
<li><a href="#trunktestsperformancecompareresultsjs">trunk/tests/performance/compare-results.js</a></li>
<li><a href="#trunktestsperformanceconfigglobalsetupjs">trunk/tests/performance/config/global-setup.js</a></li>
<li><a href="#trunktestsperformanceconfigperformancereporterjs">trunk/tests/performance/config/performance-reporter.js</a></li>
<li><a href="#trunktestsperformancelogresultsjs">trunk/tests/performance/log-results.js</a></li>
<li><a href="#trunktestsperformanceplaywrightconfigjs">trunk/tests/performance/playwright.config.js</a></li>
<li><a href="#trunktestsperformancespecsadmintestjs">trunk/tests/performance/specs/admin.test.js</a></li>
<li><a href="#trunktestsperformanceutilsjs">trunk/tests/performance/utils.js</a></li>
<li><a href="#trunktestsperformancewpcontentmupluginsservertimingphp">trunk/tests/performance/wp-content/mu-plugins/server-timing.php</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsperformancespecshometestjs">trunk/tests/performance/specs/home.test.js</a></li>
</ul>
<h3>Removed Paths</h3>
<ul>
<li><a href="#trunktestsperformanceresultsjs">trunk/tests/performance/results.js</a></li>
<li><a href="#trunktestsperformancespecsadminl10ntestjs">trunk/tests/performance/specs/admin-l10n.test.js</a></li>
<li><a href="#trunktestsperformancespecshomeblockthemel10ntestjs">trunk/tests/performance/specs/home-block-theme-l10n.test.js</a></li>
<li><a href="#trunktestsperformancespecshomeblockthemetestjs">trunk/tests/performance/specs/home-block-theme.test.js</a></li>
<li><a href="#trunktestsperformancespecshomeclassicthemel10ntestjs">trunk/tests/performance/specs/home-classic-theme-l10n.test.js</a></li>
<li><a href="#trunktestsperformancespecshomeclassicthemetestjs">trunk/tests/performance/specs/home-classic-theme.test.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkgithubworkflowsperformanceyml"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/.github/workflows/performance.yml</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/.github/workflows/performance.yml 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/.github/workflows/performance.yml 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -66,38 +66,45 @@
</span><span class="cx" style="display: block; padding: 0 10px"> # - Install WordPress.
</span><span class="cx" style="display: block; padding: 0 10px"> # - Install WordPress Importer plugin.
</span><span class="cx" style="display: block; padding: 0 10px"> # - Import mock data.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ # - Deactivate WordPress Importer plugin.
</ins><span class="cx" style="display: block; padding: 0 10px"> # - Update permalink structure.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ # - Install additional languages.
+ # - Disable external HTTP requests.
+ # - Disable cron.
+ # - List defined constants.
</ins><span class="cx" style="display: block; padding: 0 10px"> # - Install MU plugin.
</span><span class="cx" style="display: block; padding: 0 10px"> # - Run performance tests (current commit).
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- # - Print performance tests results.
- # - Check out target commit (target branch or previous commit).
- # - Switch Node.js versions if necessary.
- # - Install npm dependencies.
- # - Build WordPress.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ # - Download previous build artifact (target branch or previous commit).
+ # - Download artifact.
+ # - Unzip the build.
</ins><span class="cx" style="display: block; padding: 0 10px"> # - Run any database upgrades.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ # - Flush cache.
+ # - Delete expired transients.
</ins><span class="cx" style="display: block; padding: 0 10px"> # - Run performance tests (previous/target commit).
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- # - Print target performance tests results.
- # - Reset to original commit.
- # - Switch Node.js versions if necessary.
- # - Install npm dependencies.
</del><span class="cx" style="display: block; padding: 0 10px"> # - Set the environment to the baseline version.
</span><span class="cx" style="display: block; padding: 0 10px"> # - Run any database upgrades.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ # - Flush cache.
+ # - Delete expired transients.
</ins><span class="cx" style="display: block; padding: 0 10px"> # - Run baseline performance tests.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- # - Print baseline performance tests results.
- # - Compare results with base.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ # - Archive artifacts.
+ # - Compare results.
</ins><span class="cx" style="display: block; padding: 0 10px"> # - Add workflow summary.
</span><span class="cx" style="display: block; padding: 0 10px"> # - Set the base sha.
</span><span class="cx" style="display: block; padding: 0 10px"> # - Set commit details.
</span><span class="cx" style="display: block; padding: 0 10px"> # - Publish performance results.
</span><span class="cx" style="display: block; padding: 0 10px"> # - Ensure version-controlled files are not modified or deleted.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- # - Dispatch workflow run.
</del><span class="cx" style="display: block; padding: 0 10px"> performance:
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- name: Run performance tests
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ name: Run performance tests ${{ matrix.memcached && '(with memcached)' || '' }}
</ins><span class="cx" style="display: block; padding: 0 10px"> runs-on: ubuntu-latest
</span><span class="cx" style="display: block; padding: 0 10px"> permissions:
</span><span class="cx" style="display: block; padding: 0 10px"> contents: read
</span><span class="cx" style="display: block; padding: 0 10px"> if: ${{ ( github.repository == 'WordPress/wordpress-develop' || github.event_name == 'pull_request' ) && ! contains( github.event.before, '00000000' ) }}
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ strategy:
+ fail-fast: false
+ matrix:
+ memcached: [ true, false ]
+ env:
+ LOCAL_PHP_MEMCACHED: ${{ matrix.memcached }}
</ins><span class="cx" style="display: block; padding: 0 10px"> steps:
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Configure environment variables
</span><span class="cx" style="display: block; padding: 0 10px"> run: |
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -127,15 +134,18 @@
</span><span class="cx" style="display: block; padding: 0 10px"> run: npm ci
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Install Playwright browsers
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- run: npx playwright install --with-deps
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ run: npx playwright install --with-deps chromium
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Build WordPress
</span><span class="cx" style="display: block; padding: 0 10px"> run: npm run build
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Start Docker environment
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- run: |
- npm run env:start
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ run: npm run env:start
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ - name: Install object cache drop-in
+ if: ${{ matrix.memcached }}
+ run: cp src/wp-content/object-cache.php build/wp-content/object-cache.php
+
</ins><span class="cx" style="display: block; padding: 0 10px"> - name: Log running Docker containers
</span><span class="cx" style="display: block; padding: 0 10px"> run: docker ps -a
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -160,9 +170,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> npm run env:cli -- import themeunittestdata.wordpress.xml --authors=create --path=/var/www/${{ env.LOCAL_DIR }}
</span><span class="cx" style="display: block; padding: 0 10px"> rm themeunittestdata.wordpress.xml
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ - name: Deactivate WordPress Importer plugin
+ run: npm run env:cli -- plugin deactivate wordpress-importer --path=/var/www/${{ env.LOCAL_DIR }}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> - name: Update permalink structure
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- run: |
- npm run env:cli -- rewrite structure '/%year%/%monthnum%/%postname%/' --path=/var/www/${{ env.LOCAL_DIR }}
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ run: npm run env:cli -- rewrite structure '/%year%/%monthnum%/%postname%/' --path=/var/www/${{ env.LOCAL_DIR }}
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Install additional languages
</span><span class="cx" style="display: block; padding: 0 10px"> run: |
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -170,6 +182,17 @@
</span><span class="cx" style="display: block; padding: 0 10px"> npm run env:cli -- language plugin install de_DE --all --path=/var/www/${{ env.LOCAL_DIR }}
</span><span class="cx" style="display: block; padding: 0 10px"> npm run env:cli -- language theme install de_DE --all --path=/var/www/${{ env.LOCAL_DIR }}
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ # Prevent background update checks from impacting test stability.
+ - name: Disable external HTTP requests
+ run: npm run env:cli -- config set WP_HTTP_BLOCK_EXTERNAL true --raw --type=constant --path=/var/www/${{ env.LOCAL_DIR }}
+
+ # Prevent background tasks from impacting test stability.
+ - name: Disable cron
+ run: npm run env:cli -- config set DISABLE_WP_CRON true --raw --type=constant --path=/var/www/${{ env.LOCAL_DIR }}
+
+ - name: List defined constants
+ run: npm run env:cli -- config list --path=/var/www/${{ env.LOCAL_DIR }}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> - name: Install MU plugin
</span><span class="cx" style="display: block; padding: 0 10px"> run: |
</span><span class="cx" style="display: block; padding: 0 10px"> mkdir ./${{ env.LOCAL_DIR }}/wp-content/mu-plugins
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -178,74 +201,93 @@
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Run performance tests (current commit)
</span><span class="cx" style="display: block; padding: 0 10px"> run: npm run test:performance
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- - name: Print performance tests results
- run: node ./tests/performance/results.js
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ - name: Download previous build artifact (target branch or previous commit)
+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ id: get-previous-build
+ with:
+ script: |
+ const artifacts = await github.rest.actions.listArtifactsForRepo({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: 'wordpress-build-' + process.env.TARGET_SHA,
+ });
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- - name: Check out target commit (target branch or previous commit)
- run: |
- if [[ -z "$TARGET_REF" ]]; then
- git fetch -n origin $TARGET_SHA
- else
- git fetch -n origin $TARGET_REF
- fi
- git reset --hard $TARGET_SHA
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ const matchArtifact = artifacts.data.artifacts[0];
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- - name: Set up Node.js
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
- with:
- node-version-file: '.nvmrc'
- cache: npm
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( ! matchArtifact ) {
+ core.setFailed( 'No artifact found!' );
+ return false;
+ }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- - name: Install npm dependencies
- run: npm ci
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ const download = await github.rest.actions.downloadArtifact( {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ artifact_id: matchArtifact.id,
+ archive_format: 'zip',
+ } );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- - name: Build WordPress
- run: npm run build
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ const fs = require( 'fs' );
+ fs.writeFileSync( '${{ github.workspace }}/before.zip', Buffer.from( download.data ) )
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return true;
+
+ - name: Unzip the build
+ if: ${{ steps.get-previous-build.outputs.result }}
+ run: |
+ unzip ${{ github.workspace }}/before.zip
+ unzip -o ${{ github.workspace }}/wordpress.zip
+
</ins><span class="cx" style="display: block; padding: 0 10px"> - name: Run any database upgrades
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if: ${{ steps.get-previous-build.outputs.result }}
</ins><span class="cx" style="display: block; padding: 0 10px"> run: npm run env:cli -- core update-db --path=/var/www/${{ env.LOCAL_DIR }}
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- - name: Run target performance tests (base/previous commit)
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ - name: Flush cache
+ if: ${{ steps.get-previous-build.outputs.result }}
+ run: npm run env:cli -- cache flush --path=/var/www/${{ env.LOCAL_DIR }}
+
+ - name: Delete expired transients
+ if: ${{ steps.get-previous-build.outputs.result }}
+ run: npm run env:cli -- transient delete --expired --path=/var/www/${{ env.LOCAL_DIR }}
+
+ - name: Run target performance tests (previous/target commit)
+ if: ${{ steps.get-previous-build.outputs.result }}
</ins><span class="cx" style="display: block; padding: 0 10px"> env:
</span><span class="cx" style="display: block; padding: 0 10px"> TEST_RESULTS_PREFIX: before
</span><span class="cx" style="display: block; padding: 0 10px"> run: npm run test:performance
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- - name: Print target performance tests results
- env:
- TEST_RESULTS_PREFIX: before
- run: node ./tests/performance/results.js
-
- - name: Reset to original commit
- run: git reset --hard $GITHUB_SHA
-
- - name: Set up Node.js
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
- with:
- node-version-file: '.nvmrc'
- cache: npm
-
- - name: Install npm dependencies
- run: npm ci
-
</del><span class="cx" style="display: block; padding: 0 10px"> - name: Set the environment to the baseline version
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' }}
</ins><span class="cx" style="display: block; padding: 0 10px"> run: |
</span><span class="cx" style="display: block; padding: 0 10px"> npm run env:cli -- core update --version=${{ env.BASE_TAG }} --force --path=/var/www/${{ env.LOCAL_DIR }}
</span><span class="cx" style="display: block; padding: 0 10px"> npm run env:cli -- core version --path=/var/www/${{ env.LOCAL_DIR }}
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Run any database upgrades
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' }}
</ins><span class="cx" style="display: block; padding: 0 10px"> run: npm run env:cli -- core update-db --path=/var/www/${{ env.LOCAL_DIR }}
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ - name: Flush cache
+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' }}
+ run: npm run env:cli -- cache flush --path=/var/www/${{ env.LOCAL_DIR }}
+
+ - name: Delete expired transients
+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' }}
+ run: npm run env:cli -- transient delete --expired --path=/var/www/${{ env.LOCAL_DIR }}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> - name: Run baseline performance tests
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' }}
</ins><span class="cx" style="display: block; padding: 0 10px"> env:
</span><span class="cx" style="display: block; padding: 0 10px"> TEST_RESULTS_PREFIX: base
</span><span class="cx" style="display: block; padding: 0 10px"> run: npm run test:performance
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- - name: Print baseline performance tests results
- env:
- TEST_RESULTS_PREFIX: base
- run: node ./tests/performance/results.js
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ - name: Archive artifacts
+ uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
+ if: always()
+ with:
+ name: performance-artifacts${{ matrix.memcached && '-memcached' || '' }}-${{ github.run_id }}
+ path: artifacts
+ if-no-files-found: ignore
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- - name: Compare results with base
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ - name: Compare results
</ins><span class="cx" style="display: block; padding: 0 10px"> run: node ./tests/performance/compare-results.js ${{ runner.temp }}/summary.md
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Add workflow summary
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -253,7 +295,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Set the base sha
</span><span class="cx" style="display: block; padding: 0 10px"> # Only needed when publishing results.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' }}
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' && ! matrix.memcached }}
</ins><span class="cx" style="display: block; padding: 0 10px"> uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
</span><span class="cx" style="display: block; padding: 0 10px"> id: base-sha
</span><span class="cx" style="display: block; padding: 0 10px"> with:
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -264,7 +306,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Set commit details
</span><span class="cx" style="display: block; padding: 0 10px"> # Only needed when publishing results.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' }}
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' && ! matrix.memcached }}
</ins><span class="cx" style="display: block; padding: 0 10px"> uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
</span><span class="cx" style="display: block; padding: 0 10px"> id: commit-timestamp
</span><span class="cx" style="display: block; padding: 0 10px"> with:
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -275,7 +317,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> - name: Publish performance results
</span><span class="cx" style="display: block; padding: 0 10px"> # Only publish results on pushes to trunk.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' }}
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/trunk' && ! matrix.memcached }}
</ins><span class="cx" style="display: block; padding: 0 10px"> env:
</span><span class="cx" style="display: block; padding: 0 10px"> BASE_SHA: ${{ steps.base-sha.outputs.result }}
</span><span class="cx" style="display: block; padding: 0 10px"> COMMITTED_AT: ${{ steps.commit-timestamp.outputs.result }}
</span></span></pre></div>
<a id="trunktestsperformancecompareresultsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/performance/compare-results.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/compare-results.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/compare-results.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3,130 +3,83 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px"> * External dependencies.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-const fs = require( 'node:fs' );
-const path = require( 'node:path' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+const { readFileSync, writeFileSync, existsSync } = require( 'node:fs' );
+const { join } = require( 'node:path' );
</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"> * Internal dependencies
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-const { median } = require( './utils' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+const {
+ median,
+ formatAsMarkdownTable,
+ formatValue,
+ linkToSha,
+ standardDeviation,
+ medianAbsoluteDeviation,
+ accumulateValues,
+} = require( './utils' );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * Parse test files into JSON objects.
- *
- * @param {string} fileName The name of the file.
- * @returns An array of parsed objects from each file.
- */
-const parseFile = ( fileName ) =>
- JSON.parse(
- fs.readFileSync( path.join( __dirname, '/specs/', fileName ), 'utf8' )
- );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+process.env.WP_ARTIFACTS_PATH ??= join( process.cwd(), 'artifacts' );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// The list of test suites to log.
-const testSuites = [
- 'admin',
- 'admin-l10n',
- 'home-block-theme',
- 'home-block-theme-l10n',
- 'home-classic-theme',
- 'home-classic-theme-l10n',
-];
-
-// The current commit's results.
-const testResults = Object.fromEntries(
- testSuites
- .filter( ( key ) => fs.existsSync( path.join( __dirname, '/specs/', `${ key }.test.results.json` ) ) )
- .map( ( key ) => [ key, parseFile( `${ key }.test.results.json` ) ] )
-);
-
-// The previous commit's results.
-const prevResults = Object.fromEntries(
- testSuites
- .filter( ( key ) => fs.existsSync( path.join( __dirname, '/specs/', `before-${ key }.test.results.json` ) ) )
- .map( ( key ) => [ key, parseFile( `before-${ key }.test.results.json` ) ] )
-);
-
</del><span class="cx" style="display: block; padding: 0 10px"> const args = process.argv.slice( 2 );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px"> const summaryFile = args[ 0 ];
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Formats an array of objects as a Markdown table.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Parse test files into JSON objects.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * For example, this array:
- *
- * [
- * {
- * foo: 123,
- * bar: 456,
- * baz: 'Yes',
- * },
- * {
- * foo: 777,
- * bar: 999,
- * baz: 'No',
- * }
- * ]
- *
- * Will result in the following table:
- *
- * | foo | bar | baz |
- * |-----|-----|-----|
- * | 123 | 456 | Yes |
- * | 777 | 999 | No |
- *
- * @param {Array<Object>} rows Table rows.
- * @returns {string} Markdown table content.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param {string} fileName The name of the file.
+ * @return {Array<{file: string, title: string, results: Record<string,number[]>[]}>} Parsed object.
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function formatAsMarkdownTable( rows ) {
- let result = '';
- const headers = Object.keys( rows[ 0 ] );
- for ( const header of headers ) {
- result += `| ${ header } `;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function parseFile( fileName ) {
+ const file = join( process.env.WP_ARTIFACTS_PATH, fileName );
+ if ( ! existsSync( file ) ) {
+ return [];
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- result += '|\n';
- for ( const header of headers ) {
- result += '| ------ ';
- }
- result += '|\n';
</del><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( const row of rows ) {
- for ( const value of Object.values( row ) ) {
- result += `| ${ value } `;
- }
- result += '|\n';
- }
-
- return result;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return JSON.parse( readFileSync( file, 'utf8' ) );
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Returns a Markdown link to a Git commit on the current GitHub repository.
- *
- * For example, turns `a5c3785ed8d6a35868bc169f07e40e889087fd2e`
- * into (https://github.com/wordpress/wordpress-develop/commit/36fe58a8c64dcc83fc21bddd5fcf054aef4efb27)[36fe58a].
- *
- * @param {string} sha Commit SHA.
- * @return string Link
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @type {Array<{file: string, title: string, results: Record<string,number[]>[]}>}
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function linkToSha(sha) {
- const repoName = process.env.GITHUB_REPOSITORY || 'wordpress/wordpress-develop';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+const beforeStats = parseFile( 'before-performance-results.json' );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return `[${sha.slice(0, 7)}](https://github.com/${repoName}/commit/${sha})`;
-}
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * @type {Array<{file: string, title: string, results: Record<string,number[]>[]}>}
+ */
+const afterStats = parseFile( 'performance-results.json' );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-let summaryMarkdown = `# Performance Test Results\n\n`;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+let summaryMarkdown = `## Performance Test Results\n\n`;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-if ( process.env.GITHUB_SHA ) {
- summaryMarkdown += `🛎️ Performance test results for ${ linkToSha( process.env.GITHUB_SHA ) } are in!\n\n`;
-} else {
- summaryMarkdown += `🛎️ Performance test results are in!\n\n`;
-}
-
</del><span class="cx" style="display: block; padding: 0 10px"> if ( process.env.TARGET_SHA ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- summaryMarkdown += `This compares the results from this commit with the ones from ${ linkToSha( process.env.TARGET_SHA ) }.\n\n`;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( beforeStats.length > 0 ) {
+ if (process.env.GITHUB_SHA) {
+ summaryMarkdown += `This compares the results from this commit (${linkToSha(
+ process.env.GITHUB_SHA
+ )}) with the ones from ${linkToSha(process.env.TARGET_SHA)}.\n\n`;
+ } else {
+ summaryMarkdown += `This compares the results from this commit with the ones from ${linkToSha(
+ process.env.TARGET_SHA
+ )}.\n\n`;
+ }
+ } else {
+ summaryMarkdown += `Note: no build was found for the target commit ${linkToSha(process.env.TARGET_SHA)}. No comparison is possible.\n\n`;
+ }
</ins><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">+const numberOfRepetitions = afterStats[ 0 ].results.length;
+const numberOfIterations = Object.values( afterStats[ 0 ].results[ 0 ] )[ 0 ]
+ .length;
+
+const repetitions = `${ numberOfRepetitions } ${
+ numberOfRepetitions === 1 ? 'repetition' : 'repetitions'
+}`;
+const iterations = `${ numberOfIterations } ${
+ numberOfIterations === 1 ? 'iteration' : 'iterations'
+}`;
+
+summaryMarkdown += `All numbers are median values over ${ repetitions } with ${ iterations } each.\n\n`;
+
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( process.env.GITHUB_SHA ) {
</span><span class="cx" style="display: block; padding: 0 10px"> summaryMarkdown += `**Note:** Due to the nature of how GitHub Actions work, some variance in the results is expected.\n\n`;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -133,63 +86,72 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> console.log( 'Performance Test Results\n' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-console.log( 'Note: Due to the nature of how GitHub Actions work, some variance in the results is expected.\n' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+console.log(
+ `All numbers are median values over ${ repetitions } with ${ iterations } each.\n`
+);
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * Nicely formats a given value.
- *
- * @param {string} metric Metric.
- * @param {number} value
- */
-function formatValue( metric, value) {
- if ( null === value ) {
- return 'N/A';
- }
- if ( 'wpMemoryUsage' === metric ) {
- return `${ ( value / Math.pow( 10, 6 ) ).toFixed( 2 ) } MB`;
- }
-
- return `${ value.toFixed( 2 ) } ms`;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+if ( process.env.GITHUB_SHA ) {
+ console.log(
+ 'Note: Due to the nature of how GitHub Actions work, some variance in the results is expected.\n'
+ );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-for ( const key of testSuites ) {
- const current = testResults[ key ] || {};
- const prev = prevResults[ key ] || {};
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+for ( const { title, results } of afterStats ) {
+ const prevStat = beforeStats.find( ( s ) => s.title === title );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- const title = ( key.charAt( 0 ).toUpperCase() + key.slice( 1 ) ).replace(
- /-+/g,
- ' '
- );
-
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+ * @type {Array<Record<string, string>>}
+ */
</ins><span class="cx" style="display: block; padding: 0 10px"> const rows = [];
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( const [ metric, values ] of Object.entries( current ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ const newResults = accumulateValues( results );
+ // Only do comparison if the number of results is the same.
+ const prevResults =
+ prevStat && prevStat.results.length === results.length
+ ? accumulateValues( prevStat.results )
+ : {};
+
+ for ( const [ metric, values ] of Object.entries( newResults ) ) {
+ const prevValues = prevResults[ metric ] ? prevResults[ metric ] : null;
+
</ins><span class="cx" style="display: block; padding: 0 10px"> const value = median( values );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- const prevValue = prev[ metric ] ? median( prev[ metric ] ) : null;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ const prevValue = prevValues ? median( prevValues ) : 0;
+ const delta = value - prevValue;
+ const percentage = ( delta / value ) * 100;
+ const showDiff =
+ metric !== 'wpExtObjCache' && ! Number.isNaN( percentage );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- const delta = null !== prevValue ? value - prevValue : 0
- const percentage = ( delta / value ) * 100;
</del><span class="cx" style="display: block; padding: 0 10px"> rows.push( {
</span><span class="cx" style="display: block; padding: 0 10px"> Metric: metric,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- Before: formatValue( metric, prevValue ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ Before: prevValues ? formatValue( metric, prevValue ) : 'N/A',
</ins><span class="cx" style="display: block; padding: 0 10px"> After: formatValue( metric, value ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 'Diff abs.': formatValue( metric, delta ),
- 'Diff %': `${ percentage.toFixed( 2 ) } %`,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'Diff abs.': showDiff ? formatValue( metric, delta ) : '',
+ 'Diff %': showDiff ? `${ percentage.toFixed( 2 ) } %` : '',
+ STD: showDiff
+ ? formatValue( metric, standardDeviation( values ) )
+ : '',
+ MAD: showDiff
+ ? formatValue( metric, medianAbsoluteDeviation( values ) )
+ : '',
</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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ console.log( title );
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( rows.length > 0 ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- summaryMarkdown += `## ${ title }\n\n`;
- summaryMarkdown += `${ formatAsMarkdownTable( rows ) }\n`;
-
- console.log( title );
</del><span class="cx" style="display: block; padding: 0 10px"> console.table( rows );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } else {
+ console.log( '(no results)' );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ summaryMarkdown += `**${ title }**\n\n`;
+ summaryMarkdown += `${ formatAsMarkdownTable( rows ) }\n`;
</ins><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">+writeFileSync(
+ join( process.env.WP_ARTIFACTS_PATH, '/performance-results.md' ),
+ summaryMarkdown
+);
+
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( summaryFile ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- fs.writeFileSync(
- summaryFile,
- summaryMarkdown
- );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ writeFileSync( summaryFile, summaryMarkdown );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunktestsperformanceconfigglobalsetupjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/performance/config/global-setup.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/config/global-setup.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/config/global-setup.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -30,9 +30,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> await requestUtils.setupRest();
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Reset the test environment before running the tests.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- await Promise.all( [
- requestUtils.activateTheme( 'twentytwentyone' ),
- ] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ await Promise.all( [ requestUtils.activateTheme( 'twentytwentyone' ) ] );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> await requestContext.dispose();
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunktestsperformanceconfigperformancereporterjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/performance/config/performance-reporter.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/config/performance-reporter.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/config/performance-reporter.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,20 +1,24 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px"> * External dependencies
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-import { join, dirname, basename } from 'node:path';
-import { writeFileSync } from 'node:fs';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+import { join } from 'node:path';
+import { writeFileSync, existsSync, mkdirSync } from 'node:fs';
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Internal dependencies
- */
-import { getResultsFilename } from '../utils';
-
-/**
</del><span class="cx" style="display: block; padding: 0 10px"> * @implements {import('@playwright/test/reporter').Reporter}
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> class PerformanceReporter {
</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">+ * @type {Record<string,{title: string; results: Record< string, number[] >[];}>}
+ */
+ allResults = {};
+
+ /**
+ * Called after a test has been finished in the worker process.
+ *
+ * Used to add test results to the final summary of all tests.
+ *
</ins><span class="cx" style="display: block; padding: 0 10px"> * @param {import('@playwright/test/reporter').TestCase} test
</span><span class="cx" style="display: block; padding: 0 10px"> * @param {import('@playwright/test/reporter').TestResult} result
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -24,15 +28,59 @@
</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"> if ( performanceResults?.body ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- writeFileSync(
- join(
- dirname( test.location.file ),
- getResultsFilename( basename( test.location.file, '.js' ) )
- ),
- performanceResults.body.toString( 'utf-8' )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // 0 = empty, 1 = browser, 2 = file name, 3 = test suite name, 4 = test name.
+ const titlePath = test.titlePath();
+ const title = `${ titlePath[ 3 ] } › ${ titlePath[ 4 ] }`;
+
+ // results is an array in case repeatEach is > 1.
+
+ this.allResults[ title ] ??= {
+ file: test.location.file, // Unused, but useful for debugging.
+ results: [],
+ };
+
+ this.allResults[ title ].results.push(
+ JSON.parse( performanceResults.body.toString( 'utf-8' ) )
</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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ /**
+ * Called after all tests have been run, or testing has been interrupted.
+ *
+ * Writes all raw numbers to a file for further processing,
+ * for example to compare with a previous run.
+ *
+ * @param {import('@playwright/test/reporter').FullResult} result
+ */
+ onEnd( result ) {
+ const summary = [];
+
+ for ( const [ title, { file, results } ] of Object.entries(
+ this.allResults
+ ) ) {
+ summary.push( {
+ file,
+ title,
+ results,
+ } );
+ }
+
+ if ( ! existsSync( process.env.WP_ARTIFACTS_PATH ) ) {
+ mkdirSync( process.env.WP_ARTIFACTS_PATH );
+ }
+
+ const prefix = process.env.TEST_RESULTS_PREFIX;
+ const fileNamePrefix = prefix ? `${ prefix }-` : '';
+
+ writeFileSync(
+ join(
+ process.env.WP_ARTIFACTS_PATH,
+ `${ fileNamePrefix }performance-results.json`
+ ),
+ JSON.stringify( summary, null, 2 )
+ );
+ }
</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"> export default PerformanceReporter;
</span></span></pre></div>
<a id="trunktestsperformancelogresultsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/performance/log-results.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/log-results.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/log-results.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,71 +1,92 @@
</span><span class="cx" style="display: block; padding: 0 10px"> #!/usr/bin/env node
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*
+ * Get the test results and format them in the way required by the API.
+ *
+ * Contains some backward compatibility logic for the original test suite format,
+ * see #59900 for details.
+ */
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px"> * External dependencies.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-const fs = require( 'fs' );
-const path = require( 'path' );
</del><span class="cx" style="display: block; padding: 0 10px"> const https = require( 'https' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-const [ token, branch, hash, baseHash, timestamp, host ] = process.argv.slice( 2 );
-const { median } = require( './utils' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+const [ token, branch, hash, baseHash, timestamp, host ] =
+ process.argv.slice( 2 );
+const { median, parseFile, accumulateValues } = require( './utils' );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// The list of test suites to log.
-const testSuites = [
- 'admin',
- 'admin-l10n',
- 'home-block-theme',
- 'home-block-theme-l10n',
- 'home-classic-theme',
- 'home-classic-theme-l10n',
-];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+const testSuiteMap = {
+ 'Admin › Locale: en_US': 'admin',
+ 'Admin › Locale: de_DE': 'admin-l10n',
+ 'Front End › Theme: twentytwentyone, Locale: en_US': 'home-classic-theme',
+ 'Front End › Theme: twentytwentyone, Locale: de_DE':
+ 'home-classic-theme-l10n',
+ 'Front End › Theme: twentytwentythree, Locale: en_US': 'home-block-theme',
+ 'Front End › Theme: twentytwentythree, Locale: de_DE':
+ 'home-block-theme-l10n',
+};
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// A list of results to parse based on test suites.
-const testResults = testSuites.map(( key ) => ({
- key,
- file: `${ key }.test.results.json`,
-}));
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * @type {Array<{file: string, title: string, results: Record<string,number[]>[]}>}
+ */
+const afterStats = parseFile( 'performance-results.json' );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// A list of base results to parse based on test suites.
-const baseResults = testSuites.map(( key ) => ({
- key,
- file: `base-${ key }.test.results.json`,
-}));
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * @type {Array<{file: string, title: string, results: Record<string,number[]>[]}>}
+ */
+const baseStats = parseFile( 'base-performance-results.json' );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Parse test files into JSON objects.
- *
- * @param {string} fileName The name of the file.
- * @returns An array of parsed objects from each file.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @type {Record<string, number>}
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-const parseFile = ( fileName ) => (
- JSON.parse(
- fs.readFileSync( path.join( __dirname, '/specs/', fileName ), 'utf8' )
- )
-);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+const metrics = {};
+/**
+ * @type {Record<string, number>}
+ */
+const baseMetrics = {};
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+for ( const { title, results } of afterStats ) {
+ const testSuiteName = testSuiteMap[ title ];
+ if ( ! testSuiteName ) {
+ continue;
+ }
+
+ const baseStat = baseStats.find( ( s ) => s.title === title );
+
+ const currResults = accumulateValues( results );
+ const baseResults = accumulateValues( baseStat.results );
+
+ for ( const [ metric, values ] of Object.entries( currResults ) ) {
+ metrics[ `${ testSuiteName }-${ metric }` ] = median( values );
+ }
+
+ for ( const [ metric, values ] of Object.entries( baseResults ) ) {
+ baseMetrics[ `${ testSuiteName }-${ metric }` ] = median( values );
+ }
+}
+
+process.exit( 0 );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px"> * Gets the array of metrics from a list of results.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @param {Object[]} results A list of results to format.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @return {Object[]} Metrics.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @return {Object} Metrics.
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> const formatResults = ( results ) => {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return results.reduce(
- ( result, { key, file } ) => {
- return {
- ...result,
- ...Object.fromEntries(
- Object.entries(
- parseFile( file ) ?? {}
- ).map( ( [ metric, value ] ) => [
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return results.reduce( ( result, { key, file } ) => {
+ return {
+ ...result,
+ ...Object.fromEntries(
+ Object.entries( parseFile( file ) ?? {} ).map(
+ ( [ metric, value ] ) => [
</ins><span class="cx" style="display: block; padding: 0 10px"> key + '-' + metric,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- median ( value ),
- ] )
- ),
- };
- },
- {}
- );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ median( value ),
+ ]
+ )
+ ),
+ };
+ }, {} );
</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"> const data = new TextEncoder().encode(
</span></span></pre></div>
<a id="trunktestsperformanceplaywrightconfigjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/performance/playwright.config.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/playwright.config.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/playwright.config.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -23,9 +23,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> forbidOnly: !! process.env.CI,
</span><span class="cx" style="display: block; padding: 0 10px"> workers: 1,
</span><span class="cx" style="display: block; padding: 0 10px"> retries: 0,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ repeatEach: 2,
</ins><span class="cx" style="display: block; padding: 0 10px"> timeout: parseInt( process.env.TIMEOUT || '', 10 ) || 600_000, // Defaults to 10 minutes.
</span><span class="cx" style="display: block; padding: 0 10px"> // Don't report slow test "files", as we will be running our tests in serial.
</span><span class="cx" style="display: block; padding: 0 10px"> reportSlowTests: null,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ preserveOutput: 'never',
</ins><span class="cx" style="display: block; padding: 0 10px"> webServer: {
</span><span class="cx" style="display: block; padding: 0 10px"> ...baseConfig.webServer,
</span><span class="cx" style="display: block; padding: 0 10px"> command: 'npm run env:start',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -37,4 +39,3 @@
</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"> export default config;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del></span></pre></div>
<a id="trunktestsperformanceresultsjs"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/performance/results.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/results.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/results.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,42 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#!/usr/bin/env node
-
-/**
- * External dependencies.
- */
-const fs = require( 'node:fs' );
-const { join } = require( 'node:path' );
-const { median, getResultsFilename } = require( './utils' );
-
-const testSuites = [
- 'admin',
- 'admin-l10n',
- 'home-classic-theme',
- 'home-classic-theme-l10n',
- 'home-block-theme',
- 'home-block-theme-l10n',
-];
-
-console.log( '\n>> 🎉 Results 🎉 \n' );
-
-for ( const testSuite of testSuites ) {
- const resultsFileName = getResultsFilename( testSuite + '.test' );
- const resultsPath = join( __dirname, '/specs/', resultsFileName );
- fs.readFile( resultsPath, "utf8", ( err, data ) => {
- if ( err ) {
- console.log( "File read failed:", err );
- return;
- }
- const convertString = testSuite.charAt( 0 ).toUpperCase() + testSuite.slice( 1 );
- console.log( convertString.replace( /[-]+/g, " " ) + ':' );
-
- tableData = JSON.parse( data );
- const rawResults = [];
-
- for ( var key in tableData ) {
- if ( tableData.hasOwnProperty( key ) ) {
- rawResults[ key ] = median( tableData[ key ] );
- }
- }
- console.table( rawResults );
- });
-}
</del></span></pre></div>
<a id="trunktestsperformancespecsadminl10ntestjs"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/performance/specs/admin-l10n.test.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/specs/admin-l10n.test.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/specs/admin-l10n.test.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,52 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * WordPress dependencies
- */
-import { test } from '@wordpress/e2e-test-utils-playwright';
-
-/**
- * Internal dependencies
- */
-import { camelCaseDashes } from '../utils';
-
-const results = {
- timeToFirstByte: [],
-};
-
-test.describe( 'Admin (L10N)', () => {
- test.beforeAll( async ( { requestUtils } ) => {
- await requestUtils.activateTheme( 'twentytwentyone' );
- await requestUtils.updateSiteSettings( {
- language: 'de_DE',
- } );
- } );
-
- test.afterAll( async ( { requestUtils }, testInfo ) => {
- await testInfo.attach( 'results', {
- body: JSON.stringify( results, null, 2 ),
- contentType: 'application/json',
- } );
- await requestUtils.updateSiteSettings( {
- language: '',
- } );
- } );
-
- const iterations = Number( process.env.TEST_RUNS );
- for ( let i = 1; i <= iterations; i++ ) {
- test( `Measure load time metrics (${ i } of ${ iterations })`, async ( {
- admin,
- metrics,
- } ) => {
- await admin.visitAdminPage( '/' );
-
- const serverTiming = await metrics.getServerTiming();
-
- for ( const [ key, value ] of Object.entries( serverTiming ) ) {
- results[ camelCaseDashes( key ) ] ??= [];
- results[ camelCaseDashes( key ) ].push( value );
- }
-
- const ttfb = await metrics.getTimeToFirstByte();
- results.timeToFirstByte.push( ttfb );
- } );
- }
-} );
</del></span></pre></div>
<a id="trunktestsperformancespecsadmintestjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/performance/specs/admin.test.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/specs/admin.test.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/specs/admin.test.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -12,35 +12,59 @@
</span><span class="cx" style="display: block; padding: 0 10px"> timeToFirstByte: [],
</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">+const locales = [ 'en_US', 'de_DE' ];
+
</ins><span class="cx" style="display: block; padding: 0 10px"> test.describe( 'Admin', () => {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- test.beforeAll( async ( { requestUtils } ) => {
- await requestUtils.activateTheme( 'twentytwentyone' );
- } );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ for ( const locale of locales ) {
+ test.describe( `Locale: ${ locale }`, () => {
+ test.beforeAll( async ( { requestUtils } ) => {
+ await requestUtils.activateTheme( 'twentytwentyone' );
+ await requestUtils.updateSiteSettings( {
+ language: 'en_US' === locale ? '' : locale,
+ } );
+ } );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- test.afterAll( async ( {}, testInfo ) => {
- await testInfo.attach( 'results', {
- body: JSON.stringify( results, null, 2 ),
- contentType: 'application/json',
- } );
- } );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ test.afterAll( async ( { requestUtils }, testInfo ) => {
+ await testInfo.attach( 'results', {
+ body: JSON.stringify( results, null, 2 ),
+ contentType: 'application/json',
+ } );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- const iterations = Number( process.env.TEST_RUNS );
- for ( let i = 1; i <= iterations; i++ ) {
- test( `Measure load time metrics (${ i } of ${ iterations })`, async ( {
- admin,
- metrics,
- } ) => {
- await admin.visitAdminPage( '/' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ await requestUtils.updateSiteSettings( {
+ language: '',
+ } );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- const serverTiming = await metrics.getServerTiming();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ results.timeToFirstByte = [];
+ } );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( const [ key, value ] of Object.entries( serverTiming ) ) {
- results[ camelCaseDashes( key ) ] ??= [];
- results[ camelCaseDashes( key ) ].push( value );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ test.afterAll( async ( {}, testInfo ) => {
+ await testInfo.attach( 'results', {
+ body: JSON.stringify( results, null, 2 ),
+ contentType: 'application/json',
+ } );
+ } );
+
+ const iterations = Number( process.env.TEST_RUNS );
+ for ( let i = 1; i <= iterations; i++ ) {
+ test( `Measure load time metrics (${ i } of ${ iterations })`, async ( {
+ admin,
+ metrics,
+ } ) => {
+ await admin.visitAdminPage( '/' );
+
+ const serverTiming = await metrics.getServerTiming();
+
+ for ( const [ key, value ] of Object.entries(
+ serverTiming
+ ) ) {
+ results[ camelCaseDashes( key ) ] ??= [];
+ results[ camelCaseDashes( key ) ].push( value );
+ }
+
+ const ttfb = await metrics.getTimeToFirstByte();
+ results.timeToFirstByte.push( ttfb );
+ } );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
- const ttfb = await metrics.getTimeToFirstByte();
- results.timeToFirstByte.push( ttfb );
</del><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></pre></div>
<a id="trunktestsperformancespecshomeblockthemel10ntestjs"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/performance/specs/home-block-theme-l10n.test.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/specs/home-block-theme-l10n.test.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/specs/home-block-theme-l10n.test.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,63 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * WordPress dependencies
- */
-import { test } from '@wordpress/e2e-test-utils-playwright';
-
-/**
- * Internal dependencies
- */
-import { camelCaseDashes } from '../utils';
-
-const results = {
- timeToFirstByte: [],
- largestContentfulPaint: [],
- lcpMinusTtfb: [],
-};
-
-test.describe( 'Front End - Twenty Twenty Three (L10N)', () => {
- test.use( {
- storageState: {}, // User will be logged out.
- } );
-
- test.beforeAll( async ( { requestUtils } ) => {
- await requestUtils.activateTheme( 'twentytwentythree' );
- await requestUtils.updateSiteSettings( {
- language: 'de_DE',
- } );
- } );
-
- test.afterAll( async ( { requestUtils }, testInfo ) => {
- await testInfo.attach( 'results', {
- body: JSON.stringify( results, null, 2 ),
- contentType: 'application/json',
- } );
- await requestUtils.activateTheme( 'twentytwentyone' );
- await requestUtils.updateSiteSettings( {
- language: '',
- } );
- } );
-
- const iterations = Number( process.env.TEST_RUNS );
- for ( let i = 1; i <= iterations; i++ ) {
- test( `Measure load time metrics (${ i } of ${ iterations })`, async ( {
- page,
- metrics,
- } ) => {
- await page.goto( '/' );
-
- const serverTiming = await metrics.getServerTiming();
-
- for ( const [ key, value ] of Object.entries( serverTiming ) ) {
- results[ camelCaseDashes( key ) ] ??= [];
- results[ camelCaseDashes( key ) ].push( value );
- }
-
- const ttfb = await metrics.getTimeToFirstByte();
- const lcp = await metrics.getLargestContentfulPaint();
-
- results.largestContentfulPaint.push( lcp );
- results.timeToFirstByte.push( ttfb );
- results.lcpMinusTtfb.push( lcp - ttfb );
- } );
- }
-} );
</del></span></pre></div>
<a id="trunktestsperformancespecshomeblockthemetestjs"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/performance/specs/home-block-theme.test.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/specs/home-block-theme.test.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/specs/home-block-theme.test.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,57 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * WordPress dependencies
- */
-import { test } from '@wordpress/e2e-test-utils-playwright';
-
-/**
- * Internal dependencies
- */
-import { camelCaseDashes } from '../utils';
-
-const results = {
- timeToFirstByte: [],
- largestContentfulPaint: [],
- lcpMinusTtfb: [],
-};
-
-test.describe( 'Front End - Twenty Twenty Three', () => {
- test.use( {
- storageState: {}, // User will be logged out.
- } );
-
- test.beforeAll( async ( { requestUtils } ) => {
- await requestUtils.activateTheme( 'twentytwentythree' );
- } );
-
- test.afterAll( async ( { requestUtils }, testInfo ) => {
- await testInfo.attach( 'results', {
- body: JSON.stringify( results, null, 2 ),
- contentType: 'application/json',
- } );
- await requestUtils.activateTheme( 'twentytwentyone' );
- } );
-
- const iterations = Number( process.env.TEST_RUNS );
- for ( let i = 1; i <= iterations; i++ ) {
- test( `Measure load time metrics (${ i } of ${ iterations })`, async ( {
- page,
- metrics,
- } ) => {
- await page.goto( '/' );
-
- const serverTiming = await metrics.getServerTiming();
-
- for ( const [ key, value ] of Object.entries( serverTiming ) ) {
- results[ camelCaseDashes( key ) ] ??= [];
- results[ camelCaseDashes( key ) ].push( value );
- }
-
- const ttfb = await metrics.getTimeToFirstByte();
- const lcp = await metrics.getLargestContentfulPaint();
-
- results.largestContentfulPaint.push( lcp );
- results.timeToFirstByte.push( ttfb );
- results.lcpMinusTtfb.push( lcp - ttfb );
- } );
- }
-} );
</del></span></pre></div>
<a id="trunktestsperformancespecshomeclassicthemel10ntestjs"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/performance/specs/home-classic-theme-l10n.test.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/specs/home-classic-theme-l10n.test.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/specs/home-classic-theme-l10n.test.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,62 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * WordPress dependencies
- */
-import { test } from '@wordpress/e2e-test-utils-playwright';
-
-/**
- * Internal dependencies
- */
-import { camelCaseDashes } from '../utils';
-
-const results = {
- timeToFirstByte: [],
- largestContentfulPaint: [],
- lcpMinusTtfb: [],
-};
-
-test.describe( 'Front End - Twenty Twenty One (L10N)', () => {
- test.use( {
- storageState: {}, // User will be logged out.
- } );
-
- test.beforeAll( async ( { requestUtils } ) => {
- await requestUtils.activateTheme( 'twentytwentyone' );
- await requestUtils.updateSiteSettings( {
- language: 'de_DE',
- } );
- } );
-
- test.afterAll( async ( { requestUtils }, testInfo ) => {
- await testInfo.attach( 'results', {
- body: JSON.stringify( results, null, 2 ),
- contentType: 'application/json',
- } );
- await requestUtils.updateSiteSettings( {
- language: '',
- } );
- } );
-
- const iterations = Number( process.env.TEST_RUNS );
- for ( let i = 1; i <= iterations; i++ ) {
- test( `Measure load time metrics (${ i } of ${ iterations })`, async ( {
- page,
- metrics,
- } ) => {
- await page.goto( '/' );
-
- const serverTiming = await metrics.getServerTiming();
-
- for ( const [ key, value ] of Object.entries( serverTiming ) ) {
- results[ camelCaseDashes( key ) ] ??= [];
- results[ camelCaseDashes( key ) ].push( value );
- }
-
- const ttfb = await metrics.getTimeToFirstByte();
- const lcp = await metrics.getLargestContentfulPaint();
-
- results.largestContentfulPaint.push( lcp );
- results.timeToFirstByte.push( ttfb );
- results.lcpMinusTtfb.push( lcp - ttfb );
- } );
- }
-} );
</del></span></pre></div>
<a id="trunktestsperformancespecshomeclassicthemetestjs"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/tests/performance/specs/home-classic-theme.test.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/specs/home-classic-theme.test.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/specs/home-classic-theme.test.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,56 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * WordPress dependencies
- */
-import { test } from '@wordpress/e2e-test-utils-playwright';
-
-/**
- * Internal dependencies
- */
-import { camelCaseDashes } from '../utils';
-
-const results = {
- timeToFirstByte: [],
- largestContentfulPaint: [],
- lcpMinusTtfb: [],
-};
-
-test.describe( 'Front End - Twenty Twenty One', () => {
- test.use( {
- storageState: {}, // User will be logged out.
- } );
-
- test.beforeAll( async ( { requestUtils } ) => {
- await requestUtils.activateTheme( 'twentytwentyone' );
- } );
-
- test.afterAll( async ( {}, testInfo ) => {
- await testInfo.attach( 'results', {
- body: JSON.stringify( results, null, 2 ),
- contentType: 'application/json',
- } );
- } );
-
- const iterations = Number( process.env.TEST_RUNS );
- for ( let i = 1; i <= iterations; i++ ) {
- test( `Measure load time metrics (${ i } of ${ iterations })`, async ( {
- page,
- metrics,
- } ) => {
- await page.goto( '/' );
-
- const serverTiming = await metrics.getServerTiming();
-
- for ( const [ key, value ] of Object.entries( serverTiming ) ) {
- results[ camelCaseDashes( key ) ] ??= [];
- results[ camelCaseDashes( key ) ].push( value );
- }
-
- const ttfb = await metrics.getTimeToFirstByte();
- const lcp = await metrics.getLargestContentfulPaint();
-
- results.largestContentfulPaint.push( lcp );
- results.timeToFirstByte.push( ttfb );
- results.lcpMinusTtfb.push( lcp - ttfb );
- } );
- }
-} );
</del></span></pre></div>
<a id="trunktestsperformancespecshometestjs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/performance/specs/home.test.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/specs/home.test.js (rev 0)
+++ trunk/tests/performance/specs/home.test.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,79 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * WordPress dependencies
+ */
+import { test } from '@wordpress/e2e-test-utils-playwright';
+
+/**
+ * Internal dependencies
+ */
+import { camelCaseDashes } from '../utils';
+
+const results = {
+ timeToFirstByte: [],
+ largestContentfulPaint: [],
+ lcpMinusTtfb: [],
+};
+
+const themes = [ 'twentytwentyone', 'twentytwentythree', 'twentytwentyfour' ];
+
+const locales = [ 'en_US', 'de_DE' ];
+
+test.describe( 'Front End', () => {
+ test.use( {
+ storageState: {}, // User will be logged out.
+ } );
+
+ for ( const theme of themes ) {
+ for ( const locale of locales ) {
+ test.describe( `Theme: ${ theme }, Locale: ${ locale }`, () => {
+ test.beforeAll( async ( { requestUtils } ) => {
+ await requestUtils.activateTheme( theme );
+ await requestUtils.updateSiteSettings( {
+ language: 'en_US' === locale ? '' : locale,
+ } );
+ } );
+
+ test.afterAll( async ( { requestUtils }, testInfo ) => {
+ await testInfo.attach( 'results', {
+ body: JSON.stringify( results, null, 2 ),
+ contentType: 'application/json',
+ } );
+
+ await requestUtils.updateSiteSettings( {
+ language: '',
+ } );
+
+ results.largestContentfulPaint = [];
+ results.timeToFirstByte = [];
+ results.lcpMinusTtfb = [];
+ } );
+
+ const iterations = Number( process.env.TEST_RUNS );
+ for ( let i = 1; i <= iterations; i++ ) {
+ test( `Measure load time metrics (${ i } of ${ iterations })`, async ( {
+ page,
+ metrics,
+ } ) => {
+ await page.goto( '/' );
+
+ const serverTiming = await metrics.getServerTiming();
+
+ for ( const [ key, value ] of Object.entries(
+ serverTiming
+ ) ) {
+ results[ camelCaseDashes( key ) ] ??= [];
+ results[ camelCaseDashes( key ) ].push( value );
+ }
+
+ const ttfb = await metrics.getTimeToFirstByte();
+ const lcp = await metrics.getLargestContentfulPaint();
+
+ results.largestContentfulPaint.push( lcp );
+ results.timeToFirstByte.push( ttfb );
+ results.lcpMinusTtfb.push( lcp - ttfb );
+ } );
+ }
+ } );
+ }
+ }
+} );
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/performance/specs/home.test.js
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsperformanceutilsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/performance/utils.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/utils.js 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/utils.js 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,4 +1,27 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * External dependencies.
+ */
+const { readFileSync, existsSync } = require( 'node:fs' );
+const { join } = require( 'node:path' );
+
+process.env.WP_ARTIFACTS_PATH ??= join( process.cwd(), 'artifacts' );
+
+/**
+ * Parse test files into JSON objects.
+ *
+ * @param {string} fileName The name of the file.
+ * @return {Array<{file: string, title: string, results: Record<string,number[]>[]}>} Parsed object.
+ */
+function parseFile( fileName ) {
+ const file = join( process.env.WP_ARTIFACTS_PATH, fileName );
+ if ( ! existsSync( file ) ) {
+ return [];
+ }
+
+ return JSON.parse( readFileSync( file, 'utf8' ) );
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Computes the median number from an array numbers.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @param {number[]} array
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -13,27 +36,157 @@
</span><span class="cx" style="display: block; padding: 0 10px"> : ( numbers[ mid - 1 ] + numbers[ mid ] ) / 2;
</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">+function camelCaseDashes( str ) {
+ return str.replace( /-([a-z])/g, function ( g ) {
+ return g[ 1 ].toUpperCase();
+ } );
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Gets the result file name.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Formats an array of objects as a Markdown table.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param {string} fileName File name.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * For example, this array:
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @return {string} Result file name.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * [
+ * {
+ * foo: 123,
+ * bar: 456,
+ * baz: 'Yes',
+ * },
+ * {
+ * foo: 777,
+ * bar: 999,
+ * baz: 'No',
+ * }
+ * ]
+ *
+ * Will result in the following table:
+ *
+ * | foo | bar | baz |
+ * |-----|-----|-----|
+ * | 123 | 456 | Yes |
+ * | 777 | 999 | No |
+ *
+ * @param {Array<Object>} rows Table rows.
+ * @returns {string} Markdown table content.
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function getResultsFilename( fileName ) {
- const prefix = process.env.TEST_RESULTS_PREFIX;
- const fileNamePrefix = prefix ? `${ prefix }-` : '';
- return `${fileNamePrefix + fileName}.results.json`;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function formatAsMarkdownTable( rows ) {
+ let result = '';
+
+ if ( ! rows.length ) {
+ return result;
+ }
+
+ const headers = Object.keys( rows[ 0 ] );
+ for ( const header of headers ) {
+ result += `| ${ header } `;
+ }
+ result += '|\n';
+ for ( const header of headers ) {
+ result += '| ------ ';
+ }
+ result += '|\n';
+
+ for ( const row of rows ) {
+ for ( const value of Object.values( row ) ) {
+ result += `| ${ value } `;
+ }
+ result += '|\n';
+ }
+
+ return result;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function camelCaseDashes( str ) {
- return str.replace( /-([a-z])/g, function( g ) {
- return g[ 1 ].toUpperCase();
- } );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * Nicely formats a given value.
+ *
+ * @param {string} metric Metric.
+ * @param {number} value
+ */
+function formatValue( metric, value ) {
+ if ( null === value ) {
+ return 'N/A';
+ }
+
+ if ( 'wpMemoryUsage' === metric ) {
+ return `${ ( value / Math.pow( 10, 6 ) ).toFixed( 2 ) } MB`;
+ }
+
+ if ( 'wpExtObjCache' === metric ) {
+ return 1 === value ? 'yes' : 'no';
+ }
+
+ if ( 'wpDbQueries' === metric ) {
+ return value;
+ }
+
+ return `${ value.toFixed( 2 ) } ms`;
</ins><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">+/**
+ * Returns a Markdown link to a Git commit on the current GitHub repository.
+ *
+ * For example, turns `a5c3785ed8d6a35868bc169f07e40e889087fd2e`
+ * into (https://github.com/wordpress/wordpress-develop/commit/36fe58a8c64dcc83fc21bddd5fcf054aef4efb27)[36fe58a].
+ *
+ * @param {string} sha Commit SHA.
+ * @return string Link
+ */
+function linkToSha( sha ) {
+ const repoName =
+ process.env.GITHUB_REPOSITORY || 'wordpress/wordpress-develop';
+
+ return `[${ sha.slice(
+ 0,
+ 7
+ ) }](https://github.com/${ repoName }/commit/${ sha })`;
+}
+
+function standardDeviation( array = [] ) {
+ if ( ! array.length ) {
+ return 0;
+ }
+
+ const mean = array.reduce( ( a, b ) => a + b ) / array.length;
+ return Math.sqrt(
+ array
+ .map( ( x ) => Math.pow( x - mean, 2 ) )
+ .reduce( ( a, b ) => a + b ) / array.length
+ );
+}
+
+function medianAbsoluteDeviation( array = [] ) {
+ if ( ! array.length ) {
+ return 0;
+ }
+
+ const med = median( array );
+ return median( array.map( ( a ) => Math.abs( a - med ) ) );
+}
+
+/**
+ *
+ * @param {Array<Record<string, number[]>>} results
+ * @returns {Record<string, number[]>}
+ */
+function accumulateValues( results ) {
+ return results.reduce( ( acc, result ) => {
+ for ( const [ metric, values ] of Object.entries( result ) ) {
+ acc[ metric ] = acc[ metric ] ?? [];
+ acc[ metric ].push( ...values );
+ }
+ return acc;
+ }, {} );
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> module.exports = {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ parseFile,
</ins><span class="cx" style="display: block; padding: 0 10px"> median,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- getResultsFilename,
</del><span class="cx" style="display: block; padding: 0 10px"> camelCaseDashes,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ formatAsMarkdownTable,
+ formatValue,
+ linkToSha,
+ standardDeviation,
+ medianAbsoluteDeviation,
+ accumulateValues,
</ins><span class="cx" style="display: block; padding: 0 10px"> };
</span></span></pre></div>
<a id="trunktestsperformancewpcontentmupluginsservertimingphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/performance/wp-content/mu-plugins/server-timing.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/performance/wp-content/mu-plugins/server-timing.php 2024-05-02 13:17:45 UTC (rev 58075)
+++ trunk/tests/performance/wp-content/mu-plugins/server-timing.php 2024-05-02 13:57:49 UTC (rev 58076)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4,7 +4,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 'template_include',
</span><span class="cx" style="display: block; padding: 0 10px"> static function ( $template ) {
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- global $timestart;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ global $timestart, $wpdb;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $server_timing_values = array();
</span><span class="cx" style="display: block; padding: 0 10px"> $template_start = microtime( true );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -15,10 +15,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> add_action(
</span><span class="cx" style="display: block; padding: 0 10px"> 'shutdown',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- static function () use ( $server_timing_values, $template_start ) {
-
- global $timestart;
-
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ static function () use ( $server_timing_values, $template_start, $wpdb ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> $output = ob_get_clean();
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $server_timing_values['template'] = microtime( true ) - $template_start;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -30,7 +27,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * any numeric value can actually be passed.
</span><span class="cx" style="display: block; padding: 0 10px"> * This is a nice little trick as it allows to easily get this information in JS.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $server_timing_values['memory-usage'] = memory_get_usage();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $server_timing_values['memory-usage'] = memory_get_usage();
+ $server_timing_values['db-queries'] = $wpdb->num_queries;
+ $server_timing_values['ext-obj-cache'] = wp_using_ext_object_cache() ? 1 : 0;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $header_values = array();
</span><span class="cx" style="display: block; padding: 0 10px"> foreach ( $server_timing_values as $slug => $value ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -50,3 +49,45 @@
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> PHP_INT_MAX
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+add_action(
+ 'admin_init',
+ static function () {
+ global $timestart, $wpdb;
+
+ ob_start();
+
+ add_action(
+ 'shutdown',
+ static function () use ( $wpdb, $timestart ) {
+ $output = ob_get_clean();
+
+ $server_timing_values = array();
+
+ $server_timing_values['total'] = microtime( true ) - $timestart;
+
+ /*
+ * While values passed via Server-Timing are intended to be durations,
+ * any numeric value can actually be passed.
+ * This is a nice little trick as it allows to easily get this information in JS.
+ */
+ $server_timing_values['memory-usage'] = memory_get_usage();
+ $server_timing_values['db-queries'] = $wpdb->num_queries;
+ $server_timing_values['ext-obj-cache'] = wp_using_ext_object_cache() ? 1 : 0;
+
+ $header_values = array();
+ foreach ( $server_timing_values as $slug => $value ) {
+ if ( is_float( $value ) ) {
+ $value = round( $value * 1000.0, 2 );
+ }
+ $header_values[] = sprintf( 'wp-%1$s;dur=%2$s', $slug, $value );
+ }
+ header( 'Server-Timing: ' . implode( ', ', $header_values ) );
+
+ echo $output;
+ },
+ PHP_INT_MIN
+ );
+ },
+ PHP_INT_MAX
+);
</ins></span></pre>
</div>
</div>
</body>
</html>