Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
253
tools/test-legacy-apps.py
Normal file
253
tools/test-legacy-apps.py
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Observe original archived apps in real Gaia; failures stay visible in the report."""
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
from marionette_client import Client
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("--restart-check", action="store_true")
|
||||
args = p.parse_args()
|
||||
results = {"engine": None, "restart": args.restart_check, "apps": {}}
|
||||
with Client(2857) as client:
|
||||
profile = client.execute("return PathUtils.profileDir;")
|
||||
assert "/host-legacy-" in profile, profile
|
||||
results["engine"] = client.execute("return Services.appinfo.platformVersion;")
|
||||
client.execute(
|
||||
"try{ChromeUtils.registerWindowActor('VulpesProbe',{child:{esModuleURI:'resource://vulpes/tests/ProbeChild.sys.mjs'},allFrames:true,safeForUntrustedWebProcess:true});}catch(_){}"
|
||||
)
|
||||
|
||||
def ev(app, code):
|
||||
script = """const done=arguments[arguments.length-1];const w=Services.wm.getMostRecentWindow('vulpes:host');
|
||||
const b=w?.document.getElementById('system');if(!b){done(null);return;}
|
||||
const bc=(function f(b){if(b.currentWindowGlobal?.documentURI.spec.startsWith(PREFIX))return b;for(const c of b.children){const found=f(c);if(found)return found;}return null;})(b.browsingContext);
|
||||
if(!bc){done(null);return;}bc.currentWindowGlobal.getActor('VulpesProbe').sendQuery('Evaluate',CODE).then(v=>done({value:v}),e=>done({error:String(e)}));""".replace(
|
||||
"PREFIX", json.dumps("http://" + app + ".localhost:8765/")
|
||||
).replace(
|
||||
"CODE", json.dumps(code)
|
||||
)
|
||||
r = client.call(
|
||||
"WebDriver:ExecuteAsyncScript",
|
||||
{
|
||||
"script": script,
|
||||
"args": [],
|
||||
"newSandbox": True,
|
||||
"sandbox": "system",
|
||||
"scriptTimeout": 15000,
|
||||
},
|
||||
)["value"]
|
||||
if r and "error" in r:
|
||||
raise RuntimeError(r["error"])
|
||||
return r.get("value") if r else None
|
||||
|
||||
def wait(app, code, timeout=30):
|
||||
end = time.monotonic() + timeout
|
||||
while time.monotonic() < end:
|
||||
r = ev(app, code)
|
||||
if r:
|
||||
return r
|
||||
time.sleep(0.2)
|
||||
raise RuntimeError("Timeout: " + app + " " + code)
|
||||
|
||||
def focus():
|
||||
client.execute(
|
||||
"const w=Services.wm.getMostRecentWindow('vulpes:host');w.focus();w.document.getElementById('system').focus();return true;"
|
||||
)
|
||||
subprocess.run(
|
||||
["xdotool", "search", "--name", "^Vulpes", "windowraise", "windowfocus"],
|
||||
check=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
def point(app, selector):
|
||||
return ev(
|
||||
app,
|
||||
"const e=document.querySelector("
|
||||
+ json.dumps(selector)
|
||||
+ ');e.scrollIntoView({block:"center"});const b=e.getBoundingClientRect();return {x:mozInnerScreenX+b.x+b.width/2,y:mozInnerScreenY+b.y+b.height/2};',
|
||||
)
|
||||
|
||||
def tap(app, selector):
|
||||
pos = point(app, selector)
|
||||
focus()
|
||||
subprocess.run(
|
||||
["xdotool", "mousemove", str(round(pos["x"])), str(round(pos["y"])), "click", "1"],
|
||||
check=True,
|
||||
)
|
||||
time.sleep(0.3)
|
||||
|
||||
def shot(app):
|
||||
focus()
|
||||
subprocess.run(
|
||||
[
|
||||
"import",
|
||||
"-window",
|
||||
"root",
|
||||
str(
|
||||
ROOT
|
||||
/ "logs"
|
||||
/ ("archive-" + app + ("-restart" if args.restart_check else "") + ".png")
|
||||
),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
wait("system", "return document.body?.getAttribute('ready-state')==='fullyLoaded';", 60)
|
||||
for app in json.loads((ROOT / "tests/legacy-apps.json").read_text()):
|
||||
name = app["id"]
|
||||
r = {}
|
||||
results["apps"][name] = r
|
||||
try:
|
||||
ev(
|
||||
"system",
|
||||
'await VulpesCompat.call("apps.launch",'
|
||||
+ json.dumps({"manifestURL": "http://" + name + ".localhost:8765/manifest.webapp"})
|
||||
+ ");return true;",
|
||||
)
|
||||
wait(name, "return document.readyState==='complete';")
|
||||
wait(
|
||||
"system",
|
||||
"return [...document.querySelectorAll('.appWindow.active[transition-state=opened] iframe')].some(f=>f.src.startsWith('http://"
|
||||
+ name
|
||||
+ ".localhost:8765/'));",
|
||||
)
|
||||
time.sleep(1)
|
||||
r["loaded"] = True
|
||||
r["text"] = ev(name, "return document.body.innerText.slice(0,1800);")
|
||||
if name == "legacy-2048-for-firefox-os":
|
||||
wait(name, "return document.querySelectorAll('.tile').length>0;")
|
||||
r["initialState"] = ev(
|
||||
name, "return JSON.parse(localStorage.getItem('gameState'));"
|
||||
)
|
||||
if args.restart_check:
|
||||
expected = json.loads((ROOT / "logs/legacy-game-state.json").read_text())
|
||||
r["stateSurvivesRestart"] = r["initialState"] == expected
|
||||
else:
|
||||
tap(name, ".game-container")
|
||||
focus()
|
||||
subprocess.run(
|
||||
[
|
||||
"xdotool",
|
||||
"key",
|
||||
"--delay",
|
||||
"150",
|
||||
"Left",
|
||||
"Up",
|
||||
"Right",
|
||||
"Down",
|
||||
"Left",
|
||||
"Up",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
time.sleep(0.4)
|
||||
r["keyboardState"] = ev(
|
||||
name, "return JSON.parse(localStorage.getItem('gameState'));"
|
||||
)
|
||||
pos = point(name, ".game-container")
|
||||
focus()
|
||||
subprocess.run(
|
||||
[
|
||||
"xdotool",
|
||||
"mousemove",
|
||||
str(round(pos["x"] + 80)),
|
||||
str(round(pos["y"])),
|
||||
"mousedown",
|
||||
"1",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
for i in range(1, 11):
|
||||
subprocess.run(
|
||||
[
|
||||
"xdotool",
|
||||
"mousemove",
|
||||
str(round(pos["x"] + 80 - i * 16)),
|
||||
str(round(pos["y"])),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
time.sleep(0.025)
|
||||
subprocess.run(["xdotool", "mouseup", "1"], check=True)
|
||||
time.sleep(0.4)
|
||||
r["swipeState"] = ev(
|
||||
name, "return JSON.parse(localStorage.getItem('gameState'));"
|
||||
)
|
||||
r["keyboardChangesGame"] = r["initialState"] != r["keyboardState"]
|
||||
r["swipeChangesGame"] = r["keyboardState"] != r["swipeState"]
|
||||
(ROOT / "logs/legacy-game-state.json").write_text(json.dumps(r["swipeState"]))
|
||||
r["layout"] = ev(
|
||||
name,
|
||||
"const b=document.querySelector('.game-container').getBoundingClientRect();return {width:innerWidth,board:{left:b.left,right:b.right,width:b.width},fits:b.left>=0&&b.right<=innerWidth};",
|
||||
)
|
||||
elif name == "legacy-calculator-2":
|
||||
for key in ["C", "1", "2", "+", "3", "="]:
|
||||
tap(name, 'input[value="' + key + '"]')
|
||||
r["display"] = ev(
|
||||
name, "return document.querySelector('#display div').textContent;"
|
||||
)
|
||||
r["twelvePlusThree"] = r["display"].strip() == "15"
|
||||
else:
|
||||
wait(name, "return !!document.querySelector('a[href=\"#/podcast/new\"]');")
|
||||
r["uiReady"] = True
|
||||
r["text"] = ev(name, "return document.body.innerText;")
|
||||
r["legacyXhr"] = ev(
|
||||
name,
|
||||
"const x=new XMLHttpRequest({mozSystem:true});x.open('GET','/');x.responseType='moz-chunked-arraybuffer';return {mozSystem:typeof x.mozSystem,chunkedType:x.responseType};",
|
||||
)
|
||||
tap(name, 'a[href="#/podcast/new"]')
|
||||
wait(name, "return !!document.querySelector('input');")
|
||||
r["addForm"] = ev(
|
||||
name,
|
||||
"return {text:document.body.innerText,inputs:[...document.querySelectorAll('input')].map(e=>({type:e.type,placeholder:e.placeholder})),buttons:[...document.querySelectorAll('button')].map(e=>({text:e.textContent,type:e.type}))};",
|
||||
)
|
||||
if not args.restart_check:
|
||||
ev(
|
||||
name,
|
||||
"window.__archiveRequests=[];const original=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(method,url,...args){const item={method,url:String(url)};window.__archiveRequests.push(item);this.addEventListener('loadend',()=>{item.status=this.status;item.finished=true;});return original.call(this,method,url,...args);};return true;",
|
||||
)
|
||||
tap(name, "input")
|
||||
subprocess.run(
|
||||
[
|
||||
"xdotool",
|
||||
"key",
|
||||
"ctrl+a",
|
||||
"type",
|
||||
"--clearmodifiers",
|
||||
"--delay",
|
||||
"15",
|
||||
"https://atp.fm/episodes?format=rss",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
tap(name, "button")
|
||||
try:
|
||||
wait(name, "return window.__archiveRequests.some(r=>r.finished);", 20)
|
||||
except RuntimeError:
|
||||
r["requestTimedOut"] = True
|
||||
r["feedRequests"] = ev(name, "return window.__archiveRequests;")
|
||||
r["afterAdd"] = ev(name, "return document.body.innerText;")
|
||||
else:
|
||||
r["afterRestart"] = ev(name, "return document.body.innerText;")
|
||||
shot(name)
|
||||
except Exception as e:
|
||||
r["failure"] = str(e)
|
||||
try:
|
||||
shot(name)
|
||||
except Exception:
|
||||
pass
|
||||
r["diagnostics"] = ev(name, "return window.__vulpesDiagnostics || [];")
|
||||
r["engineConsole"] = client.execute(
|
||||
"return Services.console.getMessageArray().map(e=>e.message).filter(m=>m.includes("
|
||||
+ json.dumps(name)
|
||||
+ ")).slice(-20);"
|
||||
)
|
||||
print(name, json.dumps(r, ensure_ascii=False), flush=True)
|
||||
(
|
||||
ROOT
|
||||
/ "logs"
|
||||
/ ("legacy-apps-restart-results.json" if args.restart_check else "legacy-apps-results.json")
|
||||
).write_text(json.dumps(results, ensure_ascii=False, indent=2) + "\n")
|
||||
Loading…
Reference in a new issue