diff --git a/test/behavior/blueprint.test.ts b/test/behavior/blueprint.test.ts index 8b83b6f4..1aa050ea 100644 --- a/test/behavior/blueprint.test.ts +++ b/test/behavior/blueprint.test.ts @@ -65,9 +65,9 @@ describe("A new blueprint", () => { }); it("can have ports created and removed dynamically", () => { - // Test setup: - // [ -> [S2S] ] - + // bpS2S ports: + // In: string + // Out: string const bpS2S = landscapeModel.findBlueprint("ba24c37f-2b04-44b4-97ad-fd931c9ab77b")!; const bpNew = landscapeModel.createBlueprint({ @@ -83,27 +83,58 @@ describe("A new blueprint", () => { const bpPortIn = bpNew.getPortIn()!; const opPortIn = opNew.getPortIn()!; + // The blueprint should look like this now: + // +---------------+ + // | +-----+ | + // | O S2S | | + // | | | | + // | +-----+ | + // +---------------+ + + // We expect these types: expect(bpPortIn.getTypeIdentifier()).toBe(TypeIdentifier.Unspecified); expect(opPortIn.getTypeIdentifier()).toBe(TypeIdentifier.String); + // Now, connect in-port of the operator with in-port of the blueprint opPortIn.connect(bpPortIn, true); + // The blueprint should look like this now: + // +---------------+ + // | +-----+ | + // O--->O S2S | | + // | | | | + // | +-----+ | + // +---------------+ + + // So we expect 1 entry in the generic in-port map of the blueprint: expect(Array.from(bpPortIn.getMapSubs()).length).toEqual(1); + // Fetch this automatically created in-port of the blueprint: const newPort = bpPortIn.getMapSubs().next().value; + // Test if the connections are as expected (see ASCII sketch above) expect(newPort.isConnectedWith(opPortIn)).toBeTruthy(); expect(opPortIn.isConnectedWith(newPort)).toBeTruthy(); + // Disconnect the port now: newPort.disconnectTo(opPortIn); + // The blueprint should look like this now: + // +---------------+ + // | +-----+ | + // | O S2S | | + // | | | | + // | +-----+ | + // +---------------+ + + // And test if the automatically created in-port has vanished: expect(Array.from(bpPortIn.getMapSubs()).length).toEqual(0); }); it("can have blueprint ports removed when deleting connected operator", () => { - // Test setup: - // [ -> [S2S] ] - + // bpS2S ports: + // In: string + // Out: string const bpS2S = landscapeModel.findBlueprint("ba24c37f-2b04-44b4-97ad-fd931c9ab77b")!; const bpNew = landscapeModel.createBlueprint({ @@ -119,26 +150,64 @@ describe("A new blueprint", () => { const bpPortIn = bpNew.getPortIn()!; const opPortIn = opNew.getPortIn()!; + // The blueprint should look like this now: + // +---------------+ + // | +-----+ | + // | O S2S | | + // | | | | + // | +-----+ | + // +---------------+ + + // Now, connect in-port of the operator with in-port of the blueprint opPortIn.connect(bpPortIn, true); + // The blueprint should look like this now: + // +---------------+ + // | +-----+ | + // O--->O S2S | | + // | | | | + // | +-----+ | + // +---------------+ + + // So we expect 1 entry in the generic in-port map of the blueprint: expect(Array.from(bpPortIn.getMapSubs()).length).toEqual(1); + // Fetch this automatically created in-port of the blueprint: const newPort = bpPortIn.getMapSubs().next().value; + // Test if the connections are as expected (see ASCII sketch above) expect(newPort.isConnectedWith(opPortIn)).toBeTruthy(); expect(opPortIn.isConnectedWith(newPort)).toBeTruthy(); + // Now we remove the operator entirely: opNew.destroy(); + // The blueprint should look like this now: + // +---------------+ + // | | + // | | + // | | + // | | + // +---------------+ + + // The port should no longer be connected expect(newPort.isConnected()).toBeFalsy(); + + // And test if the automatically created in-port has vanished: expect(Array.from(bpPortIn.getMapSubs()).length).toEqual(0); + + // newPort still exists (because it is referenced in this test), but should be disconnected from any operator }); it("can have generic operator ports created and removed dynamically", () => { - // Test setup: - // [S2S] -> [G2G] -> [S2S] - + // bpS2S ports: + // In: string + // Out: string const bpS2S = landscapeModel.findBlueprint("ba24c37f-2b04-44b4-97ad-fd931c9ab77b")!; + + // bpG2G ports: + // In: Generic("itemType") + // Out: Generic("itemType") const bpG2G = landscapeModel.findBlueprint("dc1aa556-d62e-4e07-adbb-53dc317481b0")!; const bpNew = landscapeModel.createBlueprint({ @@ -151,25 +220,82 @@ describe("A new blueprint", () => { const gens = new GenericSpecifications(["itemType"]); const opG2G = bpNew.createOperator("g2g", bpG2G, new PropertyAssignments([].values(), gens), gens); + // The blueprint should look like this now: + // +--------------------------+ + // | +-----+ +-----+ | + // | O S2S O | G2G | | + // | | 1 | | | | + // | +-----+ +-----+ | + // +--------------------------+ + + // Connect out-port of S2S1 with in-port of the generic operator: opS2S1.getPortOut()!.connect(opG2G.getPortIn()!, true); + // The blueprint should look like this now: + // +--------------------------+ + // | +-----+ +-----+ | + // | O S2S O--->O G2G O | + // | | 1 | | | | + // | +-----+ +-----+ | + // +--------------------------+ + + // We expect the generic operator to have an entry at its in- and out-ports: expect(Array.from(opG2G.getPortIn()!.getMapSubs()).length).toBe(1); expect(Array.from(opG2G.getPortOut()!.getMapSubs()).length).toBe(1); + // Create a second S2S operator: const opS2S2 = bpNew.createBlankOperator(bpS2S); + // The blueprint should look like this now: + // +-------------------------------------+ + // | +-----+ +-----+ +-----+ | + // | O S2S O--->O G2G O O S2S O | + // | | 1 | | | | 2 | | + // | +-----+ +-----+ +-----+ | + // +-------------------------------------+ + + // Connect in-port of the newly created S2S2 operator with the out-port of the generic operator: opS2S2.getPortIn()!.connect(opG2G.getPortOut()!.getMapSubs().next().value, true); + // The blueprint should look like this now: + // +-------------------------------------+ + // | +-----+ +-----+ +-----+ | + // | O S2S O--->O G2G O--->O S2S O | + // | | 1 | | | | 2 | | + // | +-----+ +-----+ +-----+ | + // +-------------------------------------+ + + // The generic operator remains unchanged: expect(Array.from(opG2G.getPortIn()!.getMapSubs()).length).toBe(1); expect(Array.from(opG2G.getPortOut()!.getMapSubs()).length).toBe(1); + // Disconnect connection between S2S1 and the generic operator: opS2S1.getPortOut()!.disconnectTo(opG2G.getPortIn()!.getMapSubs().next().value); + // The blueprint should look like this now: + // +-------------------------------------+ + // | +-----+ +-----+ +-----+ | + // | O S2S O O G2G O--->O S2S O | + // | | 1 | | | | 2 | | + // | +-----+ +-----+ +-----+ | + // +-------------------------------------+ + + // The generic operator remains unchanged, because its out-port is still connected to S2S2: expect(Array.from(opG2G.getPortIn()!.getMapSubs()).length).toBe(1); expect(Array.from(opG2G.getPortOut()!.getMapSubs()).length).toBe(1); + // Now also disconnect the second connection: opG2G.getPortOut()!.getMapSubs().next().value.disconnectTo(opS2S2.getPortIn()!); + // The blueprint should look like this now: + // +-------------------------------------+ + // | +-----+ +-----+ +-----+ | + // | O S2S O | G2G | O S2S O | + // | | 1 | | | | 2 | | + // | +-----+ +-----+ +-----+ | + // +-------------------------------------+ + + // Now that G2G is no longer connected at all its ports should have vanished: expect(Array.from(opG2G.getPortIn()!.getMapSubs()).length).toBe(0); expect(Array.from(opG2G.getPortOut()!.getMapSubs()).length).toBe(0); }); diff --git a/test/behavior/port.test.ts b/test/behavior/port.test.ts index b5a04162..4f80d551 100644 --- a/test/behavior/port.test.ts +++ b/test/behavior/port.test.ts @@ -38,7 +38,14 @@ describe("A port", () => { }); it("can be generated and deleted automatically", () => { + // bpS2S ports: + // In: string + // Out: string const bpS2S = landscapeModel.findBlueprint("ba24c37f-2b04-44b4-97ad-fd931c9ab77b")!; + + // bpG2G ports: + // In: Generic("itemType") + // Out: Generic("itemType") const bpG2G = landscapeModel.findBlueprint("dc1aa556-d62e-4e07-adbb-53dc317481b0")!; const bpNew2 = landscapeModel.createBlueprint({ @@ -51,20 +58,79 @@ describe("A port", () => { const opS2S2 = bpNew2.createBlankOperator(bpS2S); const opG2G = bpNew2.createBlankOperator(bpG2G); + // Connect out-port of first S2S operator with generic in-port of the G2G operator opS2S1.getPortOut()!.connect(opG2G.getPortIn()!, true); + + // The blueprint should look like this now: + // +-------------------------------+ + // | +---+ +---+ +---+ | + // | |S2S|--->|G2G| |S2S| | + // | | 1 | | | | 2 | | + // | +---+ +---+ +---+ | + // +-------------------------------+ + + // We expect the generic in-port to have a map now with one entry: expect(Array.from(opG2G.getPortIn()!.getMapSubs()).length).toEqual(1); + // The out-port of the generic operator should have one entry as well which is fetched here: const generatedPortOut = opG2G.getPortOut()!.getMapSubs().next().value; + // Now, connect out-port of S2S 1 with in-port of the generic operator again opS2S1.getPortOut()!.connect(opG2G.getPortIn()!, true); + + // The blueprint should look like this now: + // +-------------------------------+ + // | +---+ +---+ +---+ | + // | |S2S|-+->|G2G| |S2S| | + // | | 1 | | | | | 2 | | + // | | | +->| | | | | + // | +---+ +---+ +---+ | + // +-------------------------------+ + + // So we expect two entries in the in-port map now: expect(Array.from(opG2G.getPortIn()!.getMapSubs()).length).toEqual(2); + // Connect the out-port originating from the first connect with the in-port of the second S2S operator: generatedPortOut.connect(opS2S2.getPortIn()!, true); + // The blueprint should look like this now: + // +-------------------------------+ + // | +---+ +---+ +---+ | + // | |S2S|-+->|G2G|--->|S2S| | + // | | 1 | | | | | 2 | | + // | | | +->| | | | | + // | +---+ +---+ +---+ | + // +-------------------------------+ + + // Now, we disconnect all connections of the out-port of the first S2S operator opS2S1.getPortOut()!.disconnectAll(); + + // The blueprint should look like this now: + // +-------------------------------+ + // | +---+ +---+ +---+ | + // | |S2S| |G2G|--->|S2S| | + // | | 1 | | | | 2 | | + // | | | | | | | | + // | +---+ +---+ +---+ | + // +-------------------------------+ + + // So the generic map should only have 1 remaining port, + // the second should have been deleted because it has not been connected: expect(Array.from(opG2G.getPortIn()!.getMapSubs()).length).toEqual(1); + // Now, also disonnect the remaining entry: generatedPortOut.disconnectAll(); + + // The blueprint should look like this now: + // +-------------------------------+ + // | +---+ +---+ +---+ | + // | |S2S| |G2G| |S2S| | + // | | 1 | | | | 2 | | + // | | | | | | | | + // | +---+ +---+ +---+ | + // +-------------------------------+ + + // So the map has zero entries now: expect(Array.from(opG2G.getPortIn()!.getMapSubs()).length).toEqual(0); }); diff --git a/test/behavior/stream.test.ts b/test/behavior/stream.test.ts index 68fd2689..1d6ab980 100644 --- a/test/behavior/stream.test.ts +++ b/test/behavior/stream.test.ts @@ -127,25 +127,93 @@ describe("A stream port", () => { direction: PortDirection.Out, }); + // op2OS ports: + // In: trigger + // Out: Stream(portA: string, portB: number) const op2OS = bp.createBlankOperator(landscapeModel.findBlueprint("b444f701-59fc-43a8-8cdc-8bcce9dd471d")!); + + // opG2G ports: + // In: Generic("itemType") + // Out: Generic("itemType") const opG2G = bp.createBlankOperator(landscapeModel.findBlueprint("dc1aa556-d62e-4e07-adbb-53dc317481b0")!); + // Connect out-port Stream(portA: string) of op2OS with generic in-port of opG2G op2OS.getPortOut()!.getStreamSub().findMapSub("portA").connect(opG2G.getPortIn()!, true); + + // We expect opG2G to have an out-port of type Stream(portA: string) now, + // because it is linked via the generic identifier "itemType" with the in-port we just connected + + // Connect this out-port with the blueprint out-port Array.from(opG2G.getPortOut()!.getMapSubs()).find((port) => port.getName().indexOf("portA") !== -1)!.connect(bpOut, true); + + // Connect out-port Stream(portB: number) of op2OS with generic in-port of opG2G op2OS.getPortOut()!.getStreamSub().findMapSub("portB").connect(opG2G.getPortIn()!, true); + + // We expect opG2G to have a second entry in its out-port Stream map: + // Stream(portA: string, portB: number) + + // Again, connect the second out-port of opG2G with the blueprint out-port Array.from(opG2G.getPortOut()!.getMapSubs()).find((port) => port.getName().indexOf("portB") !== -1)!.connect(bpOut, true); + // The blueprint should look like this now: + + // +----------------------+ + // | +---+ +---+ | + // | |2OS|===>|G2G|===>| (portA) + // | | |===>| |===>| (portB) + // | +---+ +---+ | + // +----------------------+ + + // The blueprint out-port should have the following form: + // Map(a: Stream(portA: string, portB: number)) + + // So we expect the blueprint out-port to have one entry in the top map (above called "a"): expect(Array.from(bpOut.getMapSubs()).length).toEqual(1); + + // We expect this one entry to be of type stream wrapped around a map with two entries ("portA" and "portB"): expect(Array.from(Array.from(bpOut.getMapSubs()).find((port) => port.getType().isStream())!.getStreamSub().getMapSubs()).length).toEqual(2); + // Now, disconnect portB of G2G from blueprint out-port Array.from(opG2G.getPortOut()!.getMapSubs()).find((port) => port.getName().indexOf("portB") !== -1)!.disconnectAll(); + // The blueprint should look like this now: + + // +----------------------+ + // | +---+ +---+ | + // | |2OS|===>|G2G|===>| (portA) + // | | |===>| | | + // | +---+ +---+ | + // +----------------------+ + + // The blueprint out-port should have the following form: + // Map(a: Stream(portA: string)) + + // So we expect the blueprint out-port to still have a map with one entry (above called "a") expect(Array.from(bpOut.getMapSubs()).length).toEqual(1); + + // But now, there should only be 1 entry in the wrapped map ("portA") expect(Array.from(Array.from(bpOut.getMapSubs()).find((port) => port.getType().isStream())!.getStreamSub().getMapSubs()).length).toEqual(1); + // Now, connect out-port portB of operator G2G again: Array.from(opG2G.getPortOut()!.getMapSubs()).find((port) => port.getName().indexOf("portB") !== -1)!.connect(bpOut, true); + // The blueprint should again look like this now: + + // +----------------------+ + // | +---+ +---+ | + // | |2OS|===>|G2G|===>| (portA) + // | | |===>| |===>| (portB) + // | +---+ +---+ | + // +----------------------+ + + // And the blueprint out-port should have the following form: + // Map(a: Stream(portA: string, portB: number)) + + // This of course should still be the case (like above): expect(Array.from(bpOut.getMapSubs()).length).toEqual(1); + + // This is actually what this test should make sure: + // The out-port "portB" of operator G2G should be within the same stream as "portA" and not be a separate entry in the out-port map: expect(Array.from(Array.from(bpOut.getMapSubs()).find((port) => port.getType().isStream())!.getStreamSub().getMapSubs()).length).toEqual(2); }); @@ -166,23 +234,74 @@ describe("A stream port", () => { direction: PortDirection.Out, }); + // opS2S ports: + // In: string + // Out: string const opS2S = bp.createBlankOperator(landscapeModel.findBlueprint("ba24c37f-2b04-44b4-97ad-fd931c9ab77b")!); + + // opSS2S ports: + // In: Stream(string) + // Out: string const opSS2S = bp.createBlankOperator(landscapeModel.findBlueprint("605be7c4-b4df-41be-998f-82f7c23e518e")!); + + // opG2G ports: + // In: Generic("itemType") + // Out: Generic("itemType") const opG2G = bp.createBlankOperator(landscapeModel.findBlueprint("384a2731-745b-4a7f-9972-ac2f4c31cf93")!); + // The blueprint should look like this now: + // +--------------------------------------+ + // | +-----+ +------+ +-----+ | + // | O S2S O O SS2S O | G2G | | + // | | | | | | | | + // | +-----+ +------+ +-----+ | + // +--------------------------------------+ + + // Connect operators: bpIn.connect(opS2S.getPortIn()!, true); opS2S.getPortOut()!.connect(bpOut, true); opSS2S.getPortOut()!.connect(bpOut, true); bpIn.getMapSubs().next().value.connect(opG2G.getPortIn()!, true); opG2G.getPortOut()!.connect(opSS2S.getPortIn()!.getStreamSub(), true); + // The blueprint should look like this now: + // +------------------------------------------------------------+ + // | | + // | +-------------------------------------------->O + // | | +------------------------>O + // | | | +===============+ | + // | +-----+ | +------+ | | +-----+ | | + // O--+-->O S2S O-+ +===>O SS2S O---+ | +->O G2G O==+ | + // | | | | | | | | | | | | + // | | +-----+ | +------+ | | +-----+ | + // | | +===================+ | | + // | +---------------------------------------+ | + // | | + // +------------------------------------------------------------+ + expect(opG2G.getPortOut()!.getTypeIdentifier()).toEqual(TypeIdentifier.Map); expect(Array.from(opG2G.getPortOut()!.getMapSubs()).length).toEqual(1); + const mapEntry = opG2G.getPortOut()!.getMapSubs().next().value; + + // Since the in-port of SS2S is a stream port, we expect the generic out-port to have inferred to be a stream as well: expect(mapEntry.getTypeIdentifier()).toEqual(TypeIdentifier.Stream); + + // Automatically created ports should be maps: expect(mapEntry.getStreamSub().getTypeIdentifier()).toEqual(TypeIdentifier.Map); + + // There should be 1 map entry: expect(Array.from(mapEntry.getStreamSub().getMapSubs()).length).toEqual(1); + + // Expect a connection between the base ports (see double-line in the ASCII sketch above) (1) expect(mapEntry.getStreamSub().getMapSubs().next().value.isConnectedWith(opSS2S.getPortIn()!.getStreamSub())).toEqual(true); + + /* + * (1) If two ports of the form Stream(x) and Stream(y) are connected, we do not expect this: + * Stream(x) --> Stream(y) + * but this: + * x --> y + */ }); it("infers the correct stream depth for converts", () => {