Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 33 additions & 0 deletions .github/workflows/_ci-package.reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,19 @@ jobs:
# Run package tests using test-package.ts script
# All apps are built in isolated environments (Electron always, Tauri if not skipBuild)
# Tauri uses skipBuild to copy pre-built binaries from separate build jobs
# #540 investigation (temporary): capture the WebKit unified log during the macOS Tauri package
# test so we can classify its intermittent failure — same idle-run-loop stall class as the e2e
# DirectEval flake, or a different flake (e.g. session-create/teardown). Scoped to macOS + tauri.
- name: 🩺 Start WebKit log stream [Package]
if: runner.os == 'macOS' && inputs.service == 'tauri'
shell: bash
run: |
mkdir -p "$GITHUB_WORKSPACE/logs/package-tests"
/usr/bin/log stream --level debug --style compact \
--predicate 'subsystem == "com.apple.WebKit" OR process == "com.apple.WebKit.WebContent" OR process == "com.apple.WebKit.GPU"' \
> "$GITHUB_WORKSPACE/logs/package-tests/webkit-log.log" 2>&1 &
echo $! > /tmp/webkit-log-pkg.pid

- name: 🧪 Execute Package Tests
id: package-tests
# WebView2 Runtime 150 dropped the CDP --remote-debugging-port for elevated
Expand Down Expand Up @@ -315,6 +328,26 @@ jobs:
;;
esac

- name: 🩺 Collect WebKit diagnostics [Package]
if: always() && runner.os == 'macOS' && inputs.service == 'tauri'
shell: bash
run: |
if [ -f /tmp/webkit-log-pkg.pid ]; then
kill "$(cat /tmp/webkit-log-pkg.pid)" 2>/dev/null || true
fi
mkdir -p "$GITHUB_WORKSPACE/logs/package-tests"
shopt -s nullglob
for dir in "$HOME/Library/Logs/DiagnosticReports" "/Library/Logs/DiagnosticReports"; do
for f in "$dir"/*.ips "$dir"/*.crash; do
case "$(basename "$f")" in
*tauri*|*WebContent*|*WebKit*|*GPU*|*Networking*)
sudo -n cp "$f" "$GITHUB_WORKSPACE/logs/package-tests/$(basename "$f").log" 2>/dev/null \
|| cp "$f" "$GITHUB_WORKSPACE/logs/package-tests/$(basename "$f").log" 2>/dev/null || true ;;
esac
done
done
echo "package diag:"; ls -la "$GITHUB_WORKSPACE/logs/package-tests" 2>/dev/null || true

- name: ⚠️ Note allowed WebView2-150 Windows failure
if: always() && (inputs.service == 'tauri' || inputs.service == 'electrobun') && runner.os == 'Windows' && steps.package-tests.outcome == 'failure'
shell: bash
Expand Down
2 changes: 1 addition & 1 deletion packages/tauri-plugin-webdriver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ block2 = "0.6"

[target."cfg(target_os = \"macos\")".dependencies.objc2-foundation]
version = "0.3"
features = [ "NSString", "NSData", "NSError", "NSArray", "NSDictionary" ]
features = [ "NSString", "NSData", "NSError", "NSArray", "NSDictionary", "NSTimer", "NSRunLoop", "NSDate", "block2" ]

[target."cfg(target_os = \"macos\")".dependencies.objc2-app-kit]
version = "0.3"
Expand Down
32 changes: 31 additions & 1 deletion packages/tauri-plugin-webdriver/src/platform/macos.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::ptr::NonNull;
use std::sync::Arc;

use async_trait::async_trait;
Expand All @@ -8,7 +9,10 @@ use objc2::rc::Retained;
use objc2::runtime::{AnyObject, ProtocolObject};
use objc2::{define_class, msg_send, DefinedClass, MainThreadMarker, MainThreadOnly};
use objc2_app_kit::{NSBitmapImageFileType, NSBitmapImageRep, NSImage};
use objc2_foundation::{NSData, NSDictionary, NSError, NSObject, NSObjectProtocol, NSString};
use objc2_foundation::{
NSData, NSDictionary, NSError, NSObject, NSObjectProtocol, NSRunLoop, NSRunLoopCommonModes,
NSString, NSTimer,
};
use objc2_web_kit::{
WKContentWorld, WKFrameInfo, WKPDFConfiguration, WKScriptMessage, WKScriptMessageHandler,
WKSnapshotConfiguration, WKUIDelegate, WKUserContentController, WKWebView,
Expand Down Expand Up @@ -89,10 +93,36 @@ pub fn register_webview_handlers<R: Runtime>(webview: &tauri::Webview<R>) {
.userContentController()
.addScriptMessageHandler_name(&eval_proto, &NSString::from_str("wdioEvalResult"));

start_runloop_pump();

tracing::debug!("Registered UI delegate + eval message handler for webview");
});
}

/// Keep the app's main run loop waking on a headless/off-screen runner.
///
/// With no display, the app parks in `-[NSApplication run]` waiting for events, so WebKit doesn't
/// promptly service WebContent's URL-scheme resource requests (page-load subresources) or eval
/// dispatches — a DirectEval can then stall for tens of seconds and time out. A low-frequency no-op
/// timer in the run loop's common modes forces it to wake, draining that pending work each tick; the
/// interval only bounds worst-case servicing latency (well under the command timeout), so it is kept
/// coarse to minimise idle wakeups. Harmless with a real display (the loop already pumps
/// continuously). This plugin ships only in WebDriver-automation builds. Scheduled once.
/// https://github.com/webdriverio/desktop-mobile/issues/540
///
/// # Safety
/// Must be called on the main thread (schedules on the main run loop).
unsafe fn start_runloop_pump() {
use std::sync::Once;
static PUMP: Once = Once::new();
PUMP.call_once(|| {
let block = RcBlock::new(|_timer: NonNull<NSTimer>| {});
let timer = NSTimer::timerWithTimeInterval_repeats_block(0.05, true, &block);
NSRunLoop::mainRunLoop().addTimer_forMode(&timer, NSRunLoopCommonModes);
tracing::debug!("Started macOS main run-loop pump for headless WebKit scheduling");
});
}

#[async_trait]
impl<R: Runtime + 'static> PlatformExecutor<R> for MacOSExecutor<R> {
// =========================================================================
Expand Down
Loading