181 lines
8.1 KiB
JavaScript
181 lines
8.1 KiB
JavaScript
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|||
|
|
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|||
|
|
|
|||
|
|
// Empreintes de référence du moteur, série node (§ 5.7, § 14.10, § 15.5) : la
|
|||
|
|
// petite démonstration et sa variante au conflit inévitable, sous les trois
|
|||
|
|
// graines et le compte réduit de test/empreintes_moteur.js, et la petite sous
|
|||
|
|
// chacune des seize combinaisons des quatre cases ; les deux encore sous le
|
|||
|
|
// compte plus long de la section bascule, où l'ordre d'acceptation qui suit
|
|||
|
|
// l'annulation de l'écart laisse sa trace dans le plan. Chaque plan rendu par
|
|||
|
|
// rechercher se compare, empreinte et grandeurs nommées, au fichier versionné
|
|||
|
|
// test/fixtures/moteur/empreintes_defaut.json : une empreinte qui change est
|
|||
|
|
// un défaut, jamais une régénération. Le fichier ne se récrit que sous
|
|||
|
|
// GTT_EMPREINTES=ecrire, par cette seule épreuve, qui recalcule alors toutes
|
|||
|
|
// les sections — la grande et le classement compris — avant de les relire.
|
|||
|
|
// La grande et sa variante sans exception se vérifient dans la série
|
|||
|
|
// node-long (empreintes.long.test.js), le classement par evaluer dans
|
|||
|
|
// src/application/empreintes.test.js.
|
|||
|
|
import assert from 'node:assert/strict';
|
|||
|
|
import { describe, test } from '../../test/lanceur.js';
|
|||
|
|
import {
|
|||
|
|
ARRET,
|
|||
|
|
BASCULE,
|
|||
|
|
CASES,
|
|||
|
|
CLASSEMENT,
|
|||
|
|
GRAINES,
|
|||
|
|
GRANDEURS,
|
|||
|
|
combinaisons,
|
|||
|
|
differences,
|
|||
|
|
ecrireEmpreintes,
|
|||
|
|
empreinteDe,
|
|||
|
|
empreinteDuPlan,
|
|||
|
|
empreintesDesCombinaisons,
|
|||
|
|
empreintesDesPlans,
|
|||
|
|
lireEmpreintes,
|
|||
|
|
modeEmpreintes,
|
|||
|
|
} from '../../test/empreintes_moteur.js';
|
|||
|
|
import { CATALOGUE } from '../demo/catalogue.js';
|
|||
|
|
import { indexerPlan, normaliser } from './configuration.js';
|
|||
|
|
import { HISTORIQUE_PAR_DEFAUT, rechercher } from './recherche.js';
|
|||
|
|
|
|||
|
|
if (modeEmpreintes(process.env) === 'ecrire') ecrireEmpreintes();
|
|||
|
|
const REFERENCE = lireEmpreintes();
|
|||
|
|
|
|||
|
|
// Les configurations de cette série, dans l'ordre du catalogue.
|
|||
|
|
const PETITES = ['petite', 'petite-conflit'];
|
|||
|
|
|
|||
|
|
describe('empreintes de référence : la petite démonstration et sa variante (§ 14.10)', () => {
|
|||
|
|
for (const cle of PETITES) {
|
|||
|
|
test(`${cle} : les plans des graines ${GRAINES.join(', ')}, à ${ARRET} mouvements, sont ceux de la référence`, () => {
|
|||
|
|
const calcule = empreintesDesPlans(cle);
|
|||
|
|
assert.deepEqual(differences(cle, calcule, REFERENCE.plans[cle]), []);
|
|||
|
|
assert.deepEqual(calcule, REFERENCE.plans[cle], cle);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
test('petite : sous chacune des seize combinaisons des quatre cases, graine 1, le plan est celui de la référence', () => {
|
|||
|
|
const calcule = empreintesDesCombinaisons();
|
|||
|
|
assert.equal(Object.keys(REFERENCE.combinaisons).length, 16);
|
|||
|
|
assert.deepEqual(differences('petite', calcule, REFERENCE.combinaisons), []);
|
|||
|
|
assert.deepEqual(calcule, REFERENCE.combinaisons);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
for (const cle of BASCULE.cles) {
|
|||
|
|
test(`${cle} : à ${BASCULE.arret} mouvements, où la meilleure affectation s’améliore après la bascule, les plans sont ceux de la référence`, () => {
|
|||
|
|
const calcule = empreintesDesPlans(cle, BASCULE.arret);
|
|||
|
|
assert.deepEqual(differences(`${cle} (bascule)`, calcule, REFERENCE.bascule[cle]), []);
|
|||
|
|
assert.deepEqual(calcule, REFERENCE.bascule[cle], cle);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
test('la référence a été relevée sous les réglages de test/empreintes_moteur.js', () => {
|
|||
|
|
assert.deepEqual(REFERENCE.reglages, {
|
|||
|
|
graines: [...GRAINES],
|
|||
|
|
arret: ARRET,
|
|||
|
|
historique: HISTORIQUE_PAR_DEFAUT,
|
|||
|
|
cases: [...CASES],
|
|||
|
|
classement: { ...CLASSEMENT, cles: [...CLASSEMENT.cles] },
|
|||
|
|
bascule: { ...BASCULE, cles: [...BASCULE.cles] },
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('la référence couvre les quatre configurations du catalogue, chacune sous chaque graine', () => {
|
|||
|
|
assert.deepEqual(
|
|||
|
|
Object.keys(REFERENCE.plans),
|
|||
|
|
CATALOGUE.map(({ cle }) => cle),
|
|||
|
|
);
|
|||
|
|
for (const { cle } of CATALOGUE) {
|
|||
|
|
assert.deepEqual(Object.keys(REFERENCE.plans[cle]), GRAINES.map(String), cle);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('empreintes de référence : ce que porte une empreinte', () => {
|
|||
|
|
// Un plan de la petite variante et son instance, la configuration du
|
|||
|
|
// catalogue sous la graine 1.
|
|||
|
|
function unPlan() {
|
|||
|
|
const configuration = CATALOGUE.find(({ cle }) => cle === 'petite-conflit').construire();
|
|||
|
|
const [{ plan }] = rechercher(configuration, { graine: 1, arret: 2_000, nombre: 1 });
|
|||
|
|
return { instance: normaliser(configuration), plan };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
test('les grandeurs nommées sont les six du § 14.10, dans cet ordre, et aucune composante du n-uplet', () => {
|
|||
|
|
assert.deepEqual(GRANDEURS, [
|
|||
|
|
'ecartAuPlafondAPrioriMax',
|
|||
|
|
'excedentCollisions',
|
|||
|
|
'collisionsCumulees',
|
|||
|
|
'rencontresRepeteesChoisies',
|
|||
|
|
'retoursChoisis',
|
|||
|
|
'redondance',
|
|||
|
|
]);
|
|||
|
|
const { instance, plan } = unPlan();
|
|||
|
|
assert.deepEqual(Object.keys(empreinteDuPlan(instance, plan).grandeurs), GRANDEURS);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('deux plans qui ne diffèrent que par la table d’une personne à un tour ont deux empreintes', () => {
|
|||
|
|
const { instance, plan } = unPlan();
|
|||
|
|
const avant = empreinteDuPlan(instance, plan);
|
|||
|
|
// La première personne des deux premières tables du tour 1 échange sa
|
|||
|
|
// place : les grandeurs peuvent rester égales, le plan ne l'est pas.
|
|||
|
|
const tours = plan.tours.map((tour) => tour.map((liste) => [...liste]));
|
|||
|
|
const [a] = tours[0][0];
|
|||
|
|
const [b] = tours[0][1];
|
|||
|
|
tours[0][0][0] = b;
|
|||
|
|
tours[0][1][0] = a;
|
|||
|
|
const apres = empreinteDuPlan(instance, { ...plan, tours });
|
|||
|
|
assert.notEqual(apres.empreinte, avant.empreinte);
|
|||
|
|
assert.notDeepEqual(indexerPlan(instance, { ...plan, tours }), indexerPlan(instance, plan));
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('l’empreinte lit chaque grandeur sous son nom : une grandeur changée change l’empreinte', () => {
|
|||
|
|
const { instance, plan } = unPlan();
|
|||
|
|
const { empreinte, grandeurs } = empreinteDuPlan(instance, plan);
|
|||
|
|
const tableDe = indexerPlan(instance, plan);
|
|||
|
|
for (const nom of GRANDEURS) {
|
|||
|
|
const autre = { ...grandeurs, [nom]: grandeurs[nom] + 1 };
|
|||
|
|
assert.notEqual(empreinteDe(tableDe, autre), empreinte, nom);
|
|||
|
|
}
|
|||
|
|
assert.equal(empreinteDe(tableDe, grandeurs), empreinte);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('les seize combinaisons sont distinctes, chacune nommée par ses cases cochées', () => {
|
|||
|
|
const toutes = combinaisons();
|
|||
|
|
assert.equal(toutes.length, 16);
|
|||
|
|
assert.equal(new Set(toutes.map(({ cle }) => cle)).size, 16);
|
|||
|
|
assert.deepEqual(toutes[0], { cle: CASES.join('+'), contraintes: Object.fromEntries(CASES.map((c) => [c, true])) });
|
|||
|
|
assert.deepEqual(toutes.at(-1), { cle: 'aucune', contraintes: Object.fromEntries(CASES.map((c) => [c, false])) });
|
|||
|
|
for (const { cle, contraintes } of toutes) {
|
|||
|
|
assert.deepEqual(Object.keys(contraintes), CASES, cle);
|
|||
|
|
const cochees = CASES.filter((c) => contraintes[c]);
|
|||
|
|
assert.equal(cle, cochees.length === 0 ? 'aucune' : cochees.join('+'));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('differences nomme la configuration, la graine absente de la référence et la grandeur changée', () => {
|
|||
|
|
const attendu = { 1: { empreinte: 'a', grandeurs: { excedentCollisions: 2, redondance: 5 } } };
|
|||
|
|
assert.deepEqual(differences('petite', attendu, attendu), []);
|
|||
|
|
assert.deepEqual(
|
|||
|
|
differences('petite', { 4: attendu[1] }, attendu),
|
|||
|
|
['petite, 4 : absente de la référence', 'petite, 1 : référence non recalculée'],
|
|||
|
|
);
|
|||
|
|
assert.deepEqual(
|
|||
|
|
differences('petite', { 1: { empreinte: 'b', grandeurs: { excedentCollisions: 3, redondance: 5 } } }, attendu),
|
|||
|
|
['petite, 1 : excedentCollisions 2 → 3'],
|
|||
|
|
);
|
|||
|
|
assert.deepEqual(
|
|||
|
|
differences('petite', { 1: { empreinte: 'b', grandeurs: attendu[1].grandeurs } }, attendu),
|
|||
|
|
['petite, 1 : plan changé, grandeurs égales'],
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('GTT_EMPREINTES : verifier par défaut, ecrire sur demande', () => {
|
|||
|
|
test('absente ou vide : verifier ; verifier et ecrire se lisent tels quels ; une autre valeur lève en la nommant', () => {
|
|||
|
|
assert.equal(modeEmpreintes({}), 'verifier');
|
|||
|
|
assert.equal(modeEmpreintes({ GTT_EMPREINTES: '' }), 'verifier');
|
|||
|
|
assert.equal(modeEmpreintes({ GTT_EMPREINTES: 'verifier' }), 'verifier');
|
|||
|
|
assert.equal(modeEmpreintes({ GTT_EMPREINTES: 'ecrire' }), 'ecrire');
|
|||
|
|
assert.throws(() => modeEmpreintes({ GTT_EMPREINTES: 'ecrir' }), /GTT_EMPREINTES.*« ecrir »/);
|
|||
|
|
});
|
|||
|
|
});
|