From cf41d163cf2e0d1cf91fd192e4cb8151adb36b30 Mon Sep 17 00:00:00 2001 From: Kai Richard Koenig Date: Fri, 18 Oct 2019 20:16:58 +0200 Subject: [PATCH 1/5] Try not be smart about types. The commit ce6fc74 introduced logic that would try to construct a new generic type from all previously connected ports. For doing so it would not use the actual type but try to reconstruct it from the current port e.g. `this`. See the method `getConnectedType`. This method however is not able to do that correctly and would often fail thus resulting in the error `types not unifiable`. The questions I was asking myself: - why would we want to try to reconstruct a type and unify it? - what was the original intention behind the implementation? - sadly that is not clear from the code, the commit or the comments :-/ - wouldn't it be easier to just leave everything as it is and not surprise the user? - how can we avoid throwing the error and not freezing the editor? In order to lower the surprise factor I chose to remove most of the original logic and simply disconnect two ports if their types are not compatible. The optimal solution in my opinion would be to check if the type change can be propagated through all connected ports - both ways - and only than apply the change w/o disconnecting anything. If at any point along the connections/edges we encounter an incompatible type, cut the connection between the root and the rest of the nodes. But you know what - that is so far out of scope right now! --- src/slang/core/abstract/port.ts | 65 ++----------------- .../core/abstract/utils/connection-check.ts | 2 +- src/slang/core/abstract/utils/generics.ts | 12 ---- src/slang/definitions/type.ts | 52 --------------- 4 files changed, 8 insertions(+), 123 deletions(-) diff --git a/src/slang/core/abstract/port.ts b/src/slang/core/abstract/port.ts index b28a8fec..937fc0ad 100644 --- a/src/slang/core/abstract/port.ts +++ b/src/slang/core/abstract/port.ts @@ -7,7 +7,7 @@ import {BlackBox} from "./blackbox"; import {SlangNode} from "./nodes"; import {PortOwner} from "./port-owner"; import {StreamPort} from "./stream"; -import {canConnectTo} from "./utils/connection-check"; +import {canConnectTo, typesCompatibleTo} from "./utils/connection-check"; import {Connections} from "./utils/connections"; import {SlangSubject} from "./utils/events"; import {GenericSpecifications} from "./utils/generics"; @@ -293,52 +293,6 @@ export abstract class GenericPortModel extends SlangNode { return type; } - public anySubStreamConnected(): boolean { - if (this.connectedWith.length !== 0) { - return true; - } - - if (this.typeIdentifier === TypeIdentifier.Map) { - for (const sub of this.getMapSubs()) { - if (sub.anySubStreamConnected()) { - return true; - } - } - } else if (this.typeIdentifier === TypeIdentifier.Stream) { - return this.getStreamSub().anySubStreamConnected(); - } - - return false; - } - - public getConnectedType(): SlangType { - const type = new SlangType(null, this.typeIdentifier, true); - switch (this.typeIdentifier) { - case TypeIdentifier.Map: - for (const subPort of this.getMapSubs()) { - if (!subPort.anySubStreamConnected()) { - continue; - } - const subType = subPort.getConnectedType(); - if (!subType.isVoid()) { - type.addMapSub(subPort.getName(), subType); - } - } - break; - case TypeIdentifier.Stream: - type.setStreamSub(this.getStreamSub().getConnectedType()); - break; - case TypeIdentifier.Generic: - type.setGenericIdentifier(this.getGenericIdentifier()); - break; - default: - if (!this.anySubStreamConnected()) { - return new SlangType(null, TypeIdentifier.Unspecified, true); - } - } - return type; - } - public getTypeIdentifier(): TypeIdentifier { return this.typeIdentifier; } @@ -598,17 +552,6 @@ export abstract class GenericPortModel extends SlangNode { fetchedGenerics.specifications.unregisterPort(fetchedGenerics.identifier, this); }); - this.subscribeDisconnected(() => { - if (this.connectedWith.length !== 0) { - return; - } - - const newType = specifications.getUnifiedType(identifier); - if (newType && !specifications.get(identifier).equals(newType)) { - specifications.specify(identifier, newType); - } - }); - if (!this.isGenericLike()) { return; } @@ -621,6 +564,12 @@ export abstract class GenericPortModel extends SlangNode { this.typeIdentifier = TypeIdentifier.Unspecified; this.genericIdentifier = undefined; } + for (const connectedWith of this.connectedWith) { + const dest = connectedWith; + if (!typesCompatibleTo(this.getType(), dest.getType())) { + this.disconnectTo(dest); + } + } }); } diff --git a/src/slang/core/abstract/utils/connection-check.ts b/src/slang/core/abstract/utils/connection-check.ts index c0b9b931..c780ce4c 100644 --- a/src/slang/core/abstract/utils/connection-check.ts +++ b/src/slang/core/abstract/utils/connection-check.ts @@ -20,7 +20,7 @@ function typesMapCompatibleTo(mapTypeA: SlangType, mapTypeB: SlangType): boolean return true; } -function typesCompatibleTo(sourceType: SlangType, destinationType: SlangType): boolean { +export function typesCompatibleTo(sourceType: SlangType, destinationType: SlangType): boolean { // Triggers can always be destinations, even for specifications, maps and streams if (destinationType.getTypeIdentifier() === TypeIdentifier.Trigger) { return true; diff --git a/src/slang/core/abstract/utils/generics.ts b/src/slang/core/abstract/utils/generics.ts index 03c43367..cce9e443 100644 --- a/src/slang/core/abstract/utils/generics.ts +++ b/src/slang/core/abstract/utils/generics.ts @@ -72,18 +72,6 @@ export class GenericSpecifications { } } - public getUnifiedType(identifier: string): SlangType | null { - const portSet = this.ports.get(identifier); - if (!portSet) { - return null; - } - let unifiedType = this.get(identifier).getOnlyFixedSubs(); - for (const registeredPort of portSet) { - unifiedType = unifiedType.union(registeredPort.getConnectedType()); - } - return unifiedType; - } - public subscribeGenericTypeChanged(identifier: string, cb: (type: SlangType | null) => void): Subscription { return this.getSubject(identifier).subscribe(cb); } diff --git a/src/slang/definitions/type.ts b/src/slang/definitions/type.ts index e874b3cf..c0813a15 100644 --- a/src/slang/definitions/type.ts +++ b/src/slang/definitions/type.ts @@ -385,58 +385,6 @@ export class SlangType { return this.typeIdentifier; } - /** - * Returns true iff this type contains at least one element that is fixed (i.e. that has not been - * inferred). - */ - public hasAnyFixedSub(): boolean { - if (this.typeIdentifier === TypeIdentifier.Map) { - for (const sub of this.getMapSubs()) { - if (sub[1].hasAnyFixedSub()) { - return true; - } - } - } else if (this.typeIdentifier === TypeIdentifier.Stream) { - return this.getStreamSub().hasAnyFixedSub(); - } else if (!this.inferred) { - return true; - } - - return false; - } - - /** - * Returns a type which is part of this type but contains only fixed elements (i.e. elements that have not been - * inferred). - */ - public getOnlyFixedSubs(): SlangType { - const type = new SlangType(null, this.typeIdentifier); - switch (this.typeIdentifier) { - case TypeIdentifier.Map: - for (const sub of this.getMapSubs()) { - if (!sub[1].hasAnyFixedSub()) { - continue; - } - const subType = sub[1].getOnlyFixedSubs(); - if (!subType.isVoid()) { - type.addMapSub(sub[0], subType); - } - } - break; - case TypeIdentifier.Stream: - type.setStreamSub(this.getStreamSub().getOnlyFixedSubs()); - break; - case TypeIdentifier.Generic: - type.setGenericIdentifier(this.getGenericIdentifier()); - break; - default: - if (!this.hasAnyFixedSub()) { - return SlangType.newUnspecified(); - } - } - return type; - } - public isElementaryPort(): boolean { return this.isPrimitive() || this.isTrigger() || this.isGeneric(); } From f4bbf1be08fdfacf29c9e382815e3c897e4195bc Mon Sep 17 00:00:00 2001 From: Kai Richard Koenig Date: Mon, 28 Oct 2019 12:43:18 +0100 Subject: [PATCH 2/5] Get all connections to the port and walk them --- src/slang/core/abstract/port.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slang/core/abstract/port.ts b/src/slang/core/abstract/port.ts index 2ddda87c..0c476554 100644 --- a/src/slang/core/abstract/port.ts +++ b/src/slang/core/abstract/port.ts @@ -564,7 +564,7 @@ export abstract class GenericPortModel extends SlangNode { this.typeIdentifier = TypeIdentifier.Unspecified; this.genericIdentifier = undefined; } - for (const connection of this.getDirectConnections()) { + for (const connection of this.getConnectionsTo()) { const dest = connection.destination; const src = connection.source; if (!typesCompatibleTo(src.getType(), dest.getType())) { From 47232edd84732eec6a89d54a8daf0991ec33cab3 Mon Sep 17 00:00:00 2001 From: Kai Richard Koenig Date: Tue, 29 Oct 2019 10:32:31 +0100 Subject: [PATCH 3/5] Source and Destinations can be triggers --- src/slang/core/abstract/utils/connection-check.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slang/core/abstract/utils/connection-check.ts b/src/slang/core/abstract/utils/connection-check.ts index c780ce4c..f04d9919 100644 --- a/src/slang/core/abstract/utils/connection-check.ts +++ b/src/slang/core/abstract/utils/connection-check.ts @@ -26,6 +26,10 @@ export function typesCompatibleTo(sourceType: SlangType, destinationType: SlangT return true; } + if (sourceType.getTypeIdentifier() === TypeIdentifier.Trigger) { + return true; + } + // Careful: destinationType.getTypeIdentifier() === TypeIdentifier.Primitive is not identical with destinationType.isPrimitive() // isPrimitive() is true for Strings, Numbers, etc. if (destinationType.getTypeIdentifier() === TypeIdentifier.Primitive && sourceType.isPrimitive()) { From 0470b73e64537b5dde28b6e59820d92e86355ad1 Mon Sep 17 00:00:00 2001 From: Taleh Didover Date: Tue, 5 Nov 2019 23:04:06 +0100 Subject: [PATCH 4/5] Make tests more readable --- src/slang/core/abstract/port-owner.ts | 10 +++ src/slang/core/abstract/port.ts | 10 ++- src/slang/core/models/blueprint.ts | 5 ++ src/slang/definitions/type.ts | 28 +++++-- src/styles/index.ts | 4 +- test/behavior/stream.test.ts | 111 ++++++++++++++++---------- 6 files changed, 116 insertions(+), 52 deletions(-) diff --git a/src/slang/core/abstract/port-owner.ts b/src/slang/core/abstract/port-owner.ts index e07b79c8..63773c9b 100644 --- a/src/slang/core/abstract/port-owner.ts +++ b/src/slang/core/abstract/port-owner.ts @@ -13,6 +13,16 @@ export abstract class PortOwner extends SlangNode { this.streamPortOwner.initialize(); } + // tslint:disable-next-line:naming-convention + public get II(): PortModel { + return this.getPortIn()!; + } + + // tslint:disable-next-line:naming-convention + public get OO(): PortModel { + return this.getPortOut()!; + } + public getPortIn(): PortModel | null { return this.scanChildNode(GenericPortModel, (p) => p.isDirectionIn()) || null; } diff --git a/src/slang/core/abstract/port.ts b/src/slang/core/abstract/port.ts index 0c476554..e534404b 100644 --- a/src/slang/core/abstract/port.ts +++ b/src/slang/core/abstract/port.ts @@ -101,6 +101,14 @@ export abstract class GenericPortModel extends SlangNode { this.streamPort.initialize(); } + public get sub(): GenericPortModel { + return this.getStreamSub(); + } + + public map(name: string): GenericPortModel { + return this.findMapSub(name); + } + public reconstruct(type: SlangType, portCtor: new (p: GenericPortModel | O, args: PortModelArgs) => PortModel, direction: PortDirection, generic: boolean = false): void { /* * When generic == true then this method is called because this port is generic-like and its specification has changed @@ -643,7 +651,7 @@ export abstract class GenericPortModel extends SlangNode { */ private connectTo(destination: PortModel, createGenerics: boolean) { if (!canConnectTo(this, destination, createGenerics)) { - throw new Error(`cannot connect: ${this.getIdentity()} --> ${destination.getIdentity()}`); + throw new Error(`cannot connect: ${this.getOwnerName()}:${this.getPortReference()} --> ${destination.getOwnerName()}:${destination.getPortReference()}`); } if ((createGenerics && this.isGenericLike()) || destination.isTrigger()) { this.connectDirectlyTo(destination, createGenerics); diff --git a/src/slang/core/models/blueprint.ts b/src/slang/core/models/blueprint.ts index 856686e5..67b6d82e 100644 --- a/src/slang/core/models/blueprint.ts +++ b/src/slang/core/models/blueprint.ts @@ -220,6 +220,11 @@ export class BlueprintModel extends BlackBox implements HasMoveablePortGroups { return this.createChildNode(BlueprintPortModel, args); } + public definePort(args: PortModelArgs): this { + this.createPort(args); + return this; + } + public getShortName(): string { return this.name; } diff --git a/src/slang/definitions/type.ts b/src/slang/definitions/type.ts index c0813a15..7d1b86f7 100644 --- a/src/slang/definitions/type.ts +++ b/src/slang/definitions/type.ts @@ -139,10 +139,18 @@ export class SlangType { return SlangType.new(TypeIdentifier.Unspecified); } + public static newTrigger(): SlangType { + return SlangType.new(TypeIdentifier.Trigger); + } + public static newString(): SlangType { return SlangType.new(TypeIdentifier.String); } + public static newNumber(): SlangType { + return SlangType.new(TypeIdentifier.Number); + } + public static newBoolean(): SlangType { return SlangType.new(TypeIdentifier.Boolean); } @@ -151,16 +159,22 @@ export class SlangType { return SlangType.new(TypeIdentifier.Generic).setGenericIdentifier(identifier); } - public static newMap(): SlangType { - return SlangType.new(TypeIdentifier.Map); + public static newMap(mapEntries?: {[n: string]: SlangType}): SlangType { + const map = SlangType.new(TypeIdentifier.Map); + if (mapEntries) { + Object.entries(mapEntries).forEach(([name, subType]) => { + map.addMapSub(name, subType); + }); + } + return map; } - public static newStream(subTid?: TypeIdentifier): SlangType { - const strType = SlangType.new(TypeIdentifier.Stream); - if (!subTid) { - return strType; + public static newStream(sub?: TypeIdentifier|SlangType): SlangType { + const stream = SlangType.new(TypeIdentifier.Stream); + if (sub) { + stream.setStreamSub(sub instanceof SlangType ? sub : SlangType.new(sub)); } - return SlangType.new(TypeIdentifier.Stream).setStreamSub(SlangType.new(subTid)); + return stream; } private readonly mapSubs: Map | undefined; diff --git a/src/styles/index.ts b/src/styles/index.ts index 996b4397..8cb18cb6 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1,3 +1 @@ -// @ts-ignore -import styling from "./index.scss"; -export const STYLING = styling.toString(); +export const STYLING: string = `@import url(https://fonts.googleapis.com/css?family=Roboto+Slab:300%7CRoboto);.joint-viewport{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.joint-paper-background,.joint-paper-grid,.joint-paper>svg{position:absolute;top:0;left:0;right:0;bottom:0}[magnet=true]:not(.joint-element){cursor:crosshair}[magnet=true]:not(.joint-element):hover{opacity:.7}.joint-element{cursor:move}.joint-element *{user-drag:none}.joint-element .scalable *{vector-effect:non-scaling-stroke}.marker-source,.marker-target{vector-effect:non-scaling-stroke}.joint-paper{position:relative}.joint-highlight-opacity{opacity:.3}.joint-link .connection,.joint-link .connection-wrap{fill:none}.marker-vertices{opacity:0;cursor:move}.marker-arrowheads{opacity:0;cursor:move;cursor:-webkit-grab;cursor:-moz-grab}.link-tools{opacity:0;cursor:pointer}.link-tools .tool-options{display:none}.joint-link:hover .link-tools,.joint-link:hover .marker-arrowheads,.joint-link:hover .marker-vertices{opacity:1}.marker-vertex-remove{cursor:pointer;opacity:.1}.marker-vertex-group:hover .marker-vertex-remove{opacity:1}.marker-vertex-remove-area{opacity:.1;cursor:pointer}.marker-vertex-group:hover .marker-vertex-remove-area{opacity:1}.joint-element .fobj{overflow:hidden}.joint-element .fobj body{background-color:transparent;margin:0;position:static}.joint-element .fobj div{text-align:center;vertical-align:middle;display:table-cell;padding:0 5px 0 5px}.sl-port{-webkit-transition-duration:.2s;transition-duration:.2s}.sl-view{width:100%;height:100%}.sl-blackbox .sl-rectangle,.sl-blackbox-ghost .sl-rectangle{fill:#fefefe;stroke:#212124;stroke-width:1.5px;paint-order:stroke}.sl-blackbox .sl-label,.sl-blackbox-ghost .sl-label{fill:#212124}.sl-blackbox-ghost .sl-rectangle{fill-opacity:.1}.sl-outer .sl-rectangle{stroke:#212124;fill:#fefefe;stroke-width:1.5px}.sl-outer.sl-blupr-elem .sl-rectangle{cursor:not-allowed;fill:rgba(33,33,36,.1)}.sl-blueprint-port .sl-label{fill:#212124}.sl-port{stroke-width:0;stroke-opacity:.6;-webkit-transition-property:stroke-width;transition-property:stroke-width}.sl-port.sl-stripe{stroke:#fff;fill:#fff}.sl-port.sl-type-number{stroke:#2e49b3;fill:#2e49b3}.sl-port.sl-type-string{stroke:#952e2e;fill:#952e2e}.sl-port.sl-type-boolean{stroke:#ff764d;fill:#ff764d}.sl-port.sl-type-binary{stroke:#83a91d;fill:#83a91d}.sl-port.sl-type-primitive{stroke:#209cee;fill:#209cee}.sl-port.sl-type-trigger{stroke:rgba(152,152,151,.5);fill:rgba(152,152,151,.5)}.sl-port.sl-type-generic{stroke:#b86bff;fill:#b86bff}.sl-port.sl-type-ghost{stroke:rgba(184,107,255,.5);fill:rgba(184,107,255,.5)}.sl-connection-wrap{fill:none;stroke:#989897;stroke-width:10px;stroke-opacity:0;stroke-linecap:round}.sl-connection-wrap:hover{stroke-opacity:.3}.sl-connection-wrap.sl-is-selected{stroke:#212124;stroke-opacity:.2}.sl-connection-wrap.sl-is-selected~.link-tools{display:initial}.sl-connection{vector-effect:none;fill:none;stroke-opacity:1}.sl-connection.sl-type-number{stroke:#2e49b3}.sl-connection.sl-type-string{stroke:#952e2e}.sl-connection.sl-type-boolean{stroke:#ff764d}.sl-connection.sl-type-binary{stroke:#83a91d}.sl-connection.sl-type-primitive{stroke:#209cee}.sl-connection.sl-type-trigger{stroke:rgba(152,152,151,.5)}.sl-connection.sl-type-generic{stroke:#b86bff}.sl-connection.sl-type-ghost{stroke:rgba(184,107,255,.5);stroke-opacity:.3}@-webkit-keyframes port-pulse{from{stroke-width:0}to{stroke-width:8px}}@keyframes port-pulse{from{stroke-width:0}to{stroke-width:8px}}.joint-paper{font-family:Roboto,sans-serif;font-size:16px;font-size:1rem}.joint-highlight-stroke{stroke:none}.available-magnet path{-webkit-animation-name:port-pulse;animation-name:port-pulse;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-direction:alternate;animation-direction:alternate;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}.tool-remove circle{fill:#f74125}.tool-remove path{fill:#fefefe}.sl-port-info{background:#f5f5f5;padding:1px;margin:0;border-radius:8px;font-size:12px;font-size:.75rem}.sl-port-info .sl-port-name{text-decoration:underline;padding:0 6px}.sl-port-info .sl-port-type{color:#fafafa;padding:0 6px;border-radius:15px}.sl-port-info .sl-port-type.sl-type-number{background-color:#2e49b3}.sl-port-info .sl-port-type.sl-type-string{background-color:#952e2e}.sl-port-info .sl-port-type.sl-type-boolean{background-color:#ff764d}.sl-port-info .sl-port-type.sl-type-binary{background-color:#83a91d}.sl-port-info .sl-port-type.sl-type-primitive{background-color:#209cee}.sl-port-info .sl-port-type.sl-type-trigger{background-color:rgba(152,152,151,.5)}.sl-port-info .sl-port-type.sl-type-generic{background-color:#b86bff}.sl-port-info .sl-port-type.sl-type-ghost{background-color:rgba(184,107,255,.5)}`; diff --git a/test/behavior/stream.test.ts b/test/behavior/stream.test.ts index 68fd2689..046469d9 100644 --- a/test/behavior/stream.test.ts +++ b/test/behavior/stream.test.ts @@ -2,9 +2,8 @@ import uuidv4 from "uuid/v4"; import {PortDirection} from "../../src/slang/core/abstract/port"; import {StreamType} from "../../src/slang/core/abstract/stream"; -import {AppModel} from "../../src/slang/core/models/app"; +import {AppModel, LandscapeModel} from "../../src/slang/core/models"; import {BlueprintType} from "../../src/slang/core/models/blueprint"; -import {LandscapeModel} from "../../src/slang/core/models/landscape"; import {SlangType, TypeIdentifier} from "../../src/slang/definitions/type"; import {TestStorageApp} from "../helpers/TestStorageApp"; import data from "../resources/definitions.json"; @@ -61,53 +60,83 @@ describe("A stream port", () => { type: BlueprintType.Local, }); - const opGIS = bp.createBlankOperator(landscapeModel.findBlueprint("10a6eea5-4d5b-43b6-9106-6820d1009e3b")!); - const opG2G = bp.createBlankOperator(landscapeModel.findBlueprint("dc1aa556-d62e-4e07-adbb-53dc317481b0")!); + const oGenericInStream = bp.createBlankOperator(landscapeModel.findBlueprint("10a6eea5-4d5b-43b6-9106-6820d1009e3b")!); + const oGenericToGeneric = bp.createBlankOperator(landscapeModel.findBlueprint("dc1aa556-d62e-4e07-adbb-53dc317481b0")!); + + oGenericToGeneric.getGenerics().specify("itemType", SlangType.new(TypeIdentifier.Number)); - opG2G.getGenerics().specify("itemType", SlangType.new(TypeIdentifier.Number)); + expect(oGenericToGeneric.OO.canConnect(oGenericInStream.II.sub)).toBeTruthy(); - const g2gOut = opG2G.getPortOut()!; - const gisInSub = opGIS.getPortIn()!.getStreamSub(); + oGenericToGeneric.OO.connect(oGenericInStream.II.sub, true); - expect(g2gOut.canConnect(gisInSub)).toEqual(true); - g2gOut.connect(gisInSub, true); - expect(g2gOut.isConnectedWith(gisInSub.getMapSubs().next().value)).toEqual(true); + expect(oGenericToGeneric.OO.isConnectedWith(oGenericInStream.II.sub.getMapSubs().next().value)).toBeTruthy(); }); it("groups streams correctly", () => { - const bp = landscapeModel.createBlueprint({ - uuid: uuidv4(), - meta: {name: "test-bp-2"}, - type: BlueprintType.Local, - }); - const bpIn = bp.createPort({ - name: "", - type: new SlangType(null, TypeIdentifier.Number), - direction: PortDirection.In, - }); - const bpOut = bp.createPort({ - name: "", - type: new SlangType(null, TypeIdentifier.Unspecified), - direction: PortDirection.Out, - }); - - bpIn.connect(bpOut, true); - - const op2OS = bp.createBlankOperator(landscapeModel.findBlueprint("b444f701-59fc-43a8-8cdc-8bcce9dd471d")!); - const opG2G = bp.createBlankOperator(landscapeModel.findBlueprint("dc1aa556-d62e-4e07-adbb-53dc317481b0")!); - - const osOut = op2OS.getPortOut()!; - const g2gIn = opG2G.getPortIn()!; - const g2gOut = opG2G.getPortOut()!; - - osOut.getStreamSub().findMapSub("portA").connect(g2gIn, true); - osOut.getStreamSub().findMapSub("portB").connect(g2gIn, true); - Array.from(g2gOut.getMapSubs()).forEach((port) => { - port.connect(bpOut, true); + const bp = landscapeModel + .createBlueprint({ + uuid: uuidv4(), + meta: {name: "test-bp-2"}, + type: BlueprintType.Local, + }) + .definePort({ + name: "", + type: SlangType.newNumber(), + direction: PortDirection.In, + }) + .definePort({ + name: "", + type: SlangType.newUnspecified(), + direction: PortDirection.Out, + }); + const bpOutStreamMap = landscapeModel + .createBlueprint({ + uuid: uuidv4(), + meta: {name: "OutStreamMap"}, + type: BlueprintType.Local, + }) + .definePort({ + name: "", + type: SlangType.newTrigger(), + direction: PortDirection.In, + }) + .definePort({ + name: "", + type: SlangType.newStream(SlangType.newMap({ + A: SlangType.newString(), + B: SlangType.newNumber(), + })), + direction: PortDirection.Out, + }); + const bpGenericToGeneric = landscapeModel + .createBlueprint({ + uuid: uuidv4(), + meta: {name: "GenericToGeneric"}, + type: BlueprintType.Local, + }) + .definePort({ + name: "", + type: SlangType.newGeneric("itemType"), + direction: PortDirection.In, + }) + .definePort({ + name: "", + type: SlangType.newGeneric("itemType"), + direction: PortDirection.Out, + }); + + const oOutStreamMap = bp.createBlankOperator(bpOutStreamMap); + const oGenericToGeneric = bp.createBlankOperator(bpGenericToGeneric); + + bp.II.connect(bp.OO, true); + oOutStreamMap.OO.sub.map("A").connect(oGenericToGeneric.II, true); + oOutStreamMap.OO.sub.map("B").connect(oGenericToGeneric.II, true); + Array.from(oGenericToGeneric.OO.getMapSubs()).forEach((port) => { + port.connect(bp.OO, true); }); - expect(Array.from(bpOut.getMapSubs()).length).toEqual(2); - expect(Array.from(Array.from(bpOut.getMapSubs()).find((port) => port.getType().isStream())!.getStreamSub().getMapSubs()).length).toEqual(2); + expect(Array.from(bp.OO.getMapSubs()).length).toEqual(2); + expect(Array.from(Array.from(bp.OO.getMapSubs()).find((port) => port.getType().isStream())!.getStreamSub().getMapSubs()).length).toEqual(2); }); it("groups streams correctly after deletion", () => { From 2e8a8a0d58eff24f03cc62228aad39eaaa08b8e9 Mon Sep 17 00:00:00 2001 From: Taleh Didover Date: Wed, 6 Nov 2019 20:50:01 +0100 Subject: [PATCH 5/5] Make tests more readable part 2 --- src/slang/core/abstract/port.ts | 4 ++++ test/behavior/stream.test.ts | 11 +++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/slang/core/abstract/port.ts b/src/slang/core/abstract/port.ts index e534404b..0fd3173a 100644 --- a/src/slang/core/abstract/port.ts +++ b/src/slang/core/abstract/port.ts @@ -105,6 +105,10 @@ export abstract class GenericPortModel extends SlangNode { return this.getStreamSub(); } + public get mapSubs(): Array> { + return Array.from(this.getMapSubs()); + } + public map(name: string): GenericPortModel { return this.findMapSub(name); } diff --git a/test/behavior/stream.test.ts b/test/behavior/stream.test.ts index 046469d9..7f0b4e5d 100644 --- a/test/behavior/stream.test.ts +++ b/test/behavior/stream.test.ts @@ -72,7 +72,7 @@ describe("A stream port", () => { expect(oGenericToGeneric.OO.isConnectedWith(oGenericInStream.II.sub.getMapSubs().next().value)).toBeTruthy(); }); - it("groups streams correctly", () => { + it("connecting 2 stream ports with a generic port will create 2 stream ports within the generic port", () => { const bp = landscapeModel .createBlueprint({ uuid: uuidv4(), @@ -128,14 +128,13 @@ describe("A stream port", () => { const oOutStreamMap = bp.createBlankOperator(bpOutStreamMap); const oGenericToGeneric = bp.createBlankOperator(bpGenericToGeneric); - bp.II.connect(bp.OO, true); oOutStreamMap.OO.sub.map("A").connect(oGenericToGeneric.II, true); oOutStreamMap.OO.sub.map("B").connect(oGenericToGeneric.II, true); - Array.from(oGenericToGeneric.OO.getMapSubs()).forEach((port) => { - port.connect(bp.OO, true); - }); - expect(Array.from(bp.OO.getMapSubs()).length).toEqual(2); + oGenericToGeneric.OO.map("A").connect(bp.OO, true); + oGenericToGeneric.OO.map("B").connect(bp.OO, true); + + expect(bp.OO.mapSubs.length).toEqual(2); expect(Array.from(Array.from(bp.OO.getMapSubs()).find((port) => port.getType().isStream())!.getStreamSub().getMapSubs()).length).toEqual(2); });