From 7deabe41e2336289d40a990fab28a7617047ce3d Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Mon, 17 Oct 2016 12:42:57 +0200 Subject: [PATCH 01/52] allow passing a seed to the random eq oracle --- .../MealyRandomWordsEQOracleProxy.java | 23 +++++++++++++++++-- .../learn-resume-settings-widget.html | 6 +++++ .../modals/learn-setup-settings-modal.html | 16 +++++++++++++ .../main/webapp/src/js/entities/EqOracle.js | 4 +++- .../webapp/src/js/services/EqOracleService.js | 2 +- .../main/webapp/tests/resources/entities.js | 12 +++++----- .../tests/unit/entities/EqOracle.tests.js | 3 ++- .../unit/entities/LearnConfiguration.tests.js | 2 +- .../core/dao/LearnerResultDAOImplTest.java | 3 ++- .../entities/LearnerConfigurationTest.java | 2 +- .../alex/core/entities/LearnerResultTest.java | 3 ++- .../MealyRandomWordsEQOracleProxyTest.java | 2 ++ .../alex/integrationtests/AlexIT.java | 3 ++- .../core/entities/LearnerResultTestData.json | 3 ++- 14 files changed, 67 insertions(+), 17 deletions(-) diff --git a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java index c3d2e5b75..95e44f642 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java @@ -44,6 +44,9 @@ public class MealyRandomWordsEQOracleProxy extends AbstractEquivalenceOracleProx /** The maximal length of the random generated words. */ private int maxLength; + /** The seed to use for the random number generator. */ + private int seed; + /** How many words should be created before ending the oracle with the assumption that no counter example exists. */ private int maxNoOfTests; @@ -54,6 +57,7 @@ public MealyRandomWordsEQOracleProxy() { this.minLength = 1; this.maxLength = 1; this.maxNoOfTests = 1; + this.seed = RANDOM_SEED; } /** @@ -66,10 +70,11 @@ public MealyRandomWordsEQOracleProxy() { * @param maxNoOfTests * Highest amount of generated word before ending the search. */ - public MealyRandomWordsEQOracleProxy(int minLength, int maxLength, int maxNoOfTests) { + public MealyRandomWordsEQOracleProxy(int minLength, int maxLength, int maxNoOfTests, int seed) { this.minLength = minLength; this.maxLength = maxLength; this.maxNoOfTests = maxNoOfTests; + this.seed = seed; } /** @@ -130,6 +135,20 @@ public void setMaxNoOfTests(int maxNoOfTests) { this.maxNoOfTests = maxNoOfTests; } + /** + * @return The seed for the random number generator. + */ + public int getSeed() { + return seed; + } + + /** + * @param seed The seed for the random number generator. + */ + public void setSeed(int seed) { + this.seed = seed; + } + @Override public void checkParameters() throws IllegalArgumentException { if (minLength < 0 || maxLength < 0) { @@ -148,7 +167,7 @@ public void checkParameters() throws IllegalArgumentException { public EquivalenceOracle, String, Word> createEqOracle(SULOracle membershipOracle) { return new RandomWordsEQOracle.MealyRandomWordsEQOracle<>(membershipOracle, minLength, maxLength, maxNoOfTests, - new Random(RANDOM_SEED)); + new Random(seed)); } } diff --git a/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html b/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html index 910d001fa..b7f94f9ae 100644 --- a/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html +++ b/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html @@ -29,6 +29,12 @@ +
+
+ +
+ +

diff --git a/main/src/main/webapp/src/html/directives/modals/learn-setup-settings-modal.html b/main/src/main/webapp/src/html/directives/modals/learn-setup-settings-modal.html index e616203eb..70c561685 100644 --- a/main/src/main/webapp/src/html/directives/modals/learn-setup-settings-modal.html +++ b/main/src/main/webapp/src/html/directives/modals/learn-setup-settings-modal.html @@ -80,6 +80,22 @@ The field must not be empty. + +
+
+ +
+ +
+ +
+ + The field must not be empty. + +
diff --git a/main/src/main/webapp/src/js/entities/EqOracle.js b/main/src/main/webapp/src/js/entities/EqOracle.js index 59e38900e..1e2f9225a 100644 --- a/main/src/main/webapp/src/js/entities/EqOracle.js +++ b/main/src/main/webapp/src/js/entities/EqOracle.js @@ -27,12 +27,14 @@ export class RandomEqOracle { * @param {number} minLength * @param {number} maxLength * @param {number} maxNoOfTests + * @param {seed} seed */ - constructor(minLength = 0, maxLength = 0, maxNoOfTests = 0) { + constructor(minLength = 0, maxLength = 0, maxNoOfTests = 0, seed = 42) { this.type = eqOracleType.RANDOM; this.minLength = minLength; this.maxLength = maxLength; this.maxNoOfTests = maxNoOfTests; + this.seed = seed; } } diff --git a/main/src/main/webapp/src/js/services/EqOracleService.js b/main/src/main/webapp/src/js/services/EqOracleService.js index 4ea364330..3943e3692 100644 --- a/main/src/main/webapp/src/js/services/EqOracleService.js +++ b/main/src/main/webapp/src/js/services/EqOracleService.js @@ -31,7 +31,7 @@ export class EqOracleService { create(obj) { switch (obj.type) { case eqOracleType.RANDOM: - return new RandomEqOracle(obj.minLength, obj.maxLength, obj.maxNoOfTests); + return new RandomEqOracle(obj.minLength, obj.maxLength, obj.maxNoOfTests, obj.seed); case eqOracleType.COMPLETE: return new CompleteEqOracle(obj.minDepth, obj.maxDepth); case eqOracleType.SAMPLE: diff --git a/main/src/main/webapp/tests/resources/entities.js b/main/src/main/webapp/tests/resources/entities.js index a6aaae98d..bf60ea5cb 100644 --- a/main/src/main/webapp/tests/resources/entities.js +++ b/main/src/main/webapp/tests/resources/entities.js @@ -53,7 +53,7 @@ ENTITIES.groups = [ ]; ENTITIES.eqOracles = { - random: {type: 'random_word', minLength: 2, maxLength: 5, maxNoOfTests: 10}, + random: {type: 'random_word', minLength: 2, maxLength: 5, maxNoOfTests: 10, seed: 42}, complete: {type: 'complete', minDepth: 2, maxDepth: 5}, sample: {type: 'sample', counterExamples: [{input: 's1', output: 'OK'}, {input: 's2', output: 'FAILED'}]}, wmethod: {type: 'wmethod', maxDepth: 2} @@ -63,7 +63,7 @@ ENTITIES.learnConfigurations = [ { symbols: [{id: 2, revision: 1}, {id: 3, revision: 1}], maxAmountOfStepsToLearn: -1, - eqOracle: {type: 'random_word', minLength: 2, maxLength: 5, maxNoOfTests: 10}, + eqOracle: {type: 'random_word', minLength: 2, maxLength: 5, maxNoOfTests: 10, seed: 42}, algorithm: 'TTT', resetSymbol: {id: 1, revision: 1}, comment: null, @@ -100,7 +100,7 @@ ENTITIES.learnResults = [{ "steps": [{ "algorithmInformation": "+====+====+\n| | s1 |\n+====+====+\n| ε | OK |\n+====+====+\n| s1 | OK |\n+====+====+\n", "counterExample": "", - "eqOracle": {"type": "random_word", "minLength": 1, "maxLength": 10, "maxNoOfTests": 20}, + "eqOracle": {"type": "random_word", "minLength": 1, "maxLength": 10, "maxNoOfTests": 20, "seed": 42}, "hypothesis": {"nodes": [0], "initNode": 0, "edges": [{"from": 0, "input": "s1", "to": 0, "output": "OK"}]}, "statistics": { "duration": { @@ -126,7 +126,7 @@ ENTITIES.learnResults = [{ },{ "algorithmInformation": "+====+====+\n| | s1 |\n+====+====+\n| ε | OK |\n+====+====+\n| s1 | OK |\n+====+====+\n", "counterExample": "", - "eqOracle": {"type": "random_word", "minLength": 1, "maxLength": 10, "maxNoOfTests": 20}, + "eqOracle": {"type": "random_word", "minLength": 1, "maxLength": 10, "maxNoOfTests": 20, "seed": 42}, "hypothesis": {"nodes": [0], "initNode": 0, "edges": [{"from": 0, "input": "s1", "to": 0, "output": "OK"}]}, "statistics": { "duration": { @@ -182,7 +182,7 @@ ENTITIES.learnResults = [{ "steps": [{ "algorithmInformation": "+====+====+\n| | s1 |\n+====+====+\n| ε | OK |\n+====+====+\n| s1 | OK |\n+====+====+\n", "counterExample": "", - "eqOracle": {"type": "random_word", "minLength": 1, "maxLength": 10, "maxNoOfTests": 20}, + "eqOracle": {"type": "random_word", "minLength": 1, "maxLength": 10, "maxNoOfTests": 20, "seed": 42}, "hypothesis": {"nodes": [0], "initNode": 0, "edges": [{"from": 0, "input": "s1", "to": 0, "output": "OK"}]}, "statistics": { "duration": { @@ -208,7 +208,7 @@ ENTITIES.learnResults = [{ }, { "algorithmInformation": "+====+====+\n| | s1 |\n+====+====+\n| ε | OK |\n+====+====+\n| s1 | OK |\n+====+====+\n", "counterExample": "", - "eqOracle": {"type": "random_word", "minLength": 1, "maxLength": 10, "maxNoOfTests": 20}, + "eqOracle": {"type": "random_word", "minLength": 1, "maxLength": 10, "maxNoOfTests": 20, "seed": 42}, "hypothesis": {"nodes": [0], "initNode": 0, "edges": [{"from": 0, "input": "s1", "to": 0, "output": "OK"}]}, "statistics": { "duration": { diff --git a/main/src/main/webapp/tests/unit/entities/EqOracle.tests.js b/main/src/main/webapp/tests/unit/entities/EqOracle.tests.js index a61622c8c..804ee070c 100644 --- a/main/src/main/webapp/tests/unit/entities/EqOracle.tests.js +++ b/main/src/main/webapp/tests/unit/entities/EqOracle.tests.js @@ -22,7 +22,8 @@ describe('EqOracle', () => { type: eqOracleType.RANDOM, minLength: 5, maxLength: 10, - maxNoOfTests: 20 + maxNoOfTests: 20, + seed: 42 }; expect(angular.toJson(oracle)).toEqual(angular.toJson(expectedOracle)); diff --git a/main/src/main/webapp/tests/unit/entities/LearnConfiguration.tests.js b/main/src/main/webapp/tests/unit/entities/LearnConfiguration.tests.js index 3a46053f4..482be9bb3 100644 --- a/main/src/main/webapp/tests/unit/entities/LearnConfiguration.tests.js +++ b/main/src/main/webapp/tests/unit/entities/LearnConfiguration.tests.js @@ -10,7 +10,7 @@ describe('LearnConfiguration', () => { const expectedConfig = { symbols: [], maxAmountOfStepsToLearn: -1, - eqOracle: {type: 'random_word', minLength: 1, maxLength: 10, maxNoOfTests: 20}, + eqOracle: {type: 'random_word', minLength: 1, maxLength: 10, maxNoOfTests: 20, seed: 42}, algorithm: 'TTT', resetSymbol: null, comment: null, diff --git a/main/src/test/java/de/learnlib/alex/core/dao/LearnerResultDAOImplTest.java b/main/src/test/java/de/learnlib/alex/core/dao/LearnerResultDAOImplTest.java index f477c6f13..4b44ab2c0 100644 --- a/main/src/test/java/de/learnlib/alex/core/dao/LearnerResultDAOImplTest.java +++ b/main/src/test/java/de/learnlib/alex/core/dao/LearnerResultDAOImplTest.java @@ -56,7 +56,8 @@ public class LearnerResultDAOImplTest { private static final long USER_ID = 21L; private static final long PROJECT_ID = 42L; private static final int RESULTS_AMOUNT = 3; - private static final MealyRandomWordsEQOracleProxy EXAMPLE_EQ_ORACLE = new MealyRandomWordsEQOracleProxy(1, 5, 10); + private static final MealyRandomWordsEQOracleProxy EXAMPLE_EQ_ORACLE = + new MealyRandomWordsEQOracleProxy(1, 5, 10, 42); @Mock private LearnerResultRepository learnerResultRepository; diff --git a/main/src/test/java/de/learnlib/alex/core/entities/LearnerConfigurationTest.java b/main/src/test/java/de/learnlib/alex/core/entities/LearnerConfigurationTest.java index 622433338..41e4d2614 100644 --- a/main/src/test/java/de/learnlib/alex/core/entities/LearnerConfigurationTest.java +++ b/main/src/test/java/de/learnlib/alex/core/entities/LearnerConfigurationTest.java @@ -40,7 +40,7 @@ public class LearnerConfigurationTest { public void shouldCreateTheCorrectDefaultJSON() throws JsonProcessingException { String expectedJSON = "{\"algorithm\":\"TTT\",\"browser\":\"htmlunitdriver\",\"comment\":\"\",\"eqOracle\":" + "{\"type\":\"random_word\",\"minLength\":" + EQ_MIN_VALUE + "," - + "\"maxLength\":" + EQ_MAX_VALUE + ",\"maxNoOfTests\":1}," + + "\"maxLength\":" + EQ_MAX_VALUE + ",\"seed\":42,\"maxNoOfTests\":1}," + "\"maxAmountOfStepsToLearn\":-1,\"project\":null,\"resetSymbol\":null,\"symbols\":[]," + "\"user\":null}"; diff --git a/main/src/test/java/de/learnlib/alex/core/entities/LearnerResultTest.java b/main/src/test/java/de/learnlib/alex/core/entities/LearnerResultTest.java index 45dfebee2..eaf1d3bdb 100644 --- a/main/src/test/java/de/learnlib/alex/core/entities/LearnerResultTest.java +++ b/main/src/test/java/de/learnlib/alex/core/entities/LearnerResultTest.java @@ -48,7 +48,8 @@ public class LearnerResultTest { private static final Algorithm TTT = new Algorithm("TTT", ""); - private static final MealyRandomWordsEQOracleProxy EXAMPLE_EQ_ORACLE = new MealyRandomWordsEQOracleProxy(1, 5, 10); + private static final MealyRandomWordsEQOracleProxy EXAMPLE_EQ_ORACLE = + new MealyRandomWordsEQOracleProxy(1, 5, 10, 42); @Test @Ignore diff --git a/main/src/test/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxyTest.java b/main/src/test/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxyTest.java index c89d9e0b8..bd68113b6 100644 --- a/main/src/test/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxyTest.java +++ b/main/src/test/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxyTest.java @@ -23,6 +23,7 @@ public class MealyRandomWordsEQOracleProxyTest { public static final int MAX_LENGTH = 10; public static final int MAX_NO_OF_TESTS = 10; + public static final int SEED = 42; private MealyRandomWordsEQOracleProxy eqOracle; @@ -32,6 +33,7 @@ public void setUp() { eqOracle.setMinLength(1); eqOracle.setMaxLength(MAX_LENGTH); eqOracle.setMaxNoOfTests(MAX_NO_OF_TESTS); + eqOracle.setSeed(SEED); } @Test diff --git a/main/src/test/java/de/learnlib/alex/integrationtests/AlexIT.java b/main/src/test/java/de/learnlib/alex/integrationtests/AlexIT.java index 480b24e1b..54996f99f 100644 --- a/main/src/test/java/de/learnlib/alex/integrationtests/AlexIT.java +++ b/main/src/test/java/de/learnlib/alex/integrationtests/AlexIT.java @@ -100,7 +100,8 @@ private void createSymbols() throws IOException { private void learn() throws InterruptedException { String url = BASE_URL + "/learner/start/" + projectId; String json = "{\"algorithm\":\"TTT\", \"browser\":\"htmlunitdriver\", \"eqOracle\": " - + "{\"type\": \"random_word\", \"minLength\": 1, \"maxLength\": 20, \"maxNoOfTests\" : 40}," + + "{\"type\": \"random_word\", \"minLength\": 1, \"maxLength\": 20, \"maxNoOfTests\" : 40, " + + "\"seed\": 42}," + "\"maxAmountOfStepsToLearn\": -1," + "\"resetSymbol\": {\"id\": 1, \"revision\": 1}," + "\"symbols\":[" diff --git a/main/src/test/resources/core/entities/LearnerResultTestData.json b/main/src/test/resources/core/entities/LearnerResultTestData.json index fee6b3ab6..1bb8ff14d 100644 --- a/main/src/test/resources/core/entities/LearnerResultTestData.json +++ b/main/src/test/resources/core/entities/LearnerResultTestData.json @@ -58,7 +58,8 @@ "type" : "random_word", "minLength" : 1, "maxLength" : 5, - "maxNoOfTests" : 10 + "maxNoOfTests" : 10, + "seed": 42 }, "hypothesis" : { "nodes" : [ 0, 1 ], From 50b53209166f45bac1f42f492ef3a8fc01a2d739 Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Mon, 17 Oct 2016 13:00:29 +0200 Subject: [PATCH 02/52] updated to selenium 3.0.0 and htmlunit-driver 2.23 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 532f6b3b2..a754e1de8 100644 --- a/pom.xml +++ b/pom.xml @@ -120,8 +120,8 @@ 5.2.4.Final 0.12.0 0.6.0 - 2.53.1 - 2.21 + 3.0.0 + 2.23 9.3.5.v20151012 0.5.2 1.3.2 From 2c78e0b25608dfec0caeb93bb13e01bed648b24e Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Tue, 18 Oct 2016 16:40:48 +0200 Subject: [PATCH 03/52] increased version number to 1.2.1-SNAPSHOT --- api/pom.xml | 2 +- main/pom.xml | 2 +- main/src/main/webapp/package.json | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index b3b40d54d..d8dcbd29c 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -7,7 +7,7 @@ de.learnlib.alex alex-parent - 1.2.0 + 1.2.1-SNAPSHOT ../pom.xml diff --git a/main/pom.xml b/main/pom.xml index dbf516f9a..679fd467f 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -7,7 +7,7 @@ de.learnlib.alex alex-parent - 1.2.0 + 1.2.1-SNAPSHOT ../pom.xml diff --git a/main/src/main/webapp/package.json b/main/src/main/webapp/package.json index 07662076f..be75ff528 100644 --- a/main/src/main/webapp/package.json +++ b/main/src/main/webapp/package.json @@ -1,6 +1,6 @@ { "name": "ALEX", - "version": "1.2.0", + "version": "1.2.1-SNAPSHOT", "description": "An interface for Active Automata Learning", "repository": { "type": "git", diff --git a/pom.xml b/pom.xml index a754e1de8..74e623fb3 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ de.learnlib.alex alex-parent - 1.2.0 + 1.2.1-SNAPSHOT pom Automata Learning Experience (ALEX) From 4ae6441658ad1df2183a58a6cc408081461a8bd4 Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Thu, 27 Oct 2016 10:40:32 +0200 Subject: [PATCH 04/52] updated selenium and htmlunitdriver --- main/pom.xml | 5 +++++ pom.xml | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/main/pom.xml b/main/pom.xml index 679fd467f..7c5889e4d 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -199,6 +199,11 @@ htmlunit-driver ${htmlunitdriver.version} + + org.seleniumhq.selenium + selenium-support + ${selenium.version} + org.seleniumhq.selenium selenium-chrome-driver diff --git a/pom.xml b/pom.xml index 74e623fb3..9b4f584fe 100644 --- a/pom.xml +++ b/pom.xml @@ -120,8 +120,8 @@ 5.2.4.Final 0.12.0 0.6.0 - 3.0.0 - 2.23 + 3.0.1 + 2.23.2 9.3.5.v20151012 0.5.2 1.3.2 From 4abc227aa2c2b7041fd6c24461b5b9f9b0ff9711 Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Thu, 27 Oct 2016 11:15:24 +0200 Subject: [PATCH 05/52] resolved #2 --- .../src/js/components/views/symbolsActionsView.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/main/src/main/webapp/src/js/components/views/symbolsActionsView.js b/main/src/main/webapp/src/js/components/views/symbolsActionsView.js index c71435121..be465143d 100644 --- a/main/src/main/webapp/src/js/components/views/symbolsActionsView.js +++ b/main/src/main/webapp/src/js/components/views/symbolsActionsView.js @@ -94,11 +94,17 @@ class SymbolsActionsView { const offHandler = $scope.$on('$stateChangeStart', (event, toState) => { if (this.hasChanged) { event.preventDefault(); - PromptService.confirm('There are unsaved changes. Do you still want to continue and discard them?') + PromptService.confirm('Do you want to save the changes?') .then(() => { + this.saveChanges().then(() => { + offHandler(); + $state.go(toState); + }); + }) + .catch(() => { offHandler(); $state.go(toState); - }); + }) } }); @@ -192,7 +198,7 @@ class SymbolsActionsView { const symbolToUpdate = new AlphabetSymbol(this.symbol); // update the symbol - this.SymbolResource.update(symbolToUpdate) + return this.SymbolResource.update(symbolToUpdate) .then(updatedSymbol => { this.symbol.revision = updatedSymbol.revision; this.ToastService.success('Symbol ' + updatedSymbol.name + ' updated'); From a39ec5980d11d88f0745549c6ed99677db72764d Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Thu, 27 Oct 2016 18:35:13 +0200 Subject: [PATCH 06/52] UX enhancement, addresses #4 --- .../main/webapp/src/html/components/views/learner-setup.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/webapp/src/html/components/views/learner-setup.html b/main/src/main/webapp/src/html/components/views/learner-setup.html index d0c2863d4..48611e6cd 100644 --- a/main/src/main/webapp/src/html/components/views/learner-setup.html +++ b/main/src/main/webapp/src/html/components/views/learner-setup.html @@ -17,7 +17,7 @@ From ae6ba2f74b97155825d66267c9797f4b389b8583 Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Sat, 29 Oct 2016 12:10:50 +0200 Subject: [PATCH 07/52] update frontend dependencies --- main/src/main/webapp/package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main/src/main/webapp/package.json b/main/src/main/webapp/package.json index be75ff528..2fc0ae894 100644 --- a/main/src/main/webapp/package.json +++ b/main/src/main/webapp/package.json @@ -31,7 +31,7 @@ "angular": "^1.5.8", "angular-animate": "^1.5.8", "angular-dragula": "^1.2.8", - "angular-jwt": "0.1.5", + "angular-jwt": "0.1.7", "angular-messages": "^1.5.8", "angular-toastr": "^2.1.1", "angular-ui-ace": "^0.2.3", @@ -40,17 +40,17 @@ "bootstrap-sass": "^3.3.7", "d3": "^3.5.17", "dagre-d3": "^0.4.17", - "font-awesome": "^4.6.3", + "font-awesome": "^4.7.0", "lodash": "^4.16.4", "n3-charts": "^2.0.28", "ng-file-upload": "^12.2.12", "selection-model": "^0.11.0", - "swagger-ui": "^2.2.5" + "swagger-ui": "^2.2.6" }, "devDependencies": { "angular-mocks": "^1.5.8", - "autoprefixer": "^6.5.0", - "babel-preset-es2015": "^6.16.0", + "autoprefixer": "^6.5.1", + "babel-preset-es2015": "^6.18.0", "babelify": "^7.3.0", "browserify-istanbul": "^2.0.0", "grunt": "^1.0.1", @@ -63,7 +63,7 @@ "grunt-contrib-watch": "^1.0.0", "grunt-html2js": "^0.3.6", "grunt-karma": "^2.0.0", - "grunt-ng-annotate": "^2.0.2", + "grunt-ng-annotate": "^3.0.0", "grunt-postcss": "^0.8.0", "grunt-sass": "^1.2.1", "istanbul": "^0.4.5", From 291d3fcd21b672c0608453ab9ad215f8792fb107 Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Sat, 5 Nov 2016 08:17:15 +0100 Subject: [PATCH 08/52] stylesheet & layout refactoring * make use of flexbox for better layouting * removed some unused styles * removed some animations to make the app feel more snappy * removed symbol history view --- .../html/components/learn-result-panel.html | 2 +- .../learn-resume-settings-widget.html | 2 +- .../src/html/components/views/about.html | 2 +- .../html/components/views/admin-users.html | 2 +- .../src/html/components/views/counters.html | 2 +- .../src/html/components/views/files.html | 2 +- .../html/components/views/learner-setup.html | 2 +- .../html/components/views/learner-start.html | 23 +-- .../components/views/projects-dashboard.html | 2 +- .../src/html/components/views/projects.html | 2 +- .../components/views/results-compare.html | 18 +- .../src/html/components/views/results.html | 2 +- .../src/html/components/views/settings.html | 2 +- .../components/views/statistics-compare.html | 2 +- .../src/html/components/views/statistics.html | 2 +- .../components/views/symbols-actions.html | 2 +- .../components/views/symbols-history.html | 34 ---- .../html/components/views/symbols-import.html | 2 +- .../components/views/symbols-test-view.html | 2 +- .../html/components/views/symbols-trash.html | 2 +- .../src/html/components/views/symbols.html | 10 +- .../html/components/views/users-settings.html | 2 +- .../webapp/src/js/components/actionBar.js | 43 ---- .../src/main/webapp/src/js/components/alex.js | 10 +- .../src/js/components/learnResultPanel.js | 18 +- .../js/components/views/learnerStartView.js | 2 +- .../js/components/views/symbolsHistoryView.js | 94 --------- .../webapp/src/js/entities/AlphabetSymbol.js | 6 - main/src/main/webapp/src/js/index.js | 2 - .../webapp/src/js/resources/SymbolResource.js | 12 -- main/src/main/webapp/src/js/routes.js | 5 - .../src/main/webapp/src/scss/_animations.scss | 17 -- .../src/main/webapp/src/scss/_hypothesis.scss | 3 + main/src/main/webapp/src/scss/_sidebar.scss | 26 ++- main/src/main/webapp/src/scss/style.scss | 186 ++++++------------ .../views/symbolsHistoryView.tests.js | 123 ------------ .../unit/resources/SymbolResource.tests.js | 15 -- 37 files changed, 131 insertions(+), 552 deletions(-) delete mode 100644 main/src/main/webapp/src/html/components/views/symbols-history.html delete mode 100644 main/src/main/webapp/src/js/components/views/symbolsHistoryView.js delete mode 100644 main/src/main/webapp/tests/unit/components/views/symbolsHistoryView.tests.js diff --git a/main/src/main/webapp/src/html/components/learn-result-panel.html b/main/src/main/webapp/src/html/components/learn-result-panel.html index 4d6f2d07b..497c367a3 100644 --- a/main/src/main/webapp/src/html/components/learn-result-panel.html +++ b/main/src/main/webapp/src/html/components/learn-result-panel.html @@ -45,7 +45,7 @@ -
+
diff --git a/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html b/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html index b7f94f9ae..f5fd42499 100644 --- a/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html +++ b/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html @@ -1,4 +1,4 @@ - +

- - - -
-
-
-
- - -
-
-
- -
-
-
-
-
- - - -
- - Revision - - - Latest revision - -
-
-
- -
-
+
+ + + + +
\ No newline at end of file diff --git a/main/src/main/webapp/src/js/components/projectList.js b/main/src/main/webapp/src/js/components/projectList.js index ccffd8046..85740514a 100644 --- a/main/src/main/webapp/src/js/components/projectList.js +++ b/main/src/main/webapp/src/js/components/projectList.js @@ -118,7 +118,6 @@ class ProjectList { delete symbol.project; delete symbol.group; delete symbol.id; - delete symbol.revision; delete symbol.hidden; symbol.actions.forEach(action => { diff --git a/main/src/main/webapp/src/js/components/views/symbolsActionsView.js b/main/src/main/webapp/src/js/components/views/symbolsActionsView.js index be465143d..c79de12e2 100644 --- a/main/src/main/webapp/src/js/components/views/symbolsActionsView.js +++ b/main/src/main/webapp/src/js/components/views/symbolsActionsView.js @@ -200,7 +200,6 @@ class SymbolsActionsView { // update the symbol return this.SymbolResource.update(symbolToUpdate) .then(updatedSymbol => { - this.symbol.revision = updatedSymbol.revision; this.ToastService.success('Symbol ' + updatedSymbol.name + ' updated'); this.hasChanged = false; }) diff --git a/main/src/main/webapp/src/js/components/views/symbolsView.js b/main/src/main/webapp/src/js/components/views/symbolsView.js index 16df9659a..90bc3952d 100644 --- a/main/src/main/webapp/src/js/components/views/symbolsView.js +++ b/main/src/main/webapp/src/js/components/views/symbolsView.js @@ -160,7 +160,6 @@ class SymbolsView { group.symbols[i].name = symbol.name; group.symbols[i].abbreviation = symbol.abbreviation; group.symbols[i].group = symbol.group; - group.symbols[i].revision = symbol.revision; } }); } @@ -235,7 +234,7 @@ class SymbolsView { } /** - * Deletes all properties that are not needed for downloading symbols which are the id, revision, project, group + * Deletes all properties that are not needed for downloading symbols which are the id, project, group * and hidden properties. They are removed so that they can later be uploaded and created like new symbols. */ exportSelectedSymbols() { @@ -251,7 +250,6 @@ class SymbolsView { symbols.forEach(symbol => { symbol.actions.forEach(action => { if (action.type === 'executeSymbol') { - action.symbolToExecute.revision = 1; symbols.forEach((s, i) => { if (s.id === action.symbolToExecute.id) { action.symbolToExecute.id = i + 1; diff --git a/main/src/main/webapp/src/js/components/widgets/counterexamplesWidget.js b/main/src/main/webapp/src/js/components/widgets/counterexamplesWidget.js index 5874f5620..3cb739aca 100644 --- a/main/src/main/webapp/src/js/components/widgets/counterexamplesWidget.js +++ b/main/src/main/webapp/src/js/components/widgets/counterexamplesWidget.js @@ -129,11 +129,11 @@ class CounterexamplesWidget { const test = () => { const testSymbols = []; - // find id/revision pairs of symbols from abbreviation in learnResult + // find ids of symbols from abbreviation in learnResult for (let i = 0; i < this.counterExample.length; i++) { for (let j = 0; j < this.symbols.length; j++) { if (this.counterExample[i].input === this.symbols[j].abbreviation) { - testSymbols.push(this.symbols[j].getIdRevisionPair()); + testSymbols.push(this.symbols[j].id()); } } } diff --git a/main/src/main/webapp/src/js/entities/AlphabetSymbol.js b/main/src/main/webapp/src/js/entities/AlphabetSymbol.js index 75d99933f..6efe61cdf 100644 --- a/main/src/main/webapp/src/js/entities/AlphabetSymbol.js +++ b/main/src/main/webapp/src/js/entities/AlphabetSymbol.js @@ -54,12 +54,6 @@ export class AlphabetSymbol { */ this.id = obj.id; - /** - * The revision of the symbol. - * @type {number} - */ - this.revision = obj.revision; - /** * The id of the project the symbol belongs to. * @type {number} @@ -94,18 +88,6 @@ export class AlphabetSymbol { return this.actions.filter(action => !action.disabled).length; } - /** - * Gets the symbol as id revision pair. - * - * @returns {{id: number, revision: number}} - */ - getIdRevisionPair() { - return { - id: this.id, - revision: this.revision - }; - } - /** * Gets a reduced version of the symbol that can be used to export it. * diff --git a/main/src/main/webapp/src/js/entities/LearnConfiguration.js b/main/src/main/webapp/src/js/entities/LearnConfiguration.js index 20b759f1e..30e441d42 100644 --- a/main/src/main/webapp/src/js/entities/LearnConfiguration.js +++ b/main/src/main/webapp/src/js/entities/LearnConfiguration.js @@ -30,8 +30,8 @@ export class LearnConfiguration { constructor(obj = {}) { /** - * The list of id/revision pairs of symbols to learn. - * @type {{id:number, revision:number}[]} + * The list of ids of symbols to learn. + * @type {number[]} */ this.symbols = obj.symbols || []; @@ -54,8 +54,8 @@ export class LearnConfiguration { this.algorithm = obj.learnAlgorithm || learnAlgorithm.TTT; /** - * The id/revision pair of the reset symbol. - * @type {{id:number,revision:number}|null} + * The id of the reset symbol. + * @type {number} */ this.resetSymbol = obj.resetSymbol || null; @@ -78,7 +78,7 @@ export class LearnConfiguration { * @param {AlphabetSymbol} symbol - The symbol to add to the config. */ addSymbol(symbol) { - this.symbols.push(symbol.getIdRevisionPair()); + this.symbols.push(symbol.id); } /** @@ -87,6 +87,6 @@ export class LearnConfiguration { * @param {AlphabetSymbol} symbol - The reset symbol to use. */ setResetSymbol(symbol) { - this.resetSymbol = symbol.getIdRevisionPair(); + this.resetSymbol = symbol.id; } } \ No newline at end of file diff --git a/main/src/main/webapp/src/js/entities/LearnResult.js b/main/src/main/webapp/src/js/entities/LearnResult.js index a9989e21b..933a388c5 100644 --- a/main/src/main/webapp/src/js/entities/LearnResult.js +++ b/main/src/main/webapp/src/js/entities/LearnResult.js @@ -51,14 +51,14 @@ export class LearnResult { this.project = obj.project; /** - * The id revision pair of the reset symbol. + * The id of the reset symbol. * @type {*} */ this.resetSymbol = obj.resetSymbol; /** * The alphabet the process has been learned with. - * @type {{id:number,revision:number}[]} + * @type {number[]} */ this.sigma = obj.sigma; diff --git a/main/src/main/webapp/src/js/entities/actions/generalActions/ExecuteSymbolGeneralAction.js b/main/src/main/webapp/src/js/entities/actions/generalActions/ExecuteSymbolGeneralAction.js index 7afc94f3d..34ed849e0 100644 --- a/main/src/main/webapp/src/js/entities/actions/generalActions/ExecuteSymbolGeneralAction.js +++ b/main/src/main/webapp/src/js/entities/actions/generalActions/ExecuteSymbolGeneralAction.js @@ -31,21 +31,13 @@ export class ExecuteSymbolGeneralAction extends Action { super(actionType.GENERAL_EXECUTE_SYMBOL, obj); /** - * idRevisionPair. - * @type {*|{id: null, revision: null}} + * id. + * @type {number} */ - this.symbolToExecute = obj.symbolToExecute || {id: null, revision: null}; - - /** - * The flag that indicates if the latest revision of the symbol should be used - * without explicitly declaring it every time. - * @type {boolean} - */ - this.useLatestRevision = obj.useLatestRevision || true; + this.symbolToExecute = obj.symbolToExecute || nul; let model = { name: obj.symbolToExecuteName || null, - maxRevision: null }; if (typeof obj.getModel !== "undefined") { @@ -61,25 +53,7 @@ export class ExecuteSymbolGeneralAction extends Action { * @returns {string} */ toString() { - if (this.useLatestRevision) { - return `Execute Symbol ${this.getModel().name} with the latest revision`; - } else { - return `Execute symbol ${this.getModel().name}, rev. ${this.symbolToExecute.revision}`; - } - } - - /** - * Update the revision of the symbol to execute. - * - * @param {AlphabetSymbol[]} symbols - The available symbols in the scope. - */ - updateRevision(symbols) { - for (let i = 0; i < symbols.length; i++) { - if (symbols[i].id === this.symbolToExecute.id) { - this.symbolToExecute.revision = symbols[i].revision; - break; - } - } + return `Execute Symbol ${this.getModel().name}.`; } /** @@ -92,9 +66,8 @@ export class ExecuteSymbolGeneralAction extends Action { this.getModel().maxRevision = null; for (let i = 0; i < symbols.length; i++) { if (symbols[i].name === name) { - this.symbolToExecute = symbols[i].getIdRevisionPair(); + this.symbolToExecute = symbols[i].id; this.getModel().name = name; - this.getModel().maxRevision = symbols[i].revision; break; } } diff --git a/main/src/main/webapp/src/js/resources/LearnerResource.js b/main/src/main/webapp/src/js/resources/LearnerResource.js index 382a811cb..0c64f5978 100644 --- a/main/src/main/webapp/src/js/resources/LearnerResource.js +++ b/main/src/main/webapp/src/js/resources/LearnerResource.js @@ -91,8 +91,8 @@ export class LearnerResource { * Verifies a possible counterexample. * * @param {number} projectId - The project id. - * @param {{id: number, revision: number}} resetSymbol - The id/revision pair of the reset symbol. - * @param {{id: number, revision: number}[]} symbols - The list of id/revision pairs of symbols. + * @param {number} resetSymbol - The id of the reset symbol. + * @param {number} symbols - The list of ids of symbols. * @returns {*} */ readOutputs(projectId, resetSymbol, symbols) { diff --git a/main/src/main/webapp/src/js/resources/SymbolResource.js b/main/src/main/webapp/src/js/resources/SymbolResource.js index 746e432cc..532b0766b 100644 --- a/main/src/main/webapp/src/js/resources/SymbolResource.js +++ b/main/src/main/webapp/src/js/resources/SymbolResource.js @@ -56,15 +56,14 @@ export class SymbolResource { } /** - * Gets a list of symbols by a list of id/revision pairs. + * Gets a list of symbols by a list of ids. * * @param {number} projectId - The id of the project. - * @param {{id:number,revision:number}[]} idRevisionPairs - The list of id/revision pairs. + * @param {number[]} ids - The list of id pairs. * @returns {*} */ - getManyByIdRevisionPairs(projectId, idRevisionPairs) { - const pairs = idRevisionPairs.map(pair => pair.id + ':' + pair.revision).join(','); - return this.$http.get(`rest/projects/${projectId}/symbols/batch/${pairs}`) + getManyByIds(projectId, ids) { + return this.$http.get(`rest/projects/${projectId}/symbols/batch/${ids.join(',')}`) .then(response => response.data.map(s => new AlphabetSymbol(s))); } @@ -92,7 +91,7 @@ export class SymbolResource { } /** - * Move symbols to another group without creating a new revision. + * Move symbols to another group. * * @param {AlphabetSymbol|AlphabetSymbol[]} symbols - The symbol[s] to be moved to another group. * @param {SymbolGroup} group - The id of the symbol group. @@ -105,7 +104,7 @@ export class SymbolResource { } /** - * Updates a single symbol and increments its revision number. + * Updates a single symbol. * * @param {AlphabetSymbol} symbol - The symbol to be updated. * @returns {*} diff --git a/main/src/main/webapp/tests/resources/entities.js b/main/src/main/webapp/tests/resources/entities.js index bf60ea5cb..4913a1eeb 100644 --- a/main/src/main/webapp/tests/resources/entities.js +++ b/main/src/main/webapp/tests/resources/entities.js @@ -29,9 +29,9 @@ ENTITIES.files = [ ]; ENTITIES.symbols = [ - {id: 1, revision: 1, name: 's1', abbreviation: 's1', group: 1, project: 1, user: 1, actions: [], hidden: false}, - {id: 2, revision: 1, name: 's2', abbreviation: 's2', group: 1, project: 1, user: 1, actions: [], hidden: true}, - {id: 3, revision: 1, name: 's3', abbreviation: 's3', group: 1, project: 1, user: 1, actions: [], hidden: false} + {id: 1, name: 's1', abbreviation: 's1', group: 1, project: 1, user: 1, actions: [], hidden: false}, + {id: 2, name: 's2', abbreviation: 's2', group: 1, project: 1, user: 1, actions: [], hidden: true}, + {id: 3, name: 's3', abbreviation: 's3', group: 1, project: 1, user: 1, actions: [], hidden: false} ]; ENTITIES.groups = [ @@ -61,11 +61,11 @@ ENTITIES.eqOracles = { ENTITIES.learnConfigurations = [ { - symbols: [{id: 2, revision: 1}, {id: 3, revision: 1}], + symbols: [2,3], maxAmountOfStepsToLearn: -1, eqOracle: {type: 'random_word', minLength: 2, maxLength: 5, maxNoOfTests: 10, seed: 42}, algorithm: 'TTT', - resetSymbol: {id: 1, revision: 1}, + resetSymbol: 1, comment: null, browser: 'htmlunitdriver' } @@ -76,7 +76,7 @@ ENTITIES.learnResults = [{ "browser": "htmlunitdriver", "hypothesis": {"nodes": [0], "initNode": 0, "edges": [{"from": 0, "input": "s1", "to": 0, "output": "OK"}]}, "project": 2, - "resetSymbol": {"id": 1, "revision": 2}, + "resetSymbol": 1, "sigma": ["s1"], "statistics": { "duration": { @@ -150,7 +150,7 @@ ENTITIES.learnResults = [{ "stepNo": 1, "stepsToLearn": -1 }], - "symbols": [{"id": 1, "revision": 2}], + "symbols": [1], "testNo": 1, "user": 1 }, { @@ -158,7 +158,7 @@ ENTITIES.learnResults = [{ "browser": "htmlunitdriver", "hypothesis": {"nodes": [0], "initNode": 0, "edges": [{"from": 0, "input": "s1", "to": 0, "output": "OK"}]}, "project": 2, - "resetSymbol": {"id": 1, "revision": 2}, + "resetSymbol": 1, "sigma": ["s1"], "statistics": { "duration": { @@ -232,7 +232,7 @@ ENTITIES.learnResults = [{ "stepNo": 1, "stepsToLearn": -1 }], - "symbols": [{"id": 1, "revision": 2}], + "symbols": [1], "testNo": 2, "user": 1 }]; \ No newline at end of file diff --git a/main/src/main/webapp/tests/unit/entities/LearnConfiguration.tests.js b/main/src/main/webapp/tests/unit/entities/LearnConfiguration.tests.js index 482be9bb3..2f730fcdb 100644 --- a/main/src/main/webapp/tests/unit/entities/LearnConfiguration.tests.js +++ b/main/src/main/webapp/tests/unit/entities/LearnConfiguration.tests.js @@ -28,23 +28,21 @@ describe('LearnConfiguration', () => { expect(angular.toJson(config)).toEqual(angular.toJson(expectedConfig)) }); - it('should add a symbol to the symbols list as id revision pair', () => { + it('should add a symbol to the symbols list as id', () => { const config = new LearnConfiguration(); const symbol = new AlphabetSymbol(ENTITIES.symbols[0]); - const pair = symbol.getIdRevisionPair(); const pre = config.symbols.length; config.addSymbol(symbol); expect(config.symbols.length).toBe(pre + 1); - expect(config.symbols.find(p => p.id === pair.id)).toEqual(pair); + expect(config.symbols.find(p => p.id === symbol.id)).toEqual(symbol.id); }); - it('should set a symbols as id revision pair as reset symbol', () => { + it('should set a symbols as id as reset symbol', () => { const config = new LearnConfiguration(); const symbol = new AlphabetSymbol(ENTITIES.symbols[0]); - const pair = symbol.getIdRevisionPair(); config.setResetSymbol(symbol); - expect(config.resetSymbol).toEqual(symbol.getIdRevisionPair()) + expect(config.resetSymbol).toEqual(symbol.id) }); }); \ No newline at end of file diff --git a/main/src/main/webapp/tests/unit/entities/Symbol.tests.js b/main/src/main/webapp/tests/unit/entities/Symbol.tests.js index df7cd1fb7..6cd1747b4 100644 --- a/main/src/main/webapp/tests/unit/entities/Symbol.tests.js +++ b/main/src/main/webapp/tests/unit/entities/Symbol.tests.js @@ -19,14 +19,6 @@ describe('AlphabetSymbol', () => { expect(symbol.countEnabledActions()).toBe(1); }); - it('should get the id revision pair of the symbol', () => { - const expected = { - id: symbol.id, - revision: symbol.revision - }; - expect(symbol.getIdRevisionPair()).toEqual(expected); - }); - it('should get the representation of the symbol that is needed to export it', () => { const expected = { name: symbol.name, diff --git a/main/src/main/webapp/tests/unit/entities/actions/generalActions/ExecuteSymbolGeneralAction.tests.js b/main/src/main/webapp/tests/unit/entities/actions/generalActions/ExecuteSymbolGeneralAction.tests.js index 7802d8677..291b172d7 100644 --- a/main/src/main/webapp/tests/unit/entities/actions/generalActions/ExecuteSymbolGeneralAction.tests.js +++ b/main/src/main/webapp/tests/unit/entities/actions/generalActions/ExecuteSymbolGeneralAction.tests.js @@ -17,7 +17,7 @@ describe('ExecuteSymbolGeneralAction', () => { negated: false, ignoreFailure: false, disabled: false, - symbolToExecute: {id: null, revision: null}, + symbolToExecute: null, useLatestRevision: true }; const action = new ExecuteSymbolGeneralAction({}); diff --git a/main/src/main/webapp/tests/unit/resources/SymbolResource.tests.js b/main/src/main/webapp/tests/unit/resources/SymbolResource.tests.js index 3191f6b8d..f0d6e983d 100644 --- a/main/src/main/webapp/tests/unit/resources/SymbolResource.tests.js +++ b/main/src/main/webapp/tests/unit/resources/SymbolResource.tests.js @@ -93,15 +93,15 @@ describe('SymbolResource', () => { expect(promise.then).toBeDefined(); }); - it('should get a list of symbols by given id revision pairs', () => { - const pairs = ENTITIES.symbols.map(s => new AlphabetSymbol(s).getIdRevisionPair()); - const concatenatedPairs = pairs.map(p => p.id + ':' + p.revision).join(','); + it('should get a list of symbols by given ids', () => { + const ids = ENTITIES.symbols.map(s => new AlphabetSymbol(s).id()); + const concatenatedIds = ids.map(p => p.id).join(','); const projectId = ENTITIES.symbols[0].project; - const uri = `rest/projects/${projectId}/symbols/batch/${concatenatedPairs}`; + const uri = `rest/projects/${projectId}/symbols/batch/${concatenatedIds}`; spyOn($http, 'get').and.callThrough(); $httpBackend.whenGET(uri).respond(200, ENTITIES.symbols); - const promise = SymbolResource.getManyByIdRevisionPairs(projectId, pairs); + const promise = SymbolResource.getManyByIds(projectId, ids); $httpBackend.flush(); expect($http.get).toHaveBeenCalledWith(uri); From 5e5b6019e1443ba28bb357799a5a4be57c15a1ee Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Wed, 9 Nov 2016 11:34:27 +0100 Subject: [PATCH 12/52] fixed aspects of symbol updates There are still problems when updating symbols and especially with the execute symbol action --- .../learnlib/alex/core/dao/SymbolDAOImpl.java | 133 +++++------------- .../ExecuteSymbolGeneralAction.js | 2 +- .../alex/rest/SymbolResourceTest.java | 12 +- 3 files changed, 41 insertions(+), 106 deletions(-) diff --git a/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java b/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java index 80b64d50f..7a89257e7 100644 --- a/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java +++ b/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java @@ -40,6 +40,7 @@ import javax.validation.ConstraintViolationException; import javax.validation.ValidationException; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -85,11 +86,8 @@ public SymbolDAOImpl(ProjectRepository projectRepository, SymbolGroupRepository public void create(Symbol symbol) throws ValidationException { LOGGER.traceEntry("create({})", symbol); try { - // create the symbol createOne(symbol); setExecuteToSymbols(symbolRepository, symbol); - - // error handling } catch (DataIntegrityViolationException e) { LOGGER.info("Symbol creation failed:", e); throw new ValidationException("Symbol could not be created.", e); @@ -118,7 +116,6 @@ public void create(Symbol symbol) throws ValidationException { @Transactional public void create(List symbols) throws ValidationException { try { - // create the symbol symbols.forEach(this::createOne); // set execute symbols @@ -127,31 +124,21 @@ public void create(List symbols) throws ValidationException { for (Symbol symbol : symbols) { setExecuteToSymbols(symbolRepository, symbol, symbolMap); } - - // error handling } catch (DataIntegrityViolationException e) { LOGGER.info("Symbol creation failed:", e); throw new ValidationException("Symbol could not be created.", e); } catch (NotFoundException e) { - symbols.forEach(s -> { - s.setId(null); - }); + symbols.forEach(s -> s.setId(null)); throw new ValidationException("Could not create a symbol because it has a reference to another" + " unknown symbol.", e); } catch (javax.validation.ConstraintViolationException e) { - symbols.forEach(s -> { - s.setId(null); - }); + symbols.forEach(s -> s.setId(null)); throw ValidationExceptionHelper.createValidationException("Symbols were not created:", e); } catch (org.hibernate.exception.ConstraintViolationException e) { - symbols.forEach(s -> { - s.setId(null); - }); + symbols.forEach(s -> s.setId(null)); throw new ValidationException("Symbols were not created: " + e.getMessage(), e); } catch (IllegalStateException e) { - symbols.forEach(s -> { - s.setId(null); - }); + symbols.forEach(s -> s.setId(null)); throw new ValidationException("Could not create symbol because it was invalid.", e); } } @@ -171,9 +158,9 @@ private void createOne(Symbol symbol) { throw new ValidationException("To create a symbol its name and abbreviation must be unique."); } - Long userId = symbol.getUserId(); + Long userId = symbol.getUserId(); Long projectId = symbol.getProjectId(); - Long groupId = symbol.getGroupId(); + Long groupId = symbol.getGroupId(); Project project = projectRepository.findOneByUser_IdAndId(userId, projectId); if (project == null) { @@ -211,9 +198,7 @@ public List getByIds(User user, Long projectId, List ids) throws N } // get the symbols - @SuppressWarnings("unchecked") // should return a list of Symbols List result = symbolRepository.findByIds(user.getId(), projectId, ids); - if (result.isEmpty()) { throw new NotFoundException("Could not find symbols in the project " + projectId + " with the group ids."); @@ -291,7 +276,6 @@ public Symbol get(User user, Long projectId, Long id) throws NotFoundException { @Override @Transactional public void update(Symbol symbol) throws IllegalArgumentException, NotFoundException, ValidationException { - // update try { Symbol updatedSymbol = update_(symbol); List actions = updatedSymbol.getActions(); @@ -303,8 +287,6 @@ public void update(Symbol symbol) throws IllegalArgumentException, NotFoundExcep } } setExecuteToSymbols(symbolRepository, updatedSymbol); - - // error handling } catch (DataIntegrityViolationException e) { LOGGER.info("Symbol update failed:", e); throw new ValidationException("Symbol could not be updated.", e); @@ -337,32 +319,20 @@ public void update(List symbols) throws IllegalArgumentException, NotFou for (Symbol symbol : updatedSymbols) { setExecuteToSymbols(symbolRepository, symbol, symbolMap); } - - // error handling } catch (javax.validation.ConstraintViolationException e) { - symbols.forEach(s -> { - s.setId(null); - }); + symbols.forEach(s -> s.setId(null)); throw ValidationExceptionHelper.createValidationException("Symbols were not updated:", e); } catch (org.hibernate.exception.ConstraintViolationException e) { - symbols.forEach(s -> { - s.setId(null); - }); + symbols.forEach(s -> s.setId(null)); throw new ValidationException("Symbols were not updated: " + e.getMessage(), e); } catch (IllegalStateException e) { - symbols.forEach(s -> { - s.setId(null); - }); + symbols.forEach(s -> s.setId(null)); throw new ValidationException("Could not update the Symbols because one is not valid.", e); } catch (IllegalArgumentException e) { - symbols.forEach(s -> { - s.setId(null); - }); + symbols.forEach(s -> s.setId(null)); throw e; } catch (NotFoundException e) { - symbols.forEach(s -> { - s.setId(null); - }); + symbols.forEach(s -> s.setId(null)); throw new NotFoundException("Could not update the Symbol because it was nowhere to be found.", e); } } @@ -382,6 +352,7 @@ private Symbol update_(Symbol symbol) throws IllegalArgumentException, NotFoundE Symbol symbolInDB; try { symbolInDB = get(symbol.getUser(), symbol.getProjectId(), symbol.getId()); + symbol.setSymbolId(symbol.getId()); symbol.setProject(symbolInDB.getProject()); } catch (NotFoundException e) { throw new NotFoundException("Update failed: Could not find the symbols with the id " + symbol.getId() @@ -396,11 +367,8 @@ private Symbol update_(Symbol symbol) throws IllegalArgumentException, NotFoundE symbol.getProjectId(), symbol.getGroupId()); - symbol.setSymbolId(0L); symbol.setGroup(newGroup); - beforeSymbolSave(symbol); - symbol = symbolRepository.save(symbol); // update group @@ -415,77 +383,54 @@ private Symbol update_(Symbol symbol) throws IllegalArgumentException, NotFoundE @Override @Transactional public void move(Symbol symbol, Long newGroupId) throws NotFoundException { - try { - move_(symbol, newGroupId); - } catch (NotFoundException e) { - throw e; - } + move(Collections.singletonList(symbol), newGroupId); } @Override @Transactional public void move(List symbols, Long newGroupId) throws NotFoundException { - try { - for (Symbol s : symbols) { - move_(s, newGroupId); - } - } catch (NotFoundException e) { - throw e; - } - } - - private void move_(Symbol symbol, Long newGroupId) throws NotFoundException { - SymbolGroup oldGroup = symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(symbol.getUserId(), - symbol.getProjectId(), - symbol.getGroupId()); + for (Symbol symbol : symbols) { + SymbolGroup oldGroup = symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(symbol.getUserId(), + symbol.getProjectId(), + symbol.getGroupId()); - SymbolGroup newGroup = symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(symbol.getUserId(), - symbol.getProjectId(), - newGroupId); + SymbolGroup newGroup = symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(symbol.getUserId(), + symbol.getProjectId(), + newGroupId); - if (newGroup == null) { - throw new NotFoundException("The group with the id " + newGroupId + " does not exist!"); - } + if (newGroup == null) { + throw new NotFoundException("The group with the id " + newGroupId + " does not exist!"); + } - if (!newGroup.equals(oldGroup)) { - Symbol s = symbolRepository.findOne(symbol.getUserId(), symbol.getProjectId(), symbol.getId()); - newGroup.addSymbol(s); + if (!newGroup.equals(oldGroup)) { + Symbol s = symbolRepository.findOne(symbol.getUserId(), symbol.getProjectId(), symbol.getId()); + newGroup.addSymbol(s); + } } } @Override @Transactional public void hide(Long userId, Long projectId, List ids) throws NotFoundException { - // update - try { - for (Long id : ids) { - Symbol symbol = getSymbol(userId, projectId, id); - loadLazyRelations(this, symbol); - symbol.setHidden(true); - symbolRepository.save(symbol); - } - } catch (NotFoundException e) { - throw e; + for (Long id : ids) { + Symbol symbol = getSymbol(userId, projectId, id); + loadLazyRelations(this, symbol); + symbol.setHidden(true); + symbolRepository.save(symbol); } } @Override @Transactional public void show(Long userId, Long projectId, List ids) throws NotFoundException { - // update - try { - for (Long id : ids) { - Symbol symbol = getSymbol(userId, projectId, id); - symbol.setHidden(false); - symbolRepository.save(symbol); - } - } catch (IllegalArgumentException e) { - throw e; + for (Long id : ids) { + Symbol symbol = getSymbol(userId, projectId, id); + symbol.setHidden(false); + symbolRepository.save(symbol); } } - private Symbol getSymbol(Long userId, Long projectId, Long symbolId) - throws NotFoundException { + private Symbol getSymbol(Long userId, Long projectId, Long symbolId) throws NotFoundException { Symbol symbol = symbolRepository.findOne(userId, projectId, symbolId); if (symbol == null) { @@ -539,7 +484,7 @@ public static void setExecuteToSymbols(SymbolRepository symbolRepository, Collec symbols.forEach(s -> symbolMap.put(s.getId(), s)); for (Symbol symbol : symbols) { - SymbolDAOImpl.setExecuteToSymbols(symbolRepository, symbol, symbolMap); + setExecuteToSymbols(symbolRepository, symbol, symbolMap); } } diff --git a/main/src/main/webapp/src/js/entities/actions/generalActions/ExecuteSymbolGeneralAction.js b/main/src/main/webapp/src/js/entities/actions/generalActions/ExecuteSymbolGeneralAction.js index 34ed849e0..4d5e7e791 100644 --- a/main/src/main/webapp/src/js/entities/actions/generalActions/ExecuteSymbolGeneralAction.js +++ b/main/src/main/webapp/src/js/entities/actions/generalActions/ExecuteSymbolGeneralAction.js @@ -34,7 +34,7 @@ export class ExecuteSymbolGeneralAction extends Action { * id. * @type {number} */ - this.symbolToExecute = obj.symbolToExecute || nul; + this.symbolToExecute = obj.symbolToExecute || null; let model = { name: obj.symbolToExecuteName || null, diff --git a/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java b/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java index d21876e5e..188bd3068 100644 --- a/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java +++ b/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java @@ -342,16 +342,6 @@ public void shouldReturn404WhenSymbolNotFound() throws NotFoundException { verify(symbolDAO).get(admin, PROJECT_TEST_ID, SYMBOL_TEST_ID); } - @Test - public void shouldGetTheRightSymbolWithRevision() throws NotFoundException { - given(symbolDAO.get(admin, PROJECT_TEST_ID, SYMBOL_TEST_ID)).willReturn(symbol); - String path = "/projects/" + PROJECT_TEST_ID + "/symbols/" + SYMBOL_TEST_ID + ":" + SYMBOL_TEST_REV; - Response response = target(path).request().header("Authorization", adminToken).get(); - - assertEquals(Status.OK.getStatusCode(), response.getStatus()); - verify(symbolDAO).get(admin, PROJECT_TEST_ID, SYMBOL_TEST_ID); - } - @Test public void shouldUpdateTheRightSymbol() throws NotFoundException { symbol.setUser(null); @@ -437,7 +427,7 @@ public void shouldGetSymbolsByAListOfIdRevisionPairs() throws NotFoundException } @Test - public void shouldResponseWith404IfIdRevisionPairsContainsUnexistantSymbolIds() throws NotFoundException { + public void shouldResponseWith404IfIdsContainNonexistentSymbolIds() throws NotFoundException { given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, ids)).willThrow(NotFoundException.class); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/1,2"; From a20faa70248524df15ce050863c833f1d856e84f Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Tue, 22 Nov 2016 19:35:55 +0100 Subject: [PATCH 13/52] fixed some problems with douplicated actions --- .../learnlib/alex/core/dao/SymbolDAOImpl.java | 43 ++++++++----------- .../alex/core/dao/SymbolGroupDAOImpl.java | 8 +++- .../learnlib/alex/core/entities/Symbol.java | 3 +- .../repositories/SymbolActionRepository.java | 16 +++++++ .../alex/core/dao/SymbolDAOImplTest.java | 6 ++- .../alex/core/dao/SymbolGroupDAOImplTest.java | 7 ++- 6 files changed, 51 insertions(+), 32 deletions(-) create mode 100644 main/src/main/java/de/learnlib/alex/core/repositories/SymbolActionRepository.java diff --git a/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java b/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java index 7a89257e7..114d1854d 100644 --- a/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java +++ b/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java @@ -24,6 +24,7 @@ import de.learnlib.alex.core.entities.SymbolVisibilityLevel; import de.learnlib.alex.core.entities.User; import de.learnlib.alex.core.repositories.ProjectRepository; +import de.learnlib.alex.core.repositories.SymbolActionRepository; import de.learnlib.alex.core.repositories.SymbolGroupRepository; import de.learnlib.alex.core.repositories.SymbolRepository; import de.learnlib.alex.exceptions.NotFoundException; @@ -63,6 +64,9 @@ public class SymbolDAOImpl implements SymbolDAO { /** The SymbolRepository to use. Will be injected. */ private SymbolRepository symbolRepository; + /** The SymbolActionRepository to use. Will be injected. */ + private SymbolActionRepository symbolActionRepository; + /** * Creates a new SymbolDAO. * @@ -72,13 +76,16 @@ public class SymbolDAOImpl implements SymbolDAO { * The SymbolGroupRepository to use. * @param symbolRepository * The SymbolRepository to use. + * @param symbolActionRepository + * The SymbolActionRepository to use. */ @Inject public SymbolDAOImpl(ProjectRepository projectRepository, SymbolGroupRepository symbolGroupRepository, - SymbolRepository symbolRepository) { + SymbolRepository symbolRepository, SymbolActionRepository symbolActionRepository) { this.projectRepository = projectRepository; this.symbolGroupRepository = symbolGroupRepository; this.symbolRepository = symbolRepository; + this.symbolActionRepository = symbolActionRepository; } @Override @@ -349,35 +356,22 @@ private Symbol update_(Symbol symbol) throws IllegalArgumentException, NotFoundE throw new ValidationException("To update a symbol its name and abbreviation must be unique."); } - Symbol symbolInDB; try { - symbolInDB = get(symbol.getUser(), symbol.getProjectId(), symbol.getId()); - symbol.setSymbolId(symbol.getId()); + Symbol symbolInDB = get(symbol.getUser(), symbol.getProjectId(), symbol.getId()); + symbol.setSymbolId(symbolInDB.getSymbolId()); symbol.setProject(symbolInDB.getProject()); + symbol.setGroup(symbolInDB.getGroup()); + + symbolActionRepository.deleteBySymbol_Id(symbolInDB.getSymbolId()); } catch (NotFoundException e) { throw new NotFoundException("Update failed: Could not find the symbols with the id " + symbol.getId() + " in the project " + symbol.getProjectId() + "."); } - SymbolGroup oldGroup = symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(symbol.getUserId(), - symbol.getProjectId(), - symbolInDB.getGroupId()); + // TODO update the group but it currently breaks the update process, use the move method for this atm - SymbolGroup newGroup = symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(symbol.getUserId(), - symbol.getProjectId(), - symbol.getGroupId()); - - symbol.setGroup(newGroup); beforeSymbolSave(symbol); - symbol = symbolRepository.save(symbol); - - // update group - if (!newGroup.equals(oldGroup)) { - Symbol s = symbolRepository.findOne(symbol.getUserId(), symbol.getProjectId(), symbol.getId()); - newGroup.addSymbol(s); - } - - return symbol; + return symbolRepository.save(symbol); } @Override @@ -459,14 +453,11 @@ private boolean nameAndAbbreviationAreNotUnique(Symbol symbol) { * The symbol. */ public static void beforeSymbolSave(Symbol symbol) { - User user = symbol.getUser(); - Project project = symbol.getProject(); - for (int i = 0; i < symbol.getActions().size(); i++) { SymbolAction action = symbol.getActions().get(i); action.setId(null); - action.setUser(user); - action.setProject(project); + action.setUser(symbol.getUser()); + action.setProject(symbol.getProject()); action.setSymbol(symbol); action.setNumber(i); } diff --git a/main/src/main/java/de/learnlib/alex/core/dao/SymbolGroupDAOImpl.java b/main/src/main/java/de/learnlib/alex/core/dao/SymbolGroupDAOImpl.java index cf2e429fc..2820e385d 100644 --- a/main/src/main/java/de/learnlib/alex/core/dao/SymbolGroupDAOImpl.java +++ b/main/src/main/java/de/learnlib/alex/core/dao/SymbolGroupDAOImpl.java @@ -21,6 +21,7 @@ import de.learnlib.alex.core.entities.SymbolGroup; import de.learnlib.alex.core.entities.User; import de.learnlib.alex.core.repositories.ProjectRepository; +import de.learnlib.alex.core.repositories.SymbolActionRepository; import de.learnlib.alex.core.repositories.SymbolGroupRepository; import de.learnlib.alex.core.repositories.SymbolRepository; import de.learnlib.alex.exceptions.NotFoundException; @@ -77,15 +78,18 @@ public class SymbolGroupDAOImpl implements SymbolGroupDAO { * The SymbolGroupRepository to use. * @param symbolRepository * The SymbolRepository to use. + * @param symbolActionRepository + * The SymbolActionRepository to use. */ @Inject public SymbolGroupDAOImpl(ProjectRepository projectRepository, SymbolGroupRepository symbolGroupRepository, - SymbolRepository symbolRepository) { + SymbolRepository symbolRepository, SymbolActionRepository symbolActionRepository) { this.projectRepository = projectRepository; this.symbolGroupRepository = symbolGroupRepository; this.symbolRepository = symbolRepository; - this.symbolDAO = new SymbolDAOImpl(projectRepository, symbolGroupRepository, symbolRepository); + this.symbolDAO = new SymbolDAOImpl(projectRepository, symbolGroupRepository, symbolRepository, + symbolActionRepository); } @Override diff --git a/main/src/main/java/de/learnlib/alex/core/entities/Symbol.java b/main/src/main/java/de/learnlib/alex/core/entities/Symbol.java index ac2e2b4e8..524a94e3c 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/Symbol.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/Symbol.java @@ -297,7 +297,6 @@ public void setGroupId(Long groupId) { * @return The ID. * @requiredField */ -// @Transient @JsonProperty public Long getId() { return this.id; @@ -384,7 +383,7 @@ public void setHidden(boolean hidden) { @OneToMany( mappedBy = "symbol", fetch = FetchType.LAZY, - cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}) + cascade = {CascadeType.ALL}) @OrderBy("number ASC") @JsonProperty public List getActions() { diff --git a/main/src/main/java/de/learnlib/alex/core/repositories/SymbolActionRepository.java b/main/src/main/java/de/learnlib/alex/core/repositories/SymbolActionRepository.java new file mode 100644 index 000000000..083b9257b --- /dev/null +++ b/main/src/main/java/de/learnlib/alex/core/repositories/SymbolActionRepository.java @@ -0,0 +1,16 @@ +package de.learnlib.alex.core.repositories; + +import de.learnlib.alex.core.entities.SymbolAction; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +/** + * The repository for symbol actions. + */ +@Repository +public interface SymbolActionRepository extends JpaRepository { + + @Transactional + Long deleteBySymbol_Id(Long symbolId); +} diff --git a/main/src/test/java/de/learnlib/alex/core/dao/SymbolDAOImplTest.java b/main/src/test/java/de/learnlib/alex/core/dao/SymbolDAOImplTest.java index 1a4982498..f38aeb007 100644 --- a/main/src/test/java/de/learnlib/alex/core/dao/SymbolDAOImplTest.java +++ b/main/src/test/java/de/learnlib/alex/core/dao/SymbolDAOImplTest.java @@ -24,6 +24,7 @@ import de.learnlib.alex.core.entities.SymbolVisibilityLevel; import de.learnlib.alex.core.entities.User; import de.learnlib.alex.core.repositories.ProjectRepository; +import de.learnlib.alex.core.repositories.SymbolActionRepository; import de.learnlib.alex.core.repositories.SymbolGroupRepository; import de.learnlib.alex.core.repositories.SymbolRepository; import de.learnlib.alex.exceptions.NotFoundException; @@ -72,11 +73,14 @@ public class SymbolDAOImplTest { @Mock private SymbolRepository symbolRepository; + @Mock + private SymbolActionRepository symbolActionRepository; + private SymbolDAO symbolDAO; @Before public void setUp() { - symbolDAO = new SymbolDAOImpl(projectRepository, symbolGroupRepository, symbolRepository); + symbolDAO = new SymbolDAOImpl(projectRepository, symbolGroupRepository, symbolRepository, symbolActionRepository); } @Test diff --git a/main/src/test/java/de/learnlib/alex/core/dao/SymbolGroupDAOImplTest.java b/main/src/test/java/de/learnlib/alex/core/dao/SymbolGroupDAOImplTest.java index bea8a3020..a629f1ce9 100644 --- a/main/src/test/java/de/learnlib/alex/core/dao/SymbolGroupDAOImplTest.java +++ b/main/src/test/java/de/learnlib/alex/core/dao/SymbolGroupDAOImplTest.java @@ -20,6 +20,7 @@ import de.learnlib.alex.core.entities.SymbolGroup; import de.learnlib.alex.core.entities.User; import de.learnlib.alex.core.repositories.ProjectRepository; +import de.learnlib.alex.core.repositories.SymbolActionRepository; import de.learnlib.alex.core.repositories.SymbolGroupRepository; import de.learnlib.alex.core.repositories.SymbolRepository; import de.learnlib.alex.exceptions.NotFoundException; @@ -62,11 +63,15 @@ public class SymbolGroupDAOImplTest { @Mock private SymbolRepository symbolRepository; + @Mock + private SymbolActionRepository symbolActionRepository; + private SymbolGroupDAO symbolGroupDAO; @Before public void setUp() { - symbolGroupDAO = new SymbolGroupDAOImpl(projectRepository, symbolGroupRepository, symbolRepository); + symbolGroupDAO = new SymbolGroupDAOImpl(projectRepository, symbolGroupRepository, symbolRepository, + symbolActionRepository); } @Test From b42870112136aae1de380b0989d4859ed84eb05a Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Wed, 23 Nov 2016 10:01:20 +0100 Subject: [PATCH 14/52] fixed remaining tests --- .../alex/core/dao/SymbolDAOImplTest.java | 10 ++++------ .../alex/rest/SymbolResourceTest.java | 20 +++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/main/src/test/java/de/learnlib/alex/core/dao/SymbolDAOImplTest.java b/main/src/test/java/de/learnlib/alex/core/dao/SymbolDAOImplTest.java index f38aeb007..be1700264 100644 --- a/main/src/test/java/de/learnlib/alex/core/dao/SymbolDAOImplTest.java +++ b/main/src/test/java/de/learnlib/alex/core/dao/SymbolDAOImplTest.java @@ -351,9 +351,9 @@ public void shouldUpdateASymbol() throws NotFoundException { symbol.setProject(project); symbol.setGroup(group); // - given(symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(USER_ID, PROJECT_ID, GROUP_ID)) - .willReturn(group); +// given(symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(USER_ID, PROJECT_ID, GROUP_ID)).willReturn(group); given(symbolRepository.findOne(USER_ID, PROJECT_ID, symbol.getId())).willReturn(symbol); + given(symbolRepository.save(symbol)).willReturn(symbol); symbolDAO.update(symbol); @@ -401,8 +401,7 @@ public void shouldHandleDataIntegrityViolationExceptionOnSymbolUpdateGracefully( symbol.setProject(project); symbol.setGroup(group); // - given(symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(USER_ID, PROJECT_ID, GROUP_ID)) - .willReturn(group); +// given(symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(USER_ID, PROJECT_ID, GROUP_ID)).willReturn(group); given(symbolRepository.findOne(USER_ID, PROJECT_ID, symbol.getId())).willReturn(symbol); given(symbolRepository.save(symbol)).willThrow(DataIntegrityViolationException.class); @@ -432,8 +431,7 @@ public void shouldHandleTransactionSystemExceptionOnGroupUpdateGracefully() thro transactionSystemException = new TransactionSystemException("Spring TransactionSystemException", rollbackException); // - given(symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(USER_ID, PROJECT_ID, GROUP_ID)) - .willReturn(group); +// given(symbolGroupRepository.findOneByUser_IdAndProject_IdAndId(USER_ID, PROJECT_ID, GROUP_ID)).willReturn(group); given(symbolRepository.findOne(USER_ID, PROJECT_ID, symbol.getId())).willReturn(symbol); given(symbolRepository.save(symbol)).willThrow(transactionSystemException); diff --git a/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java b/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java index 188bd3068..f5ba6ac97 100644 --- a/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java +++ b/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java @@ -60,7 +60,6 @@ public class SymbolResourceTest extends JerseyTest { private static final long USER_TEST_ID = 1; private static final long PROJECT_TEST_ID = 10; private static final long SYMBOL_TEST_ID = 1; - private static final long SYMBOL_TEST_REV = 3; @Mock private ProjectDAO projectDAO; @@ -151,7 +150,7 @@ public void shouldCreateValidSymbol() throws IOException { } @Test - public void shouldCreateValidSymbolWithoutProjectOrRevision() throws IOException, NotFoundException { + public void shouldCreateValidSymbolWithoutProject() throws IOException, NotFoundException { String json = "{\"abbreviation\":\"srts\",\"actions\":[],\"id\":1," + "\"name\":\"Symbol Resource Test Symbol\"}"; @@ -214,7 +213,7 @@ public void shouldCreateValidSymbols() throws IOException { } @Test - public void shouldCreateValidSymbolsWithoutProjectOrRevision() throws IOException { + public void shouldCreateValidSymbolsWithoutProject() throws IOException { // given symbol.setProject(null); ObjectMapper mapper = new ObjectMapper(); @@ -412,10 +411,10 @@ public void shouldNotUpdateMultipleSymbolsIfIdsDoNotMatch() throws NotFoundExcep } @Test - public void shouldGetSymbolsByAListOfIdRevisionPairs() throws NotFoundException { + public void shouldGetSymbolsByAListOfIds() throws NotFoundException { given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, ids)).willReturn(symbols); - Response response = target("/projects/" + PROJECT_TEST_ID + "/symbols/batch/1:1,2:1").request() + Response response = target("/projects/" + PROJECT_TEST_ID + "/symbols/batch/1,2").request() .header("Authorization", adminToken).get(); assertEquals(Status.OK.getStatusCode(), response.getStatus()); List responseSymbols = response.readEntity(new GenericType>() { }); @@ -479,7 +478,7 @@ public void shouldMoveMultipleSymbols() throws NotFoundException { ids.add(symbol.getId()); ids.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, ids)).willReturn(symbols); + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)).willReturn(symbols); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() + "/moveTo/" + group.getId(); @@ -495,7 +494,8 @@ public void ensureThatMovingSymbolsThatDoNotExistsIsHandedProperly() throws NotF ids.add(symbol.getId()); ids.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, ids)).willThrow(NotFoundException.class); + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)) + .willThrow(NotFoundException.class); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() + "/moveTo/" + group.getId(); @@ -511,7 +511,7 @@ public void ensureThatMovingMultipleSymbolsIntoTheVoidIsHandedProperly() throws ids.add(symbol.getId()); ids.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, ids)).willReturn(symbols); + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)).willReturn(symbols); willThrow(NotFoundException.class).given(symbolDAO).move(symbols, group.getId()); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() @@ -541,7 +541,7 @@ public void shouldHideMultipleSymbols() throws NotFoundException { ids.add(symbol.getId()); ids.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, ids)).willReturn(symbols); + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)).willReturn(symbols); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() + "/hide"; @@ -601,7 +601,7 @@ public void shouldShowMultipleSymbols() throws NotFoundException { ids.add(symbol.getId()); ids.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, ids)).willReturn(symbols); + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)).willReturn(symbols); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() + "/show"; From 1b1aa13f08644da7751deb08e3a26e8011756d8d Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Wed, 23 Nov 2016 11:11:56 +0100 Subject: [PATCH 15/52] fixed most checkstyle violations --- .../de/learnlib/alex/ALEXApplication.java | 7 ++- .../alex/actions/ExecuteSymbolAction.java | 2 +- .../actions/RESTSymbolActions/CallAction.java | 56 +++++++++++++++++-- .../WebSymbolActions/PressKeyAction.java | 8 +-- .../alex/core/dao/CounterDAOImpl.java | 17 +----- .../alex/core/dao/LearnerResultDAO.java | 4 +- .../alex/core/dao/SettingsDAOImpl.java | 2 - .../learnlib/alex/core/dao/SymbolDAOImpl.java | 31 ++++++---- .../alex/core/entities/Algorithm.java | 2 - .../alex/core/entities/ReadOutputConfig.java | 2 +- .../MealyRandomWordsEQOracleProxy.java | 2 + .../learnlib/alex/core/learner/Learner.java | 7 ++- .../alex/core/learner/LearnerThread.java | 3 +- .../repositories/SymbolActionRepository.java | 25 +++++++++ .../core/repositories/SymbolRepository.java | 8 ++- .../alex/rest/IFrameProxyResource.java | 3 +- .../learnlib/alex/rest/LearnerResource.java | 26 ++++++--- .../learnlib/alex/rest/SettingsResource.java | 2 - .../RESTSymbolActions/CallActionTest.java | 4 -- .../WaitForTitleActionTest.java | 1 - .../alex/core/dao/SymbolDAOImplTest.java | 29 ++++++---- .../entities/LearnerConfigurationTest.java | 5 +- .../core/learner/WebServiceConnectorTest.java | 4 +- .../LearnerResultStepRepositoryIT.java | 3 +- .../integrationtests/SymbolRepositoryIT.java | 3 +- .../alex/rest/LearnerResourceTest.java | 4 +- .../alex/rest/SymbolResourceTest.java | 44 +++++++-------- 27 files changed, 198 insertions(+), 106 deletions(-) diff --git a/main/src/main/java/de/learnlib/alex/ALEXApplication.java b/main/src/main/java/de/learnlib/alex/ALEXApplication.java index 3f7db90ab..bb025fd05 100644 --- a/main/src/main/java/de/learnlib/alex/ALEXApplication.java +++ b/main/src/main/java/de/learnlib/alex/ALEXApplication.java @@ -193,13 +193,18 @@ public void findAlgorithms() { LOGGER.info("{} LearnAlgorithms found.", algorithms.size()); } + /** + * HTTP request filter that is required for the {@link IFrameProxyResource} to work properly. + * + * @return The filter. + */ @Bean public HiddenHttpMethodFilter hiddenHttpMethodFilter() { return new HiddenHttpMethodFilter() { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - if ("POST".equals(request.getMethod()) + if (request.getMethod().equals("POST") && request.getContentType().equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) { filterChain.doFilter(request, response); } else { diff --git a/main/src/main/java/de/learnlib/alex/actions/ExecuteSymbolAction.java b/main/src/main/java/de/learnlib/alex/actions/ExecuteSymbolAction.java index 5d1b308dc..eaaaacdb8 100644 --- a/main/src/main/java/de/learnlib/alex/actions/ExecuteSymbolAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/ExecuteSymbolAction.java @@ -83,7 +83,7 @@ public Long getSymbolToExecuteAsId() { * Set a new reference to the Symbol to execute. * This does not update the actual Symbol! * - * @param symbolToExecuteAsIdRevisionPair The new IdRevisionPair of the Symbol to execute. + * @param symbolToExecuteAsId The new id of the Symbol to execute. */ public void setSymbolToExecuteAsId(Long symbolToExecuteAsId) { this.symbolToExecuteAsId = symbolToExecuteAsId; diff --git a/main/src/main/java/de/learnlib/alex/actions/RESTSymbolActions/CallAction.java b/main/src/main/java/de/learnlib/alex/actions/RESTSymbolActions/CallAction.java index 79fbd7412..dee9cb400 100644 --- a/main/src/main/java/de/learnlib/alex/actions/RESTSymbolActions/CallAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/RESTSymbolActions/CallAction.java @@ -397,9 +397,57 @@ public TestResult testRequest(String baseUrl) { * The result object for the {@link #testRequest(String)} method. */ public static class TestResult { - public int status; - public String body; - public Map cookies = new HashMap<>(); - public Map headers = new HashMap<>(); + + /** The status of the HTTP response. */ + private int status; + + /** The body of the HTTP response. */ + private String body; + + /** The cookies of the HTTP response. */ + private Map cookies = new HashMap<>(); + + /** The headers of the HTTP response. */ + private Map headers = new HashMap<>(); + + /** @return The status. */ + public int getStatus() { + return status; + } + + /** @param status The status. */ + public void setStatus(int status) { + this.status = status; + } + + /** @return The body. */ + public String getBody() { + return body; + } + + /** @param body The body. */ + public void setBody(String body) { + this.body = body; + } + + /** @return The cookies. */ + public Map getCookies() { + return cookies; + } + + /** @param cookies The cookies. */ + public void setCookies(Map cookies) { + this.cookies = cookies; + } + + /** @return The header. */ + public Map getHeaders() { + return headers; + } + + /** @param headers The headers. */ + public void setHeaders(Map headers) { + this.headers = headers; + } } } diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/PressKeyAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/PressKeyAction.java index ade4bf849..c0813b221 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/PressKeyAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/PressKeyAction.java @@ -65,17 +65,17 @@ public class PressKeyAction extends WebSymbolAction { @Override protected ExecuteResult execute(WebSiteConnector connector) { String unescapedKey = StringEscapeUtils.unescapeJava(this.key); - Keys key = Keys.getKeyFromUnicode(unescapedKey.toCharArray()[0]); + Keys keyToPress = Keys.getKeyFromUnicode(unescapedKey.toCharArray()[0]); try { WebElement element = connector.getElement(CSSUtils.escapeSelector(insertVariableValues(node))); - element.sendKeys(key); + element.sendKeys(keyToPress); LOGGER.info(LEARNER_MARKER, "Pressed the key '{}' on the element '{}' (ignoreFailure: {}, negated: {}).", - key.toString(), node, ignoreFailure, negated); + keyToPress.toString(), node, ignoreFailure, negated); return getSuccessOutput(); } catch (NoSuchElementException e) { LOGGER.info(LEARNER_MARKER, "Could not press key '{}' on element '{}' (ignoreFailure: {}, negated: {}).", - key.toString(), node, ignoreFailure, negated, e); + keyToPress.toString(), node, ignoreFailure, negated, e); return getFailedOutput(); } } diff --git a/main/src/main/java/de/learnlib/alex/core/dao/CounterDAOImpl.java b/main/src/main/java/de/learnlib/alex/core/dao/CounterDAOImpl.java index 7aefcbdb7..1df882b80 100644 --- a/main/src/main/java/de/learnlib/alex/core/dao/CounterDAOImpl.java +++ b/main/src/main/java/de/learnlib/alex/core/dao/CounterDAOImpl.java @@ -68,10 +68,7 @@ public void create(Counter counter) throws ValidationException { try { Project project = projectRepository.findOne(counter.getProjectId()); counter.setProject(project); - counterRepository.save(counter); - - // error handling } catch (DataIntegrityViolationException e) { LOGGER.info("Counter creation failed:", e); throw new javax.validation.ValidationException("Counter could not be created.", e); @@ -96,16 +93,10 @@ public List getAll(Long userId, Long projectId) throws NotFoundExceptio @Override @Transactional(readOnly = true) public Counter get(Long userId, Long projectId, String name) throws NotFoundException { - try { - Counter result = get_(userId, projectId, name); - - return result; - } catch (NotFoundException e) { - throw e; - } + return doGet(userId, projectId, name); } - private Counter get_(Long userId, Long projectId, String name) throws NotFoundException { + private Counter doGet(Long userId, Long projectId, String name) throws NotFoundException { Project project = projectRepository.findOneByUser_IdAndId(userId, projectId); if (project == null) { throw new NotFoundException("The project with the id " + projectId + " was not found."); @@ -124,10 +115,8 @@ private Counter get_(Long userId, Long projectId, String name) throws NotFoundEx @Transactional public void update(Counter counter) throws NotFoundException, ValidationException { try { - get_(counter.getUserId(), counter.getProjectId(), counter.getName()); // check if the counter exists + doGet(counter.getUserId(), counter.getProjectId(), counter.getName()); // check if the counter exists counterRepository.save(counter); - - // error handling } catch (DataIntegrityViolationException e) { LOGGER.info("Counter update failed:", e); throw new javax.validation.ValidationException("Counter could not be updated.", e); diff --git a/main/src/main/java/de/learnlib/alex/core/dao/LearnerResultDAO.java b/main/src/main/java/de/learnlib/alex/core/dao/LearnerResultDAO.java index 62b761a3a..277f317b2 100644 --- a/main/src/main/java/de/learnlib/alex/core/dao/LearnerResultDAO.java +++ b/main/src/main/java/de/learnlib/alex/core/dao/LearnerResultDAO.java @@ -132,8 +132,10 @@ LearnerResultStep createStep(LearnerResult result, LearnerResumeConfiguration co /** * Remove a complete test run of a project. * + * @param learner + * The learner. * @param user - * The user of the LearnerResult + * The user of the LearnerResult. * @param projectId * The project id. * @param testNo diff --git a/main/src/main/java/de/learnlib/alex/core/dao/SettingsDAOImpl.java b/main/src/main/java/de/learnlib/alex/core/dao/SettingsDAOImpl.java index 80c52ac5f..a1829b76b 100644 --- a/main/src/main/java/de/learnlib/alex/core/dao/SettingsDAOImpl.java +++ b/main/src/main/java/de/learnlib/alex/core/dao/SettingsDAOImpl.java @@ -18,8 +18,6 @@ import de.learnlib.alex.core.entities.Settings; import de.learnlib.alex.core.repositories.SettingsRepository; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java b/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java index 114d1854d..a7a178036 100644 --- a/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java +++ b/main/src/main/java/de/learnlib/alex/core/dao/SymbolDAOImpl.java @@ -94,7 +94,7 @@ public void create(Symbol symbol) throws ValidationException { LOGGER.traceEntry("create({})", symbol); try { createOne(symbol); - setExecuteToSymbols(symbolRepository, symbol); + setExecuteToSymbols(symbol); } catch (DataIntegrityViolationException e) { LOGGER.info("Symbol creation failed:", e); throw new ValidationException("Symbol could not be created.", e); @@ -243,7 +243,8 @@ public List getAll(User user, Long projectId, Long groupId) throws NotFo public List getAll(User user, Long projectId, Long groupId, SymbolVisibilityLevel visibilityLevel) throws NotFoundException { - List symbols = symbolRepository.findAll(user.getId(), projectId, groupId, visibilityLevel.getCriterion()); + List symbols = symbolRepository.findAll(user.getId(), projectId, groupId, + visibilityLevel.getCriterion()); symbols.forEach(s -> loadLazyRelations(this, s)); @@ -284,16 +285,16 @@ public Symbol get(User user, Long projectId, Long id) throws NotFoundException { @Transactional public void update(Symbol symbol) throws IllegalArgumentException, NotFoundException, ValidationException { try { - Symbol updatedSymbol = update_(symbol); + Symbol updatedSymbol = doUpdate(symbol); List actions = updatedSymbol.getActions(); - for(int i = 0; i < actions.size(); i++) { + for (int i = 0; i < actions.size(); i++) { SymbolAction action = actions.get(i); if (action instanceof ExecuteSymbolAction) { Long id = ((ExecuteSymbolAction) symbol.getActions().get(i)).getSymbolToExecuteAsId(); ((ExecuteSymbolAction) action).setSymbolToExecuteAsId(id); } } - setExecuteToSymbols(symbolRepository, updatedSymbol); + setExecuteToSymbols(updatedSymbol); } catch (DataIntegrityViolationException e) { LOGGER.info("Symbol update failed:", e); throw new ValidationException("Symbol could not be updated.", e); @@ -313,11 +314,10 @@ public void update(Symbol symbol) throws IllegalArgumentException, NotFoundExcep @Override @Transactional public void update(List symbols) throws IllegalArgumentException, NotFoundException, ValidationException { - // update try { List updatedSymbols = new LinkedList<>(); for (Symbol symbol : symbols) { - Symbol updatedSymbol = update_(symbol); + Symbol updatedSymbol = doUpdate(symbol); updatedSymbols.add(updatedSymbol); } @@ -344,7 +344,7 @@ public void update(List symbols) throws IllegalArgumentException, NotFou } } - private Symbol update_(Symbol symbol) throws IllegalArgumentException, NotFoundException { + private Symbol doUpdate(Symbol symbol) throws IllegalArgumentException, NotFoundException { // checks for valid symbol if (symbol.getProjectId() == null) { throw new NotFoundException("Update failed: Could not find the project with the id + " @@ -463,12 +463,22 @@ public static void beforeSymbolSave(Symbol symbol) { } } - private void setExecuteToSymbols(SymbolRepository symbolRepository, Symbol symbol) throws NotFoundException { + private void setExecuteToSymbols(Symbol symbol) throws NotFoundException { Map symbolMap = new HashMap<>(); symbolMap.put(symbol.getId(), symbol); setExecuteToSymbols(symbolRepository, symbol, symbolMap); } + /** + * Update references to executed symbols from {@link ExecuteSymbolAction}. + * + * @param symbolRepository + * An instance of the repository. + * @param symbols + * The symbols that should be updated. + * @throws NotFoundException + * If a symbol could not be found. + */ public static void setExecuteToSymbols(SymbolRepository symbolRepository, Collection symbols) throws NotFoundException { Map symbolMap = new HashMap<>(); @@ -498,7 +508,8 @@ private static void setExecuteToSymbols(SymbolRepository symbolRepository, Symbo Symbol symbolToExecute = cachedSymbols.get(id); if (symbolToExecute == null) { // it was not in the set of cached symbols - symbolToExecute = symbolRepository.findOne(action.getUser().getId(), action.getProject().getId(), id); + symbolToExecute = symbolRepository.findOne(action.getUser().getId(), action.getProject().getId(), + id); } if (symbolToExecute == null) { diff --git a/main/src/main/java/de/learnlib/alex/core/entities/Algorithm.java b/main/src/main/java/de/learnlib/alex/core/entities/Algorithm.java index bd95ef374..af45e482a 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/Algorithm.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/Algorithm.java @@ -1,7 +1,5 @@ package de.learnlib.alex.core.entities; -import de.learnlib.alex.annotations.LearnAlgorithm; - import javax.persistence.Column; import javax.persistence.Embeddable; import javax.persistence.Transient; diff --git a/main/src/main/java/de/learnlib/alex/core/entities/ReadOutputConfig.java b/main/src/main/java/de/learnlib/alex/core/entities/ReadOutputConfig.java index 1b89c5c68..46dcd0d74 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/ReadOutputConfig.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/ReadOutputConfig.java @@ -50,4 +50,4 @@ public WebBrowser getBrowser() { public void setBrowser(WebBrowser browser) { this.browser = browser; } -} \ No newline at end of file +} diff --git a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java index 95e44f642..f1326676d 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java @@ -69,6 +69,8 @@ public MealyRandomWordsEQOracleProxy() { * The maximal length of the random generated words. * @param maxNoOfTests * Highest amount of generated word before ending the search. + * @param seed + * The seed for the random number generator. */ public MealyRandomWordsEQOracleProxy(int minLength, int maxLength, int maxNoOfTests, int seed) { this.minLength = minLength; diff --git a/main/src/main/java/de/learnlib/alex/core/learner/Learner.java b/main/src/main/java/de/learnlib/alex/core/learner/Learner.java index 55032f51d..95b8c05d7 100644 --- a/main/src/main/java/de/learnlib/alex/core/learner/Learner.java +++ b/main/src/main/java/de/learnlib/alex/core/learner/Learner.java @@ -475,9 +475,10 @@ public List readOutputs(User user, Project project, Symbol resetSymbol, LOGGER.traceEntry(); LOGGER.info(LEARNER_MARKER, "Learner.readOutputs({}, {}, {}, {})", user, project, resetSymbol, symbols); - ConnectorContextHandler contextHandler = contextHandlerFactory.createContext(project, readOutputConfig.getBrowser()); - contextHandler.setResetSymbol(resetSymbol); - ConnectorManager connectors = contextHandler.createContext(); + ConnectorContextHandler ctxHandler = contextHandlerFactory.createContext( + project, readOutputConfig.getBrowser()); + ctxHandler.setResetSymbol(resetSymbol); + ConnectorManager connectors = ctxHandler.createContext(); return readOutputs(symbols, connectors); } diff --git a/main/src/main/java/de/learnlib/alex/core/learner/LearnerThread.java b/main/src/main/java/de/learnlib/alex/core/learner/LearnerThread.java index d7b530496..e6fb19215 100644 --- a/main/src/main/java/de/learnlib/alex/core/learner/LearnerThread.java +++ b/main/src/main/java/de/learnlib/alex/core/learner/LearnerThread.java @@ -342,7 +342,8 @@ private void learnSuccessiveStep() throws NotFoundException { try { learner.refineHypothesis(counterExampleDefaultProxy); } catch (NullPointerException e) { - throw new LearnerException("Presumably the detected counterexample '" + counterExampleDefaultProxy + "' was not a real counterexample!"); + throw new LearnerException("Presumably the detected counterexample '" + counterExampleDefaultProxy + + "' was not a real counterexample!"); } finally { storeLearnerMetaData(); } diff --git a/main/src/main/java/de/learnlib/alex/core/repositories/SymbolActionRepository.java b/main/src/main/java/de/learnlib/alex/core/repositories/SymbolActionRepository.java index 083b9257b..a0554ba1e 100644 --- a/main/src/main/java/de/learnlib/alex/core/repositories/SymbolActionRepository.java +++ b/main/src/main/java/de/learnlib/alex/core/repositories/SymbolActionRepository.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 TU Dortmund + * + * Licensed 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. + */ + package de.learnlib.alex.core.repositories; import de.learnlib.alex.core.entities.SymbolAction; @@ -11,6 +27,15 @@ @Repository public interface SymbolActionRepository extends JpaRepository { + /** + * Deletes all actions of a specified symbol. + * + * @param symbolId + * The id of the symbol. + * @return + * The number of deleted actions. + */ @Transactional + @SuppressWarnings("checkstyle:methodname") Long deleteBySymbol_Id(Long symbolId); } diff --git a/main/src/main/java/de/learnlib/alex/core/repositories/SymbolRepository.java b/main/src/main/java/de/learnlib/alex/core/repositories/SymbolRepository.java index e2be0c204..720c6df34 100644 --- a/main/src/main/java/de/learnlib/alex/core/repositories/SymbolRepository.java +++ b/main/src/main/java/de/learnlib/alex/core/repositories/SymbolRepository.java @@ -69,12 +69,16 @@ public interface SymbolRepository extends JpaRepository { /** * Get symbols of a user and project with a specific name. * + * @param symbolId + * The ID of the symbol. * @param userId - * The ID of the user the symbols belong to. + * The ID of the user the symbol belong to. * @param projectId * The ID of the project the symbol belongs to. * @param name - * The name of the Symbol. + * The name of the symbol. + * @param abbreviation + * The abbreviation of the symbol. * @return The symbols. */ @Query("SELECT COUNT(s) " diff --git a/main/src/main/java/de/learnlib/alex/rest/IFrameProxyResource.java b/main/src/main/java/de/learnlib/alex/rest/IFrameProxyResource.java index f6f39d43c..871932cb2 100644 --- a/main/src/main/java/de/learnlib/alex/rest/IFrameProxyResource.java +++ b/main/src/main/java/de/learnlib/alex/rest/IFrameProxyResource.java @@ -55,6 +55,7 @@ @Path("proxy") public class IFrameProxyResource { + /** The timeout time in ms. */ private static final int REQUEST_TIMEOUT_TIME = 10000; // in ms private static final Logger LOGGER = LogManager.getLogger(); @@ -156,7 +157,7 @@ private Map parseCookies(String cookies) { return cookieMap; } - private Map parseFormData(MultivaluedMap body) { + private Map parseFormData(MultivaluedMap body) { Map formData = new HashMap<>(); for (Map.Entry> entry : body.entrySet()) { String key = entry.getKey(); diff --git a/main/src/main/java/de/learnlib/alex/rest/LearnerResource.java b/main/src/main/java/de/learnlib/alex/rest/LearnerResource.java index 43109b404..517cdbacf 100644 --- a/main/src/main/java/de/learnlib/alex/rest/LearnerResource.java +++ b/main/src/main/java/de/learnlib/alex/rest/LearnerResource.java @@ -127,10 +127,8 @@ public Response start(@PathParam("project_id") long projectId, LearnerConfigurat LOGGER.traceEntry("start({}, {}) for user {}.", projectId, configuration, user); try { - if ( - (configuration.getUserId() != null && !user.getId().equals(configuration.getUserId())) - || (configuration.getProjectId() != null && !configuration.getProjectId().equals(projectId)) - ) { + if ((configuration.getUserId() != null && !user.getId().equals(configuration.getUserId())) + || (configuration.getProjectId() != null && !configuration.getProjectId().equals(projectId))) { throw new IllegalArgumentException("If an user or a project is provided in the configuration, " + "they must match the parameters in the path!"); } @@ -328,6 +326,18 @@ public Response readOutput(@PathParam("project_id") Long projectId, SymbolSet sy } // TODO: create a new resource/dao for words and move this method there then. + /** + * Get the outputs of a word when executed to the SUL. + * + * @param projectId + * The id of the project. + * @param readOutputConfig + * The config that is used to query the SUL. + * @return + * A response with the output of the word. + * @throws NotFoundException + * If a symbol could not be found. + */ @POST @Path("/words/{project_id}/outputs") @Consumes(MediaType.APPLICATION_JSON) @@ -366,22 +376,20 @@ public Response readWordOutput(@PathParam("project_id") Long projectId, ReadOutp // load all from SymbolDAO always orders the Symbols by ID private List loadSymbols(User user, Long projectId, List ids) throws NotFoundException { List symbols = new LinkedList<>(); - - for(Long id:ids) { + for (Long id : ids) { Symbol symbol = symbolDAO.get(user, projectId, id); symbols.add(symbol); } - return symbols; } /** * Test of two hypotheses are equal or not. * If a difference was found the separating word will be returned. - * Otherwise, i.e. the hypotheses are equal, + * Otherwise, i.e. the hypotheses are equal. * * @param mealyMachineProxies A List of two (!) hypotheses, which will be compared. - * @return '{"seperatingWord": " results = learnerResultRepository.findByUser_IdAndProject_IdAndTestNoIn(user, project, 0L, 2L); + List results = learnerResultRepository + .findByUser_IdAndProject_IdAndTestNoIn(user, project, 0L, 2L); assertThat(results.size(), is(equalTo(2))); assertThat(results, hasItem(equalTo(result1))); diff --git a/main/src/test/java/de/learnlib/alex/integrationtests/SymbolRepositoryIT.java b/main/src/test/java/de/learnlib/alex/integrationtests/SymbolRepositoryIT.java index cfe3706cc..b049f7cd2 100644 --- a/main/src/test/java/de/learnlib/alex/integrationtests/SymbolRepositoryIT.java +++ b/main/src/test/java/de/learnlib/alex/integrationtests/SymbolRepositoryIT.java @@ -276,7 +276,6 @@ public void shouldFetchSymbolsByIds() { symbolRepository.save(symbol2rev1); // List ids = Arrays.asList(symbol1rev0.getId(), symbol2rev1.getId()); - List symbolsFromDB = symbolRepository.findByIds(user.getId(), project.getId(), ids); assertThat(symbolsFromDB.size(), is(equalTo(2))); @@ -285,7 +284,7 @@ public void shouldFetchSymbolsByIds() { assertThat(symbolsFromDB, not(hasItem(equalTo(symbol2rev0)))); assertThat(symbolsFromDB, hasItem(equalTo(symbol2rev1))); } - + @Test public void shouldFetchSymbolsWithHighestRevisions() { User user = createUser("alex@test.example"); diff --git a/main/src/test/java/de/learnlib/alex/rest/LearnerResourceTest.java b/main/src/test/java/de/learnlib/alex/rest/LearnerResourceTest.java index bd23d691a..572cf33bc 100644 --- a/main/src/test/java/de/learnlib/alex/rest/LearnerResourceTest.java +++ b/main/src/test/java/de/learnlib/alex/rest/LearnerResourceTest.java @@ -49,7 +49,9 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; public class LearnerResourceTest extends JerseyTest { diff --git a/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java b/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java index f5ba6ac97..5923490c2 100644 --- a/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java +++ b/main/src/test/java/de/learnlib/alex/rest/SymbolResourceTest.java @@ -474,11 +474,11 @@ public void ensureThatMovingASymbolIntoTheVoidIsHandedProperly() throws NotFound @Test public void shouldMoveMultipleSymbols() throws NotFoundException { - List ids = new ArrayList<>(); - ids.add(symbol.getId()); - ids.add(symbol2.getId()); + List symbolIds = new ArrayList<>(); + symbolIds.add(symbol.getId()); + symbolIds.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)).willReturn(symbols); + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, symbolIds)).willReturn(symbols); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() + "/moveTo/" + group.getId(); @@ -490,11 +490,11 @@ public void shouldMoveMultipleSymbols() throws NotFoundException { @Test public void ensureThatMovingSymbolsThatDoNotExistsIsHandedProperly() throws NotFoundException { - List ids = new ArrayList<>(); - ids.add(symbol.getId()); - ids.add(symbol2.getId()); + List symbolIds = new ArrayList<>(); + symbolIds.add(symbol.getId()); + symbolIds.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)) + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, symbolIds)) .willThrow(NotFoundException.class); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() @@ -507,11 +507,11 @@ public void ensureThatMovingSymbolsThatDoNotExistsIsHandedProperly() throws NotF @Test public void ensureThatMovingMultipleSymbolsIntoTheVoidIsHandedProperly() throws NotFoundException { - List ids = new ArrayList<>(); - ids.add(symbol.getId()); - ids.add(symbol2.getId()); + List symbolIds = new ArrayList<>(); + symbolIds.add(symbol.getId()); + symbolIds.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)).willReturn(symbols); + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, symbolIds)).willReturn(symbols); willThrow(NotFoundException.class).given(symbolDAO).move(symbols, group.getId()); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() @@ -537,11 +537,11 @@ public void shouldHideASymbol() throws NotFoundException { @Test public void shouldHideMultipleSymbols() throws NotFoundException { - List ids = new ArrayList<>(); - ids.add(symbol.getId()); - ids.add(symbol2.getId()); + List symbolIds = new ArrayList<>(); + symbolIds.add(symbol.getId()); + symbolIds.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)).willReturn(symbols); + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, symbolIds)).willReturn(symbols); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() + "/hide"; @@ -550,7 +550,7 @@ public void shouldHideMultipleSymbols() throws NotFoundException { assertEquals(Status.OK.getStatusCode(), response.getStatus()); List responseSymbols = response.readEntity(new GenericType>() { }); assertEquals(2, responseSymbols.size()); - verify(symbolDAO).hide(USER_TEST_ID, PROJECT_TEST_ID, ids); + verify(symbolDAO).hide(USER_TEST_ID, PROJECT_TEST_ID, symbolIds); } @Test @@ -597,11 +597,11 @@ public void shouldShowASymbol() throws NotFoundException { @Test public void shouldShowMultipleSymbols() throws NotFoundException { - List ids = new ArrayList<>(); - ids.add(symbol.getId()); - ids.add(symbol2.getId()); + List symbolIds = new ArrayList<>(); + symbolIds.add(symbol.getId()); + symbolIds.add(symbol2.getId()); - given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, ids)).willReturn(symbols); + given(symbolDAO.getByIds(admin, PROJECT_TEST_ID, SymbolVisibilityLevel.ALL, symbolIds)).willReturn(symbols); String path = "/projects/" + PROJECT_TEST_ID + "/symbols/batch/" + symbol.getId() + "," + symbol2.getId() + "/show"; @@ -610,7 +610,7 @@ public void shouldShowMultipleSymbols() throws NotFoundException { assertEquals(Status.OK.getStatusCode(), response.getStatus()); List responseSymbols = response.readEntity(new GenericType>() { }); assertEquals(2, responseSymbols.size()); - verify(symbolDAO).show(USER_TEST_ID, PROJECT_TEST_ID, ids); + verify(symbolDAO).show(USER_TEST_ID, PROJECT_TEST_ID, symbolIds); } @Test From d4e745975cf686c0775996a7722856df7ddc052f Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Tue, 29 Nov 2016 10:18:49 +0100 Subject: [PATCH 16/52] update frontend dependencies --- main/src/main/webapp/package.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/main/src/main/webapp/package.json b/main/src/main/webapp/package.json index 2fc0ae894..a3cadcd92 100644 --- a/main/src/main/webapp/package.json +++ b/main/src/main/webapp/package.json @@ -28,28 +28,28 @@ "main": "index.html", "dependencies": { "ace-builds": "^1.2.5", - "angular": "^1.5.8", - "angular-animate": "^1.5.8", + "angular": "^1.5.9", + "angular-animate": "^1.5.9", "angular-dragula": "^1.2.8", - "angular-jwt": "0.1.7", - "angular-messages": "^1.5.8", + "angular-jwt": "0.1.8", + "angular-messages": "^1.5.9", "angular-toastr": "^2.1.1", "angular-ui-ace": "^0.2.3", - "angular-ui-bootstrap": "^2.2.0", - "angular-ui-router": "^0.3.1", + "angular-ui-bootstrap": "^2.3.0", + "angular-ui-router": "^0.3.2", "bootstrap-sass": "^3.3.7", "d3": "^3.5.17", "dagre-d3": "^0.4.17", "font-awesome": "^4.7.0", - "lodash": "^4.16.4", + "lodash": "^4.17.2", "n3-charts": "^2.0.28", - "ng-file-upload": "^12.2.12", + "ng-file-upload": "^12.2.13", "selection-model": "^0.11.0", - "swagger-ui": "^2.2.6" + "swagger-ui": "^2.2.8" }, "devDependencies": { - "angular-mocks": "^1.5.8", - "autoprefixer": "^6.5.1", + "angular-mocks": "^1.5.9", + "autoprefixer": "^6.5.3", "babel-preset-es2015": "^6.18.0", "babelify": "^7.3.0", "browserify-istanbul": "^2.0.0", @@ -58,7 +58,7 @@ "grunt-contrib-concat": "^1.0.1", "grunt-contrib-copy": "^1.0.0", "grunt-contrib-cssmin": "^1.0.2", - "grunt-contrib-jshint": "^1.0.0", + "grunt-contrib-jshint": "^1.1.0", "grunt-contrib-uglify": "^2.0.0", "grunt-contrib-watch": "^1.0.0", "grunt-html2js": "^0.3.6", From 78e19ebb0e36ab35261bbf8cc79587a4b026eaf2 Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Tue, 29 Nov 2016 11:26:29 +0100 Subject: [PATCH 17/52] updated statistics view removed the statistics for sigma and number of equivalence queries and added them in a table with additional info --- main/src/main/webapp/index.html | 2 +- .../components/views/statistics-compare.html | 58 +++++++++++-------- .../widgets/counterexamplesWidget.js | 2 +- .../js/services/LearnerResultChartService.js | 22 +------ 4 files changed, 38 insertions(+), 46 deletions(-) diff --git a/main/src/main/webapp/index.html b/main/src/main/webapp/index.html index 53727e631..fdfb122ff 100644 --- a/main/src/main/webapp/index.html +++ b/main/src/main/webapp/index.html @@ -29,7 +29,7 @@

- + \ No newline at end of file diff --git a/main/src/main/webapp/src/html/components/views/statistics-compare.html b/main/src/main/webapp/src/html/components/views/statistics-compare.html index 6b8b51cfb..753a5d53d 100644 --- a/main/src/main/webapp/src/html/components/views/statistics-compare.html +++ b/main/src/main/webapp/src/html/components/views/statistics-compare.html @@ -63,10 +63,42 @@

Tests:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 BrowserAlgorithmSigmaEquivalence Queries
Test
Test
+ +
+
- +
@@ -75,7 +107,7 @@

Tests: Download SVG - +
@@ -86,27 +118,7 @@

Tests:

- -
- -
-
- -
- -
- -
-
- -
-
-
- +
diff --git a/main/src/main/webapp/src/js/components/widgets/counterexamplesWidget.js b/main/src/main/webapp/src/js/components/widgets/counterexamplesWidget.js index 3cb739aca..db2f8996f 100644 --- a/main/src/main/webapp/src/js/components/widgets/counterexamplesWidget.js +++ b/main/src/main/webapp/src/js/components/widgets/counterexamplesWidget.js @@ -133,7 +133,7 @@ class CounterexamplesWidget { for (let i = 0; i < this.counterExample.length; i++) { for (let j = 0; j < this.symbols.length; j++) { if (this.counterExample[i].input === this.symbols[j].abbreviation) { - testSymbols.push(this.symbols[j].id()); + testSymbols.push(this.symbols[j].id); } } } diff --git a/main/src/main/webapp/src/js/services/LearnerResultChartService.js b/main/src/main/webapp/src/js/services/LearnerResultChartService.js index 62c4a467d..76b0b5662 100644 --- a/main/src/main/webapp/src/js/services/LearnerResultChartService.js +++ b/main/src/main/webapp/src/js/services/LearnerResultChartService.js @@ -74,18 +74,14 @@ export class LearnerResultChartService { }; const data = { - eqs: {dataset: [{x: 0, y: 0}]}, mqs: {dataset: [{x: 0, y: 0}]}, symbols: {dataset: [{x: 0, y: 0}]}, - sigma: {dataset: [{x: 0, y: 0}]}, duration: {dataset: [{x: 0, y: 0}]} }; result.steps.forEach((step, i) => { - data.eqs.dataset.push({x: i + 1, y: step.statistics.eqsUsed}); data.mqs.dataset.push({x: i + 1, y: step.statistics.mqsUsed.total}); data.symbols.dataset.push({x: i + 1, y: step.statistics.symbolsUsed.total}); - data.sigma.dataset.push({x: i + 1, y: result.sigma.length}); data.duration.dataset.push({x: i + 1, y: step.statistics.duration.total}); }); @@ -134,27 +130,21 @@ export class LearnerResultChartService { }; const data = { - eqs: {dataset: [{x: 0, y: 0}]}, mqs: {dataset: [{x: 0, y: 0}]}, symbols: {dataset: [{x: 0, y: 0}]}, - sigma: {dataset: [{x: 0, y: 0}]}, duration: {dataset: [{x: 0, y: 0}]} }; results.forEach((result, i) => { let j = i + 1; - data.eqs.dataset.push({x: j, y: result.statistics.eqsUsed}); data.mqs.dataset.push({x: j, y: result.statistics.mqsUsed.total}); data.symbols.dataset.push({x: j, y: result.statistics.symbolsUsed.total}); - data.sigma.dataset.push({x: j, y: result.sigma.length}); data.duration.dataset.push({x: j, y: result.statistics.duration.total}); }); const last = results.length + 1; - data.eqs.dataset.push({x: last, y: 0}); data.mqs.dataset.push({x: last, y: 0}); data.symbols.dataset.push({x: last, y: 0}); - data.sigma.dataset.push({x: last, y: 0}); data.duration.dataset.push({x: last, y: 0}); return { @@ -166,7 +156,7 @@ export class LearnerResultChartService { createDataMultipleComplete(results) { const colors = ['#4B6396', '#3BA3B8', '#3BB877', '#8ACF36', '#E8E835', '#F7821B', '#F74F1B', '#C01BF7']; - const props = ['eqs', 'mqs', 'symbols', 'sigma', 'duration']; + const props = ['mqs', 'symbols', 'duration']; // find value from test results where #steps is max let maxSteps = 0; @@ -203,41 +193,31 @@ export class LearnerResultChartService { }; const data = { - eqs: {dataset: []}, mqs: {dataset: []}, symbols: {dataset: []}, - sigma: {dataset: []}, duration: {dataset: []} }; const values = { - eqs: {dataset: []}, mqs: {dataset: []}, symbols: {dataset: []}, - sigma: {dataset: []}, duration: {dataset: []} }; results.forEach((result, i) => { // extract all values from the results - const eqs = result.steps.map(s => s.statistics.eqsUsed); const mqs = result.steps.map(s => s.statistics.mqsUsed.total); const symbols = result.steps.map(s => s.statistics.symbolsUsed.total); - const sigma = result.steps.map(s => result.sigma.length); const duration = result.steps.map(s => s.statistics.duration.total); // fill all other values with zeroes in order to // reduce visual bugs - while (eqs.length < maxSteps) eqs.push(0); while (mqs.length < maxSteps) mqs.push(0); while (symbols.length < maxSteps) symbols.push(0); - while (sigma.length < maxSteps) sigma.push(0); while (duration.length < maxSteps) duration.push(0); - values.eqs.dataset.push(eqs); values.mqs.dataset.push(mqs); values.symbols.dataset.push(symbols); - values.sigma.dataset.push(sigma); values.duration.dataset.push(duration); // add options for specific area From 1b931e039dd2b4be4db540a8a1cf8ced593c099a Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Tue, 29 Nov 2016 14:23:52 +0100 Subject: [PATCH 18/52] display statistics of eq oracle and learner separately in the charts only for a single learn result, it gets messy if one compares multiple learn results in that way --- .../components/views/statistics-compare.html | 171 ++++++--------- .../src/html/components/views/statistics.html | 59 ++---- .../components/views/statisticsCompareView.js | 166 +++++++-------- .../src/js/components/views/statisticsView.js | 50 +---- .../widgets/latestLearnResultWidget.js | 2 +- main/src/main/webapp/src/js/constants.js | 5 - main/src/main/webapp/src/js/routes.js | 2 +- .../js/services/LearnerResultChartService.js | 200 +++++++++++++----- 8 files changed, 306 insertions(+), 349 deletions(-) diff --git a/main/src/main/webapp/src/html/components/views/statistics-compare.html b/main/src/main/webapp/src/html/components/views/statistics-compare.html index 753a5d53d..2ae6edb05 100644 --- a/main/src/main/webapp/src/html/components/views/statistics-compare.html +++ b/main/src/main/webapp/src/html/components/views/statistics-compare.html @@ -9,11 +9,11 @@
@@ -27,116 +27,75 @@
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 BrowserAlgorithmSigmaEquivalence Queries
Test
Test
-
-

Test (Cumulated)

- - Details - -
-
+
-
-

Test (All Steps)

- - Details - -
-
+
+
-
-

Tests: - - ,  - (Cumulated) -

-
+ +
+ +
+
+ +
+ +
+ +
+
+ +
- -
-

Tests: - - ,  - (All Steps) -

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 BrowserAlgorithmSigmaEquivalence Queries
Test
Test
- -
- -
-
- - -
- -
-
- -
- -
- -
-
- -
-
-
- -
- -
-
- -
-
-
-
- -
-
- You haven't selected a chart mode. +
+ +
+ +
+
+ +
-
+
\ No newline at end of file diff --git a/main/src/main/webapp/src/html/components/views/statistics.html b/main/src/main/webapp/src/html/components/views/statistics.html index aa45c2891..843c466d3 100644 --- a/main/src/main/webapp/src/html/components/views/statistics.html +++ b/main/src/main/webapp/src/html/components/views/statistics.html @@ -5,44 +5,17 @@
- + + +
- -
@@ -54,18 +27,14 @@ link-to="statistics">
-
diff --git a/main/src/main/webapp/src/js/constants.js b/main/src/main/webapp/src/js/constants.js index 9a860fbe9..c738b0102 100644 --- a/main/src/main/webapp/src/js/constants.js +++ b/main/src/main/webapp/src/js/constants.js @@ -82,11 +82,6 @@ export const events = { RESULT_SELECTED: 'result:selected' }; -export const chartMode = { - CUMULATED: 'cumulated', - COMPLETE: 'complete' -}; - export const actionType = { // web actions diff --git a/main/src/main/webapp/src/js/routes.js b/main/src/main/webapp/src/js/routes.js index 2b797355f..4cfdd858b 100644 --- a/main/src/main/webapp/src/js/routes.js +++ b/main/src/main/webapp/src/js/routes.js @@ -105,7 +105,7 @@ export function config($stateProvider, $urlRouterProvider) { data: {requiresProject: true, roles: ['REGISTERED', 'ADMIN'], title: 'Statistics'} }) .state('statisticsCompare', { - url: '/statistics/{testNos:string}/compare/{mode:string}', + url: '/statistics/{testNos:string}', template: '', data: {requiresProject: true, roles: ['REGISTERED', 'ADMIN'], title: 'Statistics > Compare'} }) diff --git a/main/src/main/webapp/src/js/services/LearnerResultChartService.js b/main/src/main/webapp/src/js/services/LearnerResultChartService.js index 76b0b5662..feed05d79 100644 --- a/main/src/main/webapp/src/js/services/LearnerResultChartService.js +++ b/main/src/main/webapp/src/js/services/LearnerResultChartService.js @@ -13,50 +13,126 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import _ from "lodash"; +const MARGIN_OPTIONS = { + top: 20, + right: 50, + bottom: 20, + left: 50 +}; + +const GRID_OPTIONS = { + x: false, + y: true +}; + /** * The service that helps transforming learn results into chart data that is required by n3-line-chart. */ export class LearnerResultChartService { - /** - * Creates chart data for a single final result. - * - * @param {LearnResult} result - * @returns {{context, options, data}|*} - */ createDataSingleFinal(result) { - const data = this.createDataMultipleFinal([result]); - data.context = result; - return data; - } - - /** - * @param {LearnResult} result - */ - createDataSingleComplete(result) { const options = { - margin: { - top: 20, - right: 50, - bottom: 20, - left: 50 - }, - grid: { - x: false, - y: true - }, + margin: MARGIN_OPTIONS, + grid: GRID_OPTIONS, series: [{ axis: 'y', dataset: 'dataset', key: 'y', label: ' ', color: "#4B6396", - type: ['dot', 'line', 'area'], + type: ['column'], id: 'mySeries0' }], + axes: { + x: { + key: 'x', + type: 'linear', + ticks: [0, 1, 2, 3, 4], + tickFormat: (value, index) => { + return [" ", "Total", "Learner", "EQ Oracle", " "][index]; + } + }, + y: { + min: 0 + } + } + }; + + const data = { + mqs: { + dataset: [ + {x: 0, y: 0}, + {x: 1, y: result.statistics.mqsUsed.total}, + {x: 2, y: result.statistics.mqsUsed.learner}, + {x: 3, y: result.statistics.mqsUsed.eqOracle}, + {x: 4, y: 0}, + ] + }, + symbols: { + dataset: [ + {x: 0, y: 0}, + {x: 1, y: result.statistics.symbolsUsed.total}, + {x: 2, y: result.statistics.symbolsUsed.learner}, + {x: 3, y: result.statistics.symbolsUsed.eqOracle}, + {x: 4, y: 0}, + ] + }, + duration: { + dataset: [ + {x: 0, y: 0}, + {x: 1, y: result.statistics.duration.total}, + {x: 2, y: result.statistics.duration.learner}, + {x: 3, y: result.statistics.duration.eqOracle}, + {x: 4, y: 0}, + ] + } + }; + + return { + context: result, + options: options, + data: data + }; + } + + /** + * @param {LearnResult} result + */ + createDataSingleComplete(result) { + const options = { + margin: MARGIN_OPTIONS, + grid: GRID_OPTIONS, + series: [ + { + axis: 'y', + dataset: 'dataset', + key: 'y0', + label: 'Total', + color: '#4B6396', + type: ['dot', 'line', 'area'], + id: 'mySeries0' + }, + { + axis: 'y', + dataset: 'dataset', + key: 'y1', + label: 'Learner', + color: '#3BB877', + type: ['dot', 'line', 'area'], + id: 'mySeries1' + }, + { + axis: 'y', + dataset: 'dataset', + key: 'y2', + label: 'EQ Oracle', + color: '#C01BF7', + type: ['dot', 'line', 'area'], + id: 'mySeries2' + } + ], axes: { x: { key: 'x', @@ -74,17 +150,48 @@ export class LearnerResultChartService { }; const data = { - mqs: {dataset: [{x: 0, y: 0}]}, - symbols: {dataset: [{x: 0, y: 0}]}, - duration: {dataset: [{x: 0, y: 0}]} + mqs: { + dataset: [ + {x: 0, y0: 0, y1: 0, y2: 0} + ] + }, + symbols: { + dataset: [ + {x: 0, y0: 0, y1: 0, y2: 0} + ] + }, + duration: { + dataset: [ + {x: 0, y0: 0, y1: 0, y2: 0} + ] + } }; result.steps.forEach((step, i) => { - data.mqs.dataset.push({x: i + 1, y: step.statistics.mqsUsed.total}); - data.symbols.dataset.push({x: i + 1, y: step.statistics.symbolsUsed.total}); - data.duration.dataset.push({x: i + 1, y: step.statistics.duration.total}); + i = i + 1; + + data.mqs.dataset.push({ + x: i, + y0: step.statistics.mqsUsed.total, + y1: step.statistics.mqsUsed.learner, + y2: step.statistics.mqsUsed.eqOracle + }); + data.symbols.dataset.push({ + x: i, + y0: step.statistics.symbolsUsed.total, + y1: step.statistics.symbolsUsed.learner, + y2: step.statistics.symbolsUsed.eqOracle + }); + data.duration.dataset.push({ + x: i, + y0: step.statistics.duration.total, + y1: step.statistics.duration.learner, + y2: step.statistics.duration.eqOracle + }); }); + console.log(data); + return { context: result, options: options, @@ -94,16 +201,8 @@ export class LearnerResultChartService { createDataMultipleFinal(results) { const options = { - margin: { - top: 20, - right: 50, - bottom: 20, - left: 50 - }, - grid: { - x: false, - y: true - }, + margin: MARGIN_OPTIONS, + grid: GRID_OPTIONS, series: [{ axis: 'y', dataset: 'dataset', @@ -165,16 +264,8 @@ export class LearnerResultChartService { }); const options = { - margin: { - top: 20, - right: 50, - bottom: 20, - left: 50 - }, - grid: { - x: false, - y: true - }, + margin: MARGIN_OPTIONS, + grid: GRID_OPTIONS, series: [], axes: { x: { @@ -197,6 +288,7 @@ export class LearnerResultChartService { symbols: {dataset: []}, duration: {dataset: []} }; + const values = { mqs: {dataset: []}, symbols: {dataset: []}, @@ -252,6 +344,8 @@ export class LearnerResultChartService { }); }); + console.log(data); + return { context: results, options: options, From 8e3137949d5497b241d8aa3c2f7876fe1233acce Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Tue, 29 Nov 2016 14:39:21 +0100 Subject: [PATCH 19/52] small fixes --- .../main/webapp/src/js/components/views/symbolsTestView.js | 2 +- .../main/webapp/src/js/services/LearnerResultChartService.js | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/main/src/main/webapp/src/js/components/views/symbolsTestView.js b/main/src/main/webapp/src/js/components/views/symbolsTestView.js index b8dcd7835..1b95ff67b 100644 --- a/main/src/main/webapp/src/js/components/views/symbolsTestView.js +++ b/main/src/main/webapp/src/js/components/views/symbolsTestView.js @@ -108,7 +108,7 @@ export const symbolsTestView = { this.outputs = []; this.isExecuting = true; - const symbols = this.word.map(s => s.getIdRevisionPair()); + const symbols = this.word.map(s => s.id); const resetSymbol = symbols.shift(); const readOutputConfig = { diff --git a/main/src/main/webapp/src/js/services/LearnerResultChartService.js b/main/src/main/webapp/src/js/services/LearnerResultChartService.js index feed05d79..0663c9649 100644 --- a/main/src/main/webapp/src/js/services/LearnerResultChartService.js +++ b/main/src/main/webapp/src/js/services/LearnerResultChartService.js @@ -190,8 +190,6 @@ export class LearnerResultChartService { }); }); - console.log(data); - return { context: result, options: options, @@ -344,8 +342,6 @@ export class LearnerResultChartService { }); }); - console.log(data); - return { context: results, options: options, From f8647e5dd14190fb53b8967d0b92d78032852a49 Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Wed, 30 Nov 2016 11:52:08 +0100 Subject: [PATCH 20/52] added multi sul oracle allows executing multiple mqs at once --- .../algorithms/LearnAlgorithmFactory.java | 9 +- .../java/de/learnlib/alex/algorithms/DHC.java | 6 +- .../alex/algorithms/DiscriminationTree.java | 6 +- .../de/learnlib/alex/algorithms/LStar.java | 6 +- .../java/de/learnlib/alex/algorithms/TTT.java | 6 +- .../AbstractEquivalenceOracleProxy.java | 6 +- .../CompleteExplorationEQOracleProxy.java | 4 +- .../eqproxies/HypothesisEQOracleProxy.java | 4 +- .../MealyRandomWordsEQOracleProxy.java | 4 +- .../eqproxies/SampleEQOracleProxy.java | 6 +- .../eqproxies/WMethodEQOracleProxy.java | 4 +- .../learnlib/alex/core/learner/AlexSUL.java | 16 +++ .../alex/core/learner/LearnerThread.java | 53 +++++---- .../core/learner/LearnerThreadFactory.java | 5 +- .../alex/core/learner/MultiSULOracle.java | 101 ++++++++++++++++++ .../alex/core/learner/SymbolMapper.java | 13 +++ .../services/LearnAlgorithmServiceTest.java | 6 +- 17 files changed, 195 insertions(+), 60 deletions(-) create mode 100644 main/src/main/java/de/learnlib/alex/core/learner/MultiSULOracle.java diff --git a/api/src/main/java/de/learnlib/alex/algorithms/LearnAlgorithmFactory.java b/api/src/main/java/de/learnlib/alex/algorithms/LearnAlgorithmFactory.java index ff09796a1..076d6c147 100644 --- a/api/src/main/java/de/learnlib/alex/algorithms/LearnAlgorithmFactory.java +++ b/api/src/main/java/de/learnlib/alex/algorithms/LearnAlgorithmFactory.java @@ -17,7 +17,7 @@ package de.learnlib.alex.algorithms; import de.learnlib.api.LearningAlgorithm; -import de.learnlib.oracles.SULOracle; +import de.learnlib.api.MembershipOracle; import net.automatalib.words.Alphabet; /** @@ -33,17 +33,20 @@ public interface LearnAlgorithmFactory { * The Alphabet to use. * @param oracle * The MQ oracle. + * * @return A new Learner. */ - LearningAlgorithm.MealyLearner createLearner(Alphabet sigma, - SULOracle oracle); + LearningAlgorithm.MealyLearner createLearner( + Alphabet sigma, MembershipOracle.MealyMembershipOracle oracle); /** * Read the internal data of an algorithm. * * @param learner * The learner to extract the internal data from. + * * @return The internal data as a nice JSON string. + * * @throws IllegalArgumentException * If the algorithm has the wrong type or no internal data. */ diff --git a/main/src/main/java/de/learnlib/alex/algorithms/DHC.java b/main/src/main/java/de/learnlib/alex/algorithms/DHC.java index 312f4efda..715c8bf25 100644 --- a/main/src/main/java/de/learnlib/alex/algorithms/DHC.java +++ b/main/src/main/java/de/learnlib/alex/algorithms/DHC.java @@ -19,7 +19,7 @@ import de.learnlib.alex.annotations.LearnAlgorithm; import de.learnlib.algorithms.dhc.mealy.MealyDHCBuilder; import de.learnlib.api.LearningAlgorithm; -import de.learnlib.oracles.SULOracle; +import de.learnlib.api.MembershipOracle; import net.automatalib.words.Alphabet; /** @@ -29,8 +29,8 @@ public class DHC implements LearnAlgorithmFactory { @Override - public LearningAlgorithm.MealyLearner createLearner(Alphabet sigma, - SULOracle oracle) { + public LearningAlgorithm.MealyLearner createLearner( + Alphabet sigma, MembershipOracle.MealyMembershipOracle oracle) { return new MealyDHCBuilder().withAlphabet(sigma).withOracle(oracle).create(); } diff --git a/main/src/main/java/de/learnlib/alex/algorithms/DiscriminationTree.java b/main/src/main/java/de/learnlib/alex/algorithms/DiscriminationTree.java index 2fe1a667b..43e5a47a9 100644 --- a/main/src/main/java/de/learnlib/alex/algorithms/DiscriminationTree.java +++ b/main/src/main/java/de/learnlib/alex/algorithms/DiscriminationTree.java @@ -21,8 +21,8 @@ import de.learnlib.algorithms.discriminationtree.mealy.DTLearnerMealy; import de.learnlib.algorithms.discriminationtree.mealy.DTLearnerMealyBuilder; import de.learnlib.api.LearningAlgorithm; +import de.learnlib.api.MembershipOracle; import de.learnlib.discriminationtree.DTNode; -import de.learnlib.oracles.SULOracle; import net.automatalib.words.Alphabet; import net.automatalib.words.Word; @@ -33,8 +33,8 @@ public class DiscriminationTree implements LearnAlgorithmFactory { @Override - public LearningAlgorithm.MealyLearner createLearner(Alphabet sigma, - SULOracle oracle) { + public LearningAlgorithm.MealyLearner createLearner( + Alphabet sigma, MembershipOracle.MealyMembershipOracle oracle) { return new DTLearnerMealyBuilder().withAlphabet(sigma).withOracle(oracle).create(); } diff --git a/main/src/main/java/de/learnlib/alex/algorithms/LStar.java b/main/src/main/java/de/learnlib/alex/algorithms/LStar.java index 55de264c5..4629e18ba 100644 --- a/main/src/main/java/de/learnlib/alex/algorithms/LStar.java +++ b/main/src/main/java/de/learnlib/alex/algorithms/LStar.java @@ -22,7 +22,7 @@ import de.learnlib.algorithms.lstargeneric.mealy.ExtensibleLStarMealy; import de.learnlib.algorithms.lstargeneric.mealy.ExtensibleLStarMealyBuilder; import de.learnlib.api.LearningAlgorithm; -import de.learnlib.oracles.SULOracle; +import de.learnlib.api.MembershipOracle; import net.automatalib.words.Alphabet; /** @@ -32,8 +32,8 @@ public class LStar implements LearnAlgorithmFactory { @Override - public LearningAlgorithm.MealyLearner createLearner(Alphabet sigma, - SULOracle oracle) { + public LearningAlgorithm.MealyLearner createLearner( + Alphabet sigma, MembershipOracle.MealyMembershipOracle oracle) { return new ExtensibleLStarMealyBuilder().withAlphabet(sigma).withOracle(oracle).create(); } diff --git a/main/src/main/java/de/learnlib/alex/algorithms/TTT.java b/main/src/main/java/de/learnlib/alex/algorithms/TTT.java index 2b8369932..31abe9ecc 100644 --- a/main/src/main/java/de/learnlib/alex/algorithms/TTT.java +++ b/main/src/main/java/de/learnlib/alex/algorithms/TTT.java @@ -21,7 +21,7 @@ import de.learnlib.algorithms.ttt.mealy.TTTLearnerMealy; import de.learnlib.algorithms.ttt.mealy.TTTLearnerMealyBuilder; import de.learnlib.api.LearningAlgorithm; -import de.learnlib.oracles.SULOracle; +import de.learnlib.api.MembershipOracle; import net.automatalib.words.Alphabet; import net.automatalib.words.Word; @@ -32,8 +32,8 @@ public class TTT implements LearnAlgorithmFactory { @Override - public LearningAlgorithm.MealyLearner createLearner(Alphabet sigma, - SULOracle oracle) { + public LearningAlgorithm.MealyLearner createLearner( + Alphabet sigma, MembershipOracle.MealyMembershipOracle oracle) { return new TTTLearnerMealyBuilder().withAlphabet(sigma).withOracle(oracle).create(); } diff --git a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/AbstractEquivalenceOracleProxy.java b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/AbstractEquivalenceOracleProxy.java index 85f53b167..d0b227cce 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/AbstractEquivalenceOracleProxy.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/AbstractEquivalenceOracleProxy.java @@ -20,7 +20,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import de.learnlib.api.EquivalenceOracle; -import de.learnlib.oracles.SULOracle; +import de.learnlib.api.MembershipOracle; import net.automatalib.automata.transout.MealyMachine; import net.automatalib.words.Word; @@ -61,7 +61,7 @@ public abstract class AbstractEquivalenceOracleProxy implements Serializable { * The MQ oracle to test against a hypothesis. * @return An EquivalenceOracle from the LearnLib based on the proxy. */ - public abstract EquivalenceOracle, String, Word> - createEqOracle(SULOracle membershipOracle); + public abstract EquivalenceOracle, String, Word> createEqOracle( + MembershipOracle.MealyMembershipOracle membershipOracle); } diff --git a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/CompleteExplorationEQOracleProxy.java b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/CompleteExplorationEQOracleProxy.java index 7cea4ead1..70e44a9d2 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/CompleteExplorationEQOracleProxy.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/CompleteExplorationEQOracleProxy.java @@ -18,8 +18,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.api.EquivalenceOracle; +import de.learnlib.api.MembershipOracle; import de.learnlib.eqtests.basic.CompleteExplorationEQOracle; -import de.learnlib.oracles.SULOracle; import net.automatalib.automata.transout.MealyMachine; import net.automatalib.words.Word; @@ -112,7 +112,7 @@ public void checkParameters() throws IllegalArgumentException { @Override public EquivalenceOracle, String, Word> - createEqOracle(SULOracle membershipOracle) { + createEqOracle(MembershipOracle.MealyMembershipOracle membershipOracle) { return new CompleteExplorationEQOracle(membershipOracle, minDepth, maxDepth); } diff --git a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/HypothesisEQOracleProxy.java b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/HypothesisEQOracleProxy.java index cc2dca110..5476df3a8 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/HypothesisEQOracleProxy.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/HypothesisEQOracleProxy.java @@ -19,8 +19,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.learnlibproxies.CompactMealyMachineProxy; import de.learnlib.api.EquivalenceOracle; +import de.learnlib.api.MembershipOracle; import de.learnlib.eqtests.basic.SimulatorEQOracle; -import de.learnlib.oracles.SULOracle; import net.automatalib.automata.transout.MealyMachine; import net.automatalib.automata.transout.impl.compact.CompactMealy; import net.automatalib.words.Alphabet; @@ -65,7 +65,7 @@ public void checkParameters() throws IllegalArgumentException { @Override public EquivalenceOracle, String, Word> - createEqOracle(SULOracle membershipOracle) { + createEqOracle(MembershipOracle.MealyMembershipOracle membershipOracle) { Alphabet alphabet = hypothesis.createAlphabet(); CompactMealy compactMealy = hypothesis.createMealyMachine(alphabet); return new SimulatorEQOracle.MealySimulatorEQOracle<>(compactMealy); diff --git a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java index f1326676d..59d8724cb 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/MealyRandomWordsEQOracleProxy.java @@ -18,8 +18,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.api.EquivalenceOracle; +import de.learnlib.api.MembershipOracle; import de.learnlib.eqtests.basic.RandomWordsEQOracle; -import de.learnlib.oracles.SULOracle; import net.automatalib.automata.transout.MealyMachine; import net.automatalib.words.Word; @@ -167,7 +167,7 @@ public void checkParameters() throws IllegalArgumentException { @Override public EquivalenceOracle, String, Word> - createEqOracle(SULOracle membershipOracle) { + createEqOracle(MembershipOracle.MealyMembershipOracle membershipOracle) { return new RandomWordsEQOracle.MealyRandomWordsEQOracle<>(membershipOracle, minLength, maxLength, maxNoOfTests, new Random(seed)); } diff --git a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/SampleEQOracleProxy.java b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/SampleEQOracleProxy.java index a5ec6707f..c7ed345d9 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/SampleEQOracleProxy.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/SampleEQOracleProxy.java @@ -18,8 +18,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.api.EquivalenceOracle; +import de.learnlib.api.MembershipOracle; import de.learnlib.eqtests.basic.SampleSetEQOracle; -import de.learnlib.oracles.SULOracle; import net.automatalib.automata.transout.MealyMachine; import net.automatalib.words.Word; import org.hibernate.validator.constraints.NotBlank; @@ -156,8 +156,8 @@ public void addCounterExample(List counterExample) { } @Override - public EquivalenceOracle, String, Word> - createEqOracle(SULOracle membershipOracle) { + public EquivalenceOracle, String, Word> createEqOracle( + MembershipOracle.MealyMembershipOracle membershipOracle) { SampleSetEQOracle newEQ = new SampleSetEQOracle(false); for (List counterExample : counterExamples) { diff --git a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/WMethodEQOracleProxy.java b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/WMethodEQOracleProxy.java index d37035b20..351fe71f5 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/WMethodEQOracleProxy.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/learnlibproxies/eqproxies/WMethodEQOracleProxy.java @@ -18,8 +18,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.api.EquivalenceOracle; +import de.learnlib.api.MembershipOracle; import de.learnlib.eqtests.basic.WMethodEQOracle; -import de.learnlib.oracles.SULOracle; import net.automatalib.automata.transout.MealyMachine; import net.automatalib.words.Word; @@ -75,7 +75,7 @@ public void checkParameters() throws IllegalArgumentException { @Override public EquivalenceOracle, String, Word> createEqOracle( - SULOracle membershipOracle) { + MembershipOracle.MealyMembershipOracle membershipOracle) { return new WMethodEQOracle.MealyWMethodEQOracle<>(this.maxDepth, membershipOracle); } } diff --git a/main/src/main/java/de/learnlib/alex/core/learner/AlexSUL.java b/main/src/main/java/de/learnlib/alex/core/learner/AlexSUL.java index 39c35888a..6c2886f4f 100644 --- a/main/src/main/java/de/learnlib/alex/core/learner/AlexSUL.java +++ b/main/src/main/java/de/learnlib/alex/core/learner/AlexSUL.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 TU Dortmund + * + * Licensed 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. + */ + package de.learnlib.alex.core.learner; import de.learnlib.api.SUL; diff --git a/main/src/main/java/de/learnlib/alex/core/learner/LearnerThread.java b/main/src/main/java/de/learnlib/alex/core/learner/LearnerThread.java index e6fb19215..a9b352063 100644 --- a/main/src/main/java/de/learnlib/alex/core/learner/LearnerThread.java +++ b/main/src/main/java/de/learnlib/alex/core/learner/LearnerThread.java @@ -32,13 +32,11 @@ import de.learnlib.api.EquivalenceOracle; import de.learnlib.api.LearningAlgorithm.MealyLearner; import de.learnlib.api.SUL; -import de.learnlib.cache.sul.SULCache; -import de.learnlib.cache.sul.SULCaches; +import de.learnlib.cache.mealy.MealyCacheOracle; import de.learnlib.mapper.ContextExecutableInputSUL; import de.learnlib.mapper.Mappers; import de.learnlib.mapper.api.ContextExecutableInput; import de.learnlib.oracles.DefaultQuery; -import de.learnlib.oracles.SULOracle; import net.automatalib.automata.transout.MealyMachine; import net.automatalib.words.Alphabet; import net.automatalib.words.Word; @@ -77,11 +75,6 @@ public class LearnerThread extends Thread { */ private final AlexSUL sul; - /** - * The actual System Under Learning put into a cache. - */ - private SULCache cachedSUL; - /** * The DAO to remember the learn results. */ @@ -110,7 +103,10 @@ public class LearnerThread extends Thread { /** * The membership oracle. */ - private final SULOracle mqOracle; + private final MealyCacheOracle mqOracle; + + /** The mapped SUL. */ + private final SUL mappedSUL; /** * Constructor to set the LearnerThread up. @@ -137,11 +133,11 @@ public LearnerThread(LearnerResultDAO learnerResultDAO, LearnerResult result, Co ExecuteResult, ConnectorManager> ceiSUL = new ContextExecutableInputSUL<>(context); - SUL mappedSUL = Mappers.apply(symbolMapper, ceiSUL); - this.cachedSUL = SULCaches.createCache(this.sigma, mappedSUL); - this.sul = new AlexSUL<>(cachedSUL); + this.mappedSUL = Mappers.apply(symbolMapper, ceiSUL); + this.sul = new AlexSUL<>(mappedSUL); - this.mqOracle = new SULOracle<>(sul); + MultiSULOracle multiSULOracle = new MultiSULOracle<>(sul, 1); + this.mqOracle = MealyCacheOracle.createDAGCacheOracle(this.sigma, multiSULOracle); LearnAlgorithmFactory algorithm = result.getAlgorithmFactory(); this.learner = algorithm.createLearner(sigma, mqOracle); @@ -157,7 +153,8 @@ public LearnerThread(LearnerResultDAO learnerResultDAO, LearnerResult result, Co * @param learner Don't create a new learner, instead use this one. * @param symbols The Symbols to use. */ - public LearnerThread(LearnerResultDAO learnerResultDAO, LearnerResult result, SULCache existingSUL, + public LearnerThread(LearnerResultDAO learnerResultDAO, LearnerResult result, SUL mappedSUL, + MealyCacheOracle existingSUL, MealyLearner learner, Symbol... symbols) { this.finished = false; this.learnerResultDAO = learnerResultDAO; @@ -168,11 +165,10 @@ public LearnerThread(LearnerResultDAO learnerResultDAO, LearnerResult result, SU this.sigma = symbolMapper.getAlphabet(); result.setSigma(AlphabetProxy.createFrom(sigma)); - this.cachedSUL = existingSUL; - this.sul = new AlexSUL<>(cachedSUL); - - this.mqOracle = new SULOracle<>(sul); + this.mappedSUL = mappedSUL; + this.sul = new AlexSUL<>(mappedSUL); + this.mqOracle = existingSUL; this.learner = learner; } @@ -199,15 +195,6 @@ public LearnerResult getResult() { return result; } - /** - * Get the actual SUL used for the learning process wrapped in a cache.. - * - * @return The SUL used for the learning process put into a cache. - */ - public SULCache getCachedSUL() { - return cachedSUL; - } - /** * Get the learner used by the LearnerThread. * @@ -226,6 +213,16 @@ public List getSymbols() { return symbolMapper.getSymbols(); } + /** @return The MQ oracle. */ + public MealyCacheOracle getMqOracle() { + return mqOracle; + } + + /** @return the mapped SUL. */ + public SUL getMappedSUL() { + return mappedSUL; + } + @Override public void run() { ThreadContext.put("userId", String.valueOf(result.getUserId())); @@ -255,6 +252,8 @@ public void run() { } } + + /** * Get the {@link AlexSUL}. * diff --git a/main/src/main/java/de/learnlib/alex/core/learner/LearnerThreadFactory.java b/main/src/main/java/de/learnlib/alex/core/learner/LearnerThreadFactory.java index 9f522bba2..07b00d0d4 100644 --- a/main/src/main/java/de/learnlib/alex/core/learner/LearnerThreadFactory.java +++ b/main/src/main/java/de/learnlib/alex/core/learner/LearnerThreadFactory.java @@ -28,6 +28,7 @@ public class LearnerThreadFactory { * The newly created LearnResult with the configuration. * @param contextHandler * The Connectors to use. + * * @return A brand new learn thread. You have to start it by calling the .run() method on it. */ public LearnerThread createThread(LearnerResult learnerResult, ConnectorContextHandler contextHandler) { @@ -41,6 +42,7 @@ public LearnerThread createThread(LearnerResult learnerResult, ConnectorContextH * The previous thread which provides the actual algorithm and other properties. * @param learnerResult * The learner result with the new configuration in the current last step. + * * @return The new LearnerThread. You have to start it by calling the .run() method on it. */ public LearnerThread createThread(LearnerThread previousThread, LearnerResult learnerResult) { @@ -48,7 +50,8 @@ public LearnerThread createThread(LearnerThread previousThread, LearnerResult le Symbol[] symbols = symbolsList.toArray(new Symbol[symbolsList.size()]); LearningAlgorithm.MealyLearner learner = previousThread.getLearner(); - return new LearnerThread(learnerResultDAO, learnerResult, previousThread.getCachedSUL(), learner, symbols); + return new LearnerThread(learnerResultDAO, learnerResult, previousThread.getMappedSUL(), + previousThread.getMqOracle(), learner, symbols); } } diff --git a/main/src/main/java/de/learnlib/alex/core/learner/MultiSULOracle.java b/main/src/main/java/de/learnlib/alex/core/learner/MultiSULOracle.java new file mode 100644 index 000000000..0d89081aa --- /dev/null +++ b/main/src/main/java/de/learnlib/alex/core/learner/MultiSULOracle.java @@ -0,0 +1,101 @@ +/* + * Copyright 2016 TU Dortmund + * + * Licensed 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. + */ + +package de.learnlib.alex.core.learner; + +import de.learnlib.api.MembershipOracle.MealyMembershipOracle; +import de.learnlib.api.Query; +import de.learnlib.api.SUL; +import net.automatalib.words.Word; +import net.automatalib.words.WordBuilder; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.*; + +@ParametersAreNonnullByDefault +public class MultiSULOracle implements MealyMembershipOracle { + + private final SUL sul; + private final int maxConcurrentQueries; + + public MultiSULOracle(SUL sul, int maxConcurrentQueries) { + this.sul = sul; + this.maxConcurrentQueries = maxConcurrentQueries; + } + + @Override + public void processQueries(Collection>> queries) { + if (queries.size() > 0) { + processQueries(sul, queries, maxConcurrentQueries); + } + } + + private static void processQueries(SUL sul, Collection>> queries, + int maxConcurrentQueries) { + ExecutorService executor = Executors.newFixedThreadPool(queries.size()); + + List>> futures = new ArrayList<>(); // stores the futures with the words + List>> qs = new ArrayList<>(); + + for (Query> q : queries) { + qs.add(q); + + Callable> worker = () -> { + + // forking the sul allows us to pose multiple + // queries in parallel to multiple suls + SUL forkedSul = sul.fork(); + forkedSul.pre(); + + try { + + // Prefix: Execute symbols, don't record output + for (I sym : q.getPrefix()) { + forkedSul.step(sym); + } + + // Suffix: Execute symbols, outputs constitute output word + WordBuilder wb = new WordBuilder<>(q.getSuffix().length()); + for (I sym : q.getSuffix()) { + wb.add(forkedSul.step(sym)); + } + + return wb.toWord(); + } finally { + forkedSul.post(); + } + }; + + Future> submit = executor.submit(worker); + futures.add(submit); + } + + executor.shutdown(); + while (!executor.isTerminated()) { // wait for all futures to finish + } + for (int i = 0; i < futures.size(); i++) { + try { + Word output = futures.get(i).get(); + qs.get(i).answer(output); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/main/src/main/java/de/learnlib/alex/core/learner/SymbolMapper.java b/main/src/main/java/de/learnlib/alex/core/learner/SymbolMapper.java index 7b60639a9..581aad381 100644 --- a/main/src/main/java/de/learnlib/alex/core/learner/SymbolMapper.java +++ b/main/src/main/java/de/learnlib/alex/core/learner/SymbolMapper.java @@ -27,6 +27,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; @@ -117,4 +118,16 @@ public List getSymbols() { list.addAll(symbols.values()); return list; } + + @Override + public boolean canFork() { + return true; + } + + @Nonnull + @Override + public Mapper, ExecuteResult> fork() + throws UnsupportedOperationException { + return new SymbolMapper(symbols.values().toArray(new Symbol[symbols.values().size()])); + } } diff --git a/main/src/test/java/de/learnlib/alex/core/services/LearnAlgorithmServiceTest.java b/main/src/test/java/de/learnlib/alex/core/services/LearnAlgorithmServiceTest.java index eeaf04c85..7f0b914d4 100644 --- a/main/src/test/java/de/learnlib/alex/core/services/LearnAlgorithmServiceTest.java +++ b/main/src/test/java/de/learnlib/alex/core/services/LearnAlgorithmServiceTest.java @@ -4,7 +4,7 @@ import de.learnlib.alex.annotations.LearnAlgorithm; import de.learnlib.alex.core.entities.Algorithm; import de.learnlib.api.LearningAlgorithm; -import de.learnlib.oracles.SULOracle; +import de.learnlib.api.MembershipOracle; import net.automatalib.words.Alphabet; import org.junit.Test; @@ -26,7 +26,7 @@ public ValidLearnAlgorithm() { @Override public LearningAlgorithm.MealyLearner - createLearner(Alphabet sigma, SULOracle oracle) { + createLearner(Alphabet sigma, MembershipOracle.MealyMembershipOracle oracle) { return null; } @@ -44,7 +44,7 @@ private static class LearnAlgorithmWithoutAnnotation implements LearnAlgorithmFa @Override public LearningAlgorithm.MealyLearner - createLearner(Alphabet sigma, SULOracle oracle) { + createLearner(Alphabet sigma, MembershipOracle.MealyMembershipOracle oracle) { return null; } From 00bdb34cc7a0c7f46b27b3fbe18da16a8af581bd Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Wed, 30 Nov 2016 12:29:33 +0100 Subject: [PATCH 21/52] allow specifiying multiple project urls --- .../learnlib/alex/core/entities/Project.java | 24 ++++++++ .../connectors/ConnectorContextHandler.java | 58 ++++++++++++------- .../ConnectorContextHandlerFactory.java | 24 +++++--- .../html/components/project-create-form.html | 10 ++++ .../modals/project-settings-modal.html | 11 ++++ .../js/components/forms/projectCreateForm.js | 11 ++++ .../widgets/projectDetailsWidget.js | 4 ++ .../modals/projectSettingsModalHandle.js | 12 ++++ .../main/webapp/src/js/entities/Project.js | 6 ++ .../ConnectorContextHandlerTest.java | 32 +++++----- 10 files changed, 145 insertions(+), 47 deletions(-) diff --git a/main/src/main/java/de/learnlib/alex/core/entities/Project.java b/main/src/main/java/de/learnlib/alex/core/entities/Project.java index 3120044f9..f513d46e4 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/Project.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/Project.java @@ -22,6 +22,7 @@ import org.hibernate.validator.constraints.NotBlank; import javax.persistence.CascadeType; +import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; @@ -33,8 +34,10 @@ import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import java.io.Serializable; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -149,6 +152,12 @@ public class Project implements Serializable { @JsonIgnore private Set counters; + /** + * The URLs of the mirrors of the application. + */ + @Column(columnDefinition = "CLOB") + private String mirrorUrls; + /** * Default constructor. */ @@ -169,6 +178,7 @@ public Project(Long projectId) { this.nextSymbolId = 1L; this.userId = 0L; + this.mirrorUrls = ""; } /** @@ -447,6 +457,20 @@ public void setCounters(Set counters) { this.counters = counters; } + /** + * @return The mirror URLs for the project. + */ + public List getMirrorUrls() { + return Arrays.asList(mirrorUrls.split(",")); + } + + /** + * @param mirrorUrls The mirror URLs for the project. + */ + public void setMirrorUrls(List mirrorUrls) { + this.mirrorUrls = String.join(",", mirrorUrls); + } + @Override @SuppressWarnings("checkstyle:needbraces") // Auto generated by IntelliJ public boolean equals(Object o) { diff --git a/main/src/main/java/de/learnlib/alex/core/learner/connectors/ConnectorContextHandler.java b/main/src/main/java/de/learnlib/alex/core/learner/connectors/ConnectorContextHandler.java index 5c2f39c1a..1a086bf14 100644 --- a/main/src/main/java/de/learnlib/alex/core/learner/connectors/ConnectorContextHandler.java +++ b/main/src/main/java/de/learnlib/alex/core/learner/connectors/ConnectorContextHandler.java @@ -21,32 +21,40 @@ import de.learnlib.alex.exceptions.LearnerException; import de.learnlib.mapper.ContextExecutableInputSUL; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + /** * ContextHandler for the connectors. */ public class ConnectorContextHandler implements ContextExecutableInputSUL.ContextHandler { + /** The pool with the managers for the sul. */ + private BlockingQueue pool; + /** The symbol used to reset the SUL. */ private Symbol resetSymbol; - /** The manager which holds all the connectors. */ - private ConnectorManager connectors; - /** * Default constructor. */ public ConnectorContextHandler() { - this.connectors = new ConnectorManager(); + this.pool = new LinkedBlockingQueue<>(); } /** * Add a connector to the set of connectors. * - * @param connector - * The new connector. + * @param connectorManager + * The new connector manager. */ - public void addConnector(Connector connector) { - this.connectors.addConnector(connector.getClass(), connector); + public void addConnectorManager(ConnectorManager connectorManager) { + try { + pool.put(connectorManager); + } catch (InterruptedException e) { + e.printStackTrace(); + System.exit(0); + } } /** @@ -61,23 +69,22 @@ public void setResetSymbol(Symbol resetSymbol) { @Override public ConnectorManager createContext() throws LearnerException { - for (Connector connector : connectors) { - try { - connector.reset(); - } catch (Exception e) { - throw new LearnerException(e.getMessage(), e); - } + ConnectorManager connectorManager; + try { + connectorManager = pool.take(); + } catch (InterruptedException e) { + throw new LearnerException("An error occurred while creating a new context.", e); } - executeResetSymbol(); - - return connectors; - } + try { + for (Connector connector : connectorManager) connector.reset(); + } catch (Exception e) { + throw new LearnerException("An error occurred while resetting a connector.", e); + } - private void executeResetSymbol() throws LearnerException { ExecuteResult resetResult; try { - resetResult = resetSymbol.execute(connectors); + resetResult = resetSymbol.execute(connectorManager); } catch (Exception e) { throw new LearnerException("An error occurred while executing the reset symbol.", e); } @@ -86,11 +93,18 @@ private void executeResetSymbol() throws LearnerException { throw new LearnerException("The execution of the reset symbol failed on step " + resetResult.getFailedActionNumber() + "."); } + + return connectorManager; } @Override - public void disposeContext(ConnectorManager connector) { - connectors.dispose(); + public void disposeContext(ConnectorManager connectorManager) { + try { + pool.put(connectorManager); + connectorManager.dispose(); + } catch (InterruptedException e) { + throw new LearnerException(e.getMessage(), e); + } } } diff --git a/main/src/main/java/de/learnlib/alex/core/learner/connectors/ConnectorContextHandlerFactory.java b/main/src/main/java/de/learnlib/alex/core/learner/connectors/ConnectorContextHandlerFactory.java index c69d420e5..5c3efd709 100644 --- a/main/src/main/java/de/learnlib/alex/core/learner/connectors/ConnectorContextHandlerFactory.java +++ b/main/src/main/java/de/learnlib/alex/core/learner/connectors/ConnectorContextHandlerFactory.java @@ -20,6 +20,8 @@ import org.springframework.stereotype.Service; import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; /** * Factor to create a ContextHandler which knows all available connectors. @@ -38,20 +40,26 @@ public class ConnectorContextHandlerFactory { * The current project in which the context should be. * @param browser * The browser to use for the frontend learning. + * * @return A ContextHandler for the project with all the connectors. */ public ConnectorContextHandler createContext(Project project, WebBrowser browser) { ConnectorContextHandler context = new ConnectorContextHandler(); - String baseUrl = project.getBaseUrl(); - context.addConnector(new WebSiteConnector(baseUrl, browser)); - context.addConnector(new WebServiceConnector(baseUrl)); - context.addConnector(counterStoreConnector); - context.addConnector(new VariableStoreConnector()); - context.addConnector(new FileStoreConnector()); + List urls = new ArrayList<>(); + urls.add(project.getBaseUrl()); + urls.addAll(project.getMirrorUrls()); + + urls.forEach(url -> { + ConnectorManager connectorManager = new ConnectorManager(); + connectorManager.addConnector(WebSiteConnector.class, new WebSiteConnector(url, browser)); + connectorManager.addConnector(WebServiceConnector.class, new WebServiceConnector(url)); + connectorManager.addConnector(CounterStoreConnector.class, counterStoreConnector); + connectorManager.addConnector(VariableStoreConnector.class, new VariableStoreConnector()); + connectorManager.addConnector(FileStoreConnector.class, new FileStoreConnector()); + context.addConnectorManager(connectorManager); + }); return context; } - - } diff --git a/main/src/main/webapp/src/html/components/project-create-form.html b/main/src/main/webapp/src/html/components/project-create-form.html index 3b570c017..95422f798 100644 --- a/main/src/main/webapp/src/html/components/project-create-form.html +++ b/main/src/main/webapp/src/html/components/project-create-form.html @@ -59,6 +59,16 @@
+
+ + +
+
+
+ + +
+ +
`, controller: ResultListModalController, From 067709ea3da35a2db557cfee700c91ae30de1e7f Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Mon, 16 Jan 2017 10:27:05 +0100 Subject: [PATCH 43/52] find separating word between two hypotheses in the frontend --- .../learnlib/alex/rest/LearnerResource.java | 20 +++++-- .../learn-resume-settings-widget.html | 15 +++++ .../components/views/results-compare.html | 18 ++++-- ...mbols-test-view.html => symbols-test.html} | 2 +- .../directives/modals/result-list-modal.html | 45 +++++++++++++++ .../webapp/src/js/components/fileDropzone.js | 13 ++++- .../src/js/components/learnResultPanel.js | 18 +++++- .../js/components/views/resultsCompareView.js | 57 ++++++++++++++++++- .../js/components/views/symbolsTestView.js | 2 +- .../widgets/learnResumeSettingsWidget.js | 8 +++ .../modals/resultListModalHandle.js | 56 +++++++----------- .../src/js/resources/LearnerResource.js | 5 ++ main/src/main/webapp/src/scss/style.scss | 34 +++++++---- .../IncrementCounterActionTest.java | 4 +- 14 files changed, 230 insertions(+), 67 deletions(-) rename main/src/main/webapp/src/html/components/views/{symbols-test-view.html => symbols-test.html} (98%) create mode 100644 main/src/main/webapp/src/html/directives/modals/result-list-modal.html diff --git a/main/src/main/java/de/learnlib/alex/rest/LearnerResource.java b/main/src/main/java/de/learnlib/alex/rest/LearnerResource.java index 517cdbacf..0513de9e0 100644 --- a/main/src/main/java/de/learnlib/alex/rest/LearnerResource.java +++ b/main/src/main/java/de/learnlib/alex/rest/LearnerResource.java @@ -35,6 +35,7 @@ import de.learnlib.alex.security.UserPrincipal; import de.learnlib.alex.utils.ResourceErrorHandler; import de.learnlib.alex.utils.ResponseHelper; +import net.automatalib.words.Alphabet; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; @@ -384,15 +385,15 @@ private List loadSymbols(User user, Long projectId, List ids) thro } /** - * Test of two hypotheses are equal or not. + * Test if two hypotheses are equal or not. * If a difference was found the separating word will be returned. * Otherwise, i.e. the hypotheses are equal. * * @param mealyMachineProxies A List of two (!) hypotheses, which will be compared. - * @return '{"seperatingWord": "seperating word, if any"}' + * @return '{"separatingWord": "separating word, if any"}' */ @POST - @Path("/compare/") + @Path("/compare") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response compareTwoUploaded(List mealyMachineProxies) { @@ -400,13 +401,22 @@ public Response compareTwoUploaded(List mealyMachinePr LOGGER.traceEntry("compareTwoUploaded({}) for user {}.", mealyMachineProxies, user); if (mealyMachineProxies.size() != 2) { - IllegalArgumentException e = new IllegalArgumentException("You need to specify exactly two hypothesis!"); + IllegalArgumentException e = new IllegalArgumentException("You need to specify exactly two hypotheses!"); + return ResourceErrorHandler.createRESTErrorMessage("LearnerResource.readOutput", Status.BAD_REQUEST, e); + } + + // make sure both hypotheses consist of the same alphabet + Alphabet sigmaA = mealyMachineProxies.get(0).createAlphabet(); + Alphabet sigmaB = mealyMachineProxies.get(1).createAlphabet(); + + if (sigmaA.size() != sigmaB.size() || !sigmaA.containsAll(sigmaB)) { + IllegalArgumentException e = new IllegalArgumentException("The alphabets of the hypotheses are not identical!"); return ResourceErrorHandler.createRESTErrorMessage("LearnerResource.readOutput", Status.BAD_REQUEST, e); } String separatingWord = learner.compare(mealyMachineProxies.get(0), mealyMachineProxies.get(1)); LOGGER.traceExit(separatingWord); - return Response.ok("{\"seperatingWord\": \"" + separatingWord + "\"}").build(); + return Response.ok("{\"separatingWord\": \"" + separatingWord + "\"}").build(); } } diff --git a/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html b/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html index f5fd42499..3f8f0ec4b 100644 --- a/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html +++ b/main/src/main/webapp/src/html/components/learn-resume-settings-widget.html @@ -66,6 +66,21 @@
+ +
+ + +
+ + + Drag and drop *.json file here or click to upload + + + Use a Hypothesis with {{ vm.configuration.eqOracle.hypothesis.nodes.length }} states + + + +
diff --git a/main/src/main/webapp/src/html/components/views/results-compare.html b/main/src/main/webapp/src/html/components/views/results-compare.html index 21ff28780..15288308e 100644 --- a/main/src/main/webapp/src/html/components/views/results-compare.html +++ b/main/src/main/webapp/src/html/components/views/results-compare.html @@ -8,16 +8,24 @@
- + -
- +
+
+ +
+
+ +
-
\ No newline at end of file diff --git a/main/src/main/webapp/src/html/components/views/symbols-test-view.html b/main/src/main/webapp/src/html/components/views/symbols-test.html similarity index 98% rename from main/src/main/webapp/src/html/components/views/symbols-test-view.html rename to main/src/main/webapp/src/html/components/views/symbols-test.html index 64cf1ec9f..2cd9941fc 100644 --- a/main/src/main/webapp/src/html/components/views/symbols-test-view.html +++ b/main/src/main/webapp/src/html/components/views/symbols-test.html @@ -12,7 +12,7 @@ {{group.name}}
- {{symbol.name}} diff --git a/main/src/main/webapp/src/html/directives/modals/result-list-modal.html b/main/src/main/webapp/src/html/directives/modals/result-list-modal.html new file mode 100644 index 000000000..39857891a --- /dev/null +++ b/main/src/main/webapp/src/html/directives/modals/result-list-modal.html @@ -0,0 +1,45 @@ + + \ No newline at end of file diff --git a/main/src/main/webapp/src/js/components/fileDropzone.js b/main/src/main/webapp/src/js/components/fileDropzone.js index 001767403..495bedbac 100644 --- a/main/src/main/webapp/src/js/components/fileDropzone.js +++ b/main/src/main/webapp/src/js/components/fileDropzone.js @@ -57,9 +57,13 @@ class FileDropzone { */ onLoad(e) { this.$scope.$apply(() => { - this.EventBus.emit(events.FILE_LOADED, { - file: e.target.result - }); + if (this.onLoaded()) { + this.onLoaded()(e.target.result); + } else { + this.EventBus.emit(events.FILE_LOADED, { + file: e.target.result + }); + } }); } @@ -115,6 +119,9 @@ class FileDropzone { export const fileDropzone = { controller: FileDropzone, transclude: true, + bindings: { + onLoaded: '&' + }, template: `
` diff --git a/main/src/main/webapp/src/js/components/learnResultPanel.js b/main/src/main/webapp/src/js/components/learnResultPanel.js index 39836d9ed..805699dec 100644 --- a/main/src/main/webapp/src/js/components/learnResultPanel.js +++ b/main/src/main/webapp/src/js/components/learnResultPanel.js @@ -82,6 +82,7 @@ class LearnResultPanel { * @type {number} */ this.pointer = this.result.steps.length - 1; + this.emitStep(); this.$scope.$watch(() => this.result, () => { if (this.result) this.pointer = this.result.steps.length - 1; @@ -160,11 +161,21 @@ class LearnResultPanel { }); } + /** + * Emits the index of the currently shown step. + */ + emitStep() { + if (this.index >= 0 && this.onStep()) { + this.onStep()(this.index, this.pointer); + } + } + /** * Shows the first result of the test process. */ firstStep() { this.pointer = 0; + this.emitStep(); } /** @@ -175,6 +186,7 @@ class LearnResultPanel { this.lastStep(); } else { this.pointer--; + this.emitStep(); } } @@ -186,6 +198,7 @@ class LearnResultPanel { this.firstStep(); } else { this.pointer++; + this.emitStep(); } } @@ -194,6 +207,7 @@ class LearnResultPanel { */ lastStep() { this.pointer = this.result.steps.length - 1; + this.emitStep(); } } @@ -203,6 +217,8 @@ export const learnResultPanel = { controller: LearnResultPanel, controllerAs: 'vm', bindings: { - result: '=' + result: '=', + index: '=', + onStep: '&' } }; \ No newline at end of file diff --git a/main/src/main/webapp/src/js/components/views/resultsCompareView.js b/main/src/main/webapp/src/js/components/views/resultsCompareView.js index 1969cf754..d1d8477a6 100644 --- a/main/src/main/webapp/src/js/components/views/resultsCompareView.js +++ b/main/src/main/webapp/src/js/components/views/resultsCompareView.js @@ -32,14 +32,19 @@ class ResultsCompareView { * @param {LearnResultResource} LearnResultResource * @param {ErrorService} ErrorService * @param {EventBus} EventBus + * @param {LearnerResource} LearnerResource + * @param {ToastService} ToastService */ // @ngInject - constructor($timeout, $scope, $uibModal, $stateParams, SessionService, LearnResultResource, ErrorService, EventBus) { + constructor($timeout, $scope, $uibModal, $stateParams, SessionService, LearnResultResource, ErrorService, EventBus, + LearnerResource, ToastService) { this.$timeout = $timeout; this.$uibModal = $uibModal; this.LearnResultResource = LearnResultResource; this.ErrorService = ErrorService; this.EventBus = EventBus; + this.LearnerResource = LearnerResource; + this.ToastService = ToastService; /** * The project that is in the session. @@ -59,6 +64,9 @@ class ResultsCompareView { */ this.panels = []; + /** The indeces of the steps that are displayed. */ + this.panelPointers = []; + /** * The list of layout settings for the current hypothesis that is shown in a panel. * @type {Object[]} @@ -106,10 +114,57 @@ class ResultsCompareView { closePanel(index) { this.panels[index] = null; this.panels.splice(index, 1); + this.panelPointers.splice(index, 1); window.setTimeout(() => { window.dispatchEvent(new Event('resize')); }, 100); } + + /** + * Get the pointer to the step of the displayed hypotheses. + * @param {number} index + * @param {number} pointer + */ + onStep(index, pointer) { + this.panelPointers[index] = pointer; + } + + /** + * Test if a separating word between the first two displayed hypotheses can be found. + */ + showSeparatingWord() { + const hypA = this.panels[0].steps[this.panelPointers[0]].hypothesis; + const hypB = this.panels[1].steps[this.panelPointers[1]].hypothesis; + + this.LearnerResource.compare(hypA, hypB) + .then(data => { + if (data.separatingWord === "") { + this.ToastService.info("The two hypotheses are identical"); + } else { + this.$uibModal.open({ + template: ` + + + `, + resolve: { + word: () => data.separatingWord + }, + // @ngInject + controller: function ($uibModalInstance, word) { + this.word = word; + this.close = () => $uibModalInstance.close(); + }, + controllerAs: 'vm', + }) + } + }) + .catch(err => this.ToastService.danger(err.data.message)); + } } export const resultsCompareView = { diff --git a/main/src/main/webapp/src/js/components/views/symbolsTestView.js b/main/src/main/webapp/src/js/components/views/symbolsTestView.js index 897f9b7de..b296c0ab0 100644 --- a/main/src/main/webapp/src/js/components/views/symbolsTestView.js +++ b/main/src/main/webapp/src/js/components/views/symbolsTestView.js @@ -131,5 +131,5 @@ export const symbolsTestView = { } }, controllerAs: 'vm', - templateUrl: 'html/components/views/symbols-test-view.html' + templateUrl: 'html/components/views/symbols-test.html' }; \ No newline at end of file diff --git a/main/src/main/webapp/src/js/components/widgets/learnResumeSettingsWidget.js b/main/src/main/webapp/src/js/components/widgets/learnResumeSettingsWidget.js index f15e734c1..7de06b789 100644 --- a/main/src/main/webapp/src/js/components/widgets/learnResumeSettingsWidget.js +++ b/main/src/main/webapp/src/js/components/widgets/learnResumeSettingsWidget.js @@ -50,6 +50,14 @@ class LearnResumeSettingsWidget { this.selectedEqOracle = this.configuration.eqOracle.type; } + /** + * Load hypothesis. + * @param hypothesis + */ + loadHypothesis(hypothesis) { + this.configuration.eqOracle.hypothesis = JSON.parse(hypothesis); + } + /** * Creates a new eq oracle object from the selected type and assigns it to the configuration. */ diff --git a/main/src/main/webapp/src/js/directives/modals/resultListModalHandle.js b/main/src/main/webapp/src/js/directives/modals/resultListModalHandle.js index 7edef4500..d6123ed92 100644 --- a/main/src/main/webapp/src/js/directives/modals/resultListModalHandle.js +++ b/main/src/main/webapp/src/js/directives/modals/resultListModalHandle.js @@ -13,14 +13,16 @@ export class ResultListModalController { * @param {EventBus} EventBus * @param {ProjectResource} ProjectResource * @param {LearnResultResource} LearnResultResource + * @param {ToastService} ToastService */ // @ngInject - constructor(modalData, $uibModalInstance, EventBus, ProjectResource, LearnResultResource) { + constructor(modalData, $uibModalInstance, EventBus, ProjectResource, LearnResultResource, ToastService) { this.results = modalData.results; this.$uibModalInstance = $uibModalInstance; this.EventBus = EventBus; this.LearnResultResource = LearnResultResource; this.projects = []; + this.ToastService = ToastService; ProjectResource.getAll() .then(projects => this.projects = projects) @@ -51,6 +53,21 @@ export class ResultListModalController { this.close(); } + /** + * Loads a hypothesis from a json file. + * @param {string} hypothesis - The hypothesis as string + */ + loadResultFromFile(hypothesis) { + try { + this.EventBus.emit(events.RESULT_SELECTED, {result: { + steps: [{hypothesis: JSON.parse(hypothesis)}] + }}); + this.close(); + } catch(e) { + this.ToastService.danger('Could not parse the file.') + } + } + /** * Closes the modal. */ @@ -69,42 +86,7 @@ export function resultListModalHandle($uibModal) { link(scope, el) { el.on('click', () => { $uibModal.open({ - template: ` - - - `, + templateUrl: 'html/directives/modals/result-list-modal.html', controller: ResultListModalController, controllerAs: 'vm', resolve: { diff --git a/main/src/main/webapp/src/js/resources/LearnerResource.js b/main/src/main/webapp/src/js/resources/LearnerResource.js index 0c64f5978..ae0cb2f2d 100644 --- a/main/src/main/webapp/src/js/resources/LearnerResource.js +++ b/main/src/main/webapp/src/js/resources/LearnerResource.js @@ -111,4 +111,9 @@ export class LearnerResource { return this.$http.post(`rest/learner/words/${projectId}/outputs`, readOutputConfig) .then(response => response.data); } + + compare(hypA, hypB) { + return this.$http.post(`rest/learner/compare`, [hypA, hypB]) + .then(response => response.data); + } } \ No newline at end of file diff --git a/main/src/main/webapp/src/scss/style.scss b/main/src/main/webapp/src/scss/style.scss index bdf617921..e8e3502d0 100644 --- a/main/src/main/webapp/src/scss/style.scss +++ b/main/src/main/webapp/src/scss/style.scss @@ -456,21 +456,33 @@ body { @include position(absolute, 0, 0, 0, 0); display: flex; - .add-panel-button { + .buttons { position: absolute; z-index: 1000; right: 12px; bottom: 12px; - width: 42px; - height: 42px; - border-radius: 21px; - color: #fff; - background: #5cb85c; - padding-top: 11px; - cursor: pointer; - opacity: 0.75; - &:hover { - opacity: 1.0; + + .add-panel-button, .separating-word-panel-button { + float: left; + width: 42px; + height: 42px; + border-radius: 21px; + color: #fff; + padding-top: 11px; + cursor: pointer; + opacity: 0.75; + margin-left: 12px; + &:hover { + opacity: 1.0; + } + } + + .add-panel-button { + background: #5cb85c; + } + + .separating-word-panel-button { + background: #0f6ab4; } } } diff --git a/main/src/test/java/de/learnlib/alex/actions/StoreSymbolActions/IncrementCounterActionTest.java b/main/src/test/java/de/learnlib/alex/actions/StoreSymbolActions/IncrementCounterActionTest.java index 5f23b2058..266e50a02 100644 --- a/main/src/test/java/de/learnlib/alex/actions/StoreSymbolActions/IncrementCounterActionTest.java +++ b/main/src/test/java/de/learnlib/alex/actions/StoreSymbolActions/IncrementCounterActionTest.java @@ -66,6 +66,7 @@ public void setUp() { incrementAction.setUser(user); incrementAction.setProject(project); incrementAction.setName(TEST_NAME); + incrementAction.setIncrementBy(1); } @Test @@ -104,7 +105,6 @@ public void shouldSuccessfullyIncrementCounter() { ExecuteResult result = incrementAction.execute(connector); assertEquals(ExecuteResult.OK, result); - verify(counters).increment(USER_ID, PROJECT_ID, PROJECT_URL, TEST_NAME); + verify(counters).incrementBy(USER_ID, PROJECT_ID, PROJECT_URL, TEST_NAME, 1); } - } From ddb328413e7434a9f9a8c3ebd54ba1dd27936436 Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Tue, 17 Jan 2017 13:52:32 +0100 Subject: [PATCH 44/52] added action to set a variable by a regex match --- .../SetVariableByRegexGroup.java | 152 ++++++++++++++++++ .../alex/core/entities/Algorithm.java | 16 ++ .../alex/core/entities/BrowserConfig.java | 16 ++ .../alex/core/entities/SymbolAction.java | 1 + .../components/forms/actions/action-form.html | 1 + .../general/set-variable-by-regex-group.html | 26 +++ .../modals/action-create-modal.html | 1 + .../forms/actions/generalActionForms.js | 8 + main/src/main/webapp/src/js/constants.js | 1 + .../generalActions/SetVariableByRegexGroup.js | 67 ++++++++ main/src/main/webapp/src/js/index.js | 2 + .../webapp/src/js/services/ActionService.js | 3 + 12 files changed, 294 insertions(+) create mode 100644 main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByRegexGroup.java create mode 100644 main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-regex-group.html create mode 100644 main/src/main/webapp/src/js/entities/actions/generalActions/SetVariableByRegexGroup.js diff --git a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByRegexGroup.java b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByRegexGroup.java new file mode 100644 index 000000000..2b9a801cd --- /dev/null +++ b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByRegexGroup.java @@ -0,0 +1,152 @@ +/* + * Copyright 2016 TU Dortmund + * + * Licensed 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. + */ + +package de.learnlib.alex.actions.StoreSymbolActions; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.SymbolAction; +import de.learnlib.alex.core.learner.connectors.ConnectorManager; +import de.learnlib.alex.core.learner.connectors.VariableStoreConnector; +import de.learnlib.alex.core.learner.connectors.WebSiteConnector; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; +import org.hibernate.validator.constraints.NotBlank; + +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.validation.constraints.NotNull; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Action that, given a regular expression, searches in the page source for matches. + * If a match is found, it extracts the nth group, e.g. (.*?) in the regex, and saves the value into a variable. + */ +@Entity +@DiscriminatorValue("setVariableByRegexGroup") +@JsonTypeName("setVariableByRegexGroup") +public class SetVariableByRegexGroup extends SymbolAction { + + private static final long serialVersionUID = -5562530206394874225L; + + private static final Logger LOGGER = LogManager.getLogger(); + + private static final Marker LEARNER_MARKER = MarkerManager.getMarker("LEARNER"); + + /** The name of the variable. */ + @NotBlank + private String name; + + /** The regex to search in the page source. */ + @NotBlank + private String regex; + + /** Which match should be used. */ + @NotNull + private int nthMatch; + + /** Which group in the match should be used. */ + @NotNull + private int mthGroup; + + @Override + protected ExecuteResult execute(ConnectorManager connector) { + if (nthMatch < 1) { + nthMatch = 1; + } + if (mthGroup < 0) { + mthGroup = 0; + } + + String pageSource = connector.getConnector(WebSiteConnector.class) + .getDriver() + .getPageSource(); + + Matcher matcher = Pattern.compile(regex) + .matcher(pageSource); + + try { + boolean matchFound = false; + int i = 1; + + while (matcher.find()) { + if (i == nthMatch) { + String group = matcher.group(mthGroup); + connector.getConnector(VariableStoreConnector.class) + .set(name, group); + matchFound = true; + break; + } + i++; + } + + if (!matchFound) { + LOGGER.info(LEARNER_MARKER, "Could not find a string that matches regex '{}' " + + "(ignoreFailure: {}, negated: {})", regex, ignoreFailure, negated); + return getFailedOutput(); + } + } catch (IndexOutOfBoundsException e) { + LOGGER.info(LEARNER_MARKER, "Could not find group {} in regex '{}' " + + "(ignoreFailure: {}, negated: {})", mthGroup, regex, ignoreFailure, negated); + return getFailedOutput(); + } + + return getSuccessOutput(); + } + + /** @return {@link #name}. */ + public String getName() { + return name; + } + + /** @param name {@link #name}. */ + public void setName(String name) { + this.name = name; + } + + /** @return {@link #regex}. */ + public String getRegex() { + return regex; + } + + /** @param regex {@link #regex}. */ + public void setRegex(String regex) { + this.regex = regex; + } + + /** @return {@link #nthMatch}. */ + public int getNthMatch() { + return nthMatch; + } + + /** @param nthMatch {@link #nthMatch}. */ + public void setNthMatch(int nthMatch) { + this.nthMatch = nthMatch; + } + + /** @return {@link #mthGroup}. */ + public int getMthGroup() { + return mthGroup; + } + + /** @param mthGroup {@link #mthGroup}. */ + public void setMthGroup(int mthGroup) { + this.mthGroup = mthGroup; + } +} diff --git a/main/src/main/java/de/learnlib/alex/core/entities/Algorithm.java b/main/src/main/java/de/learnlib/alex/core/entities/Algorithm.java index af45e482a..9db07ad9b 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/Algorithm.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/Algorithm.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 TU Dortmund + * + * Licensed 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. + */ + package de.learnlib.alex.core.entities; import javax.persistence.Column; diff --git a/main/src/main/java/de/learnlib/alex/core/entities/BrowserConfig.java b/main/src/main/java/de/learnlib/alex/core/entities/BrowserConfig.java index ebe77c4b2..09adffacd 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/BrowserConfig.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/BrowserConfig.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 TU Dortmund + * + * Licensed 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. + */ + package de.learnlib.alex.core.entities; import com.fasterxml.jackson.annotation.JsonPropertyOrder; diff --git a/main/src/main/java/de/learnlib/alex/core/entities/SymbolAction.java b/main/src/main/java/de/learnlib/alex/core/entities/SymbolAction.java index 7293b54ea..53c90d783 100644 --- a/main/src/main/java/de/learnlib/alex/core/entities/SymbolAction.java +++ b/main/src/main/java/de/learnlib/alex/core/entities/SymbolAction.java @@ -93,6 +93,7 @@ @JsonSubTypes.Type(name = "setVariableByCookie", value = SetVariableByCookieAction.class), @JsonSubTypes.Type(name = "setVariableByNodeAttribute", value = SetVariableByNodeAttributeAction.class), @JsonSubTypes.Type(name = "setVariableByNodeCount", value = SetVariableByNodeCountAction.class), + @JsonSubTypes.Type(name = "setVariableByRegexGroup", value = SetVariableByRegexGroup.class), // Web Actions @JsonSubTypes.Type(name = "web", value = WebSymbolAction.class), @JsonSubTypes.Type(name = "web_checkNodeAttributeValue", value = CheckNodeAttributeValueAction.class), diff --git a/main/src/main/webapp/src/html/components/forms/actions/action-form.html b/main/src/main/webapp/src/html/components/forms/actions/action-form.html index 9cd1024dc..bb47d107f 100644 --- a/main/src/main/webapp/src/html/components/forms/actions/action-form.html +++ b/main/src/main/webapp/src/html/components/forms/actions/action-form.html @@ -40,6 +40,7 @@ +

diff --git a/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-regex-group.html b/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-regex-group.html new file mode 100644 index 000000000..a78c5f9c4 --- /dev/null +++ b/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-regex-group.html @@ -0,0 +1,26 @@ +

Set Variable by Regex Group

+ +

+ Set a variable to the value of the nth group in the mth match in the page source. +

+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
\ No newline at end of file diff --git a/main/src/main/webapp/src/html/directives/modals/action-create-modal.html b/main/src/main/webapp/src/html/directives/modals/action-create-modal.html index d85650d89..322ca7f75 100644 --- a/main/src/main/webapp/src/html/directives/modals/action-create-modal.html +++ b/main/src/main/webapp/src/html/directives/modals/action-create-modal.html @@ -68,6 +68,7 @@ Set variable by HTML Set variable by node attribute Set variable by node count + Set variable by regex group Wait diff --git a/main/src/main/webapp/src/js/components/forms/actions/generalActionForms.js b/main/src/main/webapp/src/js/components/forms/actions/generalActionForms.js index 68e3a1a97..f28887e2d 100644 --- a/main/src/main/webapp/src/js/components/forms/actions/generalActionForms.js +++ b/main/src/main/webapp/src/js/components/forms/actions/generalActionForms.js @@ -106,6 +106,14 @@ export const actionFormSetVariableByNodeCount = { controllerAs: 'vm' }; +export const actionFormSetVariableByRegexGroup = { + templateUrl: 'html/components/forms/actions/general/set-variable-by-regex-group.html', + bindings: { + action: '=' + }, + controllerAs: 'vm' +}; + export const actionFormWait = { templateUrl: 'html/components/forms/actions/general/wait.html', bindings: { diff --git a/main/src/main/webapp/src/js/constants.js b/main/src/main/webapp/src/js/constants.js index ed2d34d8e..f2d7f70b2 100644 --- a/main/src/main/webapp/src/js/constants.js +++ b/main/src/main/webapp/src/js/constants.js @@ -122,6 +122,7 @@ export const actionType = { GENERAL_SET_VARIABLE_BY_HTML: 'setVariableByHTML', GENERAL_SET_VARIABLE_BY_NODE_ATTRIBUTE: 'setVariableByNodeAttribute', GENERAL_SET_VARIABLE_BY_NODE_COUNT: 'setVariableByNodeCount', + GENERAL_SET_VARIABLE_BY_REGEX_GROUP: 'setVariableByRegexGroup', GENERAL_SET_VARIABLE: 'setVariable', WAIT: 'wait' }; diff --git a/main/src/main/webapp/src/js/entities/actions/generalActions/SetVariableByRegexGroup.js b/main/src/main/webapp/src/js/entities/actions/generalActions/SetVariableByRegexGroup.js new file mode 100644 index 000000000..7463223d9 --- /dev/null +++ b/main/src/main/webapp/src/js/entities/actions/generalActions/SetVariableByRegexGroup.js @@ -0,0 +1,67 @@ +/* + * Copyright 2016 TU Dortmund + * + * Licensed 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 {Action} from "../Action"; +import {actionType} from "../../../constants"; + +/** + * Action that, given a regular expression, searches in the page source for matches. + * If a match is found, it extracts the nth group, e.g. (.*?) in the regex, and saves the value into a variable. + */ +export class SetVariableByRegexGroup extends Action { + + /** + * Constructor. + * + * @param {object} obj - The object to create the action from. + */ + constructor(obj) { + super(actionType.GENERAL_SET_VARIABLE_BY_REGEX_GROUP, obj); + + /** + * The name of the variable. + * @type {string} + */ + this.name = obj.name || ''; + + /** + * The regex. + * @type {string} + */ + this.regex = obj.regex || ''; + + /** + * Which match should be used. + * @type {number} + */ + this.nthMatch = typeof obj.nthMatch !== "undefined" ? obj.nthMatch : 1; + + /** + * Which group in the match should be used. + * @type {number} + */ + this.mthGroup = typeof obj.mthGroup !== "undefined" ? obj.mthGroup : 0; + } + + /** + * A string representation of the action. + * + * @returns {string} + */ + toString() { + return `Set variable '${this.name}' to the ${this.mthGroup}. group in the ${this.nthMatch}. match of '${this.regex}'`; + } +} \ No newline at end of file diff --git a/main/src/main/webapp/src/js/index.js b/main/src/main/webapp/src/js/index.js index 27672c4b5..3d038a5b5 100644 --- a/main/src/main/webapp/src/js/index.js +++ b/main/src/main/webapp/src/js/index.js @@ -150,6 +150,7 @@ import { actionFormSetVariableByJson, actionFormSetVariableByNodeAttribute, actionFormSetVariableByNodeCount, + actionFormSetVariableByRegexGroup, actionFormWait } from "./components/forms/actions/generalActionForms"; @@ -309,6 +310,7 @@ angular .component('actionFormSetVariableByJson', actionFormSetVariableByJson) .component('actionFormSetVariableByNodeAttribute', actionFormSetVariableByNodeAttribute) .component('actionFormSetVariableByNodeCount', actionFormSetVariableByNodeCount) + .component('actionFormSetVariableByRegexGroup', actionFormSetVariableByRegexGroup) .component('actionFormWait', actionFormWait) // misc components diff --git a/main/src/main/webapp/src/js/services/ActionService.js b/main/src/main/webapp/src/js/services/ActionService.js index 09efddcfd..d6a0eec47 100644 --- a/main/src/main/webapp/src/js/services/ActionService.js +++ b/main/src/main/webapp/src/js/services/ActionService.js @@ -49,6 +49,7 @@ import {SetVariableByNodeGeneralAction} from "../entities/actions/generalActions import {SetVariableByNodeAttributeGeneralAction} from "../entities/actions/generalActions/SetVariableByNodeAttributeGeneralAction"; import {SetVariableGeneralAction} from "../entities/actions/generalActions/SetVariableGeneralAction"; import {SetVariableByNodeCountAction} from "../entities/actions/generalActions/SetVariableByNodeCountAction"; +import {SetVariableByRegexGroup} from "../entities/actions/generalActions/SetVariableByRegexGroup"; import {WaitGeneralAction} from "../entities/actions/generalActions/WaitGeneralAction"; /** @@ -134,6 +135,8 @@ export class ActionService { return new SetVariableByNodeAttributeGeneralAction(data); case actionType.GENERAL_SET_VARIABLE_BY_NODE_COUNT: return new SetVariableByNodeCountAction(data); + case actionType.GENERAL_SET_VARIABLE_BY_REGEX_GROUP: + return new SetVariableByRegexGroup(data); case actionType.WAIT: return new WaitGeneralAction(data); case actionType.WAIT_FOR_TITLE: From 1ec2e29a4b025c7cfd6391d4727de6383cbafe85 Mon Sep 17 00:00:00 2001 From: Alexander Bainczyk Date: Fri, 20 Jan 2017 15:09:53 +0100 Subject: [PATCH 45/52] allow using xpath selectors beside css selectors in all actions that deal with html elements --- CHANGELOG.md | 10 ++ .../IncrementCounterAction.java | 16 +-- .../SetVariableByHTMLElementAction.java | 42 ++++++- .../SetVariableByNodeAttributeAction.java | 11 +- .../SetVariableByNodeCountAction.java | 24 ++-- .../SetVariableByRegexGroup.java | 8 +- .../WebSymbolActions/CheckNodeAction.java | 52 +++------ .../CheckNodeAttributeValueAction.java | 14 ++- .../actions/WebSymbolActions/ClearAction.java | 31 ++---- .../actions/WebSymbolActions/ClickAction.java | 31 ++---- .../actions/WebSymbolActions/FillAction.java | 48 ++------ .../WebSymbolActions/MoveMouseAction.java | 17 +-- .../WebSymbolActions/PressKeyAction.java | 15 +-- .../WebSymbolActions/SelectAction.java | 10 +- .../WebSymbolActions/SubmitAction.java | 31 ++---- .../WebSymbolActions/WaitForNodeAction.java | 32 +++--- .../alex/core/entities/WebElementLocator.java | 105 ++++++++++++++++++ .../learner/connectors/WebSiteConnector.java | 34 +++++- .../actions/general/set-variable-by-html.html | 14 +-- .../set-variable-by-node-attribute.html | 19 +--- .../general/set-variable-by-node-count.html | 16 +-- .../forms/actions/web/check-for-node.html | 9 +- .../web/check-node-attribute-value.html | 11 +- .../forms/actions/web/clear-input.html | 9 +- .../components/forms/actions/web/click.html | 9 +- .../forms/actions/web/move-mouse.html | 9 +- .../forms/actions/web/press-key.html | 11 +- .../components/forms/actions/web/select.html | 11 +- .../forms/actions/web/send-keys.html | 40 +++++-- .../components/forms/actions/web/submit.html | 8 +- .../forms/actions/web/wait-for-node.html | 11 +- .../src/js/components/forms/nodeFormGroup.js | 47 ++++++++ .../modals/actionEditModalHandle.js | 5 +- ...SetVariableByNodeAttributeGeneralAction.js | 7 +- .../SetVariableByNodeCountAction.js | 8 +- .../SetVariableByNodeGeneralAction.js | 8 +- .../webActions/CheckAttributeValueAction.js | 6 +- .../webActions/CheckForNodeWebAction.js | 6 +- .../actions/webActions/ClearWebAction.js | 6 +- .../actions/webActions/ClickWebAction.js | 8 +- .../actions/webActions/FillWebAction.js | 6 +- .../actions/webActions/MoveMouseAction.js | 8 +- .../actions/webActions/PressKeyAction.js | 10 +- .../actions/webActions/SelectWebAction.js | 6 +- .../actions/webActions/SubmitWebAction.js | 6 +- .../actions/webActions/WaitForNodeAction.js | 6 +- main/src/main/webapp/src/js/index.js | 2 + .../main/webapp/tests/resources/entities.js | 6 +- .../tests/unit/entities/EqOracle.tests.js | 3 +- .../tests/unit/entities/Project.tests.js | 1 - .../ExecuteSymbolGeneralAction.tests.js | 3 +- .../IncrementCounterGeneralAction.tests.js | 4 +- .../SetCounterGeneralAction.tests.js | 3 +- .../SetVariableByNodeGeneralAction.tests.js | 7 +- .../webActions/CheckForNodeWebAction.tests.js | 8 +- .../webActions/ClearWebAction.tests.js | 8 +- .../webActions/ClickWebAction.tests.js | 8 +- .../actions/webActions/FillWebAction.tests.js | 8 +- .../webActions/MoveMouseAction.tests.js | 5 +- .../webActions/SelectWebAction.tests.js | 7 +- .../webActions/SubmitWebAction.tests.js | 7 +- .../webActions/WaitForNodeAction.tests.js | 5 +- .../SetVariableByHTMLElementActionTest.java | 17 ++- .../SetVariableByNodeAttributeActionTest.java | 15 ++- .../WebSymbolActions/CheckNodeActionTest.java | 20 +++- .../WebSymbolActions/ClearActionTest.java | 15 ++- .../WebSymbolActions/ClickActionTest.java | 18 ++- .../WebSymbolActions/FillActionTest.java | 15 ++- ...ouseTest.java => MoveMouseActionTest.java} | 30 +++-- .../WebSymbolActions/SelectActionTest.java | 21 ++-- .../WebSymbolActions/SubmitActionTest.java | 15 ++- .../WaitForNodeActionTest.java | 17 ++- .../alex/core/dao/SymbolDAOImplTest.java | 12 +- .../SetVariableByHTMLElementTestData.json | 5 +- .../SetVariableByNodeAttributeTestData.json | 5 +- .../websymbolactions/CheckNodeTestData.json | 5 +- .../websymbolactions/ClearTestData.json | 5 +- .../websymbolactions/ClickTestData.json | 5 +- .../websymbolactions/FillTestData.json | 5 +- .../websymbolactions/MoveMouseTestData.json | 5 +- .../websymbolactions/SelectTestData.json | 5 +- .../websymbolactions/SubmitTestData.json | 5 +- .../websymbolactions/WaitForNodeTestData.json | 5 +- .../websymbolactions/WebSymbolTestData.json | 26 ++++- 84 files changed, 706 insertions(+), 501 deletions(-) create mode 100644 main/src/main/java/de/learnlib/alex/core/entities/WebElementLocator.java create mode 100644 main/src/main/webapp/src/js/components/forms/nodeFormGroup.js rename main/src/test/java/de/learnlib/alex/actions/WebSymbolActions/{MoveMouseTest.java => MoveMouseActionTest.java} (80%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d55a9016f..953288979 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# ALEX v1.2.1 + +## Features + +* New actions: + * Set a variable by node count + * Set a variable by regex +* Switch between XPath and CSS selectors in actions +* Experimental parallel test execution support + # ALEX v1.2 ## Features diff --git a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/IncrementCounterAction.java b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/IncrementCounterAction.java index 12a4e7137..4c1031ad0 100644 --- a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/IncrementCounterAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/IncrementCounterAction.java @@ -21,7 +21,6 @@ import de.learnlib.alex.core.entities.SymbolAction; import de.learnlib.alex.core.learner.connectors.ConnectorManager; import de.learnlib.alex.core.learner.connectors.CounterStoreConnector; -import de.learnlib.alex.core.learner.connectors.VariableStoreConnector; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -64,29 +63,22 @@ public ExecuteResult execute(ConnectorManager connector) { return getSuccessOutput(); } - /** - * Get the name of the counter. - * - * @return The counter name. - */ + /** @return {@link #name}. */ public String getName() { return name; } - /** - * Set a new counter by its name. - * - * @param name - * The new name of the counter to use. - */ + /** @param name {@link #name}. */ public void setName(String name) { this.name = name; } + /** @return {@link #incrementBy}. */ public int getIncrementBy() { return incrementBy; } + /** @param incrementBy {@link #incrementBy}. */ public void setIncrementBy(int incrementBy) { this.incrementBy = incrementBy; } diff --git a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByHTMLElementAction.java b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByHTMLElementAction.java index 3eaefd8a0..30e603702 100644 --- a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByHTMLElementAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByHTMLElementAction.java @@ -18,6 +18,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.SymbolAction; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.ConnectorManager; import de.learnlib.alex.core.learner.connectors.VariableStoreConnector; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; @@ -25,10 +27,13 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; +import org.hibernate.validator.constraints.NotBlank; import org.openqa.selenium.NoSuchElementException; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; +import javax.validation.constraints.NotNull; /** * Action to set a variable to a value received from an element of the DOM tree. @@ -36,7 +41,7 @@ @Entity @DiscriminatorValue("setVariableByHTML") @JsonTypeName("setVariableByHTML") -public class SetVariableByHTMLElementAction extends SetVariableAction { +public class SetVariableByHTMLElementAction extends SymbolAction { private static final long serialVersionUID = -7654754471208209824L; @@ -44,24 +49,53 @@ public class SetVariableByHTMLElementAction extends SetVariableAction { private static final Marker LEARNER_MARKER = MarkerManager.getMarker("LEARNER"); + /** The name of the variable. */ + @NotBlank + protected String name; + + /** The locator of the element. */ + @NotNull + @Embedded + private WebElementLocator node; + @Override public ExecuteResult execute(ConnectorManager connector) { VariableStoreConnector storeConnector = connector.getConnector(VariableStoreConnector.class); WebSiteConnector webSiteConnector = connector.getConnector(WebSiteConnector.class); try { - String valueInTheNode = webSiteConnector.getElement(value).getText(); + String valueInTheNode = webSiteConnector.getElement(node).getText(); storeConnector.set(name, valueInTheNode); LOGGER.info(LEARNER_MARKER, "Set the variable '{}' to the value '{}' of the HTML node '{}' " + "(ignoreFailure: {}, negated: {}).", - name, valueInTheNode, value, ignoreFailure, negated); + name, valueInTheNode, node, ignoreFailure, negated); return getSuccessOutput(); } catch (NoSuchElementException e) { LOGGER.info(LEARNER_MARKER, "Could not set the variable '{}' to the value of the HTML node '{}' " + "(ignoreFailure: {}, negated: {}).", - name, value, ignoreFailure, negated); + name, node, ignoreFailure, negated); return getFailedOutput(); } } + + /** @return {@link #name}. */ + public String getName() { + return name; + } + + /** @param name {@link #name}. */ + public void setName(String name) { + this.name = name; + } + + /** @return {@link #node}. */ + public WebElementLocator getNode() { + return node; + } + + /** @param node {@link #node}. */ + public void setNode(WebElementLocator node) { + this.node = node; + } } diff --git a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByNodeAttributeAction.java b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByNodeAttributeAction.java index a34cb8b93..4d25cc265 100644 --- a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByNodeAttributeAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByNodeAttributeAction.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; import de.learnlib.alex.core.entities.SymbolAction; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.ConnectorManager; import de.learnlib.alex.core.learner.connectors.VariableStoreConnector; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; @@ -29,8 +30,8 @@ import org.hibernate.validator.constraints.NotBlank; import org.openqa.selenium.NoSuchElementException; -import javax.persistence.Column; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; import javax.validation.constraints.NotNull; @@ -54,8 +55,8 @@ public class SetVariableByNodeAttributeAction extends SymbolAction { /** The node to look for. */ @NotNull - @Column(columnDefinition = "CLOB") - protected String node; + @Embedded + protected WebElementLocator node; /** The attribute name of the node to look for. */ @NotNull @@ -78,14 +79,14 @@ public void setName(String name) { /** * @return The node to get the attribute from. */ - public String getNode() { + public WebElementLocator getNode() { return node; } /** * @param node The new identifier for the node to get the attribute from. */ - public void setNode(String node) { + public void setNode(WebElementLocator node) { this.node = node; } diff --git a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByNodeCountAction.java b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByNodeCountAction.java index 616e289bd..eef1fc630 100644 --- a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByNodeCountAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByNodeCountAction.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; import de.learnlib.alex.core.entities.SymbolAction; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.ConnectorManager; import de.learnlib.alex.core.learner.connectors.VariableStoreConnector; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; @@ -27,10 +28,10 @@ import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; import org.hibernate.validator.constraints.NotBlank; -import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; import javax.validation.constraints.NotNull; @@ -54,21 +55,22 @@ public class SetVariableByNodeCountAction extends SymbolAction { /** The selector of the elements. */ @NotNull - private String selector; + @Embedded + private WebElementLocator node; @Override protected ExecuteResult execute(ConnectorManager connector) { int nodeCount = 0; + node.setSelector(insertVariableValues(node.getSelector())); try { nodeCount = connector.getConnector(WebSiteConnector.class) - .getDriver() - .findElements(By.cssSelector(insertVariableValues(this.selector))) + .getElements(node) .size(); } catch (NoSuchElementException e) { LOGGER.info(LEARNER_MARKER, "Could not find elements with the selector '{}' " + "(ignoreFailure: {}, negated: {}).", - selector, ignoreFailure, negated); + node, ignoreFailure, negated); } connector.getConnector(VariableStoreConnector.class) @@ -77,19 +79,23 @@ protected ExecuteResult execute(ConnectorManager connector) { return getSuccessOutput(); } + /** @return {@link #name}. */ public String getName() { return name; } + /** @param name {@link #name}. */ public void setName(String name) { this.name = name; } - public String getSelector() { - return selector; + /** @return {@link #node}. */ + public WebElementLocator getNode() { + return node; } - public void setSelector(String selector) { - this.selector = selector; + /** @param node {@link #node}. */ + public void setNode(WebElementLocator node) { + this.node = node; } } diff --git a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByRegexGroup.java b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByRegexGroup.java index 2b9a801cd..d008f5feb 100644 --- a/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByRegexGroup.java +++ b/main/src/main/java/de/learnlib/alex/actions/StoreSymbolActions/SetVariableByRegexGroup.java @@ -97,13 +97,13 @@ protected ExecuteResult execute(ConnectorManager connector) { } if (!matchFound) { - LOGGER.info(LEARNER_MARKER, "Could not find a string that matches regex '{}' " + - "(ignoreFailure: {}, negated: {})", regex, ignoreFailure, negated); + LOGGER.info(LEARNER_MARKER, "Could not find a string that matches regex '{}' " + + "(ignoreFailure: {}, negated: {})", regex, ignoreFailure, negated); return getFailedOutput(); } } catch (IndexOutOfBoundsException e) { - LOGGER.info(LEARNER_MARKER, "Could not find group {} in regex '{}' " + - "(ignoreFailure: {}, negated: {})", mthGroup, regex, ignoreFailure, negated); + LOGGER.info(LEARNER_MARKER, "Could not find group {} in regex '{}' " + + "(ignoreFailure: {}, negated: {})", mthGroup, regex, ignoreFailure, negated); return getFailedOutput(); } diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/CheckNodeAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/CheckNodeAction.java index a31a38c41..46a63b54f 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/CheckNodeAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/CheckNodeAction.java @@ -16,20 +16,20 @@ package de.learnlib.alex.actions.WebSymbolActions; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; -import de.learnlib.alex.utils.CSSUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import org.hibernate.validator.constraints.NotBlank; import org.openqa.selenium.NoSuchElementException; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; +import javax.validation.constraints.NotNull; /** * Action to check for a specific element/ a specific text. @@ -45,51 +45,33 @@ public class CheckNodeAction extends WebSymbolAction { private static final Marker LEARNER_MARKER = MarkerManager.getMarker("LEARNER"); - /** The value the site is checked for. */ - @NotBlank - private String value; + /** The node on the site that is checked for. */ + @NotNull + @Embedded + private WebElementLocator node; - /** - * Get the value to check. - * - * @return The value to check. - */ - public String getValue() { - return value; + /** @return {@link #node}. */ + public WebElementLocator getNode() { + return node; } - /** - * Get the value to check. - * All variables and counters will be replaced with their values. - * - * @return The value to check. - */ - @JsonIgnore - public String getValueWithVariableValues() { - return insertVariableValues(value); - } - - /** - * Set the value to check for. - * - * @param value - * The new value. - */ - public void setValue(String value) { - this.value = value; + /** @param node {@link #node}. */ + public void setNode(WebElementLocator node) { + this.node = node; } @Override public ExecuteResult execute(WebSiteConnector connector) { try { - connector.getElement(CSSUtils.escapeSelector(getValueWithVariableValues())); + node.setSelector(insertVariableValues(node.getSelector())); + connector.getElement(node); LOGGER.info(LEARNER_MARKER, "Found the node '{}' (ignoreFailure: {}, negated: {}).", - value, ignoreFailure, negated); + node, ignoreFailure, negated); return getSuccessOutput(); } catch (NoSuchElementException e) { LOGGER.info(LEARNER_MARKER, "Could not find the node '{}' (ignoreFailure: {}, negated: {}).", - value, ignoreFailure, negated, e); + node, ignoreFailure, negated, e); return getFailedOutput(); } } diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/CheckNodeAttributeValueAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/CheckNodeAttributeValueAction.java index 5b068ac28..8557b19be 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/CheckNodeAttributeValueAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/CheckNodeAttributeValueAction.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -29,6 +30,7 @@ import org.openqa.selenium.WebElement; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; import javax.validation.constraints.NotNull; @@ -87,8 +89,9 @@ public String toString() { /** * The selector of the element. */ - @NotBlank - private String node; + @NotNull + @Embedded + private WebElementLocator node; /** * The attribute name of the element to check. @@ -111,7 +114,8 @@ public String toString() { @Override protected ExecuteResult execute(WebSiteConnector connector) { try { - WebElement element = connector.getElement(insertVariableValues(node)); + node.setSelector(insertVariableValues(node.getSelector())); + WebElement element = connector.getElement(node); String attributeValue = element.getAttribute(attribute); if (attributeValue == null) { LOGGER.info(LEARNER_MARKER, "Attribute '{}' not found on element '{}'", @@ -155,14 +159,14 @@ protected ExecuteResult execute(WebSiteConnector connector) { /** * @return The selector of the element. */ - public String getNode() { + public WebElementLocator getNode() { return node; } /** * @param node The selector of the element. */ - public void setNode(String node) { + public void setNode(WebElementLocator node) { this.node = node; } diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/ClearAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/ClearAction.java index 813eadf0d..7fa4328c1 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/ClearAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/ClearAction.java @@ -16,21 +16,20 @@ package de.learnlib.alex.actions.WebSymbolActions; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; -import de.learnlib.alex.utils.CSSUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import org.hibernate.validator.constraints.NotBlank; import org.openqa.selenium.NoSuchElementException; -import javax.persistence.Column; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; +import javax.validation.constraints.NotNull; /** * Action to clear on a specific element. @@ -47,44 +46,34 @@ public class ClearAction extends WebSymbolAction { private static final Marker LEARNER_MARKER = MarkerManager.getMarker("LEARNER"); /** The node to look for. */ - @NotBlank - @Column(columnDefinition = "CLOB") - private String node; + @NotNull + @Embedded + private WebElementLocator node; /** * Get the node to look for. * * @return The node to look for. */ - public String getNode() { + public WebElementLocator getNode() { return node; } - /** - * Get the node to look for. - * All variables and counters will be replaced with their values. - * - * @return The node to look for. - */ - @JsonIgnore - public String getNodeWithVariableValues() { - return insertVariableValues(node); - } - /** * Set the node to check for. * * @param node * The new node to check for. */ - public void setNode(String node) { + public void setNode(WebElementLocator node) { this.node = node; } @Override public ExecuteResult execute(WebSiteConnector connector) { try { - connector.getElement(CSSUtils.escapeSelector(getNodeWithVariableValues())).clear(); + node.setSelector(insertVariableValues(node.getSelector())); + connector.getElement(node).clear(); LOGGER.info(LEARNER_MARKER, "Cleared the element '{}' (ignoreFailure: {}, negated: {}).", node, ignoreFailure, negated); diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/ClickAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/ClickAction.java index 6cb8b1899..52d92ea16 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/ClickAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/ClickAction.java @@ -16,22 +16,20 @@ package de.learnlib.alex.actions.WebSymbolActions; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; -import de.learnlib.alex.utils.CSSUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import org.hibernate.validator.constraints.NotBlank; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; -import javax.persistence.Column; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; import javax.validation.constraints.NotNull; @@ -52,9 +50,9 @@ public class ClickAction extends WebSymbolAction { /** * The information to identify the element. */ - @NotBlank - @Column(columnDefinition = "CLOB") - private String node; + @NotNull + @Embedded + private WebElementLocator node; /** * If a double click is executed. @@ -67,21 +65,10 @@ public class ClickAction extends WebSymbolAction { * * @return The element identifier. */ - public String getNode() { + public WebElementLocator getNode() { return node; } - /** - * Get the node to look for. - * All variables and counters will be replaced with their values. - * - * @return The node to look for. - */ - @JsonIgnore - public String getNodeWithVariableValues() { - return insertVariableValues(node); - } - /** * If a double click should be executed. * @@ -105,14 +92,16 @@ public void setDoubleClick(boolean doubleClick) { * * @param node The new element identifier. */ - public void setNode(String node) { + public void setNode(WebElementLocator node) { this.node = node; } @Override public ExecuteResult execute(WebSiteConnector connector) { try { - WebElement element = connector.getElement(CSSUtils.escapeSelector(getNodeWithVariableValues())); + node.setSelector(insertVariableValues(node.getSelector())); + WebElement element = connector.getElement(node); + if (doubleClick) { new Actions(connector.getDriver()).doubleClick(element).build().perform(); } else { diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/FillAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/FillAction.java index a51c2cda9..080b2a9e0 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/FillAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/FillAction.java @@ -16,11 +16,10 @@ package de.learnlib.alex.actions.WebSymbolActions; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; -import de.learnlib.alex.utils.CSSUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; @@ -29,9 +28,10 @@ import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; -import javax.persistence.Column; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; +import javax.validation.constraints.NotNull; /** * Action to enter a text into a specific element. @@ -49,15 +49,13 @@ public class FillAction extends WebSymbolAction { /** * The node to look for. - * @requiredField */ - @NotBlank - @Column(columnDefinition = "CLOB") - protected String node; + @NotNull + @Embedded + protected WebElementLocator node; /** * The Value to insert. - * @requiredField */ @NotBlank protected String value; @@ -67,28 +65,17 @@ public class FillAction extends WebSymbolAction { * * @return The node to look for. */ - public String getNode() { + public WebElementLocator getNode() { return node; } - /** - * Get the node to look for. - * All variables and counters will be replaced with their values. - * - * @return The node to look for. - */ - @JsonIgnore - public String getNodeWithVariableValues() { - return insertVariableValues(node); - } - /** * Set the node to check for. * * @param node * The new node to check for. */ - public void setNode(String node) { + public void setNode(WebElementLocator node) { this.node = node; } @@ -101,17 +88,6 @@ public String getValue() { return value; } - /** - * Get the value used to fill the element. - * All variables and counters will be replaced with their values. - * - * @return The value. - */ - @JsonIgnore - public String getValueWithVariableValues() { - return insertVariableValues(value); - } - /** * Set the value to be used when filling the element. * @@ -124,10 +100,10 @@ public void setValue(String value) { @Override public ExecuteResult execute(WebSiteConnector connector) { - String nodeWithVariables = getNodeWithVariableValues(); - String valueWithVariables = getValueWithVariableValues(); + node.setSelector(insertVariableValues(node.getSelector())); + String valueWithVariables = insertVariableValues(value); try { - WebElement element = connector.getElement(CSSUtils.escapeSelector(nodeWithVariables)); + WebElement element = connector.getElement(node); element.clear(); element.sendKeys(valueWithVariables); @@ -137,7 +113,7 @@ public ExecuteResult execute(WebSiteConnector connector) { } catch (NoSuchElementException e) { LOGGER.info(LEARNER_MARKER, "Could not find the element '{}' to fill it with '{}' " + "(ignoreFailure: {}, negated: {}).", - nodeWithVariables, valueWithVariables, ignoreFailure, negated, e); + node, valueWithVariables, ignoreFailure, negated, e); return getFailedOutput(); } } diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/MoveMouseAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/MoveMouseAction.java index 590b363c4..6ffc7ff27 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/MoveMouseAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/MoveMouseAction.java @@ -18,8 +18,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; -import de.learnlib.alex.utils.CSSUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; @@ -28,8 +28,8 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; -import javax.persistence.Column; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; @@ -52,8 +52,8 @@ public class MoveMouseAction extends WebSymbolAction { /** * The selector of the element. */ - @Column(columnDefinition = "CLOB") - private String node; + @Embedded + private WebElementLocator node; /** * The amount in px to move the mouse in x direction from the current position. @@ -92,7 +92,7 @@ public void setOffsetY(int offsetY) { * * @return the node */ - public String getNode() { + public WebElementLocator getNode() { return node; } @@ -101,7 +101,7 @@ public String getNode() { * * @param node the node */ - public void setNode(String node) { + public void setNode(WebElementLocator node) { this.node = node; } @@ -129,13 +129,14 @@ protected ExecuteResult execute(WebSiteConnector connector) { try { Actions actions = new Actions(connector.getDriver()); - if (node == null || node.trim().equals("")) { + if (node == null || node.getSelector().trim().equals("")) { actions.moveByOffset(offsetX, offsetY).build().perform(); LOGGER.info(LEARNER_MARKER, "Moved the mouse to the position ({}, {}) " + "(ignoreFailure: {}, negated: {}).", offsetX, offsetY, ignoreFailure, negated); } else { - WebElement element = connector.getElement(CSSUtils.escapeSelector(insertVariableValues(node))); + node.setSelector(insertVariableValues(node.getSelector())); + WebElement element = connector.getElement(node); actions.moveToElement(element, offsetX, offsetY).build().perform(); LOGGER.info(LEARNER_MARKER, "Moved the mouse to the element '{}' " + "(ignoreFailure: {}, negated: {}).", diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/PressKeyAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/PressKeyAction.java index c0813b221..7a6129f70 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/PressKeyAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/PressKeyAction.java @@ -18,8 +18,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; -import de.learnlib.alex.utils.CSSUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -30,8 +30,8 @@ import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; -import javax.persistence.Column; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; import javax.validation.constraints.NotNull; @@ -52,9 +52,9 @@ public class PressKeyAction extends WebSymbolAction { /** * The selector of the element. */ - @Column(columnDefinition = "CLOB") @NotNull - private String node; + @Embedded + private WebElementLocator node; /** * The escaped string representation of the unicode that represents the key. @@ -68,7 +68,8 @@ protected ExecuteResult execute(WebSiteConnector connector) { Keys keyToPress = Keys.getKeyFromUnicode(unescapedKey.toCharArray()[0]); try { - WebElement element = connector.getElement(CSSUtils.escapeSelector(insertVariableValues(node))); + node.setSelector(insertVariableValues(node.getSelector())); + WebElement element = connector.getElement(node); element.sendKeys(keyToPress); LOGGER.info(LEARNER_MARKER, "Pressed the key '{}' on the element '{}' (ignoreFailure: {}, negated: {}).", keyToPress.toString(), node, ignoreFailure, negated); @@ -83,14 +84,14 @@ protected ExecuteResult execute(WebSiteConnector connector) { /** * @return The node. */ - public String getNode() { + public WebElementLocator getNode() { return node; } /** * @param node The node. */ - public void setNode(String node) { + public void setNode(WebElementLocator node) { this.node = node; } diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/SelectAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/SelectAction.java index 73856a666..c285f8101 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/SelectAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/SelectAction.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; -import de.learnlib.alex.utils.CSSUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; @@ -103,14 +102,17 @@ public void setSelectBy(SelectByType selectBy) { @Override public ExecuteResult execute(WebSiteConnector connector) { try { - WebElement selectElement = connector.getElement(CSSUtils.escapeSelector(getNodeWithVariableValues())); + String valueWithVariables = insertVariableValues(value); + node.setSelector(insertVariableValues(node.getSelector())); + + WebElement selectElement = connector.getElement(node); Select select = new Select(selectElement); switch (selectBy) { case VALUE: - select.selectByValue(getValueWithVariableValues()); + select.selectByValue(valueWithVariables); break; case TEXT: - select.selectByVisibleText(getValueWithVariableValues()); + select.selectByVisibleText(valueWithVariables); break; case INDEX: select.selectByIndex(Integer.parseInt(getValue())); diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/SubmitAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/SubmitAction.java index c7586ab9f..60a42d535 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/SubmitAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/SubmitAction.java @@ -16,21 +16,20 @@ package de.learnlib.alex.actions.WebSymbolActions; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; -import de.learnlib.alex.utils.CSSUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import org.hibernate.validator.constraints.NotBlank; import org.openqa.selenium.NoSuchElementException; -import javax.persistence.Column; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; +import javax.validation.constraints.NotNull; /** * Action to submit a specific element. @@ -47,44 +46,34 @@ public class SubmitAction extends WebSymbolAction { private static final Marker LEARNER_MARKER = MarkerManager.getMarker("LEARNER"); /** The information to identify the element. */ - @NotBlank - @Column(columnDefinition = "CLOB") - private String node; + @NotNull + @Embedded + private WebElementLocator node; /** * Get the node to look for. * * @return The node to look for. */ - public String getNode() { + public WebElementLocator getNode() { return node; } - /** - * Get the node to look for. - * All variables and counters will be replaced with their values. - * - * @return The node to look for. - */ - @JsonIgnore - public String getNodeWithVariableValues() { - return insertVariableValues(node); - } - /** * Set the node to check for. * * @param node * The new node to check for. */ - public void setNode(String node) { + public void setNode(WebElementLocator node) { this.node = node; } @Override public ExecuteResult execute(WebSiteConnector connector) { try { - connector.getElement(CSSUtils.escapeSelector(getNodeWithVariableValues())).submit(); + node.setSelector(insertVariableValues(node.getSelector())); + connector.getElement(node).submit(); LOGGER.info(LEARNER_MARKER, "Submitted '{}' (ignoreFailure: {}, negated: {}).", node, ignoreFailure, negated); diff --git a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/WaitForNodeAction.java b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/WaitForNodeAction.java index 216b42e56..3c4049e3b 100644 --- a/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/WaitForNodeAction.java +++ b/main/src/main/java/de/learnlib/alex/actions/WebSymbolActions/WaitForNodeAction.java @@ -3,21 +3,19 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeName; import de.learnlib.alex.core.entities.ExecuteResult; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.connectors.WebSiteConnector; -import de.learnlib.alex.utils.CSSUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import org.hibernate.validator.constraints.NotBlank; -import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; -import javax.persistence.Column; import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; import javax.persistence.Entity; import javax.validation.constraints.NotNull; @@ -88,9 +86,9 @@ public String toString() { /** * The css selector of the element. */ - @NotBlank - @Column(columnDefinition = "CLOB") - private String node; + @NotNull + @Embedded + private WebElementLocator node; /** * Which criterion is used to wait for the title. @@ -109,7 +107,7 @@ public String toString() { * * @return The selector of the element */ - public String getNode() { + public WebElementLocator getNode() { return node; } @@ -118,7 +116,7 @@ public String getNode() { * * @param node The selector of the element */ - public void setNode(String node) { + public void setNode(WebElementLocator node) { this.node = node; } @@ -165,24 +163,24 @@ protected ExecuteResult execute(WebSiteConnector connector) { } WebDriverWait wait = new WebDriverWait(connector.getDriver(), maxWaitTime); - String selector = CSSUtils.escapeSelector(insertVariableValues(node)); + node.setSelector(insertVariableValues(node.getSelector())); try { switch (waitCriterion) { case VISIBLE: - wait.until(ExpectedConditions.visibilityOf(connector.getElement(selector))); + wait.until(ExpectedConditions.visibilityOf(connector.getElement(node))); break; case INVISIBLE: - wait.until(ExpectedConditions.invisibilityOfElementLocated(By.cssSelector(selector))); + wait.until(ExpectedConditions.invisibilityOfElementLocated(node.getBy())); break; case ADDED: - wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(selector))); + wait.until(ExpectedConditions.presenceOfElementLocated(node.getBy())); break; case REMOVED: - wait.until(ExpectedConditions.stalenessOf(connector.getElement(selector))); + wait.until(ExpectedConditions.stalenessOf(connector.getElement(node))); break; case CLICKABLE: - wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector(selector))); + wait.until(ExpectedConditions.elementToBeClickable(node.getBy())); break; default: return getFailedOutput(); @@ -190,11 +188,11 @@ protected ExecuteResult execute(WebSiteConnector connector) { return getSuccessOutput(); } catch (TimeoutException e) { LOGGER.info(LEARNER_MARKER, "Waiting on the node '{}' (criterion: '{}') timed out.", - selector, waitCriterion); + node, waitCriterion); return getFailedOutput(); } catch (NoSuchElementException e) { LOGGER.info(LEARNER_MARKER, "The node with the selector {} (criterion: '{}') could not be found.", - selector, waitCriterion); + node, waitCriterion); return getFailedOutput(); } } diff --git a/main/src/main/java/de/learnlib/alex/core/entities/WebElementLocator.java b/main/src/main/java/de/learnlib/alex/core/entities/WebElementLocator.java new file mode 100644 index 000000000..af1262350 --- /dev/null +++ b/main/src/main/java/de/learnlib/alex/core/entities/WebElementLocator.java @@ -0,0 +1,105 @@ +/* + * Copyright 2016 TU Dortmund + * + * Licensed 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. + */ + +package de.learnlib.alex.core.entities; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import de.learnlib.alex.utils.CSSUtils; +import org.hibernate.validator.constraints.NotBlank; +import org.openqa.selenium.By; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * Embeddable class that helps working with selenium web elements. + */ +@Embeddable +@JsonPropertyOrder(alphabetic = true) +public class WebElementLocator implements Serializable { + + private static final long serialVersionUID = -6070241271039096113L; + + /** The type of selectors. */ + public enum Type { + CSS, XPATH + } + + /** The selector of the element[s]. */ + @NotBlank + @Column(columnDefinition = "CLOB") + private String selector; + + /** What kind of selector {@link #selector} is. */ + @NotNull + @Column(name = "selectorType") + private Type type; + + @JsonIgnore + public By getBy() { + if (type.equals(Type.CSS)) { + return By.cssSelector(CSSUtils.escapeSelector(selector)); + } else { + return By.xpath(selector); + } + } + + /** @return {@link #selector}. */ + public String getSelector() { + return selector; + } + + /** @param selector {@link #selector}. */ + public void setSelector(String selector) { + this.selector = selector; + } + + /** @return {@link #type}. */ + public Type getType() { + return type; + } + + /** @param type {@link #selector}. */ + public void setType(Type type) { + this.type = type; + } + + @Override + public String toString() { + return selector + "(" + type.toString() + ")"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + WebElementLocator that = (WebElementLocator) o; + + if (selector != null ? !selector.equals(that.selector) : that.selector != null) return false; + return type == that.type; + } + + @Override + public int hashCode() { + int result = selector != null ? selector.hashCode() : 0; + result = 31 * result + (type != null ? type.hashCode() : 0); + return result; + } +} diff --git a/main/src/main/java/de/learnlib/alex/core/learner/connectors/WebSiteConnector.java b/main/src/main/java/de/learnlib/alex/core/learner/connectors/WebSiteConnector.java index 25dea207c..c4f887e13 100644 --- a/main/src/main/java/de/learnlib/alex/core/learner/connectors/WebSiteConnector.java +++ b/main/src/main/java/de/learnlib/alex/core/learner/connectors/WebSiteConnector.java @@ -16,13 +16,18 @@ package de.learnlib.alex.core.learner.connectors; +import com.gargoylesoftware.htmlunit.javascript.host.css.CSS; import de.learnlib.alex.actions.Credentials; import de.learnlib.alex.core.entities.BrowserConfig; +import de.learnlib.alex.core.entities.WebElementLocator; import de.learnlib.alex.core.learner.BaseUrlManager; +import de.learnlib.alex.utils.CSSUtils; import org.openqa.selenium.*; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.WebDriverWait; +import java.util.List; + /** * Connector to communicate with a WebSite. * This is a facade around Seleniums {@link WebDriver}. @@ -87,14 +92,35 @@ public void get(String path, Credentials credentials) { /** * Get a {@link WebElement} from the site using a valid css query. * - * @param query + * @param locator * The query to search for the element. * @return A WebElement. * @throws NoSuchElementException - * If no Element was found. + * If no element was found. + */ + public WebElement getElement(WebElementLocator locator) throws NoSuchElementException { + if (locator.getType().equals(WebElementLocator.Type.CSS)) { + return driver.findElement(By.cssSelector(CSSUtils.escapeSelector(locator.getSelector()))); + } else { + return driver.findElement(By.xpath(locator.getSelector())); + } + } + + /** + * Same as {@link #getElement(WebElementLocator)} but returns a list of web elements. + * + * @param locator + * The query to search for the element. + * @return A list of elements. + * @throws NoSuchElementException + * If no element was found. */ - public WebElement getElement(String query) throws NoSuchElementException { - return driver.findElement(By.cssSelector(query)); + public List getElements(WebElementLocator locator) throws NoSuchElementException { + if (locator.getType().equals(WebElementLocator.Type.CSS)) { + return driver.findElements(By.cssSelector(CSSUtils.escapeSelector(locator.getSelector()))); + } else { + return driver.findElements(By.xpath(locator.getSelector())); + } } /** diff --git a/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-html.html b/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-html.html index 3e9f2d29c..e18efba8a 100644 --- a/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-html.html +++ b/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-html.html @@ -4,18 +4,10 @@

Set Variable By Node Value

Set the value of a variable to the content of a HTML element


-
- + +
- -
- -
- - -   WebPicker - \ No newline at end of file + \ No newline at end of file diff --git a/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-node-attribute.html b/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-node-attribute.html index 2a1f9fefa..1734320c6 100644 --- a/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-node-attribute.html +++ b/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-node-attribute.html @@ -4,26 +4,15 @@

Set Variable by Node Attribute

Set a variable to the value of an attribute of an HTML element


-
- + +
- -
- -
- - - - +
+
\ No newline at end of file diff --git a/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-node-count.html b/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-node-count.html index eca94c029..584de9a84 100644 --- a/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-node-count.html +++ b/main/src/main/webapp/src/html/components/forms/actions/general/set-variable-by-node-count.html @@ -4,20 +4,10 @@

Set Variable by Node Count

Set a variable to the amount of nodes that match a given selector.


-
- + +
- -
- -
- - \ No newline at end of file + \ No newline at end of file diff --git a/main/src/main/webapp/src/html/components/forms/actions/web/check-for-node.html b/main/src/main/webapp/src/html/components/forms/actions/web/check-for-node.html index 960644a34..87f910911 100644 --- a/main/src/main/webapp/src/html/components/forms/actions/web/check-for-node.html +++ b/main/src/main/webapp/src/html/components/forms/actions/web/check-for-node.html @@ -5,11 +5,4 @@

Search for Node


-
- - -
- - -   Element Picker - \ No newline at end of file + \ No newline at end of file diff --git a/main/src/main/webapp/src/html/components/forms/actions/web/check-node-attribute-value.html b/main/src/main/webapp/src/html/components/forms/actions/web/check-node-attribute-value.html index 1239b03c3..2070387ca 100644 --- a/main/src/main/webapp/src/html/components/forms/actions/web/check-node-attribute-value.html +++ b/main/src/main/webapp/src/html/components/forms/actions/web/check-node-attribute-value.html @@ -5,16 +5,7 @@

Check Node Attribute


-
- - -
- - +
diff --git a/main/src/main/webapp/src/html/components/forms/actions/web/clear-input.html b/main/src/main/webapp/src/html/components/forms/actions/web/clear-input.html index 90f170c38..3f78521b0 100644 --- a/main/src/main/webapp/src/html/components/forms/actions/web/clear-input.html +++ b/main/src/main/webapp/src/html/components/forms/actions/web/clear-input.html @@ -7,11 +7,4 @@


-
- - -
- - -   Element Picker - \ No newline at end of file + \ No newline at end of file diff --git a/main/src/main/webapp/src/html/components/forms/actions/web/click.html b/main/src/main/webapp/src/html/components/forms/actions/web/click.html index 6ce748652..c6b389654 100644 --- a/main/src/main/webapp/src/html/components/forms/actions/web/click.html +++ b/main/src/main/webapp/src/html/components/forms/actions/web/click.html @@ -7,14 +7,7 @@


-
- - -
- - -   Element Picker - +