Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0be620e
Log every simulator tap of ten selection test runs
ellatrix Oct 12, 2026
68ccb25
Serve WordPress with native PHP instead of Playground
ellatrix Oct 12, 2026
a85ec8b
Print the PHP server log when the editor does not load
ellatrix Oct 12, 2026
de67405
Launch Safari once during setup and time each setup step
ellatrix Oct 12, 2026
e7a0f77
Remove the touch diagnostics and update the iOS test docs
ellatrix Oct 12, 2026
22f223e
Match the setup-php version comment to its tag
ellatrix Oct 12, 2026
3afd497
Stop when Safari does not open the warm-up page
ellatrix Oct 12, 2026
d1718f4
Download WordPress from GitHub, the same as the end to end tests
ellatrix Oct 12, 2026
dc0cc72
Block outside requests in the iOS test site and stop the server's wor…
ellatrix Oct 12, 2026
fe99e90
Clone WordPress instead of downloading the tarball
ellatrix Oct 12, 2026
ed73791
Set up the iOS test site with WP-CLI instead of the blueprint
ellatrix Oct 12, 2026
c3e604c
Add the blueprint's two steps to run.sh with WP-CLI
ellatrix Oct 12, 2026
e5aefcd
Time the first editor load in CI (temporary)
ellatrix Oct 13, 2026
abedec7
Compare the first editor load with and without cron (temporary)
ellatrix Oct 13, 2026
05a6f20
Sample the stuck editor load (temporary)
ellatrix Oct 13, 2026
b31b4eb
Sample the first editor load in run.sh (temporary)
ellatrix Oct 13, 2026
b3b3757
Turn off the JIT for the iOS test server and remove the probe
ellatrix Oct 13, 2026
d9c2275
Turn off the JIT for WP-CLI too
ellatrix Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/ios-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,11 @@
name: ios-build-output
path: build

# The runner's Node is older than the repository requires, and
# npx enforces that requirement when it starts Playground.
- name: Use desired version of Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
- name: Set up PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
with:
node-version-file: .nvmrc
package-manager-cache: false
php-version: '8.4'
coverage: none

- name: Install xcodegen
run: brew install xcodegen
Expand All @@ -98,12 +96,14 @@
xcodebuild -version

- name: Run the tests
run: ./test/ios/run.sh
run: ./test/ios/run.sh -only-testing:SafariTests/SelectionTests -test-iterations 10

- name: Archive the test results
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ always() }}
with:
name: ios-test-results
path: test/ios/build/results.xcresult
path: |
test/ios/build/results.xcresult
test/ios/build/touches.log
if-no-files-found: ignore
21 changes: 21 additions & 0 deletions test/ios/blueprint-steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Applies the steps of blueprint.json to a WordPress install served by
native PHP: runPHP code runs with PHP, writeFile writes the file, with
Playground's /wordpress paths mapped to the install."""
import json
import os
import subprocess
import sys
import tempfile

blueprint, root = sys.argv[1], sys.argv[2]
for step in json.load( open( blueprint ) )[ "steps" ]:
if step[ "step" ] == "runPHP":
with tempfile.NamedTemporaryFile( "w", suffix=".php", delete=False ) as script:
script.write( step[ "code" ].replace( "/wordpress/", root + "/" ) )
subprocess.run( [ "php", script.name ], check=True )
elif step[ "step" ] == "writeFile":
path = step[ "path" ].replace( "/wordpress/", root + "/", 1 )
os.makedirs( os.path.dirname( path ), exist_ok=True )
open( path, "w" ).write( step[ "data" ] )
else:
sys.exit( "Unsupported blueprint step: " + step[ "step" ] )
33 changes: 33 additions & 0 deletions test/ios/diagnostics/touches.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Temporary diagnostics: one line per synthesized tap from the simulator's
touch log (backboardd, TouchEvents), saying whether the tap was lost."""
import re
import sys
from datetime import datetime

taps = []
for line in open(sys.argv[1], errors="replace"):
match = re.match(r"(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d+)", line)
if not match:
continue
time = datetime.strptime(match.group(1), "%Y-%m-%d %H:%M:%S.%f")
if "digitizer attached" in line:
taps.append({"time": time, "unknown": 0, "skipped": 0, "lost": False})
elif not taps:
continue
elif "unknown digitizer" in line:
taps[-1]["unknown"] += 1
elif "skipping event" in line:
taps[-1]["skipped"] += 1
elif "canceling paths contacts" in line and "touching" in line:
taps[-1]["lost"] = True

previous = None
for number, tap in enumerate(taps, 1):
gap = (tap["time"] - previous).total_seconds() if previous else 0
previous = tap["time"]
print(
"TAP %3d %s gap=%6.1fs unknown=%d skipped=%d %s"
% (number, tap["time"].strftime("%H:%M:%S.%f")[:-3], gap, tap["unknown"], tap["skipped"], "LOST" if tap["lost"] else "ok")
)
lost = [tap for tap in taps if tap["lost"]]
print("TAPS %d LOST %d RACED %d" % (len(taps), len(lost), sum(1 for tap in taps if tap["unknown"])))
59 changes: 44 additions & 15 deletions test/ios/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,49 @@ print( devices[ -1 ][ "udid" ] )
step "Booting simulator $UDID"
# The boot goes on in the background while WordPress starts.
xcrun simctl boot "$UDID" 2>/dev/null || true
# WordPress runs in Playground: PHP compiled to WebAssembly, no Docker.
# WordPress runs on PHP's built-in server with SQLite, set up fresh on
# every run, with the same steps as Playground's blueprint.json.
step "Starting WordPress"
PLAYGROUND_LOG=$( mktemp )
# npx runs outside the checkout: the checkout's npm settings refuse
# Playground's install scripts.
ROOT="$PWD"
( cd "$( mktemp -d )" && exec npx --yes @wp-playground/cli@3.1.53 server \
--auto-mount="$ROOT" \
--blueprint="$ROOT/test/ios/blueprint.json" \
--port "$PORT" ) > "$PLAYGROUND_LOG" 2>&1 &
PLAYGROUND_PID=$!
trap 'kill "$PLAYGROUND_PID" 2>/dev/null || true' EXIT
# Playground prints "Ready!" once it listens and the blueprint has run; the
# server answers earlier, before the plugin is active.
until grep -q "Ready!" "$PLAYGROUND_LOG"; do
kill -0 "$PLAYGROUND_PID" 2>/dev/null || { cat "$PLAYGROUND_LOG"; echo "WordPress did not start"; exit 1; }
CACHE="$ROOT/test/ios/build/cache"
WP_DIR="$ROOT/test/ios/build/wordpress"
mkdir -p "$CACHE"
[ -f "$CACHE/wp-cli.phar" ] || curl -sSLf -o "$CACHE/wp-cli.phar" https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
[ -f "$CACHE/sqlite.zip" ] || curl -sSLf -o "$CACHE/sqlite.zip" https://downloads.wordpress.org/plugin/sqlite-database-integration.latest-stable.zip
Comment thread
ellatrix marked this conversation as resolved.
Resolved
wp() { php -d memory_limit=512M "$CACHE/wp-cli.phar" --path="$WP_DIR" --quiet "$@"; }
rm -rf "$WP_DIR"
wp core download --version=latest
unzip -q "$CACHE/sqlite.zip" -d "$WP_DIR/wp-content/plugins"
SQLITE="$WP_DIR/wp-content/plugins/sqlite-database-integration"
sed -e "s#{SQLITE_IMPLEMENTATION_FOLDER_PATH}#$SQLITE#" -e "s#{SQLITE_PLUGIN}#sqlite-database-integration/load.php#" \
"$SQLITE/db.copy" > "$WP_DIR/wp-content/db.php"
wp config create --dbname=wordpress --dbuser=wordpress --dbpass=wordpress --skip-check
wp core install --url="$WP_BASE_URL" --title="iOS tests" --admin_user=admin --admin_password=password --admin_email=admin@example.com --skip-email
ln -s "$ROOT" "$WP_DIR/wp-content/plugins/gutenberg"
wp plugin activate gutenberg
python3 "$ROOT/test/ios/blueprint-steps.py" "$ROOT/test/ios/blueprint.json" "$WP_DIR"
# Like Playground's login option: every request is the administrator's.
cat > "$WP_DIR/wp-content/mu-plugins/login.php" <<'PHP'
<?php
add_action( 'init', function () {
if ( is_user_logged_in() ) {
return;
}
// The admin checks this request's cookies, so set them here too.
$expiration = time() + DAY_IN_SECONDS;
$_COOKIE[ AUTH_COOKIE ] = wp_generate_auth_cookie( 1, $expiration, 'auth' );
$_COOKIE[ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( 1, $expiration, 'logged_in' );
wp_set_auth_cookie( 1, true );
wp_set_current_user( 1 );
}, 0 );
PHP
PHP_CLI_SERVER_WORKERS=8 php -d memory_limit=512M -S "127.0.0.1:$PORT" -t "$WP_DIR" > "$ROOT/test/ios/build/server.log" 2>&1 &
SERVER_PID=$!
trap 'kill "$SERVER_PID" 2>/dev/null || true' EXIT
until curl -sf -o /dev/null "$WP_BASE_URL/wp-login.php"; do
kill -0 "$SERVER_PID" 2>/dev/null || { cat "$ROOT/test/ios/build/server.log"; echo "WordPress did not start"; exit 1; }
sleep 1
done
cat "$PLAYGROUND_LOG"

# The editor page shows whether the plugin replaces core's bundles, which
# it only does when its build exists. Its first request also does the
Expand Down Expand Up @@ -89,6 +113,7 @@ step "Generating the Xcode project"
( cd test/ios && xcodegen generate --quiet )
step "Building and running the tests"
rm -rf test/ios/build/results.xcresult
TEST_START=$( date +'%Y-%m-%d %H:%M:%S' )
STATUS=0
TEST_RUNNER_WP_BASE_URL="$WP_BASE_URL" xcodebuild test \
-project test/ios/GutenbergIOS.xcodeproj \
Expand All @@ -97,4 +122,8 @@ TEST_RUNNER_WP_BASE_URL="$WP_BASE_URL" xcodebuild test \
-derivedDataPath test/ios/build \
-resultBundlePath test/ios/build/results.xcresult \
"$@" || STATUS=$?
# Temporary diagnostics: the simulator's touch log, one line per tap.
xcrun simctl spawn "$UDID" log show --info --debug --style compact --start "$TEST_START" \
--predicate 'process == "backboardd" AND category == "TouchEvents"' > test/ios/build/touches.log 2>&1 || true
python3 test/ios/diagnostics/touches.py test/ios/build/touches.log || true
exit "$STATUS"
Loading