[IMP] tests: watched series adapts to the core count, threads below 4
Below four cores the runner carried the watched series in one process, and a relaunch after a base module took 2.5 to 3 s, over the 2 s budget of § 14.14. The configuration now reads the cores the process may use: from four, full processes and the whole series, as before; below, threads on every core, and the end-to-end test moves to the long series. No test disappears or changes behaviour. A guard refuses, in these series, what a thread refuses and a process accepts; the reporter names the mode. Checked: relaunch on 2 cores 1.0 to 1.3 s, was 2.5 to 3.0; on 4 cores 1.2 to 1.5 s; process.env is copied per thread, measured. --- FR --- [IMP] épreuves : la série surveillée suit les cœurs, threads sous 4 Sous quatre cœurs, le lanceur portait la série surveillée dans un seul processus, et une relance après un module de base prenait 2,5 à 3 s, au-delà du budget de 2 s du § 14.14. La configuration lit les cœurs que le processus peut occuper : à partir de quatre, processus complets et série entière, comme avant ; en deçà, threads sur tous les cœurs, et l'épreuve de bout en bout passe dans la série longue. Aucune épreuve ne disparaît ni ne change. Une garde refuse dans ces séries ce qu'un thread refuse et qu'un processus accepte ; le rapporteur nomme le mode. Vérifié : relance sur 2 cœurs en 1,0 à 1,3 s, contre 2,5 à 3,0 ; sur 4 cœurs en 1,2 à 1,5 s ; process.env est copié par thread, mesuré. Assisted-by: Claude Opus 5.5
This commit is contained in:
parent
1910e207cb
commit
de46c582f4
7 changed files with 320 additions and 12 deletions
15
spec.md
15
spec.md
|
|
@ -3494,11 +3494,16 @@ couverture :
|
|||
| relance sous surveillance, après une modification | **2 secondes** | le développeur commence à grouper ses modifications, et le test cesse de désigner laquelle a cassé |
|
||||
| série `node` surveillée complète, à froid | **10 secondes** | le développeur change de fenêtre ; le retour de contexte coûte plus que l'épreuve, et le niveau glisse vers « avant un commit » |
|
||||
|
||||
**Le budget se mesure sur une machine de développement d'au moins quatre
|
||||
cœurs.** Sur deux, le lanceur ne porte la série que dans un seul processus :
|
||||
la relance qui suit la modification d'un module de base — celui que presque
|
||||
toutes les épreuves importent — dépasse deux secondes, et ce dépassement y est
|
||||
accepté. Le budget à froid de dix secondes, lui, y tient.
|
||||
**La série surveillée s'adapte au nombre de cœurs, et le budget tient sur toute
|
||||
machine.** À partir de quatre cœurs, des processus complets portent la série
|
||||
entière. En deçà, le lanceur n'en porterait la série que dans un seul
|
||||
processus, et la relance qui suit un module de base — celui que presque toutes
|
||||
les épreuves importent — dépasserait deux secondes. La série s'y joue donc en
|
||||
threads sur tous les cœurs, et ses épreuves lourdes passent dans la série
|
||||
`node:long`. Aucune épreuve ne disparaît : elle change de série, et ce qu'elle
|
||||
éprouve ne dépend pas de la machine. Une garde refuse, dans ces séries, ce
|
||||
qu'un thread refuserait et qu'un processus accepte, et le lanceur annonce à
|
||||
chaque exécution le mode qu'il a retenu.
|
||||
|
||||
**Pourquoi ces deux valeurs.** En deçà de deux secondes, l'attention reste sur le
|
||||
code et le résultat se lit comme la suite du geste. Au-delà de dix, elle part
|
||||
|
|
|
|||
123
test/fils.test.js
Normal file
123
test/fils.test.js
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Sur une petite machine, les séries node et node-long se jouent en threads
|
||||
// (test/machine.js). Un thread de Node refuse ce qui changerait le processus
|
||||
// entier — process.chdir, process.umask avec un masque, les changements
|
||||
// d'identité — là où un processus complet l'accepte : une épreuve qui s'en
|
||||
// servirait passerait sur une grande machine et échouerait sur une petite.
|
||||
// La garde les refuse dans tout le code que ces séries exécutent : les
|
||||
// épreuves de src/ et de scripts/, les modules sous test/, et les scripts
|
||||
// qu'elles importent. L'environnement, lui, est copié dans chaque thread :
|
||||
// une épreuve qui écrit process.env ne trouble pas sa voisine, et la garde ne
|
||||
// le refuse pas.
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { dirname, join, relative } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, test } from './lanceur.js';
|
||||
|
||||
const RACINE = fileURLToPath(new URL('..', import.meta.url));
|
||||
|
||||
// Appels qu'un thread de Node refuse. Un motif exige la parenthèse de
|
||||
// l'appel : une phrase qui nomme process.chdir n'en est pas un. umask sans
|
||||
// argument lit le masque, ce qu'un thread accepte.
|
||||
const REFUSES_DANS_UN_THREAD = [
|
||||
['process.chdir', /\bprocess\s*\??\.\s*chdir\s*(?:\?\.)?\s*\(/g],
|
||||
['process.umask(masque)', /\bprocess\s*\??\.\s*umask\s*(?:\?\.)?\s*\(\s*[^)\s]/g],
|
||||
['process.set…id', /\bprocess\s*\??\.\s*(?:set(?:e?[ug]id|groups)|initgroups)\s*(?:\?\.)?\s*\(/g],
|
||||
];
|
||||
|
||||
// Ce fichier nomme ces appels dans ses motifs et dans ses données d'épreuve :
|
||||
// il est hors de son propre balayage.
|
||||
const CE_FICHIER = fileURLToPath(import.meta.url);
|
||||
|
||||
// Fichiers JavaScript que les séries node exécutent : les épreuves de src/ et
|
||||
// de scripts/, hors épreuves du navigateur, tout module sous test/, et les
|
||||
// scripts de scripts/, que des épreuves importent.
|
||||
function fichiersExecutes(racine) {
|
||||
const sous = (dossier) => {
|
||||
try {
|
||||
return readdirSync(join(racine, dossier), { recursive: true, withFileTypes: true })
|
||||
.filter((e) => e.isFile() && /\.[cm]?js$/.test(e.name))
|
||||
.map((e) => join(e.parentPath, e.name));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
return [
|
||||
...sous('src').filter((f) => /\.test\.[cm]?js$/.test(f) && !/\.navigateur\.test\.[cm]?js$/.test(f)),
|
||||
...sous('scripts'),
|
||||
...sous('test').filter((f) => !f.includes(`${join('test', 'fixtures')}`) && f !== CE_FICHIER),
|
||||
].sort();
|
||||
}
|
||||
|
||||
// Relevé « fichier:ligne appel » de ces appels, par fichier puis par ligne.
|
||||
// Lève quand aucun fichier n'est examiné (§ 14.2).
|
||||
function releverAppelsRefuses(racine) {
|
||||
const fichiers = fichiersExecutes(racine);
|
||||
assert.ok(fichiers.length > 0, 'aucun fichier examiné');
|
||||
return fichiers.flatMap((fichier) => {
|
||||
const texte = readFileSync(fichier, 'utf8');
|
||||
return REFUSES_DANS_UN_THREAD.flatMap(([appel, motif]) =>
|
||||
[...texte.matchAll(motif)].map(({ index }) => ({ ligne: texte.slice(0, index).split('\n').length, appel })),
|
||||
)
|
||||
.sort((a, b) => a.ligne - b.ligne)
|
||||
.map(({ ligne, appel }) => `${relative(racine, fichier)}:${ligne} ${appel}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Écrit les fichiers donnés dans une racine temporaire, rend le relevé, et
|
||||
// efface la racine.
|
||||
function avecArbre(fichiers) {
|
||||
const racine = mkdtempSync(join(tmpdir(), 'fils-'));
|
||||
try {
|
||||
for (const [chemin, texte] of Object.entries(fichiers)) {
|
||||
mkdirSync(dirname(join(racine, chemin)), { recursive: true });
|
||||
writeFileSync(join(racine, chemin), texte);
|
||||
}
|
||||
return releverAppelsRefuses(racine);
|
||||
} finally {
|
||||
rmSync(racine, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
describe('fils : ce qu’un thread refuse, hors des séries node (test/machine.js)', () => {
|
||||
test('aucune épreuve, aucun module sous test/ ni aucun script n’appelle ce qu’un thread refuse', () => {
|
||||
assert.deepEqual(releverAppelsRefuses(RACINE), []);
|
||||
});
|
||||
|
||||
test('la garde relève chaque appel par sa ligne, dans les épreuves, les modules sous test/ et les scripts, et pas ses voisins', () => {
|
||||
const fichiers = {
|
||||
'src/moteur/a.test.js': [
|
||||
"process.chdir('/tmp');",
|
||||
'process.umask(0o022);',
|
||||
'process.setuid(1000);',
|
||||
'process?.chdir?.(ici);',
|
||||
].join('\n'),
|
||||
'scripts/outil.js': 'process.setgroups([]);\n',
|
||||
'test/aide.js': 'process.initgroups("x", 1);\n',
|
||||
'src/moteur/voisins.test.js': [
|
||||
'const ici = process.cwd();',
|
||||
'const masque = process.umask();',
|
||||
"process.env.SONDE = 'copie par thread';",
|
||||
'// process.chdir changerait le processus entier.',
|
||||
].join('\n'),
|
||||
'src/moteur/module.js': "process.chdir('/tmp');\n",
|
||||
'src/interface/App.navigateur.test.js': "process.chdir('/tmp');\n",
|
||||
};
|
||||
assert.deepEqual(avecArbre(fichiers), [
|
||||
'scripts/outil.js:1 process.set…id',
|
||||
'src/moteur/a.test.js:1 process.chdir',
|
||||
'src/moteur/a.test.js:2 process.umask(masque)',
|
||||
'src/moteur/a.test.js:3 process.set…id',
|
||||
'src/moteur/a.test.js:4 process.chdir',
|
||||
'test/aide.js:1 process.set…id',
|
||||
]);
|
||||
});
|
||||
|
||||
test('un arbre sans fichier exécuté par ces séries fait échouer la garde', () => {
|
||||
assert.throws(() => avecArbre({ 'src/moteur/module.js': 'export const a = 1;\n' }), /aucun fichier examiné/);
|
||||
});
|
||||
});
|
||||
61
test/machine.js
Normal file
61
test/machine.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Réglages des séries node selon le nombre de cœurs de la machine (§ 14.14).
|
||||
//
|
||||
// Le budget de relance, deux secondes, tient avec des processus complets à
|
||||
// partir de quatre cœurs. En deçà, le lanceur ne porte la série que dans un
|
||||
// seul processus, et la relance qui suit un module de base — que presque
|
||||
// toutes les épreuves importent — dépasse ce budget : de l'ordre de trois
|
||||
// secondes sur deux cœurs. Une petite machine prend donc des threads, moins
|
||||
// coûteux à démarrer qu'un processus, sur tous ses cœurs, et les épreuves
|
||||
// lourdes de la série surveillée passent dans la série longue : la même
|
||||
// relance y tient alors sous une seconde et demie.
|
||||
//
|
||||
// Aucune épreuve ne disparaît : elle change de série, et ce qu'elle éprouve
|
||||
// ne dépend pas de la machine. Les threads partagent un processus : la garde
|
||||
// de test/fils.test.js tient la série node à l'écart de l'état global du
|
||||
// processus, sans quoi une épreuve en troublerait une autre.
|
||||
import { availableParallelism } from 'node:os';
|
||||
|
||||
// Nombre de cœurs à partir duquel la série surveillée garde des processus
|
||||
// complets et toutes ses épreuves.
|
||||
export const SEUIL_COEURS = 4;
|
||||
|
||||
// Épreuves de la série surveillée qui passent dans node-long sur une petite
|
||||
// machine. L'épreuve de bout en bout lance de vraies recherches : c'est la
|
||||
// plus lourde de la série, et toute modification du moteur la rejoue.
|
||||
export const LOURDES = Object.freeze(['src/moteur/integration.test.js']);
|
||||
|
||||
/**
|
||||
* Réglages des projets node et node-long pour un nombre de cœurs.
|
||||
*
|
||||
* @param {number} coeurs entier ≥ 1
|
||||
* @returns {{petiteMachine: boolean, coeurs: number, pool: 'forks'|'threads',
|
||||
* maxWorkers: number|undefined, horsSerieSurveillee: string[]}}
|
||||
* maxWorkers undefined laisse au lanceur son défaut
|
||||
*/
|
||||
export function reglagesSeries(coeurs) {
|
||||
if (!Number.isInteger(coeurs) || coeurs < 1) {
|
||||
throw new RangeError(`nombre de cœurs ${String(coeurs)} : entier ≥ 1 attendu`);
|
||||
}
|
||||
if (coeurs >= SEUIL_COEURS) {
|
||||
return { petiteMachine: false, coeurs, pool: 'forks', maxWorkers: undefined, horsSerieSurveillee: [] };
|
||||
}
|
||||
return { petiteMachine: true, coeurs, pool: 'threads', maxWorkers: coeurs, horsSerieSurveillee: [...LOURDES] };
|
||||
}
|
||||
|
||||
// Cœurs que ce processus peut occuper. availableParallelism suit l'affinité
|
||||
// du processus : sous « taskset -c 0,1 », il rend 2 sur une machine qui en a
|
||||
// seize, ce qui permet d'éprouver le mode d'une petite machine sur une grande.
|
||||
export const coeursDisponibles = () => availableParallelism();
|
||||
|
||||
// Ligne que le rapporteur imprime, pour qu'un développeur sache sur quelle
|
||||
// série sa relance a porté.
|
||||
export function ligneMachine({ petiteMachine, coeurs, horsSerieSurveillee }) {
|
||||
const compte = coeurs === 1 ? '1 cœur' : `${coeurs} cœurs`;
|
||||
if (!petiteMachine) return `Machine : ${compte} — processus complets, série surveillée entière.`;
|
||||
const ou = coeurs === 1 ? 'sur son seul cœur' : `sur les ${coeurs} cœurs`;
|
||||
return `Machine : ${compte}, en deçà de ${SEUIL_COEURS} — threads ${ou} ; `
|
||||
+ `en série longue : ${horsSerieSurveillee.join(', ')}.`;
|
||||
}
|
||||
104
test/machine.test.js
Normal file
104
test/machine.test.js
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Les réglages des séries node selon le nombre de cœurs (§ 14.14), et leur
|
||||
// application dans vitest.config.js.
|
||||
import assert from 'node:assert/strict';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { availableParallelism } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import configuration from '../vitest.config.js';
|
||||
import { describe, test } from './lanceur.js';
|
||||
import { LOURDES, SEUIL_COEURS, coeursDisponibles, ligneMachine, reglagesSeries } from './machine.js';
|
||||
|
||||
const RACINE = fileURLToPath(new URL('..', import.meta.url));
|
||||
const projet = (nom) => configuration.test.projects.find(({ test: { name } }) => name === nom).test;
|
||||
|
||||
describe('machine : les réglages selon le nombre de cœurs (§ 14.14)', () => {
|
||||
test('à partir de quatre cœurs, des processus complets et la série surveillée entière', () => {
|
||||
for (const coeurs of [SEUIL_COEURS, 8, 16, 64]) {
|
||||
assert.deepEqual(reglagesSeries(coeurs), {
|
||||
petiteMachine: false,
|
||||
coeurs,
|
||||
pool: 'forks',
|
||||
maxWorkers: undefined,
|
||||
horsSerieSurveillee: [],
|
||||
}, `${coeurs} cœurs`);
|
||||
}
|
||||
});
|
||||
|
||||
test('en deçà de quatre cœurs, des threads sur tous les cœurs, et les épreuves lourdes en série longue', () => {
|
||||
for (const coeurs of [1, 2, 3]) {
|
||||
assert.deepEqual(reglagesSeries(coeurs), {
|
||||
petiteMachine: true,
|
||||
coeurs,
|
||||
pool: 'threads',
|
||||
maxWorkers: coeurs,
|
||||
horsSerieSurveillee: [...LOURDES],
|
||||
}, `${coeurs} cœurs`);
|
||||
}
|
||||
});
|
||||
|
||||
test('un nombre de cœurs qui n’est pas un entier ≥ 1 lève RangeError', () => {
|
||||
for (const coeurs of [0, -1, 2.5, Number.NaN, '4', undefined]) {
|
||||
assert.throws(() => reglagesSeries(coeurs), RangeError, String(coeurs));
|
||||
}
|
||||
});
|
||||
|
||||
test('les épreuves lourdes existent, sont de la série surveillée, et la liste n’est pas vide', () => {
|
||||
assert.ok(LOURDES.length > 0, 'aucune épreuve lourde nommée');
|
||||
for (const fichier of LOURDES) {
|
||||
assert.ok(existsSync(join(RACINE, fichier)), `${fichier} absent`);
|
||||
assert.match(fichier, /\.test\.js$/);
|
||||
assert.doesNotMatch(fichier, /\.(long|navigateur)\.test\.js$/, `${fichier} n'est pas de la série surveillée`);
|
||||
}
|
||||
});
|
||||
|
||||
test('les cœurs disponibles suivent l’affinité du processus, comme availableParallelism', () => {
|
||||
assert.equal(coeursDisponibles(), availableParallelism());
|
||||
assert.ok(coeursDisponibles() >= 1);
|
||||
});
|
||||
|
||||
test('la ligne annoncée dit le mode, sur une petite machine comme sur une grande', () => {
|
||||
assert.equal(
|
||||
ligneMachine(reglagesSeries(2)),
|
||||
'Machine : 2 cœurs, en deçà de 4 — threads sur les 2 cœurs ; '
|
||||
+ 'en série longue : src/moteur/integration.test.js.',
|
||||
);
|
||||
assert.equal(
|
||||
ligneMachine(reglagesSeries(1)),
|
||||
'Machine : 1 cœur, en deçà de 4 — threads sur son seul cœur ; '
|
||||
+ 'en série longue : src/moteur/integration.test.js.',
|
||||
);
|
||||
assert.equal(ligneMachine(reglagesSeries(16)), 'Machine : 16 cœurs — processus complets, série surveillée entière.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('machine : vitest.config.js applique les réglages de cette machine', () => {
|
||||
const reglages = reglagesSeries(coeursDisponibles());
|
||||
|
||||
test('les projets node et node-long prennent le pool et le nombre de processus retenus', () => {
|
||||
for (const nom of ['node', 'node-long']) {
|
||||
assert.equal(projet(nom).pool, reglages.pool, nom);
|
||||
assert.equal(projet(nom).maxWorkers, reglages.maxWorkers, nom);
|
||||
}
|
||||
});
|
||||
|
||||
test('une épreuve hors de la série surveillée entre dans la série longue, et nulle part ailleurs', () => {
|
||||
for (const fichier of reglages.horsSerieSurveillee) {
|
||||
assert.ok(projet('node').exclude.includes(fichier), `${fichier} reste dans node`);
|
||||
assert.ok(projet('node-long').include.includes(fichier), `${fichier} manque à node-long`);
|
||||
}
|
||||
for (const fichier of LOURDES.filter((f) => !reglages.horsSerieSurveillee.includes(f))) {
|
||||
assert.ok(!projet('node').exclude.includes(fichier), `${fichier} sort de node sur cette machine`);
|
||||
assert.ok(!projet('node-long').include.includes(fichier), `${fichier} entre dans node-long sur cette machine`);
|
||||
}
|
||||
});
|
||||
|
||||
test('le projet navigateur ne prend aucun de ces réglages', () => {
|
||||
const navigateur = projet('navigateur');
|
||||
assert.equal(navigateur.pool, undefined);
|
||||
assert.equal(navigateur.maxWorkers, undefined);
|
||||
});
|
||||
});
|
||||
|
|
@ -20,7 +20,10 @@
|
|||
// n'a pas vu prendre rend la durée de son projet inconnue, et un projet à
|
||||
// plafond refuse alors au lieu de passer sans mesure (§ 14.2). Une exécution
|
||||
// interrompue imprime son bilan sans rien refuser : ses durées sont
|
||||
// partielles.
|
||||
// partielles. Le bilan finit sur le mode que la machine a fait retenir
|
||||
// (test/machine.js) : un développeur sait ainsi sur quelle série sa relance a
|
||||
// porté.
|
||||
import { coeursDisponibles, ligneMachine, reglagesSeries } from './machine.js';
|
||||
|
||||
// Plafond au mur de chaque projet, en millisecondes, par nom d'exécution du
|
||||
// projet : celui de vitest.config.js, que Vitest fait suivre du navigateur
|
||||
|
|
@ -29,8 +32,8 @@
|
|||
// chargée, et un test capricieux se désactive.
|
||||
export const PLAFONDS = new Map([
|
||||
// La série node surveillée : trente secondes (§ 14.14). Seule, elle dure
|
||||
// 1,8 s au mur sur 16 cœurs et 6,6 s sur 2 ; la commande entière, lanceur
|
||||
// compris, prend 2,5 s sur 16 cœurs.
|
||||
// 1,8 s au mur sur 16 cœurs et 3,7 s sur 2, où elle se joue en threads
|
||||
// (test/machine.js).
|
||||
['node', 30_000],
|
||||
// Posé sur la mesure de node-long : seule, 17 s au mur sur 16 cœurs et
|
||||
// 28 s sur un seul. Le plafond laisse un facteur 2,1 au cas le plus lent.
|
||||
|
|
@ -182,6 +185,7 @@ export default class Rapporteur {
|
|||
);
|
||||
const { lignes, refus } = bilan({ duree, projets, epreuves, plafonds: this.#plafonds });
|
||||
for (const ligne of lignes) this.#journal.log(ligne);
|
||||
this.#journal.log(ligneMachine(reglagesSeries(coeursDisponibles())));
|
||||
if (raison === 'interrupted') return;
|
||||
for (const message of refus) this.#journal.error(message);
|
||||
if (refus.length > 0) process.exitCode = 1;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import assert from 'node:assert/strict';
|
|||
import configuration from '../vitest.config.js';
|
||||
import Rapporteur, { PLAFONDS, bilan, dureeReunie, plusLentes } from './rapporteur.js';
|
||||
import { describe, test } from './lanceur.js';
|
||||
import { coeursDisponibles, ligneMachine, reglagesSeries } from './machine.js';
|
||||
|
||||
// Un faux fichier d'épreuves, tel que Vitest le passe au rapporteur : son
|
||||
// identifiant, son projet, son chemin, et ses épreuves, chacune
|
||||
|
|
@ -171,6 +172,8 @@ describe('rapporteur : branché sur les événements de Vitest', () => {
|
|||
' 29000 ms node src/b.test.js > b > deux',
|
||||
' 19000 ms node-long src/c.long.test.js > c > trois',
|
||||
' 7 ms node src/a.test.js > a > un',
|
||||
// Le bilan finit sur le mode que cette machine fait retenir.
|
||||
ligneMachine(reglagesSeries(coeursDisponibles())),
|
||||
]);
|
||||
assert.deepEqual(journal.erreurs, ['Le projet node dure 30,80 s au mur, au-delà de son plafond de 30,00 s (§ 14.14).']);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@
|
|||
// d'accord avec la première.
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import { playwright } from '@vitest/browser-playwright';
|
||||
import { coeursDisponibles, reglagesSeries } from './test/machine.js';
|
||||
|
||||
// Les séries node suivent le nombre de cœurs de la machine (test/machine.js) :
|
||||
// en deçà de quatre, des threads sur tous les cœurs, et les épreuves lourdes
|
||||
// de la série surveillée passent dans la série longue (§ 14.14).
|
||||
const SERIES = reglagesSeries(coeursDisponibles());
|
||||
const PROCESSUS = { pool: SERIES.pool, maxWorkers: SERIES.maxWorkers };
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
|
|
@ -50,12 +57,13 @@ export default defineConfig({
|
|||
],
|
||||
projects: [
|
||||
{ test: {
|
||||
name: 'node', environment: 'node',
|
||||
name: 'node', environment: 'node', ...PROCESSUS,
|
||||
include: ['src/**/*.test.js', 'scripts/**/*.test.js', 'test/**/*.test.js'],
|
||||
exclude: ['**/*.long.test.js', '**/*.navigateur.test.js', 'node_modules/**'] } },
|
||||
exclude: ['**/*.long.test.js', '**/*.navigateur.test.js', 'node_modules/**',
|
||||
...SERIES.horsSerieSurveillee] } },
|
||||
{ test: {
|
||||
name: 'node-long', environment: 'node',
|
||||
include: ['src/**/*.long.test.js', 'test/**/*.long.test.js'],
|
||||
name: 'node-long', environment: 'node', ...PROCESSUS,
|
||||
include: ['src/**/*.long.test.js', 'test/**/*.long.test.js', ...SERIES.horsSerieSurveillee],
|
||||
testTimeout: 120_000 } },
|
||||
{ extends: './vite.config.js',
|
||||
test: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue