Files
takana/scripts/test-atuq-inicio.py
T
SergioandClaude Opus 5 639948f2cd atuq: la página de inicio y la pestaña nueva, verificadas contra el propio motor
La v0.3 las puso por extensión de sistema y quedó como afirmación. Que el XPI
esté en el artefacto no dice nada: el override lo puede rechazar el gestor de
extensiones, lo puede pisar una política, o el navegador puede arrancar con el
chrome viejo cacheado. Ninguna de las tres falla ruidosamente — se ve una pestaña
nueva perfectamente normal, que es de otro.

Se mide preguntándole AL MOTOR, no mirando el disco: una sonda con permiso
`browserSettings` lee `homepageOverride` y `newTabPageOverride`.

    positivo  moz-extension://…/inicio.html  (las dos)
    control   about:home · about:newtab

El control negativo borra el XPI de `inicio` dentro del overlay temporal —el
rootfs real no se toca— y exige el resultado contrario.

Y una escotilla `ATUQ_DIR` en las dos sondas nuevas, con su aviso a gritos: el
corpus es compartido y hoy mismo el perfil PGO v2 de otro frente re-hasheó
`firefox` y con él `atuq`, así que el artefacto VIGENTE no existe en ningún store
hasta que alguien pague un build de horas. Sin escotilla no se puede correr una
sola prueba de atuq en esa ventana; con ella se corre contra un artefacto viejo A
SABIENDAS, y por eso el resultado no se puede citar como «atuq de hoy pasa».

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 21:03:25 +00:00

184 lines
8.2 KiB
Python

#!/usr/bin/env python3
"""¿La página de inicio y la pestaña nueva de `atuq` son las NUESTRAS? (SDD 26 §4.e)
python3 scripts/test-atuq-inicio.py # sale 0 si las dos son de atuq
python3 scripts/test-atuq-inicio.py --negative-control # sin el XPI: exige que sean las de Firefox
La v0.3 puso la página de inicio y la pestaña nueva **por extensión de sistema**, con
`chrome_url_overrides.newtab` y `chrome_settings_overrides.homepage`. Que el XPI esté en el
artefacto no dice nada de eso: un override lo puede rechazar el gestor de extensiones, lo puede
pisar una política, y —el modo de fallo propio de este proyecto— el navegador puede arrancar con el
chrome VIEJO cacheado y enseñar la página de Firefox como si nada. Ninguna de las tres cosas
falla ruidosamente: se ve una pestaña nueva perfectamente normal, que es de otro.
── CÓMO SE MIDE, SIN ABRIR PESTAÑAS NI MIRAR PÍXELES ─────────────────────────────────────────
Una extensión sonda con permiso `browserSettings` le pregunta al propio navegador cuál es su
`homepageOverride` y su `newTabPageOverride`. Es la respuesta del motor, no una inferencia: si el
override de `inicio` agarró, las dos son `moz-extension://…/inicio.html`; si no, son las de fábrica
(`about:home` / `about:newtab`). Mirar el XPI en el disco, o el `manifest.json` dentro del XPI,
mediría lo declarado — que es justo lo que ya sabíamos.
El control negativo BORRA el XPI de `inicio` dentro del overlay temporal (el rootfs real no se toca)
y exige el resultado contrario. Sin él, una sonda rota que siempre dijera «es la nuestra» se vería
idéntica a una que funciona.
REQUISITOS: el rootfs de `scripts/atuq-nested.sh` (que lo hidrata) y `atuq` sellado.
"""
import json
import os
import shutil
import subprocess
import sys
import tempfile
import zipfile
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
RFS = os.environ.get("RFS", "/mnt/cosecha/escritorios/atuq-rootfs")
ALPINE = os.path.join(ROOT, ".dev-fs/alpine")
HAMMER = os.path.join(ROOT, "target/release/hammer")
STORE = os.environ.get("HAMMER_STORE", os.path.join(ROOT, "store"))
EXT_ID = "sonda-inicio@atuq.tawasuyu"
XPI_INICIO = "/usr/lib/atuq/distribution/extensions/inicio@atuq.tawasuyu.xpi"
MANIFIESTO = {
"manifest_version": 2,
"name": "sonda-inicio",
"version": "1.0",
"browser_specific_settings": {"gecko": {"id": EXT_ID}},
"permissions": ["browserSettings"],
"background": {"scripts": ["fondo.js"]},
}
FONDO = """
function di(s) { try { dump("INICIO-TEST " + s + "\\n"); } catch (e) {} }
async function mirar() {
try {
const h = await browser.browserSettings.homepageOverride.get({});
const n = await browser.browserSettings.newTabPageOverride.get({});
di("HOMEPAGE " + h.value);
di("NEWTAB " + n.value);
} catch (e) {
di("EXCEPCIÓN " + e.message);
}
}
mirar();
"""
def fatal(msg):
print(f"✗ {msg}", file=sys.stderr)
raise SystemExit(1)
def artefacto(receta, nombre):
"""El artefacto VIGENTE, por `hammer hash` y nunca por glob."""
# ⚠ ESCOTILLA `ATUQ_DIR`, y con su aviso puesto. El corpus es compartido: otro frente puede
# mover una receta de arriba —el 2026-09-07 el perfil PGO v2 re-hasheó `firefox` y con él
# `atuq`— y entonces el artefacto VIGENTE no existe en ningún store hasta que alguien pague un
# build de horas. Sin escotilla, ninguna prueba de atuq se puede correr en esa ventana. Con
# escotilla, se puede correr contra un artefacto viejo A SABIENDAS: por eso grita, y por eso el
# resultado no se puede citar como «atuq de hoy pasa».
forzado = os.environ.get("ATUQ_DIR")
if forzado and nombre == "atuq":
if not os.path.isdir(forzado):
fatal(f"ATUQ_DIR={forzado} no existe")
print(f"⚠ ATUQ_DIR: se prueba {os.path.basename(forzado)}, que NO es necesariamente el vigente")
return forzado
r = subprocess.run([HAMMER, "--store", STORE, "hash", f"recipes/{receta}.toml"],
cwd=ROOT, capture_output=True, text=True)
if r.returncode != 0:
fatal(f"no pude hashear {receta}: {r.stderr.strip()}")
h = r.stdout.strip().splitlines()[-1].removeprefix("b3:")
d = os.path.join(STORE, f"{h}-{nombre}")
if not os.path.isdir(d):
fatal(f"{nombre} b3:{h[:8]} no está sellado — construilo antes")
return d
def main():
negativo = "--negative-control" in sys.argv[1:]
if negativo:
print("== MODO CONTROL NEGATIVO: se BORRA el XPI de inicio y se exige la página de Firefox")
if not os.path.isdir(RFS):
fatal(f"no está el rootfs en {RFS} — corré antes `scripts/atuq-nested.sh`, que lo hidrata")
atuq = artefacto("atuq", "atuq")
print(f"artefacto: {os.path.basename(atuq)}")
tmp = tempfile.mkdtemp(prefix="atuq-inicio-")
salida = os.path.join(tmp, "salida")
extdir = os.path.join(tmp, "capa", "usr", "lib", "atuq", "distribution", "extensions")
os.makedirs(os.path.join(salida, "perfil"))
os.makedirs(extdir)
try:
with zipfile.ZipFile(os.path.join(extdir, f"{EXT_ID}.xpi"), "w") as z:
z.writestr("manifest.json", json.dumps(MANIFIESTO, indent=1))
z.writestr("fondo.js", FONDO)
with open(os.path.join(salida, "perfil", "user.js"), "w") as fh:
fh.write('user_pref("browser.dom.window.dump.enabled", true);\n')
fh.write('user_pref("browser.shell.checkDefaultBrowser", false);\n')
borrar = f"rm -f {XPI_INICIO}" if negativo else "true"
guion = f"""
mkdir -p /root
[ -s /etc/machine-id ] || tr -d - < /proc/sys/kernel/random/uuid > /etc/machine-id
{borrar}
timeout 50 /usr/bin/atuq --profile /salida/perfil --no-remote --headless about:blank \
> /salida/consola.log 2>&1
exit 0
"""
subprocess.run([
"bwrap",
"--overlay-src", ALPINE, "--overlay-src", RFS, "--overlay-src", atuq,
"--overlay-src", os.path.join(tmp, "capa"), "--tmp-overlay", "/",
"--dev-bind", "/dev", "/dev", "--proc", "/proc", "--bind", "/sys", "/sys",
"--tmpfs", "/tmp", "--tmpfs", "/run", "--unshare-pid",
"--bind", salida, "/salida",
"--uid", "0", "--gid", "0",
"--setenv", "PATH", "/usr/bin:/bin:/usr/sbin:/sbin",
"--setenv", "XDG_RUNTIME_DIR", "/run", "--setenv", "HOME", "/root",
"--setenv", "XDG_DATA_DIRS", "/usr/share", "--setenv", "MOZ_HEADLESS", "1",
"--setenv", "MOZ_DISABLE_CONTENT_SANDBOX", "1",
"--setenv", "MOZ_DISABLE_RDD_SANDBOX", "1",
"sh", "-c", guion,
], check=False)
log = open(os.path.join(salida, "consola.log"), errors="replace").read()
lineas = [l for l in log.splitlines() if l.startswith("INICIO-TEST")]
for l in lineas:
print(" " + l)
if not lineas:
shutil.copy(os.path.join(salida, "consola.log"),
os.path.join(ROOT, "work", "atuq-inicio.log"))
fatal("la sonda no dijo nada — ¿no se instaló? log en work/atuq-inicio.log")
def valor(clave):
for l in lineas:
if l.startswith(f"INICIO-TEST {clave} "):
return l.split(" ", 2)[2]
return None
home, newtab = valor("HOMEPAGE"), valor("NEWTAB")
nuestras = [v for v in (home, newtab) if v and "inicio.html" in v]
if negativo:
if nuestras:
fatal("SIN el XPI de inicio las páginas siguen siendo las nuestras"
" — esta prueba no distingue nada")
print("✓ control negativo: sin el XPI son las de Firefox (la prueba SÍ lo vería)")
return 0
if len(nuestras) != 2:
shutil.copy(os.path.join(salida, "consola.log"),
os.path.join(ROOT, "work", "atuq-inicio.log"))
fatal(f"la página de inicio y/o la pestaña nueva NO son de atuq"
f" (homepage={home} newtab={newtab}) — log en work/atuq-inicio.log")
print("✓ la página de inicio y la pestaña nueva son las de atuq")
return 0
finally:
shutil.rmtree(tmp, ignore_errors=True)
sys.exit(main())