From 189ab6a91adeedf014f84e85b1efa3fd91347728 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Thu, 4 Jul 2019 20:08:29 +0200 Subject: [PATCH 01/21] Try to switch to Gravis --- .travis.yml | 77 +++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5bc19d7d18..9249902a22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,11 @@ -os: - - linux - - osx -dist: xenial -language: java -jdk: - - openjdk11 - - openjdk12 -# To be enabled when the official version is out -# - openjdk13 +language: bash git: depth: false -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - +os: + - osx + - linux + - windows +dist: xenial env: global: - secure: "OMDTeFBKjw0acBlmqh4+FZ5nnt/Aj2pF705Tta0qm3Sf61S+k1u503Dzt+nDs+/yY19HyyDFUj3+56a88IssEjd3xUVzPOz7kCW0p/lcqbAQ8jIATmoWpoMM9cOI4Xv9h+JJxOqLR54ODR0MGo/BVa51w93jsdpDnrlLNOoNf08r1bfA2Hifh/iKD/YzMyGqFI+vxvED7HhbDNG48chsr3QSGxsizXDtEZK+u0JZ6/CR2rHZTQzD6bkv3N2Vbq/4tc4pZVdH7zMXWRkrGunhHchwmgEwz90iJeo4fEeMBiWpchWg7Huxr2LUisdRKjzNqf+UjV5DljtK/RDbzyjC8vIAllD2hsmPJW1EfTOrIfIVgT2CoRrKKGIp/Dk5LkOTePMCXloe+M7gYipEWreAXeGYotAOdnxW1IoxhM3Dsbb4yxd6h1h2bFXToj0bzIEMySgivYJ0n0PhvuwWb/KUf2vt8r+ULdWvPVjgqQUDv38fgnqyfMmgMDfABlGpSxQWT2gy5VAinpgQzfNrVeOkDxxGDBLa4vOMy/yQm/bnXJyyUA0NAv5xQBCZhsDqjTOwzKWGFWkdrN0e91JjF7+bvkKnH1JQhz8yuLdLASm5LEj5nZbCZeDu4jbZhags1pkmNxwlPJjWfpzeXiiXwMdAkIn1RrkMefHPfJ5hq+G3y9I=" @@ -29,48 +17,55 @@ env: - secure: "YkhodE9pmi+NooHmAdVWFaDaVtWZv2y5qaKhHqJoMIDhdaaS4f0t5Fn9S48ndPh0VlAboD5oF8bR2BwotEJNOoI5bsA9TzlIxIFaK+yS2UrkMEpC5BswmfyidY6H8wJZPFbaGxLXuw77aPE5G0QhTO4kKjpUxJSG2Yp0y9uEOeY8FZMjSsV7i4+2XllCXMG3jc8N7UC9vvAqYA5TwzL/CyyBdrRVO7QFzb+F1V5464ha5JJlAAvrNZDHq+g0NZvBsWLAe9VgGNbDms7q7BC1ZXOHM9do86i/Ca4AHndJwl5KdipyscNdgz1IFgTHJaMB5Xqhh8lmh/1rZX/5oeChigxuLB6mOcgcqmcI8Ye/6EXSA0Y33nmfKoXmG8+EBXOPYtD8iQSJlq1hTHdwZOleUBKaDVkn57MHvdiOMH/De2kqHiBGJRpsnBtu/zK7TTocZOgis7MmeCf8FPLJ0IL69pXYlXHZeO5OC890s7AkZgWgHsctozjdJK0t6dpWNO3HP2ZGb+EysbeVjV7eSF5WBtXhAnm1RIHZRdZolCCj5zpQev6t4ULcsD171zWdvnBQ8cKWl9JxlqSaaZx9c4zp+b/atMMBpvTrIOddvCRurPVXbXR8X+KT9wExT0Sfe3RE8OIN99DU1fWXDGV6J6t0Lzveenwr47hm/2IfrjTaXls=" - secure: "C7PDJgxvoSS8x5rLpWpBmiaCoOe6D4m4em09tiV0+jBuhpv1b4h7hd2t5HbYhlN3GOomaFO86cHa6Qm7TJtexWbUFy/2/mF+oYeNsHeLu/Gv6t5k3jACLdE4ec/1Fm8Muw9yYOqDmjITY3OKBsotqcbyL6+5vw4AqvZ2jKQz8CbEVyp0vZ8OUqAJB19EwmDaUvPI051l5jiqeunLML7ThLQr+o5PmwA7pc307DiA1JfRakJvPkgmTmtjwfRUR/d0F1tC9ekrSH/8q8SkWhqIVhFQk2AGnwzOZE0Q948ieufMVhbT1WV2aitJCRv2HzpQe9Z57L58yfx59bwRfiUGMQCloY9rR/+IT9P/DdQhWbw6yj0qzEVTTZzguDANL3mUycIcXwYLdphDZzq6Cw46EwJvSY0FVJthsrfjYi8UD01zkcalvMy/ADhvpDSm/grJiFzrBLBQcMVgDBm4TB+3JTj4vTnzSZHNhubiFJ5daxxQ7Uhpi2bJKdNqGAjyxbm+fhdNWza7K1FGB5jns9UCI7bAo/RkztTyqELRqg6RHnIRz7Ta57ZeF8WEky9ziyr1LNEeBRY/qEsAtUpoAv9fmSFat2bWXkxPmZcF2DxAhYqnAPOh8dj9QbCQEBjMfgnVOt2u48ePkeh1vlV1XU7k2cyCFQnqMJpnQK5KVwdrqac=" - TERM=dumb - matrix: + - GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/" - PUBLISH="false" + matrix: + - JDK="adopt@1.11.28-0" + - JDK="adopt-openj9@1.11.28-0" + - JDK="adopt@1.12.33-0" + - JDK="1.12.0-1" matrix: include: - - jdk: openjdk11 - env: PUBLISH="true" - os: linux + - os: linux + env: PUBLISH="true" JDK="adopt@1.11.28-0" exclude: - - jdk: openjdk11 - env: PUBLISH="false" - os: linux - -before_install: >- + - os: linux + env: JDK="adopt@1.8.212-04" +before_install: |- + curl "${GRAVIS}.install-jdk-travis.sh" --output .install-jdk-travis.sh + bash .install-jdk-travis.sh if [ $TRAVIS_OS_NAME = 'linux' ]; then - sudo apt-get -qq update; - sudo apt-get install -y graphviz openjfx; + sudo apt-get -qq update + sudo apt-get install -y graphviz openjfx export SHOULD_CONFIGURE="$(if [ $TRAVIS_PULL_REQUEST = "false" ] && [ $TRAVIS_REPO_SLUG = 'AlchemistSimulator/Alchemist' ]; then echo 'true'; else echo 'false'; fi)" else export SHOULD_CONFIGURE="$(echo 'false')" fi - install: >- if $SHOULD_CONFIGURE; then openssl aes-256-cbc -K $encrypted_9993846e3f84_key -iv $encrypted_9993846e3f84_iv -in prepare_environment.sh.enc -out prepare_environment.sh -d bash prepare_environment.sh fi - -script: >- - cd alchemist; - if [ $TRAVIS_OS_NAME = "windows" ]; then - gradlew.bat clean check fatJar dokka buildDashboard projectReport --scan --stacktrace - else - ./gradlew clean check fatJar dokka buildDashboard projectReport --scan --stacktrace > >(egrep -v '(null:-1:-1)|(t find node by signature)') - fi - -after_success: >- +before_script: + - "if [ -f ~/.windows_config ]; then source ~/.windows_config; fi" +script: |- + cd "$TRAVIS_BUILD_DIR"/alchemist + ./gradlew clean check fatJar dokka buildDashboard projectReport --scan --stacktrace > >(egrep -v '(null:-1:-1)|(t find node by signature)') +before_cache: + - curl "${GRAVIS}.clean_gradle_cache.sh" --output .clean_gradle_cache.sh + - bash .clean_gradle_cache.sh +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + - $HOME/.jabba/ +after_success: |- + cd "$TRAVIS_BUILD_DIR"/alchemist if $SHOULD_CONFIGURE && $PUBLISH; then ./gradlew orchidDeploy publish --scan --stacktrace mkdir -p report cp --parent */build/reports build/reports report -R fi - deploy: - provider: releases skip_cleanup: true From e56063aed83b89f5cb5aebb6cae145dccbd2a72b Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Fri, 5 Jul 2019 12:07:31 +0200 Subject: [PATCH 02/21] Use an OpenJ9 11 version compatible with OSX --- .travis.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9249902a22..47f3ea4300 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ env: - PUBLISH="false" matrix: - JDK="adopt@1.11.28-0" - - JDK="adopt-openj9@1.11.28-0" + - JDK="adopt-openj9@1.11.0-3" - JDK="adopt@1.12.33-0" - JDK="1.12.0-1" matrix: @@ -30,10 +30,11 @@ matrix: env: PUBLISH="true" JDK="adopt@1.11.28-0" exclude: - os: linux - env: JDK="adopt@1.8.212-04" -before_install: |- - curl "${GRAVIS}.install-jdk-travis.sh" --output .install-jdk-travis.sh - bash .install-jdk-travis.sh + env: JDK="adopt@1.11.28-0" +before_install: + - curl "${GRAVIS}.install-jdk-travis.sh" --output .install-jdk-travis.sh + - bash .install-jdk-travis.sh && source ~/.jdk_config + - |- if [ $TRAVIS_OS_NAME = 'linux' ]; then sudo apt-get -qq update sudo apt-get install -y graphviz openjfx @@ -46,8 +47,6 @@ install: >- openssl aes-256-cbc -K $encrypted_9993846e3f84_key -iv $encrypted_9993846e3f84_iv -in prepare_environment.sh.enc -out prepare_environment.sh -d bash prepare_environment.sh fi -before_script: - - "if [ -f ~/.windows_config ]; then source ~/.windows_config; fi" script: |- cd "$TRAVIS_BUILD_DIR"/alchemist ./gradlew clean check fatJar dokka buildDashboard projectReport --scan --stacktrace > >(egrep -v '(null:-1:-1)|(t find node by signature)') From 2aa3e384915142298f5079d3776ce09bb3c3eab3 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Fri, 5 Jul 2019 12:37:11 +0200 Subject: [PATCH 03/21] Caching the JDK is not recommended by Travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 47f3ea4300..6a191cc036 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,7 +57,6 @@ cache: directories: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ - - $HOME/.jabba/ after_success: |- cd "$TRAVIS_BUILD_DIR"/alchemist if $SHOULD_CONFIGURE && $PUBLISH; then From b095f5ca61e97bbb6ed9ba74763237ba7279823b Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Fri, 5 Jul 2019 13:37:11 +0200 Subject: [PATCH 04/21] Try to work around the idiotic Travis git configuration --- .travis.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6a191cc036..3bfe56d697 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: bash git: depth: false os: - - osx +# - osx - linux - windows dist: xenial @@ -21,9 +21,9 @@ env: - PUBLISH="false" matrix: - JDK="adopt@1.11.28-0" - - JDK="adopt-openj9@1.11.0-3" - - JDK="adopt@1.12.33-0" - - JDK="1.12.0-1" + #- JDK="adopt-openj9@1.11.0-3" + #- JDK="adopt@1.12.33-0" + #- JDK="1.12.0-1" matrix: include: - os: linux @@ -32,6 +32,10 @@ matrix: - os: linux env: JDK="adopt@1.11.28-0" before_install: + - git config --global core.autocrlf false + - git config core.autocrlf false + - git status + - git reset --hard - curl "${GRAVIS}.install-jdk-travis.sh" --output .install-jdk-travis.sh - bash .install-jdk-travis.sh && source ~/.jdk_config - |- From 95a057f81290a116776e6a8e7b230c090a6805ac Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Fri, 5 Jul 2019 14:00:31 +0200 Subject: [PATCH 05/21] Launch status command after reset --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3bfe56d697..dfaec68a1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,6 +36,7 @@ before_install: - git config core.autocrlf false - git status - git reset --hard + - git status - curl "${GRAVIS}.install-jdk-travis.sh" --output .install-jdk-travis.sh - bash .install-jdk-travis.sh && source ~/.jdk_config - |- From 404113fa4e5783ef2e4e4be28099c1a97643b443 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Fri, 5 Jul 2019 14:13:50 +0200 Subject: [PATCH 06/21] Add build debug prints --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index dfaec68a1d..27d011322d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,8 +34,13 @@ matrix: before_install: - git config --global core.autocrlf false - git config core.autocrlf false + - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF + - git status + - git rm --cached -r . + - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF - git status - git reset --hard + - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF - git status - curl "${GRAVIS}.install-jdk-travis.sh" --output .install-jdk-travis.sh - bash .install-jdk-travis.sh && source ~/.jdk_config From bed40aaf9ce5f9063fa7045e1ef84d92b6b38b25 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Fri, 5 Jul 2019 14:20:29 +0200 Subject: [PATCH 07/21] Try not to fail after print --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 27d011322d..8b5cc77d96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,13 +34,13 @@ matrix: before_install: - git config --global core.autocrlf false - git config core.autocrlf false - - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF + - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF || echo none - git status - git rm --cached -r . - - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF + - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF || echo none - git status - git reset --hard - - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF + - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF || echo none - git status - curl "${GRAVIS}.install-jdk-travis.sh" --output .install-jdk-travis.sh - bash .install-jdk-travis.sh && source ~/.jdk_config From a9574a28546861eb0c3a670e2d2897810a3aff63 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Fri, 5 Jul 2019 14:42:54 +0200 Subject: [PATCH 08/21] Work around insane git behaviour on travis via script --- .travis.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8b5cc77d96..c3075fa7ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,16 +32,7 @@ matrix: - os: linux env: JDK="adopt@1.11.28-0" before_install: - - git config --global core.autocrlf false - - git config core.autocrlf false - - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF || echo none - - git status - - git rm --cached -r . - - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF || echo none - - git status - - git reset --hard - - find alchemist/src -not -type d -exec file "{}" ";" | grep CRLF || echo none - - git status + - curl "${GRAVIS}.win-autoclrf-workaround.sh" --output .win-autoclrf.sh && bash .win-autoclrf.sh - curl "${GRAVIS}.install-jdk-travis.sh" --output .install-jdk-travis.sh - bash .install-jdk-travis.sh && source ~/.jdk_config - |- From bc6793e515d08121771c7b90a32c91584d43d6d9 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Fri, 5 Jul 2019 15:01:29 +0200 Subject: [PATCH 09/21] Re-enable OSX and all the supported JDKs --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c3075fa7ef..0db5209b0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: bash git: depth: false os: -# - osx + - osx - linux - windows dist: xenial @@ -21,9 +21,9 @@ env: - PUBLISH="false" matrix: - JDK="adopt@1.11.28-0" - #- JDK="adopt-openj9@1.11.0-3" - #- JDK="adopt@1.12.33-0" - #- JDK="1.12.0-1" + - JDK="adopt-openj9@1.11.0-3" + - JDK="adopt@1.12.33-0" + - JDK="1.12.0-1" matrix: include: - os: linux From f182884a41dd8065742404479ee84beae0f733a5 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Sat, 6 Jul 2019 11:32:15 +0200 Subject: [PATCH 10/21] Don't fail fast on tests, generate full build --- alchemist/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/alchemist/build.gradle.kts b/alchemist/build.gradle.kts index d30cafc02c..652d4a5c5e 100644 --- a/alchemist/build.gradle.kts +++ b/alchemist/build.gradle.kts @@ -110,7 +110,6 @@ allprojects { } tasks.withType { - failFast = true testLogging { events("passed", "skipped", "failed", "standardError") exceptionFormat = TestExceptionFormat.FULL From 6b8e705b23af19a4918b3c2ab620b5f88a3482a9 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Sat, 6 Jul 2019 13:05:16 +0200 Subject: [PATCH 11/21] Use a portable path separator in Checkstyle suppressions --- alchemist/config/checkstyle/suppressions.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alchemist/config/checkstyle/suppressions.xml b/alchemist/config/checkstyle/suppressions.xml index 23971e3920..2fd7f1fca4 100644 --- a/alchemist/config/checkstyle/suppressions.xml +++ b/alchemist/config/checkstyle/suppressions.xml @@ -16,8 +16,8 @@ - + - + \ No newline at end of file From 77522ce5ae5c2fecf2c8296c720df738849eaae1 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Sat, 6 Jul 2019 14:04:07 +0200 Subject: [PATCH 12/21] temporarily disable test in engine --- .../java/it/unibo/alchemist/core/tests/TestConcurrency.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java index 7c24087371..a4497a7f20 100644 --- a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java +++ b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java @@ -61,7 +61,7 @@ public void setUp() { /** * Test if the status of a {@link Engine} changes as expected. */ - @Test +// @Test @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification = "We don't need the status of the Runnable") public void testCommandInterleaving() { final Simulation sim = new Engine<>(env, 10); @@ -103,7 +103,7 @@ public void newTest2() { final ExecutorService ex = Executors.newCachedThreadPool(); ex.submit(sim); ex.submit(sim::play); - sim.waitFor(Status.TERMINATED, 1, TimeUnit.SECONDS); + sim.waitFor(Status.TERMINATED, 10, TimeUnit.SECONDS); verifyStatus(ex, sim, Status.TERMINATED); } From d1093570b2cb505a7d58507c7fff72b9d8d54290 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Sat, 6 Jul 2019 14:51:26 +0200 Subject: [PATCH 13/21] Use tolerant timings --- .../java/it/unibo/alchemist/core/tests/TestConcurrency.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java index a4497a7f20..9e51bdaf83 100644 --- a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java +++ b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java @@ -61,7 +61,7 @@ public void setUp() { /** * Test if the status of a {@link Engine} changes as expected. */ -// @Test + @Test @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification = "We don't need the status of the Runnable") public void testCommandInterleaving() { final Simulation sim = new Engine<>(env, 10); @@ -74,7 +74,7 @@ public void testCommandInterleaving() { fail(); } verifyStatus(ex, sim, Status.PAUSED); - sim.waitFor(Status.PAUSED, 10, TimeUnit.MILLISECONDS); + sim.waitFor(Status.PAUSED, 100, TimeUnit.MILLISECONDS); verifyStatus(ex, sim, Status.PAUSED); ex.submit(sim::play); sim.waitFor(Status.RUNNING, 1, TimeUnit.SECONDS); // the method must return instantly @@ -88,7 +88,7 @@ public void testCommandInterleaving() { * the method must return immediately with a message error because is not * possible to reach RUNNING or PAUSED status while in STOPPED */ - sim.waitFor(Status.RUNNING, 10, TimeUnit.MILLISECONDS); + sim.waitFor(Status.RUNNING, 100, TimeUnit.MILLISECONDS); ex.shutdown(); verifyStatus(ex, sim, Status.TERMINATED); } From 0848bdfa91ac0b6d8f41ebb3aef4b88a7270a43b Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Mon, 8 Jul 2019 10:30:30 +0200 Subject: [PATCH 14/21] Try to build on Windows only, disable stacktrace, try to get a build scan --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0db5209b0f..19ce2de9bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ language: bash git: depth: false os: - - osx - - linux +# - osx +# - linux - windows dist: xenial env: @@ -50,7 +50,7 @@ install: >- fi script: |- cd "$TRAVIS_BUILD_DIR"/alchemist - ./gradlew clean check fatJar dokka buildDashboard projectReport --scan --stacktrace > >(egrep -v '(null:-1:-1)|(t find node by signature)') + ./gradlew clean check fatJar dokka buildDashboard projectReport --scan > >(egrep -v '(null:-1:-1)|(t find node by signature)') before_cache: - curl "${GRAVIS}.clean_gradle_cache.sh" --output .clean_gradle_cache.sh - bash .clean_gradle_cache.sh From 5ab4510e6e3f7af3a4377cb2e5daa9b1ae27aae7 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Mon, 8 Jul 2019 13:25:28 +0200 Subject: [PATCH 15/21] Make engine throw exception in case of unexpected waitFor command --- .../core/implementations/Engine.java | 4 ++-- .../alchemist/core/tests/TestConcurrency.java | 21 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/alchemist/alchemist-engine/src/main/java/it/unibo/alchemist/core/implementations/Engine.java b/alchemist/alchemist-engine/src/main/java/it/unibo/alchemist/core/implementations/Engine.java index 0d6075a057..3a6c036612 100644 --- a/alchemist/alchemist-engine/src/main/java/it/unibo/alchemist/core/implementations/Engine.java +++ b/alchemist/alchemist-engine/src/main/java/it/unibo/alchemist/core/implementations/Engine.java @@ -454,7 +454,7 @@ private void updateReaction(final Reaction r) { @Override public Status waitFor(final Status s, final long timeout, final TimeUnit tu) { if (this.compareStatuses(s) > 0) { - L.error("Attempt to wait for an illegal status: " + s + " (current state is: " + getStatus() + ")"); + throw new IllegalArgumentException("Attempt to wait for an illegal status: " + s + " (current state is: " + getStatus() + ")"); } else { statusLock.lock(); try { @@ -478,11 +478,11 @@ public Status waitFor(final Status s, final long timeout, final TimeUnit tu) { } } } + return getStatus(); } finally { statusLock.unlock(); } } - return getStatus(); } // CHECKSTYLE: FinalClassCheck OFF diff --git a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java index 9e51bdaf83..ceed71488a 100644 --- a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java +++ b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java @@ -32,6 +32,8 @@ import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -64,31 +66,26 @@ public void setUp() { @Test @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification = "We don't need the status of the Runnable") public void testCommandInterleaving() { + final ExecutorService ex = Executors.newFixedThreadPool(1); final Simulation sim = new Engine<>(env, 10); - final ExecutorService ex = Executors.newCachedThreadPool(); + sim.pause(); ex.submit(sim); - ex.submit(sim::pause); - if (sim.waitFor(Status.RUNNING, 1, TimeUnit.SECONDS) != Status.RUNNING) { // after a second the method must return - L.info("The status I was waiting for did not arrived! (as predicted)"); - } else { - fail(); - } - verifyStatus(ex, sim, Status.PAUSED); - sim.waitFor(Status.PAUSED, 100, TimeUnit.MILLISECONDS); + assertNotEquals(Status.RUNNING, sim.waitFor(Status.RUNNING, 2, TimeUnit.SECONDS)); + sim.waitFor(Status.PAUSED, 1, TimeUnit.SECONDS); verifyStatus(ex, sim, Status.PAUSED); - ex.submit(sim::play); + sim.play(); sim.waitFor(Status.RUNNING, 1, TimeUnit.SECONDS); // the method must return instantly /* * this test does only 10 steps, so, after reaching RUNNING status, the simulation stops almost * instantly, because it takes a very little time to perform 10 steps, since in every step the * simulation executes the fake reaction you can see below, which simply does nothing. */ - verifyStatus(ex, sim, Status.TERMINATED); + assertEquals(Status.TERMINATED, sim.waitFor(Status.TERMINATED, 1, TimeUnit.SECONDS)); /* * the method must return immediately with a message error because is not * possible to reach RUNNING or PAUSED status while in STOPPED */ - sim.waitFor(Status.RUNNING, 100, TimeUnit.MILLISECONDS); + assertThrows(RuntimeException.class, () -> sim.waitFor(Status.RUNNING, 100, TimeUnit.MILLISECONDS)); ex.shutdown(); verifyStatus(ex, sim, Status.TERMINATED); } From ce39b7ad4e4037cb2962e6986b92430d7c185692 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Mon, 8 Jul 2019 13:27:13 +0200 Subject: [PATCH 16/21] Delete unused field --- .../java/it/unibo/alchemist/core/tests/TestConcurrency.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java index ceed71488a..ce5fc6e488 100644 --- a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java +++ b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java @@ -24,8 +24,6 @@ import it.unibo.alchemist.model.interfaces.TimeDistribution; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -42,8 +40,6 @@ */ public class TestConcurrency { - private static final Logger L = LoggerFactory.getLogger(Engine.class); - private Environment env; /** From c9c8f3e9e0d1111d359732072b2ec33ef7c80262 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Mon, 8 Jul 2019 14:08:53 +0200 Subject: [PATCH 17/21] Use ForkJoinPool in test, try to make it more resilient --- .../alchemist/core/tests/TestConcurrency.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java index ce5fc6e488..6035faf24e 100644 --- a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java +++ b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java @@ -25,8 +25,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -61,16 +64,19 @@ public void setUp() { */ @Test @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification = "We don't need the status of the Runnable") - public void testCommandInterleaving() { - final ExecutorService ex = Executors.newFixedThreadPool(1); + public void testCommandInterleaving() throws InterruptedException, ExecutionException { final Simulation sim = new Engine<>(env, 10); sim.pause(); - ex.submit(sim); + ForkJoinPool.commonPool().submit(sim); assertNotEquals(Status.RUNNING, sim.waitFor(Status.RUNNING, 2, TimeUnit.SECONDS)); - sim.waitFor(Status.PAUSED, 1, TimeUnit.SECONDS); - verifyStatus(ex, sim, Status.PAUSED); + assertEquals(Status.PAUSED, sim.waitFor(Status.PAUSED, 1, TimeUnit.SECONDS)); + /* + * Wait for running status then send a play command + */ + Future running = ForkJoinPool.commonPool().submit(() -> sim.waitFor(Status.RUNNING, 2, TimeUnit.SECONDS)); + Thread.sleep(100); sim.play(); - sim.waitFor(Status.RUNNING, 1, TimeUnit.SECONDS); // the method must return instantly + assertEquals(Status.RUNNING, running.get()); /* * this test does only 10 steps, so, after reaching RUNNING status, the simulation stops almost * instantly, because it takes a very little time to perform 10 steps, since in every step the @@ -82,8 +88,6 @@ public void testCommandInterleaving() { * possible to reach RUNNING or PAUSED status while in STOPPED */ assertThrows(RuntimeException.class, () -> sim.waitFor(Status.RUNNING, 100, TimeUnit.MILLISECONDS)); - ex.shutdown(); - verifyStatus(ex, sim, Status.TERMINATED); } /** From e3ca2ff574489f88af5124c9be2c73e19d418224 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Mon, 8 Jul 2019 14:24:17 +0200 Subject: [PATCH 18/21] Fix missing Javadoc --- .../java/it/unibo/alchemist/core/tests/TestConcurrency.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java index 6035faf24e..5c0c06cd62 100644 --- a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java +++ b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java @@ -60,7 +60,11 @@ public void setUp() { } /** + * * Test if the status of a {@link Engine} changes as expected. + * + * @throws InterruptedException fails + * @throws ExecutionException fails */ @Test @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification = "We don't need the status of the Runnable") From 6667f68b8cc110010451a44e46edddefee57c19f Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Mon, 8 Jul 2019 14:35:26 +0200 Subject: [PATCH 19/21] Make local variable final --- .../java/it/unibo/alchemist/core/tests/TestConcurrency.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java index 5c0c06cd62..f759b7fe03 100644 --- a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java +++ b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java @@ -77,7 +77,7 @@ public void testCommandInterleaving() throws InterruptedException, ExecutionExce /* * Wait for running status then send a play command */ - Future running = ForkJoinPool.commonPool().submit(() -> sim.waitFor(Status.RUNNING, 2, TimeUnit.SECONDS)); + final Future running = ForkJoinPool.commonPool().submit(() -> sim.waitFor(Status.RUNNING, 2, TimeUnit.SECONDS)); Thread.sleep(100); sim.play(); assertEquals(Status.RUNNING, running.get()); From 391834bad9443dfa12f1620779599b368bba7bac Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Mon, 8 Jul 2019 19:35:54 +0200 Subject: [PATCH 20/21] Change concurrency in Engine --- .../core/implementations/Engine.java | 175 ++++++++++-------- .../alchemist/core/tests/TestConcurrency.java | 58 ++---- .../alchemist-interfaces/build.gradle.kts | 1 + .../alchemist/core/interfaces/Status.java | 10 + .../it/unibo/alchemist/test/TestStatus.java | 30 --- .../src/test/kotlin/TestStatus.kt | 52 ++++++ 6 files changed, 181 insertions(+), 145 deletions(-) delete mode 100644 alchemist/alchemist-interfaces/src/test/java/it/unibo/alchemist/test/TestStatus.java create mode 100644 alchemist/alchemist-interfaces/src/test/kotlin/TestStatus.kt diff --git a/alchemist/alchemist-engine/src/main/java/it/unibo/alchemist/core/implementations/Engine.java b/alchemist/alchemist-engine/src/main/java/it/unibo/alchemist/core/implementations/Engine.java index 3a6c036612..78ed20912c 100644 --- a/alchemist/alchemist-engine/src/main/java/it/unibo/alchemist/core/implementations/Engine.java +++ b/alchemist/alchemist-engine/src/main/java/it/unibo/alchemist/core/implementations/Engine.java @@ -7,6 +7,7 @@ */ package it.unibo.alchemist.core.implementations; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import it.unibo.alchemist.boundary.interfaces.OutputMonitor; import it.unibo.alchemist.core.interfaces.DependencyGraph; @@ -28,6 +29,7 @@ import org.slf4j.LoggerFactory; import java.util.ArrayDeque; +import java.util.Arrays; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -37,12 +39,19 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Stream; +import static it.unibo.alchemist.core.interfaces.Status.PAUSED; +import static it.unibo.alchemist.core.interfaces.Status.RUNNING; +import static it.unibo.alchemist.core.interfaces.Status.TERMINATED; + /** * This class implements a simulation. It offers a wide number of static * factories to ease the creation process. @@ -57,7 +66,8 @@ public final class Engine> implements Simulat private static final Logger L = LoggerFactory.getLogger(Engine.class); private static final double NANOS_TO_SEC = 1000000000.0; private final Lock statusLock = new ReentrantLock(); - private final Condition statusCondition = statusLock.newCondition(); + private final ImmutableMap statusLocks = Arrays.stream(Status.values()) + .collect(ImmutableMap.toImmutableMap(Function.identity(), it -> new SynchBox())); private final BlockingQueue commands = new LinkedBlockingQueue<>(); private final Queue afterExecutionUpdates = new ArrayDeque<>(); private final Environment env; @@ -140,18 +150,26 @@ private void checkCaller() { } } - private int compareStatuses(final Status o) { - if ((status == Status.RUNNING || status == Status.PAUSED) && (o == Status.RUNNING || o == Status.PAUSED)) { - return 0; - } else { - return status.compareTo(o); + private R doOnStatus(final Supplier fun) { + try { + statusLock.lock(); + return fun.get(); + } finally { + statusLock.unlock(); } } + private void doOnStatus(final Runnable fun) { + doOnStatus(() -> { + fun.run(); + return 0; + }); + } + private void doStep() { final Reaction mu = ipq.getNext(); if (mu == null) { - this.newStatus(Status.TERMINATED); + this.newStatus(TERMINATED); L.info("No more reactions."); } else { final Time t = mu.getTau(); @@ -185,7 +203,7 @@ private void doStep() { monitorLock.release(); } if (env.isTerminated()) { - newStatus(Status.TERMINATED); + newStatus(TERMINATED); L.info("Termination condition reached."); } currentStep++; @@ -250,13 +268,13 @@ public Time getTime() { } @Override - public synchronized void goToStep(final long step) { - runUntil(() -> getStep() < step); + public void goToStep(final long step) { + pauseWhen(() -> getStep() >= step); } @Override - public synchronized void goToTime(final Time t) { - runUntil(() -> getTime().compareTo(t) < 0); + public void goToTime(final Time t) { + pauseWhen(() -> getTime().compareTo(t) >= 0); } private void idleProcessSingleCommand() throws Throwable { @@ -284,20 +302,13 @@ public void neighborRemoved(final Node node, final Node n) { afterExecutionUpdates.add(new NeigborRemoved(node, n)); } - private void newStatus(final Status s) { - if (this.compareStatuses(s) > 0) { - L.error("Attempt to enter in an illegal status: " + s); - } else { - schedule(() -> { - statusLock.lock(); - try { - this.status = s; - statusCondition.signalAll(); - } finally { - statusLock.unlock(); - } - }); - } + private void newStatus(final Status next) { + schedule(() -> doOnStatus(() -> { + if (next.isReachableFrom(status)) { + status = next; + statusLocks.get(next).releaseAll(); + } + })); } @Override @@ -319,13 +330,13 @@ public void nodeRemoved(final Node node, final Neighborhood oldNeighborhoo } @Override - public synchronized void pause() { - newStatus(Status.PAUSED); + public void pause() { + newStatus(PAUSED); } @Override - public synchronized void play() { - newStatus(Status.RUNNING); + public void play() { + newStatus(RUNNING); } private Stream> reactionsToUpdateAfterExecution() { @@ -380,16 +391,14 @@ public void run() { while (status.equals(Status.READY)) { idleProcessSingleCommand(); } - while (status != Status.TERMINATED - && currentStep < finalStep - && currentTime.compareTo(finalTime) < 0) { + while (status != TERMINATED && currentStep < finalStep && currentTime.compareTo(finalTime) < 0) { while (!commands.isEmpty()) { processCommand(commands.poll()); } - if (status.equals(Status.RUNNING)) { + if (status.equals(RUNNING)) { doStep(); } - while (status.equals(Status.PAUSED)) { + while (status.equals(PAUSED)) { idleProcessSingleCommand(); } } @@ -397,7 +406,7 @@ public void run() { error = Optional.of(e); L.error("The simulation engine crashed.", e); } finally { - status = Status.TERMINATED; + status = TERMINATED; L.trace("Thread {} execution time: {}", currentThread, (System.nanoTime() - startExecutionTime) / NANOS_TO_SEC); commands.clear(); monitorLock.read(); @@ -409,19 +418,27 @@ public void run() { } } - private void runUntil(final BooleanSupplier condition) { - play(); - schedule(() -> { - while (status == Status.RUNNING && condition.getAsBoolean()) { - doStep(); + private void pauseWhen(final BooleanSupplier condition) { + addOutputMonitor(new OutputMonitor() { + @Override + public void finished(final Environment env, final Time time, final long step) { + } + @Override + public void initialized(final Environment env) { + if (condition.getAsBoolean()) { + pause(); + } + } + @Override + public void stepDone(final Environment env, final Reaction r, final Time time, final long step) { + initialized(env); } }); - pause(); } @Override - public synchronized void schedule(final CheckedRunnable r) { - if (getStatus().equals(Status.TERMINATED)) { + public void schedule(final CheckedRunnable r) { + if (getStatus().equals(TERMINATED)) { throw new IllegalStateException("This simulation is terminated and can not get resumed."); } commands.add(r); @@ -434,8 +451,8 @@ private void scheduleReaction(final Reaction r) { } @Override - public synchronized void terminate() { - newStatus(Status.TERMINATED); + public void terminate() { + newStatus(TERMINATED); } @Override @@ -452,37 +469,8 @@ private void updateReaction(final Reaction r) { } @Override - public Status waitFor(final Status s, final long timeout, final TimeUnit tu) { - if (this.compareStatuses(s) > 0) { - throw new IllegalArgumentException("Attempt to wait for an illegal status: " + s + " (current state is: " + getStatus() + ")"); - } else { - statusLock.lock(); - try { - if (!getStatus().equals(s)) { - boolean exit = false; - while (!exit) { - try { - if (timeout > 0) { - final boolean isOnTime = statusCondition.await(timeout, tu); - if (!isOnTime || getStatus().equals(s)) { - exit = true; - } - } else { - statusCondition.awaitUninterruptibly(); - if (getStatus().equals(s)) { - exit = true; - } - } - } catch (InterruptedException e) { - L.info("A wild spurious wakeup appears! Go, Catch Block! (wild 8-bit music rushes in background)"); - } - } - } - return getStatus(); - } finally { - statusLock.unlock(); - } - } + public Status waitFor(final Status next, final long timeout, final TimeUnit tu) { + return statusLocks.get(next).waitFor(next, timeout, tu); } // CHECKSTYLE: FinalClassCheck OFF @@ -607,4 +595,37 @@ public void performChanges() { dg.removeNeighbor(getSource(), getTarget()); } } + + private final class SynchBox { + private final AtomicInteger queueLength = new AtomicInteger(); + private final Condition statusReached = statusLock.newCondition(); + private final Condition allReleased = statusLock.newCondition(); + public Status waitFor(final Status next, final long timeout, final TimeUnit tu) { + return doOnStatus(() -> { + boolean notTimedOut = true; + while (notTimedOut && next != status && next.isReachableFrom(status)) { + try { + queueLength.getAndIncrement(); + notTimedOut = statusReached.await(timeout, tu); + queueLength.getAndDecrement(); + } catch (InterruptedException e) { + L.info("Spurious wakeup?", e); + } + } + if (queueLength.get() == 0) { + allReleased.signal(); + } + return status; + }); + } + public void releaseAll() { + doOnStatus(() -> { + while (queueLength.get() != 0) { + statusReached.signalAll(); + allReleased.awaitUninterruptibly(); + } + }); + } + } + } diff --git a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java index f759b7fe03..bcd35a065e 100644 --- a/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java +++ b/alchemist/alchemist-engine/src/test/java/it/unibo/alchemist/core/tests/TestConcurrency.java @@ -8,6 +8,7 @@ package it.unibo.alchemist.core.tests; +import com.google.common.collect.ImmutableList; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import it.unibo.alchemist.core.implementations.Engine; import it.unibo.alchemist.core.interfaces.Simulation; @@ -25,18 +26,17 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; /** * This class tests some basic Commands, like pause and start. @@ -71,16 +71,28 @@ public void setUp() { public void testCommandInterleaving() throws InterruptedException, ExecutionException { final Simulation sim = new Engine<>(env, 10); sim.pause(); - ForkJoinPool.commonPool().submit(sim); + final ExecutorService container = Executors.newCachedThreadPool(); + container.submit(sim); assertNotEquals(Status.RUNNING, sim.waitFor(Status.RUNNING, 2, TimeUnit.SECONDS)); assertEquals(Status.PAUSED, sim.waitFor(Status.PAUSED, 1, TimeUnit.SECONDS)); /* - * Wait for running status then send a play command + * Launch a hundred waiting processes, make sure they are started, then make sure everyone got notified */ - final Future running = ForkJoinPool.commonPool().submit(() -> sim.waitFor(Status.RUNNING, 2, TimeUnit.SECONDS)); + final int inWaitCount = 100; + final CountDownLatch latch = new CountDownLatch(inWaitCount); + final ImmutableList> waitList = IntStream.range(0, inWaitCount) + .mapToObj(it -> container.submit(() -> { + latch.countDown(); + return sim.waitFor(Status.RUNNING, 10, TimeUnit.SECONDS); + })) + .collect(ImmutableList.toImmutableList()); + assertTrue(latch.await(10, TimeUnit.SECONDS)); + // All threads are started, wait a bit of additional time to make sure they reached wait status Thread.sleep(100); sim.play(); - assertEquals(Status.RUNNING, running.get()); + for (final Future result: waitList) { + assertEquals(Status.RUNNING, result.get()); + } /* * this test does only 10 steps, so, after reaching RUNNING status, the simulation stops almost * instantly, because it takes a very little time to perform 10 steps, since in every step the @@ -91,37 +103,7 @@ public void testCommandInterleaving() throws InterruptedException, ExecutionExce * the method must return immediately with a message error because is not * possible to reach RUNNING or PAUSED status while in STOPPED */ - assertThrows(RuntimeException.class, () -> sim.waitFor(Status.RUNNING, 100, TimeUnit.MILLISECONDS)); - } - - /** - * Tests if the simulation ends correctly when it reaches the last step. - */ - @Test - @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification = "We don't need the status of the Runnable") - public void newTest2() { - final Simulation sim = new Engine<>(env, 10); - final ExecutorService ex = Executors.newCachedThreadPool(); - ex.submit(sim); - ex.submit(sim::play); - sim.waitFor(Status.TERMINATED, 10, TimeUnit.SECONDS); - verifyStatus(ex, sim, Status.TERMINATED); - } - - private void verifyStatus(final ExecutorService ex, final Simulation sim, final Status s) { - try { - if (ex.isShutdown()) { - if (!ex.awaitTermination(1000, TimeUnit.MILLISECONDS)) { - fail("The thread did not end on time; its status is " + sim.getStatus()); - } - assertTrue(ex.isTerminated()); - } else { - Thread.sleep(100); - assertEquals(s, sim.getStatus()); - } - } catch (InterruptedException e) { - fail(e.getMessage()); - } + assertEquals(Status.TERMINATED, sim.waitFor(Status.RUNNING, 100, TimeUnit.MILLISECONDS)); } private static final class DummyNode extends AbstractNode { diff --git a/alchemist/alchemist-interfaces/build.gradle.kts b/alchemist/alchemist-interfaces/build.gradle.kts index b1c16eac3c..873ae6aa97 100644 --- a/alchemist/alchemist-interfaces/build.gradle.kts +++ b/alchemist/alchemist-interfaces/build.gradle.kts @@ -10,4 +10,5 @@ dependencies { api(Libs.listset) api(Libs.jool_java_8) api(Libs.commons_math3) + testCompile(Libs.kotlintest_runner_junit5) } diff --git a/alchemist/alchemist-interfaces/src/main/java/it/unibo/alchemist/core/interfaces/Status.java b/alchemist/alchemist-interfaces/src/main/java/it/unibo/alchemist/core/interfaces/Status.java index d488c9df4a..3a47a6e335 100644 --- a/alchemist/alchemist-interfaces/src/main/java/it/unibo/alchemist/core/interfaces/Status.java +++ b/alchemist/alchemist-interfaces/src/main/java/it/unibo/alchemist/core/interfaces/Status.java @@ -44,4 +44,14 @@ public enum Status { */ TERMINATED; + /** + * + * @param s the destination status + * @return true if the provided status can be reached from this status (i.e., if the simulation lifecycle allows to + * get from the current status to the provided one). + */ + public boolean isReachableFrom(final Status s) { + return compareTo(s) >= 0 || this == PAUSED && s == RUNNING; + } + } diff --git a/alchemist/alchemist-interfaces/src/test/java/it/unibo/alchemist/test/TestStatus.java b/alchemist/alchemist-interfaces/src/test/java/it/unibo/alchemist/test/TestStatus.java deleted file mode 100644 index b52129b26e..0000000000 --- a/alchemist/alchemist-interfaces/src/test/java/it/unibo/alchemist/test/TestStatus.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ -package it.unibo.alchemist.test; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import it.unibo.alchemist.core.interfaces.Status; - -import org.junit.jupiter.api.Test; - - -/** - */ -public class TestStatus { - - /** - * - */ - @Test - public void test() { - assertNotNull(Status.PAUSED); - assertNotNull(Status.RUNNING); - assertNotNull(Status.TERMINATED); - } - -} diff --git a/alchemist/alchemist-interfaces/src/test/kotlin/TestStatus.kt b/alchemist/alchemist-interfaces/src/test/kotlin/TestStatus.kt new file mode 100644 index 0000000000..fca51a1071 --- /dev/null +++ b/alchemist/alchemist-interfaces/src/test/kotlin/TestStatus.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010-2019, Danilo Pianini and contributors listed in the main project's alchemist/build.gradle file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ +package it.unibo.alchemist.test + +import io.kotlintest.Matcher +import io.kotlintest.Result +import io.kotlintest.data.forall +import io.kotlintest.should +import io.kotlintest.shouldNot +import io.kotlintest.specs.StringSpec +import io.kotlintest.tables.row +import it.unibo.alchemist.core.interfaces.Status +import it.unibo.alchemist.core.interfaces.Status.INIT +import it.unibo.alchemist.core.interfaces.Status.PAUSED +import it.unibo.alchemist.core.interfaces.Status.READY +import it.unibo.alchemist.core.interfaces.Status.RUNNING +import it.unibo.alchemist.core.interfaces.Status.TERMINATED + +/** + */ +class TestStatus : StringSpec({ + "subsequent statuses should be reachable, previous ones should not" { + val allStatuses = Status.values().toSet() + forall( + row(INIT, allStatuses), + row(READY, allStatuses.minusElement(INIT)), + row(PAUSED, setOf(RUNNING, TERMINATED)), + row(RUNNING, setOf(PAUSED, TERMINATED)), + row(TERMINATED, emptySet()) + ) { initial, states -> + val reachable = states + initial + reachable.forEach { it should beReachableFrom(initial) } + allStatuses.minus(reachable).forEach { + it shouldNot beReachableFrom(initial) + } + } + } +}) { + companion object { + // Waiting for https://youtrack.jetbrains.com/issue/KT-32489 to get solved + fun beReachableFrom(initial: Status) = object : Matcher { + override fun test(value: Status) = Result(value.isReachableFrom(initial), + "$value should be reachable from $initial", + "$value should not be reachable from $initial") + } + } +} From abded7bc8edc17a8d602b86434ec05482f863cce Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Mon, 8 Jul 2019 19:36:37 +0200 Subject: [PATCH 21/21] Re-enable multi-os build --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 19ce2de9bc..d1dd047c42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ language: bash git: depth: false os: -# - osx -# - linux + - osx + - linux - windows dist: xenial env: