From 9ef5c32910ddb95ae9fe7bbff5adc89dabed9198 Mon Sep 17 00:00:00 2001 From: "francois.grand" <francois.grand@irstea.fr> Date: Thu, 21 Jun 2018 10:20:44 +0200 Subject: [PATCH] =?UTF-8?q?=20#45=20d=C3=A9tection=20des=20r=C3=A9f=C3=A9r?= =?UTF-8?q?ences=20circulaires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/mock_jasmine.ts | 8 ++ spec/value_ref/value_ref_circularity.spec.ts | 104 +++++++++++++++++++ src/nub.ts | 42 +++----- src/param/param-base.ts | 39 ++++++- src/param/param-values.ts | 7 ++ src/value_ref/object_ref.ts | 18 ++++ 6 files changed, 188 insertions(+), 30 deletions(-) create mode 100644 spec/value_ref/value_ref_circularity.spec.ts diff --git a/spec/mock_jasmine.ts b/spec/mock_jasmine.ts index e459c917..2baf1713 100644 --- a/spec/mock_jasmine.ts +++ b/spec/mock_jasmine.ts @@ -37,6 +37,14 @@ export function xit(sTxt: string, fFun: () => void) { console.warn("*** " + sTxt + " ignored ***"); } +export function fail(m?: string) { + let s = "Test failed"; + if (m !== undefined) + s += ` (${m})`; + + console.error(s); +} + /** * Classe contenant les méthodes de comparaison de Jasmine. */ diff --git a/spec/value_ref/value_ref_circularity.spec.ts b/spec/value_ref/value_ref_circularity.spec.ts new file mode 100644 index 00000000..bf06ed1b --- /dev/null +++ b/spec/value_ref/value_ref_circularity.spec.ts @@ -0,0 +1,104 @@ +import { NubTest, NubTestParams } from "../nubtest"; + +/** + * IMPORTANT ! + * Décommenter temporairement la ligne suivante (import { } from "./mock_jasmine") + * Pour exécuter ce code dans le débugger. + * Faire de même avec le fichier test_func.ts + */ +// import { describe, expect, it, xdescribe, xit, fail } from "../mock_jasmine"; + +let nub1: NubTest; +let nub2: NubTest; +let nub3: NubTest; +let prm1: NubTestParams; +let prm2: NubTestParams; +let prm3: NubTestParams; + +/** + * crée l'environnement de test. + * répété à chaque test car il manque un mock de beforeEach + */ +function createEnv() { + nub1 = new NubTest(new NubTestParams()); + prm1 = nub1.parameters as NubTestParams; + + nub2 = new NubTest(new NubTestParams()); + prm2 = nub2.parameters as NubTestParams; + + nub3 = new NubTest(new NubTestParams()); + prm3 = nub3.parameters as NubTestParams; +} + +describe("référence d'un paramètre à un autre : ", () => { + describe("vérification des références circulaires : ", () => { + it("test 1", () => { + // cas de figure (ne doit pas échouer) : + // nub2.A référence nub1.A + + createEnv(); + + try { + prm2.A.defineReference(nub1, "A"); // ne doit pas échouer + } + catch (e) { + fail(); + } + }); + + it("test 2", () => { + // cas de figure (ne doit pas échouer) : + // nub1.A référence nub2.A qui référence nub3.A + + createEnv(); + + try { + prm1.A.defineReference(nub2, "A"); + prm2.A.defineReference(nub3, "A"); + } + catch (e) { + fail(); + } + }); + + it("test 3", () => { + // cas de figure (doit échouer) : + // nub2.A référence nub1.A qui référence nub2.A + + createEnv(); + + try { + prm2.A.defineReference(nub1, "A"); // ne doit pas échouer + prm1.A.defineReference(nub2, "A"); // doit échouer + fail(); + } + catch (e) { + } + }); + + it("test 4", () => { + // cas de figure (doit échouer) : + // param1 référence param2 (OK) + // param3 référence param1 (OK) + // param2 référence param3 (doit échouer) + + createEnv(); + + try { + prm1.A.defineReference(nub2, "A"); // ne doit pas échouer + prm3.A.defineReference(nub1, "A"); // ne doit pas échouer + } + catch (e) { + fail(); + } + + + try { + prm2.A.defineReference(nub3, "A"); // doit échouer + fail(); + } + catch (e) { + } + }); + }); +}); diff --git a/src/nub.ts b/src/nub.ts index 02400b8a..120c6273 100644 --- a/src/nub.ts +++ b/src/nub.ts @@ -6,6 +6,7 @@ import { ParamValues } from "./param/param-values"; import { ParamValueMode } from "./param/param-value-mode"; import { ParamDefinition } from "."; import { IReferencedNub } from "./value_ref/object_ref"; +import { IJalhydObject } from "./jalhyd_object"; /** * Classe abstraite de Noeud de calcul : classe de base pour tous les calculs @@ -228,34 +229,19 @@ export abstract class Nub extends ComputeNode implements IReferencedNub { } } - /** - * @returns liste des paramètres liables à un paramètre - * @param p paramètre qui sert de clé de recherche des paramètres liables - */ - // public getLinkableParameters(param: ParamDefinition): any[] { - // const res: any[] = []; - - // for (const p of this._prms) - // if (p.uid !== param.uid) - // switch (p.valueMode) { - // case ParamValueMode.SINGLE: - // case ParamValueMode.MINMAX: - // case ParamValueMode.LISTE: - // switch (param.symbol) { - // case "Z1": - // case "Z2": - // if (p.symbol === "Z1" || p.symbol === "Z2") - // res.push({ "param": p, "nub": this }); - // break; - - // default: - // if (p.symbol === param.symbol) - // res.push({ "param": p, "nub": this }); - // } - // } - - // return res; - // } + public getReferencedObject(desc: string): IJalhydObject { + const tmp = desc.split("."); + + if (tmp.length == 1) // paramètre (ex "Q") + return this.getParameter(desc); + + if (tmp[1] === "") // résultat (ex "Q.") + if (this._result !== undefined) + return this._result; + + // les autres objets référençables n'implémentant pas IJalhydObject... + return undefined; + } private addPrefix(str: string, prefix: string) { return prefix === undefined ? str : `${prefix}${str}`; diff --git a/src/param/param-base.ts b/src/param/param-base.ts index ce59f8ac..7abc9172 100644 --- a/src/param/param-base.ts +++ b/src/param/param-base.ts @@ -1,7 +1,7 @@ import { Interval } from "../util/interval"; import { Message, MessageCode } from "../util/message"; -import { JalhydObject } from "../jalhyd_object" +import { JalhydObject, IJalhydObject } from "../jalhyd_object" import { ParamDomain, ParamDomainValue } from "./param-domain"; import { ParamValues } from "./param-values"; import { ParamValueMode } from "./param-value-mode"; @@ -255,8 +255,39 @@ export class BaseParam extends JalhydObject implements INubReference, NamedItera // interface INubReference + /** + * vérifie l'absence de référence circulaire + * @param seenUids liste des uids déjà vérifiés + * @param o objet à tester (son uid est il déjà dans la liste ?) + */ + private checkReferenceCircularity(o: any, seenUids: number[]) { + if ("uid" in o) { + // if (o.uid in seenUids) + if (seenUids.indexOf(o.uid) !== -1) + throw new Error(`références circulaires détectées (uids : ${seenUids})`); + + seenUids.push(o.uid); + + if ("referencedObject" in o) { + const curr = o as INubReference; + const next = curr.referencedObject; + if (next !== undefined) + this.checkReferenceCircularity(next as IJalhydObject, seenUids); + } + } + } + public defineReference(target: IReferencedNub, desc: string) { - this._paramValues.defineReference(target, desc); + const oldDef = this._paramValues.referenceDefinition; + const oldTarget = this._paramValues.referencedNub; + try { + this._paramValues.defineReference(target, desc); + this.checkReferenceCircularity(this, []); + } + catch (e) { + this._paramValues.defineReference(oldTarget, oldDef); + throw e; + } } public get referenceDefinition(): string { @@ -287,6 +318,10 @@ export class BaseParam extends JalhydObject implements INubReference, NamedItera return this._paramValues.referencedValuesIterator; } + public get referencedObject(): IJalhydObject { + return this._paramValues.referencedObject; + } + // interface NamedIterableValues public get valuesIterator(): IterableIterator<number> { diff --git a/src/param/param-values.ts b/src/param/param-values.ts index bb3dd476..c6a3fead 100644 --- a/src/param/param-values.ts +++ b/src/param/param-values.ts @@ -4,6 +4,7 @@ import { IReferencedNub, INubReference, NubReference } from "../value_ref/object import { Result } from ".."; import { ParamValueMode } from "./param-value-mode"; import { ParamValueIterator, IterableValues } from "./param-value-iterator"; +import { IJalhydObject } from "../jalhyd_object"; export class ParamValues implements INubReference, IterableValues { /** @@ -312,6 +313,12 @@ export class ParamValues implements INubReference, IterableValues { return this._nubRef.referencedValuesIterator; } + public get referencedObject(): IJalhydObject { + if (this.isReferenceDefined) + return this._nubRef.referencedObject; + return undefined; + } + // interface IterableValues public get valuesIterator(): IterableIterator<number> { diff --git a/src/value_ref/object_ref.ts b/src/value_ref/object_ref.ts index 9180de1d..cf392ddf 100644 --- a/src/value_ref/object_ref.ts +++ b/src/value_ref/object_ref.ts @@ -1,5 +1,6 @@ import { ParamValues } from "../param/param-values"; import { Result } from ".."; +import { IJalhydObject } from "../jalhyd_object"; /** * Nub dont certaines valeurs sont référençables pour réutilisation @@ -28,6 +29,11 @@ export interface IReferencedNub { * itérateur sur les valeurs */ getReferencedValuesIterator(desc: string): IterableIterator<number>; + + /** + * objet (paramètre/résultat/résultat complémentaire) référencé + */ + getReferencedObject(desc: string): IJalhydObject; } /** @@ -70,6 +76,11 @@ export interface INubReference { * itérateur sur les valeurs référencées */ readonly referencedValuesIterator: IterableIterator<number>; + + /** + * objet (paramètre/résultat/résultat complémentaire) référencé + */ + readonly referencedObject: IJalhydObject; } /** @@ -136,4 +147,11 @@ export class NubReference implements INubReference { public get referencedValuesIterator(): IterableIterator<number> { return this._referencedNub.getReferencedValuesIterator(this._refDefinition); } + + /** + * objet (paramètre/résultat/résultat complémentaire) référencé + */ + public get referencedObject(): IJalhydObject { + return this._referencedNub.getReferencedObject(this._refDefinition); + } } -- GitLab