From c391d13312247831fb1f3684f9609189118d4c7b Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Sat, 17 Oct 2026 01:51:21 +0200 Subject: [PATCH] =?UTF-8?q?test(decisions):=20comparateur=20=C3=A9tendu=20?= =?UTF-8?q?=C3=A0=20purchasedWh=20(--achat)=20et=20liste=20des=20lignes=20?= =?UTF-8?q?(--lignes)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le correctif c change une VALEUR PUBLIÉE (progress.purchasedWh), pas une consigne. --achat retire en plus ce champ de chaque niveau et dit, par scénario et charge, l'ancienne et la nouvelle valeur (ou « absent ») ; --lignes écrit chaque ligne qui change. Sans option, le comportement d'etm70 est inchangé. Co-Authored-By: Claude Opus 5.5 --- .../etm-powersync-compare-hors-motifs.py | 61 +++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/tests/auto/decisions/etm-powersync-compare-hors-motifs.py b/tests/auto/decisions/etm-powersync-compare-hors-motifs.py index 523223f..fd62e18 100755 --- a/tests/auto/decisions/etm-powersync-compare-hors-motifs.py +++ b/tests/auto/decisions/etm-powersync-compare-hors-motifs.py @@ -2,6 +2,7 @@ # Comparateur « IDENTIQUE HORS MOTIFS » (correctifs moteur a et b, etm70). # # etm-powersync-compare-hors-motifs.py [--cas ] +# [--achat] [--lignes ] # # Un lot qui ne change QUE des motifs doit laisser tout le reste de la référence `decisions` # intact à l'octet près. Ce script retire de chaque ligne, et SEULEMENT d'elle : @@ -9,10 +10,14 @@ # ligne de cycle, `plan.timeSlots[].actions[].reason` dans une ligne de plan direct ; # - le manque chiffré d'une clôture non tenue (correctif b) : `params.missingWh` d'un motif # ECO_FLOOR_MISSED porté par un niveau, et `progress.closedMissingWh` du même niveau. +# Avec --achat (correctif c, etm71), il retire EN PLUS `progress.purchasedWh` de chaque niveau : +# l'acheté se compte désormais sur l'énergie mesurée, et il est absent sans mesure. Un lot qui +# change cette VALEUR PUBLIÉE sans toucher aux consignes doit passer ce mode-là. # Tout le reste — consignes, powerW, estimatedPowerW, états, relais, courants, budget, draw, # counts, niveaux et leurs motifs, avancement — doit être IDENTIQUE. Code 0 si c'est le cas, # 1 sinon (et le premier écart est montré). La liste des motifs qui changent est imprimée, ou -# écrite en Markdown avec --cas. +# écrite en Markdown avec --cas ; --lignes écrit le numéro et le contenu de chaque ligne qui +# change (attendu puis obtenu). import json import lzma import sys @@ -34,22 +39,42 @@ def sans_manque(niveau, cle_motif): prog.pop('closedMissingWh', None) -def normaliser(ligne): - """Rend (ligne sans motifs, [(charge, motif retiré)]).""" +ABSENT = '(absent)' + + +def sans_achat(niveau, charge, achats): + prog = niveau.get('progress') + if isinstance(prog, dict): + achats.append((charge, prog.pop('purchasedWh', ABSENT))) + + +def normaliser(ligne, achat=False): + """Rend (ligne sans motifs, [(charge, motif retiré)], [(charge, purchasedWh retiré)]).""" d = json.loads(ligne) retires = [] + achats = [] if d.get('src') == 'cycle': for ch in (d.get('telemetry') or {}).get('loads', []) or []: retires.append((ch.get('loadId'), ch.pop('decision', None))) for n in ch.get('levels', []) or []: sans_manque(n, 'decision') + if achat: + sans_achat(n, ch.get('loadId'), achats) elif d.get('src') == 'plan': for s in (d.get('plan') or {}).get('timeSlots', []) or []: for a in s.get('actions', []) or []: retires.append((a.get('loadId'), a.pop('reason', None))) for n in a.get('levels', []) or []: sans_manque(n, 'reason') - return json.dumps(d, sort_keys=True, ensure_ascii=False), retires + if achat: + sans_achat(n, a.get('loadId'), achats) + return json.dumps(d, sort_keys=True, ensure_ascii=False), retires, achats + + +def achat_court(v): + if isinstance(v, (int, float)): + return f'{round(v, 2):g}' + return str(v) def motif_court(m): @@ -68,6 +93,9 @@ def main(): return 2 attendu, obtenu = lire(sys.argv[1]), lire(sys.argv[2]) sortie_cas = sys.argv[sys.argv.index('--cas') + 1] if '--cas' in sys.argv else None + sortie_lignes = sys.argv[sys.argv.index('--lignes') + 1] if '--lignes' in sys.argv else None + achat = '--achat' in sys.argv + lignes_changees = [] if len(attendu) != len(obtenu): print(f'NOMBRE DE LIGNES différent : {len(attendu)} attendues, {len(obtenu)} obtenues') @@ -79,10 +107,11 @@ def main(): if a == b: brutes_identiques += 1 continue - na, ra = normaliser(a) - nb, rb = normaliser(b) + na, ra, aa = normaliser(a, achat) + nb, rb, ab = normaliser(b, achat) + lignes_changees.append((i, a, b)) if na != nb: - print(f'ÉCART HORS MOTIFS à la ligne {i} :') + print(f'ÉCART HORS MOTIFS{" ET HORS ACHAT" if achat else ""} à la ligne {i} :') print('- ' + a[:3000]) print('+ ' + b[:3000]) return 1 @@ -90,13 +119,23 @@ def main(): for (charge, ma), (_, mb) in zip(ra, rb): if ma != mb: cas[(fn, charge, motif_court(ma), motif_court(mb))] += 1 + # L'acheté d'un niveau éco (--achat) : ancienne valeur → nouvelle, ou devenu absent. + for (charge, va), (_, vb) in zip(aa, ab): + if va != vb: + cas[(fn, charge, f'purchasedWh {achat_court(va)}', f'purchasedWh {achat_court(vb)}')] += 1 # Le manque d'un niveau change sans que le motif de tête change : à dire aussi. - if ra == rb: + if ra == rb and aa == ab: cas[(fn, '(niveau)', 'missingWh', 'missingWh corrigé')] += 1 - print(f'IDENTIQUE HORS MOTIFS : {len(attendu)} lignes, dont {brutes_identiques} identiques ' - f'à l\'octet et {len(attendu) - brutes_identiques} qui ne diffèrent que par les motifs') - lignes = ['| scénario (fonction) | charge | ancien motif | nouveau motif | lignes |', + hors = 'HORS MOTIFS ET HORS ACHAT' if achat else 'HORS MOTIFS' + print(f'IDENTIQUE {hors} : {len(attendu)} lignes, dont {brutes_identiques} identiques ' + f'à l\'octet et {len(attendu) - brutes_identiques} qui ne diffèrent que par ' + f'{"les motifs ou purchasedWh" if achat else "les motifs"}') + if sortie_lignes: + with open(sortie_lignes, 'w', encoding='utf-8') as f: + for i, a, b in lignes_changees: + f.write(f'## ligne {i}\n- {a}\n+ {b}\n') + lignes = ['| scénario (fonction) | charge | avant | après | lignes |', '|---|---|---|---|---|'] for (fn, charge, avant, apres), n in sorted(cas.items()): lignes.append(f'| `{fn}` | `{charge}` | {avant} | {apres} | {n} |')