Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .github/workflows/phone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ jobs:
env:
# Runs the shell's real-kernel relay test instead of skipping it.
OCTOS_SHELL_TEST_KERNEL: ${{ runner.temp }}/octos-kernel/target/release/octos
# A Splash entry's 64 ms budget is wall-clock time. With ~1,000
# tests in one process, an entry waits on shared locks and page
# faults, and the budget counts that against the script. These
# tests check behaviour, not time (makepad#109).
MAKEPAD_SPLASH_BUDGET_MS: "10000"
run: |
# octosense-app-hub-app is App Hub's shared shell crate, a git
# dependency with dev-dependencies: cargo cannot test it from here;
Expand Down
12 changes: 10 additions & 2 deletions crates/shell/src/host_tools/script_apps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -832,8 +832,16 @@ pub(crate) mod tests {
request.args = json!({"connection":current,"operation":"notes.list"});
let (r,sent) = reply();exec.execute(request,r);
let output = sent.lock().unwrap();
assert_eq!(output[0]["error"]["kind"],"account_scope", "{method}");
assert!(output[0]["error"]["message"].as_str().unwrap().contains("account changed"),"{}",output[0]);
if output[0]["error"]["kind"] == "api_unavailable" {
// A host without the backend API (auth is macOS and Android
// only) refuses it before the account check, once any test
// has registered the auth catalog in this process.
assert!(octosense_appstore::host_api::methods().iter()
.any(|api| api.name == method && !api.supports(octosense_appstore::host_api::platform())),"{}",output[0]);
} else {
assert_eq!(output[0]["error"]["kind"],"account_scope", "{method}");
assert!(output[0]["error"]["message"].as_str().unwrap().contains("account changed"),"{}",output[0]);
}
assert!(!is_waiting(&format!("c-{alias}")),"stale peer must not reach a backend service");
}
std::fs::remove_dir_all(host_dir).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion docs/local-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ tools/ci-local.sh --only all --linux-host --offload --list # what runs where
| --- | --- | --- |
| desktop / `desktop` | Compile the desktop; desktop tools and scripts | The shell-source, single-graph and native-apps checks |
| desktop / `native-host-api` | All of it (a real macOS host) | Nothing |
| phone / `home` | Apple icons and asset catalogs; compile Home; Home's and App Hub's tests (three fail on Linux, see `JOBS`) | The graph checks, the octos kernel, the two-lane scenario |
| phone / `home` | Apple icons and asset catalogs; compile Home | The graph checks, the octos kernel, Home's and App Hub's tests, the two-lane scenario |
| apps / `apps` | Nothing | All of it |

Each part prepares the sources itself (the job's `python3 tools/setup.py`). The host's part runs as `<job>@linux`; its steps join the table under the job, marked `(linux)`. `--check-drift` fails when a name in `mac_steps` stops matching a step.
Expand Down
9 changes: 7 additions & 2 deletions runtime-patches.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"base_revision": "32d6415fb7476345ad36ee4f98d6f844d1f07fd2",
"patch": "tools/runtime-patches/makepad-settings.patch",
"sha256": "b17352ebf59e50ab3db6ff1cb825c4e006bcdff47d83522e372b8fe585e57570",
"tree": "4fb9744e3d9525c725dedcb1100c14451d2bb118",
"tree": "6105f0025e748857a620e3c368c6d572a2493555",
"product_fixes": [
"Settings integration: Android IME synchronization, accessibility and reduced motion, GLES program-cache validation, obscured-gesture cancellation and scroll-position observation.",
"Contained-app isolation and secret-input restrictions come from the pinned upstream runtime; the Settings overlay preserves those gates.",
Expand All @@ -26,7 +26,8 @@
"WebReader refuses unsupported Linux/Windows platforms before queuing native browser operations instead of reporting success for an invisible view; this does not add embedded browser support.",
"Opted-in camera and microphone check OS grants before prompting; background/agent requests retain nonprompt authority through callbacks and cannot use desktop timeout approval. Passive location reads remain available; legacy GPS requests require foreground prompt authority.",
"Host request JSON and pre-drain queues are bounded before native allocation; delayed host callbacks retain their nonprompting origin after an agent tool completes or is cancelled.",
"Agent/background prompt denial follows detached timers, tasks, paused continuations, widget callbacks and HTTP/WebSocket callbacks, including native device helpers. A fresh foreground entry retains its own authority; scoped denial restores on exit or unwind."
"Agent/background prompt denial follows detached timers, tasks, paused continuations, widget callbacks and HTTP/WebSocket callbacks, including native device helpers. A fresh foreground entry retains its own authority; scoped denial restores on exit or unwind.",
"A Splash isolate entry keeps its 64 ms wall-clock budget unless MAKEPAD_SPLASH_BUDGET_MS sets another (makepad#109). Only the shell test step sets it: ~1,000 tests in one process wait on shared locks and page faults, which a wall-clock budget counts against the script."
],
"stacked": [
{
Expand Down Expand Up @@ -100,6 +101,10 @@
{
"patch": "tools/runtime-patches/makepad-async-prompt-provenance.patch",
"sha256": "46bb7620ccb4f6a76a08d0333c0249d2619123da2a2649f55294fd4d35f0d905"
},
{
"patch": "tools/runtime-patches/makepad-splash-budget.patch",
"sha256": "cebcae3327ec955a2b37eb1f3353e97160d702bd4081abb264006fee4ac17ee5"
}
]
}
Expand Down
15 changes: 5 additions & 10 deletions tools/ci_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,17 +297,12 @@ def load_workflow(name):
"desktop.yml:desktop": {"mac_steps": ["Compile the desktop with the shared runtime",
"Desktop tools and scripts"]},
"desktop.yml:native-host-api": {"macos_only": True},
# Home's test suite stays here until it passes on Linux: on the first
# offloaded run (8 Oct 2026) 1017 of 1020 shell tests did. Failing were
# host_tools::script_apps::tests::backend_data_aliases_cannot_reuse_a_stale_peer_with_the_current_handle
# (auth.backend.* is not a Linux host API, so the executor answers
# api_unavailable before the account check the test expects) and two
# module_resize_tests::photos_preview_* (the Splash script time budget
# ran out on the host's slower cores).
# Home's tests run on the host too. Their Splash tests rely on the test
# step's MAKEPAD_SPLASH_BUDGET_MS: ~1,000 tests in one process at the
# host's 72 threads stretch a 6 ms script entry past makepad's 64 ms
# wall-clock budget (makepad#109).
"phone.yml:home": {"mac_steps": ["Validate launcher icons and Apple asset catalogs",
"Compile Home and its bundled modules",
"Build Home, App Hub admission and runtime policy tests",
"Test Home, App Hub admission and runtime policy"]},
"Compile Home and its bundled modules"]},
"apps.yml:services": {"linux_only_note": True},
"apps.yml:kernel-security": {"linux_only_note": True},
"apps.yml:apps": {"mac_steps": []},
Expand Down
39 changes: 39 additions & 0 deletions tools/runtime-patches/makepad-splash-budget.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--- a/widgets/src/widget_async.rs
+++ b/widgets/src/widget_async.rs
@@ -503,14 +503,31 @@ fn with_splash_prompt_authority<R>(vm: &mut ScriptVm, f: impl FnOnce(&mut Script
f(vm)
}

+/// How long one entry into a Splash isolate may run: 64 ms, unless
+/// `MAKEPAD_SPLASH_BUDGET_MS` says otherwise. The budget is wall-clock time,
+/// so it also counts time the entry spends waiting for the CPU or a lock. A
+/// test binary running a thousand tests in one process is the case for the
+/// override: there the wall clock says little about the script.
+fn splash_entry_budget() -> std::time::Duration {
+ static BUDGET: std::sync::OnceLock<std::time::Duration> = std::sync::OnceLock::new();
+ *BUDGET.get_or_init(|| {
+ let ms = std::env::var("MAKEPAD_SPLASH_BUDGET_MS")
+ .ok()
+ .and_then(|value| value.trim().parse::<u64>().ok())
+ .filter(|&ms| ms > 0)
+ .unwrap_or(64);
+ std::time::Duration::from_millis(ms)
+ })
+}
+
/// A Splash isolate runs untrusted-ish user script on the UI thread; cap how long any
/// single entry into it may run.
fn with_splash_budget<R>(vm: &mut ScriptVm, f: impl FnOnce(&mut ScriptVm) -> R) -> R {
- let old_budget = vm.bx.run_budget.replace(ScriptRunBudget::from_durations(
- std::time::Duration::from_millis(64),
- std::time::Duration::from_millis(64),
- 512,
- ));
+ let budget = splash_entry_budget();
+ let old_budget = vm
+ .bx
+ .run_budget
+ .replace(ScriptRunBudget::from_durations(budget, budget, 512));
let out = with_splash_prompt_authority(vm, f);
vm.bx.run_budget = old_budget;
out
Loading