From 09e020387be86c5b3f65475f0724927c5cca8e8d Mon Sep 17 00:00:00 2001 From: Tomer Date: Thu, 22 Mar 2018 11:24:45 +0200 Subject: [PATCH 01/28] convert gotos to conditional form --- .../src/neo/IO/legacy/goto-elimination.js | 32 +++++++++ .../IO/legacy/goto-elimination.spec.js | 69 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js create mode 100644 packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js new file mode 100644 index 000000000..9a03599c6 --- /dev/null +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -0,0 +1,32 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +export function transformToConditional(goto) { + return ([ + { + command: "if", + target: goto.command === "gotoIf" ? goto.target : "true" + }, + { + command: "goto", + target: goto.command === "gotoIf" ? goto.value : goto.target + }, + { + command: "end" + } + ]); +} diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js new file mode 100644 index 000000000..d735124b7 --- /dev/null +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -0,0 +1,69 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import { transformToConditional } from "../../../IO/legacy/goto-elimination"; + +describe("goto conditional conversion", () => { + it("should convert unconditional goto to conditional goto", () => { + const procedure = [ + { + command: "goto", + target: "label_1" + } + ]; + expect(transformToConditional(procedure[0])).toEqual([ + { + command: "if", + target: "true" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + } + ]); + }); + it("should convert compound goto to conditional goto", () => { + const procedure = [ + { + command: "gotoIf", + target: "condition", + value: "label_1" + } + ]; + expect(transformToConditional(procedure[0])).toEqual([ + { + command: "if", + target: "condition" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + } + ]); + }); +}); + +describe("goto elimination transformation", () => { + it("should eliminate goto before label statement", () => { + }); +}); From 82cdd43f79b39afc5775e43b9ab267ee5697bdb9 Mon Sep 17 00:00:00 2001 From: Tomer Date: Thu, 22 Mar 2018 12:08:41 +0200 Subject: [PATCH 02/28] added elimination transformation tests --- .../IO/legacy/goto-elimination.spec.js | 126 +++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index d735124b7..ace75476e 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -import { transformToConditional } from "../../../IO/legacy/goto-elimination"; +import { transformToConditional, eliminate } from "../../../IO/legacy/goto-elimination"; describe("goto conditional conversion", () => { it("should convert unconditional goto to conditional goto", () => { @@ -65,5 +65,129 @@ describe("goto conditional conversion", () => { describe("goto elimination transformation", () => { it("should eliminate goto before label statement", () => { + const procedure = [ + { + command: "statement" + }, + { + command: "if", + target: "true" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + } + ]; + expect(eliminate(procedure)).toEqual([ + { + command: "statement" + }, + { + command: "if", + target: "!true" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + } + ]); + }); + it("should eliminate goto after label statement", () => { + const procedure = [ + { + command: "statement" + }, + + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "if", + target: "true" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + } + ]; + expect(eliminate(procedure)).toEqual([ + { + command: "statement" + }, + + { + command: "statement" + }, + { + command: "do" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "endDo", + target: "true" + }, + { + command: "statement" + } + ]); }); }); From baacf4e70006e71aac9178f1712b32c529a4aa6e Mon Sep 17 00:00:00 2001 From: Tomer Date: Thu, 22 Mar 2018 14:29:21 +0200 Subject: [PATCH 03/28] added outward movement transformation tests --- .../IO/legacy/goto-elimination.spec.js | 204 +++++++++++++++++- 1 file changed, 203 insertions(+), 1 deletion(-) diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index ace75476e..5c8024dc8 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -import { transformToConditional, eliminate } from "../../../IO/legacy/goto-elimination"; +import { transformToConditional, eliminate, transformOutward } from "../../../IO/legacy/goto-elimination"; describe("goto conditional conversion", () => { it("should convert unconditional goto to conditional goto", () => { @@ -191,3 +191,205 @@ describe("goto elimination transformation", () => { ]); }); }); + +describe("outward movement transformation", () => { + it("should move goto out of a while", () => { + const procedure = [ + { + command: "statement" + }, + { + command: "while", + target: "condition" + }, + { + command: "statement" + }, + { + command: "if", + target: "condition2" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + } + ]; + expect(transformOutward(procedure)).toEqual([ + { + command: "statement" + }, + { + command: "while", + target: "condition" + }, + { + command: "statement" + }, + { + command: "storeValue", + target: "condition2", + value: "label_1" + }, + { + command: "if", + target: "${label_1}" + }, + { + command: "break" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "if", + target: "${label_1}" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + } + ]); + }); + it("should move goto out of an if", () => { + const procedure = [ + { + command: "statement" + }, + { + command: "if", + target: "condition" + }, + { + command: "statement" + }, + { + command: "if", + target: "condition2" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + } + ]; + expect(transformOutward(procedure)).toEqual([ + { + command: "statement" + }, + { + command: "if", + target: "condition" + }, + { + command: "statement" + }, + { + command: "storeValue", + target: "condition2", + value: "label_1" + }, + { + command: "if", + target: "!${label_1}" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "end" + }, + { + command: "if", + target: "${label_1}" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + } + ]); + }); +}); From 864be6eeabd85d0e6fa81deb8aac4b54230eb8be Mon Sep 17 00:00:00 2001 From: Tomer Date: Thu, 22 Mar 2018 14:39:13 +0200 Subject: [PATCH 04/28] label elimination test --- .../IO/legacy/goto-elimination.spec.js | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index 5c8024dc8..a78c47cb3 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -import { transformToConditional, eliminate, transformOutward } from "../../../IO/legacy/goto-elimination"; +import { transformToConditional, eliminateGoto, eliminateLabel, transformOutward } from "../../../IO/legacy/goto-elimination"; describe("goto conditional conversion", () => { it("should convert unconditional goto to conditional goto", () => { @@ -97,7 +97,7 @@ describe("goto elimination transformation", () => { command: "statement" } ]; - expect(eliminate(procedure)).toEqual([ + expect(eliminateGoto(procedure)).toEqual([ { command: "statement" }, @@ -160,7 +160,7 @@ describe("goto elimination transformation", () => { command: "statement" } ]; - expect(eliminate(procedure)).toEqual([ + expect(eliminateGoto(procedure)).toEqual([ { command: "statement" }, @@ -192,6 +192,30 @@ describe("goto elimination transformation", () => { }); }); +describe("label elimination", () => { + const label = { + command: "label", + target: "label_1" + }; + const procedure = [ + { + command: "statement" + }, + label, + { + command: "statement" + } + ]; + expect(eliminateLabel(procedure, label)).toEqual([ + { + command: "statement" + }, + { + command: "statement" + } + ]); +}); + describe("outward movement transformation", () => { it("should move goto out of a while", () => { const procedure = [ From 8315f4a50897e61c37c51fdeabc9afe303c7ff2d Mon Sep 17 00:00:00 2001 From: Tomer Date: Thu, 22 Mar 2018 15:55:49 +0200 Subject: [PATCH 05/28] conditional goto elimination --- .../src/neo/IO/legacy/goto-elimination.js | 26 +++++++++++ .../IO/legacy/goto-elimination.spec.js | 46 ++++++++++--------- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 9a03599c6..0001ff354 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -30,3 +30,29 @@ export function transformToConditional(goto) { } ]); } + +export function eliminateLabel(procedure, label) { + return procedure.filter(p => p !== label); +} + +export function eliminateGoto(procedure, goto, label) { + // only for siblings + const gotoIndex = procedure.indexOf(goto), labelIndex = procedure.indexOf(label); + if (gotoIndex < labelIndex) { + // eliminate using conditional + const ifIndex = gotoIndex - 1; + const p = [...procedure]; + p[ifIndex] = Object.assign({...p[ifIndex]}, {target: `!${p[ifIndex].target}`}); + p.splice(labelIndex, 0, { command: "end" }); + p.splice(gotoIndex, 2); + + return p; + } else { + // eliminate using do while + } +} + +function isConditionalGoto(procedure, goto) { + const index = procedure.indexOf(goto); + return (procedure[index - 1].command === "if" && procedure[index + 1] === "end"); +} diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index a78c47cb3..21127b97b 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -97,7 +97,7 @@ describe("goto elimination transformation", () => { command: "statement" } ]; - expect(eliminateGoto(procedure)).toEqual([ + expect(eliminateGoto(procedure, procedure[2], procedure[7])).toEqual([ { command: "statement" }, @@ -193,27 +193,29 @@ describe("goto elimination transformation", () => { }); describe("label elimination", () => { - const label = { - command: "label", - target: "label_1" - }; - const procedure = [ - { - command: "statement" - }, - label, - { - command: "statement" - } - ]; - expect(eliminateLabel(procedure, label)).toEqual([ - { - command: "statement" - }, - { - command: "statement" - } - ]); + it("should eliminate labels", () => { + const label = { + command: "label", + target: "label_1" + }; + const procedure = [ + { + command: "statement" + }, + label, + { + command: "statement" + } + ]; + expect(eliminateLabel(procedure, label)).toEqual([ + { + command: "statement" + }, + { + command: "statement" + } + ]); + }); }); describe("outward movement transformation", () => { From 4798990b0dadd67a17e6ab859d5d3c68f5800690 Mon Sep 17 00:00:00 2001 From: Tomer Date: Thu, 22 Mar 2018 16:22:39 +0200 Subject: [PATCH 06/28] repitition goto elimination --- .../selenium-ide/src/neo/IO/legacy/goto-elimination.js | 8 +++++--- .../src/neo/__test__/IO/legacy/goto-elimination.spec.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 0001ff354..840858cf4 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -37,19 +37,21 @@ export function eliminateLabel(procedure, label) { export function eliminateGoto(procedure, goto, label) { // only for siblings + const p = [...procedure]; const gotoIndex = procedure.indexOf(goto), labelIndex = procedure.indexOf(label); if (gotoIndex < labelIndex) { // eliminate using conditional const ifIndex = gotoIndex - 1; - const p = [...procedure]; p[ifIndex] = Object.assign({...p[ifIndex]}, {target: `!${p[ifIndex].target}`}); p.splice(labelIndex, 0, { command: "end" }); p.splice(gotoIndex, 2); - - return p; } else { // eliminate using do while + const ifIndex = gotoIndex - 1; + p.splice(ifIndex, 3, { command: "endDo", target: p[ifIndex].target }); + p.splice(labelIndex, 0, { command: "do" }); } + return p; } function isConditionalGoto(procedure, goto) { diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index 21127b97b..9ea337ac1 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -160,7 +160,7 @@ describe("goto elimination transformation", () => { command: "statement" } ]; - expect(eliminateGoto(procedure)).toEqual([ + expect(eliminateGoto(procedure, procedure[6], procedure[2])).toEqual([ { command: "statement" }, From eb6db87cf098617b920634968cfe4218d385e38a Mon Sep 17 00:00:00 2001 From: Tomer Date: Thu, 22 Mar 2018 16:34:36 +0200 Subject: [PATCH 07/28] inward movement tests --- .../src/neo/__test__/IO/legacy/goto-elimination.spec.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index 9ea337ac1..c5cde41a1 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -419,3 +419,10 @@ describe("outward movement transformation", () => { ]); }); }); + +describe("inward movement transformation", () => { + it("should move goto into a while", () => { + }); + it("should move goto into an if", () => { + }); +}); From 3b87af8b5f602a47465fb278a9c21409f6557aa6 Mon Sep 17 00:00:00 2001 From: Tomer Date: Thu, 22 Mar 2018 17:51:10 +0200 Subject: [PATCH 08/28] block level helper functions --- .../src/neo/IO/legacy/goto-elimination.js | 53 +++++++++++++++++++ .../IO/legacy/goto-elimination.spec.js | 2 +- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 840858cf4..3b42667cd 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -54,6 +54,59 @@ export function eliminateGoto(procedure, goto, label) { return p; } +export function transformOutward(procedure, goto) { + const block = findEnclosingBlock(procedure, goto); + const end = findBlockClose(procedure, block); + const p = [...procedure]; + if (block !== "if") { + // outward loop movement + } else { + // outward conditional movement + } + return p; +} + +function findEnclosingBlock(procedure, goto) { + // remember to skip the enclosing if + for (let i = procedure.indexOf(goto) - 2; i >= 0; i--) { + if (isBlock(procedure[i])) return procedure[i]; + } +} + +function findBlockClose(procedure, block) { + let level = 1, index = procedure.indexOf(block); + while (level !== 0) { + index++; + if (isBlock(procedure[index])) { + level++; + } else if (isBlockEnd(procedure[index])) { + level--; + } + } + return procedure[index]; +} + +function isBlock(statement) { + switch(statement.command) { + case "if": + case "do": + case "while": + return true; + default: + return false; + } +} + +function isBlockEnd(statement) { + switch(statement.command) { + case "end": + case "endDo": + return true; + default: + return false; + } +} + function isConditionalGoto(procedure, goto) { const index = procedure.indexOf(goto); return (procedure[index - 1].command === "if" && procedure[index + 1] === "end"); diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index c5cde41a1..a31553e95 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -262,7 +262,7 @@ describe("outward movement transformation", () => { command: "statement" } ]; - expect(transformOutward(procedure)).toEqual([ + expect(transformOutward(procedure, procedure[4])).toEqual([ { command: "statement" }, From 990cf5cada9ab837823355eb7e0a9dfc1a6baa4e Mon Sep 17 00:00:00 2001 From: Tomer Date: Sun, 1 Apr 2018 11:51:52 +0300 Subject: [PATCH 09/28] inward movement tests --- .../IO/legacy/goto-elimination.spec.js | 107 +++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index a31553e95..b0a027cb1 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -import { transformToConditional, eliminateGoto, eliminateLabel, transformOutward } from "../../../IO/legacy/goto-elimination"; +import { transformToConditional, eliminateGoto, eliminateLabel, transformOutward, transformInward } from "../../../IO/legacy/goto-elimination"; describe("goto conditional conversion", () => { it("should convert unconditional goto to conditional goto", () => { @@ -422,7 +422,110 @@ describe("outward movement transformation", () => { describe("inward movement transformation", () => { it("should move goto into a while", () => { + const procedure = [ + { + command: "statement" + }, + { + command: "if", + target: "condition" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "while", + target: "condition2" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "statement" + } + ]; + expect(transformInward(procedure)).toEqual([ + { + command: "statement" + }, + { + command: "storeValue", + target: "condition", + value: "label_1" + }, + { + command: "if", + target: "!${label_1}" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "while", + target: "${label_1} || condition2" + }, + { + command: "if", + target: "${label_1}" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "storeValue", + target: "false", + value: "label_1" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "statement" + } + ]); + }); + it("should move goto into an if's then", () => { }); - it("should move goto into an if", () => { + it("should move goto into an if's else", () => { }); }); From d254b6315d39dd711750e2f1e7a25eeb501547d5 Mon Sep 17 00:00:00 2001 From: Tomer Date: Sun, 1 Apr 2018 13:31:00 +0300 Subject: [PATCH 10/28] loop outward movement transformation implementation --- .../src/neo/IO/legacy/goto-elimination.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 3b42667cd..c68e247f1 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -60,12 +60,29 @@ export function transformOutward(procedure, goto) { const p = [...procedure]; if (block !== "if") { // outward loop movement + transformOutwardLoop(p, goto, block, end); } else { // outward conditional movement } return p; } +export function transformOutwardLoop(p, goto, block, end) { + const ifIndex = p.indexOf(goto) - 1; + p.splice(ifIndex, 3, + { command: "storeValue", target: p[ifIndex].target, value: goto.target }, + { command: "if", target: `\${${goto.target}}` }, + { command: "break" }, + { command: "end" } + ); + const endIndex = p.indexOf(end); + p.splice(endIndex + 1, 0, + { command: "if", target: `\${${goto.target}}` }, + { command: "goto", target: goto.target }, + { command: "end" } + ); +} + function findEnclosingBlock(procedure, goto) { // remember to skip the enclosing if for (let i = procedure.indexOf(goto) - 2; i >= 0; i--) { From 87bf8059a9d96d30716ad0d966887c0f266cc44b Mon Sep 17 00:00:00 2001 From: Tomer Date: Sun, 1 Apr 2018 13:47:15 +0300 Subject: [PATCH 11/28] conditional outward movement transformation implementation --- .../src/neo/IO/legacy/goto-elimination.js | 20 +++++++++++++++++-- .../IO/legacy/goto-elimination.spec.js | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index c68e247f1..c6f2a8a46 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -58,16 +58,17 @@ export function transformOutward(procedure, goto) { const block = findEnclosingBlock(procedure, goto); const end = findBlockClose(procedure, block); const p = [...procedure]; - if (block !== "if") { + if (block.command !== "if") { // outward loop movement transformOutwardLoop(p, goto, block, end); } else { // outward conditional movement + transformOutwardConditional(p, goto, block, end); } return p; } -export function transformOutwardLoop(p, goto, block, end) { +function transformOutwardLoop(p, goto, block, end) { const ifIndex = p.indexOf(goto) - 1; p.splice(ifIndex, 3, { command: "storeValue", target: p[ifIndex].target, value: goto.target }, @@ -83,6 +84,21 @@ export function transformOutwardLoop(p, goto, block, end) { ); } +function transformOutwardConditional(p, goto, block, end) { + const ifIndex = p.indexOf(goto) - 1; + p.splice(ifIndex, 3, + { command: "storeValue", target: p[ifIndex].target, value: goto.target }, + { command: "if", target: `!\${${goto.target}}` } + ); + const endIndex = p.indexOf(end); + p.splice(endIndex + 1, 0, + { command: "end" }, + { command: "if", target: `\${${goto.target}}` }, + { command: "goto", target: goto.target }, + { command: "end" } + ); +} + function findEnclosingBlock(procedure, goto) { // remember to skip the enclosing if for (let i = procedure.indexOf(goto) - 2; i >= 0; i--) { diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index b0a027cb1..fc1e168d1 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -363,7 +363,7 @@ describe("outward movement transformation", () => { command: "statement" } ]; - expect(transformOutward(procedure)).toEqual([ + expect(transformOutward(procedure, procedure[4])).toEqual([ { command: "statement" }, From 748caf0f99e09d47ec427500fd98c4e2f24758c7 Mon Sep 17 00:00:00 2001 From: Tomer Date: Sun, 1 Apr 2018 14:24:59 +0300 Subject: [PATCH 12/28] inward movement tests --- .../IO/legacy/goto-elimination.spec.js | 230 +++++++++++++++++- 1 file changed, 229 insertions(+), 1 deletion(-) diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index fc1e168d1..0d89988bd 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -464,7 +464,7 @@ describe("inward movement transformation", () => { command: "statement" } ]; - expect(transformInward(procedure)).toEqual([ + expect(transformInward(procedure, procedure[2])).toEqual([ { command: "statement" }, @@ -525,7 +525,235 @@ describe("inward movement transformation", () => { ]); }); it("should move goto into an if's then", () => { + const procedure = [ + { + command: "statement" + }, + { + command: "if", + target: "condition" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "if", + target: "condition2" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "else" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "statement" + } + ]; + expect(transformInward(procedure, procedure[2])).toEqual([ + { + command: "statement" + }, + { + command: "storeValue", + target: "condition", + value: "label_1" + }, + { + command: "if", + target: "!${label_1}" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "if", + target: "${label_1} || condition2" + }, + { + command: "if", + target: "${label_1}" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "else" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "statement" + } + ]); }); it("should move goto into an if's else", () => { + const procedure = [ + { + command: "statement" + }, + { + command: "if", + target: "condition" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "if", + target: "condition2" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "else" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "statement" + } + ]; + expect(transformInward(procedure, procedure[2])).toEqual([ + { + command: "statement" + }, + { + command: "storeValue", + target: "condition", + value: "label_1" + }, + { + command: "if", + target: "!${label_1}" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "if", + target: "!${label_1} && condition2" + }, + { + command: "statement" + }, + { + command: "statement" + }, + { + command: "else" + }, + { + command: "if", + target: "${label_1}" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "statement" + } + ]); }); }); From 9114ead95e6d6e884345de78785e92d2c85b5599 Mon Sep 17 00:00:00 2001 From: Tomer Date: Sun, 1 Apr 2018 15:58:50 +0300 Subject: [PATCH 13/28] lift transformation --- .../src/neo/IO/legacy/goto-elimination.js | 40 ++++++++++ .../IO/legacy/goto-elimination.spec.js | 76 ++++++++++++++++++- 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index c6f2a8a46..b848c6cd2 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -99,6 +99,46 @@ function transformOutwardConditional(p, goto, block, end) { ); } +export function transformInward(procedure, goto) { +} + +export function lift(procedure, goto, label) { + const p = [ + { command: "storeValue", target: "false", value: goto.target }, + ...procedure + ]; + const labelIndex = p.indexOf(label); + p.splice(labelIndex, 0, + { + command: "do" + }, + { + command: "if", + target: `\${${goto.target}}` + }, + { + command: "goto", + target: goto.target + }, + { + command: "end" + } + ); + const ifIndex = p.indexOf(goto) - 1; + p.splice(ifIndex, 3, + { + command: "storeValue", + target: "condition", + value: goto.target + }, + { + command: "endDo", + target: `\${${goto.target}}` + } + ); + return p; +} + function findEnclosingBlock(procedure, goto) { // remember to skip the enclosing if for (let i = procedure.indexOf(goto) - 2; i >= 0; i--) { diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index 0d89988bd..ca978e4b0 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -import { transformToConditional, eliminateGoto, eliminateLabel, transformOutward, transformInward } from "../../../IO/legacy/goto-elimination"; +import { transformToConditional, eliminateGoto, eliminateLabel, transformOutward, transformInward, lift } from "../../../IO/legacy/goto-elimination"; describe("goto conditional conversion", () => { it("should convert unconditional goto to conditional goto", () => { @@ -757,3 +757,77 @@ describe("inward movement transformation", () => { ]); }); }); + +describe("lifting transformation", () => { + it("should lift the goto above it's label", () => { + const procedure = [ + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "if", + target: "condition" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "statement" + } + ]; + expect(lift(procedure, procedure[4], procedure[1])).toEqual([ + { + command: "storeValue", + target: "false", + value: "label_1" + }, + { + command: "statement" + }, + { + command: "do" + }, + { + command: "if", + target: "${label_1}" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "storeValue", + target: "condition", + value: "label_1" + }, + { + command: "endDo", + target: "${label_1}" + }, + { + command: "statement" + } + ]); + }); +}); From 3e842d67a5b18508fb1152ac1f3aabf5542174b6 Mon Sep 17 00:00:00 2001 From: Tomer Date: Mon, 2 Apr 2018 10:10:07 +0300 Subject: [PATCH 14/28] goto elimination stub --- .../src/neo/IO/legacy/goto-elimination.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index b848c6cd2..3275060de 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -15,6 +15,38 @@ // specific language governing permissions and limitations // under the License. +export function eliminate(procedure) { + let p = [...procedure]; + + // Change all gotos to conditionals + const unconditionalGotos = p.filter(statement => (statement.command === "goto" || statement.command === "gotoIf")); + unconditionalGotos.forEach(goto => { + const index = p.indexOf(goto); + p.splice(index, 1, transformToConditional(goto)); + }); + + const labels = p.filter(statement => statement.command === "label"); + const gotos = p.filter(statement => statement.command === "goto"); + + // start eliminating + while (gotos.length) { + const goto = gotos[0]; + const label = findMatchingLabel(labels, goto); + + // Make goto and label directly related + + // Make goto and label siblings + + // goto and label are siblings + // eliminate goto + eliminateGoto(p, goto, label); + // remove it from the list of gotos + gotos.shift(); + } + + return p; +} + export function transformToConditional(goto) { return ([ { @@ -159,6 +191,10 @@ function findBlockClose(procedure, block) { return procedure[index]; } +function findMatchingLabel(listOfLabels, goto) { + return listOfLabels.filter(statement => (statement.command === "label" && statement.target === goto.target)); +} + function isBlock(statement) { switch(statement.command) { case "if": From 4decabcb588e2af44faf41e09fa58f053c928bed Mon Sep 17 00:00:00 2001 From: Tomer Date: Mon, 2 Apr 2018 11:24:49 +0300 Subject: [PATCH 15/28] automatically replace undefines in input --- .../src/neo/__test__/models/Command.spec.js | 11 +++++++++++ packages/selenium-ide/src/neo/models/Command.js | 8 ++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/selenium-ide/src/neo/__test__/models/Command.spec.js b/packages/selenium-ide/src/neo/__test__/models/Command.spec.js index 536b273d5..dd2fa085e 100644 --- a/packages/selenium-ide/src/neo/__test__/models/Command.spec.js +++ b/packages/selenium-ide/src/neo/__test__/models/Command.spec.js @@ -41,6 +41,17 @@ describe("Command", () => { command.setCommand("test invalid"); expect(command.isValid).toBeFalsy(); }); + it("should replace undefined input with empty strings", () => { + const command = new Command(); + command.setCommand(); + command.setTarget(); + command.setValue(); + command.setComment(); + expect(command.command).toEqual(""); + expect(command.target).toEqual(""); + expect(command.value).toEqual(""); + expect(command.comment).toEqual(""); + }); it("should set the target", () => { const command = new Command(); command.setTarget("a"); diff --git a/packages/selenium-ide/src/neo/models/Command.js b/packages/selenium-ide/src/neo/models/Command.js index 71df0bfd1..e1613c0e6 100644 --- a/packages/selenium-ide/src/neo/models/Command.js +++ b/packages/selenium-ide/src/neo/models/Command.js @@ -43,23 +43,23 @@ export default class Command { } @action.bound setComment(comment) { - this.comment = comment; + this.comment = comment || ""; } @action.bound setCommand(command) { if (CommandsValues[command]) { this.command = CommandsValues[command]; } else { - this.command = command; + this.command = command || ""; } } @action.bound setTarget(target) { - this.target = target; + this.target = target || ""; } @action.bound setValue(value) { - this.value = value.replace(/\n/g, "\\n"); + this.value = value ? value.replace(/\n/g, "\\n") : ""; } static fromJS = function(jsRep) { From af272ac55d002626cc42a664ebe8894b0b255e23 Mon Sep 17 00:00:00 2001 From: Tomer Date: Mon, 2 Apr 2018 14:00:04 +0300 Subject: [PATCH 16/28] eliminate gotos on migration --- packages/selenium-ide/src/neo/IO/legacy/migrate.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/migrate.js b/packages/selenium-ide/src/neo/IO/legacy/migrate.js index b53f0d4d4..e6bd58f12 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/migrate.js +++ b/packages/selenium-ide/src/neo/IO/legacy/migrate.js @@ -19,6 +19,7 @@ import convert from "xml-js"; import xmlescape from "xml-escape"; import xmlunescape from "unescape"; import JSZip from "jszip"; +import { eliminate } from "./goto-elimination"; export function migrateProject(zippedData) { return JSZip.loadAsync(zippedData).then(zip => { @@ -100,13 +101,13 @@ export function migrateTestCase(data) { { id: data, name: result.html.body.table.thead.tr.td._text, - commands: result.html.body.table.tbody.tr.filter(row => !/^wait/.test(row.td[0]._text)).map(row => ( + commands: eliminate(result.html.body.table.tbody.tr.filter(row => !/^wait/.test(row.td[0]._text)).map(row => ( { command: row.td[0]._text && row.td[0]._text.replace("AndWait", "") || "", target: xmlunescape(parseTarget(row.td[1])), value: xmlunescape(row.td[2]._text || "") } - )) + ))) } ], suites: [] From 96b95a36325482608f9c9d6b51ef2f3108386885 Mon Sep 17 00:00:00 2001 From: Tomer Date: Mon, 2 Apr 2018 14:00:22 +0300 Subject: [PATCH 17/28] keep the same goto pointer throughout the elimination --- .../src/neo/IO/legacy/goto-elimination.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 3275060de..92f8e95a3 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -22,7 +22,7 @@ export function eliminate(procedure) { const unconditionalGotos = p.filter(statement => (statement.command === "goto" || statement.command === "gotoIf")); unconditionalGotos.forEach(goto => { const index = p.indexOf(goto); - p.splice(index, 1, transformToConditional(goto)); + p.splice(index, 1, ...transformToConditional(goto)); }); const labels = p.filter(statement => statement.command === "label"); @@ -39,7 +39,7 @@ export function eliminate(procedure) { // goto and label are siblings // eliminate goto - eliminateGoto(p, goto, label); + p = eliminateGoto(p, goto, label); // remove it from the list of gotos gotos.shift(); } @@ -111,7 +111,7 @@ function transformOutwardLoop(p, goto, block, end) { const endIndex = p.indexOf(end); p.splice(endIndex + 1, 0, { command: "if", target: `\${${goto.target}}` }, - { command: "goto", target: goto.target }, + goto, { command: "end" } ); } @@ -126,7 +126,7 @@ function transformOutwardConditional(p, goto, block, end) { p.splice(endIndex + 1, 0, { command: "end" }, { command: "if", target: `\${${goto.target}}` }, - { command: "goto", target: goto.target }, + goto, { command: "end" } ); } @@ -148,10 +148,7 @@ export function lift(procedure, goto, label) { command: "if", target: `\${${goto.target}}` }, - { - command: "goto", - target: goto.target - }, + goto, { command: "end" } @@ -192,7 +189,7 @@ function findBlockClose(procedure, block) { } function findMatchingLabel(listOfLabels, goto) { - return listOfLabels.filter(statement => (statement.command === "label" && statement.target === goto.target)); + return listOfLabels.find(statement => (statement.command === "label" && statement.target === goto.target)); } function isBlock(statement) { From 411ca48ef824481254104049937a0c2c2a38399c Mon Sep 17 00:00:00 2001 From: Tomer Date: Mon, 2 Apr 2018 15:30:29 +0300 Subject: [PATCH 18/28] relationship definition --- .../src/neo/IO/legacy/goto-elimination.js | 48 +++++- .../IO/legacy/goto-elimination.spec.js | 149 +++++++++++++++++- 2 files changed, 191 insertions(+), 6 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 92f8e95a3..76acea79d 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -179,11 +179,7 @@ function findBlockClose(procedure, block) { let level = 1, index = procedure.indexOf(block); while (level !== 0) { index++; - if (isBlock(procedure[index])) { - level++; - } else if (isBlockEnd(procedure[index])) { - level--; - } + level = levelIncrement(procedure[index], level); } return procedure[index]; } @@ -213,7 +209,49 @@ function isBlockEnd(statement) { } } +function levelIncrement(statement, level = 0) { + if (isBlock(statement)) { + level++; + } else if (isBlockEnd(statement)) { + level--; + } + return level; +} + function isConditionalGoto(procedure, goto) { const index = procedure.indexOf(goto); return (procedure[index - 1].command === "if" && procedure[index + 1] === "end"); } + +// for this calculation a tree would've been a better data structure +export function relation(procedure, goto, label) { + let level, first, outOfBlock; + for (let index = 0; index < procedure.length; index++) { + const statement = procedure[index]; + level = levelIncrement(statement, level); + // 0 is truthy + if (first !== undefined && outOfBlock === undefined && level < first) { + outOfBlock = level; + } else if (first !== undefined && outOfBlock !== undefined && level > outOfBlock) { + return Relationship.IndirectlyRelated; + } + + if (first === undefined && (statement === goto || statement === label)) { + // matched the first one + first = level; + } else if (first !== undefined && (statement === goto || statement === label)) { + // matched the second one + if (level === first) { + return Relationship.Siblings; + } else { + return Relationship.DirectlyRelated; + } + } + } +} + +export const Relationship = { + Siblings: "siblings", + DirectlyRelated: "related", + IndirectlyRelated: "unrelated" +}; diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index ca978e4b0..05e68a549 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -15,7 +15,16 @@ // specific language governing permissions and limitations // under the License. -import { transformToConditional, eliminateGoto, eliminateLabel, transformOutward, transformInward, lift } from "../../../IO/legacy/goto-elimination"; +import { + transformToConditional, + eliminateGoto, + eliminateLabel, + transformOutward, + transformInward, + lift, + relation, + Relationship +} from "../../../IO/legacy/goto-elimination"; describe("goto conditional conversion", () => { it("should convert unconditional goto to conditional goto", () => { @@ -831,3 +840,141 @@ describe("lifting transformation", () => { ]); }); }); + +describe("goto-label relations", () => { + it("goto should be sibling of label at level 0", () => { + const procedure = [ + { + command: "statement" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + } + ]; + expect(relation(procedure, procedure[1], procedure[3])).toBe(Relationship.Siblings); + }); + it("goto should be sibling of label inside the same block", () => { + const procedure = [ + { + command: "statement" + }, + { + command: "if", + target: "true" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "label", + target: "label_1" + }, + { + command: "statement" + }, + { + command: "end" + } + ]; + expect(relation(procedure, procedure[2], procedure[4])).toBe(Relationship.Siblings); + }); + it("should work for reverse label-goto as well", () => { + const procedure = [ + { + command: "label", + target: "label_1" + }, + { + command: "goto", + target: "label_1" + } + ]; + expect(relation(procedure, procedure[1], procedure[0])).toBe(Relationship.Siblings); + }); + it("should be directly related if the goto is nested inside the label block", () => { + const procedure = [ + { + command: "if", + target: "true" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + }, + { + command: "label", + target: "label_1" + } + ]; + expect(relation(procedure, procedure[1], procedure[3])).toBe(Relationship.DirectlyRelated); + }); + it("should be directly related if the label is nested inside the goto block", () => { + const procedure = [ + { + command: "if", + target: "true" + }, + { + command: "label", + target: "label_1" + }, + { + command: "end" + }, + { + command: "goto", + target: "label_1" + } + ]; + expect(relation(procedure, procedure[3], procedure[1])).toBe(Relationship.DirectlyRelated); + }); + it("should be indirectly related if goto and label are nested inside different branches of the procedure", () => { + const procedure = [ + { + command: "if", + target: "true" + }, + { + command: "label", + target: "label_1" + }, + { + command: "end" + }, + { + command: "if", + target: "true" + }, + { + command: "if", + target: "true" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + } + ]; + expect(relation(procedure, procedure[5], procedure[1])).toBe(Relationship.IndirectlyRelated); + }); +}); From ea6cfa9e01550ac570ee25364427ebb373ec038f Mon Sep 17 00:00:00 2001 From: Tomer Date: Mon, 2 Apr 2018 17:03:37 +0300 Subject: [PATCH 19/28] relationship takes conditional gotos into account --- .../src/neo/IO/legacy/goto-elimination.js | 71 +++++++++++----- .../IO/legacy/goto-elimination.spec.js | 83 +++++++++++++++++-- 2 files changed, 128 insertions(+), 26 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 76acea79d..729bf0ba7 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -33,15 +33,30 @@ export function eliminate(procedure) { const goto = gotos[0]; const label = findMatchingLabel(labels, goto); - // Make goto and label directly related - - // Make goto and label siblings + const relationship = relation(p, goto, label); + if (relationship === Relationship.IndirectlyRelated) { + // Make goto and label directly related + // Ignoring switch and else statements + p = transformOutward(p, goto); + } else if (relationship === Relationship.DirectlyRelated) { + // Make goto and label siblings + if (calculateLevel(goto) > calculateLevel(label)) { + p = transformOutward(p, goto); + } else { + if (p.indexOf(goto) > p.indexOf(label)) { + p = lift(p, goto, label); + } + // Inward transformation + throw new Error("not implemented"); + } + } else { + // goto and label are siblings - // goto and label are siblings - // eliminate goto - p = eliminateGoto(p, goto, label); - // remove it from the list of gotos - gotos.shift(); + // eliminate goto + p = eliminateGoto(p, goto, label); + // remove it from the list of gotos + gotos.shift(); + } } return p; @@ -218,9 +233,17 @@ function levelIncrement(statement, level = 0) { return level; } +function calculateLevel(procedure, statement) { + let level = 0; + for (let index = 0; index < procedure.length; index++) { + level = levelIncrement(procedure[index], level); + if (procedure[index] === statement) return level; + } +} + function isConditionalGoto(procedure, goto) { const index = procedure.indexOf(goto); - return (procedure[index - 1].command === "if" && procedure[index + 1] === "end"); + return (procedure[index].command === "goto" && procedure[index - 1].command === "if" && procedure[index + 1].command === "end"); } // for this calculation a tree would've been a better data structure @@ -228,23 +251,31 @@ export function relation(procedure, goto, label) { let level, first, outOfBlock; for (let index = 0; index < procedure.length; index++) { const statement = procedure[index]; - level = levelIncrement(statement, level); + // if its a conditional goto, decrease the level, and skip the end + if (isConditionalGoto(procedure, statement)) { + level--; + index++; + } else { + level = levelIncrement(statement, level); + } // 0 is truthy if (first !== undefined && outOfBlock === undefined && level < first) { outOfBlock = level; - } else if (first !== undefined && outOfBlock !== undefined && level > outOfBlock) { + } else if (first !== undefined && outOfBlock !== undefined && level > outOfBlock && !isBlock(statement)) { return Relationship.IndirectlyRelated; } - if (first === undefined && (statement === goto || statement === label)) { - // matched the first one - first = level; - } else if (first !== undefined && (statement === goto || statement === label)) { - // matched the second one - if (level === first) { - return Relationship.Siblings; - } else { - return Relationship.DirectlyRelated; + if (statement === goto || statement === label) { + if (first === undefined) { + // matched the first one + first = level; + } else if (first !== undefined) { + // matched the second one + if (level === first) { + return Relationship.Siblings; + } else { + return Relationship.DirectlyRelated; + } } } } diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index 05e68a549..d328b3d12 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -847,10 +847,17 @@ describe("goto-label relations", () => { { command: "statement" }, + { + command: "if", + target: "true" + }, { command: "goto", target: "label_1" }, + { + command: "end" + }, { command: "statement" }, @@ -862,13 +869,17 @@ describe("goto-label relations", () => { command: "statement" } ]; - expect(relation(procedure, procedure[1], procedure[3])).toBe(Relationship.Siblings); + expect(relation(procedure, procedure[2], procedure[5])).toBe(Relationship.Siblings); }); it("goto should be sibling of label inside the same block", () => { const procedure = [ { command: "statement" }, + { + command: "while", + target: "true" + }, { command: "if", target: "true" @@ -877,6 +888,9 @@ describe("goto-label relations", () => { command: "goto", target: "label_1" }, + { + command: "end" + }, { command: "statement" }, @@ -891,7 +905,7 @@ describe("goto-label relations", () => { command: "end" } ]; - expect(relation(procedure, procedure[2], procedure[4])).toBe(Relationship.Siblings); + expect(relation(procedure, procedure[3], procedure[6])).toBe(Relationship.Siblings); }); it("should work for reverse label-goto as well", () => { const procedure = [ @@ -899,15 +913,55 @@ describe("goto-label relations", () => { command: "label", target: "label_1" }, + { + command: "if", + target: "true" + }, + { + command: "goto", + target: "label_1" + }, + { + command: "end" + } + ]; + expect(relation(procedure, procedure[2], procedure[0])).toBe(Relationship.Siblings); + }); + it("goto should be sibling of label even with block between them", () => { + const procedure = [ + { + command: "if", + target: "true" + }, { command: "goto", target: "label_1" + }, + { + command: "end" + }, + { + command: "while" + }, + { + command: "statement" + }, + { + command: "end" + }, + { + command: "label", + target: "label_1" } ]; - expect(relation(procedure, procedure[1], procedure[0])).toBe(Relationship.Siblings); + expect(relation(procedure, procedure[1], procedure[5])).toBe(Relationship.Siblings); }); it("should be directly related if the goto is nested inside the label block", () => { const procedure = [ + { + command: "while", + target: "true" + }, { command: "if", target: "true" @@ -919,12 +973,15 @@ describe("goto-label relations", () => { { command: "end" }, + { + command: "end" + }, { command: "label", target: "label_1" } ]; - expect(relation(procedure, procedure[1], procedure[3])).toBe(Relationship.DirectlyRelated); + expect(relation(procedure, procedure[2], procedure[5])).toBe(Relationship.DirectlyRelated); }); it("should be directly related if the label is nested inside the goto block", () => { const procedure = [ @@ -939,12 +996,19 @@ describe("goto-label relations", () => { { command: "end" }, + { + command: "if", + target: "true" + }, { command: "goto", target: "label_1" + }, + { + command: "end" } ]; - expect(relation(procedure, procedure[3], procedure[1])).toBe(Relationship.DirectlyRelated); + expect(relation(procedure, procedure[4], procedure[1])).toBe(Relationship.DirectlyRelated); }); it("should be indirectly related if goto and label are nested inside different branches of the procedure", () => { const procedure = [ @@ -963,6 +1027,10 @@ describe("goto-label relations", () => { command: "if", target: "true" }, + { + command: "while", + target: "true" + }, { command: "if", target: "true" @@ -971,10 +1039,13 @@ describe("goto-label relations", () => { command: "goto", target: "label_1" }, + { + command: "end" + }, { command: "end" } ]; - expect(relation(procedure, procedure[5], procedure[1])).toBe(Relationship.IndirectlyRelated); + expect(relation(procedure, procedure[6], procedure[1])).toBe(Relationship.IndirectlyRelated); }); }); From 2cce536be1e314c46abfa4a1a01b745ae8a1f8dc Mon Sep 17 00:00:00 2001 From: Tomer Date: Mon, 2 Apr 2018 17:44:06 +0300 Subject: [PATCH 20/28] renamed storeValue to store --- .../src/neo/IO/legacy/goto-elimination.js | 8 ++++---- .../__test__/IO/legacy/goto-elimination.spec.js | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 729bf0ba7..41abf62d1 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -118,7 +118,7 @@ export function transformOutward(procedure, goto) { function transformOutwardLoop(p, goto, block, end) { const ifIndex = p.indexOf(goto) - 1; p.splice(ifIndex, 3, - { command: "storeValue", target: p[ifIndex].target, value: goto.target }, + { command: "store", target: p[ifIndex].target, value: goto.target }, { command: "if", target: `\${${goto.target}}` }, { command: "break" }, { command: "end" } @@ -134,7 +134,7 @@ function transformOutwardLoop(p, goto, block, end) { function transformOutwardConditional(p, goto, block, end) { const ifIndex = p.indexOf(goto) - 1; p.splice(ifIndex, 3, - { command: "storeValue", target: p[ifIndex].target, value: goto.target }, + { command: "store", target: p[ifIndex].target, value: goto.target }, { command: "if", target: `!\${${goto.target}}` } ); const endIndex = p.indexOf(end); @@ -151,7 +151,7 @@ export function transformInward(procedure, goto) { export function lift(procedure, goto, label) { const p = [ - { command: "storeValue", target: "false", value: goto.target }, + { command: "store", target: "false", value: goto.target }, ...procedure ]; const labelIndex = p.indexOf(label); @@ -171,7 +171,7 @@ export function lift(procedure, goto, label) { const ifIndex = p.indexOf(goto) - 1; p.splice(ifIndex, 3, { - command: "storeValue", + command: "store", target: "condition", value: goto.target }, diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index d328b3d12..269f92033 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -283,7 +283,7 @@ describe("outward movement transformation", () => { command: "statement" }, { - command: "storeValue", + command: "store", target: "condition2", value: "label_1" }, @@ -384,7 +384,7 @@ describe("outward movement transformation", () => { command: "statement" }, { - command: "storeValue", + command: "store", target: "condition2", value: "label_1" }, @@ -478,7 +478,7 @@ describe("inward movement transformation", () => { command: "statement" }, { - command: "storeValue", + command: "store", target: "condition", value: "label_1" }, @@ -518,7 +518,7 @@ describe("inward movement transformation", () => { target: "label_1" }, { - command: "storeValue", + command: "store", target: "false", value: "label_1" }, @@ -590,7 +590,7 @@ describe("inward movement transformation", () => { command: "statement" }, { - command: "storeValue", + command: "store", target: "condition", value: "label_1" }, @@ -706,7 +706,7 @@ describe("inward movement transformation", () => { command: "statement" }, { - command: "storeValue", + command: "store", target: "condition", value: "label_1" }, @@ -797,7 +797,7 @@ describe("lifting transformation", () => { ]; expect(lift(procedure, procedure[4], procedure[1])).toEqual([ { - command: "storeValue", + command: "store", target: "false", value: "label_1" }, @@ -826,7 +826,7 @@ describe("lifting transformation", () => { command: "statement" }, { - command: "storeValue", + command: "store", target: "condition", value: "label_1" }, From a02b79f72b1ba76fe052205a755b540b56cda1d3 Mon Sep 17 00:00:00 2001 From: Tomer Date: Mon, 2 Apr 2018 17:52:10 +0300 Subject: [PATCH 21/28] fixed lift goto lookup --- packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 41abf62d1..5e94225eb 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -168,7 +168,7 @@ export function lift(procedure, goto, label) { command: "end" } ); - const ifIndex = p.indexOf(goto) - 1; + const ifIndex = p.lastIndexOf(goto) - 1; p.splice(ifIndex, 3, { command: "store", From 859d76fdc0531e2306a6528f0983cc4774c99b76 Mon Sep 17 00:00:00 2001 From: Tomer Date: Tue, 3 Apr 2018 13:30:12 +0300 Subject: [PATCH 22/28] inward loop movement transformation --- .../src/neo/IO/legacy/goto-elimination.js | 53 ++++++++++++++++++- .../IO/legacy/goto-elimination.spec.js | 6 +-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 5e94225eb..4fcdcdc6f 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -15,6 +15,9 @@ // specific language governing permissions and limitations // under the License. +// GENENRAL NOTE: procedure is non-mutable while p is +// Do not mutate statements, create new references +// Don't break goto references, you do not need to change them anyway (just their location) export function eliminate(procedure) { let p = [...procedure]; @@ -146,7 +149,41 @@ function transformOutwardConditional(p, goto, block, end) { ); } -export function transformInward(procedure, goto) { +export function transformInward(procedure, goto, label) { + const block = findFirstEnclosingBlock(procedure, procedure.indexOf(goto), label); + const p = [...procedure]; + if (block.command !== "if") { + // outward loop movement + transformInwardLoop(p, goto, block, label); + } else { + // outward conditional movement + transformInwardConditional(p, goto, block, label); + } + return p; +} + +function transformInwardLoop(p, goto, block, label) { + const ifIndex = p.indexOf(goto) - 1; + p.splice(ifIndex, 3, + { command: "store", target: p[ifIndex].target, value: goto.target }, + { command: "if", target: `!\${${goto.target}}` } + ); + const blockIndex = p.indexOf(block); + p.splice(blockIndex, 1, + { command: "end" }, + { command: "while", target: `\${${goto.target}} || ${block.target}` }, + { command: "if", target: `\${${goto.target}}` }, + goto, + { command: "end" } + ); + const labelIndex = p.indexOf(label); + p.splice(labelIndex + 1, 0, + { command: "store", target: "false", value: goto.target } + ); + return p; +} + +function transformInwardConditional() { } export function lift(procedure, goto, label) { @@ -183,6 +220,20 @@ export function lift(procedure, goto, label) { return p; } +function findFirstEnclosingBlock(procedure, offset, statement) { + const blocks = []; + for (let i = offset; i < procedure.length; i++) { + const block = procedure[i]; + if (isBlock(block)) { + blocks.push(block); + } else if (isBlockEnd(block)) { + blocks.pop(block); + } else if (block === statement) { + return blocks[0]; + } + } +} + function findEnclosingBlock(procedure, goto) { // remember to skip the enclosing if for (let i = procedure.indexOf(goto) - 2; i >= 0; i--) { diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index 269f92033..da118b42a 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -473,7 +473,7 @@ describe("inward movement transformation", () => { command: "statement" } ]; - expect(transformInward(procedure, procedure[2])).toEqual([ + expect(transformInward(procedure, procedure[2], procedure[8])).toEqual([ { command: "statement" }, @@ -585,7 +585,7 @@ describe("inward movement transformation", () => { command: "statement" } ]; - expect(transformInward(procedure, procedure[2])).toEqual([ + expect(transformInward(procedure, procedure[2], procedure[8])).toEqual([ { command: "statement" }, @@ -701,7 +701,7 @@ describe("inward movement transformation", () => { command: "statement" } ]; - expect(transformInward(procedure, procedure[2])).toEqual([ + expect(transformInward(procedure, procedure[2], procedure[11])).toEqual([ { command: "statement" }, From 8272f3943c0c47fd70b5249b53731187e178c5be Mon Sep 17 00:00:00 2001 From: Tomer Date: Tue, 3 Apr 2018 14:31:28 +0300 Subject: [PATCH 23/28] pretty printer for procedures --- .../src/neo/IO/legacy/goto-elimination.js | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 4fcdcdc6f..ec7dd0a91 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -43,14 +43,14 @@ export function eliminate(procedure) { p = transformOutward(p, goto); } else if (relationship === Relationship.DirectlyRelated) { // Make goto and label siblings - if (calculateLevel(goto) > calculateLevel(label)) { + if (calculateLevel(p, goto) > calculateLevel(p, label)) { p = transformOutward(p, goto); } else { if (p.indexOf(goto) > p.indexOf(label)) { p = lift(p, goto, label); } // Inward transformation - throw new Error("not implemented"); + p = transformInward(p, goto, label); } } else { // goto and label are siblings @@ -184,6 +184,7 @@ function transformInwardLoop(p, goto, block, label) { } function transformInwardConditional() { + throw new Error("not implemented"); } export function lift(procedure, goto, label) { @@ -285,7 +286,8 @@ function levelIncrement(statement, level = 0) { } function calculateLevel(procedure, statement) { - let level = 0; + // ignore the goto condition in terms of level + let level = statement.command === "goto" ? -1 : 0; for (let index = 0; index < procedure.length; index++) { level = levelIncrement(procedure[index], level); if (procedure[index] === statement) return level; @@ -337,3 +339,17 @@ export const Relationship = { DirectlyRelated: "related", IndirectlyRelated: "unrelated" }; + +export function prettyPrint(procedure) { + let level = 0; + return procedure.map(statement => { + if (isBlockEnd(statement)) { + level--; + } + let r = `${" ".repeat(level)}${statement.command} ${statement.target || ""}`; + if (isBlock(statement)) { + level++; + } + return r; + }).join("\n"); +} From de5c6f0fd8224d848a79835a83c02db62d99cd34 Mon Sep 17 00:00:00 2001 From: Tomer Date: Tue, 3 Apr 2018 15:41:05 +0300 Subject: [PATCH 24/28] skip else as it is improbable we'll face that case --- .../src/neo/__test__/IO/legacy/goto-elimination.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index da118b42a..e0cda6f4b 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -649,7 +649,7 @@ describe("inward movement transformation", () => { } ]); }); - it("should move goto into an if's else", () => { + it.skip("should move goto into an if's else", () => { const procedure = [ { command: "statement" From 610281620466b286aed4a44923b728ea42eed745 Mon Sep 17 00:00:00 2001 From: Tomer Date: Tue, 3 Apr 2018 15:42:49 +0300 Subject: [PATCH 25/28] inward then movement transformation --- .../src/neo/IO/legacy/goto-elimination.js | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index ec7dd0a91..c3484d7bc 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -152,17 +152,17 @@ function transformOutwardConditional(p, goto, block, end) { export function transformInward(procedure, goto, label) { const block = findFirstEnclosingBlock(procedure, procedure.indexOf(goto), label); const p = [...procedure]; - if (block.command !== "if") { + if (block.command !== "else") { // outward loop movement - transformInwardLoop(p, goto, block, label); + transformInwardLoopConditional(p, goto, block, label); } else { // outward conditional movement - transformInwardConditional(p, goto, block, label); + transformInwardElseConditional(p, goto, block, label); } return p; } -function transformInwardLoop(p, goto, block, label) { +function transformInwardLoopConditional(p, goto, block, label) { const ifIndex = p.indexOf(goto) - 1; p.splice(ifIndex, 3, { command: "store", target: p[ifIndex].target, value: goto.target }, @@ -171,19 +171,23 @@ function transformInwardLoop(p, goto, block, label) { const blockIndex = p.indexOf(block); p.splice(blockIndex, 1, { command: "end" }, - { command: "while", target: `\${${goto.target}} || ${block.target}` }, + { command: block.command, target: `\${${goto.target}} || ${block.target}` }, { command: "if", target: `\${${goto.target}}` }, goto, { command: "end" } ); const labelIndex = p.indexOf(label); - p.splice(labelIndex + 1, 0, - { command: "store", target: "false", value: goto.target } - ); + + // make sure that the next time the while condition isn't shorted + if (block.command === "while") { + p.splice(labelIndex + 1, 0, + { command: "store", target: "false", value: goto.target } + ); + } return p; } -function transformInwardConditional() { +function transformInwardElseConditional() { throw new Error("not implemented"); } From 282facae93d0e02e1b9bcce51008b7992e27378c Mon Sep 17 00:00:00 2001 From: Tomer Date: Tue, 3 Apr 2018 15:42:59 +0300 Subject: [PATCH 26/28] fix broken test --- .../selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js index f589ef3bd..5f07a768a 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js @@ -57,7 +57,7 @@ describe("selenium test case migration", () => { it("should decode the input post conversion", () => { const file = fs.readFileSync(path.join(__dirname, "IDE_test_8.html")).toString(); const project = migrateTestCase(file); - expect(project.tests[0].commands[14].target).toBe("//a[@onclick='return confirm(\"Wollen Sie den Datensatz wirklich löschen?\")']"); + expect(project.tests[0].commands[16].target).toBe("//a[@onclick='return confirm(\"Wollen Sie den Datensatz wirklich löschen?\")']"); }); }); From 1c3ca59fdc7e431d74ef5d97618f55133d3e23d8 Mon Sep 17 00:00:00 2001 From: Tomer Date: Tue, 3 Apr 2018 15:51:52 +0300 Subject: [PATCH 27/28] eliminate labels --- packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js | 6 +++--- .../src/neo/__test__/IO/legacy/goto-elimination.spec.js | 4 ++-- .../selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index c3484d7bc..090f28625 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -62,7 +62,7 @@ export function eliminate(procedure) { } } - return p; + return eliminateLabels(p); } export function transformToConditional(goto) { @@ -81,8 +81,8 @@ export function transformToConditional(goto) { ]); } -export function eliminateLabel(procedure, label) { - return procedure.filter(p => p !== label); +export function eliminateLabels(procedure) { + return procedure.filter(s => s.command !== "label"); } export function eliminateGoto(procedure, goto, label) { diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js index e0cda6f4b..bd8cb585c 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/goto-elimination.spec.js @@ -18,7 +18,7 @@ import { transformToConditional, eliminateGoto, - eliminateLabel, + eliminateLabels, transformOutward, transformInward, lift, @@ -216,7 +216,7 @@ describe("label elimination", () => { command: "statement" } ]; - expect(eliminateLabel(procedure, label)).toEqual([ + expect(eliminateLabels(procedure)).toEqual([ { command: "statement" }, diff --git a/packages/selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js b/packages/selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js index 5f07a768a..fab42c6a7 100644 --- a/packages/selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js +++ b/packages/selenium-ide/src/neo/__test__/IO/legacy/migrate.spec.js @@ -57,7 +57,7 @@ describe("selenium test case migration", () => { it("should decode the input post conversion", () => { const file = fs.readFileSync(path.join(__dirname, "IDE_test_8.html")).toString(); const project = migrateTestCase(file); - expect(project.tests[0].commands[16].target).toBe("//a[@onclick='return confirm(\"Wollen Sie den Datensatz wirklich löschen?\")']"); + expect(project.tests[0].commands[13].target).toBe("//a[@onclick='return confirm(\"Wollen Sie den Datensatz wirklich löschen?\")']"); }); }); From 5087e2b14a4c9df6b440febf972e85715af65482 Mon Sep 17 00:00:00 2001 From: Tomer Date: Tue, 3 Apr 2018 16:07:58 +0300 Subject: [PATCH 28/28] remove empty blocks --- .../selenium-ide/src/neo/IO/legacy/goto-elimination.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js index 090f28625..aadb19bf7 100644 --- a/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js +++ b/packages/selenium-ide/src/neo/IO/legacy/goto-elimination.js @@ -62,7 +62,7 @@ export function eliminate(procedure) { } } - return eliminateLabels(p); + return optimize(eliminateLabels(p)); } export function transformToConditional(goto) { @@ -81,6 +81,14 @@ export function transformToConditional(goto) { ]); } +function optimize(procedure) { + return procedure.filter((statement, i) => { + if (isBlock(statement)) return !isBlockEnd(procedure[i + 1]); + if (isBlockEnd(statement)) return !isBlock(procedure[i - 1]); + return true; + }); +} + export function eliminateLabels(procedure) { return procedure.filter(s => s.command !== "label"); }