[FIX] matrix: UTF-8 locale for Chromium, skip ps reading when absent
On Linux, Chromium converts origin-private file names through the process locale. The debian:13 and fedora:44 images set no LANG, so every accented name failed with TypeMismatchError there, while Arch, which sets C.UTF-8, passed. The container steps now run under LANG=C.UTF-8, and a browser test names that cause when a Chromium cannot write an accented name. The long test that reads the real ps skips with its reason when ps is missing. Checked: debian-13 and fedora-44 test_navigateur and test_long green in containers; 36 browser and 26 node-long tests locally. --- FR --- [FIX] matrice : locale UTF-8 pour Chromium, lecture ps sautée sans ps Sous Linux, Chromium convertit les noms de fichiers de l'origine privée par la locale du processus. Les images debian:13 et fedora:44 ne posent aucun LANG : chaque nom accentué y échouait (TypeMismatchError), alors qu'Arch, qui pose C.UTF-8, passait. Les étapes des conteneurs tournent sous LANG=C.UTF-8, et une épreuve du navigateur nomme cette cause quand un Chromium n'écrit pas un nom accentué. L'épreuve longue qui lit le vrai ps se saute, raison dite, quand ps manque. Vérifié : debian-13 et fedora-44 au vert en conteneur ; 36 + 26 en local. Assisted-by: Claude Opus 5.5
This commit is contained in:
parent
8d03d5ba41
commit
63efdb21a1
3 changed files with 47 additions and 7 deletions
|
|
@ -119,8 +119,15 @@ preparation() {
|
|||
|
||||
# Étapes sous le compte dev, chacune annoncée par une ligne « === étape
|
||||
# statut » que le bilan relit. L'essai et le serveur de Vite ne se jouent
|
||||
# que sur une installation réussie.
|
||||
# que sur une installation réussie. Elles tournent sous LANG=C.UTF-8, des
|
||||
# paramètres régionaux UTF-8 comme ceux d'un poste de développement, que la
|
||||
# plupart des images ne posent pas : sous le « C » qu'elles laissent,
|
||||
# Chromium ne convertit plus un nom hors ASCII de l'OPFS, et les épreuves
|
||||
# du navigateur n'éprouvent plus le projet. C.UTF-8 vient avec la glibc,
|
||||
# sans paquet de langue ; une image qui ne le porterait pas retomberait sur
|
||||
# « C », comme sans la variable.
|
||||
ETAPES_DEV='
|
||||
export LANG=C.UTF-8
|
||||
etape() { local nom="$1"; shift; if "$@"; then echo "=== $nom ok"; else echo "=== $nom ÉCHEC"; return 1; fi; }
|
||||
cd "$HOME/projet"
|
||||
etape install_dev ./install_dev.sh || exit 1
|
||||
|
|
|
|||
|
|
@ -124,6 +124,27 @@ describe('fichiers web : la plateforme et sa racine (§ 8.8, § 13.1)', () => {
|
|||
expect(await fs.typeSupport(DOCUMENTS)).toBe('inconnu');
|
||||
});
|
||||
|
||||
// Sous Linux, Chromium convertit le nom d'une entrée de l'OPFS par les
|
||||
// paramètres régionaux de son processus. Hors UTF-8 — LANG absent,
|
||||
// LC_ALL=C —, un nom hors ASCII ne se convertit pas et désigne le dossier
|
||||
// qui le porte : un fichier y lève TypeMismatchError, un dossier y rend son
|
||||
// parent. Les épreuves qui écrivent un nom accentué n'éprouvent alors plus
|
||||
// le module ; celle-ci, sur l'OPFS seul, nomme la cause.
|
||||
test("l'OPFS de ce Chromium nomme une entrée hors ASCII : un fichier et un dossier accentués se créent et se listent sous leur nom", async () => {
|
||||
const racine = await opfs();
|
||||
const cause = 'Chromium tourne sous des paramètres régionaux qui ne sont pas UTF-8 : lancer les épreuves sous LANG=C.UTF-8';
|
||||
let refus = null;
|
||||
try {
|
||||
await racine.getFileHandle('\u{E9}t\u{E9}.txt', { create: true });
|
||||
} catch (erreur) {
|
||||
refus = erreur.name;
|
||||
}
|
||||
expect(refus, cause).toBeNull();
|
||||
const dossier = await racine.getDirectoryHandle('\u{E9}', { create: true });
|
||||
expect(await dossier.isSameEntry(racine), cause).toBe(false);
|
||||
expect(await nomsOpfs(), cause).toStrictEqual(['\u{E9}', '\u{E9}t\u{E9}.txt']);
|
||||
});
|
||||
|
||||
test("la racine documents est le dossier gestion_table_tournante_libre de l'OPFS : ce qui s'y écrit s'y lit en UTF-8 sans marque d'ordre d'octets, et ce qui s'y pose se lit et se liste", async () => {
|
||||
const fs = await preparer();
|
||||
await fs.creerDossier(DOCUMENTS, 'corbeille');
|
||||
|
|
|
|||
|
|
@ -57,18 +57,23 @@ function dossierTemporaire() {
|
|||
}
|
||||
|
||||
// test du lanceur, dont chaque épreuve retire à sa fin les dossiers
|
||||
// temporaires qu'elle a ouverts.
|
||||
// temporaires qu'elle a ouverts ; le corps reçoit le contexte de l'épreuve.
|
||||
function testNettoye(nom, ...suite) {
|
||||
const corps = suite.pop();
|
||||
test(nom, ...suite, async () => {
|
||||
test(nom, ...suite, async (contexte) => {
|
||||
try {
|
||||
await corps();
|
||||
await corps(contexte);
|
||||
} finally {
|
||||
for (const dossier of ouverts.splice(0)) rmSync(dossier, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Ce que dit une commande lancée par spawnSync : l'exception qui l'a
|
||||
// empêchée de démarrer, sinon sa sortie d'erreur. Toujours une chaîne,
|
||||
// seul message que node:assert accepte.
|
||||
const ditPar = (lancee) => String(lancee.error?.message ?? lancee.stderr ?? '');
|
||||
|
||||
// Charge electron/preload.cjs comme un rendu en bac à sable, contre un
|
||||
// electron dont invoke porte chaque appel au gestionnaire que servir a posé
|
||||
// sur ce canal ; arguments et réponse sont copiés comme l'IPC les copie, et
|
||||
|
|
@ -350,7 +355,14 @@ describe('coquille sur un vrai disque', () => {
|
|||
testNettoye(
|
||||
"la lecture par ps (macOS) passe TZ=UTC et LC_ALL=C à la commande : le démarrage inscrit est en temps universel, et un processus d'un autre fuseau lit la séance vivante",
|
||||
{ skip: SOUS_WINDOWS },
|
||||
async () => {
|
||||
async (contexte) => {
|
||||
// L'épreuve joue macOS avec le ps de l'hôte. Sous Linux, la coquille
|
||||
// lit /proc et ne demande pas ps, qu'une image de système minimale ne
|
||||
// porte pas : sans lui, l'épreuve se saute et dit pourquoi.
|
||||
if (spawnSync('ps', ['-p', String(process.pid)]).error?.code === 'ENOENT') {
|
||||
contexte.skip("ps introuvable sur ce système : la lecture du démarrage par ps ne s'y éprouve pas");
|
||||
return;
|
||||
}
|
||||
const documents = join(dossierTemporaire(), 'Documents');
|
||||
mkdirSync(documents, { recursive: true });
|
||||
const fichiers = creerFichiers({
|
||||
|
|
@ -366,7 +378,7 @@ describe('coquille sur un vrai disque', () => {
|
|||
env: { ...process.env, LC_ALL: 'C', TZ: 'UTC' },
|
||||
encoding: 'utf8',
|
||||
});
|
||||
assert.equal(enUtc.status, 0, enUtc.stderr);
|
||||
assert.equal(enUtc.status, 0, ditPar(enUtc));
|
||||
assert.equal(pose.demarrage, enUtc.stdout.trim());
|
||||
|
||||
// Un second processus, dans un fuseau qui n'est pas celui de
|
||||
|
|
@ -387,7 +399,7 @@ describe('coquille sur un vrai disque', () => {
|
|||
},
|
||||
encoding: 'utf8',
|
||||
});
|
||||
assert.equal(relu.status, 0, relu.stderr);
|
||||
assert.equal(relu.status, 0, ditPar(relu));
|
||||
assert.deepEqual(JSON.parse(relu.stdout), {
|
||||
fuseau: autre,
|
||||
tenu: { pris: false, seance: 'seance-a', depuis: pose.depuis, vivant: true },
|
||||
|
|
|
|||
Loading…
Reference in a new issue